1 /*****************************************************************************
2 * coregraphicslayer.m: CoreGraphics video output for NPAPI plugins
3 *****************************************************************************
4 * Copyright (C) 2013 VLC authors and VideoLAN
7 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #import <vlc_common.h>
33 #import <vlc_plugin.h>
34 #import <vlc_vout_display.h>
35 #import <vlc_picture_pool.h>
37 #import <QuartzCore/QuartzCore.h>
38 #import <AppKit/AppKit.h>
40 static int Open(vlc_object_t *);
41 static void Close(vlc_object_t *);
44 set_description(N_("CoreGraphics video output"))
45 set_shortname("CoreGraphics")
47 set_category(CAT_VIDEO)
48 set_subcategory(SUBCAT_VIDEO_VOUT)
49 set_capability("vout display", 0)
51 set_callbacks(Open, Close)
54 @protocol VLCCoreGraphicsVideoLayerEmbedding <NSObject>
55 - (void)addVoutLayer:(CALayer *)aLayer;
56 - (void)removeVoutLayer:(CALayer *)aLayer;
57 - (CGSize)currentOutputSize;
60 @interface VLCCoreGraphicsLayer : CALayer {
61 CGImageRef _lastFrame;
65 @property (nonatomic) vout_display_t *vd;
66 - (void)setLastFrame:(CGImageRef)lastFrame;
69 /*****************************************************************************
71 *****************************************************************************/
72 struct vout_display_sys_t {
74 picture_resource_t resource;
76 VLCCoreGraphicsLayer *cgLayer;
81 size_t componentsPerPixel;
82 size_t bitsPerComponent;
85 CALayer <VLCCoreGraphicsVideoLayerEmbedding> *container;
86 CGColorSpaceRef colorspace;
88 static picture_pool_t *Pool(vout_display_t *, unsigned);
89 static void Display(vout_display_t *, picture_t *, subpicture_t *);
90 static int Control(vout_display_t *, int, va_list);
92 /*****************************************************************************
93 * OpenVideo: activates dummy vout display method
94 *****************************************************************************/
95 static int Open(vlc_object_t *object)
97 vout_display_t *vd = (vout_display_t *)object;
98 vout_display_sys_t *sys = calloc(1, sizeof(*sys));
99 NSAutoreleasePool *nsPool = nil;
105 id container = var_CreateGetAddress(vd, "drawable-nsobject");
107 vout_display_DeleteWindow(vd, NULL);
109 vout_window_cfg_t wnd_cfg;
111 memset(&wnd_cfg, 0, sizeof(wnd_cfg));
112 wnd_cfg.type = VOUT_WINDOW_TYPE_NSOBJECT;
113 wnd_cfg.x = var_InheritInteger(vd, "video-x");
114 wnd_cfg.y = var_InheritInteger(vd, "video-y");
115 wnd_cfg.height = vd->cfg->display.height;
116 wnd_cfg.width = vd->cfg->display.width;
118 sys->embed = vout_display_NewWindow(vd, &wnd_cfg);
120 container = sys->embed->handle.nsobject;
123 msg_Err(vd, "No drawable-nsobject found!");
128 /* store for later, released in Close() */
129 sys->container = [container retain];
132 nsPool = [[NSAutoreleasePool alloc] init];
134 if ([container respondsToSelector:@selector(addVoutLayer:)]) {
135 msg_Dbg(vd, "container implements implicit protocol");
136 sys->cgLayer = [[VLCCoreGraphicsLayer alloc] init];
137 [container addVoutLayer:sys->cgLayer];
138 } else if ([container respondsToSelector:@selector(addSublayer:)] || [container isKindOfClass:[CALayer class]]) {
139 msg_Dbg(vd, "container doesn't implement implicit protocol, fallback mode used");
140 sys->cgLayer = [[VLCCoreGraphicsLayer alloc] init];
141 [container addSublayer:sys->cgLayer];
143 msg_Err(vd, "Provided NSObject container isn't compatible");
146 [sys->cgLayer setVd:vd];
148 /* setup output format */
149 video_format_t fmt = vd->fmt;
151 char *chroma = "RGBA";
152 fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, chroma);
153 msg_Dbg(vd, "forcing chroma 0x%.8x (%4.4s)", fmt.i_chroma, (char*)&fmt.i_chroma);
155 video_format_FixRgb(&fmt);
156 msg_Dbg(vd, "will use pixel format %4.4s", (char*)&fmt.i_chroma);
158 /* setup vout display */
159 vout_display_info_t info = vd->info;
160 info.has_hide_mouse = false;
165 vd->display = Display;
166 vd->control = Control;
170 /* setup initial state */
172 if ([container respondsToSelector:@selector(currentOutputSize)])
173 outputSize = [container currentOutputSize];
175 outputSize = [sys->container visibleRect].size;
176 vout_display_SendEventFullscreen(vd, false);
177 vout_display_SendEventDisplaySize(vd, (int)outputSize.width, (int)outputSize.height, false);
179 sys->colorspace = CGColorSpaceCreateDeviceRGB();
180 sys->componentsPerPixel = 4;
181 sys->bitsPerComponent = sizeof(unsigned char) * 8;
182 sys->bitsPerPixel = sys->bitsPerComponent * sys->componentsPerPixel;
193 static void Close(vlc_object_t *object)
195 vout_display_t *vd = (vout_display_t *)object;
196 vout_display_sys_t *sys = vd->sys;
199 picture_pool_Delete(sys->pool);
202 if ([sys->container respondsToSelector:@selector(removeVoutLayer:)])
203 [sys->container removeVoutLayer:sys->cgLayer];
205 [sys->cgLayer removeFromSuperlayer];
206 [sys->cgLayer release];
210 [sys->container release];
215 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
217 vout_display_sys_t *sys = vd->sys;
219 sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
223 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
225 vout_display_sys_t *sys = vd->sys;
228 msg_Warn(vd, "no cglayer to Display in");
231 uint32_t sourceWidth, sourceHeight;
233 sourceWidth = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
234 sourceHeight = picture->p[0].i_visible_lines;
236 const int crop_offset = vd->source.i_y_offset * picture->p->i_pitch +
237 vd->source.i_x_offset * picture->p->i_pixel_pitch;
239 CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
240 &picture->p->p_pixels[crop_offset],
241 sizeof(picture->p->p_pixels[crop_offset]),
243 CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(dataRef);
245 CGImageRef newFrame = CGImageCreate(sourceWidth,
247 sys->bitsPerComponent,
249 sys->componentsPerPixel * sourceWidth,
251 kCGBitmapByteOrder16Big,
255 kCGRenderingIntentPerceptual);
257 CGDataProviderRelease(dataProvider);
262 [sys->cgLayer setLastFrame:newFrame];
265 VLC_UNUSED(subpicture);
266 picture_Release(picture);
269 static int Control(vout_display_t *vd, int query, va_list args)
277 @implementation VLCCoreGraphicsLayer
282 if (self = [super init]) {
283 [CATransaction begin];
284 self.needsDisplayOnBoundsChange = YES;
285 self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
286 [CATransaction commit];
297 - (void)setLastFrame:(CGImageRef)lastFrame
300 /* drop frame since we currently drawing */
301 CGImageRelease(lastFrame);
305 _lastFrame = CGImageCreateCopy(lastFrame);
306 CGImageRelease(lastFrame);
307 CGRect invalidRect = CGRectMake(0, 0, CGImageGetWidth(_lastFrame), CGImageGetHeight(_lastFrame));
308 [CATransaction begin];
309 [self setNeedsDisplayInRect:invalidRect];
310 [CATransaction commit];
313 - (void)drawInContext:(CGContextRef)cgContext
326 CGRect layerRect = self.bounds;
327 float display_width = 0.;
328 float display_height = 0.;
329 float media_width = CGImageGetWidth(_lastFrame);
330 float media_height = CGImageGetHeight(_lastFrame);
332 float src_aspect = (float)media_width / media_height;
333 float dst_aspect = (float)layerRect.size.width/layerRect.size.height;
334 if (src_aspect > dst_aspect) {
335 if (layerRect.size.width != media_width) { //don't scale if size equal
336 display_width = layerRect.size.width;
337 display_height = display_width / src_aspect; // + 0.5);
339 display_width = media_width;
340 display_height = media_height;
343 if (layerRect.size.height != media_height) { //don't scale if size equal
344 display_height = layerRect.size.height;
345 display_width = display_height * src_aspect; // + 0.5);
347 display_width = media_width;
348 display_height = media_height;
352 /* Compute the position of the video */
353 float left = (layerRect.size.width - display_width) / 2.;
354 float top = (layerRect.size.height - display_height) / 2.;
356 CGContextSaveGState(cgContext);
358 // draw a clear background
359 CGContextClearRect(cgContext, CGRectMake(0., 0., layerRect.size.width, layerRect.size.height));
362 CGRect rect = CGRectMake(left, top, display_width, display_height);
363 CGContextDrawImage(cgContext, rect, _lastFrame);
366 CGContextRestoreGState(cgContext);
369 - (void)resizeWithOldSuperlayerSize:(CGSize)size
371 [super resizeWithOldSuperlayerSize:size];
376 CGSize outputSize = [_vd->sys->container currentOutputSize];
378 vout_display_place_t place;
379 vout_display_cfg_t cfg_tmp = *(_vd->cfg);
380 cfg_tmp.display.width = outputSize.width;
381 cfg_tmp.display.height = outputSize.height;
383 vout_display_PlacePicture (&place, &_vd->source, &cfg_tmp, false);
384 vout_display_SendEventDisplaySize (_vd, outputSize.width, outputSize.height, _vd->cfg->is_fullscreen);