Main.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. #define PLUGIN_VERSION L"2.70"
  2. #include "Main.h"
  3. #include <windows.h>
  4. #include <stdio.h>
  5. #include <locale.h>
  6. #include "resource.h"
  7. #include "../Winamp/in2.h"
  8. #include "../Winamp/wa_ipc.h"
  9. #include "../nu/AutoChar.h"
  10. #include "api__in_mp4.h"
  11. #include <api/service/waservicefactory.h>
  12. #pragma warning(disable:4786)
  13. #include "mpeg4audio.h"
  14. #include <shlwapi.h>
  15. #include <malloc.h>
  16. #include "VirtualIO.h"
  17. #include "AlbumArt.h"
  18. #include <assert.h>
  19. #include "../in_wmvdrm/Remaining.h"
  20. #include "VideoThread.h"
  21. #include "RawMediaReader.h"
  22. #include "../nu/Singleton.h"
  23. #include <strsafe.h>
  24. Remaining remaining;
  25. nu::VideoClock video_clock;
  26. AlbumArtFactory albumArtFactory;
  27. wchar_t m_ini[MAX_PATH] = {0};
  28. int infoDlg(const wchar_t *fn, HWND hwnd);
  29. #define WM_WA_IPC WM_USER
  30. #define WM_WA_MPEG_EOF WM_USER+2
  31. HANDLE hThread;
  32. static DWORD WINAPI playProc(LPVOID lpParameter);
  33. static int paused, m_kill;
  34. HANDLE killEvent, seekEvent, pauseEvent;
  35. static int m_opened;
  36. static RawMediaReaderService raw_media_reader_service;
  37. static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
  38. static void stop();
  39. // wasabi based services for localisation support
  40. api_language *WASABI_API_LNG = 0;
  41. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  42. api_config *AGAVE_API_CONFIG = 0;
  43. api_memmgr *WASABI_API_MEMMGR = 0;
  44. api_application *WASABI_API_APP = 0;
  45. int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
  46. {
  47. MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
  48. msgbx.lpszText = message;
  49. msgbx.lpszCaption = title;
  50. msgbx.lpszIcon = MAKEINTRESOURCE(102);
  51. msgbx.hInstance = GetModuleHandle(0);
  52. msgbx.dwStyle = MB_USERICON;
  53. msgbx.hwndOwner = parent;
  54. return MessageBoxIndirect(&msgbx);
  55. }
  56. void about(HWND hwndParent)
  57. {
  58. wchar_t message[1024] = {0}, text[1024] = {0};
  59. WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_MPEG4_AUDIO_DECODER_OLD,text,1024);
  60. StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
  61. mod.description, TEXT(__DATE__));
  62. DoAboutMessageBox(hwndParent,text,message);
  63. }
  64. static const wchar_t defaultExtensions_nonpro[] = {L"M4A;MP4"};
  65. static const wchar_t defaultExtensions_pro[] = {L"M4A;MP4;M4V"};
  66. const wchar_t *defaultExtensions = defaultExtensions_pro;
  67. // the return pointer has been malloc'd. Use free() when you are done.
  68. char *BuildExtensions(const char *extensions)
  69. {
  70. char name[64] = {0};
  71. WASABI_API_LNGSTRING_BUF(IDS_MP4_FILE,name,64);
  72. size_t length = strlen(extensions) + 1 + strlen(name) + 2;
  73. char *newExt = (char *)calloc(length, sizeof(char));
  74. char *ret = newExt; // save because we modify newExt
  75. // copy extensions
  76. StringCchCopyExA(newExt, length, extensions, &newExt, &length, 0);
  77. newExt++;
  78. length--;
  79. // copy description
  80. StringCchCopyExA(newExt, length, name, &newExt, &length, 0);
  81. newExt++;
  82. length--;
  83. // double null terminate
  84. assert(length == 1);
  85. *newExt = 0;
  86. return ret;
  87. }
  88. int init()
  89. {
  90. if (!IsWindow(mod.hMainWindow))
  91. return IN_INIT_FAILURE;
  92. killEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  93. seekEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  94. pauseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  95. mod.service->service_register(&albumArtFactory);
  96. raw_factory.Register(mod.service, &raw_media_reader_service);
  97. waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
  98. if (sf)
  99. AGAVE_API_CONFIG = (api_config *)sf->getInterface();
  100. sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);
  101. if (sf)
  102. WASABI_API_APP = (api_application *)sf->getInterface();
  103. sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);
  104. if (sf)
  105. WASABI_API_MEMMGR = (api_memmgr *)sf->getInterface();
  106. sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);
  107. if (sf)
  108. WAC_API_DOWNLOADMANAGER = (api_downloadManager *)sf->getInterface();
  109. sf = mod.service->service_getServiceByGuid(ThreadPoolGUID);
  110. if (sf)
  111. WASABI_API_THREADPOOL = (api_threadpool *)sf->getInterface();
  112. // loader so that we can get the localisation service api for use
  113. sf = mod.service->service_getServiceByGuid(languageApiGUID);
  114. if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
  115. // need to have this initialised before we try to do anything with localisation features
  116. WASABI_API_START_LANG(mod.hDllInstance,InMp4LangGUID);
  117. static wchar_t szDescription[256];
  118. StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MPEG4_AUDIO_DECODER),PLUGIN_VERSION);
  119. mod.description = (char*)szDescription;
  120. const wchar_t *inipath = (wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
  121. PathCombineW(m_ini, inipath, L"Plugins");
  122. CreateDirectoryW(m_ini, NULL);
  123. PathAppendW(m_ini, L"in_mp4.ini");
  124. wchar_t exts[1024] = {0};
  125. GetPrivateProfileStringW(L"in_mp4", L"extensionlist", defaultExtensions, exts, 1024, m_ini);
  126. mod.FileExtensions = BuildExtensions(AutoChar(exts));
  127. return IN_INIT_SUCCESS;
  128. }
  129. void quit()
  130. {
  131. CloseHandle(killEvent);
  132. CloseHandle(seekEvent);
  133. raw_factory.Deregister(mod.service);
  134. waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
  135. if (sf)
  136. sf->releaseInterface(AGAVE_API_CONFIG);
  137. sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);
  138. if (sf)
  139. sf->releaseInterface(WASABI_API_APP);
  140. sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);
  141. if (sf)
  142. sf->releaseInterface( WAC_API_DOWNLOADMANAGER );
  143. mod.service->service_deregister(&albumArtFactory);
  144. free(mod.FileExtensions);
  145. }
  146. int isourfile(const wchar_t *fn)
  147. {
  148. return 0;
  149. }
  150. void config(HWND hwndParent);
  151. void setoutputtime(int time_in_ms)
  152. {
  153. m_needseek = time_in_ms;
  154. SetEvent(seekEvent);
  155. }
  156. MP4TrackId GetVideoTrack(MP4FileHandle infile)
  157. {
  158. int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
  159. for (int i = 0; i < numTracks; i++)
  160. {
  161. MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
  162. const char* trackType = MP4GetTrackType(infile, trackId);
  163. if (!lstrcmpA(trackType, MP4_VIDEO_TRACK_TYPE))
  164. return trackId;
  165. }
  166. /* can't decode this */
  167. return MP4_INVALID_TRACK_ID;
  168. }
  169. MP4TrackId GetAudioTrack(MP4FileHandle infile)
  170. {
  171. int ret = MP4_INVALID_TRACK_ID;
  172. __try
  173. {
  174. /* find AAC track */
  175. int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
  176. for (int i = 0; i < numTracks; i++)
  177. {
  178. MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
  179. if (trackId != MP4_INVALID_TRACK_ID)
  180. {
  181. const char* trackType = MP4GetTrackType(infile, trackId);
  182. if (trackType && !lstrcmpA(trackType, MP4_AUDIO_TRACK_TYPE))
  183. return trackId;
  184. }
  185. }
  186. /* can't decode this */
  187. return MP4_INVALID_TRACK_ID;
  188. }
  189. __except(EXCEPTION_EXECUTE_HANDLER)
  190. {
  191. return MP4_INVALID_TRACK_ID;
  192. }
  193. return ret;
  194. }
  195. MP4SampleId numSamples, numVideoSamples;
  196. MP4FileHandle MP4hFile;
  197. MP4TrackId audio_track, video_track;
  198. double m_length;
  199. volatile int m_needseek = -1;
  200. unsigned int audio_srate, audio_nch, audio_bps, audio_bitrate=0;
  201. unsigned int video_bitrate=0;
  202. wchar_t lastfn[MAX_PATH*4] = L"";
  203. MP4AudioDecoder *audio = 0;
  204. waServiceFactory *audioFactory = 0, *videoFactory = 0;
  205. MP4VideoDecoder *video = 0;
  206. uint32_t m_timescale = 0, m_video_timescale = 0;
  207. static void *reader = 0;
  208. bool audio_chunk = false;
  209. enum
  210. {
  211. READER_UNICODE=0,
  212. READER_HTTP=1,
  213. };
  214. int reader_type=READER_UNICODE;
  215. bool open_mp4(const wchar_t *fn)
  216. {
  217. audio = 0;
  218. video = 0;
  219. if (!_wcsnicmp(fn, L"http://", 7) || !_wcsnicmp(fn, L"https://", 8))
  220. {
  221. reader = CreateReader(fn, killEvent);
  222. reader_type=READER_HTTP;
  223. MP4hFile = MP4ReadEx(fn, reader, &HTTPIO);
  224. }
  225. else
  226. {
  227. reader = CreateUnicodeReader(fn);
  228. if (!reader)
  229. return false;
  230. reader_type=READER_UNICODE;
  231. MP4hFile = MP4ReadEx(fn, reader, &UnicodeIO);
  232. }
  233. if (!MP4hFile)
  234. {
  235. return false;
  236. }
  237. m_opened = 1;
  238. unsigned int output_bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
  239. if (output_bits >= 24)
  240. output_bits = 24;
  241. else
  242. output_bits = 16;
  243. unsigned int max_channels;
  244. // get max channels
  245. if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true))
  246. max_channels = 6;
  247. else if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false))
  248. max_channels = 1;
  249. else
  250. max_channels = 2;
  251. audio_track = GetAudioTrack(MP4hFile);
  252. if (audio_track == MP4_INVALID_TRACK_ID || !CreateDecoder(MP4hFile, audio_track, audio, audioFactory) || audio->OpenMP4(MP4hFile, audio_track, output_bits, max_channels, false) != MP4_SUCCESS)
  253. {
  254. audio = 0;
  255. video_clock.Start();
  256. }
  257. video_track = GetVideoTrack(MP4hFile);
  258. if (video_track != MP4_INVALID_TRACK_ID)
  259. {
  260. CreateVideoDecoder(MP4hFile, video_track, video, videoFactory);
  261. if (video)
  262. video->Open(MP4hFile, video_track);
  263. }
  264. else
  265. video=0;
  266. if (!audio && !video)
  267. {
  268. return false;
  269. }
  270. numVideoSamples = MP4GetTrackNumberOfSamples(MP4hFile, video_track);
  271. m_video_timescale = MP4GetTrackTimeScale(MP4hFile, video_track);
  272. unsigned __int64 trackDuration;
  273. double lengthAudio = 0;
  274. double lengthVideo = 0;
  275. if (audio_track != MP4_INVALID_TRACK_ID)
  276. {
  277. if (audio)
  278. {
  279. ConfigureDecoderASC(MP4hFile, audio_track, audio);
  280. audio_chunk = !!audio->RequireChunks();
  281. }
  282. else
  283. audio_chunk = false;
  284. numSamples = audio_chunk?MP4GetTrackNumberOfChunks(MP4hFile, audio_track):MP4GetTrackNumberOfSamples(MP4hFile, audio_track);
  285. m_timescale = MP4GetTrackTimeScale(MP4hFile, audio_track);
  286. trackDuration = MP4GetTrackDuration(MP4hFile, audio_track);
  287. lengthAudio = (double)(__int64)trackDuration / (double)m_timescale;
  288. }
  289. else
  290. {
  291. numSamples = numVideoSamples;
  292. trackDuration = MP4GetTrackDuration(MP4hFile, video_track);
  293. lengthVideo = (double)(__int64)trackDuration / (double)m_video_timescale;
  294. }
  295. /* length in Sec. */
  296. m_length = max(lengthAudio, lengthVideo); //(double)(__int64)trackDuration / (double)m_timescale;
  297. audio_bitrate = MP4GetTrackBitRate(MP4hFile, audio_track) / 1000;
  298. if (video)
  299. video_bitrate = MP4GetTrackBitRate(MP4hFile, video_track) / 1000;
  300. else
  301. video_bitrate = 0;
  302. if (audio && audio->SetGain(GetGain(MP4hFile)) == MP4_SUCCESS)
  303. mod.UsesOutputPlug |= 8;
  304. else
  305. mod.UsesOutputPlug &= ~8;
  306. return true;
  307. }
  308. int play(const wchar_t *fn)
  309. {
  310. video_clock.Reset();
  311. if (!videoOutput) // grab this now while we're on the main thread
  312. videoOutput = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
  313. audio = 0;
  314. video = 0;
  315. paused = 0;
  316. m_kill = 0;
  317. ResetEvent(killEvent);
  318. m_length = 0;
  319. ResetEvent(seekEvent);
  320. m_needseek = -1;
  321. SetEvent(pauseEvent);
  322. if (m_force_seek != -1)
  323. {
  324. setoutputtime(m_force_seek);
  325. }
  326. m_opened = 0;
  327. lstrcpynW(lastfn, fn, MAX_PATH*4);
  328. DWORD thread_id;
  329. HANDLE threadCreatedEvent = CreateEvent(0, FALSE, FALSE, 0);
  330. hThread = CreateThread(NULL, NULL, PlayProc, (LPVOID)threadCreatedEvent, NULL, &thread_id);
  331. SetThreadPriority(hThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
  332. WaitForSingleObject(threadCreatedEvent, INFINITE);
  333. CloseHandle(threadCreatedEvent);
  334. return 0;
  335. }
  336. static inline wchar_t *IncSafe(wchar_t *val, int x)
  337. {
  338. while (x--)
  339. {
  340. if (*val)
  341. val++;
  342. }
  343. return val;
  344. }
  345. void GetGaps(MP4FileHandle mp4, unsigned __int32 &pre, unsigned __int32 &post)
  346. {
  347. wchar_t gap_data[128] = {0};
  348. if (GetCustomMetadata(mp4, "iTunSMPB", gap_data, 128) && gap_data[0])
  349. {
  350. wchar_t *itr = IncSafe(gap_data, 9);
  351. pre = wcstoul(itr, 0, 16);
  352. itr = IncSafe(itr, 9);
  353. post = wcstoul(itr, 0, 16);
  354. // don't care about total number of samples, really
  355. /*
  356. itr+=9;
  357. unsigned int numSamples = wcstoul(itr, 0, 16);*/
  358. }
  359. else
  360. {
  361. pre = 0;
  362. post = 0;
  363. }
  364. }
  365. float GetGain(MP4FileHandle mp4, bool allowDefault)
  366. {
  367. if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
  368. {
  369. float dB = 0, peak = 1.0f;
  370. wchar_t gain[128] = L"", peakVal[128] = L"";
  371. _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
  372. switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
  373. {
  374. case 0: // track
  375. if ((!GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128) || !gain[0])
  376. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  377. GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128);
  378. if ((!GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128) || !peakVal[0])
  379. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  380. GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128);
  381. break;
  382. case 1:
  383. if ((!GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128) || !gain[0])
  384. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  385. GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128);
  386. if ((!GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128) || !peakVal[0])
  387. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  388. GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128);
  389. break;
  390. }
  391. if (gain[0])
  392. {
  393. if (gain[0] == L'+')
  394. dB = (float)_wtof_l(&gain[1],C_locale);
  395. else
  396. dB = (float)_wtof_l(gain,C_locale);
  397. }
  398. else if (allowDefault)
  399. {
  400. dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
  401. return powf(10.0f, dB / 20.0f);
  402. }
  403. if (peakVal[0])
  404. {
  405. peak = (float)_wtof_l(peakVal,C_locale);
  406. }
  407. switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
  408. {
  409. case 0: // apply gain
  410. return powf(10.0f, dB / 20.0f);
  411. case 1: // apply gain, but don't clip
  412. return min(powf(10.0f, dB / 20.0f), 1.0f / peak);
  413. case 2: // normalize
  414. return 1.0f / peak;
  415. case 3: // prevent clipping
  416. if (peak > 1.0f)
  417. return 1.0f / peak;
  418. else
  419. return 1.0f;
  420. }
  421. }
  422. return 1.0f; // no gain
  423. }
  424. bool first;
  425. void pause()
  426. {
  427. paused = 1;
  428. if (audio)
  429. {
  430. mod.outMod->Pause(1);
  431. }
  432. else
  433. {
  434. video_clock.Pause();
  435. }
  436. ResetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state
  437. }
  438. void unpause()
  439. {
  440. paused = 0;
  441. if (audio)
  442. {
  443. mod.outMod->Pause(0);
  444. }
  445. else
  446. {
  447. video_clock.Unpause();
  448. }
  449. SetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state
  450. }
  451. int ispaused()
  452. {
  453. return paused;
  454. }
  455. void stop()
  456. {
  457. if (reader && reader_type==READER_HTTP) StopReader(reader);
  458. lastfn[0] = 0;
  459. SetEvent(killEvent);
  460. m_kill = 1;
  461. WaitForSingleObject(hThread, INFINITE);
  462. mod.outMod->Close();
  463. mod.SAVSADeInit();
  464. if (m_opened) MP4Close(MP4hFile);
  465. MP4hFile = 0;
  466. m_opened = 0;
  467. if (audio)
  468. {
  469. audio->Close();
  470. audioFactory->releaseInterface(audio);
  471. }
  472. audioFactory = 0;
  473. audio = 0;
  474. if (video)
  475. {
  476. video->Close();
  477. videoFactory->releaseInterface(video);
  478. }
  479. videoFactory=0;
  480. video = 0;
  481. if (reader)
  482. {
  483. if (reader_type == READER_HTTP)
  484. DestroyReader(reader);
  485. else
  486. DestroyUnicodeReader(reader);
  487. }
  488. reader = 0;
  489. }
  490. int getlength()
  491. {
  492. return (int)(m_length*1000);
  493. }
  494. int getoutputtime()
  495. {
  496. if (m_needseek == -1)
  497. return (int)GetClock();
  498. else
  499. return m_needseek; // this prevents the seekbar from jumping around while the playthread is seeking
  500. }
  501. void setvolume(int volume)
  502. {
  503. mod.outMod->SetVolume(volume);
  504. }
  505. void setpan(int pan)
  506. {
  507. mod.outMod->SetPan(pan);
  508. }
  509. /*
  510. void FillInfo(HWND hwndDlg, MP4FileHandle hMp4);
  511. void CALLBACK CurrentlyPlayingInfoBox(ULONG_PTR param)
  512. {
  513. ThreadInfoBox *threadInfo = (ThreadInfoBox *)param;
  514. FillInfo(threadInfo->hwndDlg, MP4hFile);
  515. SetEvent(threadInfo->completionEvent);
  516. }
  517. */
  518. void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms)
  519. {
  520. if (!filename || !*filename) // currently playing file
  521. {
  522. if (length_in_ms) *length_in_ms = getlength();
  523. if (title) // get non-path portion.of filename
  524. {
  525. lstrcpynW(title, lastfn, GETFILEINFO_TITLE_LENGTH);
  526. PathStripPathW(title);
  527. PathRemoveExtensionW(title);
  528. }
  529. }
  530. else // some other file
  531. {
  532. if (length_in_ms) // calculate length
  533. {
  534. *length_in_ms = -1000; // the default is unknown file length (-1000).
  535. MP4FileHandle hMp4 = MP4Read(filename);
  536. if (hMp4)
  537. {
  538. double lengthAudio = 0;
  539. double lengthVideo = 0;
  540. MP4TrackId audio_track = GetAudioTrack(hMp4);
  541. if (audio_track != -1)
  542. {
  543. int timescale = MP4GetTrackTimeScale(hMp4, audio_track);
  544. unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, audio_track);
  545. lengthAudio = (double)(__int64)trackDuration / (double)timescale;
  546. }
  547. MP4TrackId video_track = GetVideoTrack(hMp4);
  548. if (video_track != -1)
  549. {
  550. int timescale = MP4GetTrackTimeScale(hMp4, video_track);
  551. unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, video_track);
  552. lengthVideo = (double)(__int64)trackDuration / (double)timescale;
  553. }
  554. *length_in_ms = (int)(max(lengthAudio, lengthVideo) * 1000);
  555. MP4Close(hMp4);
  556. }
  557. }
  558. if (title) // get non path portion of filename
  559. {
  560. lstrcpynW(title, filename, GETFILEINFO_TITLE_LENGTH);
  561. PathStripPathW(title);
  562. PathRemoveExtensionW(title);
  563. }
  564. }
  565. }
  566. void eq_set(int on, char data[10], int preamp)
  567. {}
  568. // module definition.
  569. In_Module mod =
  570. {
  571. IN_VER_RET, // defined in IN2.H
  572. "nullsoft(in_mp4.dll)", //"Nullsoft MPEG-4 Audio Decoder v1.22"
  573. 0, // hMainWindow (filled in by winamp)
  574. 0, // hDllInstance (filled in by winamp)
  575. 0, // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
  576. 1, // is_seekable
  577. 1, // uses output plug-in system
  578. config,
  579. about,
  580. init,
  581. quit,
  582. getfileinfo,
  583. infoDlg,
  584. isourfile,
  585. play,
  586. pause,
  587. unpause,
  588. ispaused,
  589. stop,
  590. getlength,
  591. getoutputtime,
  592. setoutputtime,
  593. setvolume,
  594. setpan,
  595. 0, 0, 0, 0, 0, 0, 0, 0, 0, // visualization calls filled in by winamp
  596. 0, 0, // dsp calls filled in by winamp
  597. eq_set,
  598. NULL, // setinfo call filled in by winamp
  599. 0 // out_mod filled in by winamp
  600. };
  601. extern "C"
  602. {
  603. __declspec(dllexport) In_Module * winampGetInModule2()
  604. {
  605. return &mod;
  606. }
  607. }