diff options
Diffstat (limited to 'Src/Plugins/Input/in_wmvdrm')
108 files changed, 14228 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp b/Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp new file mode 100644 index 00000000..485e0f77 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/ASXLoader.h b/Src/Plugins/Input/in_wmvdrm/ASXLoader.h new file mode 100644 index 00000000..c562014b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ASXLoader.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp b/Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp new file mode 100644 index 00000000..a1dc08d2 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp @@ -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; diff --git a/Src/Plugins/Input/in_wmvdrm/AlbumArt.h b/Src/Plugins/Input/in_wmvdrm/AlbumArt.h new file mode 100644 index 00000000..129377c3 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AlbumArt.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp b/Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp new file mode 100644 index 00000000..8c9c8efc --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp @@ -0,0 +1 @@ +#include "AllocLayer.h"
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AllocLayer.h b/Src/Plugins/Input/in_wmvdrm/AllocLayer.h new file mode 100644 index 00000000..ed07a3bd --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/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 diff --git a/Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp b/Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp new file mode 100644 index 00000000..7af75b24 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AudioFormat.h b/Src/Plugins/Input/in_wmvdrm/AudioFormat.h new file mode 100644 index 00000000..81aceb39 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AudioFormat.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp b/Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp new file mode 100644 index 00000000..c02f30d4 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp @@ -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(); +} diff --git a/Src/Plugins/Input/in_wmvdrm/AudioLayer.h b/Src/Plugins/Input/in_wmvdrm/AudioLayer.h new file mode 100644 index 00000000..2f836530 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AudioLayer.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/AudioThread.cpp b/Src/Plugins/Input/in_wmvdrm/AudioThread.cpp new file mode 100644 index 00000000..b034c636 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AudioThread.cpp @@ -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); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AudioThread.h b/Src/Plugins/Input/in_wmvdrm/AudioThread.h new file mode 100644 index 00000000..4192d604 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AudioThread.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AutoChar.h b/Src/Plugins/Input/in_wmvdrm/AutoChar.h new file mode 100644 index 00000000..46cd840e --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AutoChar.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/AutoWide.h b/Src/Plugins/Input/in_wmvdrm/AutoWide.h new file mode 100644 index 00000000..9248dda6 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/AutoWide.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp b/Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp new file mode 100644 index 00000000..d41a7cfd --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp @@ -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(); +} + diff --git a/Src/Plugins/Input/in_wmvdrm/BufferLayer.h b/Src/Plugins/Input/in_wmvdrm/BufferLayer.h new file mode 100644 index 00000000..fdfccdbc --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/BufferLayer.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/BufferPool.h b/Src/Plugins/Input/in_wmvdrm/BufferPool.h new file mode 100644 index 00000000..b4c1ec2a --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/BufferPool.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/CachedData.h b/Src/Plugins/Input/in_wmvdrm/CachedData.h new file mode 100644 index 00000000..8782ead1 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/CachedData.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp b/Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp new file mode 100644 index 00000000..cce27870 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp @@ -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); +} diff --git a/Src/Plugins/Input/in_wmvdrm/ClockLayer.h b/Src/Plugins/Input/in_wmvdrm/ClockLayer.h new file mode 100644 index 00000000..1fb89230 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ClockLayer.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp new file mode 100644 index 00000000..ece52a0e --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/ConfigDialog.h b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.h new file mode 100644 index 00000000..e2b22e4f --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.h @@ -0,0 +1,4 @@ +#ifndef NULLSOFT_CONFIGDIALOGH +#define NULLSOFT_CONFIGDIALOGH +INT_PTR CALLBACK PreferencesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/DESIGN.txt b/Src/Plugins/Input/in_wmvdrm/DESIGN.txt new file mode 100644 index 00000000..2785c5c5 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/DESIGN.txt @@ -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
+
diff --git a/Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp b/Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp new file mode 100644 index 00000000..53ee3cf4 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp @@ -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; +} diff --git a/Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp new file mode 100644 index 00000000..d07fa008 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/ExtendedRead.h b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.h new file mode 100644 index 00000000..1e9cd063 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/FactoryHelper.h b/Src/Plugins/Input/in_wmvdrm/FactoryHelper.h new file mode 100644 index 00000000..aeae00b5 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/FactoryHelper.h @@ -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); + } +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp new file mode 100644 index 00000000..eb0965f1 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp @@ -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; +} +*/
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h new file mode 100644 index 00000000..76d9b7d9 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/FileTypes.cpp b/Src/Plugins/Input/in_wmvdrm/FileTypes.cpp new file mode 100644 index 00000000..839ae21b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/FileTypes.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/FileTypes.h b/Src/Plugins/Input/in_wmvdrm/FileTypes.h new file mode 100644 index 00000000..335ddb70 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/FileTypes.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/GainLayer.cpp b/Src/Plugins/Input/in_wmvdrm/GainLayer.cpp new file mode 100644 index 00000000..84811f79 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/GainLayer.cpp @@ -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(); +} diff --git a/Src/Plugins/Input/in_wmvdrm/GainLayer.h b/Src/Plugins/Input/in_wmvdrm/GainLayer.h new file mode 100644 index 00000000..6eb6c67d --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/GainLayer.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/Main.h b/Src/Plugins/Input/in_wmvdrm/Main.h new file mode 100644 index 00000000..85b3e4b5 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/Main.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/MediaThread.cpp b/Src/Plugins/Input/in_wmvdrm/MediaThread.cpp new file mode 100644 index 00000000..1120c952 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/MediaThread.cpp @@ -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 +} diff --git a/Src/Plugins/Input/in_wmvdrm/MediaThread.h b/Src/Plugins/Input/in_wmvdrm/MediaThread.h new file mode 100644 index 00000000..3dd3fdcb --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/MediaThread.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTag.cpp b/Src/Plugins/Input/in_wmvdrm/MetaTag.cpp new file mode 100644 index 00000000..9eecb772 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/MetaTag.cpp @@ -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; diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTag.h b/Src/Plugins/Input/in_wmvdrm/MetaTag.h new file mode 100644 index 00000000..8c674663 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/MetaTag.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp new file mode 100644 index 00000000..727727b9 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp @@ -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; diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h new file mode 100644 index 00000000..0387401d --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/OutputStream.h b/Src/Plugins/Input/in_wmvdrm/OutputStream.h new file mode 100644 index 00000000..bf0fe3db --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/OutputStream.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp new file mode 100644 index 00000000..7fee0a5e --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h new file mode 100644 index 00000000..98eae155 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/RawReader.cpp b/Src/Plugins/Input/in_wmvdrm/RawReader.cpp new file mode 100644 index 00000000..1189b0ab --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/RawReader.cpp @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/RawReader.h b/Src/Plugins/Input/in_wmvdrm/RawReader.h new file mode 100644 index 00000000..f7d2e80b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/RawReader.h @@ -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; +}; diff --git a/Src/Plugins/Input/in_wmvdrm/Remaining.h b/Src/Plugins/Input/in_wmvdrm/Remaining.h new file mode 100644 index 00000000..acfbbffe --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/Remaining.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp b/Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp new file mode 100644 index 00000000..2b7eabdd --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp @@ -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(); +} diff --git a/Src/Plugins/Input/in_wmvdrm/SeekLayer.h b/Src/Plugins/Input/in_wmvdrm/SeekLayer.h new file mode 100644 index 00000000..7b896845 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/SeekLayer.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/StatusHook.cpp b/Src/Plugins/Input/in_wmvdrm/StatusHook.cpp new file mode 100644 index 00000000..45cf618a --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/StatusHook.cpp @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/StatusHook.h b/Src/Plugins/Input/in_wmvdrm/StatusHook.h new file mode 100644 index 00000000..400c134a --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/StatusHook.h @@ -0,0 +1,7 @@ +#ifndef NULLSOFT_STATUSHOOKH +#define NULLSOFT_STATUSHOOKH + +#include <windows.h> +void Hook(HWND winamp); +void Unhook(HWND winamp); +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/TODO.txt b/Src/Plugins/Input/in_wmvdrm/TODO.txt new file mode 100644 index 00000000..3c5fa698 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/TODO.txt @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/TagAlias.cpp b/Src/Plugins/Input/in_wmvdrm/TagAlias.cpp new file mode 100644 index 00000000..5f5e8e59 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/TagAlias.cpp @@ -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; +} diff --git a/Src/Plugins/Input/in_wmvdrm/TagAlias.h b/Src/Plugins/Input/in_wmvdrm/TagAlias.h new file mode 100644 index 00000000..56a25be1 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/TagAlias.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp new file mode 100644 index 00000000..35130024 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp @@ -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; + } +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h new file mode 100644 index 00000000..d1530cb0 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp b/Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp new file mode 100644 index 00000000..cee4297b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp @@ -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(); +} diff --git a/Src/Plugins/Input/in_wmvdrm/VideoLayer.h b/Src/Plugins/Input/in_wmvdrm/VideoLayer.h new file mode 100644 index 00000000..1f95ccdc --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoLayer.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp new file mode 100644 index 00000000..511fb80a --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp @@ -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); + } +} + diff --git a/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.h b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.h new file mode 100644 index 00000000..e5f72d50 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/VideoThread.cpp b/Src/Plugins/Input/in_wmvdrm/VideoThread.cpp new file mode 100644 index 00000000..fd6f1f09 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoThread.cpp @@ -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)); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/VideoThread.h b/Src/Plugins/Input/in_wmvdrm/VideoThread.h new file mode 100644 index 00000000..2127710f --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/VideoThread.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WMCallback.cpp b/Src/Plugins/Input/in_wmvdrm/WMCallback.cpp new file mode 100644 index 00000000..cc92ce70 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMCallback.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WMCallback.h b/Src/Plugins/Input/in_wmvdrm/WMCallback.h new file mode 100644 index 00000000..4ae68c42 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMCallback.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp new file mode 100644 index 00000000..92a6db3c --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp @@ -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(); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WMDRMModule.h b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.h new file mode 100644 index 00000000..52812930 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WMHandler.cpp b/Src/Plugins/Input/in_wmvdrm/WMHandler.cpp new file mode 100644 index 00000000..090e1cd7 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMHandler.cpp @@ -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; + } + diff --git a/Src/Plugins/Input/in_wmvdrm/WMHandler.h b/Src/Plugins/Input/in_wmvdrm/WMHandler.h new file mode 100644 index 00000000..b2ac60f9 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMHandler.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/WMInformation.cpp b/Src/Plugins/Input/in_wmvdrm/WMInformation.cpp new file mode 100644 index 00000000..a9c5701b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMInformation.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WMInformation.h b/Src/Plugins/Input/in_wmvdrm/WMInformation.h new file mode 100644 index 00000000..a9e619b6 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMInformation.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp new file mode 100644 index 00000000..7f1d6a20 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WMPlaylist.h b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.h new file mode 100644 index 00000000..8147e5d0 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp b/Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp new file mode 100644 index 00000000..6978cae8 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp @@ -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; diff --git a/Src/Plugins/Input/in_wmvdrm/WPLLoader.h b/Src/Plugins/Input/in_wmvdrm/WPLLoader.h new file mode 100644 index 00000000..650cc58b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WPLLoader.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp b/Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp new file mode 100644 index 00000000..39192a8e --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp @@ -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); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/WaitLayer.h b/Src/Plugins/Input/in_wmvdrm/WaitLayer.h new file mode 100644 index 00000000..5b4c7add --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WaitLayer.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp b/Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp new file mode 100644 index 00000000..1dfc8ecf --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp @@ -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)); +} diff --git a/Src/Plugins/Input/in_wmvdrm/WinampInterface.h b/Src/Plugins/Input/in_wmvdrm/WinampInterface.h new file mode 100644 index 00000000..227b834c --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/WinampInterface.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/XMLString.cpp b/Src/Plugins/Input/in_wmvdrm/XMLString.cpp new file mode 100644 index 00000000..bc36a151 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/XMLString.cpp @@ -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; diff --git a/Src/Plugins/Input/in_wmvdrm/XMLString.h b/Src/Plugins/Input/in_wmvdrm/XMLString.h new file mode 100644 index 00000000..b80380eb --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/XMLString.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/api.cpp b/Src/Plugins/Input/in_wmvdrm/api.cpp new file mode 100644 index 00000000..05749577 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/api.cpp @@ -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); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/api.h b/Src/Plugins/Input/in_wmvdrm/api.h new file mode 100644 index 00000000..4e226ea1 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/api.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/config.cpp b/Src/Plugins/Input/in_wmvdrm/config.cpp new file mode 100644 index 00000000..f0f93737 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/config.cpp @@ -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); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/config.h b/Src/Plugins/Input/in_wmvdrm/config.h new file mode 100644 index 00000000..09920677 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/config.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/directdraw.cpp b/Src/Plugins/Input/in_wmvdrm/directdraw.cpp new file mode 100644 index 00000000..a62de00e --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/directdraw.cpp @@ -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 :) +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/directdraw.h b/Src/Plugins/Input/in_wmvdrm/directdraw.h new file mode 100644 index 00000000..caa80038 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/directdraw.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp b/Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp new file mode 100644 index 00000000..ffb825dc --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp @@ -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; + + + + diff --git a/Src/Plugins/Input/in_wmvdrm/factory_Handler.h b/Src/Plugins/Input/in_wmvdrm/factory_Handler.h new file mode 100644 index 00000000..16215543 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/factory_Handler.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/in_wm.rc b/Src/Plugins/Input/in_wmvdrm/in_wm.rc new file mode 100644 index 00000000..1b57130b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/in_wm.rc @@ -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 + diff --git a/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln new file mode 100644 index 00000000..af0ece5c --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj new file mode 100644 index 00000000..e4d8d491 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj @@ -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>
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj.filters b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj.filters new file mode 100644 index 00000000..98ac6298 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj.filters @@ -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>
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/loadini.cpp b/Src/Plugins/Input/in_wmvdrm/loadini.cpp new file mode 100644 index 00000000..482abeb7 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/loadini.cpp @@ -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); + } +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/loadini.h b/Src/Plugins/Input/in_wmvdrm/loadini.h new file mode 100644 index 00000000..4d57eb6b --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/loadini.h @@ -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
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/main.cpp b/Src/Plugins/Input/in_wmvdrm/main.cpp new file mode 100644 index 00000000..af26d4e0 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/main.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/output/AudioOut.h b/Src/Plugins/Input/in_wmvdrm/output/AudioOut.h new file mode 100644 index 00000000..90d36202 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/output/AudioOut.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp new file mode 100644 index 00000000..3bf43ea6 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp @@ -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); +} diff --git a/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h new file mode 100644 index 00000000..10d0ead7 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/res_wav/resource.h b/Src/Plugins/Input/in_wmvdrm/res_wav/resource.h new file mode 100644 index 00000000..479d2ebf --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/res_wav/resource.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/resource.h b/Src/Plugins/Input/in_wmvdrm/resource.h new file mode 100644 index 00000000..87c2538f --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/resource.h @@ -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 diff --git a/Src/Plugins/Input/in_wmvdrm/util.cpp b/Src/Plugins/Input/in_wmvdrm/util.cpp new file mode 100644 index 00000000..a783cea7 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/util.cpp @@ -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); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/util.h b/Src/Plugins/Input/in_wmvdrm/util.h new file mode 100644 index 00000000..8c8890da --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/util.h @@ -0,0 +1,20 @@ +#ifndef NULLSOFT_UTILH +#define NULLSOFT_UTILH + +#include <windows.h> + +void WaitForEvent(HANDLE hEvent, DWORD msMaxWaitTime); +char *HRErrorCode(HRESULT hr); + +void GuidString(GUID guid, wchar_t *target, size_t len); + +GUID StringGUID(const wchar_t *source); + +void BinaryString(unsigned char *binary, size_t size, wchar_t *final, size_t len); + +const wchar_t *UserTextDescription(unsigned char *binary, size_t size); +const wchar_t *UserTextString(unsigned char *binary, size_t size); + +int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message); + +#endif diff --git a/Src/Plugins/Input/in_wmvdrm/version.rc2 b/Src/Plugins/Input/in_wmvdrm/version.rc2 new file mode 100644 index 00000000..ade63c0d --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,9,5,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Input Plug-in" + VALUE "FileVersion", "3,9,5,0" + VALUE "InternalName", "Nullsoft Windows Media Decoder" + VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "in_wm.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp new file mode 100644 index 00000000..7aed89de --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp @@ -0,0 +1,771 @@ +#include "main.h" +#include "vid_ddraw.h" +#include "directdraw.h" + + void *frame; + int width, height, flip; + int needchange; + unsigned int type; + LPDIRECTDRAW lpDD; + LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary; + DDCAPS capsDrv; + unsigned int uDestSizeAlign, uSrcSizeAlign; + DWORD dwUpdateFlags; + RECT rs, rd; + RECT lastresizerect; + bool initing; + + LPDIRECTDRAWCLIPPER lpddsClipper; + DDPIXELFORMAT m_ddpf; + int m_depth; + RGBQUAD *m_palette; + + RECT winRect; + RECT m_monRect; + +void getViewport(RECT *r, HWND wnd, int full, RECT *sr); +#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x)) +DDrawVideoOutput::DDrawVideoOutput() : frame(0), width(0), height(0), + flip(0), type(0), uDestSizeAlign(0), + uSrcSizeAlign(0), dwUpdateFlags(0), + m_depth(0) +{ + lpDD = NULL; + lpddsOverlay = NULL; + lastresizerect.bottom = 0; + lastresizerect.top = 0; + lastresizerect.left = 0; + lastresizerect.right = 0; + + lpddsPrimary = NULL; + lpddsClipper = NULL; + + initing = false; + needchange = 0; + m_palette = NULL; + memset(&winRect, 0, sizeof(winRect)); +} + +void DDrawVideoOutput::close() +{ + // LPDIRECTDRAWSURFACE o=lpddsOverlay; + lpddsOverlay = NULL; + // if(o) o->Release(); + //if(lpddsPrimary) lpddsPrimary->Release(); + // if(lpddsClipper) lpddsClipper->Release(); + if (lpDD) lpDD->Release(); lpDD = 0;// BU added NULL check in response to talkback +// removeFullScreen(); +} + +DDrawVideoOutput::~DDrawVideoOutput() +{ + DDrawVideoOutput::close(); +} + +void DDrawVideoOutput::drawSubtitle(SubsItem *item) +{ +} + +int DDrawVideoOutput::create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int ptype, int flipit, double aspectratio) +{ + this->parent = parent; + type = ptype; + width = w; + height = h; + flip = flipit; + adjuster = _adjuster; + + initing = true; + HWND hwnd = this->parent; + + if (lpDD) lpDD->Release(); + lpDD = NULL; + + update_monitor_coords(); + + if (!foundGUID) DDrawCreate(NULL, &lpDD, NULL); + else DDrawCreate(&m_devguid, &lpDD, NULL); + + + if (!lpDD) + { + initing = false; + return 0; + } + + lpDD->SetCooperativeLevel(hwnd, DDSCL_NOWINDOWCHANGES | DDSCL_NORMAL); + + DDSURFACEDESC ddsd; + INIT_DIRECTDRAW_STRUCT(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + HRESULT ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL ); + + if (FAILED(ddrval) || !lpddsPrimary) + { + initing=false; + return 0; + } + + HRESULT v = -1; + DDSURFACEDESC DDsd = {sizeof(DDsd), }; + lpddsPrimary->GetSurfaceDesc(&ddsd); + DDsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; //create the surface at screen depth + DDsd.dwWidth = w; + DDsd.dwHeight = h; + DDsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; + if (config_video.ddraw()) v = lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL); + if (!config_video.ddraw() || FAILED(v)) + { + // fall back to system memory if video mem doesn't work + DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; + v = lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL); + } + if (FAILED(v)) + { + // this video card sucks then :) + lpddsOverlay = NULL; + initing = false; + return 0; + } + + // get the depth + m_depth = 8; + INIT_DIRECTDRAW_STRUCT(m_ddpf); + if (lpddsOverlay->GetPixelFormat(&m_ddpf) >= 0) + { + m_depth = m_ddpf.dwRGBBitCount; + if (m_depth == 16 && m_ddpf.dwGBitMask == 0x03e0) m_depth = 15; + } + + if (lpDD->CreateClipper(0, &lpddsClipper, NULL) != DD_OK) + { + lpddsOverlay = NULL; + initing = false; + return 0; + } + + lpddsClipper->SetHWnd(0, hwnd); + lpddsPrimary->SetClipper(lpddsClipper); + initing = false; + + //get current monitor + getViewport(&m_monRect, hwnd, 1, NULL); + + return 1; +} + +int DDrawVideoOutput::onPaint(HWND hwnd) +{ + return 0; +} + +void DDrawVideoOutput::displayFrame(const char * /*buf*/, int size, int time) +{ + DDSURFACEDESC dd = {sizeof(dd), }; + if (config_video.vsync()) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); + if (lpddsOverlay->Lock(NULL, &dd, DDLOCK_WAIT, NULL) != DD_OK) + { + needchange = 1; + return ; + } + if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2')) + { + const YV12_PLANES *planes = (YV12_PLANES *)frame; + // convert yv12 to rgb + int bytes = m_depth >> 3; + if (m_depth == 15) bytes = 2; + int i, j, y00, y01, y10, y11, u, v; + unsigned char *pY = (unsigned char *)planes->y.baseAddr; + unsigned char *pU = (unsigned char *)planes->u.baseAddr; + unsigned char *pV = (unsigned char *)planes->v.baseAddr; + unsigned char *pOut = (unsigned char*)dd.lpSurface; + const int rvScale = (int) (2.017 * 65536.0); //91881; + const int gvScale = - (int) (0.392 * 65536.0); // -22553; + const int guScale = - (int) (0.813 * 65536.0); // -46801; + const int buScale = (int) (1.596 * 65536.0); //116129; + const int yScale = (int)( 1.164 * 65536.0 ); //(1.164*65536.0); + int addOut = dd.lPitch * 2 - width * bytes; + int yrb = planes->y.rowBytes; + int addL = dd.lPitch; + + /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */ +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) + + if (flip) + { + pOut += (dd.lPitch) * (height - 1); + addOut = -dd.lPitch * 2 - width * bytes; + addL = -addL; + } + + for (j = 0; j <= height - 2; j += 2) + { + for (i = 0; i <= width - 2; i += 2) + { + y00 = *pY - 16; + y01 = *(pY + 1) - 16; + y10 = *(pY + yrb) - 16; + y11 = *(pY + yrb + 1) - 16; + u = (*pU++) - 128; + v = (*pV++) - 128; + + { + int r, g, b; + + g = guScale * v + gvScale * u; + r = buScale * v; + b = rvScale * u; + + y00 *= yScale; y01 *= yScale; + y10 *= yScale; y11 *= yScale; + + switch (m_depth) + { + case 15: + { + unsigned short *rgb = (unsigned short *)pOut; + rgb[0] = ((LIMIT(r + y00) >> 3) << 10) | ((LIMIT(g + y00) >> 3) << 5) | (LIMIT(b + y00) >> 3); + rgb[1] = ((LIMIT(r + y01) >> 3) << 10) | ((LIMIT(g + y01) >> 3) << 5) | (LIMIT(b + y01) >> 3); + rgb += addL / 2; + rgb[0] = ((LIMIT(r + y10) >> 3) << 10) | ((LIMIT(g + y10) >> 3) << 5) | (LIMIT(b + y10) >> 3); + rgb[1] = ((LIMIT(r + y11) >> 3) << 10) | ((LIMIT(g + y11) >> 3) << 5) | (LIMIT(b + y11) >> 3); + } + break; + case 16: + { + unsigned short *rgb = (unsigned short *)pOut; + rgb[0] = ((LIMIT(r + y00) >> 3) << 11) | ((LIMIT(g + y00) >> 2) << 5) | (LIMIT(b + y00) >> 3); + rgb[1] = ((LIMIT(r + y01) >> 3) << 11) | ((LIMIT(g + y01) >> 2) << 5) | (LIMIT(b + y01) >> 3); + rgb += addL / 2; + rgb[0] = ((LIMIT(r + y10) >> 3) << 11) | ((LIMIT(g + y10) >> 2) << 5) | (LIMIT(b + y10) >> 3); + rgb[1] = ((LIMIT(r + y11) >> 3) << 11) | ((LIMIT(g + y11) >> 2) << 5) | (LIMIT(b + y11) >> 3); + } + break; + case 24: + { + unsigned char *rgb = pOut; + /* Write out top two pixels */ + rgb[0] = LIMIT(b + y00); rgb[1] = LIMIT(g + y00); rgb[2] = LIMIT(r + y00); + rgb[3] = LIMIT(b + y01); rgb[4] = LIMIT(g + y01); rgb[5] = LIMIT(r + y01); + + /* Skip down to next line to write out bottom two pixels */ + rgb += addL; + rgb[0] = LIMIT(b + y10); rgb[1] = LIMIT(g + y10); rgb[2] = LIMIT(r + y10); + rgb[3] = LIMIT(b + y11); rgb[4] = LIMIT(g + y11); rgb[5] = LIMIT(r + y11); + } + break; + case 32: + { + unsigned char *rgb = pOut; + /* Write out top two pixels */ + rgb[0] = LIMIT(b + y00); rgb[1] = LIMIT(g + y00); rgb[2] = LIMIT(r + y00); + rgb[4] = LIMIT(b + y01); rgb[5] = LIMIT(g + y01); rgb[6] = LIMIT(r + y01); + + /* Skip down to next line to write out bottom two pixels */ + rgb += addL; + rgb[0] = LIMIT(b + y10); rgb[1] = LIMIT(g + y10); rgb[2] = LIMIT(r + y10); + rgb[4] = LIMIT(b + y11); rgb[5] = LIMIT(g + y11); rgb[6] = LIMIT(r + y11); + } + break; + } + } + + pY += 2; + pOut += 2 * bytes; + } + pY += yrb + yrb - width; + pU += planes->u.rowBytes - width / 2; + pV += planes->v.rowBytes - width / 2; + pOut += addOut; + } + } + else if (type == VIDEO_MAKETYPE('R', 'G', '3', '2')) + { + //FUCKO: do we need to support 8bits depth? + switch (m_depth) + { + case 15: + { // convert RGB32 -> RGB16 (555) + const char *a = (const char *)frame; + char *b = (char *)dd.lpSurface; + int l = width * 4, l2 = dd.lPitch; + int ladj = l; + if (flip) { a += l * (height - 1); ladj = -ladj; } + for (int i = 0;i < height;i++) + { + short *dest = (short *)b; + int *src = (int *)a; + for (int j = 0;j < width;j++) + { + int c = *(src++); + int r = c >> 16; + int g = (c >> 8) & 0xff; + int b = (c) & 0xff; + *(dest++) = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); + } + a += ladj; b += l2; + } + } + break; + case 16: + { // convert RGB32 -> RGB16 + //FUCKO: this assumes 565 + const char *a = (const char *)frame; + char *b = (char *)dd.lpSurface; + int l = width * 4, l2 = dd.lPitch; + int ladj = l; + if (flip) { a += l * (height - 1); ladj = -ladj; } + for (int i = 0;i < height;i++) + { + short *dest = (short *)b; + int *src = (int *)a; + for (int j = 0;j < width;j++) + { + //FUCKO: optimize here + int c = *(src++); + int r = c >> 16; + int g = (c >> 8) & 0xff; + int b = (c) & 0xff; + *(dest++) = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + } + a += ladj; b += l2; + } + } + break; + case 24: + { // convert RGB32 -> RGB24 + const char *a = (const char *)frame; + char *b = (char *)dd.lpSurface; + int l = width * 4, l2 = dd.lPitch; + int ladj = l; + if (flip) { a += l * (height - 1); ladj = -ladj; } + for (int i = 0;i < height;i++) + { + char *dest = (char *)b; + int *src = (int *)a; + for (int j = 0;j < width;j++) + { + //FUCKO: optimize here + int c = *(src++); + int r = c >> 16; + int g = (c >> 8) & 0xff; + int b = (c) & 0xff; + *dest++ = b; + *dest++ = g; + *dest++ = r; + } + a += ladj; b += l2; + } + } + break; + case 32: + { // straight RGB32 copy + const char *a = (const char *)frame; + char *b = (char *)dd.lpSurface; + int l = width * 4, l2 = dd.lPitch; + int ladj = l; + if (flip) { a += l * (height - 1); ladj = -ladj; } + for (int i = 0;i < height;i++) + { + memcpy(b, a, l); + a += ladj; b += l2; + } + } + break; + } + } + else if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2') || type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y')) + { + //const char *a = (const char *)frame; + //char *b = (char *)dd.lpSurface; + int /*l = width * 2, */l2 = dd.lPitch; + if (flip) + { + //b += (height - 1) * l2; + l2 = -l2; + } + switch (m_depth) + { + case 15: + { + // yuy2->rgb16 (555) conversion + unsigned char *src = (unsigned char *)frame; + unsigned short *dst = (unsigned short *)dd.lpSurface; + int line, col; //, linewidth; + int y, yy; + int u, v; + int vr, ug, vg, ub; + unsigned char *py, *pu, *pv; + + //linewidth = width - (width >> 1); + py = src; + pu = src + 1; + pv = src + 3; + + int pitchadd = dd.lPitch / 2 - width; + + for (line = 0; line < height; line++) + { + for (col = 0; col < width; col++) + { +#undef LIMIT + #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) ) + + y = *py; + yy = y << 8; + u = *pu - 128; + ug = 88 * u; + ub = 454 * u; + v = *pv - 128; + vg = 183 * v; + vr = 359 * v; + + unsigned char b = LIMIT(yy + ub ); + unsigned char g = LIMIT(yy - ug - vg); + unsigned char r = LIMIT(yy + vr); + *(dst++) = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); + + py += 2; + if ( (col & 1) == 1) + { + pu += 4; // skip yvy every second y + pv += 4; // skip yuy every second y + } + } // ..for col + dst += pitchadd; + } /* ..for line */ + } + break; + case 16: + { + // yuy2->rgb16 conversion + //FUCKO: only supports 565 + unsigned char *src = (unsigned char *)frame; + unsigned short *dst = (unsigned short *)dd.lpSurface; + int line, col; //, linewidth; + int y, yy; + int u, v; + int vr, ug, vg, ub; + unsigned char *py, *pu, *pv; + + //linewidth = width - (width >> 1); + py = src; + pu = src + 1; + pv = src + 3; + + int pitchadd = dd.lPitch / 2 - width; + + for (line = 0; line < height; line++) + { + for (col = 0; col < width; col++) + { +#undef LIMIT + #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) ) + + y = *py; + yy = y << 8; + u = *pu - 128; + ug = 88 * u; + ub = 454 * u; + v = *pv - 128; + vg = 183 * v; + vr = 359 * v; + + unsigned char b = LIMIT(yy + ub ); + unsigned char g = LIMIT(yy - ug - vg); + unsigned char r = LIMIT(yy + vr); + *(dst++) = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + + py += 2; + if ( (col & 1) ) + { + pu += 4; // skip yvy every second y + pv += 4; // skip yuy every second y + } + } // ..for col + dst += pitchadd; + } /* ..for line */ + } + break; + case 24: + { + // yuy2->rgb24 conversion + unsigned char *src = (unsigned char *)frame; + unsigned char *dst = (unsigned char *)dd.lpSurface; + int line, col; //, linewidth; + int y, yy; + int u, v; + int vr, ug, vg, ub; + unsigned char *py, *pu, *pv; + + //linewidth = width - (width >> 1); + py = src; + pu = src + 1; + pv = src + 3; + + int pitchadd = dd.lPitch - (width * 3); + + for (line = 0; line < height; line++) + { + for (col = 0; col < width; col++) + { +#undef LIMIT + #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) ) + + y = *py; + yy = y << 8; + u = *pu - 128; + ug = 88 * u; + ub = 454 * u; + v = *pv - 128; + vg = 183 * v; + vr = 359 * v; + + *(dst++) = LIMIT(yy + ub ); + *(dst++) = LIMIT(yy - ug - vg); + *(dst++) = LIMIT(yy + vr); + + py += 2; + if ( (col & 1) == 1) + { + pu += 4; // skip yvy every second y + pv += 4; // skip yuy every second y + } + } // ..for col + dst += pitchadd; + } /* ..for line */ + } + break; + case 32: + { + // yuy2->rgb32 conversion + unsigned char *src = (unsigned char *)frame; + unsigned char *dst = (unsigned char *)dd.lpSurface; + int line, col; //, linewidth; + int y, yy; + int u, v; + int vr, ug, vg, ub; + unsigned char *py, *pu, *pv; + + //linewidth = width - (width >> 1); + py = src; + pu = src + 1; + pv = src + 3; + + int pitchadd = dd.lPitch - (width * 4); + + for (line = 0; line < height; line++) + { + for (col = 0; col < width; col++) + { +#undef LIMIT + #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) ) + + y = *py; + yy = y << 8; + u = *pu - 128; + ug = 88 * u; + ub = 454 * u; + v = *pv - 128; + vg = 183 * v; + vr = 359 * v; + + *dst++ = LIMIT(yy + ub ); // b + *dst++ = LIMIT(yy - ug - vg); // g + *dst++ = LIMIT(yy + vr); // r + dst++; + + py += 2; + if ( (col & 1) == 1) + { + pu += 4; // skip yvy every second y + pv += 4; // skip yuy every second y + } + } // ..for col + dst += pitchadd; + } /* ..for line */ + } + break; + } + } + else if (type == VIDEO_MAKETYPE('R', 'G', '2', '4')) + { + //FUCKO: only ->RGB32 conversion supported + switch (m_depth) + { + case 32: + { + const char *a = (const char *)frame; + char *b = (char *)dd.lpSurface; + int l = width, l2 = dd.lPitch; + int ladj = l * 3; + if (!flip) ladj = 0; + if (flip) { a += (l * 3) * (height - 1); ladj = -(ladj + l * 3); } + l2 -= l * 4; + for (int i = 0;i < height;i++) + { + //memcpy(b,a,l); + for (int j = 0;j < l;j++) + { + b[0] = a[0]; + b[1] = a[1]; + b[2] = a[2]; + b += 4; a += 3; + } + a += ladj; b += l2; + } + } + break; + } + } + else if (type == VIDEO_MAKETYPE('R', 'G', 'B', '8') && m_palette) + { + unsigned char *d = (unsigned char *)dd.lpSurface; + int pitch = dd.lPitch; + unsigned char *src = (unsigned char *)frame; + int newwidth = (width + 3) & 0xfffc; + src += newwidth * height - 1; + for (int j = 0;j < height;j++) + { + switch (m_depth) + { + case 15: + case 16: + { + unsigned short *dest = (unsigned short *)d; + for (int i = 0;i < newwidth;i++) + { + unsigned char c = src[ -newwidth + 1 + i]; + RGBQUAD *rgb = &m_palette[c]; + switch (m_depth) + { + case 15: *(dest++) = ((rgb->rgbRed >> 3) << 10) | ((rgb->rgbGreen >> 3) << 5) | (rgb->rgbBlue >> 3); break; + case 16: *(dest++) = ((rgb->rgbRed >> 3) << 11) | ((rgb->rgbGreen >> 2) << 5) | (rgb->rgbBlue >> 3); break; + } + } + } + break; + case 24: + case 32: + { + unsigned char *dest = d; + for (int i = 0;i < newwidth;i++) + { + unsigned char c = src[ -newwidth + 1 + i]; + RGBQUAD *rgb = &m_palette[c]; + *dest++ = rgb->rgbBlue; + *dest++ = rgb->rgbGreen; + *dest++ = rgb->rgbRed; + if (m_depth == 32) dest++; + } + } + break; + } + d += pitch; + src -= newwidth; + } + } + + lpddsOverlay->Unlock(&dd); + + + RECT r; + HWND hwnd = this->parent; + if (!IsWindow(hwnd)) return ; + + // if(GetParent(hwnd)) hwnd=GetParent(hwnd); + + GetClientRect(hwnd, &r); + RECT fullr = r; + adjuster->adjustAspect(r); + if (r.left != lastresizerect.left || r.right != lastresizerect.right || r.top != lastresizerect.top || + r.bottom != lastresizerect.bottom) + { + if (r.left != 0) + { + RECT tmp = {0, 0, r.left, fullr.bottom}; + InvalidateRect(hwnd, &tmp, TRUE); + } + + if (r.right != fullr.right) + { + RECT tmp = {r.right, 0, fullr.right, fullr.bottom}; + InvalidateRect(hwnd, &tmp, TRUE); + } + if (r.top != 0) + { + RECT tmp = {r.left, 0, r.right, r.top}; + InvalidateRect(hwnd, &tmp, TRUE); + } + if (r.bottom != fullr.bottom) + { + RECT tmp = {r.left, r.bottom, r.right, fullr.bottom}; + InvalidateRect(hwnd, &tmp, TRUE); + } + + lastresizerect = r; + } + + ClientToScreen(hwnd, (LPPOINT)&r); + ClientToScreen(hwnd, ((LPPOINT)&r) + 1); + + // transform coords from windows desktop coords (where 0,0==upper-left corner of box encompassing all monitors) + // to the coords for the monitor we're displaying on: + r.left -= m_mon_x; + r.right -= m_mon_x; + r.top -= m_mon_y; + r.bottom -= m_mon_y; + + HDC hdc = NULL; + HDC inhdc = NULL; + + RECT *pSrcRect = NULL; + + { + if (!config_video.ddraw() || lpddsPrimary->Blt(&r, lpddsOverlay, pSrcRect, DDBLT_WAIT, 0) != DD_OK) + { + // as a last resort, BitBlt(). + if (lpddsOverlay->GetDC(&inhdc) != DD_OK) + { + needchange = 1; + return ; + } + if (lpddsPrimary->GetDC(&hdc) != DD_OK) + { + lpddsOverlay->ReleaseDC(inhdc); inhdc = NULL; + needchange = 1; + return ; + } + + int src_w = width; + int src_h = pSrcRect ? (pSrcRect->bottom - pSrcRect->top) : height; + if (r.right - r.left == src_w && r.bottom - r.top == src_h) + BitBlt(hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, inhdc, 0, 0, SRCCOPY); + else + StretchBlt(hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, inhdc, 0, 0, src_w, src_h, SRCCOPY); + } + } + + if (hdc) { lpddsPrimary->ReleaseDC(hdc); hdc = NULL; } + if (inhdc) { lpddsOverlay->ReleaseDC(inhdc); inhdc = NULL; } +} + +void DDrawVideoOutput::timerCallback() +{ + //check if the video has been dragged to another monitor + RECT curRect; + + getViewport(&curRect, parent, 1, NULL); + if (memcmp(&curRect, &m_monRect, sizeof(RECT))) + needchange = 1; +} + +void DDrawVideoOutput::resetSubtitle() +{ +} + +void DDrawVideoOutput::Refresh() +{ + // do nothing, we'll refresh on the next frame +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/vid_ddraw.h b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.h new file mode 100644 index 00000000..184d5817 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.h @@ -0,0 +1,50 @@ +#ifndef NULLSOFT_IN_WMVDRM_VID_DDRAW_H +#define NULLSOFT_IN_WMVDRM_VID_DDRAW_H + +#include <ddraw.h> +#include "VideoOutputChildDDraw.h" + +class SubsItem; + +class DDrawVideoOutput : public VideoOutputChildDDraw +{ +public: + DDrawVideoOutput(); + virtual ~DDrawVideoOutput(); + int create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int ptype, int flipit, double aspectratio); + int needChange() { return needchange; } + int onPaint(HWND hwnd); + void displayFrame(const char *buf, int size, int time); + void timerCallback(); + void setPalette(RGBQUAD *pal) { m_palette = pal; } + void drawSubtitle(SubsItem *item); + void resetSubtitle(); + void setVFlip(int on) { flip = on; } + void Refresh(); + void close(); + void SetFrame(void *_frame) { frame = _frame; } +private: + void *frame; + int width, height, flip; + int needchange; + unsigned int type; + LPDIRECTDRAW lpDD; + LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary; + DDCAPS capsDrv; + unsigned int uDestSizeAlign, uSrcSizeAlign; + DWORD dwUpdateFlags; + RECT rs, rd; + RECT lastresizerect; + bool initing; + + LPDIRECTDRAWCLIPPER lpddsClipper; + DDPIXELFORMAT m_ddpf; + int m_depth; + RGBQUAD *m_palette; + + RECT winRect; + RECT m_monRect; +}; + +extern DDrawVideoOutput ddraw; +#endif diff --git a/Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp b/Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp new file mode 100644 index 00000000..dd686489 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp @@ -0,0 +1,708 @@ +#include "main.h" +#include <windows.h> +#include <multimon.h> +#include "vid_overlay.h" +#include "directdraw.h" +#include <api.h> +#include "../Winamp/wa_ipc.h" +#include <assert.h> +void getViewport(RECT *r, HWND wnd, int full, RECT *sr); + + +void YV12_to_YUY2(unsigned char *output, const YV12_PLANES *planes, int pitch, int width, int height, int flip) +{ + const unsigned char *yi = planes->y.baseAddr; + const unsigned char *ui = planes->u.baseAddr; + const unsigned char *vi = planes->v.baseAddr; + if (flip) + output += pitch * (height - 1); + while (height > 0) + { + int x = width; + unsigned char *oo = output; + + while (x > 0) + { + output[0] = *yi++; output[1] = *ui++; output[2] = *yi++; output[3] = *vi++; + output += 4; x -= 2; + } + ui -= width / 2; + vi -= width / 2; + yi += planes->y.rowBytes - width; + x = width; + if (flip) output = oo - pitch; + else output += pitch - width * 2; + oo = output; + while (x > 0) + { + output[0] = *yi++; output[1] = *ui++; output[2] = *yi++; output[3] = *vi++; + output += 4; x -= 2; + } + if (flip) output = oo - pitch; + else output += pitch - width * 2; + ui += planes->u.rowBytes - (width / 2); + vi += planes->v.rowBytes - (width / 2); + yi += planes->y.rowBytes - width; + height -= 2; + } +} + +void YV12_to_UYVY(unsigned char *output, const YV12_PLANES *planes, int pitch, int width, int height, int flip) +{ + const unsigned char *yi = planes->y.baseAddr; + const unsigned char *ui = planes->u.baseAddr; + const unsigned char *vi = planes->v.baseAddr; + + if (flip) output += pitch * (height - 1); + while (height > 0) + { + int x = width; + unsigned char *oo = output; + + while (x > 0) + { + output[0] = *ui++; output[1] = *yi++; output[2] = *vi++; output[3] = *yi++; + output += 4; x -= 2; + } + + ui -= width / 2; + vi -= width / 2; + yi += planes->y.rowBytes - width; + x = width; + if (flip) output = oo - pitch; + else output += pitch - width * 2; + oo = output; + while (x > 0) + { + output[0] = *ui++; output[1] = *yi++; output[2] = *vi++; output[3] = *yi++; + output += 4; x -= 2; + } + + if (flip) output = oo - pitch; + else output += pitch - width * 2; + ui += planes->u.rowBytes - (width / 2); + vi += planes->v.rowBytes - (width / 2); + yi += planes->y.rowBytes - width; + height -= 2; + } +} + +void YV12_to_YV12(unsigned char *output, const YV12_PLANES *planes, int pitch, int width, int height, int flip) +{ // woo native YV12 copy + int f = !!flip; + unsigned char *o = output + (f * height * pitch); + const char *i = (const char*)planes->y.baseAddr; + + + int d_o = pitch; + if (f) d_o = -d_o; + else o -= d_o; + + int h2 = height; + while (h2--) + { + o += d_o; memcpy(o, i, width); i += planes->y.rowBytes; + } + + d_o /= 2; + + int w2 = width / 2; + h2 = height / 2; + i = (const char*)planes->v.baseAddr; + o = output + (height * pitch * (f + 4)) / 4; + + if (!f) o -= d_o; + while (h2--) + { + o += d_o; memcpy(o, i, w2); i += planes->v.rowBytes; + } + o = output + (height * pitch * (f + 5)) / 4; + i = (const char*)planes->u.baseAddr; + h2 = height / 2; + + if (!f) o -= d_o; + while (h2--) + { + o += d_o; memcpy(o, i, w2);i += planes->u.rowBytes; + } +} + + +void YUY2_to_YUY2(unsigned char *output, const char *buf, int pitch, int width, int height, int flip) +{ + const char *a = buf; + unsigned char *b = output; + int l = width * 2, l2 = pitch; + if (flip) + { + b += (height - 1) * l2; + l2 = -l2; + } + + + //wee straight YUY2 copy + for (int i = 0;i < height;i++) + { + memcpy(b, a, l); + b += l2; + a += l; + } + +} + +void YUY2_to_UYVY(unsigned char *output, const char *buf, int pitch, int width, int height, int flip) +{ + const char *a = buf; + unsigned char *b = output; + int l = width * 2, l2 = pitch; + if (flip) + { + b += (height - 1) * l2; + l2 = -l2; + } + + for (int i = 0;i < height;i++) + { + int x = width / 2; + while (x-- > 0) + { + b[0] = a[1]; + b[1] = a[0]; + b[2] = a[3]; + b[3] = a[2]; + a += 4; + b += 4; + } + memcpy(b, a, l); + b += l2; + a += l; + } +} + +#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x)) +// I like to set these to 255,0,255 to test that we arent drawing this color too many playces +#define OV_COL_R 16 +#define OV_COL_G 0 +#define OV_COL_B 16 + +OverlayVideoOutput::OverlayVideoOutput() : frame(NULL), width(0), height(0), + flip(0), type(0), uDestSizeAlign(0), uSrcSizeAlign(0), dwUpdateFlags(0) +{ + lpDD = NULL; + m_closed = 0; + overlay_color = RGB(OV_COL_R, OV_COL_G, OV_COL_B); + lpddsOverlay = NULL; + lpddsPrimary = NULL; + lpBackBuffer = NULL; + + yuy2_output = uyvy_output = 0; + initing = false; + needchange = 0; + memset(&m_oldrd, 0, sizeof(m_oldrd)); + memset(&winRect, 0, sizeof(winRect)); + m_fontsize = 0; +} + +OverlayVideoOutput::~OverlayVideoOutput() +{ + OverlayVideoOutput::close(); +} + + +static DWORD DD_ColorMatch(LPDIRECTDRAWSURFACE pdds, COLORREF rgb) +{ + COLORREF rgbT; + HDC hdc; + DWORD dw = CLR_INVALID; + DDSURFACEDESC ddsd; + HRESULT hres; + + // + // use GDI SetPixel to color match for us + // + if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK) + { + rgbT = GetPixel(hdc, 0, 0); // save current pixel value + SetPixel(hdc, 0, 0, rgb); // set our value + pdds->ReleaseDC(hdc); + } + + // now lock the surface so we can read back the converted color + ddsd.dwSize = sizeof(ddsd); + while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) == + DDERR_WASSTILLDRAWING) + ; + + if (hres == DD_OK) + { + dw = *(DWORD *)ddsd.lpSurface; // get DWORD + if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32) + dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; // mask it to bpp + pdds->Unlock(NULL); + } + + + // now put the color that was there back. + if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK) + { + SetPixel(hdc, 0, 0, rgbT); + pdds->ReleaseDC(hdc); + } + + return dw; +} + +int OverlayVideoOutput::create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int ptype, int flipit, double aspectratio) +{ + OverlayVideoOutput::close(); + this->parent = parent; + type = ptype; + width = w; + height = h; + flip = flipit; + + adjuster = _adjuster; + + initing = true; + HWND hwnd = this->parent; + + if (lpDD) lpDD->Release(); + lpDD = NULL; + + update_monitor_coords(); + + if (!foundGUID) DDrawCreate(NULL, &lpDD, NULL); + else DDrawCreate(&m_devguid, &lpDD, NULL); + + + if (!lpDD) + { + initing = false; + return 0; + } + + lpDD->SetCooperativeLevel(hwnd, DDSCL_NOWINDOWCHANGES | DDSCL_NORMAL); + + DDSURFACEDESC ddsd; + INIT_DIRECTDRAW_STRUCT(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL ); + + if (!lpddsPrimary) + { + if (lpDD) lpDD->Release(); + lpDD = NULL; + initing=false; + return 0; + } + + // init overlay + DDSURFACEDESC ddsdOverlay; + INIT_DIRECTDRAW_STRUCT(ddsdOverlay); + //ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; + //ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_PITCH; + //ddsdOverlay.dwBackBufferCount=0; + ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsdOverlay.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_PITCH | DDSD_BACKBUFFERCOUNT; + ddsdOverlay.dwBackBufferCount = 1; + ddsdOverlay.dwWidth = w; + ddsdOverlay.dwHeight = h; + ddsdOverlay.lPitch = w * 4; + DDPIXELFORMAT pf[] = + { + {sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'U', 'Y', '2'), 0, 0, 0, 0, 0}, + {sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('U', 'Y', 'V', 'Y'), 0, 0, 0, 0, 0}, // UYVY + {sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'V', '1', '2'), 0, 0, 0, 0, 0}, + }; + int tab[5] = {0}; + if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2')) + { + tab[0] = 0; // default is YUY2 + tab[1] = 1; + tab[2] = -1; + } + else if (type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y')) + { + tab[0] = 1; // make UYVY default + tab[1] = 0; + tab[2] = -1; + } + else if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2')) + { + if (config_video.yv12()) + { + tab[0] = 2; + tab[1] = 0; + tab[2] = 1; + tab[3] = -1; + } + else + { + //use YUY2 + tab[0] = 0; // default is YUY2 + tab[1] = 1; + tab[2] = -1; + } + } + else + { + tab[0] = -1; // default is RGB + } + + int x = 4096; + HRESULT v = -1; + for (x = 0; x < sizeof(tab) / sizeof(tab[0]) && tab[x] >= 0; x ++) + { + ddsdOverlay.ddpfPixelFormat = pf[tab[x]]; + v = lpDD->CreateSurface(&ddsdOverlay, &lpddsOverlay, NULL); + if (!FAILED(v)) break; + } + if (FAILED(v) || x >= sizeof(tab) / sizeof(tab[0]) || tab[x] < 0) + { + initing = false; + return 0; + } + + yuy2_output = (tab[x] == 0); + uyvy_output = (tab[x] == 1); + + //get the backbuffer surface + DDSCAPS ddscaps; + ddscaps.dwCaps = DDSCAPS_BACKBUFFER; + v = lpddsOverlay->GetAttachedSurface(&ddscaps, &lpBackBuffer); + if (v != DD_OK || lpBackBuffer == 0) + { + // make it use normal vsync + lpBackBuffer=0; + initing = FALSE; + return 0; + } + + INIT_DIRECTDRAW_STRUCT(capsDrv); + lpDD->GetCaps(&capsDrv, NULL); + + uDestSizeAlign = capsDrv.dwAlignSizeDest; + uSrcSizeAlign = capsDrv.dwAlignSizeSrc; + + dwUpdateFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE; + + DEVMODE d; + d.dmSize = sizeof(d); + d.dmDriverExtra = 0; + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &d); + + int rv = OV_COL_R, gv = OV_COL_G, bv = OV_COL_B; + overlay_color = RGB(rv, gv, bv); + + if (d.dmBitsPerPel == 8) + { + overlay_color = RGB(255, 0, 255); + } + + INIT_DIRECTDRAW_STRUCT(ovfx); + ovfx.dwDDFX = 0; + switch (d.dmBitsPerPel) + { + case 8: + ovfx.dckDestColorkey.dwColorSpaceLowValue = 253; + break; + case 16: + ovfx.dckDestColorkey.dwColorSpaceLowValue = ((rv >> 3) << 11) | ((gv >> 2) << 5) | (bv >> 3); + break; + case 15: + ovfx.dckDestColorkey.dwColorSpaceLowValue = ((rv >> 3) << 10) | ((gv >> 3) << 5) | (bv >> 3); + break; + case 24: + case 32: + ovfx.dckDestColorkey.dwColorSpaceLowValue = (rv << 16) | (gv << 8) | bv; + break; + } + + //try to get the correct bit depth thru directdraw (for fucked up 16 bits displays for ie.) + { + DDSURFACEDESC DDsd = {sizeof(DDsd), }; + lpddsPrimary->GetSurfaceDesc(&ddsd); + DDsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; //create the surface at screen depth + DDsd.dwWidth = 8; + DDsd.dwHeight = 8; + DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; + LPDIRECTDRAWSURFACE tempsurf; + if (lpDD->CreateSurface(&DDsd, &tempsurf, NULL) == DD_OK) + { + int res = DD_ColorMatch(tempsurf, overlay_color); + if (res != CLR_INVALID) ovfx.dckDestColorkey.dwColorSpaceLowValue = res; + tempsurf->Release(); + } + } + + ovfx.dckDestColorkey.dwColorSpaceHighValue = ovfx.dckDestColorkey.dwColorSpaceLowValue; + + getRects(&rs, &rd); + if (FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) + { + initing = false; + return 0; + } + initing = false; + + DDSURFACEDESC dd = {sizeof(dd), }; + if (lpddsOverlay->Lock(NULL, &dd, DDLOCK_WAIT, NULL) != DD_OK) return 0; + unsigned char *o = (unsigned char*)dd.lpSurface; + if (uyvy_output || yuy2_output) + { + int x = dd.lPitch * height / 2; + while (x--) + { + if (uyvy_output) + { + *o++ = 128; + *o++ = 0; + } + else + { + *o++ = 0; + *o++ = -128; + } + } + } + else + { + memset(o, 0, dd.lPitch*height); o += dd.lPitch * height; + memset(o, 128, dd.lPitch*height / 2); + } + lpddsOverlay->Unlock(&dd); + + m_closed=0; + needchange = 0; + InvalidateRect(hwnd, NULL, TRUE); + + return 1; +} + +void OverlayVideoOutput::close() +{ + m_closed = 1; + if (lpddsOverlay) lpddsOverlay->UpdateOverlay(NULL, lpddsPrimary, NULL, DDOVER_HIDE , NULL); + if (lpBackBuffer) lpBackBuffer->Release(); lpBackBuffer=0; + if (lpddsOverlay) lpddsOverlay->Release(); lpddsOverlay=0; + if (lpddsPrimary) lpddsPrimary->Release(); lpddsPrimary=0; + if (lpDD) lpDD->Release(); lpDD=0; // BU added NULL check in response to talkback +} + +void OverlayVideoOutput::getRects(RECT *drs, RECT *drd, int fixmultimon) const +{ + //if(GetParent(hwnd)) hwnd=GetParent(hwnd); + + RECT rd, rs; + GetClientRect(parent, &rd); + ClientToScreen(parent, (LPPOINT)&rd); + ClientToScreen(parent, ((LPPOINT)&rd) + 1); + + adjuster->adjustAspect(rd); + + rd.left -= m_mon_x; + rd.right -= m_mon_x; + rd.top -= m_mon_y; + rd.bottom -= m_mon_y; + + memset(&rs, 0, sizeof(rs)); + rs.right = width; + rs.bottom = height; + + if (fixmultimon) + { + //resize overlay for off-screen + RECT rfull; + getViewport(&rfull, parent, 1, NULL); + + rfull.left -= m_mon_x; + rfull.right -= m_mon_x; + rfull.top -= m_mon_y; + rfull.bottom -= m_mon_y; + + if (rd.right > rfull.right) + { + int diff = rd.right - rfull.right; + float sc = (float)(width) / (float)(rd.right - rd.left); + rd.right = rfull.right; + rs.right = width - (int)(diff * sc); + } + if (rd.left < rfull.left) + { + int diff = rfull.left - rd.left; + float sc = (float)(width) / (float)(rd.right - rd.left); + rd.left = rfull.left; + rs.left = (int)(diff * sc); + } + if (rd.bottom > rfull.bottom) + { + int diff = rd.bottom - rfull.bottom; + float sc = (float)(height) / (float)(rd.bottom - rd.top); + rd.bottom = rfull.bottom; + rs.bottom = height - (int)(diff * sc); + } + if (rd.top < rfull.top) + { + int diff = rfull.top - rd.top; + float sc = (float)(height) / (float)(rd.bottom - rd.top); + rd.top = rfull.top; + rs.top = (int)(diff * sc); + } + } + + if (capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC && uDestSizeAlign) + { + rs.left = (int)((rs.left + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign; + rs.right = (int)((rs.right + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign; + } + if (capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST && uDestSizeAlign) + { + rd.left = (int)((rd.left + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign; + rd.right = (int)((rd.right + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign; + } + + *drd = rd; + *drs = rs; +} + +void OverlayVideoOutput::timerCallback() +{ + if (!adjuster) + return; + RECT rd, rs; + getRects(&rs, &rd); + + if (memcmp(&m_oldrd, &rd, sizeof(RECT))) + { + m_oldrd = rd; + if (!initing && lpddsOverlay) + if (FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) + { + needchange = 1; + } + InvalidateRect(parent, NULL, FALSE); + } +} + +int OverlayVideoOutput::onPaint(HWND hwnd) +{ + if (!adjuster) + return 0; + PAINTSTRUCT p; + BeginPaint(hwnd, &p); + + if (!m_closed) + { + RECT r, rs, rfull, clientRect; + RECT drawRect; + getRects(&rs, &r, 0); // we don't just fill the entire client rect, cause that looks gross + + getViewport(&rfull, hwnd, 1, NULL); + + // go from this screen coords to global coords + r.left += rfull.left; + r.top += rfull.top; + r.right += rfull.left; + r.bottom += rfull.top; + + // go from global back to client + ScreenToClient(hwnd, (LPPOINT)&r); + ScreenToClient(hwnd, ((LPPOINT)&r) + 1); + + + HBRUSH br = (HBRUSH) GetStockObject(BLACK_BRUSH); + GetClientRect(hwnd, &clientRect); + + // left black box + drawRect.left = clientRect.left; + drawRect.right = r.left; + drawRect.top = clientRect.top; + drawRect.bottom = clientRect.bottom; + FillRect(p.hdc, &drawRect, br); + + // right black box + drawRect.left = r.right; + drawRect.right = clientRect.right; + drawRect.top = clientRect.top; + drawRect.bottom = clientRect.bottom; + FillRect(p.hdc, &drawRect, br); + + // top black box + drawRect.left = clientRect.left; + drawRect.right = clientRect.right; + drawRect.top = clientRect.top; + drawRect.bottom = r.top; + FillRect(p.hdc, &drawRect, br); + + // bottom black box + drawRect.left = clientRect.left; + drawRect.right = clientRect.right; + drawRect.top = r.bottom; + drawRect.bottom = clientRect.bottom; + FillRect(p.hdc, &drawRect, br); + + LOGBRUSH lb = {BS_SOLID, (COLORREF)overlay_color, }; + br = CreateBrushIndirect(&lb); + + FillRect(p.hdc, &r, br); + DeleteObject(br); + } + + EndPaint(hwnd, &p); + + return 1; +} + +void OverlayVideoOutput::displayFrame(const char * /*buf*/, int size, int time) +{ + if (m_closed) + return; + DDSURFACEDESC dd = {sizeof(dd), }; + //CT> vsync wait not used anymore + //if (config_video.vsync()) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0); + HRESULT result; + //if ((result=lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL)) != DD_OK) { + if ((result = lpBackBuffer->Lock(NULL, &dd, DDLOCK_WAIT, NULL)) != DD_OK) + { + if (result == DDERR_SURFACELOST) needchange = 1; + return ; + } + if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2')) + { + const YV12_PLANES *planes = (YV12_PLANES *)frame; + if (uyvy_output) + YV12_to_UYVY((unsigned char*)dd.lpSurface, planes, dd.lPitch, width, height, flip); + else if (yuy2_output) + YV12_to_YUY2((unsigned char*)dd.lpSurface, planes, dd.lPitch, width, height, flip); + else + YV12_to_YV12((unsigned char*)dd.lpSurface, planes, dd.lPitch, width, height, flip); + } + else if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2')) + { + if (yuy2_output) + YUY2_to_YUY2((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip); + else if (uyvy_output) + YUY2_to_UYVY((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip); + else + YUY2_to_YUY2((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip); // is this right? + } + else if (type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y')) + { + if (yuy2_output) + YUY2_to_UYVY((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip); + else + YUY2_to_YUY2((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip); + } + + lpBackBuffer->Unlock(&dd); + lpddsOverlay->Flip(lpBackBuffer, DDFLIP_WAIT); +} + +void OverlayVideoOutput::drawSubtitle(SubsItem *item) +{ +} + +void OverlayVideoOutput::resetSubtitle() +{ +} diff --git a/Src/Plugins/Input/in_wmvdrm/vid_overlay.h b/Src/Plugins/Input/in_wmvdrm/vid_overlay.h new file mode 100644 index 00000000..d7d811a6 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/vid_overlay.h @@ -0,0 +1,55 @@ +#ifndef NULLSOFT_VIDEO_OVERLAY_H +#define NULLSOFT_VIDEO_OVERLAY_H + +#include <ddraw.h> +#include <multimon.h> +#include "VideoOutputChildDDraw.h" + +class SubsItem; + +class OverlayVideoOutput : public VideoOutputChildDDraw { + +public: + OverlayVideoOutput(); + virtual ~OverlayVideoOutput(); + int create(HWND parent,VideoAspectAdjuster *_adjuster, int w, int h, unsigned int type, int flipit, double aspectratio); //return 1 if ok + int needChange() { return needchange; } + int onPaint(HWND hwnd); + void displayFrame(const char *buf, int size, int time); + void timerCallback(); + void close(); + void drawSubtitle(SubsItem *item); + void resetSubtitle(); + void setVFlip(int on) { flip=on; } + void Refresh() { if (parent) InvalidateRect(parent, NULL, TRUE); } + void SetFrame(void *_frame) { frame = _frame; } +private: + void *frame; + int width, height, flip; + int m_closed; + int needchange; + unsigned int type; + LPDIRECTDRAW lpDD; + LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary; + LPDIRECTDRAWSURFACE lpBackBuffer; + + DDCAPS capsDrv; + unsigned int uDestSizeAlign, uSrcSizeAlign; + DWORD dwUpdateFlags; + DDOVERLAYFX ovfx; + RECT rs,rd; + RECT m_oldrd; + RECT winRect; + + int overlay_color; + bool initing; + int yuy2_output, uyvy_output; + + void getRects(RECT *drs, RECT *drd, int fixmultimon=1) const; + + int m_fontsize; +}; + +extern OverlayVideoOutput overlay; + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_wmvdrm/vidutils.cpp b/Src/Plugins/Input/in_wmvdrm/vidutils.cpp new file mode 100644 index 00000000..6361afe3 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/vidutils.cpp @@ -0,0 +1,130 @@ +#include "main.h" +#include "api.h" +#include <multimon.h> + +#undef GetSystemMetrics +void getViewport(RECT *r, HWND wnd, int full, RECT *sr) +{ + POINT *p = NULL; + if (p || sr || wnd) + { + static int initted = 0; + static HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags); + static HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags); + static HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags); + static BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFOEX lpmi); + if (!initted) + { + HINSTANCE h = LoadLibraryW(L"user32.dll"); + if (h) + { + Mfp = (HMONITOR (WINAPI *)(POINT, DWORD)) GetProcAddress(h, "MonitorFromPoint"); + Mfr = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect"); + Mfw = (HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow"); + Gmi = (BOOL (WINAPI *)(HMONITOR, LPMONITORINFOEX)) GetProcAddress(h, "GetMonitorInfoW"); + } + initted = 1; + } + + if (Mfp && Mfr && Mfw && Gmi) + { + HMONITOR hm = NULL; + + if (sr) + hm = Mfr(sr, MONITOR_DEFAULTTONEAREST); + else if (wnd) + hm = Mfw(wnd, MONITOR_DEFAULTTONEAREST); + else if (p) + hm = Mfp(*p, MONITOR_DEFAULTTONEAREST); + + if (hm) + { + MONITORINFOEXW mi; + memset(&mi, 0, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (Gmi(hm, &mi)) + { + if (!full) + *r = mi.rcWork; + else + *r = mi.rcMonitor; + + return ; + } + } + } + } + if (full) + { // this might be borked =) + r->top = r->left = 0; + r->right = GetSystemMetrics(SM_CXSCREEN); + r->bottom = GetSystemMetrics(SM_CYSCREEN); + } + else + { + SystemParametersInfoW(SPI_GETWORKAREA, 0, r, 0); + } +} + +VideoConfig::VideoConfig() : group(0), +itemYV12(0), itemOverlay(0), itemVsync(0), itemDDraw(0) +{} + +bool VideoConfig::yv12() +{ + GetGroup(); + if (!itemYV12) + return false; + + return itemYV12->GetBool(); +} + +bool VideoConfig::overlays() +{ + GetGroup(); + if (!itemOverlay) + return true; // overlay by default + + return itemOverlay->GetBool(); +} + +bool VideoConfig::vsync() +{ + GetGroup(); + if (!itemVsync) + return false; // no vsync by default + + return itemVsync->GetBool(); +} + +bool VideoConfig::ddraw() +{ + GetGroup(); + if (!itemDDraw) + return true; + + return itemDDraw->GetBool(); +} + +void VideoConfig::GetGroup() +{ + if (group) + return ; + + // {2135E318-6919-4bcf-99D2-62BE3FCA8FA6} + static const GUID videoConfigGroupGUID = + { 0x2135e318, 0x6919, 0x4bcf, { 0x99, 0xd2, 0x62, 0xbe, 0x3f, 0xca, 0x8f, 0xa6 } }; + + group = AGAVE_API_CONFIG->GetGroup(videoConfigGroupGUID); + if (group) + { + itemYV12 = group->GetItem(L"YV12"); + itemOverlay = group->GetItem(L"overlay"); + itemVsync = group->GetItem(L"vsync"); + itemDDraw = group->GetItem(L"ddraw"); + } +} + + +VideoConfig config_video; diff --git a/Src/Plugins/Input/in_wmvdrm/vidutils.h b/Src/Plugins/Input/in_wmvdrm/vidutils.h new file mode 100644 index 00000000..34b83a90 --- /dev/null +++ b/Src/Plugins/Input/in_wmvdrm/vidutils.h @@ -0,0 +1,24 @@ +#ifndef NULLSOFT_IN_WMVDRM_VID_UTILS_H +#define NULLSOFT_IN_WMVDRM_VID_UTILS_H + +#include "../Agave/Config/ifc_configgroup.h" + +class VideoConfig +{ +public: + VideoConfig(); + bool yv12(); + bool overlays(); + bool vsync(); + bool ddraw(); + +private: + void GetGroup(); + + ifc_configgroup *group; + ifc_configitem *itemYV12, *itemOverlay, *itemVsync, *itemDDraw; +}; + +extern VideoConfig config_video; + +#endif
\ No newline at end of file |