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 SendEvents( vlc_object_t *, char const *,
91 vlc_value_t, vlc_value_t, void * );
93 /*****************************************************************************
95 *****************************************************************************/
96 #define EFFECT_TEXT N_("Select effect")
97 #define EFFECT_LONGTEXT N_( \
98 "Allows you to select different visual effects.")
101 set_description( _("OpenGL video output") );
102 set_capability( "video output", 20 );
103 add_shortcut( "opengl" );
104 set_callbacks( CreateVout, DestroyVout );
106 add_string( "opengl-effect", "none", NULL, EFFECT_TEXT,
107 EFFECT_LONGTEXT, VLC_TRUE );
110 /*****************************************************************************
111 * vout_sys_t: video output method descriptor
112 *****************************************************************************
113 * This structure is part of the video output thread descriptor.
114 * It describes the OpenGL specific properties of the output thread.
115 *****************************************************************************/
118 vout_thread_t *p_vout;
120 uint8_t *pp_buffer[2];
124 GLuint p_textures[2];
129 /*****************************************************************************
130 * CreateVout: This function allocates and initializes the OpenGL vout method.
131 *****************************************************************************/
132 static int CreateVout( vlc_object_t *p_this )
134 vout_thread_t *p_vout = (vout_thread_t *)p_this;
137 /* Allocate structure */
138 p_vout->p_sys = p_sys = malloc( sizeof( vout_sys_t ) );
141 msg_Err( p_vout, "out of memory" );
145 var_Create( p_vout, "opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
148 p_sys->i_tex_width = p_vout->render.i_width;
149 p_sys->i_tex_height = p_vout->render.i_height;
151 /* A texture must have a size aligned on a power of 2 */
152 p_sys->i_tex_width = GetAlignedSize( p_vout->render.i_width );
153 p_sys->i_tex_height = GetAlignedSize( p_vout->render.i_height );
156 msg_Dbg( p_vout, "Texture size: %dx%d", p_sys->i_tex_width,
157 p_sys->i_tex_height );
161 (vout_thread_t *)vlc_object_create( p_this, VLC_OBJECT_OPENGL );
162 if( p_sys->p_vout == NULL )
164 msg_Err( p_vout, "out of memory" );
167 vlc_object_attach( p_sys->p_vout, p_this );
169 p_sys->p_vout->i_window_width = p_vout->i_window_width;
170 p_sys->p_vout->i_window_height = p_vout->i_window_height;
171 p_sys->p_vout->b_fullscreen = p_vout->b_fullscreen;
172 p_sys->p_vout->render.i_width = p_vout->render.i_width;
173 p_sys->p_vout->render.i_height = p_vout->render.i_height;
174 p_sys->p_vout->render.i_aspect = p_vout->render.i_aspect;
175 p_sys->p_vout->b_scale = p_vout->b_scale;
176 p_sys->p_vout->i_alignment = p_vout->i_alignment;
178 p_sys->p_vout->p_module =
179 module_Need( p_sys->p_vout, "opengl provider", NULL, 0 );
180 if( p_sys->p_vout->p_module == NULL )
182 msg_Err( p_vout, "No OpenGL provider found" );
183 vlc_object_detach( p_sys->p_vout );
184 vlc_object_destroy( p_sys->p_vout );
188 p_vout->pf_init = Init;
189 p_vout->pf_end = End;
190 p_vout->pf_manage = Manage;
191 p_vout->pf_render = Render;
192 p_vout->pf_display = DisplayVideo;
193 p_vout->pf_control = Control;
195 /* Forward events from the opengl provider */
196 var_AddCallback( p_sys->p_vout, "mouse-x", SendEvents, p_vout );
197 var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout );
198 var_AddCallback( p_sys->p_vout, "mouse-moved", SendEvents, p_vout );
199 var_AddCallback( p_sys->p_vout, "mouse-clicked", SendEvents, p_vout );
204 /*****************************************************************************
205 * Init: initialize the OpenGL video thread output method
206 *****************************************************************************/
207 static int Init( vout_thread_t *p_vout )
209 vout_sys_t *p_sys = p_vout->p_sys;
210 int i_pixel_pitch, i_index;
213 p_sys->p_vout->pf_init( p_sys->p_vout );
216 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
217 p_vout->output.i_rmask = 0x00ff0000;
218 p_vout->output.i_gmask = 0x0000ff00;
219 p_vout->output.i_bmask = 0x000000ff;
222 #if VLCGL_RGB_FORMAT == GL_RGB
223 # if VLCGL_RGB_TYPE == GL_UNSIGNED_BYTE
224 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
225 p_vout->output.i_rmask = 0x000000ff;
226 p_vout->output.i_gmask = 0x0000ff00;
227 p_vout->output.i_bmask = 0x00ff0000;
230 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
231 p_vout->output.i_rmask = 0xf800;
232 p_vout->output.i_gmask = 0x07e0;
233 p_vout->output.i_bmask = 0x001f;
237 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
238 p_vout->output.i_rmask = 0x000000ff;
239 p_vout->output.i_gmask = 0x0000ff00;
240 p_vout->output.i_bmask = 0x00ff0000;
245 /* Since OpenGL can do rescaling for us, stick to the default
246 * coordinates and aspect. */
247 p_vout->output.i_width = p_vout->render.i_width;
248 p_vout->output.i_height = p_vout->render.i_height;
249 p_vout->output.i_aspect = p_vout->render.i_aspect;
251 /* We know the chroma, allocate one buffer which will be used
252 * directly by the decoder */
253 p_sys->pp_buffer[0] =
254 malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
255 if( !p_sys->pp_buffer[0] )
257 msg_Err( p_vout, "Out of memory" );
260 p_sys->pp_buffer[1] =
261 malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
262 if( !p_sys->pp_buffer[1] )
264 msg_Err( p_vout, "Out of memory" );
268 p_vout->p_picture[0].i_planes = 1;
269 p_vout->p_picture[0].p->p_pixels = p_sys->pp_buffer[0];
270 p_vout->p_picture[0].p->i_lines = p_vout->output.i_height;
271 p_vout->p_picture[0].p->i_pixel_pitch = i_pixel_pitch;
272 p_vout->p_picture[0].p->i_pitch = p_vout->output.i_width *
273 p_vout->p_picture[0].p->i_pixel_pitch;
274 p_vout->p_picture[0].p->i_visible_pitch = p_vout->output.i_width *
275 p_vout->p_picture[0].p->i_pixel_pitch;
277 p_vout->p_picture[0].i_status = DESTROYED_PICTURE;
278 p_vout->p_picture[0].i_type = DIRECT_PICTURE;
280 PP_OUTPUTPICTURE[ 0 ] = &p_vout->p_picture[0];
282 I_OUTPUTPICTURES = 1;
284 glGenTextures( 2, p_sys->p_textures );
286 for( i_index = 0; i_index < 2; i_index++ )
288 glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
290 /* Set the texture parameters */
291 glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
293 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
294 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
296 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP );
297 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP );
299 glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
302 /* Tell the driver not to make a copy of the texture but to use
304 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
305 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
308 /* Use VRAM texturing */
309 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
310 GL_STORAGE_CACHED_APPLE );
312 /* Use AGP texturing */
313 glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
314 GL_STORAGE_SHARED_APPLE );
318 /* Call glTexImage2D only once, and use glTexSubImage2D later */
319 glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
320 p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
321 p_sys->pp_buffer[i_index] );
325 glDisable(GL_DEPTH_TEST);
326 glDepthMask(GL_FALSE);
327 glDisable(GL_CULL_FACE);
328 glClear( GL_COLOR_BUFFER_BIT );
330 /* Check if the user asked for useless visual effects */
331 var_Get( p_vout, "opengl-effect", &val );
332 if( !val.psz_string || !strcmp( val.psz_string, "none" ))
334 p_sys->i_effect = OPENGL_EFFECT_NONE;
336 else if( !strcmp( val.psz_string, "cube" ) )
338 p_sys->i_effect = OPENGL_EFFECT_CUBE;
340 glEnable( GL_CULL_FACE);
341 //glEnable( GL_DEPTH_TEST );
343 else if( !strcmp( val.psz_string, "transparent-cube" ) )
345 p_sys->i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
347 glDisable( GL_DEPTH_TEST );
348 glEnable( GL_BLEND );
349 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
353 msg_Warn( p_vout, "no valid opengl effect provided, using "
355 p_sys->i_effect = OPENGL_EFFECT_NONE;
357 if( val.psz_string ) free( val.psz_string );
359 if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
360 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
362 /* Set the perpective */
363 glMatrixMode( GL_PROJECTION );
365 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
366 glMatrixMode( GL_MODELVIEW );
368 glTranslatef( 0.0, 0.0, - 5.0 );
374 /*****************************************************************************
375 * End: terminate GLX video thread output method
376 *****************************************************************************/
377 static void End( vout_thread_t *p_vout )
383 /*****************************************************************************
384 * Destroy: destroy GLX video thread output method
385 *****************************************************************************
386 * Terminate an output method created by CreateVout
387 *****************************************************************************/
388 static void DestroyVout( vlc_object_t *p_this )
390 vout_thread_t *p_vout = (vout_thread_t *)p_this;
391 vout_sys_t *p_sys = p_vout->p_sys;
393 module_Unneed( p_sys->p_vout, p_sys->p_vout->p_module );
394 vlc_object_detach( p_sys->p_vout );
395 vlc_object_destroy( p_sys->p_vout );
397 /* Free the texture buffer*/
398 if( p_sys->pp_buffer[0] ) free( p_sys->pp_buffer[0] );
399 if( p_sys->pp_buffer[1] ) free( p_sys->pp_buffer[1] );
404 /*****************************************************************************
405 * Manage: handle Sys events
406 *****************************************************************************
407 * This function should be called regularly by video output thread. It returns
408 * a non null value if an error occured.
409 *****************************************************************************/
410 static int Manage( vout_thread_t *p_vout )
412 vout_sys_t *p_sys = p_vout->p_sys;
413 return p_sys->p_vout->pf_manage( p_sys->p_vout );
416 /*****************************************************************************
417 * Render: render previously calculated output
418 *****************************************************************************/
419 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
421 vout_sys_t *p_sys = p_vout->p_sys;
422 float f_width, f_height;
424 /* glTexCoord works differently with GL_TEXTURE_2D and
425 GL_TEXTURE_RECTANGLE_EXT */
427 f_width = (float)p_vout->output.i_width;
428 f_height = (float)p_vout->output.i_height;
430 f_width = (float)p_vout->output.i_width / p_sys->i_tex_width;
431 f_height = (float)p_vout->output.i_height / p_sys->i_tex_height;
434 glClear( GL_COLOR_BUFFER_BIT );
436 /* On Win32/GLX, we do this the usual way:
437 + Fill the buffer with new content,
438 + Reload the texture,
441 On OS X with VRAM or AGP texturing, the order has to be:
442 + Reload the texture,
443 + Fill the buffer with new content,
446 (Thanks to gcc from the Arstechnica forums for the tip)
448 Therefore, we have to use two buffers and textures. On Win32/GLX,
449 we reload the texture to be displayed and use it right away. On
450 OS X, we first render, then reload the texture to be used next
454 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
457 /* Update the texture */
458 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
459 p_vout->render.i_width, p_vout->render.i_height,
460 VLCGL_RGB_FORMAT, VLCGL_RGB_TYPE, p_sys->pp_buffer[0] );
463 if( p_sys->i_effect == OPENGL_EFFECT_NONE )
465 glEnable( VLCGL_TARGET );
466 glBegin( GL_POLYGON );
467 glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 );
468 glTexCoord2f( f_width, 0.0 ); glVertex2f( 1.0, 1.0 );
469 glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 );
470 glTexCoord2f( 0.0, f_height ); glVertex2f( -1.0, -1.0 );
475 glRotatef( 1.0, 0.3, 0.5, 0.7 );
477 glEnable( VLCGL_TARGET );
481 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
482 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
483 glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
484 glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
487 glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
488 glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
489 glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
490 glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
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 );
518 glDisable( VLCGL_TARGET );
522 p_sys->i_index = ( p_sys->i_index + 1 ) & 1;
523 p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
525 /* Update the texture */
526 glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
527 glTexSubImage2D( VLCGL_TARGET, 0, 0, 0, p_sys->i_tex_width,
528 p_sys->i_tex_height, VLCGL_FORMAT, VLCGL_TYPE,
529 p_sys->pp_buffer[p_sys->i_index] );
533 /*****************************************************************************
534 * DisplayVideo: displays previously rendered output
535 *****************************************************************************/
536 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
538 vout_sys_t *p_sys = p_vout->p_sys;
539 p_sys->p_vout->pf_swap( p_sys->p_vout );
542 int GetAlignedSize( int i_size )
544 /* Return the nearest power of 2 */
546 while( i_result < i_size )
553 /*****************************************************************************
554 * Control: control facility for the vout
555 *****************************************************************************/
556 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
558 vout_sys_t *p_sys = p_vout->p_sys;
560 if( p_sys->p_vout->pf_control )
561 return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args );
563 return vout_vaControlDefault( p_vout, i_query, args );
566 /*****************************************************************************
567 * SendEvents: forward mouse and keyboard events to the parent p_vout
568 *****************************************************************************/
569 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
570 vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
572 return var_Set( (vlc_object_t *)_p_vout, psz_var, newval );