1 /*****************************************************************************
2 * vout.c: Windows DirectX video output display method
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: directx.c,v 1.23 2003/10/17 16:40:09 gbazin 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 /*****************************************************************************
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 *****************************************************************************/
37 #include <errno.h> /* ENOMEM */
38 #include <stdlib.h> /* free() */
39 #include <string.h> /* strerror() */
50 /*****************************************************************************
52 * Defining them here allows us to get rid of the dxguid library during
54 *****************************************************************************/
56 DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 );
57 DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 );
59 /*****************************************************************************
61 *****************************************************************************/
62 static int OpenVideo ( vlc_object_t * );
63 static void CloseVideo ( vlc_object_t * );
65 static int Init ( vout_thread_t * );
66 static void End ( vout_thread_t * );
67 static int Manage ( vout_thread_t * );
68 static void Display ( vout_thread_t *, picture_t * );
70 static int NewPictureVec ( vout_thread_t *, picture_t *, int );
71 static void FreePictureVec ( vout_thread_t *, picture_t *, int );
72 static int UpdatePictureStruct( vout_thread_t *, picture_t *, int );
74 static int DirectXInitDDraw ( vout_thread_t *p_vout );
75 static void DirectXCloseDDraw ( vout_thread_t *p_vout );
76 static int DirectXCreateDisplay ( vout_thread_t *p_vout );
77 static void DirectXCloseDisplay ( vout_thread_t *p_vout );
78 static int DirectXCreateSurface ( vout_thread_t *p_vout,
79 LPDIRECTDRAWSURFACE2 *, int, int, int );
80 static void DirectXCloseSurface ( vout_thread_t *p_vout,
81 LPDIRECTDRAWSURFACE2 );
82 static int DirectXCreateClipper ( vout_thread_t *p_vout );
83 static void DirectXGetDDrawCaps ( vout_thread_t *p_vout );
84 static int DirectXLockSurface ( vout_thread_t *p_vout, picture_t *p_pic );
85 static int DirectXUnlockSurface ( vout_thread_t *p_vout, picture_t *p_pic );
87 /* Object variables callbacks */
88 static int OnTopCallback( vlc_object_t *, char const *,
89 vlc_value_t, vlc_value_t, void * );
91 /*****************************************************************************
93 *****************************************************************************/
94 #define ON_TOP_TEXT N_("Always on top")
95 #define ON_TOP_LONGTEXT N_("Place the directx window on top of other windows")
96 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
97 #define HW_YUV_LONGTEXT N_( \
98 "Try to use hardware acceleration for YUV->RGB conversions. " \
99 "This option doesn't have any effect when using overlays." )
100 #define SYSMEM_TEXT N_("Use video buffers in system memory")
101 #define SYSMEM_LONGTEXT N_( \
102 "Create video buffers in system memory instead of video memory. This " \
103 "isn't recommended as usually using video memory allows to benefit from " \
104 "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
105 "This option doesn't have any effect when using overlays." )
106 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
107 #define TRIPLEBUF_LONGTEXT N_( \
108 "Try to use triple bufferring when using YUV overlays. That results in " \
109 "much better video quality (no flickering)." )
112 add_category_hint( N_("Video"), NULL, VLC_FALSE );
113 add_bool( "directx-on-top", 0, NULL, ON_TOP_TEXT, ON_TOP_LONGTEXT, VLC_FALSE );
114 add_bool( "directx-hw-yuv", 1, NULL, HW_YUV_TEXT, HW_YUV_LONGTEXT, VLC_TRUE );
115 add_bool( "directx-use-sysmem", 0, NULL, SYSMEM_TEXT, SYSMEM_LONGTEXT, VLC_TRUE );
116 add_bool( "directx-3buffering", 1, NULL, TRIPLEBUF_TEXT, TRIPLEBUF_LONGTEXT, VLC_TRUE );
117 set_description( _("DirectX video output") );
118 set_capability( "video output", 100 );
119 add_shortcut( "directx" );
120 set_callbacks( OpenVideo, CloseVideo );
124 /* check if we registered a window class because we need to
127 if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
128 UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
131 /*****************************************************************************
132 * OpenVideo: allocate DirectX video thread output method
133 *****************************************************************************
134 * This function allocates and initialize the DirectX vout method.
135 *****************************************************************************/
136 static int OpenVideo( vlc_object_t *p_this )
138 vout_thread_t * p_vout = (vout_thread_t *)p_this;
139 vlc_value_t val, text;
141 /* Allocate structure */
142 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
143 if( p_vout->p_sys == NULL )
145 msg_Err( p_vout, "out of memory" );
149 /* Initialisations */
150 p_vout->pf_init = Init;
151 p_vout->pf_end = End;
152 p_vout->pf_manage = Manage;
153 p_vout->pf_render = NULL;
154 p_vout->pf_display = Display;
156 p_vout->p_sys->p_ddobject = NULL;
157 p_vout->p_sys->p_display = NULL;
158 p_vout->p_sys->p_current_surface = NULL;
159 p_vout->p_sys->p_clipper = NULL;
160 p_vout->p_sys->hbrush = NULL;
161 p_vout->p_sys->hwnd = NULL;
162 p_vout->p_sys->hparent = NULL;
163 p_vout->p_sys->i_changes = 0;
164 p_vout->p_sys->b_caps_overlay_clipping = 0;
165 SetRectEmpty( &p_vout->p_sys->rect_display );
166 p_vout->p_sys->b_using_overlay = config_GetInt( p_vout, "overlay" );
167 p_vout->p_sys->b_use_sysmem = config_GetInt( p_vout, "directx-use-sysmem");
168 p_vout->p_sys->b_hw_yuv = config_GetInt( p_vout, "directx-hw-yuv" );
169 p_vout->p_sys->b_3buf_overlay = config_GetInt( p_vout, "directx-3buffering" );
171 p_vout->p_sys->b_cursor_hidden = 0;
172 p_vout->p_sys->i_lastmoved = mdate();
174 /* Set main window's size */
175 p_vout->p_sys->i_window_width = p_vout->i_window_width;
176 p_vout->p_sys->i_window_height = p_vout->i_window_height;
178 /* Create the DirectXEventThread, this thread is created by us to isolate
179 * the Win32 PeekMessage function calls. We want to do this because
180 * Windows can stay blocked inside this call for a long time, and when
181 * this happens it thus blocks vlc's video_output thread.
182 * DirectXEventThread will take care of the creation of the video
183 * window (because PeekMessage has to be called from the same thread which
184 * created the window). */
185 msg_Dbg( p_vout, "creating DirectXEventThread" );
186 p_vout->p_sys->p_event =
187 vlc_object_create( p_vout, sizeof(event_thread_t) );
188 p_vout->p_sys->p_event->p_vout = p_vout;
189 if( vlc_thread_create( p_vout->p_sys->p_event,
190 "DirectX Events Thread", DirectXEventThread,
193 msg_Err( p_vout, "cannot create DirectXEventThread" );
194 vlc_object_destroy( p_vout->p_sys->p_event );
195 p_vout->p_sys->p_event = NULL;
199 if( p_vout->p_sys->p_event->b_error )
201 msg_Err( p_vout, "DirectXEventThread failed" );
205 vlc_object_attach( p_vout->p_sys->p_event, p_vout );
207 msg_Dbg( p_vout, "DirectXEventThread running" );
209 /* Initialise DirectDraw */
210 if( DirectXInitDDraw( p_vout ) )
212 msg_Err( p_vout, "cannot initialize DirectDraw" );
216 /* Create the directx display */
217 if( DirectXCreateDisplay( p_vout ) )
219 msg_Err( p_vout, "cannot initialize DirectDraw" );
223 /* Add a variable to indicate if the window should be on top of others */
224 var_Create( p_vout, "directx-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
225 text.psz_string = _("Always on top");
226 var_Change( p_vout, "directx-on-top", VLC_VAR_SETTEXT, &text, NULL );
227 var_Get( p_vout, "directx-on-top", &val );
228 p_vout->p_sys->b_on_top_change = val.b_bool;
229 var_AddCallback( p_vout, "directx-on-top", OnTopCallback, NULL );
234 CloseVideo( VLC_OBJECT(p_vout) );
238 /*****************************************************************************
239 * Init: initialize DirectX video thread output method
240 *****************************************************************************
241 * This function create the directx surfaces needed by the output thread.
242 * It is called at the beginning of the thread.
243 *****************************************************************************/
244 static int Init( vout_thread_t *p_vout )
248 /* Initialize the output structure.
249 * Since DirectDraw can do rescaling for us, stick to the default
250 * coordinates and aspect. */
251 p_vout->output.i_width = p_vout->render.i_width;
252 p_vout->output.i_height = p_vout->render.i_height;
253 p_vout->output.i_aspect = p_vout->render.i_aspect;
255 #define MAX_DIRECTBUFFERS 1
256 /* Right now we use only 1 directbuffer because we don't want the
257 * video decoder to decode directly into direct buffers as they are
258 * created into video memory and video memory is _really_ slow */
260 /* Choose the chroma we will try first. */
261 switch( p_vout->render.i_chroma )
263 case VLC_FOURCC('Y','U','Y','2'):
264 case VLC_FOURCC('Y','U','N','V'):
265 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
267 case VLC_FOURCC('U','Y','V','Y'):
268 case VLC_FOURCC('U','Y','N','V'):
269 case VLC_FOURCC('Y','4','2','2'):
270 p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y');
272 case VLC_FOURCC('Y','V','Y','U'):
273 p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U');
276 p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
280 NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
282 i_chroma_backup = p_vout->output.i_chroma;
284 if( !I_OUTPUTPICTURES )
286 /* hmmm, it didn't work! Let's try commonly supported chromas */
287 if( p_vout->output.i_chroma != VLC_FOURCC('Y','V','1','2') )
289 p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
290 NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
292 if( !I_OUTPUTPICTURES )
294 /* hmmm, it still didn't work! Let's try another one */
295 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
296 NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
300 if( !I_OUTPUTPICTURES )
302 /* If it still didn't work then don't try to use an overlay */
303 p_vout->output.i_chroma = i_chroma_backup;
304 p_vout->p_sys->b_using_overlay = 0;
305 NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
308 /* Change the window title bar text */
309 if( p_vout->p_sys->hparent )
311 else if( p_vout->p_sys->b_using_overlay )
312 SetWindowText( p_vout->p_sys->hwnd,
313 VOUT_TITLE " (hardware YUV overlay DirectX output)" );
314 else if( p_vout->p_sys->b_hw_yuv )
315 SetWindowText( p_vout->p_sys->hwnd,
316 VOUT_TITLE " (hardware YUV DirectX output)" );
317 else SetWindowText( p_vout->p_sys->hwnd,
318 VOUT_TITLE " (software RGB DirectX output)" );
323 /*****************************************************************************
324 * End: terminate Sys video thread output method
325 *****************************************************************************
326 * Terminate an output method created by Create.
327 * It is called at the end of the thread.
328 *****************************************************************************/
329 static void End( vout_thread_t *p_vout )
331 FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES );
335 /*****************************************************************************
336 * CloseVideo: destroy Sys video thread output method
337 *****************************************************************************
338 * Terminate an output method created by Create
339 *****************************************************************************/
340 static void CloseVideo( vlc_object_t *p_this )
342 vout_thread_t * p_vout = (vout_thread_t *)p_this;
344 msg_Dbg( p_vout, "CloseVideo" );
346 var_Destroy( p_vout, "directx-on-top" );
348 DirectXCloseDisplay( p_vout );
349 DirectXCloseDDraw( p_vout );
351 if( p_vout->p_sys->p_event )
353 vlc_object_detach( p_vout->p_sys->p_event );
355 /* Kill DirectXEventThread */
356 p_vout->p_sys->p_event->b_die = VLC_TRUE;
358 /* we need to be sure DirectXEventThread won't stay stuck in
359 * GetMessage, so we send a fake message */
360 if( p_vout->p_sys->hwnd )
362 PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
365 vlc_thread_join( p_vout->p_sys->p_event );
366 vlc_object_destroy( p_vout->p_sys->p_event );
371 free( p_vout->p_sys );
372 p_vout->p_sys = NULL;
376 /*****************************************************************************
377 * Manage: handle Sys events
378 *****************************************************************************
379 * This function should be called regularly by the video output thread.
380 * It returns a non null value if an error occured.
381 *****************************************************************************/
382 static int Manage( vout_thread_t *p_vout )
384 WINDOWPLACEMENT window_placement;
386 /* If we do not control our window, we check for geometry changes
387 * ourselves because the parent might not send us its events. */
388 if( p_vout->p_sys->hparent )
390 DirectXUpdateRects( p_vout, VLC_FALSE );
393 /* We used to call the Win32 PeekMessage function here to read the window
394 * messages. But since window can stay blocked into this function for a
395 * long time (for example when you move your window on the screen), I
396 * decided to isolate PeekMessage in another thread. */
401 if( p_vout->i_changes & VOUT_SCALE_CHANGE
402 || p_vout->p_sys->i_changes & VOUT_SCALE_CHANGE )
404 msg_Dbg( p_vout, "scale change" );
405 if( !p_vout->p_sys->b_using_overlay )
406 InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
408 DirectXUpdateOverlay( p_vout );
409 p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
410 p_vout->p_sys->i_changes &= ~VOUT_SCALE_CHANGE;
416 if( p_vout->i_changes & VOUT_SIZE_CHANGE
417 || p_vout->p_sys->i_changes & VOUT_SIZE_CHANGE )
419 msg_Dbg( p_vout, "size change" );
420 if( !p_vout->p_sys->b_using_overlay )
421 InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
423 DirectXUpdateOverlay( p_vout );
424 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
425 p_vout->p_sys->i_changes &= ~VOUT_SIZE_CHANGE;
431 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
432 || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
436 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
438 /* We need to switch between Maximized and Normal sized window */
439 window_placement.length = sizeof(WINDOWPLACEMENT);
440 GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
441 if( p_vout->b_fullscreen )
443 /* Maximized window */
444 window_placement.showCmd = SW_SHOWMAXIMIZED;
445 /* Change window style, no borders and no title bar */
446 SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, 0 );
452 window_placement.showCmd = SW_SHOWNORMAL;
453 /* Change window style, borders and title bar */
454 SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
455 WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE );
458 SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
460 /* Update the object variable and trigger callback */
461 val.b_bool = p_vout->b_fullscreen;
462 var_Set( p_vout, "fullscreen", val );
464 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
465 p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
471 if( (!p_vout->p_sys->b_cursor_hidden) &&
472 ( (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 ) )
474 /* Hide the mouse automatically */
475 if( p_vout->p_sys->hwnd != p_vout->p_sys->hparent )
477 p_vout->p_sys->b_cursor_hidden = VLC_TRUE;
478 PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
483 * "Always on top" status change
485 if( p_vout->p_sys->b_on_top_change )
488 HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
490 var_Get( p_vout, "directx-on-top", &val );
492 /* Set the window on top if necessary */
493 if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
496 CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
497 MF_BYCOMMAND | MFS_CHECKED );
498 SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
499 SWP_NOSIZE | SWP_NOMOVE );
502 /* The window shouldn't be on top */
503 if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
506 CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
507 MF_BYCOMMAND | MFS_UNCHECKED );
508 SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
509 SWP_NOSIZE | SWP_NOMOVE );
512 p_vout->p_sys->b_on_top_change = VLC_FALSE;
515 /* Check if the event thread is still running */
516 if( p_vout->p_sys->p_event->b_die )
518 return VLC_EGENERIC; /* exit */
524 /*****************************************************************************
525 * Display: displays previously rendered output
526 *****************************************************************************
527 * This function sends the currently rendered image to the display, wait until
528 * it is displayed and switch the two rendering buffers, preparing next frame.
529 *****************************************************************************/
530 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
534 if( (p_vout->p_sys->p_display == NULL) )
536 msg_Warn( p_vout, "no display!!" );
540 /* Our surface can be lost so be sure to check this
541 * and restore it if need be */
542 if( IDirectDrawSurface2_IsLost( p_vout->p_sys->p_display )
543 == DDERR_SURFACELOST )
545 if( IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ) == DD_OK &&
546 p_vout->p_sys->b_using_overlay )
547 DirectXUpdateOverlay( p_vout );
550 if( !p_vout->p_sys->b_using_overlay )
554 /* We ask for the "NOTEARING" option */
555 memset( &ddbltfx, 0, sizeof(DDBLTFX) );
556 ddbltfx.dwSize = sizeof(DDBLTFX);
557 ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
559 /* Blit video surface to display */
560 dxresult = IDirectDrawSurface2_Blt( p_vout->p_sys->p_display,
561 &p_vout->p_sys->rect_dest_clipped,
562 p_pic->p_sys->p_surface,
563 &p_vout->p_sys->rect_src_clipped,
564 DDBLT_ASYNC, &ddbltfx );
565 if( dxresult != DD_OK )
567 msg_Warn( p_vout, "could not blit surface (error %li)", dxresult );
572 else /* using overlay */
574 /* Flip the overlay buffers if we are using back buffers */
575 if( p_pic->p_sys->p_front_surface == p_pic->p_sys->p_surface )
580 dxresult = IDirectDrawSurface2_Flip( p_pic->p_sys->p_front_surface,
582 if( dxresult != DD_OK )
584 msg_Warn( p_vout, "could not flip overlay (error %li)", dxresult );
587 /* set currently displayed pic */
588 p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface;
590 /* Lock surface to get all the required info */
591 if( DirectXLockSurface( p_vout, p_pic ) )
594 msg_Warn( p_vout, "cannot lock surface" );
597 DirectXUnlockSurface( p_vout, p_pic );
602 /* following functions are local */
604 /*****************************************************************************
605 * DirectXInitDDraw: Takes care of all the DirectDraw initialisations
606 *****************************************************************************
607 * This function initialise and allocate resources for DirectDraw.
608 *****************************************************************************/
609 static int DirectXInitDDraw( vout_thread_t *p_vout )
612 HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
613 LPDIRECTDRAW p_ddobject;
615 msg_Dbg( p_vout, "DirectXInitDDraw" );
617 /* load direct draw DLL */
618 p_vout->p_sys->hddraw_dll = LoadLibrary("DDRAW.DLL");
619 if( p_vout->p_sys->hddraw_dll == NULL )
621 msg_Warn( p_vout, "DirectXInitDDraw failed loading ddraw.dll" );
625 OurDirectDrawCreate =
626 (void *)GetProcAddress(p_vout->p_sys->hddraw_dll, "DirectDrawCreate");
627 if ( OurDirectDrawCreate == NULL )
629 msg_Err( p_vout, "DirectXInitDDraw failed GetProcAddress" );
633 /* Initialize DirectDraw now */
634 dxresult = OurDirectDrawCreate( NULL, &p_ddobject, NULL );
635 if( dxresult != DD_OK )
637 msg_Err( p_vout, "DirectXInitDDraw cannot initialize DDraw" );
641 /* Get the IDirectDraw2 interface */
642 dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2,
643 (LPVOID *)&p_vout->p_sys->p_ddobject );
644 /* Release the unused interface */
645 IDirectDraw_Release( p_ddobject );
646 if( dxresult != DD_OK )
648 msg_Err( p_vout, "cannot get IDirectDraw2 interface" );
652 /* Set DirectDraw Cooperative level, ie what control we want over Windows
654 dxresult = IDirectDraw2_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
655 p_vout->p_sys->hwnd, DDSCL_NORMAL );
656 if( dxresult != DD_OK )
658 msg_Err( p_vout, "cannot set direct draw cooperative level" );
662 /* Probe the capabilities of the hardware */
663 DirectXGetDDrawCaps( p_vout );
665 msg_Dbg( p_vout, "End DirectXInitDDraw" );
669 if( p_vout->p_sys->p_ddobject )
670 IDirectDraw2_Release( p_vout->p_sys->p_ddobject );
671 if( p_vout->p_sys->hddraw_dll )
672 FreeLibrary( p_vout->p_sys->hddraw_dll );
673 p_vout->p_sys->hddraw_dll = NULL;
674 p_vout->p_sys->p_ddobject = NULL;
678 /*****************************************************************************
679 * DirectXCreateDisplay: create the DirectDraw display.
680 *****************************************************************************
681 * Create and initialize display according to preferences specified in the vout
683 *****************************************************************************/
684 static int DirectXCreateDisplay( vout_thread_t *p_vout )
688 LPDIRECTDRAWSURFACE p_display;
689 DDPIXELFORMAT pixel_format;
691 msg_Dbg( p_vout, "DirectXCreateDisplay" );
693 /* Now get the primary surface. This surface is what you actually see
695 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
696 ddsd.dwSize = sizeof(DDSURFACEDESC);
697 ddsd.dwFlags = DDSD_CAPS;
698 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
700 dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
703 if( dxresult != DD_OK )
705 msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult );
709 dxresult = IDirectDrawSurface_QueryInterface( p_display,
710 &IID_IDirectDrawSurface2,
711 (LPVOID *)&p_vout->p_sys->p_display );
712 /* Release the old interface */
713 IDirectDrawSurface_Release( p_display );
714 if ( dxresult != DD_OK )
716 msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface "
717 "(error %li)", dxresult );
721 /* The clipper will be used only in non-overlay mode */
722 DirectXCreateClipper( p_vout );
726 /* compute the colorkey pixel value from the RGB value we've got */
727 memset( &pixel_format, 0, sizeof( DDPIXELFORMAT ));
728 pixel_format.dwSize = sizeof( DDPIXELFORMAT );
729 dxresult = IDirectDrawSurface2_GetPixelFormat( p_vout->p_sys->p_display,
731 if( dxresult != DD_OK )
733 msg_Warn( p_vout, "DirectXUpdateOverlay GetPixelFormat failed "
734 "(error %li)", dxresult );
736 p_vout->p_sys->i_colorkey = (DWORD)((( p_vout->p_sys->i_rgb_colorkey
737 * pixel_format.dwRBitMask) / 255)
738 & pixel_format.dwRBitMask );
745 /*****************************************************************************
746 * DirectXCreateClipper: Create a clipper that will be used when blitting the
747 * RGB surface to the main display.
748 *****************************************************************************
749 * This clipper prevents us to modify by mistake anything on the screen
750 * which doesn't belong to our window. For example when a part of our video
751 * window is hidden by another window.
752 *****************************************************************************/
753 static int DirectXCreateClipper( vout_thread_t *p_vout )
757 msg_Dbg( p_vout, "DirectXCreateClipper" );
759 /* Create the clipper */
760 dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0,
761 &p_vout->p_sys->p_clipper, NULL );
762 if( dxresult != DD_OK )
764 msg_Warn( p_vout, "cannot create clipper (error %li)", dxresult );
768 /* Associate the clipper to the window */
769 dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0,
770 p_vout->p_sys->hwnd);
771 if( dxresult != DD_OK )
773 msg_Warn( p_vout, "cannot attach clipper to window (error %li)",
778 /* associate the clipper with the surface */
779 dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
780 p_vout->p_sys->p_clipper);
781 if( dxresult != DD_OK )
783 msg_Warn( p_vout, "cannot attach clipper to surface (error %li)",
791 if( p_vout->p_sys->p_clipper )
793 IDirectDrawClipper_Release( p_vout->p_sys->p_clipper );
795 p_vout->p_sys->p_clipper = NULL;
799 /*****************************************************************************
800 * DirectXCreateSurface: create an YUV overlay or RGB surface for the video.
801 *****************************************************************************
802 * The best method of display is with an YUV overlay because the YUV->RGB
803 * conversion is done in hardware.
804 * You can also create a plain RGB surface.
805 * ( Maybe we could also try an RGB overlay surface, which could have hardware
806 * scaling and which would also be faster in window mode because you don't
807 * need to do any blitting to the main display...)
808 *****************************************************************************/
809 static int DirectXCreateSurface( vout_thread_t *p_vout,
810 LPDIRECTDRAWSURFACE2 *pp_surface_final,
811 int i_chroma, int b_overlay,
815 LPDIRECTDRAWSURFACE p_surface;
818 /* Create the video surface */
821 /* Now try to create the YUV overlay surface.
822 * This overlay will be displayed on top of the primary surface.
823 * A color key is used to determine whether or not the overlay will be
824 * displayed, ie the overlay will be displayed in place of the primary
825 * surface wherever the primary surface will have this color.
826 * The video window has been created with a background of this color so
827 * the overlay will be only displayed on top of this window */
829 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
830 ddsd.dwSize = sizeof(DDSURFACEDESC);
831 ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
832 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
833 ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
834 ddsd.dwFlags = DDSD_CAPS |
838 ddsd.dwFlags |= (i_backbuffers ? DDSD_BACKBUFFERCOUNT : 0);
839 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY |
841 ddsd.ddsCaps.dwCaps |= (i_backbuffers ? DDSCAPS_COMPLEX | DDSCAPS_FLIP
843 ddsd.dwHeight = p_vout->render.i_height;
844 ddsd.dwWidth = p_vout->render.i_width;
845 ddsd.dwBackBufferCount = i_backbuffers;
847 dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
850 if( dxresult != DD_OK )
852 *pp_surface_final = NULL;
859 vlc_bool_t b_rgb_surface =
860 ( i_chroma == VLC_FOURCC('R','G','B','2') )
861 || ( i_chroma == VLC_FOURCC('R','V','1','5') )
862 || ( i_chroma == VLC_FOURCC('R','V','1','6') )
863 || ( i_chroma == VLC_FOURCC('R','V','2','4') )
864 || ( i_chroma == VLC_FOURCC('R','V','3','2') );
866 memset( &ddsd, 0, sizeof( DDSURFACEDESC ) );
867 ddsd.dwSize = sizeof(DDSURFACEDESC);
868 ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
869 ddsd.dwFlags = DDSD_HEIGHT |
872 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
873 ddsd.dwHeight = p_vout->render.i_height;
874 ddsd.dwWidth = p_vout->render.i_width;
876 if( p_vout->p_sys->b_use_sysmem )
877 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
879 ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
883 ddsd.dwFlags |= DDSD_PIXELFORMAT;
884 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
885 ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
888 dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
891 if( dxresult != DD_OK )
893 *pp_surface_final = NULL;
898 /* Now that the surface is created, try to get a newer DirectX interface */
899 dxresult = IDirectDrawSurface_QueryInterface( p_surface,
900 &IID_IDirectDrawSurface2,
901 (LPVOID *)pp_surface_final );
902 IDirectDrawSurface_Release( p_surface ); /* Release the old interface */
903 if ( dxresult != DD_OK )
905 msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface "
906 "(error %li)", dxresult );
907 *pp_surface_final = NULL;
913 /* Check the overlay is useable as some graphics cards allow creating
914 * several overlays but only one can be used at one time. */
915 p_vout->p_sys->p_current_surface = *pp_surface_final;
916 if( DirectXUpdateOverlay( p_vout ) != VLC_SUCCESS )
918 IDirectDrawSurface2_Release( *pp_surface_final );
919 *pp_surface_final = NULL;
920 msg_Err( p_vout, "overlay unuseable (might already be in use)" );
928 /*****************************************************************************
929 * DirectXUpdateOverlay: Move or resize overlay surface on video display.
930 *****************************************************************************
931 * This function is used to move or resize an overlay surface on the screen.
932 * Ususally the overlay is moved by the user and thus, by a move or resize
934 *****************************************************************************/
935 int DirectXUpdateOverlay( vout_thread_t *p_vout )
941 if( p_vout->p_sys->p_current_surface == NULL ||
942 !p_vout->p_sys->b_using_overlay )
945 /* The new window dimensions should already have been computed by the
946 * caller of this function */
948 /* Position and show the overlay */
949 memset(&ddofx, 0, sizeof(DDOVERLAYFX));
950 ddofx.dwSize = sizeof(DDOVERLAYFX);
951 ddofx.dckDestColorkey.dwColorSpaceLowValue = p_vout->p_sys->i_colorkey;
952 ddofx.dckDestColorkey.dwColorSpaceHighValue = p_vout->p_sys->i_colorkey;
954 dwFlags = DDOVER_SHOW;
955 if( !p_vout->p_sys->b_caps_overlay_clipping )
956 dwFlags |= DDOVER_KEYDESTOVERRIDE;
958 dxresult = IDirectDrawSurface2_UpdateOverlay(
959 p_vout->p_sys->p_current_surface,
960 &p_vout->p_sys->rect_src_clipped,
961 p_vout->p_sys->p_display,
962 &p_vout->p_sys->rect_dest_clipped,
964 if(dxresult != DD_OK)
967 "DirectXUpdateOverlay cannot move or resize overlay" );
974 /*****************************************************************************
975 * DirectXCloseDDraw: Release the DDraw object allocated by DirectXInitDDraw
976 *****************************************************************************
977 * This function returns all resources allocated by DirectXInitDDraw.
978 *****************************************************************************/
979 static void DirectXCloseDDraw( vout_thread_t *p_vout )
981 msg_Dbg( p_vout, "DirectXCloseDDraw" );
982 if( p_vout->p_sys->p_ddobject != NULL )
984 IDirectDraw2_Release(p_vout->p_sys->p_ddobject);
985 p_vout->p_sys->p_ddobject = NULL;
988 if( p_vout->p_sys->hddraw_dll != NULL )
990 FreeLibrary( p_vout->p_sys->hddraw_dll );
991 p_vout->p_sys->hddraw_dll = NULL;
995 /*****************************************************************************
996 * DirectXCloseDisplay: close and reset the DirectX display device
997 *****************************************************************************
998 * This function returns all resources allocated by DirectXCreateDisplay.
999 *****************************************************************************/
1000 static void DirectXCloseDisplay( vout_thread_t *p_vout )
1002 msg_Dbg( p_vout, "DirectXCloseDisplay" );
1004 if( p_vout->p_sys->p_clipper != NULL )
1006 msg_Dbg( p_vout, "DirectXCloseDisplay clipper" );
1007 IDirectDrawClipper_Release( p_vout->p_sys->p_clipper );
1008 p_vout->p_sys->p_clipper = NULL;
1011 if( p_vout->p_sys->p_display != NULL )
1013 msg_Dbg( p_vout, "DirectXCloseDisplay display" );
1014 IDirectDrawSurface2_Release( p_vout->p_sys->p_display );
1015 p_vout->p_sys->p_display = NULL;
1019 /*****************************************************************************
1020 * DirectXCloseSurface: close the YUV overlay or RGB surface.
1021 *****************************************************************************
1022 * This function returns all resources allocated for the surface.
1023 *****************************************************************************/
1024 static void DirectXCloseSurface( vout_thread_t *p_vout,
1025 LPDIRECTDRAWSURFACE2 p_surface )
1027 msg_Dbg( p_vout, "DirectXCloseSurface" );
1028 if( p_surface != NULL )
1030 IDirectDrawSurface2_Release( p_surface );
1034 /*****************************************************************************
1035 * NewPictureVec: allocate a vector of identical pictures
1036 *****************************************************************************
1037 * Returns 0 on success, -1 otherwise
1038 *****************************************************************************/
1039 static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic,
1043 int i_ret = VLC_SUCCESS;
1044 LPDIRECTDRAWSURFACE2 p_surface;
1046 msg_Dbg( p_vout, "NewPictureVec overlay:%s chroma:%.4s",
1047 p_vout->p_sys->b_using_overlay ? "yes" : "no",
1048 (char *)&p_vout->output.i_chroma );
1050 I_OUTPUTPICTURES = 0;
1052 /* First we try to use an YUV overlay surface.
1053 * The overlay surface that we create won't be used to decode directly
1054 * into it because accessing video memory directly is way to slow (remember
1055 * that pictures are decoded macroblock per macroblock). Instead the video
1056 * will be decoded in picture buffers in system memory which will then be
1057 * memcpy() to the overlay surface. */
1058 if( p_vout->p_sys->b_using_overlay )
1060 /* Triple buffering rocks! it doesn't have any processing overhead
1061 * (you don't have to wait for the vsync) and provides for a very nice
1062 * video quality (no tearing). */
1063 if( p_vout->p_sys->b_3buf_overlay )
1064 i_ret = DirectXCreateSurface( p_vout, &p_surface,
1065 p_vout->output.i_chroma,
1066 p_vout->p_sys->b_using_overlay,
1067 2 /* number of backbuffers */ );
1069 if( !p_vout->p_sys->b_3buf_overlay || i_ret != VLC_SUCCESS )
1071 /* Try to reduce the number of backbuffers */
1072 i_ret = DirectXCreateSurface( p_vout, &p_surface,
1073 p_vout->output.i_chroma,
1074 p_vout->p_sys->b_using_overlay,
1075 0 /* number of backbuffers */ );
1078 if( i_ret == VLC_SUCCESS )
1081 picture_t front_pic;
1082 picture_sys_t front_pic_sys;
1083 front_pic.p_sys = &front_pic_sys;
1085 /* Allocate internal structure */
1086 p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) );
1087 if( p_pic[0].p_sys == NULL )
1089 DirectXCloseSurface( p_vout, p_surface );
1093 /* set front buffer */
1094 p_pic[0].p_sys->p_front_surface = p_surface;
1096 /* Get the back buffer */
1097 memset( &dds_caps, 0, sizeof( DDSCAPS ) );
1098 dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1099 if( DD_OK != IDirectDrawSurface2_GetAttachedSurface(
1100 p_surface, &dds_caps,
1101 &p_pic[0].p_sys->p_surface ) )
1103 msg_Warn( p_vout, "NewPictureVec could not get back buffer" );
1104 /* front buffer is the same as back buffer */
1105 p_pic[0].p_sys->p_surface = p_surface;
1109 p_vout->p_sys->p_current_surface = front_pic.p_sys->p_surface =
1110 p_pic[0].p_sys->p_front_surface;
1112 /* Reset the front buffer memory */
1113 if( DirectXLockSurface( p_vout, &front_pic ) == VLC_SUCCESS )
1116 for( i = 0; i < front_pic.i_planes; i++ )
1117 for( j = 0; j < front_pic.p[i].i_lines; j++)
1118 memset( front_pic.p[i].p_pixels + j *
1119 front_pic.p[i].i_pitch, 127,
1120 front_pic.p[i].i_visible_pitch );
1122 DirectXUnlockSurface( p_vout, &front_pic );
1125 DirectXUpdateOverlay( p_vout );
1126 I_OUTPUTPICTURES = 1;
1127 msg_Dbg( p_vout, "YUV overlay created successfully" );
1131 /* As we can't have an overlay, we'll try to create a plain offscreen
1132 * surface. This surface will reside in video memory because there's a
1133 * better chance then that we'll be able to use some kind of hardware
1134 * acceleration like rescaling, blitting or YUV->RGB conversions.
1135 * We then only need to blit this surface onto the main display when we
1136 * want to display it */
1137 if( !p_vout->p_sys->b_using_overlay )
1139 if( p_vout->p_sys->b_hw_yuv )
1143 vlc_bool_t b_result = VLC_FALSE;
1145 /* Check if the chroma is supported first. This is required
1146 * because a few buggy drivers don't mind creating the surface
1147 * even if they don't know about the chroma. */
1148 if( IDirectDraw2_GetFourCCCodes( p_vout->p_sys->p_ddobject,
1151 pi_codes = malloc( i_codes * sizeof(DWORD) );
1152 if( pi_codes && IDirectDraw2_GetFourCCCodes(
1153 p_vout->p_sys->p_ddobject, &i_codes, pi_codes ) )
1155 for( i = 0; i < (int)i_codes; i++ )
1157 if( p_vout->output.i_chroma == pi_codes[i] )
1159 b_result = VLC_TRUE;
1167 i_ret = DirectXCreateSurface( p_vout, &p_surface,
1168 p_vout->output.i_chroma,
1170 0 /* no back buffers */ );
1172 p_vout->p_sys->b_hw_yuv = VLC_FALSE;
1175 if( i_ret || !p_vout->p_sys->b_hw_yuv )
1177 /* Our last choice is to use a plain RGB surface */
1178 DDPIXELFORMAT ddpfPixelFormat;
1180 ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
1181 IDirectDrawSurface2_GetPixelFormat( p_vout->p_sys->p_display,
1184 if( ddpfPixelFormat.dwFlags & DDPF_RGB )
1186 switch( ddpfPixelFormat.dwRGBBitCount )
1188 case 8: /* FIXME: set the palette */
1189 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
1192 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
1195 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
1198 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
1201 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
1204 msg_Err( p_vout, "unknown screen depth" );
1205 return VLC_EGENERIC;
1207 p_vout->output.i_rmask = ddpfPixelFormat.dwRBitMask;
1208 p_vout->output.i_gmask = ddpfPixelFormat.dwGBitMask;
1209 p_vout->output.i_bmask = ddpfPixelFormat.dwBBitMask;
1212 p_vout->p_sys->b_hw_yuv = 0;
1214 i_ret = DirectXCreateSurface( p_vout, &p_surface,
1215 p_vout->output.i_chroma,
1217 0 /* no back buffers */ );
1219 if( i_ret && !p_vout->p_sys->b_use_sysmem )
1221 /* Give it a last try with b_use_sysmem enabled */
1222 p_vout->p_sys->b_use_sysmem = 1;
1224 i_ret = DirectXCreateSurface( p_vout, &p_surface,
1225 p_vout->output.i_chroma,
1227 0 /* no back buffers */ );
1231 if( i_ret == VLC_SUCCESS )
1233 /* Allocate internal structure */
1234 p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) );
1235 if( p_pic[0].p_sys == NULL )
1237 DirectXCloseSurface( p_vout, p_surface );
1241 p_pic[0].p_sys->p_surface = p_pic[0].p_sys->p_front_surface
1244 I_OUTPUTPICTURES = 1;
1246 msg_Dbg( p_vout, "created plain surface of chroma:%.4s",
1247 (char *)&p_vout->output.i_chroma );
1252 /* Now that we've got all our direct-buffers, we can finish filling in the
1253 * picture_t structures */
1254 for( i = 0; i < I_OUTPUTPICTURES; i++ )
1256 p_pic[i].i_status = DESTROYED_PICTURE;
1257 p_pic[i].i_type = DIRECT_PICTURE;
1258 p_pic[i].pf_lock = DirectXLockSurface;
1259 p_pic[i].pf_unlock = DirectXUnlockSurface;
1260 PP_OUTPUTPICTURE[i] = &p_pic[i];
1262 if( DirectXLockSurface( p_vout, &p_pic[i] ) != VLC_SUCCESS )
1265 FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES );
1266 I_OUTPUTPICTURES = 0;
1267 msg_Err( p_vout, "cannot lock surface" );
1268 return VLC_EGENERIC;
1270 DirectXUnlockSurface( p_vout, &p_pic[i] );
1273 msg_Dbg( p_vout, "End NewPictureVec (%s)",
1274 I_OUTPUTPICTURES ? "succeeded" : "failed" );
1279 /*****************************************************************************
1280 * FreePicture: destroy a picture vector allocated with NewPictureVec
1281 *****************************************************************************
1283 *****************************************************************************/
1284 static void FreePictureVec( vout_thread_t *p_vout, picture_t *p_pic,
1289 for( i = 0; i < i_num_pics; i++ )
1291 DirectXCloseSurface( p_vout, p_pic[i].p_sys->p_front_surface );
1293 for( i = 0; i < i_num_pics; i++ )
1295 free( p_pic[i].p_sys );
1300 /*****************************************************************************
1301 * UpdatePictureStruct: updates the internal data in the picture_t structure
1302 *****************************************************************************
1303 * This will setup stuff for use by the video_output thread
1304 *****************************************************************************/
1305 static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic,
1308 switch( p_vout->output.i_chroma )
1310 case VLC_FOURCC('R','G','B','2'):
1311 case VLC_FOURCC('R','V','1','5'):
1312 case VLC_FOURCC('R','V','1','6'):
1313 case VLC_FOURCC('R','V','2','4'):
1314 case VLC_FOURCC('R','V','3','2'):
1315 p_pic->p->p_pixels = p_pic->p_sys->ddsd.lpSurface;
1316 p_pic->p->i_lines = p_vout->output.i_height;
1317 p_pic->p->i_pitch = p_pic->p_sys->ddsd.lPitch;
1318 switch( p_vout->output.i_chroma )
1320 case VLC_FOURCC('R','G','B','2'):
1321 p_pic->p->i_pixel_pitch = 1;
1323 case VLC_FOURCC('R','V','1','5'):
1324 case VLC_FOURCC('R','V','1','6'):
1325 p_pic->p->i_pixel_pitch = 2;
1327 case VLC_FOURCC('R','V','2','4'):
1328 p_pic->p->i_pixel_pitch = 3;
1330 case VLC_FOURCC('R','V','3','2'):
1331 p_pic->p->i_pixel_pitch = 4;
1334 return VLC_EGENERIC;
1336 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1337 p_pic->p->i_pixel_pitch;
1338 p_pic->i_planes = 1;
1341 case VLC_FOURCC('Y','V','1','2'):
1343 p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface;
1344 p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
1345 p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch;
1346 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
1347 p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width *
1348 p_pic->p[Y_PLANE].i_pixel_pitch;
1350 p_pic->V_PIXELS = p_pic->Y_PIXELS
1351 + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch;
1352 p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
1353 p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1354 p_pic->p[V_PLANE].i_pixel_pitch = 1;
1355 p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1356 p_pic->p[V_PLANE].i_pixel_pitch;
1358 p_pic->U_PIXELS = p_pic->V_PIXELS
1359 + p_pic->p[V_PLANE].i_lines * p_pic->p[V_PLANE].i_pitch;
1360 p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
1361 p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1362 p_pic->p[U_PLANE].i_pixel_pitch = 1;
1363 p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1364 p_pic->p[U_PLANE].i_pixel_pitch;
1366 p_pic->i_planes = 3;
1369 case VLC_FOURCC('I','Y','U','V'):
1371 p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface;
1372 p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
1373 p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch;
1374 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
1375 p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width *
1376 p_pic->p[Y_PLANE].i_pixel_pitch;
1378 p_pic->U_PIXELS = p_pic->Y_PIXELS
1379 + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch;
1380 p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
1381 p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1382 p_pic->p[U_PLANE].i_pixel_pitch = 1;
1383 p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1384 p_pic->p[U_PLANE].i_pixel_pitch;
1386 p_pic->V_PIXELS = p_pic->U_PIXELS
1387 + p_pic->p[U_PLANE].i_lines * p_pic->p[U_PLANE].i_pitch;
1388 p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
1389 p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1390 p_pic->p[V_PLANE].i_pixel_pitch = 1;
1391 p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1392 p_pic->p[V_PLANE].i_pixel_pitch;
1394 p_pic->i_planes = 3;
1397 case VLC_FOURCC('Y','U','Y','2'):
1399 p_pic->p->p_pixels = p_pic->p_sys->ddsd.lpSurface;
1400 p_pic->p->i_lines = p_vout->output.i_height;
1401 p_pic->p->i_pitch = p_pic->p_sys->ddsd.lPitch;
1402 p_pic->p->i_pixel_pitch = 2;
1403 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1404 p_pic->p->i_pixel_pitch;
1406 p_pic->i_planes = 1;
1410 /* Unknown chroma, tell the guy to get lost */
1411 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
1412 p_vout->output.i_chroma,
1413 (char*)&p_vout->output.i_chroma );
1414 return VLC_EGENERIC;
1420 /*****************************************************************************
1421 * DirectXGetDDrawCaps: Probe the capabilities of the hardware
1422 *****************************************************************************
1423 * It is nice to know which features are supported by the hardware so we can
1424 * find ways to optimize our rendering.
1425 *****************************************************************************/
1426 static void DirectXGetDDrawCaps( vout_thread_t *p_vout )
1431 /* This is just an indication of whether or not we'll support overlay,
1432 * but with this test we don't know if we support YUV overlay */
1433 memset( &ddcaps, 0, sizeof( DDCAPS ));
1434 ddcaps.dwSize = sizeof(DDCAPS);
1435 dxresult = IDirectDraw2_GetCaps( p_vout->p_sys->p_ddobject,
1437 if(dxresult != DD_OK )
1439 msg_Warn( p_vout, "cannot get caps" );
1443 BOOL bHasOverlay, bHasOverlayFourCC, bCanClipOverlay,
1444 bHasColorKey, bCanStretch, bCanBltFourcc;
1446 /* Determine if the hardware supports overlay surfaces */
1447 bHasOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAY) ==
1448 DDCAPS_OVERLAY) ? TRUE : FALSE;
1449 /* Determine if the hardware supports overlay surfaces */
1450 bHasOverlayFourCC = ((ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC) ==
1451 DDCAPS_OVERLAYFOURCC) ? TRUE : FALSE;
1452 /* Determine if the hardware supports overlay surfaces */
1453 bCanClipOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAYCANTCLIP) ==
1455 /* Determine if the hardware supports colorkeying */
1456 bHasColorKey = ((ddcaps.dwCaps & DDCAPS_COLORKEY) ==
1457 DDCAPS_COLORKEY) ? TRUE : FALSE;
1458 /* Determine if the hardware supports scaling of the overlay surface */
1459 bCanStretch = ((ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ==
1460 DDCAPS_OVERLAYSTRETCH) ? TRUE : FALSE;
1461 /* Determine if the hardware supports color conversion during a blit */
1462 bCanBltFourcc = ((ddcaps.dwCaps & DDCAPS_BLTFOURCC ) ==
1463 DDCAPS_BLTFOURCC) ? TRUE : FALSE;
1465 msg_Dbg( p_vout, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
1466 "can_clip_overlay=%i colorkey=%i stretch=%i "
1468 bHasOverlay, bHasOverlayFourCC, bCanClipOverlay,
1469 bHasColorKey, bCanStretch, bCanBltFourcc );
1471 /* Overlay clipping support is interesting for us as it means we can
1472 * get rid of the colorkey alltogether */
1473 p_vout->p_sys->b_caps_overlay_clipping = bCanClipOverlay;
1475 /* Don't ask for troubles */
1476 if( !bCanBltFourcc ) p_vout->p_sys->b_hw_yuv = FALSE;
1480 /*****************************************************************************
1481 * DirectXLockSurface: Lock surface and get picture data pointer
1482 *****************************************************************************
1483 * This function locks a surface and get the surface descriptor which amongst
1484 * other things has the pointer to the picture data.
1485 *****************************************************************************/
1486 static int DirectXLockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1490 /* Lock the surface to get a valid pointer to the picture buffer */
1491 memset( &p_pic->p_sys->ddsd, 0, sizeof( DDSURFACEDESC ));
1492 p_pic->p_sys->ddsd.dwSize = sizeof(DDSURFACEDESC);
1493 dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface,
1494 NULL, &p_pic->p_sys->ddsd,
1495 DDLOCK_NOSYSLOCK | DDLOCK_WAIT,
1497 if( dxresult != DD_OK )
1499 if( dxresult == DDERR_INVALIDPARAMS )
1501 /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
1502 * in an invalid params error */
1503 dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, NULL,
1504 &p_pic->p_sys->ddsd,
1507 if( dxresult == DDERR_SURFACELOST )
1509 /* Your surface can be lost so be sure
1510 * to check this and restore it if needed */
1512 /* When using overlays with back-buffers, we need to restore
1513 * the front buffer so the back-buffers get restored as well. */
1514 if( p_vout->p_sys->b_using_overlay )
1515 IDirectDrawSurface2_Restore( p_pic->p_sys->p_front_surface );
1517 IDirectDrawSurface2_Restore( p_pic->p_sys->p_surface );
1519 dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, NULL,
1520 &p_pic->p_sys->ddsd,
1522 if( dxresult == DDERR_SURFACELOST )
1523 msg_Dbg( p_vout, "DirectXLockSurface: DDERR_SURFACELOST" );
1525 if( dxresult != DD_OK )
1527 return VLC_EGENERIC;
1531 /* Now we have a pointer to the surface memory, we can update our picture
1533 if( UpdatePictureStruct( p_vout, p_pic, p_vout->output.i_chroma )
1536 DirectXUnlockSurface( p_vout, p_pic );
1537 return VLC_EGENERIC;
1543 /*****************************************************************************
1544 * DirectXUnlockSurface: Unlock a surface locked by DirectXLockSurface().
1545 *****************************************************************************/
1546 static int DirectXUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1548 /* Unlock the Surface */
1549 if( IDirectDrawSurface2_Unlock( p_pic->p_sys->p_surface, NULL ) == DD_OK )
1552 return VLC_EGENERIC;
1555 /*****************************************************************************
1556 * object variables callbacks: a bunch of object variables are used by the
1557 * interfaces to interact with the vout.
1558 *****************************************************************************/
1559 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
1560 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1562 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1563 p_vout->p_sys->b_on_top_change = VLC_TRUE;