Commit 1cc498b2 authored by Felix Paul Kühne's avatar Felix Paul Kühne

screen/mac: clean-up, improve and partially rewrite the code

TODO: cursor rendering
parent fbf37518
......@@ -29,49 +29,23 @@
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# import "config.h"
# include "config.h"
#endif
#import <vlc_common.h>
#import <vlc_block.h>
// Fix ourselves ColorSync headers that gets included in ApplicationServices.
#define DisposeCMProfileIterateUPP(a) DisposeCMProfileIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused)))
#define DisposeCMMIterateUPP(a) DisposeCMMIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused)))
#define __MACHINEEXCEPTIONS__
#import <ApplicationServices/ApplicationServices.h>
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl.h>
#import <stdlib.h>
#import "screen.h"
typedef int CGSConnectionRef;
extern CGError CGSNewConnection( void *, CGSConnectionRef * );
extern CGError CGSReleaseConnection( CGSConnectionRef );
extern CGError CGSGetGlobalCursorDataSize( CGSConnectionRef, int * );
extern CGError CGSGetGlobalCursorData( CGSConnectionRef, unsigned char *,
int *, int *, CGRect *, CGPoint *,
int *, int *, int * );
extern CGError CGSGetCurrentCursorLocation( CGSConnectionRef, CGPoint * );
extern int CGSCurrentCursorSeed( void );
#import <ApplicationServices/ApplicationServices.h>
#import <QuartzCore/QuartzCore.h>
typedef CGImageRef ( *typeofCGDisplayCreateImageForRect )( CGDirectDisplayID displayID, CGRect rect );
#import "screen.h"
extern int CGSMainConnectionID();
extern CGImageRef CGSCreateRegisteredCursorImage(int, char*, CGPoint*);
struct screen_data_t
{
CGLContextObj screen;
char *screen_image;
CGLContextObj clipped;
char *clipped_image;
GLuint cursor_texture;
int cursor_seed;
bool cursor_need_update;
CGRect cursor_rect;
CGPoint cursor_hot;
double cursor_texture_map_u, cursor_texture_map_v;
block_t *p_block;
int width;
int height;
......@@ -81,88 +55,45 @@ struct screen_data_t
int screen_width;
int screen_height;
CGSConnectionRef connection;
CFBundleRef bundle;
typeofCGDisplayCreateImageForRect myCGDisplayCreateImageForRect;
CGDirectDisplayID display_id;
};
CGLError screen_CreateContext( CGLContextObj *context,
CGLPixelFormatAttribute a0,
CGLPixelFormatAttribute a1,
CGLPixelFormatAttribute a2,
CGLPixelFormatAttribute a3 );
int screen_DrawCursor( demux_sys_t *p_sys, CGPoint *cursor_pos );
int screen_CaptureScreen( demux_sys_t *p_sys );
int screen_InitCapture( demux_t *p_demux )
int screen_InitCapture(demux_t *p_demux)
{
demux_sys_t *p_sys = p_demux->p_sys;
screen_data_t *p_data;
CGLError returnedError;
unsigned int i;
int i_bits_per_pixel, i_chroma = 0;
p_sys->p_data = p_data =
( screen_data_t * )calloc( 1, sizeof( screen_data_t ) );
p_sys->p_data = p_data = calloc(1, sizeof(screen_data_t ) );
if (!p_data)
return VLC_ENOMEM;
/* fetch the screen we should capture */
p_data->display_id = kCGDirectMainDisplay;
unsigned int displayCount;
displayCount = 0;
returnedError = CGGetOnlineDisplayList( 0, NULL, &displayCount );
if( !returnedError )
{
unsigned int displayCount = 0;
returnedError = CGGetOnlineDisplayList(0, NULL, &displayCount);
if (!returnedError) {
CGDirectDisplayID *ids;
ids = ( CGDirectDisplayID * )malloc( displayCount * sizeof( CGDirectDisplayID ) );
returnedError = CGGetOnlineDisplayList( displayCount, ids, &displayCount );
if( !returnedError )
{
if ( p_sys->i_display_id > 0 )
{
for( i = 0; i < displayCount; i ++ )
{
if( p_sys->i_display_id == ids[i] )
{
if (!returnedError) {
if (p_sys->i_display_id > 0) {
for (unsigned int i = 0; i < displayCount; i++) {
if (p_sys->i_display_id == ids[i]) {
p_data->display_id = ids[i];
break;
}
}
}
else if ( p_sys->i_screen_index > 0 && p_sys->i_screen_index <= displayCount )
{
} else if (p_sys->i_screen_index > 0 && p_sys->i_screen_index <= displayCount)
p_data->display_id = ids[p_sys->i_screen_index - 1];
}
}
free( ids );
}
/* CGImage Function
* CGDisplayCreateImageForRect is available in Mac OS X v10.6 and later */
p_data->myCGDisplayCreateImageForRect = NULL;
CFURLRef frameworkURL = NULL;
CFStringRef path = CFSTR( "file://localhost/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework" );
frameworkURL = CFURLCreateWithString( kCFAllocatorDefault, path, NULL );
if( frameworkURL != NULL )
{
p_data->bundle = CFBundleCreate( kCFAllocatorDefault, frameworkURL );
if( p_data->bundle != NULL )
{
p_data->myCGDisplayCreateImageForRect =
( typeofCGDisplayCreateImageForRect )CFBundleGetFunctionPointerForName
( p_data->bundle, CFSTR( "CGDisplayCreateImageForRect" ) );
free(ids);
}
CFRelease( frameworkURL );
}
/* Screen Size */
CGRect rect = CGDisplayBounds( p_data->display_id );
/* Get the device context for the whole screen */
CGRect rect = CGDisplayBounds(p_data->display_id);
p_data->screen_left = rect.origin.x;
p_data->screen_top = rect.origin.y;
p_data->screen_width = rect.size.width;
......@@ -170,437 +101,143 @@ int screen_InitCapture( demux_t *p_demux )
p_data->width = p_sys->i_width;
p_data->height = p_sys->i_height;
if( p_data->width <= 0 || p_data->height <= 0 )
{
if( p_data->width <= 0 || p_data->height <= 0 ) {
p_data->width = p_data->screen_width;
p_data->height = p_data->screen_height;
}
/* Screen Context */
if( p_data->myCGDisplayCreateImageForRect == NULL )
{
returnedError =
screen_CreateContext( &p_data->screen,
kCGLPFAFullScreen,
kCGLPFADisplayMask,
( CGLPixelFormatAttribute )CGDisplayIDToOpenGLDisplayMask( p_data->display_id ),
( CGLPixelFormatAttribute )0 );
if( returnedError )
goto errorHandling;
returnedError = CGLSetCurrentContext( p_data->screen );
if( returnedError )
goto errorHandling;
returnedError = CGLSetFullScreen( p_data->screen );
if( returnedError )
goto errorHandling;
}
/* Clipped Context */
returnedError =
screen_CreateContext( &p_data->clipped,
kCGLPFAOffScreen,
kCGLPFAColorSize,
( CGLPixelFormatAttribute )32,
( CGLPixelFormatAttribute )0 );
if( returnedError )
goto errorHandling;
returnedError = CGLSetCurrentContext( p_data->clipped );
if( returnedError )
goto errorHandling;
/* Clipped Image */
p_data->clipped_image =
( char * )malloc( p_data->width * p_data->height * 4 );
returnedError = CGLSetOffScreen( p_data->clipped, p_data->width, p_data->height, p_data->width * 4, p_data->clipped_image );
if( returnedError )
goto errorHandling;
/* Screen Image */
if( p_data->myCGDisplayCreateImageForRect != NULL )
{
p_data->screen_image =
( char * )malloc( p_data->screen_width * p_data->screen_height * 4 );
CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(CGDisplayCopyDisplayMode(p_data->display_id));
int length = CFStringGetLength(pixelEncoding);
length++;
char *psz_name = (char *)malloc(length);
CFStringGetCString(pixelEncoding, psz_name, length, kCFStringEncodingUTF8);
msg_Dbg(p_demux, "pixel encoding is '%s'", psz_name);
CFRelease(pixelEncoding);
if (!strcmp(psz_name, IO32BitDirectPixels)) {
i_chroma = VLC_CODEC_RGB32;
i_bits_per_pixel = 32;
} else if (!strcmp(psz_name, IO16BitDirectPixels)) {
i_chroma = VLC_CODEC_RGB16;
i_bits_per_pixel = 16;
} else if (!strcmp(psz_name, IO8BitIndexedPixels)) {
i_chroma = VLC_CODEC_RGB8;
i_bits_per_pixel = 8;
} else {
msg_Err(p_demux, "unsupported pixel encoding");
free(p_data);
return VLC_EGENERIC;
}
else
{
p_data->screen_image =
( char * )malloc( p_data->width * p_data->height * 4 );
free(psz_name);
/* setup format */
es_format_Init(&p_sys->fmt, VIDEO_ES, i_chroma );
p_sys->fmt.video.i_visible_width =
p_sys->fmt.video.i_width = rect.size.width;
p_sys->fmt.video.i_visible_height =
p_sys->fmt.video.i_height = rect.size.height;
p_sys->fmt.video.i_bits_per_pixel = i_bits_per_pixel;
p_sys->fmt.video.i_chroma = i_chroma;
switch (i_chroma) {
case VLC_CODEC_RGB15:
p_sys->fmt.video.i_rmask = 0x7c00;
p_sys->fmt.video.i_gmask = 0x03e0;
p_sys->fmt.video.i_bmask = 0x001f;
break;
case VLC_CODEC_RGB24:
p_sys->fmt.video.i_rmask = 0x00ff0000;
p_sys->fmt.video.i_gmask = 0x0000ff00;
p_sys->fmt.video.i_bmask = 0x000000ff;
break;
case VLC_CODEC_RGB32:
p_sys->fmt.video.i_rmask = 0x00ff0000;
p_sys->fmt.video.i_gmask = 0x0000ff00;
p_sys->fmt.video.i_bmask = 0x000000ff;
break;
default:
msg_Warn( p_demux, "Unknown RGB masks" );
break;
}
/* Cursor */
CGSNewConnection( NULL, &( p_data->connection ) );
p_data->cursor_need_update = 1;
p_data->cursor_seed = 0;
glGenTextures( 1, &( p_data->cursor_texture ) );
glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
/* System */
es_format_Init( &p_sys->fmt, VIDEO_ES, VLC_CODEC_RGB32 );
/* p_sys->fmt.video.i_* must set to screen size, not subscreen size */
p_sys->fmt.video.i_width = p_data->screen_width;
p_sys->fmt.video.i_visible_width = p_data->screen_width;
p_sys->fmt.video.i_height = p_data->screen_height;
p_sys->fmt.video.i_bits_per_pixel = 32;
return VLC_SUCCESS;
errorHandling:
msg_Err( p_demux, "Core OpenGL failure: %s", CGLErrorString( returnedError ) );
return VLC_EGENERIC;
}
int screen_CloseCapture( demux_t *p_demux )
int screen_CloseCapture(demux_t *p_demux)
{
screen_data_t *p_data = ( screen_data_t * )p_demux->p_sys->p_data;
CGLSetCurrentContext( NULL );
/* Cursor */
glBindTexture( GL_TEXTURE_2D, 0 );
glDeleteTextures( 1, &( p_data->cursor_texture ) );
CGSReleaseConnection( p_data->connection );
/* Screen Image */
if( p_data->screen_image != NULL )
{
free( p_data->screen_image );
p_data->screen_image = NULL;
}
/* Clipped Image */
if( p_data->clipped_image != NULL )
{
free( p_data->clipped_image );
p_data->clipped_image = NULL;
}
/* Clipped Context */
CGLClearDrawable( p_data->clipped );
CGLDestroyContext( p_data->clipped );
/* Screen Context */
if( p_data->myCGDisplayCreateImageForRect == NULL )
{
CGLClearDrawable( p_data->screen );
CGLDestroyContext( p_data->screen );
}
/* CGImage */
demux_sys_t *p_sys = p_demux->p_sys;
screen_data_t *p_data = p_sys->p_data;
CFRelease( p_data->bundle );
if (p_data->p_block)
block_Release(p_data->p_block);
free( p_data );
free(p_data);
return VLC_SUCCESS;
}
block_t *screen_Capture( demux_t *p_demux )
block_t *screen_Capture(demux_t *p_demux)
{
demux_sys_t *p_sys = p_demux->p_sys;
screen_data_t *p_data = ( screen_data_t * )p_sys->p_data;
block_t *p_block;
int i_size;
i_size = p_sys->fmt.video.i_height * p_sys->fmt.video.i_width * 4;
if( !( p_block = block_Alloc( i_size ) ) )
{
msg_Warn( p_demux, "cannot get block" );
return NULL;
}
CGRect capture_rect;
CGImageRef image;
CGDataProviderRef dataProvider;
CFDataRef data;
/* forward cursor location */
CGPoint cursor_pos;
CGError cursor_result;
cursor_pos.x = 0;
cursor_pos.y = 0;
cursor_result =
CGSGetCurrentCursorLocation( p_data->connection, &cursor_pos );
CGEventRef event = CGEventCreate(NULL);
cursor_pos = CGEventGetLocation(event);
CFRelease(event);
cursor_pos.x -= p_data->screen_left;
cursor_pos.y -= p_data->screen_top;
if( p_sys->b_follow_mouse
&& cursor_result == kCGErrorSuccess )
{
FollowMouse( p_sys, cursor_pos.x, cursor_pos.y );
}
screen_CaptureScreen( p_sys );
CGLSetCurrentContext( p_data->clipped );
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT );
glDrawPixels( p_data->width,
p_data->height,
GL_RGBA, GL_UNSIGNED_BYTE, p_data->screen_image );
if( cursor_result == kCGErrorSuccess )
{
screen_DrawCursor( p_sys, &cursor_pos );
}
glReadPixels( 0, 0,
p_data->width,
p_data->height,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
p_block->p_buffer );
return p_block;
}
CGLError screen_CreateContext( CGLContextObj *context,
CGLPixelFormatAttribute a0,
CGLPixelFormatAttribute a1,
CGLPixelFormatAttribute a2,
CGLPixelFormatAttribute a3 )
{
CGLPixelFormatAttribute attribs[4];
CGLPixelFormatObj pix;
GLint npix;
CGLError returnedError;
attribs[0] = a0;
attribs[1] = a1;
attribs[2] = a2;
attribs[3] = a3;
returnedError = CGLChoosePixelFormat( attribs, &pix, &npix );
if( returnedError )
{
return returnedError;
}
returnedError = CGLCreateContext( pix, NULL, context );
if( returnedError )
{
return returnedError;
}
if (p_sys->b_follow_mouse)
FollowMouse(p_sys, cursor_pos.x, cursor_pos.y);
returnedError = CGLDestroyPixelFormat( pix );
if( returnedError )
{
return returnedError;
}
return kCGLNoError;
}
#define POT(V,N) V = 1; while( V < N ) { V <<= 1; }
int screen_DrawCursor( demux_sys_t *p_sys, CGPoint *cursor_pos )
{
int size;
int tmp1, tmp2, tmp3, tmp4;
unsigned char *cursor_image;
screen_data_t *p_data = p_sys->p_data;
int seed = CGSCurrentCursorSeed();
if( seed != p_data->cursor_seed )
{
p_data->cursor_need_update = 1;
if( CGSGetGlobalCursorDataSize( p_data->connection, &size )
!= kCGErrorSuccess)
{
return VLC_EGENERIC;
}
cursor_image = ( unsigned char * )malloc( size );
if( CGSGetGlobalCursorData( p_data->connection,
cursor_image, &size,
&tmp1,
&p_data->cursor_rect, &p_data->cursor_hot,
&tmp2, &tmp3, &tmp4 )
!= kCGErrorSuccess )
{
free( cursor_image );
return VLC_EGENERIC;
}
long int pot_width, pot_height;
POT( pot_width, p_data->cursor_rect.size.width );
POT( pot_height, p_data->cursor_rect.size.height );
p_data->cursor_texture_map_u =
p_data->cursor_rect.size.width / ( double )pot_width;
p_data->cursor_texture_map_v =
p_data->cursor_rect.size.height / ( double )pot_height;
/* We need transparent image larger than original,
* use calloc to clear alpha value to 0. */
char *pot_cursor_image = ( char * )calloc( pot_width * pot_height * 4, sizeof( char ) );
int width, height;
char *from, *to;
width = p_data->cursor_rect.size.width;
height = p_data->cursor_rect.size.height;
from = ( char * )cursor_image;
to = pot_cursor_image;
#ifdef __LITTLE_ENDIAN__
int y, fromwidth, towidth;
fromwidth = width * 4;
towidth = pot_width * 4;
for( y = height; y; y -- )
{
memcpy( to, from, fromwidth );
to += towidth;
from += fromwidth;
}
#else
int x, y, diff;
diff = ( pot_width - width ) * 4;
for( y = height; y; y -- )
{
for( x = width; x; x -- )
{
to[0] = from[3];
to[1] = from[2];
to[2] = from[1];
to[3] = from[0];
to += 4;
from += 4;
}
capture_rect.origin.x = p_sys->i_left;
capture_rect.origin.y = p_sys->i_top;
capture_rect.size.width = p_data->width;
capture_rect.size.height = p_data->height;
to += diff;
}
#if 0
// FIXME: actually plot cursor image into snapshot
/* fetch cursor image */
CGImageRef cursor_image;
int cid = CGSMainConnectionID();
CGPoint outHotSpot;
cursor_image = CGSCreateRegisteredCursorImage(cid, (char *)"com.apple.coregraphics.GlobalCurrent", &outHotSpot);
#endif
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
glTexImage2D( GL_TEXTURE_2D, 0,
GL_RGBA8,
pot_width, pot_height, 0,
GL_RGBA, GL_UNSIGNED_BYTE,
pot_cursor_image );
p_data->cursor_need_update = 0;
p_data->cursor_seed = seed;
free( pot_cursor_image );
free( cursor_image );
}
else if( p_data->cursor_need_update )
{
return VLC_EGENERIC;
/* fetch image data */
image = CGDisplayCreateImageForRect(p_data->display_id, capture_rect);
if (image) {
/* build block */
int i_buffer = (p_sys->fmt.video.i_bits_per_pixel + 7) / 8 * p_sys->fmt.video.i_width * p_sys->fmt.video.i_height;
p_block = block_Alloc(i_buffer);
if (!p_block) {
msg_Warn(p_demux, "can't get block");
return NULL;
}
double x, y;
double x1, y1, x2, y2;
x = cursor_pos->x - p_sys->i_left - p_data->cursor_hot.x;
y = cursor_pos->y - p_sys->i_top - p_data->cursor_hot.y;
x1 = 2.0 * x / p_data->width - 1.0;
y1 = 2.0 * y / p_data->height - 1.0;
x2 = 2.0 * ( x + p_data->cursor_rect.size.width ) / p_data->width - 1.0;
y2 = 2.0 * ( y + p_data->cursor_rect.size.height ) / p_data->height - 1.0;
glColor3f( 1.0f, 1.0f, 1.0f );
glEnable( GL_TEXTURE_2D );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
glBegin( GL_POLYGON );
glTexCoord2f( 0.0, 0.0 );
glVertex2f( x1, y1 );
glTexCoord2f( p_data->cursor_texture_map_u, 0.0 );
glVertex2f( x2, y1 );
glTexCoord2f( p_data->cursor_texture_map_u, p_data->cursor_texture_map_v );
glVertex2f( x2, y2 );
glTexCoord2f( 0.0, p_data->cursor_texture_map_v );
glVertex2f( x1, y2 );
glEnd();
glDisable( GL_BLEND );
glDisable( GL_TEXTURE_2D );
dataProvider = CGImageGetDataProvider(image);
data = CGDataProviderCopyData(dataProvider);
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), p_block->p_buffer);
return VLC_SUCCESS;
}
CFRelease(data);
CFRelease(dataProvider);
CFRelease(image);
int screen_CaptureScreen( demux_sys_t *p_sys )
{
screen_data_t *p_data = p_sys->p_data;
if( p_data->myCGDisplayCreateImageForRect != NULL )
{
CGImageRef captured_image;
CGContextRef captured_bitmap;
CGRect capture_rect;
CGRect draw_rect;
free(p_sys->p_mouse);
capture_rect.origin.x = p_sys->i_left;
capture_rect.origin.y = p_sys->i_top;
capture_rect.size.width = p_data->width;
capture_rect.size.height = p_data->height;
captured_image =
p_data->myCGDisplayCreateImageForRect( p_data->display_id,
capture_rect );
captured_bitmap =
CGBitmapContextCreate( ( unsigned char * )p_data->screen_image,
capture_rect.size.width,
capture_rect.size.height,
8,
capture_rect.size.width * 4,
CGColorSpaceCreateDeviceRGB(),
kCGImageAlphaPremultipliedLast );
draw_rect.size.width = CGImageGetWidth( captured_image );
draw_rect.size.height = CGImageGetHeight( captured_image );
draw_rect.origin.x = 0;
draw_rect.origin.y = capture_rect.size.height - draw_rect.size.height;
CGContextDrawImage( captured_bitmap, draw_rect, captured_image );
CGContextRelease( captured_bitmap );
CGImageRelease( captured_image );
}
else
{
CGLSetCurrentContext( p_data->screen );
glReadPixels( p_sys->i_left,
p_data->screen_height - p_sys->i_top - p_data->height,
p_data->width,
p_data->height,
GL_RGBA, GL_UNSIGNED_BYTE,
p_data->screen_image );
return p_block;
}
return VLC_SUCCESS;
msg_Warn(p_demux, "no image!");
return NULL;
}
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