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