mirror of https://code.videolan.org/videolan/vlc
New CoreGraphics based video output drawing on CoreAnimation layers
This commit is contained in:
parent
a01f401773
commit
70bc0a33a6
|
@ -3119,6 +3119,15 @@ then
|
|||
VLC_ADD_PLUGIN([vout_macosx])
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl Mac CoreGraphics Layer Vout
|
||||
AC_ARG_ENABLE(coregraphicslayer-vout,
|
||||
[ --enable-coregraphicslayer-vout CoreGraphics layered video output module (default disabled)])
|
||||
if test "${enable_coregraphicslayer_vout}" = "yes"; then
|
||||
VLC_ADD_LIBS([vout_coregraphicslayer],[-Wl,-framework,Cocoa])
|
||||
VLC_ADD_LIBS([vout_coregraphicslayer],[-Wl,-framework,QuartzCore])
|
||||
VLC_ADD_PLUGIN([vout_coregraphicslayer])
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl freetype module
|
||||
|
|
|
@ -7,6 +7,7 @@ SOURCES_directfb = directfb.c
|
|||
SOURCES_vmem = vmem.c
|
||||
SOURCES_yuv = yuv.c
|
||||
SOURCES_vout_macosx = macosx.m opengl.h opengl.c
|
||||
SOURCES_vout_coregraphicslayer = coregraphicslayer.m
|
||||
SOURCES_vout_ios2 = ios2.m opengl.h opengl.c
|
||||
SOURCES_android_surface = androidsurface.c
|
||||
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
/*****************************************************************************
|
||||
* coregraphicslayer.m: CoreGraphics video output for NPAPI plugins
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2013 VLC authors and VideoLAN
|
||||
* $Id$
|
||||
*
|
||||
* Authors: Felix Paul Kühne <fkuehne # videolan.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser 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
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# import "config.h"
|
||||
#endif
|
||||
|
||||
#import <vlc_common.h>
|
||||
#import <vlc_plugin.h>
|
||||
#import <vlc_vout_display.h>
|
||||
#import <vlc_picture_pool.h>
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
static int Open(vlc_object_t *);
|
||||
static void Close(vlc_object_t *);
|
||||
|
||||
vlc_module_begin ()
|
||||
set_description(N_("CoreGraphics video output"))
|
||||
set_shortname("CoreGraphics")
|
||||
|
||||
set_category(CAT_VIDEO)
|
||||
set_subcategory(SUBCAT_VIDEO_VOUT)
|
||||
set_capability("vout display", 0)
|
||||
|
||||
set_callbacks(Open, Close)
|
||||
vlc_module_end ()
|
||||
|
||||
@protocol VLCCoreGraphicsVideoLayerEmbedding <NSObject>
|
||||
- (void)addVoutLayer:(CALayer *)aLayer;
|
||||
- (void)removeVoutLayer:(CALayer *)aLayer;
|
||||
- (CGSize)currentOutputSize;
|
||||
@end
|
||||
|
||||
@interface VLCCoreGraphicsLayer : CALayer {
|
||||
CGImageRef _lastFrame;
|
||||
bool lock;
|
||||
vout_display_t *_vd;
|
||||
}
|
||||
@property (nonatomic) vout_display_t *vd;
|
||||
- (void)setLastFrame:(CGImageRef)lastFrame;
|
||||
@end
|
||||
|
||||
/*****************************************************************************
|
||||
* Local prototypes
|
||||
*****************************************************************************/
|
||||
struct vout_display_sys_t {
|
||||
picture_pool_t *pool;
|
||||
picture_resource_t resource;
|
||||
|
||||
VLCCoreGraphicsLayer *cgLayer;
|
||||
|
||||
vout_window_t *embed;
|
||||
bool has_first_frame;
|
||||
|
||||
size_t componentsPerPixel;
|
||||
size_t bitsPerComponent;
|
||||
size_t bitsPerPixel;
|
||||
|
||||
CALayer <VLCCoreGraphicsVideoLayerEmbedding> *container;
|
||||
CGColorSpaceRef colorspace;
|
||||
};
|
||||
static picture_pool_t *Pool(vout_display_t *, unsigned);
|
||||
static void Display(vout_display_t *, picture_t *, subpicture_t *);
|
||||
static int Control(vout_display_t *, int, va_list);
|
||||
|
||||
/*****************************************************************************
|
||||
* OpenVideo: activates dummy vout display method
|
||||
*****************************************************************************/
|
||||
static int Open(vlc_object_t *object)
|
||||
{
|
||||
vout_display_t *vd = (vout_display_t *)object;
|
||||
vout_display_sys_t *sys = calloc(1, sizeof(*sys));
|
||||
NSAutoreleasePool *nsPool = nil;
|
||||
|
||||
if (!sys)
|
||||
return VLC_ENOMEM;
|
||||
sys->pool = NULL;
|
||||
|
||||
id container = var_CreateGetAddress(vd, "drawable-nsobject");
|
||||
if (container)
|
||||
vout_display_DeleteWindow(vd, NULL);
|
||||
else {
|
||||
vout_window_cfg_t wnd_cfg;
|
||||
|
||||
memset(&wnd_cfg, 0, sizeof(wnd_cfg));
|
||||
wnd_cfg.type = VOUT_WINDOW_TYPE_NSOBJECT;
|
||||
wnd_cfg.x = var_InheritInteger(vd, "video-x");
|
||||
wnd_cfg.y = var_InheritInteger(vd, "video-y");
|
||||
wnd_cfg.height = vd->cfg->display.height;
|
||||
wnd_cfg.width = vd->cfg->display.width;
|
||||
|
||||
sys->embed = vout_display_NewWindow(vd, &wnd_cfg);
|
||||
if (sys->embed)
|
||||
container = sys->embed->handle.nsobject;
|
||||
|
||||
if (!container) {
|
||||
msg_Err(vd, "No drawable-nsobject found!");
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
/* store for later, released in Close() */
|
||||
sys->container = [container retain];
|
||||
|
||||
/* allocate pool */
|
||||
nsPool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
if ([container respondsToSelector:@selector(addVoutLayer:)]) {
|
||||
msg_Dbg(vd, "container implements implicit protocol");
|
||||
sys->cgLayer = [[VLCCoreGraphicsLayer alloc] init];
|
||||
[container addVoutLayer:sys->cgLayer];
|
||||
} else if ([container respondsToSelector:@selector(addSublayer:)] || [container isKindOfClass:[CALayer class]]) {
|
||||
msg_Dbg(vd, "container doesn't implement implicit protocol, fallback mode used");
|
||||
sys->cgLayer = [[VLCCoreGraphicsLayer alloc] init];
|
||||
[container addSublayer:sys->cgLayer];
|
||||
} else {
|
||||
msg_Err(vd, "Provided NSObject container isn't compatible");
|
||||
goto bailout;
|
||||
}
|
||||
[sys->cgLayer setVd:vd];
|
||||
|
||||
/* setup output format */
|
||||
video_format_t fmt = vd->fmt;
|
||||
|
||||
char *chroma = "RGBA";
|
||||
fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, chroma);
|
||||
msg_Dbg(vd, "forcing chroma 0x%.8x (%4.4s)", fmt.i_chroma, (char*)&fmt.i_chroma);
|
||||
|
||||
video_format_FixRgb(&fmt);
|
||||
msg_Dbg(vd, "will use pixel format %4.4s", (char*)&fmt.i_chroma);
|
||||
|
||||
/* setup vout display */
|
||||
vout_display_info_t info = vd->info;
|
||||
info.has_hide_mouse = false;
|
||||
vd->sys = sys;
|
||||
vd->fmt = fmt;
|
||||
vd->info = info;
|
||||
vd->pool = Pool;
|
||||
vd->display = Display;
|
||||
vd->control = Control;
|
||||
vd->prepare = NULL;
|
||||
vd->manage = NULL;
|
||||
|
||||
/* setup initial state */
|
||||
CGSize outputSize;
|
||||
if ([container respondsToSelector:@selector(currentOutputSize)])
|
||||
outputSize = [container currentOutputSize];
|
||||
else
|
||||
outputSize = [sys->container visibleRect].size;
|
||||
vout_display_SendEventFullscreen(vd, false);
|
||||
vout_display_SendEventDisplaySize(vd, (int)outputSize.width, (int)outputSize.height, false);
|
||||
|
||||
sys->colorspace = CGColorSpaceCreateDeviceRGB();
|
||||
sys->componentsPerPixel = 4;
|
||||
sys->bitsPerComponent = sizeof(unsigned char) * 8;
|
||||
sys->bitsPerPixel = sys->bitsPerComponent * sys->componentsPerPixel;
|
||||
|
||||
return VLC_SUCCESS;
|
||||
|
||||
bailout:
|
||||
if (nsPool)
|
||||
[nsPool release];
|
||||
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
static void Close(vlc_object_t *object)
|
||||
{
|
||||
vout_display_t *vd = (vout_display_t *)object;
|
||||
vout_display_sys_t *sys = vd->sys;
|
||||
|
||||
if (sys->pool)
|
||||
picture_pool_Delete(sys->pool);
|
||||
|
||||
if (sys->cgLayer) {
|
||||
if ([sys->container respondsToSelector:@selector(removeVoutLayer:)])
|
||||
[sys->container removeVoutLayer:sys->cgLayer];
|
||||
else
|
||||
[sys->cgLayer removeFromSuperlayer];
|
||||
[sys->cgLayer release];
|
||||
}
|
||||
|
||||
if (sys->container)
|
||||
[sys->container release];
|
||||
|
||||
free(sys);
|
||||
}
|
||||
|
||||
static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
|
||||
{
|
||||
vout_display_sys_t *sys = vd->sys;
|
||||
if (!sys->pool)
|
||||
sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
|
||||
return sys->pool;
|
||||
}
|
||||
|
||||
static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
|
||||
{
|
||||
vout_display_sys_t *sys = vd->sys;
|
||||
|
||||
if (!sys->cgLayer) {
|
||||
msg_Warn(vd, "no cglayer to Display in");
|
||||
return;
|
||||
}
|
||||
uint32_t sourceWidth, sourceHeight;
|
||||
|
||||
sourceWidth = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
|
||||
sourceHeight = picture->p[0].i_visible_lines;
|
||||
|
||||
const int crop_offset = vd->source.i_y_offset * picture->p->i_pitch +
|
||||
vd->source.i_x_offset * picture->p->i_pixel_pitch;
|
||||
|
||||
CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
|
||||
&picture->p->p_pixels[crop_offset],
|
||||
sizeof(picture->p->p_pixels[crop_offset]),
|
||||
kCFAllocatorNull);
|
||||
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(dataRef);
|
||||
|
||||
CGImageRef newFrame = CGImageCreate(sourceWidth,
|
||||
sourceHeight,
|
||||
sys->bitsPerComponent,
|
||||
sys->bitsPerPixel,
|
||||
sys->componentsPerPixel * sourceWidth,
|
||||
sys->colorspace,
|
||||
kCGBitmapByteOrder16Big,
|
||||
dataProvider,
|
||||
NULL,
|
||||
true,
|
||||
kCGRenderingIntentPerceptual);
|
||||
|
||||
CGDataProviderRelease(dataProvider);
|
||||
|
||||
if (!newFrame)
|
||||
goto end;
|
||||
|
||||
[sys->cgLayer setLastFrame:newFrame];
|
||||
|
||||
end:
|
||||
VLC_UNUSED(subpicture);
|
||||
picture_Release(picture);
|
||||
}
|
||||
|
||||
static int Control(vout_display_t *vd, int query, va_list args)
|
||||
{
|
||||
VLC_UNUSED(vd);
|
||||
VLC_UNUSED(query);
|
||||
VLC_UNUSED(args);
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
@implementation VLCCoreGraphicsLayer
|
||||
@synthesize vd=_vd;
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
[CATransaction begin];
|
||||
self.needsDisplayOnBoundsChange = YES;
|
||||
self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (bool)locked
|
||||
{
|
||||
return lock;
|
||||
}
|
||||
|
||||
- (void)setLastFrame:(CGImageRef)lastFrame
|
||||
{
|
||||
if (lock) {
|
||||
/* drop frame since we currently drawing */
|
||||
CGImageRelease(lastFrame);
|
||||
return;
|
||||
}
|
||||
|
||||
_lastFrame = CGImageCreateCopy(lastFrame);
|
||||
CGImageRelease(lastFrame);
|
||||
CGRect invalidRect = CGRectMake(0, 0, CGImageGetWidth(_lastFrame), CGImageGetHeight(_lastFrame));
|
||||
[CATransaction begin];
|
||||
[self setNeedsDisplayInRect:invalidRect];
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
- (void)drawInContext:(CGContextRef)cgContext
|
||||
{
|
||||
if (!cgContext)
|
||||
return;
|
||||
|
||||
if (!_lastFrame)
|
||||
return;
|
||||
|
||||
if (lock)
|
||||
return;
|
||||
|
||||
lock = YES;
|
||||
|
||||
CGRect layerRect = self.bounds;
|
||||
float display_width = 0.;
|
||||
float display_height = 0.;
|
||||
float media_width = CGImageGetWidth(_lastFrame);
|
||||
float media_height = CGImageGetHeight(_lastFrame);
|
||||
|
||||
float src_aspect = (float)media_width / media_height;
|
||||
float dst_aspect = (float)layerRect.size.width/layerRect.size.height;
|
||||
if (src_aspect > dst_aspect) {
|
||||
if (layerRect.size.width != media_width) { //don't scale if size equal
|
||||
display_width = layerRect.size.width;
|
||||
display_height = display_width / src_aspect; // + 0.5);
|
||||
} else {
|
||||
display_width = media_width;
|
||||
display_height = media_height;
|
||||
}
|
||||
} else {
|
||||
if (layerRect.size.height != media_height) { //don't scale if size equal
|
||||
display_height = layerRect.size.height;
|
||||
display_width = display_height * src_aspect; // + 0.5);
|
||||
} else {
|
||||
display_width = media_width;
|
||||
display_height = media_height;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute the position of the video */
|
||||
float left = (layerRect.size.width - display_width) / 2.;
|
||||
float top = (layerRect.size.height - display_height) / 2.;
|
||||
|
||||
CGContextSaveGState(cgContext);
|
||||
|
||||
// draw a clear background
|
||||
CGContextClearRect(cgContext, CGRectMake(0., 0., layerRect.size.width, layerRect.size.height));
|
||||
|
||||
// draw the image
|
||||
CGRect rect = CGRectMake(left, top, display_width, display_height);
|
||||
CGContextDrawImage(cgContext, rect, _lastFrame);
|
||||
lock = NO;
|
||||
|
||||
CGContextRestoreGState(cgContext);
|
||||
}
|
||||
|
||||
- (void)resizeWithOldSuperlayerSize:(CGSize)size
|
||||
{
|
||||
[super resizeWithOldSuperlayerSize:size];
|
||||
|
||||
if (!_vd)
|
||||
return;
|
||||
|
||||
CGSize outputSize = [_vd->sys->container currentOutputSize];
|
||||
|
||||
vout_display_place_t place;
|
||||
vout_display_cfg_t cfg_tmp = *(_vd->cfg);
|
||||
cfg_tmp.display.width = outputSize.width;
|
||||
cfg_tmp.display.height = outputSize.height;
|
||||
|
||||
vout_display_PlacePicture (&place, &_vd->source, &cfg_tmp, false);
|
||||
vout_display_SendEventDisplaySize (_vd, outputSize.width, outputSize.height, _vd->cfg->is_fullscreen);
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue