Main.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  1. #define PLUGIN_NAME "Nullsoft NSV Decoder"
  2. #define PLUGIN_VERSION L"1.76"
  3. #include <windows.h>
  4. #include "../Winamp/in2.h"
  5. #include "../nsv/nsvplay/main.h"
  6. #include "resource.h"
  7. #include "../nu/AutoWide.h"
  8. #include "../nu/AutoCharFn.h"
  9. #define NO_IVIDEO_DECLARE
  10. #include "../winamp/wa_ipc.h"
  11. #include "../Winamp/strutil.h"
  12. #include "api.h"
  13. extern In_Module mod; // the output module (filled in near the bottom of this file)
  14. #define g_hInstance mod.hDllInstance
  15. #define WNDMENU_CAPTION L"Winamp in_nsv"
  16. #define MODAL_ABOUT
  17. #define LOC_MODAL_ABOUT
  18. #include "../nsv/nsvplay/about.h"
  19. #undef g_hInstance
  20. #include <shlwapi.h>
  21. #include <strsafe.h>
  22. extern int config_precseek;
  23. extern int config_vidoffs;
  24. extern int config_bufms;
  25. extern int config_prebufms;
  26. extern int config_underunbuf;
  27. extern int config_bufms_f;
  28. extern int config_prebufms_f;
  29. extern int config_underunbuf_f;
  30. // {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
  31. static const GUID playbackConfigGroupGUID =
  32. {
  33. 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf }
  34. };
  35. char lastfn[1024] = {0}; // currently playing file (used for getting info on the current file)
  36. static char statusbuf[1024];
  37. char stream_url[1024] = {0};
  38. ULONGLONG g_bufferstat;
  39. int m_last_bitrate;
  40. void config_read();
  41. void config_write();
  42. void config(HWND hwndParent);
  43. int file_length = 0; // file length, in bytes
  44. // Used for correcting DSP plug-in pitch changes
  45. int paused = 0; // are we paused?
  46. volatile int seek_needed; // if != -1, it is the point that the decode
  47. // thread should seek to, in ms.
  48. CRITICAL_SECTION g_decoder_cs;
  49. char g_streaminfobuf[512] = {0};
  50. int g_streaminfobuf_used = 0;
  51. char error_string[128] = {0};
  52. volatile int killDecodeThread = 0; // the kill switch for the decode thread
  53. HANDLE thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread
  54. int has_opened_outmod = 0;
  55. int m_srate = 0; // seek needs this
  56. int decoders_initted = 0;
  57. api_config *AGAVE_API_CONFIG = 0;
  58. api_memmgr *WASABI_API_MEMMGR = 0;
  59. // wasabi based services for localisation support
  60. api_language *WASABI_API_LNG = 0;
  61. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  62. void process_url(char *url)
  63. {
  64. lstrcpynA(stream_url, url, sizeof(stream_url));
  65. // if (!strncmp(stream_url,"hTtP",4))
  66. // {
  67. // DWORD dw;
  68. // SendMessageTimeout(mod.hMainWindow,WM_USER,(WPARAM)0,241,SMTO_NORMAL,500,&dw);
  69. // } // I (Tag) removed this support, its annoying.
  70. DWORD_PTR dw = 0;
  71. if (stream_url[0]) SendMessageTimeout(mod.hMainWindow, WM_USER, (WPARAM)stream_url, 241, SMTO_NORMAL, 500, &dw);
  72. }
  73. char last_title_sent[256] = {0};
  74. void process_metadata(char *data, int len)
  75. {
  76. if (len && *data)
  77. {
  78. char *ld;
  79. int x;
  80. if (len > 4096) return ;
  81. for (x = 0; x < len; x ++)
  82. if (!data[x]) break;
  83. if (x == len) return ;
  84. while ((ld = strstr(data, "='")))
  85. {
  86. char * n = data;
  87. ld[0] = 0;
  88. ld += 2;
  89. data = strstr(ld, "';");
  90. if (data)
  91. {
  92. data[0] = 0;
  93. data += 2;
  94. if (!lstrcmpiA(n, "StreamTitle"))
  95. {
  96. lstrcpynA(last_title_sent, ld, sizeof(last_title_sent));
  97. last_title_sent[sizeof(last_title_sent) - 1] = 0;
  98. PostMessage(mod.hMainWindow, WM_USER, 0, 243);
  99. }
  100. else if (!lstrcmpiA(n, "StreamUrl"))
  101. {
  102. process_url(ld);
  103. }
  104. }
  105. else break;
  106. }
  107. }
  108. }
  109. class WA2AudioOutput : public IAudioOutput
  110. {
  111. public:
  112. WA2AudioOutput(int srate, int nch, int bps)
  113. {
  114. memset(m_stuffbuf, 0, sizeof(m_stuffbuf));
  115. decode_pos_samples = 0;
  116. m_srate = srate; m_bps = bps; m_nch = nch;
  117. m_open_success = 0;
  118. m_stuffbuf_u = 0;
  119. int maxlat = mod.outMod->Open(srate, nch, bps, -1, -1);
  120. if (maxlat == 0 && strstr(lastfn, "://"))
  121. {
  122. maxlat = -1;
  123. mod.outMod->Close(); // boom
  124. }
  125. if (maxlat >= 0)
  126. {
  127. mod.SetInfo( -1, srate / 1000, nch, 1);
  128. mod.SAVSAInit(maxlat, srate);
  129. mod.VSASetInfo(srate, nch);
  130. mod.outMod->SetVolume( -666);
  131. m_open_success = 1;
  132. has_opened_outmod = 1;
  133. }
  134. }
  135. ~WA2AudioOutput(){}
  136. int canwrite()
  137. {
  138. int a = mod.outMod->CanWrite();
  139. if (mod.dsp_isactive() == 1) a /= 2;
  140. return a & ~((m_nch * (m_bps / 8)) - 1);
  141. } // returns bytes writeable
  142. void write(void *buf, int len)
  143. {
  144. char *b = (char *)buf;
  145. int s = 576 * m_nch * (m_bps / 8);
  146. if (s > sizeof(m_stuffbuf)) s = sizeof(m_stuffbuf);
  147. while (len > 0)
  148. {
  149. int l = s;
  150. if (!m_stuffbuf_u && len >= s) // straight copy of data
  151. {
  152. int dms = (int) ((decode_pos_samples * (__int64)1000) / (__int64)m_srate);
  153. mod.SAAddPCMData(b, m_nch, m_bps, dms);
  154. mod.VSAAddPCMData(b, m_nch, m_bps, dms);
  155. }
  156. else if (m_stuffbuf_u + len >= s)
  157. {
  158. int dms = (int) (((decode_pos_samples - (m_stuffbuf_u / m_nch / (m_bps / 8))) * (__int64)1000) / (__int64)m_srate);
  159. l = (s - m_stuffbuf_u);
  160. memcpy(m_stuffbuf + m_stuffbuf_u, b, l);
  161. m_stuffbuf_u = 0;
  162. mod.SAAddPCMData(m_stuffbuf, m_nch, m_bps, dms);
  163. mod.VSAAddPCMData(m_stuffbuf, m_nch, m_bps, dms);
  164. }
  165. else // put all of len into m_stuffbuf
  166. {
  167. memcpy(m_stuffbuf + m_stuffbuf_u, b, len);
  168. m_stuffbuf_u += len;
  169. l = len;
  170. }
  171. if (l > len)l = len; // this shouldn't happen but we'll leave it here just in case
  172. decode_pos_samples += (l / m_nch / (m_bps / 8));
  173. if (mod.dsp_isactive())
  174. {
  175. static char sample_buffer[576*2*(16 / 8)*2];
  176. int spll = l / m_nch / (m_bps / 8);
  177. memcpy(sample_buffer, b, l);
  178. int l2 = l;
  179. if (spll > 0) l2 = mod.dsp_dosamples((short *)sample_buffer, spll, m_bps, m_nch, m_srate) * (m_nch * (m_bps / 8));
  180. mod.outMod->Write(sample_buffer, l2);
  181. }
  182. else mod.outMod->Write(b, l);
  183. len -= l;
  184. b += l;
  185. }
  186. }
  187. ULONGLONG getwritepos()
  188. {
  189. return (unsigned int) ((decode_pos_samples * 1000) / m_srate);
  190. }
  191. ULONGLONG getpos()
  192. {
  193. if (seek_needed != -1) return seek_needed;
  194. return (unsigned int) ((decode_pos_samples * 1000) / m_srate) +
  195. (mod.outMod->GetOutputTime() - mod.outMod->GetWrittenTime()) - config_vidoffs;
  196. }
  197. void flush(unsigned int newtime)
  198. {
  199. m_stuffbuf_u = 0;
  200. mod.outMod->Flush(newtime);
  201. decode_pos_samples = (((__int64)newtime) * m_srate) / 1000;
  202. }
  203. void pause(int pause)
  204. {
  205. mod.outMod->Pause(pause);
  206. }
  207. int get_open_success() { return m_open_success; }
  208. int isplaying(void) { return mod.outMod->IsPlaying(); }
  209. private:
  210. __int64 decode_pos_samples; // current decoding position, in milliseconds.
  211. int m_nch, m_bps;
  212. int m_open_success;
  213. int m_stuffbuf_u;
  214. char m_stuffbuf[576*2*2];
  215. };
  216. IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8])
  217. {
  218. if (outfmt[1] && outfmt[2] && outfmt[3] && outfmt[0] == NSV_MAKETYPE('P', 'C', 'M', ' '))
  219. {
  220. WA2AudioOutput *r = new WA2AudioOutput(outfmt[1], outfmt[2], outfmt[3]);
  221. if (r->get_open_success()) return r;
  222. delete r;
  223. }
  224. return NULL;
  225. }
  226. DWORD WINAPI DecodeThread(LPVOID b); // the decode thread procedure
  227. void about(HWND hwndParent)
  228. {
  229. do_about(hwndParent,WASABI_API_LNG_HINST);
  230. }
  231. void SetFileExtensions(void)
  232. {
  233. static char fileExtensionsString[1200] = {0}; // "NSV;NSA\0Nullsoft Audio/Video File (*.NSV;*.NSA)\0"
  234. char* end = 0;
  235. StringCchCopyExA(fileExtensionsString, 1200, "NSV;NSA", &end, 0, 0);
  236. StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_NSA_NSV_FILE), 0, 0, 0);
  237. mod.FileExtensions = fileExtensionsString;
  238. }
  239. int init()
  240. {
  241. if (!IsWindow(mod.hMainWindow))
  242. return IN_INIT_FAILURE;
  243. waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
  244. if (sf) AGAVE_API_CONFIG = (api_config *)sf->getInterface();
  245. sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);
  246. if (sf) WASABI_API_MEMMGR = reinterpret_cast<api_memmgr*>(sf->getInterface());
  247. // loader so that we can get the localisation service api for use
  248. sf = mod.service->service_getServiceByGuid(languageApiGUID);
  249. if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
  250. // need to have this initialised before we try to do anything with localisation features
  251. WASABI_API_START_LANG(mod.hDllInstance,InNSVLangGUID);
  252. static wchar_t szDescription[256];
  253. StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_NSV_DECODER),PLUGIN_VERSION);
  254. mod.description = (char*)szDescription;
  255. SetFileExtensions();
  256. config_read();
  257. InitializeCriticalSection(&g_decoder_cs);
  258. return IN_INIT_SUCCESS;
  259. }
  260. void quit()
  261. {
  262. Decoders_Quit();
  263. DeleteCriticalSection(&g_decoder_cs);
  264. waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
  265. if (sf) sf->releaseInterface(AGAVE_API_CONFIG);
  266. sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);
  267. if (sf) sf->releaseInterface(WASABI_API_MEMMGR);
  268. }
  269. int isourfile(const char *fn)
  270. {
  271. // used for detecting URL streams.. unused here.
  272. // return !strncmp(fn,"http://",7); to detect HTTP streams, etc
  273. return !_strnicmp( fn, "unsv://", 7 );
  274. }
  275. NSVDecoder *m_decoder = 0;
  276. IVideoOutput *m_video_output = 0;
  277. int g_play_needseek = -1;
  278. int play(const char *fn)
  279. {
  280. m_last_bitrate = -1;
  281. g_play_needseek = -1;
  282. last_title_sent[0] = 0;
  283. g_bufferstat = 0;
  284. mod.is_seekable = 0;
  285. has_opened_outmod = 0;
  286. error_string[0] = 0;
  287. if (!decoders_initted)
  288. {
  289. decoders_initted = 1;
  290. char buf[MAX_PATH] = {0}, *p = buf;
  291. GetModuleFileNameA(mod.hDllInstance, buf, sizeof(buf));
  292. while (p && *p) p++;
  293. while (p && p > buf && *p != '\\') p--;
  294. if (p) *p = 0;
  295. Decoders_Init(buf);
  296. }
  297. unsigned long thread_id = 0;
  298. paused = 0;
  299. seek_needed = -1;
  300. EnterCriticalSection(&g_decoder_cs);
  301. if (strstr(fn, "://"))
  302. WASABI_API_LNGSTRING_BUF(IDS_CONNECTING,error_string,128);
  303. else
  304. WASABI_API_LNGSTRING_BUF(IDS_OPENING,error_string,128);
  305. LeaveCriticalSection(&g_decoder_cs);
  306. m_video_output = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_USER, 0, IPC_GET_IVIDEOOUTPUT);
  307. if (!m_video_output) return 1;
  308. m_video_output->open(0, 0, 0, 0, 0);
  309. m_decoder = new NSVDecoder(fn, m_video_output, NULL);
  310. lstrcpynA(lastfn, fn, sizeof(lastfn));
  311. if (strstr(fn, "://") || !strncmp(fn, "\\\\", 2))
  312. {
  313. m_decoder->SetPreciseSeeking(config_precseek&2);
  314. m_decoder->SetBuffering(config_bufms, config_prebufms, config_underunbuf);
  315. }
  316. else
  317. {
  318. m_decoder->SetPreciseSeeking(config_precseek&1);
  319. m_decoder->SetBuffering(config_bufms_f, config_prebufms_f, config_underunbuf_f);
  320. }
  321. // launch decode thread
  322. killDecodeThread = 0;
  323. thread_handle = (HANDLE)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DecodeThread, NULL, 0, &thread_id);
  324. SetThreadPriority(thread_handle, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
  325. return 0;
  326. }
  327. // standard pause implementation
  328. void pause()
  329. {
  330. paused = 1;
  331. // if (has_opened_outmod) mod.outMod->Pause(1);
  332. // else
  333. if (m_decoder) m_decoder->pause(1);
  334. }
  335. void unpause()
  336. {
  337. paused = 0;
  338. // if (has_opened_outmod) mod.outMod->Pause(0);
  339. //else
  340. if (m_decoder) m_decoder->pause(0);
  341. }
  342. int ispaused() { return paused; }
  343. // stop playing.
  344. void stop()
  345. {
  346. g_play_needseek = -1;
  347. if (thread_handle != INVALID_HANDLE_VALUE)
  348. {
  349. killDecodeThread = 1;
  350. int nTimes = 0;
  351. const int maxTimes = 1000;
  352. while (WaitForSingleObject(thread_handle, 0) == WAIT_TIMEOUT)
  353. {
  354. MSG msg = {0};
  355. if (PeekMessage(&msg, NULL, 0, 0, 1))
  356. {
  357. TranslateMessage(&msg);
  358. DispatchMessage(&msg);
  359. }
  360. else
  361. Sleep(10);
  362. nTimes++;
  363. if (nTimes == maxTimes)
  364. {
  365. #ifdef WINAMPX
  366. SendStatus( WINAMPX_STATUS_ERROR_KILLING_THREAD, 0 );
  367. #else
  368. /*MessageBox(mod.hMainWindow, "error asking thread to die!\n",
  369. "error killing decode thread", 0);*/
  370. #endif
  371. TerminateThread(thread_handle, 0);
  372. break;
  373. }
  374. }
  375. CloseHandle(thread_handle);
  376. thread_handle = INVALID_HANDLE_VALUE;
  377. }
  378. if (has_opened_outmod) mod.outMod->Close();
  379. g_bufferstat = 0;
  380. has_opened_outmod = 0;
  381. mod.SAVSADeInit();
  382. EnterCriticalSection(&g_decoder_cs);
  383. delete(m_decoder);
  384. m_decoder = NULL;
  385. LeaveCriticalSection(&g_decoder_cs);
  386. g_streaminfobuf[0] = 0;
  387. }
  388. int getlength()
  389. {
  390. if (m_decoder)
  391. {
  392. int x = m_decoder->getlen();
  393. if (x != -1) return x;
  394. }
  395. return -1000;
  396. }
  397. int getoutputtime()
  398. {
  399. if (g_bufferstat) return (int)g_bufferstat;
  400. EnterCriticalSection(&g_decoder_cs);
  401. if (m_decoder)
  402. {
  403. LeaveCriticalSection(&g_decoder_cs);
  404. return (int)(m_decoder ? m_decoder->getpos() + config_vidoffs : 0);
  405. }
  406. LeaveCriticalSection(&g_decoder_cs);
  407. return 0;
  408. }
  409. void setoutputtime(int time_in_ms)
  410. {
  411. seek_needed = time_in_ms;
  412. }
  413. void setvolume(int volume) { mod.outMod->SetVolume(volume); }
  414. void setpan(int pan) { mod.outMod->SetPan(pan); }
  415. int infoDlg(const char *fn, HWND hwnd);
  416. // this is an odd function. it is used to get the title and/or
  417. // length of a track.
  418. // if filename is either NULL or of length 0, it means you should
  419. // return the info of lastfn. Otherwise, return the information
  420. // for the file in filename.
  421. // if title is NULL, no title is copied into it.
  422. // if length_in_ms is NULL, no length is copied into it.
  423. void getfileinfo(const char *filename, char *title, int *length_in_ms)
  424. {
  425. if (!filename || !*filename) // currently playing file
  426. {
  427. EnterCriticalSection(&g_decoder_cs);
  428. if (length_in_ms) *length_in_ms = getlength();
  429. if (title) // get non-path portion.of filename
  430. {
  431. char *p = NULL;
  432. if (m_decoder)
  433. {
  434. p = m_decoder->getTitle();
  435. }
  436. if (!p)
  437. {
  438. p = lastfn + strlen(lastfn);
  439. while (p && *p != '\\' && p >= lastfn) p--;
  440. p++;
  441. }
  442. while (p && *p == ';') p++;
  443. title[0] = 0;
  444. if (error_string[0])
  445. {
  446. StringCchPrintfA(title, GETFILEINFO_TITLE_LENGTH, "[%s] ", error_string);
  447. }
  448. if (!error_string[0] && last_title_sent[0])
  449. {
  450. StringCchPrintfA(title, GETFILEINFO_TITLE_LENGTH-strlen(title), "%s (%s)", last_title_sent, p);
  451. }
  452. else
  453. lstrcpynA(title + strlen(title), p, FILETITLE_SIZE);
  454. }
  455. LeaveCriticalSection(&g_decoder_cs);
  456. }
  457. else if (1) // some other file
  458. {
  459. if ((length_in_ms || title) && !strstr(filename, "://"))
  460. {
  461. nsv_InBS bs;
  462. nsv_fileHeader hdr = {0, };
  463. if (length_in_ms) // calculate length
  464. {
  465. *length_in_ms = -1000; // the default is unknown file length (-1000).
  466. }
  467. if (title) // get non path portion of filename
  468. {
  469. const char *p = filename + strlen(filename);
  470. while (p && *p != '\\' && p >= filename) p--;
  471. lstrcpynA(title, ++p, GETFILEINFO_TITLE_LENGTH);
  472. }
  473. IDataReader *rdr = CreateReader(filename);
  474. if (rdr)
  475. {
  476. while (!rdr->iseof())
  477. {
  478. char buf[1024] = {0};
  479. int l = (int)rdr->read(buf, sizeof(buf));
  480. if (!l) break;
  481. bs.add(buf, l);
  482. l = nsv_readheader(bs, &hdr);
  483. if (l <= 0)
  484. {
  485. if (!l)
  486. {
  487. if (length_in_ms) *length_in_ms = hdr.file_lenms;
  488. if (title && hdr.metadata)
  489. {
  490. char *t = nsv_getmetadata(hdr.metadata, "TITLE");
  491. if (t) lstrcpynA(title, t, 1024);
  492. }
  493. }
  494. free(hdr.metadata);
  495. free(hdr.toc);
  496. break;
  497. }
  498. }
  499. delete rdr;
  500. }
  501. // try to parse out lengths
  502. }
  503. }
  504. }
  505. void eq_set(int on, char data[10], int preamp)
  506. {}
  507. DWORD WINAPI DecodeThread(LPVOID b)
  508. {
  509. int last_bpos = -1;
  510. int firstsynch = 0;
  511. ULONGLONG next_status_time = 0;
  512. while (!killDecodeThread)
  513. {
  514. EnterCriticalSection(&g_decoder_cs);
  515. int r = m_decoder->run((int*)&killDecodeThread);
  516. LeaveCriticalSection(&g_decoder_cs);
  517. if (r < 0)
  518. {
  519. if (m_decoder->get_error())
  520. {
  521. EnterCriticalSection(&g_decoder_cs);
  522. lstrcpynA(error_string, m_decoder->get_error(), sizeof(error_string));
  523. LeaveCriticalSection(&g_decoder_cs);
  524. PostMessage(mod.hMainWindow, WM_USER, 0, 243);
  525. Sleep(200);
  526. }
  527. break;
  528. }
  529. else if (!r)
  530. {
  531. Sleep(1);
  532. int br = m_decoder->getBitrate() / 1000;
  533. if (br != m_last_bitrate)
  534. {
  535. m_last_bitrate = br;
  536. mod.SetInfo(br, -1, -1, -1);
  537. }
  538. int bpos = m_decoder->getBufferPos();
  539. if (bpos > 255)
  540. {
  541. ULONGLONG obuf = g_bufferstat;
  542. g_bufferstat = 0;
  543. if (last_bpos >= 0)
  544. {
  545. EnterCriticalSection(&g_decoder_cs);
  546. error_string[0] = 0;
  547. LeaveCriticalSection(&g_decoder_cs);
  548. PostMessage(mod.hMainWindow, WM_USER, 0, 243);
  549. last_bpos = -1;
  550. int csa = mod.SAGetMode();
  551. if (csa && obuf)
  552. {
  553. char tempdata[75*2] = {0, };
  554. mod.SAAdd(tempdata, (int)++obuf, (csa == 3) ? 0x80000003 : csa);
  555. }
  556. }
  557. }
  558. else
  559. {
  560. if (!g_bufferstat)
  561. {
  562. if (!has_opened_outmod) mod.SAVSAInit(10, 44100);
  563. g_bufferstat = m_decoder->getpos() + 1;
  564. }
  565. if (bpos != last_bpos)
  566. {
  567. last_bpos = bpos;
  568. EnterCriticalSection(&g_decoder_cs);
  569. StringCchPrintfA(error_string, 128, WASABI_API_LNGSTRING(IDS_BUFFER_X), (bpos*100) / 256);
  570. LeaveCriticalSection(&g_decoder_cs);
  571. int csa = mod.SAGetMode();
  572. char tempdata[2*75] = {0, };
  573. int x;
  574. if (csa&1)
  575. {
  576. for (x = 0; x < bpos*75 / 256; x ++)
  577. {
  578. tempdata[x] = x * 16 / 75;
  579. }
  580. }
  581. if (csa&2)
  582. {
  583. int offs = (csa & 1) ? 75 : 0;
  584. x = 0;
  585. while (x < bpos*75 / 256)
  586. {
  587. tempdata[offs + x++] = -6 + x * 14 / 75;
  588. }
  589. while (x < 75)
  590. {
  591. tempdata[offs + x++] = 0;
  592. }
  593. }
  594. if (csa == 4)
  595. {
  596. tempdata[0] = tempdata[1] = (bpos * 127 / 256);
  597. }
  598. if (csa) mod.SAAdd(tempdata, (int)++g_bufferstat, (csa == 3) ? 0x80000003 : csa);
  599. PostMessage(mod.hMainWindow, WM_USER, 0, 243);
  600. }
  601. }
  602. if (GetTickCount64() > next_status_time || GetTickCount64() < next_status_time - 5000)
  603. {
  604. char statusbuf[1024] = {0};
  605. EnterCriticalSection(&g_decoder_cs);
  606. g_streaminfobuf[0] = 0;
  607. if (g_streaminfobuf_used)
  608. {
  609. char *outp = g_streaminfobuf;
  610. size_t size = 512;
  611. const char *p = m_decoder->getServerHeader("server");
  612. if (!p) p = m_decoder->getServerHeader("icy-notice2");
  613. if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
  614. StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_SERVER), p);
  615. p = m_decoder->getServerHeader("content-type");
  616. if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
  617. StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_CONTENT_TYPE), p);
  618. p = m_decoder->getServerHeader("content-length");
  619. if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
  620. StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_CONTENT_LENGTH), p);
  621. p = m_decoder->getServerHeader("icy-name");
  622. if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
  623. StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_STREAM_NAME), p);
  624. }
  625. lstrcpynA(statusbuf, "NSV: ", 1024);
  626. { // codecs
  627. char *sb = statusbuf;
  628. size_t size = 1024;
  629. int l = m_decoder->getlen();
  630. if (l > 0)
  631. {
  632. l /= 1000;
  633. if (l >= 3600)
  634. {
  635. StringCchPrintfExA(sb, size, &sb, &size, 0, "%d:", l / 3600);
  636. l %= 3600;
  637. StringCchPrintfExA(sb, size, &sb, &size, 0, "%02d:%02d", l / 60, l % 60);
  638. }
  639. else
  640. StringCchPrintfExA(sb, size, &sb, &size, 0, "%d:%02d", l / 60, l % 60);
  641. }
  642. int a = (m_decoder->getBitrate() + 500) / 1000;
  643. if (a)
  644. {
  645. if (strlen(statusbuf) > 5)
  646. StringCchCatExA(sb, size, " @ ", &sb, &size, 0);
  647. StringCchPrintfExA(sb, size, &sb, &size, 0, "%d%s", a, WASABI_API_LNGSTRING(IDS_KBPS));
  648. }
  649. if (strlen(statusbuf) > 5)
  650. StringCchCatExA(sb, size, ", ", &sb, &size, 0);
  651. char *p = statusbuf + strlen(statusbuf);
  652. m_decoder->getVideoDesc(p);
  653. if (p && !*p) StringCchCopyExA(p, size, "?, ", &p, &size, 0);
  654. else if (!strncmp(p, "NONE", 4)) *p = 0;
  655. else StringCchCatExA(p, size, ", ", &p, &size, 0);
  656. p = statusbuf + strlen(statusbuf);
  657. m_decoder->getAudioDesc(p);
  658. if (p && !*p) StringCchCopyExA(p, size, "?", &p, &size, 0);
  659. else if (!strncmp(p, "NONE", 4)) *p = 0;
  660. }
  661. LeaveCriticalSection(&g_decoder_cs);
  662. m_video_output->extended(VIDUSER_SET_INFOSTRING, (intptr_t)statusbuf, 0);
  663. next_status_time = GetTickCount64() + 2500;
  664. }
  665. }
  666. else
  667. {
  668. if (!firstsynch)
  669. {
  670. if (m_decoder->canseek())
  671. {
  672. mod.is_seekable = 1;
  673. if (g_play_needseek >= 0) seek_needed = g_play_needseek;
  674. g_play_needseek = -1;
  675. }
  676. firstsynch++;
  677. PostMessage(mod.hMainWindow, WM_USER, 0, 243);
  678. }
  679. }
  680. if (seek_needed >= 0)
  681. {
  682. EnterCriticalSection(&g_decoder_cs);
  683. m_decoder->seek(seek_needed);
  684. seek_needed = -1;
  685. LeaveCriticalSection(&g_decoder_cs);
  686. }
  687. }
  688. if (!killDecodeThread)
  689. {
  690. PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  691. }
  692. m_decoder->CloseVideo();
  693. return 0;
  694. }
  695. // module definition.
  696. In_Module mod =
  697. {
  698. IN_VER_RET, // defined in IN2.H
  699. "nullsoft(in_nsv.dll)",
  700. 0, // hMainWindow (filled in by winamp)
  701. 0, // hDllInstance (filled in by winamp)
  702. 0, // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
  703. 1, // is_seekable
  704. 1, // uses output plug-in system
  705. config,
  706. about,
  707. init,
  708. quit,
  709. getfileinfo,
  710. infoDlg,
  711. isourfile,
  712. play,
  713. pause,
  714. unpause,
  715. ispaused,
  716. stop,
  717. getlength,
  718. getoutputtime,
  719. setoutputtime,
  720. setvolume,
  721. setpan,
  722. 0, 0, 0, 0, 0, 0, 0, 0, 0, // visualization calls filled in by winamp
  723. 0, 0, // dsp calls filled in by winamp
  724. eq_set,
  725. NULL, // setinfo call filled in by winamp
  726. 0 // out_mod filled in by winamp
  727. };
  728. static FILETIME ftLastWriteTime;
  729. // is used to determine if the last write time of the file has changed when
  730. // asked to get the metadata for the same cached file so we can update things
  731. BOOL HasFileTimeChanged(const wchar_t *fn)
  732. {
  733. WIN32_FILE_ATTRIBUTE_DATA fileData = {0};
  734. if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
  735. {
  736. if(CompareFileTime(&ftLastWriteTime, &fileData.ftLastWriteTime))
  737. {
  738. ftLastWriteTime = fileData.ftLastWriteTime;
  739. return TRUE;
  740. }
  741. }
  742. return FALSE;
  743. }
  744. extern "C"
  745. {
  746. __declspec( dllexport ) In_Module * winampGetInModule2()
  747. {
  748. return &mod;
  749. }
  750. wchar_t lastextfn[1024] = {0};
  751. // Keep track of file timestamp for file system change notification handling
  752. FILETIME last_write_time = {0, 0};
  753. static int valid;
  754. static nsv_fileHeader hdr;
  755. bool isFileChanged(const wchar_t* file)
  756. {
  757. WIN32_FIND_DATAW FindFileData = {0};
  758. HANDLE hFind = FindFirstFileW(file, &FindFileData);
  759. if (hFind == INVALID_HANDLE_VALUE)
  760. return true;
  761. FindClose(hFind);
  762. if ((last_write_time.dwHighDateTime ==
  763. FindFileData.ftLastWriteTime.dwHighDateTime) &&
  764. (last_write_time.dwLowDateTime ==
  765. FindFileData.ftLastWriteTime.dwLowDateTime))
  766. {
  767. return false;
  768. }
  769. return true;
  770. }
  771. __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
  772. {
  773. if (!_stricmp(data, "type"))
  774. {
  775. if (!fn || !fn[0] || _wcsicmp(PathFindExtensionW(fn), L".nsa")) // if extension is NOT nsa
  776. lstrcpyn(dest, L"1", destlen); //video
  777. else
  778. lstrcpyn(dest, L"0", destlen); // audio
  779. return 1;
  780. }
  781. if (!fn || (fn && !fn[0]))
  782. return 0;
  783. if (!_stricmp(data, "family"))
  784. {
  785. int pID = -1;
  786. LPCWSTR e = PathFindExtensionW(fn);
  787. if (L'.' != *e) return 0;
  788. e++;
  789. DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
  790. if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"NSV", -1)) pID = IDS_FAMILY_STRING_NSV;
  791. if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"NSA", -1)) pID = IDS_FAMILY_STRING_NSA;
  792. if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1;
  793. return 0;
  794. }
  795. //the file name differs from the last file
  796. //name but we need to check the time stamp too
  797. if (_wcsicmp(fn, lastextfn) || isFileChanged(fn) || HasFileTimeChanged(fn))
  798. {
  799. free(hdr.metadata);
  800. memset(&hdr, 0, sizeof(hdr));
  801. valid = 0;
  802. lstrcpyn(lastextfn, fn, ARRAYSIZE(lastextfn));
  803. nsv_InBS bs;
  804. IDataReader *rdr = CreateReader(AutoCharFn(lastextfn));
  805. if (rdr)
  806. {
  807. while (!rdr->iseof())
  808. {
  809. char buf[1024] = {0};
  810. int l = (int)rdr->read(buf, sizeof(buf));
  811. if (!l) break;
  812. bs.add(buf, l);
  813. l = nsv_readheader(bs, &hdr);
  814. if (l <= 0)
  815. {
  816. free(hdr.toc);
  817. if (!l)
  818. {
  819. valid = 1;
  820. //Save time stamp
  821. WIN32_FIND_DATAW FindFileData = {0};
  822. HANDLE hFind = FindFirstFileW(fn, &FindFileData);
  823. if (hFind == INVALID_HANDLE_VALUE)
  824. {
  825. last_write_time.dwHighDateTime = NULL;
  826. last_write_time.dwLowDateTime = NULL;
  827. }
  828. else
  829. {
  830. last_write_time.dwHighDateTime = FindFileData.ftLastWriteTime.dwHighDateTime;
  831. last_write_time.dwLowDateTime = FindFileData.ftLastWriteTime.dwLowDateTime;
  832. FindClose(hFind);
  833. }
  834. break;
  835. }
  836. break;
  837. }
  838. }
  839. delete rdr;
  840. }
  841. }
  842. dest[0] = 0;
  843. if (!valid)
  844. {
  845. return 0;
  846. }
  847. if (!_stricmp(data, "length"))
  848. {
  849. if (hdr.file_lenms > 0)
  850. {
  851. StringCchPrintfW(dest, destlen, L"%d", hdr.file_lenms);
  852. }
  853. }
  854. else if (hdr.metadata)
  855. {
  856. const char *t = nsv_getmetadata(hdr.metadata, (char*)data);
  857. if (t) lstrcpyn(dest, AutoWide(t), destlen);
  858. }
  859. return 1;
  860. }
  861. }