Commit d4ac5770 authored by Eric Petit's avatar Eric Petit

ALL: OS X OpenGL provider

  + vout.m contains the common code for the QT video output and the
    GL provider (window creation, events handling)
  + opengl.c: higher priority on OS X
 Things are a bit broken atm, any help to debug is welcome ;p
parent bb7f54bb
...@@ -25,6 +25,8 @@ SOURCES_macosx = \ ...@@ -25,6 +25,8 @@ SOURCES_macosx = \
output.h \ output.h \
output.m \ output.m \
vout.m \ vout.m \
voutqt.m \
voutgl.m \
vout.h \ vout.h \
$(NULL) $(NULL)
...@@ -291,7 +291,9 @@ ...@@ -291,7 +291,9 @@
} }
else else
{ {
[o_window toggleFullscreen]; vlc_value_t val;
var_Get( p_vout, "fullscreen", &val );
var_Set( p_vout, "fullscreen", (vlc_value_t)!val.b_bool );
} }
break; break;
} }
......
...@@ -38,8 +38,11 @@ ...@@ -38,8 +38,11 @@
int E_(OpenIntf) ( vlc_object_t * ); int E_(OpenIntf) ( vlc_object_t * );
void E_(CloseIntf) ( vlc_object_t * ); void E_(CloseIntf) ( vlc_object_t * );
int E_(OpenVideo) ( vlc_object_t * ); int E_(OpenVideoQT) ( vlc_object_t * );
void E_(CloseVideo) ( vlc_object_t * ); void E_(CloseVideoQT) ( vlc_object_t * );
int E_(OpenVideoGL) ( vlc_object_t * );
void E_(CloseVideoGL) ( vlc_object_t * );
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
...@@ -59,49 +62,28 @@ void E_(CloseVideo) ( vlc_object_t * ); ...@@ -59,49 +62,28 @@ void E_(CloseVideo) ( vlc_object_t * );
"of the movie when resizing the video, stretch the video " \ "of the movie when resizing the video, stretch the video " \
"to fill the entire window." ) "to fill the entire window." )
#define MACOSX_VOUT_TEXT N_("video rendering mode")
#define MACOSX_VOUT_LONGTEXT N_("The default method is OpenGL " \
"for Quartz Extreme machines and Quartz for the others.")
#define OPENGL_EFFECT_TEXT N_("OpenGL effect")
#define OPENGL_EFFECT_LONGTEXT N_("Use 'None' to display the video " \
"without any fantasy, 'Cube' to let the video play on " \
"the faces of a rotating cube, 'Transparent cube' do make this " \
"cube transparent." )
#define FILL_TEXT N_("Fill fullscreen") #define FILL_TEXT N_("Fill fullscreen")
#define FILL_LONGTEXT N_("In fullscreen mode, crop the picture if " \ #define FILL_LONGTEXT N_("In fullscreen mode, crop the picture if " \
"necessary in order to fill the screen without black " \ "necessary in order to fill the screen without black " \
"borders (OpenGL only)." ) "borders (OpenGL only)." )
static char * effect_list[] = { "none", "cube", "transparent-cube" };
static char * effect_list_text[] = { N_("None"), N_("Cube"),
N_("Transparent cube") };
static char *ppsz_vout_list[] = { "auto", "quartz", "opengl" };
static char *ppsz_vout_list_text[] = { N_("Auto"), "Quartz", "OpenGL" };
vlc_module_begin(); vlc_module_begin();
set_description( _("Mac OS X interface, sound and video") ); set_description( _("Mac OS X interface, sound and video") );
set_capability( "interface", 100 ); set_capability( "interface", 100 );
set_callbacks( E_(OpenIntf), E_(CloseIntf) ); set_callbacks( E_(OpenIntf), E_(CloseIntf) );
add_submodule(); add_submodule();
set_capability( "video output", 200 ); set_capability( "video output", 100 );
set_callbacks( E_(OpenVideo), E_(CloseVideo) ); set_callbacks( E_(OpenVideoQT), E_(CloseVideoQT) );
add_integer( "macosx-vdev", 0, NULL, VDEV_TEXT, VDEV_LONGTEXT, add_integer( "macosx-vdev", 0, NULL, VDEV_TEXT, VDEV_LONGTEXT,
VLC_FALSE ); VLC_FALSE );
add_bool( "macosx-stretch", 0, NULL, STRETCH_TEXT, STRETCH_LONGTEXT, add_bool( "macosx-stretch", 0, NULL, STRETCH_TEXT, STRETCH_LONGTEXT,
VLC_FALSE ); VLC_FALSE );
add_float_with_range( "macosx-opaqueness", 1, 0, 1, NULL, add_float_with_range( "macosx-opaqueness", 1, 0, 1, NULL,
OPAQUENESS_TEXT, OPAQUENESS_LONGTEXT, VLC_TRUE ); OPAQUENESS_TEXT, OPAQUENESS_LONGTEXT, VLC_TRUE );
add_string( "macosx-vout", "auto", NULL, MACOSX_VOUT_TEXT,
MACOSX_VOUT_LONGTEXT, VLC_TRUE );
change_string_list( ppsz_vout_list, ppsz_vout_list_text, 0 );
add_string( "macosx-opengl-effect", "none", NULL,
OPENGL_EFFECT_TEXT, OPENGL_EFFECT_LONGTEXT,
VLC_TRUE );
change_string_list( effect_list, effect_list_text, 0 );
add_bool( "macosx-fill", 0, NULL, FILL_TEXT, FILL_LONGTEXT, add_bool( "macosx-fill", 0, NULL, FILL_TEXT, FILL_LONGTEXT,
VLC_TRUE ); VLC_TRUE );
add_submodule();
set_capability( "opengl provider", 100 );
set_callbacks( E_(OpenVideoGL), E_(CloseVideoGL) );
vlc_module_end(); vlc_module_end();
...@@ -30,10 +30,18 @@ ...@@ -30,10 +30,18 @@
@interface VLCWindow : NSWindow @interface VLCWindow : NSWindow
{ {
vout_thread_t * p_vout; vout_thread_t * p_vout;
Ptr p_fullscreen_state;
mtime_t i_time_mouse_last_moved;
} }
- (void)setVout:(vout_thread_t *)_p_vout; - (id)initWithVout:(vout_thread_t *)_p_vout
- (vout_thread_t *)getVout; frame:(NSRect *)s_frame;
- (void)close;
- (void)setOnTop:(bool)b_on_top;
- (void)hideMouse:(bool)b_hide;
- (void)manage;
- (void)scaleWindowWithFactor: (float)factor; - (void)scaleWindowWithFactor: (float)factor;
- (void)toggleFloatOnTop; - (void)toggleFloatOnTop;
...@@ -44,65 +52,3 @@ ...@@ -44,65 +52,3 @@
- (BOOL)windowShouldClose:(id)sender; - (BOOL)windowShouldClose:(id)sender;
@end @end
/*****************************************************************************
* VLCView interface
*****************************************************************************/
@interface VLCQTView : NSQuickDrawView
{
}
@end
/*****************************************************************************
* VLCView interface
*****************************************************************************/
@interface VLCGLView : NSOpenGLView
{
vout_thread_t * p_vout;
int i_effect;
unsigned long pi_textures[2];
float f_x;
float f_y;
int initDone;
}
- (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) p_vout;
- (void) initTextures;
- (void) reloadTexture: (int) index;
- (void) cleanUp;
@end
/*****************************************************************************
* vout_sys_t: MacOS X video output method descriptor
*****************************************************************************/
struct vout_sys_t
{
NSAutoreleasePool *o_pool;
NSRect s_rect;
VLCWindow * o_window;
VLCQTView * o_qtview;
int i_opengl;
int b_pos_saved;
vlc_bool_t b_mouse_moved;
mtime_t i_time_mouse_last_moved;
#ifdef __QUICKTIME__
CodecType i_codec;
CGrafPtr p_qdport;
ImageSequence i_seq;
MatrixRecordPtr p_matrix;
DecompressorComponent img_dc;
ImageDescriptionHandle h_img_descr;
Ptr p_fullscreen_state;
#endif
/* OpenGL */
VLCGLView * o_glview;
uint8_t * p_data[2];
uint8_t * p_data_orig[2];
int i_cur_pic;
};
...@@ -32,1025 +32,179 @@ ...@@ -32,1025 +32,179 @@
#include <stdlib.h> /* free() */ #include <stdlib.h> /* free() */
#include <string.h> /* strerror() */ #include <string.h> /* strerror() */
/* BeginFullScreen, EndFullScreen */
#include <QuickTime/QuickTime.h> #include <QuickTime/QuickTime.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <vlc_keys.h> #include <vlc_keys.h>
#include "intf.h" #include "intf.h"
#include "vout.h" #include "vout.h"
#define QT_MAX_DIRECTBUFFERS 10
#define VL_MAX_DISPLAYS 16
#define OPENGL_EFFECT_NONE 1
#define OPENGL_EFFECT_CUBE 2
#define OPENGL_EFFECT_TRANSPARENT_CUBE 4
struct picture_sys_t
{
void *p_data;
unsigned int i_size;
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int InitVideo ( vout_thread_t * );
static void EndVideo ( vout_thread_t * );
static int ManageVideo ( vout_thread_t * );
static void DisplayVideo ( vout_thread_t *, picture_t * );
static int ControlVideo ( vout_thread_t *, int, va_list );
static int CoCreateWindow ( vout_thread_t * );
static int CoDestroyWindow ( vout_thread_t * );
static int CoToggleFullscreen ( vout_thread_t * );
static int CoSetWindowOnTop ( vout_thread_t *, BOOL );
static void VLCHideMouse ( vout_thread_t *, BOOL );
static void QTScaleMatrix ( vout_thread_t * );
static int QTCreateSequence ( vout_thread_t * );
static void QTDestroySequence ( vout_thread_t * );
static int QTNewPicture ( vout_thread_t *, picture_t * );
static void QTFreePicture ( vout_thread_t *, picture_t * );
/*****************************************************************************
* OpenVideo: allocates MacOS X video thread output method
*****************************************************************************
* This function allocates and initializes a MacOS X vout method.
*****************************************************************************/
int E_(OpenVideo) ( vlc_object_t *p_this )
{
vout_thread_t * p_vout = (vout_thread_t *)p_this;
vlc_value_t val;
OSErr err;
int i_timeout;
p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
if( p_vout->p_sys == NULL )
{
msg_Err( p_vout, "out of memory" );
return( 1 );
}
memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
/* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
for( i_timeout = 20 ; i_timeout-- ; )
{
if( NSApp == NULL )
{
msleep( INTF_IDLE_SLEEP );
}
}
if( NSApp == NULL )
{
/* no MacOS X intf, unable to communicate with MT */
msg_Err( p_vout, "no MacOS X interface present" );
free( p_vout->p_sys );
return( 1 );
}
p_vout->pf_init = InitVideo;
p_vout->pf_end = EndVideo;
p_vout->pf_manage = ManageVideo;
p_vout->pf_render = NULL;
p_vout->pf_display = DisplayVideo;
p_vout->pf_control = ControlVideo;
p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
p_vout->p_sys->b_mouse_moved = VLC_TRUE;
p_vout->p_sys->i_time_mouse_last_moved = mdate();
/* set original window size */
p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
var_Create( p_vout, "macosx-vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
/* Check if we should use QuickTime or OpenGL */
var_Get( p_vout, "macosx-vout", &val);
if( !strncmp( val.psz_string, "auto", 4 ) )
{
p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
}
else if( !strncmp( val.psz_string, "opengl", 6 ) )
{
p_vout->p_sys->i_opengl = VLC_TRUE;
}
else
{
p_vout->p_sys->i_opengl = VLC_FALSE;
}
free( val.psz_string );
if( !p_vout->p_sys->i_opengl )
{
/* Initialize QuickTime */
p_vout->p_sys->h_img_descr =
(ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
p_vout->p_sys->p_matrix =
(MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
p_vout->p_sys->p_fullscreen_state = NULL;
if( ( err = EnterMovies() ) != noErr )
{
msg_Err( p_vout, "EnterMovies failed: %d", err );
free( p_vout->p_sys->p_matrix );
DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
free( p_vout->p_sys );
return VLC_EGENERIC;
}
/* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
/* Can we find the right chroma ? */
err = FindCodec( kComponentVideoUnsigned, bestSpeedCodec,
nil, &p_vout->p_sys->img_dc );
vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
if( err == noErr && p_vout->p_sys->img_dc != 0 )
{
p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
p_vout->p_sys->i_codec = kComponentVideoUnsigned;
}
else
{
msg_Err( p_vout, "failed to find an appropriate codec" );
}
if( p_vout->p_sys->img_dc == 0 )
{
free( p_vout->p_sys->p_matrix );
DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
free( p_vout->p_sys );
return VLC_EGENERIC;
}
msg_Dbg( p_vout, "using Quartz mode" );
}
else
{
p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
msg_Dbg( p_vout, "using OpenGL mode" );
}
/* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
NSArray * o_screens = [NSScreen screens];
if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
{
int i = 1;
vlc_value_t val2, text;
NSScreen * o_screen;
var_Get( p_vout, "macosx-vdev", &val );
var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
VLC_VAR_HASCHOICE );
text.psz_string = _("Video device");
var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
NSEnumerator * o_enumerator = [o_screens objectEnumerator];
while( (o_screen = [o_enumerator nextObject]) != NULL )
{
char psz_temp[255];
NSRect s_rect = [o_screen frame];
snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
"%s %d (%dx%d)", _("Screen"), i,
(int)s_rect.size.width, (int)s_rect.size.height );
text.psz_string = psz_temp;
val2.i_int = i;
var_Change( p_vout, "video-device",
VLC_VAR_ADDCHOICE, &val2, &text );
if( ( i - 1 ) == val.i_int )
{
var_Set( p_vout, "video-device", val2 );
}
i++;
}
var_AddCallback( p_vout, "video-device", vout_VarCallback,
NULL );
val2.b_bool = VLC_TRUE;
var_Set( p_vout, "intf-change", val2 );
}
/* Spawn window */
if( CoCreateWindow( p_vout ) )
{
msg_Err( p_vout, "unable to create a window" );
if( !p_vout->p_sys->i_opengl )
{
free( p_vout->p_sys->p_matrix );
DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
}
free( p_vout->p_sys );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* CloseVideo: destroy video thread output method
*****************************************************************************/
void E_(CloseVideo) ( vlc_object_t *p_this )
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
vout_thread_t * p_vout = (vout_thread_t *)p_this;
if( CoDestroyWindow( p_vout ) )
{
msg_Err( p_vout, "unable to destroy window" );
}
if ( p_vout->p_sys->p_fullscreen_state != NULL )
{
EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
}
if( !p_vout->p_sys->i_opengl )
{
/* Clean Up Quicktime environment */
ExitMovies();
free( p_vout->p_sys->p_matrix );
DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
}
else
{
[p_vout->p_sys->o_glview cleanUp];
}
[o_pool release];
free( p_vout->p_sys );
}
/*****************************************************************************
* InitVideo: initialize video thread output method
*****************************************************************************/
static int InitOpenGL ( vout_thread_t *p_vout );
static int InitQuickTime( vout_thread_t *p_vout );
static int InitVideo ( vout_thread_t *p_vout )
{
I_OUTPUTPICTURES = 0;
/* Initialize the output structure; we already found a codec,
* and the corresponding chroma we will be using. Since we can
* arbitrary scale, stick to the coordinates and aspect. */
p_vout->output.i_width = p_vout->render.i_width;
p_vout->output.i_height = p_vout->render.i_height;
p_vout->output.i_aspect = p_vout->render.i_aspect;
if( p_vout->p_sys->i_opengl )
{
return InitOpenGL( p_vout );
}
else
{
return InitQuickTime( p_vout );
}
}
static int InitOpenGL( vout_thread_t *p_vout )
{
picture_t *p_pic = NULL;
int i_bytes;
int i_index;
/* Apple OpenGL extensions only accept YUV as YUY2 */
p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
p_vout->output.i_rmask = 0xFF0000;
p_vout->output.i_gmask = 0x00FF00;
p_vout->output.i_bmask = 0x0000FF;
/* Allocate our 2 picture buffers */
i_bytes = 2 * p_vout->output.i_width * p_vout->output.i_height;
p_vout->p_sys->p_data[0] = vlc_memalign(
&p_vout->p_sys->p_data_orig[0], 16, i_bytes );
p_vout->p_sys->p_data[1] = vlc_memalign(
&p_vout->p_sys->p_data_orig[1], 16, i_bytes );
p_vout->p_sys->i_cur_pic = 1;
/* We declare only one picture and will switch buffers manually */
while( I_OUTPUTPICTURES < 1 )
{
/* Find an empty picture slot */
for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
{
if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
{
p_pic = p_vout->p_picture + i_index;
break;
}
}
if( p_pic == NULL )
{
break;
}
vout_InitPicture( VLC_OBJECT( p_vout ), p_pic,
p_vout->output.i_chroma, p_vout->output.i_width,
p_vout->output.i_height, p_vout->output.i_aspect );
p_pic->p_data = p_vout->p_sys->p_data[0];
p_pic->p[0].p_pixels = p_pic->p_data;
for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
{
p_pic->p[i_index].p_pixels =
p_pic->p[i_index-1].p_pixels +
p_pic->p[i_index-1].i_lines *
p_pic->p[i_index-1].i_pitch;
}
p_pic->i_status = DESTROYED_PICTURE;
p_pic->i_type = DIRECT_PICTURE;
PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
I_OUTPUTPICTURES++;
}
[p_vout->p_sys->o_glview lockFocus];
[p_vout->p_sys->o_glview initTextures];
[p_vout->p_sys->o_glview reshape];
[p_vout->p_sys->o_glview unlockFocus];
return 0;
}
static int InitQuickTime( vout_thread_t *p_vout )
{
picture_t *p_pic;
int i_index;
SetPort( p_vout->p_sys->p_qdport );
QTScaleMatrix( p_vout );
if( QTCreateSequence( p_vout ) )
{
msg_Err( p_vout, "unable to create sequence" );
return( 1 );
}
/* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
{
p_pic = NULL;
/* Find an empty picture slot */
for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
{
if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
{
p_pic = p_vout->p_picture + i_index;
break;
}
}
/* Allocate the picture */
if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
{
break;
}
p_pic->i_status = DESTROYED_PICTURE;
p_pic->i_type = DIRECT_PICTURE;
PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
I_OUTPUTPICTURES++;
}
return 0;
}
/***************************************************************************** /*****************************************************************************
* EndVideo: terminate video thread output method * VLCWindow implementation
*****************************************************************************/
static void EndVideo( vout_thread_t *p_vout )
{
int i_index;
if( !p_vout->p_sys->i_opengl )
{
QTDestroySequence( p_vout );
}
/* Free the direct buffers we allocated */
for( i_index = I_OUTPUTPICTURES; i_index; )
{
i_index--;
if( !p_vout->p_sys->i_opengl )
{
QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
}
else
{
free( p_vout->p_sys->p_data_orig[0] );
free( p_vout->p_sys->p_data_orig[1] );
}
}
}
/*****************************************************************************
* ManageVideo: handle events
*****************************************************************************
* This function should be called regularly by video output thread. It manages
* console events. It returns a non null value on error.
*****************************************************************************/
static int ManageVideo( vout_thread_t *p_vout )
{
if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
{
if( CoToggleFullscreen( p_vout ) )
{
return( 1 );
}
p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
}
if( p_vout->i_changes & VOUT_SIZE_CHANGE )
{
if( !p_vout->p_sys->i_opengl )
{
QTScaleMatrix( p_vout );
SetDSequenceMatrix( p_vout->p_sys->i_seq,
p_vout->p_sys->p_matrix );
}
p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
}
/* hide/show mouse cursor
* this code looks unnecessarily complicated, but is necessary like this.
* it has to deal with multiple monitors and therefore checks a lot */
if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
{
if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
{
VLCHideMouse( p_vout, YES );
}
}
else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
{
VLCHideMouse( p_vout, NO );
}
/* disable screen saver */
UpdateSystemActivity( UsrActivity );
return( 0 );
}
/*****************************************************************************
* vout_Display: displays previously rendered output
*****************************************************************************
* This function sends the currently rendered image to the display.
*****************************************************************************/ *****************************************************************************/
static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic ) @implementation VLCWindow
{
if( !p_vout->p_sys->i_opengl )
{
OSErr err;
CodecFlags flags;
if( ( err = DecompressSequenceFrameWhen(
p_vout->p_sys->i_seq,
p_pic->p_sys->p_data,
p_pic->p_sys->i_size,
codecFlagUseImageBuffer, &flags, NULL, NULL ) != noErr ) )
{
msg_Warn( p_vout, "DecompressSequenceFrameWhen failed: %d", err );
}
else
{
QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
}
}
else
{
if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
{
int i_index;
int i_old = p_vout->p_sys->i_cur_pic;
int i_new = ( i_old + 1 ) % 2;
/* Draw the new picture */
p_vout->p_sys->i_cur_pic = i_new;
[p_vout->p_sys->o_glview drawRect:
[p_vout->p_sys->o_glview bounds]];
/* Reload the other texture. Textures have to be reloaded
before the buffer is filled (thanks to gcc from
arstechnica forums) */
[p_vout->p_sys->o_glview reloadTexture: i_old];
/* Switch buffers */
p_pic->p_data = p_vout->p_sys->p_data[i_old];
p_pic->p[0].p_pixels = p_pic->p_data;
for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
{
p_pic->p[i_index].p_pixels =
p_pic->p[i_index-1].p_pixels +
p_pic->p[i_index-1].i_lines *
p_pic->p[i_index-1].i_pitch;
}
[p_vout->p_sys->o_glview unlockFocus];
}
}
}
/***************************************************************************** - (id)initWithVout:(vout_thread_t *)_p_vout frame:(NSRect *)s_frame
* ControlVideo: control facility for the vout
*****************************************************************************/
static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
{ {
vlc_bool_t b_arg; [self setReleasedWhenClosed: YES];
switch( i_query )
{
case VOUT_SET_STAY_ON_TOP:
b_arg = va_arg( args, vlc_bool_t );
CoSetWindowOnTop( p_vout, b_arg );
return VLC_SUCCESS;
case VOUT_CLOSE: p_vout = _p_vout;
case VOUT_REPARENT: p_fullscreen_state = NULL;
default: i_time_mouse_last_moved = mdate();
return vout_vaControlDefault( p_vout, i_query, args );
}
}
/*****************************************************************************
* CoCreateWindow: create new window
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int CoCreateWindow( vout_thread_t *p_vout )
{
vlc_value_t val;
NSScreen * o_screen; NSScreen * o_screen;
vlc_bool_t b_main_screen; vlc_bool_t b_main_screen;
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
/* Allocate the window. It will be autoreleased when it receives 'close' */
p_vout->p_sys->o_window = [VLCWindow alloc];
[p_vout->p_sys->o_window setReleasedWhenClosed: YES];
/* Find out on which screen to open the window */ /* Find out on which screen to open the window */
var_Get( p_vout, "video-device", &val ); int i_device = var_GetInteger( p_vout, "video-device" );
if( val.i_int < 0 ) if( i_device < 0 )
{ {
/* No preference specified. Use the main screen */ /* No preference specified. Use the main screen */
o_screen = [NSScreen mainScreen]; o_screen = [NSScreen mainScreen];
b_main_screen = 1; b_main_screen = 1;
} }
else else
{ {
NSArray *o_screens = [NSScreen screens]; NSArray *o_screens = [NSScreen screens];
unsigned int i_index = val.i_int;
if( [o_screens count] < i_index )
{
o_screen = [NSScreen mainScreen];
b_main_screen = 1;
}
else
{
i_index--;
o_screen = [o_screens objectAtIndex: i_index];
val.i_int = i_index;
var_Set( p_vout, "macosx-vdev", val );
b_main_screen = (i_index == 0);
}
}
if( p_vout->b_fullscreen )
{
NSRect screen_rect = [o_screen frame];
screen_rect.origin.x = screen_rect.origin.y = 0;
if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
NULL, NULL, fullScreenAllowEvents );
/* Creates a window with size: screen_rect on o_screen */
[p_vout->p_sys->o_window
initWithContentRect: screen_rect
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreBuffered
defer: NO screen: o_screen];
[p_vout->p_sys->o_window setVout: p_vout];
p_vout->p_sys->b_mouse_moved = YES;
p_vout->p_sys->i_time_mouse_last_moved = mdate();
}
else
{
unsigned int i_stylemask = NSTitledWindowMask |
NSMiniaturizableWindowMask |
NSClosableWindowMask |
NSResizableWindowMask;
if ( p_vout->p_sys->p_fullscreen_state != NULL )
EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
p_vout->p_sys->p_fullscreen_state = NULL;
[p_vout->p_sys->o_window
initWithContentRect: p_vout->p_sys->s_rect
styleMask: i_stylemask
backing: NSBackingStoreBuffered
defer: NO screen: o_screen];
[p_vout->p_sys->o_window setVout: p_vout];
var_Get( p_vout, "macosx-opaqueness", &val);
[p_vout->p_sys->o_window setAlphaValue: val.f_float];
var_Get( p_vout, "video-on-top", &val );
if( val.b_bool ) [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
if( !p_vout->p_sys->b_pos_saved ) [p_vout->p_sys->o_window center];
}
if( !p_vout->p_sys->i_opengl )
{
#define o_qtview p_vout->p_sys->o_qtview
o_qtview = [[VLCQTView alloc] init];
[p_vout->p_sys->o_window setContentView: o_qtview];
[o_qtview autorelease];
/* Retrieve the QuickDraw port */
[o_qtview lockFocus];
p_vout->p_sys->p_qdport = [o_qtview qdPort];
[o_qtview unlockFocus];
#undef o_qtview
}
else
{
#define o_glview p_vout->p_sys->o_glview
o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
[p_vout->p_sys->o_window setContentView: o_glview];
[o_glview autorelease];
#undef o_glview
}
[p_vout->p_sys->o_window updateTitle];
[p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
[o_pool release];
return( 0);
}
/*****************************************************************************
* CoDestroyWindow: destroy window
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int CoDestroyWindow( vout_thread_t *p_vout )
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
VLCHideMouse( p_vout, NO );
if( !p_vout->b_fullscreen )
{
NSRect s_rect;
/* remember the window position before we enter fullscreen */
s_rect = [[p_vout->p_sys->o_window contentView] frame];
p_vout->p_sys->s_rect.size = s_rect.size;
s_rect = [p_vout->p_sys->o_window frame];
p_vout->p_sys->s_rect.origin = s_rect.origin;
p_vout->p_sys->b_pos_saved = YES;
}
p_vout->p_sys->p_qdport = nil;
[p_vout->p_sys->o_window close];
p_vout->p_sys->o_window = nil;
[o_pool release];
return 0;
}
/*****************************************************************************
* CoToggleFullscreen: toggle fullscreen
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int CoToggleFullscreen( vout_thread_t *p_vout )
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
if( !p_vout->p_sys->i_opengl )
{
QTDestroySequence( p_vout );
}
if( CoDestroyWindow( p_vout ) )
{
msg_Err( p_vout, "unable to destroy window" );
return( 1 );
}
p_vout->b_fullscreen = !p_vout->b_fullscreen;
if( CoCreateWindow( p_vout ) )
{
msg_Err( p_vout, "unable to create window" );
return( 1 );
}
if( p_vout->p_sys->i_opengl ) if( [o_screens count] < (unsigned) i_device )
{ {
[p_vout->p_sys->o_glview lockFocus]; o_screen = [NSScreen mainScreen];
[p_vout->p_sys->o_glview initTextures]; b_main_screen = 1;
[p_vout->p_sys->o_glview reshape];
[p_vout->p_sys->o_glview drawRect:
[p_vout->p_sys->o_glview bounds]];
[p_vout->p_sys->o_glview unlockFocus];
} }
else else
{ {
SetPort( p_vout->p_sys->p_qdport ); i_device--;
QTScaleMatrix( p_vout ); o_screen = [o_screens objectAtIndex: i_device];
var_SetInteger( p_vout, "macosx-vdev", i_device );
if( QTCreateSequence( p_vout ) ) b_main_screen = ( i_device == 0 );
{
msg_Err( p_vout, "unable to create sequence" );
return( 1 );
} }
} }
[o_pool release]; NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
return 0;
}
/***************************************************************************** if( p_vout->b_fullscreen )
* CoSetWindowOnTop: Switches the "always on top" state of the video window
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int CoSetWindowOnTop( vout_thread_t *p_vout, BOOL b_on_top )
{
if( p_vout->p_sys->o_window )
{ {
if( b_on_top ) NSRect screen_rect = [o_screen frame];
screen_rect.origin.x = screen_rect.origin.y = 0;
/* Creates a window with size: screen_rect on o_screen */
[self initWithContentRect: screen_rect
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreBuffered
defer: NO screen: o_screen];
if( b_main_screen )
{ {
[p_vout->p_sys->o_window setLevel: NSStatusWindowLevel]; BeginFullScreen( &p_fullscreen_state, NULL, 0, 0,
NULL, NULL, fullScreenAllowEvents );
}
} }
else else
{ {
[p_vout->p_sys->o_window setLevel: NSNormalWindowLevel]; unsigned int i_stylemask = NSTitledWindowMask |
} NSMiniaturizableWindowMask |
return VLC_SUCCESS; NSClosableWindowMask |
} NSResizableWindowMask;
return VLC_EGENERIC;
}
/*****************************************************************************
* VLCHideMouse: if b_hide then hide the cursor
*****************************************************************************/
static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
{
BOOL b_inside;
NSRect s_rect; NSRect s_rect;
NSPoint ml; if( !s_frame )
NSWindow *o_window = p_vout->p_sys->o_window;
NSView *o_contents = [o_window contentView];
s_rect = [o_contents bounds];
ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
ml = [o_contents convertPoint:ml fromView:nil];
b_inside = [o_contents mouse: ml inRect: s_rect];
if ( b_hide && b_inside )
{ {
/* only hide if mouse over VLCQTView */ s_rect.size.width = p_vout->i_window_width;
[NSCursor setHiddenUntilMouseMoves: YES]; s_rect.size.height = p_vout->i_window_height;
} }
else if ( !b_hide ) else
{ {
[NSCursor setHiddenUntilMouseMoves: NO]; s_rect = *s_frame;
} }
p_vout->p_sys->b_mouse_moved = NO;
p_vout->p_sys->i_time_mouse_last_moved = mdate();
return;
}
/*****************************************************************************
* QTScaleMatrix: scale matrix
*****************************************************************************/
static void QTScaleMatrix( vout_thread_t *p_vout )
{
Rect s_rect;
vlc_value_t val;
unsigned int i_width, i_height;
Fixed factor_x, factor_y;
unsigned int i_offset_x = 0;
unsigned int i_offset_y = 0;
GetPortBounds( p_vout->p_sys->p_qdport, &s_rect ); [self initWithContentRect: s_rect
styleMask: i_stylemask
backing: NSBackingStoreBuffered
defer: NO screen: o_screen];
i_width = s_rect.right - s_rect.left; [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
i_height = s_rect.bottom - s_rect.top;
var_Get( p_vout, "macosx-stretch", &val ); if( var_GetBool( p_vout, "video-on-top" ) )
if( val.b_bool )
{ {
factor_x = FixDiv( Long2Fix( i_width ), [self setLevel: NSStatusWindowLevel];
Long2Fix( p_vout->output.i_width ) );
factor_y = FixDiv( Long2Fix( i_height ),
Long2Fix( p_vout->output.i_height ) );
} }
else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
{
int i_adj_width = i_height * p_vout->output.i_aspect /
VOUT_ASPECT_FACTOR;
factor_x = FixDiv( Long2Fix( i_adj_width ),
Long2Fix( p_vout->output.i_width ) );
factor_y = FixDiv( Long2Fix( i_height ),
Long2Fix( p_vout->output.i_height ) );
i_offset_x = (i_width - i_adj_width) / 2; if( !s_frame )
}
else
{ {
int i_adj_height = i_width * VOUT_ASPECT_FACTOR / [self center];
p_vout->output.i_aspect; }
factor_x = FixDiv( Long2Fix( i_width ),
Long2Fix( p_vout->output.i_width ) );
factor_y = FixDiv( Long2Fix( i_adj_height ),
Long2Fix( p_vout->output.i_height ) );
i_offset_y = (i_height - i_adj_height) / 2;
} }
SetIdentityMatrix( p_vout->p_sys->p_matrix ); [self updateTitle];
[self makeKeyAndOrderFront: nil];
ScaleMatrix( p_vout->p_sys->p_matrix, /* We'll catch mouse events */
factor_x, factor_y, [self setAcceptsMouseMovedEvents: YES];
Long2Fix(0), Long2Fix(0) ); [self makeFirstResponder: self];
TranslateMatrix( p_vout->p_sys->p_matrix, [o_pool release];
Long2Fix(i_offset_x), Long2Fix(i_offset_y) ); return self;
} }
/***************************************************************************** - (void)close
* QTCreateSequence: create a new sequence
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int QTCreateSequence( vout_thread_t *p_vout )
{ {
OSErr err; if( p_fullscreen_state )
ImageDescriptionPtr p_descr;
HLock( (Handle)p_vout->p_sys->h_img_descr );
p_descr = *p_vout->p_sys->h_img_descr;
p_descr->idSize = sizeof(ImageDescription);
p_descr->cType = p_vout->p_sys->i_codec;
p_descr->version = 2;
p_descr->revisionLevel = 0;
p_descr->vendor = 'mpla';
p_descr->width = p_vout->output.i_width;
p_descr->height = p_vout->output.i_height;
p_descr->hRes = Long2Fix(72);
p_descr->vRes = Long2Fix(72);
p_descr->spatialQuality = codecLosslessQuality;
p_descr->frameCount = 1;
p_descr->clutID = -1;
p_descr->dataSize = 0;
p_descr->depth = 24;
HUnlock( (Handle)p_vout->p_sys->h_img_descr );
if( ( err = DecompressSequenceBeginS(
&p_vout->p_sys->i_seq,
p_vout->p_sys->h_img_descr,
NULL,
(p_descr->width * p_descr->height * 16) / 8,
p_vout->p_sys->p_qdport,
NULL, NULL,
p_vout->p_sys->p_matrix,
srcCopy, NULL,
codecFlagUseImageBuffer,
codecLosslessQuality,
bestSpeedCodec ) ) )
{ {
msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err ); EndFullScreen( p_fullscreen_state, NULL );
return( 1 );
} }
[super close];
return( 0 );
} }
/***************************************************************************** - (void)setOnTop:(bool)b_on_top
* QTDestroySequence: destroy sequence
*****************************************************************************/
static void QTDestroySequence( vout_thread_t *p_vout )
{ {
CDSequenceEnd( p_vout->p_sys->i_seq ); if( b_on_top )
{
[self setLevel: NSStatusWindowLevel];
}
else
{
[self setLevel: NSNormalWindowLevel];
}
} }
/***************************************************************************** - (void)hideMouse:(bool)b_hide
* QTNewPicture: allocate a picture
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
{ {
/* We know the chroma, allocate a buffer which will be used BOOL b_inside;
* directly by the decoder */ NSPoint ml;
p_pic->p_sys = malloc( sizeof( picture_sys_t ) ); NSView *o_contents = [self contentView];
ml = [self convertScreenToBase:[NSEvent mouseLocation]];
ml = [o_contents convertPoint:ml fromView:nil];
b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
if( p_pic->p_sys == NULL ) if( b_hide && b_inside )
{ {
return( -1 ); [NSCursor setHiddenUntilMouseMoves: YES];
} }
else if( !b_hide )
vout_InitPicture( VLC_OBJECT( p_vout), p_pic, p_vout->output.i_chroma,
p_vout->output.i_width, p_vout->output.i_height,
p_vout->output.i_aspect );
switch( p_vout->output.i_chroma )
{ {
case VLC_FOURCC('Y','U','Y','2'): [NSCursor setHiddenUntilMouseMoves: NO];
p_pic->p_sys->i_size = p_vout->output.i_width * p_vout->output.i_height * 2;
/* Allocate the memory buffer */
p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
16, p_pic->p_sys->i_size );
p_pic->p[0].p_pixels = p_pic->p_data;
p_pic->p[0].i_lines = p_vout->output.i_height;
p_pic->p[0].i_pitch = p_vout->output.i_width * 2;
p_pic->p[0].i_pixel_pitch = 1;
p_pic->p[0].i_visible_pitch = p_vout->output.i_width * 2;
p_pic->i_planes = 1;
p_pic->p_sys->p_data = (void *)p_pic->p[0].p_pixels;
break;
default:
/* Unknown chroma, tell the guy to get lost */
free( p_pic->p_sys );
msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
p_pic->i_planes = 0;
return( -1 );
} }
return( 0 );
} }
/***************************************************************************** - (void)manage
* QTFreePicture: destroy a picture allocated with QTNewPicture
*****************************************************************************/
static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
{ {
switch( p_vout->output.i_chroma ) if( p_fullscreen_state )
{ {
case VLC_FOURCC('I','4','2','0'): if( mdate() - i_time_mouse_last_moved > 3000000 )
free( p_pic->p_data_orig ); {
break; [self hideMouse: YES];
}
}
else
{
[self hideMouse: NO];
} }
free( p_pic->p_sys ); /* Disable screensaver */
} UpdateSystemActivity( UsrActivity );
/*****************************************************************************
* VLCWindow implementation
*****************************************************************************/
@implementation VLCWindow
- (void)setVout:(vout_thread_t *)_p_vout
{
p_vout = _p_vout;
}
- (vout_thread_t *)getVout
{
return( p_vout );
} }
- (void)scaleWindowWithFactor: (float)factor - (void)scaleWindowWithFactor: (float)factor
...@@ -1066,18 +220,18 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -1066,18 +220,18 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
topleftbase.y = [self frame].size.height; topleftbase.y = [self frame].size.height;
topleftscreen = [self convertBaseToScreen: topleftbase]; topleftscreen = [self convertBaseToScreen: topleftbase];
if( p_vout->output.i_height * p_vout->output.i_aspect > if( p_vout->render.i_height * p_vout->render.i_aspect >
p_vout->output.i_width * VOUT_ASPECT_FACTOR ) p_vout->render.i_width * VOUT_ASPECT_FACTOR )
{ {
i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect / i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
VOUT_ASPECT_FACTOR; VOUT_ASPECT_FACTOR;
newsize.width = (int) ( i_corrected_width * factor ); newsize.width = (int) ( i_corrected_width * factor );
newsize.height = (int) ( p_vout->render.i_height * factor ); newsize.height = (int) ( p_vout->render.i_height * factor );
} }
else else
{ {
i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR / i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
p_vout->output.i_aspect; p_vout->render.i_aspect;
newsize.width = (int) ( p_vout->render.i_width * factor ); newsize.width = (int) ( p_vout->render.i_width * factor );
newsize.height = (int) ( i_corrected_height * factor ); newsize.height = (int) ( i_corrected_height * factor );
} }
...@@ -1212,8 +366,7 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -1212,8 +366,7 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
} }
else else
{ {
[self setTitle: [self setTitle: [NSString stringWithCString: VOUT_TITLE]];
[NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
} }
} }
...@@ -1234,597 +387,175 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -1234,597 +387,175 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
return NO; return NO;
} }
@end - (BOOL)acceptsFirstResponder
{
/* Common QT and OpenGL code to catch mouse events */ return YES;
#define CATCH_MOUSE_EVENTS \
- (BOOL)acceptsFirstResponder \
{ \
return( YES ); \
} \
\
- (BOOL)becomeFirstResponder \
{ \
id o_window = [self window]; \
\
[o_window setAcceptsMouseMovedEvents: YES]; \
return( YES ); \
} \
\
- (BOOL)resignFirstResponder \
{ \
vout_thread_t * vout; \
id o_window = [self window]; \
vout = (vout_thread_t *)[o_window getVout]; \
\
[o_window setAcceptsMouseMovedEvents: NO]; \
VLCHideMouse( vout, NO ); \
return( YES ); \
} \
\
- (void)mouseDown:(NSEvent *)o_event \
{ \
vout_thread_t * vout; \
id o_window = [self window]; \
vout = (vout_thread_t *)[o_window getVout]; \
vlc_value_t val; \
\
switch( [o_event type] ) \
{ \
case NSLeftMouseDown: \
{ \
var_Get( vout, "mouse-button-down", &val ); \
val.i_int |= 1; \
var_Set( vout, "mouse-button-down", val ); \
} \
break; \
\
default: \
[super mouseDown: o_event]; \
break; \
} \
} \
\
- (void)otherMouseDown:(NSEvent *)o_event \
{ \
vout_thread_t * vout; \
id o_window = [self window]; \
vout = (vout_thread_t *)[o_window getVout]; \
vlc_value_t val; \
\
switch( [o_event type] ) \
{ \
case NSOtherMouseDown: \
{ \
var_Get( vout, "mouse-button-down", &val ); \
val.i_int |= 2; \
var_Set( vout, "mouse-button-down", val ); \
} \
break; \
\
default: \
[super mouseDown: o_event]; \
break; \
} \
} \
\
- (void)rightMouseDown:(NSEvent *)o_event \
{ \
vout_thread_t * vout; \
id o_window = [self window]; \
vout = (vout_thread_t *)[o_window getVout]; \
vlc_value_t val; \
\
switch( [o_event type] ) \
{ \
case NSRightMouseDown: \
{ \
var_Get( vout, "mouse-button-down", &val ); \
val.i_int |= 4; \
var_Set( vout, "mouse-button-down", val ); \
} \
break; \
\
default: \
[super mouseDown: o_event]; \
break; \
} \
} \
\
- (void)mouseUp:(NSEvent *)o_event \
{ \
vout_thread_t * vout; \
id o_window = [self window]; \
vout = (vout_thread_t *)[o_window getVout]; \
vlc_value_t val; \
\
switch( [o_event type] ) \
{ \
case NSLeftMouseUp: \
{ \
vlc_value_t b_val; \
b_val.b_bool = VLC_TRUE; \
var_Set( vout, "mouse-clicked", b_val ); \
\
var_Get( vout, "mouse-button-down", &val ); \
val.i_int &= ~1; \
var_Set( vout, "mouse-button-down", val ); \
} \
break; \
\
default: \
[super mouseUp: o_event]; \
break; \
} \
} \
\
- (void)otherMouseUp:(NSEvent *)o_event \
{ \
vout_thread_t * vout; \
id o_window = [self window]; \
vout = (vout_thread_t *)[o_window getVout]; \
vlc_value_t val; \
\
switch( [o_event type] ) \
{ \
case NSOtherMouseUp: \
{ \
var_Get( vout, "mouse-button-down", &val ); \
val.i_int &= ~2; \
var_Set( vout, "mouse-button-down", val ); \
} \
break; \
\
default: \
[super mouseUp: o_event]; \
break; \
} \
} \
\
- (void)rightMouseUp:(NSEvent *)o_event \
{ \
vout_thread_t * vout; \
id o_window = [self window]; \
vout = (vout_thread_t *)[o_window getVout]; \
vlc_value_t val; \
\
switch( [o_event type] ) \
{ \
case NSRightMouseUp: \
{ \
var_Get( vout, "mouse-button-down", &val ); \
val.i_int &= ~4; \
var_Set( vout, "mouse-button-down", val ); \
} \
break; \
\
default: \
[super mouseUp: o_event]; \
break; \
} \
} \
\
- (void)mouseDragged:(NSEvent *)o_event \
{ \
[self mouseMoved:o_event]; \
} \
\
- (void)otherMouseDragged:(NSEvent *)o_event \
{ \
[self mouseMoved:o_event]; \
} \
\
- (void)rightMouseDragged:(NSEvent *)o_event \
{ \
[self mouseMoved:o_event]; \
} }
/***************************************************************************** - (BOOL)becomeFirstResponder
* VLCQTView implementation
*****************************************************************************/
@implementation VLCQTView
- (void)drawRect:(NSRect)rect
{ {
vout_thread_t * p_vout; return YES;
id o_window = [self window];
p_vout = (vout_thread_t *)[o_window getVout];
[[NSColor blackColor] set];
NSRectFill( rect );
[super drawRect: rect];
p_vout->i_changes |= VOUT_SIZE_CHANGE;
} }
CATCH_MOUSE_EVENTS - (BOOL)resignFirstResponder
- (void)mouseMoved:(NSEvent *)o_event
{ {
NSPoint ml; /* We need to stay the first responder or we'll miss some
NSRect s_rect; events */
BOOL b_inside; return NO;
vout_thread_t * p_vout;
id o_window = [self window];
p_vout = (vout_thread_t *)[o_window getVout];
s_rect = [self bounds];
ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
b_inside = [self mouse: ml inRect: s_rect];
if( b_inside )
{
vlc_value_t val;
int i_width, i_height, i_x, i_y;
vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
(unsigned int)s_rect.size.height,
&i_x, &i_y, &i_width, &i_height );
val.i_int = ( ((int)ml.x) - i_x ) *
p_vout->render.i_width / i_width;
var_Set( p_vout, "mouse-x", val );
val.i_int = ( ((int)ml.y) - i_y ) *
p_vout->render.i_height / i_height;
var_Set( p_vout, "mouse-y", val );
val.b_bool = VLC_TRUE;
var_Set( p_vout, "mouse-moved", val );
p_vout->p_sys->i_time_mouse_last_moved = mdate();
p_vout->p_sys->b_mouse_moved = YES;
}
[super mouseMoved: o_event];
} }
@end - (void)mouseDown:(NSEvent *)o_event
/*****************************************************************************
* VLCGLView implementation
*****************************************************************************/
@implementation VLCGLView
- (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
{ {
vlc_value_t val; vlc_value_t val;
p_vout = _p_vout;
NSOpenGLPixelFormatAttribute attribs[] = switch( [o_event type] )
{ {
NSOpenGLPFAAccelerated, case NSLeftMouseDown:
NSOpenGLPFANoRecovery,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAWindow,
0
};
NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
initWithAttributes: attribs];
if( !fmt )
{ {
msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" ); var_Get( p_vout, "mouse-button-down", &val );
return nil; val.i_int |= 1;
var_Set( p_vout, "mouse-button-down", val );
} }
break;
self = [super initWithFrame:frame pixelFormat: fmt]; default:
[fmt release]; [super mouseDown: o_event];
break;
[[self openGLContext] makeCurrentContext];
[[self openGLContext] update];
/* Black background */
glClearColor( 0.0, 0.0, 0.0, 0.0 );
/* Check if the user asked for useless visual effects */
var_Get( p_vout, "macosx-opengl-effect", &val );
if( !val.psz_string || !strcmp( val.psz_string, "none" ))
{
i_effect = OPENGL_EFFECT_NONE;
} }
else if( !strcmp( val.psz_string, "cube" ) ) }
{
i_effect = OPENGL_EFFECT_CUBE;
glEnable( GL_DEPTH_TEST ); - (void)otherMouseDown:(NSEvent *)o_event
} {
else if( !strcmp( val.psz_string, "transparent-cube" ) ) vlc_value_t val;
{
i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
glDisable( GL_DEPTH_TEST ); switch( [o_event type] )
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
}
else
{ {
msg_Warn( p_vout, "no valid opengl effect provided, using " case NSOtherMouseDown:
"\"none\"" );
i_effect = OPENGL_EFFECT_NONE;
}
if( val.psz_string ) free( val.psz_string );
if( i_effect & ( OPENGL_EFFECT_CUBE |
OPENGL_EFFECT_TRANSPARENT_CUBE ) )
{ {
/* Set the perpective */ var_Get( p_vout, "mouse-button-down", &val );
glMatrixMode( GL_PROJECTION ); val.i_int |= 2;
glLoadIdentity(); var_Set( p_vout, "mouse-button-down", val );
glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0.0, 0.0, - 5.0 );
} }
break;
pi_textures[0] = 0; default:
pi_textures[1] = 0; [super mouseDown: o_event];
initDone = 0; break;
}
return self;
} }
- (void)reshape - (void)rightMouseDown:(NSEvent *)o_event
{ {
vlc_value_t val; vlc_value_t val;
NSRect bounds;
if( !initDone ) switch( [o_event type] )
{ {
return; case NSRightMouseDown:
{
var_Get( p_vout, "mouse-button-down", &val );
val.i_int |= 4;
var_Set( p_vout, "mouse-button-down", val );
} }
break;
[[self openGLContext] makeCurrentContext]; default:
[super mouseDown: o_event];
break;
}
}
bounds = [self bounds]; - (void)mouseUp:(NSEvent *)o_event
glViewport( 0, 0, (GLint) bounds.size.width, {
(GLint) bounds.size.height ); vlc_value_t val;
var_Get( p_vout, "macosx-stretch", &val ); switch( [o_event type] )
if( val.b_bool )
{
f_x = 1.0;
f_y = 1.0;
}
else
{
/* Quad size is set in order to preserve the aspect ratio */
var_Get( p_vout, "macosx-fill", &val );
int fill = ( val.b_bool && p_vout->b_fullscreen );
int large = ( bounds.size.height * p_vout->output.i_aspect <
bounds.size.width * VOUT_ASPECT_FACTOR );
if( ( large && !fill ) || ( !large && fill ) )
{ {
f_x = bounds.size.height * p_vout->output.i_aspect / case NSLeftMouseUp:
VOUT_ASPECT_FACTOR / bounds.size.width;
f_y = 1.0;
}
else
{ {
f_x = 1.0; vlc_value_t b_val;
f_y = bounds.size.width * VOUT_ASPECT_FACTOR / b_val.b_bool = VLC_TRUE;
p_vout->output.i_aspect / bounds.size.height; var_Set( p_vout, "mouse-clicked", b_val );
var_Get( p_vout, "mouse-button-down", &val );
val.i_int &= ~1;
var_Set( p_vout, "mouse-button-down", val );
} }
break;
default:
[super mouseUp: o_event];
break;
} }
} }
- (void)initTextures - (void)otherMouseUp:(NSEvent *)o_event
{ {
int i; vlc_value_t val;
[[self openGLContext] makeCurrentContext];
/* Free previous texture if any */ switch( [o_event type] )
if( initDone )
{ {
glDeleteTextures( 2, pi_textures ); case NSOtherMouseUp:
}
glEnable( GL_TEXTURE_RECTANGLE_EXT );
glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
/* Tell the driver not to make a copy of the texture but to use
our buffer */
glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
/* Create textures */
glGenTextures( 2, pi_textures );
for( i = 0; i < 2; i++ )
{ {
glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] ); var_Get( p_vout, "mouse-button-down", &val );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); val.i_int &= ~2;
var_Set( p_vout, "mouse-button-down", val );
/* Linear interpolation */
glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
GL_TEXTURE_MAG_FILTER, GL_LINEAR );
/* Use VRAM texturing */
glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
p_vout->output.i_width, p_vout->output.i_height, 0,
GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
p_vout->p_sys->p_data[i] );
} }
break;
/* Swap buffers only during the vertical retrace of the monitor. default:
http://developer.apple.com/documentation/GraphicsImaging/ [super mouseUp: o_event];
Conceptual/OpenGL/chap5/chapter_5_section_44.html */ break;
long params[] = { 1 }; }
CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
params );
initDone = 1;
} }
- (void)reloadTexture: (int) index - (void)rightMouseUp:(NSEvent *)o_event
{ {
if( !initDone ) vlc_value_t val;
switch( [o_event type] )
{ {
return; case NSRightMouseUp:
{
var_Get( p_vout, "mouse-button-down", &val );
val.i_int &= ~4;
var_Set( p_vout, "mouse-button-down", val );
} }
break;
[[self openGLContext] makeCurrentContext]; default:
[super mouseUp: o_event];
glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] ); break;
glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width ); }
/* glTexSubImage2D is faster than glTexImage2D
http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
TextureRange/MainOpenGLView.m.htm */
glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
p_vout->output.i_width, p_vout->output.i_height,
GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
p_vout->p_sys->p_data[index] );
}
- (void) cleanUp
{
initDone = 0;
} }
- (void) drawQuad - (void)mouseDragged:(NSEvent *)o_event
{ {
glBegin( GL_QUADS ); [self mouseMoved: o_event];
/* Top left */
glTexCoord2f( 0.0, 0.0 );
glVertex2f( - f_x, f_y );
/* Bottom left */
glTexCoord2f( 0.0, (float) p_vout->output.i_height );
glVertex2f( - f_x, - f_y );
/* Bottom right */
glTexCoord2f( (float) p_vout->output.i_width,
(float) p_vout->output.i_height );
glVertex2f( f_x, - f_y );
/* Top right */
glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
glVertex2f( f_x, f_y );
glEnd();
} }
- (void) drawCube - (void)otherMouseDragged:(NSEvent *)o_event
{ {
glBegin( GL_QUADS ); [self mouseMoved: o_event];
/* Front */
glTexCoord2f( 0.0, 0.0 );
glVertex3f( - 1.0, 1.0, 1.0 );
glTexCoord2f( 0.0, (float) p_vout->output.i_height );
glVertex3f( - 1.0, - 1.0, 1.0 );
glTexCoord2f( (float) p_vout->output.i_width,
(float) p_vout->output.i_height );
glVertex3f( 1.0, - 1.0, 1.0 );
glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
glVertex3f( 1.0, 1.0, 1.0 );
/* Left */
glTexCoord2f( 0.0, 0.0 );
glVertex3f( - 1.0, 1.0, - 1.0 );
glTexCoord2f( 0.0, (float) p_vout->output.i_height );
glVertex3f( - 1.0, - 1.0, - 1.0 );
glTexCoord2f( (float) p_vout->output.i_width,
(float) p_vout->output.i_height );
glVertex3f( - 1.0, - 1.0, 1.0 );
glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
glVertex3f( - 1.0, 1.0, 1.0 );
/* Back */
glTexCoord2f( 0.0, 0.0 );
glVertex3f( 1.0, 1.0, - 1.0 );
glTexCoord2f( 0.0, (float) p_vout->output.i_height );
glVertex3f( 1.0, - 1.0, - 1.0 );
glTexCoord2f( (float) p_vout->output.i_width,
(float) p_vout->output.i_height );
glVertex3f( - 1.0, - 1.0, - 1.0 );
glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
glVertex3f( - 1.0, 1.0, - 1.0 );
/* Right */
glTexCoord2f( 0.0, 0.0 );
glVertex3f( 1.0, 1.0, 1.0 );
glTexCoord2f( 0.0, (float) p_vout->output.i_height );
glVertex3f( 1.0, - 1.0, 1.0 );
glTexCoord2f( (float) p_vout->output.i_width,
(float) p_vout->output.i_height );
glVertex3f( 1.0, - 1.0, - 1.0 );
glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
glVertex3f( 1.0, 1.0, - 1.0 );
/* Top */
glTexCoord2f( 0.0, 0.0 );
glVertex3f( - 1.0, 1.0, - 1.0 );
glTexCoord2f( 0.0, (float) p_vout->output.i_height );
glVertex3f( - 1.0, 1.0, 1.0 );
glTexCoord2f( (float) p_vout->output.i_width,
(float) p_vout->output.i_height );
glVertex3f( 1.0, 1.0, 1.0 );
glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
glVertex3f( 1.0, 1.0, - 1.0 );
/* Bottom */
glTexCoord2f( 0.0, 0.0 );
glVertex3f( - 1.0, - 1.0, 1.0 );
glTexCoord2f( 0.0, (float) p_vout->output.i_height );
glVertex3f( - 1.0, - 1.0, - 1.0 );
glTexCoord2f( (float) p_vout->output.i_width,
(float) p_vout->output.i_height );
glVertex3f( 1.0, - 1.0, - 1.0 );
glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
glVertex3f( 1.0, - 1.0, 1.0 );
glEnd();
} }
- (void) drawRect: (NSRect) rect - (void)rightMouseDragged:(NSEvent *)o_event
{ {
[[self openGLContext] makeCurrentContext]; [self mouseMoved: o_event];
/* Black background */
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
if( !initDone )
{
glFlush();
return;
}
/* Draw */
glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
pi_textures[p_vout->p_sys->i_cur_pic] );
if( i_effect & ( OPENGL_EFFECT_CUBE |
OPENGL_EFFECT_TRANSPARENT_CUBE ) )
{
glRotatef( 1.0, 0.3, 0.5, 0.7 );
[self drawCube];
}
else
{
[self drawQuad];
}
/* Draw */
glFlush();
} }
CATCH_MOUSE_EVENTS
- (void)mouseMoved:(NSEvent *)o_event - (void)mouseMoved:(NSEvent *)o_event
{ {
NSPoint ml; NSPoint ml;
NSRect s_rect; NSRect s_rect;
BOOL b_inside; BOOL b_inside;
NSView * o_view;
i_time_mouse_last_moved = mdate();
s_rect = [self bounds]; o_view = [self contentView];
ml = [self convertPoint: [o_event locationInWindow] fromView: nil]; s_rect = [o_view bounds];
b_inside = [self mouse: ml inRect: s_rect]; ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
b_inside = [o_view mouse: ml inRect: s_rect];
if( b_inside ) if( b_inside )
{ {
...@@ -1835,19 +566,24 @@ CATCH_MOUSE_EVENTS ...@@ -1835,19 +566,24 @@ CATCH_MOUSE_EVENTS
(unsigned int)s_rect.size.height, (unsigned int)s_rect.size.height,
&i_x, &i_y, &i_width, &i_height ); &i_x, &i_y, &i_width, &i_height );
val.i_int = ( (int)ml.x - i_x ) * val.i_int = ( ((int)ml.x) - i_x ) *
p_vout->render.i_width / i_width; p_vout->render.i_width / i_width;
var_Set( p_vout, "mouse-x", val ); var_Set( p_vout, "mouse-x", val );
/* Y coordinate is inverted in OpenGL */ if( [[o_view className] isEqualToString: @"VLCGLView"] )
{
val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) * val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
p_vout->render.i_height / i_height; p_vout->render.i_height / i_height;
}
else
{
val.i_int = ( ((int)ml.y) - i_y ) *
p_vout->render.i_height / i_height;
}
var_Set( p_vout, "mouse-y", val ); var_Set( p_vout, "mouse-y", val );
val.b_bool = VLC_TRUE; val.b_bool = VLC_TRUE;
var_Set( p_vout, "mouse-moved", val ); var_Set( p_vout, "mouse-moved", val );
p_vout->p_sys->i_time_mouse_last_moved = mdate();
p_vout->p_sys->b_mouse_moved = YES;
} }
[super mouseMoved: o_event]; [super mouseMoved: o_event];
......
/*****************************************************************************
* vout.m: MacOS X video output module
*****************************************************************************
* Copyright (C) 2001-2003 VideoLAN
* $Id: vout.m 8351 2004-08-02 13:06:38Z hartman $
*
* Authors: Colin Delacroix <colin@zoy.org>
* Florian G. Pflug <fgp@phlo.org>
* Jon Lech Johansen <jon-vl@nanocrew.net>
* Derk-Jan Hartman <hartman at videolan dot org>
* Eric Petit <titer@m0k.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <errno.h> /* ENOMEM */
#include <stdlib.h> /* free() */
#include <string.h> /* strerror() */
#include <vlc_keys.h>
#include "intf.h"
#include "vout.h"
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
/*****************************************************************************
* VLCView interface
*****************************************************************************/
@interface VLCGLView : NSOpenGLView
{
vout_thread_t * p_vout;
}
- (id)initWithFrame: (NSRect) frame vout: (vout_thread_t*) p_vout;
@end
struct vout_sys_t
{
NSAutoreleasePool *o_pool;
VLCWindow * o_window;
VLCGLView * o_glview;
vlc_bool_t b_saved_frame;
NSRect s_frame;
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Init ( vout_thread_t * p_vout );
static void End ( vout_thread_t * p_vout );
static int Manage ( vout_thread_t * p_vout );
static void Swap ( vout_thread_t * p_vout );
int E_(OpenVideoGL) ( vlc_object_t * p_this )
{
vout_thread_t * p_vout = (vout_thread_t *) p_this;
int i_timeout;
vlc_value_t val;
if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) )
{
msg_Warn( p_vout, "no hardware acceleration" );
return VLC_EGENERIC;
}
msg_Dbg( p_vout, "display is Quartz Extreme accelerated" );
p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
if( p_vout->p_sys == NULL )
{
msg_Err( p_vout, "out of memory" );
return( 1 );
}
memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
/* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
for( i_timeout = 20 ; i_timeout-- ; )
{
if( NSApp == NULL )
{
msleep( INTF_IDLE_SLEEP );
}
}
if( NSApp == NULL )
{
/* No MacOS X intf, unable to communicate with MT */
msg_Err( p_vout, "no MacOS X interface present" );
return VLC_EGENERIC;
}
p_vout->pf_init = Init;
p_vout->pf_end = End;
p_vout->pf_manage = Manage;
p_vout->pf_swap = Swap;
p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
var_Create( p_vout, "macosx-vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
/* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
NSArray * o_screens = [NSScreen screens];
if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
{
int i = 1;
vlc_value_t val2, text;
NSScreen * o_screen;
var_Get( p_vout, "macosx-vdev", &val );
var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
VLC_VAR_HASCHOICE );
text.psz_string = _("Video device");
var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
NSEnumerator * o_enumerator = [o_screens objectEnumerator];
while( (o_screen = [o_enumerator nextObject]) != NULL )
{
char psz_temp[255];
NSRect s_rect = [o_screen frame];
snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
"%s %d (%dx%d)", _("Screen"), i,
(int)s_rect.size.width, (int)s_rect.size.height );
text.psz_string = psz_temp;
val2.i_int = i;
var_Change( p_vout, "video-device",
VLC_VAR_ADDCHOICE, &val2, &text );
if( ( i - 1 ) == val.i_int )
{
var_Set( p_vout, "video-device", val2 );
}
i++;
}
var_AddCallback( p_vout, "video-device", vout_VarCallback,
NULL );
val2.b_bool = VLC_TRUE;
var_Set( p_vout, "intf-change", val2 );
}
/* Spawn window */
p_vout->p_sys->o_window = [[VLCWindow alloc] initWithVout: p_vout
frame: nil];
/* Add OpenGL view */
#define o_glview p_vout->p_sys->o_glview
o_glview = [[VLCGLView alloc] initWithFrame:
[p_vout->p_sys->o_window frame] vout: p_vout];
[p_vout->p_sys->o_window setContentView: o_glview];
[o_glview autorelease];
#undef o_glview
return VLC_SUCCESS;
}
int E_(CloseVideoGL) ( vlc_object_t * p_this )
{
vout_thread_t * p_vout = (vout_thread_t *) p_this;
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
[p_vout->p_sys->o_window close];
[o_pool release];
return VLC_SUCCESS;
}
static int Init( vout_thread_t * p_vout )
{
[[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
return VLC_SUCCESS;
}
static void End( vout_thread_t * p_vout )
{
[[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
}
static int Manage( vout_thread_t * p_vout )
{
if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
if( !p_vout->b_fullscreen )
{
/* Save window size and position */
p_vout->p_sys->s_frame.size =
[[p_vout->p_sys->o_window contentView] frame].size;
p_vout->p_sys->s_frame.origin =
[p_vout->p_sys->o_window frame].origin;
p_vout->p_sys->b_saved_frame = VLC_TRUE;
}
[p_vout->p_sys->o_window close];
p_vout->b_fullscreen = !p_vout->b_fullscreen;
if( p_vout->p_sys->b_saved_frame )
{
p_vout->p_sys->o_window = [[VLCWindow alloc]
initWithVout: p_vout frame: &p_vout->p_sys->s_frame];
}
else
{
p_vout->p_sys->o_window = [[VLCWindow alloc]
initWithVout: p_vout frame: nil];
}
#define o_glview p_vout->p_sys->o_glview
o_glview = [[VLCGLView alloc] initWithFrame: [p_vout->p_sys->o_window frame] vout: p_vout];
[p_vout->p_sys->o_window setContentView: o_glview];
[o_glview autorelease];
[[o_glview openGLContext] makeCurrentContext];
#undef o_glview
[o_pool release];
p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
}
[p_vout->p_sys->o_window manage];
return VLC_SUCCESS;
}
static void Swap( vout_thread_t * p_vout )
{
[[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
{
glFlush();
[p_vout->p_sys->o_glview unlockFocus];
}
}
/*****************************************************************************
* VLCGLView implementation
*****************************************************************************/
@implementation VLCGLView
- (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
{
p_vout = _p_vout;
NSOpenGLPixelFormatAttribute attribs[] =
{
NSOpenGLPFAAccelerated,
NSOpenGLPFANoRecovery,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAWindow,
0
};
NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
initWithAttributes: attribs];
if( !fmt )
{
msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
return nil;
}
self = [super initWithFrame:frame pixelFormat: fmt];
[fmt release];
[[self openGLContext] makeCurrentContext];
[[self openGLContext] update];
/* Swap buffers only during the vertical retrace of the monitor.
http://developer.apple.com/documentation/GraphicsImaging/
Conceptual/OpenGL/chap5/chapter_5_section_44.html */
long params[] = { 1 };
CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
params );
return self;
}
- (void)reshape
{
int x, y;
NSRect bounds = [self bounds];
[[self openGLContext] makeCurrentContext];
if( bounds.size.height * p_vout->render.i_aspect <
bounds.size.width * VOUT_ASPECT_FACTOR )
{
x = bounds.size.height * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
y = bounds.size.height;
}
else
{
x = bounds.size.width;
y = bounds.size.width * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
}
glViewport( ( bounds.size.width - x ) / 2,
( bounds.size.height - y ) / 2, x, y );
glClear( GL_COLOR_BUFFER_BIT );
}
- (void) drawRect: (NSRect) rect
{
[[self openGLContext] makeCurrentContext];
glFlush();
}
@end
/*****************************************************************************
* vout.m: MacOS X video output module
*****************************************************************************
* Copyright (C) 2001-2003 VideoLAN
* $Id: vout.m 8351 2004-08-02 13:06:38Z hartman $
*
* Authors: Colin Delacroix <colin@zoy.org>
* Florian G. Pflug <fgp@phlo.org>
* Jon Lech Johansen <jon-vl@nanocrew.net>
* Derk-Jan Hartman <hartman at videolan dot org>
* Eric Petit <titer@m0k.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <errno.h> /* ENOMEM */
#include <stdlib.h> /* free() */
#include <string.h> /* strerror() */
#include <QuickTime/QuickTime.h>
#include <vlc_keys.h>
#include "intf.h"
#include "vout.h"
#define QT_MAX_DIRECTBUFFERS 10
#define VL_MAX_DISPLAYS 16
/*****************************************************************************
* VLCView interface
*****************************************************************************/
@interface VLCQTView : NSQuickDrawView
{
vout_thread_t * p_vout;
}
- (id) initWithVout:(vout_thread_t *)p_vout;
@end
struct vout_sys_t
{
NSAutoreleasePool *o_pool;
VLCWindow * o_window;
VLCQTView * o_qtview;
vlc_bool_t b_saved_frame;
NSRect s_frame;
CodecType i_codec;
CGrafPtr p_qdport;
ImageSequence i_seq;
MatrixRecordPtr p_matrix;
DecompressorComponent img_dc;
ImageDescriptionHandle h_img_descr;
};
struct picture_sys_t
{
void *p_data;
unsigned int i_size;
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int InitVideo ( vout_thread_t * );
static void EndVideo ( vout_thread_t * );
static int ManageVideo ( vout_thread_t * );
static void DisplayVideo ( vout_thread_t *, picture_t * );
static int ControlVideo ( vout_thread_t *, int, va_list );
static int CoToggleFullscreen( vout_thread_t *p_vout );
static void QTScaleMatrix ( vout_thread_t * );
static int QTCreateSequence ( vout_thread_t * );
static void QTDestroySequence ( vout_thread_t * );
static int QTNewPicture ( vout_thread_t *, picture_t * );
static void QTFreePicture ( vout_thread_t *, picture_t * );
/*****************************************************************************
* OpenVideo: allocates MacOS X video thread output method
*****************************************************************************
* This function allocates and initializes a MacOS X vout method.
*****************************************************************************/
int E_(OpenVideoQT) ( vlc_object_t *p_this )
{
vout_thread_t * p_vout = (vout_thread_t *)p_this;
vlc_value_t val;
OSErr err;
int i_timeout;
p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
if( p_vout->p_sys == NULL )
{
msg_Err( p_vout, "out of memory" );
return( 1 );
}
memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
/* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
for( i_timeout = 20 ; i_timeout-- ; )
{
if( NSApp == NULL )
{
msleep( INTF_IDLE_SLEEP );
}
}
if( NSApp == NULL )
{
/* no MacOS X intf, unable to communicate with MT */
msg_Err( p_vout, "no MacOS X interface present" );
free( p_vout->p_sys );
return( 1 );
}
p_vout->pf_init = InitVideo;
p_vout->pf_end = EndVideo;
p_vout->pf_manage = ManageVideo;
p_vout->pf_render = NULL;
p_vout->pf_display = DisplayVideo;
p_vout->pf_control = ControlVideo;
p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
var_Create( p_vout, "macosx-vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
var_Create( p_vout, "macosx-opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
/* Initialize QuickTime */
p_vout->p_sys->h_img_descr =
(ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
p_vout->p_sys->p_matrix =
(MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
if( ( err = EnterMovies() ) != noErr )
{
msg_Err( p_vout, "EnterMovies failed: %d", err );
free( p_vout->p_sys->p_matrix );
DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
free( p_vout->p_sys );
return VLC_EGENERIC;
}
/* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
/* Can we find the right chroma ? */
err = FindCodec( kComponentVideoUnsigned, bestSpeedCodec,
nil, &p_vout->p_sys->img_dc );
vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
if( err == noErr && p_vout->p_sys->img_dc != 0 )
{
p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
p_vout->p_sys->i_codec = kComponentVideoUnsigned;
}
else
{
msg_Err( p_vout, "failed to find an appropriate codec" );
}
if( p_vout->p_sys->img_dc == 0 )
{
free( p_vout->p_sys->p_matrix );
DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
free( p_vout->p_sys );
return VLC_EGENERIC;
}
/* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
NSArray * o_screens = [NSScreen screens];
if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
{
int i = 1;
vlc_value_t val2, text;
NSScreen * o_screen;
var_Get( p_vout, "macosx-vdev", &val );
var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
VLC_VAR_HASCHOICE );
text.psz_string = _("Video device");
var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
NSEnumerator * o_enumerator = [o_screens objectEnumerator];
while( (o_screen = [o_enumerator nextObject]) != NULL )
{
char psz_temp[255];
NSRect s_rect = [o_screen frame];
snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
"%s %d (%dx%d)", _("Screen"), i,
(int)s_rect.size.width, (int)s_rect.size.height );
text.psz_string = psz_temp;
val2.i_int = i;
var_Change( p_vout, "video-device",
VLC_VAR_ADDCHOICE, &val2, &text );
if( ( i - 1 ) == val.i_int )
{
var_Set( p_vout, "video-device", val2 );
}
i++;
}
var_AddCallback( p_vout, "video-device", vout_VarCallback,
NULL );
val2.b_bool = VLC_TRUE;
var_Set( p_vout, "intf-change", val2 );
}
/* Spawn window */
p_vout->p_sys->o_window =
[[VLCWindow alloc] initWithVout: p_vout frame: nil];
#define o_qtview p_vout->p_sys->o_qtview
o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
[p_vout->p_sys->o_window setContentView: o_qtview];
[o_qtview autorelease];
/* Retrieve the QuickDraw port */
[o_qtview lockFocus];
p_vout->p_sys->p_qdport = [o_qtview qdPort];
[o_qtview unlockFocus];
#undef o_qtview
return VLC_SUCCESS;
}
/*****************************************************************************
* CloseVideo: destroy video thread output method
*****************************************************************************/
void E_(CloseVideoQT) ( vlc_object_t *p_this )
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
vout_thread_t * p_vout = (vout_thread_t *)p_this;
[p_vout->p_sys->o_window close];
[p_vout->p_sys->o_window release];
/* Clean Up Quicktime environment */
ExitMovies();
free( p_vout->p_sys->p_matrix );
DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
[o_pool release];
free( p_vout->p_sys );
}
/*****************************************************************************
* InitVideo: initialize video thread output method
*****************************************************************************/
static int InitVideo ( vout_thread_t *p_vout )
{
picture_t *p_pic;
int i_index;
I_OUTPUTPICTURES = 0;
/* Initialize the output structure; we already found a codec,
* and the corresponding chroma we will be using. Since we can
* arbitrary scale, stick to the coordinates and aspect. */
p_vout->output.i_width = p_vout->render.i_width;
p_vout->output.i_height = p_vout->render.i_height;
p_vout->output.i_aspect = p_vout->render.i_aspect;
SetPort( p_vout->p_sys->p_qdport );
QTScaleMatrix( p_vout );
if( QTCreateSequence( p_vout ) )
{
msg_Err( p_vout, "unable to create sequence" );
return( 1 );
}
/* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
{
p_pic = NULL;
/* Find an empty picture slot */
for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
{
if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
{
p_pic = p_vout->p_picture + i_index;
break;
}
}
/* Allocate the picture */
if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
{
break;
}
p_pic->i_status = DESTROYED_PICTURE;
p_pic->i_type = DIRECT_PICTURE;
PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
I_OUTPUTPICTURES++;
}
return 0;
}
/*****************************************************************************
* EndVideo: terminate video thread output method
*****************************************************************************/
static void EndVideo( vout_thread_t *p_vout )
{
int i_index;
QTDestroySequence( p_vout );
/* Free the direct buffers we allocated */
for( i_index = I_OUTPUTPICTURES; i_index; )
{
i_index--;
QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
}
}
/*****************************************************************************
* ManageVideo: handle events
*****************************************************************************
* This function should be called regularly by video output thread. It manages
* console events. It returns a non null value on error.
*****************************************************************************/
static int ManageVideo( vout_thread_t *p_vout )
{
if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
{
if( CoToggleFullscreen( p_vout ) )
{
return( 1 );
}
p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
}
if( p_vout->i_changes & VOUT_SIZE_CHANGE )
{
QTScaleMatrix( p_vout );
SetDSequenceMatrix( p_vout->p_sys->i_seq,
p_vout->p_sys->p_matrix );
p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
}
[p_vout->p_sys->o_window manage];
return( 0 );
}
/*****************************************************************************
* vout_Display: displays previously rendered output
*****************************************************************************
* This function sends the currently rendered image to the display.
*****************************************************************************/
static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
{
OSErr err;
CodecFlags flags;
if( ( err = DecompressSequenceFrameWhen(
p_vout->p_sys->i_seq,
p_pic->p_sys->p_data,
p_pic->p_sys->i_size,
codecFlagUseImageBuffer, &flags, NULL, NULL ) != noErr ) )
{
msg_Warn( p_vout, "DecompressSequenceFrameWhen failed: %d", err );
}
else
{
QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
}
}
/*****************************************************************************
* ControlVideo: control facility for the vout
*****************************************************************************/
static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
{
vlc_bool_t b_arg;
switch( i_query )
{
case VOUT_SET_STAY_ON_TOP:
b_arg = va_arg( args, vlc_bool_t );
[p_vout->p_sys->o_window setOnTop: b_arg];
return VLC_SUCCESS;
case VOUT_CLOSE:
case VOUT_REPARENT:
default:
return vout_vaControlDefault( p_vout, i_query, args );
}
}
/*****************************************************************************
* CoToggleFullscreen: toggle fullscreen
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int CoToggleFullscreen( vout_thread_t *p_vout )
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
QTDestroySequence( p_vout );
if( !p_vout->b_fullscreen )
{
/* Save window size and position */
p_vout->p_sys->s_frame.size =
[[p_vout->p_sys->o_window contentView] frame].size;
p_vout->p_sys->s_frame.origin =
[p_vout->p_sys->o_window frame].origin;
p_vout->p_sys->b_saved_frame = VLC_TRUE;
}
[p_vout->p_sys->o_window close];
p_vout->b_fullscreen = !p_vout->b_fullscreen;
if( p_vout->p_sys->b_saved_frame )
{
p_vout->p_sys->o_window = [[VLCWindow alloc]
initWithVout: p_vout frame: &p_vout->p_sys->s_frame];
}
else
{
p_vout->p_sys->o_window = [[VLCWindow alloc]
initWithVout: p_vout frame: nil];
}
#define o_qtview p_vout->p_sys->o_qtview
o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
[p_vout->p_sys->o_window setContentView: o_qtview];
[o_qtview autorelease];
/* Retrieve the QuickDraw port */
[o_qtview lockFocus];
p_vout->p_sys->p_qdport = [o_qtview qdPort];
[o_qtview unlockFocus];
#undef o_qtview
SetPort( p_vout->p_sys->p_qdport );
QTScaleMatrix( p_vout );
if( QTCreateSequence( p_vout ) )
{
msg_Err( p_vout, "unable to create sequence" );
return( 1 );
}
[o_pool release];
return 0;
}
/*****************************************************************************
* QTScaleMatrix: scale matrix
*****************************************************************************/
static void QTScaleMatrix( vout_thread_t *p_vout )
{
Rect s_rect;
vlc_value_t val;
unsigned int i_width, i_height;
Fixed factor_x, factor_y;
unsigned int i_offset_x = 0;
unsigned int i_offset_y = 0;
GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
i_width = s_rect.right - s_rect.left;
i_height = s_rect.bottom - s_rect.top;
var_Get( p_vout, "macosx-stretch", &val );
if( val.b_bool )
{
factor_x = FixDiv( Long2Fix( i_width ),
Long2Fix( p_vout->output.i_width ) );
factor_y = FixDiv( Long2Fix( i_height ),
Long2Fix( p_vout->output.i_height ) );
}
else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
{
int i_adj_width = i_height * p_vout->output.i_aspect /
VOUT_ASPECT_FACTOR;
factor_x = FixDiv( Long2Fix( i_adj_width ),
Long2Fix( p_vout->output.i_width ) );
factor_y = FixDiv( Long2Fix( i_height ),
Long2Fix( p_vout->output.i_height ) );
i_offset_x = (i_width - i_adj_width) / 2;
}
else
{
int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
p_vout->output.i_aspect;
factor_x = FixDiv( Long2Fix( i_width ),
Long2Fix( p_vout->output.i_width ) );
factor_y = FixDiv( Long2Fix( i_adj_height ),
Long2Fix( p_vout->output.i_height ) );
i_offset_y = (i_height - i_adj_height) / 2;
}
SetIdentityMatrix( p_vout->p_sys->p_matrix );
ScaleMatrix( p_vout->p_sys->p_matrix,
factor_x, factor_y,
Long2Fix(0), Long2Fix(0) );
TranslateMatrix( p_vout->p_sys->p_matrix,
Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
}
/*****************************************************************************
* QTCreateSequence: create a new sequence
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int QTCreateSequence( vout_thread_t *p_vout )
{
OSErr err;
ImageDescriptionPtr p_descr;
HLock( (Handle)p_vout->p_sys->h_img_descr );
p_descr = *p_vout->p_sys->h_img_descr;
p_descr->idSize = sizeof(ImageDescription);
p_descr->cType = p_vout->p_sys->i_codec;
p_descr->version = 2;
p_descr->revisionLevel = 0;
p_descr->vendor = 'mpla';
p_descr->width = p_vout->output.i_width;
p_descr->height = p_vout->output.i_height;
p_descr->hRes = Long2Fix(72);
p_descr->vRes = Long2Fix(72);
p_descr->spatialQuality = codecLosslessQuality;
p_descr->frameCount = 1;
p_descr->clutID = -1;
p_descr->dataSize = 0;
p_descr->depth = 24;
HUnlock( (Handle)p_vout->p_sys->h_img_descr );
if( ( err = DecompressSequenceBeginS(
&p_vout->p_sys->i_seq,
p_vout->p_sys->h_img_descr,
NULL,
(p_descr->width * p_descr->height * 16) / 8,
p_vout->p_sys->p_qdport,
NULL, NULL,
p_vout->p_sys->p_matrix,
srcCopy, NULL,
codecFlagUseImageBuffer,
codecLosslessQuality,
bestSpeedCodec ) ) )
{
msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
return( 1 );
}
return( 0 );
}
/*****************************************************************************
* QTDestroySequence: destroy sequence
*****************************************************************************/
static void QTDestroySequence( vout_thread_t *p_vout )
{
CDSequenceEnd( p_vout->p_sys->i_seq );
}
/*****************************************************************************
* QTNewPicture: allocate a picture
*****************************************************************************
* Returns 0 on success, 1 otherwise
*****************************************************************************/
static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
/* We know the chroma, allocate a buffer which will be used
* directly by the decoder */
p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
if( p_pic->p_sys == NULL )
{
return( -1 );
}
vout_InitPicture( VLC_OBJECT( p_vout), p_pic, p_vout->output.i_chroma,
p_vout->output.i_width, p_vout->output.i_height,
p_vout->output.i_aspect );
switch( p_vout->output.i_chroma )
{
case VLC_FOURCC('Y','U','Y','2'):
p_pic->p_sys->i_size = p_vout->output.i_width * p_vout->output.i_height * 2;
/* Allocate the memory buffer */
p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
16, p_pic->p_sys->i_size );
p_pic->p[0].p_pixels = p_pic->p_data;
p_pic->p[0].i_lines = p_vout->output.i_height;
p_pic->p[0].i_pitch = p_vout->output.i_width * 2;
p_pic->p[0].i_pixel_pitch = 1;
p_pic->p[0].i_visible_pitch = p_vout->output.i_width * 2;
p_pic->i_planes = 1;
p_pic->p_sys->p_data = (void *)p_pic->p[0].p_pixels;
break;
default:
/* Unknown chroma, tell the guy to get lost */
free( p_pic->p_sys );
msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
p_pic->i_planes = 0;
return( -1 );
}
return( 0 );
}
/*****************************************************************************
* QTFreePicture: destroy a picture allocated with QTNewPicture
*****************************************************************************/
static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
{
switch( p_vout->output.i_chroma )
{
case VLC_FOURCC('I','4','2','0'):
free( p_pic->p_data_orig );
break;
}
free( p_pic->p_sys );
}
/*****************************************************************************
* VLCQTView implementation
*****************************************************************************/
@implementation VLCQTView
- (id) initWithVout:(vout_thread_t *)_p_vout
{
p_vout = _p_vout;
return [super init];
}
- (void)drawRect:(NSRect)rect
{
[[NSColor blackColor] set];
NSRectFill( rect );
[super drawRect: rect];
p_vout->i_changes |= VOUT_SIZE_CHANGE;
}
@end
...@@ -87,6 +87,7 @@ static int Control ( vout_thread_t *, int, va_list ); ...@@ -87,6 +87,7 @@ static int Control ( vout_thread_t *, int, va_list );
static inline int GetAlignedSize( int ); static inline int GetAlignedSize( int );
static int InitTextures( vout_thread_t * );
static int SendEvents( vlc_object_t *, char const *, static int SendEvents( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * ); vlc_value_t, vlc_value_t, void * );
...@@ -99,7 +100,11 @@ static int SendEvents( vlc_object_t *, char const *, ...@@ -99,7 +100,11 @@ static int SendEvents( vlc_object_t *, char const *,
vlc_module_begin(); vlc_module_begin();
set_description( _("OpenGL video output") ); set_description( _("OpenGL video output") );
#ifdef SYS_DARWIN
set_capability( "video output", 200 );
#else
set_capability( "video output", 20 ); set_capability( "video output", 20 );
#endif
add_shortcut( "opengl" ); add_shortcut( "opengl" );
set_callbacks( CreateVout, DestroyVout ); set_callbacks( CreateVout, DestroyVout );
...@@ -179,7 +184,7 @@ static int CreateVout( vlc_object_t *p_this ) ...@@ -179,7 +184,7 @@ static int CreateVout( vlc_object_t *p_this )
module_Need( p_sys->p_vout, "opengl provider", NULL, 0 ); module_Need( p_sys->p_vout, "opengl provider", NULL, 0 );
if( p_sys->p_vout->p_module == NULL ) if( p_sys->p_vout->p_module == NULL )
{ {
msg_Err( p_vout, "No OpenGL provider found" ); msg_Warn( p_vout, "No OpenGL provider found" );
vlc_object_detach( p_sys->p_vout ); vlc_object_detach( p_sys->p_vout );
vlc_object_destroy( p_sys->p_vout ); vlc_object_destroy( p_sys->p_vout );
return VLC_ENOOBJ; return VLC_ENOOBJ;
...@@ -192,6 +197,8 @@ static int CreateVout( vlc_object_t *p_this ) ...@@ -192,6 +197,8 @@ static int CreateVout( vlc_object_t *p_this )
p_vout->pf_display = DisplayVideo; p_vout->pf_display = DisplayVideo;
p_vout->pf_control = Control; p_vout->pf_control = Control;
var_Create( p_sys->p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
/* Forward events from the opengl provider */ /* Forward events from the opengl provider */
var_AddCallback( p_sys->p_vout, "mouse-x", SendEvents, p_vout ); var_AddCallback( p_sys->p_vout, "mouse-x", SendEvents, p_vout );
var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout ); var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout );
...@@ -207,7 +214,7 @@ static int CreateVout( vlc_object_t *p_this ) ...@@ -207,7 +214,7 @@ static int CreateVout( vlc_object_t *p_this )
static int Init( vout_thread_t *p_vout ) static int Init( vout_thread_t *p_vout )
{ {
vout_sys_t *p_sys = p_vout->p_sys; vout_sys_t *p_sys = p_vout->p_sys;
int i_pixel_pitch, i_index; int i_pixel_pitch;
vlc_value_t val; vlc_value_t val;
p_sys->p_vout->pf_init( p_sys->p_vout ); p_sys->p_vout->pf_init( p_sys->p_vout );
...@@ -281,45 +288,7 @@ static int Init( vout_thread_t *p_vout ) ...@@ -281,45 +288,7 @@ static int Init( vout_thread_t *p_vout )
I_OUTPUTPICTURES = 1; I_OUTPUTPICTURES = 1;
glGenTextures( 2, p_sys->p_textures ); InitTextures( p_vout );
for( i_index = 0; i_index < 2; i_index++ )
{
glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
/* Set the texture parameters */
glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
#ifdef SYS_DARWIN
/* Tell the driver not to make a copy of the texture but to use
our buffer */
glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
#if 0
/* Use VRAM texturing */
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
GL_STORAGE_CACHED_APPLE );
#else
/* Use AGP texturing */
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
GL_STORAGE_SHARED_APPLE );
#endif
#endif
/* Call glTexImage2D only once, and use glTexSubImage2D later */
glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
p_sys->pp_buffer[i_index] );
}
glDisable(GL_BLEND); glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
...@@ -410,7 +379,49 @@ static void DestroyVout( vlc_object_t *p_this ) ...@@ -410,7 +379,49 @@ static void DestroyVout( vlc_object_t *p_this )
static int Manage( vout_thread_t *p_vout ) static int Manage( vout_thread_t *p_vout )
{ {
vout_sys_t *p_sys = p_vout->p_sys; vout_sys_t *p_sys = p_vout->p_sys;
return p_sys->p_vout->pf_manage( p_sys->p_vout ); int i_ret, i_fullscreen_change;
i_fullscreen_change = ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE );
p_sys->p_vout->i_changes = p_vout->i_changes;
i_ret = p_sys->p_vout->pf_manage( p_sys->p_vout );
p_vout->i_changes = p_sys->p_vout->i_changes;
#ifdef SYS_DARWIN
/* On OS X, we create the window and the GL view when entering
fullscreen - the textures have to be inited again */
if( i_fullscreen_change )
{
InitTextures( p_vout );
switch( p_sys->i_effect )
{
case OPENGL_EFFECT_CUBE:
glEnable( GL_CULL_FACE );
break;
case OPENGL_EFFECT_TRANSPARENT_CUBE:
glDisable( GL_DEPTH_TEST );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
break;
}
if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
OPENGL_EFFECT_TRANSPARENT_CUBE ) )
{
/* Set the perpective */
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0.0, 0.0, - 5.0 );
}
}
#endif
return i_ret;
} }
/***************************************************************************** /*****************************************************************************
...@@ -563,6 +574,55 @@ static int Control( vout_thread_t *p_vout, int i_query, va_list args ) ...@@ -563,6 +574,55 @@ static int Control( vout_thread_t *p_vout, int i_query, va_list args )
return vout_vaControlDefault( p_vout, i_query, args ); return vout_vaControlDefault( p_vout, i_query, args );
} }
static int InitTextures( vout_thread_t *p_vout )
{
vout_sys_t *p_sys = p_vout->p_sys;
int i_index;
glDeleteTextures( 2, p_sys->p_textures );
glGenTextures( 2, p_sys->p_textures );
for( i_index = 0; i_index < 2; i_index++ )
{
glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
/* Set the texture parameters */
glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
#ifdef SYS_DARWIN
/* Tell the driver not to make a copy of the texture but to use
our buffer */
glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
#if 0
/* Use VRAM texturing */
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
GL_STORAGE_CACHED_APPLE );
#else
/* Use AGP texturing */
glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
GL_STORAGE_SHARED_APPLE );
#endif
#endif
/* Call glTexImage2D only once, and use glTexSubImage2D later */
glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
p_sys->pp_buffer[i_index] );
}
return 0;
}
/***************************************************************************** /*****************************************************************************
* SendEvents: forward mouse and keyboard events to the parent p_vout * SendEvents: forward mouse and keyboard events to the parent p_vout
*****************************************************************************/ *****************************************************************************/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment