DSDisplay.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /*
  2. * Copyright (C) 2010-2011 Mamadou Diop.
  3. *
  4. * Contact: Mamadou Diop <diopmamadou(at)doubango.org>
  5. *
  6. * This file is part of Open Source Doubango Framework.
  7. *
  8. * DOUBANGO is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * DOUBANGO is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with DOUBANGO.
  20. *
  21. */
  22. #include "internals/DSDisplay.h"
  23. #include "internals/DSUtils.h"
  24. #include "tsk_list.h"
  25. #include "tsk_debug.h"
  26. #include <string>
  27. using namespace std;
  28. #define USE_OVERLAY 0
  29. #define OVERLAY_TIMEOUT 3
  30. #define WM_GRAPHNOTIFY WM_APP + 1
  31. #define FSCREEN_MIN_IDEAL_WIDTH 352
  32. #define FSCREEN_MIN_IDEAL_HEIGHT 288
  33. typedef struct tdshow_display_s {
  34. TSK_DECLARE_OBJECT;
  35. HWND hwnd;
  36. DSDisplay* display;
  37. }
  38. tdshow_display_t;
  39. typedef tsk_list_t tdshow_displays_L_t;
  40. const tsk_object_def_t *tdshow_display_def_t;
  41. // Static list to find which display is link to a given hWnd
  42. static tdshow_displays_L_t* __directshow__Displays = tsk_null;
  43. /*== Predicate function to find tdshow_display_t object by HWND. */
  44. static int __pred_find_display_by_hwnd(const tsk_list_item_t *item, const void *hWnd)
  45. {
  46. if(item && item->data) {
  47. const tdshow_display_t *display = (const tdshow_display_t *)item->data;
  48. int ret = 0;
  49. tsk_subsat_int32_ptr(display->hwnd, *((HWND*)hWnd), &ret);
  50. return ret;
  51. }
  52. return -1;
  53. }
  54. // C Callback that dispatch event to the right display
  55. static LRESULT CALLBACK __directshow__WndProcWindow(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  56. {
  57. LRESULT result = FALSE;
  58. BOOL resultSet = FALSE;
  59. if(__directshow__Displays) {
  60. tsk_list_lock(__directshow__Displays);
  61. const tdshow_display_t *display = (const tdshow_display_t *)tsk_list_find_object_by_pred(__directshow__Displays, __pred_find_display_by_hwnd, &hWnd);
  62. if((resultSet = (display && display->display))) {
  63. result = display->display->handleEvents(hWnd, uMsg, wParam, lParam);
  64. }
  65. tsk_list_unlock(__directshow__Displays);
  66. }
  67. return resultSet ? result : DefWindowProc(hWnd, uMsg, wParam, lParam);
  68. }
  69. DSDisplay::DSDisplay(HRESULT *hr)
  70. {
  71. this->window = NULL;
  72. this->parentWindowProc = NULL;
  73. this->hooked = false;
  74. this->fullscreen = false;
  75. this->bPluginFirefox = false;
  76. this->top = 0;
  77. this->left = 0;
  78. this->width = this->imgWidth = 176;
  79. this->height = this->imgHeight = 144;
  80. this->fps = 15;
  81. this->graph = new DSDisplayGraph(hr);
  82. if (FAILED(*hr)) {
  83. return;
  84. }
  85. #if USE_OVERLAY
  86. this->overlay = new DSDisplayOverlay();
  87. #else
  88. this->overlay = NULL;
  89. #endif
  90. this->graph->getVideoWindow()->put_Visible(OAFALSE);
  91. }
  92. DSDisplay::~DSDisplay()
  93. {
  94. this->unhook();
  95. SAFE_DELETE_PTR(this->overlay);
  96. SAFE_DELETE_PTR(this->graph);
  97. }
  98. void DSDisplay::start()
  99. {
  100. if (!this->graph->isRunning()) {
  101. this->hook();
  102. }
  103. if (!this->graph->isRunning() || this->graph->isPaused()) {
  104. this->graph->start();
  105. }
  106. this->graph->getVideoWindow()->put_Visible(OATRUE);
  107. }
  108. void DSDisplay::pause()
  109. {
  110. this->graph->pause();
  111. }
  112. void DSDisplay::stop()
  113. {
  114. if (this->graph->isRunning()) {
  115. this->setFullscreen(false);
  116. this->graph->stop();
  117. this->unhook();
  118. }
  119. }
  120. void DSDisplay::attach(INT64 parent)
  121. {
  122. this->attach((void*)parent);
  123. }
  124. void DSDisplay::attach(void *parent)
  125. {
  126. // Don't reattach if this is the same parent
  127. if (this->isAttached() && parent) {
  128. HWND hwnd = reinterpret_cast<HWND>(parent);
  129. if (hwnd != this->window) {
  130. this->detach();
  131. }
  132. }
  133. // Gets the handle of the parent
  134. this->window = reinterpret_cast<HWND>(parent);
  135. // Hook to the parent WindowProc
  136. this->hook();
  137. #if USE_OVERLAY
  138. // Allows the overlay to initialize
  139. this->overlay->attach(this->window, this->graph);
  140. #endif
  141. }
  142. void DSDisplay::detach(void *parent)
  143. {
  144. // The detach action is only valid and if this is the same parent
  145. if (parent) {
  146. HWND hwnd = reinterpret_cast<HWND>(parent);
  147. if (hwnd == this->window) {
  148. this->detach();
  149. }
  150. }
  151. }
  152. void DSDisplay::detach()
  153. {
  154. if (!this->isAttached()) {
  155. return;
  156. }
  157. #if USE_OVERLAY
  158. // Clean up overlay
  159. this->overlay->detach();
  160. #endif
  161. // Unhook from the parent WindowProc
  162. this->unhook();
  163. // Set the handle of the parent to NULL
  164. this->window = NULL;
  165. }
  166. bool DSDisplay::isAttached()
  167. {
  168. return (this->window != NULL);
  169. }
  170. int DSDisplay::getWidth()
  171. {
  172. return this->width;
  173. }
  174. int DSDisplay::getHeight()
  175. {
  176. return this->height;
  177. }
  178. void DSDisplay::setSize(int w, int h)
  179. {
  180. //this->width = w;
  181. //this->height = h;
  182. if (!this->fullscreen) {
  183. this->graph->setImageFormat(w, h);
  184. if(this->hooked) {
  185. #if 0
  186. #if defined(VMR9_WINDOWLESS)
  187. RECT rc;
  188. SetRect(&rc, 0, 0, w, h);
  189. this->graph->getWindowlessControl()->SetVideoPosition(&rc, &rc);
  190. #else
  191. this->graph->getVideoWindow()->SetWindowPosition(0, 0, this->width , this->height);
  192. #endif
  193. #endif
  194. }
  195. }
  196. }
  197. void DSDisplay::applyRatio(RECT rect)
  198. {
  199. long w = rect.right - rect.left;
  200. long h = rect.bottom - rect.top;
  201. float ratio = ((float)this->imgWidth/(float)this->imgHeight);
  202. // (w/h)=ratio =>
  203. // 1) h=w/ratio
  204. // and
  205. // 2) w=h*ratio
  206. this->width = (int)(w/ratio) > h ? (int)(h * ratio) : w;
  207. this->height = (int)(this->width/ratio) > h ? h : (int)(this->width/ratio);
  208. this->left = ((w - this->width) >> 1);
  209. this->top = ((h - this->height) >> 1);
  210. }
  211. bool DSDisplay::isFullscreen()
  212. {
  213. #if defined(VMR9_WINDOWLESS)
  214. // TODO
  215. #else
  216. long result;
  217. HRESULT hr = this->graph->getVideoWindow()->get_FullScreenMode(&result);
  218. if (SUCCEEDED(hr)) {
  219. this->fullscreen = (result == OATRUE);
  220. }
  221. else {
  222. TSK_DEBUG_ERROR("get_FullScreenMode failed with %ld", hr);
  223. this->fullscreen = FALSE;
  224. }
  225. #endif
  226. return this->fullscreen;
  227. }
  228. void DSDisplay::setFullscreen(bool value)
  229. {
  230. if(!this->canFullscreen()) {
  231. TSK_DEBUG_WARN("Cannot fullscreen");
  232. return;
  233. }
  234. HRESULT hr;
  235. #if defined(VMR9_WINDOWLESS)
  236. // TODO
  237. #else
  238. if (this->isFullscreen() == value) {
  239. return;
  240. }
  241. hr = this->graph->getVideoWindow()->put_FullScreenMode(value ? OATRUE : OAFALSE);
  242. if (SUCCEEDED(hr)) {
  243. this->fullscreen = value;
  244. #if USE_OVERLAY
  245. this->overlay->show(this->fullscreen ? (OVERLAY_TIMEOUT * this->graph->getDisplayFps()) : 0);
  246. #endif
  247. }
  248. else {
  249. TSK_DEBUG_ERROR("put_FullScreenMode failed with %ld", hr);
  250. }
  251. #endif
  252. }
  253. void DSDisplay::setPluginFirefox(bool value)
  254. {
  255. bPluginFirefox = value;
  256. }
  257. bool DSDisplay::canFullscreen()
  258. {
  259. #if defined(VMR9_WINDOWLESS)
  260. // TODO
  261. #else
  262. if(this->graph) {
  263. UINT image_w, image_h;
  264. if( this->graph->getImageFormat(image_w, image_h) ) {
  265. //this->graph->getVideoWindow()->GetMinIdealImageSize(&ideal_w, &ideal_h);
  266. return (((long)image_w >= FSCREEN_MIN_IDEAL_WIDTH) && ((long)image_h >= FSCREEN_MIN_IDEAL_HEIGHT));
  267. }
  268. }
  269. #endif
  270. return false;
  271. }
  272. void DSDisplay::setFps(int fps_)
  273. {
  274. this->fps = fps_;
  275. this->graph->setDisplayFps(fps_);
  276. }
  277. // w and h are the size of the buffer not the display
  278. void DSDisplay::handleVideoFrame(const void* data, int w, int h)
  279. {
  280. if (this->graph->isRunning()) {
  281. // The graph will take care of changing the source filter if needed
  282. // in case of dimension change or anything else...
  283. this->graph->handleFrame(data, w, h);
  284. if(this->imgWidth != w || this->imgHeight != h) {
  285. this->imgWidth = w;
  286. this->imgHeight = h;
  287. if(this->window) {
  288. SendMessage(this->window, WM_SIZE, SIZE_RESTORED, MAKELPARAM(this->width , this->height));
  289. }
  290. }
  291. #if USE_OVERLAY
  292. this->overlay->update();
  293. #endif
  294. }
  295. }
  296. LRESULT DSDisplay::handleEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  297. {
  298. switch(uMsg) {
  299. case WM_CREATE:
  300. case WM_SIZE:
  301. case WM_MOVE: {
  302. RECT rect = {0};
  303. GetWindowRect(hWnd, &rect);
  304. applyRatio(rect);
  305. #if defined(VMR9_WINDOWLESS)
  306. this->graph->getWindowlessControl()->SetVideoPosition(&rect, &rect);
  307. #else
  308. this->graph->getVideoWindow()->SetWindowPosition(this->left, this->top, this->width , this->height);
  309. #endif
  310. }
  311. break;
  312. case WM_LBUTTONDBLCLK:
  313. if(this->canFullscreen()) {
  314. this->setFullscreen(true);
  315. }
  316. break;
  317. case WM_FULLSCREEN_SET:
  318. if(this->canFullscreen()) {
  319. this->setFullscreen(!this->isFullscreen());
  320. }
  321. break;
  322. case WM_LBUTTONDOWN:
  323. case WM_RBUTTONDOWN:
  324. case WM_KEYDOWN:
  325. if(this->isFullscreen()) {
  326. #if USE_OVERLAY
  327. // Re-Show overlay
  328. this->overlay->show(OVERLAY_TIMEOUT * this->graph->getDisplayFps());
  329. #endif
  330. }
  331. break;
  332. case WM_CHAR:
  333. case WM_KEYUP:
  334. if(this->isFullscreen() && (wParam == 0x1B || wParam == VK_ESCAPE)) {
  335. // escape
  336. this->setFullscreen(false);
  337. }
  338. break;
  339. case WM_GRAPHNOTIFY: {
  340. long evCode;
  341. LONG_PTR param1, param2;
  342. HRESULT hr;
  343. while (hr = this->graph->getMediaEvent()->GetEvent(&evCode, &param1, &param2, 0), SUCCEEDED(hr)) {
  344. hr = this->graph->getMediaEvent()->FreeEventParams(evCode, param1, param2);
  345. switch(evCode) {
  346. case EC_FULLSCREEN_LOST:
  347. #if USE_OVERLAY
  348. this->overlay->show(0);
  349. #endif
  350. break;
  351. case EC_COMPLETE:
  352. case EC_USERABORT:
  353. default:
  354. break;
  355. }
  356. }
  357. }
  358. break;
  359. #if defined(VMR9_WINDOWLESS)
  360. case WM_DISPLAYCHANGE: {
  361. this->graph->getWindowlessControl()->DisplayModeChanged();
  362. }
  363. break;
  364. case WM_PAINT: {
  365. RECT rect = {0};
  366. GetWindowRect(hWnd, &rect);
  367. PAINTSTRUCT ps;
  368. HDC hdc = BeginPaint(hWnd, &ps);
  369. this->graph->getWindowlessControl()->RepaintVideo(hWnd, hdc);
  370. EndPaint(hWnd, &ps);
  371. }
  372. break;
  373. #endif
  374. }
  375. return bPluginFirefox ? DefWindowProc(hWnd, uMsg, wParam, lParam) : CallWindowProc(this->parentWindowProc, hWnd, uMsg, wParam, lParam);
  376. }
  377. void DSDisplay::hook()
  378. {
  379. HRESULT hr;
  380. if (!this->window) {
  381. return;
  382. }
  383. if(this->hooked) {
  384. return;
  385. }
  386. this->hooked = TRUE;
  387. bool lock = (__directshow__Displays != NULL);
  388. if(lock) {
  389. tsk_list_lock(__directshow__Displays);
  390. }
  391. {
  392. // Gets the parent Window procedure
  393. #if defined(_WIN32_WCE)
  394. // Workaround for bug in SetWindowLong, call twice the API
  395. //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) __directshow__WndProcWindow );
  396. //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) __directshow__WndProcWindow );
  397. //__directshow__Displays[this->window] = this;
  398. #else
  399. this->parentWindowProc = (WNDPROC) SetWindowLongPtr(this->window, GWLP_WNDPROC, (LONG_PTR) __directshow__WndProcWindow);
  400. // Add this instance to the callback map
  401. tsk_object_new(tdshow_display_def_t, this->window, this);
  402. #endif
  403. }
  404. if(lock) {
  405. tsk_list_unlock(__directshow__Displays);
  406. }
  407. RECT rect;
  408. GetWindowRect(this->window, &rect);
  409. applyRatio(rect);
  410. #if defined(VMR9_WINDOWLESS)
  411. rect.left = 0;
  412. rect.top = 0;
  413. rect.right = this->width;
  414. rect.bottom = this->height;
  415. // TODO : Review
  416. hr = this->graph->getWindowlessControl()->SetVideoClippingWindow(this->window);
  417. hr = this->graph->getWindowlessControl()->SetBorderColor(RGB(0, 0, 128));
  418. hr = this->graph->getWindowlessControl()->SetVideoPosition(NULL, &rect);
  419. #else
  420. // TODO : Review the order
  421. hr = this->graph->getVideoWindow()->put_Owner((OAHWND) this->window);
  422. hr = this->graph->getVideoWindow()->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
  423. hr = this->graph->getVideoWindow()->SetWindowPosition(this->left, this->top, this->width, this->height);
  424. hr = this->graph->getVideoWindow()->put_MessageDrain((OAHWND) this->window);
  425. hr = this->graph->getVideoWindow()->put_Visible(OATRUE);
  426. #endif
  427. hr = this->graph->getMediaEvent()->SetNotifyWindow((OAHWND) this->window, WM_GRAPHNOTIFY, 0);
  428. }
  429. void DSDisplay::unhook()
  430. {
  431. HRESULT hr;
  432. if(!this->window) {
  433. return;
  434. }
  435. if(!this->hooked) {
  436. return;
  437. }
  438. hr = this->graph->getMediaEvent()->SetNotifyWindow(NULL, WM_GRAPHNOTIFY, 0);
  439. #if defined(VMR9_WINDOWLESS)
  440. // TODO : Review
  441. hr = this->graph->getWindowlessControl()->SetVideoClippingWindow(NULL);
  442. #else
  443. // TODO : Review the order
  444. hr = this->graph->getVideoWindow()->put_Visible(OAFALSE);
  445. hr = this->graph->getVideoWindow()->put_MessageDrain((OAHWND) NULL);
  446. hr = this->graph->getVideoWindow()->put_Owner((OAHWND) NULL);
  447. hr = this->graph->getVideoWindow()->put_AutoShow(OAFALSE);
  448. #endif
  449. bool lock = (__directshow__Displays != NULL);
  450. if(lock) {
  451. tsk_list_lock(__directshow__Displays);
  452. }
  453. {
  454. // Remove this instance from the callback map
  455. tsk_list_remove_item_by_pred(__directshow__Displays, __pred_find_display_by_hwnd, &this->window);
  456. // Restore parent Window procedure
  457. #if defined(_WIN32_WCE)
  458. // Workaround for bug in SetWindowLong, call twice the API
  459. //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) this->parentWindowProc );
  460. //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) this->parentWindowProc );
  461. #else
  462. SetWindowLongPtr(this->window, GWLP_WNDPROC, (LONG_PTR) this->parentWindowProc);
  463. #endif
  464. }
  465. if(lock) {
  466. tsk_list_unlock(__directshow__Displays);
  467. }
  468. this->hooked = FALSE;
  469. }
  470. //=================================================================================================
  471. // String object definition
  472. //
  473. static tsk_object_t* tdshow_display_ctor(tsk_object_t * self, va_list * app)
  474. {
  475. tdshow_display_t *display = (tdshow_display_t *)self;
  476. if(display) {
  477. display->hwnd = va_arg(*app, HWND);
  478. display->display = va_arg(*app, DSDisplay*);
  479. if(!__directshow__Displays) {
  480. __directshow__Displays = tsk_list_create();
  481. }
  482. tsk_list_push_back_data(__directshow__Displays, (void**)&display);
  483. }
  484. return self;
  485. }
  486. static tsk_object_t* tdshow_display_dtor(tsk_object_t * self)
  487. {
  488. tdshow_display_t *display = (tdshow_display_t *)self;
  489. if(display) {
  490. if(__directshow__Displays) {
  491. tsk_list_remove_item_by_data(__directshow__Displays, display);
  492. //if(TSK_LIST_IS_EMPTY(__directshow__Displays)){
  493. // TSK_OBJECT_SAFE_FREE(__directshow__Displays);
  494. //}
  495. }
  496. }
  497. return self;
  498. }
  499. static int tdshow_display_cmp(const tsk_object_t *_d1, const tsk_object_t *_d2)
  500. {
  501. const tdshow_display_t *d1 = (const tdshow_display_t *)_d1;
  502. const tdshow_display_t *d2 = (const tdshow_display_t *)_d2;
  503. if(d1 && d2) {
  504. int ret = 0;
  505. tsk_subsat_int32_ptr(d1->hwnd, d2->hwnd, &ret);
  506. return ret;
  507. }
  508. else if(!d1 && !d2) {
  509. return 0;
  510. }
  511. else {
  512. return -1;
  513. }
  514. }
  515. static const tsk_object_def_t tdshow_display_def_s = {
  516. sizeof(tdshow_display_t),
  517. tdshow_display_ctor,
  518. tdshow_display_dtor,
  519. tdshow_display_cmp,
  520. };
  521. extern const tsk_object_def_t *tdshow_display_def_t = &tdshow_display_def_s;