dshowrender.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. //
  2. // This file contains code that supports an alternate method playing dshow data...
  3. // This will have dshow take control of rendering the data (audio or video).
  4. // Used for mms streams
  5. //
  6. #ifdef WINAMPX
  7. #include <windows.h>
  8. #include <math.h>
  9. #include "message.h"
  10. #include "../jnetlib/jnetlib.h"
  11. #include "in2.h"
  12. #include <AtlBase.h>
  13. #include <streams.h>
  14. #include <qedit.h>
  15. #include <qnetwork.h>
  16. #ifdef DEFGUID
  17. #include <initguid.h> // declares DEFINE_GUID to declare an EXTERN_C const.
  18. #endif
  19. #define IPC_GET_IVIDEOOUTPUT 500
  20. // Externs
  21. extern HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister);
  22. extern void RemoveFromRot(DWORD pdwRegister);
  23. extern bool ReportMissingCodec(char *fn);
  24. extern In_Module mod; // the output module (filled in near the bottom of this file)
  25. extern char lastfn[MAX_PATH]; // currently playing file (used for getting info on the current file)
  26. extern char lastfn_status[256];
  27. extern int file_length; // file length, in bytes
  28. extern int decode_pos_ms; // current decoding position, in milliseconds.
  29. extern int paused; // are we paused?
  30. extern volatile int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
  31. extern int m_length;
  32. extern int paused; // are we paused?
  33. extern bool s_using_dsr;
  34. // Static Vars and Defines
  35. class IVideoOutput
  36. {
  37. public:
  38. virtual ~IVideoOutput() { }
  39. virtual int open(int w, int h, int vflip, double aspectratio, unsigned int fmt)=0;
  40. virtual void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { }
  41. virtual void close()=0;
  42. virtual void draw(void *frame)=0;
  43. virtual void drawSubtitle(/*SubsItem **/ void *item) { }
  44. virtual void showStatusMsg(const char *text) { }
  45. virtual int get_latency() { return 0; }
  46. virtual void notifyBufferState(int bufferstate) { } /* 0-255*/
  47. virtual int extended(int param1, int param2, int param3) { return 0; } // Dispatchable, eat this!
  48. };
  49. static IVideoOutput * m_video_output;
  50. static CComPtr<IGraphBuilder> s_pGraphBuilder;
  51. static CComPtr<IMediaEventEx> s_pMediaEventEx;
  52. static CComPtr<IMediaControl> s_pMediaControl;
  53. static CComPtr<IVideoWindow> s_pVideoWindow;
  54. static CComPtr<IBasicAudio> s_pBasicAudio;
  55. static CComPtr<IBasicVideo> s_pBasicVideo;
  56. static CComPtr<IMediaSeeking> s_pMediaSeeking;
  57. static HWND s_dsr_notif_hwnd = NULL;
  58. static HWND s_hVideoWnd = NULL;
  59. static WNDPROC s_OriginalVideoWndProc = NULL;
  60. static RECT s_parentRect;
  61. static DWORD GraphEdit_dwRegister = 0;
  62. static int s_buffering = 0;
  63. static int s_bufferstat = 0;
  64. static BOOL s_bAudioOnly;
  65. static int s_setVolumeOnStart = -1;
  66. // Forward Declarations
  67. LRESULT CALLBACK dsr_TimerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  68. LRESULT CALLBACK dsr_SubclassParentWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  69. void dsr_setvolume(int volume);
  70. void dsr_handleNotifyEvents();
  71. void dsr_stop();
  72. // Macros
  73. #define BeginEnumFilters(pFilterGraph, pEnumFilters, pBaseFilter) \
  74. {CComPtr<IEnumFilters> pEnumFilters; \
  75. if(pFilterGraph && SUCCEEDED(pFilterGraph->EnumFilters(&pEnumFilters))) \
  76. { \
  77. for(CComPtr<IBaseFilter> pBaseFilter; S_OK == pEnumFilters->Next(1, &pBaseFilter, 0); pBaseFilter = NULL) \
  78. { \
  79. #define EndEnumFilters }}}
  80. static void SendStatus(int status, int arg ) {
  81. mod.fire_winampstatus( status, arg );
  82. }
  83. //------------------------------------------------------------------------------
  84. // Name: CheckVisibility()
  85. // Desc: Set global values for presence of video window.
  86. //------------------------------------------------------------------------------
  87. bool CheckVisibility(void) // returns false for failure
  88. {
  89. HRESULT hr;
  90. if ((!s_pVideoWindow) || (!s_pBasicVideo))
  91. {
  92. s_bAudioOnly = TRUE; // Audio-only files have no video interfaces.
  93. return TRUE;
  94. }
  95. s_bAudioOnly = FALSE;
  96. long lVisible;
  97. hr = s_pVideoWindow->get_Visible(&lVisible); // If this is an audio-only clip, get_Visible() won't work.
  98. if ((FAILED(hr)) && (hr == E_NOINTERFACE))
  99. {
  100. s_bAudioOnly = TRUE;
  101. return TRUE;
  102. }
  103. return !FAILED(hr);
  104. }
  105. static RECT fitMediaToWindow(RECT rectWindow, int mediaWidth, int mediaHeight)
  106. {
  107. RECT retval;
  108. int windowWidth = rectWindow.right - rectWindow.left;
  109. int windowHeight = rectWindow.bottom - rectWindow.top;
  110. if (mediaHeight*windowWidth > mediaWidth*windowHeight)
  111. {
  112. // Gap is on left&right sides
  113. int nOutWidth = windowHeight* mediaWidth / mediaHeight;
  114. int nGap = (windowWidth - nOutWidth)/2;
  115. retval.top = rectWindow.top;
  116. retval.bottom = retval.top + windowHeight;
  117. retval.left = rectWindow.left + nGap;
  118. retval.right = retval.left + nOutWidth;
  119. }
  120. else
  121. {
  122. // Gap is on the top/bottom sides
  123. int nOutHeight = windowWidth* mediaHeight / mediaWidth;
  124. int nGap = (windowHeight - nOutHeight)/2;
  125. retval.left = rectWindow.left;
  126. retval.right = retval.left + windowWidth;
  127. retval.top = rectWindow.top + nGap;
  128. retval.bottom = retval.top + nOutHeight;
  129. }
  130. return retval;
  131. }
  132. void dsr_releaseObjects()
  133. {
  134. if(s_dsr_notif_hwnd)
  135. {
  136. KillTimer(s_dsr_notif_hwnd,112);
  137. DestroyWindow(s_dsr_notif_hwnd);
  138. }
  139. s_dsr_notif_hwnd=NULL;
  140. if ((s_OriginalVideoWndProc) && (s_hVideoWnd))
  141. {
  142. SetWindowLong(s_hVideoWnd, GWL_WNDPROC, (LONG_PTR)s_OriginalVideoWndProc);
  143. s_OriginalVideoWndProc = NULL;
  144. s_hVideoWnd = NULL;
  145. }
  146. s_pMediaEventEx = NULL;
  147. s_pGraphBuilder = NULL;
  148. s_pMediaControl = NULL;
  149. s_pVideoWindow = NULL;
  150. s_pBasicAudio = NULL;
  151. s_pBasicVideo = NULL;
  152. s_pMediaSeeking = NULL;
  153. s_using_dsr = false;
  154. }
  155. // Prefix used for functions here are dsd_ (
  156. int dsr_play(char *fn)
  157. {
  158. HRESULT hr;
  159. hr = s_pGraphBuilder.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER);
  160. if (FAILED(hr)) return 1;
  161. #ifdef _DEBUG
  162. AddToRot(s_pGraphBuilder, &GraphEdit_dwRegister);
  163. #endif
  164. s_pGraphBuilder->QueryInterface(IID_IMediaEvent, (void **)&s_pMediaEventEx);
  165. if(!s_pMediaEventEx)
  166. {
  167. dsr_releaseObjects();
  168. return 1;
  169. }
  170. m_video_output=(IVideoOutput *)SendMessage(mod.hMainWindow,WM_USER,0,IPC_GET_IVIDEOOUTPUT);
  171. if(!m_video_output)
  172. {
  173. dsr_releaseObjects();
  174. return -200; // Can't play file
  175. }
  176. // Create window that will receive filter notifications
  177. static int classReg=0;
  178. if(!classReg)
  179. {
  180. WNDCLASS wc={0,};
  181. wc.style = CS_DBLCLKS;
  182. wc.lpfnWndProc = dsr_TimerWndProc;
  183. wc.hInstance = mod.hDllInstance;
  184. wc.hIcon = NULL;
  185. wc.hCursor = NULL;
  186. wc.lpszClassName = "in_dshowClass2";
  187. if (!RegisterClass(&wc))
  188. {
  189. dsr_releaseObjects();
  190. return 1;
  191. }
  192. classReg=1;
  193. }
  194. s_dsr_notif_hwnd=CreateWindow("in_dshowClass2","dshow_notif2",NULL,0,0,1,1,NULL,NULL,mod.hDllInstance,NULL);
  195. SetTimer(s_dsr_notif_hwnd,112,500,0);
  196. // Build a normal graph (start rendering)
  197. WCHAR f[4096];
  198. MultiByteToWideChar(CP_ACP,0,fn,lstrlen(fn)+1,f,4096);
  199. hr = s_pGraphBuilder->RenderFile(f,NULL);
  200. if (FAILED(hr)) {
  201. dsr_handleNotifyEvents();
  202. dsr_releaseObjects();
  203. if ((hr == CLASS_E_CLASSNOTAVAILABLE) || (hr == VFW_E_UNSUPPORTED_VIDEO) || (hr == VFW_E_NO_DECOMPRESSOR))
  204. {
  205. if (ReportMissingCodec(fn)) // returns true if we sent a message
  206. return -500; // Unsupported format
  207. return -200; // Can't play file
  208. }
  209. return 1;
  210. }
  211. // Check if it's a partial playing of the file (likely video codec missing)
  212. if ((hr == VFW_S_PARTIAL_RENDER) || (hr == VFW_S_VIDEO_NOT_RENDERED))
  213. {
  214. if (!ReportMissingCodec(fn)) // Report the missing codec if we can determine it
  215. mod.fire_winampstatus(WINAMPX_STATUS_MISSING_AVI_CODEC, 0); // If we can't report a null codec missing
  216. }
  217. if (FAILED(s_pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&s_pMediaControl)) ||
  218. FAILED(s_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&s_pMediaSeeking)) ||
  219. FAILED(s_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME)))
  220. {
  221. dsr_releaseObjects();
  222. return 1;
  223. }
  224. // Get length of file
  225. int mylength = -1;
  226. LONGLONG length;
  227. if (SUCCEEDED(s_pMediaSeeking->GetDuration(&length)))
  228. {
  229. mylength=(int)(length/10000);
  230. }
  231. m_length = mylength;
  232. // Query for video interfaces, which may not be relevant for audio files
  233. s_pGraphBuilder->QueryInterface(IID_IVideoWindow, (void **)&s_pVideoWindow);
  234. s_pGraphBuilder->QueryInterface(IID_IBasicVideo, (void **)&s_pBasicVideo);
  235. // Query for audio interfaces, which may not be relevant for video-only files
  236. s_pGraphBuilder->QueryInterface(IID_IBasicAudio, (void **)&s_pBasicAudio);
  237. if (s_setVolumeOnStart != -1)
  238. {
  239. dsr_setvolume(s_setVolumeOnStart);
  240. s_setVolumeOnStart = -1;
  241. }
  242. // Is this an audio-only file (no video component)?
  243. CheckVisibility();
  244. if (!s_bAudioOnly)
  245. {
  246. m_video_output->open(1, 1, 0, 1.0f, VIDEO_MAKETYPE('N','O','N','E')); // Dummy Size of 1x1
  247. s_hVideoWnd = (HWND)m_video_output->extended(VIDUSER_GET_VIDEOHWND, 0, 0);
  248. InvalidateRect(s_hVideoWnd, NULL, TRUE);
  249. // Setup the video window
  250. s_pVideoWindow->put_Owner((OAHWND) s_hVideoWnd);
  251. s_pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
  252. RECT grc;
  253. GetClientRect(s_hVideoWnd, &grc);
  254. s_parentRect = grc;
  255. if ((s_pBasicVideo) && (s_pVideoWindow))
  256. {
  257. long videoWidth, videoHeight;
  258. if (SUCCEEDED(s_pBasicVideo->GetVideoSize(&videoWidth, &videoHeight)))
  259. {
  260. RECT r = fitMediaToWindow(grc, videoWidth, videoHeight);
  261. s_pVideoWindow->SetWindowPosition(r.left, r.top, r.right-r.left, r.bottom-r.top);
  262. }
  263. }
  264. // Intercept resize messages
  265. s_OriginalVideoWndProc = (WNDPROC) SetWindowLong(s_hVideoWnd, GWL_WNDPROC, (LONG_PTR)dsr_SubclassParentWndProc);
  266. }
  267. // Run the graph to play the media file
  268. hr = s_pMediaControl->Run();
  269. if (FAILED(hr))
  270. {
  271. dsr_stop();
  272. return 1;
  273. }
  274. return 0;
  275. }
  276. // stop playing.
  277. void dsr_stop()
  278. {
  279. if (s_pMediaControl)
  280. s_pMediaControl->Stop();
  281. if (s_pVideoWindow)
  282. {
  283. s_pVideoWindow->put_Visible(OAFALSE);
  284. s_pVideoWindow->put_Owner(NULL);
  285. }
  286. if (m_video_output)
  287. m_video_output->close();
  288. mod.outMod->Close();
  289. mod.SAVSADeInit();
  290. dsr_releaseObjects();
  291. m_length=-1;
  292. lastfn[0]=0;
  293. }
  294. // Standard Pause Routines
  295. void dsr_pause() {
  296. paused=1;
  297. if (s_pMediaControl)
  298. {
  299. s_pMediaControl->Pause();
  300. }
  301. }
  302. void dsr_unpause() {
  303. paused=0;
  304. if (s_pMediaControl)
  305. {
  306. s_pMediaControl->Run();
  307. }
  308. }
  309. int dsr_getoutputtime() {
  310. if (s_bufferstat)
  311. return s_bufferstat;
  312. if (s_pMediaSeeking)
  313. {
  314. LONGLONG pos;
  315. if (SUCCEEDED(s_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME)) &&
  316. SUCCEEDED(s_pMediaSeeking->GetCurrentPosition(&pos)))
  317. {
  318. return (int)(pos/10000);
  319. }
  320. }
  321. return 0;
  322. }
  323. void dsr_setoutputtime(int time_in_ms) {
  324. if(s_pMediaSeeking) {
  325. DWORD dwCaps = AM_SEEKING_CanSeekAbsolute;
  326. if (s_pMediaSeeking->CheckCapabilities(&dwCaps) == S_OK)
  327. {
  328. int oldpause=paused;
  329. if(oldpause) dsr_unpause();
  330. LONGLONG l=((LONGLONG)time_in_ms)*10000;
  331. s_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
  332. s_pMediaSeeking->SetPositions(&l,AM_SEEKING_AbsolutePositioning,NULL,AM_SEEKING_NoPositioning);
  333. mod.outMod->Flush(time_in_ms);
  334. if(oldpause) dsr_pause();
  335. }
  336. }
  337. }
  338. void dsr_setvolume(int volume)
  339. {
  340. if (s_pBasicAudio)
  341. {
  342. volume = min(volume, 255);
  343. volume = max(volume, 0);
  344. s_pBasicAudio->put_Volume(log(volume+1)/log(256)*10000-10000); // Map (0,255) to (-10000,0) log scale (as ActiveX is in DB's)
  345. }
  346. else
  347. {
  348. s_setVolumeOnStart = volume;
  349. }
  350. }
  351. LRESULT CALLBACK dsr_TimerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  352. {
  353. if(uMsg==WM_TIMER && wParam==112 && s_dsr_notif_hwnd)
  354. {
  355. dsr_handleNotifyEvents();
  356. if(s_buffering)
  357. {
  358. BeginEnumFilters(s_pGraphBuilder, pEF, pBF)
  359. {
  360. if(CComQIPtr<IAMNetworkStatus, &IID_IAMNetworkStatus> pAMNS = pBF)
  361. {
  362. long BufferingProgress = 0;
  363. if(SUCCEEDED(pAMNS->get_BufferingProgress(&BufferingProgress)) && BufferingProgress > 0)
  364. {
  365. wsprintf(lastfn_status,"Buffering (%ld%%)",BufferingProgress);
  366. if(m_video_output) m_video_output->extended(VIDUSER_SET_INFOSTRING,(int)&lastfn_status,0);
  367. int bpos=BufferingProgress;
  368. int csa = mod.SAGetMode();
  369. char tempdata[75*2]={0,};
  370. int x;
  371. if (csa&1)
  372. {
  373. for (x = 0; x < bpos*75/100; x ++)
  374. {
  375. tempdata[x]=x*16/75;
  376. }
  377. }
  378. if (csa&2)
  379. {
  380. int offs=(csa&1) ? 75 : 0;
  381. x=0;
  382. while (x < bpos*75/100)
  383. {
  384. tempdata[offs + x++]=-6+x*14/75;
  385. }
  386. while (x < 75)
  387. {
  388. tempdata[offs + x++]=0;
  389. }
  390. }
  391. if (csa==4)
  392. {
  393. tempdata[0]=tempdata[1]=(bpos*127/100);
  394. }
  395. if (csa) mod.SAAdd(tempdata,++s_bufferstat,(csa==3)?0x80000003:csa);
  396. PostMessage(mod.hMainWindow,WM_USER,0,243);
  397. SendStatus(WINAMPX_STATUS_PREBUFFERING_PCT, 255*BufferingProgress/100);
  398. break;
  399. }
  400. }
  401. }
  402. EndEnumFilters
  403. }
  404. }
  405. return (DefWindowProc(hwnd, uMsg, wParam, lParam));
  406. }
  407. void dsr_handleNotifyEvents()
  408. {
  409. // char s[256];
  410. for (;;) {
  411. long evCode, param1, param2;
  412. HRESULT h = s_pMediaEventEx->GetEvent(&evCode, &param1, &param2, 0);
  413. if (FAILED(h)) break;
  414. switch(evCode) {
  415. case EC_BUFFERING_DATA:
  416. {
  417. // sprintf(s, "Handling Event: EC_BUFFERING_DATA: %d\n", param1);
  418. // OutputDebugString(s);
  419. s_buffering=param1;
  420. if(!s_buffering)
  421. {
  422. lastfn_status[0]=0;
  423. s_bufferstat=0;
  424. PostMessage(mod.hMainWindow,WM_USER,0,243);
  425. break;
  426. }
  427. }
  428. break;
  429. case EC_COMPLETE:
  430. {
  431. // OutputDebugString("Handling Event: EC_COMPLETE\n");
  432. PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
  433. }
  434. default:
  435. {
  436. // sprintf(s, "Handling Event: 0x%x : param1=0x%x; param2 = 0x%x\n", evCode, param1, param2);
  437. // OutputDebugString(s);
  438. }
  439. }
  440. s_pMediaEventEx->FreeEventParams(evCode, param1, param2);
  441. }
  442. }
  443. // Subclass of the Window containing the code. (Used to check if the parent got resized)
  444. LRESULT CALLBACK dsr_SubclassParentWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  445. {
  446. LRESULT retval = CallWindowProc(s_OriginalVideoWndProc, hwnd, uMsg, wParam, lParam);
  447. if ((uMsg == WM_SIZE) && (s_hVideoWnd))
  448. {
  449. RECT grc;
  450. GetClientRect(s_hVideoWnd, &grc);
  451. if (!EqualRect(&grc, &s_parentRect))
  452. {
  453. s_parentRect = grc;
  454. if ((s_pBasicVideo) && (s_pVideoWindow))
  455. {
  456. long videoWidth, videoHeight;
  457. if (SUCCEEDED(s_pBasicVideo->GetVideoSize(&videoWidth, &videoHeight)))
  458. {
  459. RECT r = fitMediaToWindow(grc, videoWidth, videoHeight);
  460. s_pVideoWindow->SetWindowPosition(r.left, r.top, r.right-r.left, r.bottom-r.top);
  461. }
  462. }
  463. }
  464. }
  465. if ((uMsg == WM_PARENTNOTIFY) && (LOWORD(wParam)==WM_RBUTTONDOWN))
  466. {
  467. SendStatus(WINAMPX_STATUS_VIDEO_RIGHT_CLICK, lParam);
  468. }
  469. if ((uMsg == WM_PARENTNOTIFY) && (LOWORD(wParam)==WM_LBUTTONDOWN))
  470. {
  471. static DWORD dwLastTime = 0;
  472. int dwTimeNow = GetTickCount();
  473. if (((dwTimeNow - dwLastTime) < GetDoubleClickTime()) && (dwLastTime != 0))
  474. {
  475. PostMessage(hwnd, WM_USER_DSR_LBUTTONDBLCLK, 0, 0); // Notify the video window that we double clicked
  476. dwLastTime = 0;
  477. }
  478. else
  479. dwLastTime = dwTimeNow;
  480. }
  481. if ((uMsg == WM_USER_DSR_FULLSCREEN) && (s_pVideoWindow))
  482. {
  483. s_pVideoWindow->HideCursor(wParam ? OATRUE : OAFALSE);
  484. }
  485. return retval;
  486. }
  487. #endif // WINAMPX