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