Initial community commit
This commit is contained in:
@@ -0,0 +1,490 @@
|
||||
#include "main.h"
|
||||
#include "api.h"
|
||||
#include "ASXLoader.h"
|
||||
#include <stdio.h>
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "../xml/ifc_xmlreadercallback.h"
|
||||
#include "../xml/obj_xml.h"
|
||||
#include "api.h"
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
#include "../Winamp/strutil.h"
|
||||
#include <strsafe.h>
|
||||
#include "XMLString.h"
|
||||
|
||||
void SetUserAgent(api_httpreceiver *http)
|
||||
{
|
||||
char agent[256] = {0};
|
||||
StringCchPrintfA(agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString());
|
||||
http->addheader(agent);
|
||||
}
|
||||
|
||||
class ASXInfo : public ifc_plentryinfo
|
||||
{
|
||||
public:
|
||||
ASXInfo()
|
||||
{
|
||||
memset( returnTemp, 0, sizeof( returnTemp ) );
|
||||
}
|
||||
|
||||
const wchar_t *GetExtendedInfo( const wchar_t *parameter )
|
||||
{
|
||||
if ( !_wcsicmp( parameter, L"context" ) )
|
||||
{
|
||||
if ( isRadio )
|
||||
return L"radio";
|
||||
}
|
||||
else if ( !_wcsicmp( parameter, L"repeat" ) )
|
||||
{
|
||||
if ( repeat )
|
||||
{
|
||||
StringCchPrintfW( returnTemp, 20, L"%d", repeat );
|
||||
|
||||
return returnTemp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isRadio = false;
|
||||
int repeat = 0;
|
||||
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
|
||||
wchar_t returnTemp[ 20 ];
|
||||
};
|
||||
|
||||
#define CBCLASS ASXInfo
|
||||
START_DISPATCH;
|
||||
CB( IFC_PLENTRYINFO_GETEXTENDEDINFO, GetExtendedInfo )
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
class ASXXML : public ifc_xmlreadercallback
|
||||
{
|
||||
public:
|
||||
ASXXML(ifc_playlistloadercallback *_playlist, const wchar_t *_root, obj_xml *_parser) : playlist(_playlist), rootPath(_root), parser(_parser)
|
||||
{
|
||||
}
|
||||
|
||||
void OnFileHelper(ifc_playlistloadercallback *playlist, const wchar_t *filename, const wchar_t *title, int length, ifc_plentryinfo *extraInfo)
|
||||
{
|
||||
if (wcsstr(filename, L"://") || PathIsRootW(filename))
|
||||
{
|
||||
playlist->OnFile(filename, title, length, extraInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t fullPath[MAX_PATH] = {0}, canonicalizedPath[MAX_PATH] = {0};
|
||||
PathCombineW(fullPath, rootPath, filename);
|
||||
PathCanonicalizeW(canonicalizedPath, fullPath);
|
||||
playlist->OnFile(canonicalizedPath, title, length, extraInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
|
||||
{
|
||||
if (!_wcsicmp(xmltag, L"ENTRYREF"))
|
||||
{
|
||||
const wchar_t *url = params->getItemValue(L"HREF");
|
||||
|
||||
const wchar_t *titleHack = params->getItemValue(L"CLIENTBIND");
|
||||
int lengthHack = -1;
|
||||
wchar_t titleBuf[256] = L"";
|
||||
|
||||
if (titleHack)
|
||||
{
|
||||
// get the length out of the parantheses
|
||||
StringCchCopyW(titleBuf, 256, titleHack);
|
||||
wchar_t *end = titleBuf + lstrlenW(titleBuf);
|
||||
while (end && *end && *end != '(' && end != titleBuf)
|
||||
end = CharPrevW(titleBuf, end);
|
||||
|
||||
*end = 0;
|
||||
end++;
|
||||
lengthHack = _wtoi(end);
|
||||
}
|
||||
|
||||
wchar_t filename[FILENAME_SIZE] = {0};
|
||||
if (wcschr(url, L'?'))
|
||||
StringCchPrintfW(filename, FILENAME_SIZE, L"%s&=.asx", url);
|
||||
else
|
||||
StringCchPrintfW(filename, FILENAME_SIZE, L"%s?.asx", url);
|
||||
|
||||
OnFileHelper(playlist, filename, titleBuf, lengthHack*1000, &info);
|
||||
|
||||
}
|
||||
else if (!_wcsicmp(xmlpath, L"ASX\fENTRY\fREF") || !_wcsicmp(xmlpath, L"ASX\fREPEAT\fENTRY\fREF"))
|
||||
{
|
||||
const wchar_t *track = params->getItemValue(L"HREF");
|
||||
wchar_t fullTitle[128] = {0}, fullFilename[FILENAME_SIZE] = {0};
|
||||
// if there is no extension given, we need to add ?.wma or &=.wma to the end of the URL
|
||||
// this could be 2 lines of code if that wasn't the case :(
|
||||
if (track)
|
||||
{
|
||||
const wchar_t *trackTitle = 0;
|
||||
if (title.GetString()[0] && artist.GetString()[0])
|
||||
{
|
||||
StringCchPrintfW(fullTitle, 128, L"%s - %s", artist.GetString(), title.GetString());
|
||||
trackTitle = fullTitle;
|
||||
}
|
||||
if (!_wcsnicmp(track, L"http://", 7))
|
||||
{
|
||||
const wchar_t *end = scanstr_backcW(track, L"/.", 0);
|
||||
if (!end || *end == L'/')
|
||||
{
|
||||
if (wcschr(track, L'?'))
|
||||
StringCchPrintfW(fullFilename, FILENAME_SIZE, L"%s&=.wma", track);
|
||||
else
|
||||
StringCchPrintfW(fullFilename, FILENAME_SIZE, L"%s?.wma", track);
|
||||
track = fullFilename;
|
||||
}
|
||||
}
|
||||
|
||||
OnFileHelper(playlist, track, trackTitle, -1, &info);
|
||||
}
|
||||
}
|
||||
else if (!_wcsicmp(xmltag, L"REPEAT"))
|
||||
{
|
||||
const wchar_t *param;
|
||||
if (param = params->getItemValue(L"count"))
|
||||
{
|
||||
info.repeat = _wtoi(param);
|
||||
}
|
||||
else
|
||||
info.repeat = -1;
|
||||
}
|
||||
else if (!_wcsicmp(xmltag, L"PARAM"))
|
||||
{
|
||||
const wchar_t *param;
|
||||
if (param = params->getItemValue(L"name"))
|
||||
{
|
||||
if (!_wcsicmp(param, L"context"))
|
||||
{
|
||||
const wchar_t * value = params->getItemValue(L"value");
|
||||
if (!_wcsicmp(value, L"station"))
|
||||
info.isRadio = true;
|
||||
}
|
||||
else if (!_wcsicmp(param, L"encoding"))
|
||||
{
|
||||
const wchar_t *value=params->getItemValue(L"value");
|
||||
if (value)
|
||||
parser->xmlreader_setEncoding(value); // I hope we can set it on the fly like this!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag)
|
||||
{
|
||||
if (!_wcsicmp(xmltag, L"REPEAT"))
|
||||
{
|
||||
info.repeat = 0;
|
||||
}
|
||||
}
|
||||
ifc_playlistloadercallback *playlist;
|
||||
XMLString title, artist;
|
||||
ASXInfo info;
|
||||
const wchar_t *rootPath;
|
||||
obj_xml *parser;
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
|
||||
};
|
||||
|
||||
#define CBCLASS ASXXML
|
||||
START_DISPATCH;
|
||||
VCB(ONSTARTELEMENT, StartTag)
|
||||
VCB(ONENDELEMENT, EndTag)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
|
||||
/*
|
||||
TODO:
|
||||
|
||||
don't add tracks until all parts of the "ENTRY" tag are processed. There are some ASX playlists where the title comes AFTER the ref's
|
||||
|
||||
maybe have separate XML callbacks for metadata (title, author, shit like that) so that logic can be separated from the main ASX logic
|
||||
*/
|
||||
|
||||
// ASX isn't really XML. That means we need to URL-encode the text so our XML parser doesn't choke
|
||||
// if microsoft followed standards, the world would be a better place.
|
||||
int ASXLoader::GayASX_to_XML_converter(obj_xml *parser, char *buffer, int len)
|
||||
{
|
||||
// benski> I have no idea if ASX is always ASCII, or if it's UTF-8 or what.
|
||||
// but really I can't be bothered with Microsoft's lameness right now, so we'll assume it's local code page for the time being
|
||||
char *start = buffer;
|
||||
int sofar = 0;
|
||||
for (int i = 0;i < len;i++)
|
||||
{
|
||||
if (buffer[i] == '&')
|
||||
{
|
||||
if (sofar)
|
||||
{
|
||||
if (parser->xmlreader_feed(start, sofar) != API_XML_SUCCESS)
|
||||
return API_XML_FAILURE;
|
||||
}
|
||||
|
||||
if (parser->xmlreader_feed("&", 5) != API_XML_SUCCESS) // no null terminator
|
||||
return API_XML_FAILURE;
|
||||
start = &buffer[i + 1];
|
||||
sofar = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* ok, this might look really weird
|
||||
* but ASX doesn't have case sensitivity
|
||||
* so lots of playlists have things like
|
||||
* <title>This is the title</Title>
|
||||
* and so we have to accomodate
|
||||
* for this nonsense
|
||||
*/
|
||||
|
||||
if (inTag && !inQuotes)
|
||||
buffer[i] = toupper(buffer[i]);
|
||||
|
||||
if (buffer[i] == '>')
|
||||
{
|
||||
inTag=false;
|
||||
}
|
||||
else if (buffer[i] == '<')
|
||||
{
|
||||
inTag=true;
|
||||
}
|
||||
|
||||
// dro> only do uppercase handling on parts of the tag not inbetween quotes
|
||||
// (some servers just don't like having the urls case messed with, the swines)
|
||||
if (buffer[i] == '"')
|
||||
{
|
||||
if(!inQuotes)
|
||||
inQuotes=true;
|
||||
else
|
||||
inQuotes=false;
|
||||
}
|
||||
|
||||
sofar++;
|
||||
}
|
||||
}
|
||||
if (sofar && parser->xmlreader_feed(start, sofar) != API_XML_SUCCESS)
|
||||
return API_XML_FAILURE;
|
||||
OutputDebugStringA(buffer);
|
||||
return API_XML_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#define HTTP_BUFFER_SIZE 16384
|
||||
int ASXLoader::FeedXMLHTTP(api_httpreceiver *http, obj_xml *parser, bool *noData)
|
||||
{
|
||||
char downloadedData[HTTP_BUFFER_SIZE] = {0};
|
||||
int xmlResult = API_XML_SUCCESS;
|
||||
int downloadSize = http->get_bytes(downloadedData, HTTP_BUFFER_SIZE);
|
||||
if (downloadSize)
|
||||
{
|
||||
xmlResult = GayASX_to_XML_converter(parser, downloadedData, downloadSize);
|
||||
*noData=false;
|
||||
}
|
||||
else
|
||||
*noData = true;
|
||||
|
||||
return xmlResult;
|
||||
}
|
||||
|
||||
void ASXLoader::RunXMLDownload(api_httpreceiver *http, obj_xml *parser)
|
||||
{
|
||||
int ret;
|
||||
bool noData;
|
||||
do
|
||||
{
|
||||
Sleep(50);
|
||||
ret = http->run();
|
||||
if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS)
|
||||
return ;
|
||||
}
|
||||
while (ret == HTTPRECEIVER_RUN_OK);
|
||||
|
||||
// finish off the data
|
||||
do
|
||||
{
|
||||
if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS)
|
||||
return ;
|
||||
} while (!noData);
|
||||
|
||||
parser->xmlreader_feed(0, 0);
|
||||
}
|
||||
|
||||
int ASXLoader::LoadFile(obj_xml *parser, const wchar_t *filename)
|
||||
{
|
||||
HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
|
||||
while (true)
|
||||
{
|
||||
char data[1024] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead)
|
||||
{
|
||||
if (GayASX_to_XML_converter(parser, data, bytesRead) != API_XML_SUCCESS)
|
||||
{
|
||||
CloseHandle(file);
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
CloseHandle(file);
|
||||
if (parser->xmlreader_feed(0, 0) != API_XML_SUCCESS)
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
|
||||
return IFC_PLAYLISTLOADER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int ASXLoader::LoadURL(obj_xml *parser, const wchar_t *url)
|
||||
{
|
||||
api_httpreceiver *http = 0;
|
||||
waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
|
||||
if (sf) http = (api_httpreceiver *)sf->getInterface();
|
||||
|
||||
if (!http)
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
http->AllowCompression();
|
||||
http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, winamp.GetProxy());
|
||||
SetUserAgent(http);
|
||||
http->connect(AutoChar(url));
|
||||
int ret;
|
||||
|
||||
do
|
||||
{
|
||||
Sleep(10);
|
||||
ret = http->run();
|
||||
if (ret == -1) // connection failed
|
||||
break;
|
||||
|
||||
// ---- check our reply code ----
|
||||
int replycode = http->getreplycode();
|
||||
switch (replycode)
|
||||
{
|
||||
case 0:
|
||||
case 100:
|
||||
break;
|
||||
case 200:
|
||||
{
|
||||
RunXMLDownload(http, parser);
|
||||
sf->releaseInterface(http);
|
||||
return IFC_PLAYLISTLOADER_SUCCESS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sf->releaseInterface(http);
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
}
|
||||
}
|
||||
while (ret == HTTPRECEIVER_RUN_OK);
|
||||
//const char *er = http->geterrorstr();
|
||||
sf->releaseInterface(http);
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
}
|
||||
|
||||
static int loadasxv2fn(const wchar_t *filename, ifc_playlistloadercallback *playlist)
|
||||
{
|
||||
int i=1;
|
||||
wchar_t ref[FILENAME_SIZE] = {0};
|
||||
wchar_t key[100] = {0};
|
||||
while (1)
|
||||
{
|
||||
StringCchPrintfW(key, 100, L"Ref%d", i++);
|
||||
GetPrivateProfileStringW(L"Reference", key, L"?", ref, FILENAME_SIZE, filename);
|
||||
if (!lstrcmpiW(ref, L"?"))
|
||||
break;
|
||||
else
|
||||
{
|
||||
if (!_wcsnicmp(ref, L"http://", 7))
|
||||
{
|
||||
const wchar_t *end = scanstr_backcW(ref, L"/.", 0);
|
||||
if (!end || *end == L'/')
|
||||
{
|
||||
if (wcschr(ref, L'?'))
|
||||
StringCchCatW(ref, FILENAME_SIZE, L"&=.wma");
|
||||
else
|
||||
StringCchCatW(ref, FILENAME_SIZE, L"?.wma");
|
||||
}
|
||||
}
|
||||
|
||||
playlist->OnFile(ref, 0, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
return IFC_PLAYLISTLOADER_SUCCESS;
|
||||
}
|
||||
|
||||
int ASXLoader::Load(const wchar_t *filename, ifc_playlistloadercallback *playlist)
|
||||
{
|
||||
obj_xml *parser = 0;
|
||||
waServiceFactory *parserFactory = 0;
|
||||
|
||||
HANDLE quickTest = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
|
||||
if (quickTest != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
char reference[11] = {0};
|
||||
DWORD bytesRead=0;
|
||||
ReadFile(quickTest, reference, 11, &bytesRead, 0);
|
||||
CloseHandle(quickTest);
|
||||
if (bytesRead == 11 && !_strnicmp(reference, "[Reference]", 11))
|
||||
return loadasxv2fn(filename, playlist);
|
||||
}
|
||||
|
||||
parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
|
||||
if (parserFactory)
|
||||
parser = (obj_xml *)parserFactory->getInterface();
|
||||
|
||||
if (parser)
|
||||
{
|
||||
wchar_t rootPath[MAX_PATH] = {0};
|
||||
const wchar_t *callbackPath = playlist->GetBasePath();
|
||||
if (callbackPath)
|
||||
lstrcpynW(rootPath, callbackPath, MAX_PATH);
|
||||
else
|
||||
{
|
||||
lstrcpynW(rootPath, filename, MAX_PATH);
|
||||
PathRemoveFileSpecW(rootPath);
|
||||
}
|
||||
|
||||
ASXXML asxXml(playlist, rootPath, parser);
|
||||
parser->xmlreader_registerCallback(L"ASX\f*", &asxXml);
|
||||
parser->xmlreader_registerCallback(L"ASX\fENTRY\fTITLE", &asxXml.title);
|
||||
parser->xmlreader_registerCallback(L"ASX\fENTRY\fAUTHOR", &asxXml.artist);
|
||||
parser->xmlreader_open();
|
||||
parser->xmlreader_setEncoding(L"windows-1252");
|
||||
|
||||
int ret;
|
||||
if (wcsstr(filename, L"://"))
|
||||
ret = LoadURL(parser, filename);
|
||||
else
|
||||
ret = LoadFile(parser, filename);
|
||||
|
||||
parser->xmlreader_unregisterCallback(&asxXml);
|
||||
parser->xmlreader_unregisterCallback(&asxXml.title);
|
||||
parser->xmlreader_unregisterCallback(&asxXml.artist);
|
||||
parser->xmlreader_close();
|
||||
parserFactory->releaseInterface(parser);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
}
|
||||
|
||||
#define CBCLASS ASXLoader
|
||||
START_DISPATCH;
|
||||
CB(IFC_PLAYLISTLOADER_LOAD, Load)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef NULLSOFT_PLAYLIST_ASX_LOADER_H
|
||||
#define NULLSOFT_PLAYLIST_ASX_LOADER_H
|
||||
|
||||
#include "../playlist/ifc_playlistloader.h"
|
||||
#include "../playlist/ifc_playlistloadercallback.h"
|
||||
#include <stdio.h>
|
||||
|
||||
class obj_xml;
|
||||
class api_httpreceiver;
|
||||
class ASXLoader : public ifc_playlistloader
|
||||
{
|
||||
public:
|
||||
ASXLoader() : inTag(false), inQuotes(false) {}
|
||||
int Load(const wchar_t *filename, ifc_playlistloadercallback *playlist);
|
||||
|
||||
private:
|
||||
int LoadURL(obj_xml *parser, const wchar_t *url);
|
||||
int LoadFile(obj_xml *parser, const wchar_t *filename);
|
||||
void RunXMLDownload(api_httpreceiver *http, obj_xml *parser);
|
||||
int FeedXMLHTTP(api_httpreceiver *http, obj_xml *parser, bool *noData);
|
||||
int GayASX_to_XML_converter(obj_xml *parser, char *buffer, int len);
|
||||
|
||||
|
||||
protected:
|
||||
bool inTag;
|
||||
bool inQuotes;
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "main.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "AlbumArt.h"
|
||||
#include "util.h"
|
||||
#include <shlwapi.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
bool ASF_AlbumArtProvider::IsMine(const wchar_t *filename)
|
||||
{
|
||||
const wchar_t *ext = PathFindExtension(filename);
|
||||
if (ext && *ext)
|
||||
{
|
||||
ext++;
|
||||
return fileTypes.GetAVType(ext) != -1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ASF_AlbumArtProvider::ProviderType()
|
||||
{
|
||||
return ALBUMARTPROVIDER_TYPE_EMBEDDED;
|
||||
}
|
||||
|
||||
bool NameToAPICType(const wchar_t *name, int &num)
|
||||
{
|
||||
if (!name || !*name) // default to cover
|
||||
num=0x3;
|
||||
else if (!_wcsicmp(name, L"fileicon")) // 32x32 pixels 'file icon' (PNG only)
|
||||
num=0x1;
|
||||
else if (!_wcsicmp(name, L"icon")) // Other file icon
|
||||
num=0x2;
|
||||
else if (!_wcsicmp(name, L"cover")) // Cover (front)
|
||||
num=0x3;
|
||||
else if (!_wcsicmp(name, L"back")) // Cover (back)
|
||||
num=0x4;
|
||||
else if (!_wcsicmp(name, L"leaflet")) // Leaflet page
|
||||
num=0x5;
|
||||
else if (!_wcsicmp(name, L"media")) // Media (e.g. lable side of CD)
|
||||
num=0x6;
|
||||
else if (!_wcsicmp(name, L"leadartist")) //Lead artist/lead performer/soloist
|
||||
num=0x7;
|
||||
else if (!_wcsicmp(name, L"artist")) // Artist/performer
|
||||
num=0x8;
|
||||
else if (!_wcsicmp(name, L"conductor")) // Conductor
|
||||
num=0x9;
|
||||
else if (!_wcsicmp(name, L"band")) // Band/Orchestra
|
||||
num=0xA;
|
||||
else if (!_wcsicmp(name, L"composer")) // Composer
|
||||
num=0xB;
|
||||
else if (!_wcsicmp(name, L"lyricist")) // Lyricist/text writer
|
||||
num=0xC;
|
||||
else if (!_wcsicmp(name, L"location")) // Recording Location
|
||||
num=0xD;
|
||||
else if (!_wcsicmp(name, L"recording")) // During recording
|
||||
num=0xE;
|
||||
else if (!_wcsicmp(name, L"performance")) // During performance
|
||||
num=0xF;
|
||||
else if (!_wcsicmp(name, L"preview")) // Movie/video screen capture
|
||||
num=0x10;
|
||||
else if (!_wcsicmp(name, L"fish")) // A bright coloured fish
|
||||
num=0x11;
|
||||
else if (!_wcsicmp(name, L"illustration")) // Illustration
|
||||
num=0x12;
|
||||
else if (!_wcsicmp(name, L"artistlogo")) // Band/artist logotype
|
||||
num=0x13;
|
||||
else if (!_wcsicmp(name, L"publisherlogo")) // Publisher/Studio logotype
|
||||
num=0x14;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int ASF_AlbumArtProvider::GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
|
||||
{
|
||||
int pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
WMInformation wm(filename);
|
||||
if (wm.GetPicture(bits, len, mimeType, pictype))
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
int ASF_AlbumArtProvider::SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
|
||||
{
|
||||
int pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
WMInformation wm(filename);
|
||||
if (!wm.MakeWritable(filename))
|
||||
return ALBUMARTPROVIDER_READONLY; // can't write
|
||||
|
||||
if (wm.SetPicture(bits, len, mimeType, pictype))
|
||||
{
|
||||
wm.Flush();
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
int ASF_AlbumArtProvider::DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
|
||||
{
|
||||
int pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
WMInformation wm(filename);
|
||||
if (!wm.MakeWritable(filename))
|
||||
{
|
||||
if (wm.HasPicture(pictype))
|
||||
return ALBUMARTPROVIDER_READONLY; // can't write
|
||||
else
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
if (wm.DeletePicture(pictype))
|
||||
{
|
||||
wm.Flush();
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
#define CBCLASS ASF_AlbumArtProvider
|
||||
START_DISPATCH;
|
||||
CB(SVC_ALBUMARTPROVIDER_PROVIDERTYPE, ProviderType);
|
||||
CB(SVC_ALBUMARTPROVIDER_GETALBUMARTDATA, GetAlbumArtData);
|
||||
CB(SVC_ALBUMARTPROVIDER_SETALBUMARTDATA, SetAlbumArtData);
|
||||
CB(SVC_ALBUMARTPROVIDER_DELETEALBUMART, DeleteAlbumArt);
|
||||
CB(SVC_ALBUMARTPROVIDER_ISMINE, IsMine);
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
static ASF_AlbumArtProvider albumArtProvider;
|
||||
|
||||
// {B4184902-EE79-4015-B9A4-76209C6153FA}
|
||||
static const GUID asf_albumartproviderGUID =
|
||||
{ 0xb4184902, 0xee79, 0x4015, { 0xb9, 0xa4, 0x76, 0x20, 0x9c, 0x61, 0x53, 0xfa } };
|
||||
|
||||
|
||||
FOURCC AlbumArtFactory::GetServiceType()
|
||||
{
|
||||
return svc_albumArtProvider::SERVICETYPE;
|
||||
}
|
||||
|
||||
const char *AlbumArtFactory::GetServiceName()
|
||||
{
|
||||
return "ASF Album Art Provider";
|
||||
}
|
||||
|
||||
GUID AlbumArtFactory::GetGUID()
|
||||
{
|
||||
return asf_albumartproviderGUID;
|
||||
}
|
||||
|
||||
void *AlbumArtFactory::GetInterface(int global_lock)
|
||||
{
|
||||
return &albumArtProvider;
|
||||
}
|
||||
|
||||
int AlbumArtFactory::SupportNonLockingInterface()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int AlbumArtFactory::ReleaseInterface(void *ifc)
|
||||
{
|
||||
//plugin.service->service_unlock(ifc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *AlbumArtFactory::GetTestString()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AlbumArtFactory::ServiceNotify(int msg, int param1, int param2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CBCLASS
|
||||
#undef CBCLASS
|
||||
#endif
|
||||
|
||||
#define CBCLASS AlbumArtFactory
|
||||
START_DISPATCH;
|
||||
CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
|
||||
CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
|
||||
CB(WASERVICEFACTORY_GETGUID, GetGUID)
|
||||
CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
|
||||
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
|
||||
CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
|
||||
CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
|
||||
CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
|
||||
END_DISPATCH;
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef NULLSOFT_IN_MP3_ALBUMART_H
|
||||
#define NULLSOFT_IN_MP3_ALBUMART_H
|
||||
|
||||
#include "../Agave/AlbumArt/svc_albumArtProvider.h"
|
||||
|
||||
class ASF_AlbumArtProvider : public svc_albumArtProvider
|
||||
{
|
||||
public:
|
||||
bool IsMine(const wchar_t *filename);
|
||||
int ProviderType();
|
||||
// implementation note: use WASABI_API_MEMMGR to alloc bits and mimetype, so that the recipient can free through that
|
||||
int GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType);
|
||||
int SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType);
|
||||
int DeleteAlbumArt(const wchar_t *filename, const wchar_t *type);
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include <api/service/services.h>
|
||||
|
||||
class AlbumArtFactory : public waServiceFactory
|
||||
{
|
||||
public:
|
||||
FOURCC GetServiceType();
|
||||
const char *GetServiceName();
|
||||
GUID GetGUID();
|
||||
void *GetInterface(int global_lock);
|
||||
int SupportNonLockingInterface();
|
||||
int ReleaseInterface(void *ifc);
|
||||
const char *GetTestString();
|
||||
int ServiceNotify(int msg, int param1, int param2);
|
||||
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
#include "AllocLayer.h"
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef NULLSOFT_ALLOCLAYERH
|
||||
#define NULLSOFT_ALLOCLAYERH
|
||||
|
||||
#include "WMHandler.h"
|
||||
#include "BufferPool.h"
|
||||
#include <cassert>
|
||||
class AllocLayer : public WMHandler
|
||||
{
|
||||
public:
|
||||
AllocLayer(IWMReader *reader)
|
||||
: readerAdvanced(0),
|
||||
listenOutput( -1),
|
||||
maxSize(0)
|
||||
|
||||
{
|
||||
reader->QueryInterface(&readerAdvanced);
|
||||
|
||||
}
|
||||
~AllocLayer()
|
||||
{
|
||||
if (readerAdvanced)
|
||||
{
|
||||
readerAdvanced->Release();
|
||||
readerAdvanced = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Listen(long output)
|
||||
{
|
||||
listenOutput = output;
|
||||
if (output != -1)
|
||||
{
|
||||
readerAdvanced->SetAllocateForOutput(listenOutput, TRUE);
|
||||
readerAdvanced->GetMaxOutputSampleSize(listenOutput, &maxSize);
|
||||
assert(maxSize>0);
|
||||
pool.SetAllocSize(maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Listen(long output, long numBuffers)
|
||||
{
|
||||
listenOutput = output;
|
||||
if (output != -1)
|
||||
{
|
||||
readerAdvanced->SetAllocateForOutput(listenOutput, TRUE);
|
||||
readerAdvanced->GetMaxOutputSampleSize(listenOutput, &maxSize);
|
||||
assert(maxSize>0);
|
||||
pool.SetAllocSize(maxSize);
|
||||
pool.PreAllocate(numBuffers);
|
||||
pool.limit=numBuffers;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void FreeBuffers()
|
||||
{
|
||||
pool.FreeBuffers();
|
||||
}
|
||||
|
||||
|
||||
BufferPool pool;
|
||||
private:
|
||||
|
||||
void AllocateOutput(long outputNum, long bufferSize, INSSBuffer *&buffer)
|
||||
{
|
||||
if (outputNum == listenOutput)
|
||||
{
|
||||
assert(maxSize >= bufferSize);
|
||||
buffer = pool.GetBuffer(bufferSize);
|
||||
}
|
||||
else
|
||||
WMHandler::AllocateOutput(outputNum, bufferSize, buffer); // let other handlers have a shot at it first.
|
||||
}
|
||||
|
||||
// WMHandler
|
||||
long listenOutput;
|
||||
DWORD maxSize;
|
||||
IWMReaderAdvanced *readerAdvanced;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "main.h"
|
||||
#include "AudioLayer.h"
|
||||
|
||||
unsigned long AudioFormat::AudioSamplesToMilliseconds(unsigned long samples)
|
||||
{
|
||||
return MulDiv(samples, 1000, SampleRate());
|
||||
}
|
||||
|
||||
unsigned long AudioFormat::AudioBytesToMilliseconds(unsigned long bytes)
|
||||
{
|
||||
return MulDiv(AudioBytesToSamples(bytes), 1000, SampleRate());
|
||||
}
|
||||
|
||||
unsigned long AudioFormat::AudioMillisecondsToBytes(DWORD milliseconds)
|
||||
{
|
||||
return AudioSamplesToBytes(MulDiv(milliseconds, SampleRate(), 1000));
|
||||
}
|
||||
|
||||
unsigned long AudioFormat::AudioDurationToBytes(QWORD duration)
|
||||
{
|
||||
// TODO: potential integer overflow
|
||||
return AudioSamplesToBytes(MulDiv((int)duration, SampleRate(), 1000*10000));
|
||||
}
|
||||
|
||||
unsigned long AudioFormat::AudioBytesToSamples(unsigned long bytes)
|
||||
{
|
||||
return bytes / waveFormat->Format.nBlockAlign;
|
||||
}
|
||||
|
||||
unsigned long AudioFormat::AudioSamplesToBytes(unsigned long samples)
|
||||
{
|
||||
return samples * waveFormat->Format.nBlockAlign;
|
||||
}
|
||||
|
||||
long AudioFormat::Channels()
|
||||
{
|
||||
return waveFormat->Format.nChannels;
|
||||
}
|
||||
|
||||
long AudioFormat::ValidBits()
|
||||
{
|
||||
if (waveFormat->Format.wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
return waveFormat->Format.wBitsPerSample;
|
||||
}
|
||||
if (waveFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
|
||||
{
|
||||
return waveFormat->Samples.wValidBitsPerSample;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long AudioFormat::BitSize()
|
||||
{
|
||||
return waveFormat->Format.wBitsPerSample;
|
||||
}
|
||||
|
||||
long AudioFormat::SampleRate()
|
||||
{
|
||||
return waveFormat->Format.nSamplesPerSec;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef NULLSOFT_IN_WMVDRM_AUDIOFORMAT_H
|
||||
#define NULLSOFT_IN_WMVDRM_AUDIOFORMAT_H
|
||||
|
||||
#include <mmreg.h>
|
||||
#include <wmsdk.h>
|
||||
|
||||
class AudioFormat
|
||||
{
|
||||
public:
|
||||
AudioFormat() : waveFormat(0)
|
||||
{
|
||||
}
|
||||
~AudioFormat()
|
||||
{
|
||||
delete [] waveFormat;
|
||||
}
|
||||
unsigned long AudioBytesToSamples(unsigned long bytes);
|
||||
unsigned long AudioSamplesToBytes(unsigned long samples);
|
||||
unsigned long AudioBytesToMilliseconds(unsigned long bytes);
|
||||
unsigned long AudioMillisecondsToBytes(DWORD milliseconds);
|
||||
unsigned long AudioDurationToBytes(QWORD duration);
|
||||
unsigned long AudioSamplesToMilliseconds(unsigned long samples);
|
||||
long Channels();
|
||||
long ValidBits();
|
||||
long BitSize();
|
||||
long SampleRate();
|
||||
//protected:
|
||||
void Open(WM_MEDIA_TYPE *mediaType)
|
||||
{
|
||||
delete[] waveFormat;
|
||||
waveFormat = (WAVEFORMATEXTENSIBLE *) new unsigned char[mediaType->cbFormat];
|
||||
memcpy(waveFormat, mediaType->pbFormat, mediaType->cbFormat);
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
delete [] waveFormat;
|
||||
waveFormat=0;
|
||||
}
|
||||
|
||||
private:
|
||||
WAVEFORMATEXTENSIBLE *waveFormat;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,253 @@
|
||||
#include "Main.h"
|
||||
#include "AudioLayer.h"
|
||||
#include "VideoLayer.h"
|
||||
#include <Mmreg.h>
|
||||
#include <cassert>
|
||||
#include "util.h"
|
||||
#include "config.h"
|
||||
#include "AudioThread.h"
|
||||
#include "api.h"
|
||||
|
||||
|
||||
#pragma warning(disable:4355) // warning C4355: 'this' : used in base member initializer list
|
||||
|
||||
AudioLayer::AudioLayer(IWMReader *_reader)
|
||||
: reader(_reader), audioOutputNum( -1),
|
||||
reader2(0), offset(0), new_offset(0),
|
||||
startPosition(0), videoCatchup(0),
|
||||
opened(false), killSwitch(0),
|
||||
audioThread(this), latency(0)
|
||||
{
|
||||
reader->AddRef();
|
||||
if (FAILED(reader->QueryInterface(&reader2)))
|
||||
reader2 = 0;
|
||||
|
||||
killSwitch = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
}
|
||||
|
||||
void AudioLayer::Opened()
|
||||
{
|
||||
|
||||
ResetEvent(killSwitch);
|
||||
if (AudioLayer::OpenAudio())
|
||||
{
|
||||
ResetEvent(killSwitch);
|
||||
|
||||
BOOL dedicatedThread = config_audio_dedicated_thread ? TRUE : FALSE;
|
||||
reader2->SetOutputSetting(audioOutputNum, g_wszDedicatedDeliveryThread, WMT_TYPE_BOOL, (BYTE *) & dedicatedThread, sizeof(dedicatedThread));
|
||||
|
||||
BOOL outOfOrder = config_audio_outoforder ? TRUE : FALSE;
|
||||
reader2->SetOutputSetting(audioOutputNum, g_wszDeliverOnReceive, WMT_TYPE_BOOL, (BYTE *) & outOfOrder, sizeof(outOfOrder));
|
||||
|
||||
BOOL justInTime = config_lowmemory ? TRUE : FALSE;
|
||||
reader2->SetOutputSetting(audioOutputNum, g_wszJustInTimeDecode, WMT_TYPE_BOOL, (BYTE *) & justInTime, sizeof(justInTime));
|
||||
|
||||
opened = true;
|
||||
offset = ((QWORD)latency) * 10000;
|
||||
new_offset = config_audio_early ? (latency + config_audio_early_pad) : 0;
|
||||
reader2->SetOutputSetting(audioOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & new_offset , sizeof(new_offset));
|
||||
|
||||
winamp.OpenViz(latency, SampleRate());
|
||||
}
|
||||
|
||||
WMHandler::Opened();
|
||||
}
|
||||
|
||||
void AudioLayer::Started()
|
||||
{
|
||||
ResetEvent(killSwitch);
|
||||
if (opened)
|
||||
{
|
||||
audioThread.Start(&First());
|
||||
}
|
||||
|
||||
WMHandler::Started();
|
||||
}
|
||||
|
||||
void AudioLayer::Stopped()
|
||||
{
|
||||
if (opened)
|
||||
audioThread.Stop();
|
||||
WMHandler::Stopped();
|
||||
|
||||
}
|
||||
|
||||
WM_MEDIA_TYPE *NewMediaType(IWMOutputMediaProps *props)
|
||||
{
|
||||
DWORD mediaTypeSize;
|
||||
props->GetMediaType(0, &mediaTypeSize);
|
||||
WM_MEDIA_TYPE *mediaType = (WM_MEDIA_TYPE *)new unsigned char[mediaTypeSize];
|
||||
props->GetMediaType(mediaType, &mediaTypeSize);
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
bool AudioLayer::OpenAudio()
|
||||
{
|
||||
audioOutputNum = -1;
|
||||
DWORD numOutputs, output, format, numFormats;
|
||||
IWMOutputMediaProps *formatProperties;
|
||||
GUID mediaType;
|
||||
if (FAILED((reader->GetOutputCount(&numOutputs))))
|
||||
return false;
|
||||
for (output = 0;output < numOutputs;output++)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD speakerConfig = config_audio_num_channels;
|
||||
|
||||
if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false)) // force mono?
|
||||
speakerConfig = DSSPEAKER_MONO;
|
||||
else if (AGAVE_API_CONFIG && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true)) // is surround disallowed?
|
||||
speakerConfig = DSSPEAKER_STEREO;
|
||||
|
||||
hr = reader2->SetOutputSetting(output, g_wszSpeakerConfig, WMT_TYPE_DWORD, (BYTE *) & speakerConfig, sizeof(speakerConfig));
|
||||
assert(hr == S_OK);
|
||||
|
||||
BOOL discreteChannels = TRUE;
|
||||
hr = reader2->SetOutputSetting(output, g_wszEnableDiscreteOutput, WMT_TYPE_BOOL, (BYTE *) & discreteChannels , sizeof(discreteChannels ));
|
||||
assert(hr == S_OK);
|
||||
|
||||
if (FAILED(reader->GetOutputFormatCount(output, &numFormats)))
|
||||
continue;
|
||||
for (format = 0;format < numFormats;format++)
|
||||
{
|
||||
|
||||
reader->GetOutputFormat(output, format, &formatProperties);
|
||||
formatProperties->GetType(&mediaType);
|
||||
if (mediaType == WMMEDIATYPE_Audio)
|
||||
{
|
||||
|
||||
WM_MEDIA_TYPE *mediaType = NewMediaType(formatProperties);
|
||||
if (mediaType->subtype == WMMEDIASUBTYPE_PCM)
|
||||
{
|
||||
if (AGAVE_API_CONFIG)
|
||||
{
|
||||
unsigned int bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
|
||||
|
||||
WAVEFORMATEXTENSIBLE *waveFormat = (WAVEFORMATEXTENSIBLE *) mediaType->pbFormat;
|
||||
if (waveFormat->Format.cbSize >= 22)
|
||||
waveFormat->Samples.wValidBitsPerSample=bits;
|
||||
waveFormat->Format.wBitsPerSample=bits;
|
||||
waveFormat->Format.nBlockAlign = (waveFormat->Format.wBitsPerSample / 8) * waveFormat->Format.nChannels;
|
||||
waveFormat->Format.nAvgBytesPerSec=waveFormat->Format.nSamplesPerSec * waveFormat->Format.nBlockAlign;
|
||||
if (FAILED(formatProperties->SetMediaType(mediaType)))
|
||||
{
|
||||
// blah, just use the default settings then
|
||||
delete[] mediaType;
|
||||
mediaType = NewMediaType(formatProperties);
|
||||
}
|
||||
}
|
||||
AudioFormat::Open(mediaType);
|
||||
delete mediaType;
|
||||
bool video = false;
|
||||
First().HasVideo(video);
|
||||
|
||||
// this is needed to prevent an audio glitch on first playback
|
||||
if (out)
|
||||
{
|
||||
extern WMDRM mod;
|
||||
out->SetVolume(mod.GetVolume());
|
||||
out->SetPan(mod.GetPan());
|
||||
}
|
||||
|
||||
latency = out->Open(SampleRate(), Channels(), ValidBits(), (video ? -666 : -1), -1);
|
||||
|
||||
if (latency >= 0)
|
||||
{
|
||||
audioOutputNum = output;
|
||||
reader->SetOutputProps(audioOutputNum, formatProperties);
|
||||
formatProperties->Release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
formatProperties->Release();
|
||||
AudioFormat::Close();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
delete mediaType;
|
||||
formatProperties->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioLayer::SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
|
||||
{
|
||||
if (outputNum == audioOutputNum)
|
||||
{
|
||||
if (WaitForSingleObject(killSwitch, 0) == WAIT_OBJECT_0)
|
||||
return ;
|
||||
|
||||
if (videoCatchup)
|
||||
{
|
||||
videoCatchup = videoCatchup / 20000;
|
||||
videoCatchup = min(videoCatchup, offset / 40000);
|
||||
unsigned int num = (unsigned int) (videoCatchup / VIDEO_ACCEPTABLE_JITTER_MS);
|
||||
while (num--)
|
||||
{
|
||||
if (WaitForSingleObject(killSwitch, VIDEO_ACCEPTABLE_JITTER_MS) == WAIT_OBJECT_0)
|
||||
return ;
|
||||
|
||||
}
|
||||
|
||||
videoCatchup = 0;
|
||||
}
|
||||
|
||||
while (!audioThread.AddBuffer(sample, timeStamp, flags, false))
|
||||
{
|
||||
if (WaitForSingleObject(killSwitch, VIDEO_ACCEPTABLE_JITTER_MS) == WAIT_OBJECT_0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
WMHandler::SampleReceived(timeStamp, duration, outputNum, flags, sample);
|
||||
}
|
||||
|
||||
void AudioLayer::VideoCatchup(QWORD time)
|
||||
{
|
||||
videoCatchup = time;
|
||||
WMHandler::VideoCatchup(time);
|
||||
}
|
||||
|
||||
void AudioLayer::Closed()
|
||||
{
|
||||
if (opened)
|
||||
{
|
||||
out->Close();
|
||||
winamp.CloseViz();
|
||||
}
|
||||
opened = false;
|
||||
|
||||
//AudioFormat::Close();
|
||||
WMHandler::Closed();
|
||||
}
|
||||
|
||||
AudioLayer::~AudioLayer()
|
||||
{
|
||||
audioThread.Kill();
|
||||
if (reader2)
|
||||
reader2->Release();
|
||||
if (reader)
|
||||
reader->Release();
|
||||
CloseHandle(killSwitch);
|
||||
}
|
||||
|
||||
void AudioLayer::Kill()
|
||||
{
|
||||
SetEvent(killSwitch);
|
||||
if (opened)
|
||||
audioThread.SignalStop();
|
||||
WMHandler::Kill();
|
||||
|
||||
if (opened)
|
||||
audioThread.WaitForStop();
|
||||
}
|
||||
|
||||
void AudioLayer::EndOfFile()
|
||||
{
|
||||
if (!opened || audioThread.EndOfFile())
|
||||
WMHandler::EndOfFile();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef NULLSOFT_AUDIOLAYERH
|
||||
#define NULLSOFT_AUDIOLAYERH
|
||||
|
||||
#include "WMHandler.h"
|
||||
#include <mmreg.h>
|
||||
#include "AudioThread.h"
|
||||
#include "AudioFormat.h"
|
||||
|
||||
class AudioLayer : public WMHandler, public AudioFormat
|
||||
{
|
||||
public:
|
||||
AudioLayer(IWMReader *_reader);
|
||||
~AudioLayer();
|
||||
bool IsOpen()
|
||||
{
|
||||
return opened;
|
||||
}
|
||||
void Kill();
|
||||
bool OpenAudio();
|
||||
|
||||
void StartAudioThread();
|
||||
private:
|
||||
// WMHandler events
|
||||
|
||||
void Opened();
|
||||
void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample);
|
||||
void VideoCatchup(QWORD time);
|
||||
void Closed();
|
||||
void EndOfFile();
|
||||
void Started();
|
||||
void Stopped();
|
||||
|
||||
// other people's data
|
||||
IWMReader *reader;
|
||||
|
||||
// our data
|
||||
QWORD startPosition;
|
||||
|
||||
int audioOutputNum;
|
||||
IWMReaderAdvanced2 *reader2;
|
||||
QWORD offset;
|
||||
DWORD new_offset;
|
||||
QWORD videoCatchup;
|
||||
bool opened;
|
||||
HANDLE killSwitch;
|
||||
int latency;
|
||||
|
||||
AudioThread audioThread;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "Main.h"
|
||||
#include "AudioThread.h"
|
||||
#include "AudioLayer.h"
|
||||
#include <assert.h>
|
||||
|
||||
extern unsigned long endTime;
|
||||
|
||||
DWORD WINAPI AudThread_stub(void *ptr)
|
||||
{
|
||||
((AudioThread *)ptr)->AudThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AudioThread::Start(WMHandler *_output)
|
||||
{
|
||||
assert(_output);
|
||||
output = _output;
|
||||
eof=0;
|
||||
ResetEvent(stopped);
|
||||
QueueUserAPC(MediaThread_StartAPC, thread, reinterpret_cast<ULONG_PTR>(static_cast<MediaThread *>(this)));
|
||||
}
|
||||
|
||||
AudioThread::AudioThread(AudioLayer *audio) : output(0), audioLayer(audio)
|
||||
{
|
||||
DWORD id;
|
||||
thread = CreateThread(NULL, 256*1024, AudThread_stub, (void *)this, NULL, &id);
|
||||
SetThreadPriority(thread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
}
|
||||
|
||||
void AudioThread::AudThread()
|
||||
{
|
||||
int endbreak=0;
|
||||
while (true)
|
||||
{
|
||||
switch (WaitForSingleObjectEx(killEvent, wait, TRUE))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
//StopAPC();
|
||||
return;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
{
|
||||
if (buffers.empty() || endbreak)
|
||||
{
|
||||
SetEvent(bufferFreed);
|
||||
|
||||
if (eof==1)
|
||||
{
|
||||
eof=2;
|
||||
output->EndOfFile();
|
||||
}
|
||||
endbreak = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
MediaBuffer *buffer = buffers.front();
|
||||
DWORD length;
|
||||
void *data;
|
||||
buffer->buffer->GetBufferAndLength((BYTE **)&data, &length);
|
||||
|
||||
//if (out->CanWrite() >= length)
|
||||
{
|
||||
QWORD timestamptemp = buffer->timestamp/10000LL;
|
||||
DWORD timestamp = static_cast<DWORD>(timestamptemp);
|
||||
|
||||
if (buffer->flags & WM_SF_DISCONTINUITY)
|
||||
{
|
||||
// fill with silence!
|
||||
int msToFill = timestamp - out->GetWrittenTime(); // TODO: maybe use microsoft's time resolution?
|
||||
if (msToFill > 0 && msToFill < 2000)
|
||||
{
|
||||
int bytes = audioLayer->AudioMillisecondsToBytes(msToFill);
|
||||
__int8 *zeroes = (__int8 *)calloc(bytes, 1);
|
||||
if (zeroes)
|
||||
{
|
||||
output->AudioDataReceived(zeroes, bytes, timestamp);
|
||||
free(zeroes);
|
||||
}
|
||||
else
|
||||
{
|
||||
out->Flush(timestamp);
|
||||
}
|
||||
}
|
||||
else if (msToFill > 0)
|
||||
{
|
||||
out->Flush(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
output->AudioDataReceived(data, length, timestamp);
|
||||
|
||||
// TODO seen a few crash reports failing around here
|
||||
// might be the cause of the random wma fails
|
||||
// but crash dump doesn't help too much afaict
|
||||
try {
|
||||
buffer->buffer->Release();
|
||||
delete buffer;
|
||||
} catch (...) {}
|
||||
|
||||
//buffers.pop_front();
|
||||
if (buffers.size())
|
||||
{
|
||||
buffers.erase(buffers.begin());
|
||||
}
|
||||
|
||||
unsigned long x = endTime;
|
||||
if (x && timestamp > x)
|
||||
{
|
||||
eof = 1; // reached the end baby....
|
||||
endbreak = 1;
|
||||
}
|
||||
}
|
||||
if (buffers.size() < config_audio_cache_frames)
|
||||
SetEvent(bufferFreed);
|
||||
}
|
||||
continue;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioThread::AddAPC(MediaBuffer *buffer)
|
||||
{
|
||||
OrderedInsert(buffer);
|
||||
if (buffers.size() >= config_audio_cache_frames)
|
||||
ResetEvent(bufferFreed);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef NULLSOFT_AUDIOTHREADH
|
||||
#define NULLSOFT_AUDIOTHREADH
|
||||
|
||||
#include "WMHandler.h"
|
||||
#include "MediaThread.h"
|
||||
#include <wmsdk.h>
|
||||
|
||||
class AudioLayer;
|
||||
|
||||
class AudioThread : public MediaThread
|
||||
{
|
||||
public:
|
||||
AudioThread(AudioLayer *audio);
|
||||
void Start(WMHandler *output);
|
||||
|
||||
/* AddBuffers put an audio buffer in the queue
|
||||
it returns true if it was added
|
||||
it returns false if it was NOT added. it is up to YOU (the caller) to sleep for a while and call again
|
||||
*/
|
||||
void AudThread();
|
||||
bool EndOfFile()
|
||||
{
|
||||
if (buffers.empty()) // if the buffers are empty, then our thread might never get a chance to signal EOF
|
||||
return true;
|
||||
|
||||
if (eof)
|
||||
return true;
|
||||
eof=1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void AddAPC(MediaBuffer *);
|
||||
int eof;
|
||||
WMHandler *output;
|
||||
AudioLayer *audioLayer;
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef NULLSOFT_AUTOCHARH
|
||||
#define NULLSOFT_AUTOCHARH
|
||||
|
||||
class AutoChar
|
||||
{
|
||||
public:
|
||||
AutoChar(const wchar_t *convert) : allocated(false), narrow(0)
|
||||
{
|
||||
// review maybe CP_UTF8?
|
||||
|
||||
int size = WideCharToMultiByte(CP_ACP, 0, convert, -1, 0, 0, NULL, NULL);
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
narrow = new char[size];
|
||||
allocated=true;
|
||||
|
||||
if (!WideCharToMultiByte(CP_ACP, 0, convert, -1, narrow, size, NULL, NULL))
|
||||
{
|
||||
delete [] narrow;
|
||||
narrow=0;
|
||||
allocated=false;
|
||||
}
|
||||
}
|
||||
~AutoChar()
|
||||
{
|
||||
if (allocated)
|
||||
{
|
||||
delete [] narrow;
|
||||
narrow=0;
|
||||
allocated=false;
|
||||
}
|
||||
}
|
||||
operator char *()
|
||||
{
|
||||
return narrow;
|
||||
}
|
||||
private:
|
||||
bool allocated;
|
||||
char *narrow;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef AUTOWIDEH
|
||||
#define AUTOWIDEH
|
||||
|
||||
class AutoWide
|
||||
{
|
||||
public:
|
||||
AutoWide(const char *convert) : allocated(false), wide(0)
|
||||
{
|
||||
// review maybe CP_UTF8?
|
||||
int size = MultiByteToWideChar(CP_ACP, 0, convert, -1, 0,0);
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
wide = new unsigned short[size];
|
||||
allocated=true;
|
||||
if (!MultiByteToWideChar(CP_ACP, 0, convert, -1, wide,size))
|
||||
{
|
||||
delete wide;
|
||||
wide=0;
|
||||
allocated=false;
|
||||
}
|
||||
}
|
||||
~AutoWide()
|
||||
{
|
||||
if (allocated)
|
||||
{
|
||||
delete wide;
|
||||
wide=0;
|
||||
allocated=false;
|
||||
}
|
||||
}
|
||||
operator unsigned short *()
|
||||
{
|
||||
return wide;
|
||||
}
|
||||
private:
|
||||
bool allocated;
|
||||
unsigned short *wide;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "BufferLayer.h"
|
||||
#include "Main.h"
|
||||
#include "resource.h"
|
||||
#define killEvent events[0]
|
||||
#define startEvent events[1]
|
||||
|
||||
enum
|
||||
{
|
||||
KILL_EVENT = 0,
|
||||
START_EVENT = 1,
|
||||
};
|
||||
|
||||
DWORD WINAPI BufferLayer::BufThread_stub(void *ptr)
|
||||
{
|
||||
((BufferLayer *)ptr)->BufThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
BufferLayer::BufferLayer(IWMReader *reader) : reader2(0), buffering(false)
|
||||
{
|
||||
if (FAILED(reader->QueryInterface(&reader2)))
|
||||
reader2 = 0;
|
||||
|
||||
startEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
killEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
DWORD id;
|
||||
thread = CreateThread(NULL, 128*1024, BufThread_stub, (void *)this, NULL, &id);
|
||||
}
|
||||
|
||||
BufferLayer::~BufferLayer()
|
||||
{
|
||||
SetEvent(killEvent);
|
||||
ResetEvent(startEvent);
|
||||
WaitForSingleObject(thread, INFINITE);
|
||||
if (reader2) reader2->Release(); reader2 = 0;
|
||||
}
|
||||
|
||||
|
||||
void BufferLayer::BufferingStarted()
|
||||
{
|
||||
winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_BUFFERING));
|
||||
buffering=true;
|
||||
SetEvent(startEvent);
|
||||
WMHandler::BufferingStarted();
|
||||
}
|
||||
|
||||
void BufferLayer::BufferingStopped()
|
||||
{
|
||||
winamp.SetStatus(L"");
|
||||
buffering=false;
|
||||
ResetEvent(startEvent);
|
||||
WMHandler::BufferingStopped();
|
||||
}
|
||||
|
||||
int BufferLayer::Wait()
|
||||
{
|
||||
if (WaitForSingleObject(killEvent, 0) == WAIT_OBJECT_0)
|
||||
return KILL_EVENT;
|
||||
|
||||
return WaitForMultipleObjects(2, events, FALSE, INFINITE) - WAIT_OBJECT_0;
|
||||
|
||||
}
|
||||
|
||||
void BufferLayer::BufThread()
|
||||
{
|
||||
do
|
||||
{
|
||||
switch (Wait())
|
||||
{
|
||||
case KILL_EVENT:
|
||||
return ;
|
||||
case START_EVENT:
|
||||
{
|
||||
if (reader2)
|
||||
{
|
||||
DWORD percent;
|
||||
QWORD throwAway;
|
||||
if (SUCCEEDED(reader2->GetBufferProgress(&percent, &throwAway)))
|
||||
winamp.Buffering(percent, WASABI_API_LNGSTRINGW(IDS_BUFFERING));
|
||||
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
|
||||
void BufferLayer::OpenFailed()
|
||||
{
|
||||
ResetEvent(startEvent);
|
||||
WMHandler::OpenFailed();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef NULLSOFT_BUFFERLAYERH
|
||||
#define NULLSOFT_BUFFERLAYERH
|
||||
|
||||
#include "WMHandler.h"
|
||||
|
||||
class BufferLayer : public WMHandler
|
||||
{
|
||||
public:
|
||||
BufferLayer(IWMReader *reader);
|
||||
~BufferLayer();
|
||||
|
||||
protected:
|
||||
void BufferingStarted();
|
||||
void BufferingStopped();
|
||||
void OpenFailed();
|
||||
|
||||
private:
|
||||
static DWORD WINAPI BufThread_stub(void *ptr);
|
||||
void BufThread();
|
||||
int Wait();
|
||||
HANDLE events[2];
|
||||
IWMReaderAdvanced2 *reader2;
|
||||
HANDLE thread;
|
||||
volatile bool buffering;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,203 @@
|
||||
#ifndef NULLSOFT_BUFFERPOOLH
|
||||
#define NULLSOFT_BUFFERPOOLH
|
||||
|
||||
#include <wmsdk.h>
|
||||
#include "../nu/AutoLock.h"
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
using namespace Nullsoft::Utility;
|
||||
class Buffer;
|
||||
class Pool
|
||||
{
|
||||
public:
|
||||
virtual void ReturnBuffer(Buffer *buffer) = 0;
|
||||
};
|
||||
|
||||
class Buffer : public INSSBuffer
|
||||
{
|
||||
public:
|
||||
Buffer(size_t _size, Pool *_pool)
|
||||
: size(_size),
|
||||
length(0),
|
||||
pool(_pool),
|
||||
refCount(1)
|
||||
{
|
||||
buffer = new unsigned char[size];
|
||||
}
|
||||
|
||||
~Buffer()
|
||||
{
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return ++refCount;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
assert(refCount > 0);
|
||||
|
||||
if (--refCount == 0)
|
||||
{
|
||||
length = 0;
|
||||
pool->ReturnBuffer(this);
|
||||
}
|
||||
return refCount;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
|
||||
{
|
||||
if (IID_INSSBuffer == iid)
|
||||
{
|
||||
*ppvObject = static_cast<INSSBuffer *>(this);
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetBuffer(BYTE **ppdwBuffer)
|
||||
{
|
||||
*ppdwBuffer = (BYTE *)buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetBufferAndLength(BYTE **ppdwBuffer, DWORD *pdwLength)
|
||||
{
|
||||
*ppdwBuffer = (BYTE *)buffer;
|
||||
*pdwLength = length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLength(DWORD *pdwLength)
|
||||
{
|
||||
*pdwLength = length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetMaxLength(DWORD *pdwLength)
|
||||
{
|
||||
*pdwLength = size;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetLength(DWORD dwLength)
|
||||
{
|
||||
length = dwLength;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool CanFit(size_t sizeCompare)
|
||||
{
|
||||
return (sizeCompare <= size);
|
||||
}
|
||||
size_t size, length;
|
||||
Pool *pool;
|
||||
int refCount;
|
||||
unsigned char *buffer;
|
||||
};
|
||||
|
||||
class BufferPool : public Pool
|
||||
{
|
||||
typedef std::deque<Buffer *> PoolList;
|
||||
public:
|
||||
long limit;
|
||||
BufferPool() : allocSize(0),
|
||||
poolSize(0),
|
||||
limit(0)
|
||||
{}
|
||||
|
||||
~BufferPool()
|
||||
{
|
||||
FreeBuffers();
|
||||
}
|
||||
void FreeBuffers()
|
||||
{
|
||||
|
||||
AutoLock lock (bufferGuard);
|
||||
while (!pool.empty())
|
||||
{
|
||||
Buffer *buff = pool.front();
|
||||
delete buff;
|
||||
pool.pop_front();
|
||||
poolSize = 0;
|
||||
}
|
||||
}
|
||||
void ReturnBuffer(Buffer *buffer)
|
||||
{
|
||||
AutoLock lock (bufferGuard);
|
||||
pool.push_back(buffer);
|
||||
}
|
||||
Buffer *SearchForBuffer(size_t size)
|
||||
{
|
||||
PoolList::iterator itr;
|
||||
AutoLock lock (bufferGuard);
|
||||
for (itr = pool.begin();itr != pool.end();itr++)
|
||||
{
|
||||
if ((*itr)->CanFit(size))
|
||||
{
|
||||
Buffer *buff = *itr;
|
||||
pool.erase(itr);
|
||||
buff->AddRef();
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PreAllocate(size_t count)
|
||||
{
|
||||
if (!allocSize)
|
||||
return ;
|
||||
for (size_t i = 0;i != count;i++)
|
||||
{
|
||||
AutoLock lock (bufferGuard);
|
||||
pool.push_back( new Buffer(allocSize , this));
|
||||
}
|
||||
}
|
||||
INSSBuffer *GetBuffer(size_t size)
|
||||
{
|
||||
Buffer *buff = SearchForBuffer(size);
|
||||
|
||||
/*
|
||||
while (!buff && poolSize>=limit && limit)
|
||||
{
|
||||
Sleep(1);
|
||||
buff = SearchForBuffer(size);
|
||||
}*/
|
||||
|
||||
if (!buff)
|
||||
{
|
||||
poolSize++;
|
||||
std::cerr << "poolsize = " << poolSize << std::endl;
|
||||
buff = new Buffer(allocSize ? allocSize : size, this);
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
|
||||
void test()
|
||||
{
|
||||
std::cerr << "pool.size() == " << pool.size();
|
||||
std::cerr << " and poolsize == " << poolSize << std::endl;
|
||||
}
|
||||
|
||||
void SetAllocSize(long size)
|
||||
{
|
||||
allocSize = size;
|
||||
}
|
||||
|
||||
PoolList pool;
|
||||
LockGuard bufferGuard;
|
||||
size_t allocSize;
|
||||
size_t poolSize;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef CACHEDDATAH
|
||||
#define CACHEDDATAH
|
||||
|
||||
template <class DataType>
|
||||
class CachedData
|
||||
{
|
||||
public:
|
||||
CachedData() : cached(false)
|
||||
{
|
||||
}
|
||||
CachedData(DataType _data) : cached(true), data(_data)
|
||||
{
|
||||
}
|
||||
operator DataType()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
bool IsCached()
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
void ClearCache()
|
||||
{
|
||||
cached=false;
|
||||
}
|
||||
DataType *operator &()
|
||||
{
|
||||
if (cached)
|
||||
return &data;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class FromType>
|
||||
bool operator =(FromType from)
|
||||
{
|
||||
data = from;
|
||||
cached=true;
|
||||
return cached;
|
||||
}
|
||||
|
||||
private:
|
||||
bool cached;
|
||||
DataType data;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
#include "Main.h"
|
||||
#include "ClockLayer.h"
|
||||
#include "config.h"
|
||||
|
||||
ClockLayer::ClockLayer(IWMReader *reader)
|
||||
: clock(0), startTime(0),
|
||||
clockTick(2500000), curTime(0),
|
||||
startTimeMilliseconds(0),
|
||||
realTime(false),
|
||||
lastOutputTime(0)
|
||||
{
|
||||
if (FAILED(reader->QueryInterface(&clock)))
|
||||
clock=0;
|
||||
}
|
||||
|
||||
void ClockLayer::Opened()
|
||||
{
|
||||
realTime=!config_clock;
|
||||
WMHandler::Opened();
|
||||
|
||||
if (!realTime)
|
||||
{
|
||||
HRESULT hr = clock->SetUserProvidedClock(TRUE);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
realTime=false;
|
||||
}
|
||||
}
|
||||
else
|
||||
clock->SetUserProvidedClock(FALSE);
|
||||
curTime = startTime;
|
||||
}
|
||||
|
||||
void ClockLayer::Started()
|
||||
{
|
||||
if (!realTime && config_clock)
|
||||
clock->DeliverTime((QWORD) - 1);
|
||||
|
||||
if (startTimeMilliseconds != 0)
|
||||
out->Flush(startTimeMilliseconds);
|
||||
|
||||
SetLastOutputTime(startTimeMilliseconds);
|
||||
WMHandler::Started();
|
||||
}
|
||||
|
||||
void ClockLayer::Clock()
|
||||
{
|
||||
if (!realTime && config_clock)
|
||||
clock->DeliverTime((QWORD) - 1);
|
||||
}
|
||||
|
||||
void ClockLayer::SetStartTimeMilliseconds(long time)
|
||||
{
|
||||
startTimeMilliseconds=time;
|
||||
startTime = time;
|
||||
startTime *= 10000;
|
||||
}
|
||||
|
||||
void ClockLayer::TimeReached(QWORD &timeReached)
|
||||
{
|
||||
curTime = timeReached;
|
||||
}
|
||||
|
||||
QWORD ClockLayer::GetStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
void ClockLayer::GoRealTime()
|
||||
{
|
||||
realTime = true;
|
||||
}
|
||||
|
||||
int ClockLayer::GetOutputTime()
|
||||
{
|
||||
if (realTime)
|
||||
return (int) (curTime / 10000LL);
|
||||
else
|
||||
return lastOutputTime + (out->GetOutputTime() - out->GetWrittenTime());
|
||||
}
|
||||
|
||||
void ClockLayer::TimeToSync(QWORD timeStamp, __int64 &diff)
|
||||
{
|
||||
if (realTime)
|
||||
diff = 0;
|
||||
else
|
||||
{
|
||||
QWORD outputTime = this->GetOutputTime();
|
||||
outputTime *= 10000LL;
|
||||
diff = timeStamp - outputTime;
|
||||
}
|
||||
}
|
||||
|
||||
void ClockLayer::SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
|
||||
{
|
||||
if (realTime)
|
||||
curTime = timeStamp;
|
||||
|
||||
WMHandler::SampleReceived(timeStamp, duration, outputNum, flags, sample);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef NULLSOFT_CLOCKLAYERH
|
||||
#define NULLSOFT_CLOCKLAYERH
|
||||
|
||||
#include "WMHandler.h"
|
||||
class ClockLayer : public WMHandler
|
||||
{
|
||||
public:
|
||||
ClockLayer(IWMReader *reader);
|
||||
|
||||
void SetStartTimeMilliseconds(long time);
|
||||
QWORD GetStartTime();
|
||||
|
||||
void GoRealTime();
|
||||
int GetOutputTime();
|
||||
void SetLastOutputTime(int _outputTime)
|
||||
{
|
||||
lastOutputTime = _outputTime;
|
||||
}
|
||||
void Clock();
|
||||
private:
|
||||
// WMHandler
|
||||
void Opened();
|
||||
void Started();
|
||||
void TimeReached(QWORD &timeReached);
|
||||
void TimeToSync(QWORD timeStamp, __int64 &diff);
|
||||
void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample);
|
||||
|
||||
IWMReaderAdvanced *clock;
|
||||
|
||||
QWORD startTime, clockTick, curTime;
|
||||
DWORD startTimeMilliseconds;
|
||||
bool realTime;
|
||||
int lastOutputTime;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,398 @@
|
||||
#include "Main.h"
|
||||
#include "resource.h"
|
||||
#include "../nu/ListView.h"
|
||||
#include "FileTypes.h"
|
||||
#include "AutoChar.h"
|
||||
|
||||
W_ListView typeList;
|
||||
FileTypes::TypeList types;
|
||||
#define CFGSET(hwnd, code, boolval) CheckDlgButton(hwnd, code, ((boolval)?BST_CHECKED:BST_UNCHECKED))
|
||||
|
||||
struct SpeakerSetup
|
||||
{
|
||||
int description;
|
||||
DWORD value;
|
||||
};
|
||||
SpeakerSetup speakerList[] =
|
||||
{
|
||||
{IDS_STEREO,DSSPEAKER_STEREO},
|
||||
{IDS_QUADROPHONIC,DSSPEAKER_QUAD},
|
||||
{IDS_SURROUND,DSSPEAKER_SURROUND},
|
||||
{IDS_5_1,DSSPEAKER_5POINT1},
|
||||
{IDS_7_1,DSSPEAKER_7POINT1},
|
||||
};
|
||||
|
||||
void FillFileTypes()
|
||||
{
|
||||
typeList.Clear();
|
||||
|
||||
long attrCount = types.size();
|
||||
int pos=0;
|
||||
for (long i = 0;i < attrCount;i++)
|
||||
{
|
||||
typeList.InsertItem(pos, types[i].wtype, 0);
|
||||
typeList.SetItemText(pos, 1, types[i].description);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetTypes()
|
||||
{
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock (fileTypes.typeGuard);
|
||||
types=fileTypes.types;
|
||||
}
|
||||
FillFileTypes();
|
||||
}
|
||||
|
||||
void Preferences_Populate(HWND hwndDlg)
|
||||
{
|
||||
CFGSET(hwndDlg, IDC_HTTPMETA, config_http_metadata);
|
||||
|
||||
/* CFGSET(hwndDlg, IDC_SILENT, !config_no_silent);
|
||||
CFGSET(hwndDlg, IDC_UNTRUSTED, config_untrusted_ok);
|
||||
*/
|
||||
CFGSET(hwndDlg, IDC_EXTRA_ASX, config_extra_asx_extensions);
|
||||
|
||||
SetDlgItemInt(hwndDlg,IDC_BUFFER_TIME, config_buffer_time, FALSE/*unsigned*/);
|
||||
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_RESETCONTENT, 0, 0);
|
||||
for (int i = 0;i < sizeof(speakerList)/sizeof(speakerList[0]) ;i++)
|
||||
{
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_ADDSTRING, 0, (LPARAM) WASABI_API_LNGSTRINGW(speakerList[i].description));
|
||||
if (speakerList[i].value == config_audio_num_channels)
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_SETCURSEL, i, 0);
|
||||
}
|
||||
|
||||
ResetTypes();
|
||||
}
|
||||
void Preferences_Init(HWND hwndDlg)
|
||||
{
|
||||
typeList.setwnd(GetDlgItem(hwndDlg, IDC_TYPELIST));
|
||||
typeList.AddCol(WASABI_API_LNGSTRINGW(IDS_EXT), 75);
|
||||
typeList.AddCol(WASABI_API_LNGSTRINGW(IDS_DESCRIPTION), 250);
|
||||
Preferences_Populate(hwndDlg);
|
||||
|
||||
if(config_col1 == -1)
|
||||
typeList.AutoSizeColumn(0);
|
||||
else
|
||||
typeList.SetColumnWidth(0, config_col1);
|
||||
|
||||
if(config_col2 == -1)
|
||||
typeList.AutoSizeColumn(1);
|
||||
else
|
||||
typeList.SetColumnWidth(1, config_col2);
|
||||
|
||||
if (NULL != WASABI_API_APP)
|
||||
WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(typeList.getwnd(), TRUE);
|
||||
}
|
||||
|
||||
void Preferences_TypeRemove(HWND hwndDlg)
|
||||
{
|
||||
int next = -1;
|
||||
|
||||
do
|
||||
{
|
||||
next = typeList.GetNextSelected(next);
|
||||
if (next != -1)
|
||||
{
|
||||
free(types[next].wtype);
|
||||
types[next].wtype=0;
|
||||
}
|
||||
} while (next != -1);
|
||||
|
||||
for (FileTypes::TypeList::iterator itr = types.begin(); itr != types.end(); )
|
||||
{
|
||||
if (!itr->wtype || !itr->wtype[0])
|
||||
types.erase(itr);
|
||||
else
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
void OnOk(HWND hwndDlg)
|
||||
{
|
||||
config_http_metadata=IsDlgButtonChecked(hwndDlg, IDC_HTTPMETA)== BST_CHECKED;
|
||||
// config_no_silent=IsDlgButtonChecked(hwndDlg, IDC_SILENT) != BST_CHECKED;
|
||||
// config_untrusted_ok=IsDlgButtonChecked(hwndDlg, IDC_UNTRUSTED)== BST_CHECKED;
|
||||
config_extra_asx_extensions=IsDlgButtonChecked(hwndDlg, IDC_EXTRA_ASX)== BST_CHECKED;
|
||||
wchar_t temp[64] = {0};
|
||||
|
||||
GetDlgItemText(hwndDlg,IDC_BUFFER_TIME, temp, 64);
|
||||
int newBufferTime= _wtoi(temp);
|
||||
if (newBufferTime<1000)
|
||||
newBufferTime=1000;
|
||||
if (newBufferTime>60000)
|
||||
newBufferTime=60000;
|
||||
config_buffer_time=newBufferTime;
|
||||
|
||||
int numChannels=SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_GETCURSEL, 0, 0);
|
||||
if (numChannels!=CB_ERR)
|
||||
config_audio_num_channels = speakerList[numChannels].value;
|
||||
|
||||
fileTypes.SetTypes(types);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK AddTypeProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
CFGSET(hwndDlg, IDC_FILEEXTENSION, true);
|
||||
CFGSET(hwndDlg, IDC_TYPE_AUDIO, true);
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_PROTOCOL:
|
||||
SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_PROTOCOL));
|
||||
break;
|
||||
case IDC_FILEEXTENSION:
|
||||
SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_EXTENSION));
|
||||
break;
|
||||
case IDOK:
|
||||
{
|
||||
wchar_t type[MAX_PATH] = {0}, description[1024] = {0};
|
||||
GetDlgItemText(hwndDlg,IDC_TYPE,type,MAX_PATH);
|
||||
GetDlgItemText(hwndDlg,IDC_DESCRIPTION,description,1024);
|
||||
bool isProtocol = IsDlgButtonChecked(hwndDlg, IDC_PROTOCOL)== BST_CHECKED;
|
||||
int avType = IsDlgButtonChecked(hwndDlg, IDC_TYPE_VIDEO)== BST_CHECKED;
|
||||
types.push_back(FileType(type, description, isProtocol, avType));
|
||||
EndDialog(hwndDlg, 0);
|
||||
}
|
||||
break;
|
||||
case IDCANCEL:
|
||||
EndDialog(hwndDlg, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void AddType(HWND hwndDlg)
|
||||
{
|
||||
WASABI_API_DIALOGBOXW(IDD_ADDTYPE, hwndDlg, AddTypeProc);
|
||||
}
|
||||
|
||||
static size_t editIndex=0;
|
||||
static INT_PTR CALLBACK EditTypeProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
SetWindowText(hwndDlg, WASABI_API_LNGSTRINGW(IDS_EDIT_FILE_TYPE));
|
||||
CFGSET(hwndDlg, IDC_TYPE_AUDIO, (types[editIndex].avType==FileType::AUDIO));
|
||||
CFGSET(hwndDlg, IDC_TYPE_VIDEO, (types[editIndex].avType==FileType::VIDEO));
|
||||
CFGSET(hwndDlg, IDC_PROTOCOL, types[editIndex].isProtocol);
|
||||
CFGSET(hwndDlg, IDC_FILEEXTENSION, !types[editIndex].isProtocol);
|
||||
SetDlgItemText(hwndDlg,IDC_TYPE, types[editIndex].wtype);
|
||||
SetDlgItemText(hwndDlg,IDC_DESCRIPTION, types[editIndex].description);
|
||||
if (types[editIndex].isProtocol)
|
||||
SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_PROTOCOL));
|
||||
else
|
||||
SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_EXTENSION));
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_PROTOCOL:
|
||||
SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_PROTOCOL));
|
||||
break;
|
||||
case IDC_FILEEXTENSION:
|
||||
SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_EXTENSION));
|
||||
break;
|
||||
case IDOK:
|
||||
{
|
||||
wchar_t type[MAX_PATH] = {0}, description[1024] = {0};
|
||||
GetDlgItemText(hwndDlg,IDC_TYPE,type,MAX_PATH);
|
||||
GetDlgItemText(hwndDlg,IDC_DESCRIPTION,description,1024);
|
||||
bool isProtocol = IsDlgButtonChecked(hwndDlg, IDC_PROTOCOL)== BST_CHECKED;
|
||||
|
||||
types[editIndex].avType = (IsDlgButtonChecked(hwndDlg, IDC_TYPE_VIDEO)== BST_CHECKED);
|
||||
if(types[editIndex].wtype)free(types[editIndex].wtype);
|
||||
types[editIndex].wtype = _wcsdup(type);
|
||||
|
||||
types[editIndex].isProtocol = isProtocol;
|
||||
if(types[editIndex].description)free(types[editIndex].description);
|
||||
types[editIndex].description = _wcsdup(description);
|
||||
EndDialog(hwndDlg, 0);
|
||||
}
|
||||
break;
|
||||
case IDCANCEL:
|
||||
EndDialog(hwndDlg, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void EditType(HWND hwndDlg)
|
||||
{
|
||||
editIndex=-1;
|
||||
do
|
||||
{
|
||||
editIndex=typeList.GetNextSelected(editIndex);
|
||||
if (editIndex != -1)
|
||||
{
|
||||
WASABI_API_DIALOGBOXW(IDD_ADDTYPE, hwndDlg, EditTypeProc);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
void Advanced_Init(HWND hwndDlg)
|
||||
{
|
||||
CFGSET(hwndDlg, IDC_AUDIO_THREAD, config_audio_dedicated_thread);
|
||||
CFGSET(hwndDlg, IDC_AUDIO_EARLY, config_audio_early);
|
||||
CFGSET(hwndDlg, IDC_AUDIO_OUTOFORDER, config_audio_outoforder);
|
||||
CFGSET(hwndDlg, IDC_AUDIO_DROP, config_video_catchup);
|
||||
|
||||
CFGSET(hwndDlg, IDC_VIDEO_THREAD, config_video_dedicated_thread);
|
||||
CFGSET(hwndDlg, IDC_VIDEO_EARLY, config_video_early);
|
||||
CFGSET(hwndDlg, IDC_VIDEO_OUTOFORDER, config_video_outoforder);
|
||||
CFGSET(hwndDlg, IDC_VIDEO_NOTIFY, config_video_notifylate);
|
||||
|
||||
CFGSET(hwndDlg, IDC_REALTIME, !config_clock);
|
||||
CFGSET(hwndDlg, IDC_LOWMEMORY, config_lowmemory);
|
||||
|
||||
SetDlgItemInt(hwndDlg,IDC_AUDIO_CACHE_FRAMES, config_audio_cache_frames, TRUE/*signed*/);
|
||||
SetDlgItemInt(hwndDlg,IDC_VIDEO_CACHE_FRAMES, config_video_cache_frames, TRUE);
|
||||
SetDlgItemInt(hwndDlg,IDC_VIDEO_DROP_THRESHOLD, config_video_drop_threshold, TRUE);
|
||||
SetDlgItemInt(hwndDlg,IDC_VIDEO_JITTER, config_video_jitter, TRUE);
|
||||
SetDlgItemInt(hwndDlg,IDC_AUDIO_EARLYPAD, config_audio_early_pad, TRUE);
|
||||
SetDlgItemInt(hwndDlg,IDC_VIDEO_EARLYPAD, config_video_early_pad,TRUE);
|
||||
|
||||
}
|
||||
|
||||
void Advanced_OnOK(HWND hwndDlg)
|
||||
{
|
||||
config_audio_dedicated_thread=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_THREAD)== BST_CHECKED;
|
||||
config_audio_early=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_EARLY)== BST_CHECKED;
|
||||
config_audio_outoforder=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_OUTOFORDER)== BST_CHECKED;
|
||||
config_video_catchup=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_DROP)== BST_CHECKED;
|
||||
|
||||
config_video_dedicated_thread=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_THREAD)== BST_CHECKED;
|
||||
config_video_early=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_EARLY)== BST_CHECKED;
|
||||
config_video_outoforder=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_OUTOFORDER)== BST_CHECKED;
|
||||
config_video_notifylate=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_NOTIFY)== BST_CHECKED;
|
||||
|
||||
config_clock=IsDlgButtonChecked(hwndDlg, IDC_REALTIME)!= BST_CHECKED;
|
||||
config_lowmemory=IsDlgButtonChecked(hwndDlg, IDC_LOWMEMORY)== BST_CHECKED;
|
||||
|
||||
wchar_t temp[64] = {0};
|
||||
|
||||
GetDlgItemText(hwndDlg,IDC_AUDIO_CACHE_FRAMES, temp, 64);
|
||||
config_audio_cache_frames = _wtoi(temp);
|
||||
|
||||
GetDlgItemText(hwndDlg,IDC_VIDEO_CACHE_FRAMES, temp, 64);
|
||||
config_video_cache_frames= _wtoi(temp);
|
||||
|
||||
GetDlgItemText(hwndDlg,IDC_VIDEO_DROP_THRESHOLD, temp, 64);
|
||||
config_video_drop_threshold= _wtoi(temp);
|
||||
|
||||
GetDlgItemText(hwndDlg,IDC_VIDEO_JITTER, temp, 64);
|
||||
config_video_jitter= _wtoi(temp);
|
||||
|
||||
GetDlgItemText(hwndDlg,IDC_AUDIO_EARLYPAD, temp, 64);
|
||||
config_audio_early_pad= _wtoi(temp);
|
||||
|
||||
GetDlgItemText(hwndDlg,IDC_VIDEO_EARLYPAD, temp, 64);
|
||||
config_video_early_pad= _wtoi(temp);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK AdvancedDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
Advanced_Init(hwndDlg);
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDOK:
|
||||
Advanced_OnOK(hwndDlg);
|
||||
EndDialog(hwndDlg, 0);
|
||||
break;
|
||||
case IDCANCEL:
|
||||
EndDialog(hwndDlg, 0);
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Advanced(HWND hwndDlg)
|
||||
{
|
||||
WASABI_API_DIALOGBOXW(IDD_ADVANCED, hwndDlg, AdvancedDialogProc);
|
||||
}
|
||||
|
||||
void Preferences_Default(HWND hwndDlg)
|
||||
{
|
||||
if (config_no_video)
|
||||
{
|
||||
// TODO: ask the user if they want to enable video support
|
||||
}
|
||||
DefaultConfig();
|
||||
fileTypes.LoadDefaults();
|
||||
Preferences_Populate(hwndDlg);
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK PreferencesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
Preferences_Init(hwndDlg);
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
config_col1 = typeList.GetColumnWidth(0);
|
||||
config_col2 = typeList.GetColumnWidth(1);
|
||||
|
||||
if (NULL != WASABI_API_APP)
|
||||
WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(typeList.getwnd(), FALSE);
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_ADVANCED:
|
||||
Advanced(hwndDlg);
|
||||
break;
|
||||
case IDC_DEFAULTTYPE:
|
||||
Preferences_Default(hwndDlg);
|
||||
break;
|
||||
case IDC_ADDTYPE:
|
||||
AddType(hwndDlg);
|
||||
FillFileTypes();
|
||||
break;
|
||||
case IDC_EDITTYPE:
|
||||
EditType(hwndDlg);
|
||||
if(typeList.GetNextSelected(editIndex)!=-1)
|
||||
FillFileTypes();
|
||||
break;
|
||||
case IDC_REMOVETYPE:
|
||||
Preferences_TypeRemove(hwndDlg);
|
||||
FillFileTypes();
|
||||
break;
|
||||
case IDOK:
|
||||
OnOk(hwndDlg);
|
||||
case IDCANCEL:
|
||||
EndDialog(hwndDlg, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifndef NULLSOFT_CONFIGDIALOGH
|
||||
#define NULLSOFT_CONFIGDIALOGH
|
||||
INT_PTR CALLBACK PreferencesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
Windows Media works in an asynchronous manner.
|
||||
The WM Decompressor (IWMReader) requires that you provide a class
|
||||
deriving from IWMReader (and, optionally, IWMReaderAdvanced) to receive information
|
||||
Each delivery of uncompressed media arrives in an OnSample method
|
||||
Status messages arrive in an OnStatus method.
|
||||
|
||||
The main difficulty that arrives from this approach is that ALL messages funnel through
|
||||
a single function. Audio data and Video data arrive in the same function.
|
||||
Even worse, status messages for file opened, clock error, http buffering, and DRM requirements all arrive in the same function!
|
||||
|
||||
In order to handle this properly, a "chain of event handlers" class was developed (WMHandler)
|
||||
Any object wishing to receive data or status messages can derive from the WMHandler. All unhandled
|
||||
messages are automatically passed to the next handler in the chain. Messages that are handled can be
|
||||
passed or not passed based on programming requirements.
|
||||
|
||||
The main object sets up the chain. The >> operator has been convienently defined to faciliate the chaining.
|
||||
The left side of the >> line must be the source (WMCallback)
|
||||
(e.g. callback >> clock >> drm >> video >> audio >> wait >> this;)
|
||||
Handlers may create their own mini-chains (which will get added in-whole) using the WMHandler::Inject() method.
|
||||
|
||||
Threading
|
||||
------
|
||||
7 basic threads
|
||||
|
||||
1. Main (Windows) thread
|
||||
2. Callback/OnStatus thread
|
||||
3. Audio Decoder thread
|
||||
4. Video Decoder thread
|
||||
5. Audio buffering thread (ours)
|
||||
6. Video buffering thread (ours)
|
||||
7. Buffering-percent status thread (ours)
|
||||
|
||||
There might be additional threads (created in another module) calling in, for functions like getextendedfileinfo
|
||||
|
||||
@@ -0,0 +1,597 @@
|
||||
#include "main.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "WMPlaylist.h"
|
||||
#include "resource.h"
|
||||
#include <math.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
WMInformation *setFileInfo = 0;
|
||||
static wchar_t *setFileInfoName=0;
|
||||
static bool forcedStop = false;
|
||||
static int outTime = 0;
|
||||
float GetGain(WMInformation *info, bool allowDefault);
|
||||
|
||||
static const wchar_t *extension(const wchar_t *fn)
|
||||
{
|
||||
const wchar_t *x = PathFindExtension(fn);
|
||||
|
||||
if (*x)
|
||||
return CharNext(x);
|
||||
else
|
||||
return x;
|
||||
}
|
||||
|
||||
static bool KeywordMatch(const char *mainString, const char *keyword)
|
||||
{
|
||||
return !_stricmp(mainString, keyword);
|
||||
}
|
||||
|
||||
static bool KeywordMatch(const wchar_t *mainString, const wchar_t *keyword)
|
||||
{
|
||||
return !lstrcmpiW(mainString, keyword);
|
||||
}
|
||||
|
||||
static bool StartsWith(const wchar_t *mainString, const wchar_t *substring)
|
||||
{
|
||||
return !_wcsnicmp(mainString, substring, lstrlenW(substring));
|
||||
}
|
||||
|
||||
static int Width(int dec)
|
||||
{
|
||||
// there's probably a better way
|
||||
int width=3;
|
||||
while (width && (dec % 10) == 0)
|
||||
{
|
||||
dec/=10;
|
||||
width--;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
int GetExtendedInformation(WMInformation *getExtendedFileInfo, const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
|
||||
{
|
||||
AutoWide tagNameW(data);
|
||||
const wchar_t *tagName = GetAlias(tagNameW);
|
||||
|
||||
if (KeywordMatch(tagName, L"streammetadata"))
|
||||
{
|
||||
if (config_http_metadata)
|
||||
{
|
||||
lstrcpyn(dest, L"1", destlen);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (KeywordMatch(tagName, L"type"))
|
||||
{
|
||||
if (!fn || !fn[0])
|
||||
lstrcpyn(dest, (config_no_video ? L"0" : L"1"), destlen);
|
||||
else if (getExtendedFileInfo->IsAttribute(g_wszWMHasVideo))
|
||||
lstrcpyn(dest, L"1", destlen);
|
||||
else if (getExtendedFileInfo->IsAttribute(g_wszWMHasAudio))
|
||||
lstrcpyn(dest, L"0", destlen);
|
||||
else
|
||||
{
|
||||
switch (fileTypes.GetAVType(extension(fn)))
|
||||
{
|
||||
case FileType::AUDIO:
|
||||
lstrcpyn(dest, L"0", destlen);
|
||||
break;
|
||||
case FileType::VIDEO:
|
||||
lstrcpyn(dest, L"1", destlen);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(tagName, L"rateable"))
|
||||
{
|
||||
dest[0] = '1';
|
||||
dest[1] = 0;
|
||||
return 1;
|
||||
}
|
||||
else if (StartsWith(tagName, L"WM/"))
|
||||
{
|
||||
getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
|
||||
return 1;
|
||||
}
|
||||
/*else if (KeywordMatch(data, "burnable")) // note: this isn't supposed to be any kind of protection - just keeps the burner from misbehaving on protected tracks
|
||||
{
|
||||
if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
|
||||
lstrcpyn(dest, L"0", destlen);
|
||||
else
|
||||
lstrcpyn(dest, L"1", destlen);
|
||||
|
||||
return 1;
|
||||
}*/
|
||||
else if (KeywordMatch(data, "noburnreason")) // note: this isn't supposed to be any kind of protection - just keeps the burner from misbehaving on protected tracks
|
||||
{
|
||||
if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
|
||||
{
|
||||
lstrcpyn(dest, L"DRM (copy protected) file", destlen);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
|
||||
{
|
||||
getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "bitrate"))
|
||||
{
|
||||
StringCchPrintfW(dest, destlen, L"%u", getExtendedFileInfo->GetBitrate() / 1000);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "vbr"))
|
||||
{
|
||||
if (getExtendedFileInfo->IsAttribute(g_wszWMIsVBR))
|
||||
StringCchCopyW(dest, destlen, L"1");
|
||||
else if (getExtendedFileInfo->IsNotAttribute(g_wszWMIsVBR))
|
||||
StringCchCopyW(dest, destlen, L"0");
|
||||
|
||||
return 1;
|
||||
}
|
||||
//else if (KeywordMatch(data, "srate"))
|
||||
else if (KeywordMatch(data, "length"))
|
||||
{
|
||||
long length = getExtendedFileInfo->GetLengthMilliseconds();
|
||||
if (length == -1000)
|
||||
return 0;
|
||||
|
||||
_itow(length, dest, 10);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "rating"))
|
||||
{
|
||||
wchar_t rating_string[128] = {0};
|
||||
getExtendedFileInfo->GetAttribute(L"WM/SharedUserRating", rating_string, 128);
|
||||
int rating = _wtoi(rating_string);
|
||||
if (rating == 0)
|
||||
dest[0]=0;
|
||||
else if (rating >= 1 && rating <= 12)
|
||||
dest[0]=L'1';
|
||||
else if (rating >= 13 && rating <= 37)
|
||||
dest[0]=L'2';
|
||||
else if (rating >= 38 && rating <= 62)
|
||||
dest[0]=L'3';
|
||||
else if (rating >= 63 && rating <= 86)
|
||||
dest[0]=L'4';
|
||||
else
|
||||
dest[0]=L'5';
|
||||
dest[1]=0;
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "replaygain_track_gain")
|
||||
|| KeywordMatch(data, "replaygain_track_peak")
|
||||
|| KeywordMatch(data, "replaygain_album_gain")
|
||||
|| KeywordMatch(data, "replaygain_album_peak"))
|
||||
{
|
||||
getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "gain"))
|
||||
{
|
||||
StringCchPrintfW(dest, destlen, L"%-+.2f dB", (float)log10f(GetGain(getExtendedFileInfo, false))*20.0f);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "audiocodec"))
|
||||
{
|
||||
if (!getExtendedFileInfo->GetCodecName(dest, destlen))
|
||||
dest[0]=0;
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "lossless"))
|
||||
{
|
||||
wchar_t codecname[1024] = {0};
|
||||
if (!getExtendedFileInfo->GetCodecName(codecname, 1024))
|
||||
dest[0]=0;
|
||||
else
|
||||
{
|
||||
dest[0] = wcsstr(codecname, L"Lossless")?'1':'0';
|
||||
dest[1]=0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "GracenoteFileID"))
|
||||
{
|
||||
getExtendedFileInfo->GetAttribute_BinString(L"GN/UniqueFileIdentifier", dest, destlen);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "GracenoteExtData"))
|
||||
{
|
||||
getExtendedFileInfo->GetAttribute_BinString(L"GN/ExtData", dest, destlen);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "formatinformation"))
|
||||
{
|
||||
// this is a bit of a clusterfuck, but it's safe and (hopefully) logically laid out.
|
||||
wchar_t codec[128]=L"", duration[64]=L"", bitrate[64]=L"", filesize[64]=L"", wmver[64]=L"", seekable[64]=L"";
|
||||
wchar_t stridable[64]=L"", broadcast[64]=L"", protect[64]=L"", trusted[64]=L"", contents[64]=L"";
|
||||
wchar_t buf[128]=L""; // temporary buffer
|
||||
|
||||
// get the codec name string
|
||||
if (getExtendedFileInfo->GetCodecName(buf, 128) && buf[0])
|
||||
StringCchPrintf(codec,128,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_CODEC),buf);
|
||||
|
||||
// get the length string formatted h:mm:ss.tttt
|
||||
long t = getExtendedFileInfo->GetLengthMilliseconds();
|
||||
if (t)
|
||||
{
|
||||
long h = t/36000000;
|
||||
long m = (t/60000)%60;
|
||||
long s = (t/1000)%60;
|
||||
long ms = t%1000;
|
||||
if (h)
|
||||
StringCchPrintf(duration,64,L"%s: %u:%02u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),h,m,s,ms);
|
||||
else if (m)
|
||||
StringCchPrintf(duration,64,L"%s: %u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),m,s,ms);
|
||||
else
|
||||
StringCchPrintf(duration,64,L"%s: %u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),s,ms);
|
||||
}
|
||||
|
||||
// get the bitrate string formatted 128.235 kbps
|
||||
long br = getExtendedFileInfo->GetBitrate();
|
||||
wchar_t kbps[16] = {0};
|
||||
StringCchPrintf(bitrate,64,L"%s: %.*f %s\n",
|
||||
WASABI_API_LNGSTRINGW(IDS_BITRATE),
|
||||
Width(br%1000),
|
||||
br/1000.0,
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_KBPS,kbps,16));
|
||||
|
||||
// get the filesize string, with commas grouping in threes
|
||||
buf[0]=0;
|
||||
getExtendedFileInfo->GetAttribute(L"FileSize",buf,64);
|
||||
uint64_t fs = _wcstoui64(buf, 0, 10);
|
||||
if (fs)
|
||||
{
|
||||
uint64_t fsgb = (fs/1000000000LL);
|
||||
uint64_t fsmb = (fs/1000000LL)%1000LL;
|
||||
uint64_t fskb = (fs/1000LL)%1000LL;
|
||||
uint64_t fsb = fs%1000LL;
|
||||
if (fsgb)
|
||||
StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsgb,fsmb,fskb,fsb);
|
||||
else if (fsmb)
|
||||
StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsmb,fskb,fsb);
|
||||
else if (fskb)
|
||||
StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fskb,fsb);
|
||||
else
|
||||
StringCchPrintf(filesize,64,L"%s: %I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsb);
|
||||
}
|
||||
|
||||
// 4 boolean flags, compose their strings
|
||||
wchar_t yes[64] = {0}, no[64] = {0};
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_YES,yes,64);
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_NO,no,64);
|
||||
|
||||
buf[0]=0;
|
||||
getExtendedFileInfo->GetAttribute(L"WMFSDKVersion",buf,128);
|
||||
if (buf[0])
|
||||
StringCchPrintf(wmver,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_WMVER),buf);
|
||||
|
||||
StringCchPrintf(seekable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_SEEKABLE),getExtendedFileInfo->IsAttribute(L"Seekable")?yes:no);
|
||||
StringCchPrintf(stridable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_STRIDABLE),getExtendedFileInfo->IsAttribute(L"Stridable")?yes:no);
|
||||
StringCchPrintf(broadcast,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_BROADCAST),getExtendedFileInfo->IsAttribute(L"Broadcast")?yes:no);
|
||||
StringCchPrintf(protect,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_PROTECTED),getExtendedFileInfo->IsAttribute(L"Is_Protected")?yes:no);
|
||||
StringCchPrintf(trusted,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_TRUSTED),getExtendedFileInfo->IsAttribute(L"Is_Trusted")?yes:no);
|
||||
|
||||
// file contents. bit gross i know.
|
||||
wchar_t cont[4][16]={L"",L"",L"",L""};
|
||||
int i=0;
|
||||
if (getExtendedFileInfo->IsAttribute(L"HasAudio"))
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_AUDIO,cont[i++],16);
|
||||
if (getExtendedFileInfo->IsAttribute(L"HasVideo"))
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_VIDEO,cont[i++],16);
|
||||
if (getExtendedFileInfo->IsAttribute(L"HasImage"))
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_IMAGE,cont[i++],16);
|
||||
if (getExtendedFileInfo->IsAttribute(L"HasScript"))
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_SCRIPT,cont[i++],16);
|
||||
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_NONE,buf,64);
|
||||
if (i == 0)
|
||||
StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), buf);
|
||||
else if (i == 1)
|
||||
StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0]);
|
||||
else if (i == 2)
|
||||
StringCchPrintf(contents,64,L"%s: %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1]);
|
||||
else if (i == 3)
|
||||
StringCchPrintf(contents,64,L"%s: %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2]);
|
||||
else if (i == 4)
|
||||
StringCchPrintf(contents,64,L"%s: %s, %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2], cont[3]);
|
||||
|
||||
// compose our string together!
|
||||
StringCchPrintf(dest,destlen,L"%s%s%s%s%s%s%s%s%s%s%s",codec, duration, bitrate, filesize, wmver, seekable, stridable, broadcast, protect, trusted, contents);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if 0 // had to disable this because it was locking the file from being deleted
|
||||
WMInformation *lastGetInfo = 0;
|
||||
wchar_t *lastGetInfoFn;
|
||||
#endif
|
||||
|
||||
extern "C" __declspec(dllexport)
|
||||
int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
|
||||
{
|
||||
/* Check if there's a status message for this filename
|
||||
doing this forces Winamp to hit plugin.getfileinfo, which gives us better control
|
||||
over adding things like [Individualizing] to the playlist title for local files
|
||||
*/
|
||||
if (winamp.HasStatus(fn))
|
||||
return 0;
|
||||
|
||||
if ((!fn || !*fn) && KeywordMatch(data, "type"))
|
||||
{
|
||||
if (config_no_video)
|
||||
lstrcpyn(dest, L"0", destlen);
|
||||
else
|
||||
lstrcpyn(dest, L"1", destlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (KeywordMatch(data, "mime"))
|
||||
{
|
||||
int len;
|
||||
const wchar_t *p;
|
||||
if (!fn || !fn[0]) return 0;
|
||||
len = lstrlenW(fn);
|
||||
if (len < 4 || L'.' != fn[len - 4]) return 0;
|
||||
p = &fn[len - 3];
|
||||
if (!_wcsicmp(p, L"WMA")) { StringCchCopyW(dest, destlen, L"audio/x-ms-wma"); return 1; }
|
||||
if (!_wcsicmp(p, L"WMV")) { StringCchCopyW(dest, destlen, L"video/x-ms-wmv"); return 1; }
|
||||
if (!_wcsicmp(p, L"ASF")) { StringCchCopyW(dest, destlen, L"video/x-ms-asf"); return 1; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (KeywordMatch(data, "family"))
|
||||
{
|
||||
LPCWSTR e, p(NULL);
|
||||
DWORD lcid;
|
||||
size_t i;
|
||||
int len2(0);
|
||||
|
||||
if (!fn || !*fn) return 0;
|
||||
e = PathFindExtensionW(fn);
|
||||
if (L'.' != *e) return 0;
|
||||
e++;
|
||||
|
||||
if (!*e) return 0;
|
||||
lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
|
||||
|
||||
for (i = 0; i < fileTypes.types.size() && !p; i++)
|
||||
{
|
||||
if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, fileTypes.types.at(i).wtype, -1))
|
||||
p = fileTypes.types.at(i).description;
|
||||
}
|
||||
|
||||
if (p)
|
||||
{
|
||||
wchar_t szTest[16];
|
||||
if (S_OK == StringCchPrintfW(szTest, sizeof(szTest)/sizeof(wchar_t), L" (*.%s)", e))
|
||||
{
|
||||
int len1 = lstrlenW(szTest);
|
||||
len2 = lstrlenW(p);
|
||||
if (len2 > len1 && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, szTest, -1, (p + len2 - len1), -1))
|
||||
len2 -= len1;
|
||||
}
|
||||
}
|
||||
|
||||
return (p && S_OK == StringCchCopyNW(dest, destlen, p, len2));
|
||||
}
|
||||
|
||||
if (!config_http_metadata && PathIsURL(fn))
|
||||
return 0;
|
||||
|
||||
if (KeywordMatch(data, "type") && !PathFileExistsW(fn))
|
||||
{
|
||||
switch (fileTypes.GetAVType(extension(fn)))
|
||||
{
|
||||
case FileType::AUDIO:
|
||||
lstrcpyn(dest, L"0", destlen);
|
||||
return 1;
|
||||
case FileType::VIDEO:
|
||||
lstrcpyn(dest, L"1", destlen);
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (activePlaylist.IsMe(fn))
|
||||
{
|
||||
WMInformation getExtendedFileInfo(activePlaylist.GetFileName());
|
||||
|
||||
return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
WMInformation getExtendedFileInfo(fn);
|
||||
|
||||
return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
|
||||
}
|
||||
|
||||
#if 0 // had to disable this because it was locking the file from being deleted
|
||||
if (lastGetInfo && lastGetInfoFn && !_wcsicmp(fn, lastGetInfoFn))
|
||||
{
|
||||
return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
|
||||
}
|
||||
|
||||
delete lastGetInfo;
|
||||
lastGetInfo=0;
|
||||
free(lastGetInfoFn);
|
||||
lastGetInfoFn=0;
|
||||
|
||||
if (activePlaylist.IsMe(fn))
|
||||
lastGetInfoFn = _wcsdup(activePlaylist.GetFileName());
|
||||
else
|
||||
lastGetInfoFn = _wcsdup(fn);
|
||||
|
||||
lastGetInfo = new WMInformation(lastGetInfoFn);
|
||||
if (lastGetInfo->ErrorOpening())
|
||||
{
|
||||
if (KeywordMatch(data, "type"))
|
||||
{
|
||||
switch (fileTypes.GetAVType(extension(fn)))
|
||||
{
|
||||
case FileType::AUDIO:
|
||||
lstrcpyn(dest, L"0", destlen);
|
||||
return 1;
|
||||
case FileType::VIDEO:
|
||||
lstrcpyn(dest, L"1", destlen);
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
delete lastGetInfo;
|
||||
lastGetInfo=0;
|
||||
free(lastGetInfoFn);
|
||||
lastGetInfoFn=0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int winampClearExtendedFileInfoW(const wchar_t *fn)
|
||||
{
|
||||
// TODO: press stop if it's the currently playing file
|
||||
WMInformation wmInfo(fn);
|
||||
wmInfo.ClearAllAttributes();
|
||||
wmInfo.Flush();
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
|
||||
{
|
||||
// if (!lastSetInfoFilename.empty() && lastSetInfoFilename != fn)
|
||||
// dosomething();
|
||||
|
||||
#if 0 // had to disable this because it was locking the file from being deleted
|
||||
if (lastGetInfoFn && !_wcsicmp(lastGetInfoFn,fn))
|
||||
{
|
||||
delete lastGetInfo;
|
||||
lastGetInfo=0;
|
||||
free(lastGetInfoFn);
|
||||
lastGetInfoFn=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!setFileInfo)
|
||||
{
|
||||
if (activePlaylist.IsMe(fn) && mod.playing)
|
||||
{
|
||||
forcedStop = true;
|
||||
outTime = mod.GetOutputTime();
|
||||
winamp.PressStop();
|
||||
}
|
||||
free(setFileInfoName);
|
||||
setFileInfoName = _wcsdup(fn);
|
||||
setFileInfo = new WMInformation(fn);
|
||||
if (!setFileInfo->MakeWritable(fn))
|
||||
return 0; // can't write
|
||||
}
|
||||
|
||||
AutoWide tagNameW(data);
|
||||
const wchar_t *tagName = GetAlias(tagNameW);
|
||||
|
||||
if (StartsWith(tagName, L"WM/"))
|
||||
{
|
||||
setFileInfo->SetAttribute(tagName, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
|
||||
{
|
||||
setFileInfo->SetAttribute(tagName, val);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "rating"))
|
||||
{
|
||||
int rating = _wtoi(val);
|
||||
if (rating == 0)
|
||||
setFileInfo->SetAttribute(L"WM/SharedUserRating", L"",WMT_TYPE_DWORD);
|
||||
else
|
||||
{
|
||||
switch(rating)
|
||||
{
|
||||
case 1:
|
||||
setFileInfo->SetAttribute(L"WM/SharedUserRating", L"1",WMT_TYPE_DWORD);
|
||||
break;
|
||||
case 2:
|
||||
setFileInfo->SetAttribute(L"WM/SharedUserRating", L"25",WMT_TYPE_DWORD);
|
||||
break;
|
||||
case 3:
|
||||
setFileInfo->SetAttribute(L"WM/SharedUserRating", L"50",WMT_TYPE_DWORD);
|
||||
break;
|
||||
case 4:
|
||||
setFileInfo->SetAttribute(L"WM/SharedUserRating", L"75",WMT_TYPE_DWORD);
|
||||
break;
|
||||
default:
|
||||
setFileInfo->SetAttribute(L"WM/SharedUserRating", L"99",WMT_TYPE_DWORD);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (KeywordMatch(data, "replaygain_track_gain")
|
||||
|| KeywordMatch(data, "replaygain_track_peak")
|
||||
|| KeywordMatch(data, "replaygain_album_gain")
|
||||
|| KeywordMatch(data, "replaygain_album_peak"))
|
||||
{
|
||||
setFileInfo->SetAttribute(tagName, val);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "GracenoteFileID"))
|
||||
{
|
||||
setFileInfo->SetAttribute_BinString(L"GN/UniqueFileIdentifier", val);
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "GracenoteExtData"))
|
||||
{
|
||||
setFileInfo->SetAttribute_BinString(L"GN/ExtData", val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// else if (KeywordMatch(data, "bitrate"))
|
||||
//else if (KeywordMatch(data, "disc"))
|
||||
// else if (KeywordMatch(data, "vbr"))
|
||||
//else if (KeywordMatch(data, "srate"))
|
||||
// else if (KeywordMatch(data, "length"))
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int winampWriteExtendedFileInfo()
|
||||
{
|
||||
if (setFileInfo)
|
||||
{
|
||||
bool flushOK = setFileInfo->Flush();
|
||||
delete setFileInfo;
|
||||
setFileInfo = 0;
|
||||
|
||||
if (forcedStop)
|
||||
{
|
||||
mod.startAtMilliseconds = outTime;
|
||||
winamp.PressPlay();
|
||||
}
|
||||
forcedStop=false;
|
||||
|
||||
if (flushOK)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
#include "main.h"
|
||||
#include "ExtendedRead.h"
|
||||
|
||||
extern WM_MEDIA_TYPE *NewMediaType(IWMOutputMediaProps *props);
|
||||
|
||||
ExtendedReadStruct::ExtendedReadStruct() : buffer(0), reader(0), streamNum(0), bufferUsed(0), endOfFile(false), length(0)
|
||||
{}
|
||||
|
||||
ExtendedReadStruct::ExtendedReadStruct(IWMSyncReader *_reader) : buffer(0), reader(0), streamNum(0), bufferUsed(0), endOfFile(false), length(0)
|
||||
{
|
||||
reader = _reader;
|
||||
reader->AddRef();
|
||||
}
|
||||
|
||||
#define CBCLASS ExtendedReadStruct
|
||||
START_DISPATCH;
|
||||
CB(IFC_AUDIOSTREAM_READAUDIO, ReadAudio)
|
||||
END_DISPATCH;
|
||||
|
||||
ExtendedReadStruct::~ExtendedReadStruct()
|
||||
{
|
||||
if (reader)
|
||||
{
|
||||
reader->Close();
|
||||
reader->Release();
|
||||
}
|
||||
if (buffer)
|
||||
buffer->Release();
|
||||
}
|
||||
|
||||
size_t ExtendedReadStruct::ReadAudio(void *_buffer, size_t len)
|
||||
{
|
||||
__int8 *dest = reinterpret_cast<__int8 *>(_buffer);
|
||||
int bytesCopied = 0;
|
||||
/*
|
||||
while we still have bytes left
|
||||
{
|
||||
read a buffer
|
||||
copy buffer to user passed buffer
|
||||
if we have stuff left in the buffer, save it and return
|
||||
if we hit EOF, return
|
||||
}
|
||||
*/
|
||||
size_t frameSize = (BitSize()/8)*Channels();
|
||||
len -= (len % frameSize); // only do whole frames
|
||||
while (len)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
BYTE *bufferBytes;
|
||||
DWORD bufferTotal;
|
||||
buffer->GetBufferAndLength(&bufferBytes, &bufferTotal);
|
||||
|
||||
if (bufferUsed < bufferTotal)
|
||||
{
|
||||
size_t toCopy = min(bufferTotal - bufferUsed, len);
|
||||
memcpy(dest, bufferBytes + bufferUsed, toCopy);
|
||||
bufferUsed += toCopy;
|
||||
len -= toCopy;
|
||||
dest += toCopy;
|
||||
bytesCopied += toCopy;
|
||||
|
||||
if (bufferUsed == bufferTotal)
|
||||
{
|
||||
bufferUsed = 0;
|
||||
buffer->Release();
|
||||
buffer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!endOfFile)
|
||||
{
|
||||
QWORD sampleTime, duration;
|
||||
DWORD flags;
|
||||
DWORD outputNum;
|
||||
WORD streamNum2;
|
||||
HRESULT hr;
|
||||
hr = reader->GetNextSample(streamNum, &buffer, &sampleTime, &duration, &flags, & outputNum, &streamNum2);
|
||||
if (hr == NS_E_NO_MORE_SAMPLES)
|
||||
endOfFile = true;
|
||||
|
||||
}
|
||||
else
|
||||
return bytesCopied;
|
||||
}
|
||||
|
||||
return bytesCopied;
|
||||
}
|
||||
|
||||
bool ExtendedReadStruct::Open(const wchar_t *filename)
|
||||
{
|
||||
//mod.InitWM();
|
||||
if (!reader)
|
||||
{
|
||||
if (!SUCCEEDED(WMCreateSyncReader(NULL, 0, &reader)))
|
||||
{
|
||||
reader = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(reader->Open(filename)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader->Release();
|
||||
reader = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ExtendedReadStruct::FindOutput(int bits, int channels)
|
||||
{
|
||||
DWORD numOutputs, output, format, numFormats;
|
||||
IWMOutputMediaProps *formatProperties;
|
||||
GUID mediaType;
|
||||
DWORD audioOutputNum;
|
||||
|
||||
if (FAILED((reader->GetOutputCount(&numOutputs))))
|
||||
return false;
|
||||
|
||||
for (output = 0;output < numOutputs;output++)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD speakerConfig;
|
||||
switch (channels)
|
||||
{
|
||||
case 1:
|
||||
speakerConfig = DSSPEAKER_MONO;
|
||||
break;
|
||||
case 2:
|
||||
speakerConfig = DSSPEAKER_STEREO;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
speakerConfig = config_audio_num_channels; // TODO: force max channels?
|
||||
}
|
||||
|
||||
hr = reader->SetOutputSetting(output, g_wszSpeakerConfig, WMT_TYPE_DWORD, (BYTE *) & speakerConfig, sizeof(speakerConfig));
|
||||
assert(hr == S_OK);
|
||||
|
||||
BOOL discreteChannels = TRUE;
|
||||
hr = reader->SetOutputSetting(output, g_wszEnableDiscreteOutput, WMT_TYPE_BOOL, (BYTE *) & discreteChannels , sizeof(discreteChannels));
|
||||
assert(hr == S_OK);
|
||||
|
||||
if (FAILED(reader->GetOutputFormatCount(output, &numFormats)))
|
||||
continue;
|
||||
|
||||
for (format = 0;format < numFormats;format++)
|
||||
{
|
||||
reader->GetOutputFormat(output, format, &formatProperties);
|
||||
formatProperties->GetType(&mediaType);
|
||||
if (mediaType != WMMEDIATYPE_Audio)
|
||||
continue;
|
||||
|
||||
WM_MEDIA_TYPE *mediaType = NewMediaType(formatProperties);
|
||||
if (mediaType->subtype == WMMEDIASUBTYPE_PCM)
|
||||
{
|
||||
if (bits)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE *waveFormat = (WAVEFORMATEXTENSIBLE *) mediaType->pbFormat;
|
||||
if (waveFormat->Format.cbSize >= 22)
|
||||
waveFormat->Samples.wValidBitsPerSample = bits;
|
||||
waveFormat->Format.wBitsPerSample = bits;
|
||||
waveFormat->Format.nBlockAlign = (waveFormat->Format.wBitsPerSample / 8) * waveFormat->Format.nChannels;
|
||||
waveFormat->Format.nAvgBytesPerSec = waveFormat->Format.nSamplesPerSec * waveFormat->Format.nBlockAlign;
|
||||
if (FAILED(formatProperties->SetMediaType(mediaType)))
|
||||
{
|
||||
// blah, just use the default settings then
|
||||
delete[] mediaType;
|
||||
mediaType = NewMediaType(formatProperties);
|
||||
}
|
||||
}
|
||||
|
||||
AudioFormat::Open(mediaType);
|
||||
delete[] mediaType;
|
||||
audioOutputNum = output;
|
||||
reader->SetOutputProps(audioOutputNum, formatProperties);
|
||||
reader->GetStreamNumberForOutput(audioOutputNum, &streamNum);
|
||||
formatProperties->Release();
|
||||
reader->SetReadStreamSamples(streamNum, FALSE);
|
||||
|
||||
IWMHeaderInfo *headerInfo = 0;
|
||||
reader->QueryInterface(&headerInfo);
|
||||
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_QWORD;
|
||||
WORD byteLength = sizeof(QWORD);
|
||||
WORD blah = 0;
|
||||
headerInfo->GetAttributeByName(&blah, g_wszWMDuration, &type, (BYTE *)&length, &byteLength);
|
||||
|
||||
headerInfo->Release();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
delete[] mediaType;
|
||||
formatProperties->Release();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__declspec(dllexport) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
|
||||
{
|
||||
ExtendedReadStruct *read = new ExtendedReadStruct;
|
||||
|
||||
if (read->Open(fn)
|
||||
&& read->FindOutput(*bps, *nch))
|
||||
{
|
||||
*nch = read->Channels();
|
||||
*bps = read->BitSize();
|
||||
*srate = read->SampleRate();
|
||||
__int64 bytespersec = ((*nch) * (*bps / 8) * (*srate));
|
||||
__int64 s = (bytespersec * ((__int64)read->length)) / (__int64)(1000 * 10000);
|
||||
*size = (int)s;
|
||||
return reinterpret_cast<intptr_t>(read);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete read;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__declspec(dllexport) intptr_t winampGetExtendedRead_getData(intptr_t handle, char *dest, int len, int *killswitch)
|
||||
{
|
||||
ExtendedReadStruct *read = reinterpret_cast<ExtendedReadStruct *>(handle);
|
||||
return read->ReadAudio(dest, len);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__declspec(dllexport) void winampGetExtendedRead_close(intptr_t handle)
|
||||
{
|
||||
ExtendedReadStruct *read = reinterpret_cast<ExtendedReadStruct *>(handle);
|
||||
delete read;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef NULLSOFT_IN_WMVDRM_EXTENDEDREAD_H
|
||||
#define NULLSOFT_IN_WMVDRM_EXTENDEDREAD_H
|
||||
|
||||
#include "AudioFormat.h"
|
||||
#include "../Agave/DecodeFile/ifc_audiostream.h"
|
||||
#include "main.h"
|
||||
|
||||
struct ExtendedReadStruct : public AudioFormat, public ifc_audiostream
|
||||
{
|
||||
public:
|
||||
ExtendedReadStruct();
|
||||
ExtendedReadStruct(IWMSyncReader *_reader);
|
||||
~ExtendedReadStruct();
|
||||
|
||||
bool Open(const wchar_t *filename);
|
||||
bool FindOutput(int bits, int channels);
|
||||
size_t ReadAudio(void *buffer, size_t sizeBytes);
|
||||
|
||||
IWMSyncReader *reader;
|
||||
WORD streamNum;
|
||||
|
||||
INSSBuffer *buffer;
|
||||
size_t bufferUsed;
|
||||
bool endOfFile;
|
||||
QWORD length;
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "api.h"
|
||||
#include <api/service/waservicefactory.h>
|
||||
|
||||
template <class api_T>
|
||||
void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
|
||||
{
|
||||
if (plugin.service)
|
||||
{
|
||||
waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
|
||||
if (factory)
|
||||
api_t = (api_T *)factory->getInterface();
|
||||
}
|
||||
}
|
||||
|
||||
template <class api_T>
|
||||
void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
|
||||
{
|
||||
if (plugin.service)
|
||||
{
|
||||
waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
|
||||
if (factory)
|
||||
factory->releaseInterface(api_t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,831 @@
|
||||
#include "Main.h"
|
||||
#include "FileInfoDialog.h"
|
||||
#include "WMInformation.h"
|
||||
#include "resource.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
|
||||
#include "WMDRMModule.h"
|
||||
#include "WMPlaylist.h"
|
||||
|
||||
// blah! commented out a load of shit because we can't have the advanced pane edit data cause wma sucks.
|
||||
|
||||
class Info
|
||||
{
|
||||
public:
|
||||
Info(const wchar_t *filename);
|
||||
~Info();
|
||||
//bool Save(HWND parent);
|
||||
int Error();
|
||||
int GetNumMetadataItems();
|
||||
void EnumMetadata(int n,wchar_t *key,int keylen, wchar_t *val, int vallen);
|
||||
//void RemoveMetadata(wchar_t * key);
|
||||
//void RemoveMetadata(int n);
|
||||
//void SetMetadata(wchar_t *key, wchar_t *val);
|
||||
//void SetMetadata(int n, wchar_t *key, wchar_t *val);
|
||||
//void SetTag(int n,wchar_t *key); // changes the key name
|
||||
private:
|
||||
WMInformation wminfo;
|
||||
const wchar_t *filename;
|
||||
};
|
||||
|
||||
Info::Info(const wchar_t *filename) : wminfo(filename), filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
Info::~Info()
|
||||
{
|
||||
}
|
||||
/*
|
||||
bool Info::Save(HWND parent)
|
||||
{
|
||||
if (!wminfo.MakeWritable(filename))
|
||||
{
|
||||
wchar_t title[64] = {0};
|
||||
if (activePlaylist.IsMe(filename) && mod.playing)
|
||||
{
|
||||
// TODO: this is a race condition. we might have stopped in between the above if () and now...
|
||||
int outTime = mod.GetOutputTime();
|
||||
winamp.PressStop();
|
||||
wminfo.MakeWritable(filename);
|
||||
|
||||
SendMessage(parent,WM_USER+1,0,0);
|
||||
//WriteEditBoxes();
|
||||
|
||||
wminfo.Flush();
|
||||
wminfo.MakeReadOnly(filename);
|
||||
mod.startAtMilliseconds=outTime;
|
||||
winamp.PressPlay();
|
||||
return true;
|
||||
}
|
||||
else if (!wminfo.MakeReadOnly(filename))
|
||||
MessageBox(parent, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST),
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
|
||||
else
|
||||
MessageBox(parent, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS),
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SendMessage(parent,WM_USER+1,0,0);
|
||||
//WriteEditBoxes();
|
||||
|
||||
if (!wminfo.Flush())
|
||||
{
|
||||
wchar_t* title = WASABI_API_LNGSTRINGW(IDS_SAVE_FAILED);
|
||||
MessageBox(NULL, title, title, MB_OK);
|
||||
}
|
||||
|
||||
wminfo.MakeReadOnly(filename);
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
int Info::Error()
|
||||
{
|
||||
return wminfo.ErrorOpening();
|
||||
}
|
||||
|
||||
int Info::GetNumMetadataItems()
|
||||
{
|
||||
return wminfo.GetNumberAttributes();
|
||||
}
|
||||
|
||||
void Info::EnumMetadata(int n,wchar_t *key,int keylen, wchar_t *val, int vallen)
|
||||
{
|
||||
if(keylen) key[0]=0;
|
||||
if(vallen) val[0]=0;
|
||||
wminfo.GetAttribute(n, key, keylen, val, vallen);
|
||||
}
|
||||
/*
|
||||
void Info::RemoveMetadata(wchar_t * key)
|
||||
{
|
||||
wminfo.DeleteAttribute(key);
|
||||
}
|
||||
|
||||
void Info::RemoveMetadata(int n)
|
||||
{
|
||||
wchar_t key[256] = {0};
|
||||
EnumMetadata(n,key,256,NULL,0);
|
||||
if(key[0])
|
||||
RemoveMetadata(key);
|
||||
}
|
||||
|
||||
void Info::SetMetadata(wchar_t *key, wchar_t *val)
|
||||
{
|
||||
wminfo.SetAttribute(key,val);
|
||||
}
|
||||
|
||||
void Info::SetTag(int n, wchar_t *key)
|
||||
{ // changes the key name
|
||||
wchar_t val[2048]=L"";
|
||||
wchar_t oldkey[256]=L"";
|
||||
EnumMetadata(n,oldkey,256,val,2048);
|
||||
RemoveMetadata(oldkey);
|
||||
wminfo.SetAttribute(key,val);
|
||||
}
|
||||
*/
|
||||
bool WMTagToWinampTag(wchar_t * tag, int len)
|
||||
{
|
||||
const wchar_t *f = GetAlias_rev(tag);
|
||||
if(f)
|
||||
{
|
||||
lstrcpyn(tag,f,len);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WinampTagToWMTag(wchar_t * tag, int len)
|
||||
{
|
||||
const wchar_t *f = GetAlias(tag);
|
||||
if(f)
|
||||
{
|
||||
lstrcpyn(tag,f,len);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK ChildProc_Advanced(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
static int ismychange=0;
|
||||
switch(msg)
|
||||
{
|
||||
case WM_NOTIFYFORMAT:
|
||||
return NFR_UNICODE;
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
//SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
|
||||
HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT);
|
||||
LVCOLUMN lvc = {0, };
|
||||
lvc.mask = LVCF_TEXT|LVCF_WIDTH;
|
||||
lvc.pszText = WASABI_API_LNGSTRINGW(IDS_NAME);
|
||||
lvc.cx = 82;
|
||||
ListView_InsertColumn(hwndlist, 0, &lvc);
|
||||
lvc.pszText = WASABI_API_LNGSTRINGW(IDS_VALUE);
|
||||
lvc.cx = 160;
|
||||
ListView_InsertColumn(hwndlist, 1, &lvc);
|
||||
|
||||
Info *info = (Info *)lParam;
|
||||
int n = info->GetNumMetadataItems();
|
||||
for(int i=0; i<n; i++) {
|
||||
wchar_t key[512] = {0};
|
||||
wchar_t value[2048] = {0};
|
||||
info->EnumMetadata(i,key,512,value,2048);
|
||||
if(key[0]) {
|
||||
LVITEMW lvi={LVIF_TEXT,i,0};
|
||||
lvi.pszText = key;
|
||||
SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
|
||||
lvi.iSubItem=1;
|
||||
lvi.pszText = value;
|
||||
SendMessage(hwndlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
}
|
||||
}
|
||||
ListView_SetColumnWidth(hwndlist,0,LVSCW_AUTOSIZE);
|
||||
ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE);
|
||||
|
||||
//SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
//SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
//EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
//EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
//EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
|
||||
delete info;
|
||||
}
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
{
|
||||
HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
ListView_DeleteAllItems(hwndlist);
|
||||
while(ListView_DeleteColumn(hwndlist,0));
|
||||
Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
|
||||
if(info) delete info;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
case WM_USER+1:
|
||||
{
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
info->wminfo.ClearAllAttributes();
|
||||
wchar_t key[100] = {0}, value[2048] = {0};
|
||||
HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
int n = ListView_GetItemCount(hlist);
|
||||
for(int i=0; i<n; i++)
|
||||
{
|
||||
key[0]=value[0]=0;
|
||||
LVITEMW lvi={LVIF_TEXT,i,0};
|
||||
lvi.pszText=key;
|
||||
lvi.cchTextMax=100;
|
||||
SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
lvi.iSubItem = 1;
|
||||
lvi.pszText = value;
|
||||
lvi.cchTextMax=2048;
|
||||
SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
if(key[0])
|
||||
info->SetMetadata(key,value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case WM_USER:
|
||||
if(wParam && lParam && !ismychange)
|
||||
{
|
||||
wchar_t * value = (wchar_t*)lParam;
|
||||
wchar_t tag[100] = {0};
|
||||
lstrcpynW(tag,(wchar_t*)wParam,100);
|
||||
WinampTagToWMTag(tag,100);
|
||||
/*
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
if(!*value) info->RemoveMetadata(tag);
|
||||
else info->SetMetadata(tag,value);
|
||||
*/
|
||||
HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
int n = ListView_GetItemCount(hlist);
|
||||
for(int i=0; i<n; i++)
|
||||
{
|
||||
wchar_t key[100]=L"";
|
||||
LVITEMW lvi={LVIF_TEXT,i,0};
|
||||
lvi.pszText=key;
|
||||
lvi.cchTextMax=100;
|
||||
SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
if(!_wcsicmp(key,tag))
|
||||
{
|
||||
lvi.iSubItem = 1;
|
||||
lvi.pszText = value;
|
||||
SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
if(!*value)
|
||||
ListView_DeleteItem(hlist,i);
|
||||
/*
|
||||
else if(ListView_GetItemState(hlist,i,LVIS_SELECTED))
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// bew hew, not found
|
||||
LVITEMW lvi={0,0x7FFFFFF0,0};
|
||||
n = SendMessage(hlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
|
||||
lvi.mask = LVIF_TEXT;
|
||||
lvi.iItem = n;
|
||||
lvi.iSubItem = 0;
|
||||
lvi.pszText = tag;
|
||||
SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
lvi.iSubItem = 1;
|
||||
lvi.pszText = value;
|
||||
SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
}
|
||||
break;
|
||||
/*
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR l=(LPNMHDR)lParam;
|
||||
if(l->idFrom==IDC_LIST && l->code == LVN_ITEMCHANGED) {
|
||||
LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;
|
||||
if(lv->uNewState & LVIS_SELECTED) {
|
||||
int n = lv->iItem;
|
||||
LVITEMW lvi={LVIF_TEXT,lv->iItem,0};
|
||||
wchar_t key[100] = {0};
|
||||
wchar_t value[1024] = {0};
|
||||
lvi.pszText=key;
|
||||
lvi.cchTextMax=100;
|
||||
SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
lvi.pszText=value;
|
||||
lvi.cchTextMax=1024;
|
||||
lvi.iSubItem=1;
|
||||
SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,key);
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
|
||||
sel = n;
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),TRUE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),TRUE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),TRUE);
|
||||
}
|
||||
if(lv->uOldState & LVIS_SELECTED) {
|
||||
int n = lv->iItem;
|
||||
sel = -1;
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case WM_COMMAND:
|
||||
switch(LOWORD(wParam)) {
|
||||
case IDOK:
|
||||
{
|
||||
//Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
|
||||
//info->Save(hwndDlg);
|
||||
}
|
||||
break;
|
||||
/*
|
||||
case IDC_NAME:
|
||||
case IDC_VALUE:
|
||||
if(HIWORD(wParam) == EN_CHANGE && sel>=0) {
|
||||
wchar_t key[100] = {0};
|
||||
wchar_t value[1024] = {0};
|
||||
LVITEMW lvi={LVIF_TEXT,sel,0};
|
||||
GetDlgItemTextW(hwndDlg,IDC_NAME,key,100);
|
||||
GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024);
|
||||
lvi.pszText=key;
|
||||
lvi.cchTextMax=100;
|
||||
SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
lvi.pszText=value;
|
||||
lvi.cchTextMax=1024;
|
||||
lvi.iSubItem=1;
|
||||
SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
WMTagToWinampTag(key,100);
|
||||
ismychange=1;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
|
||||
ismychange=0;
|
||||
}
|
||||
else if(HIWORD(wParam) == EN_KILLFOCUS && sel>=0) {
|
||||
wchar_t key[100] = {0};
|
||||
wchar_t value[1024] = {0};
|
||||
GetDlgItemTextW(hwndDlg,IDC_NAME,key,100);
|
||||
GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024);
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
wchar_t oldkey[100]=L"";
|
||||
bool newitem=true;
|
||||
if(sel < info->GetNumMetadataItems()) {
|
||||
info->EnumMetadata(sel,oldkey,100,0,0);
|
||||
newitem=false;
|
||||
}
|
||||
|
||||
if(!newitem && wcscmp(oldkey,key)) { // key changed
|
||||
info->SetTag(sel,key);
|
||||
} else {
|
||||
info->SetMetadata(key,value);
|
||||
}
|
||||
WMTagToWinampTag(key,100);
|
||||
ismychange=1;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
|
||||
ismychange=0;
|
||||
}
|
||||
break;
|
||||
case IDC_BUTTON_DEL:
|
||||
if(sel >= 0) {
|
||||
wchar_t tag[100] = {0};
|
||||
GetDlgItemTextW(hwndDlg,IDC_NAME,tag,100);
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
if(sel < info->GetNumMetadataItems())
|
||||
info->RemoveMetadata(sel);
|
||||
ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_LIST),sel);
|
||||
sel=-1;
|
||||
WMTagToWinampTag(tag,100);
|
||||
ismychange=1;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(WPARAM)L"");
|
||||
ismychange=0;
|
||||
}
|
||||
break;
|
||||
case IDC_BUTTON_DELALL:
|
||||
ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_LIST));
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
sel=-1;
|
||||
{
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
int n = info->GetNumMetadataItems();
|
||||
while(n>0) {
|
||||
--n;
|
||||
wchar_t tag[100] = {0};
|
||||
info->EnumMetadata(n,tag,100,0,0);
|
||||
WMTagToWinampTag(tag,100);
|
||||
ismychange=0;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(WPARAM)L"");
|
||||
ismychange=1;
|
||||
info->RemoveMetadata(n);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IDC_BUTTON_ADD:
|
||||
{
|
||||
HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
LVITEMW lvi={0,0x7FFFFFF0,0};
|
||||
int n = SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
|
||||
ListView_SetItemState(hwndlist,n,LVIS_SELECTED,LVIS_SELECTED);
|
||||
}
|
||||
break;
|
||||
*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
// return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)
|
||||
// if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
|
||||
__declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab.
|
||||
// Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced").
|
||||
// filename will be valid for the life of your window. n is the tab number. This function will first be
|
||||
// called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like).
|
||||
// The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel.
|
||||
// when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue");
|
||||
// this will be broadcast to all panes (including yours) as a WM_USER.
|
||||
__declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen)
|
||||
{
|
||||
if(n == 0) { // add first pane
|
||||
//SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1);
|
||||
Info *info = new Info(filename);
|
||||
if(info->Error())
|
||||
{
|
||||
delete info;
|
||||
return NULL;
|
||||
}
|
||||
return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO,parent,ChildProc_Advanced,(LPARAM)info);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* CUT> we're now using the unified file info dlg. I'll leave this commented out incase we want to do an advanced tab later on.
|
||||
|
||||
#define CREATEDIALOGBOX DialogBoxParam
|
||||
#define CLOSEDIALOGBOX(x) EndDialog(x, 0)
|
||||
extern WMDRM mod;
|
||||
enum
|
||||
{
|
||||
AttributeColumn = 0,
|
||||
ValueColumn = 1,
|
||||
};
|
||||
|
||||
void FileInfoDialog::FillAttributeList()
|
||||
{
|
||||
attributeList.Clear();
|
||||
WORD attrCount = wmInfo->GetNumberAttributes();
|
||||
wchar_t attrName[32768] = {0}, value[32768] = {0};
|
||||
int pos=0;
|
||||
for (WORD i = 0;i != attrCount;i++)
|
||||
{
|
||||
wmInfo->GetAttribute(i, attrName, 32768, value, 32768);
|
||||
// if (!AttributeInStandardEditor(attrName.c_str()))
|
||||
attributeList.InsertItem(pos, (wchar_t *)attrName, 0);
|
||||
attributeList.SetItemText(pos, 1, (wchar_t *)value);
|
||||
pos++;
|
||||
}
|
||||
|
||||
attributeList.AutoColumnWidth(1);
|
||||
}
|
||||
|
||||
void FileInfoDialog::FillEditBoxes()
|
||||
{
|
||||
wchar_t temp[32768] = {0};
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMAuthor, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_ARTIST), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMTitle, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_TITLE), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMAlbumTitle, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUM), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMDescription, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_COMMENTS), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMGenre, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_GENRE), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMYear, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_YEAR), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMTrackNumber, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_TRACK), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMPublisher, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_PUBLISHER), temp);
|
||||
|
||||
wmInfo->GetAttribute(g_wszWMAlbumArtist, temp, 32768);
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUMARTIST), temp);
|
||||
}
|
||||
|
||||
void FileInfoDialog::Init(HWND _hwnd)
|
||||
{
|
||||
fileInfoHWND = _hwnd;
|
||||
|
||||
attributeList.setwnd(GetDlgItem(fileInfoHWND, IDC_METADATALIST));
|
||||
|
||||
attributeList.AddCol(WASABI_API_LNGSTRINGW(IDS_ATTRIBUTE), 150);
|
||||
attributeList.AddCol(WASABI_API_LNGSTRINGW(IDS_VALUE), 1);
|
||||
attributeList.AutoColumnWidth(1);
|
||||
|
||||
if (fileNameToShow)
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_FILENAME), fileNameToShow);
|
||||
else
|
||||
SetWindowText(GetDlgItem(fileInfoHWND, IDC_FILENAME), fileName);
|
||||
FillEditBoxes();
|
||||
FillAttributeList();
|
||||
|
||||
if (wmInfo->NonWritable())
|
||||
{
|
||||
EnableWindow(GetDlgItem(fileInfoHWND, IDC_APPLY), FALSE);
|
||||
EnableWindow(GetDlgItem(fileInfoHWND, IDC_REVERT), FALSE);
|
||||
EnableWindow(GetDlgItem(fileInfoHWND, IDOK), FALSE);
|
||||
SetDlgItemText(fileInfoHWND, IDCANCEL, WASABI_API_LNGSTRINGW(IDS_CLOSE));
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_TITLE), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_ARTIST), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUM), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_COMMENTS), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_GENRE), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_YEAR), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_TRACK), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_PUBLISHER), EM_SETREADONLY, TRUE, 0);
|
||||
SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUMARTIST), EM_SETREADONLY, TRUE, 0);
|
||||
}
|
||||
SetFocus(GetDlgItem(fileInfoHWND, IDCANCEL));
|
||||
SendMessage(fileInfoHWND, DM_SETDEFID, GetDlgCtrlID(GetDlgItem(fileInfoHWND, IDCANCEL)),0);
|
||||
}
|
||||
|
||||
void FileInfoDialog::Revert()
|
||||
{
|
||||
FillEditBoxes();
|
||||
FillAttributeList();
|
||||
}
|
||||
|
||||
BOOL FileInfoDialog::MetadataList_Notify(NMHDR *header)
|
||||
{
|
||||
switch (header->code)
|
||||
{
|
||||
|
||||
case LVN_ITEMCHANGED:
|
||||
{
|
||||
LPNMLISTVIEW lvNotif = (LPNMLISTVIEW)header;
|
||||
if ((lvNotif->uOldState & LVIS_SELECTED)
|
||||
&& !(lvNotif->uNewState & LVIS_SELECTED))
|
||||
{
|
||||
EnableWindow(GetDlgItem(fileInfoHWND, IDC_EDIT),0);
|
||||
EnableWindow(GetDlgItem(fileInfoHWND, IDC_DELETE), 0);
|
||||
}
|
||||
if (lvNotif->uNewState & LVIS_SELECTED)
|
||||
{
|
||||
if (lvNotif->iItem != -1)
|
||||
{
|
||||
EnableWindow(GetDlgItem(fileInfoHWND, IDC_EDIT),1);
|
||||
EnableWindow(GetDlgItem(fileInfoHWND, IDC_DELETE), 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FileInfoDialog::AttributeInStandardEditor(const wchar_t *attrName)
|
||||
{
|
||||
return (!wcscmp(attrName, g_wszWMTitle)
|
||||
||!wcscmp(attrName, g_wszWMAuthor)
|
||||
||!wcscmp(attrName, g_wszWMAlbumTitle)
|
||||
||!wcscmp(attrName, g_wszWMDescription)
|
||||
||!wcscmp(attrName, g_wszWMGenre)
|
||||
||!wcscmp(attrName, g_wszWMYear)
|
||||
||!wcscmp(attrName, g_wszWMTrackNumber)
|
||||
||!wcscmp(attrName, g_wszWMPublisher)
|
||||
|| !wcscmp(attrName, g_wszWMAlbumArtist));
|
||||
}
|
||||
|
||||
void FileInfoDialog::WriteEditBoxHelper(const wchar_t attrName[], DWORD IDC, wchar_t *&temp, int &size)
|
||||
{
|
||||
int thisSize = GetWindowTextLength(GetDlgItem(fileInfoHWND, IDC))+1;
|
||||
if (thisSize && thisSize>size)
|
||||
{
|
||||
if (temp)
|
||||
delete[] temp;
|
||||
temp = new wchar_t[thisSize];
|
||||
size=thisSize;
|
||||
|
||||
}
|
||||
|
||||
GetWindowText(GetDlgItem(fileInfoHWND, IDC), temp, size);
|
||||
wmInfo->SetAttribute(attrName, temp);
|
||||
}
|
||||
|
||||
void FileInfoDialog::WriteEditBoxes()
|
||||
{
|
||||
wchar_t *temp=0;
|
||||
int thisSize=0;
|
||||
int size=0;
|
||||
|
||||
WriteEditBoxHelper(g_wszWMTitle, IDC_EDIT_TITLE, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMAuthor, IDC_EDIT_ARTIST, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMAlbumTitle, IDC_EDIT_ALBUM, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMDescription, IDC_EDIT_COMMENTS, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMGenre, IDC_EDIT_GENRE, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMYear, IDC_EDIT_YEAR, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMTrackNumber, IDC_EDIT_TRACK, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMPublisher, IDC_EDIT_PUBLISHER, temp, size);
|
||||
WriteEditBoxHelper(g_wszWMAlbumArtist, IDC_EDIT_ALBUMARTIST, temp, size);
|
||||
}
|
||||
|
||||
void FileInfoDialog::WriteAttributeListA()
|
||||
{
|
||||
int attributeTextLength=0, valueTextLength=0;
|
||||
char *attribute=0, *value=0;
|
||||
size_t numAttrs = attributeList.GetCount();
|
||||
for (size_t i=0;i!=numAttrs;i++)
|
||||
{
|
||||
int textLength;
|
||||
textLength = attributeList.GetTextLength(i, 0);
|
||||
if (textLength>attributeTextLength)
|
||||
{
|
||||
if (attribute)
|
||||
delete[] attribute;
|
||||
attribute = new char[textLength];
|
||||
attributeTextLength=textLength;
|
||||
}
|
||||
attributeList.GetText(i, 0, attribute, attributeTextLength);
|
||||
|
||||
textLength = attributeList.GetTextLength(i, 0);
|
||||
if (textLength>valueTextLength)
|
||||
{
|
||||
if (value)
|
||||
delete[] value;
|
||||
value = new char[textLength];
|
||||
valueTextLength=textLength;
|
||||
}
|
||||
attributeList.GetText(i, 0, value, valueTextLength);
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfoDialog::WriteAttributeList()
|
||||
{
|
||||
int attributeTextLength=0, valueTextLength=0;
|
||||
wchar_t *attribute=0, *value=0;
|
||||
size_t numAttrs = attributeList.GetCount();
|
||||
for (size_t i=0;i!=numAttrs;i++)
|
||||
{
|
||||
int textLength;
|
||||
textLength = attributeList.GetTextLength(i, 0);
|
||||
if (textLength>attributeTextLength)
|
||||
{
|
||||
if (attribute)
|
||||
delete[] attribute;
|
||||
attribute = new wchar_t[textLength];
|
||||
attributeTextLength=textLength;
|
||||
}
|
||||
attributeList.GetText(i, 0, attribute, attributeTextLength);
|
||||
|
||||
textLength = attributeList.GetTextLength(i, 0);
|
||||
if (textLength>valueTextLength)
|
||||
{
|
||||
if (value)
|
||||
delete[] value;
|
||||
value = new wchar_t[textLength];
|
||||
valueTextLength=textLength;
|
||||
}
|
||||
attributeList.GetText(i, 0, value, valueTextLength);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileInfoDialog::Apply()
|
||||
{
|
||||
edited=true;
|
||||
if (!wmInfo->MakeWritable(fileName))
|
||||
{
|
||||
wchar_t title[64] = {0};
|
||||
if (activePlaylist.IsMe(fileName) && mod.playing)
|
||||
{
|
||||
// TODO: this is a race condition. we might have stopped in between the above if () and now...
|
||||
int outTime = mod.GetOutputTime();
|
||||
winamp.PressStop();
|
||||
wmInfo->MakeWritable(fileName);
|
||||
WriteEditBoxes();
|
||||
wmInfo->Flush();
|
||||
wmInfo->MakeReadOnly(fileName);
|
||||
mod.startAtMilliseconds=outTime;
|
||||
winamp.PressPlay();
|
||||
return true;
|
||||
}
|
||||
else if (!wmInfo->MakeReadOnly(fileName))
|
||||
MessageBox(fileInfoHWND, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST),
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
|
||||
else
|
||||
MessageBox(fileInfoHWND, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS),
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WriteEditBoxes();
|
||||
if (!wmInfo->Flush())
|
||||
{
|
||||
wchar_t* title = WASABI_API_LNGSTRINGW(IDS_SAVE_FAILED);
|
||||
MessageBox(NULL, title, title, MB_OK);
|
||||
}
|
||||
|
||||
wmInfo->MakeReadOnly(fileName);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL FileInfoDialog::OnOk()
|
||||
{
|
||||
if (Apply())
|
||||
CLOSEDIALOGBOX(fileInfoHWND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL FileInfoDialog::OnCancel()
|
||||
{
|
||||
CLOSEDIALOGBOX(fileInfoHWND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
INT_PTR WINAPI FileInfoDialog::FileInfoProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||
{
|
||||
FileInfoDialog *fileInfoDialog = (FileInfoDialog *)GetWindowLongPtr(wnd, GWLP_USERDATA);
|
||||
switch (msg)
|
||||
{
|
||||
case WM_NOTIFYFORMAT:
|
||||
return NFR_UNICODE;
|
||||
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR l = (LPNMHDR)lp;
|
||||
|
||||
if (l->hwndFrom == GetDlgItem(wnd, IDC_METADATALIST))
|
||||
return fileInfoDialog->MetadataList_Notify(l);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_INITDIALOG:
|
||||
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)lp);
|
||||
fileInfoDialog = (FileInfoDialog *)lp;
|
||||
fileInfoDialog->Init(wnd);
|
||||
return FALSE;
|
||||
case WM_DESTROY:
|
||||
if (fileInfoDialog)
|
||||
{
|
||||
//delete fileInfoDialog;
|
||||
//fileInfoDialog=0;
|
||||
SetWindowLongPtr(wnd, GWLP_USERDATA, 0);
|
||||
}
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wp))
|
||||
{
|
||||
case IDC_REVERT:
|
||||
fileInfoDialog->Revert();
|
||||
break;
|
||||
case IDCANCEL:
|
||||
return fileInfoDialog->OnCancel();
|
||||
break;
|
||||
case IDOK:
|
||||
return fileInfoDialog->OnOk();
|
||||
break;
|
||||
case IDC_APPLY:
|
||||
fileInfoDialog->Apply();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FileInfoDialog::FileInfoDialog(HINSTANCE _hInstance, HWND parent,const wchar_t *_fileName)
|
||||
: wmInfo(0),hInstance(_hInstance),fileName(0),fileNameToShow(0), edited(false)
|
||||
{
|
||||
if (activePlaylist.IsMe(_fileName))
|
||||
{
|
||||
fileName = _wcsdup(activePlaylist.GetFileName());
|
||||
fileNameToShow = _wcsdup(_fileName);
|
||||
}
|
||||
else
|
||||
fileName = _wcsdup(_fileName);
|
||||
|
||||
wmInfo = new WMInformation(fileName);
|
||||
CREATEDIALOGBOX(hInstance, MAKEINTRESOURCE(IDD_FILEINFO), parent, FileInfoProc, (LPARAM)this);
|
||||
}
|
||||
|
||||
FileInfoDialog::~FileInfoDialog()
|
||||
{
|
||||
delete wmInfo;
|
||||
wmInfo=0;
|
||||
free(fileName);
|
||||
free(fileNameToShow);
|
||||
}
|
||||
|
||||
bool FileInfoDialog::WasEdited()
|
||||
{
|
||||
return edited;
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef NULLSOFT_FILEINFODIALOGH
|
||||
#define NULLSOFT_FILEINFODIALOGH
|
||||
|
||||
#include "../nu/listview.h"
|
||||
#include "WMInformation.h"
|
||||
/* CUT> we're now using the unified file info dlg. I'll leave this commented out incase we want to do an advanced tab later on.
|
||||
class FileInfoDialog
|
||||
{
|
||||
public:
|
||||
FileInfoDialog(HINSTANCE _hInstance, HWND parent, const wchar_t *fileName);
|
||||
~FileInfoDialog();
|
||||
void Init(HWND _hwnd);
|
||||
static INT_PTR WINAPI FileInfoProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp);
|
||||
BOOL MetadataList_Notify(NMHDR *header);
|
||||
BOOL Edit_Notify(NMHDR *header);
|
||||
BOOL OnOk();
|
||||
BOOL OnCancel();
|
||||
bool WasEdited();
|
||||
private:
|
||||
void FillAttributeList();
|
||||
void WriteAttributeList();
|
||||
void WriteAttributeListA();
|
||||
void FillEditBoxes();
|
||||
void WriteEditBoxes();
|
||||
bool Apply();
|
||||
void Revert();
|
||||
void FileInfoDialog::WriteEditBoxHelper(const wchar_t attrName[], DWORD IDC, wchar_t *&temp, int &size);
|
||||
bool AttributeInStandardEditor(const wchar_t *attrName);
|
||||
HWND fileInfoHWND;
|
||||
WMInformation *wmInfo;
|
||||
W_ListView attributeList;
|
||||
HINSTANCE hInstance;
|
||||
|
||||
wchar_t *fileName;
|
||||
wchar_t *fileNameToShow;
|
||||
|
||||
bool edited;
|
||||
|
||||
};
|
||||
*/
|
||||
#endif
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "main.h"
|
||||
#include "FileTypes.h"
|
||||
#include "Config.h"
|
||||
#include "../nu/Config.h"
|
||||
#include "resource.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
FileTypes fileTypes;
|
||||
extern Nullsoft::Utility::Config wmConfig;
|
||||
|
||||
bool FileTypes::IsSupportedURL(const wchar_t *fn)
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(typeGuard);
|
||||
TypeList::iterator itr;
|
||||
for (itr=types.begin();itr!=types.end();itr++)
|
||||
{
|
||||
if (itr->isProtocol && !_wcsnicmp(fn, itr->type, wcslen(itr->type)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileTypes::IsDefault()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileTypes::LoadDefaults()
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(typeGuard);
|
||||
types.clear();
|
||||
types.push_back(FileType(L"WMA", WASABI_API_LNGSTRINGW(IDS_WMA_AUDIO_FILE), false, FileType::AUDIO));
|
||||
if (!config_no_video)
|
||||
{
|
||||
types.push_back(FileType(L"WMV", WASABI_API_LNGSTRINGW(IDS_WMA_VIDEO_FILE), false, FileType::VIDEO));
|
||||
types.push_back(FileType(L"ASF", WASABI_API_LNGSTRINGW(IDS_ASF_STREAM), false, FileType::VIDEO));
|
||||
}
|
||||
types.push_back(FileType(L"MMS://", WASABI_API_LNGSTRINGW(IDS_WINDOWS_MEDIA_STREAM), true, FileType::VIDEO));
|
||||
types.push_back(FileType(L"MMSU://", WASABI_API_LNGSTRINGW(IDS_WINDOWS_MEDIA_STREAM), true, FileType::VIDEO));
|
||||
types.push_back(FileType(L"MMST://", WASABI_API_LNGSTRINGW(IDS_WINDOWS_MEDIA_STREAM), true, FileType::VIDEO));
|
||||
}
|
||||
|
||||
void FileTypes::ReadConfig()
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(typeGuard);
|
||||
int numTypes = wmConfig.cfg_int(L"numtypes", -1);
|
||||
if (numTypes!=-1)
|
||||
{
|
||||
for (size_t i=0;i!=numTypes;i++)
|
||||
{
|
||||
wchar_t type[1024] = {0}, description[1024] = {0}, temp[64] = {0};
|
||||
|
||||
StringCchPrintf(temp, 64, L"type%u", i);
|
||||
wmConfig.cfg_str(temp).GetString(type, 1024);
|
||||
StringCchPrintf(temp, 64, L"description%u", i);
|
||||
wmConfig.cfg_str(temp).GetString(description, 1024);
|
||||
StringCchPrintf(temp, 64, L"protocol%u", i);
|
||||
bool protocol = !!wmConfig.cfg_int(temp, 0);
|
||||
StringCchPrintf(temp, 64, L"avtype%u", i);
|
||||
int avtype = !!wmConfig.cfg_int(temp, 0);
|
||||
if (!(config_no_video && !protocol && avtype==FileType::VIDEO)) // if we havn't explicity disabled video support
|
||||
types.push_back(FileType(type, description, protocol, avtype));
|
||||
}
|
||||
}
|
||||
else
|
||||
fileTypes.LoadDefaults();
|
||||
|
||||
ResetTypes();
|
||||
numTypes=types.size();
|
||||
for (size_t i=0;i!=numTypes;i++)
|
||||
{
|
||||
if (!types[i].isProtocol)
|
||||
AddType(AutoChar(types[i].type), AutoChar(types[i].description));
|
||||
}
|
||||
|
||||
CheckVideo();
|
||||
}
|
||||
|
||||
void FileTypes::SetTypes(TypeList &newTypes)
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(typeGuard);
|
||||
types=newTypes;
|
||||
ResetTypes();
|
||||
int numTypes=types.size();
|
||||
for (size_t i=0;i!=numTypes;i++)
|
||||
{
|
||||
if (!types[i].isProtocol)
|
||||
AddType(AutoChar(types[i].type), AutoChar(types[i].description));
|
||||
}
|
||||
|
||||
CheckVideo();
|
||||
}
|
||||
void FileTypes::SaveConfig()
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(typeGuard);
|
||||
wmConfig.cfg_int(L"numtypes", -1) = types.size();
|
||||
for (size_t i=0;i!=types.size();i++)
|
||||
{
|
||||
wchar_t temp[64] = {0};
|
||||
|
||||
StringCchPrintf(temp, 64, L"type%u", i);
|
||||
wmConfig.cfg_str(temp) = types[i].wtype;
|
||||
StringCchPrintf(temp, 64, L"description%u", i);
|
||||
wmConfig.cfg_str(temp) = types[i].description;
|
||||
StringCchPrintf(temp, 64, L"protocol%u", i);
|
||||
wmConfig.cfg_int(temp, 0) = types[i].isProtocol;
|
||||
StringCchPrintf(temp, 64, L"avtype%u", i);
|
||||
wmConfig.cfg_int(temp, 0) = types[i].avType;
|
||||
}
|
||||
}
|
||||
|
||||
void FileTypes::CheckVideo()
|
||||
{
|
||||
bool videoPresent=false;
|
||||
Nullsoft::Utility::AutoLock lock(typeGuard);
|
||||
//wmConfig.cfg_int(L"numtypes", -1) = types.size();
|
||||
for (size_t i=0;i!=types.size();i++)
|
||||
if (types[i].avType == FileType::VIDEO)
|
||||
videoPresent=true;
|
||||
|
||||
config_no_video = !videoPresent;
|
||||
}
|
||||
|
||||
void FileTypes::ResetTypes()
|
||||
{
|
||||
free(typesString);
|
||||
typesString=0;
|
||||
}
|
||||
|
||||
void FileTypes::AddType(const char *extension, const char *description)
|
||||
{
|
||||
size_t oldSize=0, size=0;
|
||||
|
||||
if (typesString)
|
||||
{
|
||||
char *temp=typesString;
|
||||
while (temp && *temp++)
|
||||
{
|
||||
size++;
|
||||
if (*temp == 0)
|
||||
{
|
||||
size++;
|
||||
temp++;
|
||||
}
|
||||
} ;
|
||||
oldSize=size;
|
||||
}
|
||||
size += lstrlenA(extension)+1;
|
||||
size += lstrlenA(description)+1;
|
||||
|
||||
char *newTypes = (char *)calloc(size+1, sizeof(char));
|
||||
if (newTypes)
|
||||
{
|
||||
memcpy(newTypes, typesString, oldSize);
|
||||
free(typesString);
|
||||
typesString=newTypes;
|
||||
newTypes += oldSize;
|
||||
size-=oldSize;
|
||||
StringCchCopyA(newTypes, size, extension);
|
||||
int extSize = lstrlenA(extension)+1;
|
||||
newTypes+=extSize;
|
||||
size-=extSize;
|
||||
StringCchCopyA(newTypes, size, description);
|
||||
newTypes+=lstrlenA(description)+1;
|
||||
*newTypes=0;
|
||||
plugin.FileExtensions = typesString;
|
||||
}
|
||||
}
|
||||
|
||||
int FileTypes::GetAVType(const wchar_t *ext)
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(typeGuard);
|
||||
for (size_t i=0;i!=types.size();i++)
|
||||
{
|
||||
if (!types[i].isProtocol && !lstrcmpi(types[i].type, ext))
|
||||
return types[i].avType;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifndef NULLSOFT_FILETYPESH
|
||||
#define NULLSOFT_FILETYPESH
|
||||
|
||||
#include <vector>
|
||||
#include "../nu/AutoLock.h"
|
||||
#include "AutoChar.h"
|
||||
|
||||
class FileType
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
AUDIO = 0,
|
||||
VIDEO = 1,
|
||||
};
|
||||
FileType() : wtype(0), type(0), description(0), isProtocol(false), avType(AUDIO)
|
||||
{
|
||||
}
|
||||
|
||||
FileType(wchar_t *_type, wchar_t *_description, bool _protocol, int _avType)
|
||||
{
|
||||
wtype=_wcsdup(_type);
|
||||
type = _wcsdup(_type);
|
||||
description = _wcsdup(_description);
|
||||
isProtocol = _protocol;
|
||||
avType = _avType;
|
||||
}
|
||||
~FileType()
|
||||
{
|
||||
free(type);
|
||||
free(wtype);
|
||||
free(description);
|
||||
}
|
||||
FileType(const FileType ©) :wtype(0), type(0), description(0)
|
||||
{
|
||||
operator =(copy);
|
||||
}
|
||||
void operator =(const FileType ©)
|
||||
{
|
||||
|
||||
if (copy.wtype)
|
||||
wtype=_wcsdup(copy.wtype);
|
||||
if (copy.type)
|
||||
type = _wcsdup(copy.type);
|
||||
if (copy.description)
|
||||
description = _wcsdup(copy.description);
|
||||
isProtocol = copy.isProtocol;
|
||||
avType = copy.avType;
|
||||
}
|
||||
wchar_t *type;
|
||||
wchar_t *wtype;
|
||||
wchar_t *description;
|
||||
bool isProtocol;
|
||||
int avType; // audio or video
|
||||
};
|
||||
|
||||
class FileTypes
|
||||
{
|
||||
public:
|
||||
FileTypes()
|
||||
: typesString(0),
|
||||
typeGuard(GUARDNAME("FileTypes::typeGuard"))
|
||||
{}
|
||||
~FileTypes()
|
||||
{
|
||||
free(typesString);
|
||||
typesString=0;
|
||||
}
|
||||
int GetAVType(const wchar_t *ext);
|
||||
bool IsSupportedURL(const wchar_t *fn);
|
||||
bool IsDefault();
|
||||
void LoadDefaults();
|
||||
void ReadConfig();
|
||||
void SaveConfig();
|
||||
void CheckVideo();
|
||||
|
||||
typedef std::vector<FileType> TypeList;
|
||||
void SetTypes(TypeList &newTypes);
|
||||
TypeList types;
|
||||
Nullsoft::Utility::LockGuard typeGuard;
|
||||
private:
|
||||
char *typesString;
|
||||
void ResetTypes();
|
||||
void AddType(const char *type, const char *description);
|
||||
};
|
||||
|
||||
extern FileTypes fileTypes;
|
||||
#endif
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "main.h"
|
||||
#include "GainLayer.h"
|
||||
#include <malloc.h>
|
||||
#include <math.h>
|
||||
#include <locale.h>
|
||||
#include "api.h"
|
||||
|
||||
static void FillFloat(float *floatBuf, void *samples, size_t bps, size_t numSamples, size_t numChannels, float preamp)
|
||||
{
|
||||
switch (bps)
|
||||
{
|
||||
case 8:
|
||||
{
|
||||
unsigned __int8 *samples8 = (unsigned __int8 *)samples;
|
||||
size_t totalSamples = numSamples * numChannels;
|
||||
for (size_t x = 0; x != totalSamples; x ++)
|
||||
{
|
||||
floatBuf[x] = (float)(samples8[x] - 128) * preamp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
{
|
||||
short *samples16 = (short *)samples;
|
||||
size_t totalSamples = numSamples * numChannels;
|
||||
for (size_t x = 0; x != totalSamples; x ++)
|
||||
{
|
||||
floatBuf[x] = (float)samples16[x] * preamp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
{
|
||||
unsigned __int8 *samples8 = (unsigned __int8 *)samples;
|
||||
size_t totalSamples = numSamples * numChannels;
|
||||
for (size_t x = 0; x != totalSamples; x ++)
|
||||
{
|
||||
long temp = (((long)samples8[0]) << 8);
|
||||
temp = temp | (((long)samples8[1]) << 16);
|
||||
temp = temp | (((long)samples8[2]) << 24);
|
||||
floatBuf[x] = (float)temp * preamp;
|
||||
samples8 += 3;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline static float fastclip(float x, const float a, const float b)
|
||||
{
|
||||
float x1 = (float)fabs (x - a);
|
||||
float x2 = (float)fabs (x - b);
|
||||
x = x1 + (a + b);
|
||||
x -= x2;
|
||||
x *= 0.5f;
|
||||
return (x);
|
||||
}
|
||||
|
||||
#define PA_CLIP_( val, min, max )\
|
||||
{ val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
|
||||
|
||||
void Float32_To_Int16_Clip(
|
||||
void *destinationBuffer, signed int destinationStride,
|
||||
void *sourceBuffer, signed int sourceStride,
|
||||
unsigned int count)
|
||||
{
|
||||
float *src = (float*)sourceBuffer;
|
||||
signed short *dest = (signed short*)destinationBuffer;
|
||||
|
||||
while ( count-- )
|
||||
{
|
||||
long samp = lrint(*src);
|
||||
|
||||
PA_CLIP_( samp, -0x8000, 0x7FFF );
|
||||
*dest = (signed short) samp;
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
}
|
||||
}
|
||||
inline static void clip(double &x, double a, double b)
|
||||
{
|
||||
double x1 = fabs (x - a);
|
||||
double x2 = fabs (x - b);
|
||||
x = x1 + (a + b);
|
||||
x -= x2;
|
||||
x *= 0.5;
|
||||
}
|
||||
|
||||
void Float32_To_Int24_Clip(
|
||||
void *destinationBuffer, signed int destinationStride,
|
||||
void *sourceBuffer, signed int sourceStride,
|
||||
unsigned int count)
|
||||
{
|
||||
float *src = (float*)sourceBuffer;
|
||||
unsigned char *dest = (unsigned char*)destinationBuffer;
|
||||
|
||||
while ( count-- )
|
||||
{
|
||||
/* convert to 32 bit and drop the low 8 bits */
|
||||
double scaled = *src;
|
||||
clip( scaled, -2147483648., 2147483647. );
|
||||
signed long temp = (signed long) scaled;
|
||||
|
||||
dest[0] = (unsigned char)(temp >> 8);
|
||||
dest[1] = (unsigned char)(temp >> 16);
|
||||
dest[2] = (unsigned char)(temp >> 24);
|
||||
src += sourceStride;
|
||||
dest += destinationStride * 3;
|
||||
}
|
||||
}
|
||||
|
||||
static void FillSamples(void *samples, float *floatBuf, size_t bps, size_t numSamples, size_t numChannels)
|
||||
{
|
||||
switch (bps)
|
||||
{
|
||||
case 16:
|
||||
Float32_To_Int16_Clip(samples, 1, floatBuf, 1, numSamples*numChannels);
|
||||
break;
|
||||
case 24:
|
||||
Float32_To_Int24_Clip(samples, 1, floatBuf, 1, numSamples*numChannels);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
float GetGain(WMInformation *info, bool allowDefault)
|
||||
{
|
||||
if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
|
||||
{
|
||||
float dB = 0, peak = 1.0f;
|
||||
wchar_t gain[64]=L"", peakVal[64]=L"";
|
||||
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
|
||||
{
|
||||
case 0: // track
|
||||
info->GetAttribute(L"replaygain_track_gain", gain, 64);
|
||||
if (!gain[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
info->GetAttribute(L"replaygain_album_gain", gain, 64);
|
||||
|
||||
info->GetAttribute(L"replaygain_track_peak", peakVal, 64);
|
||||
if (!peakVal[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
info->GetAttribute(L"replaygain_album_peak", peakVal, 64);
|
||||
|
||||
break;
|
||||
case 1:
|
||||
info->GetAttribute(L"replaygain_album_gain", gain, 64);
|
||||
if (!gain[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
info->GetAttribute(L"replaygain_track_gain", gain, 64);
|
||||
|
||||
info->GetAttribute(L"replaygain_album_peak", peakVal, 64);
|
||||
if (!peakVal[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
info->GetAttribute(L"replaygain_track_peak", peakVal, 64);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
|
||||
|
||||
if (gain[0])
|
||||
{
|
||||
if (gain[0] == L'+')
|
||||
dB = static_cast<float>(_wtof_l(&gain[1],C_locale));
|
||||
else
|
||||
dB = static_cast<float>(_wtof_l(gain,C_locale));
|
||||
}
|
||||
else if (allowDefault)
|
||||
{
|
||||
dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
|
||||
return powf(10.0f, dB / 20.0f);
|
||||
}
|
||||
|
||||
if (peakVal[0])
|
||||
{
|
||||
peak = static_cast<float>(_wtof_l(peakVal,C_locale));
|
||||
}
|
||||
|
||||
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
|
||||
{
|
||||
case 0: // apply gain
|
||||
return powf(10.0f, dB / 20.0f);
|
||||
case 1: // apply gain, but don't clip
|
||||
return min(powf(10.0f, dB / 20.0f), 1.0f / peak);
|
||||
case 2: // normalize
|
||||
return 1.0f / peak;
|
||||
case 3: // prevent clipping
|
||||
if (peak > 1.0f)
|
||||
return 1.0f / peak;
|
||||
else
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 1.0f; // no gain
|
||||
}
|
||||
|
||||
|
||||
void GainLayer::AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
size_t samples = audio->AudioBytesToSamples(sizeBytes);
|
||||
int channels = audio->Channels();
|
||||
if (floatSize < (samples * channels))
|
||||
{
|
||||
delete [] floatData;
|
||||
floatSize = samples * channels;
|
||||
floatData = new float[floatSize];
|
||||
}
|
||||
if (outSize < sizeBytes)
|
||||
{
|
||||
delete [] outData;
|
||||
outSize=sizeBytes;
|
||||
outData = (void *)new __int8[sizeBytes];
|
||||
}
|
||||
|
||||
FillFloat(floatData, _data, audio->BitSize(), samples , channels, replayGain);
|
||||
FillSamples(outData, floatData, audio->BitSize(), samples , channels);
|
||||
|
||||
WMHandler::AudioDataReceived(outData, sizeBytes, timestamp);
|
||||
}
|
||||
else
|
||||
WMHandler::AudioDataReceived(_data, sizeBytes, timestamp);
|
||||
}
|
||||
|
||||
void GainLayer::Opened()
|
||||
{
|
||||
enabled= (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false));
|
||||
if (enabled)
|
||||
replayGain = GetGain(info, true);
|
||||
WMHandler::Opened();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef NULLSOFT_GAIN_LAYER_H
|
||||
#define NULLSOFT_GAIN_LAYER_H
|
||||
|
||||
#include "WMHandler.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "WMInformation.h"
|
||||
class GainLayer : public WMHandler
|
||||
{
|
||||
public:
|
||||
GainLayer(AudioFormat *_audio, WMInformation *_info)
|
||||
: audio(_audio), info(_info), enabled(false), replayGain(1.0f),
|
||||
floatData(0),floatSize(0), outData(0), outSize(0)
|
||||
{}
|
||||
~GainLayer()
|
||||
{
|
||||
delete[]floatData;
|
||||
delete[]outData;
|
||||
}
|
||||
void AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp);
|
||||
void Opened();
|
||||
AudioFormat *audio;
|
||||
WMInformation *info;
|
||||
bool enabled;
|
||||
float replayGain;
|
||||
|
||||
float *floatData;
|
||||
size_t floatSize;
|
||||
|
||||
void *outData;
|
||||
size_t outSize;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef NULLSOFT_MAINH
|
||||
#define NULLSOFT_MAINH
|
||||
|
||||
#define WMDRM_VERSION L"3.95"
|
||||
|
||||
#include "WinampInterface.h"
|
||||
#include "../Winamp/in2.h"
|
||||
#include <windows.h>
|
||||
#include "WMDRMModule.h"
|
||||
#include <shlwapi.h>
|
||||
#include <wmsdk.h>
|
||||
#include "config.h"
|
||||
#include "WMHandler.h"
|
||||
#include "util.h"
|
||||
#include "FileTypes.h"
|
||||
#include "TagAlias.h"
|
||||
#include "WMInformation.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
#include "vidutils.h"
|
||||
#include "api.h"
|
||||
|
||||
extern WMInformation *setFileInfo;
|
||||
|
||||
struct IDispatch;
|
||||
extern IDispatch *winampExternal;
|
||||
|
||||
extern WinampInterface winamp;
|
||||
extern In_Module plugin;
|
||||
|
||||
extern WMDRM mod;
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define SHOW_CALLBACKS
|
||||
#endif
|
||||
|
||||
//#define SHOW_CALLBACKS
|
||||
|
||||
#ifdef SHOW_CALLBACKS
|
||||
#include <iostream>
|
||||
#define WMTCASE(sw) case sw: std::cerr << #sw << std::endl;
|
||||
#define WMT_SHOW_HR_CODE(hr) std::cerr << HRErrorCode(hr) << std::endl;
|
||||
#else
|
||||
#define WMTCASE(sw) case sw:
|
||||
#define WMT_SHOW_HR_CODE(hr)
|
||||
#endif
|
||||
|
||||
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
|
||||
static const GUID playbackConfigGroupGUID =
|
||||
{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "main.h"
|
||||
#include "MediaThread.h"
|
||||
#include "config.h"
|
||||
|
||||
MediaThread::MediaThread() : wait(INFINITE), thread(0)
|
||||
{
|
||||
killEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
stopped = CreateEvent(NULL, TRUE, TRUE, NULL);
|
||||
bufferFreed = CreateEvent(NULL, TRUE, TRUE, NULL);
|
||||
}
|
||||
|
||||
MediaThread::~MediaThread()
|
||||
{
|
||||
Kill();
|
||||
if (thread)
|
||||
CloseHandle(thread);
|
||||
}
|
||||
|
||||
VOID CALLBACK MediaThread_StartAPC(ULONG_PTR param)
|
||||
{
|
||||
reinterpret_cast<MediaThread *>(param)->StartAPC();
|
||||
}
|
||||
|
||||
void MediaThread::StartAPC()
|
||||
{
|
||||
wait=config_video_jitter;
|
||||
}
|
||||
|
||||
void MediaThread::StopAPC()
|
||||
{
|
||||
BufferList::iterator itr;
|
||||
for (itr = buffers.begin();itr != buffers.end();itr++)
|
||||
{
|
||||
(*itr)->buffer->Release();
|
||||
delete (*itr);
|
||||
}
|
||||
|
||||
buffers.clear();
|
||||
SetEvent(stopped);
|
||||
SetEvent(bufferFreed);
|
||||
wait=INFINITE;
|
||||
}
|
||||
|
||||
static VOID CALLBACK MediaThread_StopAPC(ULONG_PTR param)
|
||||
{
|
||||
reinterpret_cast<MediaThread *>(param)->StopAPC();
|
||||
}
|
||||
|
||||
void MediaThread::Stop()
|
||||
{
|
||||
ResetEvent(stopped);
|
||||
QueueUserAPC(MediaThread_StopAPC, thread, reinterpret_cast<ULONG_PTR>(this));
|
||||
WaitForSingleObject(stopped, INFINITE);
|
||||
}
|
||||
|
||||
void MediaThread::WaitForStop()
|
||||
{
|
||||
WaitForSingleObject(stopped, INFINITE);
|
||||
}
|
||||
|
||||
void MediaThread::SignalStop()
|
||||
{
|
||||
ResetEvent(stopped);
|
||||
QueueUserAPC(MediaThread_StopAPC, thread, reinterpret_cast<ULONG_PTR>(this));
|
||||
}
|
||||
|
||||
void MediaThread::Kill()
|
||||
{
|
||||
SetEvent(killEvent);
|
||||
WaitForSingleObject(stopped, INFINITE);
|
||||
}
|
||||
|
||||
void MediaThread::OrderedInsert(MediaBuffer *buffer)
|
||||
{
|
||||
BufferList::iterator itr;
|
||||
for (itr = buffers.begin();itr != buffers.end(); itr++)
|
||||
{
|
||||
if ((*itr)->timestamp > buffer->timestamp)
|
||||
{
|
||||
buffers.insert(itr, buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (itr == buffers.end())
|
||||
buffers.push_back(buffer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
VOID CALLBACK MediaThread_AddAPC(ULONG_PTR param)
|
||||
{
|
||||
MediaBufferAPC *apc = reinterpret_cast<MediaBufferAPC *>(param);
|
||||
apc->thread->AddAPC(apc->buffer);
|
||||
delete apc;
|
||||
}
|
||||
|
||||
bool MediaThread::AddBuffer(INSSBuffer *buff, QWORD ts, unsigned long flags, bool drmProtected)
|
||||
{
|
||||
if (WaitForSingleObject(bufferFreed, 0) == WAIT_TIMEOUT)
|
||||
return false;
|
||||
|
||||
buff->AddRef();
|
||||
MediaBuffer *buffer = new MediaBuffer(buff, ts, flags, drmProtected);
|
||||
MediaBufferAPC *apc = new MediaBufferAPC;
|
||||
apc->buffer = buffer;
|
||||
apc->thread = this;
|
||||
QueueUserAPC(MediaThread_AddAPC, thread, reinterpret_cast<ULONG_PTR>(apc));
|
||||
Sleep(config_video_jitter); // sleep for a bit to keep the thread from going nuts
|
||||
return true; // added
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef NULLSOFT_MEDIATHREADH
|
||||
#define NULLSOFT_MEDIATHREADH
|
||||
|
||||
#include <deque>
|
||||
#include <wmsdk.h>
|
||||
#include <vector>
|
||||
|
||||
VOID CALLBACK MediaThread_StartAPC(ULONG_PTR param);
|
||||
VOID CALLBACK MediaThread_AddAPC(ULONG_PTR param);
|
||||
struct MediaBuffer
|
||||
{
|
||||
MediaBuffer(INSSBuffer *b, QWORD t, unsigned long f, bool d) : buffer(b), timestamp(t), flags(f), drmProtected(d) {}
|
||||
INSSBuffer *buffer;
|
||||
QWORD timestamp;
|
||||
unsigned long flags;
|
||||
bool drmProtected;
|
||||
};
|
||||
struct MediaBufferAPC;
|
||||
|
||||
class MediaThread
|
||||
{
|
||||
public:
|
||||
MediaThread();
|
||||
~MediaThread();
|
||||
|
||||
bool AddBuffer(INSSBuffer *buff, QWORD ts, unsigned long flags, bool drmProtected);
|
||||
|
||||
void Stop();
|
||||
void SignalStop();
|
||||
void WaitForStop();
|
||||
void Kill();
|
||||
|
||||
public:
|
||||
void StopAPC();
|
||||
void StartAPC();
|
||||
|
||||
virtual void AddAPC(MediaBuffer *buffer)=0;
|
||||
|
||||
protected:
|
||||
void OrderedInsert(MediaBuffer *buffer);
|
||||
|
||||
protected:
|
||||
int wait;
|
||||
HANDLE thread;
|
||||
HANDLE killEvent, stopped, bufferFreed;
|
||||
|
||||
typedef std::vector<MediaBuffer*> BufferList;
|
||||
BufferList buffers;
|
||||
};
|
||||
|
||||
struct MediaBufferAPC
|
||||
{
|
||||
MediaBuffer *buffer;
|
||||
MediaThread *thread;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "main.h"
|
||||
#include "MetaTag.h"
|
||||
#include "FileTypes.h"
|
||||
#include "TagAlias.h"
|
||||
|
||||
ASFMetaTag::~ASFMetaTag()
|
||||
{
|
||||
delete info;
|
||||
}
|
||||
|
||||
const wchar_t *ASFMetaTag::getName()
|
||||
{
|
||||
return L"ASF Metadata";
|
||||
}
|
||||
|
||||
GUID ASFMetaTag::getGUID()
|
||||
{
|
||||
return getServiceGuid();
|
||||
}
|
||||
|
||||
int ASFMetaTag::getFlags()
|
||||
{
|
||||
return METATAG_FILE_INFO;
|
||||
}
|
||||
|
||||
int ASFMetaTag::isOurFile(const wchar_t *filename)
|
||||
{
|
||||
const wchar_t *ext = PathFindExtension(filename);
|
||||
return !lstrcmpiW(ext, L".WMA")
|
||||
|| !lstrcmpiW(ext, L".WMV")
|
||||
|| !lstrcmpiW(ext, L".ASF");
|
||||
|
||||
}
|
||||
|
||||
int ASFMetaTag::metaTag_open(const wchar_t *filename)
|
||||
{
|
||||
info = new WMInformation(filename);
|
||||
return METATAG_SUCCESS; // TODO: can we verify this?
|
||||
}
|
||||
|
||||
void ASFMetaTag::metaTag_close()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
const wchar_t *ASFMetaTag::enumSupportedTag(int n, int *datatype)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ASFMetaTag::getTagSize(const wchar_t *tag, size_t *sizeBytes)
|
||||
{
|
||||
size_t size;
|
||||
const wchar_t *tagName = GetAlias(tag);
|
||||
if (info && info->GetAttributeSize(tagName, size))
|
||||
{
|
||||
*sizeBytes = size;
|
||||
return METATAG_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return METATAG_UNKNOWN_TAG;
|
||||
}
|
||||
}
|
||||
|
||||
int ASFMetaTag::getMetaData(const wchar_t *tag, __int8 *buf, int buflenBytes, int datatype)
|
||||
{
|
||||
const wchar_t *tagName = GetAlias(tag);
|
||||
info->GetAttribute(tagName, reinterpret_cast<wchar_t *>(buf), buflenBytes / sizeof(wchar_t));
|
||||
return METATAG_SUCCESS;
|
||||
//return METATAG_FAILED;
|
||||
}
|
||||
|
||||
int ASFMetaTag::setMetaData(const wchar_t *tag, const __int8 *buf, int buflenBytes, int datatype )
|
||||
{
|
||||
return METATAG_FAILED;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CBCLASS
|
||||
#undef CBCLASS
|
||||
#endif
|
||||
|
||||
#define CBCLASS ASFMetaTag
|
||||
START_DISPATCH;
|
||||
CB(SVC_METATAG_GETNAME,getName)
|
||||
CB(SVC_METATAG_GETGUID,getGUID)
|
||||
CB(SVC_METATAG_GETFLAGS,getFlags)
|
||||
CB(SVC_METATAG_ISOURFILE,isOurFile)
|
||||
CB(SVC_METATAG_OPEN,metaTag_open)
|
||||
VCB(SVC_METATAG_CLOSE,metaTag_close)
|
||||
CB(SVC_METATAG_ENUMTAGS,enumSupportedTag)
|
||||
CB(SVC_METATAG_GETTAGSIZE,getTagSize)
|
||||
CB(SVC_METATAG_GETMETADATA,getMetaData)
|
||||
CB(SVC_METATAG_SETMETADATA,setMetaData)
|
||||
END_DISPATCH;
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef NULLSOFT_IN_WMVDRM_METATAG_H
|
||||
#define NULLSOFT_IN_WMVDRM_METATAG_H
|
||||
|
||||
#include "../Agave/Metadata/svc_metatag.h"
|
||||
#include "WMInformation.h"
|
||||
|
||||
// {8FF721E1-2FF4-4721-A7D5-60FDB32FEB1F}
|
||||
static const GUID asfMetaTagGUID =
|
||||
{ 0x8ff721e1, 0x2ff4, 0x4721, { 0xa7, 0xd5, 0x60, 0xfd, 0xb3, 0x2f, 0xeb, 0x1f } };
|
||||
|
||||
class ASFMetaTag : public svc_metaTag
|
||||
{
|
||||
public:
|
||||
static const GUID getServiceGuid() { return asfMetaTagGUID; }
|
||||
static const char *getServiceName() { return "ASF Metadata"; }
|
||||
public:
|
||||
ASFMetaTag() : info(0)
|
||||
{
|
||||
}
|
||||
~ASFMetaTag();
|
||||
|
||||
/* These methods are to be used by api_metadata */
|
||||
const wchar_t *getName(); // i.e. "ID3v2" or something
|
||||
GUID getGUID(); // this needs to be the same GUID that you use when registering your service factory
|
||||
int getFlags(); // how this service gets its info
|
||||
int isOurFile(const wchar_t *filename);
|
||||
int metaTag_open(const wchar_t *filename);
|
||||
void metaTag_close(); // self-destructs when this is called (you don't need to call serviceFactory->releaseInterface)
|
||||
|
||||
/* user API starts here */
|
||||
const wchar_t *enumSupportedTag(int n, int *datatype); // returns a list of understood tags. might not be complete (see note [1])
|
||||
int getTagSize(const wchar_t *tag, size_t *sizeBytes); // always gives you BYTES, not characters (be careful with your strings)
|
||||
int getMetaData(const wchar_t *tag, __int8 *buf, int buflenBytes, int datatype); // buflen is BYTES, not characters (be careful with your strings)
|
||||
int setMetaData(const wchar_t *tag, const __int8 *buf, int buflenBytes, int datatype);
|
||||
|
||||
private:
|
||||
WMInformation *info;
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "main.h"
|
||||
#include "MetaTag.h"
|
||||
#include "api.h"
|
||||
#include "MetaTagFactory.h"
|
||||
|
||||
FOURCC MetaTagFactory::GetServiceType()
|
||||
{
|
||||
return WaSvc::METATAG;
|
||||
}
|
||||
|
||||
const char *MetaTagFactory::GetServiceName()
|
||||
{
|
||||
return ASFMetaTag::getServiceName();
|
||||
}
|
||||
|
||||
GUID MetaTagFactory::GetGUID()
|
||||
{
|
||||
return ASFMetaTag::getServiceGuid();
|
||||
}
|
||||
|
||||
void *MetaTagFactory::GetInterface(int global_lock)
|
||||
{
|
||||
svc_metaTag *ifc= new ASFMetaTag;
|
||||
// if (global_lock)
|
||||
// plugin.service->service_lock(this, (void *)ifc);
|
||||
return ifc;
|
||||
}
|
||||
|
||||
int MetaTagFactory::SupportNonLockingInterface()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int MetaTagFactory::ReleaseInterface(void *ifc)
|
||||
{
|
||||
//plugin.service->service_unlock(ifc);
|
||||
svc_metaTag *metaTag = static_cast<svc_metaTag *>(ifc);
|
||||
ASFMetaTag *asfMetaTag = static_cast<ASFMetaTag *>(metaTag);
|
||||
delete asfMetaTag;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *MetaTagFactory::GetTestString()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int MetaTagFactory::ServiceNotify(int msg, int param1, int param2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CBCLASS
|
||||
#undef CBCLASS
|
||||
#endif
|
||||
|
||||
#define CBCLASS MetaTagFactory
|
||||
START_DISPATCH;
|
||||
CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
|
||||
CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
|
||||
CB(WASERVICEFACTORY_GETGUID, GetGUID)
|
||||
CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
|
||||
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
|
||||
CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
|
||||
CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
|
||||
CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
|
||||
END_DISPATCH;
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef NULLSOFT_IN_WMVDRM_METATAGFACTORY_H
|
||||
#define NULLSOFT_IN_WMVDRM_METATAGFACTORY_H
|
||||
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include <api/service/services.h>
|
||||
|
||||
class MetaTagFactory : public waServiceFactory
|
||||
{
|
||||
public:
|
||||
FOURCC GetServiceType();
|
||||
const char *GetServiceName();
|
||||
GUID GetGUID();
|
||||
void *GetInterface(int global_lock);
|
||||
int SupportNonLockingInterface();
|
||||
int ReleaseInterface(void *ifc);
|
||||
const char *GetTestString();
|
||||
int ServiceNotify(int msg, int param1, int param2);
|
||||
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
#ifndef NULLSOFT_OUTPUTSTREAMH
|
||||
#define NULLSOFT_OUTPUTSTREAMH
|
||||
|
||||
#include <wmsdk.h>
|
||||
#define NULLSOFT_INTERFACE_BEGIN(RIID, OBJ) void **&NULLSOFT_interfaceHolder = OBJ; REFIID NULLSOFT_IID = RIID;
|
||||
#define NULLSOFT_VALID_INTERFACE(a) if (NULLSOFT_IID == IID_ ## a) { *NULLSOFT_interfaceHolder = static_cast<a *>(this); return S_OK; }
|
||||
#define NULLSOFT_INTERFACE_END() *NULLSOFT_interfaceHolder = 0; return E_NOINTERFACE;
|
||||
|
||||
class OutputStream : public IWMOutputMediaProps
|
||||
{
|
||||
public:
|
||||
OutputStream(IWMMediaProps *props) : mediaType(0)
|
||||
{
|
||||
DWORD mediaTypeSize;
|
||||
props->GetMediaType(0, &mediaTypeSize);
|
||||
if (mediaTypeSize)
|
||||
{
|
||||
mediaType = (WM_MEDIA_TYPE *)new unsigned char[mediaTypeSize];
|
||||
props->GetMediaType(mediaType, &mediaTypeSize);
|
||||
}
|
||||
}
|
||||
|
||||
~OutputStream()
|
||||
{
|
||||
if (mediaType)
|
||||
{
|
||||
delete mediaType;
|
||||
mediaType = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GUID &GetSubType() const
|
||||
{
|
||||
return mediaType->subtype;
|
||||
}
|
||||
|
||||
WM_MEDIA_TYPE *mediaType;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
|
||||
{
|
||||
NULLSOFT_INTERFACE_BEGIN(riid, ppvObject)
|
||||
NULLSOFT_VALID_INTERFACE(IWMOutputMediaProps);
|
||||
NULLSOFT_VALID_INTERFACE(IWMMediaProps);
|
||||
NULLSOFT_INTERFACE_END()
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetType(GUID *pguidType)
|
||||
{
|
||||
if (!mediaType) return E_FAIL;
|
||||
*pguidType = mediaType->majortype;
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetMediaType(WM_MEDIA_TYPE *pType, DWORD *pcbType)
|
||||
{
|
||||
if (!mediaType) return E_FAIL;
|
||||
if (!pType)
|
||||
{
|
||||
if (!pcbType) return E_INVALIDARG;
|
||||
*pcbType = sizeof(WM_MEDIA_TYPE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*pcbType < sizeof(WM_MEDIA_TYPE)) ASF_E_BUFFERTOOSMALL;
|
||||
memcpy(pType, mediaType, sizeof(WM_MEDIA_TYPE));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetMediaType(WM_MEDIA_TYPE *pType)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetStreamGroupName(WCHAR *pwszName, WORD *pcchName)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetConnectionName(WCHAR *pwszName, WORD *pcchName)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
};
|
||||
#include <uuids.h>
|
||||
class VideoOutputStream : public OutputStream
|
||||
{
|
||||
public:
|
||||
|
||||
VideoOutputStream(IWMMediaProps *props) : OutputStream(props)
|
||||
{}
|
||||
|
||||
WMVIDEOINFOHEADER *VideoInfo() const
|
||||
{
|
||||
return (WMVIDEOINFOHEADER *)mediaType->pbFormat;
|
||||
}
|
||||
int SourceWidth() const
|
||||
{
|
||||
return VideoInfo()->rcSource.right - VideoInfo()->rcSource.left;
|
||||
}
|
||||
int DestinationWidth() const
|
||||
{
|
||||
return VideoInfo()->rcTarget.right - VideoInfo()->rcTarget.left;
|
||||
}
|
||||
|
||||
int DestinationHeight() const
|
||||
{
|
||||
return VideoInfo()->rcTarget.bottom - VideoInfo()->rcTarget.top;
|
||||
}
|
||||
|
||||
bool Flipped() const
|
||||
{
|
||||
BITMAPINFOHEADER &info = VideoInfo()->bmiHeader;
|
||||
if (info.biHeight < 0 || info.biCompression == 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
int bmiHeight()
|
||||
{
|
||||
return VideoInfo()->bmiHeader.biYPelsPerMeter;
|
||||
}
|
||||
int bmiWidth()
|
||||
{
|
||||
return VideoInfo()->bmiHeader.biXPelsPerMeter;
|
||||
}
|
||||
RGBQUAD *CreatePalette()
|
||||
{
|
||||
RGBQUAD *palette = (RGBQUAD *)calloc(1, 1024);
|
||||
BITMAPINFOHEADER &info = VideoInfo()->bmiHeader;
|
||||
memcpy(palette, (char *)(&info) + 40, info.biClrUsed * 4);
|
||||
return palette;
|
||||
}
|
||||
int FourCC() const
|
||||
{
|
||||
BITMAPINFOHEADER &info = VideoInfo()->bmiHeader;
|
||||
int fourcc = info.biCompression;
|
||||
if (fourcc == BI_RGB)
|
||||
{
|
||||
switch(info.biBitCount)
|
||||
{
|
||||
case 32:
|
||||
fourcc='23GR'; // RG32
|
||||
break;
|
||||
case 24:
|
||||
fourcc='42GR'; // RG24
|
||||
break;
|
||||
case 8:
|
||||
fourcc='8BGR'; // RGB8
|
||||
break;
|
||||
}
|
||||
} else if (fourcc == BI_BITFIELDS)
|
||||
fourcc = 0; // TODO: calc a CC that winamp likes
|
||||
return fourcc;
|
||||
}
|
||||
|
||||
bool IsVideo() const
|
||||
{
|
||||
return !!(mediaType->formattype == WMFORMAT_VideoInfo);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,167 @@
|
||||
#include "main.h"
|
||||
#include "config.h"
|
||||
#include "PlaylistHandler.h"
|
||||
#include "WPLLoader.h"
|
||||
#include "ASXLoader.h"
|
||||
#include <shlwapi.h>
|
||||
#include "resource.h"
|
||||
|
||||
void GetExtension(const wchar_t *filename, wchar_t *ext, size_t extCch)
|
||||
{
|
||||
const wchar_t *s = PathFindExtensionW(filename);
|
||||
if (!PathIsURLW(filename)
|
||||
|| (!wcsstr(s, L"?") && !wcsstr(s, L"&") && !wcsstr(s, L"=") && *s))
|
||||
{
|
||||
lstrcpynW(ext, s, extCch);
|
||||
return ;
|
||||
}
|
||||
// s is not a terribly good extension, let's try again
|
||||
{
|
||||
wchar_t *copy = _wcsdup(filename);
|
||||
s = L"";
|
||||
again:
|
||||
{
|
||||
wchar_t *p = StrRChrW(copy,NULL, L'?');
|
||||
if (p)
|
||||
{
|
||||
*p = 0;
|
||||
s = PathFindExtensionW(copy);
|
||||
if (!*s) goto again;
|
||||
}
|
||||
lstrcpynW(ext, s, extCch);
|
||||
}
|
||||
free(copy);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------- WPL --------------------------------------- */
|
||||
const wchar_t *WPLHandler::enumExtensions(size_t n)
|
||||
{
|
||||
switch(n)
|
||||
{
|
||||
case 0:
|
||||
return L"WPL";
|
||||
/*case 1:
|
||||
return L"ZPL";*/
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int WPLHandler::SupportedFilename(const wchar_t *filename)
|
||||
{
|
||||
wchar_t ext[16] = {0};
|
||||
GetExtension(filename, ext, 16);
|
||||
|
||||
if (!lstrcmpiW(ext, L".WPL") || !lstrcmpiW(ext, L".ZPL"))
|
||||
return SVC_PLAYLISTHANDLER_SUCCESS;
|
||||
|
||||
// TODO: open file and sniff it for file signature
|
||||
return SVC_PLAYLISTHANDLER_FAILED;
|
||||
}
|
||||
|
||||
ifc_playlistloader *WPLHandler::CreateLoader(const wchar_t *filename)
|
||||
{
|
||||
return new WPLLoader;
|
||||
}
|
||||
|
||||
void WPLHandler::ReleaseLoader(ifc_playlistloader *loader)
|
||||
{
|
||||
WPLLoader *pls;
|
||||
|
||||
pls = static_cast<WPLLoader *>(loader);
|
||||
delete pls;
|
||||
}
|
||||
|
||||
const wchar_t *WPLHandler::GetName()
|
||||
{
|
||||
static wchar_t wplpl[64];
|
||||
// no point re-loading this all of the time since it won't change once we've been loaded
|
||||
return (!wplpl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_WINDOWS_MEDIA_PLAYLIST,wplpl,64):wplpl);
|
||||
}
|
||||
|
||||
/* --- ASX --- */
|
||||
const wchar_t *ASXHandler::enumExtensions(size_t n)
|
||||
{
|
||||
|
||||
switch(n)
|
||||
{
|
||||
case 0:
|
||||
return L"ASX";
|
||||
case 1:
|
||||
if (config_extra_asx_extensions)
|
||||
return L"WAX";
|
||||
else
|
||||
return 0;
|
||||
case 2:
|
||||
if (config_extra_asx_extensions)
|
||||
return L"WMX";
|
||||
else
|
||||
return 0;
|
||||
case 3:
|
||||
if (config_extra_asx_extensions)
|
||||
return L"WVX";
|
||||
else
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ASXHandler::SupportedFilename(const wchar_t *filename)
|
||||
{
|
||||
wchar_t ext[16] = {0};
|
||||
GetExtension(filename, ext, 16);
|
||||
|
||||
if (!lstrcmpiW(ext, L".ASX"))
|
||||
return SVC_PLAYLISTHANDLER_SUCCESS;
|
||||
if (!lstrcmpiW(ext, L".WAX"))
|
||||
return SVC_PLAYLISTHANDLER_SUCCESS;
|
||||
if (!lstrcmpiW(ext, L".WMX"))
|
||||
return SVC_PLAYLISTHANDLER_SUCCESS;
|
||||
if (!lstrcmpiW(ext, L".WVX"))
|
||||
return SVC_PLAYLISTHANDLER_SUCCESS;
|
||||
|
||||
// TODO: open file and sniff it for file signature
|
||||
return SVC_PLAYLISTHANDLER_FAILED;
|
||||
}
|
||||
|
||||
ifc_playlistloader *ASXHandler::CreateLoader(const wchar_t *filename)
|
||||
{
|
||||
return new ASXLoader;
|
||||
}
|
||||
|
||||
void ASXHandler::ReleaseLoader(ifc_playlistloader *loader)
|
||||
{
|
||||
ASXLoader *pls;
|
||||
|
||||
pls = static_cast<ASXLoader *>(loader);
|
||||
delete pls;
|
||||
}
|
||||
|
||||
const wchar_t *ASXHandler::GetName()
|
||||
{
|
||||
static wchar_t asxpl[64];
|
||||
// no point re-loading this all of the time since it won't change once we've been loaded
|
||||
return (!asxpl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_ASX_PLAYLIST,asxpl,64):asxpl);
|
||||
}
|
||||
|
||||
#define CBCLASS WPLHandler
|
||||
START_DISPATCH;
|
||||
CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, enumExtensions)
|
||||
CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename)
|
||||
CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader)
|
||||
VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader)
|
||||
CB(SVC_PLAYLISTHANDLER_GETNAME, GetName)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
#define CBCLASS ASXHandler
|
||||
START_DISPATCH;
|
||||
CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, enumExtensions)
|
||||
CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename)
|
||||
CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader)
|
||||
VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader)
|
||||
CB(SVC_PLAYLISTHANDLER_GETNAME, GetName)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef NULLSOFT_PLAYLISTS_HANDLER_H
|
||||
#define NULLSOFT_PLAYLISTS_HANDLER_H
|
||||
|
||||
#include "../playlist/svc_playlisthandler.h"
|
||||
#include <bfc/platform/types.h>
|
||||
|
||||
#define DECLARE_HANDLER(className) class className ## Handler : public svc_playlisthandler {\
|
||||
public:\
|
||||
const wchar_t *enumExtensions(size_t n); \
|
||||
int SupportedFilename(const wchar_t *filename); \
|
||||
ifc_playlistloader *CreateLoader(const wchar_t *filename);\
|
||||
void ReleaseLoader(ifc_playlistloader *loader);\
|
||||
const wchar_t *GetName(); \
|
||||
protected: RECVS_DISPATCH;}
|
||||
|
||||
DECLARE_HANDLER(WPL);
|
||||
DECLARE_HANDLER(ASX);
|
||||
|
||||
// {DC13A85D-7C61-4462-8CB4-9EBBBD86FA7C}
|
||||
static const GUID wplHandlerGUID =
|
||||
{ 0xdc13a85d, 0x7c61, 0x4462, { 0x8c, 0xb4, 0x9e, 0xbb, 0xbd, 0x86, 0xfa, 0x7c } };
|
||||
// {8909A743-6F00-43ef-B3F6-365000347DE3}
|
||||
|
||||
static const GUID asxHandlerGUID =
|
||||
{ 0x8909a743, 0x6f00, 0x43ef, { 0xb3, 0xf6, 0x36, 0x50, 0x0, 0x34, 0x7d, 0xe3 } };
|
||||
// {6F62CBB8-7E1F-43eb-B3F6-01C2601029A3}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
#include "main.h"
|
||||
#include "RawReader.h"
|
||||
#include "FileTypes.h"
|
||||
#include <shlwapi.h>
|
||||
|
||||
#define NS_E_FILE_IS_CORRUPTED _HRESULT_TYPEDEF_(0xC00D080DL)
|
||||
|
||||
bool IsMyExtension(const wchar_t *filename)
|
||||
{
|
||||
const wchar_t *ext = PathFindExtension(filename);
|
||||
if (ext && *ext)
|
||||
{
|
||||
ext++;
|
||||
return fileTypes.GetAVType(ext) != -1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int RawMediaReaderService::CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **out_reader)
|
||||
{
|
||||
if (IsMyExtension(filename))
|
||||
{
|
||||
IWMSyncReader *reader = 0;
|
||||
if (!SUCCEEDED(WMCreateSyncReader(NULL, 0, &reader)))
|
||||
{
|
||||
return NErr_FailedCreate;
|
||||
}
|
||||
|
||||
if (FAILED(reader->Open(filename)))
|
||||
{
|
||||
reader->Release();
|
||||
reader = 0;
|
||||
return NErr_FileNotFound; // TODO: check HRESULT
|
||||
}
|
||||
|
||||
RawMediaReader *raw_reader = new RawMediaReader();
|
||||
if (!raw_reader)
|
||||
{
|
||||
reader->Close();
|
||||
reader->Release();
|
||||
return NErr_OutOfMemory;
|
||||
}
|
||||
|
||||
int ret = raw_reader->Initialize(reader);
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
delete raw_reader;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*out_reader = raw_reader;
|
||||
return NErr_Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NErr_False;
|
||||
}
|
||||
}
|
||||
|
||||
#define CBCLASS RawMediaReaderService
|
||||
START_DISPATCH;
|
||||
CB(CREATERAWMEDIAREADER, CreateRawMediaReader);
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
RawMediaReader::RawMediaReader()
|
||||
{
|
||||
reader=0;
|
||||
stream_num=0;
|
||||
buffer_used=0;
|
||||
end_of_file=false;
|
||||
length=0;
|
||||
buffer=0;
|
||||
next_output=0;
|
||||
}
|
||||
|
||||
RawMediaReader::~RawMediaReader()
|
||||
{
|
||||
if (reader)
|
||||
{
|
||||
reader->Close();
|
||||
reader->Release();
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
buffer->Release();
|
||||
}
|
||||
}
|
||||
|
||||
int RawMediaReader::Initialize(IWMSyncReader *reader)
|
||||
{
|
||||
this->reader=reader;
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
int RawMediaReader::Read(void *out_buffer, size_t buffer_size, size_t *bytes_read)
|
||||
{
|
||||
/* we don't care about these, but the API does not allows NULL */
|
||||
QWORD sample_time = 0, duration = 0;
|
||||
size_t bytesCopied = 0;
|
||||
uint8_t *dest = (uint8_t *)out_buffer;
|
||||
for (;;)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
BYTE *bufferBytes = 0;
|
||||
DWORD bufferTotal = 0;
|
||||
buffer->GetBufferAndLength(&bufferBytes, &bufferTotal);
|
||||
|
||||
if (buffer_used < bufferTotal)
|
||||
{
|
||||
size_t toCopy = min(bufferTotal - buffer_used, buffer_size);
|
||||
memcpy(dest, bufferBytes + buffer_used, toCopy);
|
||||
buffer_used += toCopy;
|
||||
buffer_size -= toCopy;
|
||||
dest += toCopy;
|
||||
bytesCopied += toCopy;
|
||||
|
||||
if (buffer_used == bufferTotal)
|
||||
{
|
||||
buffer_used = 0;
|
||||
buffer->Release();
|
||||
buffer = 0;
|
||||
}
|
||||
}
|
||||
if (buffer_size == 0)
|
||||
{
|
||||
*bytes_read = bytesCopied;
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream_num == 0)
|
||||
{
|
||||
DWORD outputs = 0;
|
||||
HRESULT hr=reader->GetOutputCount(&outputs);
|
||||
if (FAILED(hr))
|
||||
return NErr_Error;
|
||||
if (next_output >= outputs)
|
||||
return NErr_EndOfFile;
|
||||
hr=reader->GetStreamNumberForOutput(next_output, &stream_num);
|
||||
if (FAILED(hr))
|
||||
return NErr_Error;
|
||||
hr=reader->SetReadStreamSamples(stream_num, TRUE);
|
||||
if (FAILED(hr))
|
||||
return NErr_Error;
|
||||
next_output++;
|
||||
}
|
||||
|
||||
DWORD flags = 0;
|
||||
HRESULT r = reader->GetNextSample(stream_num, &buffer, &sample_time, &duration, &flags, 0, 0);
|
||||
if (r == NS_E_NO_MORE_SAMPLES || r == NS_E_FILE_IS_CORRUPTED)
|
||||
{
|
||||
stream_num=0;
|
||||
}
|
||||
}
|
||||
|
||||
return NErr_Error;
|
||||
}
|
||||
|
||||
size_t RawMediaReader::Release()
|
||||
{
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CBCLASS RawMediaReader
|
||||
START_DISPATCH;
|
||||
CB(RELEASE, Release);
|
||||
CB(RAW_READ, Read);
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "../Agave/DecodeFile/svc_raw_media_reader.h"
|
||||
#include "../Agave/DecodeFile/ifc_raw_media_reader.h"
|
||||
#include <mmreg.h>
|
||||
#include <wmsdk.h>
|
||||
|
||||
// {9AF5FD89-DC41-4F2A-A156-8D1399FDE57B}
|
||||
static const GUID wm_raw_reader_guid =
|
||||
{ 0x9af5fd89, 0xdc41, 0x4f2a, { 0xa1, 0x56, 0x8d, 0x13, 0x99, 0xfd, 0xe5, 0x7b } };
|
||||
|
||||
class RawMediaReaderService : public svc_raw_media_reader
|
||||
{
|
||||
public:
|
||||
static const char *getServiceName() { return "Windows Media Audio Raw Reader"; }
|
||||
static GUID getServiceGuid() { return wm_raw_reader_guid; }
|
||||
int CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **reader);
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
class RawMediaReader : public ifc_raw_media_reader
|
||||
{
|
||||
public:
|
||||
RawMediaReader();
|
||||
~RawMediaReader();
|
||||
int Initialize(IWMSyncReader *reader);
|
||||
int Read(void *buffer, size_t buffer_size, size_t *bytes_read);
|
||||
size_t Release();
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
private:
|
||||
IWMSyncReader *reader;
|
||||
WORD stream_num;
|
||||
|
||||
INSSBuffer *buffer;
|
||||
size_t buffer_used;
|
||||
bool end_of_file;
|
||||
QWORD length;
|
||||
DWORD next_output;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef NULLSOFT_REMAININGH
|
||||
#define NULLSOFT_REMAININGH
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
/* this class is used to store leftover samples */
|
||||
|
||||
class Remaining
|
||||
{
|
||||
public:
|
||||
Remaining()
|
||||
: store(0), size(0), used(0)
|
||||
{}
|
||||
|
||||
void Allocate(unsigned long _size)
|
||||
{
|
||||
assert(_size);
|
||||
used=0;
|
||||
size=_size;
|
||||
if (store)
|
||||
delete [] store;
|
||||
store = new unsigned char [size];
|
||||
}
|
||||
|
||||
/* Saves the incoming data and updates the pointer positions */
|
||||
template <class storage_t>
|
||||
void UpdatingWrite(storage_t *&data, unsigned long &bytes)
|
||||
{
|
||||
unsigned long bytesToWrite = min(bytes, SizeRemaining());
|
||||
Write(data, bytesToWrite);
|
||||
assert(bytesToWrite);
|
||||
data = (storage_t *)((char *)data + bytesToWrite);
|
||||
bytes -= bytesToWrite;
|
||||
}
|
||||
|
||||
void Write(void *data, unsigned long bytes)
|
||||
{
|
||||
unsigned char *copy = (unsigned char *)store;
|
||||
copy+=used;
|
||||
memcpy(copy, data, bytes);
|
||||
used+=bytes;
|
||||
}
|
||||
|
||||
unsigned long SizeRemaining()
|
||||
{
|
||||
return size-used;
|
||||
}
|
||||
|
||||
bool Empty()
|
||||
{
|
||||
return !used;
|
||||
}
|
||||
bool Full()
|
||||
{
|
||||
return size == used;
|
||||
}
|
||||
void *GetData()
|
||||
{
|
||||
return (void *)store;
|
||||
}
|
||||
|
||||
void Flush()
|
||||
{
|
||||
used=0;
|
||||
}
|
||||
unsigned char *store;
|
||||
long size, used;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,376 @@
|
||||
#include "SeekLayer.h"
|
||||
#include "Main.h"
|
||||
#include "output/AudioOut.h"
|
||||
#include "util.h"
|
||||
#include <assert.h>
|
||||
using namespace Nullsoft::Utility;
|
||||
|
||||
struct OpenThreadData
|
||||
{
|
||||
IWMReaderCallback *callback;
|
||||
wchar_t *url;
|
||||
IWMReader *reader;
|
||||
|
||||
void open()
|
||||
{
|
||||
reader->Open(url, callback, 0);
|
||||
}
|
||||
|
||||
OpenThreadData(IWMReader *_reader, const wchar_t *_url, IWMReaderCallback *_callback)
|
||||
{
|
||||
reader = _reader;
|
||||
reader->AddRef();
|
||||
callback = _callback;
|
||||
callback->AddRef();
|
||||
|
||||
url = _wcsdup(_url);
|
||||
}
|
||||
|
||||
~OpenThreadData()
|
||||
{
|
||||
free(url);
|
||||
reader->Release();
|
||||
callback->Release();
|
||||
}
|
||||
};
|
||||
DWORD WINAPI OpenThread(void *param)
|
||||
{
|
||||
OpenThreadData *data = (OpenThreadData *)param;
|
||||
data->open();
|
||||
delete data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NEW_SEEK
|
||||
SeekLayer::SeekLayer(IWMReader *_reader, ClockLayer *_clock)
|
||||
: seekPos(0), reader(_reader), playState(PLAYSTATE_CLOSED), metadata(NULL), clock(_clock),
|
||||
needPause(false), paused(false), needStop(false),
|
||||
seekGuard(GUARDNAME("Seek Guard")),
|
||||
oldState_buffer(PLAYSTATE_NONE)
|
||||
{
|
||||
reader->AddRef();
|
||||
reader->QueryInterface(&reader2);
|
||||
}
|
||||
|
||||
void SeekLayer::DoStop()
|
||||
{
|
||||
if (paused)
|
||||
reader->Resume();
|
||||
|
||||
reader->Stop();
|
||||
First().Stopping();
|
||||
First().Kill();
|
||||
if (paused)
|
||||
{
|
||||
paused = false;
|
||||
out->Pause(0);
|
||||
}
|
||||
needStop = false;
|
||||
}
|
||||
|
||||
void SeekLayer::SeekTo(long position)
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("SeekTo"));
|
||||
if (paused)
|
||||
{
|
||||
reader->Resume();
|
||||
}
|
||||
First().Stopping();
|
||||
First().Kill();
|
||||
seekPos = position;
|
||||
clock->SetLastOutputTime(position);
|
||||
clock->SetStartTimeMilliseconds(position);
|
||||
out->Flush(position);
|
||||
QWORD qSeekPos = position;
|
||||
qSeekPos *= 10000;
|
||||
reader->Start(qSeekPos, 0, 1.0f, NULL);
|
||||
if (paused)
|
||||
{
|
||||
reader->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void SeekLayer::Pause()
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("Pause"));
|
||||
if (playState == PLAYSTATE_STARTED)
|
||||
{
|
||||
paused = true;
|
||||
reader->Pause();
|
||||
out->Pause(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
needPause = true;
|
||||
}
|
||||
}
|
||||
|
||||
int SeekLayer::Open(const wchar_t *filename, IWMReaderCallback *callback)
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("Open"));
|
||||
assert(playState == PLAYSTATE_CLOSED);
|
||||
needStop = false;
|
||||
playState = PLAYSTATE_OPENING;
|
||||
DWORD dummyId;
|
||||
CreateThread(NULL, 0, OpenThread, new OpenThreadData(reader, filename, callback), 0, &dummyId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SeekLayer::Stop()
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("Stop"));
|
||||
needStop = true;
|
||||
|
||||
switch (playState)
|
||||
{
|
||||
case PLAYSTATE_BUFFERING:
|
||||
// needStop=false;
|
||||
reader2->StopBuffering();
|
||||
// reader->Stop();
|
||||
break;
|
||||
|
||||
case PLAYSTATE_OPENING:
|
||||
// wait for it to open (or connect) and then we'll kill it there
|
||||
break;
|
||||
|
||||
case PLAYSTATE_NONE:
|
||||
case PLAYSTATE_STOPPED:
|
||||
if (FAILED(reader->Close())) // reader->Close() is sometimes synchronous, and sometimes not valid here
|
||||
{
|
||||
playState = PLAYSTATE_CLOSED;
|
||||
return ;
|
||||
}
|
||||
break;
|
||||
|
||||
case PLAYSTATE_OPENED:
|
||||
case PLAYSTATE_STARTED:
|
||||
reader->Stop();
|
||||
First().Stopping();
|
||||
First().Kill();
|
||||
break;
|
||||
|
||||
case PLAYSTATE_CLOSED:
|
||||
needStop = false;
|
||||
break;
|
||||
/*
|
||||
case PLAYSTATE_BUFFERING:
|
||||
reader2->StopBuffering();
|
||||
reader->Stop();
|
||||
break;*/
|
||||
|
||||
case PLAYSTATE_SEEK:
|
||||
break;
|
||||
}
|
||||
|
||||
while (playState != PLAYSTATE_CLOSED)
|
||||
{
|
||||
lock.ManualUnlock();
|
||||
Sleep(55);
|
||||
lock.ManualLock(MANUALLOCKNAME("[Manual Lock]Stop"));
|
||||
}
|
||||
needStop = false;
|
||||
}
|
||||
|
||||
void SeekLayer::Unpause()
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("Unpause"));
|
||||
if (playState == PLAYSTATE_STARTED)
|
||||
{
|
||||
paused = false;
|
||||
out->Pause(0);
|
||||
reader->Resume();
|
||||
clock->Clock();
|
||||
}
|
||||
else
|
||||
{
|
||||
needPause = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SeekLayer::Opened()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("SeekLayer::Opened"));
|
||||
if (needStop)
|
||||
{
|
||||
playState = PLAYSTATE_OPENED;
|
||||
lock.ManualUnlock();
|
||||
reader->Close();
|
||||
lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Opened"));
|
||||
return ;
|
||||
}
|
||||
|
||||
playState = PLAYSTATE_OPENED;
|
||||
}
|
||||
WMHandler::Opened();
|
||||
}
|
||||
|
||||
void SeekLayer::Stopped()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("Stopped"));
|
||||
if (needStop)
|
||||
{
|
||||
playState = PLAYSTATE_STOPPED;
|
||||
lock.ManualUnlock();
|
||||
reader->Close();
|
||||
lock.ManualLock(MANUALLOCKNAME("Stopped"));
|
||||
}
|
||||
else
|
||||
{
|
||||
playState = PLAYSTATE_STOPPED;
|
||||
}
|
||||
|
||||
WMHandler::Stopped();
|
||||
}
|
||||
}
|
||||
|
||||
void SeekLayer::Started()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("Started"));
|
||||
playState = PLAYSTATE_STARTED;
|
||||
|
||||
if (needStop)
|
||||
{
|
||||
reader->Stop();
|
||||
First().Stopping();
|
||||
First().Kill();
|
||||
return ;
|
||||
}
|
||||
else if (needPause)
|
||||
{
|
||||
Pause();
|
||||
needPause = false;
|
||||
}
|
||||
}
|
||||
WMHandler::Started();
|
||||
}
|
||||
|
||||
void SeekLayer::Closed()
|
||||
{
|
||||
playState = PLAYSTATE_CLOSED;
|
||||
paused = false;
|
||||
needPause = false;
|
||||
needStop = false;
|
||||
seekPos = 0;
|
||||
|
||||
WMHandler::Closed();
|
||||
}
|
||||
|
||||
void SeekLayer::BufferingStarted()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("BufferingStarted"));
|
||||
if (playState == PLAYSTATE_OPENED)
|
||||
oldState_buffer = PLAYSTATE_NONE;
|
||||
else
|
||||
oldState_buffer = playState;
|
||||
if (playState != PLAYSTATE_STARTED)
|
||||
playState = PLAYSTATE_BUFFERING;
|
||||
if (needStop)
|
||||
reader2->StopBuffering();
|
||||
|
||||
}
|
||||
WMHandler::BufferingStarted();
|
||||
}
|
||||
|
||||
|
||||
void SeekLayer::BufferingStopped()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("BufferingStopped"));
|
||||
if (needStop)
|
||||
reader->Stop();
|
||||
playState = oldState_buffer;
|
||||
}
|
||||
WMHandler::BufferingStopped();
|
||||
}
|
||||
|
||||
|
||||
void SeekLayer::EndOfFile()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("EndOfFile"));
|
||||
if (needStop)
|
||||
return ;
|
||||
}
|
||||
WMHandler::EndOfFile();
|
||||
|
||||
}
|
||||
|
||||
void SeekLayer::Connecting()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("SeekLayer::Connecting"));
|
||||
if (needStop)
|
||||
{
|
||||
playState = PLAYSTATE_NONE;
|
||||
lock.ManualUnlock();
|
||||
reader->Close();
|
||||
lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Connecting"));
|
||||
return ;
|
||||
}
|
||||
playState = PLAYSTATE_NONE;
|
||||
}
|
||||
WMHandler::Connecting();
|
||||
}
|
||||
|
||||
|
||||
void SeekLayer::Locating()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("SeekLayer::Locating"));
|
||||
if (needStop)
|
||||
{
|
||||
playState = PLAYSTATE_NONE;
|
||||
lock.ManualUnlock();
|
||||
reader->Close();
|
||||
lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Locating"));
|
||||
return ;
|
||||
}
|
||||
playState = PLAYSTATE_NONE;
|
||||
}
|
||||
WMHandler::Locating();
|
||||
}
|
||||
|
||||
|
||||
void SeekLayer::OpenCalled()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("SeekLayer::OpenCalled"));
|
||||
if (needStop)
|
||||
{
|
||||
playState = PLAYSTATE_NONE;
|
||||
lock.ManualUnlock();
|
||||
reader->Close();
|
||||
lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::OpenCalled"));
|
||||
return ;
|
||||
}
|
||||
|
||||
playState = PLAYSTATE_NONE;
|
||||
}
|
||||
WMHandler::OpenCalled();
|
||||
}
|
||||
|
||||
void SeekLayer::OpenFailed()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("SeekLayer::OpenFailed"));
|
||||
if (playState == PLAYSTATE_OPENING)
|
||||
playState = PLAYSTATE_NONE;
|
||||
}
|
||||
WMHandler::OpenFailed();
|
||||
}
|
||||
|
||||
void SeekLayer::Error()
|
||||
{
|
||||
{
|
||||
AutoLock lock (seekGuard LOCKNAME("SeekLayer::Error"));
|
||||
/*if (playState == PLAYSTATE_OPENING)
|
||||
playState = PLAYSTATE_CLOSED;
|
||||
else */if (playState != PLAYSTATE_CLOSED)
|
||||
playState = PLAYSTATE_NONE;
|
||||
}
|
||||
WMHandler::Error();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef NULLSOFT_SEEKLAYERH
|
||||
#define NULLSOFT_SEEKLAYERH
|
||||
|
||||
#include "WMHandler.h"
|
||||
#include "../nu/AutoLock.h"
|
||||
#include "ClockLayer.h"
|
||||
class SeekLayer : public WMHandler
|
||||
{
|
||||
enum PlayState
|
||||
{
|
||||
PLAYSTATE_NONE,
|
||||
PLAYSTATE_OPENING,
|
||||
PLAYSTATE_OPENED,
|
||||
PLAYSTATE_BUFFERING,
|
||||
PLAYSTATE_STARTED,
|
||||
PLAYSTATE_STOPPED,
|
||||
PLAYSTATE_CLOSED,
|
||||
PLAYSTATE_SEEK,
|
||||
|
||||
};
|
||||
public:
|
||||
SeekLayer(IWMReader *_reader, ClockLayer *_clock);
|
||||
void SeekTo(long position);
|
||||
void Pause();
|
||||
void Unpause();
|
||||
void Stop();
|
||||
int Open(const wchar_t *filename, IWMReaderCallback *callback);
|
||||
|
||||
private:
|
||||
void BufferingStarted();
|
||||
void BufferingStopped();
|
||||
void Started();
|
||||
void Stopped();
|
||||
void Closed();
|
||||
void Opened();
|
||||
void OpenCalled();
|
||||
void Connecting();
|
||||
void Locating();
|
||||
void EndOfFile();
|
||||
void OpenFailed();
|
||||
void Error();
|
||||
|
||||
private:
|
||||
void DoStop();
|
||||
bool needPause, paused, needStop;
|
||||
long seekPos;
|
||||
Nullsoft::Utility::LockGuard seekGuard;
|
||||
IWMReader *reader;
|
||||
IWMReaderAdvanced2 *reader2;
|
||||
IWMMetadataEditor *metadata;
|
||||
ClockLayer *clock;
|
||||
PlayState playState, oldState_buffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
#if 0
|
||||
#include <windows.h>
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "Main.h"
|
||||
#include <shlwapi.h>
|
||||
|
||||
static WNDPROC waProc=0;
|
||||
static bool winampisUnicode=false;
|
||||
|
||||
static LRESULT WINAPI StatusHookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
|
||||
if (msg == WM_WA_IPC && lParam == IPC_HOOK_TITLESW)
|
||||
{
|
||||
LRESULT downTheLine = winampisUnicode?CallWindowProcW(waProc, hwnd, msg, wParam, lParam):CallWindowProcA(waProc, hwnd, msg, wParam, lParam);
|
||||
waHookTitleStructW *hook = (waHookTitleStructW *)wParam;
|
||||
if (!PathIsURLW(hook->filename) && winamp.GetStatusHook(hook->title, 2048, hook->filename))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return downTheLine;
|
||||
}
|
||||
|
||||
if (waProc)
|
||||
{
|
||||
if (winampisUnicode)
|
||||
return CallWindowProcW(waProc, hwnd, msg, wParam, lParam);
|
||||
else
|
||||
return CallWindowProcA(waProc, hwnd, msg, wParam, lParam);
|
||||
}
|
||||
else
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
void Hook(HWND winamp)
|
||||
{
|
||||
if (winamp)
|
||||
{
|
||||
winampisUnicode = !!IsWindowUnicode(winamp);
|
||||
if (winampisUnicode)
|
||||
waProc = (WNDPROC)SetWindowLongPtrW(winamp, GWLP_WNDPROC, (LONG_PTR)StatusHookProc);
|
||||
else
|
||||
waProc = (WNDPROC)SetWindowLongPtrA(winamp, GWLP_WNDPROC, (LONG_PTR)StatusHookProc);
|
||||
}
|
||||
}
|
||||
|
||||
void Unhook(HWND winamp)
|
||||
{
|
||||
// if (winamp && GetWindowLongA(winamp,GWL_WNDPROC) == (LONG)StatusHookProc)
|
||||
//SetWindowLong(winamp, GWL_WNDPROC, (LONG)waProc);
|
||||
//waProc=0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef NULLSOFT_STATUSHOOKH
|
||||
#define NULLSOFT_STATUSHOOKH
|
||||
|
||||
#include <windows.h>
|
||||
void Hook(HWND winamp);
|
||||
void Unhook(HWND winamp);
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
advanced preferences:
|
||||
defaults button
|
||||
|
||||
why do some videos not end properly?
|
||||
|
||||
potential race condition over WMDRM::fn
|
||||
|
||||
make the messagebox for 'do you want to acquire this shit' have its own message loop so we can killswitch it
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "main.h"
|
||||
#include "TagAlias.h"
|
||||
|
||||
struct TagAliases
|
||||
{
|
||||
const wchar_t *winampTag;
|
||||
const wchar_t *wmaTag;
|
||||
};
|
||||
|
||||
const TagAliases aliases[] =
|
||||
{
|
||||
{L"comment", g_wszWMDescription},
|
||||
{L"album", g_wszWMAlbumTitle},
|
||||
{L"genre", g_wszWMGenre},
|
||||
{L"year", g_wszWMYear},
|
||||
{L"track", g_wszWMTrackNumber},
|
||||
{L"artist", g_wszWMAuthor},
|
||||
{L"title", g_wszWMTitle},
|
||||
{L"copyright", g_wszWMCopyright},
|
||||
{L"composer", g_wszWMComposer},
|
||||
{L"albumartist", g_wszWMAlbumArtist},
|
||||
{L"bpm", g_wszWMBeatsPerMinute},
|
||||
{L"publisher", g_wszWMPublisher},
|
||||
{L"ISRC", g_wszWMISRC},
|
||||
{L"lyricist", g_wszWMWriter},
|
||||
{L"conductor", g_wszWMConductor},
|
||||
{L"tool", g_wszWMToolName},
|
||||
{L"encoder", g_wszWMEncodingSettings},
|
||||
{L"key", g_wszWMInitialKey},
|
||||
{L"mood", g_wszWMMood},
|
||||
{L"disc", g_wszWMPartOfSet},
|
||||
{L"height", g_wszWMVideoHeight},
|
||||
{L"width", g_wszWMVideoWidth},
|
||||
{L"category", g_wszWMCategory},
|
||||
{L"producer", g_wszWMProducer},
|
||||
{L"director", g_wszWMDirector},
|
||||
{L"fps", g_wszWMVideoFrameRate},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
|
||||
const wchar_t *GetAlias(const wchar_t *tag)
|
||||
{
|
||||
int i = 0;
|
||||
while (aliases[i].winampTag)
|
||||
{
|
||||
if (!lstrcmpiW(tag, aliases[i].winampTag))
|
||||
return aliases[i].wmaTag;
|
||||
i++;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
const wchar_t *GetAlias_rev(const wchar_t *tag)
|
||||
{
|
||||
int i = 0;
|
||||
while (aliases[i].wmaTag)
|
||||
{
|
||||
if (!lstrcmpiW(tag, aliases[i].wmaTag))
|
||||
return aliases[i].winampTag;
|
||||
i++;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef NULLSOFT_IN_WMVDRM_TAGALIAS_H
|
||||
#define NULLSOFT_IN_WMVDRM_TAGALIAS_H
|
||||
|
||||
#include <wchar.h>
|
||||
const wchar_t *GetAlias(const wchar_t *tag);
|
||||
const wchar_t *GetAlias_rev(const wchar_t *tag);
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "main.h"
|
||||
#include "VideoDataConverter.h"
|
||||
#include <cassert>
|
||||
|
||||
class YV12Converter : public VideoDataConverter
|
||||
{
|
||||
public:
|
||||
YV12Converter(int w, int h)
|
||||
: width(w), height(h)
|
||||
{
|
||||
yv12.y.rowBytes = width;
|
||||
yv12.v.rowBytes = width / 2;
|
||||
yv12.u.rowBytes = width / 2;
|
||||
vOffset = width*height;
|
||||
uOffset = width*height/4;
|
||||
}
|
||||
|
||||
void *Convert(void *videoData)
|
||||
{
|
||||
yv12.y.baseAddr = (unsigned char*)videoData;
|
||||
yv12.v.baseAddr = yv12.y.baseAddr + vOffset;
|
||||
yv12.u.baseAddr = yv12.v.baseAddr + uOffset;
|
||||
|
||||
return (void *)&yv12;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
int vOffset, uOffset;
|
||||
YV12_PLANES yv12;
|
||||
};
|
||||
|
||||
VideoDataConverter *MakeConverter(VideoOutputStream *stream)
|
||||
{
|
||||
switch (stream->FourCC())
|
||||
{
|
||||
case '21VY':
|
||||
return new YV12Converter(stream->DestinationWidth(), stream->DestinationHeight());
|
||||
default:
|
||||
return new VideoDataConverter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef NULLSOFT_VIDEODATACONVERTERH
|
||||
#define NULLSOFT_VIDEODATACONVERTERH
|
||||
|
||||
#include "OutputStream.h"
|
||||
class VideoDataConverter
|
||||
{
|
||||
public:
|
||||
virtual void *Convert(void *videoData)
|
||||
{
|
||||
return videoData;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
VideoDataConverter *MakeConverter(VideoOutputStream *stream);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,300 @@
|
||||
#include "Main.h"
|
||||
#include "VideoLayer.h"
|
||||
#include <initguid.h>
|
||||
#include <wmsdkidl.h>
|
||||
#include <cassert>
|
||||
#include "util.h"
|
||||
#include "resource.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "config.h"
|
||||
#define VIDEO_ACCEPTABLE_DROP (config_video_drop_threshold*10000)
|
||||
|
||||
VideoLayer::VideoLayer(IWMReader *_reader)
|
||||
: reader(_reader), videoOutputNum(-1),
|
||||
reader2(0), offset(0), nextRest(0),
|
||||
converter(NULL), videoOpened(false),
|
||||
video_output_opened(false),
|
||||
killSwitch(0), aspect(0),
|
||||
earlyDelivery(0), fourcc(0),
|
||||
drmProtected(false),
|
||||
videoStream(0), flip(false),
|
||||
videoWidth(0), videoHeight(0)
|
||||
{
|
||||
reader->AddRef();
|
||||
if (FAILED(reader->QueryInterface(&reader2)))
|
||||
reader2 = 0;
|
||||
|
||||
if (FAILED(reader->QueryInterface(&header)))
|
||||
header = 0;
|
||||
|
||||
killSwitch = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
}
|
||||
|
||||
VideoLayer::~VideoLayer()
|
||||
{
|
||||
videoThread.Kill();
|
||||
if (reader2)
|
||||
reader2->Release();
|
||||
if (header)
|
||||
header->Release();
|
||||
reader->Release();
|
||||
CloseHandle(killSwitch);
|
||||
}
|
||||
|
||||
bool AcceptableFormat(GUID &subtype)
|
||||
{
|
||||
if (subtype == WMMEDIASUBTYPE_YV12
|
||||
|| subtype == WMMEDIASUBTYPE_YUY2
|
||||
|| subtype == WMMEDIASUBTYPE_UYVY
|
||||
//|| subtype == WMMEDIASUBTYPE_YVYU
|
||||
|| subtype == WMMEDIASUBTYPE_RGB24
|
||||
|| subtype == WMMEDIASUBTYPE_RGB32
|
||||
|| subtype == WMMEDIASUBTYPE_I420
|
||||
|| subtype == WMMEDIASUBTYPE_IYUV
|
||||
|| subtype == WMMEDIASUBTYPE_RGB1
|
||||
|| subtype == WMMEDIASUBTYPE_RGB4
|
||||
|| subtype == WMMEDIASUBTYPE_RGB8
|
||||
|| subtype == WMMEDIASUBTYPE_RGB565
|
||||
|| subtype == WMMEDIASUBTYPE_RGB555
|
||||
)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VideoLayer::AttemptOpenVideo(VideoOutputStream *attempt)
|
||||
{
|
||||
videoWidth = attempt->DestinationWidth();
|
||||
videoHeight = attempt->DestinationHeight();
|
||||
flip = attempt->Flipped();
|
||||
fourcc = attempt->FourCC();
|
||||
if (!fourcc)
|
||||
return false;
|
||||
|
||||
aspect = 1.0;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool VideoLayer::OpenVideo()
|
||||
{
|
||||
videoOutputNum = -1;
|
||||
DWORD numOutputs, numFormats;
|
||||
IWMOutputMediaProps *formatProperties;
|
||||
VideoOutputStream *stream;
|
||||
GUID mediaType;
|
||||
|
||||
reader->GetOutputCount(&numOutputs);
|
||||
|
||||
for (DWORD output = 0;output < numOutputs;output++)
|
||||
{
|
||||
// test the default format first, and if that fails, iterate through the rest
|
||||
const int defaultFormat = -1;
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = reader->GetOutputFormatCount(output, &numFormats)))
|
||||
continue;
|
||||
|
||||
for (int format = 0/*defaultFormat*/;format != numFormats;format++)
|
||||
{
|
||||
if (format == defaultFormat)
|
||||
reader->GetOutputProps(output, &formatProperties);
|
||||
else
|
||||
reader->GetOutputFormat(output, format, &formatProperties);
|
||||
|
||||
formatProperties->GetType(&mediaType);
|
||||
|
||||
if (mediaType == WMMEDIATYPE_Video)
|
||||
{
|
||||
stream = new VideoOutputStream(formatProperties);
|
||||
|
||||
if (stream->IsVideo() // if it's video
|
||||
&& AcceptableFormat(stream->GetSubType()) // and a video format we like
|
||||
&& AttemptOpenVideo(stream)) // and winamp was able to open it
|
||||
{
|
||||
videoOpened = true;
|
||||
int fourcc = stream->FourCC();
|
||||
if (fourcc == '8BGR')
|
||||
{
|
||||
RGBQUAD *palette = stream->CreatePalette();
|
||||
winamp.SetVideoPalette(palette);
|
||||
|
||||
// TODO: don't leak the palette
|
||||
}
|
||||
char *cc = (char *) & fourcc;
|
||||
char status[512] = {0};
|
||||
StringCchPrintfA(status, 512, WASABI_API_LNGSTRING(IDS_WINDOWS_MEDIA_XXX),
|
||||
stream->DestinationWidth(), stream->DestinationHeight(), cc[0], cc[1], cc[2], cc[3]);
|
||||
winamp.SetVideoStatusText(status);
|
||||
converter = MakeConverter(stream);
|
||||
videoOutputNum = output;
|
||||
videoStream = stream;
|
||||
reader->SetOutputProps(output, formatProperties);
|
||||
formatProperties->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
delete stream;
|
||||
stream = 0;
|
||||
|
||||
}
|
||||
formatProperties->Release();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VideoLayer::SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
|
||||
{
|
||||
if (outputNum == videoOutputNum)
|
||||
{
|
||||
if (WaitForSingleObject(killSwitch, 0) == WAIT_OBJECT_0)
|
||||
return ;
|
||||
|
||||
INSSBuffer3 *buff3;
|
||||
if (SUCCEEDED(sample->QueryInterface(&buff3)))
|
||||
{
|
||||
short aspectHex = 0;
|
||||
DWORD size = 2;
|
||||
buff3->GetProperty(WM_SampleExtensionGUID_PixelAspectRatio, &aspectHex, &size);
|
||||
if (aspectHex)
|
||||
{
|
||||
double newAspect = (double)((aspectHex & 0xFF00) >> 8) / (double)(aspectHex & 0xFF) ;
|
||||
|
||||
if (newAspect != aspect)
|
||||
{
|
||||
aspect = newAspect;
|
||||
videoThread.OpenVideo(drmProtected, videoWidth, videoHeight, flip, aspect, fourcc);
|
||||
video_output_opened=true;
|
||||
}
|
||||
}
|
||||
buff3->Release();
|
||||
}
|
||||
|
||||
if (!video_output_opened)
|
||||
{
|
||||
videoThread.OpenVideo(drmProtected, videoWidth, videoHeight, flip, aspect, fourcc);
|
||||
video_output_opened=true;
|
||||
}
|
||||
|
||||
__int64 timeDiff;
|
||||
First().TimeToSync(timeStamp, timeDiff);
|
||||
|
||||
if (timeDiff < -VIDEO_ACCEPTABLE_DROP) // late
|
||||
{
|
||||
timeDiff = -timeDiff;
|
||||
if (config_video_catchup) First().VideoCatchup(timeDiff);
|
||||
if (config_video_framedropoffset) this->VideoFrameDrop((DWORD)(timeDiff / 10000));
|
||||
if (config_video_notifylate) reader2->NotifyLateDelivery(timeDiff);
|
||||
|
||||
// drop the frame
|
||||
}
|
||||
else // early
|
||||
{
|
||||
while (!videoThread.AddBuffer(sample, timeStamp, flags, drmProtected))
|
||||
{
|
||||
if (WaitForSingleObject(killSwitch, VIDEO_ACCEPTABLE_JITTER_MS) == WAIT_OBJECT_0)
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
WMHandler::SampleReceived(timeStamp, duration, outputNum, flags, sample);
|
||||
}
|
||||
|
||||
void VideoLayer::Opened()
|
||||
{
|
||||
WORD stream = 0;
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
|
||||
BOOL value;
|
||||
WORD valueLen = sizeof(value);
|
||||
header->GetAttributeByName(&stream, g_wszWMProtected, &type, (BYTE *)&value, &valueLen);
|
||||
drmProtected = !!value;
|
||||
|
||||
ResetEvent(killSwitch);
|
||||
if (OpenVideo())
|
||||
{
|
||||
ResetEvent(killSwitch);
|
||||
HRESULT hr;
|
||||
|
||||
BOOL dedicatedThread = config_video_dedicated_thread ? TRUE : FALSE;
|
||||
hr = reader2->SetOutputSetting(videoOutputNum, g_wszDedicatedDeliveryThread, WMT_TYPE_BOOL, (BYTE *) & dedicatedThread, sizeof(dedicatedThread));
|
||||
assert(hr == S_OK);
|
||||
|
||||
earlyDelivery = config_video_early ? config_video_early_pad : 0;
|
||||
hr = reader2->SetOutputSetting(videoOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & earlyDelivery , sizeof(earlyDelivery));
|
||||
assert(hr == S_OK);
|
||||
|
||||
BOOL outOfOrder = config_video_outoforder ? TRUE : FALSE;
|
||||
hr = reader2->SetOutputSetting(videoOutputNum, g_wszDeliverOnReceive, WMT_TYPE_BOOL, (BYTE *) & outOfOrder, sizeof(outOfOrder));
|
||||
assert(hr == S_OK);
|
||||
|
||||
BOOL justInTime = config_lowmemory ? TRUE : FALSE;
|
||||
hr = reader2->SetOutputSetting(videoOutputNum, g_wszJustInTimeDecode, WMT_TYPE_BOOL, (BYTE *) & justInTime, sizeof(justInTime));
|
||||
assert(hr == S_OK);
|
||||
}
|
||||
else
|
||||
{
|
||||
videoOpened = false;
|
||||
}
|
||||
|
||||
WMHandler::Opened();
|
||||
}
|
||||
|
||||
void VideoLayer::VideoFrameDrop(DWORD lateness)
|
||||
{
|
||||
//earlyDelivery+=lateness;
|
||||
lateness += earlyDelivery;
|
||||
HRESULT hr = reader2->SetOutputSetting(videoOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & lateness, sizeof(lateness));
|
||||
assert(hr == S_OK);
|
||||
}
|
||||
|
||||
void VideoLayer::Closed()
|
||||
{
|
||||
if (video_output_opened)
|
||||
{
|
||||
videoThread.CloseVideo(drmProtected);
|
||||
video_output_opened = false;
|
||||
}
|
||||
videoOpened = false;
|
||||
delete videoStream;
|
||||
videoStream=0;
|
||||
WMHandler::Closed();
|
||||
}
|
||||
|
||||
|
||||
bool VideoLayer::IsOpen()
|
||||
{
|
||||
return videoOpened;
|
||||
}
|
||||
|
||||
void VideoLayer::HasVideo(bool &video)
|
||||
{
|
||||
video=videoOpened;
|
||||
}
|
||||
|
||||
void VideoLayer::Kill()
|
||||
{
|
||||
SetEvent(killSwitch);
|
||||
if (videoOpened)
|
||||
videoThread.SignalStop();//SignalStop();
|
||||
|
||||
WMHandler::Kill();
|
||||
if (videoOpened)
|
||||
videoThread.WaitForStop();
|
||||
}
|
||||
|
||||
void VideoLayer::Started()
|
||||
{
|
||||
ResetEvent(killSwitch);
|
||||
if (videoOpened)
|
||||
videoThread.Start(converter, &First());
|
||||
WMHandler::Started();
|
||||
}
|
||||
|
||||
void VideoLayer::Stopped()
|
||||
{
|
||||
if (videoOpened)
|
||||
videoThread.Stop();
|
||||
WMHandler::Stopped();
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef NULLSOFT_VIDEOLAYERH
|
||||
#define NULLSOFT_VIDEOLAYERH
|
||||
|
||||
#include "WMHandler.h"
|
||||
#include "OutputStream.h"
|
||||
#include <wmsdk.h>
|
||||
#include "VideoDataConverter.h"
|
||||
#include "Config.h"
|
||||
#include "VideoThread.h"
|
||||
|
||||
#define VIDEO_ACCEPTABLE_JITTER (config_video_jitter*10000)
|
||||
#define VIDEO_ACCEPTABLE_JITTER_MS (config_video_jitter)
|
||||
|
||||
class VideoLayer : public WMHandler
|
||||
{
|
||||
public:
|
||||
VideoLayer(IWMReader *_reader);
|
||||
~VideoLayer();
|
||||
bool IsOpen();
|
||||
void Kill();
|
||||
private:
|
||||
// WMHandler
|
||||
void VideoFrameDrop(DWORD lateness);
|
||||
void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample);
|
||||
void Opened();
|
||||
void AudioBufferMilliseconds(long ms);
|
||||
void Closed();
|
||||
|
||||
void Started();
|
||||
void Stopped();
|
||||
void HasVideo(bool &video);
|
||||
// utility methods
|
||||
bool AttemptOpenVideo(VideoOutputStream *attempt);
|
||||
bool OpenVideo();
|
||||
|
||||
// other people's data
|
||||
IWMReader *reader;
|
||||
|
||||
// our data
|
||||
IWMReaderAdvanced2 *reader2;
|
||||
IWMHeaderInfo *header;
|
||||
int videoOutputNum;
|
||||
DWORD offset;
|
||||
long nextRest;
|
||||
VideoDataConverter *converter;
|
||||
VideoOutputStream *videoStream;
|
||||
|
||||
bool videoOpened;
|
||||
QWORD catchupTime;
|
||||
double aspect;
|
||||
int fourcc;
|
||||
bool flip;
|
||||
int videoWidth, videoHeight;
|
||||
HANDLE killSwitch;
|
||||
DWORD earlyDelivery;
|
||||
bool drmProtected;
|
||||
bool video_output_opened;
|
||||
VideoThread videoThread;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "main.h"
|
||||
#include "VideoOutputChildDDraw.h"
|
||||
#include <multimon.h>
|
||||
#include <ddraw.h>
|
||||
|
||||
class MonitorFinder
|
||||
{
|
||||
public:
|
||||
MonitorFinder(HMONITOR hm) : m_monitor_to_find(hm), m_found_devguid(0)
|
||||
{}
|
||||
|
||||
HMONITOR m_monitor_to_find;
|
||||
int m_found_devguid;
|
||||
GUID m_devguid;
|
||||
};
|
||||
|
||||
static BOOL WINAPI DDEnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm)
|
||||
{
|
||||
MonitorFinder *ovo = (MonitorFinder *)lpContext;
|
||||
if (ovo->m_found_devguid) return 1;
|
||||
if (hm == ovo->m_monitor_to_find)
|
||||
{
|
||||
ovo->m_devguid = *lpGUID;
|
||||
ovo->m_found_devguid = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VideoOutputChildDDraw::update_monitor_coords()
|
||||
{
|
||||
//find the correct monitor if multiple monitor support is present
|
||||
m_mon_x = 0;
|
||||
m_mon_y = 0;
|
||||
|
||||
HINSTANCE h = LoadLibrary(L"user32.dll");
|
||||
if (h)
|
||||
{
|
||||
HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT, DWORD)) GetProcAddress(h, "MonitorFromPoint");
|
||||
HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
|
||||
HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags) = (HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
|
||||
BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR, LPMONITORINFO)) GetProcAddress(h, "GetMonitorInfoA");
|
||||
if (Mfp && Mfr && Mfw && Gmi)
|
||||
{
|
||||
HMONITOR hm = Mfw(parent, 0);
|
||||
if (hm)
|
||||
{
|
||||
HINSTANCE hdd = LoadLibrary(L"ddraw.dll");
|
||||
if (hdd)
|
||||
{
|
||||
typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HMONITOR);
|
||||
typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEX)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
|
||||
LPDIRECTDRAWENUMERATEEX lpDDEnumEx;
|
||||
lpDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hdd, "DirectDrawEnumerateExW");
|
||||
if (lpDDEnumEx)
|
||||
{
|
||||
MonitorFinder finder(hm);
|
||||
|
||||
lpDDEnumEx(&DDEnumCallbackEx, &finder, DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES);
|
||||
foundGUID=!!finder.m_found_devguid;
|
||||
if (foundGUID)
|
||||
{
|
||||
m_devguid=finder.m_devguid;
|
||||
MONITORINFOEXW mi;
|
||||
memset(&mi, 0, sizeof(mi));
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (Gmi(hm, &mi))
|
||||
{
|
||||
m_mon_x = mi.rcMonitor.left;
|
||||
m_mon_y = mi.rcMonitor.top;
|
||||
}
|
||||
}
|
||||
}
|
||||
FreeLibrary(hdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
FreeLibrary(h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef NULLSOFT_VIDEOOUTPUTCHILDDDRAWH
|
||||
#define NULLSOFT_VIDEOOUTPUTCHILDDDRAWH
|
||||
#include "../Winamp/VideoOutputChild.h"
|
||||
|
||||
class VideoOutputChildDDraw : public VideoRenderer
|
||||
{
|
||||
public:
|
||||
VideoOutputChildDDraw() : m_mon_x(0), m_mon_y(0), foundGUID(false), parent(0), adjuster(0) {}
|
||||
VideoAspectAdjuster *adjuster;
|
||||
void update_monitor_coords();
|
||||
int m_mon_x, m_mon_y;
|
||||
bool foundGUID;
|
||||
GUID m_devguid;
|
||||
HWND parent;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
#include "Main.h"
|
||||
#include "VideoThread.h"
|
||||
#include "VideoLayer.h"
|
||||
#include "config.h"
|
||||
#include <windows.h>
|
||||
|
||||
DWORD WINAPI VidThread_stub(void *ptr)
|
||||
{
|
||||
((VideoThread *)ptr)->VidThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
VideoThread::VideoThread() : converter(0), clock(0)
|
||||
{
|
||||
drm = false;
|
||||
|
||||
DWORD id;
|
||||
thread = CreateThread(NULL, 256*1024, VidThread_stub, (void *)this, NULL, &id);
|
||||
SetThreadPriority(thread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
}
|
||||
|
||||
void VideoThread::Start(VideoDataConverter *_converter, WMHandler *_clock)
|
||||
{
|
||||
clock = _clock;
|
||||
if (converter != _converter)
|
||||
{
|
||||
if (converter)
|
||||
delete converter;
|
||||
converter = _converter;
|
||||
}
|
||||
ResetEvent(stopped);
|
||||
QueueUserAPC(MediaThread_StartAPC, thread, reinterpret_cast<ULONG_PTR>(this));
|
||||
}
|
||||
|
||||
void VideoThread::VidThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (WaitForSingleObjectEx(killEvent, wait, TRUE))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
//StopAPC();
|
||||
return;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
{
|
||||
if (buffers.empty())
|
||||
{
|
||||
SetEvent(bufferFreed);
|
||||
continue;
|
||||
}
|
||||
|
||||
MediaBuffer *buffer = buffers.front();
|
||||
|
||||
__int64 diff;
|
||||
clock->TimeToSync(buffer->timestamp, diff);
|
||||
if (diff < VIDEO_ACCEPTABLE_JITTER)
|
||||
{
|
||||
void *data;
|
||||
DWORD size;
|
||||
|
||||
buffer->buffer->GetBufferAndLength((BYTE **)&data, &size);
|
||||
if (buffer->drmProtected)
|
||||
winamp.EncryptedDrawFrame(converter->Convert(data));
|
||||
else
|
||||
winamp.DrawFrame(converter->Convert(data));
|
||||
|
||||
try {
|
||||
buffer->buffer->Release();
|
||||
delete buffer;
|
||||
} catch (...) {}
|
||||
|
||||
//buffers.pop_front();
|
||||
if (buffers.size())
|
||||
{
|
||||
buffers.erase(buffers.begin());
|
||||
}
|
||||
}
|
||||
if (buffers.size() < config_video_cache_frames)
|
||||
SetEvent(bufferFreed);
|
||||
}
|
||||
continue;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoThread::AddAPC(MediaBuffer *buffer)
|
||||
{
|
||||
if (buffers.empty())
|
||||
{
|
||||
__int64 diff;
|
||||
clock->TimeToSync(buffer->timestamp, diff);
|
||||
if (diff < VIDEO_ACCEPTABLE_JITTER)
|
||||
{
|
||||
void *data;
|
||||
DWORD size;
|
||||
buffer->buffer->GetBufferAndLength((BYTE **)&data, &size);
|
||||
if (buffer->drmProtected)
|
||||
winamp.EncryptedDrawFrame(converter->Convert(data));
|
||||
else
|
||||
winamp.DrawFrame(converter->Convert(data));
|
||||
|
||||
buffer->buffer->Release();
|
||||
if (buffers.size() >= config_video_cache_frames)
|
||||
ResetEvent(bufferFreed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OrderedInsert(buffer);
|
||||
|
||||
if (buffers.size() >= config_video_cache_frames)
|
||||
ResetEvent(bufferFreed);
|
||||
}
|
||||
|
||||
struct VideoOpenParameters
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int color_format;
|
||||
double aspect;
|
||||
int flip;
|
||||
bool drm;
|
||||
};
|
||||
|
||||
VOID CALLBACK VideoThread::VideoThread_VideoOpenAPC(ULONG_PTR params)
|
||||
{
|
||||
VideoOpenParameters *p = (VideoOpenParameters *)params;
|
||||
if (p->drm)
|
||||
{
|
||||
winamp.OpenEncryptedVideo(p->width, p->height, !!p->flip, p->aspect, p->color_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
winamp.OpenVideo(p->width, p->height, !!p->flip, p->aspect, p->color_format);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoThread::OpenVideo(bool drm, int width, int height, bool flip, double aspect, int fourcc)
|
||||
{
|
||||
VideoOpenParameters *p = new VideoOpenParameters;
|
||||
p->width = width;
|
||||
p->height = height;
|
||||
p->color_format = fourcc;
|
||||
p->aspect = aspect;
|
||||
p->flip = flip;
|
||||
p->drm = drm;
|
||||
this->drm = drm;
|
||||
QueueUserAPC(VideoThread_VideoOpenAPC, thread, reinterpret_cast<ULONG_PTR>(p));
|
||||
}
|
||||
|
||||
VOID CALLBACK VideoThread::VideoThread_VideoCloseAPC(ULONG_PTR params)
|
||||
{
|
||||
if (params)
|
||||
winamp.CloseEncryptedVideo();
|
||||
else
|
||||
winamp.CloseVideo();
|
||||
}
|
||||
|
||||
void VideoThread::CloseVideo(bool drm)
|
||||
{
|
||||
QueueUserAPC(VideoThread_VideoCloseAPC, thread, static_cast<ULONG_PTR>(drm));
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef NULLSOFTVIDEOTHREADH
|
||||
#define NULLSOFTVIDEOTHREADH
|
||||
|
||||
#include "VideoDataConverter.h"
|
||||
#include "WMHandler.h"
|
||||
#include <deque>
|
||||
#include <wmsdk.h>
|
||||
#include "MediaThread.h"
|
||||
|
||||
class VideoThread : public MediaThread
|
||||
{
|
||||
public:
|
||||
VideoThread();
|
||||
void Start(VideoDataConverter *_converter, WMHandler *_clock);
|
||||
|
||||
/* AddBuffers put a video buffer in the queue
|
||||
it returns true if it was added
|
||||
it returns false if it was NOT added. it is up to YOU (the caller) to sleep for a while and call again
|
||||
*/
|
||||
void VidThread();
|
||||
|
||||
void OpenVideo(bool drm, int width, int height, bool flip, double aspect, int fourcc);
|
||||
void CloseVideo(bool drm);
|
||||
private:
|
||||
static VOID CALLBACK VideoThread_VideoOpenAPC(ULONG_PTR params);
|
||||
static VOID CALLBACK VideoThread_VideoCloseAPC(ULONG_PTR params);
|
||||
|
||||
|
||||
void AddAPC(MediaBuffer *);
|
||||
VideoDataConverter *converter;
|
||||
WMHandler *clock;
|
||||
bool drm;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,294 @@
|
||||
#include "Main.h"
|
||||
#include "WMCallback.h"
|
||||
#include <algorithm>
|
||||
#include "WMHandler.h"
|
||||
#include "util.h"
|
||||
#include <cassert>
|
||||
|
||||
#define CAST_TO(x) if (riid== IID_##x) { *ppvObject=static_cast<x *>(this); AddRef(); return S_OK; }
|
||||
#define CAST_TO_VIA(x,y) if (riid== IID_##x) { *ppvObject=static_cast<y *>(this); AddRef(); return S_OK; }
|
||||
HRESULT WMCallback::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject )
|
||||
{
|
||||
CAST_TO(IWMReaderCallback);
|
||||
CAST_TO_VIA(IUnknown, IWMReaderCallback);
|
||||
CAST_TO(IWMReaderCallbackAdvanced);
|
||||
#ifdef _DEBUG
|
||||
CAST_TO(IWMCredentialCallback);
|
||||
#endif
|
||||
|
||||
*ppvObject = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG WMCallback::AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
ULONG WMCallback::Release()
|
||||
{
|
||||
if (InterlockedDecrement(&refCount) == 0)
|
||||
{
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
|
||||
HRESULT WMCallback::OnStatus(WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType,
|
||||
BYTE __RPC_FAR *pValue, void __RPC_FAR *pvContext)
|
||||
{
|
||||
if (!handler)
|
||||
return S_OK;
|
||||
|
||||
switch (Status)
|
||||
{
|
||||
WMTCASE(WMT_INIT_PLAYLIST_BURN)
|
||||
handler->InitPlaylistBurn();
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_NO_RIGHTS)
|
||||
handler->NoRights((wchar_t *)pValue);
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_NO_RIGHTS_EX)
|
||||
handler->NoRightsEx((WM_GET_LICENSE_DATA *&)pValue);
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_NEEDS_INDIVIDUALIZATION)
|
||||
handler->Individualize();
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_END_OF_STREAMING)
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_LICENSEURL_SIGNATURE_STATE)
|
||||
handler->SignatureState((WMT_DRMLA_TRUST *&)pValue);
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_ACQUIRE_LICENSE)
|
||||
WMT_SHOW_HR_CODE(hr)
|
||||
switch (hr)
|
||||
{
|
||||
case NS_S_DRM_LICENSE_ACQUIRED:
|
||||
handler->LicenseAcquired();
|
||||
break;
|
||||
case NS_S_DRM_MONITOR_CANCELLED:
|
||||
handler->MonitorCancelled();
|
||||
break;
|
||||
case NS_S_DRM_ACQUIRE_CANCELLED:
|
||||
handler->SilentCancelled();
|
||||
break;
|
||||
default:
|
||||
handler->AcquireLicense((WM_GET_LICENSE_DATA *&)pValue);
|
||||
}
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_INDIVIDUALIZE)
|
||||
handler->IndividualizeStatus((WM_INDIVIDUALIZE_STATUS *)pValue);
|
||||
break;
|
||||
|
||||
//the file has been opened
|
||||
WMTCASE(WMT_OPENED)
|
||||
if (SUCCEEDED(hr))
|
||||
handler->Opened();
|
||||
else
|
||||
{
|
||||
switch (hr)
|
||||
{
|
||||
WMTCASE(NS_E_DRM_APPCERT_REVOKED)
|
||||
WMTCASE(NS_E_DRM_LICENSE_APP_NOTALLOWED)
|
||||
handler->DRMExpired();
|
||||
WMTCASE(NS_E_LICENSE_REQUIRED)
|
||||
handler->LicenseRequired();
|
||||
break;
|
||||
WMTCASE(NS_E_DRM_NEEDS_INDIVIDUALIZATION)
|
||||
handler->NeedsIndividualization();
|
||||
break;
|
||||
WMTCASE(E_ACCESSDENIED)
|
||||
handler->AccessDenied();
|
||||
break;
|
||||
default:
|
||||
WMT_SHOW_HR_CODE(hr);
|
||||
handler->Error();
|
||||
return S_OK;
|
||||
}
|
||||
handler->OpenCalled();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Playback of the opened file has begun.
|
||||
WMTCASE( WMT_STARTED)
|
||||
if (SUCCEEDED(hr))
|
||||
handler->Started();
|
||||
else
|
||||
{
|
||||
switch (hr)
|
||||
{
|
||||
WMTCASE(E_ABORT)
|
||||
//handler->OpenFailed();
|
||||
break;
|
||||
WMTCASE(NS_E_DRM_REOPEN_CONTENT)
|
||||
handler->Error();
|
||||
break;
|
||||
default:
|
||||
WMT_SHOW_HR_CODE(hr);
|
||||
handler->Error();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_NEW_METADATA)
|
||||
if (SUCCEEDED(hr))
|
||||
handler->NewMetadata();
|
||||
break;
|
||||
|
||||
// The previously playing reader has stopped.
|
||||
WMTCASE( WMT_STOPPED)
|
||||
if (SUCCEEDED(hr))
|
||||
handler->Stopped();
|
||||
else
|
||||
{
|
||||
WMT_SHOW_HR_CODE(hr);
|
||||
handler->Error();
|
||||
}
|
||||
break;
|
||||
|
||||
// The previously playing reader has stopped.
|
||||
WMTCASE( WMT_CLOSED)
|
||||
if (SUCCEEDED(hr))
|
||||
handler->Closed();
|
||||
else
|
||||
{
|
||||
WMT_SHOW_HR_CODE(hr);
|
||||
handler->Error();
|
||||
}
|
||||
break;
|
||||
|
||||
WMTCASE(WMT_ERROR)
|
||||
WMT_SHOW_HR_CODE(hr);
|
||||
//handler->Error();
|
||||
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_BUFFERING_START)
|
||||
if (SUCCEEDED(hr))
|
||||
handler->BufferingStarted();
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_BUFFERING_STOP)
|
||||
if (SUCCEEDED(hr))
|
||||
handler->BufferingStopped();
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_EOF)
|
||||
WMT_SHOW_HR_CODE(hr);
|
||||
handler->EndOfFile();
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_LOCATING)
|
||||
handler->Locating();
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_CONNECTING)
|
||||
handler->Connecting();
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_PREROLL_READY)
|
||||
break;
|
||||
|
||||
WMTCASE( WMT_PREROLL_COMPLETE)
|
||||
break;
|
||||
WMTCASE(WMT_NEW_SOURCEFLAGS)
|
||||
break;
|
||||
WMTCASE(WMT_MISSING_CODEC)
|
||||
|
||||
WMT_SHOW_HR_CODE(hr);
|
||||
#ifdef DEBUG
|
||||
std::cerr << dwType << std::endl;
|
||||
std::wcerr << GuidString(*(GUID *)pValue) << std::endl;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
std::cerr << "unknown message = " << Status << std::endl;
|
||||
#endif
|
||||
break;
|
||||
};
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WMCallback::OnStreamSelection(WORD wStreamCount, WORD *pStreamNumbers, WMT_STREAM_SELECTION *pSelections, void *pvContext)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "OnStreamSelection" << std::endl;
|
||||
#endif
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT WMCallback::OnOutputPropsChanged(DWORD dwOutputNum, WM_MEDIA_TYPE *pMediaType, void *pvContext)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "OnOutputPropsChanged" << std::endl;
|
||||
#endif
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT WMCallback::AllocateForStream(WORD wStreamNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT WMCallback::AllocateForOutput(DWORD dwOutputNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT WMCallback::OnStreamSample(WORD wStreamNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// 0x5A, 0xA5, 0x00, 0x03, 0x74, 0x00, 0x01, 0x01, 0x77,
|
||||
//------------------------------------------------------------------------------
|
||||
// Name: CWAPlugin::OnSample()
|
||||
// Desc: IWMReaderCallback method to process samples.
|
||||
//------------------------------------------------------------------------------
|
||||
HRESULT WMCallback::OnSample(DWORD dwOutputNum, QWORD cnsSampleTime,
|
||||
QWORD cnsSampleDuration, DWORD dwFlags,
|
||||
INSSBuffer __RPC_FAR *pSample, void __RPC_FAR *pvContext)
|
||||
{
|
||||
if (!handler)
|
||||
return S_OK;
|
||||
handler->SampleReceived(cnsSampleTime, cnsSampleDuration, dwOutputNum, dwFlags, pSample);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WMCallback::OnTime(QWORD cnsCurrentTime, void *pvContext)
|
||||
{
|
||||
if (!handler)
|
||||
return S_OK;
|
||||
handler->TimeReached(cnsCurrentTime);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT WMCallback::AcquireCredentials(WCHAR* pwszRealm, WCHAR* pwszSite,
|
||||
WCHAR* pwszUser, DWORD cchUser,
|
||||
WCHAR* pwszPassword, DWORD cchPassword,
|
||||
HRESULT hrStatus, DWORD* pdwFlags)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
std::cout << "WMCallback::AcquireCredentials" << std::endl;
|
||||
std::wcout << pwszRealm << std::endl;
|
||||
std::wcout << pwszSite << std::endl;
|
||||
std::wcout << HRErrorCode(hrStatus) << std::endl;
|
||||
return S_OK;
|
||||
#endif
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef NULLSOFT_WMCALLBACK
|
||||
#define NULLSOFT_WMCALLBACK
|
||||
|
||||
#include <wmsdk.h>
|
||||
#include <deque>
|
||||
#include "WMHandler.h"
|
||||
|
||||
|
||||
class WMCallback : public IWMReaderCallback, public IWMReaderCallbackAdvanced, public IWMCredentialCallback
|
||||
{
|
||||
|
||||
public:
|
||||
WMCallback() : refCount(0), handler(0)
|
||||
{
|
||||
AddRef();
|
||||
}
|
||||
|
||||
~WMCallback()
|
||||
{
|
||||
}
|
||||
|
||||
WMHandler &operator >> (WMHandler *_handler)
|
||||
{
|
||||
handler = _handler;
|
||||
return *handler;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
HRESULT STDMETHODCALLTYPE OnStatus(WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType, BYTE __RPC_FAR *pValue, void __RPC_FAR *pvContext);
|
||||
virtual HRESULT STDMETHODCALLTYPE OnSample(DWORD dwOutputNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer __RPC_FAR *pSample, void __RPC_FAR *pvContext);
|
||||
|
||||
/* IWMReaderCallbackAdvanced */
|
||||
HRESULT STDMETHODCALLTYPE OnStreamSample(WORD wStreamNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext);
|
||||
HRESULT STDMETHODCALLTYPE OnTime(QWORD cnsCurrentTime, void *pvContext);
|
||||
HRESULT STDMETHODCALLTYPE OnStreamSelection(WORD wStreamCount, WORD *pStreamNumbers, WMT_STREAM_SELECTION *pSelections, void *pvContext);
|
||||
HRESULT STDMETHODCALLTYPE OnOutputPropsChanged(DWORD dwOutputNum, WM_MEDIA_TYPE *pMediaType, void *pvContext);
|
||||
HRESULT STDMETHODCALLTYPE AllocateForStream(WORD wStreamNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext);
|
||||
HRESULT STDMETHODCALLTYPE AllocateForOutput(DWORD dwOutputNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext);
|
||||
|
||||
/* IWMCredentialCallback */
|
||||
HRESULT STDMETHODCALLTYPE AcquireCredentials(WCHAR* pwszRealm, WCHAR* pwszSite, WCHAR* pwszUser, DWORD cchUser, WCHAR* pwszPassword, DWORD cchPassword, HRESULT hrStatus, DWORD* pdwFlags);
|
||||
|
||||
long refCount;
|
||||
WMHandler *handler;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,649 @@
|
||||
#include "Main.h"
|
||||
#include "WMDRMModule.h"
|
||||
#include "AutoWide.h"
|
||||
#include "WMInformation.h"
|
||||
#include "AutoChar.h"
|
||||
#include "FileInfoDialog.h"
|
||||
#include "ConfigDialog.h"
|
||||
#include "resource.h"
|
||||
#include "StatusHook.h"
|
||||
#include "../nu/Config.h"
|
||||
#include "util.h"
|
||||
#include "WMPlaylist.h"
|
||||
#include "api.h"
|
||||
#include "output/OutPlugin.h"
|
||||
#include "output/AudioOut.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
extern Nullsoft::Utility::Config wmConfig;
|
||||
|
||||
#define SAMPLES_PER_BLOCK 576
|
||||
AudioOut *out = 0;
|
||||
|
||||
unsigned long endTime = 0;
|
||||
unsigned long startTime = 0;
|
||||
|
||||
void InitOutputs(HWND hMainWindow, HMODULE hDllInstance)
|
||||
{}
|
||||
|
||||
void WMDRM::AssignOutput()
|
||||
{
|
||||
out = &pluginOut;
|
||||
}
|
||||
|
||||
WMDRM::WMDRM()
|
||||
: paused(false),
|
||||
clock(0), audio(0), video(0), wait(0), info(0), buffer(0), seek(0), reader(NULL), gain(0),
|
||||
killswitch(0), opened(false),
|
||||
drmProtected(false),
|
||||
volume(-666), pan(0),
|
||||
reader2(0), network(0), playing(false),
|
||||
startAtMilliseconds(0),
|
||||
dspBuffer(0),
|
||||
vizBuffer(0),
|
||||
reader1(0),
|
||||
flushed(false)
|
||||
{
|
||||
killswitch = CreateEvent(NULL, TRUE, TRUE, NULL);
|
||||
}
|
||||
|
||||
WMDRM::~WMDRM()
|
||||
{
|
||||
DeleteObject(killswitch);
|
||||
delete [] dspBuffer;
|
||||
delete [] vizBuffer;
|
||||
}
|
||||
|
||||
static int winampVersion=0;
|
||||
|
||||
#define WINAMP_VERSION_MINOR1(winampVersion) ((winampVersion & 0x000000F0)>>4) // returns, i.e. 0x01 for 5.12 and 0x02 for 5.2...
|
||||
#define WINAMP_VERSION_MINOR2(winampVersion) ((winampVersion & 0x0000000F)) // returns, i.e. 0x02 for 5.12 and 0x00 for 5.2...
|
||||
static void MakeVersionString(QWORD *ver)
|
||||
{
|
||||
if (!winampVersion)
|
||||
winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
|
||||
|
||||
LARGE_INTEGER temp;
|
||||
temp.HighPart = MAKELONG(WINAMP_VERSION_MINOR1(winampVersion), WINAMP_VERSION_MAJOR(winampVersion));
|
||||
temp.LowPart = MAKELONG(WASABI_API_APP->main_getBuildNumber(), WINAMP_VERSION_MINOR2(winampVersion));
|
||||
|
||||
*ver = temp.QuadPart;
|
||||
}
|
||||
|
||||
static void MakeUserAgentString(wchar_t str[256])
|
||||
{
|
||||
if (!winampVersion)
|
||||
winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
|
||||
|
||||
StringCchPrintfW(str, 256, L"WinampASF/%01x.%02x",
|
||||
WINAMP_VERSION_MAJOR(winampVersion),
|
||||
WINAMP_VERSION_MINOR(winampVersion));
|
||||
}
|
||||
|
||||
|
||||
void WMDRM::InitWM()
|
||||
{
|
||||
static int triedInit = 0;
|
||||
if (!triedInit)
|
||||
{
|
||||
if (FAILED(WMCreateReader(0, WMT_RIGHT_PLAYBACK, &reader)) || !reader)
|
||||
{
|
||||
reader = 0;
|
||||
plugin.FileExtensions = "\0";
|
||||
return ;
|
||||
}
|
||||
if (FAILED(reader->QueryInterface(&reader1)))
|
||||
reader1 = 0;
|
||||
|
||||
if (FAILED(reader->QueryInterface(&reader2)))
|
||||
reader2 = 0;
|
||||
|
||||
if (FAILED(reader->QueryInterface(&network)))
|
||||
network = 0;
|
||||
|
||||
if (reader1)
|
||||
{
|
||||
QWORD verStr;
|
||||
wchar_t userAgent[256] = {0};
|
||||
MakeVersionString(&verStr);
|
||||
MakeUserAgentString(userAgent);
|
||||
WM_READER_CLIENTINFO info;
|
||||
ZeroMemory(&info, sizeof(WM_READER_CLIENTINFO));
|
||||
info.cbSize = sizeof(WM_READER_CLIENTINFO);
|
||||
info.wszHostExe = L"winamp.exe";
|
||||
info.qwHostVersion = verStr;
|
||||
info.wszPlayerUserAgent = userAgent;
|
||||
info.wszBrowserWebPage = L"http://www.winamp.com";
|
||||
|
||||
reader1->SetClientInfo(&info);
|
||||
}
|
||||
|
||||
clock = new ClockLayer(reader);
|
||||
audio = new AudioLayer(reader);
|
||||
video = new VideoLayer(reader);
|
||||
wait = new WaitLayer(reader);
|
||||
info = new WMInformation(reader);
|
||||
buffer = new BufferLayer(reader);
|
||||
seek = new SeekLayer(reader, clock);
|
||||
gain = new GainLayer(audio, info);
|
||||
|
||||
callback >> seek >> buffer >> clock >> video >> audio >> wait >> gain >> this;
|
||||
|
||||
triedInit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void WMDRM::Init()
|
||||
{
|
||||
winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
|
||||
InitOutputs(plugin.hMainWindow, plugin.hDllInstance);
|
||||
|
||||
//Hook(plugin.hMainWindow);
|
||||
}
|
||||
|
||||
void WMDRM::Config(HWND hwndParent)
|
||||
{
|
||||
WASABI_API_DIALOGBOXW(IDD_CONFIG, hwndParent, PreferencesDialogProc);
|
||||
}
|
||||
|
||||
void WMDRM::Quit()
|
||||
{
|
||||
activePlaylist.Clear();
|
||||
delete setFileInfo; setFileInfo = 0;
|
||||
delete clock; clock = 0;
|
||||
delete audio; audio = 0;
|
||||
delete video; video = 0;
|
||||
delete wait; wait = 0;
|
||||
delete info; info = 0;
|
||||
delete buffer; buffer = 0;
|
||||
delete seek; seek = 0;
|
||||
|
||||
if (network) network->Release(); network = 0;
|
||||
if (reader2) reader2->Release(); reader2 = 0;
|
||||
if (reader1) reader1->Release(); reader1 = 0;
|
||||
if (reader) reader->Release(); reader = 0;
|
||||
|
||||
// Unhook(plugin.hMainWindow);
|
||||
}
|
||||
|
||||
static void BuildTitle(WMInformation *info, const wchar_t *file, wchar_t *str, size_t len)
|
||||
{
|
||||
if (info)
|
||||
{
|
||||
wchar_t artist[256] = L"", title[256] = L"";
|
||||
info->GetAttribute(g_wszWMAuthor, artist, 256);
|
||||
info->GetAttribute(g_wszWMTitle, title, 256);
|
||||
|
||||
if (!artist[0] && !title[0])
|
||||
{
|
||||
if (file && *file)
|
||||
{
|
||||
StringCchCopy(str, len, file);
|
||||
}
|
||||
}
|
||||
else if (artist[0] && title[0])
|
||||
StringCchPrintf(str, len, L"%s - %s", artist, title);
|
||||
else if (artist[0])
|
||||
StringCchCopy(str, len, artist);
|
||||
else if (title[0])
|
||||
StringCchCopy(str, len, title);
|
||||
}
|
||||
else if (file)
|
||||
StringCchCopy(str, len, file);
|
||||
|
||||
}
|
||||
|
||||
void WMDRM::GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms)
|
||||
{
|
||||
InitWM();
|
||||
|
||||
if (length_in_ms) *length_in_ms = -1000;
|
||||
if (file && file[0])
|
||||
{
|
||||
bool isURL = !!PathIsURL(file);
|
||||
if (config_http_metadata || !isURL)
|
||||
{
|
||||
WMInformation getFileInfo(file, true);
|
||||
|
||||
if (title)
|
||||
{
|
||||
BuildTitle(&getFileInfo, file, title, GETFILEINFO_TITLE_LENGTH);
|
||||
}
|
||||
if (length_in_ms) *length_in_ms = getFileInfo.GetLengthMilliseconds();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (title)
|
||||
StringCchCopy(title, GETFILEINFO_TITLE_LENGTH, file);
|
||||
}
|
||||
winamp.GetStatus(title, 256, file);
|
||||
}
|
||||
else if (activePlaylist.GetFileName())
|
||||
{
|
||||
//isURL = !!wcsstr(activePlaylist.GetFileName(), L"://");
|
||||
if (wait && !wait->IsOpen()) // if it's not open, fill in with some default data... WMDRM::Opened() will refresh the title ...
|
||||
{
|
||||
StringCchCopy(title, GETFILEINFO_TITLE_LENGTH, activePlaylist.GetOriginalFileName());
|
||||
if (length_in_ms) *length_in_ms = -1000;
|
||||
//if (isURL)
|
||||
winamp.GetStatus(title, 256, activePlaylist.GetOriginalFileName());
|
||||
return ;
|
||||
}
|
||||
|
||||
if (title)
|
||||
{
|
||||
BuildTitle(info, activePlaylist.GetOriginalFileName(), title, GETFILEINFO_TITLE_LENGTH);
|
||||
}
|
||||
if (info)
|
||||
if (length_in_ms) *length_in_ms = info->GetLengthMilliseconds();
|
||||
else
|
||||
if (length_in_ms) *length_in_ms = -1000;
|
||||
|
||||
//if (isURL)
|
||||
winamp.GetStatus(title, 256, activePlaylist.GetOriginalFileName());
|
||||
}
|
||||
}
|
||||
|
||||
int WMDRM::InfoBox(const wchar_t *fn, HWND hwndParent)
|
||||
{
|
||||
/* CUT> we're now using the unified file info dialogue
|
||||
FileInfoDialog dialog(WASABI_API_LNG_HINST, hwndParent, fn);
|
||||
if (dialog.WasEdited())
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WMDRM::IsOurFile(const in_char *fn)
|
||||
{
|
||||
// if (!reader)
|
||||
// return 0;
|
||||
if (wcsstr(fn, L".asx")) // TODO: need something WAY better than this
|
||||
return 1;
|
||||
return fileTypes.IsSupportedURL(fn);
|
||||
}
|
||||
|
||||
int WMDRM::Play(const wchar_t * fn)
|
||||
{
|
||||
InitWM();
|
||||
|
||||
if (!reader)
|
||||
return -1;
|
||||
if (network)
|
||||
network->SetBufferingTime((QWORD)config_buffer_time*10000LL);
|
||||
|
||||
ResetEvent(killswitch);
|
||||
wait->ResetForOpen();
|
||||
|
||||
activePlaylist.Clear();
|
||||
activePlaylist.playlistFilename = _wcsdup(fn);
|
||||
if (playlistManager->Load(fn, &activePlaylist) != PLAYLISTMANAGER_SUCCESS)
|
||||
activePlaylist.OnFile(fn, 0, -1, 0); // add it manually (TODO: need a better way to do this)
|
||||
|
||||
winamp.GetVideoOutput();
|
||||
playing = true;
|
||||
startTime = winamp.GetStart() * 1000;
|
||||
if (!startAtMilliseconds)
|
||||
startAtMilliseconds = startTime;
|
||||
|
||||
endTime = winamp.GetEnd() * 1000;
|
||||
clock->SetLastOutputTime(startAtMilliseconds); // normally 0, but set when metadata editor needs to stop / restart a file
|
||||
AssignOutput();
|
||||
clock->SetStartTimeMilliseconds(startAtMilliseconds); // normally 0, but set when metadata editor needs to stop / restart a file
|
||||
startAtMilliseconds = 0;
|
||||
return seek->Open(activePlaylist.GetFileName(), &callback);
|
||||
}
|
||||
|
||||
void WMDRM::ReOpen()
|
||||
{
|
||||
if (opened)
|
||||
seek->Stop();
|
||||
seek->Open(activePlaylist.GetFileName(), &callback);
|
||||
}
|
||||
|
||||
void WMDRM::Pause()
|
||||
{
|
||||
paused = true;
|
||||
if (seek)
|
||||
seek->Pause();
|
||||
}
|
||||
|
||||
void WMDRM::UnPause()
|
||||
{
|
||||
paused = false;
|
||||
if (seek)
|
||||
seek->Unpause();
|
||||
}
|
||||
|
||||
int WMDRM::IsPaused()
|
||||
{
|
||||
return (int)paused;
|
||||
}
|
||||
|
||||
void WMDRM::Stop()
|
||||
{
|
||||
if (!playing)
|
||||
return ;
|
||||
|
||||
playing = false;
|
||||
SetEvent(killswitch);
|
||||
if (paused)
|
||||
UnPause();
|
||||
if (seek)
|
||||
seek->Stop();
|
||||
}
|
||||
|
||||
|
||||
void WMDRM::Closed()
|
||||
{
|
||||
opened = false;
|
||||
WMHandler::Closed();
|
||||
}
|
||||
|
||||
int WMDRM::GetLength()
|
||||
{
|
||||
if (info)
|
||||
return info->GetLengthMilliseconds();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WMDRM::GetOutputTime()
|
||||
{
|
||||
if (!opened)
|
||||
{
|
||||
//if (winamp.bufferCount)
|
||||
return winamp.bufferCount;
|
||||
//return 0;
|
||||
}
|
||||
return clock->GetOutputTime();
|
||||
}
|
||||
|
||||
void WMDRM::SetOutputTime(int time_in_ms)
|
||||
{
|
||||
if (startTime || endTime)
|
||||
{
|
||||
unsigned int seektime = time_in_ms;
|
||||
if (endTime && seektime > endTime)
|
||||
seektime = endTime;
|
||||
if (startTime && seektime < startTime)
|
||||
seektime = startTime;
|
||||
seek->SeekTo(seektime);
|
||||
return ;
|
||||
}
|
||||
seek->SeekTo(time_in_ms);
|
||||
}
|
||||
|
||||
void WMDRM::SetVolume(int volume)
|
||||
{
|
||||
this->volume = volume;
|
||||
if (out)
|
||||
out->SetVolume(volume);
|
||||
}
|
||||
|
||||
void WMDRM::SetPan(int pan)
|
||||
{
|
||||
this->pan = pan;
|
||||
if (out)
|
||||
out->SetPan(pan);
|
||||
}
|
||||
|
||||
void WMDRM::EQSet(int on, char data[10], int preamp)
|
||||
{}
|
||||
|
||||
void WMDRM::BuildBuffers()
|
||||
{
|
||||
remaining.Allocate(audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK));
|
||||
|
||||
// TODO: check against old size
|
||||
delete [] dspBuffer;
|
||||
delete [] vizBuffer;
|
||||
dspBuffer = new unsigned char[audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK) * 2];
|
||||
vizBuffer = new unsigned char[audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK) * 2];
|
||||
}
|
||||
|
||||
void WMDRM::AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp)
|
||||
{
|
||||
// TODO: apply replaygain first
|
||||
// but if we change bitdepth, we'll have to be careful about calling audio->AudioSamplesToBytes() and similiar functions
|
||||
|
||||
unsigned char *data = (unsigned char *)_data;
|
||||
if (!remaining.Empty())
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
remaining.Flush();
|
||||
return ;
|
||||
}
|
||||
remaining.UpdatingWrite(data, sizeBytes);
|
||||
if (remaining.Full())
|
||||
{
|
||||
OutputAudioSamples(remaining.GetData(), SAMPLES_PER_BLOCK, timestamp);
|
||||
remaining.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
long samplesLeft = audio->AudioBytesToSamples(sizeBytes);
|
||||
|
||||
while (samplesLeft)
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
remaining.Flush();
|
||||
return ;
|
||||
}
|
||||
|
||||
if (samplesLeft >= SAMPLES_PER_BLOCK)
|
||||
{
|
||||
OutputAudioSamples(data, SAMPLES_PER_BLOCK, timestamp);
|
||||
data += audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK);
|
||||
samplesLeft -= SAMPLES_PER_BLOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long bytesLeft = audio->AudioSamplesToBytes(samplesLeft);
|
||||
remaining.UpdatingWrite(data, bytesLeft);
|
||||
samplesLeft = audio->AudioBytesToSamples(bytesLeft); // should always be 0
|
||||
assert(samplesLeft == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WMDRM::QuantizedViz(void *data, long sizeBytes, DWORD timestamp)
|
||||
{
|
||||
if (drmProtected)
|
||||
{
|
||||
assert(sizeBytes == audio->Channels() *(audio->BitSize() / 8) * SAMPLES_PER_BLOCK);
|
||||
memset(vizBuffer, 0, sizeBytes);
|
||||
ptrdiff_t stride = audio->BitSize() / 8;
|
||||
size_t position = stride - 1;
|
||||
unsigned char *origData = (unsigned char *)data;
|
||||
for (int i = 0;i < SAMPLES_PER_BLOCK*audio->Channels();i++) // winamp hardcodes this ...
|
||||
{
|
||||
vizBuffer[position] = (origData[position] & 0xFC); // 6 bits of precision, enough for viz.
|
||||
position += stride;
|
||||
}
|
||||
|
||||
plugin.SAAddPCMData((char *) vizBuffer, audio->Channels(), audio->ValidBits(), timestamp);
|
||||
plugin.VSAAddPCMData((char *) vizBuffer, audio->Channels(), audio->ValidBits(), timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.SAAddPCMData((char *) data, audio->Channels(), audio->ValidBits(), timestamp);
|
||||
plugin.VSAAddPCMData((char *) data, audio->Channels(), audio->ValidBits(), timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
long WMDRM::GetPosition()
|
||||
{
|
||||
if (!opened)
|
||||
return 0;
|
||||
return out->GetWrittenTime();
|
||||
}
|
||||
|
||||
void WMDRM::OutputAudioSamples(void *data, long samples)
|
||||
{
|
||||
DWORD timestamp = out->GetWrittenTime();
|
||||
OutputAudioSamples(data, samples, timestamp);
|
||||
}
|
||||
|
||||
void WMDRM::OutputAudioSamples(void *data, long samples, DWORD ×tamp)
|
||||
{
|
||||
clock->SetLastOutputTime(timestamp);
|
||||
timestamp += audio->AudioSamplesToMilliseconds(samples);
|
||||
|
||||
|
||||
//clock->SetLastOutputTime(winamp.GetWrittenTime());
|
||||
|
||||
//in theory, we could check mod->dsp_isactive(), but that opens up a potential race condition ...
|
||||
memcpy(dspBuffer, data, audio->AudioSamplesToBytes(samples));
|
||||
int dspSize = samples;
|
||||
if (!drmProtected)
|
||||
dspSize = plugin.dsp_dosamples((short *)dspBuffer, samples, audio->BitSize(), audio->Channels(), audio->SampleRate());
|
||||
dspSize = audio->AudioSamplesToBytes(dspSize);
|
||||
if (samples == SAMPLES_PER_BLOCK)
|
||||
QuantizedViz(dspBuffer, dspSize, timestamp);
|
||||
while (out->CanWrite() <= dspSize)
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
|
||||
{
|
||||
remaining.Flush();
|
||||
return ;
|
||||
}
|
||||
}
|
||||
out->Write((char *)dspBuffer, dspSize);
|
||||
/*long bytesAvail = */out->CanWrite();
|
||||
}
|
||||
|
||||
void WMDRM::Opened()
|
||||
{
|
||||
//winamp.ResetBuffering();
|
||||
drmProtected = info->IsAttribute(g_wszWMProtected);
|
||||
ResetEvent(killswitch);
|
||||
if (!audio->IsOpen())
|
||||
{
|
||||
if (video->IsOpen())
|
||||
{
|
||||
winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_REALTIME));
|
||||
clock->GoRealTime();
|
||||
plugin.is_seekable = info->IsSeekable() ? 1 : 0;
|
||||
winamp.SetAudioInfo(info->GetBitrate() / 1000, 0, 0);
|
||||
//out->SetVolume( -666); // set default volume
|
||||
}
|
||||
else
|
||||
{
|
||||
// no audio or video!!
|
||||
seek->Stop();
|
||||
First().OpenFailed();
|
||||
return ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildBuffers();
|
||||
plugin.is_seekable = info->IsSeekable() ? 1 : 0;
|
||||
winamp.SetAudioInfo(info->GetBitrate() / 1000, audio->SampleRate() / 1000, audio->Channels());
|
||||
out->SetVolume(volume); // set default volume
|
||||
out->SetPan(pan);
|
||||
winamp.SetVizInfo(audio->SampleRate(), audio->Channels());
|
||||
}
|
||||
opened = true;
|
||||
winamp.ClearStatus();
|
||||
reader->Start(clock->GetStartTime(), 0, 1.0f, NULL);
|
||||
WMHandler::Opened();
|
||||
|
||||
}
|
||||
|
||||
void WMDRM::Started()
|
||||
{
|
||||
ResetEvent(killswitch);
|
||||
winamp.ResetBuffering();
|
||||
winamp.ClearStatus();
|
||||
WMHandler::Started();
|
||||
}
|
||||
void WMDRM::EndOfFile()
|
||||
{
|
||||
if (audio->IsOpen())
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
if (remaining.used)
|
||||
{
|
||||
OutputAudioSamples(remaining.GetData(), audio->AudioBytesToSamples(remaining.used));
|
||||
remaining.Flush();
|
||||
}
|
||||
out->Write(0, 0);
|
||||
while (out->IsPlaying())
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: if we have a playlist, start the next track instead of telling winamp to go to the next track
|
||||
if (playing)
|
||||
winamp.EndOfFile();
|
||||
WMHandler::EndOfFile();
|
||||
}
|
||||
|
||||
void WMDRM::NewMetadata()
|
||||
{
|
||||
winamp.RefreshTitle();
|
||||
WMHandler::NewMetadata();
|
||||
}
|
||||
|
||||
void WMDRM::Error()
|
||||
{
|
||||
// wait 200 ms for the killswitch (aka hitting stop)
|
||||
// this allows the user to hit "stop" and not have to continue cycling through songs if there are a whole bunch of bad/missing WMAs in the playlist
|
||||
if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
|
||||
winamp.EndOfFile();
|
||||
}
|
||||
|
||||
void WMDRM::OpenFailed()
|
||||
{
|
||||
if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0) // wait 200 ms for the killswitch (see above notes)
|
||||
winamp.EndOfFile();
|
||||
}
|
||||
|
||||
void WMDRM::Stopped()
|
||||
{
|
||||
remaining.Flush();
|
||||
WMHandler::Stopped();
|
||||
}
|
||||
|
||||
void WMDRM::Kill()
|
||||
{
|
||||
SetEvent(killswitch);
|
||||
WMHandler::Kill();
|
||||
}
|
||||
|
||||
void WMDRM::NewSourceFlags()
|
||||
{
|
||||
plugin.is_seekable = info->IsSeekable() ? 1 : 0;
|
||||
}
|
||||
|
||||
void WMDRM::Connecting()
|
||||
{
|
||||
winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_CONNECTING));
|
||||
WMHandler::Connecting();
|
||||
}
|
||||
|
||||
void WMDRM::Locating()
|
||||
{
|
||||
winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_LOCATING));
|
||||
WMHandler::Locating();
|
||||
}
|
||||
|
||||
void WMDRM::AccessDenied()
|
||||
{
|
||||
winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_ACCESS_DENIED));
|
||||
if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0) // wait 200 ms for the killswitch (see above notes)
|
||||
winamp.PressStop();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef NULLSOFT_WMDRMMODULEH
|
||||
#define NULLSOFT_WMDRMMODULEH
|
||||
|
||||
#include "Remaining.h"
|
||||
#include <wmsdk.h>
|
||||
// layers
|
||||
#include "AudioLayer.h"
|
||||
#include "VideoLayer.h"
|
||||
#include "ClockLayer.h"
|
||||
#include "WaitLayer.h"
|
||||
#include "BufferLayer.h"
|
||||
#include "SeekLayer.h"
|
||||
#include "GainLayer.h"
|
||||
|
||||
#include "WMHandler.h"
|
||||
#include "WMCallback.h"
|
||||
#include "WMInformation.h"
|
||||
|
||||
class WMDRM : public WMHandler
|
||||
{
|
||||
public:
|
||||
WMDRM();
|
||||
~WMDRM();
|
||||
void Config(HWND hwndParent);
|
||||
void Init();
|
||||
void Quit();
|
||||
void GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms);
|
||||
int InfoBox(const wchar_t *file, HWND hwndParent);
|
||||
int IsOurFile(const wchar_t *fn);
|
||||
int Play(const wchar_t *fn);
|
||||
void Pause();
|
||||
void UnPause();
|
||||
int IsPaused();
|
||||
void Stop();
|
||||
int GetLength();
|
||||
int GetOutputTime();
|
||||
void SetOutputTime(int time_in_ms);
|
||||
void SetVolume(int volume);
|
||||
void SetPan(int pan);
|
||||
int GetVolume() { return volume; }
|
||||
int GetPan() { return pan; }
|
||||
void EQSet(int on, char data[10], int preamp);
|
||||
void BuildBuffers();
|
||||
void OutputAudioSamples(void *data, long samples, DWORD&);
|
||||
void OutputAudioSamples(void *data, long samples);
|
||||
void QuantizedViz(void *data, long sizeBytes, DWORD);
|
||||
long GetPosition();
|
||||
void EndOfFile();
|
||||
bool OpenVideo(int fourcc, int width, int height, bool flipped);
|
||||
void ReOpen();
|
||||
void NewSourceFlags();
|
||||
// const char *GetFile() { return fn.c_str();}
|
||||
bool playing;
|
||||
int startAtMilliseconds;
|
||||
void InitWM();
|
||||
protected:
|
||||
//WMHandler
|
||||
void AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp);
|
||||
void Opened();
|
||||
void NewMetadata();
|
||||
void Closed();
|
||||
void Started();
|
||||
void Error();
|
||||
void OpenFailed();
|
||||
void Stopped();
|
||||
void Kill();
|
||||
|
||||
BufferLayer *buffer;
|
||||
ClockLayer *clock;
|
||||
#ifndef NO_DRM
|
||||
DRMLayer *drm;
|
||||
#endif
|
||||
AudioLayer *audio;
|
||||
VideoLayer *video;
|
||||
WaitLayer *wait;
|
||||
SeekLayer *seek;
|
||||
GainLayer *gain;
|
||||
WMCallback callback;
|
||||
IWMReader *reader;
|
||||
IWMReaderAdvanced *reader1;
|
||||
IWMReaderAdvanced2 *reader2;
|
||||
IWMReaderNetworkConfig *network;
|
||||
WMInformation *info;
|
||||
Remaining remaining;
|
||||
unsigned char *dspBuffer, *vizBuffer;
|
||||
int volume, pan;
|
||||
bool flushed, paused;
|
||||
|
||||
HANDLE killswitch;
|
||||
|
||||
bool opened;
|
||||
bool drmProtected;
|
||||
void Connecting();
|
||||
void Locating();
|
||||
void AssignOutput();
|
||||
void AccessDenied();
|
||||
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,181 @@
|
||||
#include "main.h"
|
||||
#include <assert.h>
|
||||
|
||||
void WMHandler::OpenFailed()
|
||||
{
|
||||
if (next)
|
||||
next->OpenFailed();
|
||||
}
|
||||
|
||||
void WMHandler::ReOpen()
|
||||
{
|
||||
if (next)
|
||||
next->ReOpen();
|
||||
}
|
||||
|
||||
void WMHandler::Started()
|
||||
{
|
||||
if (next)
|
||||
next->Started();
|
||||
}
|
||||
|
||||
void WMHandler::Stopped()
|
||||
{
|
||||
if (next)
|
||||
next->Stopped();
|
||||
}
|
||||
|
||||
void WMHandler::PreRollComplete()
|
||||
{
|
||||
if (next)
|
||||
next->PreRollComplete();
|
||||
}
|
||||
|
||||
void WMHandler::EndOfFile()
|
||||
{
|
||||
if (next)
|
||||
next->EndOfFile();
|
||||
}
|
||||
|
||||
void WMHandler::Closed()
|
||||
{
|
||||
if (next)
|
||||
next->Closed();
|
||||
}
|
||||
|
||||
void WMHandler::BufferingStarted()
|
||||
{
|
||||
if (next)
|
||||
next->BufferingStarted();
|
||||
}
|
||||
|
||||
void WMHandler::BufferingStopped()
|
||||
{
|
||||
if (next)
|
||||
next->BufferingStopped();
|
||||
}
|
||||
|
||||
void WMHandler::NewMetadata()
|
||||
{
|
||||
if (next)
|
||||
next->NewMetadata();
|
||||
}
|
||||
|
||||
void WMHandler::Individualize()
|
||||
{
|
||||
if (next)
|
||||
next->Individualize();
|
||||
}
|
||||
|
||||
void WMHandler::SignatureState(WMT_DRMLA_TRUST *&state)
|
||||
{
|
||||
if (next)
|
||||
next->SignatureState(state);
|
||||
}
|
||||
|
||||
void WMHandler::NoRightsEx(WM_GET_LICENSE_DATA *&licenseData)
|
||||
{
|
||||
if (next)
|
||||
next->NoRightsEx(licenseData);
|
||||
}
|
||||
|
||||
void WMHandler::AcquireLicense(WM_GET_LICENSE_DATA *&licenseData)
|
||||
{
|
||||
if (next)
|
||||
next->AcquireLicense(licenseData);
|
||||
}
|
||||
|
||||
void WMHandler::AllocateOutput(long outputNum, long bufferSize, INSSBuffer *&buffer)
|
||||
{
|
||||
if (next)
|
||||
next->AllocateOutput(outputNum, bufferSize, buffer);
|
||||
}
|
||||
|
||||
void WMHandler::VideoCatchup(QWORD time)
|
||||
{
|
||||
if (next)
|
||||
next->VideoCatchup(time);
|
||||
}
|
||||
|
||||
void WMHandler::TimeToSync(QWORD timeStamp,__int64 &diff)
|
||||
{
|
||||
if (next)
|
||||
next->TimeToSync(timeStamp, diff);
|
||||
}
|
||||
|
||||
void WMHandler::Error()
|
||||
{
|
||||
if (next)
|
||||
next->Error();
|
||||
}
|
||||
|
||||
void WMHandler::LicenseRequired()
|
||||
{
|
||||
if (next)
|
||||
next->LicenseRequired();
|
||||
}
|
||||
|
||||
void WMHandler::NoRights(wchar_t *licenseData)
|
||||
{
|
||||
if (next)
|
||||
next->NoRights(licenseData);
|
||||
}
|
||||
|
||||
|
||||
WMHandler::WMHandler() : next(0), prev(0)
|
||||
{}
|
||||
WMHandler::~WMHandler()
|
||||
{
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
|
||||
}
|
||||
|
||||
WMHandler &WMHandler::operator << (WMHandler &chain)
|
||||
{
|
||||
assert(chain.next == 0);
|
||||
assert(prev == 0);
|
||||
|
||||
prev = &chain;
|
||||
chain.next = this;
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
WMHandler &WMHandler::operator >> (WMHandler &chain)
|
||||
{
|
||||
if (chain.prev)
|
||||
{
|
||||
operator >>(chain.prev);
|
||||
return chain;
|
||||
}
|
||||
|
||||
assert (next == 0);
|
||||
assert (chain.prev == 0);
|
||||
|
||||
next = &chain;
|
||||
chain.prev = this;
|
||||
|
||||
return chain;
|
||||
}
|
||||
WMHandler&WMHandler::operator << (WMHandler *chain)
|
||||
{
|
||||
return operator <<(*chain);
|
||||
}
|
||||
|
||||
WMHandler &WMHandler::operator >> (WMHandler *chain)
|
||||
{
|
||||
return operator >>(*chain);
|
||||
}
|
||||
|
||||
WMHandler &WMHandler::First()
|
||||
{
|
||||
if (prev)
|
||||
return prev->First();
|
||||
else
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef NULLSOFT_WMHANDLERH
|
||||
#define NULLSOFT_WMHANDLERH
|
||||
#include <wmsdk.h>
|
||||
|
||||
#define NEXT(x) { if (next) next->x; }
|
||||
|
||||
enum DRM_INDIVIDUALIZATION_STATUS {
|
||||
INDI_UNDEFINED = 0x0000,
|
||||
INDI_BEGIN = 0x0001,
|
||||
INDI_SUCCEED = 0x0002,
|
||||
INDI_FAIL = 0x0004,
|
||||
INDI_CANCEL = 0x0008,
|
||||
INDI_DOWNLOAD = 0x0010,
|
||||
INDI_INSTALL = 0x0020
|
||||
};
|
||||
|
||||
enum DRM_HTTP_STATUS {
|
||||
HTTP_NOTINITIATED = 0,
|
||||
HTTP_CONNECTING = 1,
|
||||
HTTP_REQUESTING = 2,
|
||||
HTTP_RECEIVING = 3,
|
||||
HTTP_COMPLETED = 4
|
||||
};
|
||||
|
||||
typedef struct _WMGetLicenseData {
|
||||
DWORD dwSize;
|
||||
HRESULT hr;
|
||||
WCHAR* wszURL;
|
||||
WCHAR* wszLocalFilename;
|
||||
BYTE* pbPostData;
|
||||
DWORD dwPostDataSize;
|
||||
} WM_GET_LICENSE_DATA;
|
||||
|
||||
|
||||
typedef struct _WMIndividualizeStatus {
|
||||
HRESULT hr;
|
||||
DRM_INDIVIDUALIZATION_STATUS enIndiStatus;
|
||||
LPSTR pszIndiRespUrl;
|
||||
DWORD dwHTTPRequest;
|
||||
DRM_HTTP_STATUS enHTTPStatus;
|
||||
DWORD dwHTTPReadProgress;
|
||||
DWORD dwHTTPReadTotal;
|
||||
} WM_INDIVIDUALIZE_STATUS;
|
||||
|
||||
class WMHandler //: public Chainable<WMHandler>
|
||||
{
|
||||
public:
|
||||
WMHandler();
|
||||
~WMHandler();
|
||||
WMHandler &operator << (WMHandler &chain);
|
||||
WMHandler &operator >> (WMHandler &chain);
|
||||
WMHandler&operator << (WMHandler *chain);
|
||||
WMHandler &operator >> (WMHandler *chain);
|
||||
WMHandler &First();
|
||||
|
||||
virtual void Opened() NEXT(Opened())
|
||||
virtual void OpenFailed();
|
||||
virtual void ReOpen();
|
||||
|
||||
virtual void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
|
||||
NEXT(SampleReceived(timeStamp, duration, outputNum, flags, sample))
|
||||
|
||||
virtual void AudioDataReceived(void *data, unsigned long sizeBytes, DWORD timestamp)
|
||||
NEXT(AudioDataReceived(data, sizeBytes, timestamp))
|
||||
|
||||
virtual void TimeReached(QWORD &timeReached) NEXT(TimeReached(timeReached))
|
||||
virtual void NewSourceFlags() NEXT(NewSourceFlags())
|
||||
virtual void HasVideo(bool &video) NEXT(HasVideo(video))
|
||||
virtual void Started();
|
||||
virtual void Stopped();
|
||||
virtual void Stopping() NEXT(Stopping())
|
||||
virtual void DRMExpired() NEXT(DRMExpired())
|
||||
|
||||
virtual void Error();
|
||||
|
||||
virtual void Kill() NEXT(Kill())
|
||||
virtual void PreRollComplete();
|
||||
|
||||
virtual void EndOfFile();
|
||||
virtual void Closed();
|
||||
virtual void BufferingStarted();
|
||||
virtual void BufferingStopped();
|
||||
virtual void NewMetadata();
|
||||
virtual void Connecting() NEXT(Connecting())
|
||||
virtual void Locating() NEXT(Locating())
|
||||
|
||||
virtual void Individualize();
|
||||
virtual void NeedsIndividualization() NEXT(NeedsIndividualization())
|
||||
virtual void IndividualizeStatus(WM_INDIVIDUALIZE_STATUS *status) NEXT(IndividualizeStatus(status))
|
||||
|
||||
virtual void SignatureState(WMT_DRMLA_TRUST *&state);
|
||||
virtual void NoRights(wchar_t *licenseData);
|
||||
virtual void NoRightsEx(WM_GET_LICENSE_DATA *&licenseData);
|
||||
virtual void AcquireLicense(WM_GET_LICENSE_DATA *&licenseData);
|
||||
virtual void LicenseRequired();
|
||||
virtual void BrowserClosed() NEXT(BrowserClosed())
|
||||
virtual void LicenseAcquired() NEXT(LicenseAcquired())
|
||||
virtual void AllocateOutput(long outputNum, long bufferSize, INSSBuffer *&buffer);
|
||||
virtual void MonitorCancelled() NEXT(MonitorCancelled())
|
||||
virtual void SilentCancelled() NEXT(SilentCancelled())
|
||||
|
||||
virtual void VideoCatchup(QWORD time);
|
||||
virtual void TimeToSync(QWORD timeStamp, __int64 &diff);
|
||||
virtual void OpenCalled() NEXT(OpenCalled())
|
||||
|
||||
virtual void InitPlaylistBurn() NEXT(InitPlaylistBurn())
|
||||
virtual void AccessDenied() NEXT(AccessDenied())
|
||||
|
||||
private:
|
||||
WMHandler *next, *prev;
|
||||
};
|
||||
#undef NEXT
|
||||
#endif
|
||||
@@ -0,0 +1,880 @@
|
||||
#include "main.h"
|
||||
#include "WMInformation.h"
|
||||
#include "resource.h"
|
||||
#include <exception>
|
||||
#include <strsafe.h>
|
||||
|
||||
class AutoByte
|
||||
{
|
||||
public:
|
||||
AutoByte(size_t bytes)
|
||||
: data(0)
|
||||
{
|
||||
data = new BYTE[bytes];
|
||||
}
|
||||
~AutoByte()
|
||||
{
|
||||
if (data)
|
||||
delete[] data;
|
||||
data = 0;
|
||||
}
|
||||
operator void *()
|
||||
{
|
||||
return (void *)data;
|
||||
}
|
||||
|
||||
BYTE *data;
|
||||
};
|
||||
|
||||
static void StoreData(WMT_ATTR_DATATYPE type, BYTE *value, DWORD length, wchar_t *valueStr, size_t len)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case WMT_TYPE_DWORD:
|
||||
StringCchPrintf(valueStr, len, L"%lu", *(DWORD *)value);
|
||||
break;
|
||||
|
||||
case WMT_TYPE_STRING:
|
||||
lstrcpyn(valueStr, (wchar_t *)value, len);
|
||||
break;
|
||||
|
||||
case -1: // hack // if (attrName == L"WM/Text")
|
||||
StringCchPrintf(valueStr, len, L"%s/%s", UserTextDescription(value, length), UserTextString(value, length));
|
||||
break;
|
||||
case WMT_TYPE_BINARY:
|
||||
BinaryString(value, length, valueStr, len);
|
||||
break;
|
||||
case WMT_TYPE_BOOL:
|
||||
if (*(BOOL *)value)
|
||||
{
|
||||
lstrcpyn(valueStr, L"True", len);
|
||||
}
|
||||
else
|
||||
{
|
||||
lstrcpyn(valueStr, L"False", len);
|
||||
}
|
||||
break;
|
||||
case WMT_TYPE_QWORD:
|
||||
StringCchPrintf(valueStr, len, L"%I64u", *(QWORD *)value);
|
||||
break;
|
||||
case WMT_TYPE_WORD:
|
||||
StringCchPrintf(valueStr, len, L"%hu", *(WORD *)value);
|
||||
break;
|
||||
case WMT_TYPE_GUID:
|
||||
GuidString(*(GUID *)value, valueStr, len);
|
||||
break;
|
||||
default:
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN,valueStr,len);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
WMInformation::WMInformation(const wchar_t *fileName, bool noBlock)
|
||||
: editor(0), editor2(0), header(0), header3(0), reader(0), header2(0), openError(false)
|
||||
{
|
||||
if (fileName && fileName[0]
|
||||
&& WMCreateEditor(&editor) == S_OK)
|
||||
{
|
||||
if (SUCCEEDED(editor->QueryInterface(&editor2)))
|
||||
{
|
||||
if (SUCCEEDED(editor2->OpenEx(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE)))
|
||||
{
|
||||
// good to go
|
||||
editor2->QueryInterface(&header);
|
||||
editor->QueryInterface(&header2);
|
||||
editor2->QueryInterface(&header3);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
editor2 = 0;
|
||||
if (SUCCEEDED(editor->Open(fileName)))
|
||||
{
|
||||
// good to go
|
||||
editor->QueryInterface(&header);
|
||||
editor->QueryInterface(&header2);
|
||||
editor->QueryInterface(&header3);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
// can't open it through the metadata editor interface, let's open a reader
|
||||
|
||||
if (editor)
|
||||
editor->Release();
|
||||
editor = 0;
|
||||
if (editor2)
|
||||
editor2->Release();
|
||||
editor2 = 0;
|
||||
if (FAILED(WMCreateReader(0, WMT_RIGHT_PLAYBACK, &reader)))
|
||||
{
|
||||
reader = 0;
|
||||
return ;
|
||||
}
|
||||
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
callback >> this;
|
||||
|
||||
if (FAILED(reader->Open(fileName, &callback, 0)))
|
||||
{
|
||||
reader->Release();
|
||||
reader = 0;
|
||||
return ;
|
||||
}
|
||||
if (noBlock)
|
||||
WaitForEvent(hEvent, INFINITE);
|
||||
else
|
||||
WaitForSingleObject(hEvent, INFINITE);
|
||||
|
||||
CloseHandle(hEvent);
|
||||
if (openError)
|
||||
{
|
||||
reader->Release();
|
||||
reader = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader->QueryInterface(&header);
|
||||
reader->QueryInterface(&header2);
|
||||
reader->QueryInterface(&header3);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
WMInformation::WMInformation(IWMReader *_reader)
|
||||
: reader(0), // reader is if we create an internal reader, we don't want to save the passed one (so we don't close it on someone else :)
|
||||
editor(0), editor2(0), header(0),
|
||||
header3(0), header2(0),
|
||||
openError(false), hEvent(NULL)
|
||||
{
|
||||
if (FAILED(_reader->QueryInterface(&header)))
|
||||
header = 0;
|
||||
if (FAILED(_reader->QueryInterface(&header2)))
|
||||
header2 = 0;
|
||||
if (FAILED(_reader->QueryInterface(&header3)))
|
||||
header3 = 0; // this error is OK, we can deal with it.
|
||||
}
|
||||
|
||||
/*
|
||||
WMInformation::WMInformation(IWMSyncReader *reader)
|
||||
: editor(0), editor2(0), header(0), header3(0)
|
||||
{
|
||||
reader->QueryInterface(&header);
|
||||
reader->QueryInterface(&header3);
|
||||
}*/
|
||||
|
||||
WMInformation::WMInformation(IWMMetadataEditor *_editor)
|
||||
: editor(_editor), editor2(0), header(0),
|
||||
header3(0), reader(0), header2(0),
|
||||
openError(false), hEvent(NULL)
|
||||
{
|
||||
editor->AddRef();
|
||||
editor->QueryInterface(&editor2);
|
||||
editor->QueryInterface(&header);
|
||||
editor->QueryInterface(&header2);
|
||||
editor->QueryInterface(&header3);
|
||||
}
|
||||
|
||||
|
||||
WMInformation::~WMInformation()
|
||||
{
|
||||
if (editor)
|
||||
{
|
||||
editor->Close();
|
||||
editor->Release();
|
||||
editor = 0;
|
||||
}
|
||||
if (editor2)
|
||||
editor2->Release();
|
||||
editor2 = 0;
|
||||
if (header)
|
||||
header->Release();
|
||||
header = 0;
|
||||
if (header2)
|
||||
header2->Release();
|
||||
header2 = 0;
|
||||
if (header3)
|
||||
header3->Release();
|
||||
header3 = 0;
|
||||
if (reader)
|
||||
{
|
||||
reader->Close();
|
||||
reader->Release();
|
||||
reader = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool WMInformation::GetDataType(const wchar_t *name, WMT_ATTR_DATATYPE &type)
|
||||
{
|
||||
if (!name)
|
||||
return false;
|
||||
|
||||
WORD stream = 0;
|
||||
WORD dataLen = 0;
|
||||
if (header && SUCCEEDED(header->GetAttributeByName(&stream, name, &type, 0, &dataLen)))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void WMInformation::DeleteAttribute(const wchar_t *attrName)
|
||||
{
|
||||
|
||||
WORD indexCount = 0;
|
||||
if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, attrName, NULL, 0, &indexCount)))
|
||||
{
|
||||
WORD *indices = new WORD[indexCount];
|
||||
if (SUCCEEDED(header3->GetAttributeIndices(0, attrName, NULL, indices, &indexCount)))
|
||||
{
|
||||
for (size_t i = 0;i != indexCount;i++)
|
||||
{
|
||||
header3->DeleteAttribute(0, indices[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WMInformation::SetAttribute_BinString(const wchar_t *attrName, wchar_t *value)
|
||||
{
|
||||
if (!header || !attrName || !value)
|
||||
return ;
|
||||
|
||||
if (!*value)
|
||||
{
|
||||
DeleteAttribute(attrName);
|
||||
return ;
|
||||
}
|
||||
|
||||
AutoChar data(value);
|
||||
header->SetAttribute(0, attrName, WMT_TYPE_BINARY, (BYTE *)(char *)data, (WORD)strlen(data));
|
||||
}
|
||||
|
||||
void WMInformation::GetAttribute_BinString(const wchar_t attrName[], wchar_t *valueStr, size_t len)
|
||||
{
|
||||
if (!header)
|
||||
{
|
||||
valueStr[0]=0;
|
||||
return ;
|
||||
}
|
||||
|
||||
WMT_ATTR_DATATYPE type;
|
||||
WORD length = 0;
|
||||
HRESULT hr;
|
||||
WORD streamNum = 0;
|
||||
if (!header || FAILED(header->GetAttributeByName(&streamNum,
|
||||
attrName,
|
||||
&type,
|
||||
0,
|
||||
&length)))
|
||||
{
|
||||
valueStr[0]=0;
|
||||
return ;
|
||||
}
|
||||
AutoByte v(length);
|
||||
BYTE *value = v.data;
|
||||
|
||||
hr = header->GetAttributeByName(&streamNum,
|
||||
attrName,
|
||||
&type,
|
||||
value,
|
||||
&length);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
valueStr[0]=0;
|
||||
return ;
|
||||
}
|
||||
|
||||
|
||||
int converted = MultiByteToWideChar(CP_ACP, 0, (const char *)value, length, valueStr, len-1);
|
||||
valueStr[converted]=0;
|
||||
}
|
||||
|
||||
void WMInformation::SetAttribute(const wchar_t *attrName, wchar_t *value, WMT_ATTR_DATATYPE defaultType)
|
||||
{
|
||||
if (!header || !attrName || !value)
|
||||
return ;
|
||||
|
||||
if (!*value)
|
||||
{
|
||||
DeleteAttribute(attrName);
|
||||
return ;
|
||||
}
|
||||
|
||||
WMT_ATTR_DATATYPE type;
|
||||
|
||||
if (!GetDataType(attrName, type))
|
||||
type = defaultType;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case WMT_TYPE_DWORD:
|
||||
{
|
||||
DWORD dwordValue = wcstoul(value, 0, 10);
|
||||
header->SetAttribute(0, attrName, WMT_TYPE_DWORD, (BYTE *) &dwordValue, sizeof(dwordValue));
|
||||
}
|
||||
break;
|
||||
case WMT_TYPE_STRING:
|
||||
{
|
||||
WORD size = static_cast<WORD>((lstrlen(value) + 1) * sizeof(wchar_t));
|
||||
header->SetAttribute(0, attrName, WMT_TYPE_STRING, (BYTE *)value, size);
|
||||
}
|
||||
break;
|
||||
case WMT_TYPE_BINARY:
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
break;
|
||||
case WMT_TYPE_BOOL:
|
||||
{
|
||||
BOOL boolValue;
|
||||
if (!_wcsicmp(L"true", value))
|
||||
boolValue = TRUE;
|
||||
else
|
||||
boolValue = FALSE;
|
||||
|
||||
header->SetAttribute(0, attrName, WMT_TYPE_BOOL, (BYTE *)&boolValue, sizeof(boolValue));
|
||||
}
|
||||
break;
|
||||
case WMT_TYPE_QWORD:
|
||||
{
|
||||
{
|
||||
QWORD qwordValue = _wcstoui64(value, 0, 10);
|
||||
header->SetAttribute(0, attrName, WMT_TYPE_QWORD, (BYTE *) &qwordValue, sizeof(qwordValue));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WMT_TYPE_WORD:
|
||||
{
|
||||
{
|
||||
WORD wordValue = static_cast<WORD>(wcstoul(value, 0, 10));
|
||||
header->SetAttribute(0, attrName, WMT_TYPE_WORD, (BYTE *) &wordValue, sizeof(wordValue));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WMT_TYPE_GUID:
|
||||
{
|
||||
GUID guidValue = StringGUID(value);
|
||||
header->SetAttribute(0, attrName, WMT_TYPE_GUID, (BYTE *) &guidValue, sizeof(guidValue));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool WMInformation::GetAttributeSize(const wchar_t *name, size_t &size)
|
||||
{
|
||||
WORD stream = 0;
|
||||
WORD resultSize;
|
||||
WMT_ATTR_DATATYPE type;
|
||||
|
||||
if (!header || FAILED(header->GetAttributeByName(&stream, name, &type, 0, &resultSize)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
size = resultSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD WMInformation::GetDWORDAttr(const wchar_t name[])
|
||||
{
|
||||
WORD stream = 0;
|
||||
DWORD result;
|
||||
|
||||
WORD resultSizeWord = sizeof(result);
|
||||
DWORD resultSize = sizeof(result);
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_DWORD;
|
||||
WORD count = 1;
|
||||
WORD indices[1] = {0};
|
||||
if ((!header3
|
||||
|| FAILED(header3->GetAttributeIndices(0, name, NULL, indices, &count))
|
||||
|| FAILED(header3->GetAttributeByIndexEx(0, indices[0], 0, 0, &type, NULL, (BYTE *) &result, &resultSize)))
|
||||
&&
|
||||
(!header || FAILED(header->GetAttributeByName(&stream, name, &type, (BYTE *)&result, &resultSizeWord))))
|
||||
return 0;
|
||||
else
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
long WMInformation::GetLongAttr(const wchar_t name[])
|
||||
{
|
||||
WORD stream = 0;
|
||||
long result;
|
||||
WORD resultSize = sizeof(result);
|
||||
WMT_ATTR_DATATYPE type;
|
||||
|
||||
if (!header || FAILED(header->GetAttributeByName(&stream, name, &type, (BYTE *)&result, &resultSize)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WMInformation::GetBoolAttr(const wchar_t name[])
|
||||
{
|
||||
WORD stream = 0;
|
||||
BOOL result;
|
||||
WORD resultSize = sizeof(result);
|
||||
WMT_ATTR_DATATYPE type;
|
||||
|
||||
if (!header || FAILED(header->GetAttributeByName(&stream, name, &type, (BYTE *)&result, &resultSize)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!result;
|
||||
}
|
||||
|
||||
bool WMInformation::IsSeekable()
|
||||
{
|
||||
return GetBoolAttr(g_wszWMSeekable);
|
||||
}
|
||||
|
||||
long WMInformation::GetLengthMilliseconds()
|
||||
{
|
||||
WORD stream = 0;
|
||||
long long duration = 0;
|
||||
WORD resultSize = sizeof(duration);
|
||||
WMT_ATTR_DATATYPE type;
|
||||
|
||||
if (!header || FAILED(header->GetAttributeByName(&stream, g_wszWMDuration, &type, (BYTE *)&duration, &resultSize)))
|
||||
{
|
||||
return -1000;
|
||||
}
|
||||
|
||||
duration /= 10000LL;
|
||||
return (long)duration;
|
||||
}
|
||||
|
||||
long WMInformation::GetBitrate()
|
||||
{
|
||||
return GetDWORDAttr(g_wszWMCurrentBitrate);
|
||||
}
|
||||
|
||||
WORD WMInformation::GetNumberAttributes()
|
||||
{
|
||||
WORD numAttr = 0;
|
||||
if ((!header3 || FAILED(header3->GetAttributeCountEx(0, &numAttr)))
|
||||
&& (!header || FAILED(header->GetAttributeCount(0, &numAttr))))
|
||||
return 0;
|
||||
else
|
||||
return numAttr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WMInformation::GetAttribute(WORD index, wchar_t *attrName, size_t attrLen, wchar_t *valueStr, size_t valueStrLen)
|
||||
{
|
||||
wchar_t _attrName[1025] = {0};
|
||||
WORD nameLen = sizeof(_attrName) / sizeof(_attrName[0]);
|
||||
WMT_ATTR_DATATYPE type;
|
||||
WORD lang;
|
||||
WORD stream = 0;
|
||||
DWORD length = 0;
|
||||
WORD lengthWord = 0;
|
||||
|
||||
if ((!header3 || FAILED(header3->GetAttributeByIndexEx(0, index, _attrName, &nameLen, &type, &lang, 0, &length)))
|
||||
&& (!header || FAILED(header->GetAttributeByIndex(index, &stream, _attrName, &nameLen, &type, 0, &lengthWord))))
|
||||
{
|
||||
attrName[0]=0;
|
||||
valueStr[0]=0;
|
||||
return ;
|
||||
}
|
||||
if (lengthWord)
|
||||
length = lengthWord;
|
||||
|
||||
AutoByte v(length);
|
||||
BYTE *value = v.data;
|
||||
|
||||
lstrcpyn(attrName, _attrName, attrLen);
|
||||
if ((!header3 || FAILED(header3->GetAttributeByIndexEx(0, index, _attrName, &nameLen, &type, &lang, value, &length)))
|
||||
&& (!header || FAILED(header->GetAttributeByIndex(index, &stream, _attrName, &nameLen, &type, value, &lengthWord))))
|
||||
{
|
||||
attrName[0]=0;
|
||||
valueStr[0]=0;
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
if (attrName == L"WM/Text")
|
||||
{
|
||||
type = (WMT_ATTR_DATATYPE)-1; // hack
|
||||
StringCchCat(attrName, attrLen, L":");
|
||||
StringCchCat(attrName, attrLen, UserTextDescription(value, length));
|
||||
}
|
||||
StoreData(type, value, length, valueStr, valueStrLen);
|
||||
}
|
||||
|
||||
void WMInformation::GetAttribute(const wchar_t attrName[], wchar_t *valueStr, size_t len)
|
||||
{
|
||||
if (!header)
|
||||
{
|
||||
valueStr[0]=0;
|
||||
return ;
|
||||
}
|
||||
|
||||
WMT_ATTR_DATATYPE type;
|
||||
WORD length = 0;
|
||||
HRESULT hr;
|
||||
WORD streamNum = 0;
|
||||
if (!header || FAILED(header->GetAttributeByName(&streamNum,
|
||||
attrName,
|
||||
&type,
|
||||
0,
|
||||
&length)))
|
||||
{
|
||||
valueStr[0]=0;
|
||||
return ;
|
||||
}
|
||||
AutoByte v(length);
|
||||
BYTE *value = v.data;
|
||||
|
||||
hr = header->GetAttributeByName(&streamNum,
|
||||
attrName,
|
||||
&type,
|
||||
value,
|
||||
&length);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
valueStr[0]=0;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (attrName == L"WM/Text")
|
||||
type = (WMT_ATTR_DATATYPE)-1; // hack
|
||||
StoreData(type, value, length, valueStr, len);
|
||||
}
|
||||
|
||||
|
||||
bool WMInformation::MakeWritable(const wchar_t *fileName)
|
||||
{
|
||||
if (!editor || !editor2)
|
||||
return false;
|
||||
|
||||
if (FAILED(editor2->OpenEx(fileName, GENERIC_READ | GENERIC_WRITE, 0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WMInformation::Flush()
|
||||
{
|
||||
if (!editor2 || FAILED(editor->Flush()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WMInformation::IsAttribute(const wchar_t attrName[])
|
||||
{
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
|
||||
WORD length = sizeof(BOOL);
|
||||
WORD streamNum = 0;
|
||||
BOOL value;
|
||||
if (!header || FAILED(header->GetAttributeByName(&streamNum,
|
||||
attrName,
|
||||
&type,
|
||||
(BYTE *)&value,
|
||||
&length)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !!value;
|
||||
}
|
||||
}
|
||||
|
||||
bool WMInformation::IsNotAttribute(const wchar_t attrName[])
|
||||
{
|
||||
if (!header)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
|
||||
WORD length = sizeof(BOOL);
|
||||
WORD streamNum = 0;
|
||||
BOOL value;
|
||||
if (!header || FAILED(header->GetAttributeByName(&streamNum,
|
||||
attrName,
|
||||
&type,
|
||||
(BYTE *)&value,
|
||||
&length)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !value;
|
||||
}
|
||||
}
|
||||
|
||||
bool WMInformation::MakeReadOnly(const wchar_t *fileName)
|
||||
{
|
||||
if (!editor || !editor2)
|
||||
return false;
|
||||
|
||||
//editor->Close();
|
||||
if (FAILED(editor2->OpenEx(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WMInformation::NonWritable()
|
||||
{
|
||||
if (!editor2)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void WMInformation::DeleteUserText(const wchar_t *description)
|
||||
{
|
||||
WORD indexCount = 0;
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
|
||||
WORD nameLen = 128;
|
||||
if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, L"WM/Text", NULL, 0, &indexCount)))
|
||||
{
|
||||
WORD *indices = new WORD[indexCount];
|
||||
if (SUCCEEDED(header3->GetAttributeIndices(0, L"WM/Text", NULL, indices, &indexCount)))
|
||||
{
|
||||
for (size_t index = 0;index != indexCount;index++)
|
||||
{
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
|
||||
WORD lang = 0;
|
||||
DWORD length = 0;
|
||||
wchar_t _attrName[128] = L"WM/Text";
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
|
||||
{
|
||||
AutoByte v(length);
|
||||
BYTE *value = v.data;
|
||||
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
|
||||
{
|
||||
if (UserTextDescription(value, length) == description)
|
||||
{
|
||||
header3->DeleteAttribute(0, indices[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WMInformation::SetUserText(const wchar_t *description, const wchar_t *valueStr)
|
||||
{
|
||||
if (!header3 || !description || !valueStr)
|
||||
return;
|
||||
|
||||
WM_USER_TEXT userText;
|
||||
userText.pwszDescription = (LPWSTR)description;
|
||||
userText.pwszText = (LPWSTR) valueStr;
|
||||
|
||||
WORD index;
|
||||
header3->AddAttribute(0, L"WM/Text", &index, WMT_TYPE_BINARY, 0, (BYTE *) &userText, sizeof(userText));
|
||||
}
|
||||
|
||||
void WMInformation::ClearAllAttributes()
|
||||
{
|
||||
WORD numAttrs;
|
||||
header3->GetAttributeCountEx(0xFFFF, &numAttrs);
|
||||
while (numAttrs--)
|
||||
{
|
||||
header3->DeleteAttribute(0xFFFF, numAttrs);
|
||||
}
|
||||
}
|
||||
|
||||
bool WMInformation::GetCodecName(wchar_t *storage, size_t len)
|
||||
{
|
||||
if (!header2)
|
||||
return false;
|
||||
|
||||
DWORD codecs=0;
|
||||
header2->GetCodecInfoCount(&codecs);
|
||||
for (DWORD i=0;i!=codecs;i++)
|
||||
{
|
||||
WORD nameLen=0, descriptionLen=0, infoLen = 0;
|
||||
WMT_CODEC_INFO_TYPE type;
|
||||
header2->GetCodecInfo(i, &nameLen, 0, &descriptionLen, 0, &type, &infoLen, 0);
|
||||
if (type == WMT_CODECINFO_AUDIO)
|
||||
{
|
||||
wchar_t *name = new wchar_t[nameLen];
|
||||
wchar_t *description = new wchar_t[descriptionLen];
|
||||
BYTE *info = new BYTE[infoLen];
|
||||
header2->GetCodecInfo(i, &nameLen, name, &descriptionLen, description, &type, &infoLen, info);
|
||||
lstrcpynW(storage, name, len);
|
||||
delete[] name;
|
||||
delete[]description;
|
||||
delete[] info;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WMInformation::GetPicture(void **data, size_t *len, wchar_t **mimeType, int pictype)
|
||||
{
|
||||
WORD indexCount = 0;
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
|
||||
WORD nameLen = 128;
|
||||
if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, 0, &indexCount)))
|
||||
{
|
||||
WORD *indices = new WORD[indexCount];
|
||||
if (SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, indices, &indexCount)))
|
||||
{
|
||||
for (size_t index = 0;index != indexCount;index++)
|
||||
{
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
|
||||
WORD lang = 0;
|
||||
DWORD length = 0;
|
||||
wchar_t _attrName[128] = {0};
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
|
||||
{
|
||||
AutoByte v(length);
|
||||
BYTE *value = v.data;
|
||||
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
|
||||
{
|
||||
WM_PICTURE *picture = (WM_PICTURE *)value;
|
||||
if (picture->bPictureType == pictype)
|
||||
{
|
||||
*len = picture->dwDataLen;
|
||||
*data = WASABI_API_MEMMGR->sysMalloc(*len);
|
||||
memcpy(*data, picture->pbData, *len);
|
||||
wchar_t *type=0;
|
||||
if (picture->pwszMIMEType)
|
||||
type = wcschr(picture->pwszMIMEType, L'/');
|
||||
|
||||
if (type && *type)
|
||||
{
|
||||
type++;
|
||||
|
||||
wchar_t *type2 = wcschr(type, L'/');
|
||||
if (type2 && *type2) type2++;
|
||||
else type2 = type;
|
||||
|
||||
size_t mimelen = wcslen(type2)+1;
|
||||
*mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(mimelen*sizeof(wchar_t));
|
||||
StringCchCopyW(*mimeType, mimelen, type2);
|
||||
}
|
||||
else
|
||||
*mimeType = 0; // unknown!
|
||||
delete[] indices;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] indices;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WMInformation::SetPicture(void *data, size_t len, const wchar_t *mimeType, int type)
|
||||
{
|
||||
WM_PICTURE picture;
|
||||
picture.bPictureType = type;
|
||||
picture.dwDataLen = len;
|
||||
picture.pbData = (BYTE *)data;
|
||||
picture.pwszDescription=L"";
|
||||
wchar_t mt[32] = {0};
|
||||
if (wcsstr(mimeType, L"/") != 0)
|
||||
{
|
||||
StringCchCopyW(mt, 32, mimeType);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfW(mt, 32, L"image/%s", mimeType);
|
||||
}
|
||||
picture.pwszMIMEType = mt;
|
||||
return SUCCEEDED(header->SetAttribute(0, g_wszWMPicture, WMT_TYPE_BINARY, (const BYTE *)&picture, sizeof(picture)));
|
||||
}
|
||||
|
||||
bool WMInformation::HasPicture(int pictype)
|
||||
{
|
||||
WORD indexCount = 0;
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
|
||||
WORD nameLen = 128;
|
||||
if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, 0, &indexCount)))
|
||||
{
|
||||
WORD *indices = new WORD[indexCount];
|
||||
if (SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, indices, &indexCount)))
|
||||
{
|
||||
for (size_t index = 0;index != indexCount;index++)
|
||||
{
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
|
||||
WORD lang = 0;
|
||||
DWORD length = 0;
|
||||
wchar_t _attrName[128] = {0};
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
|
||||
{
|
||||
AutoByte v(length);
|
||||
BYTE *value = v.data;
|
||||
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
|
||||
{
|
||||
WM_PICTURE *picture = (WM_PICTURE *)value;
|
||||
if (picture->bPictureType == pictype)
|
||||
{
|
||||
delete[] indices;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] indices;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WMInformation::DeletePicture(int pictype)
|
||||
{
|
||||
WORD indexCount = 0;
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
|
||||
WORD nameLen = 128;
|
||||
if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, 0, &indexCount)))
|
||||
{
|
||||
WORD *indices = new WORD[indexCount];
|
||||
if (SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, indices, &indexCount)))
|
||||
{
|
||||
for (size_t index = 0;index != indexCount;index++)
|
||||
{
|
||||
WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
|
||||
WORD lang = 0;
|
||||
DWORD length = 0;
|
||||
wchar_t _attrName[128] = {0};
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
|
||||
{
|
||||
AutoByte v(length);
|
||||
BYTE *value = v.data;
|
||||
|
||||
if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
|
||||
{
|
||||
WM_PICTURE *picture = (WM_PICTURE *)value;
|
||||
if (picture->bPictureType == pictype)
|
||||
{
|
||||
header3->DeleteAttribute(0, indices[index]);
|
||||
delete[] indices;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] indices;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef NULLSOFT_WMINFORMATIONH
|
||||
#define NULLSOFT_WMINFORMATIONH
|
||||
|
||||
#include <wmsdk.h>
|
||||
#include "WMCallback.h"
|
||||
|
||||
class WMInformation : public WMHandler
|
||||
{
|
||||
public:
|
||||
WMInformation(const wchar_t *fileName, bool noBlock=false);
|
||||
WMInformation(IWMReader *reader);
|
||||
//WMInformation(IWMSyncReader *reader);
|
||||
WMInformation(IWMMetadataEditor *_editor);
|
||||
//WMInformation();
|
||||
bool ErrorOpening()
|
||||
{
|
||||
return openError;
|
||||
} // TODO: benski> this is only valid for WMInformation(const wchar_t *fileName, bool noBlock=false)!!!
|
||||
virtual ~WMInformation();
|
||||
|
||||
bool MakeWritable(const wchar_t *fileName);
|
||||
bool NonWritable();
|
||||
bool MakeReadOnly(const wchar_t *fileName);
|
||||
bool Flush();
|
||||
bool IsSeekable();
|
||||
long GetLengthMilliseconds();
|
||||
long GetBitrate();
|
||||
WORD GetNumberAttributes();
|
||||
void ClearAllAttributes();
|
||||
bool IsAttribute(const wchar_t attrName[]); // false might mean "attribute not found", see IsNotAttribute
|
||||
bool IsNotAttribute(const wchar_t attrName[]); // false might mean "attribute not found", see IsAttribute
|
||||
void GetAttribute(WORD index, wchar_t *attrName, size_t attrLen, wchar_t *valueStr, size_t valueStrLen);
|
||||
void GetAttribute(const wchar_t attrName[], wchar_t *valueStr, size_t len);
|
||||
void SetAttribute(const wchar_t *attrName, wchar_t *value, WMT_ATTR_DATATYPE defaultType = WMT_TYPE_STRING);
|
||||
void DeleteAttribute(const wchar_t *attrName);
|
||||
bool GetAttributeSize(const wchar_t *attrName, size_t &size);
|
||||
void LicenseRequired()
|
||||
{
|
||||
First().OpenFailed();
|
||||
}
|
||||
void SetAttribute_BinString(const wchar_t *attrName, wchar_t *value);
|
||||
void GetAttribute_BinString(const wchar_t attrName[], wchar_t *valueStr, size_t len);
|
||||
|
||||
void DeleteUserText(const wchar_t *description);
|
||||
void SetUserText(const wchar_t *description, const wchar_t *valueStr);
|
||||
|
||||
bool GetCodecName(wchar_t *storage, size_t len);
|
||||
bool GetPicture(void **data, size_t *len, wchar_t **mimeType, int type);
|
||||
bool SetPicture(void *data, size_t len, const wchar_t *mimeType, int type);
|
||||
bool DeletePicture(int type);
|
||||
bool HasPicture(int type);
|
||||
private:
|
||||
bool GetDataType(const wchar_t *name, WMT_ATTR_DATATYPE &type);
|
||||
long GetLongAttr(const wchar_t name[]);
|
||||
bool GetBoolAttr(const wchar_t name[]);
|
||||
DWORD GetDWORDAttr(const wchar_t name[]);
|
||||
struct IWMMetadataEditor *editor;
|
||||
struct IWMMetadataEditor2 *editor2;
|
||||
struct IWMHeaderInfo *header;
|
||||
struct IWMHeaderInfo2 *header2;
|
||||
struct IWMHeaderInfo3 *header3;
|
||||
struct IWMReader *reader;
|
||||
|
||||
WMCallback callback;
|
||||
HANDLE hEvent;
|
||||
bool openError;
|
||||
void NeedsIndividualization()
|
||||
{
|
||||
First().OpenFailed();
|
||||
}
|
||||
void Opened()
|
||||
{
|
||||
openError=false;
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
void OpenFailed()
|
||||
{
|
||||
openError=true;
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
void Error()
|
||||
{
|
||||
openError=true;
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "main.h"
|
||||
#include "WMPlaylist.h"
|
||||
|
||||
WMPlaylist activePlaylist;
|
||||
|
||||
void WMPlaylist::OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info )
|
||||
{
|
||||
//if (playstring.empty())
|
||||
if ( playstring )
|
||||
free( playstring );
|
||||
|
||||
playstring = _wcsdup( filename );
|
||||
}
|
||||
|
||||
const wchar_t *WMPlaylist::GetFileName()
|
||||
{
|
||||
return ( playstring ? playstring : L"" );
|
||||
}
|
||||
|
||||
const wchar_t *WMPlaylist::GetOriginalFileName()
|
||||
{
|
||||
return ( playlistFilename ? playlistFilename : L"" );
|
||||
}
|
||||
|
||||
bool WMPlaylist::IsMe( const char *filename )
|
||||
{
|
||||
return IsMe( (const wchar_t *)AutoWide( filename ) );
|
||||
}
|
||||
|
||||
bool WMPlaylist::IsMe( const wchar_t *filename )
|
||||
{
|
||||
if ( playlistFilename && !_wcsicmp( playlistFilename, filename ) )
|
||||
return true;
|
||||
|
||||
if ( playstring && !_wcsicmp( playstring, filename ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define CBCLASS WMPlaylist
|
||||
START_DISPATCH;
|
||||
VCB( IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile )
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef NULLSOFT_IN_WMVDRM_WMPLAYLIST_H
|
||||
#define NULLSOFT_IN_WMVDRM_WMPLAYLIST_H
|
||||
|
||||
#include "../playlist/ifc_playlistloadercallback.h"
|
||||
|
||||
class WMPlaylist : public ifc_playlistloadercallback
|
||||
{
|
||||
public:
|
||||
WMPlaylist() {}
|
||||
|
||||
~WMPlaylist()
|
||||
{
|
||||
if ( playstring )
|
||||
free( playstring );
|
||||
|
||||
if ( playlistFilename )
|
||||
free( playlistFilename );
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if ( playstring )
|
||||
free( playstring );
|
||||
|
||||
playstring = 0;
|
||||
|
||||
if ( playlistFilename )
|
||||
free( playlistFilename );
|
||||
|
||||
playlistFilename = 0;
|
||||
}
|
||||
|
||||
void OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info );
|
||||
|
||||
const wchar_t *GetFileName();
|
||||
const wchar_t *GetOriginalFileName();
|
||||
/* TODO: need something like these, just not sure exact what yet
|
||||
bool ForceStartTime(int &);
|
||||
bool ForceLength(int &);
|
||||
bool ForceNoSeek();
|
||||
*/
|
||||
bool IsMe( const char *filename );
|
||||
bool IsMe( const wchar_t *filename );
|
||||
|
||||
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
|
||||
public:
|
||||
wchar_t *playstring = 0;
|
||||
wchar_t *playlistFilename = 0;
|
||||
};
|
||||
|
||||
extern WMPlaylist activePlaylist;
|
||||
|
||||
#endif // !NULLSOFT_IN_WMVDRM_WMPLAYLIST_H
|
||||
@@ -0,0 +1,128 @@
|
||||
#include "main.h"
|
||||
#include "WPLLoader.h"
|
||||
#include <stdio.h>
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "../xml/ifc_xmlreadercallback.h"
|
||||
#include "../xml/obj_xml.h"
|
||||
#include "api.h"
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include <shlwapi.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
class WPLXML : public ifc_xmlreadercallback
|
||||
{
|
||||
public:
|
||||
WPLXML(ifc_playlistloadercallback *_playlist, const wchar_t *wplFilename) : playlist(_playlist)
|
||||
{
|
||||
lstrcpynW(rootPath, wplFilename, MAX_PATH);
|
||||
PathRemoveFileSpecW(rootPath);
|
||||
}
|
||||
void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
|
||||
{
|
||||
//not necessary YET, it will be if we register for more things: if (!_wcsicmp(xmlpath, L"smil\fbody\fseq\fmedia"))
|
||||
{
|
||||
const wchar_t *track = params->getItemValue(L"src");
|
||||
|
||||
if (track)
|
||||
{
|
||||
if (PathIsRootW(track) || PathIsURLW(track))
|
||||
{
|
||||
playlist->OnFile(track, 0, -1, 0); // TODO: more info!!!
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t fullPath[MAX_PATH] = {0}, canonicalizedPath[MAX_PATH] = {0};
|
||||
PathCombineW(fullPath, rootPath, track);
|
||||
PathCanonicalizeW(canonicalizedPath, fullPath);
|
||||
playlist->OnFile(canonicalizedPath, 0, -1, 0); // TODO: more info!!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ifc_playlistloadercallback *playlist;
|
||||
wchar_t rootPath[MAX_PATH];
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
#ifdef CBCLASS
|
||||
#undef CBCLASS
|
||||
#endif
|
||||
|
||||
#define CBCLASS WPLXML
|
||||
START_DISPATCH;
|
||||
VCB(ONSTARTELEMENT, StartTag)
|
||||
END_DISPATCH;
|
||||
|
||||
|
||||
WPLLoader::WPLLoader()
|
||||
{
|
||||
}
|
||||
|
||||
WPLLoader::~WPLLoader(void)
|
||||
{
|
||||
//Close();
|
||||
}
|
||||
|
||||
int WPLLoader::Load(const wchar_t *filename, ifc_playlistloadercallback *playlist)
|
||||
{
|
||||
HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
|
||||
obj_xml *parser=0;
|
||||
waServiceFactory *parserFactory=0;
|
||||
|
||||
parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
|
||||
if (parserFactory)
|
||||
parser = (obj_xml *)parserFactory->getInterface();
|
||||
|
||||
if (parser)
|
||||
{
|
||||
WPLXML wplXml(playlist, filename);
|
||||
parser->xmlreader_registerCallback(L"smil\fbody\fseq\fmedia", &wplXml);
|
||||
parser->xmlreader_open();
|
||||
parser->xmlreader_setEncoding(L"UTF-8"); // WPL is always UTF-8, but doesn't explicitly have it
|
||||
|
||||
while (true)
|
||||
{
|
||||
char data[1024] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead)
|
||||
{
|
||||
if (parser->xmlreader_feed(data, bytesRead) != API_XML_SUCCESS)
|
||||
{
|
||||
CloseHandle(file);
|
||||
parser->xmlreader_unregisterCallback(&wplXml);
|
||||
parser->xmlreader_close();
|
||||
parserFactory->releaseInterface(parser);
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
CloseHandle(file);
|
||||
parser->xmlreader_feed(0, 0);
|
||||
|
||||
parser->xmlreader_unregisterCallback(&wplXml);
|
||||
parser->xmlreader_close();
|
||||
parserFactory->releaseInterface(parser);
|
||||
return IFC_PLAYLISTLOADER_SUCCESS;
|
||||
}
|
||||
|
||||
CloseHandle(file);
|
||||
return IFC_PLAYLISTLOADER_FAILED;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CBCLASS
|
||||
#undef CBCLASS
|
||||
#endif
|
||||
|
||||
#define CBCLASS WPLLoader
|
||||
START_DISPATCH;
|
||||
CB(IFC_PLAYLISTLOADER_LOAD, Load)
|
||||
END_DISPATCH;
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef NULLSOFT_PLAYLIST_WPL_LOADER_H
|
||||
#define NULLSOFT_PLAYLIST_WPL_LOADER_H
|
||||
|
||||
#include "../playlist/ifc_playlistloader.h"
|
||||
#include "../playlist/ifc_playlistloadercallback.h"
|
||||
#include <stdio.h>
|
||||
class WPLLoader : public ifc_playlistloader
|
||||
{
|
||||
public:
|
||||
int Load(const wchar_t *filename, ifc_playlistloadercallback *playlist);
|
||||
|
||||
public:
|
||||
WPLLoader();
|
||||
virtual ~WPLLoader(void);
|
||||
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "main.h"
|
||||
#include "WaitLayer.h"
|
||||
#include "util.h"
|
||||
|
||||
WaitLayer::WaitLayer(IWMReader *_reader)
|
||||
: reader(_reader), stopEvent(0)
|
||||
{
|
||||
reader->AddRef();
|
||||
openEvent = CreateEvent(0, TRUE, FALSE, 0);
|
||||
}
|
||||
|
||||
WaitLayer::~WaitLayer()
|
||||
{
|
||||
reader->Release();
|
||||
reader=0;
|
||||
}
|
||||
|
||||
void WaitLayer::Opened()
|
||||
{
|
||||
SetEvent(openEvent);
|
||||
WMHandler::Opened();
|
||||
}
|
||||
|
||||
bool WaitLayer::IsOpen()
|
||||
{
|
||||
return WaitForSingleObject(openEvent, 0) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
void WaitLayer::OpenCalled()
|
||||
{
|
||||
SetEvent(openEvent);
|
||||
WMHandler::OpenCalled();
|
||||
}
|
||||
|
||||
void WaitLayer::OpenFailed()
|
||||
{
|
||||
SetEvent(openEvent);
|
||||
WMHandler::OpenFailed();
|
||||
}
|
||||
|
||||
bool WaitLayer::WaitForOpen(int time_ms)
|
||||
{
|
||||
return WaitForSingleObject(openEvent, time_ms) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
void WaitLayer::ResetForOpen()
|
||||
{
|
||||
ResetEvent(openEvent);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef NULLSOFT_WAITLAYERH
|
||||
#define NULLSOFT_WAITLAYERH
|
||||
|
||||
#include "WMHandler.h"
|
||||
|
||||
class WaitLayer : public WMHandler
|
||||
{
|
||||
public:
|
||||
WaitLayer(IWMReader *_reader);
|
||||
~WaitLayer();
|
||||
|
||||
void ResetForOpen();
|
||||
bool WaitForOpen(int time_ms);
|
||||
bool IsOpen();
|
||||
protected:
|
||||
/* inherited from WMCallback */
|
||||
void OpenCalled();
|
||||
void OpenFailed();
|
||||
void Opened();
|
||||
|
||||
IWMReader *reader; // not ours
|
||||
HANDLE stopEvent, openEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,183 @@
|
||||
#include "Main.h"
|
||||
#include "WinampInterface.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include <cassert>
|
||||
#include "WMDRMModule.h"
|
||||
#include <strsafe.h>
|
||||
#include "WMPlaylist.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
WinampInterface winamp;
|
||||
|
||||
extern WMDRM mod;
|
||||
using namespace Nullsoft::Utility;
|
||||
|
||||
#ifndef NO_DRM
|
||||
#include "vid_overlay.h"
|
||||
#include "vid_ddraw.h"
|
||||
|
||||
OverlayVideoOutput overlay;
|
||||
DDrawVideoOutput ddraw;
|
||||
#endif
|
||||
|
||||
|
||||
WinampInterface::WinampInterface()
|
||||
: videoWindow(0), bufferCount(0),
|
||||
statusGuard(GUARDNAME("WinampInterface::statusGuard"))
|
||||
{
|
||||
statusFilename[0] = 0;
|
||||
status[0] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@returns winamp's video window handle
|
||||
*/
|
||||
HWND WinampInterface::GetVideoWindow()
|
||||
{
|
||||
return (HWND)GetVideoOutput()->extended(VIDUSER_GET_VIDEOHWND, 0, 0); // ask for the video hwnd
|
||||
}
|
||||
|
||||
IVideoOutput *WinampInterface::GetVideoOutput()
|
||||
{
|
||||
if (!videoWindow)
|
||||
videoWindow = (IVideoOutput *)SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT); // ask winamp for an interface to the video output
|
||||
return videoWindow;
|
||||
|
||||
}
|
||||
|
||||
void WinampInterface::EndOfFile()
|
||||
{
|
||||
PostMessage(GetWinampWindow(), WM_WA_MPEG_EOF, 0, 0);
|
||||
}
|
||||
|
||||
HWND WinampInterface::GetWinampWindow()
|
||||
{
|
||||
return plugin.hMainWindow;
|
||||
}
|
||||
|
||||
void WinampInterface::SetStatus(wchar_t *_status)
|
||||
{
|
||||
{
|
||||
AutoLock lock (statusGuard);
|
||||
StringCchCopy(status, 1024, _status);
|
||||
StringCchCopy(statusFilename, FILENAME_SIZE, activePlaylist.GetFileName());
|
||||
}
|
||||
PostMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_UPDTITLE);
|
||||
}
|
||||
|
||||
bool WinampInterface::GetStatus(wchar_t *title, size_t titleLen, const wchar_t *filename)
|
||||
{
|
||||
AutoLock lock (statusGuard);
|
||||
|
||||
if (status[0] && title && filename && !lstrcmpi(statusFilename, filename))
|
||||
{
|
||||
StringCchPrintf(title, titleLen, L"[%s]%s", status, filename);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WinampInterface::GetStatusHook(wchar_t *title, size_t titleLen, const wchar_t *filename)
|
||||
{
|
||||
AutoLock lock (statusGuard);
|
||||
|
||||
if (status[0] && title && filename && !lstrcmpi(statusFilename, filename))
|
||||
{
|
||||
wchar_t *oldTitle = _wcsdup(title);
|
||||
StringCchPrintf(title, titleLen, L"[%s]%s", status, oldTitle);
|
||||
free(oldTitle);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WinampInterface::HasStatus(const wchar_t *filename)
|
||||
{
|
||||
AutoLock lock (statusGuard);
|
||||
if (status[0] && filename && !lstrcmpi(statusFilename, filename))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void WinampInterface::ClearStatus()
|
||||
{
|
||||
{
|
||||
//AutoLock lock (statusGuard); // should be safe not to lock here
|
||||
status[0] = 0;
|
||||
statusFilename[0]=0;
|
||||
}
|
||||
PostMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_UPDTITLE);
|
||||
}
|
||||
|
||||
void WinampInterface::EncryptedDrawFrame(void *frame)
|
||||
{
|
||||
#ifndef NO_DRM
|
||||
overlay.SetFrame(frame);
|
||||
ddraw.SetFrame(frame);
|
||||
SecureZeroMemory(&frame, sizeof(void *));
|
||||
GetVideoOutput()->draw((void *)1);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WinampInterface::OpenEncryptedVideo(int width, int height, bool flip, double aspect, int fourcc)
|
||||
{
|
||||
#ifndef NO_DRM
|
||||
VideoOpenStruct openVideo = {width, height, flip, aspect, fourcc};
|
||||
|
||||
bool openedOK = false;
|
||||
if (config_video.overlays())
|
||||
{
|
||||
openedOK = !!GetVideoOutput()->extended(VIDUSER_OPENVIDEORENDERER, (intptr_t)(VideoRenderer *)&overlay, (intptr_t)&openVideo);
|
||||
if (openedOK)
|
||||
return true;
|
||||
}
|
||||
|
||||
openedOK = !!GetVideoOutput()->extended(VIDUSER_OPENVIDEORENDERER, (intptr_t)(VideoRenderer *)&ddraw, (intptr_t)&openVideo);
|
||||
if (openedOK)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WinampInterface::CloseEncryptedVideo()
|
||||
{
|
||||
GetVideoOutput()->extended(VIDUSER_CLOSEVIDEORENDERER, 0, 0);
|
||||
}
|
||||
|
||||
void WinampInterface::Buffering(int bufStatus, const wchar_t *displayString)
|
||||
{
|
||||
char tempdata[75*2] = {0, };
|
||||
|
||||
int csa = plugin.SAGetMode();
|
||||
if (csa & 1)
|
||||
{
|
||||
for (int x = 0; x < bufStatus*75 / 100; x ++)
|
||||
tempdata[x] = x * 16 / 75;
|
||||
}
|
||||
else if (csa&2)
|
||||
{
|
||||
int offs = (csa & 1) ? 75 : 0;
|
||||
int x = 0;
|
||||
while (x < bufStatus*75 / 100)
|
||||
{
|
||||
tempdata[offs + x++] = -6 + x * 14 / 75;
|
||||
}
|
||||
while (x < 75)
|
||||
{
|
||||
tempdata[offs + x++] = 0;
|
||||
}
|
||||
}
|
||||
else if (csa == 4)
|
||||
{
|
||||
tempdata[0] = tempdata[1] = (bufStatus * 127 / 100);
|
||||
}
|
||||
if (csa) plugin.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa);
|
||||
|
||||
wchar_t temp[64] = {0};
|
||||
StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus);
|
||||
SetStatus(temp);
|
||||
//SetVideoStatusText(temp); // TODO: find a way to set the old status back
|
||||
GetVideoOutput()->notifyBufferState(static_cast<int>(bufStatus*2.55f));
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef NULLSOFT_WINAMPINTERFACEH
|
||||
#define NULLSOFT_WINAMPINTERFACEH
|
||||
|
||||
#include "Main.h"
|
||||
#include <windows.h>
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "../Winamp/In2.h"
|
||||
#include "../Winamp/strutil.h"
|
||||
#include "output/AudioOut.h"
|
||||
#include "../nu/AutoLock.h"
|
||||
|
||||
extern AudioOut *out;
|
||||
extern In_Module plugin;
|
||||
|
||||
class WinampInterface
|
||||
{
|
||||
public:
|
||||
WinampInterface();
|
||||
|
||||
HWND GetVideoWindow();
|
||||
|
||||
IVideoOutput *GetVideoOutput();
|
||||
void EndOfFile();
|
||||
HWND GetWinampWindow();
|
||||
|
||||
void RefreshTitle()
|
||||
{
|
||||
PostMessage(plugin.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
|
||||
}
|
||||
|
||||
const char *GetProxy()
|
||||
{
|
||||
return (const char *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_PROXY_STRING);
|
||||
}
|
||||
|
||||
void ResetBuffering() { bufferCount=0;}
|
||||
void Buffering(int bufStatus, const wchar_t *displayString);
|
||||
|
||||
bool OpenEncryptedVideo(int width, int height, bool flip, double aspect, int fourcc);
|
||||
bool OpenVideo(int width, int height, bool flip, double aspect, int fourcc)
|
||||
{
|
||||
GetVideoOutput()->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
|
||||
bool video = (GetVideoOutput()->open(width, height, flip ? 1 : 0, aspect, fourcc) == 0);
|
||||
return video;
|
||||
}
|
||||
|
||||
ULONG_PTR GetStart()
|
||||
{
|
||||
return SendMessage(plugin.hMainWindow, WM_WA_IPC,0,IPC_GETPLAYITEM_START);
|
||||
}
|
||||
|
||||
ULONG_PTR GetEnd()
|
||||
{
|
||||
return SendMessage(plugin.hMainWindow, WM_WA_IPC,0,IPC_GETPLAYITEM_END);
|
||||
}
|
||||
|
||||
void PressStop()
|
||||
{
|
||||
SendMessage(plugin.hMainWindow, WM_COMMAND, 40047, 0);
|
||||
}
|
||||
|
||||
void PressPlay()
|
||||
{
|
||||
SendMessage(plugin.hMainWindow, WM_COMMAND,40045, 0);
|
||||
}
|
||||
|
||||
void DrawFrame(void *frame)
|
||||
{
|
||||
GetVideoOutput()->draw(frame);
|
||||
}
|
||||
|
||||
void EncryptedDrawFrame(void *frame);
|
||||
void SetVideoStatusText(char *text)
|
||||
{
|
||||
GetVideoOutput()->extended(VIDUSER_SET_INFOSTRING,(INT_PTR)text,0);
|
||||
}
|
||||
void SetVideoPalette(RGBQUAD *palette)
|
||||
{
|
||||
GetVideoOutput()->extended(VIDUSER_SET_PALETTE,(INT_PTR)palette,0);
|
||||
}
|
||||
void CloseViz()
|
||||
{
|
||||
plugin.SAVSADeInit();
|
||||
}
|
||||
void CloseEncryptedVideo();
|
||||
void CloseVideo()
|
||||
{
|
||||
GetVideoOutput()->close();
|
||||
}
|
||||
|
||||
void SetAudioInfo(int bitRateKiloBits, int sampleRateKiloHertz, int channels)
|
||||
{
|
||||
plugin.SetInfo(bitRateKiloBits, sampleRateKiloHertz, channels, 1);
|
||||
}
|
||||
|
||||
void OpenViz(int maxLatency, int sampleRate)
|
||||
{
|
||||
plugin.SAVSAInit(maxLatency, sampleRate);
|
||||
}
|
||||
|
||||
void SetVizInfo(int sampleRate, int channels)
|
||||
{
|
||||
plugin.VSASetInfo(sampleRate, channels);
|
||||
}
|
||||
|
||||
bool GetStatusHook(wchar_t *title, size_t titleLen, const wchar_t *filename);
|
||||
bool HasStatus(const wchar_t *filename);
|
||||
void SetStatus(wchar_t *_status);
|
||||
bool GetStatus(wchar_t *title, size_t titleLen, const wchar_t *filename);
|
||||
|
||||
void ClearStatus();
|
||||
Nullsoft::Utility::LockGuard statusGuard;
|
||||
int bufferCount;
|
||||
|
||||
private:
|
||||
wchar_t status[1024];
|
||||
wchar_t statusFilename[FILENAME_SIZE];
|
||||
IVideoOutput *videoWindow;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "main.h"
|
||||
#include "XMLString.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
XMLString::XMLString()
|
||||
{
|
||||
data[0]=0;
|
||||
}
|
||||
|
||||
void XMLString::Reset()
|
||||
{
|
||||
data[0]=0;
|
||||
}
|
||||
|
||||
const wchar_t *XMLString::GetString()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
void XMLString::StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
|
||||
{
|
||||
data[0]=0;
|
||||
}
|
||||
|
||||
|
||||
void XMLString::TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str)
|
||||
{
|
||||
StringCchCatW(data, 256, str);
|
||||
}
|
||||
|
||||
|
||||
void XMLString::ManualSet(const wchar_t *string)
|
||||
{
|
||||
StringCchCatW(data, 256, string);
|
||||
}
|
||||
|
||||
#ifdef CBCLASS
|
||||
#undef CBCLASS
|
||||
#endif
|
||||
|
||||
#define CBCLASS XMLString
|
||||
START_DISPATCH;
|
||||
VCB(ONSTARTELEMENT, StartTag)
|
||||
VCB(ONCHARDATA, TextHandler)
|
||||
END_DISPATCH;
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef NULLSOFT_WINAMP_XMLSTRING_H
|
||||
#define NULLSOFT_WINAMP_XMLSTRING_H
|
||||
|
||||
|
||||
#include "../xml/ifc_xmlreadercallback.h"
|
||||
/*
|
||||
this one is an xml callback that just saves the last encountered string
|
||||
*/
|
||||
|
||||
|
||||
class XMLString : public ifc_xmlreadercallback
|
||||
{
|
||||
public:
|
||||
XMLString();
|
||||
void Reset();
|
||||
const wchar_t *GetString();
|
||||
void ManualSet(const wchar_t *string);
|
||||
private:
|
||||
/* XML callbacks */
|
||||
void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params);
|
||||
void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag);
|
||||
void TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str);
|
||||
|
||||
wchar_t data[256]; // for now, we'll make it dynamic later
|
||||
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "main.h"
|
||||
#include "api.h"
|
||||
#include <windows.h>
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "FactoryHelper.h"
|
||||
#include "MetaTagFactory.h"
|
||||
#include "factory_Handler.h"
|
||||
#include "AlbumArt.h"
|
||||
#include "RawReader.h"
|
||||
#include "../nu/Singleton.h"
|
||||
MetaTagFactory metaTagFactory;
|
||||
|
||||
api_service *serviceManager = 0;
|
||||
api_playlistmanager *playlistManager=0;
|
||||
api_config *AGAVE_API_CONFIG=0;
|
||||
api_application *applicationApi=0;
|
||||
|
||||
// wasabi based services for localisation support
|
||||
api_language *WASABI_API_LNG = 0;
|
||||
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
|
||||
api_memmgr *WASABI_API_MEMMGR = 0;
|
||||
WPLHandlerFactory wplHandlerFactory;
|
||||
ASXHandlerFactory asxHandlerFactory;
|
||||
AlbumArtFactory albumArtFactory;
|
||||
static RawMediaReaderService raw_media_reader_service;
|
||||
static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
|
||||
|
||||
int LoadWasabi()
|
||||
{
|
||||
ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
|
||||
ServiceBuild(playlistManager, api_playlistmanagerGUID);
|
||||
ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
|
||||
ServiceBuild(WASABI_API_LNG, languageApiGUID);
|
||||
ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
|
||||
plugin.service->service_register(&metaTagFactory);
|
||||
plugin.service->service_register(&wplHandlerFactory);
|
||||
plugin.service->service_register(&asxHandlerFactory);
|
||||
plugin.service->service_register(&albumArtFactory);
|
||||
raw_factory.Register(plugin.service, &raw_media_reader_service);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void UnloadWasabi()
|
||||
{
|
||||
plugin.service->service_deregister(&metaTagFactory);
|
||||
plugin.service->service_deregister(&wplHandlerFactory);
|
||||
plugin.service->service_deregister(&asxHandlerFactory);
|
||||
plugin.service->service_deregister(&albumArtFactory);
|
||||
plugin.service->service_deregister(&raw_factory);
|
||||
ServiceRelease(playlistManager, api_playlistmanagerGUID);
|
||||
ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
|
||||
ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
|
||||
ServiceRelease(WASABI_API_LNG, languageApiGUID);
|
||||
ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef NULLSOFT_API_H
|
||||
#define NULLSOFT_API_H
|
||||
|
||||
#include <windows.h>
|
||||
int LoadWasabi();
|
||||
void UnloadWasabi();
|
||||
|
||||
#include "../playlist/api_playlistmanager.h"
|
||||
extern api_playlistmanager *playlistManager;
|
||||
#define AGAVE_API_PLAYLISTMANAGER playlistManager
|
||||
|
||||
#include "../Agave/Config/api_config.h"
|
||||
extern api_config *config;
|
||||
#define AGAVE_API_CONFIG config
|
||||
|
||||
#include <api/application/api_application.h>
|
||||
extern api_application *applicationApi;
|
||||
#define WASABI_API_APP applicationApi
|
||||
|
||||
#include <api/memmgr/api_memmgr.h>
|
||||
extern api_memmgr *memmgr;
|
||||
#define WASABI_API_MEMMGR memmgr
|
||||
|
||||
#include "../Agave/Language/api_language.h"
|
||||
|
||||
// these are custom defines for the out_wave and out_ds embedded implementations
|
||||
extern HINSTANCE WASABI_API_LNG_HINST_WAV, WASABI_API_LNG_HINST_WAV_ORIG;
|
||||
extern HINSTANCE WASABI_API_LNG_HINST_DS, WASABI_API_LNG_HINST_DS_ORIG;
|
||||
|
||||
#define WASABI_API_LNGSTRING_WAV(uID) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID)
|
||||
#define WASABI_API_LNGSTRING_BUF_WAV(uID,buf,len) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID,buf,len)
|
||||
|
||||
#define WASABI_API_LNGSTRINGW_WAV(uID) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID)
|
||||
#define WASABI_API_LNGSTRINGW_BUF_WAV(uID,buf,len) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID,buf,len)
|
||||
|
||||
#define WASABI_API_LNGSTRING_DS(uID) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID)
|
||||
#define WASABI_API_LNGSTRING_BUF_DS(uID,buf,len) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID,buf,len)
|
||||
|
||||
#define WASABI_API_LNGSTRINGW_DS(uID) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID)
|
||||
#define WASABI_API_LNGSTRINGW_BUF_DS(uID,buf,len) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID,buf,len)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#define WM_DEFINE_CONFIG 1
|
||||
#include "config.h"
|
||||
#include "loadini.h"
|
||||
#include "main.h"
|
||||
#include "../nu/Config.h"
|
||||
|
||||
bool config_no_video = false;
|
||||
extern Nullsoft::Utility::Config wmConfig;
|
||||
#pragma warning(disable:4800)
|
||||
#define READ(type, name) config_##name = (type)wmConfig.cfg_int(TEXT("config_") TEXT(#name), default_##name)
|
||||
#define WRITE(type, name) wmConfig.cfg_int(TEXT("config_") TEXT(#name), default_##name) = (int)config_##name
|
||||
#define DEFAULT(name) config_##name = default_##name
|
||||
|
||||
void ReadConfig()
|
||||
{
|
||||
READ(bool, lowmemory);
|
||||
READ(bool, clock);
|
||||
|
||||
READ(bool, video_dedicated_thread);
|
||||
READ(bool, video_early);
|
||||
READ(int, video_early_pad);
|
||||
READ(bool, video_outoforder);
|
||||
READ(bool, video_catchup);
|
||||
READ(int, video_jitter);
|
||||
READ(int, video_drop_threshold);
|
||||
READ(size_t, video_cache_frames);
|
||||
READ(bool, video_notifylate);
|
||||
READ(bool, video_framedropoffset);
|
||||
|
||||
READ(bool, audio_outoforder);
|
||||
READ(bool, audio_dedicated_thread);
|
||||
READ(int, audio_early_pad);
|
||||
READ(bool, audio_early);
|
||||
READ(size_t, audio_cache_frames);
|
||||
READ(size_t, audio_num_channels);
|
||||
|
||||
// READ(bool, no_silent);
|
||||
// READ(bool, untrusted_ok);
|
||||
|
||||
READ(bool, http_metadata);
|
||||
READ(size_t, buffer_time);
|
||||
|
||||
READ(bool, extra_asx_extensions);
|
||||
|
||||
READ(int, col1);
|
||||
READ(int, col2);
|
||||
}
|
||||
|
||||
void WriteConfig()
|
||||
{
|
||||
WRITE(bool, lowmemory);
|
||||
|
||||
WRITE(bool, clock);
|
||||
|
||||
WRITE(bool, video_dedicated_thread);
|
||||
WRITE(bool, video_early);
|
||||
WRITE(int, video_early_pad);
|
||||
WRITE(bool, video_outoforder);
|
||||
WRITE(bool, video_catchup);
|
||||
WRITE(int, video_jitter);
|
||||
WRITE(int, video_drop_threshold);
|
||||
WRITE(size_t, video_cache_frames);
|
||||
WRITE(bool, video_notifylate);
|
||||
WRITE(bool, video_framedropoffset);
|
||||
|
||||
WRITE(bool, audio_outoforder);
|
||||
WRITE(bool, audio_dedicated_thread);
|
||||
WRITE(int, audio_early_pad);
|
||||
WRITE(bool, audio_early);
|
||||
WRITE(size_t, audio_cache_frames);
|
||||
WRITE(size_t, audio_num_channels);
|
||||
|
||||
// WRITE(bool, no_silent);
|
||||
// WRITE(bool, untrusted_ok);
|
||||
|
||||
WRITE(bool, http_metadata);
|
||||
WRITE(size_t, buffer_time);
|
||||
|
||||
WRITE(bool, extra_asx_extensions);
|
||||
|
||||
WRITE(int, col1);
|
||||
WRITE(int, col2);
|
||||
}
|
||||
|
||||
void DefaultConfig()
|
||||
{
|
||||
DEFAULT(http_metadata);
|
||||
// DEFAULT(no_silent);
|
||||
// DEFAULT(untrusted_ok);
|
||||
DEFAULT(buffer_time);
|
||||
DEFAULT(audio_num_channels);
|
||||
|
||||
DEFAULT(audio_outoforder);
|
||||
DEFAULT(audio_dedicated_thread);
|
||||
DEFAULT(audio_early_pad);
|
||||
DEFAULT(audio_early);
|
||||
DEFAULT(audio_cache_frames);
|
||||
|
||||
DEFAULT(lowmemory);
|
||||
|
||||
DEFAULT(clock);
|
||||
|
||||
DEFAULT(video_dedicated_thread);
|
||||
DEFAULT(video_early);
|
||||
DEFAULT(video_early_pad);
|
||||
DEFAULT(video_outoforder);
|
||||
DEFAULT(video_catchup);
|
||||
DEFAULT(video_jitter);
|
||||
DEFAULT(video_drop_threshold);
|
||||
DEFAULT(video_cache_frames);
|
||||
DEFAULT(video_notifylate);
|
||||
DEFAULT(video_framedropoffset);
|
||||
|
||||
DEFAULT(extra_asx_extensions);
|
||||
|
||||
DEFAULT(col1);
|
||||
DEFAULT(col2);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef NULLSOFT_CONFIGH
|
||||
#define NULLSOFT_CONFIGH
|
||||
#include "dsound.h"
|
||||
#ifdef WM_DEFINE_CONFIG
|
||||
#define DEFVAL(x) =x
|
||||
#define CFGEXTERN
|
||||
#else
|
||||
#define DEFVAL(x)
|
||||
#define CFGEXTERN extern
|
||||
#endif
|
||||
|
||||
#define CFG(type, name, defval) CFGEXTERN type config_##name DEFVAL(defval); CFGEXTERN type default_##name DEFVAL(defval);
|
||||
|
||||
CFG(bool, lowmemory, true);
|
||||
CFG(bool, clock, true);
|
||||
|
||||
CFG(bool, video_dedicated_thread, true);
|
||||
CFG(bool, video_early, false);
|
||||
CFG(int, video_early_pad, 500);
|
||||
CFG(bool, video_outoforder, true);
|
||||
CFG(bool, video_catchup, true);
|
||||
CFG(int, video_jitter, 5);
|
||||
CFG(int, video_drop_threshold, 15);
|
||||
CFG(size_t, video_cache_frames, 16);
|
||||
CFG(bool, video_notifylate, true);
|
||||
CFG(bool, video_framedropoffset, false);
|
||||
//CFG(bool, video_flip, false);
|
||||
|
||||
CFG(bool, audio_outoforder, false);
|
||||
CFG(bool, audio_dedicated_thread, true);
|
||||
CFG(int, audio_early_pad, 0);
|
||||
CFG(bool, audio_early, false);
|
||||
CFG(size_t, audio_cache_frames, 12);
|
||||
CFG(DWORD, audio_num_channels, DSSPEAKER_5POINT1);
|
||||
|
||||
CFG(bool, no_silent, false);
|
||||
CFG(bool, untrusted_ok, false);
|
||||
|
||||
CFG(bool, http_metadata, false);
|
||||
CFG(size_t, buffer_time, 5000);
|
||||
|
||||
CFG(int, col1, -1);
|
||||
CFG(int, col2, -1);
|
||||
|
||||
extern bool config_no_video;
|
||||
|
||||
CFG(bool, extra_asx_extensions, false);
|
||||
void ReadConfig(), WriteConfig(), DefaultConfig();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "main.h"
|
||||
#include "directdraw.h"
|
||||
|
||||
HRESULT (WINAPI *_DirectDrawCreate)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter) = 0;
|
||||
|
||||
HRESULT DDrawCreate(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter)
|
||||
{
|
||||
static int a = 0;
|
||||
if (!_DirectDrawCreate && !a)
|
||||
{
|
||||
a++;
|
||||
HINSTANCE h = LoadLibrary(L"ddraw.dll");
|
||||
if (h)
|
||||
{
|
||||
*(void**)&_DirectDrawCreate = (void*)GetProcAddress(h, "DirectDrawCreate");
|
||||
}
|
||||
}
|
||||
|
||||
if (_DirectDrawCreate)
|
||||
return _DirectDrawCreate(lpGUID, lplpDD, pUnkOuter);
|
||||
else
|
||||
return S_OK; // TODO: uhhh no this should be an error :)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef NULLSOFT_DDRAWH
|
||||
#define NULLSOFT_DDRAWH
|
||||
#include <ddraw.h>
|
||||
|
||||
extern HRESULT (WINAPI *_DirectDrawCreate)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
|
||||
|
||||
HRESULT DDrawCreate(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "main.h"
|
||||
#include "api.h"
|
||||
#include "factory_Handler.h"
|
||||
#include "PlaylistHandler.h"
|
||||
#include <api/service/services.h>
|
||||
|
||||
WPLHandler wplHandler;
|
||||
ASXHandler asxHandler;
|
||||
|
||||
|
||||
#define DEFINE_HANDLER_FACTORY(CLASSNAME, className) const char *CLASSNAME ## HandlerFactory::GetServiceName() { return #CLASSNAME ## "Playlist Handler"; }\
|
||||
GUID CLASSNAME ## HandlerFactory::GetGUID(){ return className ## HandlerGUID;}\
|
||||
void *CLASSNAME ## HandlerFactory::GetInterface(int global_lock){ return &className ## Handler;}
|
||||
|
||||
|
||||
DEFINE_HANDLER_FACTORY(WPL, wpl);
|
||||
DEFINE_HANDLER_FACTORY(ASX, asx);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
FOURCC CommonHandlerFactory::GetServiceType()
|
||||
{
|
||||
return WaSvc::PLAYLISTHANDLER;
|
||||
}
|
||||
|
||||
int CommonHandlerFactory::SupportNonLockingInterface()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CommonHandlerFactory::ReleaseInterface(void *ifc)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *CommonHandlerFactory::GetTestString()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int CommonHandlerFactory::ServiceNotify(int msg, int param1, int param2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#undef CBCLASS
|
||||
#define CBCLASS WPLHandlerFactory
|
||||
START_DISPATCH;
|
||||
CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
|
||||
CB(WASERVICEFACTORY_GETGUID, GetGUID)
|
||||
CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
|
||||
#undef CBCLASS
|
||||
#define CBCLASS CommonHandlerFactory
|
||||
CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
|
||||
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
|
||||
CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
|
||||
CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
|
||||
CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
|
||||
END_DISPATCH;
|
||||
|
||||
#undef CBCLASS
|
||||
#define CBCLASS ASXHandlerFactory
|
||||
START_DISPATCH;
|
||||
CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
|
||||
CB(WASERVICEFACTORY_GETGUID, GetGUID)
|
||||
CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
|
||||
#undef CBCLASS
|
||||
#define CBCLASS CommonHandlerFactory
|
||||
CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
|
||||
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
|
||||
CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
|
||||
CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
|
||||
CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
|
||||
END_DISPATCH;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef NULLSOFT_FACTORY_HANDLER_H
|
||||
#define NULLSOFT_FACTORY_HANDLER_H
|
||||
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include <api/service/services.h>
|
||||
|
||||
class CommonHandlerFactory : public waServiceFactory
|
||||
{
|
||||
public:
|
||||
|
||||
FOURCC GetServiceType();
|
||||
int SupportNonLockingInterface();
|
||||
int ReleaseInterface(void *ifc);
|
||||
const char *GetTestString();
|
||||
int ServiceNotify(int msg, int param1, int param2);
|
||||
|
||||
};
|
||||
|
||||
#define DECLARE_HANDLER_FACTORY(CLASSNAME) class CLASSNAME : public CommonHandlerFactory {\
|
||||
public:\
|
||||
const char *GetServiceName();\
|
||||
GUID GetGUID();\
|
||||
void *GetInterface(int global_lock);\
|
||||
protected:\
|
||||
RECVS_DISPATCH;}
|
||||
|
||||
DECLARE_HANDLER_FACTORY(ASXHandlerFactory);
|
||||
DECLARE_HANDLER_FACTORY(WPLHandlerFactory);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,331 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""version.rc2""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_CONFIG DIALOGEX 0, 0, 344, 142
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Windows Media Decoder Settings"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "General",IDC_STATIC,4,4,156,59
|
||||
CONTROL "Retrieve metadata for HTTP streams",IDC_HTTPMETA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,16,133,10
|
||||
LTEXT "Streaming prebuffer: ",IDC_STATIC,10,30,70,8
|
||||
EDITTEXT IDC_BUFFER_TIME,81,28,41,12,ES_AUTOHSCROLL
|
||||
LTEXT "ms",IDC_STATIC,126,30,10,8
|
||||
LTEXT "Speaker Configuration:",IDC_STATIC,10,43,75,12,SS_CENTERIMAGE
|
||||
COMBOBOX IDC_AUDIO_SPEAKER_COUNT,88,43,66,83,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
|
||||
GROUPBOX "Advanced Settings",IDC_STATIC,4,67,156,52
|
||||
LTEXT "Warning: For advanced users only!",IDC_STATIC,10,83,128,10
|
||||
PUSHBUTTON "Advanced ...",IDC_ADVANCED,52,98,50,13
|
||||
/* GROUPBOX "Protected Media",IDC_STATIC,4,80,156,42
|
||||
CONTROL "Try to acquire licenses silently",IDC_SILENT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,93,111,10
|
||||
CONTROL "Always accept untrusted certificates",IDC_UNTRUSTED,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,106,133,10
|
||||
*/
|
||||
GROUPBOX "Filetypes",IDC_STATIC,164,4,176,134
|
||||
CONTROL "",IDC_TYPELIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,172,17,161,85
|
||||
PUSHBUTTON "Add ...",IDC_ADDTYPE,172,107,51,13
|
||||
PUSHBUTTON "Edit ...",IDC_EDITTYPE,227,107,50,13
|
||||
PUSHBUTTON "Remove",IDC_REMOVETYPE,281,107,52,13
|
||||
CONTROL "Enable WAX/WMX/WVX playlist extensions",IDC_EXTRA_ASX,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,172,124,151,10
|
||||
PUSHBUTTON "Save",IDOK,4,125,46,13
|
||||
PUSHBUTTON "Cancel",IDCANCEL,54,125,48,13
|
||||
PUSHBUTTON "Defaults",IDC_DEFAULTTYPE,110,125,50,13
|
||||
END
|
||||
|
||||
IDD_ADDTYPE DIALOGEX 0, 0, 168, 124
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Add File Type"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
LTEXT "Type",IDC_STATIC_TYPE,4,9,36,8,NOT WS_GROUP
|
||||
EDITTEXT IDC_TYPE,44,7,120,12,ES_AUTOHSCROLL
|
||||
LTEXT "Description",IDC_STATIC,4,25,36,8,NOT WS_GROUP
|
||||
EDITTEXT IDC_DESCRIPTION,44,23,120,12,ES_AUTOHSCROLL
|
||||
GROUPBOX "Extension Type",IDC_STATIC,4,40,160,30
|
||||
CONTROL "File Extension",IDC_FILEEXTENSION,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,53,60,10
|
||||
CONTROL "Protocol",IDC_PROTOCOL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,84,53,42,10
|
||||
GROUPBOX "Media Type",IDC_STATIC,4,73,160,30
|
||||
CONTROL "Audio Only",IDC_TYPE_AUDIO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,86,51,10
|
||||
CONTROL "Audio/Video",IDC_TYPE_VIDEO,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,84,86,54,10
|
||||
DEFPUSHBUTTON "OK",IDOK,60,107,50,13
|
||||
PUSHBUTTON "Cancel",IDCANCEL,114,107,50,13
|
||||
END
|
||||
|
||||
IDD_ADVANCED DIALOGEX 0, 0, 190, 214
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Windows Media Decoder Advanced Settings"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "Audio",IDC_STATIC,4,4,182,60
|
||||
CONTROL "Dedicated Delivery Thread",IDC_AUDIO_THREAD,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,204,15,101,10
|
||||
CONTROL "Deliver samples early",IDC_AUDIO_EARLY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,18,80,10
|
||||
LTEXT "and add",IDC_STATIC,96,18,30,10,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_AUDIO_EARLYPAD,126,17,40,12,ES_AUTOHSCROLL
|
||||
LTEXT "ms",IDC_STATIC,168,18,11,10,SS_CENTERIMAGE
|
||||
CONTROL "Allow out-of-order frames",IDC_AUDIO_OUTOFORDER,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,204,25,99,10
|
||||
LTEXT "cache",IDC_STATIC,12,32,21,12,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_AUDIO_CACHE_FRAMES,37,32,40,12,ES_AUTOHSCROLL
|
||||
LTEXT "frames",IDC_STATIC,81,32,30,12,SS_CENTERIMAGE
|
||||
CONTROL "Drop audio frames instead of video frames",IDC_AUDIO_DROP,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,48,152,10
|
||||
GROUPBOX "General",IDC_STATIC,4,164,182,28
|
||||
CONTROL "Low Memory",IDC_LOWMEMORY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,175,56,10
|
||||
CONTROL "Real Time",IDC_REALTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,108,175,47,10
|
||||
GROUPBOX "Video",IDC_STATIC,4,68,182,93
|
||||
CONTROL "Dedicated Delivery Thread",IDC_VIDEO_THREAD,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,205,113,101,10
|
||||
CONTROL "Deliver samples early",IDC_VIDEO_EARLY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,81,80,10
|
||||
LTEXT "by",IDC_STATIC,96,81,10,10,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_VIDEO_EARLYPAD,108,81,40,12,ES_AUTOHSCROLL
|
||||
LTEXT "ms",IDC_STATIC,150,81,11,10,SS_CENTERIMAGE
|
||||
CONTROL "Allow out-of-order frames",IDC_VIDEO_OUTOFORDER,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,205,126,99,10
|
||||
LTEXT "Cache",IDC_STATIC,14,97,21,12,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_VIDEO_CACHE_FRAMES,42,97,40,12,ES_AUTOHSCROLL
|
||||
LTEXT "frames",IDC_STATIC,88,97,30,12,SS_CENTERIMAGE
|
||||
LTEXT "Jitter:",IDC_STATIC,14,113,20,12,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_VIDEO_JITTER,42,113,40,12,ES_AUTOHSCROLL
|
||||
LTEXT "ms",IDC_STATIC,88,113,10,12,SS_CENTERIMAGE
|
||||
LTEXT "Frame drop threshold:",IDC_STATIC,12,131,72,10,SS_CENTERIMAGE
|
||||
EDITTEXT IDC_VIDEO_DROP_THRESHOLD,88,129,40,12,ES_AUTOHSCROLL
|
||||
LTEXT "ms",IDC_STATIC,132,131,10,10,SS_CENTERIMAGE
|
||||
CONTROL "Report dropped frames to decoder",IDC_VIDEO_NOTIFY,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,145,127,10
|
||||
DEFPUSHBUTTON "OK",IDOK,82,197,50,13
|
||||
PUSHBUTTON "Cancel",IDCANCEL,136,197,50,13
|
||||
END
|
||||
|
||||
IDD_INFO DIALOGEX 0, 0, 341, 164
|
||||
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "Advanced",-1,0,0,341,164
|
||||
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,13,327,143
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO
|
||||
BEGIN
|
||||
IDD_CONFIG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 4
|
||||
RIGHTMARGIN, 340
|
||||
TOPMARGIN, 4
|
||||
BOTTOMMARGIN, 138
|
||||
END
|
||||
|
||||
IDD_ADDTYPE, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 4
|
||||
RIGHTMARGIN, 164
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 120
|
||||
END
|
||||
|
||||
IDD_ADVANCED, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 4
|
||||
RIGHTMARGIN, 186
|
||||
TOPMARGIN, 4
|
||||
BOTTOMMARGIN, 210
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_WINDOWS_MEDIA_DECODER "Nullsoft Windows Media Decoder v%s"
|
||||
65535 "{C5B78F09-3222-4a64-AA98-F1ABC5A9E355}"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_WINDOWS_MEDIA_DECODER_OLD "Nullsoft Windows Media Decoder"
|
||||
IDS_REALTIME "Realtime"
|
||||
IDS_CONNECTING "Connecting"
|
||||
IDS_LOCATING "Locating"
|
||||
IDS_ACCESS_DENIED "Access Denied"
|
||||
IDS_STEREO "Stereo"
|
||||
IDS_QUADROPHONIC "Quadrophonic"
|
||||
IDS_SURROUND "Surround"
|
||||
IDS_5_1 "5.1"
|
||||
IDS_7_1 "7.1"
|
||||
IDS_EXT "Ext"
|
||||
IDS_DESCRIPTION "Description"
|
||||
IDS_PROTOCOL "Protocol"
|
||||
IDS_EXTENSION "Extension"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_EDIT_FILE_TYPE "Edit File Type"
|
||||
IDS_WMA_AUDIO_FILE "Windows Media Audio File (*.WMA)"
|
||||
IDS_WMA_VIDEO_FILE "Windows Media Video File (*.WMV)"
|
||||
IDS_ASF_STREAM "Advanced Streaming Format (*.ASF)"
|
||||
IDS_WINDOWS_MEDIA_STREAM "Windows Media Stream"
|
||||
IDS_UNKNOWN_ERROR "unknown error: %x"
|
||||
IDS_ATTRIBUTE "Attribute"
|
||||
IDS_VALUE "Value"
|
||||
IDS_CLOSE "Close"
|
||||
IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST
|
||||
"Unable to write to file\nDoes the file still exist?"
|
||||
IDS_UNABLE_TO_WRITE_TO_FILE "Unable to write to file"
|
||||
IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS
|
||||
"Unable to write to file\nYou may not have access to modify this file."
|
||||
IDS_SAVE_FAILED "Save failed"
|
||||
IDS_TRUE "True"
|
||||
IDS_FALSE "False"
|
||||
IDS_UNKNOWN "Unknown"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_WINDOWS_MEDIA_XXX "Windows Media: %dx%d %c%c%c%c"
|
||||
IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_NEED_LICENSE
|
||||
"Windows Media Protected Content\n\nIn order to play this file, you must acquire a license.\nA website will launch to perform this process.\n\nProceed?"
|
||||
IDS_LICENSE_REQUIRED "License required"
|
||||
IDS_BEGINNING_UPDATE "Beginning Update"
|
||||
IDS_UPDATE_FAILED "Update Failed"
|
||||
IDS_UPDATE_CANCELLED "Update Cancelled"
|
||||
IDS_IDLE "Idle"
|
||||
IDS_REQUESTING "Requesting"
|
||||
IDS_RECEIVING_DATA "Receiving Data"
|
||||
IDS_COMPLETED "Completed"
|
||||
IDS_INSTALLING "Installing"
|
||||
IDS_FILE_PROTECTED_CANNOT_PLAY_IN_WINAMP
|
||||
"This file is protected, but cannot be played in Winamp."
|
||||
IDS_LICENSE_ACQUIRED "License Acquired"
|
||||
IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_UNTRUSTED_SOURCE
|
||||
"Windows Media Protected Content\n\nThe license for this license is from an untrusted source.\n\nProceed?"
|
||||
IDS_UNTRUSTED_LICENSE "Untrusted License"
|
||||
IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_TAMPERED_LICENSE
|
||||
"Windows Media Protected Content\n\nThe license for this license appeared to have been tampered.\nIt is recommended that you answer ""No"".\n\nProceed?"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TAMPERED_LICENSE "Tampered License"
|
||||
IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_SECURITY_UPDATE
|
||||
"Windows Media Protected Content\n\nIn order to play this file, a security update is required.\n\nProceed?"
|
||||
IDS_SECURITY_UPDATE_REQUIRED "Security Update Required"
|
||||
IDS_UPDATING "Updating"
|
||||
IDS_ENCRYPTED_IN_OP_LEVEL_HIGHER_THAN_SUPPORTED
|
||||
"This file is encrypted with an Output Protection Level higher than supported in Winamp."
|
||||
IDS_WINDOWS_MEDIA_PLAYBACK_FAILURE "Windows Media Playback Failure"
|
||||
IDS_UPDATE_REQUIRED "Update required"
|
||||
IDS_ACQUIRING_LICENSE "Acquiring License"
|
||||
IDS_DRM_LICENSE_EXPIRED_VISIT_WINAMP_COM
|
||||
"DRM License has expired! Please visit Winamp.com"
|
||||
IDS_ERROR "Error"
|
||||
IDS_BUFFERING "Buffering"
|
||||
IDS_UNKNOWN_ERROR_WAV "Unknown Error."
|
||||
IDS_CODEC "Codec"
|
||||
IDS_DURATION "Duration"
|
||||
IDS_BITRATE "Bitrate"
|
||||
IDS_FILESIZE "File Size"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_YES "Yes"
|
||||
IDS_NO "No"
|
||||
IDS_WMVER "WM Version"
|
||||
IDS_SEEKABLE "Seekable"
|
||||
IDS_STRIDABLE "Stridable"
|
||||
IDS_BROADCAST "Broadcast"
|
||||
IDS_PROTECTED "Protected"
|
||||
IDS_TRUSTED "Trusted"
|
||||
IDS_CONTAINS "Contains"
|
||||
IDS_NAME "Name"
|
||||
IDS_KBPS "kbps"
|
||||
IDS_ABOUT_TEXT "%s\n© 2005-2023 Winamp SA\nWritten by: Ben Allison\nBuild date: %s\n\nWindows Media is a trademark\nof Microsoft Corporation."
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_AUDIO "Audio"
|
||||
IDS_VIDEO "Video"
|
||||
IDS_IMAGE "Image"
|
||||
IDS_SCRIPT "Script"
|
||||
IDS_NONE "None"
|
||||
IDS_WINDOWS_MEDIA_PLAYLIST "Windows Media Playlist"
|
||||
IDS_ASX_PLAYLIST "Advanced Streaming Redirector (ASX)"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
#include "version.rc2"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29613.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_wm", "in_wmvdrm.vcxproj", "{9362E6C2-EFCC-4104-8671-78222E0A5FCC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|x64.Build.0 = Debug|x64
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|Win32.Build.0 = Release|Win32
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|x64.ActiveCfg = Release|x64
|
||||
{9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3A0E00EA-C715-4273-A005-BCCCC6158254}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,361 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>in_wm</ProjectName>
|
||||
<ProjectGuid>{9362E6C2-EFCC-4104-8671-78222E0A5FCC}</ProjectGuid>
|
||||
<RootNamespace>in_wmvdrm</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg">
|
||||
<VcpkgEnabled>false</VcpkgEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
|
||||
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
|
||||
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<ObjectFileName>$(IntDir)</ObjectFileName>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4005;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<ObjectFileName>$(IntDir)</ObjectFileName>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4005;4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\nu\CCVersion.cpp" />
|
||||
<ClCompile Include="..\..\..\nu\listview.cpp" />
|
||||
<ClCompile Include="..\..\..\Winamp\strutil.cpp" />
|
||||
<ClCompile Include="AlbumArt.cpp" />
|
||||
<ClCompile Include="api.cpp" />
|
||||
<ClCompile Include="ASXLoader.cpp" />
|
||||
<ClCompile Include="AudioFormat.cpp" />
|
||||
<ClCompile Include="AudioLayer.cpp" />
|
||||
<ClCompile Include="AudioThread.cpp" />
|
||||
<ClCompile Include="BufferLayer.cpp" />
|
||||
<ClCompile Include="ClockLayer.cpp" />
|
||||
<ClCompile Include="config.cpp">
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WM_DEFINE_CONFIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WM_DEFINE_CONFIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ConfigDialog.cpp" />
|
||||
<ClCompile Include="directdraw.cpp" />
|
||||
<ClCompile Include="ExtendedFileInfo.cpp" />
|
||||
<ClCompile Include="ExtendedRead.cpp" />
|
||||
<ClCompile Include="factory_Handler.cpp" />
|
||||
<ClCompile Include="FileInfoDialog.cpp" />
|
||||
<ClCompile Include="FileTypes.cpp" />
|
||||
<ClCompile Include="GainLayer.cpp" />
|
||||
<ClCompile Include="loadini.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="MediaThread.cpp" />
|
||||
<ClCompile Include="MetaTag.cpp" />
|
||||
<ClCompile Include="MetaTagFactory.cpp" />
|
||||
<ClCompile Include="output\OutPlugin.cpp" />
|
||||
<ClCompile Include="PlaylistHandler.cpp" />
|
||||
<ClCompile Include="RawReader.cpp" />
|
||||
<ClCompile Include="SeekLayer.cpp" />
|
||||
<ClCompile Include="StatusHook.cpp" />
|
||||
<ClCompile Include="TagAlias.cpp" />
|
||||
<ClCompile Include="util.cpp" />
|
||||
<ClCompile Include="VideoDataConverter.cpp" />
|
||||
<ClCompile Include="VideoLayer.cpp" />
|
||||
<ClCompile Include="VideoOutputChildDDraw.cpp" />
|
||||
<ClCompile Include="VideoThread.cpp" />
|
||||
<ClCompile Include="vidutils.cpp" />
|
||||
<ClCompile Include="vid_ddraw.cpp" />
|
||||
<ClCompile Include="vid_overlay.cpp" />
|
||||
<ClCompile Include="WaitLayer.cpp" />
|
||||
<ClCompile Include="WinampInterface.cpp" />
|
||||
<ClCompile Include="WMCallback.cpp" />
|
||||
<ClCompile Include="WMDRMModule.cpp" />
|
||||
<ClCompile Include="WMHandler.cpp" />
|
||||
<ClCompile Include="WMInformation.cpp" />
|
||||
<ClCompile Include="WMPlaylist.cpp" />
|
||||
<ClCompile Include="WPLLoader.cpp" />
|
||||
<ClCompile Include="XMLString.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\nu\listview.h" />
|
||||
<ClInclude Include="..\..\Output\out_ds\res_wa2\resource.h" />
|
||||
<ClInclude Include="..\..\..\Winamp\strutil.h" />
|
||||
<ClInclude Include="AlbumArt.h" />
|
||||
<ClInclude Include="api.h" />
|
||||
<ClInclude Include="ASXLoader.h" />
|
||||
<ClInclude Include="AudioFormat.h" />
|
||||
<ClInclude Include="AudioLayer.h" />
|
||||
<ClInclude Include="AudioThread.h" />
|
||||
<ClInclude Include="AutoChar.h" />
|
||||
<ClInclude Include="AutoWide.h" />
|
||||
<ClInclude Include="BufferLayer.h" />
|
||||
<ClInclude Include="ClockLayer.h" />
|
||||
<ClInclude Include="config.h" />
|
||||
<ClInclude Include="ConfigDialog.h" />
|
||||
<ClInclude Include="directdraw.h" />
|
||||
<ClInclude Include="ExtendedRead.h" />
|
||||
<ClInclude Include="factory_Handler.h" />
|
||||
<ClInclude Include="FileInfoDialog.h" />
|
||||
<ClInclude Include="FileTypes.h" />
|
||||
<ClInclude Include="GainLayer.h" />
|
||||
<ClInclude Include="loadini.h" />
|
||||
<ClInclude Include="Main.h" />
|
||||
<ClInclude Include="MediaThread.h" />
|
||||
<ClInclude Include="MetaTag.h" />
|
||||
<ClInclude Include="MetaTagFactory.h" />
|
||||
<ClInclude Include="OutputStream.h" />
|
||||
<ClInclude Include="output\AudioOut.h" />
|
||||
<ClInclude Include="output\OutPlugin.h" />
|
||||
<ClInclude Include="PlaylistHandler.h" />
|
||||
<ClInclude Include="RawReader.h" />
|
||||
<ClInclude Include="Remaining.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="SeekLayer.h" />
|
||||
<ClInclude Include="StatusHook.h" />
|
||||
<ClInclude Include="TagAlias.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
<ClInclude Include="VideoDataConverter.h" />
|
||||
<ClInclude Include="VideoLayer.h" />
|
||||
<ClInclude Include="VideoOutputChildDDraw.h" />
|
||||
<ClInclude Include="VideoThread.h" />
|
||||
<ClInclude Include="vidutils.h" />
|
||||
<ClInclude Include="vid_ddraw.h" />
|
||||
<ClInclude Include="vid_overlay.h" />
|
||||
<ClInclude Include="WaitLayer.h" />
|
||||
<ClInclude Include="WinampInterface.h" />
|
||||
<ClInclude Include="WMCallback.h" />
|
||||
<ClInclude Include="WMDRMModule.h" />
|
||||
<ClInclude Include="WMHandler.h" />
|
||||
<ClInclude Include="WMInformation.h" />
|
||||
<ClInclude Include="WMPlaylist.h" />
|
||||
<ClInclude Include="WPLLoader.h" />
|
||||
<ClInclude Include="XMLString.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_wm.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="DESIGN.txt" />
|
||||
<Text Include="TODO.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
|
||||
<Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,330 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AlbumArt.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="api.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ASXLoader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AudioFormat.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AudioLayer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AudioThread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BufferLayer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ClockLayer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="config.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ConfigDialog.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="directdraw.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedFileInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedRead.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="factory_Handler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileInfoDialog.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileTypes.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GainLayer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="loadini.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MediaThread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MetaTag.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MetaTagFactory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="output\OutPlugin.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PlaylistHandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RawReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SeekLayer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StatusHook.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TagAlias.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="util.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vid_ddraw.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vid_overlay.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VideoDataConverter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VideoLayer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VideoOutputChildDDraw.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VideoThread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vidutils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WaitLayer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WinampInterface.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WMCallback.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WMDRMModule.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WMHandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WMInformation.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WMPlaylist.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WPLLoader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="XMLString.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\nu\CCVersion.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\nu\listview.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\Winamp\strutil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AlbumArt.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="api.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ASXLoader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AudioFormat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AudioLayer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="output\AudioOut.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AudioThread.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AutoChar.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AutoWide.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BufferLayer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ClockLayer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="config.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ConfigDialog.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="directdraw.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ExtendedRead.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="factory_Handler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FileInfoDialog.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FileTypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GainLayer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="loadini.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MediaThread.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MetaTagFactory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MetaTag.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="output\OutPlugin.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OutputStream.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PlaylistHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RawReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Remaining.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SeekLayer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StatusHook.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TagAlias.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vid_ddraw.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vid_overlay.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VideoDataConverter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VideoLayer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VideoOutputChildDDraw.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VideoThread.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vidutils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WaitLayer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WinampInterface.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WMCallback.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WMDRMModule.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WMHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WMInformation.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WMPlaylist.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WPLLoader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="XMLString.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\nu\listview.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Output\out_ds\res_wa2\resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Winamp\strutil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="DESIGN.txt" />
|
||||
<Text Include="TODO.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{c26aaf40-61a0-4d01-b985-d9da875ad265}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Ressource Files">
|
||||
<UniqueIdentifier>{e22165b9-a669-4451-bca6-7d77a42f2d99}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{375a8628-880b-412e-b643-d12761afdc4b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_wm.rc">
|
||||
<Filter>Ressource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "main.h"
|
||||
#include "loadini.h"
|
||||
#include "AutoWide.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
extern wchar_t INI_FILE[MAX_PATH];
|
||||
void IniFile(HWND hMainWindow)
|
||||
{
|
||||
if (!INI_FILE[0])
|
||||
{
|
||||
lstrcpyn(INI_FILE, (wchar_t *)SendMessage(hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILEW), MAX_PATH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef NULLSOFT_LOADINIH
|
||||
#define NULLSOFT_LOADINIH
|
||||
|
||||
#include <windows.h>
|
||||
extern wchar_t INI_FILE[MAX_PATH];
|
||||
void IniFile(HWND hMainWindow);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "Main.h"
|
||||
#include "api.h"
|
||||
#include "loadini.h"
|
||||
#include "FileTypes.h"
|
||||
#include <commctrl.h>
|
||||
#include "../nu/Config.h"
|
||||
#include "../nu/CCVersion.h"
|
||||
#include "resource.h"
|
||||
|
||||
wchar_t INI_FILE[MAX_PATH] = L"";
|
||||
IDispatch *winampExternal = 0;
|
||||
Nullsoft::Utility::Config wmConfig;
|
||||
WMDRM mod;
|
||||
HINSTANCE WASABI_API_LNG_HINST_WAV = 0;
|
||||
HINSTANCE WASABI_API_LNG_HINST_DS = 0;
|
||||
|
||||
int Init()
|
||||
{
|
||||
if (!IsWindow(plugin.hMainWindow))
|
||||
return IN_INIT_FAILURE;
|
||||
|
||||
if (!LoadWasabi())
|
||||
return IN_INIT_FAILURE;
|
||||
|
||||
plugin.UsesOutputPlug |= 8;
|
||||
|
||||
// need to have this initialised before we try to do anything with localisation features
|
||||
WASABI_API_START_LANG(plugin.hDllInstance,InWmLangGUID);
|
||||
|
||||
static wchar_t szDescription[256];
|
||||
swprintf(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_WINDOWS_MEDIA_DECODER),WMDRM_VERSION);
|
||||
plugin.description = (char*)szDescription;
|
||||
|
||||
IniFile(plugin.hMainWindow);
|
||||
wmConfig.SetFile(INI_FILE, L"in_wm");
|
||||
ReadConfig();
|
||||
fileTypes.ReadConfig();
|
||||
if (NULL == winampExternal)
|
||||
{
|
||||
winampExternal = (IDispatch *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_DISPATCH_OBJECT); // ask for winamp's
|
||||
if (winampExternal == (IDispatch *)1)
|
||||
winampExternal = 0;
|
||||
}
|
||||
|
||||
mod.Init();
|
||||
return IN_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void Quit()
|
||||
{
|
||||
mod.Quit();
|
||||
UnloadWasabi();
|
||||
fileTypes.types.clear();
|
||||
|
||||
if (NULL != winampExternal)
|
||||
{
|
||||
winampExternal->Release();
|
||||
winampExternal = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Config(HWND parent)
|
||||
{
|
||||
mod.Config(parent);
|
||||
fileTypes.SaveConfig();
|
||||
WriteConfig();
|
||||
}
|
||||
|
||||
void About(HWND parent)
|
||||
{
|
||||
wchar_t message[1024] = {0}, text[1024] = {0};
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WINDOWS_MEDIA_DECODER_OLD,text,1024);
|
||||
wsprintfW(message, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
|
||||
plugin.description, TEXT(__DATE__));
|
||||
DoAboutMessageBox(parent,text,message);
|
||||
}
|
||||
|
||||
void GetFileInfo(const in_char *file, wchar_t *title, int *length_in_ms) { mod.GetFileInfo(file, title, length_in_ms); }
|
||||
int InfoDialog(const in_char *file, HWND parent) { return mod.InfoBox(file, parent); }
|
||||
int IsOurFile(const in_char *fn) { return mod.IsOurFile(fn); }
|
||||
int Play(const in_char *fn) {return mod.Play(fn); }
|
||||
void Pause() { mod.Pause(); }
|
||||
void Resume() { mod.UnPause(); }
|
||||
int IsPaused() { return mod.IsPaused(); }
|
||||
void Stop() { mod.Stop(); }
|
||||
int GetLength() { return mod.GetLength(); }
|
||||
int GetOutputTime() { return mod.GetOutputTime(); }
|
||||
void SetOutputTime(int time_in_ms) { return mod.SetOutputTime(time_in_ms); }
|
||||
void SetVolume(int volume) { mod.SetVolume(volume); }
|
||||
void SetPan(int pan) { mod.SetPan(pan); }
|
||||
void EQSet(int on, char data[10], int preamp) { mod.EQSet(on, data, preamp); }
|
||||
|
||||
In_Module plugin =
|
||||
{
|
||||
IN_VER_RET, // defined in IN2.H
|
||||
"nullsoft(in_wm.dll)",
|
||||
0, // hMainWindow (filled in by winamp)
|
||||
0, // hDllInstance (filled in by winamp)
|
||||
0, // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
|
||||
0, // is_seekable
|
||||
1, // uses output plug-in system
|
||||
Config,
|
||||
About,
|
||||
Init,
|
||||
Quit,
|
||||
GetFileInfo,
|
||||
InfoDialog,
|
||||
IsOurFile,
|
||||
Play,
|
||||
Pause,
|
||||
Resume,
|
||||
IsPaused,
|
||||
Stop,
|
||||
|
||||
GetLength,
|
||||
GetOutputTime,
|
||||
SetOutputTime,
|
||||
|
||||
SetVolume,
|
||||
SetPan,
|
||||
|
||||
0,0,0,0,0,0,0,0,0, // visualization calls filled in by winamp
|
||||
|
||||
0,0, // dsp calls filled in by winamp
|
||||
|
||||
EQSet,
|
||||
|
||||
NULL, // setinfo call filled in by winamp
|
||||
|
||||
0, // out_mod filled in by winamp
|
||||
};
|
||||
|
||||
extern "C" __declspec( dllexport ) In_Module * winampGetInModule2()
|
||||
{
|
||||
return &plugin;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef NULLSOFT_AUDIOOUTH
|
||||
#define NULLSOFT_AUDIOOUTH
|
||||
|
||||
#include <windows.h>
|
||||
#include "../../../../Winamp/out.h"
|
||||
|
||||
enum InitState
|
||||
{
|
||||
StatusNone = 0,
|
||||
StatusInit,
|
||||
StatusQuit
|
||||
};
|
||||
|
||||
class AudioOut
|
||||
{
|
||||
public:
|
||||
AudioOut() : dllInstance(0), winampWnd(NULL) {}
|
||||
virtual void Init() = 0;
|
||||
virtual void Quit() = 0;
|
||||
virtual int CanWrite() = 0;
|
||||
virtual int GetWrittenTime() = 0;
|
||||
virtual int IsPlaying() = 0;
|
||||
virtual int Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms) = 0;
|
||||
virtual void Close() = 0;
|
||||
virtual int Write(char *buf, int len) = 0;
|
||||
virtual void Flush(int t) = 0;
|
||||
virtual void SetVolume(int _volume) = 0;
|
||||
virtual int Pause(int new_state) = 0;
|
||||
virtual int GetOutputTime() = 0;
|
||||
virtual void SetPan(int _pan) = 0;
|
||||
virtual void About(HWND p) = 0;
|
||||
virtual void Config(HWND w) = 0;
|
||||
|
||||
HINSTANCE dllInstance;
|
||||
HWND winampWnd;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "OutPlugin.h"
|
||||
#include "../Winamp/In2.h"
|
||||
|
||||
#include "WMDRMModule.h"
|
||||
extern In_Module plugin;
|
||||
OutPlugin pluginOut;
|
||||
|
||||
OutPlugin::OutPlugin()
|
||||
{}
|
||||
|
||||
void OutPlugin::Init()
|
||||
{
|
||||
plugin.outMod->Init();
|
||||
}
|
||||
void OutPlugin::Quit()
|
||||
{
|
||||
plugin.outMod->Quit();
|
||||
}
|
||||
int OutPlugin::CanWrite()
|
||||
{
|
||||
return plugin.outMod->CanWrite();
|
||||
}
|
||||
int OutPlugin::GetWrittenTime()
|
||||
{
|
||||
return plugin.outMod->GetWrittenTime();
|
||||
}
|
||||
int OutPlugin::IsPlaying()
|
||||
{
|
||||
return plugin.outMod->IsPlaying();
|
||||
}
|
||||
int OutPlugin::Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms)
|
||||
{
|
||||
return plugin.outMod->Open(samplerate, numchannels, bitspersamp, bufferlenms, prebufferms);
|
||||
}
|
||||
void OutPlugin::Close()
|
||||
{
|
||||
plugin.outMod->Close();
|
||||
}
|
||||
int OutPlugin::Write(char *buf, int len)
|
||||
{
|
||||
return plugin.outMod->Write(buf, len);
|
||||
}
|
||||
void OutPlugin::Flush(int t)
|
||||
{
|
||||
plugin.outMod->Flush(t);
|
||||
}
|
||||
void OutPlugin::SetVolume(int _volume)
|
||||
{
|
||||
plugin.outMod->SetVolume(_volume);
|
||||
}
|
||||
int OutPlugin::Pause(int new_state)
|
||||
{
|
||||
return plugin.outMod->Pause(new_state);
|
||||
}
|
||||
int OutPlugin::GetOutputTime()
|
||||
{
|
||||
return plugin.outMod->GetOutputTime();
|
||||
}
|
||||
void OutPlugin::SetPan(int _pan)
|
||||
{
|
||||
plugin.outMod->SetPan(_pan);
|
||||
}
|
||||
void OutPlugin::About(HWND p)
|
||||
{
|
||||
plugin.outMod->About(p);
|
||||
}
|
||||
void OutPlugin::Config(HWND w)
|
||||
{
|
||||
plugin.outMod->Config(w);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef NULLSOFT_OUTPLUGINH
|
||||
#define NULLSOFT_OUTPLUGINH
|
||||
|
||||
#include "AudioOut.h"
|
||||
|
||||
class OutPlugin : public AudioOut
|
||||
{
|
||||
public:
|
||||
OutPlugin();
|
||||
void Init();
|
||||
void Quit();
|
||||
int CanWrite();
|
||||
int GetWrittenTime();
|
||||
int IsPlaying();
|
||||
int Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms);
|
||||
void Close();
|
||||
int Write(char *buf, int len);
|
||||
void Flush(int t);
|
||||
void SetVolume(int _volume);
|
||||
int Pause(int new_state);
|
||||
int GetOutputTime();
|
||||
void SetPan(int _pan);
|
||||
void About(HWND p);
|
||||
void Config(HWND w);
|
||||
};
|
||||
|
||||
extern OutPlugin pluginOut;
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by out_wave.rc
|
||||
//
|
||||
#define IDS_NULLSOFT_WAVEOUT_OLD 0
|
||||
#define IDS_NOT_ACTIVE 1
|
||||
#define IDS_UNKNOWN_MMSYSTEM_ERROR 2
|
||||
#define IDC_RESET 3
|
||||
#define IDS_UNSUPPORTED_PCM_FORMAT 3
|
||||
#define IDS_ANOTHER_PROGRAM_IS_USING_THE_SOUNDCARD 4
|
||||
#define IDS_NO_SOUND_DEVICES_FOUND 5
|
||||
#define IDS_INTERNAL_DRIVER_ERROR 6
|
||||
#define IDS_REINSTALL_SOUNDCARD_DRIVERS 7
|
||||
#define IDS_ERROR_CODE_WINDOWS_ERROR_MESSAGE 8
|
||||
#define IDS_ERROR_CODE 9
|
||||
#define IDS_DATA_FORMAT 10
|
||||
#define IDS_BUFFER_STATUS 11
|
||||
#define IDS_LATENCY 12
|
||||
#define IDS_ABOUT 13
|
||||
#define IDS_PREFS_TITLE 15
|
||||
#define IDS_ERROR 16
|
||||
#define IDS_WAVE_U_MS 17
|
||||
#define IDS_ABOUT_STRING 18
|
||||
#define IDS_ABOUT_TEXT 18
|
||||
#define IDD_CONFIG 300
|
||||
#define IDD_WAVE_CONFIG 300
|
||||
#define IDC_GAPLESS 1000
|
||||
#define IDC_BUF_SIZE 1001
|
||||
#define IDC_SPIN1 1002
|
||||
#define IDC_PRIMARY 1003
|
||||
#define IDC_PREBUF_SIZE 1003
|
||||
#define IDC_DEV 1004
|
||||
#define IDC_HACK 1005
|
||||
#define IDC_EXCLUSIVE 1006
|
||||
#define IDC_PREBUFFER 1007
|
||||
#define IDC_SPIN2 1008
|
||||
#define IDC_VOL_ENABLE 1009
|
||||
#define IDC_ALT_VOL 1010
|
||||
#define IDC_VOL_RESET 1011
|
||||
#define IDC_PREB_TEXT 1012
|
||||
#define IDC_STATE 1013
|
||||
#define IDC_PREBUFFER_1 1014
|
||||
#define IDC_PREBUFFER_2 1015
|
||||
#define IDC_PREBUF_DISP_1 1016
|
||||
#define IDC_PREBUF_DISP_2 1017
|
||||
#define IDC_BLAH 1018
|
||||
#define IDC_BUFFER 1020
|
||||
#define IDC_BUF_DISP 1021
|
||||
#define IDS_NULLSOFT_WAVEOUT 65534
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 301
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1019
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,266 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by fileinfo.rc
|
||||
//
|
||||
#define IDS_NULLSOFT_WINDOWS_MEDIA_DECODER_OLD 0
|
||||
#define IDS_WMDRM_ABOUT 1
|
||||
#define IDS_ABOUT 2
|
||||
#define IDS_REALTIME 3
|
||||
#define IDS_CONNECTING 4
|
||||
#define IDS_LOCATING 5
|
||||
#define IDS_ACCESS_DENIED 6
|
||||
#define IDS_STEREO 7
|
||||
#define IDS_QUADROPHONIC 8
|
||||
#define IDS_SURROUND 9
|
||||
#define IDS_5_1 10
|
||||
#define IDS_7_1 11
|
||||
#define IDS_EXT 12
|
||||
#define IDS_DESCRIPTION 13
|
||||
#define IDS_PROTOCOL 14
|
||||
#define IDS_EXTENSION 15
|
||||
#define IDS_EDIT_FILE_TYPE 16
|
||||
#define IDS_WMA_AUDIO_FILE 17
|
||||
#define IDS_WMA_VIDEO_FILE 18
|
||||
#define IDS_ASF_STREAM 19
|
||||
#define IDS_WINDOWS_MEDIA_STREAM 20
|
||||
#define IDS_UNKNOWN_ERROR 21
|
||||
#define IDS_ATTRIBUTE 22
|
||||
#define IDS_VALUE 23
|
||||
#define IDS_CLOSE 24
|
||||
#define IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST 25
|
||||
#define IDS_UNABLE_TO_WRITE_TO_FILE 26
|
||||
#define IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS 27
|
||||
#define IDS_SAVE_FAILED 28
|
||||
#define IDS_TRUE 29
|
||||
#define IDS_FALSE 30
|
||||
#define IDS_UNKNOWN 31
|
||||
#define IDS_WINDOWS_MEDIA_XXX 32
|
||||
#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_NEED_LICENSE 33
|
||||
#define IDS_LICENSE_REQUIRED 34
|
||||
#define IDS_BEGINNING_UPDATE 35
|
||||
#define IDS_UPDATE_FAILED 36
|
||||
#define IDS_UPDATE_CANCELLED 37
|
||||
#define IDS_IDLE 38
|
||||
#define IDS_REQUESTING 39
|
||||
#define IDS_RECEIVING_DATA 40
|
||||
#define IDS_COMPLETED 41
|
||||
#define IDS_INSTALLING 42
|
||||
#define IDS_FILE_PROTECTED_CANNOT_PLAY_IN_WINAMP 43
|
||||
#define IDS_LICENSE_ACQUIRED 44
|
||||
#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_UNTRUSTED_SOURCE 45
|
||||
#define IDS_UNTRUSTED_LICENSE 46
|
||||
#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_TAMPERED_LICENSE 47
|
||||
#define IDS_TAMPERED_LICENSE 48
|
||||
#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_SECURITY_UPDATE 49
|
||||
#define IDS_SECURITY_UPDATE_REQUIRED 50
|
||||
#define IDS_UPDATING 51
|
||||
#define IDS_ENCRYPTED_IN_OP_LEVEL_HIGHER_THAN_SUPPORTED 52
|
||||
#define IDS_WINDOWS_MEDIA_PLAYBACK_FAILURE 53
|
||||
#define IDS_UPDATE_REQUIRED 54
|
||||
#define IDS_ACQUIRING_LICENSE 55
|
||||
#define IDS_DRM_LICENSE_EXPIRED_VISIT_WINAMP_COM 56
|
||||
#define IDS_ERROR 57
|
||||
#define IDS_STRING58 58
|
||||
#define IDS_BUFFERING 58
|
||||
#define IDS_STRING59 59
|
||||
#define IDS_UNKNOWN_ERROR_WAV 59
|
||||
#define IDS_CODEC 60
|
||||
#define IDS_DURATION 61
|
||||
#define IDS_BITRATE 62
|
||||
#define IDS_FILESIZE 63
|
||||
#define IDS_YES 64
|
||||
#define IDS_NO 65
|
||||
#define IDS_WMVER 66
|
||||
#define IDS_SEEKABLE 67
|
||||
#define IDS_STRIDABLE 68
|
||||
#define IDS_BROADCAST 69
|
||||
#define IDS_PROTECTED 70
|
||||
#define IDS_TRUSTED 71
|
||||
#define IDS_CONTAINS 72
|
||||
#define IDS_NAME 73
|
||||
#define IDS_STRING74 74
|
||||
#define IDS_KBPS 74
|
||||
#define IDS_ABOUT_TEXT 75
|
||||
#define IDD_CONFIG 105
|
||||
#define IDD_CONFIG_STATUS 112
|
||||
#define IDD_ADDTYPE 113
|
||||
#define IDD_ADVANCED 114
|
||||
#define IDS_AUDIO 117
|
||||
#define IDS_VIDEO 118
|
||||
#define IDS_IMAGE 119
|
||||
#define IDS_SCRIPT 120
|
||||
#define IDS_NONE 121
|
||||
#define IDS_WINDOWS_MEDIA_PLAYLIST 122
|
||||
#define IDS_STRING124 123
|
||||
#define IDS_ASX_PLAYLIST 123
|
||||
#define IDD_INFO 131
|
||||
#define IDC_EDIT 1003
|
||||
#define IDC_DEV 1004
|
||||
#define IDC_REVERT 1006
|
||||
#define IDC_BUTTON5 1007
|
||||
#define IDC_DELETE 1007
|
||||
#define IDC_REMOVETYPE 1007
|
||||
#define IDC_ADD 1008
|
||||
#define IDC_APPLY 1009
|
||||
#define IDC_TEMP 1009
|
||||
#define IDC_VOL_ENABLE 1009
|
||||
#define IDC_METADATALIST 1010
|
||||
#define IDC_ALT_VOL 1010
|
||||
#define IDC_VOL_RESET 1011
|
||||
#define IDC_EDIT1 1011
|
||||
#define IDC_AUDIO_EARLY_PAD 1011
|
||||
#define IDC_VIDEO_EARLYPAD 1011
|
||||
#define IDC_BUFFER_TIME 1011
|
||||
#define IDC_VIDEO_EARLY_PAD 1012
|
||||
#define IDC_EDIT2 1012
|
||||
#define IDC_AUDIO_EARLYPAD 1012
|
||||
#define IDC_STATE 1013
|
||||
#define IDC_FILENAME 1014
|
||||
#define IDC_STATIC_TITLE 1015
|
||||
#define IDC_STATIC_ARTIST 1016
|
||||
#define IDC_STATIC_ALBUM 1017
|
||||
#define IDC_STATIC_TRACK 1018
|
||||
#define IDC_BLAH 1018
|
||||
#define IDC_STATIC_YEAR 1019
|
||||
#define IDC_STATIC_GENRE 1020
|
||||
#define IDC_STATIC_COMMENTS 1021
|
||||
#define IDC_EDIT_TITLE 1022
|
||||
#define IDC_EDIT_ARTIST 1023
|
||||
#define IDC_EDIT_ALBUM 1024
|
||||
#define IDC_EDIT_COMMENTS 1025
|
||||
#define IDC_EDIT_GENRE 1026
|
||||
#define IDC_EDIT_YEAR 1027
|
||||
#define IDC_EDIT_TRACK 1028
|
||||
#define IDC_BUTTON_DELALL 1028
|
||||
#define IDC_LOWMEMORY 1029
|
||||
#define IDC_EDIT_GENRE2 1029
|
||||
#define IDC_EDIT_PUBLISHER 1029
|
||||
#define IDC_STATIC_GENRE2 1030
|
||||
#define IDC_AUDIO_OUTOFORDER 1031
|
||||
#define IDC_EDIT_GENRE3 1031
|
||||
#define IDC_EDIT_ALBUMARTIST 1031
|
||||
#define IDC_AUDIO_DEDICATED_THREAD 1032
|
||||
#define IDC_VIDEO_OUTOFORDER 1033
|
||||
#define IDC_AUDIO_EARLY 1034
|
||||
#define IDC_VIDEO_DEDICATED_THREAD 1035
|
||||
#define IDC_VIDEO_EARLY 1036
|
||||
#define IDC_VIDEO_JITTER 1039
|
||||
#define IDC_VIDEO_CACHE_FRAMES 1040
|
||||
#define IDC_VIDEO_CATCHUP 1042
|
||||
#define IDC_VIDEO_NOTIFYLATE 1043
|
||||
#define IDC_CHECK3 1044
|
||||
#define IDC_VIDEO_FRAMEDROPOFFSET 1044
|
||||
#define IDC_SILENT 1044
|
||||
#define IDC_REALTIME 1044
|
||||
#define IDC_BROWSER 1045
|
||||
#define IDC_EDIT5 1049
|
||||
#define IDC_RESET 1052
|
||||
#define IDC_GLOBAL_FADES 1053
|
||||
#define IDC_CUSTOM_FADE_SPIN 1054
|
||||
#define IDC_LOGVOL_SPIN 1055
|
||||
#define IDC_REFRESH_SPIN 1056
|
||||
#define IDC_FADE 1057
|
||||
#define IDC_FADE_SPIN 1058
|
||||
#define IDC_CREATE_PRIMARY 1059
|
||||
#define IDC_PAUSEFADE2 1060
|
||||
#define IDC_DEVICE 1061
|
||||
#define IDC_WAITx 1062
|
||||
#define IDC_PREBUFFER_2 1063
|
||||
#define IDC_KILLSIL 1064
|
||||
#define IDC_DB 1065
|
||||
#define IDC_DB_DISPLAY 1066
|
||||
#define IDC_PREBUFFER_1 1067
|
||||
#define IDC_LIST 1068
|
||||
#define IDC_PREBUF_DISP_1 1069
|
||||
#define IDC_CUSTOM_FADE 1070
|
||||
#define IDC_PREBUF_DISP_2 1071
|
||||
#define IDC_USE_CUSTOM_FADE 1072
|
||||
#define IDC_BUFFER 1073
|
||||
#define IDC_BUF_DISP 1074
|
||||
#define IDC_STATIC_MS 1075
|
||||
#define IDC_BUF_RESET 1076
|
||||
#define IDC_VOLUME 1077
|
||||
#define IDC_TAB1 1078
|
||||
#define IDC_TAB 1079
|
||||
#define IDC_DEVICE_INFO 1080
|
||||
#define IDC_PDS_FAQ 1081
|
||||
#define IDC_FADE_GROUP 1082
|
||||
#define IDC_FADE_ENABLED 1083
|
||||
#define IDC_REFRESH 1084
|
||||
#define IDC_STAT_COPY 1085
|
||||
#define IDC_LOGVOL_MIN 1086
|
||||
#define IDC_LOGVOL_STATIC 1087
|
||||
#define IDC_LOGVOL_STATIC2 1088
|
||||
#define IDC_FADEVOL 1089
|
||||
#define IDC_PREBUF_AUTO 1090
|
||||
#define IDC_STATIC_BLEH 1091
|
||||
#define IDC_WAVE_GAPLESS 1092
|
||||
#define IDC_WAVE_RESET 1093
|
||||
#define IDC_WAVE_BUF_SIZE 1093
|
||||
#define IDC_WAVE_SPIN1 1094
|
||||
#define IDC_WAVE_PRIMARY 1095
|
||||
#define IDC_WAVE_PREBUF_SIZE 1095
|
||||
#define IDC_WAVE_DEV 1096
|
||||
#define IDC_WAVE_HACK 1097
|
||||
#define IDC_WAVE_EXCLUSIVE 1098
|
||||
#define IDC_WAVE_PREBUFFER 1099
|
||||
#define IDC_WAVE_SPIN2 1100
|
||||
#define IDC_WAVE_VOL_ENABLE 1101
|
||||
#define IDC_WAVE_ALT_VOL 1102
|
||||
#define IDC_WAVE_VOL_RESET 1103
|
||||
#define IDC_WAVE_PREB_TEXT 1104
|
||||
#define IDC_WAVE_STATE 1105
|
||||
#define IDC_WAVE_PREBUFFER_1 1106
|
||||
#define IDC_WAVE_PREBUFFER_2 1107
|
||||
#define IDC_WAVE_PREBUF_DISP_1 1108
|
||||
#define IDC_WAVE_PREBUF_DISP_2 1109
|
||||
#define IDC_WAVE_BLAH 1110
|
||||
#define IDC_WAVE_BUFFER 1111
|
||||
#define IDC_WAVE_BUF_DISP 1112
|
||||
#define IDC_VOLMODE 1113
|
||||
#define IDC_LOGFADES 1114
|
||||
#define IDC_TEXT1 1115
|
||||
#define IDC_HW_MIX 1116
|
||||
#define IDC_VER 1117
|
||||
#define IDC_LIST2 1119
|
||||
#define IDC_FLIP 1120
|
||||
#define IDC_HTTPMETA 1121
|
||||
#define IDC_ADVANCED 1122
|
||||
#define IDC_DEFAULTTYPE 1123
|
||||
#define IDC_ADDTYPE 1124
|
||||
#define IDC_EDITTYPE 1125
|
||||
#define IDC_TYPELIST 1126
|
||||
#define IDC_UNTRUSTED 1127
|
||||
#define IDC_TYPE 1129
|
||||
#define IDC_DESCRIPTION 1130
|
||||
#define IDC_FILEEXTENSION 1131
|
||||
#define IDC_PROTOCOL 1132
|
||||
#define IDC_VIDEO_THREAD 1135
|
||||
#define IDC_AUDIO_DROP 1138
|
||||
#define IDC_CHECK8 1139
|
||||
#define IDC_VIDEO_NOTIFY 1139
|
||||
#define IDC_AUDIO_THREAD 1140
|
||||
#define IDC_EDIT4 1145
|
||||
#define IDC_EDIT7 1146
|
||||
#define IDC_AUDIO_CACHE_FRAMES 1146
|
||||
#define IDC_EDIT6 1148
|
||||
#define IDC_VIDEO_DROP_THRESHOLD 1148
|
||||
#define IDC_EDIT9 1149
|
||||
#define IDC_TYPE_AUDIO 1151
|
||||
#define IDC_TYPE_VIDEO 1152
|
||||
#define IDC_STATIC_TYPE 1153
|
||||
#define IDC_TYPE_AUDIO2 1154
|
||||
#define IDC_EXTRA_ASX 1154
|
||||
#define IDC_AUDIO_SPEAKER_COUNT 10000
|
||||
#define IDS_NULLSOFT_WINDOWS_MEDIA_DECODER 65534
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 127
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1155
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "main.h"
|
||||
#include "resource.h"
|
||||
#include <stdio.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
//static const GUID INVALID_GUID =
|
||||
//{ 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
|
||||
void WaitForEvent(HANDLE hEvent, DWORD msMaxWaitTime)
|
||||
{
|
||||
// DWORD i;
|
||||
MSG msg;
|
||||
const unsigned long eachWait = 10;
|
||||
unsigned long totalWait = 0;
|
||||
|
||||
while (WaitForSingleObject(hEvent, eachWait) == WAIT_TIMEOUT)
|
||||
{
|
||||
while (PeekMessage(&msg, (HWND) NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
//TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
totalWait += eachWait;
|
||||
if (totalWait >= msMaxWaitTime)
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
char *HRErrorCode(HRESULT hr)
|
||||
{
|
||||
#define HR_ERROR_CODE(x) case x: return #x
|
||||
switch (hr)
|
||||
{
|
||||
HR_ERROR_CODE(E_OUTOFMEMORY);
|
||||
HR_ERROR_CODE(E_UNEXPECTED);
|
||||
HR_ERROR_CODE(S_OK);
|
||||
HR_ERROR_CODE(S_FALSE);
|
||||
HR_ERROR_CODE(E_NOINTERFACE);
|
||||
HR_ERROR_CODE(NS_E_PROTECTED_CONTENT);
|
||||
HR_ERROR_CODE(E_INVALIDARG);
|
||||
HR_ERROR_CODE(NS_E_DRM_NO_RIGHTS);
|
||||
HR_ERROR_CODE(NS_E_DRM_LICENSE_NOTACQUIRED);
|
||||
HR_ERROR_CODE(NS_E_DRM_ACQUIRING_LICENSE);
|
||||
HR_ERROR_CODE(NS_S_DRM_ACQUIRE_CANCELLED);
|
||||
HR_ERROR_CODE(NS_E_LICENSE_OUTOFDATE);
|
||||
HR_ERROR_CODE(NS_E_LICENSE_INCORRECT_RIGHTS);
|
||||
HR_ERROR_CODE(NS_E_DRM_REOPEN_CONTENT);
|
||||
HR_ERROR_CODE(NS_E_DRM_LICENSE_APPSECLOW);
|
||||
HR_ERROR_CODE(E_ABORT);
|
||||
HR_ERROR_CODE(NS_E_INVALID_REQUEST);
|
||||
HR_ERROR_CODE(NS_S_DRM_LICENSE_ACQUIRED);
|
||||
HR_ERROR_CODE(NS_S_DRM_MONITOR_CANCELLED);
|
||||
HR_ERROR_CODE(NS_E_FILE_NOT_FOUND);
|
||||
HR_ERROR_CODE(NS_E_FILE_OPEN_FAILED);
|
||||
HR_ERROR_CODE(NS_E_SERVER_NOT_FOUND);
|
||||
HR_ERROR_CODE(NS_E_UNRECOGNIZED_STREAM_TYPE);
|
||||
HR_ERROR_CODE(NS_E_NO_STREAM);
|
||||
HR_ERROR_CODE(E_ACCESSDENIED);
|
||||
HR_ERROR_CODE(NS_S_DRM_BURNABLE_TRACK);
|
||||
HR_ERROR_CODE(NS_S_DRM_BURNABLE_TRACK_WITH_PLAYLIST_RESTRICTION);
|
||||
HR_ERROR_CODE(NS_E_DRM_TRACK_EXCEEDED_PLAYLIST_RESTICTION);
|
||||
HR_ERROR_CODE(NS_E_DRM_TRACK_EXCEEDED_TRACKBURN_RESTRICTION);
|
||||
}
|
||||
static char temp[50];
|
||||
StringCchPrintfA(temp, 50, WASABI_API_LNGSTRING(IDS_UNKNOWN_ERROR), hr);
|
||||
return temp;
|
||||
|
||||
}
|
||||
|
||||
void GuidString(GUID guid, wchar_t *target, size_t len)
|
||||
{
|
||||
StringCchPrintf( target, len, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
||||
(int)guid.Data1, (int)guid.Data2, (int)guid.Data3,
|
||||
(int)guid.Data4[0], (int)guid.Data4[1],
|
||||
(int)guid.Data4[2], (int)guid.Data4[3],
|
||||
(int)guid.Data4[4], (int)guid.Data4[5],
|
||||
(int)guid.Data4[6], (int)guid.Data4[7] );
|
||||
}
|
||||
|
||||
const wchar_t *UserTextDescription(unsigned char *binary, size_t size)
|
||||
{
|
||||
WM_USER_TEXT *userText = (WM_USER_TEXT *)binary;
|
||||
|
||||
return userText->pwszDescription;
|
||||
}
|
||||
|
||||
const wchar_t *UserTextString(unsigned char *binary, size_t size)
|
||||
{
|
||||
WM_USER_TEXT *userText = (WM_USER_TEXT *)binary;
|
||||
|
||||
return userText->pwszText;
|
||||
}
|
||||
|
||||
void BinaryString(unsigned char *binary, size_t size, wchar_t *final, size_t len)
|
||||
{
|
||||
wchar_t * const start = new wchar_t[2 + size*2 + 1]; // 0x + 2 hex per byte + null terminator
|
||||
wchar_t *target = start;
|
||||
*target++='0';
|
||||
*target++='x';
|
||||
|
||||
size_t i;
|
||||
for (i = 0;i!=size;i++)
|
||||
{
|
||||
wchar_t temp[3] = {0};
|
||||
_itow(binary[i], temp, 16);
|
||||
if (!temp[0])
|
||||
{
|
||||
*target++ = '0';
|
||||
*target++ = '0';
|
||||
}
|
||||
else if (!temp[1])
|
||||
{
|
||||
*target++ = '0';
|
||||
*target++ = temp[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
*target++ = temp[0];
|
||||
*target++ = temp[1];
|
||||
}
|
||||
}
|
||||
*target = 0;
|
||||
lstrcpyn(final, start, len);
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
GUID StringGUID(const wchar_t *source)
|
||||
{
|
||||
if (source == NULL) return INVALID_GUID;
|
||||
|
||||
GUID guid = GUID_NULL;
|
||||
int Data1, Data2, Data3;
|
||||
int Data4[8] = {0};
|
||||
|
||||
// {1B3CA60C-DA98-4826-B4A9-D79748A5FD73}
|
||||
int n = swscanf(source, L" { %08x - %04x - %04x - %02x%02x - %02x%02x%02x%02x%02x%02x } ",
|
||||
&Data1, &Data2, &Data3, Data4 + 0, Data4 + 1,
|
||||
Data4 + 2, Data4 + 3, Data4 + 4, Data4 + 5, Data4 + 6, Data4 + 7 );
|
||||
|
||||
if (n != 11) return INVALID_GUID;
|
||||
|
||||
// Cross assign all the values
|
||||
guid.Data1 = Data1;
|
||||
guid.Data2 = Data2;
|
||||
guid.Data3 = Data3;
|
||||
guid.Data4[0] = Data4[0];
|
||||
guid.Data4[1] = Data4[1];
|
||||
guid.Data4[2] = Data4[2];
|
||||
guid.Data4[3] = Data4[3];
|
||||
guid.Data4[4] = Data4[4];
|
||||
guid.Data4[5] = Data4[5];
|
||||
guid.Data4[6] = Data4[6];
|
||||
guid.Data4[7] = Data4[7];
|
||||
|
||||
return guid;
|
||||
}
|
||||
|
||||
int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
|
||||
{
|
||||
MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
|
||||
msgbx.lpszText = message;
|
||||
msgbx.lpszCaption = title;
|
||||
msgbx.lpszIcon = MAKEINTRESOURCE(102);
|
||||
msgbx.hInstance = GetModuleHandle(0);
|
||||
msgbx.dwStyle = MB_USERICON;
|
||||
msgbx.hwndOwner = parent;
|
||||
return MessageBoxIndirect(&msgbx);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user