1 /*****************************************************************************
2 * vout_directx.c: Windows DirectX video output display method
3 *****************************************************************************
4 * Copyright (C) 1998, 1999, 2000 VideoLAN
5 * $Id: vout_directx.c,v 1.1 2001/06/02 01:09:03 sam Exp $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 #define MODULE_NAME directx
25 #include "modules_inner.h"
27 /* This is a list of what needs to be fixed:
29 * For now, this plugin only works when YUV overlay is supported (which it
30 * should be nowadays on most of the video cards under Windows)...
32 * The overlay doesn't use double-buffering.
34 * Use Shane Harper's optimizations for YUV
37 /*****************************************************************************
40 *****************************************************************************/
43 #include <errno.h> /* ENOMEM */
44 #include <stdlib.h> /* free() */
45 #include <string.h> /* strerror() */
58 #include "video_output.h"
61 #include "interface.h"
65 #include "modules_export.h"
67 #define OVERLAY_COLOR_KEY 1 /* color on top of which the overlay will be
68 * displayed. 1 should be almost black but
69 * not black (which is too common a color) */
71 /*****************************************************************************
72 * vout_sys_t: video output DirectX method descriptor
73 *****************************************************************************
74 * This structure is part of the video output thread descriptor.
75 * It describes the DirectX specific properties of an output thread.
76 *****************************************************************************/
77 typedef struct vout_sys_s
80 LPDIRECTDRAW p_ddobject; /* DirectDraw object */
81 LPDIRECTDRAWSURFACE p_display; /* display device */
82 LPDIRECTDRAWSURFACE p_overlay; /* overlay device */
83 LPDIRECTDRAWCLIPPER p_clipper; /* clipper */
84 HWND hwnd; /* Handle of the main */
87 int i_image_width; /* size of the decoded image */
89 int i_window_width; /* size of the displayed image */
92 boolean_t b_display_enabled;
96 boolean_t b_cursor_autohidden;
99 char *p_windx_buf[2]; /* Buffer information */
103 /*****************************************************************************
105 *****************************************************************************/
106 static int vout_Probe ( probedata_t *p_data );
107 static int vout_Create ( struct vout_thread_s * );
108 static int vout_Init ( struct vout_thread_s * );
109 static void vout_End ( struct vout_thread_s * );
110 static void vout_Destroy ( struct vout_thread_s * );
111 static int vout_Manage ( struct vout_thread_s * );
112 static void vout_Display ( struct vout_thread_s * );
113 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
114 u16 *blue, u16 *transp );
116 static int WinDXCreateWindow ( vout_thread_t *p_vout );
117 static int WinDXInitDDraw ( vout_thread_t *p_vout );
118 static int WinDXCreateDisplay ( vout_thread_t *p_vout );
119 static int WinDXCreateYUVOverlay ( vout_thread_t *p_vout );
120 static int WinDXUpdateOverlay ( vout_thread_t *p_vout );
121 static int WinDXClipOverlay ( vout_thread_t *p_vout );
122 static void WinDXCloseDDraw ( vout_thread_t *p_vout );
123 static void WinDXCloseWindow ( vout_thread_t *p_vout );
124 static void WinDXCloseDisplay ( vout_thread_t *p_vout );
125 static void WinDXCloseYUVOverlay ( vout_thread_t *p_vout );
127 /*****************************************************************************
128 * Functions exported as capabilities. They are declared as static so that
129 * we don't pollute the namespace too much.
130 *****************************************************************************/
131 void _M( vout_getfunctions )( function_list_t * p_function_list )
133 p_function_list->pf_probe = vout_Probe;
134 p_function_list->functions.vout.pf_create = vout_Create;
135 p_function_list->functions.vout.pf_init = vout_Init;
136 p_function_list->functions.vout.pf_end = vout_End;
137 p_function_list->functions.vout.pf_destroy = vout_Destroy;
138 p_function_list->functions.vout.pf_manage = vout_Manage;
139 p_function_list->functions.vout.pf_display = vout_Display;
140 p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
143 /*****************************************************************************
144 * vout_Probe: probe the video driver and return a score
145 *****************************************************************************
146 * This function tries to initialize Windows DirectX and returns a score to
147 * the plugin manager so that it can select the best plugin.
148 *****************************************************************************/
149 static int vout_Probe( probedata_t *p_data )
151 if( TestMethod( VOUT_METHOD_VAR, "directx" ) )
159 /*****************************************************************************
160 * vout_Create: allocate DirectX video thread output method
161 *****************************************************************************
162 * This function allocates and initialize the DirectX vout method.
163 *****************************************************************************/
164 static int vout_Create( vout_thread_t *p_vout )
166 /* Allocate structure */
167 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
168 if( p_vout->p_sys == NULL )
170 intf_ErrMsg( "vout error: can't create p_sys (%s)", strerror(ENOMEM) );
174 p_vout->p_sys->b_cursor = 1; /* TODO should be done with a main_GetInt.. */
176 p_vout->p_sys->b_cursor_autohidden = 0;
177 p_vout->p_sys->b_display_enabled = 0;
179 p_vout->p_sys->i_lastmoved = mdate();
181 p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
182 VOUT_FULLSCREEN_DEFAULT );
183 p_vout->p_sys->b_overlay = main_GetIntVariable( VOUT_OVERLAY_VAR,
184 VOUT_OVERLAY_DEFAULT );
185 p_vout->p_sys->i_window_width = main_GetIntVariable( VOUT_WIDTH_VAR,
186 VOUT_WIDTH_DEFAULT );
187 p_vout->p_sys->i_window_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
188 VOUT_HEIGHT_DEFAULT );
189 /* We don't know yet the dimensions of the video so the best guess is to
190 * pick the same as the window */
191 p_vout->p_sys->i_image_width = p_vout->p_sys->i_window_width;
192 p_vout->p_sys->i_image_height = p_vout->p_sys->i_window_height;
194 /* Create a window for the video */
195 /* Creating a window under Windows also initializes the thread's event
197 if( WinDXCreateWindow( p_vout ) )
199 intf_ErrMsg( "vout error: can't create window" );
200 free( p_vout->p_sys );
204 /* Initialise DirectDraw */
205 if( WinDXInitDDraw( p_vout ) )
207 intf_ErrMsg( "vout error: can't initialise DirectDraw" );
208 WinDXCloseWindow( p_vout );
209 free( p_vout->p_sys );
213 /* create the directx display */
214 if( WinDXCreateDisplay( p_vout ) )
216 intf_ErrMsg( "vout error: can't initialise DirectDraw" );
217 WinDXCloseDDraw( p_vout );
218 WinDXCloseWindow( p_vout );
219 free( p_vout->p_sys );
226 /*****************************************************************************
227 * vout_Init: initialize DirectX video thread output method
228 *****************************************************************************
230 *****************************************************************************/
231 static int vout_Init( vout_thread_t *p_vout )
236 /*****************************************************************************
237 * vout_End: terminate Sys video thread output method
238 *****************************************************************************
239 * Terminate an output method created by vout_Create.
240 * It is called at the end of the thread.
241 *****************************************************************************/
242 static void vout_End( vout_thread_t *p_vout )
247 /*****************************************************************************
248 * vout_Destroy: destroy Sys video thread output method
249 *****************************************************************************
250 * Terminate an output method created by vout_Create
251 *****************************************************************************/
252 static void vout_Destroy( vout_thread_t *p_vout )
254 WinDXCloseDisplay( p_vout );
255 WinDXCloseDDraw( p_vout );
256 WinDXCloseWindow( p_vout );
258 if( p_vout->p_sys != NULL )
260 free( p_vout->p_sys );
261 p_vout->p_sys = NULL;
266 /*****************************************************************************
267 * vout_Manage: handle Sys events
268 *****************************************************************************
269 * This function should be called regularly by video output thread. It returns
270 * a non null value if an error occured.
271 *****************************************************************************/
272 static int vout_Manage( vout_thread_t *p_vout )
275 WINDOWPLACEMENT window_placement;
277 while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
279 if( GetMessage(&msg, NULL, 0, 0) >= 0 )
281 switch( msg.message )
284 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_QUIT" );
285 p_main->p_intf->b_die = 1;
289 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_MOVE" );
290 if( !p_vout->b_need_render )
292 WinDXUpdateOverlay( p_vout );
294 /* don't create a never ending loop */
299 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_PAINT" );
300 if( !p_vout->b_need_render )
302 WinDXClipOverlay( p_vout );
304 /* don't create a never ending loop */
309 intf_WarnMsg( 3, "vout: WinDX WinProc WM_CHAR" );
314 p_main->p_intf->b_die = 1;
319 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
322 case '0': network_ChannelJoin( 0 ); break;
323 case '1': network_ChannelJoin( 1 ); break;
324 case '2': network_ChannelJoin( 2 ); break;
325 case '3': network_ChannelJoin( 3 ); break;
326 case '4': network_ChannelJoin( 4 ); break;
327 case '5': network_ChannelJoin( 5 ); break;
328 case '6': network_ChannelJoin( 6 ); break;
329 case '7': network_ChannelJoin( 7 ); break;
330 case '8': network_ChannelJoin( 8 ); break;
331 case '9': network_ChannelJoin( 9 ); break;
334 if( intf_ProcessKey( p_main->p_intf,
335 (char )msg.wParam ) )
337 intf_DbgMsg( "unhandled key '%c' (%i)",
338 (char)msg.wParam, msg.wParam );
346 TranslateMessage(&msg);
347 DispatchMessage(&msg);
360 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
362 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
364 /* We need to switch between Maximized and Normal sized window */
365 window_placement.length = sizeof(WINDOWPLACEMENT);
366 GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
367 if( p_vout->b_fullscreen )
369 /* Maximized window */
370 window_placement.showCmd = SW_SHOWMAXIMIZED;
371 /* Change window style, no borders and no title bar */
372 SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, 0 );
378 window_placement.showCmd = SW_SHOWNORMAL;
379 /* Change window style, borders and title bar */
380 SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
381 WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE );
384 SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
385 /*WinDXUpdateOverlay( p_vout );*/
387 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
393 /*****************************************************************************
394 * vout_SetPalette: sets an 8 bpp palette
395 *****************************************************************************
396 * This function sets the palette given as an argument. It does not return
397 * anything, but could later send information on which colors it was unable
399 *****************************************************************************/
400 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
401 u16 *blue, u16 *transp)
407 /*****************************************************************************
408 * vout_Display: displays previously rendered output
409 *****************************************************************************
410 * This function send the currently rendered image to the display, wait until
411 * it is displayed and switch the two rendering buffer, preparing next frame.
412 *****************************************************************************/
413 static void vout_Display( vout_thread_t *p_vout )
418 int i_image_width = p_vout->p_rendered_pic->i_width;
419 int i_image_height = p_vout->p_rendered_pic->i_height;
422 if( (p_vout->p_sys->p_display == NULL) )
424 intf_WarnMsg( 3, "vout error: WinDX no display!!" );
428 /* The first time this function is called it enables the display */
429 p_vout->p_sys->b_display_enabled = 1;
431 if( p_vout->b_need_render )
438 * p_vout->p_rendered_pic->p_y/u/v contains the YUV buffers to
441 /* TODO: support for streams other than 4:2:0 */
443 /* if the size of the decoded pictures has changed then we close the
444 * YUVOverlay (which doesn't have the right size anymore). */
445 if( p_vout->p_sys->i_image_width != i_image_width
446 || p_vout->p_sys->i_image_height != i_image_height )
448 intf_WarnMsg( 3, "vout: WinDX overlay size changed" );
449 p_vout->p_sys->i_image_width = i_image_width;
450 p_vout->p_sys->i_image_height = i_image_height;
451 WinDXCloseYUVOverlay( p_vout );
454 if( p_vout->p_sys->p_overlay == NULL )
456 intf_WarnMsg( 3, "vout: WinDX no overlay, open one..." );
457 if( WinDXCreateYUVOverlay( p_vout ) )
459 intf_WarnMsg( 3, "vout: WinDX cannot open a new overlay !!" );
462 /* Display the Overlay */
463 p_vout->p_sys->b_display_enabled = 1;
464 WinDXUpdateOverlay( p_vout );
467 /* Lock the overlay surface */
468 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
469 ddsd.dwSize = sizeof(DDSURFACEDESC);
470 dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_overlay, NULL,
471 &ddsd, DDLOCK_NOSYSLOCK, NULL);
472 if ( dxresult == DDERR_SURFACELOST )
474 /* Your surface can be lost (thanks to windows) so be sure
475 * to check this and restore it if needed */
476 dxresult = IDirectDrawSurface_Restore( p_vout->p_sys->p_overlay );
477 dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_overlay,
478 NULL, &ddsd, DDLOCK_NOSYSLOCK
479 | DDLOCK_WAIT, NULL);
481 if( dxresult != DD_OK )
483 intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
487 /* Now we can do the actual image copy.
488 * The copy has to be done line by line because of the special case
489 * when the Pitch does not equal the width of the picture */
490 for( i=0; i < ddsd.dwHeight/2; i++)
492 /* copy Y, we copy two lines at once */
493 memcpy(ddsd.lpSurface + i*2*ddsd.u1.lPitch,
494 p_vout->p_rendered_pic->p_y + i*2*i_image_width,
496 memcpy(ddsd.lpSurface + (i*2+1)*ddsd.u1.lPitch,
497 p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
500 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
501 + i * ddsd.u1.lPitch/2,
502 p_vout->p_rendered_pic->p_v + i*i_image_width/2,
505 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
506 + (ddsd.dwHeight * ddsd.u1.lPitch/4)
507 + i * ddsd.u1.lPitch/2,
508 p_vout->p_rendered_pic->p_u + i*i_image_width/2,
512 /* Unlock the Surface */
513 dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
521 /* following functions are local */
524 /*****************************************************************************
525 * WinDXEventProc: This is the window event processing function.
526 *****************************************************************************
527 * On Windows, when you create a window you have to attach an event processing
528 * function to it. The aim of this function is to manage "Queued Messages" and
529 * "Nonqueued Messages".
530 * Queued Messages are those picked up and retransmitted by vout_Manage
531 * (using the GetMessage function).
532 * Nonqueued Messages are those that Windows will send directly to this
533 * function (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
534 *****************************************************************************/
535 long FAR PASCAL WinDXEventProc( HWND hwnd, UINT message,
536 WPARAM wParam, LPARAM lParam )
542 intf_WarnMsg( 3, "vout: WinDX WinProc WM_ACTIVEAPP" );
547 intf_WarnMsg( 3, "vout: WinDX WinProc WM_SETCURSOR" );
548 /* turn the cursor off by setting it's value to NULL */
553 intf_WarnMsg( 3, "vout: WinDX WinProc WM_CREATE" );
556 /* test your key states in this case */
558 intf_WarnMsg( 3, "vout: WinDX WinProc WM_KEYDOWN" );
563 PostMessage(hwnd,WM_CLOSE,0,0);
569 /* this case is touched when the application is shutting down */
571 intf_WarnMsg( 3, "vout: WinDX WinProc WM_DESTROY" );
572 PostQuitMessage( 0 );
576 intf_WarnMsg( 3, "vout: WinDX WinProc WM_QUIT" );
582 case SC_SCREENSAVE: /* catch the screensaver */
583 case SC_MONITORPOWER: /* catch the monitor turn-off */
584 intf_WarnMsg( 3, "vout: WinDX WinProc WM_SYSCOMMAND" );
585 return 0; /* this stops them from happening */
591 case WM_WINDOWPOSCHANGED:
592 intf_WarnMsg( 3, "vout: WinDX WinProc WM_MOVE, WMSIZE" );
593 PostMessage(hwnd,WM_MOVE,0,0);
598 return DefWindowProc(hwnd, message, wParam, lParam);
601 /*****************************************************************************
602 * WinDXCreateWindow: create a windows window where the video will play.
603 *****************************************************************************
604 * Before creating a direct draw surface, we need to create a window in which
605 * the video will be displayed. This window will also allow us to capture the
607 *****************************************************************************/
608 static int WinDXCreateWindow( vout_thread_t *p_vout )
611 WNDCLASS wc; /* window class components */
614 intf_WarnMsg( 3, "vout: WinDX WinDXCreateWindow" );
616 /* get this module's instance */
617 hInstance = GetModuleHandle(NULL);
619 /* fill in the window class structure */
620 wc.style = 0; /* no special styles */
621 wc.lpfnWndProc = (WNDPROC)WinDXEventProc; /* event handler */
622 wc.cbClsExtra = 0; /* no extra class data */
623 wc.cbWndExtra = 0; /* no extra window data */
624 wc.hInstance = hInstance; /* instance */
625 wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); /* load a default icon */
626 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* load a default cursor */
627 wc.hbrBackground = NULL; /* redraw our own bg */
628 wc.lpszMenuName = NULL; /* no menu */
629 wc.lpszClassName = "VLC DirectX"; /* use a special class */
631 /* register the window class */
632 if (!RegisterClass(&wc)) {
633 intf_WarnMsg( 3, "vout: WinDX register window FAILED" );
637 /* when you create a window you give the dimensions you wish it to have.
638 * Unfortunatly these dimensions will include the borders and title bar.
639 * We use the following function to find out the size of the window
640 * corresponding to the useable surface we want */
641 rect_window.top = 10;
642 rect_window.left = 10;
643 rect_window.right = rect_window.left + p_vout->p_sys->i_window_width;
644 rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
645 AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
647 /* create the window */
648 p_vout->p_sys->hwnd = CreateWindow("VLC DirectX",/* name of window class */
649 "VLC DirectX", /* window title bar text */
651 | WS_SIZEBOX | WS_VISIBLE, /* window style */
652 10, /* default X coordinate */
653 10, /* default Y coordinate */
654 rect_window.right - rect_window.left, /* window width */
655 rect_window.bottom - rect_window.top, /* window height */
656 NULL, /* no parent window */
657 NULL, /* no menu in this window */
658 hInstance, /* handle of this program instance */
659 NULL); /* no additional arguments */
661 if (p_vout->p_sys->hwnd == NULL) {
662 intf_WarnMsg( 3, "vout: WinDX create window FAILED" );
666 /* now display the window */
667 ShowWindow(p_vout->p_sys->hwnd, SW_SHOW);
672 /*****************************************************************************
673 * WinDXInitDDraw: Takes care of all the DirectDraw initialisations
674 *****************************************************************************
675 * This function initialise and allocate resources for DirectDraw.
676 *****************************************************************************/
677 static int WinDXInitDDraw( vout_thread_t *p_vout )
682 intf_WarnMsg( 3, "vout: WinDX WinDXInitDDraw" );
684 /* Initialize DirectDraw */
685 dxresult = DirectDrawCreate( NULL, &p_vout->p_sys->p_ddobject, NULL );
686 if( dxresult != DD_OK )
688 intf_ErrMsg( "vout error: can't initialize Direct Draw" );
692 /* Set DirectDraw Cooperative level, ie what control we want over Windows
694 if( p_vout->b_fullscreen )
696 flags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
700 flags = DDSCL_NORMAL;
703 dxresult = IDirectDraw_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
704 p_vout->p_sys->hwnd, flags );
705 if( dxresult != DD_OK )
707 intf_ErrMsg( "vout error: can't set direct draw cooperative level." );
708 IDirectDraw_Release(p_vout->p_sys->p_ddobject);
709 p_vout->p_sys->p_ddobject = NULL;
716 /*****************************************************************************
717 * WinDXCreateDisplay: create the DirectDraw display.
718 *****************************************************************************
719 * Create and initialize display according to preferences specified in the vout
721 *****************************************************************************/
722 static int WinDXCreateDisplay( vout_thread_t *p_vout )
727 BOOL bHasOverlay, bHasColorKey, bCanStretch;
729 /* Now create the primary surface. This surface is the displayed surface */
730 /* The following two steps are important! */
731 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
732 ddsd.dwSize = sizeof(DDSURFACEDESC);
733 ddsd.dwFlags = DDSD_CAPS;
734 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
736 dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
738 &p_vout->p_sys->p_display, NULL );
739 if( dxresult != DD_OK )
741 intf_ErrMsg( "vout error: can't create direct draw primary surface." );
742 p_vout->p_sys->p_display = NULL;
746 /* Now create a clipper for our window.
747 * This clipper prevents us to modify by mistake anything on the screen
748 * (primary surface) which doesn't belong to our window */
749 dxresult = IDirectDraw_CreateClipper(p_vout->p_sys->p_ddobject, 0,
750 &p_vout->p_sys->p_clipper, NULL);
751 if( dxresult != DD_OK )
753 intf_ErrMsg( "vout error: can't create clipper." );
754 IDirectDrawSurface_Release( p_vout->p_sys->p_display );
755 p_vout->p_sys->p_display = NULL;
759 dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0,
760 p_vout->p_sys->hwnd);
761 if( dxresult != DD_OK )
763 intf_ErrMsg( "vout error: can't attach clipper to window." );
764 IDirectDrawSurface_Release( p_vout->p_sys->p_display );
765 p_vout->p_sys->p_display = NULL;
769 dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
770 p_vout->p_sys->p_clipper);
771 if( dxresult != DD_OK )
773 intf_ErrMsg( "vout error: can't attach clipper to surface." );
774 IDirectDrawSurface_Release( p_vout->p_sys->p_display );
775 p_vout->p_sys->p_display = NULL;
780 /* Probe the capabilities of the hardware */
781 /* This is just an indication of whever or not we'll support overlay,
782 * but with this test we don't know if we support YUV overlay */
783 memset( &ddcaps, 0, sizeof( DDCAPS ));
784 ddcaps.dwSize = sizeof(DDCAPS);
785 dxresult = IDirectDraw_GetCaps( p_vout->p_sys->p_ddobject,
787 if(dxresult != DD_OK )
789 intf_ErrMsg( "vout error: can't get caps." );
791 bHasColorKey = FALSE;
796 /* Determine if the hardware supports overlay surfaces */
797 bHasOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAY) ==
798 DDCAPS_OVERLAY) ? TRUE : FALSE;
799 /* Determine if the hardware supports colorkeying */
800 bHasColorKey = ((ddcaps.dwCaps & DDCAPS_COLORKEY) ==
801 DDCAPS_COLORKEY) ? TRUE : FALSE;
802 /* Determine if the hardware supports scaling of the overlay surface */
803 bCanStretch = ((ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ==
804 DDCAPS_OVERLAYSTRETCH) ? TRUE : FALSE;
805 intf_WarnMsg( 3, "vout: WinDX Caps: overlay=%i colorkey=%i stretch=%i",
806 bHasOverlay, bHasColorKey, bCanStretch );
809 if( bHasOverlay && bHasColorKey && bCanStretch )
811 if( !WinDXCreateYUVOverlay( p_vout ) )
813 /* Overlay created successfully */
814 p_vout->b_need_render = 0;
819 /* Now do some initialisation for video_output */
820 if( p_vout->b_need_render )
822 /* if we want a valid pointer to the surface memory, we must lock
824 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
825 ddsd.dwSize = sizeof(DDSURFACEDESC);
826 dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_display,
828 DDLOCK_NOSYSLOCK, NULL);
829 if ( dxresult == DDERR_SURFACELOST )
831 /* Your surface can be lost so be sure
832 * to check this and restore it if needed */
833 dxresult = IDirectDrawSurface_Restore( p_vout->p_sys->p_display );
834 dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_display,
835 NULL, &ddsd, DDLOCK_NOSYSLOCK
836 | DDLOCK_WAIT, NULL);
838 if( dxresult != DD_OK )
840 intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
844 /* Set the pointer to the surface memory */
845 p_vout->p_sys->p_windx_buf[ 0 ] = ddsd.lpSurface;
846 /* back buffer, none for now */
847 p_vout->p_sys->p_windx_buf[ 1 ] = ddsd.lpSurface;
850 /* Set thread information */
851 p_vout->i_width = ddsd.dwWidth;
852 p_vout->i_height = ddsd.dwHeight;
853 p_vout->i_bytes_per_line = ddsd.u1.lPitch;
855 p_vout->i_screen_depth = ddsd.ddpfPixelFormat.u1.dwRGBBitCount;
856 p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.u1.dwRGBBitCount/8;
858 p_vout->i_red_mask = ddsd.ddpfPixelFormat.u2.dwRBitMask;
859 p_vout->i_green_mask = ddsd.ddpfPixelFormat.u3.dwGBitMask;
860 p_vout->i_blue_mask = ddsd.ddpfPixelFormat.u4.dwBBitMask;
862 /* Unlock the Surface */
863 dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_display,
865 /* FIXME: palette in 8bpp ?? */
866 /* Set and initialize buffers */
867 vout_SetBuffers( p_vout, p_vout->p_sys->p_windx_buf[ 0 ],
868 p_vout->p_sys->p_windx_buf[ 1 ] );
872 /* Lock the surface */
873 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
874 ddsd.dwSize = sizeof(DDSURFACEDESC);
875 dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_overlay,
876 NULL, &ddsd, DDLOCK_NOSYSLOCK, NULL);
877 if ( dxresult == DDERR_SURFACELOST )
879 /* Your surface can be lost (thanks to windows) so be sure
880 * to check this every time you want to do something with
882 dxresult = IDirectDrawSurface_Restore(
883 p_vout->p_sys->p_overlay );
884 dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_overlay
885 , NULL, &ddsd,DDLOCK_NOSYSLOCK| DDLOCK_WAIT, NULL);
887 if( dxresult != DD_OK )
889 intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
893 p_vout->p_sys->p_windx_buf[ 0 ] = ddsd.lpSurface;
894 p_vout->p_sys->p_windx_buf[ 1 ] = ddsd.lpSurface;
896 /* Set thread information */
897 p_vout->i_width = ddsd.dwWidth;
898 p_vout->i_height = ddsd.dwHeight;
899 p_vout->i_bytes_per_line = ddsd.u1.lPitch;
901 /* Unlock the Surface */
902 dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
905 vout_SetBuffers( p_vout, p_vout->p_sys->p_windx_buf[ 0 ],
906 p_vout->p_sys->p_windx_buf[ 1 ] );
912 /*****************************************************************************
913 * WinDXCreateYUVOveraly: create an YUV overlay surface for the video.
914 *****************************************************************************
915 * The best method of display is with an YUV overlay because the YUV->RGB
916 * conversion is done in hardware.
917 * This function will try to create an YUV overlay.
918 *****************************************************************************/
919 static int WinDXCreateYUVOverlay( vout_thread_t *p_vout )
924 /* Now create the overlay surface. This overlay will be displayed on
925 * top of the primary surface.
926 * A color key is used to determine whether or not the overlay will be
927 * displayed, ie the overlay will be displayed in place of the primary
928 * surface wherever the primary surface will have this color.
929 * This color will be painted by WinDXClipOverlay which in turn is called
930 * by a WM_PAINT event */
932 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
933 ddsd.dwSize = sizeof(DDSURFACEDESC);
934 ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
935 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
936 ddsd.ddpfPixelFormat.dwFourCC = mmioFOURCC('Y','V','1','2');
937 ddsd.ddpfPixelFormat.u1.dwYUVBitCount = 16;
939 ddsd.dwSize = sizeof(DDSURFACEDESC);
940 ddsd.dwFlags = DDSD_CAPS |
944 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
945 ddsd.dwHeight = p_vout->p_sys->i_image_height;
946 ddsd.dwWidth = p_vout->p_sys->i_image_width;
948 dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
950 &p_vout->p_sys->p_overlay, NULL );
951 if( dxresult != DD_OK )
953 intf_ErrMsg( "vout error: can't create overlay surface." );
957 intf_WarnMsg( 3, "vout: WinDX YUV overlay created successfully" );
959 /* Hide the overlay for now */
960 IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
962 p_vout->p_sys->p_display,
970 /*****************************************************************************
971 * WinDXUpdateOverlay: Move or resize overlay surface on video display.
972 *****************************************************************************
973 * This function is used to move or resize an overlay surface on the screen.
974 * Ususally the overlay is moved by the user and thus, by a move or resize
975 * event (in vout_Manage).
976 *****************************************************************************/
977 static int WinDXUpdateOverlay( vout_thread_t *p_vout )
980 RECT rect_window, rect_client;
984 if( p_vout->p_sys->p_overlay == NULL || p_vout->b_need_render)
986 intf_WarnMsg( 3, "vout: WinDX no overlay !!" );
990 if( !p_vout->p_sys->b_display_enabled )
996 /* Now get the coordinates of the window. We don't actually want the
997 * window coordinates but these of the usable surface inside the window.
998 * By specification rect_client.right = rect_client.top = 0 */
999 GetWindowRect(p_vout->p_sys->hwnd, &rect_window);
1000 GetClientRect(p_vout->p_sys->hwnd, &rect_client);;
1001 rect_window.left = ( (rect_window.right - rect_window.left) -
1002 rect_client.right ) / 2 + rect_window.left;
1003 rect_window.right = rect_window.left + rect_client.right;
1004 rect_window.top = rect_window.bottom - rect_client.bottom;
1006 /* We want to keep the aspect ratio of the video */
1007 if( p_vout->b_scale )
1009 switch( p_vout->p_rendered_pic->i_aspect_ratio )
1011 case AR_16_9_PICTURE:
1012 if( ((rect_window.right-rect_window.left)*9)
1013 > ((rect_window.bottom-rect_window.top)*16) )
1016 temp = (rect_window.bottom-rect_window.top)*16/9;
1017 temp = (rect_window.right-rect_window.left) - temp;
1018 rect_window.left += (temp/2);
1019 rect_window.right -= (temp/2);
1024 temp = (rect_window.right-rect_window.left)*9/16;
1025 temp = (rect_window.bottom-rect_window.top) - temp;
1026 rect_window.top += (temp/2);
1027 rect_window.bottom -= (temp/2);
1031 case AR_221_1_PICTURE:
1032 if( ((rect_window.right-rect_window.left)*100)
1033 > ((rect_window.bottom-rect_window.top)*221) )
1036 temp = (rect_window.bottom-rect_window.top)*221/100;
1037 temp = (rect_window.right-rect_window.left) - temp;
1038 rect_window.left += (temp/2);
1039 rect_window.right -= (temp/2);
1044 temp = (rect_window.right-rect_window.left)*100/221;
1045 temp = (rect_window.bottom-rect_window.top) - temp;
1046 rect_window.top += (temp/2);
1047 rect_window.bottom -= (temp/2);
1051 case AR_SQUARE_PICTURE:
1052 if( (rect_window.right-rect_window.left)
1053 > (rect_window.bottom-rect_window.top) )
1056 temp = (rect_window.bottom-rect_window.top);
1057 temp = (rect_window.right-rect_window.left) - temp;
1058 rect_window.left += (temp/2);
1059 rect_window.right -= (temp/2);
1064 temp = (rect_window.right-rect_window.left);
1065 temp = (rect_window.bottom-rect_window.top) - temp;
1066 rect_window.top += (temp/2);
1067 rect_window.bottom -= (temp/2);
1071 case AR_3_4_PICTURE:
1073 if( ((rect_window.right-rect_window.left)*3)
1074 > ((rect_window.bottom-rect_window.top)*4) )
1077 temp = (rect_window.bottom-rect_window.top)*4/3;
1078 temp = (rect_window.right-rect_window.left) - temp;
1079 rect_window.left += (temp/2);
1080 rect_window.right -= (temp/2);
1085 temp = (rect_window.right-rect_window.left)*3/4;
1086 temp = (rect_window.bottom-rect_window.top) - temp;
1087 rect_window.top += (temp/2);
1088 rect_window.bottom -= (temp/2);
1095 /* Position and show the overlay */
1096 memset(&ddofx, 0, sizeof(DDOVERLAYFX));
1097 ddofx.dwSize = sizeof(DDOVERLAYFX);
1098 ddofx.dckDestColorkey.dwColorSpaceLowValue = OVERLAY_COLOR_KEY;
1099 ddofx.dckDestColorkey.dwColorSpaceHighValue = OVERLAY_COLOR_KEY;
1101 dwFlags = DDOVER_KEYDESTOVERRIDE | DDOVER_SHOW;
1103 dxresult = IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
1105 p_vout->p_sys->p_display,
1109 if(dxresult != DD_OK)
1111 intf_WarnMsg( 3, "vout: WinDX WM_MOVE can't move or resize overlay" );
1117 /*****************************************************************************
1118 * WinDXClipOveraly: Clip overlay surface on video display.
1119 *****************************************************************************
1120 * The overlay is displayed on top of the primary surface (which is the
1122 * This overlay must be clipped whenever another window is supposed to cover
1124 * For this, we use a color key which we paint on the parts of the window
1125 * which aren't covered by anything else. The video adapter will then only
1126 * display the overlay on the surfaces painted with this color key.
1127 * This function is called whenever a WM_PAINT event is generated
1129 *****************************************************************************/
1130 static int WinDXClipOverlay( vout_thread_t *p_vout )
1137 if( p_vout->p_sys->p_overlay == NULL || p_vout->b_need_render)
1139 intf_WarnMsg( 3, "vout: WinDX no overlay !!" );
1143 BeginPaint(p_vout->p_sys->hwnd, &ps);
1145 /* Fill the client area with colour key */
1146 ptClient.x = ps.rcPaint.left;
1147 ptClient.y = ps.rcPaint.top;
1148 ClientToScreen(p_vout->p_sys->hwnd, &ptClient);
1149 rectBlt.left = ptClient.x;
1150 rectBlt.top = ptClient.y;
1152 ptClient.x = ps.rcPaint.right;
1153 ptClient.y = ps.rcPaint.bottom;
1154 ClientToScreen(p_vout->p_sys->hwnd, &ptClient);
1155 rectBlt.right = ptClient.x;
1156 rectBlt.bottom = ptClient.y;
1158 memset(&ddbfx, 0, sizeof(DDBLTFX));
1159 ddbfx.dwSize = sizeof(DDBLTFX);
1160 ddbfx.u5.dwFillColor = OVERLAY_COLOR_KEY;
1162 IDirectDrawSurface_Blt(p_vout->p_sys->p_display,
1166 DDBLT_COLORFILL, // | DDBLT_WAIT,
1169 EndPaint(p_vout->p_sys->hwnd, &ps);
1174 /*****************************************************************************
1175 * WinDXCloseWindow: close the window created by WinDXCreateWindow
1176 *****************************************************************************
1177 * This function returns all resources allocated by WinDXCreateWindow.
1178 *****************************************************************************/
1179 static void WinDXCloseWindow( vout_thread_t *p_vout )
1181 HINSTANCE hInstance;
1183 if( p_vout->p_sys->hwnd != INVALID_HANDLE_VALUE )
1185 DestroyWindow( p_vout->p_sys->hwnd);
1186 p_vout->p_sys->hwnd = INVALID_HANDLE_VALUE;
1189 hInstance = GetModuleHandle(NULL);
1190 UnregisterClass( "VLC DirectX", /* class name */
1191 hInstance ); /* handle to application instance */
1195 /*****************************************************************************
1196 * WinDXCloseDDraw: Release the DDraw object allocated by WinDXInitDDraw
1197 *****************************************************************************
1198 * This function returns all resources allocated by WinDXInitDDraw.
1199 *****************************************************************************/
1200 static void WinDXCloseDDraw( vout_thread_t *p_vout )
1202 if( p_vout->p_sys->p_ddobject != NULL )
1204 IDirectDraw_Release(p_vout->p_sys->p_ddobject);
1205 p_vout->p_sys->p_ddobject = NULL;
1209 /*****************************************************************************
1210 * WinDXCloseDisplay: close and reset DirectX device
1211 *****************************************************************************
1212 * This function returns all resources allocated by WinDXCreateDisplay and
1213 * restore the original state of the device.
1214 *****************************************************************************/
1215 static void WinDXCloseDisplay( vout_thread_t *p_vout )
1217 if( p_vout->p_sys->p_display != NULL )
1219 if( p_vout->p_sys->p_overlay != NULL )
1221 IDirectDraw_Release( p_vout->p_sys->p_overlay );
1222 p_vout->p_sys->p_overlay = NULL;
1225 if( p_vout->p_sys->p_clipper != NULL )
1227 IDirectDraw_Release( p_vout->p_sys->p_clipper );
1228 p_vout->p_sys->p_clipper = NULL;
1231 IDirectDraw_Release( p_vout->p_sys->p_display );
1232 p_vout->p_sys->p_display = NULL;
1236 /*****************************************************************************
1237 * WinDXCloseYUVOverlay: close the overlay surface
1238 *****************************************************************************
1239 * This function returns all resources allocated by the overlay surface.
1240 * We also call this function when the decoded picture change its dimensions
1241 * (in that case we close the overlay surface and reopen another with the
1242 * right dimensions).
1243 *****************************************************************************/
1244 static void WinDXCloseYUVOverlay( vout_thread_t *p_vout )
1246 if( p_vout->p_sys->p_overlay != NULL )
1248 IDirectDraw_Release( p_vout->p_sys->p_overlay );
1249 p_vout->p_sys->p_overlay = NULL;
1251 p_vout->p_sys->b_display_enabled = 0;