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_("Select effect")
98 #define EFFECT_LONGTEXT N_( \
99 "Allows you to select different visual effects.")
102 set_description( _("OpenGL video output") );
104 set_capability( "video output", 200 );
106 set_capability( "video output", 20 );
108 add_shortcut( "opengl" );
109 set_callbacks( CreateVout, DestroyVout );
111 add_string( "opengl-effect", "none", NULL, EFFECT_TEXT,
112 EFFECT_LONGTEXT, VLC_TRUE );
115 /*****************************************************************************
116 * vout_sys_t: video output method descriptor
117 *****************************************************************************
118 * This structure is part of the video output thread descriptor.
119 * It describes the OpenGL specific properties of the output thread.
120 *****************************************************************************/
123 vout_thread_t *p_vout;
125 uint8_t *pp_buffer[2];
129 GLuint p_textures[2];
134 /*****************************************************************************
135 * CreateVout: This function allocates and initializes the OpenGL vout method.
136 *****************************************************************************/
137 static int CreateVout( vlc_object_t *p_this )
139 vout_thread_t *p_vout = (vout_thread_t *)p_this;
142 /* Allocate structure */
143 p_vout->p_sys = p_sys = malloc( sizeof( vout_sys_t ) );
146 msg_Err( p_vout, "out of memory" );
150 var_Create( p_vout, "opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
153 p_sys->i_tex_width = p_vout->render.i_width;
154 p_sys->i_tex_height = p_vout->render.i_height;
156 /* A texture must have a size aligned on a power of 2 */
157 p_sys->i_tex_width = GetAlignedSize( p_vout->render.i_width );
158 p_sys->i_tex_height = GetAlignedSize( p_vout->render.i_height );
161 msg_Dbg( p_vout, "Texture size: %dx%d", p_sys->i_tex_width,
162 p_sys->i_tex_height );
166 (vout_thread_t *)vlc_object_create( p_this, VLC_OBJECT_OPENGL );
167 if( p_sys->p_vout == NULL )
169 msg_Err( p_vout, "out of memory" );
172 vlc_object_attach( p_sys->p_vout, p_this );
174 p_sys->p_vout->i_window_width = p_vout->i_window_width;
175 p_sys->p_vout->i_window_height = p_vout->i_window_height;
176 p_sys->p_vout->b_fullscreen = p_vout->b_fullscreen;
177 p_sys->p_vout->render.i_width = p_vout->render.i_width;
178 p_sys->p_vout->render.i_height = p_vout->render.i_height;
179 p_sys->p_vout->render.i_aspect = p_vout->render.i_aspect;
180 p_sys->p_vout->b_scale = p_vout->b_scale;
181 p_sys->p_vout->i_alignment = p_vout->i_alignment;
183 p_sys->p_vout->p_module =
184 module_Need( p_sys->p_vout, "opengl provider", NULL, 0 );
185 if( p_sys->p_vout->p_module == NULL )
187 msg_Warn( p_vout, "No OpenGL provider found" );
188 vlc_object_detach( p_sys->p_vout );
189 vlc_object_destroy( p_sys->p_vout );
193 p_vout->pf_init = Init;
194 p_vout->pf_end = End;
195 p_vout->pf_manage = Manage;
196 p_vout->pf_render = Render;
197 p_vout->pf_display = DisplayVideo;
198 p_vout->pf_control = Control;
200 var_Create( p_sys->p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
202 /* Forward events from the opengl provider */
203 var_AddCallback( p_sys->p_vout, "mouse-x", SendEvents, p_vout );
204 var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout );
205 var_AddCallback( p_sys->p_vout, "mouse-moved", SendEvents, p_vout );
206 var_AddCallback( p_sys->p_vout, "mouse-clicked", SendEvents, p_vout );
211 /*****************************************************************************
212 * Init: initialize the OpenGL video thread output method
213 *****************************************************************************/
214 static int Init( vout_thread_t *p_vout )
216 vout_sys_t *p_sys = p_vout->p_sys;
220 p_sys->p_vout->pf_init( p_sys->p_vout );
223 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
224 p_vout->output.i_rmask = 0x00ff0000;
225 p_vout->output.i_gmask = 0x0000ff00;
226 p_vout->output.i_bmask = 0x000000ff;
229 #if VLCGL_RGB_FORMAT == GL_RGB
230 # if VLCGL_RGB_TYPE == GL_UNSIGNED_BYTE
231 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
232 p_vout->output.i_rmask = 0x000000ff;
233 p_vout->output.i_gmask = 0x0000ff00;
234 p_vout->output.i_bmask = 0x00ff0000;
237 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
238 p_vout->output.i_rmask = 0xf800;
239 p_vout->output.i_gmask = 0x07e0;
240 p_vout->output.i_bmask = 0x001f;
244 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
245 p_vout->output.i_rmask = 0x000000ff;
246 p_vout->output.i_gmask = 0x0000ff00;
247 p_vout->output.i_bmask = 0x00ff0000;
252 /* Since OpenGL can do rescaling for us, stick to the default
253 * coordinates and aspect. */
254 p_vout->output.i_width = p_vout->render.i_width;
255 p_vout->output.i_height = p_vout->render.i_height;
256 p_vout->output.i_aspect = p_vout->render.i_aspect;
258 /* We know the chroma, allocate one buffer which will be used
259 * directly by the decoder */
260 p_sys->pp_buffer[0] =
261 malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
262 if( !p_sys->pp_buffer[0] )
264 msg_Err( p_vout, "Out of memory" );
267 p_sys->pp_buffer[1] =
268 malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
269 if( !p_sys->pp_buffer[1] )
271 msg_Err( p_vout, "Out of memory" );
275 p_vout->p_picture[0].i_planes = 1;
276 p_vout->p_picture[0].p->p_pixels = p_sys->pp_buffer[0];
277 p_vout->p_picture[0].p->i_lines = p_vout->output.i_height;
278 p_vout->p_picture[0].p->i_pixel_pitch = i_pixel_pitch;
279 p_vout->p_picture[0].p->i_pitch = p_vout->output.i_width *
280 p_vout->p_picture[0].p->i_pixel_pitch;
281 p_vout->p_picture[0].p->i_visible_pitch = p_vout->output.i_width *
282 p_vout->p_picture[0].p->i_pixel_pitch;
284 p_vout->p_picture[0].i_status = DESTROYED_PICTURE;
285 p_vout->p_picture[0].i_type = DIRECT_PICTURE;
287 PP_OUTPUTPICTURE[ 0 ] = &p_vout->p_picture[0];
289 I_OUTPUTPICTURES = 1;
291 InitTextures( p_vout );
294 glDisable(GL_DEPTH_TEST);
295 glDepthMask(GL_FALSE);
296 glDisable(GL_CULL_FACE);
297 glClear( GL_COLOR_BUFFER_BIT );
299 /* Check if the user asked for useless visual effects */
300 var_Get( p_vout, "opengl-effect", &val );
301 if( !val.psz_string || !strcmp( val.psz_string, "none" ))
303 p_sys->i_effect = OPENGL_EFFECT_NONE;
305 else if( !strcmp( val.psz_string, "cube" ) )
307 p_sys->i_effect = OPENGL_EFFECT_CUBE;
309 glEnable( GL_CULL_FACE);
310 //glEnable( GL_DEPTH_TEST );
312 else if( !strcmp( val.psz_string, "transparent-cube" ) )
314 p_sys->i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
316 glDisable( GL_DEPTH_TEST );
317 glEnable( GL_BLEND );
318 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
322 msg_Warn( p_vout, "no valid opengl effect provided, using "
324 p_sys->i_effect = OPENGL_EFFECT_NONE;
326 if( val.psz_string ) free( val.psz_string );
328 if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
329 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
331 /* Set the perpective */
332 glMatrixMode( GL_PROJECTION );
334 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
335 glMatrixMode( GL_MODELVIEW );
337 glTranslatef( 0.0, 0.0, - 5.0 );
343 /*****************************************************************************
344 * End: terminate GLX video thread output method
345 *****************************************************************************/
346 static void End( vout_thread_t *p_vout )
352 /*****************************************************************************
353 * Destroy: destroy GLX video thread output method
354 *****************************************************************************
355 * Terminate an output method created by CreateVout
356 *****************************************************************************/
357 static void DestroyVout( vlc_object_t *p_this )
359 vout_thread_t *p_vout = (vout_thread_t *)p_this;
360 vout_sys_t *p_sys = p_vout->p_sys;
362 module_Unneed( p_sys->p_vout, p_sys->p_vout->p_module );
363 vlc_object_detach( p_sys->p_vout );
364 vlc_object_destroy( p_sys->p_vout );
366 /* Free the texture buffer*/
367 if( p_sys->pp_buffer[0] ) free( p_sys->pp_buffer[0] );
368 if( p_sys->pp_buffer[1] ) free( p_sys->pp_buffer[1] );
373 /*****************************************************************************
374 * Manage: handle Sys events
375 *****************************************************************************
376 * This function should be called regularly by video output thread. It returns
377 * a non null value if an error occurred.
378 *****************************************************************************/
379 static int Manage( vout_thread_t *p_vout )
381 vout_sys_t *p_sys = p_vout->p_sys;
382 int i_ret, i_fullscreen_change;
384 i_fullscreen_change = ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE );
386 p_sys->p_vout->i_changes = p_vout->i_changes;
387 i_ret = p_sys->p_vout->pf_manage( p_sys->p_vout );
388 p_vout->i_changes = p_sys->p_vout->i_changes;
391 /* On OS X, we create the window and the GL view when entering
392 fullscreen - the textures have to be inited again */
393 if( i_fullscreen_change )
395 InitTextures( p_vout );
397 switch( p_sys->i_effect )
399 case OPENGL_EFFECT_CUBE:
400 glEnable( GL_CULL_FACE );
403 case OPENGL_EFFECT_TRANSPARENT_CUBE:
404 glDisable( GL_DEPTH_TEST );
405 glEnable( GL_BLEND );
406 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
410 if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
411 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
413 /* Set the perpective */
414 glMatrixMode( GL_PROJECTION );
416 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
417 glMatrixMode( GL_MODELVIEW );
419 glTranslatef( 0.0, 0.0, - 5.0 );
427 /*****************************************************************************
428 * Render: render previously calculated output
429 *****************************************************************************/
430 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
432 vout_sys_t *p_sys = p_vout->p_sys;
433 float f_width, f_height;
435 /* glTexCoord works differently with GL_TEXTURE_2D and
436 GL_TEXTURE_RECTANGLE_EXT */
438 f_width = (float)p_vout->output.i_width;
439 f_height = (float)p_vout->output.i_height;
441 f_width = (float)p_vout->output.i_width / p_sys->i_tex_width;
442 f_height = (float)p_vout->output.i_height / p_sys->i_tex_height;
445 glClear( GL_COLOR_BUFFER_BIT );
447 /* On Win32/GLX, we do this the usual way:
448 + Fill the buffer with new content,
449 + Reload the texture,
452 On OS X with VRAM or AGP texturing, the order has to be:
453 + Reload the texture,
454 + Fill the buffer with new content,
457 (Thanks to gcc from the Arstechnica forums for the tip)
459 Therefore, we have to use two buffers and textures. On Win32/GLX,
460 we reload the texture to be displayed and use it right away. On
461 OS X, we first render, then reload the texture to be used next
465 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
468 /* Update the texture */
469 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
470 p_vout->render.i_width, p_vout->render.i_height,
471 VLCGL_RGB_FORMAT, VLCGL_RGB_TYPE, p_sys->pp_buffer[0] );
474 if( p_sys->i_effect == OPENGL_EFFECT_NONE )
476 glEnable( VLCGL_TARGET );
477 glBegin( GL_POLYGON );
478 glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 );
479 glTexCoord2f( f_width, 0.0 ); glVertex2f( 1.0, 1.0 );
480 glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 );
481 glTexCoord2f( 0.0, f_height ); glVertex2f( -1.0, -1.0 );
486 glRotatef( 1.0, 0.3, 0.5, 0.7 );
488 glEnable( VLCGL_TARGET );
492 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
493 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
494 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
495 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
498 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
499 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
500 glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
501 glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
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 );
529 glDisable( VLCGL_TARGET );
533 p_sys->i_index = ( p_sys->i_index + 1 ) & 1;
534 p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
536 /* Update the texture */
537 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
538 glTexSubImage2D( VLCGL_TARGET, 0, 0, 0, p_sys->i_tex_width,
539 p_sys->i_tex_height, VLCGL_FORMAT, VLCGL_TYPE,
540 p_sys->pp_buffer[p_sys->i_index] );
544 /*****************************************************************************
545 * DisplayVideo: displays previously rendered output
546 *****************************************************************************/
547 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
549 vout_sys_t *p_sys = p_vout->p_sys;
550 p_sys->p_vout->pf_swap( p_sys->p_vout );
553 int GetAlignedSize( int i_size )
555 /* Return the nearest power of 2 */
557 while( i_result < i_size )
564 /*****************************************************************************
565 * Control: control facility for the vout
566 *****************************************************************************/
567 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
569 vout_sys_t *p_sys = p_vout->p_sys;
571 if( p_sys->p_vout->pf_control )
572 return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args );
574 return vout_vaControlDefault( p_vout, i_query, args );
577 static int InitTextures( vout_thread_t *p_vout )
579 vout_sys_t *p_sys = p_vout->p_sys;
582 glDeleteTextures( 2, p_sys->p_textures );
583 glGenTextures( 2, p_sys->p_textures );
585 for( i_index = 0; i_index < 2; i_index++ )
587 glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
589 /* Set the texture parameters */
590 glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
592 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
593 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
595 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP );
596 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP );
598 glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
601 /* Tell the driver not to make a copy of the texture but to use
603 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
604 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
607 /* Use VRAM texturing */
608 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
609 GL_STORAGE_CACHED_APPLE );
611 /* Use AGP texturing */
612 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
613 GL_STORAGE_SHARED_APPLE );
617 /* Call glTexImage2D only once, and use glTexSubImage2D later */
618 glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
619 p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
620 p_sys->pp_buffer[i_index] );
626 /*****************************************************************************
627 * SendEvents: forward mouse and keyboard events to the parent p_vout
628 *****************************************************************************/
629 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
630 vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
632 return var_Set( (vlc_object_t *)_p_vout, psz_var, newval );