]> git.sesse.net Git - vlc/blob - modules/video_output/opengl.c
Video filters and outputs strings (Refs:#438)
[vlc] / modules / video_output / opengl.c
1 /*****************************************************************************
2  * opengl.c: OpenGL video output
3  *****************************************************************************
4  * Copyright (C) 2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet <asmax@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Eric Petit <titer@m0k.org>
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 #include <stdlib.h>                                      /* malloc(), free() */
31 #include <string.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/vout.h>
35
36 #ifdef __APPLE__
37 #include <OpenGL/gl.h>
38 #include <OpenGL/glext.h>
39
40 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
41    This allows sizes which are not powers of 2 */
42 #define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
43
44 /* OS X OpenGL supports YUV. Hehe. */
45 #define VLCGL_FORMAT GL_YCBCR_422_APPLE
46 #define VLCGL_TYPE   GL_UNSIGNED_SHORT_8_8_APPLE
47 #else
48
49 #include <GL/gl.h>
50 #define VLCGL_TARGET GL_TEXTURE_2D
51
52 /* RV16 */
53 #ifndef GL_UNSIGNED_SHORT_5_6_5
54 #define GL_UNSIGNED_SHORT_5_6_5 0x8363
55 #endif
56 //#define VLCGL_RGB_FORMAT GL_RGB
57 //#define VLCGL_RGB_TYPE GL_UNSIGNED_SHORT_5_6_5
58
59 /* RV24 */
60 //#define VLCGL_RGB_FORMAT GL_RGB
61 //#define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
62
63 /* RV32 */
64 #define VLCGL_RGB_FORMAT GL_RGBA
65 #define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
66
67 /* YUY2 */
68 #ifndef YCBCR_MESA
69 #define YCBCR_MESA 0x8757
70 #endif
71 #ifndef UNSIGNED_SHORT_8_8_MESA
72 #define UNSIGNED_SHORT_8_8_MESA 0x85BA
73 #endif
74 #define VLCGL_YUV_FORMAT YCBCR_MESA
75 #define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
76
77 /* Use RGB on Win32/GLX */
78 #define VLCGL_FORMAT VLCGL_RGB_FORMAT
79 #define VLCGL_TYPE   VLCGL_RGB_TYPE
80 //#define VLCGL_FORMAT VLCGL_YUV_FORMAT
81 //#define VLCGL_TYPE   VLCGL_YUV_TYPE
82 #endif
83
84 #ifndef GL_CLAMP_TO_EDGE
85 #   define GL_CLAMP_TO_EDGE 0x812F
86 #endif
87
88 /* OpenGL effects */
89 #define OPENGL_EFFECT_NONE             1
90 #define OPENGL_EFFECT_CUBE             2
91 #define OPENGL_EFFECT_TRANSPARENT_CUBE 4
92
93 /*****************************************************************************
94  * Vout interface
95  *****************************************************************************/
96 static int  CreateVout   ( vlc_object_t * );
97 static void DestroyVout  ( vlc_object_t * );
98 static int  Init         ( vout_thread_t * );
99 static void End          ( vout_thread_t * );
100 static int  Manage       ( vout_thread_t * );
101 static void Render       ( vout_thread_t *, picture_t * );
102 static void DisplayVideo ( vout_thread_t *, picture_t * );
103 static int  Control      ( vout_thread_t *, int, va_list );
104
105 static inline int GetAlignedSize( int );
106
107 static int InitTextures( vout_thread_t * );
108 static int SendEvents( vlc_object_t *, char const *,
109                        vlc_value_t, vlc_value_t, void * );
110
111 /*****************************************************************************
112  * Module descriptor
113  *****************************************************************************/
114 #define SPEED_TEXT N_( "OpenGL cube rotation speed" )
115 /*****************************************************************************
116  * Module descriptor
117  *****************************************************************************/
118 #define SPEED_TEXT N_( "OpenGL cube rotation speed" )
119 #define SPEED_LONGTEXT N_( "Rotation speed of the OpenGL cube effect, if "\                                "enabled." )
120
121 #define EFFECT_TEXT N_("Effect")
122 #define EFFECT_LONGTEXT N_( \
123     "Several OpenGL visual effects are available." )
124
125 static char *ppsz_effects[] = {
126         "none", "cube", "transparent-cube" };
127 static char *ppsz_effects_text[] = {
128         N_("None"), N_("Cube"), N_("Transparent Cube") };
129
130 vlc_module_begin();
131     set_shortname( "OpenGL" );
132     set_category( CAT_VIDEO );
133     set_subcategory( SUBCAT_VIDEO_VOUT );
134     set_description( _("OpenGL video output") );
135 #ifdef __APPLE__
136     set_capability( "video output", 200 );
137 #else
138     set_capability( "video output", 20 );
139 #endif
140     add_shortcut( "opengl" );
141     add_float( "opengl-cube-speed", 2.0, NULL, SPEED_TEXT,
142                     SPEED_LONGTEXT, VLC_TRUE );
143     set_callbacks( CreateVout, DestroyVout );
144     add_string( "opengl-effect", "none", NULL, EFFECT_TEXT,
145                  EFFECT_LONGTEXT, VLC_FALSE );
146         change_string_list( ppsz_effects, ppsz_effects_text, 0 );
147 vlc_module_end();
148
149 /*****************************************************************************
150  * vout_sys_t: video output method descriptor
151  *****************************************************************************
152  * This structure is part of the video output thread descriptor.
153  * It describes the OpenGL specific properties of the output thread.
154  *****************************************************************************/
155 struct vout_sys_t
156 {
157     vout_thread_t *p_vout;
158
159     uint8_t    *pp_buffer[2];
160     int         i_index;
161     int         i_tex_width;
162     int         i_tex_height;
163     GLuint      p_textures[2];
164
165     int         i_effect;
166
167     float       f_speed;
168 };
169
170 /*****************************************************************************
171  * CreateVout: This function allocates and initializes the OpenGL vout method.
172  *****************************************************************************/
173 static int CreateVout( vlc_object_t *p_this )
174 {
175     vout_thread_t *p_vout = (vout_thread_t *)p_this;
176     vout_sys_t *p_sys;
177
178     /* Allocate structure */
179     p_vout->p_sys = p_sys = malloc( sizeof( vout_sys_t ) );
180     if( p_sys == NULL )
181     {
182         msg_Err( p_vout, "out of memory" );
183         return VLC_EGENERIC;
184     }
185
186     var_Create( p_vout, "opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
187
188     p_sys->i_index = 0;
189 #ifdef __APPLE__
190     p_sys->i_tex_width  = p_vout->fmt_in.i_width;
191     p_sys->i_tex_height = p_vout->fmt_in.i_height;
192 #else
193     /* A texture must have a size aligned on a power of 2 */
194     p_sys->i_tex_width  = GetAlignedSize( p_vout->fmt_in.i_width );
195     p_sys->i_tex_height = GetAlignedSize( p_vout->fmt_in.i_height );
196 #endif
197
198     msg_Dbg( p_vout, "Texture size: %dx%d", p_sys->i_tex_width,
199              p_sys->i_tex_height );
200
201     /* Get window */
202     p_sys->p_vout =
203         (vout_thread_t *)vlc_object_create( p_this, VLC_OBJECT_OPENGL );
204     if( p_sys->p_vout == NULL )
205     {
206         msg_Err( p_vout, "out of memory" );
207         return VLC_ENOMEM;
208     }
209     vlc_object_attach( p_sys->p_vout, p_this );
210
211     p_sys->p_vout->i_window_width = p_vout->i_window_width;
212     p_sys->p_vout->i_window_height = p_vout->i_window_height;
213     p_sys->p_vout->b_fullscreen = p_vout->b_fullscreen;
214     p_sys->p_vout->render.i_width = p_vout->render.i_width;
215     p_sys->p_vout->render.i_height = p_vout->render.i_height;
216     p_sys->p_vout->render.i_aspect = p_vout->render.i_aspect;
217     p_sys->p_vout->fmt_render = p_vout->fmt_render;
218     p_sys->p_vout->fmt_in = p_vout->fmt_in;
219     p_sys->p_vout->b_scale = p_vout->b_scale;
220     p_sys->p_vout->i_alignment = p_vout->i_alignment;
221
222     p_sys->p_vout->p_module =
223         module_Need( p_sys->p_vout, "opengl provider", NULL, 0 );
224     if( p_sys->p_vout->p_module == NULL )
225     {
226         msg_Warn( p_vout, "No OpenGL provider found" );
227         vlc_object_detach( p_sys->p_vout );
228         vlc_object_destroy( p_sys->p_vout );
229         return VLC_ENOOBJ;
230     }
231
232     p_sys->f_speed = var_CreateGetFloat( p_vout, "opengl-cube-speed" );
233
234     p_vout->pf_init = Init;
235     p_vout->pf_end = End;
236     p_vout->pf_manage = Manage;
237     p_vout->pf_render = Render;
238     p_vout->pf_display = DisplayVideo;
239     p_vout->pf_control = Control;
240
241     /* Forward events from the opengl provider */
242     var_Create( p_sys->p_vout, "mouse-x", VLC_VAR_INTEGER );
243     var_Create( p_sys->p_vout, "mouse-y", VLC_VAR_INTEGER );
244     var_Create( p_sys->p_vout, "mouse-moved", VLC_VAR_BOOL );
245     var_Create( p_sys->p_vout, "mouse-clicked", VLC_VAR_INTEGER );
246     var_Create( p_sys->p_vout, "mouse-button-down", VLC_VAR_INTEGER );
247     var_Create( p_sys->p_vout, "video-on-top",
248                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
249
250     var_AddCallback( p_sys->p_vout, "mouse-x", SendEvents, p_vout );
251     var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout );
252     var_AddCallback( p_sys->p_vout, "mouse-moved", SendEvents, p_vout );
253     var_AddCallback( p_sys->p_vout, "mouse-clicked", SendEvents, p_vout );
254     var_AddCallback( p_sys->p_vout, "mouse-button-down", SendEvents, p_vout );
255
256     return VLC_SUCCESS;
257 }
258
259 /*****************************************************************************
260  * Init: initialize the OpenGL video thread output method
261  *****************************************************************************/
262 static int Init( vout_thread_t *p_vout )
263 {
264     vout_sys_t *p_sys = p_vout->p_sys;
265     int i_pixel_pitch;
266     vlc_value_t val;
267
268     p_sys->p_vout->pf_init( p_sys->p_vout );
269
270 /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
271 #if ( defined( WORDS_BIGENDIAN ) && VLCGL_FORMAT == GL_YCBCR_422_APPLE ) || (VLCGL_FORMAT == YCBCR_MESA)
272     p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
273     i_pixel_pitch = 2;
274
275 #elif (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
276     p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y');
277     i_pixel_pitch = 2;
278
279 #elif VLCGL_FORMAT == GL_RGB
280 #   if VLCGL_TYPE == GL_UNSIGNED_BYTE
281     p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
282     p_vout->output.i_rmask = 0x000000ff;
283     p_vout->output.i_gmask = 0x0000ff00;
284     p_vout->output.i_bmask = 0x00ff0000;
285     i_pixel_pitch = 3;
286 #   else
287     p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
288     p_vout->output.i_rmask = 0xf800;
289     p_vout->output.i_gmask = 0x07e0;
290     p_vout->output.i_bmask = 0x001f;
291     i_pixel_pitch = 2;
292 #   endif
293 #else
294     p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
295     p_vout->output.i_rmask = 0x000000ff;
296     p_vout->output.i_gmask = 0x0000ff00;
297     p_vout->output.i_bmask = 0x00ff0000;
298     i_pixel_pitch = 4;
299 #endif
300
301     /* Since OpenGL can do rescaling for us, stick to the default
302      * coordinates and aspect. */
303     p_vout->output.i_width  = p_vout->render.i_width;
304     p_vout->output.i_height = p_vout->render.i_height;
305     p_vout->output.i_aspect = p_vout->render.i_aspect;
306
307     p_vout->fmt_out = p_vout->fmt_in;
308     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
309
310     /* We know the chroma, allocate one buffer which will be used
311      * directly by the decoder */
312     p_sys->pp_buffer[0] =
313         malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
314     if( !p_sys->pp_buffer[0] )
315     {
316         msg_Err( p_vout, "Out of memory" );
317         return -1;
318     }
319     p_sys->pp_buffer[1] =
320         malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
321     if( !p_sys->pp_buffer[1] )
322     {
323         msg_Err( p_vout, "Out of memory" );
324         return -1;
325     }
326
327     p_vout->p_picture[0].i_planes = 1;
328     p_vout->p_picture[0].p->p_pixels = p_sys->pp_buffer[0];
329     p_vout->p_picture[0].p->i_lines = p_vout->output.i_height;
330     p_vout->p_picture[0].p->i_visible_lines = p_vout->output.i_height;
331     p_vout->p_picture[0].p->i_pixel_pitch = i_pixel_pitch;
332     p_vout->p_picture[0].p->i_pitch = p_vout->output.i_width *
333         p_vout->p_picture[0].p->i_pixel_pitch;
334     p_vout->p_picture[0].p->i_visible_pitch = p_vout->output.i_width *
335         p_vout->p_picture[0].p->i_pixel_pitch;
336
337     p_vout->p_picture[0].i_status = DESTROYED_PICTURE;
338     p_vout->p_picture[0].i_type   = DIRECT_PICTURE;
339
340     PP_OUTPUTPICTURE[ 0 ] = &p_vout->p_picture[0];
341
342     I_OUTPUTPICTURES = 1;
343
344     if( p_sys->p_vout->pf_lock &&
345         p_sys->p_vout->pf_lock( p_sys->p_vout ) )
346     {
347         msg_Warn( p_vout, "could not lock OpenGL provider" );
348         return 0;
349     }
350
351     InitTextures( p_vout );
352
353     glDisable(GL_BLEND);
354     glDisable(GL_DEPTH_TEST);
355     glDepthMask(GL_FALSE);
356     glDisable(GL_CULL_FACE);
357     glClear( GL_COLOR_BUFFER_BIT );
358
359     /* Check if the user asked for useless visual effects */
360     var_Get( p_vout, "opengl-effect", &val );
361     if( !val.psz_string || !strcmp( val.psz_string, "none" ))
362     {
363         p_sys->i_effect = OPENGL_EFFECT_NONE;
364     }
365     else if( !strcmp( val.psz_string, "cube" ) )
366     {
367         p_sys->i_effect = OPENGL_EFFECT_CUBE;
368
369         glEnable( GL_CULL_FACE);
370         //glEnable( GL_DEPTH_TEST );
371     }
372     else if( !strcmp( val.psz_string, "transparent-cube" ) )
373     {
374         p_sys->i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
375
376         glDisable( GL_DEPTH_TEST );
377         glEnable( GL_BLEND );
378         glBlendFunc( GL_SRC_ALPHA, GL_ONE );
379     }
380     else
381     {
382         msg_Warn( p_vout, "no valid opengl effect provided, using "
383                   "\"none\"" );
384         p_sys->i_effect = OPENGL_EFFECT_NONE;
385     }
386     if( val.psz_string ) free( val.psz_string );
387
388     if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
389                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
390     {
391         /* Set the perpective */
392         glMatrixMode( GL_PROJECTION );
393         glLoadIdentity();
394         glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
395         glMatrixMode( GL_MODELVIEW );
396         glLoadIdentity();
397         glTranslatef( 0.0, 0.0, - 5.0 );
398     }
399
400     if( p_sys->p_vout->pf_unlock )
401     {
402         p_sys->p_vout->pf_unlock( p_sys->p_vout );
403     }
404
405     return 0;
406 }
407
408 /*****************************************************************************
409  * End: terminate GLX video thread output method
410  *****************************************************************************/
411 static void End( vout_thread_t *p_vout )
412 {
413     vout_sys_t *p_sys = p_vout->p_sys;
414
415     if( p_sys->p_vout->pf_lock &&
416         p_sys->p_vout->pf_lock( p_sys->p_vout ) )
417     {
418         msg_Warn( p_vout, "could not lock OpenGL provider" );
419         return;
420     }
421
422     glFinish();
423     glFlush();
424
425     if( p_sys->p_vout->pf_unlock )
426     {
427         p_sys->p_vout->pf_unlock( p_sys->p_vout );
428     }
429 }
430
431 /*****************************************************************************
432  * Destroy: destroy GLX video thread output method
433  *****************************************************************************
434  * Terminate an output method created by CreateVout
435  *****************************************************************************/
436 static void DestroyVout( vlc_object_t *p_this )
437 {
438     vout_thread_t *p_vout = (vout_thread_t *)p_this;
439     vout_sys_t *p_sys = p_vout->p_sys;
440
441     module_Unneed( p_sys->p_vout, p_sys->p_vout->p_module );
442     vlc_object_detach( p_sys->p_vout );
443     vlc_object_destroy( p_sys->p_vout );
444
445     /* Free the texture buffer*/
446     if( p_sys->pp_buffer[0] ) free( p_sys->pp_buffer[0] );
447     if( p_sys->pp_buffer[1] ) free( p_sys->pp_buffer[1] );
448
449     free( p_sys );
450 }
451
452 /*****************************************************************************
453  * Manage: handle Sys events
454  *****************************************************************************
455  * This function should be called regularly by video output thread. It returns
456  * a non null value if an error occurred.
457  *****************************************************************************/
458 static int Manage( vout_thread_t *p_vout )
459 {
460     vout_sys_t *p_sys = p_vout->p_sys;
461     int i_ret, i_fullscreen_change;
462
463     i_fullscreen_change = ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE );
464
465     p_vout->fmt_out.i_x_offset = p_sys->p_vout->fmt_in.i_x_offset =
466         p_vout->fmt_in.i_x_offset;
467     p_vout->fmt_out.i_y_offset = p_sys->p_vout->fmt_in.i_y_offset =
468         p_vout->fmt_in.i_y_offset;
469     p_vout->fmt_out.i_visible_width = p_sys->p_vout->fmt_in.i_visible_width =
470         p_vout->fmt_in.i_visible_width;
471     p_vout->fmt_out.i_visible_height = p_sys->p_vout->fmt_in.i_visible_height =
472         p_vout->fmt_in.i_visible_height;
473     p_vout->fmt_out.i_aspect = p_sys->p_vout->fmt_in.i_aspect =
474         p_vout->fmt_in.i_aspect;
475     p_vout->fmt_out.i_sar_num = p_sys->p_vout->fmt_in.i_sar_num =
476         p_vout->fmt_in.i_sar_num;
477     p_vout->fmt_out.i_sar_den = p_sys->p_vout->fmt_in.i_sar_den =
478         p_vout->fmt_in.i_sar_den;
479     p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
480
481     p_sys->p_vout->i_changes = p_vout->i_changes;
482     i_ret = p_sys->p_vout->pf_manage( p_sys->p_vout );
483     p_vout->i_changes = p_sys->p_vout->i_changes;
484
485 #ifdef __APPLE__
486     if( p_sys->p_vout->pf_lock &&
487         p_sys->p_vout->pf_lock( p_sys->p_vout ) )
488     {
489         msg_Warn( p_vout, "could not lock OpenGL provider" );
490         return i_ret;
491     }
492
493     /* On OS X, we create the window and the GL view when entering
494        fullscreen - the textures have to be inited again */
495     if( i_fullscreen_change )
496     {
497         InitTextures( p_vout );
498
499         switch( p_sys->i_effect )
500         {
501             case OPENGL_EFFECT_CUBE:
502                 glEnable( GL_CULL_FACE );
503                 break;
504
505             case OPENGL_EFFECT_TRANSPARENT_CUBE:
506                 glDisable( GL_DEPTH_TEST );
507                 glEnable( GL_BLEND );
508                 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
509                 break;
510         }
511
512         if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
513                     OPENGL_EFFECT_TRANSPARENT_CUBE ) )
514         {
515             /* Set the perpective */
516             glMatrixMode( GL_PROJECTION );
517             glLoadIdentity();
518             glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
519             glMatrixMode( GL_MODELVIEW );
520             glLoadIdentity();
521             glTranslatef( 0.0, 0.0, - 5.0 );
522         }
523     }
524
525     if( p_sys->p_vout->pf_unlock )
526     {
527         p_sys->p_vout->pf_unlock( p_sys->p_vout );
528     }
529 #endif
530
531     return i_ret;
532 }
533
534 /*****************************************************************************
535  * Render: render previously calculated output
536  *****************************************************************************/
537 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
538 {
539     vout_sys_t *p_sys = p_vout->p_sys;
540
541     /* On Win32/GLX, we do this the usual way:
542        + Fill the buffer with new content,
543        + Reload the texture,
544        + Use the texture.
545
546        On OS X with VRAM or AGP texturing, the order has to be:
547        + Reload the texture,
548        + Fill the buffer with new content,
549        + Use the texture.
550
551        (Thanks to gcc from the Arstechnica forums for the tip)
552
553        Therefore, we have to use two buffers and textures. On Win32/GLX,
554        we reload the texture to be displayed and use it right away. On
555        OS X, we first render, then reload the texture to be used next
556        time. */
557
558     if( p_sys->p_vout->pf_lock &&
559         p_sys->p_vout->pf_lock( p_sys->p_vout ) )
560     {
561         msg_Warn( p_vout, "could not lock OpenGL provider" );
562         return;
563     }
564
565 #ifdef __APPLE__
566     int i_new_index;
567     i_new_index = ( p_sys->i_index + 1 ) & 1;
568
569
570     /* Update the texture */
571     glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_new_index] );
572     glTexSubImage2D( VLCGL_TARGET, 0, 0, 0,
573                      p_vout->fmt_out.i_width,
574                      p_vout->fmt_out.i_height,
575                      VLCGL_FORMAT, VLCGL_TYPE, p_sys->pp_buffer[i_new_index] );
576
577     /* Bind to the previous texture for drawing */
578     glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
579
580     /* Switch buffers */
581     p_sys->i_index = i_new_index;
582     p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
583
584 #else
585     /* Update the texture */
586     glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
587                      p_vout->fmt_out.i_width,
588                      p_vout->fmt_out.i_height,
589                      VLCGL_FORMAT, VLCGL_TYPE, p_sys->pp_buffer[0] );
590 #endif
591
592     if( p_sys->p_vout->pf_unlock )
593     {
594         p_sys->p_vout->pf_unlock( p_sys->p_vout );
595     }
596 }
597
598 /*****************************************************************************
599  * DisplayVideo: displays previously rendered output
600  *****************************************************************************/
601 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
602 {
603     vout_sys_t *p_sys = p_vout->p_sys;
604     float f_width, f_height, f_x, f_y;
605
606     if( p_sys->p_vout->pf_lock &&
607         p_sys->p_vout->pf_lock( p_sys->p_vout ) )
608     {
609         msg_Warn( p_vout, "could not lock OpenGL provider" );
610         return;
611     }
612
613     /* glTexCoord works differently with GL_TEXTURE_2D and
614        GL_TEXTURE_RECTANGLE_EXT */
615 #ifdef __APPLE__
616     f_x = (float)p_vout->fmt_out.i_x_offset;
617     f_y = (float)p_vout->fmt_out.i_y_offset;
618     f_width = (float)p_vout->fmt_out.i_x_offset +
619               (float)p_vout->fmt_out.i_visible_width;
620     f_height = (float)p_vout->fmt_out.i_y_offset +
621                (float)p_vout->fmt_out.i_visible_height;
622 #else
623     f_x = (float)p_vout->fmt_out.i_x_offset / p_sys->i_tex_width;
624     f_y = (float)p_vout->fmt_out.i_y_offset / p_sys->i_tex_height;
625     f_width = ( (float)p_vout->fmt_out.i_x_offset +
626                 p_vout->fmt_out.i_visible_width ) / p_sys->i_tex_width;
627     f_height = ( (float)p_vout->fmt_out.i_y_offset +
628                  p_vout->fmt_out.i_visible_height ) / p_sys->i_tex_height;
629 #endif
630
631     /* Why drawing here and not in Render()? Because this way, the
632        OpenGL providers can call pf_display to force redraw. Currently,
633        the OS X provider uses it to get a smooth window resizing */
634
635     glClear( GL_COLOR_BUFFER_BIT );
636
637     if( p_sys->i_effect == OPENGL_EFFECT_NONE )
638     {
639         glEnable( VLCGL_TARGET );
640         glBegin( GL_POLYGON );
641         glTexCoord2f( f_x, f_y ); glVertex2f( -1.0, 1.0 );
642         glTexCoord2f( f_width, f_y ); glVertex2f( 1.0, 1.0 );
643         glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 );
644         glTexCoord2f( f_x, f_height ); glVertex2f( -1.0, -1.0 );
645         glEnd();
646     }
647     else
648     {
649         glRotatef( 0.5 * p_sys->f_speed , 0.3, 0.5, 0.7 );
650
651         glEnable( VLCGL_TARGET );
652         glBegin( GL_QUADS );
653
654         /* Front */
655         glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, 1.0, 1.0 );
656         glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
657         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
658         glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, 1.0, 1.0 );
659
660         /* Left */
661         glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, 1.0, - 1.0 );
662         glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
663         glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
664         glTexCoord2f( f_width, f_y ); glVertex3f( - 1.0, 1.0, 1.0 );
665
666         /* Back */
667         glTexCoord2f( f_x, f_y ); glVertex3f( 1.0, 1.0, - 1.0 );
668         glTexCoord2f( f_x, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
669         glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
670         glTexCoord2f( f_width, f_y ); glVertex3f( - 1.0, 1.0, - 1.0 );
671
672         /* Right */
673         glTexCoord2f( f_x, f_y ); glVertex3f( 1.0, 1.0, 1.0 );
674         glTexCoord2f( f_x, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
675         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
676         glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, 1.0, - 1.0 );
677
678         /* Top */
679         glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, 1.0, - 1.0 );
680         glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, 1.0, 1.0 );
681         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, 1.0, 1.0 );
682         glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, 1.0, - 1.0 );
683
684         /* Bottom */
685         glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, - 1.0, 1.0 );
686         glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
687         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
688         glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, - 1.0, 1.0 );
689         glEnd();
690     }
691
692     glDisable( VLCGL_TARGET );
693
694     p_sys->p_vout->pf_swap( p_sys->p_vout );
695
696     if( p_sys->p_vout->pf_unlock )
697     {
698         p_sys->p_vout->pf_unlock( p_sys->p_vout );
699     }
700 }
701
702 int GetAlignedSize( int i_size )
703 {
704     /* Return the nearest power of 2 */
705     int i_result = 1;
706     while( i_result < i_size )
707     {
708         i_result *= 2;
709     }
710     return i_result;
711 }
712
713 /*****************************************************************************
714  * Control: control facility for the vout
715  *****************************************************************************/
716 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
717 {
718     vout_sys_t *p_sys = p_vout->p_sys;
719
720     switch( i_query )
721     {
722     case VOUT_SNAPSHOT:
723         return vout_vaControlDefault( p_vout, i_query, args );
724
725     default:
726         if( p_sys->p_vout->pf_control )
727             return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args );
728         else
729             return vout_vaControlDefault( p_vout, i_query, args );
730     }
731 }
732
733 static int InitTextures( vout_thread_t *p_vout )
734 {
735     vout_sys_t *p_sys = p_vout->p_sys;
736     int i_index;
737
738     glDeleteTextures( 2, p_sys->p_textures );
739     glGenTextures( 2, p_sys->p_textures );
740
741     for( i_index = 0; i_index < 2; i_index++ )
742     {
743         glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
744
745         /* Set the texture parameters */
746         glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
747
748         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
749         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
750
751         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
752         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
753
754         glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
755
756 #ifdef __APPLE__
757         /* Tell the driver not to make a copy of the texture but to use
758            our buffer */
759         glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
760         glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
761
762 #if 0
763         /* Use VRAM texturing */
764         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
765                          GL_STORAGE_CACHED_APPLE );
766 #else
767         /* Use AGP texturing */
768         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
769                          GL_STORAGE_SHARED_APPLE );
770 #endif
771 #endif
772
773         /* Call glTexImage2D only once, and use glTexSubImage2D later */
774         glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
775                       p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
776                       p_sys->pp_buffer[i_index] );
777     }
778
779     return 0;
780 }
781
782 /*****************************************************************************
783  * SendEvents: forward mouse and keyboard events to the parent p_vout
784  *****************************************************************************/
785 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
786                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
787 {
788     return var_Set( (vlc_object_t *)_p_vout, psz_var, newval );
789 }