12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- #include "main.h"
- #include "VirtualIO.h"
- #include "../nu/ns_wc.h"
- #include "../nu/AutoChar.h"
- #include <vector>
- #include "../Winamp/wa_ipc.h"
- #include "api__in_mp4.h"
- #include "Stopper.h"
- #include <shlwapi.h>
- #include <strsafe.h>
- #include "resource.h"
- static inline wchar_t *IncSafe(wchar_t *val, int x)
- {
- while (x--)
- {
- if (*val)
- val++;
- }
- return val;
- }
- bool KeywordMatch(const char *mainString, const char *keyword)
- {
- return !_stricmp(mainString, keyword);
- }
- bool GetCustomMetadata(MP4FileHandle mp4, char *metadata, wchar_t *dest, int destlen, const char *owner)
- {
- u_int8_t *value;
- u_int32_t size;
- bool success = MP4GetMetadataFreeForm(mp4, metadata, &value, &size, owner);
- if (success)
- {
- int cnt = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)value, size, dest, destlen - 1);
- dest[cnt] = 0;
- MP4Free(value);
- }
- else
- dest[0] = 0;
- return success;
- }
- bool HasVideo(MP4FileHandle infile)
- {
- /* find Video track */
- int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
- for (int i = 0; i < numTracks; i++)
- {
- MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
- const char* trackType = MP4GetTrackType(infile, trackId);
- if (!lstrcmpA(trackType, MP4_VIDEO_TRACK_TYPE))
- return true;
- }
- /* can't decode this */
- return false;
- }
- typedef struct __INFOKEYWORD
- {
- LPCWSTR buffer;
- INT len;
- } INFOKEYWORD;
- typedef std::vector<INFOKEYWORD> KeywordList;
- /*static void KeywordList_PushBack(KeywordList *list, LPCWSTR buffer, INT len)
- {
- if (NULL == list) return;
-
- INFOKEYWORD kw = { buffer, len };
- list->push_back(kw);
- }
- static LPCWSTR ParseFormatInfoLine(LPCWSTR line, KeywordList *list)
- {
- if (NULL == line || L'\0' == *line) return line;
- LPCWSTR cursor = line;
-
- for(;;)
- {
- switch(*cursor)
- {
- case L'\r':
- if (L'\n' == *(cursor + 1))
- {
- KeywordList_PushBack(list, line, (INT)(cursor - line));
- cursor += 2;
- return cursor;
- }
- case L'\0':
- KeywordList_PushBack(list, line, (INT)(cursor - line));
- return NULL;
- case L'\t':
- KeywordList_PushBack(list, line, (INT)(cursor - line));
- line = ++cursor;
- break;
- default:
- cursor++;
- break;
- }
- }
- }
- static HRESULT FormatInformationString(LPWSTR pszBuffer, INT cchBufferMax, LPCSTR formatInfo)
- {
- if (NULL == pszBuffer) return E_POINTER;
- *pszBuffer = L'\0';
- if (NULL == formatInfo)
- return S_OK;
- INT cchFormat = lstrlenA(formatInfo);
- if (0 == cchFormat) return S_OK;
- LPWSTR pszFormat = (LPWSTR)malloc(sizeof(WCHAR) * (cchFormat + 1));
- if (NULL == pszFormat) return E_OUTOFMEMORY;
- INT result = MultiByteToWideCharSZ(CP_UTF8, 0, formatInfo, cchFormat, pszFormat, cchFormat + 1);
- if (0 == result)
- {
- DWORD errorCode = GetLastError();
- return HRESULT_FROM_WIN32(errorCode);
- }
- HRESULT hr(S_OK);
- KeywordList keys;
- KeywordList values;
- LPCWSTR line = pszFormat;
- line = ParseFormatInfoLine(line, &keys);
- line = ParseFormatInfoLine(line, &values);
- size_t count = keys.size();
- if (0 != count && count == values.size())
- {
- LPWSTR cursor = pszBuffer;
- size_t remaining = cchBufferMax;
- for (size_t index = 0; index < count; index++)
- {
- if (cursor != pszBuffer)
- hr = StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, 0);
- if (SUCCEEDED(hr) && 0 != keys[index].len)
- {
- hr = StringCchCopyNEx(cursor, remaining, keys[index].buffer, keys[index].len, &cursor, &remaining, 0);
- if (SUCCEEDED(hr))
- hr = StringCchCopyEx(cursor, remaining, L":", &cursor, &remaining, 0);
- }
- if (SUCCEEDED(hr) && 0 != values[index].len)
- {
- if (SUCCEEDED(hr) && 0 != keys[index].len)
- hr = StringCchCopyEx(cursor, remaining, L" ", &cursor, &remaining, 0);
-
- hr = StringCchCopyNEx(cursor, remaining, values[index].buffer, values[index].len, &cursor, &remaining, 0);
- }
-
- if (FAILED(hr))
- break;
- }
- }
- else
- {
- hr = StringCchCopy(pszBuffer, cchBufferMax, pszFormat);
- }
- if (NULL != pszFormat)
- free(pszFormat);
- return hr;
- }*/
- static MP4FileHandle getFileInfoMP4 = 0;
- static wchar_t getFileInfoFn[MAX_PATH]=L"";
- static void *getFileInfoReader=0;
- static FILETIME ftLastWriteTime;
- // is used to determine if the last write time of the file has changed when
- // asked to get the metadata for the same cached file so we can update things
- BOOL HasFileTimeChanged(const wchar_t *fn)
- {
- WIN32_FILE_ATTRIBUTE_DATA fileData = {0};
- if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
- {
- if(CompareFileTime(&ftLastWriteTime, &fileData.ftLastWriteTime))
- {
- ftLastWriteTime = fileData.ftLastWriteTime;
- return TRUE;
- }
- }
- return FALSE;
- }
- void UpdateFileTimeChanged(const wchar_t *fn)
- {
- WIN32_FILE_ATTRIBUTE_DATA fileData;
- if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
- {
- ftLastWriteTime = fileData.ftLastWriteTime;
- }
- }
- extern "C"
- {
- __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, size_t destlen)
- {
- if (KeywordMatch(data, "type"))
- {
- if (!fn || (fn && !fn[0]) || !PathFileExistsW(fn))
- {
- const wchar_t *e = PathFindExtensionW(fn);
- DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
- // check known video extensions
- if ((CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L".M4V", -1))
- || (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L".MOV", -1))
- || (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L".F4V", -1))
- || (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L".MP4", -1)))
- dest[0] = '1';
- else
- dest[0] = '0';
- dest[1] = 0;
- return 1;
- }
- }
- else if (KeywordMatch(data, "rateable"))
- {
- dest[0] = '1';
- dest[1] = 0;
- return 1;
- }
- if (!fn || (fn && !fn[0])) return 0;
- if (KeywordMatch(data, "family"))
- {
- LPCWSTR e;
- int pID = -1;
- e = PathFindExtensionW(fn);
- if (L'.' != *e) return 0;
- e++;
- DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
- if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"M4A", -1)) pID = IDS_FAMILY_STRING_M4A;
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"MP4", -1)) pID = IDS_FAMILY_STRING_MPEG4;
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"M4V", -1)) pID = IDS_FAMILY_STRING_M4V;
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"MOV", -1)) pID = IDS_FAMILY_STRING_QUICKTIME;
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"3GP", -1)) pID = IDS_FAMILY_STRING_3GPP;
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"F4V", -1)) pID = IDS_FAMILY_STRING_FLV;
- if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1;
- return 0;
- }
- if (KeywordMatch(data, "mime"))
- {
- LPCWSTR e;
- e = PathFindExtensionW(fn);
- if (L'.' != *e) return 0;
- e++;
- DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
- if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"M4A", -1)) { StringCchCopyW(dest, destlen, L"audio/mp4"); return 1; }
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"MP4", -1)) { StringCchCopyW(dest, destlen, L"audio/mp4"); return 1; }
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"M4V", -1)) { StringCchCopyW(dest, destlen, L"video/mp4"); return 1; }
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"MOV", -1)) { StringCchCopyW(dest, destlen, L"video/quicktime"); return 1; }
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"3GP", -1)) { StringCchCopyW(dest, destlen, L"video/3gp"); return 1; }
- else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"F4V", -1)) { StringCchCopyW(dest, destlen, L"video/f4v"); return 1; }
- return 0;
- }
- if (!getFileInfoMP4 || lstrcmpi(getFileInfoFn, fn) || HasFileTimeChanged(fn)) // different file than last time?
- {
- lstrcpyn(getFileInfoFn, fn, MAX_PATH);
- if (getFileInfoMP4)
- MP4Close(getFileInfoMP4);
- getFileInfoMP4=0;
- if (getFileInfoReader)
- DestroyUnicodeReader(getFileInfoReader);
- getFileInfoReader = CreateUnicodeReader(fn);
- if (!getFileInfoReader)
- {
- getFileInfoFn[0]=0;
- return 0;
- }
- getFileInfoMP4 = MP4ReadEx(fn, getFileInfoReader, &UnicodeIO);
- if (!getFileInfoMP4)
- {
- DestroyUnicodeReader(getFileInfoReader);
- getFileInfoReader=0;
- getFileInfoFn[0]=0;
- return 0;
- }
- else
- {
- UnicodeClose(getFileInfoReader); // go ahead and close the file so we don't lock it
- }
- }
- bool success = false;
- char *pVal = NULL;
- uint16_t *pVal_utf16 = NULL;
- if (KeywordMatch(data, "type"))
- {
- if (GetVideoTrack(getFileInfoMP4) != MP4_INVALID_TRACK_ID) // check for a video track
- dest[0] = '1';
- else // assume no video mean audio (not necessarily true, for weird mp4 stuff like systems & text)
- dest[0] = '0';
- dest[1] = 0;
- success = true;
- }
- else if (KeywordMatch(data, "lossless"))
- {
- dest[0]='0';
- dest[1]=0;
- MP4TrackId track = GetAudioTrack(getFileInfoMP4);
- if (track != MP4_INVALID_TRACK_ID)
- {
- // TODO: benski> We should check more than just ALAC
- // potentially asking the mpeg4audio services
- // but for now this should suffice.
- const char *media_data_name = MP4GetTrackMediaDataName(getFileInfoMP4, track);
- if (media_data_name && KeywordMatch (media_data_name, "alac"))
- dest[0]='1';
- }
- success=true;
- }
- else if (KeywordMatch(data, "title"))
- {
- success = MP4GetMetadataName(getFileInfoMP4, &pVal);
- if (!success)
- success = MP4Get3GPMetadata(getFileInfoMP4, "titl", &pVal_utf16);
- }
- else if (KeywordMatch(data, "album"))
- {
- success = MP4GetMetadataAlbum(getFileInfoMP4, &pVal);
- if (!success)
- success = MP4Get3GPMetadata(getFileInfoMP4, "albm", &pVal_utf16);
- }
- else if (KeywordMatch(data, "artist"))
- {
- success = MP4GetMetadataArtist(getFileInfoMP4, &pVal);
- if (!success)
- success = MP4Get3GPMetadata(getFileInfoMP4, "perf", &pVal_utf16);
- }
- else if (KeywordMatch(data, "rating"))
- {
- success = MP4GetMetadataRating(getFileInfoMP4, &pVal);
- // add a /* to enable reading from 3GP metadata, otherwise is only used on normal MP4
- if (success)/*/
- if (!success)
- {
- if ((success = MP4Get3GPMetadata(getFileInfoMP4, "rate", &pVal_utf16)))
- {
- wchar_t *value = (wchar_t*)pVal_utf16;
- if(value && *value) {
- int rating = _wtoi(value);
- // keeps things limited to our range of 0-100
- if (rating >= 100) {
- rating = 5;
- }
- // 1-100 case
- else if (rating > 0 && rating < 100) {
- rating = (rating /= 20);
- // shift up by one rating when in next band
- // 1-20 = 1, 21-40 = 2, 41-60 = 3, 61-80 = 4, 81-100 = 5
- rating += ((_wtoi(value) - (rating * 20)) > 0);
- }
- // otherwise just make sure and set zero
- else {
- rating = 0;
- }
- StringCchPrintfW(dest, destlen, L"%u", rating);
- MP4Free(pVal_utf16);
- return 1;
- }
- }
- }
- else/**/
- {
- char *value = (char*)pVal;
- if(value && *value) {
- int rating = atoi(value);
- // keeps things limited to our range of 0-100
- if (rating >= 100) {
- rating = 5;
- }
- // 1-100 case
- else if (rating > 0 && rating < 100) {
- rating = (rating /= 20);
- // shift up by one rating when in next band
- // 1-20 = 1, 21-40 = 2, 41-60 = 3, 61-80 = 4, 81-100 = 5
- rating += ((atoi(value) - (rating * 20)) > 0);
- }
- // otherwise just make sure and set zero
- else {
- rating = 0;
- }
- StringCchPrintfW(dest, destlen, L"%u", rating);
- MP4Free(pVal);
- return 1;
- }
- }
- }
- else if (KeywordMatch(data, "comment"))
- success = MP4GetMetadataComment(getFileInfoMP4, &pVal);
- else if (KeywordMatch(data, "albumartist"))
- success = MP4GetMetadataAlbumArtist(getFileInfoMP4, &pVal);
- else if (KeywordMatch(data, "gain"))
- {
- StringCchPrintfW(dest, destlen, L"%+-.2f dB", (float)log10f(GetGain(getFileInfoMP4, false))*20.0f);
- success = true;
- }
- else if (KeywordMatch(data, "replaygain_track_gain"))
- {
- GetCustomMetadata(getFileInfoMP4, "replaygain_track_gain", dest, destlen);
- success = true;
- }
- else if (KeywordMatch(data, "replaygain_album_gain"))
- {
- GetCustomMetadata(getFileInfoMP4, "replaygain_album_gain", dest, destlen);
- success = true;
- }
- else if (KeywordMatch(data, "replaygain_track_peak"))
- {
- GetCustomMetadata(getFileInfoMP4, "replaygain_track_peak", dest, destlen);
- success = true;
- }
- else if (KeywordMatch(data, "replaygain_album_peak"))
- {
- GetCustomMetadata(getFileInfoMP4, "replaygain_album_peak", dest, destlen);
- success = true;
- }
- else if (KeywordMatch(data, "bpm"))
- {
- unsigned __int16 tempo = 0;
- success = MP4GetMetadataTempo(getFileInfoMP4, &tempo);
- if (success && tempo)
- StringCchPrintf(dest, destlen, L"%u", tempo);
- }
- else if (KeywordMatch(data, "year"))
- {
- success = MP4GetMetadataYear(getFileInfoMP4, &pVal);
- if (!success)
- {
- uint64_t val = 0;
- success = MP4Get3GPMetadataInteger(getFileInfoMP4, "yrrc", &val);
- if (success)
- {
- StringCchPrintf(dest, destlen, L"%I64u", val);
- }
- }
- }
- else if (KeywordMatch(data, "bitrate"))
- {
- uint32_t audio_bitrate = 0, video_bitrate = 0;
- MP4TrackId track = GetAudioTrack(getFileInfoMP4);
- if (track != MP4_INVALID_TRACK_ID)
- audio_bitrate = MP4GetTrackBitRate(getFileInfoMP4, track) / 1000;
- track = GetVideoTrack(getFileInfoMP4);
- if (track != MP4_INVALID_TRACK_ID)
- video_bitrate = MP4GetTrackBitRate(getFileInfoMP4, track) / 1000;
- if (audio_bitrate || video_bitrate)
- StringCchPrintf(dest, destlen, L"%u", audio_bitrate+video_bitrate);
- else
- dest[0] = 0;
- success = true;
- }
- else if (KeywordMatch(data, "height"))
- {
- MP4TrackId track = GetVideoTrack(getFileInfoMP4);
- if (track != MP4_INVALID_TRACK_ID)
- {
- uint16_t height = MP4GetTrackVideoHeight(getFileInfoMP4, track);
- if (height)
- {
- StringCchPrintf(dest, destlen, L"%u", height);
- }
- else
- dest[0]=0;
- success=true;
- }
- }
- else if (KeywordMatch(data, "width"))
- {
-
- MP4TrackId track = GetVideoTrack(getFileInfoMP4);
- if (track != MP4_INVALID_TRACK_ID)
- {
- uint16_t width = MP4GetTrackVideoWidth(getFileInfoMP4, track);
- if (width)
- {
- StringCchPrintf(dest, destlen, L"%u", width);
- }
- else
- dest[0]=0;
- success=true;
- }
- }
- //else if(KeywordMatch(data,"srate")) wsprintf(dest,"%d",srate);
- //else if(KeywordMatch(data,"stereo")) wsprintf(dest,"%d",is_stereo);
- //else if(KeywordMatch(data,"vbr")) wsprintf(dest,"%d",is_vbr);
- else if (KeywordMatch(data, "genre"))
- {
- success = MP4GetMetadataGenre(getFileInfoMP4, &pVal);
- if (!success)
- success = MP4Get3GPMetadata(getFileInfoMP4, "gnre", &pVal_utf16);
- }
- else if (KeywordMatch(data, "disc"))
- {
- unsigned __int16 dummy = 0, dummy2 = 0;
- success = MP4GetMetadataDisk(getFileInfoMP4, &dummy, &dummy2);
- if (success && dummy)
- {
- if (dummy2)
- StringCchPrintf(dest, destlen, L"%u/%u", dummy, dummy2);
- else
- StringCchPrintf(dest, destlen, L"%u", dummy);
- }
- else
- dest[0] = 0;
- }
- else if (KeywordMatch(data, "track"))
- {
- unsigned __int16 dummy = 0, dummy2 = 0;
- success = MP4GetMetadataTrack(getFileInfoMP4, &dummy, &dummy2);
- if (success && dummy)
- {
- if (dummy2)
- StringCchPrintf(dest, destlen, L"%u/%u", dummy, dummy2);
- else
- StringCchPrintf(dest, destlen, L"%u", dummy);
- }
- else
- dest[0] = 0;
- }
- else if (KeywordMatch(data, "length"))
- {
- /* TODO: use sample rate and number of samples from iTunSMPB to get a more exact length */
- //MP4TrackId track = GetAudioTrack(getFileInfoMP4);
- //if (track != MP4_INVALID_TRACK_ID)
- //{
- // int m_timescale = MP4GetTrackTimeScale(getFileInfoMP4, track);
- // unsigned __int64 trackDuration = MP4GetTrackDuration(getFileInfoMP4, track);
- // double m_length = (double)(__int64)trackDuration / (double)m_timescale;
- // StringCchPrintf(dest, destlen, L"%d", (int)(m_length*1000));
- // success = true;
- //}
- double lengthAudio = 0;
- double lengthVideo = 0;
- double m_length = 0;
- MP4TrackId audio_track = GetAudioTrack(getFileInfoMP4);
- if (audio_track != -1)
- {
- int timescale = MP4GetTrackTimeScale(getFileInfoMP4, audio_track);
- unsigned __int64 trackDuration = MP4GetTrackDuration(getFileInfoMP4, audio_track);
- lengthAudio = (double)(__int64)trackDuration / (double)timescale;
- }
- MP4TrackId video_track = GetVideoTrack(getFileInfoMP4);
- if (video_track != -1)
- {
- int timescale = MP4GetTrackTimeScale(getFileInfoMP4, video_track);
- unsigned __int64 trackDuration = MP4GetTrackDuration(getFileInfoMP4, video_track);
- lengthVideo = (double)(__int64)trackDuration / (double)timescale;
- }
- m_length = (max(lengthAudio, lengthVideo));
- StringCchPrintf(dest, destlen, L"%d", (int)(m_length * 1000));
- success = true;
- }
- else if (KeywordMatch(data, "tool"))
- success = MP4GetMetadataTool(getFileInfoMP4, &pVal);
- else if (KeywordMatch(data, "composer"))
- success = MP4GetMetadataWriter(getFileInfoMP4, &pVal);
- else if (KeywordMatch(data, "category"))
- success = MP4GetMetadataGrouping(getFileInfoMP4, &pVal);
- else if (KeywordMatch(data, "GracenoteFileID"))
- {
- GetCustomMetadata(getFileInfoMP4, "gnid", dest, destlen);
- success = true;
- }
- else if (KeywordMatch(data, "GracenoteExtData"))
- {
- GetCustomMetadata(getFileInfoMP4, "gnxd", dest, destlen);
- success = true;
- }
- else if (KeywordMatch(data, "publisher"))
- {
- GetCustomMetadata(getFileInfoMP4, "publisher", dest, destlen, "com.nullsoft.winamp");
- success = true;
- }
- else if (KeywordMatch(data, "pregap"))
- {
- wchar_t gap_data[128] = {0};
- if (GetCustomMetadata(getFileInfoMP4, "iTunSMPB", gap_data, 128) && gap_data[0])
- {
- wchar_t *itr = IncSafe(gap_data, 9);
- unsigned int pregap = wcstoul(itr, 0, 16);
- StringCchPrintfW(dest, destlen, L"%u",pregap);
- success=true;
- }
- }
- else if (KeywordMatch(data, "postgap"))
- {
- wchar_t gap_data[128] = {0};
- if (GetCustomMetadata(getFileInfoMP4, "iTunSMPB", gap_data, 128) && gap_data[0])
- {
- wchar_t *itr = IncSafe(gap_data, 18);
- unsigned int postgap = wcstoul(itr, 0, 16);
- StringCchPrintfW(dest, destlen, L"%u",postgap);
- success=true;
- }
- }
- else if (KeywordMatch(data, "numsamples"))
- {
- wchar_t gap_data[128] = {0};
- if (GetCustomMetadata(getFileInfoMP4, "iTunSMPB", gap_data, 128) && gap_data[0])
- {
- wchar_t *itr = IncSafe(gap_data,27);
- unsigned __int64 numsamples = _wcstoui64(itr, 0, 16);
- StringCchPrintfW(dest, destlen, L"%I64u", numsamples);
- success=true;
- }
- }
- else if (KeywordMatch(data, "formatinformation"))
- {
- MP4TrackId track = GetAudioTrack(getFileInfoMP4);
- if (track != MP4_INVALID_TRACK_ID)
- {
- char *track_type = MP4PrintAudioInfo(getFileInfoMP4, track);
- if (track_type)
- {
- uint32_t bitrate = MP4GetTrackBitRate(getFileInfoMP4, track);
- StringCchPrintfEx(dest, destlen, &dest, &destlen, 0, WASABI_API_LNGSTRINGW(IDS_AUDIO_INFO), track_type, (bitrate + 500) / 1000);
- MP4Free(track_type);
- }
- }
- track = GetVideoTrack(getFileInfoMP4);
- if (track != MP4_INVALID_TRACK_ID)
- {
- char *track_type = MP4PrintVideoInfo(getFileInfoMP4, track);
- if (track_type)
- {
- uint32_t bitrate = MP4GetTrackBitRate(getFileInfoMP4, track);
- StringCchPrintfEx(dest, destlen, &dest, &destlen, 0, WASABI_API_LNGSTRINGW(IDS_VIDEO_INFO), track_type, (bitrate + 500) / 1000);
- u_int16_t width = MP4GetTrackVideoWidth(getFileInfoMP4, track);
- u_int16_t height = MP4GetTrackVideoHeight(getFileInfoMP4, track);
- if (width && height)
- {
- // TODO: framerate, but the MP4GetTrackVideoFrameRate method just guesses based on duration and samples
- StringCchPrintfEx(dest, destlen, &dest, &destlen, 0, L"\t%ux%u\n", width, height);
- }
- MP4Free(track_type);
- }
- }
- success=true;
- }
- else // don't understand the name
- {
- // MP4Close(getFileInfoMP4);
- return 0;
- }
- if (pVal)
- {
- MultiByteToWideCharSZ(CP_UTF8, 0, pVal, -1, dest, destlen);
- MP4Free(pVal);
- }
- else if (pVal_utf16)
- {
- StringCchCopyW(dest, destlen, (const wchar_t *)pVal_utf16);
- MP4Free(pVal_utf16);
- }
- if (!success)
- dest[0] = 0;
- //MP4Close(getFileInfoMP4);
- return 1;
- }
- static MP4FileHandle setFileInfoMP4 = 0;
- static int m_last_err = 0;
- static wchar_t m_last_ext_fn[MAX_PATH] = L"";
- static void *setFileInfoReader = 0;
- static bool setFile3GP=false;
- /*static int SetExtendedInfo3GP(const char *data, const wchar_t *val)
- {
- if (KeywordMatch(data, "title"))
- {
- if (val && *val) MP4Set3GPMetadata(setFileInfoMP4, "titl", (const uint16_t *)val);
- else MP4Delete3GPMetadata(setFileInfoMP4, "titl");
- }
- else if (KeywordMatch(data, "album"))
- {
- if (val && *val) MP4Set3GPMetadata(setFileInfoMP4, "albm", (const uint16_t *)val);
- else MP4Delete3GPMetadata(setFileInfoMP4, "albm");
- }
- else if (KeywordMatch(data, "genre"))
- {
- if (val && *val) MP4Set3GPMetadata(setFileInfoMP4, "gnre", (const uint16_t *)val);
- else MP4Delete3GPMetadata(setFileInfoMP4, "gnre");
- }
- else if (KeywordMatch(data, "artist"))
- {
- if (val && *val) MP4Set3GPMetadata(setFileInfoMP4, "perf", (const uint16_t *)val);
- else MP4Delete3GPMetadata(setFileInfoMP4, "perf");
- }
- else if (KeywordMatch(data, "year"))
- {
- if (val && *val) MP4Set3GPMetadataInteger(setFileInfoMP4, "yrrc", _wtoi64(val));
- else MP4Delete3GPMetadata(setFileInfoMP4, "yrrc");
- }
- else
- return 0;
- return 1;
- }*/
- static int SetExtendedInfoMP4(const char *data, const wchar_t *val)
- {
- if (KeywordMatch(data, "title"))
- {
- if (val && *val) MP4SetMetadataName(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataName(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "album"))
- {
- if (val && *val)
- MP4SetMetadataAlbum(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataAlbum(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "artist"))
- {
- if (val && *val)
- MP4SetMetadataArtist(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataArtist(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "rating"))
- {
- if (val && *val)
- {
- char temp[128] = {0};
- StringCchPrintfA(temp, 128, "%u", _wtoi(val) * 20);
- MP4SetMetadataRating(setFileInfoMP4, temp);
- }
- else MP4DeleteMetadataRating(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "comment"))
- {
- if (val && *val)
- MP4SetMetadataComment(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataComment(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "year"))
- {
- if (val && *val)
- MP4SetMetadataYear(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataYear(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "genre"))
- {
- if (val && *val)
- MP4SetMetadataGenre(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataGenre(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "albumartist"))
- {
- if (val && *val)
- MP4SetMetadataAlbumArtist(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataAlbumArtist(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "replaygain_track_gain"))
- {
- if (val && *val)
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_track_gain", "com.apple.iTunes");
- AutoChar utf8(val, CP_UTF8);
- MP4SetMetadataFreeForm(setFileInfoMP4, "replaygain_track_gain", (u_int8_t *)(char *)utf8, lstrlenA(utf8), "org.hydrogenaudio.replaygain");
- }
- else
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_track_gain", "com.apple.iTunes");
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_track_gain", "org.hydrogenaudio.replaygain");
- }
- }
- else if (KeywordMatch(data, "replaygain_track_peak"))
- {
- if (val && *val)
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_track_peak", "com.apple.iTunes");
- AutoChar utf8(val, CP_UTF8);
- MP4SetMetadataFreeForm(setFileInfoMP4, "replaygain_track_peak", (u_int8_t *)(char *)utf8, lstrlenA(utf8), "org.hydrogenaudio.replaygain");
- }
- else
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_track_peak", "com.apple.iTunes");
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_track_peak", "org.hydrogenaudio.replaygain");
- }
- }
- else if (KeywordMatch(data, "replaygain_album_gain"))
- {
- if (val && *val)
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_album_gain", "com.apple.iTunes");
- AutoChar utf8(val, CP_UTF8);
- MP4SetMetadataFreeForm(setFileInfoMP4, "replaygain_album_gain", (u_int8_t *)(char *)utf8, lstrlenA(utf8), "org.hydrogenaudio.replaygain");
- }
- else
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_album_gain", "com.apple.iTunes");
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_album_gain", "org.hydrogenaudio.replaygain");
- }
- }
- else if (KeywordMatch(data, "replaygain_album_peak"))
- {
- if (val && *val)
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_album_peak", "com.apple.iTunes");
- AutoChar utf8(val, CP_UTF8);
- MP4SetMetadataFreeForm(setFileInfoMP4, "replaygain_album_peak", (u_int8_t *)(char *)utf8, lstrlenA(utf8), "org.hydrogenaudio.replaygain");
- }
- else
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_album_peak", "com.apple.iTunes");
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "replaygain_album_peak", "org.hydrogenaudio.replaygain");
- }
- }
- else if (KeywordMatch(data, "track"))
- {
- int track = _wtoi(val);
- if (track)
- {
- int tracks = 0;
- const wchar_t *_tracks = wcschr(val, L'/');
- if (_tracks) tracks = _wtoi(_tracks + 1);
- MP4SetMetadataTrack(setFileInfoMP4, track, tracks);
- }
- else
- MP4DeleteMetadataTrack(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "disc"))
- {
- int disc = _wtoi(val);
- if (disc)
- {
- int discs = 0;
- const wchar_t *_discs = wcschr(val, L'/');
- if (_discs) discs = _wtoi(_discs + 1);
- MP4SetMetadataDisk(setFileInfoMP4, disc, discs);
- }
- else
- MP4DeleteMetadataDisk(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "tool"))
- {
- if (val && *val)
- MP4SetMetadataTool(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataTool(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "bpm"))
- {
- if (val && *val && *val != '0')
- MP4SetMetadataTempo(setFileInfoMP4, _wtoi(val));
- else
- MP4DeleteMetadataTempo(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "composer"))
- {
- if (val && *val)
- MP4SetMetadataWriter(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataWriter(setFileInfoMP4);
- }
- else if (KeywordMatch(data, "GracenoteFileID"))
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "gnid", "com.apple.iTunes"); // delete obselete metadata storage scheme
- if (val && *val)
- {
- AutoChar utf8(val, CP_UTF8);
- MP4SetMetadataFreeForm(setFileInfoMP4, "gnid", (u_int8_t *)(char *)utf8, lstrlenA(utf8), "com.gracenote.cddb");
- }
- else
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "gnid", "com.gracenote.cddb");
- }
- }
- else if (KeywordMatch(data, "GracenoteExtData"))
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "gnxd", "com.apple.iTunes");// delete obselete metadata storage scheme
- if (val && *val)
- {
- AutoChar utf8(val, CP_UTF8);
- MP4SetMetadataFreeForm(setFileInfoMP4, "gnxd", (u_int8_t *)(char *)utf8, lstrlenA(utf8), "com.gracenote.cddb");
- }
- else
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "gnxd", "com.gracenote.cddb");
- }
- }
- else if (KeywordMatch(data, "publisher"))
- {
- if (val && *val)
- {
- AutoChar utf8(val, CP_UTF8);
- MP4SetMetadataFreeForm(setFileInfoMP4, "publisher", (u_int8_t *)(char *)utf8, lstrlenA(utf8), "com.nullsoft.winamp");
- }
- else
- {
- MP4DeleteMetadataFreeForm(setFileInfoMP4, "publisher", "com.nullsoft.winamp");
- }
- }
- else if (KeywordMatch(data, "category"))
- {
- if (val && *val)
- MP4SetMetadataGrouping(setFileInfoMP4, AutoChar(val, CP_UTF8));
- else MP4DeleteMetadataGrouping(setFileInfoMP4);
- }
- else
- return 0;
- return 1;
- }
- static Stopper stopper;
- __declspec( dllexport ) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, const wchar_t *val)
- {
- if (!setFileInfoMP4 || lstrcmpi(m_last_ext_fn, fn)) // different file than last time?
- {
- m_last_err = 0;
- lstrcpyn(m_last_ext_fn, fn, MAX_PATH);
- if (setFileInfoMP4)
- MP4Close(setFileInfoMP4);
- /* TODO: make MP4ModifyEx so we can use this
- if (setFileInfoReader)
- DestroyUnicodeReader(setFileInfoReader);
- setFileInfoReader = CreateUnicodeReader(fn);
- if (!setFileInfoReader)
- return 0;
- */
- if (!_wcsicmp(m_last_ext_fn, lastfn))
- stopper.Stop();
- setFileInfoMP4 = MP4Modify(m_last_ext_fn, 0, 0);
- if (!setFileInfoMP4)
- {
- stopper.ChangeTracking(1); // enable stats updating
- //DestroyUnicodeReader(setFileInfoReader);
- m_last_err = 1;
- return 0;
- }
- setFile3GP = false;
- const char *brand=0;
- if (MP4GetStringProperty(setFileInfoMP4, "ftyp.majorBrand", &brand))
- {
- if (!strncmp(brand, "3gp6", 4))
- setFile3GP=true;
- }
- }
- /*
- if (setFile3GP)
- return SetExtendedInfo3GP(data, val);
- else*/
- return SetExtendedInfoMP4(data, val);
- }
- __declspec(dllexport) int winampWriteExtendedFileInfo()
- {
- int err = m_last_err;
- m_last_err = 0;
- // clear this as well, so dirty info isn't read back
- if (getFileInfoMP4)
- MP4Close(getFileInfoMP4);
- getFileInfoMP4=0;
- if (getFileInfoReader)
- DestroyUnicodeReader(getFileInfoReader);
- getFileInfoReader=0;
- if (setFileInfoMP4)
- {
- MP4Close(setFileInfoMP4);
- MP4Optimize(m_last_ext_fn, NULL, 0); //put the fields at the beginning of the file
- setFileInfoMP4 = 0;
- m_last_ext_fn[0] = 0;
- stopper.Play();
- }
- // update last modified so we're not re-queried on our own updates
- UpdateFileTimeChanged(m_last_ext_fn);
- return !err;
- }
- }
|