Improve Aspect Ratio and Crop support
[vlc.git] / modules / video_output / sdl.c
1 /*****************************************************************************
2  * sdl.c: SDL video output display method
3  *****************************************************************************
4  * Copyright (C) 1998-2001 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Pierre Baillet <oct@zoy.org>
9  *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30
31 #include <vlc/vlc.h>
32 #include <vlc_interface.h>
33 #include <vlc_playlist.h>
34 #include <vlc_vout.h>
35 #include <vlc_keys.h>
36 //#include <vlc_aout.h>
37
38 #include <sys/types.h>
39 #ifndef WIN32
40 #   include <netinet/in.h>                            /* BSD: struct in_addr */
41 #endif
42
43 #include SDL_INCLUDE_FILE
44
45 /* SDL is not able to crop overlays - so use only 1 direct buffer */
46 #define SDL_MAX_DIRECTBUFFERS 1
47 #define SDL_DEFAULT_BPP 16
48
49 /*****************************************************************************
50  * vout_sys_t: video output SDL method descriptor
51  *****************************************************************************
52  * This structure is part of the video output thread descriptor.
53  * It describes the SDL specific properties of an output thread.
54  *****************************************************************************/
55 struct vout_sys_t
56 {
57     SDL_Surface *   p_display;                             /* display device */
58
59     int i_width;
60     int i_height;
61
62     /* For YUV output */
63     SDL_Overlay * p_overlay;   /* An overlay we keep to grab the XVideo port */
64
65     /* For RGB output */
66     int i_surfaces;
67
68     vlc_bool_t  b_cursor;
69     vlc_bool_t  b_cursor_autohidden;
70     mtime_t     i_lastmoved;
71     mtime_t     i_lastpressed;                        /* to track dbl-clicks */
72 };
73
74 /*****************************************************************************
75  * picture_sys_t: direct buffer method descriptor
76  *****************************************************************************
77  * This structure is part of the picture descriptor, it describes the
78  * SDL specific properties of a direct buffer.
79  *****************************************************************************/
80 struct picture_sys_t
81 {
82     SDL_Overlay *p_overlay;
83 };
84
85 /*****************************************************************************
86  * Local prototypes
87  *****************************************************************************/
88 static int  Open      ( vlc_object_t * );
89 static void Close     ( vlc_object_t * );
90 static int  Init      ( vout_thread_t * );
91 static void End       ( vout_thread_t * );
92 static int  Manage    ( vout_thread_t * );
93 static void Display   ( vout_thread_t *, picture_t * );
94
95 static int  OpenDisplay     ( vout_thread_t * );
96 static void CloseDisplay    ( vout_thread_t * );
97 static int  NewPicture      ( vout_thread_t *, picture_t * );
98 static void SetPalette      ( vout_thread_t *,
99                               uint16_t *, uint16_t *, uint16_t * );
100
101 static int ConvertKey( SDLKey );
102
103
104 #define CHROMA_TEXT N_("SDL chroma format")
105 #define CHROMA_LONGTEXT N_( \
106     "Force the SDL renderer to use a specific chroma format instead of " \
107     "trying to improve performances by using the most efficient one.")
108
109 /*****************************************************************************
110  * Module descriptor
111  *****************************************************************************/
112 vlc_module_begin();
113     set_shortname( "SDL" );
114     set_category( CAT_VIDEO );
115     set_subcategory( SUBCAT_VIDEO_VOUT );
116     set_description( _("Simple DirectMedia Layer video output") );
117     set_capability( "video output", 60 );
118     add_shortcut( "sdl" );
119     add_string( "sdl-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, VLC_TRUE );
120     set_callbacks( Open, Close );
121 #if defined( __i386__ ) || defined( __x86_64__ )
122     /* On i386, SDL is linked against svgalib */
123     linked_with_a_crap_library_which_uses_atexit();
124 #endif
125 vlc_module_end();
126
127 /*****************************************************************************
128  * OpenVideo: allocate SDL video thread output method
129  *****************************************************************************
130  * This function allocate and initialize a SDL vout method. It uses some of the
131  * vout properties to choose the correct mode, and change them according to the
132  * mode actually used.
133  *****************************************************************************/
134 static int Open ( vlc_object_t *p_this )
135 {
136     vout_thread_t * p_vout = (vout_thread_t *)p_this;
137     /* XXX: check for conflicts with the SDL audio output */
138     vlc_mutex_t *lock = var_AcquireMutex( "sdl" );
139
140 #ifdef HAVE_SETENV
141     char *psz_method;
142 #endif
143
144     if( lock == NULL )
145         return VLC_ENOMEM;
146
147     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
148     if( p_vout->p_sys == NULL )
149     {
150         vlc_mutex_unlock( lock );
151         return VLC_ENOMEM;
152     }
153
154     if( SDL_WasInit( SDL_INIT_VIDEO ) != 0 )
155     {
156         vlc_mutex_unlock( lock );
157         free( p_vout->p_sys );
158         return VLC_EGENERIC;
159     }
160
161     /* Allocate structure */
162
163     p_vout->pf_init = Init;
164     p_vout->pf_end = End;
165     p_vout->pf_manage = Manage;
166     p_vout->pf_render = NULL;
167     p_vout->pf_display = Display;
168
169 #ifdef HAVE_SETENV
170     psz_method = config_GetPsz( p_vout, "vout" );
171     if( psz_method )
172     {
173         while( *psz_method && *psz_method != ':' )
174         {
175             psz_method++;
176         }
177
178         if( *psz_method )
179         {
180             setenv( "SDL_VIDEODRIVER", psz_method + 1, 1 );
181         }
182     }
183 #endif
184
185     /* Initialize library */
186     if( SDL_Init( SDL_INIT_VIDEO
187 #ifndef WIN32
188     /* Win32 SDL implementation doesn't support SDL_INIT_EVENTTHREAD yet*/
189                 | SDL_INIT_EVENTTHREAD
190 #endif
191 #ifdef DEBUG
192     /* In debug mode you may want vlc to dump a core instead of staying
193      * stuck */
194                 | SDL_INIT_NOPARACHUTE
195 #endif
196                 ) < 0 )
197     {
198         msg_Err( p_vout, "cannot initialize SDL (%s)", SDL_GetError() );
199         free( p_vout->p_sys );
200         vlc_mutex_unlock( lock );
201         return VLC_EGENERIC;
202     }
203
204     /* Translate keys into unicode */
205     SDL_EnableUNICODE(1);
206
207     vlc_mutex_unlock( lock );
208
209     p_vout->p_sys->b_cursor = 1;
210     p_vout->p_sys->b_cursor_autohidden = 0;
211     p_vout->p_sys->i_lastmoved = mdate();
212
213     if( OpenDisplay( p_vout ) )
214     {
215         msg_Err( p_vout, "cannot set up SDL (%s)", SDL_GetError() );
216         SDL_QuitSubSystem( SDL_INIT_VIDEO );
217         free( p_vout->p_sys );
218         return VLC_EGENERIC;
219     }
220
221     return VLC_SUCCESS;
222 }
223
224 /*****************************************************************************
225  * Init: initialize SDL video thread output method
226  *****************************************************************************
227  * This function initialize the SDL display device.
228  *****************************************************************************/
229 static int Init( vout_thread_t *p_vout )
230 {
231     int i_index;
232     picture_t *p_pic;
233
234     p_vout->p_sys->i_surfaces = 0;
235
236     I_OUTPUTPICTURES = 0;
237
238     /* Initialize the output structure */
239     if( p_vout->p_sys->p_overlay == NULL )
240     {
241         /* All we have is an RGB image with square pixels */
242         p_vout->output.i_width  = p_vout->p_sys->i_width;
243         p_vout->output.i_height = p_vout->p_sys->i_height;
244         p_vout->output.i_aspect = p_vout->output.i_width
245                                    * VOUT_ASPECT_FACTOR
246                                    / p_vout->output.i_height;
247     }
248     else
249     {
250         /* We may need to convert the chroma, but at least we keep the
251          * aspect ratio */
252         p_vout->output.i_width  = p_vout->render.i_width;
253         p_vout->output.i_height = p_vout->render.i_height;
254         p_vout->output.i_aspect = p_vout->render.i_aspect;
255     }
256
257     /* Try to initialize SDL_MAX_DIRECTBUFFERS direct buffers */
258     while( I_OUTPUTPICTURES < SDL_MAX_DIRECTBUFFERS )
259     {
260         p_pic = NULL;
261
262         /* Find an empty picture slot */
263         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
264         {
265             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
266             {
267                 p_pic = p_vout->p_picture + i_index;
268                 break;
269             }
270         }
271
272         /* Allocate the picture if we found one */
273         if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
274         {
275             break;
276         }
277
278         p_pic->i_status = DESTROYED_PICTURE;
279         p_pic->i_type   = DIRECT_PICTURE;
280
281         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
282
283         I_OUTPUTPICTURES++;
284     }
285
286     return VLC_SUCCESS;
287 }
288
289 /*****************************************************************************
290  * End: terminate Sys video thread output method
291  *****************************************************************************
292  * Terminate an output method created by OpenVideo
293  *****************************************************************************/
294 static void End( vout_thread_t *p_vout )
295 {
296     int i_index;
297
298     /* Free the output buffers we allocated */
299     for( i_index = I_OUTPUTPICTURES ; i_index ; )
300     {
301         i_index--;
302         if( p_vout->p_sys->p_overlay == NULL )
303         {
304             /* RGB picture */
305         }
306         else
307         {
308             SDL_UnlockYUVOverlay(
309                     PP_OUTPUTPICTURE[ i_index ]->p_sys->p_overlay );
310             SDL_FreeYUVOverlay(
311                     PP_OUTPUTPICTURE[ i_index ]->p_sys->p_overlay );
312         }
313         free( PP_OUTPUTPICTURE[ i_index ]->p_sys );
314     }
315 }
316
317 /*****************************************************************************
318  * CloseVideo: destroy Sys video thread output method
319  *****************************************************************************
320  * Terminate an output method created by vout_SDLCreate
321  *****************************************************************************/
322 static void Close ( vlc_object_t *p_this )
323 {
324     vout_thread_t * p_vout = (vout_thread_t *)p_this;
325
326     CloseDisplay( p_vout );
327     SDL_QuitSubSystem( SDL_INIT_VIDEO );
328
329     free( p_vout->p_sys );
330 }
331
332 /*****************************************************************************
333  * Manage: handle Sys events
334  *****************************************************************************
335  * This function should be called regularly by video output thread. It returns
336  * a non null value if an error occurred.
337  *****************************************************************************/
338 static int Manage( vout_thread_t *p_vout )
339 {
340     SDL_Event event;                                            /* SDL event */
341     vlc_value_t val;
342     unsigned int i_width, i_height, i_x, i_y;
343
344     /* Process events */
345     while( SDL_PollEvent( &event ) )
346     {
347         switch( event.type )
348         {
349         case SDL_VIDEORESIZE:                          /* Resizing of window */
350             /* Update dimensions */
351             p_vout->i_changes |= VOUT_SIZE_CHANGE;
352             p_vout->i_window_width = p_vout->p_sys->i_width = event.resize.w;
353             p_vout->i_window_height = p_vout->p_sys->i_height = event.resize.h;
354             break;
355
356         /* Mouse move */
357         case SDL_MOUSEMOTION:
358             vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
359                                p_vout->p_sys->i_height,
360                                &i_x, &i_y, &i_width, &i_height );
361
362             val.i_int = ( event.motion.x - i_x )
363                          * p_vout->render.i_width / i_width;
364             var_Set( p_vout, "mouse-x", val );
365             val.i_int = ( event.motion.y - i_y )
366                          * p_vout->render.i_height / i_height;
367             var_Set( p_vout, "mouse-y", val );
368
369             val.b_bool = VLC_TRUE;
370             var_Set( p_vout, "mouse-moved", val );
371
372             if( p_vout->p_sys->b_cursor &&
373                 (abs(event.motion.xrel) > 2 || abs(event.motion.yrel) > 2) )
374             {
375                 if( p_vout->p_sys->b_cursor_autohidden )
376                 {
377                     p_vout->p_sys->b_cursor_autohidden = 0;
378                     SDL_ShowCursor( 1 );
379                 }
380                 else
381                 {
382                     p_vout->p_sys->i_lastmoved = mdate();
383                 }
384             }
385             break;
386
387         /* Mouse button released */
388         case SDL_MOUSEBUTTONUP:
389             switch( event.button.button )
390             {
391             case SDL_BUTTON_LEFT:
392                 var_Get( p_vout, "mouse-button-down", &val );
393                 val.i_int &= ~1;
394                 var_Set( p_vout, "mouse-button-down", val );
395
396                 val.b_bool = VLC_TRUE;
397                 var_Set( p_vout, "mouse-clicked", val );
398                 break;
399
400             case SDL_BUTTON_MIDDLE:
401                 {
402                     playlist_t *p_playlist;
403
404                     var_Get( p_vout, "mouse-button-down", &val );
405                     val.i_int &= ~2;
406                     var_Set( p_vout, "mouse-button-down", val );
407
408                     p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
409                                               FIND_ANYWHERE );
410                     if( p_playlist != NULL )
411                     {
412                         vlc_value_t val;
413                         var_Get( p_playlist, "intf-show", &val );
414                         val.b_bool = !val.b_bool;
415                         var_Set( p_playlist, "intf-show", val );
416                         vlc_object_release( p_playlist );
417                     }
418                 }
419                 break;
420
421             case SDL_BUTTON_RIGHT:
422                 {
423                     intf_thread_t *p_intf;
424                     playlist_t *p_playlist;
425
426                     var_Get( p_vout, "mouse-button-down", &val );
427                     val.i_int &= ~4;
428                     var_Set( p_vout, "mous-button-down", val );
429                     p_intf = vlc_object_find( p_vout, VLC_OBJECT_INTF,
430                                                       FIND_ANYWHERE );
431                     if( p_intf )
432                     {
433                         p_intf->b_menu_change = 1;
434                         vlc_object_release( p_intf );
435                     }
436
437                     p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
438                                                 FIND_ANYWHERE );
439
440                     if( p_playlist != NULL )
441                     {
442                         vlc_value_t val;
443                         val.b_bool = VLC_TRUE;
444                         var_Set( p_playlist, "intf-popupmenu", val );
445                         vlc_object_release( p_playlist );
446                     }
447                 }
448                 break;
449             }
450             break;
451
452         /* Mouse button pressed */
453         case SDL_MOUSEBUTTONDOWN:
454             switch( event.button.button )
455             {
456             case SDL_BUTTON_LEFT:
457                 var_Get( p_vout, "mouse-button-down", &val );
458                 val.i_int |= 1;
459                 var_Set( p_vout, "mouse-button-down", val );
460
461                 /* detect double-clicks */
462                 if( ( mdate() - p_vout->p_sys->i_lastpressed ) < 300000 )
463                     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
464
465                 p_vout->p_sys->i_lastpressed = mdate();
466                 break;
467
468             case SDL_BUTTON_MIDDLE:
469                 var_Get( p_vout, "mouse-button-down", &val );
470                 val.i_int |= 2;
471                 var_Set( p_vout, "mouse-button-down", val );
472                 break;
473
474             case SDL_BUTTON_RIGHT:
475                 var_Get( p_vout, "mouse-button-down", &val );
476                 val.i_int |= 4;
477                 var_Set( p_vout, "mouse-button-down", val );
478                 break;
479             }
480             break;
481
482         /* Quit event (close the window) */
483         case SDL_QUIT:
484             {
485                 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
486                 if( p_playlist != NULL )
487                 {
488                     playlist_Stop( p_playlist );
489                     vlc_object_release( p_playlist );
490                 }
491             }
492             break;
493
494         /* Key pressed */
495         case SDL_KEYDOWN:
496             val.i_int = ConvertKey( event.key.keysym.sym );
497
498             if( !val.i_int )
499             {
500                 if( ( event.key.keysym.unicode & 0xff80 ) == 0 )
501                 {
502                     val.i_int = event.key.keysym.unicode & 0x7f;
503                 }
504             }
505
506             if( val.i_int )
507             {
508                 if( ( event.key.keysym.mod & KMOD_SHIFT ) )
509                 {
510                     val.i_int |= KEY_MODIFIER_SHIFT;
511                 }
512                 if( ( event.key.keysym.mod & KMOD_CTRL ) )
513                 {
514                     val.i_int |= KEY_MODIFIER_CTRL;
515                 }
516                 if( ( event.key.keysym.mod & KMOD_ALT ) )
517                 {
518                     val.i_int |= KEY_MODIFIER_ALT;
519                 }
520                 var_Set( p_vout->p_libvlc, "key-pressed", val );
521             }
522
523         default:
524             break;
525         }
526     }
527
528     /* Fullscreen change */
529     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
530     {
531         vlc_value_t val_fs;
532
533         /* Update the object variable and trigger callback */
534         val_fs.b_bool = !p_vout->b_fullscreen;
535         p_vout->b_fullscreen = !p_vout->b_fullscreen;
536         var_Set( p_vout, "fullscreen", val_fs );
537
538         /*TODO: add the "always on top" code here !*/
539
540         p_vout->p_sys->b_cursor_autohidden = 0;
541         SDL_ShowCursor( p_vout->p_sys->b_cursor &&
542                         ! p_vout->p_sys->b_cursor_autohidden );
543
544         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
545         p_vout->i_changes |= VOUT_SIZE_CHANGE;
546     }
547
548     /* Crop or Aspect Ratio Changes */
549     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
550         p_vout->i_changes & VOUT_ASPECT_CHANGE )
551     {
552         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
553         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
554
555         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
556         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
557         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
558         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
559         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
560         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
561         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
562         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
563
564         p_vout->i_changes |= VOUT_SIZE_CHANGE;
565     }
566
567     /* Size change */
568     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
569     {
570         msg_Dbg( p_vout, "video display resized (%dx%d)",
571                  p_vout->p_sys->i_width, p_vout->p_sys->i_height );
572
573         CloseDisplay( p_vout );
574         OpenDisplay( p_vout );
575
576         /* We don't need to signal the vout thread about the size change if
577          * we can handle rescaling ourselves */
578         if( p_vout->p_sys->p_overlay != NULL )
579             p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
580     }
581
582     /* Pointer change */
583     if( ! p_vout->p_sys->b_cursor_autohidden &&
584         ( mdate() - p_vout->p_sys->i_lastmoved > 2000000 ) )
585     {
586         /* Hide the mouse automatically */
587         p_vout->p_sys->b_cursor_autohidden = 1;
588         SDL_ShowCursor( 0 );
589     }
590
591     return VLC_SUCCESS;
592 }
593
594 /*****************************************************************************
595  * Key events handling
596  *****************************************************************************/
597 static struct
598 {
599     SDLKey sdl_key;
600     int i_vlckey;
601 } sdlkeys_to_vlckeys[] =
602 {
603     { SDLK_F1,  KEY_F1 },
604     { SDLK_F2,  KEY_F2 },
605     { SDLK_F3,  KEY_F3 },
606     { SDLK_F4,  KEY_F4 },
607     { SDLK_F5,  KEY_F5 },
608     { SDLK_F6,  KEY_F6 },
609     { SDLK_F7,  KEY_F7 },
610     { SDLK_F8,  KEY_F8 },
611     { SDLK_F9,  KEY_F9 },
612     { SDLK_F10, KEY_F10 },
613     { SDLK_F11, KEY_F11 },
614     { SDLK_F12, KEY_F12 },
615
616     { SDLK_RETURN, KEY_ENTER },
617     { SDLK_KP_ENTER, KEY_ENTER },
618     { SDLK_SPACE, KEY_SPACE },
619     { SDLK_ESCAPE, KEY_ESC },
620
621     { SDLK_MENU, KEY_MENU },
622     { SDLK_LEFT, KEY_LEFT },
623     { SDLK_RIGHT, KEY_RIGHT },
624     { SDLK_UP, KEY_UP },
625     { SDLK_DOWN, KEY_DOWN },
626
627     { SDLK_HOME, KEY_HOME },
628     { SDLK_END, KEY_END },
629     { SDLK_PAGEUP, KEY_PAGEUP },
630     { SDLK_PAGEDOWN,  KEY_PAGEDOWN },
631
632     { SDLK_INSERT, KEY_INSERT },
633     { SDLK_DELETE, KEY_DELETE },
634     /*TODO: find a equivalent for SDL 
635     { , KEY_MEDIA_NEXT_TRACK }
636     { , KEY_MEDIA_PREV_TRACK }
637     { , KEY_VOLUME_MUTE }
638     { , KEY_VOLUME_DOWN }
639     { , KEY_VOLUME_UP }
640     { , KEY_MEDIA_PLAY_PAUSE }
641     { , KEY_MEDIA_PLAY_PAUSE }*/
642
643     { 0, 0 }
644 };
645
646 static int ConvertKey( SDLKey sdl_key )
647 {
648     int i;
649     for( i=0; sdlkeys_to_vlckeys[i].sdl_key != 0; i++ )
650     {
651         if( sdlkeys_to_vlckeys[i].sdl_key == sdl_key )
652         {
653             return sdlkeys_to_vlckeys[i].i_vlckey;
654         }
655     }
656     return 0;
657 }
658
659
660 /*****************************************************************************
661  * Display: displays previously rendered output
662  *****************************************************************************
663  * This function sends the currently rendered image to the display.
664  *****************************************************************************/
665 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
666 {
667     unsigned int x, y, w, h;
668     SDL_Rect disp;
669
670     vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
671                        &x, &y, &w, &h );
672     disp.x = x;
673     disp.y = y;
674     disp.w = w;
675     disp.h = h;
676
677     if( p_vout->p_sys->p_overlay == NULL )
678     {
679         /* RGB picture */
680         SDL_Flip( p_vout->p_sys->p_display );
681     }
682     else
683     {
684         /* Overlay picture */
685         SDL_UnlockYUVOverlay( p_pic->p_sys->p_overlay);
686         SDL_DisplayYUVOverlay( p_pic->p_sys->p_overlay , &disp );
687         SDL_LockYUVOverlay( p_pic->p_sys->p_overlay);
688     }
689 }
690
691 /* following functions are local */
692
693 /*****************************************************************************
694  * OpenDisplay: open and initialize SDL device
695  *****************************************************************************
696  * Open and initialize display according to preferences specified in the vout
697  * thread fields.
698  *****************************************************************************/
699 static int OpenDisplay( vout_thread_t *p_vout )
700 {
701     uint32_t i_flags;
702     int i_bpp;
703
704     /* SDL fucked up fourcc definitions on bigendian machines */
705     uint32_t i_sdl_chroma;
706     char *psz_chroma = NULL;
707     uint32_t i_chroma = 0;
708
709     /* Set main window's size */
710     p_vout->p_sys->i_width = p_vout->b_fullscreen ? p_vout->output.i_width :
711                                                     p_vout->i_window_width;
712     p_vout->p_sys->i_height = p_vout->b_fullscreen ? p_vout->output.i_height :
713                                                      p_vout->i_window_height;
714
715     /* Initialize flags and cursor */
716     i_flags = SDL_ANYFORMAT | SDL_HWPALETTE | SDL_HWSURFACE | SDL_DOUBLEBUF;
717     i_flags |= p_vout->b_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE;
718
719     i_bpp = SDL_VideoModeOK( p_vout->p_sys->i_width, p_vout->p_sys->i_height,
720                              SDL_DEFAULT_BPP, i_flags );
721     if( i_bpp == 0 )
722     {
723         msg_Err( p_vout, "no video mode available" );
724         return VLC_EGENERIC;
725     }
726
727     p_vout->p_sys->p_display = SDL_SetVideoMode( p_vout->p_sys->i_width,
728                                                  p_vout->p_sys->i_height,
729                                                  i_bpp, i_flags );
730
731     if( p_vout->p_sys->p_display == NULL )
732     {
733         msg_Err( p_vout, "cannot set video mode" );
734         return VLC_EGENERIC;
735     }
736
737     SDL_LockSurface( p_vout->p_sys->p_display );
738
739     if( ( psz_chroma = config_GetPsz( p_vout, "sdl-chroma" ) ) )
740     {
741         if( strlen( psz_chroma ) >= 4 )
742         {
743             memcpy(&i_chroma, psz_chroma, 4);
744             msg_Dbg( p_vout, "Forcing chroma to 0x%.8x (%4.4s)", i_chroma, (char*)&i_chroma );
745         }
746         else
747         {
748             free( psz_chroma );
749             psz_chroma = NULL;
750         }
751     }
752
753     /* Choose the chroma we will try first. */
754     do
755     {
756         if( !psz_chroma ) i_chroma = 0;
757         switch( i_chroma ? i_chroma : p_vout->render.i_chroma )
758         {
759             case VLC_FOURCC('Y','U','Y','2'):
760             case VLC_FOURCC('Y','U','N','V'):
761                 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
762                 i_sdl_chroma = SDL_YUY2_OVERLAY;
763                 break;
764             case VLC_FOURCC('U','Y','V','Y'):
765             case VLC_FOURCC('U','Y','N','V'):
766             case VLC_FOURCC('Y','4','2','2'):
767                 p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y');
768                 i_sdl_chroma = SDL_UYVY_OVERLAY;
769                 break;
770             case VLC_FOURCC('Y','V','Y','U'):
771                 p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U');
772                 i_sdl_chroma = SDL_YVYU_OVERLAY;
773                 break;
774             case VLC_FOURCC('Y','V','1','2'):
775             case VLC_FOURCC('I','4','2','0'):
776             case VLC_FOURCC('I','Y','U','V'):
777             default:
778                 p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
779                 i_sdl_chroma = SDL_YV12_OVERLAY;
780                 break;
781         }
782         free( psz_chroma ); psz_chroma = NULL;
783
784         p_vout->p_sys->p_overlay =
785             SDL_CreateYUVOverlay( 32, 32, i_sdl_chroma,
786                                   p_vout->p_sys->p_display );
787         /* FIXME: if the first overlay we find is software, don't stop,
788          * because we may find a hardware one later ... */
789     }
790     while( i_chroma && !p_vout->p_sys->p_overlay );
791
792
793     /* If this best choice failed, fall back to other chromas */
794     if( p_vout->p_sys->p_overlay == NULL )
795     {
796         p_vout->output.i_chroma = VLC_FOURCC('I','Y','U','V');
797         p_vout->p_sys->p_overlay =
798             SDL_CreateYUVOverlay( 32, 32, SDL_IYUV_OVERLAY,
799                                   p_vout->p_sys->p_display );
800     }
801
802     if( p_vout->p_sys->p_overlay == NULL )
803     {
804         p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
805         p_vout->p_sys->p_overlay =
806             SDL_CreateYUVOverlay( 32, 32, SDL_YV12_OVERLAY,
807                                   p_vout->p_sys->p_display );
808     }
809
810     if( p_vout->p_sys->p_overlay == NULL )
811     {
812         p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
813         p_vout->p_sys->p_overlay =
814             SDL_CreateYUVOverlay( 32, 32, SDL_YUY2_OVERLAY,
815                                   p_vout->p_sys->p_display );
816     }
817
818     if( p_vout->p_sys->p_overlay == NULL )
819     {
820         msg_Warn( p_vout, "no SDL overlay for 0x%.8x (%4.4s)",
821                   p_vout->render.i_chroma, (char*)&p_vout->render.i_chroma );
822
823         switch( p_vout->p_sys->p_display->format->BitsPerPixel )
824         {
825             case 8:
826                 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
827                 p_vout->output.pf_setpalette = SetPalette;
828                 break;
829             case 15:
830                 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
831                 break;
832             case 16:
833                 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
834                 break;
835             case 24:
836                 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
837                 break;
838             case 32:
839                 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
840                 break;
841             default:
842                 msg_Err( p_vout, "unknown screen depth %i",
843                          p_vout->p_sys->p_display->format->BitsPerPixel );
844                 SDL_UnlockSurface( p_vout->p_sys->p_display );
845                 SDL_FreeSurface( p_vout->p_sys->p_display );
846                 return VLC_EGENERIC;
847         }
848
849         p_vout->output.i_rmask = p_vout->p_sys->p_display->format->Rmask;
850         p_vout->output.i_gmask = p_vout->p_sys->p_display->format->Gmask;
851         p_vout->output.i_bmask = p_vout->p_sys->p_display->format->Bmask;
852
853         SDL_WM_SetCaption( VOUT_TITLE " (software RGB SDL output)",
854                            VOUT_TITLE " (software RGB SDL output)" );
855     }
856     else
857     {
858         if( p_vout->p_sys->p_overlay->hw_overlay )
859         {
860             SDL_WM_SetCaption( VOUT_TITLE " (hardware YUV SDL output)",
861                                VOUT_TITLE " (hardware YUV SDL output)" );
862         }
863         else
864         {
865             SDL_WM_SetCaption( VOUT_TITLE " (software YUV SDL output)",
866                                VOUT_TITLE " (software YUV SDL output)" );
867         }
868     }
869
870     SDL_EventState( SDL_KEYUP, SDL_IGNORE );               /* ignore keys up */
871
872     return VLC_SUCCESS;
873 }
874
875 /*****************************************************************************
876  * CloseDisplay: close and reset SDL device
877  *****************************************************************************
878  * This function returns all resources allocated by OpenDisplay and restore
879  * the original state of the device.
880  *****************************************************************************/
881 static void CloseDisplay( vout_thread_t *p_vout )
882 {
883     SDL_FreeYUVOverlay( p_vout->p_sys->p_overlay );
884     SDL_UnlockSurface ( p_vout->p_sys->p_display );
885     SDL_FreeSurface( p_vout->p_sys->p_display );
886 }
887
888 /*****************************************************************************
889  * NewPicture: allocate a picture
890  *****************************************************************************
891  * Returns 0 on success, -1 otherwise
892  *****************************************************************************/
893 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
894 {
895     int i_width  = p_vout->output.i_width;
896     int i_height = p_vout->output.i_height;
897
898     if( p_vout->p_sys->p_overlay == NULL )
899     {
900         /* RGB picture */
901         if( p_vout->p_sys->i_surfaces )
902         {
903             /* We already allocated this surface, return */
904             return VLC_EGENERIC;
905         }
906
907         p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
908
909         if( p_pic->p_sys == NULL )
910         {
911             return VLC_ENOMEM;
912         }
913
914         switch( p_vout->p_sys->p_display->format->BitsPerPixel )
915         {
916             case 8:
917                 p_pic->p->i_pixel_pitch = 1;
918                 break;
919             case 15:
920             case 16:
921                 p_pic->p->i_pixel_pitch = 2;
922                 break;
923             case 24:
924             case 32:
925                 p_pic->p->i_pixel_pitch = 4;
926                 break;
927             default:
928                 return VLC_EGENERIC;
929         }
930
931         p_pic->p->p_pixels = p_vout->p_sys->p_display->pixels;
932         p_pic->p->i_lines = p_vout->p_sys->p_display->h;
933         p_pic->p->i_visible_lines = p_vout->p_sys->p_display->h;
934         p_pic->p->i_pitch = p_vout->p_sys->p_display->pitch;
935         p_pic->p->i_visible_pitch =
936             p_pic->p->i_pixel_pitch * p_vout->p_sys->p_display->w;
937
938         p_vout->p_sys->i_surfaces++;
939
940         p_pic->i_planes = 1;
941     }
942     else
943     {
944         p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
945
946         if( p_pic->p_sys == NULL )
947         {
948             return VLC_ENOMEM;
949         }
950
951         p_pic->p_sys->p_overlay =
952             SDL_CreateYUVOverlay( i_width, i_height,
953                                   p_vout->output.i_chroma,
954                                   p_vout->p_sys->p_display );
955
956         if( p_pic->p_sys->p_overlay == NULL )
957         {
958             free( p_pic->p_sys );
959             return VLC_EGENERIC;
960         }
961
962         SDL_LockYUVOverlay( p_pic->p_sys->p_overlay );
963
964         p_pic->Y_PIXELS = p_pic->p_sys->p_overlay->pixels[0];
965         p_pic->p[Y_PLANE].i_lines = p_pic->p_sys->p_overlay->h;
966         p_pic->p[Y_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h;
967         p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[0];
968
969         switch( p_vout->output.i_chroma )
970         {
971         case SDL_YV12_OVERLAY:
972             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
973             p_pic->p[Y_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w;
974
975             p_pic->U_PIXELS = p_pic->p_sys->p_overlay->pixels[2];
976             p_pic->p[U_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
977             p_pic->p[U_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
978             p_pic->p[U_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[2];
979             p_pic->p[U_PLANE].i_pixel_pitch = 1;
980             p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
981
982             p_pic->V_PIXELS = p_pic->p_sys->p_overlay->pixels[1];
983             p_pic->p[V_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
984             p_pic->p[V_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
985             p_pic->p[V_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[1];
986             p_pic->p[V_PLANE].i_pixel_pitch = 1;
987             p_pic->p[V_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
988
989             p_pic->i_planes = 3;
990             break;
991
992         case SDL_IYUV_OVERLAY:
993             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
994             p_pic->p[Y_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w;
995
996             p_pic->U_PIXELS = p_pic->p_sys->p_overlay->pixels[1];
997             p_pic->p[U_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
998             p_pic->p[U_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
999             p_pic->p[U_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[1];
1000             p_pic->p[U_PLANE].i_pixel_pitch = 1;
1001             p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
1002
1003             p_pic->V_PIXELS = p_pic->p_sys->p_overlay->pixels[2];
1004             p_pic->p[V_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
1005             p_pic->p[V_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
1006             p_pic->p[V_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[2];
1007             p_pic->p[V_PLANE].i_pixel_pitch = 1;
1008             p_pic->p[V_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
1009
1010             p_pic->i_planes = 3;
1011             break;
1012
1013         default:
1014             p_pic->p[Y_PLANE].i_pixel_pitch = 2;
1015             p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w * 2;
1016
1017             p_pic->i_planes = 1;
1018             break;
1019         }
1020     }
1021
1022     return VLC_SUCCESS;
1023 }
1024
1025 /*****************************************************************************
1026  * SetPalette: sets an 8 bpp palette
1027  *****************************************************************************/
1028 static void SetPalette( vout_thread_t *p_vout,
1029                         uint16_t *red, uint16_t *green, uint16_t *blue )
1030 {
1031     SDL_Color colors[256];
1032     int i;
1033
1034     /* Fill colors with color information */
1035     for( i = 0; i < 256; i++ )
1036     {
1037         colors[ i ].r = red[ i ] >> 8;
1038         colors[ i ].g = green[ i ] >> 8;
1039         colors[ i ].b = blue[ i ] >> 8;
1040     }
1041
1042     /* Set palette */
1043     if( SDL_SetColors( p_vout->p_sys->p_display, colors, 0, 256 ) == 0 )
1044     {
1045         msg_Err( p_vout, "failed to set palette" );
1046     }
1047 }
1048