[vlc-devel] [PATCH 1/8] Add CALayer module
David Menestrina
dmenest-vlc at ofb.net
Fri Sep 10 00:12:28 CEST 2010
---
configure.ac | 10 +
modules/video_output/Modules.am | 1 +
modules/video_output/calayer.m | 427 +++++++++++++++++++++++++++++++++++++++
3 files changed, 438 insertions(+), 0 deletions(-)
create mode 100644 modules/video_output/calayer.m
diff --git a/configure.ac b/configure.ac
index 775135d..39393dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4075,6 +4075,16 @@ then
VLC_ADD_PLUGIN([vout_macosx])
fi
+AC_ARG_ENABLE(macosx-calayer,
+ [ --enable-macosx-calayer Mac OS X CALayer video output module (default enabled on Mac OS X)])
+if test "x${enable_macosx_calayer}" != "xno" &&
+ (test "${SYS}" = "darwin" || test "${enable_macosx_calayer}" = "yes")
+then
+ VLC_ADD_LDFLAGS([vout_calayer],[-Wl,-framework,Cocoa])
+ VLC_ADD_LDFLAGS([vout_calayer],[-Wl,-framework,QuartzCore])
+ VLC_ADD_PLUGIN([vout_calayer])
+fi
+
AC_ARG_ENABLE(macosx-dialog-provider,
[ --enable-macosx-dialog-provider Mac OS X dialog module (default enabled on Mac OS X)])
if test "x${enable_macosx_dialog_provider}" != "xno" &&
diff --git a/modules/video_output/Modules.am b/modules/video_output/Modules.am
index 9034b71..cae8a07 100644
--- a/modules/video_output/Modules.am
+++ b/modules/video_output/Modules.am
@@ -15,6 +15,7 @@ SOURCES_hd1000v = hd1000v.cpp
SOURCES_snapshot = snapshot.c
SOURCES_opengl = opengl.c opengl.h
SOURCES_directfb = directfb.c
+SOURCES_vout_calayer = calayer.m
SOURCES_vmem = vmem.c
SOURCES_yuv = yuv.c
SOURCES_vout_wrapper = wrapper.c
diff --git a/modules/video_output/calayer.m b/modules/video_output/calayer.m
new file mode 100644
index 0000000..307122f
--- /dev/null
+++ b/modules/video_output/calayer.m
@@ -0,0 +1,427 @@
+/*****************************************************************************
+ * calayer.m: MacOS X CALayer vout provider
+ *****************************************************************************
+ * Copyright (C) 2001-2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: David Menestrina <dmenest-vlc at ofb dot net>
+ * Colin Delacroix <colin at zoy.org>
+ * Florian G. Pflug <fgp at phlo.org>
+ * Jon Lech Johansen <jon-vl at nanocrew.net>
+ * Derk-Jan Hartman <hartman at videolan dot org>
+ * Eric Petit <titer at m0k.org>
+ * Benjamin Pracht <bigben at videolan dot org>
+ * Damien Fouilleul <damienf at videolan dot org>
+ * Pierre d'Herbemont <pdherbemont at videolan dot 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/QuartzCore.h>
+#import <ApplicationServices/ApplicationServices.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_display.h>
+
+/**
+ * This vout display module is written to support the Core Animation
+ * drawing model for the mozilla plugin on 64-bit Mac. Of course
+ * it also can be used to embed VLC in any Mac application.
+ *
+ * To use this vout, you must set the drawable-nsobject variable
+ * to a CALayer * (this is done via the libvlc_media_player_set_nsobject()
+ * function). This module will modify the "delegate" property of
+ * the given layer. The delegate will draw video frames into the CALayer,
+ * using the layer's bounds to fill the layer (while respecting the
+ * current aspect ratio).
+ */
+
+/**
+ * Forward declarations
+ */
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+static picture_pool_t *Pool(vout_display_t *vd, unsigned count);
+
+static void Display(vout_display_t *vd, picture_t *pic);
+static int Control(vout_display_t *vd, int query, va_list ap);
+
+/**
+ * Module declaration
+ */
+vlc_module_begin ()
+ /* Will be loaded even without interface module. see voutgl.m */
+ set_shortname("CALayer")
+ set_description( N_("Mac OS X CALayer video output (requires drawable-nsobject)"))
+ set_category(CAT_VIDEO)
+ set_subcategory(SUBCAT_VIDEO_VOUT )
+ set_capability("vout display", 0)
+ set_callbacks(Open, Close)
+
+ add_shortcut("calayer")
+ add_shortcut("vout_calayer")
+vlc_module_end ()
+
+ at interface VLCLayerDelegate : NSObject
+{
+ vout_display_t *vd;
+ picture_t *currentpic;
+ pthread_mutex_t mutex;
+}
+- (id) initWithVd: (vout_display_t *) p_vd;
+- (void) drawLayer: (CALayer *)layer inContext: (CGContextRef) theContext;
+- (void) setPic: (picture_t *) p_pic;
+ at end
+
+struct vout_display_sys_t
+{
+ // The layer to put our pictures in.
+ CALayer *p_calayer;
+
+ // The layer's drawing delegate
+ VLCLayerDelegate *p_delegate;
+
+ // The picture pool.
+ picture_pool_t *p_pool;
+
+ // Because p_pool is an opaque type, we must keep copies of important internal
+ // pool data for access to the pictures' sys structures.
+ unsigned int picture_count;
+ picture_t **p_pictures;
+};
+
+typedef struct picture_sys_t
+{
+ // We store a data provider for each picture to avoid continually
+ // reallocating this structure.
+ CGDataProviderRef p_provider;
+} calayer_picture_sys_t;
+
+ at implementation VLCLayerDelegate : NSObject
+- (id) initWithVd: (vout_display_t *) p_vd
+{
+ if( self = [super init] )
+ {
+ currentpic = NULL;
+ vd = p_vd;
+ pthread_mutex_init(&mutex, NULL);
+ }
+
+ return self;
+}
+/**
+ * This function draws the current image into the layer with a size
+ * and position based on the layer's current bounds.
+ */
+- (void) drawLayer: (CALayer *)layer inContext: (CGContextRef) theContext
+{
+ // Determine where to draw the image.
+ vout_display_cfg_t place_cfg = *vd->cfg;
+ CGRect bounds = layer.bounds;
+ place_cfg.display.width = bounds.size.width;
+ place_cfg.display.height = bounds.size.height;
+ vout_display_place_t place;
+ vout_display_PlacePicture(&place, &vd->source, &place_cfg, FALSE);
+
+ CGRect imagePosition = CGRectMake(place.x, place.y, place.width, place.height);
+
+ pthread_mutex_lock(&mutex);
+ if (currentpic != NULL)
+ {
+ // Create a CGImage to put in the layer
+ CGImageRef image = CGImageCreate(currentpic->format.i_width, currentpic->format.i_height,
+ 8, 32, currentpic->p[0].i_pitch, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipLast |
+ kCGBitmapByteOrder32Big, currentpic->p_sys->p_provider, NULL, TRUE,
+ kCGRenderingIntentDefault);
+ CGContextDrawImage(theContext, imagePosition, image);
+ CGImageRelease(image);
+ }
+ pthread_mutex_unlock(&mutex);
+
+}
+/**
+ * This function is called by the vout display's Display() function to deliver
+ * the most recent frame to the delegate. This function will release the old
+ * (if any) picture_t back to the pool. The provided picture_t pointer may be
+ * NULL.
+ */
+- (void) setPic: (picture_t *) p_pic
+{
+ pthread_mutex_lock(&mutex);
+
+ if (currentpic != NULL)
+ picture_Release(currentpic);
+
+ currentpic = p_pic;
+
+ if (currentpic != NULL)
+ picture_Hold(currentpic);
+
+ pthread_mutex_unlock(&mutex);
+}
+- (void) dealloc
+{
+ vd = NULL;
+ [self setPic: NULL];
+ pthread_mutex_destroy(&mutex);
+ [super dealloc];
+}
+ at end
+
+static CALayer *getDrawable(vout_display_t *vd)
+{
+ CALayer *layer = var_CreateGetAddress(vd, "drawable-nsobject");
+ var_Destroy(vd, "drawable-nsobject");
+ return layer;
+}
+
+static int Open(vlc_object_t *this)
+{
+ vout_display_t *vd = (vout_display_t *)this;
+ vout_display_sys_t *sys = calloc(1, sizeof(*sys));
+ vd->sys = sys;
+
+ if( !sys )
+ return VLC_ENOMEM;
+
+ CALayer *layer = getDrawable(vd);
+ if( !layer )
+ {
+ msg_Dbg(vd, "No drawable-nsobject, passing over.");
+ goto error;
+ }
+
+ // Store a pointer to the layer, construct and assign the delegate
+ sys->p_calayer = [layer retain];
+ sys->p_delegate = [[VLCLayerDelegate alloc] initWithVd:vd];
+ sys->p_calayer.delegate = sys->p_delegate;
+
+ // Apple docs mention RGBA-32, so we'll do that.
+ video_format_t fmt = vd->fmt;
+ fmt.i_chroma = VLC_CODEC_RGBA;
+
+ vout_display_info_t info = vd->info;
+ info.has_pictures_invalid = false;
+
+ // Setup fields in vd
+ vd->pool = Pool;
+ vd->prepare = NULL;
+ vd->display = Display;
+ vd->control = Control;
+ vd->manage = NULL;
+ vd->info = info;
+ vd->fmt = fmt;
+
+ return VLC_SUCCESS;
+
+error:
+ Close(this);
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * vout display callbacks
+ *****************************************************************************/
+
+/**
+ * This function adds module specific "sys" data to the given picture_t.
+ * We cache a CGDataProvider for each picture_t.
+ */
+static void fillPictureSysData(picture_t *pic)
+{
+ calayer_picture_sys_t *picsys = pic->p_sys = calloc(1, sizeof(calayer_picture_sys_t));
+
+ // Create the data provider/CGImage
+ CGDataProviderRef data = CGDataProviderCreateWithData(NULL,
+ pic->p[0].p_pixels, pic->p[0].i_pitch * pic->p[0].i_lines, NULL);
+
+ picsys->p_provider = data;
+}
+
+/**
+ * This funtion frees the module specific data (created by fillPictureSysData)
+ * in the given picture_t.
+ */
+static void freePictureSysData(picture_t *pic)
+{
+ if( pic->p_sys )
+ {
+ CGDataProviderRelease(pic->p_sys->p_provider);
+ free(pic->p_sys);
+ pic->p_sys = NULL;
+ }
+}
+
+/**
+ * This function creates our picture pool and stores it in our sys structure.
+ */
+static void fillPool(vout_display_t *vd, unsigned count)
+{
+ picture_t **picture = calloc(count, sizeof *picture);
+ if( !picture )
+ return;
+
+ // Create count pictures, and add sys data to each one.
+ for( int i = 0; i < count; i++ ) {
+ picture[i] = picture_NewFromFormat(&vd->fmt);
+ if( !picture[i] )
+ goto error;
+ fillPictureSysData(picture[i]);
+ }
+
+ // Create the pool from the picture array
+ picture_pool_t *pool = picture_pool_New(count, picture);
+ if( !pool )
+ goto error;
+
+ // Store the relevant information in our sys structure.
+ vd->sys->p_pool = pool;
+ vd->sys->p_pictures = picture;
+ vd->sys->picture_count = count;
+ return;
+
+error:
+ for( int i = 0; i < count; i++ ) {
+ if( !picture[i] )
+ break;
+ picture_Release(picture[i]);
+ }
+ free(picture);
+ return;
+}
+
+/**
+ * This function frees the picture pool created by fillPool().
+ */
+static void freePool(vout_display_sys_t *sys)
+{
+ picture_pool_t *pool = sys->p_pool;
+ if( !pool )
+ return;
+
+ // free the sys data from each picture
+ for( int i = 0; i < sys->picture_count; i++ )
+ freePictureSysData(sys->p_pictures[i]);
+
+ // free the pool itself
+ picture_pool_Delete(pool);
+
+ sys->p_pool = NULL;
+
+ if( sys->p_pictures )
+ free(sys->p_pictures);
+ sys->p_pictures = NULL;
+}
+
+static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
+{
+ vout_display_sys_t *sys = vd->sys;
+
+ if( !sys->p_pool )
+ {
+ // We always request an extra picture_t in the pool because
+ // the VLCLayerDelegate holds its picture_t out of the pool
+ fillPool(vd, count + 1);
+ }
+ assert(sys->p_pool);
+ return sys->p_pool;
+}
+
+static void Display(vout_display_t *vd, picture_t *pic)
+{
+ vout_display_sys_t *sys = vd->sys;
+ assert(pic->i_planes == 1);
+ assert(pic->format.i_bits_per_pixel == 32);
+
+ assert(sys != NULL);
+ assert(sys->p_calayer != NULL);
+
+ // send the picture to the delegate and ask the main thread to redraw the layer.
+ [sys->p_delegate setPic:pic];
+ [sys->p_calayer performSelectorOnMainThread: @selector(setNeedsDisplay) withObject:nil waitUntilDone:false];
+
+ // We release our reference to the picture_t here, but the delegate still holds
+ // a reference that will not be released until the next picture_t is handed to it.
+ picture_Release (pic);
+}
+
+static int Control (vout_display_t *vd, int query, va_list ap)
+{
+ vout_display_sys_t *sys = vd->sys;
+
+ switch (query)
+ {
+ case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
+ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
+ case VOUT_DISPLAY_CHANGE_ZOOM:
+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
+ {
+ /* todo */
+ return VLC_EGENERIC;
+ }
+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
+ {
+ const vout_display_cfg_t *cfg = vd->cfg;
+ const video_format_t *source = va_arg(ap, const video_format_t *);
+
+ // Aspect ratio changes are handled by drawing the picture in the new
+ // aspect ratio in the VLCLayerDelegate. So we just return success here.
+ return VLC_SUCCESS;
+ }
+
+ case VOUT_DISPLAY_HIDE_MOUSE:
+ return VLC_SUCCESS;
+
+ case VOUT_DISPLAY_GET_OPENGL:
+ {
+ return VLC_EGENERIC;
+ }
+ case VOUT_DISPLAY_RESET_PICTURES:
+ assert (0);
+ default:
+ msg_Err (vd, "Unknown request in Mac OS X CALayer vout display");
+ return VLC_EGENERIC;
+ }
+}
+
+void Close(vlc_object_t *this)
+{
+ vout_display_t *vd = (vout_display_t *)this;
+
+ if( vd->sys )
+ {
+ vout_display_sys_t *sys = vd->sys;
+
+ [sys->p_delegate setPic:NULL];
+ sys->p_calayer.delegate = NULL;
+ [sys->p_delegate release];
+ [sys->p_calayer release];
+
+ freePool(sys);
+ free(sys);
+ vd->sys = NULL;
+ }
+}
--
1.7.0.3
More information about the vlc-devel
mailing list