1 /*****************************************************************************
2 * directx.c: Windows DirectDraw video output
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
27 * This plugin will use YUV overlay if supported, using overlay will result in
28 * the best video quality (hardware interpolation when rescaling the picture)
29 * and the fastest display as it requires less processing.
31 * If YUV overlay is not supported this plugin will use RGB offscreen video
32 * surfaces that will be blitted onto the primary surface (display) to
33 * effectively display the pictures. This fallback method also enables us to
34 * display video in window mode.
36 *****************************************************************************/
44 #include <vlc_common.h>
45 #include <vlc_plugin.h>
46 #include <vlc_vout_display.h>
47 #include <vlc_charset.h> /* FromT */
51 #include <commctrl.h> /* ListView_(Get|Set)* */
55 /* Unicode function "DirectDrawEnumerateExW" has been desactivated
56 since in some cases this function fails and the callbacks are not
57 called. If the Unicode mode is restored, one should modify the
58 prototype of the callbacks and call the FromT conversion function.
60 #define DIRECTDRAWENUMERATEEX_NAME "DirectDrawEnumerateExA"
62 /*****************************************************************************
64 *****************************************************************************/
65 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
66 #define HW_YUV_LONGTEXT N_(\
67 "Try to use hardware acceleration for YUV->RGB conversions. " \
68 "This option doesn't have any effect when using overlays.")
70 #define SYSMEM_TEXT N_("Use video buffers in system memory")
71 #define SYSMEM_LONGTEXT N_(\
72 "Create video buffers in system memory instead of video memory. This " \
73 "isn't recommended as usually using video memory allows benefiting from " \
74 "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
75 "This option doesn't have any effect when using overlays.")
77 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
78 #define TRIPLEBUF_LONGTEXT N_(\
79 "Try to use triple buffering when using YUV overlays. That results in " \
80 "much better video quality (no flickering).")
82 #define DEVICE_TEXT N_("Name of desired display device")
83 #define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
84 "specify the Windows device name of the display that you want the video " \
85 "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
86 "\"\\\\.\\DISPLAY2\".")
88 #define DX_HELP N_("Recommended video output for Windows XP. " \
89 "Incompatible with Vista's Aero interface" )
91 static int Open (vlc_object_t *);
92 static void Close(vlc_object_t *);
94 static int FindDevicesCallback(vlc_object_t *, const char *,
97 set_shortname("DirectX")
98 set_description(N_("DirectX (DirectDraw) video output"))
100 set_category(CAT_VIDEO)
101 set_subcategory(SUBCAT_VIDEO_VOUT)
102 add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT,
104 add_bool("directx-use-sysmem", false, SYSMEM_TEXT, SYSMEM_LONGTEXT,
106 add_bool("directx-3buffering", true, TRIPLEBUF_TEXT,
107 TRIPLEBUF_LONGTEXT, true)
108 add_string("directx-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
109 change_string_cb(FindDevicesCallback)
111 set_capability("vout display", 230)
112 add_shortcut("directx")
113 set_callbacks(Open, Close)
116 /*****************************************************************************
118 *****************************************************************************/
120 struct picture_sys_t {
121 LPDIRECTDRAWSURFACE2 surface;
122 LPDIRECTDRAWSURFACE2 front_surface;
126 /*****************************************************************************
128 * Defining them here allows us to get rid of the dxguid library during
130 *****************************************************************************/
131 #include <initguid.h>
134 DEFINE_GUID(IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56);
135 DEFINE_GUID(IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27);
137 static picture_pool_t *Pool (vout_display_t *, unsigned);
138 static void Display(vout_display_t *, picture_t *, subpicture_t *);
139 static int Control(vout_display_t *, int, va_list);
140 static void Manage (vout_display_t *);
143 static int WallpaperCallback(vlc_object_t *, char const *,
144 vlc_value_t, vlc_value_t, void *);
146 static int DirectXOpen(vout_display_t *, video_format_t *fmt);
147 static void DirectXClose(vout_display_t *);
149 static int DirectXLock(picture_t *);
150 static void DirectXUnlock(picture_t *);
152 static int DirectXUpdateOverlay(vout_display_t *, LPDIRECTDRAWSURFACE2 surface);
154 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper);
156 /** This function allocates and initialize the DirectX vout display.
158 static int Open(vlc_object_t *object)
160 vout_display_t *vd = (vout_display_t *)object;
161 vout_display_sys_t *sys;
163 /* Allocate structure */
164 vd->sys = sys = calloc(1, sizeof(*sys));
168 /* Load direct draw DLL */
169 sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
170 if (!sys->hddraw_dll) {
171 msg_Warn(vd, "DirectXInitDDraw failed loading ddraw.dll");
177 sys->use_wallpaper = var_CreateGetBool(vd, "video-wallpaper");
179 sys->use_overlay = false;//var_CreateGetBool(vd, "overlay"); /* FIXME */
180 sys->restore_overlay = false;
181 var_Create(vd, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
188 video_format_t fmt = vd->fmt;
190 if (DirectXOpen(vd, &fmt))
194 vout_display_info_t info = vd->info;
196 info.has_double_click = true;
197 info.has_hide_mouse = false;
198 info.has_pictures_invalid = true;
199 info.has_event_thread = true;
201 /* Interaction TODO support starting with wallpaper mode */
202 vlc_mutex_init(&sys->lock);
203 sys->ch_wallpaper = sys->use_wallpaper;
204 sys->wallpaper_requested = sys->use_wallpaper;
205 sys->use_wallpaper = false;
208 val.psz_string = _("Wallpaper");
209 var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
210 var_AddCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
212 /* Setup vout_display now that everything is fine */
218 vd->display = Display;
219 vd->control = Control;
227 FreeLibrary(sys->hddraw_dll);
232 /** Terminate a vout display created by Open.
234 static void Close(vlc_object_t *object)
236 vout_display_t *vd = (vout_display_t *)object;
237 vout_display_sys_t *sys = vd->sys;
239 var_DelCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
240 vlc_mutex_destroy(&sys->lock);
242 /* Make sure the wallpaper is restored */
243 WallpaperChange(vd, false);
250 FreeLibrary(sys->hddraw_dll);
254 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
257 return vd->sys->pool;
259 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
261 vout_display_sys_t *sys = vd->sys;
263 assert(sys->display);
265 /* Our surface can be lost so be sure to check this
266 * and restore it if need be */
267 if (IDirectDrawSurface2_IsLost(sys->display) == DDERR_SURFACELOST) {
268 if (IDirectDrawSurface2_Restore(sys->display) == DD_OK) {
269 if (sys->use_overlay)
270 DirectXUpdateOverlay(vd, NULL);
273 if (sys->restore_overlay)
274 DirectXUpdateOverlay(vd, NULL);
277 DirectXUnlock(picture);
279 if (sys->use_overlay) {
280 /* Flip the overlay buffers if we are using back buffers */
281 if (picture->p_sys->surface != picture->p_sys->front_surface) {
282 HRESULT hr = IDirectDrawSurface2_Flip(picture->p_sys->front_surface,
285 msg_Warn(vd, "could not flip overlay (error %li)", hr);
288 /* Blit video surface to display with the NOTEARING option */
290 ZeroMemory(&ddbltfx, sizeof(ddbltfx));
291 ddbltfx.dwSize = sizeof(ddbltfx);
292 ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
294 HRESULT hr = IDirectDrawSurface2_Blt(sys->display,
295 &sys->rect_dest_clipped,
296 picture->p_sys->surface,
297 &sys->rect_src_clipped,
298 DDBLT_ASYNC, &ddbltfx);
300 msg_Warn(vd, "could not blit surface (error %li)", hr);
302 DirectXLock(picture);
304 if (sys->is_first_display) {
305 IDirectDraw_WaitForVerticalBlank(sys->ddobject,
306 DDWAITVB_BLOCKBEGIN, NULL);
307 if (sys->use_overlay) {
308 HBRUSH brush = CreateSolidBrush(sys->i_rgb_colorkey);
309 /* set the colorkey as the backgound brush for the video window */
310 SetClassLongPtr(sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
315 picture_Release(picture);
316 VLC_UNUSED(subpicture);
318 static int Control(vout_display_t *vd, int query, va_list args)
320 vout_display_sys_t *sys = vd->sys;
323 case VOUT_DISPLAY_RESET_PICTURES:
325 /* Make sure the wallpaper is restored */
326 if (sys->use_wallpaper) {
327 vlc_mutex_lock(&sys->lock);
328 if (!sys->ch_wallpaper) {
329 sys->ch_wallpaper = true;
330 sys->wallpaper_requested = true;
332 vlc_mutex_unlock(&sys->lock);
334 WallpaperChange(vd, false);
336 return DirectXOpen(vd, &vd->fmt);
338 return CommonControl(vd, query, args);
341 static void Manage(vout_display_t *vd)
343 vout_display_sys_t *sys = vd->sys;
347 if (sys->changes & DX_POSITION_CHANGE) {
349 if (sys->use_overlay)
350 DirectXUpdateOverlay(vd, NULL);
352 /* Check if we are still on the same monitor */
353 HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
354 if (sys->hmonitor != hmon) {
355 vout_display_SendEventPicturesInvalid(vd);
358 sys->changes &= ~DX_POSITION_CHANGE;
361 /* Wallpaper mode change */
362 vlc_mutex_lock(&sys->lock);
363 const bool ch_wallpaper = sys->ch_wallpaper;
364 const bool wallpaper_requested = sys->wallpaper_requested;
365 sys->ch_wallpaper = false;
366 vlc_mutex_unlock(&sys->lock);
369 WallpaperChange(vd, wallpaper_requested);
372 if (sys->restore_overlay)
373 DirectXUpdateOverlay(vd, NULL);
377 static int DirectXOpenDDraw(vout_display_t *);
378 static void DirectXCloseDDraw(vout_display_t *);
380 static int DirectXOpenDisplay(vout_display_t *vd);
381 static void DirectXCloseDisplay(vout_display_t *vd);
383 static int DirectXCreatePool(vout_display_t *, bool *, video_format_t *);
384 static void DirectXDestroyPool(vout_display_t *);
386 static int DirectXOpen(vout_display_t *vd, video_format_t *fmt)
388 vout_display_sys_t *sys = vd->sys;
390 assert(!sys->ddobject);
391 assert(!sys->display);
392 assert(!sys->clipper);
394 /* Initialise DirectDraw */
395 if (DirectXOpenDDraw(vd)) {
396 msg_Err(vd, "cannot initialize DirectX DirectDraw");
400 /* Create the directx display */
401 if (DirectXOpenDisplay(vd)) {
402 msg_Err(vd, "cannot initialize DirectX DirectDraw");
405 UpdateRects(vd, NULL, NULL, true);
407 /* Create the picture pool */
408 if (DirectXCreatePool(vd, &sys->use_overlay, fmt)) {
409 msg_Err(vd, "cannot create any DirectX surface");
414 if (sys->use_overlay)
415 DirectXUpdateOverlay(vd, NULL);
416 EventThreadUseOverlay(sys->event, sys->use_overlay);
418 /* Change the window title bar text */
419 const char *fallback;
420 if (sys->use_overlay)
421 fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
422 else if (vlc_fourcc_IsYUV(fmt->i_chroma))
423 fallback = VOUT_TITLE " (hardware YUV DirectX output)";
425 fallback = VOUT_TITLE " (software RGB DirectX output)";
426 EventThreadUpdateTitle(sys->event, fallback);
430 static void DirectXClose(vout_display_t *vd)
432 DirectXDestroyPool(vd);
433 DirectXCloseDisplay(vd);
434 DirectXCloseDDraw(vd);
438 static BOOL WINAPI DirectXOpenDDrawCallback(GUID *guid, LPSTR desc,
439 LPSTR drivername, VOID *context,
442 vout_display_t *vd = context;
443 vout_display_sys_t *sys = vd->sys;
445 /* This callback function is called by DirectDraw once for each
446 * available DirectDraw device.
448 * Returning TRUE keeps enumerating.
453 char *psz_drivername = drivername;
454 char *psz_desc = desc;
456 msg_Dbg(vd, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername);
458 char *device = var_GetString(vd, "directx-device");
460 /* Check for forced device */
461 if (device && *device && !strcmp(psz_drivername, device)) {
462 MONITORINFO monitor_info;
463 monitor_info.cbSize = sizeof(MONITORINFO);
465 if (GetMonitorInfoA(hmon, &monitor_info)) {
468 /* Move window to the right screen */
469 GetWindowRect(sys->hwnd, &rect);
470 if (!IntersectRect(&rect, &rect, &monitor_info.rcWork)) {
471 rect.left = monitor_info.rcWork.left;
472 rect.top = monitor_info.rcWork.top;
473 msg_Dbg(vd, "DirectXEnumCallback: setting window "
474 "position to %ld,%ld", rect.left, rect.top);
475 SetWindowPos(sys->hwnd, NULL,
476 rect.left, rect.top, 0, 0,
477 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
480 sys->hmonitor = hmon;
484 if (hmon == sys->hmonitor) {
485 msg_Dbg(vd, "selecting %s, %s", psz_desc, psz_drivername);
487 free(sys->display_driver);
488 sys->display_driver = malloc(sizeof(*guid));
489 if (sys->display_driver)
490 *sys->display_driver = *guid;
496 * Probe the capabilities of the hardware
498 * It is nice to know which features are supported by the hardware so we can
499 * find ways to optimize our rendering.
501 static void DirectXGetDDrawCaps(vout_display_t *vd)
503 vout_display_sys_t *sys = vd->sys;
505 /* This is just an indication of whether or not we'll support overlay,
506 * but with this test we don't know if we support YUV overlay */
508 ZeroMemory(&ddcaps, sizeof(ddcaps));
509 ddcaps.dwSize = sizeof(ddcaps);
510 HRESULT hr = IDirectDraw2_GetCaps(sys->ddobject, &ddcaps, NULL);
512 msg_Warn(vd, "cannot get caps");
516 /* Determine if the hardware supports overlay surfaces */
517 const bool has_overlay = ddcaps.dwCaps & DDCAPS_OVERLAY;
518 /* Determine if the hardware supports overlay surfaces */
519 const bool has_overlay_fourcc = ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC;
520 /* Determine if the hardware supports overlay deinterlacing */
521 const bool can_deinterlace = ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN;
522 /* Determine if the hardware supports colorkeying */
523 const bool has_color_key = ddcaps.dwCaps & DDCAPS_COLORKEY;
524 /* Determine if the hardware supports scaling of the overlay surface */
525 const bool can_stretch = ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH;
526 /* Determine if the hardware supports color conversion during a blit */
527 sys->can_blit_fourcc = ddcaps.dwCaps & DDCAPS_BLTFOURCC;
528 /* Determine overlay source boundary alignment */
529 const bool align_boundary_src = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC;
530 /* Determine overlay destination boundary alignment */
531 const bool align_boundary_dest = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST;
532 /* Determine overlay destination size alignment */
533 const bool align_size_src = ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC;
534 /* Determine overlay destination size alignment */
535 const bool align_size_dest = ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST;
537 msg_Dbg(vd, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
538 "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
540 has_overlay, has_overlay_fourcc, can_deinterlace,
541 has_color_key, can_stretch, sys->can_blit_fourcc);
543 if (align_boundary_src || align_boundary_dest || align_size_src || align_size_dest) {
544 if (align_boundary_src)
545 vd->sys->i_align_src_boundary = ddcaps.dwAlignBoundarySrc;
546 if (align_boundary_dest)
547 vd->sys->i_align_dest_boundary = ddcaps.dwAlignBoundaryDest;
549 vd->sys->i_align_src_size = ddcaps.dwAlignSizeSrc;
551 vd->sys->i_align_dest_size = ddcaps.dwAlignSizeDest;
554 "align_boundary_src=%i,%i align_boundary_dest=%i,%i "
555 "align_size_src=%i,%i align_size_dest=%i,%i",
556 align_boundary_src, vd->sys->i_align_src_boundary,
557 align_boundary_dest, vd->sys->i_align_dest_boundary,
558 align_size_src, vd->sys->i_align_src_size,
559 align_size_dest, vd->sys->i_align_dest_size);
566 static int DirectXOpenDDraw(vout_display_t *vd)
568 vout_display_sys_t *sys = vd->sys;
572 HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
573 OurDirectDrawCreate =
574 (void *)GetProcAddress(sys->hddraw_dll, "DirectDrawCreate");
575 if (!OurDirectDrawCreate) {
576 msg_Err(vd, "DirectXInitDDraw failed GetProcAddress");
581 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA, LPVOID, DWORD);
582 OurDirectDrawEnumerateEx =
583 (void *)GetProcAddress(sys->hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
585 if (OurDirectDrawEnumerateEx) {
586 char *device = var_GetString(vd, "directx-device");
588 msg_Dbg(vd, "directx-device: %s", device);
592 sys->hmonitor = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
594 /* Enumerate displays */
595 OurDirectDrawEnumerateEx(DirectXOpenDDrawCallback,
596 vd, DDENUM_ATTACHEDSECONDARYDEVICES);
599 /* Initialize DirectDraw now */
600 LPDIRECTDRAW ddobject;
601 hr = OurDirectDrawCreate(sys->display_driver, &ddobject, NULL);
603 msg_Err(vd, "DirectXInitDDraw cannot initialize DDraw");
607 /* Get the IDirectDraw2 interface */
609 hr = IDirectDraw_QueryInterface(ddobject, &IID_IDirectDraw2,
611 /* Release the unused interface */
612 IDirectDraw_Release(ddobject);
615 msg_Err(vd, "cannot get IDirectDraw2 interface");
616 sys->ddobject = NULL;
621 /* Set DirectDraw Cooperative level, ie what control we want over Windows
623 hr = IDirectDraw2_SetCooperativeLevel(sys->ddobject, NULL, DDSCL_NORMAL);
625 msg_Err(vd, "cannot set direct draw cooperative level");
629 /* Get the size of the current display device */
631 MONITORINFO monitor_info;
632 monitor_info.cbSize = sizeof(MONITORINFO);
633 GetMonitorInfoA(vd->sys->hmonitor, &monitor_info);
634 sys->rect_display = monitor_info.rcMonitor;
636 sys->rect_display.left = 0;
637 sys->rect_display.top = 0;
638 sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
639 sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
642 msg_Dbg(vd, "screen dimensions (%lix%li,%lix%li)",
643 sys->rect_display.left,
644 sys->rect_display.top,
645 sys->rect_display.right,
646 sys->rect_display.bottom);
648 /* Probe the capabilities of the hardware */
649 DirectXGetDDrawCaps(vd);
654 static void DirectXCloseDDraw(vout_display_t *vd)
656 vout_display_sys_t *sys = vd->sys;
658 IDirectDraw2_Release(sys->ddobject);
660 sys->ddobject = NULL;
662 free(sys->display_driver);
663 sys->display_driver = NULL;
665 sys->hmonitor = NULL;
669 * Create a clipper that will be used when blitting the RGB surface to the main display.
671 * This clipper prevents us to modify by mistake anything on the screen
672 * which doesn't belong to our window. For example when a part of our video
673 * window is hidden by another window.
675 static void DirectXCreateClipper(vout_display_t *vd)
677 vout_display_sys_t *sys = vd->sys;
680 /* Create the clipper */
681 hr = IDirectDraw2_CreateClipper(sys->ddobject, 0, &sys->clipper, NULL);
683 msg_Warn(vd, "cannot create clipper (error %li)", hr);
687 /* Associate the clipper to the window */
688 hr = IDirectDrawClipper_SetHWnd(sys->clipper, 0, sys->hvideownd);
690 msg_Warn(vd, "cannot attach clipper to window (error %li)", hr);
694 /* associate the clipper with the surface */
695 hr = IDirectDrawSurface_SetClipper(sys->display, sys->clipper);
698 msg_Warn(vd, "cannot attach clipper to surface (error %li)", hr);
706 IDirectDrawClipper_Release(sys->clipper);
711 * It finds out the 32bits RGB pixel value of the colorkey.
713 static uint32_t DirectXFindColorkey(vout_display_t *vd, uint32_t *color)
715 vout_display_sys_t *sys = vd->sys;
720 ddsd.dwSize = sizeof(ddsd);
721 hr = IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL);
725 uint32_t backup = *(uint32_t *)ddsd.lpSurface;
727 switch (ddsd.ddpfPixelFormat.dwRGBBitCount) {
729 *(uint8_t *)ddsd.lpSurface = *color | (*color << 4);
732 *(uint8_t *)ddsd.lpSurface = *color;
736 *(uint16_t *)ddsd.lpSurface = *color;
739 /* Seems to be problematic so we'll just put black as the colorkey */
742 *(uint32_t *)ddsd.lpSurface = *color;
745 IDirectDrawSurface2_Unlock(sys->display, NULL);
750 if (IDirectDrawSurface2_GetDC(sys->display, &hdc) == DD_OK) {
751 rgb = GetPixel(hdc, 0, 0);
752 IDirectDrawSurface2_ReleaseDC(sys->display, hdc);
757 /* Restore the pixel value */
758 ddsd.dwSize = sizeof(ddsd);
759 if (IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) {
760 *(uint32_t *)ddsd.lpSurface = backup;
761 IDirectDrawSurface2_Unlock(sys->display, NULL);
768 * Create and initialize display according to preferences specified in the vout
771 static int DirectXOpenDisplay(vout_display_t *vd)
773 vout_display_sys_t *sys = vd->sys;
776 /* Now get the primary surface. This surface is what you actually see
779 ZeroMemory(&ddsd, sizeof(ddsd));
780 ddsd.dwSize = sizeof(ddsd);
781 ddsd.dwFlags = DDSD_CAPS;
782 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
784 LPDIRECTDRAWSURFACE display;
785 hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &display, NULL);
787 msg_Err(vd, "cannot get primary surface (error %li)", hr);
792 hr = IDirectDrawSurface_QueryInterface(display, &IID_IDirectDrawSurface2,
794 /* Release the old interface */
795 IDirectDrawSurface_Release(display);
798 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
804 /* The clipper will be used only in non-overlay mode */
805 DirectXCreateClipper(vd);
807 /* Make sure the colorkey will be painted */
809 sys->i_rgb_colorkey = DirectXFindColorkey(vd, &sys->i_colorkey);
813 static void DirectXCloseDisplay(vout_display_t *vd)
815 vout_display_sys_t *sys = vd->sys;
817 if (sys->clipper != NULL)
818 IDirectDrawClipper_Release(sys->clipper);
820 if (sys->display != NULL)
821 IDirectDrawSurface2_Release(sys->display);
828 * Create an YUV overlay or RGB surface for the video.
830 * The best method of display is with an YUV overlay because the YUV->RGB
831 * conversion is done in hardware.
832 * You can also create a plain RGB surface.
833 * (Maybe we could also try an RGB overlay surface, which could have hardware
834 * scaling and which would also be faster in window mode because you don't
835 * need to do any blitting to the main display...)
837 static int DirectXCreateSurface(vout_display_t *vd,
838 LPDIRECTDRAWSURFACE2 *surface,
839 const video_format_t *fmt,
843 int backbuffer_count)
845 vout_display_sys_t *sys = vd->sys;
849 ZeroMemory(&ddsd, sizeof(ddsd));
850 ddsd.dwSize = sizeof(ddsd);
851 ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
852 ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
853 ddsd.dwWidth = fmt->i_visible_width;
854 ddsd.dwHeight = fmt->i_visible_height;
856 ddsd.dwFlags |= DDSD_PIXELFORMAT;
857 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
858 ddsd.ddpfPixelFormat.dwFourCC = fourcc;
861 ddsd.dwFlags |= DDSD_CAPS;
862 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
863 if (backbuffer_count > 0)
864 ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
866 if (backbuffer_count > 0) {
867 ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
868 ddsd.dwBackBufferCount = backbuffer_count;
871 ddsd.dwFlags |= DDSD_CAPS;
872 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
874 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
876 ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
879 /* Create the video surface */
880 LPDIRECTDRAWSURFACE surface_v1;
881 if (IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &surface_v1, NULL) != DD_OK)
884 /* Now that the surface is created, try to get a newer DirectX interface */
885 HRESULT hr = IDirectDrawSurface_QueryInterface(surface_v1,
886 &IID_IDirectDrawSurface2,
888 IDirectDrawSurface_Release(surface_v1);
890 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
895 /* Check the overlay is useable as some graphics cards allow creating
896 * several overlays but only one can be used at one time. */
897 if (DirectXUpdateOverlay(vd, *surface)) {
898 IDirectDrawSurface2_Release(*surface);
899 msg_Err(vd, "overlay unuseable (might already be in use)");
907 static void DirectXDestroySurface(LPDIRECTDRAWSURFACE2 surface)
909 IDirectDrawSurface2_Release(surface);
912 * This function locks a surface and get the surface descriptor.
914 static int DirectXLockSurface(LPDIRECTDRAWSURFACE2 front_surface,
915 LPDIRECTDRAWSURFACE2 surface,
920 DDSURFACEDESC ddsd_dummy;
924 ZeroMemory(ddsd, sizeof(*ddsd));
925 ddsd->dwSize = sizeof(*ddsd);
926 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
928 if (hr == DDERR_INVALIDPARAMS) {
929 /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
930 * in an invalid params error */
931 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
933 if (hr == DDERR_SURFACELOST) {
934 /* Your surface can be lost so be sure
935 * to check this and restore it if needed */
937 /* When using overlays with back-buffers, we need to restore
938 * the front buffer so the back-buffers get restored as well. */
939 if (front_surface != surface)
940 IDirectDrawSurface2_Restore(front_surface);
942 IDirectDrawSurface2_Restore(surface);
944 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
951 static void DirectXUnlockSurface(LPDIRECTDRAWSURFACE2 front_surface,
952 LPDIRECTDRAWSURFACE2 surface)
954 VLC_UNUSED(front_surface);
955 IDirectDrawSurface2_Unlock(surface, NULL);
957 static int DirectXCheckLockingSurface(LPDIRECTDRAWSURFACE2 front_surface,
958 LPDIRECTDRAWSURFACE2 surface)
960 if (DirectXLockSurface(front_surface, surface, NULL))
963 DirectXUnlockSurface(front_surface, surface);
974 static DWORD DirectXGetFourcc(vlc_fourcc_t codec)
976 static const dx_format_t dx_formats[] = {
977 { VLC_CODEC_YUYV, MAKEFOURCC('Y','U','Y','2') },
978 { VLC_CODEC_UYVY, MAKEFOURCC('U','Y','V','Y') },
979 { VLC_CODEC_YVYU, MAKEFOURCC('Y','V','Y','U') },
980 { VLC_CODEC_YV12, MAKEFOURCC('Y','V','1','2') },
981 { VLC_CODEC_I420, MAKEFOURCC('Y','V','1','2') },
982 { VLC_CODEC_J420, MAKEFOURCC('Y','V','1','2') },
986 for (unsigned i = 0; dx_formats[i].codec != 0; i++) {
987 if (dx_formats[i].codec == codec)
988 return dx_formats[i].fourcc;
993 static int DirectXCreatePictureResourceYuvOverlay(vout_display_t *vd,
994 const video_format_t *fmt,
997 vout_display_sys_t *sys = vd->sys;
999 bool allow_3buf = var_InheritBool(vd, "directx-3buffering");
1001 /* The overlay surface that we create won't be used to decode directly
1002 * into it because accessing video memory directly is way to slow (remember
1003 * that pictures are decoded macroblock per macroblock). Instead the video
1004 * will be decoded in picture buffers in system memory which will then be
1005 * memcpy() to the overlay surface. */
1006 LPDIRECTDRAWSURFACE2 front_surface;
1007 int ret = VLC_EGENERIC;
1009 /* Triple buffering rocks! it doesn't have any processing overhead
1010 * (you don't have to wait for the vsync) and provides for a very nice
1011 * video quality (no tearing). */
1012 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 2);
1015 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 0);
1017 return VLC_EGENERIC;
1018 msg_Dbg(vd, "YUV overlay surface created successfully");
1020 /* Get the back buffer */
1021 LPDIRECTDRAWSURFACE2 surface;
1023 ZeroMemory(&dds_caps, sizeof(dds_caps));
1024 dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1025 if (IDirectDrawSurface2_GetAttachedSurface(front_surface, &dds_caps, &surface) != DD_OK) {
1026 msg_Warn(vd, "Failed to get surface back buffer");
1027 /* front buffer is the same as back buffer */
1028 surface = front_surface;
1031 if (DirectXCheckLockingSurface(front_surface, surface)) {
1032 DirectXDestroySurface(front_surface);
1033 return VLC_EGENERIC;
1037 picture_sys_t *picsys = sys->picsys;
1038 picsys->front_surface = front_surface;
1039 picsys->surface = surface;
1040 picsys->fallback = NULL;
1043 static int DirectXCreatePictureResourceYuv(vout_display_t *vd,
1044 const video_format_t *fmt,
1047 vout_display_sys_t *sys = vd->sys;
1049 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1051 /* As we can't have an overlay, we'll try to create a plain offscreen
1052 * surface. This surface will reside in video memory because there's a
1053 * better chance then that we'll be able to use some kind of hardware
1054 * acceleration like rescaling, blitting or YUV->RGB conversions.
1055 * We then only need to blit this surface onto the main display when we
1056 * want to display it */
1058 /* Check if the chroma is supported first. This is required
1059 * because a few buggy drivers don't mind creating the surface
1060 * even if they don't know about the chroma. */
1062 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, NULL) != DD_OK)
1063 return VLC_EGENERIC;
1065 DWORD *list = calloc(count, sizeof(*list));
1068 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, list) != DD_OK) {
1070 return VLC_EGENERIC;
1073 for (index = 0; index < count; index++) {
1074 if (list[index] == fourcc)
1079 return VLC_EGENERIC;
1082 LPDIRECTDRAWSURFACE2 surface;
1083 if (DirectXCreateSurface(vd, &surface, fmt, fourcc, false, allow_sysmem, 0))
1084 return VLC_EGENERIC;
1085 msg_Dbg(vd, "YUV plain surface created successfully");
1087 if (DirectXCheckLockingSurface(surface, surface)) {
1088 DirectXDestroySurface(surface);
1089 return VLC_EGENERIC;
1093 picture_sys_t *picsys = sys->picsys;
1094 picsys->front_surface = surface;
1095 picsys->surface = surface;
1096 picsys->fallback = NULL;
1099 static int DirectXCreatePictureResourceRgb(vout_display_t *vd,
1100 video_format_t *fmt)
1102 vout_display_sys_t *sys = vd->sys;
1103 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1105 /* Our last choice is to use a plain RGB surface */
1106 DDPIXELFORMAT ddpfPixelFormat;
1107 ZeroMemory(&ddpfPixelFormat, sizeof(ddpfPixelFormat));
1108 ddpfPixelFormat.dwSize = sizeof(ddpfPixelFormat);
1110 IDirectDrawSurface2_GetPixelFormat(sys->display, &ddpfPixelFormat);
1111 if ((ddpfPixelFormat.dwFlags & DDPF_RGB) == 0)
1112 return VLC_EGENERIC;
1114 switch (ddpfPixelFormat.dwRGBBitCount) {
1116 fmt->i_chroma = VLC_CODEC_RGB8;
1119 fmt->i_chroma = VLC_CODEC_RGB15;
1122 fmt->i_chroma = VLC_CODEC_RGB16;
1125 fmt->i_chroma = VLC_CODEC_RGB24;
1128 fmt->i_chroma = VLC_CODEC_RGB32;
1131 msg_Err(vd, "unknown screen depth");
1132 return VLC_EGENERIC;
1134 fmt->i_rmask = ddpfPixelFormat.dwRBitMask;
1135 fmt->i_gmask = ddpfPixelFormat.dwGBitMask;
1136 fmt->i_bmask = ddpfPixelFormat.dwBBitMask;
1139 LPDIRECTDRAWSURFACE2 surface;
1140 int ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, allow_sysmem, 0);
1141 if (ret && !allow_sysmem)
1142 ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, true, 0);
1144 return VLC_EGENERIC;
1145 msg_Dbg(vd, "RGB plain surface created successfully");
1147 if (DirectXCheckLockingSurface(surface, surface)) {
1148 DirectXDestroySurface(surface);
1149 return VLC_EGENERIC;
1153 picture_sys_t *picsys = sys->picsys;
1154 picsys->front_surface = surface;
1155 picsys->surface = surface;
1156 picsys->fallback = NULL;
1160 static int DirectXCreatePictureResource(vout_display_t *vd,
1162 video_format_t *fmt)
1164 vout_display_sys_t *sys = vd->sys;
1167 picture_sys_t *picsys = calloc(1, sizeof(*picsys));
1168 if (unlikely(picsys == NULL))
1170 sys->picsys = picsys;
1173 bool allow_hw_yuv = sys->can_blit_fourcc &&
1174 vlc_fourcc_IsYUV(fmt->i_chroma) &&
1175 var_InheritBool(vd, "directx-hw-yuv");
1176 bool allow_overlay = var_InheritBool(vd, "overlay");
1178 /* Try to use an yuv surface */
1180 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
1181 /* Try with overlay first */
1182 for (unsigned pass = allow_overlay ? 0 : 1; pass < 2; pass++) {
1183 for (unsigned i = 0; list[i] != 0; i++) {
1184 const DWORD fourcc = DirectXGetFourcc(list[i]);
1189 if (DirectXCreatePictureResourceYuvOverlay(vd, fmt, fourcc))
1192 if (DirectXCreatePictureResourceYuv(vd, fmt, fourcc))
1196 *use_overlay = pass == 0;
1197 fmt->i_chroma = list[i];
1204 return DirectXCreatePictureResourceRgb(vd, fmt);
1206 static void DirectXDestroyPictureResource(vout_display_t *vd)
1208 vout_display_sys_t *sys = vd->sys;
1210 if (sys->picsys->front_surface != sys->picsys->surface)
1211 DirectXDestroySurface(sys->picsys->surface);
1212 DirectXDestroySurface(sys->picsys->front_surface);
1213 if (sys->picsys->fallback)
1214 picture_Release(sys->picsys->fallback);
1217 static int DirectXLock(picture_t *picture)
1220 if (DirectXLockSurface(picture->p_sys->front_surface,
1221 picture->p_sys->surface, &ddsd))
1222 return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
1224 CommonUpdatePicture(picture, NULL, ddsd.lpSurface, ddsd.lPitch);
1227 static void DirectXUnlock(picture_t *picture)
1229 DirectXUnlockSurface(picture->p_sys->front_surface,
1230 picture->p_sys->surface);
1233 static int DirectXCreatePool(vout_display_t *vd,
1234 bool *use_overlay, video_format_t *fmt)
1236 vout_display_sys_t *sys = vd->sys;
1241 if (DirectXCreatePictureResource(vd, use_overlay, fmt))
1242 return VLC_EGENERIC;
1244 /* Create the associated picture */
1245 picture_resource_t resource = { .p_sys = sys->picsys };
1246 picture_t *picture = picture_NewFromResource(fmt, &resource);
1248 DirectXDestroyPictureResource(vd);
1253 /* Wrap it into a picture pool */
1254 picture_pool_configuration_t cfg;
1255 memset(&cfg, 0, sizeof(cfg));
1256 cfg.picture_count = 1;
1257 cfg.picture = &picture;
1258 cfg.lock = DirectXLock;
1259 cfg.unlock = DirectXUnlock;
1261 sys->pool = picture_pool_NewExtended(&cfg);
1263 picture_Release(picture);
1264 DirectXDestroyPictureResource(vd);
1269 static void DirectXDestroyPool(vout_display_t *vd)
1271 vout_display_sys_t *sys = vd->sys;
1274 DirectXDestroyPictureResource(vd);
1275 picture_pool_Delete(sys->pool);
1281 * Move or resize overlay surface on video display.
1283 * This function is used to move or resize an overlay surface on the screen.
1284 * Ususally the overlay is moved by the user and thus, by a move or resize
1287 static int DirectXUpdateOverlay(vout_display_t *vd, LPDIRECTDRAWSURFACE2 surface)
1289 vout_display_sys_t *sys = vd->sys;
1291 RECT src = sys->rect_src_clipped;
1292 RECT dst = sys->rect_dest_clipped;
1294 if (sys->use_wallpaper) {
1295 src.left = vd->source.i_x_offset;
1296 src.top = vd->source.i_y_offset;
1297 src.right = vd->source.i_x_offset + vd->source.i_visible_width;
1298 src.bottom = vd->source.i_y_offset + vd->source.i_visible_height;
1299 AlignRect(&src, sys->i_align_src_boundary, sys->i_align_src_size);
1301 vout_display_cfg_t cfg = *vd->cfg;
1302 cfg.display.width = sys->rect_display.right;
1303 cfg.display.height = sys->rect_display.bottom;
1305 vout_display_place_t place;
1306 vout_display_PlacePicture(&place, &vd->source, &cfg, true);
1308 dst.left = sys->rect_display.left + place.x;
1309 dst.top = sys->rect_display.top + place.y;
1310 dst.right = dst.left + place.width;
1311 dst.bottom = dst.top + place.height;
1312 AlignRect(&dst, sys->i_align_dest_boundary, sys->i_align_dest_size);
1317 return VLC_EGENERIC;
1318 surface = sys->picsys->front_surface;
1321 /* The new window dimensions should already have been computed by the
1322 * caller of this function */
1324 /* Position and show the overlay */
1326 ZeroMemory(&ddofx, sizeof(ddofx));
1327 ddofx.dwSize = sizeof(ddofx);
1328 ddofx.dckDestColorkey.dwColorSpaceLowValue = sys->i_colorkey;
1329 ddofx.dckDestColorkey.dwColorSpaceHighValue = sys->i_colorkey;
1331 HRESULT hr = IDirectDrawSurface2_UpdateOverlay(surface,
1332 &src, sys->display, &dst,
1333 DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx);
1334 sys->restore_overlay = hr != DD_OK;
1337 msg_Warn(vd, "DirectDrawUpdateOverlay cannot move/resize overlay");
1338 return VLC_EGENERIC;
1344 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper)
1346 vout_display_sys_t *sys = vd->sys;
1348 if (!sys->use_wallpaper == !use_wallpaper)
1351 HWND hwnd = FindWindow(_T("Progman"), NULL);
1353 hwnd = FindWindowEx(hwnd, NULL, _T("SHELLDLL_DefView"), NULL);
1355 hwnd = FindWindowEx(hwnd, NULL, _T("SysListView32"), NULL);
1357 msg_Warn(vd, "couldn't find \"SysListView32\" window, "
1358 "wallpaper mode not supported");
1362 msg_Dbg(vd, "wallpaper mode %s", use_wallpaper ? "enabled" : "disabled");
1363 sys->use_wallpaper = use_wallpaper;
1365 if (sys->use_wallpaper) {
1366 sys->color_bkg = ListView_GetBkColor(hwnd);
1367 sys->color_bkgtxt = ListView_GetTextBkColor(hwnd);
1369 ListView_SetBkColor(hwnd, sys->i_rgb_colorkey);
1370 ListView_SetTextBkColor(hwnd, sys->i_rgb_colorkey);
1372 ListView_SetBkColor(hwnd, sys->color_bkg);
1373 ListView_SetTextBkColor(hwnd, sys->color_bkgtxt);
1376 /* Update desktop */
1377 InvalidateRect(hwnd, NULL, TRUE);
1380 if (sys->use_overlay)
1381 DirectXUpdateOverlay(vd, NULL);
1385 static int WallpaperCallback(vlc_object_t *object, char const *cmd,
1386 vlc_value_t oldval, vlc_value_t newval, void *data)
1388 vout_display_t *vd = (vout_display_t *)object;
1389 vout_display_sys_t *sys = vd->sys;
1390 VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
1392 vlc_mutex_lock(&sys->lock);
1393 const bool ch_wallpaper = !sys->wallpaper_requested != !newval.b_bool;
1394 sys->ch_wallpaper |= ch_wallpaper;
1395 sys->wallpaper_requested = newval.b_bool;
1396 vlc_mutex_unlock(&sys->lock);
1407 /*****************************************************************************
1408 * config variable callback
1409 *****************************************************************************/
1410 static BOOL WINAPI DirectXEnumCallback2(GUID *guid, LPSTR desc,
1411 LPSTR drivername, VOID *data,
1414 enum_context_t *ctx = data;
1416 VLC_UNUSED(guid); VLC_UNUSED(desc); VLC_UNUSED(hmon);
1418 char *psz_drivername = drivername;
1419 ctx->values = xrealloc(ctx->values, (ctx->count + 1) * sizeof(char *));
1420 ctx->descs = xrealloc(ctx->descs, (ctx->count + 1) * sizeof(char *));
1422 ctx->values[ctx->count] = strdup(psz_drivername);
1423 ctx->descs[ctx->count] = strdup(psz_drivername);
1426 return TRUE; /* Keep enumerating */
1429 static int FindDevicesCallback(vlc_object_t *object, const char *name,
1430 char ***values, char ***descs)
1434 ctx.values = xmalloc(sizeof(char *));
1435 ctx.descs = xmalloc(sizeof(char *));
1436 ctx.values[0] = strdup("");
1437 ctx.descs[0] = strdup(_("Default"));
1440 /* Load direct draw DLL */
1441 HINSTANCE hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
1442 if (hddraw_dll != NULL)
1444 /* Enumerate displays */
1445 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA,
1447 (void *)GetProcAddress(hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
1448 if (OurDirectDrawEnumerateEx != NULL)
1449 OurDirectDrawEnumerateEx(DirectXEnumCallback2, &ctx,
1450 DDENUM_ATTACHEDSECONDARYDEVICES);
1451 FreeLibrary(hddraw_dll);
1457 *values = ctx.values;