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