1 /*****************************************************************************
2 * opengl.c: OpenGL video output
3 *****************************************************************************
4 * Copyright (C) 2004 VideoLAN
7 * Authors: Cyril Deguet <asmax@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <errno.h> /* ENOMEM */
29 #include <stdlib.h> /* malloc(), free() */
36 #include <OpenGL/gl.h>
37 #include <OpenGL/glext.h>
39 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
40 This allows sizes which are not powers of 2 */
41 #define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
43 /* OS X OpenGL supports YUV. Hehe. */
44 #define VLCGL_FORMAT GL_YCBCR_422_APPLE
45 #define VLCGL_TYPE GL_UNSIGNED_SHORT_8_8_APPLE
49 #define VLCGL_TARGET GL_TEXTURE_2D
52 #ifndef GL_UNSIGNED_SHORT_5_6_5
53 #define GL_UNSIGNED_SHORT_5_6_5 0x8363
55 //#define VLCGL_RGB_FORMAT GL_RGB
56 //#define VLCGL_RGB_TYPE GL_UNSIGNED_SHORT_5_6_5
59 //#define VLCGL_RGB_FORMAT GL_RGB
60 //#define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
63 #define VLCGL_RGB_FORMAT GL_RGBA
64 #define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
66 /* Use RGB on Win32/GLX */
67 #define VLCGL_FORMAT VLCGL_RGB_FORMAT
68 #define VLCGL_TYPE VLCGL_RGB_TYPE
72 #define OPENGL_EFFECT_NONE 1
73 #define OPENGL_EFFECT_CUBE 2
74 #define OPENGL_EFFECT_TRANSPARENT_CUBE 4
76 /*****************************************************************************
78 *****************************************************************************/
79 static int CreateVout ( vlc_object_t * );
80 static void DestroyVout ( vlc_object_t * );
81 static int Init ( vout_thread_t * );
82 static void End ( vout_thread_t * );
83 static int Manage ( vout_thread_t * );
84 static void Render ( vout_thread_t *, picture_t * );
85 static void DisplayVideo ( vout_thread_t *, picture_t * );
86 static int Control ( vout_thread_t *, int, va_list );
88 static inline int GetAlignedSize( int );
90 static int InitTextures( vout_thread_t * );
91 static int SendEvents( vlc_object_t *, char const *,
92 vlc_value_t, vlc_value_t, void * );
94 /*****************************************************************************
96 *****************************************************************************/
97 #define EFFECT_TEXT N_("Effect")
98 #define EFFECT_LONGTEXT N_( \
99 "Allows you to select different visual effects.")
101 static char *ppsz_effects[] = {
102 "none", "cube", "transparent-cube", NULL };
103 static char *ppsz_effects_text[] = {
104 N_("None"), N_("Cube"), N_("Transparent Cube"), NULL };
107 set_description( _("OpenGL video output") );
109 set_capability( "video output", 0 );
111 set_capability( "video output", 20 );
113 add_shortcut( "opengl" );
114 set_callbacks( CreateVout, DestroyVout );
116 add_string( "opengl-effect", "none", NULL, EFFECT_TEXT,
117 EFFECT_LONGTEXT, VLC_TRUE );
118 change_string_list( ppsz_effects, ppsz_effects_text, 0 );
121 /*****************************************************************************
122 * vout_sys_t: video output method descriptor
123 *****************************************************************************
124 * This structure is part of the video output thread descriptor.
125 * It describes the OpenGL specific properties of the output thread.
126 *****************************************************************************/
129 vout_thread_t *p_vout;
131 uint8_t *pp_buffer[2];
135 GLuint p_textures[2];
140 /*****************************************************************************
141 * CreateVout: This function allocates and initializes the OpenGL vout method.
142 *****************************************************************************/
143 static int CreateVout( vlc_object_t *p_this )
145 vout_thread_t *p_vout = (vout_thread_t *)p_this;
148 /* Allocate structure */
149 p_vout->p_sys = p_sys = malloc( sizeof( vout_sys_t ) );
152 msg_Err( p_vout, "out of memory" );
156 var_Create( p_vout, "opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
159 p_sys->i_tex_width = p_vout->render.i_width;
160 p_sys->i_tex_height = p_vout->render.i_height;
162 /* A texture must have a size aligned on a power of 2 */
163 p_sys->i_tex_width = GetAlignedSize( p_vout->render.i_width );
164 p_sys->i_tex_height = GetAlignedSize( p_vout->render.i_height );
167 msg_Dbg( p_vout, "Texture size: %dx%d", p_sys->i_tex_width,
168 p_sys->i_tex_height );
172 (vout_thread_t *)vlc_object_create( p_this, VLC_OBJECT_OPENGL );
173 if( p_sys->p_vout == NULL )
175 msg_Err( p_vout, "out of memory" );
178 vlc_object_attach( p_sys->p_vout, p_this );
180 p_sys->p_vout->i_window_width = p_vout->i_window_width;
181 p_sys->p_vout->i_window_height = p_vout->i_window_height;
182 p_sys->p_vout->b_fullscreen = p_vout->b_fullscreen;
183 p_sys->p_vout->render.i_width = p_vout->render.i_width;
184 p_sys->p_vout->render.i_height = p_vout->render.i_height;
185 p_sys->p_vout->render.i_aspect = p_vout->render.i_aspect;
186 p_sys->p_vout->b_scale = p_vout->b_scale;
187 p_sys->p_vout->i_alignment = p_vout->i_alignment;
189 p_sys->p_vout->p_module =
190 module_Need( p_sys->p_vout, "opengl provider", NULL, 0 );
191 if( p_sys->p_vout->p_module == NULL )
193 msg_Warn( p_vout, "No OpenGL provider found" );
194 vlc_object_detach( p_sys->p_vout );
195 vlc_object_destroy( p_sys->p_vout );
199 p_vout->pf_init = Init;
200 p_vout->pf_end = End;
201 p_vout->pf_manage = Manage;
202 p_vout->pf_render = Render;
203 p_vout->pf_display = DisplayVideo;
204 p_vout->pf_control = Control;
206 /* Forward events from the opengl provider */
207 var_Create( p_sys->p_vout, "mouse-x", VLC_VAR_INTEGER );
208 var_Create( p_sys->p_vout, "mouse-y", VLC_VAR_INTEGER );
209 var_Create( p_sys->p_vout, "mouse-moved", VLC_VAR_BOOL );
210 var_Create( p_sys->p_vout, "mouse-clicked", VLC_VAR_INTEGER );
211 var_Create( p_sys->p_vout, "video-on-top",
212 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
214 var_AddCallback( p_sys->p_vout, "mouse-x", SendEvents, p_vout );
215 var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout );
216 var_AddCallback( p_sys->p_vout, "mouse-moved", SendEvents, p_vout );
217 var_AddCallback( p_sys->p_vout, "mouse-clicked", SendEvents, p_vout );
222 /*****************************************************************************
223 * Init: initialize the OpenGL video thread output method
224 *****************************************************************************/
225 static int Init( vout_thread_t *p_vout )
227 vout_sys_t *p_sys = p_vout->p_sys;
231 p_sys->p_vout->pf_init( p_sys->p_vout );
234 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
235 p_vout->output.i_rmask = 0x00ff0000;
236 p_vout->output.i_gmask = 0x0000ff00;
237 p_vout->output.i_bmask = 0x000000ff;
240 #if VLCGL_RGB_FORMAT == GL_RGB
241 # if VLCGL_RGB_TYPE == GL_UNSIGNED_BYTE
242 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
243 p_vout->output.i_rmask = 0x000000ff;
244 p_vout->output.i_gmask = 0x0000ff00;
245 p_vout->output.i_bmask = 0x00ff0000;
248 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
249 p_vout->output.i_rmask = 0xf800;
250 p_vout->output.i_gmask = 0x07e0;
251 p_vout->output.i_bmask = 0x001f;
255 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
256 p_vout->output.i_rmask = 0x000000ff;
257 p_vout->output.i_gmask = 0x0000ff00;
258 p_vout->output.i_bmask = 0x00ff0000;
263 /* Since OpenGL can do rescaling for us, stick to the default
264 * coordinates and aspect. */
265 p_vout->output.i_width = p_vout->render.i_width;
266 p_vout->output.i_height = p_vout->render.i_height;
267 p_vout->output.i_aspect = p_vout->render.i_aspect;
269 /* We know the chroma, allocate one buffer which will be used
270 * directly by the decoder */
271 p_sys->pp_buffer[0] =
272 malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
273 if( !p_sys->pp_buffer[0] )
275 msg_Err( p_vout, "Out of memory" );
278 p_sys->pp_buffer[1] =
279 malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
280 if( !p_sys->pp_buffer[1] )
282 msg_Err( p_vout, "Out of memory" );
286 p_vout->p_picture[0].i_planes = 1;
287 p_vout->p_picture[0].p->p_pixels = p_sys->pp_buffer[0];
288 p_vout->p_picture[0].p->i_lines = p_vout->output.i_height;
289 p_vout->p_picture[0].p->i_visible_lines = p_vout->output.i_height;
290 p_vout->p_picture[0].p->i_pixel_pitch = i_pixel_pitch;
291 p_vout->p_picture[0].p->i_pitch = p_vout->output.i_width *
292 p_vout->p_picture[0].p->i_pixel_pitch;
293 p_vout->p_picture[0].p->i_visible_pitch = p_vout->output.i_width *
294 p_vout->p_picture[0].p->i_pixel_pitch;
296 p_vout->p_picture[0].i_status = DESTROYED_PICTURE;
297 p_vout->p_picture[0].i_type = DIRECT_PICTURE;
299 PP_OUTPUTPICTURE[ 0 ] = &p_vout->p_picture[0];
301 I_OUTPUTPICTURES = 1;
303 InitTextures( p_vout );
306 glDisable(GL_DEPTH_TEST);
307 glDepthMask(GL_FALSE);
308 glDisable(GL_CULL_FACE);
309 glClear( GL_COLOR_BUFFER_BIT );
311 /* Check if the user asked for useless visual effects */
312 var_Get( p_vout, "opengl-effect", &val );
313 if( !val.psz_string || !strcmp( val.psz_string, "none" ))
315 p_sys->i_effect = OPENGL_EFFECT_NONE;
317 else if( !strcmp( val.psz_string, "cube" ) )
319 p_sys->i_effect = OPENGL_EFFECT_CUBE;
321 glEnable( GL_CULL_FACE);
322 //glEnable( GL_DEPTH_TEST );
324 else if( !strcmp( val.psz_string, "transparent-cube" ) )
326 p_sys->i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
328 glDisable( GL_DEPTH_TEST );
329 glEnable( GL_BLEND );
330 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
334 msg_Warn( p_vout, "no valid opengl effect provided, using "
336 p_sys->i_effect = OPENGL_EFFECT_NONE;
338 if( val.psz_string ) free( val.psz_string );
340 if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
341 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
343 /* Set the perpective */
344 glMatrixMode( GL_PROJECTION );
346 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
347 glMatrixMode( GL_MODELVIEW );
349 glTranslatef( 0.0, 0.0, - 5.0 );
355 /*****************************************************************************
356 * End: terminate GLX video thread output method
357 *****************************************************************************/
358 static void End( vout_thread_t *p_vout )
364 /*****************************************************************************
365 * Destroy: destroy GLX video thread output method
366 *****************************************************************************
367 * Terminate an output method created by CreateVout
368 *****************************************************************************/
369 static void DestroyVout( vlc_object_t *p_this )
371 vout_thread_t *p_vout = (vout_thread_t *)p_this;
372 vout_sys_t *p_sys = p_vout->p_sys;
374 module_Unneed( p_sys->p_vout, p_sys->p_vout->p_module );
375 vlc_object_detach( p_sys->p_vout );
376 vlc_object_destroy( p_sys->p_vout );
378 /* Free the texture buffer*/
379 if( p_sys->pp_buffer[0] ) free( p_sys->pp_buffer[0] );
380 if( p_sys->pp_buffer[1] ) free( p_sys->pp_buffer[1] );
385 /*****************************************************************************
386 * Manage: handle Sys events
387 *****************************************************************************
388 * This function should be called regularly by video output thread. It returns
389 * a non null value if an error occurred.
390 *****************************************************************************/
391 static int Manage( vout_thread_t *p_vout )
393 vout_sys_t *p_sys = p_vout->p_sys;
394 int i_ret, i_fullscreen_change;
396 i_fullscreen_change = ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE );
398 p_sys->p_vout->i_changes = p_vout->i_changes;
399 i_ret = p_sys->p_vout->pf_manage( p_sys->p_vout );
400 p_vout->i_changes = p_sys->p_vout->i_changes;
403 /* On OS X, we create the window and the GL view when entering
404 fullscreen - the textures have to be inited again */
405 if( i_fullscreen_change )
407 InitTextures( p_vout );
409 switch( p_sys->i_effect )
411 case OPENGL_EFFECT_CUBE:
412 glEnable( GL_CULL_FACE );
415 case OPENGL_EFFECT_TRANSPARENT_CUBE:
416 glDisable( GL_DEPTH_TEST );
417 glEnable( GL_BLEND );
418 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
422 if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
423 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
425 /* Set the perpective */
426 glMatrixMode( GL_PROJECTION );
428 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
429 glMatrixMode( GL_MODELVIEW );
431 glTranslatef( 0.0, 0.0, - 5.0 );
439 /*****************************************************************************
440 * Render: render previously calculated output
441 *****************************************************************************/
442 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
444 vout_sys_t *p_sys = p_vout->p_sys;
445 float f_width, f_height;
447 /* glTexCoord works differently with GL_TEXTURE_2D and
448 GL_TEXTURE_RECTANGLE_EXT */
450 f_width = (float)p_vout->output.i_width;
451 f_height = (float)p_vout->output.i_height;
453 f_width = (float)p_vout->output.i_width / p_sys->i_tex_width;
454 f_height = (float)p_vout->output.i_height / p_sys->i_tex_height;
457 glClear( GL_COLOR_BUFFER_BIT );
459 /* On Win32/GLX, we do this the usual way:
460 + Fill the buffer with new content,
461 + Reload the texture,
464 On OS X with VRAM or AGP texturing, the order has to be:
465 + Reload the texture,
466 + Fill the buffer with new content,
469 (Thanks to gcc from the Arstechnica forums for the tip)
471 Therefore, we have to use two buffers and textures. On Win32/GLX,
472 we reload the texture to be displayed and use it right away. On
473 OS X, we first render, then reload the texture to be used next
477 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
480 /* Update the texture */
481 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
482 p_vout->render.i_width, p_vout->render.i_height,
483 VLCGL_RGB_FORMAT, VLCGL_RGB_TYPE, p_sys->pp_buffer[0] );
486 if( p_sys->i_effect == OPENGL_EFFECT_NONE )
488 glEnable( VLCGL_TARGET );
489 glBegin( GL_POLYGON );
490 glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 );
491 glTexCoord2f( f_width, 0.0 ); glVertex2f( 1.0, 1.0 );
492 glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 );
493 glTexCoord2f( 0.0, f_height ); glVertex2f( -1.0, -1.0 );
498 glRotatef( 1.0, 0.3, 0.5, 0.7 );
500 glEnable( VLCGL_TARGET );
504 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
505 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
506 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
507 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
510 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
511 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
512 glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
513 glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
516 glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
517 glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
518 glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
519 glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
522 glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
523 glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
524 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
525 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
528 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
529 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, 1.0, 1.0 );
530 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, 1.0, 1.0 );
531 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
534 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, - 1.0, 1.0 );
535 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
536 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
537 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, - 1.0, 1.0 );
541 glDisable( VLCGL_TARGET );
545 p_sys->i_index = ( p_sys->i_index + 1 ) & 1;
546 p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
548 /* Update the texture */
549 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
550 glTexSubImage2D( VLCGL_TARGET, 0, 0, 0, p_sys->i_tex_width,
551 p_sys->i_tex_height, VLCGL_FORMAT, VLCGL_TYPE,
552 p_sys->pp_buffer[p_sys->i_index] );
556 /*****************************************************************************
557 * DisplayVideo: displays previously rendered output
558 *****************************************************************************/
559 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
561 vout_sys_t *p_sys = p_vout->p_sys;
562 p_sys->p_vout->pf_swap( p_sys->p_vout );
565 int GetAlignedSize( int i_size )
567 /* Return the nearest power of 2 */
569 while( i_result < i_size )
576 /*****************************************************************************
577 * Control: control facility for the vout
578 *****************************************************************************/
579 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
581 vout_sys_t *p_sys = p_vout->p_sys;
583 if( p_sys->p_vout->pf_control )
584 return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args );
586 return vout_vaControlDefault( p_vout, i_query, args );
589 static int InitTextures( vout_thread_t *p_vout )
591 vout_sys_t *p_sys = p_vout->p_sys;
594 glDeleteTextures( 2, p_sys->p_textures );
595 glGenTextures( 2, p_sys->p_textures );
597 for( i_index = 0; i_index < 2; i_index++ )
599 glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
601 /* Set the texture parameters */
602 glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
604 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
605 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
607 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP );
608 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP );
610 glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
613 /* Tell the driver not to make a copy of the texture but to use
615 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
616 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
619 /* Use VRAM texturing */
620 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
621 GL_STORAGE_CACHED_APPLE );
623 /* Use AGP texturing */
624 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
625 GL_STORAGE_SHARED_APPLE );
629 /* Call glTexImage2D only once, and use glTexSubImage2D later */
630 glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
631 p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
632 p_sys->pp_buffer[i_index] );
638 /*****************************************************************************
639 * SendEvents: forward mouse and keyboard events to the parent p_vout
640 *****************************************************************************/
641 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
642 vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
644 return var_Set( (vlc_object_t *)_p_vout, psz_var, newval );