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_visible_lines = p_vout->output.i_height;
279 p_vout->p_picture[0].p->i_pixel_pitch = i_pixel_pitch;
280 p_vout->p_picture[0].p->i_pitch = p_vout->output.i_width *
281 p_vout->p_picture[0].p->i_pixel_pitch;
282 p_vout->p_picture[0].p->i_visible_pitch = p_vout->output.i_width *
283 p_vout->p_picture[0].p->i_pixel_pitch;
285 p_vout->p_picture[0].i_status = DESTROYED_PICTURE;
286 p_vout->p_picture[0].i_type = DIRECT_PICTURE;
288 PP_OUTPUTPICTURE[ 0 ] = &p_vout->p_picture[0];
290 I_OUTPUTPICTURES = 1;
292 InitTextures( p_vout );
295 glDisable(GL_DEPTH_TEST);
296 glDepthMask(GL_FALSE);
297 glDisable(GL_CULL_FACE);
298 glClear( GL_COLOR_BUFFER_BIT );
300 /* Check if the user asked for useless visual effects */
301 var_Get( p_vout, "opengl-effect", &val );
302 if( !val.psz_string || !strcmp( val.psz_string, "none" ))
304 p_sys->i_effect = OPENGL_EFFECT_NONE;
306 else if( !strcmp( val.psz_string, "cube" ) )
308 p_sys->i_effect = OPENGL_EFFECT_CUBE;
310 glEnable( GL_CULL_FACE);
311 //glEnable( GL_DEPTH_TEST );
313 else if( !strcmp( val.psz_string, "transparent-cube" ) )
315 p_sys->i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
317 glDisable( GL_DEPTH_TEST );
318 glEnable( GL_BLEND );
319 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
323 msg_Warn( p_vout, "no valid opengl effect provided, using "
325 p_sys->i_effect = OPENGL_EFFECT_NONE;
327 if( val.psz_string ) free( val.psz_string );
329 if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
330 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
332 /* Set the perpective */
333 glMatrixMode( GL_PROJECTION );
335 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
336 glMatrixMode( GL_MODELVIEW );
338 glTranslatef( 0.0, 0.0, - 5.0 );
344 /*****************************************************************************
345 * End: terminate GLX video thread output method
346 *****************************************************************************/
347 static void End( vout_thread_t *p_vout )
353 /*****************************************************************************
354 * Destroy: destroy GLX video thread output method
355 *****************************************************************************
356 * Terminate an output method created by CreateVout
357 *****************************************************************************/
358 static void DestroyVout( vlc_object_t *p_this )
360 vout_thread_t *p_vout = (vout_thread_t *)p_this;
361 vout_sys_t *p_sys = p_vout->p_sys;
363 module_Unneed( p_sys->p_vout, p_sys->p_vout->p_module );
364 vlc_object_detach( p_sys->p_vout );
365 vlc_object_destroy( p_sys->p_vout );
367 /* Free the texture buffer*/
368 if( p_sys->pp_buffer[0] ) free( p_sys->pp_buffer[0] );
369 if( p_sys->pp_buffer[1] ) free( p_sys->pp_buffer[1] );
374 /*****************************************************************************
375 * Manage: handle Sys events
376 *****************************************************************************
377 * This function should be called regularly by video output thread. It returns
378 * a non null value if an error occurred.
379 *****************************************************************************/
380 static int Manage( vout_thread_t *p_vout )
382 vout_sys_t *p_sys = p_vout->p_sys;
383 int i_ret, i_fullscreen_change;
385 i_fullscreen_change = ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE );
387 p_sys->p_vout->i_changes = p_vout->i_changes;
388 i_ret = p_sys->p_vout->pf_manage( p_sys->p_vout );
389 p_vout->i_changes = p_sys->p_vout->i_changes;
392 /* On OS X, we create the window and the GL view when entering
393 fullscreen - the textures have to be inited again */
394 if( i_fullscreen_change )
396 InitTextures( p_vout );
398 switch( p_sys->i_effect )
400 case OPENGL_EFFECT_CUBE:
401 glEnable( GL_CULL_FACE );
404 case OPENGL_EFFECT_TRANSPARENT_CUBE:
405 glDisable( GL_DEPTH_TEST );
406 glEnable( GL_BLEND );
407 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
411 if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
412 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
414 /* Set the perpective */
415 glMatrixMode( GL_PROJECTION );
417 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
418 glMatrixMode( GL_MODELVIEW );
420 glTranslatef( 0.0, 0.0, - 5.0 );
428 /*****************************************************************************
429 * Render: render previously calculated output
430 *****************************************************************************/
431 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
433 vout_sys_t *p_sys = p_vout->p_sys;
434 float f_width, f_height;
436 /* glTexCoord works differently with GL_TEXTURE_2D and
437 GL_TEXTURE_RECTANGLE_EXT */
439 f_width = (float)p_vout->output.i_width;
440 f_height = (float)p_vout->output.i_height;
442 f_width = (float)p_vout->output.i_width / p_sys->i_tex_width;
443 f_height = (float)p_vout->output.i_height / p_sys->i_tex_height;
446 glClear( GL_COLOR_BUFFER_BIT );
448 /* On Win32/GLX, we do this the usual way:
449 + Fill the buffer with new content,
450 + Reload the texture,
453 On OS X with VRAM or AGP texturing, the order has to be:
454 + Reload the texture,
455 + Fill the buffer with new content,
458 (Thanks to gcc from the Arstechnica forums for the tip)
460 Therefore, we have to use two buffers and textures. On Win32/GLX,
461 we reload the texture to be displayed and use it right away. On
462 OS X, we first render, then reload the texture to be used next
466 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
469 /* Update the texture */
470 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
471 p_vout->render.i_width, p_vout->render.i_height,
472 VLCGL_RGB_FORMAT, VLCGL_RGB_TYPE, p_sys->pp_buffer[0] );
475 if( p_sys->i_effect == OPENGL_EFFECT_NONE )
477 glEnable( VLCGL_TARGET );
478 glBegin( GL_POLYGON );
479 glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 );
480 glTexCoord2f( f_width, 0.0 ); glVertex2f( 1.0, 1.0 );
481 glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 );
482 glTexCoord2f( 0.0, f_height ); glVertex2f( -1.0, -1.0 );
487 glRotatef( 1.0, 0.3, 0.5, 0.7 );
489 glEnable( VLCGL_TARGET );
493 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
494 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
495 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
496 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
499 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
500 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
501 glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
502 glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
505 glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
506 glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
507 glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
508 glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
511 glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
512 glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
513 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
514 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
517 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
518 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, 1.0, 1.0 );
519 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, 1.0, 1.0 );
520 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
523 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, - 1.0, 1.0 );
524 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
525 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
526 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, - 1.0, 1.0 );
530 glDisable( VLCGL_TARGET );
534 p_sys->i_index = ( p_sys->i_index + 1 ) & 1;
535 p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
537 /* Update the texture */
538 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
539 glTexSubImage2D( VLCGL_TARGET, 0, 0, 0, p_sys->i_tex_width,
540 p_sys->i_tex_height, VLCGL_FORMAT, VLCGL_TYPE,
541 p_sys->pp_buffer[p_sys->i_index] );
545 /*****************************************************************************
546 * DisplayVideo: displays previously rendered output
547 *****************************************************************************/
548 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
550 vout_sys_t *p_sys = p_vout->p_sys;
551 p_sys->p_vout->pf_swap( p_sys->p_vout );
554 int GetAlignedSize( int i_size )
556 /* Return the nearest power of 2 */
558 while( i_result < i_size )
565 /*****************************************************************************
566 * Control: control facility for the vout
567 *****************************************************************************/
568 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
570 vout_sys_t *p_sys = p_vout->p_sys;
572 if( p_sys->p_vout->pf_control )
573 return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args );
575 return vout_vaControlDefault( p_vout, i_query, args );
578 static int InitTextures( vout_thread_t *p_vout )
580 vout_sys_t *p_sys = p_vout->p_sys;
583 glDeleteTextures( 2, p_sys->p_textures );
584 glGenTextures( 2, p_sys->p_textures );
586 for( i_index = 0; i_index < 2; i_index++ )
588 glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
590 /* Set the texture parameters */
591 glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
593 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
594 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
596 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP );
597 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP );
599 glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
602 /* Tell the driver not to make a copy of the texture but to use
604 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
605 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
608 /* Use VRAM texturing */
609 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
610 GL_STORAGE_CACHED_APPLE );
612 /* Use AGP texturing */
613 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
614 GL_STORAGE_SHARED_APPLE );
618 /* Call glTexImage2D only once, and use glTexSubImage2D later */
619 glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
620 p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
621 p_sys->pp_buffer[i_index] );
627 /*****************************************************************************
628 * SendEvents: forward mouse and keyboard events to the parent p_vout
629 *****************************************************************************/
630 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
631 vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
633 return var_Set( (vlc_object_t *)_p_vout, psz_var, newval );