]> git.sesse.net Git - vlc/blob - plugins/directx/vout_directx.c
* fixed a bug in the directx window creation function. We register a window
[vlc] / plugins / directx / vout_directx.c
1 /*****************************************************************************
2  * vout_directx.c: Windows DirectX video output display method
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: vout_directx.c,v 1.34 2002/05/18 15:34:04 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble:
26  *
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.
30  *
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.
35  * 
36  *****************************************************************************/
37 #include <errno.h>                                                 /* ENOMEM */
38 #include <stdlib.h>                                                /* free() */
39 #include <string.h>                                            /* strerror() */
40
41 #include <videolan/vlc.h>
42
43 #include <ddraw.h>
44
45 #include "netutils.h"
46
47 #include "video.h"
48 #include "video_output.h"
49
50 #include "interface.h"
51
52 #include "vout_directx.h"
53
54 /*****************************************************************************
55  * DirectDraw GUIDs.
56  * Defining them here allows us to get rid of the dxguid library during
57  * the linking stage.
58  *****************************************************************************/
59 #include <initguid.h>
60 DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 );
61 DEFINE_GUID( IID_IDirectDrawSurface3, 0xDA044E00,0x69B2,0x11D0,0xA1,0xD5,0x00,0xAA,0x00,0xB8,0xDF,0xBB );
62
63 /*****************************************************************************
64  * Local prototypes.
65  *****************************************************************************/
66 static int  vout_Create    ( vout_thread_t * );
67 static void vout_Destroy   ( vout_thread_t * );
68 static int  vout_Init      ( vout_thread_t * );
69 static void vout_End       ( vout_thread_t * );
70 static int  vout_Manage    ( vout_thread_t * );
71 static void vout_Render    ( vout_thread_t *, picture_t * );
72 static void vout_Display   ( vout_thread_t *, picture_t * );
73
74 static int  NewPictureVec  ( vout_thread_t *, picture_t *, int );
75 static void FreePictureVec ( vout_thread_t *, picture_t *, int );
76 static int  UpdatePictureStruct( vout_thread_t *, picture_t *, int );
77
78 static int  DirectXInitDDraw      ( vout_thread_t *p_vout );
79 static void DirectXCloseDDraw     ( vout_thread_t *p_vout );
80 static int  DirectXCreateDisplay  ( vout_thread_t *p_vout );
81 static void DirectXCloseDisplay   ( vout_thread_t *p_vout );
82 static int  DirectXCreateSurface  ( vout_thread_t *p_vout,
83                                     LPDIRECTDRAWSURFACE3 *, int, int, int );
84 static void DirectXCloseSurface   ( vout_thread_t *p_vout,
85                                     LPDIRECTDRAWSURFACE3 );
86 static int  DirectXCreateClipper  ( vout_thread_t *p_vout );
87 static void DirectXGetDDrawCaps   ( vout_thread_t *p_vout );
88 static int  DirectXGetSurfaceDesc ( picture_t *p_pic );
89
90 /*****************************************************************************
91  * Functions exported as capabilities. They are declared as static so that
92  * we don't pollute the namespace too much.
93  *****************************************************************************/
94 void _M( vout_getfunctions )( function_list_t * p_function_list )
95 {
96     p_function_list->functions.vout.pf_create     = vout_Create;
97     p_function_list->functions.vout.pf_init       = vout_Init;
98     p_function_list->functions.vout.pf_end        = vout_End;
99     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
100     p_function_list->functions.vout.pf_manage     = vout_Manage;
101     p_function_list->functions.vout.pf_render     = vout_Render;
102     p_function_list->functions.vout.pf_display    = vout_Display;
103 }
104
105 /*****************************************************************************
106  * vout_Create: allocate DirectX video thread output method
107  *****************************************************************************
108  * This function allocates and initialize the DirectX vout method.
109  *****************************************************************************/
110 static int vout_Create( vout_thread_t *p_vout )
111 {
112     /* Allocate structure */
113     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
114     if( p_vout->p_sys == NULL )
115     {
116         intf_ErrMsg( "vout error: can't create p_sys (%s)", strerror(ENOMEM) );
117         return( 1 );
118     }
119
120     /* Initialisations */
121     p_vout->p_sys->p_ddobject = NULL;
122     p_vout->p_sys->p_display = NULL;
123     p_vout->p_sys->p_current_surface = NULL;
124     p_vout->p_sys->p_clipper = NULL;
125     p_vout->p_sys->hbrush = NULL;
126     p_vout->p_sys->hwnd = NULL;
127     p_vout->p_sys->i_changes = 0;
128     p_vout->p_sys->b_event_thread_die = 0;
129     p_vout->p_sys->b_caps_overlay_clipping = 0;
130     SetRectEmpty( &p_vout->p_sys->rect_display );
131     p_vout->p_sys->b_using_overlay = !config_GetIntVariable( "nooverlay" );
132     p_vout->p_sys->b_use_sysmem = config_GetIntVariable( "directx-use-sysmem");
133     p_vout->p_sys->b_hw_yuv = !config_GetIntVariable( "no-directx-hw-yuv" );
134
135     p_vout->p_sys->b_cursor_hidden = 0;
136     p_vout->p_sys->i_lastmoved = mdate();
137
138     /* Set main window's size */
139     p_vout->p_sys->i_window_width = p_vout->i_window_width;
140     p_vout->p_sys->i_window_height = p_vout->i_window_height;
141
142     /* Set locks and condition variables */
143     vlc_mutex_init( &p_vout->p_sys->event_thread_lock );
144     vlc_cond_init( &p_vout->p_sys->event_thread_wait );
145     p_vout->p_sys->i_event_thread_status = THREAD_CREATE;
146
147     /* Create the DirectXEventThread, this thread is created by us to isolate
148      * the Win32 PeekMessage function calls. We want to do this because
149      * Windows can stay blocked inside this call for a long time, and when
150      * this happens it thus blocks vlc's video_output thread.
151      * DirectXEventThread will take care of the creation of the video
152      * window (because PeekMessage has to be called from the same thread which
153      * created the window). */
154     intf_WarnMsg( 3, "vout: vout_Create creating DirectXEventThread" );
155     if( vlc_thread_create( &p_vout->p_sys->event_thread_id,
156                            "DirectX Events Thread",
157                            (void *) DirectXEventThread, (void *) p_vout) )
158     {
159         intf_ErrMsg( "vout error: can't create DirectXEventThread" );
160         intf_ErrMsg("vout error: %s", strerror(ENOMEM));
161         free( p_vout->p_sys );
162         return( 1 );
163     }
164
165     /* We need to wait for the actual creation of the thread and window */
166     vlc_mutex_lock( &p_vout->p_sys->event_thread_lock );
167     if( p_vout->p_sys->i_event_thread_status == THREAD_CREATE )
168     {
169         vlc_cond_wait ( &p_vout->p_sys->event_thread_wait,
170                         &p_vout->p_sys->event_thread_lock );
171     }
172     vlc_mutex_unlock( &p_vout->p_sys->event_thread_lock );
173     if( p_vout->p_sys->i_event_thread_status != THREAD_READY )
174     {
175         intf_ErrMsg( "vout error: DirectXEventThread failed" );
176         free( p_vout->p_sys );
177         return( 1 );
178     }
179
180     intf_WarnMsg( 3, "vout: vout_Create DirectXEventThread running" );
181
182
183     /* Initialise DirectDraw */
184     if( DirectXInitDDraw( p_vout ) )
185     {
186         intf_ErrMsg( "vout error: can't initialise DirectDraw" );
187
188         /* Kill DirectXEventThread */
189         p_vout->p_sys->b_event_thread_die = 1;
190         /* we need to be sure DirectXEventThread won't stay stuck in
191          * GetMessage, so we send a fake message */
192         PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
193         vlc_thread_join( p_vout->p_sys->event_thread_id );
194
195         return ( 1 );
196     }
197
198     /* Create the directx display */
199     if( DirectXCreateDisplay( p_vout ) )
200     {
201         intf_ErrMsg( "vout error: can't initialise DirectDraw" );
202         DirectXCloseDDraw( p_vout );
203
204         /* Kill DirectXEventThread */
205         p_vout->p_sys->b_event_thread_die = 1;
206         /* we need to be sure DirectXEventThread won't stay stuck in
207          * GetMessage, so we send a fake message */
208         PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
209         vlc_thread_join( p_vout->p_sys->event_thread_id );
210
211         return ( 1 );
212     }
213
214     return( 0 );
215 }
216
217 /*****************************************************************************
218  * vout_Init: initialize DirectX video thread output method
219  *****************************************************************************
220  * This function create the directx surfaces needed by the output thread.
221  * It is called at the beginning of the thread.
222  *****************************************************************************/
223 static int vout_Init( vout_thread_t *p_vout )
224 {
225
226     /* Initialize the output structure.
227      * Since DirectDraw can do rescaling for us, stick to the default
228      * coordinates and aspect. */
229     p_vout->output.i_width  = p_vout->render.i_width;
230     p_vout->output.i_height = p_vout->render.i_height;
231     p_vout->output.i_aspect = p_vout->render.i_aspect;
232
233 #define MAX_DIRECTBUFFERS 1
234     /* Right now we use only 1 directbuffer because we don't want the
235      * video decoder to decode directly into direct buffers as they are
236      * created into video memory and video memory is _really_ slow */
237
238     NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
239
240     /* Change the window title bar text */
241     if( p_vout->p_sys->b_using_overlay )
242         SetWindowText( p_vout->p_sys->hwnd,
243                        "VLC DirectX (using hardware overlay)" );
244     else if( p_vout->p_sys->b_hw_yuv )
245         SetWindowText( p_vout->p_sys->hwnd,
246                        "VLC DirectX (using hardware YUV->RGB conversion)" );
247
248     return( 0 );
249 }
250
251 /*****************************************************************************
252  * vout_End: terminate Sys video thread output method
253  *****************************************************************************
254  * Terminate an output method created by vout_Create.
255  * It is called at the end of the thread.
256  *****************************************************************************/
257 static void vout_End( vout_thread_t *p_vout )
258 {
259     FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES );
260     return;
261 }
262
263 /*****************************************************************************
264  * vout_Destroy: destroy Sys video thread output method
265  *****************************************************************************
266  * Terminate an output method created by vout_Create
267  *****************************************************************************/
268 static void vout_Destroy( vout_thread_t *p_vout )
269 {
270     intf_WarnMsg( 3, "vout: vout_Destroy" );
271     DirectXCloseDisplay( p_vout );
272     DirectXCloseDDraw( p_vout );
273
274     /* Kill DirectXEventThread */
275     p_vout->p_sys->b_event_thread_die = 1;
276     /* we need to be sure DirectXEventThread won't stay stuck in GetMessage,
277      * so we send a fake message */
278     if( p_vout->p_sys->i_event_thread_status == THREAD_READY &&
279         p_vout->p_sys->hwnd )
280     {
281         PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
282         vlc_thread_join( p_vout->p_sys->event_thread_id );
283     }
284
285     if( p_vout->p_sys != NULL )
286     {
287         free( p_vout->p_sys );
288         p_vout->p_sys = NULL;
289     }
290 }
291
292 /*****************************************************************************
293  * vout_Manage: handle Sys events
294  *****************************************************************************
295  * This function should be called regularly by the video output thread.
296  * It returns a non null value if an error occured.
297  *****************************************************************************/
298 static int vout_Manage( vout_thread_t *p_vout )
299 {
300     WINDOWPLACEMENT window_placement;
301
302     /* We used to call the Win32 PeekMessage function here to read the window
303      * messages. But since window can stay blocked into this function for a
304      * long time (for example when you move your window on the screen), I
305      * decided to isolate PeekMessage in another thread. */
306
307     /*
308      * Scale Change 
309      */
310     if( p_vout->i_changes & VOUT_SCALE_CHANGE
311         || p_vout->p_sys->i_changes & VOUT_SCALE_CHANGE)
312     {
313         intf_WarnMsg( 3, "vout: vout_Manage Scale Change" );
314         if( !p_vout->p_sys->b_using_overlay )
315             InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
316         else
317             DirectXUpdateOverlay( p_vout );
318         p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
319         p_vout->p_sys->i_changes &= ~VOUT_SCALE_CHANGE;
320     }
321
322     /*
323      * Size Change 
324      */
325     if( p_vout->i_changes & VOUT_SIZE_CHANGE
326         || p_vout->p_sys->i_changes & VOUT_SIZE_CHANGE )
327     {
328         intf_WarnMsg( 3, "vout: vout_Manage Size Change" );
329         if( !p_vout->p_sys->b_using_overlay )
330             InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
331         else
332             DirectXUpdateOverlay( p_vout );
333         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
334         p_vout->p_sys->i_changes &= ~VOUT_SIZE_CHANGE;
335     }
336
337     /*
338      * Fullscreen change
339      */
340     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
341         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
342     {
343         p_vout->b_fullscreen = ! p_vout->b_fullscreen;
344
345         /* We need to switch between Maximized and Normal sized window */
346         window_placement.length = sizeof(WINDOWPLACEMENT);
347         GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
348         if( p_vout->b_fullscreen )
349         {
350             /* Maximized window */
351             window_placement.showCmd = SW_SHOWMAXIMIZED;
352             /* Change window style, no borders and no title bar */
353             SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, 0 );
354
355         }
356         else
357         {
358             /* Normal window */
359             window_placement.showCmd = SW_SHOWNORMAL;
360             /* Change window style, borders and title bar */
361             SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
362                            WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE );
363         }
364
365         SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
366
367         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
368         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
369     }
370
371     /*
372      * Pointer change
373      */
374     if( (!p_vout->p_sys->b_cursor_hidden) &&
375         ( (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 ) )
376     {
377         /* Hide the mouse automatically */
378         p_vout->p_sys->b_cursor_hidden = 1;
379         PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
380     }
381
382     /* Check if the event thread is still running */
383     if( p_vout->p_sys->b_event_thread_die )
384         return 1; /* exit */
385
386     return( 0 );
387 }
388
389 /*****************************************************************************
390  * vout_Render: render previously calculated output
391  *****************************************************************************/
392 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
393 {
394     ;
395 }
396
397 /*****************************************************************************
398  * vout_Display: displays previously rendered output
399  *****************************************************************************
400  * This function sends the currently rendered image to the display, wait until
401  * it is displayed and switch the two rendering buffers, preparing next frame.
402  *****************************************************************************/
403 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
404 {
405     HRESULT dxresult;
406
407     if( (p_vout->p_sys->p_display == NULL) )
408     {
409         intf_WarnMsg( 3, "vout error: vout_Display no display!!" );
410         return;
411     }
412
413     if( !p_vout->p_sys->b_using_overlay )
414     {
415         DDBLTFX  ddbltfx;
416
417         /* We ask for the "NOTEARING" option */
418         memset( &ddbltfx, 0, sizeof(DDBLTFX) );
419         ddbltfx.dwSize = sizeof(DDBLTFX);
420         ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
421
422         /* Blit video surface to display */
423         dxresult = IDirectDrawSurface3_Blt(p_vout->p_sys->p_display,
424                                            &p_vout->p_sys->rect_dest_clipped,
425                                            p_pic->p_sys->p_surface,
426                                            &p_vout->p_sys->rect_src_clipped,
427                                            DDBLT_ASYNC, &ddbltfx );
428         if ( dxresult == DDERR_SURFACELOST )
429         {
430             /* Our surface can be lost so be sure
431              * to check this and restore it if needed */
432             IDirectDrawSurface3_Restore( p_vout->p_sys->p_display );
433
434             /* Now that the surface has been restored try to display again */
435             dxresult = IDirectDrawSurface3_Blt(p_vout->p_sys->p_display,
436                                            &p_vout->p_sys->rect_dest_clipped,
437                                            p_pic->p_sys->p_surface,
438                                            &p_vout->p_sys->rect_src_clipped,
439                                            DDBLT_ASYNC, &ddbltfx );
440         }
441
442         if( dxresult != DD_OK )
443         {
444             intf_WarnMsg( 3, "vout: could not Blit the surface" );
445             return;
446         }
447
448     }
449     else /* using overlay */
450     {
451
452         /* Flip the overlay buffers if we are using back buffers */
453         if( p_pic->p_sys->p_front_surface == p_pic->p_sys->p_surface )
454             return;
455
456         dxresult = IDirectDrawSurface3_Flip( p_pic->p_sys->p_front_surface,
457                                              NULL, DDFLIP_WAIT );
458         if ( dxresult == DDERR_SURFACELOST )
459         {
460             /* Our surface can be lost so be sure
461              * to check this and restore it if needed */
462             IDirectDrawSurface3_Restore( p_vout->p_sys->p_display );
463             IDirectDrawSurface3_Restore( p_pic->p_sys->p_front_surface );
464
465             /* Now that the surface has been restored try to display again */
466             dxresult = IDirectDrawSurface3_Flip( p_pic->p_sys->p_front_surface,
467                                                  NULL, DDFLIP_WAIT );
468             DirectXUpdateOverlay( p_vout );
469         }
470
471         if( dxresult != DD_OK )
472             intf_WarnMsg( 8, "vout: couldn't flip overlay surface" );
473
474
475         if( !DirectXGetSurfaceDesc( p_pic ) )
476         {
477             /* AAARRGG */
478             intf_ErrMsg( "vout error: vout_Display cannot get surface desc" );
479             return;
480         }
481
482         if( !UpdatePictureStruct( p_vout, p_pic, p_vout->output.i_chroma ) )
483         {
484             /* AAARRGG */
485             intf_ErrMsg( "vout error: vout_Display unvalid pic chroma" );
486             return;
487         }
488
489         /* set currently displayed pic */
490         p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface;
491     }
492
493 }
494
495
496 /* following functions are local */
497
498 /*****************************************************************************
499  * DirectXInitDDraw: Takes care of all the DirectDraw initialisations
500  *****************************************************************************
501  * This function initialise and allocate resources for DirectDraw.
502  *****************************************************************************/
503 static int DirectXInitDDraw( vout_thread_t *p_vout )
504 {
505     HRESULT    dxresult;
506     HRESULT    (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
507     LPDIRECTDRAW  p_ddobject;
508
509     intf_WarnMsg( 3, "vout: DirectXInitDDraw" );
510
511     /* load direct draw DLL */
512     p_vout->p_sys->hddraw_dll = LoadLibrary("DDRAW.DLL");
513     if( p_vout->p_sys->hddraw_dll == NULL )
514     {
515         intf_WarnMsg( 3, "vout: DirectXInitDDraw failed loading ddraw.dll" );
516         return( 1 );
517     }
518       
519     OurDirectDrawCreate = 
520       (void *)GetProcAddress(p_vout->p_sys->hddraw_dll, "DirectDrawCreate");
521     if ( OurDirectDrawCreate == NULL )
522     {
523         intf_ErrMsg( "vout error: DirectXInitDDraw failed GetProcAddress" );
524         FreeLibrary( p_vout->p_sys->hddraw_dll );
525         p_vout->p_sys->hddraw_dll = NULL;
526         return( 1 );    
527     }
528
529     /* Initialize DirectDraw now */
530     dxresult = OurDirectDrawCreate( NULL, &p_ddobject, NULL );
531     if( dxresult != DD_OK )
532     {
533         intf_ErrMsg( "vout error: DirectXInitDDraw can't initialize DDraw" );
534         p_vout->p_sys->p_ddobject = NULL;
535         FreeLibrary( p_vout->p_sys->hddraw_dll );
536         p_vout->p_sys->hddraw_dll = NULL;
537         return( 1 );
538     }
539
540     /* Set DirectDraw Cooperative level, ie what control we want over Windows
541      * display */
542     dxresult = IDirectDraw_SetCooperativeLevel( p_ddobject,
543                                            p_vout->p_sys->hwnd, DDSCL_NORMAL );
544     if( dxresult != DD_OK )
545     {
546         intf_ErrMsg( "vout error: can't set direct draw cooperative level." );
547         IDirectDraw_Release( p_ddobject );
548         p_vout->p_sys->p_ddobject = NULL;
549         FreeLibrary( p_vout->p_sys->hddraw_dll );
550         p_vout->p_sys->hddraw_dll = NULL;
551         return( 1 );
552     }
553
554     /* Get the IDirectDraw2 interface */
555     dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2,
556                                         (LPVOID *)&p_vout->p_sys->p_ddobject );
557     if( dxresult != DD_OK )
558     {
559         intf_ErrMsg( "vout error: can't get IDirectDraw2 interface." );
560         IDirectDraw_Release( p_ddobject );
561         p_vout->p_sys->p_ddobject = NULL;
562         FreeLibrary( p_vout->p_sys->hddraw_dll );
563         p_vout->p_sys->hddraw_dll = NULL;
564         return( 1 );
565     }
566     else
567     {
568         /* Release the unused interface */
569         IDirectDraw_Release( p_ddobject );
570     }
571
572     /* Probe the capabilities of the hardware */
573     DirectXGetDDrawCaps( p_vout );
574
575     intf_WarnMsg( 3, "vout: End DirectXInitDDraw" );
576     return( 0 );
577 }
578
579 /*****************************************************************************
580  * DirectXCreateDisplay: create the DirectDraw display.
581  *****************************************************************************
582  * Create and initialize display according to preferences specified in the vout
583  * thread fields.
584  *****************************************************************************/
585 static int DirectXCreateDisplay( vout_thread_t *p_vout )
586 {
587     HRESULT              dxresult;
588     DDSURFACEDESC        ddsd;
589     LPDIRECTDRAWSURFACE  p_display;
590     DDPIXELFORMAT   pixel_format;
591
592     intf_WarnMsg( 3, "vout: DirectXCreateDisplay" );
593
594     /* Now get the primary surface. This surface is what you actually see
595      * on your screen */
596     memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
597     ddsd.dwSize = sizeof(DDSURFACEDESC);
598     ddsd.dwFlags = DDSD_CAPS;
599     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
600
601     dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
602                                            &ddsd,
603                                            &p_display, NULL );
604     if( dxresult != DD_OK )
605     {
606         intf_ErrMsg( "vout error: can't get direct draw primary surface." );
607         p_vout->p_sys->p_display = NULL;
608         return( 1 );
609     }
610
611     dxresult = IDirectDrawSurface_QueryInterface( p_display,
612                                          &IID_IDirectDrawSurface3,
613                                          (LPVOID *)&p_vout->p_sys->p_display );
614     if ( dxresult != DD_OK )
615     {
616         intf_ErrMsg( "vout error: can't get IDirectDrawSurface3 interface." );
617         IDirectDrawSurface_Release( p_display );
618         p_vout->p_sys->p_display = NULL;
619         return( 1 );
620     }
621     else
622     {
623         /* Release the old interface */
624         IDirectDrawSurface_Release( p_display );
625     }
626
627
628     /* The clipper will be used only in non-overlay mode */
629     DirectXCreateClipper( p_vout );
630
631
632 #if 1
633     /* compute the colorkey pixel value from the RGB value we've got */
634     memset( &pixel_format, 0, sizeof( DDPIXELFORMAT ));
635     pixel_format.dwSize = sizeof( DDPIXELFORMAT );
636     dxresult = IDirectDrawSurface3_GetPixelFormat( p_vout->p_sys->p_display,
637                                                    &pixel_format );
638     if( dxresult != DD_OK )
639         intf_WarnMsg( 3, "vout: DirectXUpdateOverlay GetPixelFormat failed" );
640     p_vout->p_sys->i_colorkey = (DWORD)((( p_vout->p_sys->i_rgb_colorkey
641                                            * pixel_format.dwRBitMask) / 255)
642                                         & pixel_format.dwRBitMask);
643 #endif
644
645     return( 0 );
646 }
647
648
649 /*****************************************************************************
650  * DirectXCreateClipper: Create a clipper that will be used when blitting the
651  *                       RGB surface to the main display.
652  *****************************************************************************
653  * This clipper prevents us to modify by mistake anything on the screen
654  * which doesn't belong to our window. For example when a part of our video
655  * window is hidden by another window.
656  *****************************************************************************/
657 static int DirectXCreateClipper( vout_thread_t *p_vout )
658 {
659     HRESULT dxresult;
660
661     intf_WarnMsg( 3, "vout: DirectXCreateClipper" );
662
663     /* Create the clipper */
664     dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0,
665                                            &p_vout->p_sys->p_clipper, NULL );
666     if( dxresult != DD_OK )
667     {
668         intf_WarnMsg( 3, "vout: DirectXCreateClipper can't create clipper." );
669         p_vout->p_sys->p_clipper = NULL;
670         return( 1 );
671     }
672
673     /* associate the clipper to the window */
674     dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0,
675                                           p_vout->p_sys->hwnd);
676     if( dxresult != DD_OK )
677     {
678         intf_WarnMsg( 3,
679             "vout: DirectXCreateClipper can't attach clipper to window." );
680         IDirectDrawSurface_Release( p_vout->p_sys->p_clipper );
681         p_vout->p_sys->p_clipper = NULL;
682         return( 1 );
683     }
684
685     /* associate the clipper with the surface */
686     dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
687                                              p_vout->p_sys->p_clipper);
688     if( dxresult != DD_OK )
689     {
690         intf_WarnMsg( 3,
691             "vout: DirectXCreateClipper can't attach clipper to surface." );
692         IDirectDrawSurface_Release( p_vout->p_sys->p_clipper );
693         p_vout->p_sys->p_clipper = NULL;
694         return( 1 );
695     }    
696
697     return( 0 );
698 }
699
700 /*****************************************************************************
701  * DirectXCreateSurface: create an YUV overlay or RGB surface for the video.
702  *****************************************************************************
703  * The best method of display is with an YUV overlay because the YUV->RGB
704  * conversion is done in hardware.
705  * You can also create a plain RGB surface.
706  * ( Maybe we could also try an RGB overlay surface, which could have hardware
707  * scaling and which would also be faster in window mode because you don't
708  * need to do any blitting to the main display...)
709  *****************************************************************************/
710 static int DirectXCreateSurface( vout_thread_t *p_vout,
711                                  LPDIRECTDRAWSURFACE3 *pp_surface_final,
712                                  int i_chroma, int b_overlay,
713                                  int i_backbuffers )
714 {
715     HRESULT dxresult;
716     LPDIRECTDRAWSURFACE p_surface;
717     DDSURFACEDESC ddsd;
718
719     /* Create the video surface */
720     if( b_overlay )
721     {
722         /* Now try to create the YUV overlay surface.
723          * This overlay will be displayed on top of the primary surface.
724          * A color key is used to determine whether or not the overlay will be
725          * displayed, ie the overlay will be displayed in place of the primary
726          * surface wherever the primary surface will have this color.
727          * The video window has been created with a background of this color so
728          * the overlay will be only displayed on top of this window */
729
730         memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
731         ddsd.dwSize = sizeof(DDSURFACEDESC);
732         ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
733         ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
734         ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
735         ddsd.dwFlags = DDSD_CAPS |
736                        DDSD_HEIGHT |
737                        DDSD_WIDTH |
738                        DDSD_PIXELFORMAT;
739         ddsd.dwFlags |= (i_backbuffers ? DDSD_BACKBUFFERCOUNT : 0);
740         ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY |
741                               DDSCAPS_VIDEOMEMORY;
742         ddsd.ddsCaps.dwCaps |= (i_backbuffers ? DDSCAPS_COMPLEX | DDSCAPS_FLIP
743                                 : 0 );
744         ddsd.dwHeight = p_vout->render.i_height;
745         ddsd.dwWidth = p_vout->render.i_width;
746         ddsd.dwBackBufferCount = i_backbuffers;
747
748         dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
749                                                &ddsd,
750                                                &p_surface, NULL );
751         if( dxresult != DD_OK )
752         {
753             *pp_surface_final = NULL;
754             return 0;
755         }
756     }
757
758     if( !b_overlay )
759     {
760         boolean_t b_rgb_surface = ( i_chroma == FOURCC_RGB2 ) ||
761             ( i_chroma == FOURCC_RV15 ) || ( i_chroma == FOURCC_RV16 ) ||
762             ( i_chroma == FOURCC_RV24 ) || ( i_chroma == FOURCC_RV32 );
763
764         memset( &ddsd, 0, sizeof( DDSURFACEDESC ) );
765         ddsd.dwSize = sizeof(DDSURFACEDESC);
766         ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
767         ddsd.dwFlags = DDSD_HEIGHT |
768                        DDSD_WIDTH |
769                        DDSD_CAPS;
770         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
771         ddsd.dwHeight = p_vout->render.i_height;
772         ddsd.dwWidth = p_vout->render.i_width;
773
774         if( p_vout->p_sys->b_use_sysmem )
775             ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
776         else
777             ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
778
779         if( !b_rgb_surface )
780         {
781             ddsd.dwFlags |= DDSD_PIXELFORMAT;
782             ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
783             ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
784         }
785
786         dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
787                                                &ddsd,
788                                                &p_surface, NULL );
789         if( dxresult != DD_OK )
790         {
791             *pp_surface_final = NULL;
792             return 0;
793         }
794     }
795
796     /* Now that the surface is created, try to get a newer DirectX interface */
797     dxresult = IDirectDrawSurface_QueryInterface( p_surface,
798                                      &IID_IDirectDrawSurface3,
799                                      (LPVOID *)pp_surface_final );
800     IDirectDrawSurface_Release( p_surface );    /* Release the old interface */
801     if ( dxresult != DD_OK )
802     {
803         intf_ErrMsg( "vout error: can't get IDirectDrawSurface3 interface." );
804         *pp_surface_final = NULL;
805         return 0;
806     }
807
808     return 1;
809 }
810
811 /*****************************************************************************
812  * DirectXUpdateOverlay: Move or resize overlay surface on video display.
813  *****************************************************************************
814  * This function is used to move or resize an overlay surface on the screen.
815  * Ususally the overlay is moved by the user and thus, by a move or resize
816  * event (in vout_Manage).
817  *****************************************************************************/
818 void DirectXUpdateOverlay( vout_thread_t *p_vout )
819 {
820     DDOVERLAYFX     ddofx;
821     DWORD           dwFlags;
822     HRESULT         dxresult;
823
824     if( p_vout->p_sys->p_current_surface == NULL ||
825         !p_vout->p_sys->b_using_overlay )
826         return;
827
828     /* The new window dimensions should already have been computed by the
829      * caller of this function */
830
831     /* Position and show the overlay */
832     memset(&ddofx, 0, sizeof(DDOVERLAYFX));
833     ddofx.dwSize = sizeof(DDOVERLAYFX);
834     ddofx.dckDestColorkey.dwColorSpaceLowValue = p_vout->p_sys->i_colorkey;
835     ddofx.dckDestColorkey.dwColorSpaceHighValue = p_vout->p_sys->i_colorkey;
836
837     dwFlags = DDOVER_SHOW;
838     if( !p_vout->p_sys->b_caps_overlay_clipping )
839         dwFlags |= DDOVER_KEYDESTOVERRIDE;
840
841     dxresult = IDirectDrawSurface3_UpdateOverlay(
842                                          p_vout->p_sys->p_current_surface,
843                                          &p_vout->p_sys->rect_src_clipped,
844                                          p_vout->p_sys->p_display,
845                                          &p_vout->p_sys->rect_dest_clipped,
846                                          dwFlags,
847                                          &ddofx );
848     if(dxresult != DD_OK)
849     {
850         intf_WarnMsg( 3,
851           "vout: DirectXUpdateOverlay can't move or resize overlay" );
852     }
853
854 }
855
856 /*****************************************************************************
857  * DirectXCloseDDraw: Release the DDraw object allocated by DirectXInitDDraw
858  *****************************************************************************
859  * This function returns all resources allocated by DirectXInitDDraw.
860  *****************************************************************************/
861 static void DirectXCloseDDraw( vout_thread_t *p_vout )
862 {
863     intf_WarnMsg(3, "vout: DirectXCloseDDraw" );
864     if( p_vout->p_sys->p_ddobject != NULL )
865     {
866         IDirectDraw2_Release(p_vout->p_sys->p_ddobject);
867         p_vout->p_sys->p_ddobject = NULL;
868     }
869
870     if( p_vout->p_sys->hddraw_dll != NULL )
871     {
872         FreeLibrary( p_vout->p_sys->hddraw_dll );
873         p_vout->p_sys->hddraw_dll = NULL;
874     }
875 }
876
877 /*****************************************************************************
878  * DirectXCloseDisplay: close and reset the DirectX display device
879  *****************************************************************************
880  * This function returns all resources allocated by DirectXCreateDisplay.
881  *****************************************************************************/
882 static void DirectXCloseDisplay( vout_thread_t *p_vout )
883 {
884     intf_WarnMsg( 3, "vout: DirectXCloseDisplay" );
885
886     if( p_vout->p_sys->p_clipper != NULL )
887     {
888         intf_WarnMsg( 3, "vout: DirectXCloseDisplay clipper" );
889         IDirectDraw2_Release( p_vout->p_sys->p_clipper );
890         p_vout->p_sys->p_clipper = NULL;
891     }
892
893     if( p_vout->p_sys->p_display != NULL )
894     {
895         intf_WarnMsg( 3, "vout: DirectXCloseDisplay display" );
896         IDirectDraw2_Release( p_vout->p_sys->p_display );
897         p_vout->p_sys->p_display = NULL;
898     }
899 }
900
901 /*****************************************************************************
902  * DirectXCloseSurface: close the YUV overlay or RGB surface.
903  *****************************************************************************
904  * This function returns all resources allocated for the surface.
905  *****************************************************************************/
906 static void DirectXCloseSurface( vout_thread_t *p_vout,
907                                  LPDIRECTDRAWSURFACE3 p_surface )
908 {
909     intf_WarnMsg( 3, "vout: DirectXCloseSurface" );
910     if( p_surface != NULL )
911     {
912         IDirectDraw2_Release( p_surface );
913     }
914 }
915
916 /*****************************************************************************
917  * NewPictureVec: allocate a vector of identical pictures
918  *****************************************************************************
919  * Returns 0 on success, -1 otherwise
920  *****************************************************************************/
921 static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic,
922                           int i_num_pics )
923 {
924     int i;
925     boolean_t b_result_ok;
926     LPDIRECTDRAWSURFACE3 p_surface;
927
928     intf_WarnMsg( 3, "vout: NewPictureVec" );
929
930     I_OUTPUTPICTURES = 0;
931
932     /* Choose the chroma we will try first. */
933     switch( p_vout->render.i_chroma )
934     {
935         case FOURCC_YUY2:
936         case FOURCC_YUNV:
937             p_vout->output.i_chroma = FOURCC_YUY2;
938             break;
939         case FOURCC_UYVY:
940         case FOURCC_UYNV:
941         case FOURCC_Y422:
942             p_vout->output.i_chroma = FOURCC_UYVY;
943             break;
944         case FOURCC_YVYU:
945             p_vout->output.i_chroma = FOURCC_YVYU;
946             break;
947         default:
948             p_vout->output.i_chroma = FOURCC_YV12;
949             break;
950     }
951
952     /* First we try to use an YUV overlay surface.
953      * The overlay surface that we create won't be used to decode directly
954      * into it because accessing video memory directly is way to slow (remember
955      * that pictures are decoded macroblock per macroblock). Instead the video
956      * will be decoded in picture buffers in system memory which will then be
957      * memcpy() to the overlay surface. */
958     if( p_vout->p_sys->b_using_overlay )
959     {
960         /* Triple buffering rocks! it doesn't have any processing overhead
961          * (you don't have to wait for the vsync) and provides for a very nice
962          * video quality (no tearing). */
963
964         b_result_ok = DirectXCreateSurface( p_vout, &p_surface,
965                                             p_vout->output.i_chroma,
966                                             p_vout->p_sys->b_using_overlay,
967                                             2 /* number of backbuffers */ );
968
969         if( !b_result_ok )
970             /* Try to reduce the number of backbuffers */
971             b_result_ok = DirectXCreateSurface( p_vout, &p_surface,
972                                                 p_vout->output.i_chroma,
973                                                 p_vout->p_sys->b_using_overlay,
974                                                 0 /* number of backbuffers */);
975
976         if( b_result_ok )
977         {
978             DDSCAPS dds_caps;
979             picture_t front_pic;
980             picture_sys_t front_pic_sys;
981             front_pic.p_sys = &front_pic_sys;
982
983             /* Allocate internal structure */
984             p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) );
985             if( p_pic[0].p_sys == NULL )
986             {
987                 DirectXCloseSurface( p_vout, p_surface );
988                 return -1;
989             }
990
991             /* set front buffer */
992             p_pic[0].p_sys->p_front_surface = p_surface;
993
994             /* Get the back buffer */
995             memset( &dds_caps, 0, sizeof( DDSCAPS ) );
996             dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
997             if( DD_OK != IDirectDrawSurface3_GetAttachedSurface(
998                                                 p_surface, &dds_caps,
999                                                 &p_pic[0].p_sys->p_surface ) )
1000             {
1001                 intf_WarnMsg( 3, "vout: NewPictureVec couldn't get "
1002                               "back buffer" );
1003                 /* front buffer is the same as back buffer */
1004                 p_pic[0].p_sys->p_surface = p_surface;
1005             }
1006
1007
1008             p_vout->p_sys->p_current_surface = front_pic.p_sys->p_surface =
1009                 p_pic[0].p_sys->p_front_surface;
1010
1011             /* reset the front buffer memory */
1012             if( DirectXGetSurfaceDesc( &front_pic ) &&
1013                 UpdatePictureStruct( p_vout, &front_pic,
1014                                      p_vout->output.i_chroma ) )
1015             {
1016                 int j;
1017                 for( j = 0; j < front_pic.i_planes; j++ )
1018                     memset( front_pic.p[j].p_pixels, 127,
1019                             front_pic.p[j].i_lines * front_pic.p[j].i_pitch
1020                             * front_pic.p[j].i_pixel_bytes );
1021             }
1022
1023             DirectXUpdateOverlay( p_vout );
1024             I_OUTPUTPICTURES = 1;
1025             intf_WarnMsg( 3,"vout: DirectX YUV overlay created successfully" );
1026         }
1027         else
1028         {
1029             intf_WarnMsg( 3, "vout: can't create an YUV overlay surface." );
1030             p_vout->p_sys->b_using_overlay = 0;
1031         }
1032     }
1033
1034     /* As we can't have an overlay, we'll try to create a plain offscreen
1035      * surface. This surface will reside in video memory because there's a
1036      * better chance then that we'll be able to use some kind of hardware
1037      * acceleration like rescaling, blitting or YUV->RGB conversions.
1038      * We then only need to blit this surface onto the main display when we
1039      * want to display it */
1040     if( !p_vout->p_sys->b_using_overlay )
1041     {
1042
1043         if( p_vout->p_sys->b_hw_yuv )
1044             b_result_ok = DirectXCreateSurface( p_vout, &p_surface,
1045                                                 p_vout->output.i_chroma,
1046                                                 p_vout->p_sys->b_using_overlay,
1047                                                 0 /* no back buffers */ );
1048
1049         if( !p_vout->p_sys->b_hw_yuv || !b_result_ok )
1050         {
1051             /* Our last choice is to use a plain RGB surface */
1052             DDPIXELFORMAT ddpfPixelFormat;
1053
1054             ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
1055             IDirectDrawSurface3_GetPixelFormat( p_vout->p_sys->p_display,
1056                                                 &ddpfPixelFormat );
1057
1058             if( ddpfPixelFormat.dwFlags & DDPF_RGB )
1059             {
1060                 switch( ddpfPixelFormat.dwRGBBitCount )
1061                 {
1062                 case 8: /* FIXME: set the palette */
1063                     p_vout->output.i_chroma = FOURCC_RGB2; break;
1064                 case 15:
1065                     p_vout->output.i_chroma = FOURCC_RV15; break;
1066                 case 16:
1067                     p_vout->output.i_chroma = FOURCC_RV16; break;
1068                 case 24:
1069                     p_vout->output.i_chroma = FOURCC_RV24; break;
1070                 case 32:
1071                     p_vout->output.i_chroma = FOURCC_RV32; break;
1072                 default:
1073                     intf_ErrMsg( "vout error: unknown screen depth" );
1074                     return( 0 );
1075                 }
1076                 p_vout->output.i_rmask = ddpfPixelFormat.dwRBitMask;
1077                 p_vout->output.i_gmask = ddpfPixelFormat.dwGBitMask;
1078                 p_vout->output.i_bmask = ddpfPixelFormat.dwBBitMask;
1079             }
1080
1081             p_vout->p_sys->b_hw_yuv = 0;
1082
1083             b_result_ok = DirectXCreateSurface( p_vout, &p_surface,
1084                                                 p_vout->output.i_chroma,
1085                                                 p_vout->p_sys->b_using_overlay,
1086                                                 0 /* no back buffers */ );
1087         }
1088
1089         if( b_result_ok )
1090         {
1091             /* Allocate internal structure */
1092             p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) );
1093             if( p_pic[0].p_sys == NULL )
1094             {
1095                 DirectXCloseSurface( p_vout, p_surface );
1096                 return -1;
1097             }
1098             p_pic[0].p_sys->p_surface = p_pic[0].p_sys->p_front_surface
1099                 = p_surface;
1100
1101             I_OUTPUTPICTURES = 1;
1102
1103             intf_WarnMsg( 3, "vout: DirectX plain surface created "
1104                              "successfully" );
1105         }
1106         else
1107             intf_ErrMsg( "vout error: DirectX can't create plain surface." );
1108     }
1109
1110
1111     /* Now that we've got all our direct-buffers, we can finish filling in the
1112      * picture_t structures */
1113     for( i = 0; i < I_OUTPUTPICTURES; i++ )
1114     {
1115         p_pic[i].i_status = DESTROYED_PICTURE;
1116         p_pic[i].i_type   = DIRECT_PICTURE;
1117         PP_OUTPUTPICTURE[i] = &p_pic[i];
1118
1119         if( !DirectXGetSurfaceDesc( &p_pic[i] ) )
1120         {
1121             /* AAARRGG */
1122             FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES );
1123             I_OUTPUTPICTURES = 0;
1124             return -1;
1125         }
1126
1127         if( !UpdatePictureStruct(p_vout, &p_pic[i], p_vout->output.i_chroma) )
1128         {
1129
1130             /* Unknown chroma, tell the guy to get lost */
1131             intf_ErrMsg( "vout error: never heard of chroma 0x%.8x (%4.4s)",
1132                          p_vout->output.i_chroma,
1133                          (char*)&p_vout->output.i_chroma );
1134             FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES );
1135             I_OUTPUTPICTURES = 0;
1136             return -1;
1137         }
1138     }
1139
1140     intf_WarnMsg( 3, "vout: End NewPictureVec");
1141     return 0;
1142 }
1143
1144 /*****************************************************************************
1145  * FreePicture: destroy a picture vector allocated with NewPictureVec
1146  *****************************************************************************
1147  * 
1148  *****************************************************************************/
1149 static void FreePictureVec( vout_thread_t *p_vout, picture_t *p_pic,
1150                             int i_num_pics )
1151 {
1152     int i;
1153
1154     for( i = 0; i < i_num_pics; i++ )
1155     {
1156         DirectXCloseSurface( p_vout, p_pic[i].p_sys->p_front_surface );
1157
1158         for( i = 0; i < i_num_pics; i++ )
1159         {
1160             free( p_pic[i].p_sys );
1161         }
1162     }
1163 }
1164
1165 /*****************************************************************************
1166  * UpdatePictureStruct: updates the internal data in the picture_t structure
1167  *****************************************************************************
1168  * This will setup stuff for use by the video_output thread
1169  *****************************************************************************/
1170 static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic,
1171                                 int i_chroma )
1172 {
1173
1174     switch( p_vout->output.i_chroma )
1175     {
1176
1177         case FOURCC_RGB2:
1178         case FOURCC_RV15:
1179         case FOURCC_RV16:
1180         case FOURCC_RV24:
1181         case FOURCC_RV32:
1182             p_pic->p->p_pixels = p_pic->p_sys->ddsd.lpSurface;
1183             p_pic->p->i_lines = p_vout->output.i_height;
1184             p_pic->p->i_pitch = p_pic->p_sys->ddsd.lPitch;
1185             p_pic->p->b_margin = 0;
1186             p_pic->i_planes = 1;
1187             switch( p_vout->output.i_chroma )
1188             {
1189                 case FOURCC_RGB2:
1190                     p_pic->p->i_pixel_bytes = 1;
1191                     break;
1192                 case FOURCC_RV15:
1193                 case FOURCC_RV16:
1194                     p_pic->p->i_pixel_bytes = 2;
1195                     break;
1196                 case FOURCC_RV24:
1197                 case FOURCC_RV32:
1198                     p_pic->p->i_pixel_bytes = 4;
1199                     break;
1200                 default:
1201                     return( -1 );
1202             }
1203             break;
1204
1205         case FOURCC_YV12:
1206
1207             p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface;
1208             p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
1209             p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch;
1210             p_pic->p[Y_PLANE].i_pixel_bytes = 1;
1211             p_pic->p[Y_PLANE].b_margin = 0;
1212
1213             p_pic->V_PIXELS =  p_pic->Y_PIXELS
1214               + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch;
1215             p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
1216             p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1217             p_pic->p[V_PLANE].i_pixel_bytes = 1;
1218             p_pic->p[V_PLANE].b_margin = 0;
1219
1220             p_pic->U_PIXELS = p_pic->V_PIXELS
1221               + p_pic->p[V_PLANE].i_lines * p_pic->p[V_PLANE].i_pitch;
1222             p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
1223             p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1224             p_pic->p[U_PLANE].i_pixel_bytes = 1;
1225             p_pic->p[U_PLANE].b_margin = 0;
1226
1227             p_pic->i_planes = 3;
1228             break;
1229
1230         case FOURCC_IYUV:
1231
1232             p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface;
1233             p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
1234             p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch;
1235             p_pic->p[Y_PLANE].i_pixel_bytes = 1;
1236             p_pic->p[Y_PLANE].b_margin = 0;
1237
1238             p_pic->U_PIXELS = p_pic->Y_PIXELS
1239               + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch;
1240             p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
1241             p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1242             p_pic->p[U_PLANE].i_pixel_bytes = 1;
1243             p_pic->p[U_PLANE].b_margin = 0;
1244
1245             p_pic->V_PIXELS =  p_pic->U_PIXELS
1246               + p_pic->p[U_PLANE].i_lines * p_pic->p[U_PLANE].i_pitch;
1247             p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
1248             p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1249             p_pic->p[V_PLANE].i_pixel_bytes = 1;
1250             p_pic->p[V_PLANE].b_margin = 0;
1251
1252             p_pic->i_planes = 3;
1253             break;
1254
1255         default:
1256             /* Not supported */
1257             return 0;
1258
1259     }
1260
1261     return 1;
1262 }
1263
1264 /*****************************************************************************
1265  * DirectXGetDDrawCaps: Probe the capabilities of the hardware
1266  *****************************************************************************
1267  * It is nice to know which features are supported by the hardware so we can
1268  * find ways to optimize our rendering.
1269  *****************************************************************************/
1270 static void DirectXGetDDrawCaps( vout_thread_t *p_vout )
1271 {
1272     DDCAPS ddcaps;
1273     HRESULT dxresult;
1274
1275     /* This is just an indication of whether or not we'll support overlay,
1276      * but with this test we don't know if we support YUV overlay */
1277     memset( &ddcaps, 0, sizeof( DDCAPS ));
1278     ddcaps.dwSize = sizeof(DDCAPS);
1279     dxresult = IDirectDraw2_GetCaps( p_vout->p_sys->p_ddobject,
1280                                      &ddcaps, NULL );
1281     if(dxresult != DD_OK )
1282     {
1283         intf_WarnMsg( 3,"vout error: can't get caps." );
1284     }
1285     else
1286     {
1287         BOOL bHasOverlay, bHasOverlayFourCC, bCanClipOverlay,
1288              bHasColorKey, bCanStretch;
1289
1290         /* Determine if the hardware supports overlay surfaces */
1291         bHasOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAY) ==
1292                        DDCAPS_OVERLAY) ? TRUE : FALSE;
1293         /* Determine if the hardware supports overlay surfaces */
1294         bHasOverlayFourCC = ((ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC) ==
1295                        DDCAPS_OVERLAYFOURCC) ? TRUE : FALSE;
1296         /* Determine if the hardware supports overlay surfaces */
1297         bCanClipOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAYCANTCLIP) ==
1298                        0 ) ? TRUE : FALSE;
1299         /* Determine if the hardware supports colorkeying */
1300         bHasColorKey = ((ddcaps.dwCaps & DDCAPS_COLORKEY) ==
1301                         DDCAPS_COLORKEY) ? TRUE : FALSE;
1302         /* Determine if the hardware supports scaling of the overlay surface */
1303         bCanStretch = ((ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ==
1304                        DDCAPS_OVERLAYSTRETCH) ? TRUE : FALSE;
1305         intf_WarnMsg( 3, "vout: DirectDraw Capabilities:" );
1306         intf_WarnMsg( 3, "       overlay=%i yuvoverlay=%i can_clip_overlay=%i "
1307                          "colorkey=%i stretch=%i",
1308                       bHasOverlay, bHasOverlayFourCC, bCanClipOverlay,
1309                       bHasColorKey, bCanStretch );
1310
1311         /* Overlay clipping support is interesting for us as it means we can
1312          * get rid of the colorkey alltogether */
1313         p_vout->p_sys->b_caps_overlay_clipping = bCanClipOverlay;
1314
1315     }
1316 }
1317
1318 /*****************************************************************************
1319  * DirectXGetSurfaceDesc: Get some more information about the surface
1320  *****************************************************************************
1321  * This function get and stores the surface descriptor which among things
1322  * has the pointer to the picture data.
1323  *****************************************************************************/
1324 static int DirectXGetSurfaceDesc( picture_t *p_pic )
1325 {
1326     HRESULT dxresult;
1327
1328     /* Lock the surface to get a valid pointer to the picture buffer */
1329     memset( &p_pic->p_sys->ddsd, 0, sizeof( DDSURFACEDESC ));
1330     p_pic->p_sys->ddsd.dwSize = sizeof(DDSURFACEDESC);
1331     dxresult = IDirectDrawSurface3_Lock( p_pic->p_sys->p_surface,
1332                                          NULL, &p_pic->p_sys->ddsd,
1333                                          DDLOCK_NOSYSLOCK | DDLOCK_WAIT,
1334                                          NULL );
1335     if ( dxresult == DDERR_SURFACELOST )
1336     {
1337         /* Your surface can be lost so be sure
1338          * to check this and restore it if needed */
1339         dxresult = IDirectDrawSurface3_Restore( p_pic->p_sys->p_surface );
1340         dxresult = IDirectDrawSurface3_Lock( p_pic->p_sys->p_surface, NULL,
1341                                              &p_pic->p_sys->ddsd,
1342                                              DDLOCK_NOSYSLOCK | DDLOCK_WAIT,
1343                                              NULL);
1344     }
1345     if( dxresult != DD_OK )
1346     {
1347         intf_ErrMsg( "vout: DirectXGetSurfaceDesc can't lock surface" );
1348         return 0;
1349     }
1350
1351     /* Unlock the Surface */
1352     dxresult = IDirectDrawSurface3_Unlock( p_pic->p_sys->p_surface, NULL );
1353
1354     return 1;
1355 }