demux: avi: invalidate skipped chunks
[vlc.git] / modules / video_output / caopengllayer.m
blobed77118f99e59df9fe6c3e6b8727c13e3a3f8f3c
1 /*****************************************************************************
2  * caopengllayer.m: CAOpenGLLayer (Mac OS X) video output
3  *****************************************************************************
4  * Copyright (C) 2014-2016 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: David Fuhrmann <david dot fuhrmann at googlemail dot com>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
9  *          Pierre d'Herbemont <pdherbemont at videolan dot org>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_vout_display.h>
37 #include <vlc_opengl.h>
39 #import <QuartzCore/QuartzCore.h>
40 #import <Cocoa/Cocoa.h>
41 #import <OpenGL/OpenGL.h>
42 #import <dlfcn.h>               /* dlsym */
44 #include "opengl/vout_helper.h"
46 #define OSX_EL_CAPITAN (NSAppKitVersionNumber >= 1404)
48 #if MAC_OS_X_VERSION_MIN_ALLOWED <= MAC_OS_X_VERSION_10_11
49 const CFStringRef kCGColorSpaceDCIP3 = CFSTR("kCGColorSpaceDCIP3");
50 const CFStringRef kCGColorSpaceITUR_709 = CFSTR("kCGColorSpaceITUR_709");
51 const CFStringRef kCGColorSpaceITUR_2020 = CFSTR("kCGColorSpaceITUR_2020");
52 #endif
54 /*****************************************************************************
55  * Vout interface
56  *****************************************************************************/
57 static int  Open   (vlc_object_t *);
58 static void Close  (vlc_object_t *);
60 vlc_module_begin()
61     set_description(N_("Core Animation OpenGL Layer (Mac OS X)"))
62     set_capability("vout display", 0)
63     set_category(CAT_VIDEO)
64     set_subcategory(SUBCAT_VIDEO_VOUT)
65     set_callbacks(Open, Close)
66 vlc_module_end()
68 static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count);
69 static void PictureRender   (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture);
70 static void PictureDisplay  (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture);
71 static int Control          (vout_display_t *vd, int query, va_list ap);
73 static void *OurGetProcAddress (vlc_gl_t *gl, const char *name);
74 static int OpenglLock         (vlc_gl_t *gl);
75 static void OpenglUnlock       (vlc_gl_t *gl);
76 static void OpenglSwap         (vlc_gl_t *gl);
78 @protocol VLCCoreAnimationVideoLayerEmbedding <NSObject>
79 - (void)addVoutLayer:(CALayer *)aLayer;
80 - (void)removeVoutLayer:(CALayer *)aLayer;
81 - (CGSize)currentOutputSize;
82 @end
84 @interface VLCCAOpenGLLayer : CAOpenGLLayer
86 @property (nonatomic, readwrite) vout_display_t* voutDisplay;
88 @end
91 struct vout_display_sys_t {
93     picture_pool_t *pool;
94     picture_resource_t resource;
96     CALayer <VLCCoreAnimationVideoLayerEmbedding> *container;
97     vout_window_t *embed;
98     VLCCAOpenGLLayer *cgLayer;
100     CGColorSpaceRef cgColorSpace;
102     CGLContextObj glContext;
104     vlc_gl_t *gl;
105     vout_display_opengl_t *vgl;
107     vout_display_place_t place;
109     bool  b_frame_available;
112 /*****************************************************************************
113  * Open: This function allocates and initializes the OpenGL vout method.
114  *****************************************************************************/
115 static int Open (vlc_object_t *p_this)
117     vout_display_t *vd = (vout_display_t *)p_this;
118     vout_display_sys_t *sys;
120     /* Allocate structure */
121     vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
122     if (sys == NULL)
123         return VLC_EGENERIC;
125     @autoreleasepool {
126         id container = var_CreateGetAddress(vd, "drawable-nsobject");
127         if (container)
128             vout_display_DeleteWindow(vd, NULL);
129         else {
130             sys->embed = vout_display_NewWindow(vd, VOUT_WINDOW_TYPE_NSOBJECT);
131             if (sys->embed)
132                 container = sys->embed->handle.nsobject;
134             if (!container) {
135                 msg_Err(vd, "No drawable-nsobject found!");
136                 goto bailout;
137             }
138         }
140         /* store for later, released in Close() */
141         sys->container = [container retain];
143         [CATransaction begin];
144         sys->cgLayer = [[VLCCAOpenGLLayer alloc] init];
145         [sys->cgLayer setVoutDisplay:vd];
147         [sys->cgLayer performSelectorOnMainThread:@selector(display)
148                                        withObject:nil
149                                     waitUntilDone:YES];
151         if ([container respondsToSelector:@selector(addVoutLayer:)]) {
152             msg_Dbg(vd, "container implements implicit protocol");
153             [container addVoutLayer:sys->cgLayer];
154         } else if ([container respondsToSelector:@selector(addSublayer:)] ||
155                    [container isKindOfClass:[CALayer class]]) {
156             msg_Dbg(vd, "container doesn't implement implicit protocol, fallback mode used");
157             [container addSublayer:sys->cgLayer];
158         } else {
159             msg_Err(vd, "Provided NSObject container isn't compatible");
160             [sys->cgLayer release];
161             sys->cgLayer = nil;
162             [CATransaction commit];
163             goto bailout;
164         }
165         [CATransaction commit];
167         if (!sys->cgLayer)
168             goto bailout;
170         if (!sys->glContext)
171             msg_Warn(vd, "we might not have an OpenGL context yet");
173         /* Initialize common OpenGL video display */
174         sys->gl = vlc_object_create(vd, sizeof(*sys->gl));
175         if (unlikely(!sys->gl))
176             goto bailout;
177         sys->gl->makeCurrent = OpenglLock;
178         sys->gl->releaseCurrent = OpenglUnlock;
179         sys->gl->swap = OpenglSwap;
180         sys->gl->getProcAddress = OurGetProcAddress;
181         sys->gl->sys = sys;
183         const vlc_fourcc_t *subpicture_chromas;
184         video_format_t fmt = vd->fmt;
185         if (!OpenglLock(sys->gl)) {
186             sys->vgl = vout_display_opengl_New(&vd->fmt, &subpicture_chromas,
187                                                sys->gl, &vd->cfg->viewpoint);
188             OpenglUnlock(sys->gl);
189         } else
190             sys->vgl = NULL;
191         if (!sys->vgl) {
192             msg_Err(vd, "Error while initializing opengl display.");
193             goto bailout;
194         }
196         /* setup vout display */
197         vout_display_info_t info = vd->info;
198         info.subpicture_chromas = subpicture_chromas;
199         info.has_hide_mouse = true;
200         vd->info = info;
202         vd->pool    = Pool;
203         vd->prepare = PictureRender;
204         vd->display = PictureDisplay;
205         vd->control = Control;
207         /* handle color space if supported by the OS */
208         if ([sys->cgLayer respondsToSelector:@selector(setColorspace:)]) {
210             /* support for BT.709 and BT.2020 color spaces was introduced with OS X 10.11
211              * on older OS versions, we can't show correct colors, so we fallback on linear RGB */
212             if (OSX_EL_CAPITAN) {
213                 switch (fmt.primaries) {
214                     case COLOR_PRIMARIES_BT601_525:
215                     case COLOR_PRIMARIES_BT601_625:
216                     {
217                         msg_Dbg(vd, "Using BT.601 color space");
218                         sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
219                         break;
220                     }
221                     case COLOR_PRIMARIES_BT709:
222                     {
223                         msg_Dbg(vd, "Using BT.709 color space");
224                         sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709);
225                         break;
226                     }
227                     case COLOR_PRIMARIES_BT2020:
228                     {
229                         msg_Dbg(vd, "Using BT.2020 color space");
230                         sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020);
231                         break;
232                     }
233                     case COLOR_PRIMARIES_DCI_P3:
234                     {
235                         msg_Dbg(vd, "Using DCI P3 color space");
236                         sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceDCIP3);
237                         break;
238                     }
239                     default:
240                     {
241                         msg_Dbg(vd, "Guessing color space based on video dimensions (%ix%i)", fmt.i_visible_width, fmt.i_visible_height);
242                         if (fmt.i_visible_height >= 2000 || fmt.i_visible_width >= 3800) {
243                             msg_Dbg(vd, "Using BT.2020 color space");
244                             sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020);
245                         } else if (fmt.i_height > 576) {
246                             msg_Dbg(vd, "Using BT.709 color space");
247                             sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709);
248                         } else {
249                             msg_Dbg(vd, "SD content, using linear RGB color space");
250                             sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
251                         }
252                         break;
253                     }
254                 }
255             } else {
256                 msg_Dbg(vd, "OS does not support BT.709 or BT.2020 color spaces, output may vary");
257                 sys->cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
258             }
260             [sys->cgLayer setColorspace: sys->cgColorSpace];
261         } else {
262             msg_Dbg(vd, "OS does not support custom color spaces, output may be undefined");
263         }
265         /* request our screen's HDR mode (introduced in OS X 10.11) */
266         if ([sys->cgLayer respondsToSelector:@selector(setWantsExtendedDynamicRangeContent:)]) {
267             [sys->cgLayer setWantsExtendedDynamicRangeContent:YES];
268         }
270         /* setup initial state */
271         CGSize outputSize;
272         if ([container respondsToSelector:@selector(currentOutputSize)])
273             outputSize = [container currentOutputSize];
274         else
275             outputSize = [sys->container visibleRect].size;
276         vout_display_SendEventDisplaySize(vd, (int)outputSize.width, (int)outputSize.height);
277         
278         return VLC_SUCCESS;
279         
280     bailout:
281         Close(p_this);
282         return VLC_EGENERIC;
283     }
286 static void Close (vlc_object_t *p_this)
288     vout_display_t *vd = (vout_display_t *)p_this;
289     vout_display_sys_t *sys = vd->sys;
291     if (sys->cgLayer) {
292         if ([sys->container respondsToSelector:@selector(removeVoutLayer:)])
293             [sys->container removeVoutLayer:sys->cgLayer];
294         else
295             [sys->cgLayer removeFromSuperlayer];
296         [sys->cgLayer release];
297     }
299     if (sys->container)
300         [sys->container release];
302     if (sys->embed)
303         vout_display_DeleteWindow(vd, sys->embed);
305     if (sys->vgl != NULL && !OpenglLock(sys->gl)) {
306         vout_display_opengl_Delete(sys->vgl);
307         OpenglUnlock(sys->gl);
308     }
310     if (sys->gl != NULL)
311         vlc_object_release(sys->gl);
313     if (sys->glContext)
314         CGLReleaseContext(sys->glContext);
316     if (sys->cgColorSpace != nil)
317         CGColorSpaceRelease(sys->cgColorSpace);
319     free(sys);
322 static picture_pool_t *Pool (vout_display_t *vd, unsigned count)
324     vout_display_sys_t *sys = vd->sys;
326     if (!sys->pool && !OpenglLock(sys->gl)) {
327         sys->pool = vout_display_opengl_GetPool(sys->vgl, count);
328         OpenglUnlock(sys->gl);
329         assert(sys->pool);
330     }
331     return sys->pool;
334 static void PictureRender (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
336     vout_display_sys_t *sys = vd->sys;
338     if (pic == NULL) {
339         msg_Warn(vd, "invalid pic, skipping frame");
340         return;
341     }
343     @synchronized (sys->cgLayer) {
344         if (!OpenglLock(sys->gl)) {
345             vout_display_opengl_Prepare(sys->vgl, pic, subpicture);
346             OpenglUnlock(sys->gl);
347         }
348     }
351 static void PictureDisplay (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
353     vout_display_sys_t *sys = vd->sys;
355     @synchronized (sys->cgLayer) {
356         sys->b_frame_available = YES;
358         /* Calling display on the non-main thread is not officially supported, but
359          * its suggested at several places and works fine here. Flush is thread-safe
360          * and makes sure the picture is actually displayed. */
361         [sys->cgLayer display];
362         [CATransaction flush];
363     }
365     picture_Release(pic);
367     if (subpicture)
368         subpicture_Delete(subpicture);
371 static int Control (vout_display_t *vd, int query, va_list ap)
373     vout_display_sys_t *sys = vd->sys;
375     if (!vd->sys)
376         return VLC_EGENERIC;
378     switch (query)
379     {
380         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
381         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
382         case VOUT_DISPLAY_CHANGE_ZOOM:
383         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
384         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
385         {
386             const vout_display_cfg_t *cfg;
387             const video_format_t *source;
389             if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
390                 source = (const video_format_t *)va_arg (ap, const video_format_t *);
391                 cfg = vd->cfg;
392             } else {
393                 source = &vd->source;
394                 cfg = (const vout_display_cfg_t*)va_arg (ap, const vout_display_cfg_t *);
395             }
397             /* we always use our current frame here */
398             vout_display_cfg_t cfg_tmp = *cfg;
399             [CATransaction lock];
400             CGRect bounds = [sys->cgLayer visibleRect];
401             [CATransaction unlock];
402             cfg_tmp.display.width = bounds.size.width;
403             cfg_tmp.display.height = bounds.size.height;
405             vout_display_place_t place;
406             vout_display_PlacePicture (&place, source, &cfg_tmp, false);
407             if (OpenglLock(sys->gl))
408                 return VLC_EGENERIC;
410             vout_display_opengl_SetWindowAspectRatio(sys->vgl, (float)place.width / place.height);
411             OpenglUnlock(sys->gl);
413             sys->place = place;
415             return VLC_SUCCESS;
416         }
418         case VOUT_DISPLAY_HIDE_MOUSE:
419         {
420             [NSCursor setHiddenUntilMouseMoves: YES];
421             return VLC_SUCCESS;
422         }
424         case VOUT_DISPLAY_CHANGE_VIEWPOINT:
425         {
426             int ret;
428             if (OpenglLock(sys->gl))
429                 return VLC_EGENERIC;
431             ret = vout_display_opengl_SetViewpoint(sys->vgl,
432                 &va_arg (ap, const vout_display_cfg_t* )->viewpoint);
433             OpenglUnlock(sys->gl);
434             return ret;
435         }
437         case VOUT_DISPLAY_RESET_PICTURES:
438             vlc_assert_unreachable ();
439         default:
440             msg_Err (vd, "Unhandled request %d", query);
441         case VOUT_DISPLAY_CHANGE_FULLSCREEN:
442             return VLC_EGENERIC;
443     }
445     return VLC_SUCCESS;
448 #pragma mark -
449 #pragma mark OpenGL callbacks
451 static int OpenglLock (vlc_gl_t *gl)
453     vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys;
455     if(!sys->glContext) {
456         return 1;
457     }
459     CGLError err = CGLLockContext(sys->glContext);
460     if (kCGLNoError == err) {
461         CGLSetCurrentContext(sys->glContext);
462         return 0;
463     }
464     return 1;
467 static void OpenglUnlock (vlc_gl_t *gl)
469     vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys;
471     if (!sys->glContext) {
472         return;
473     }
475     CGLUnlockContext(sys->glContext);
478 static void OpenglSwap (vlc_gl_t *gl)
480     glFlush();
483 static void *OurGetProcAddress (vlc_gl_t *gl, const char *name)
485     VLC_UNUSED(gl);
487     return dlsym(RTLD_DEFAULT, name);
490 #pragma mark -
491 #pragma mark CA layer
493 /*****************************************************************************
494  * @implementation VLCCAOpenGLLayer
495  *****************************************************************************/
496 @implementation VLCCAOpenGLLayer
498 - (id)init {
500     self = [super init];
501     if (self) {
502         [CATransaction lock];
503         self.needsDisplayOnBoundsChange = YES;
504         self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
505         self.asynchronous = NO;
506         [CATransaction unlock];
507     }
509     return self;
512 - (void)setVoutDisplay:(vout_display_t *)aVd
514     _voutDisplay = aVd;
517 - (void)resizeWithOldSuperlayerSize:(CGSize)size
519     [super resizeWithOldSuperlayerSize: size];
521     CGSize boundsSize = self.visibleRect.size;
523     if (_voutDisplay)
524         vout_display_SendEventDisplaySize(_voutDisplay, boundsSize.width, boundsSize.height);
527 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
529     /* Only draw the frame if we have a frame that was previously rendered */
530     if (!_voutDisplay)
531         return false;
533     return _voutDisplay->sys->b_frame_available;
536 - (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
538     if (!_voutDisplay)
539         return;
540     vout_display_sys_t *sys = _voutDisplay->sys;
542     if (!sys->vgl)
543         return;
545     CGRect bounds = [self visibleRect];
547     // x / y are top left corner, but we need the lower left one
548     glViewport (sys->place.x, bounds.size.height - (sys->place.y + sys->place.height), sys->place.width, sys->place.height);
550     // flush is also done by this method, no need to call super
551     vout_display_opengl_Display (sys->vgl, &_voutDisplay->source);
552     sys->b_frame_available = NO;
555 -(CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
557     // The default is fine for this demonstration.
558     return [super copyCGLPixelFormatForDisplayMask:mask];
561 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat
563     // Only one opengl context is allowed for the module lifetime
564     if(_voutDisplay->sys->glContext) {
565         msg_Dbg(_voutDisplay, "Return existing context: %p", _voutDisplay->sys->glContext);
566         return _voutDisplay->sys->glContext;
567     }
569     CGLContextObj context = [super copyCGLContextForPixelFormat:pixelFormat];
571     // Swap buffers only during the vertical retrace of the monitor.
572     //http://developer.apple.com/documentation/GraphicsImaging/
573     //Conceptual/OpenGL/chap5/chapter_5_section_44.html /
575     GLint params = 1;
576     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
577                      &params );
579     @synchronized (self) {
580         _voutDisplay->sys->glContext = context;
581     }
583     return context;
586 - (void)releaseCGLContext:(CGLContextObj)glContext
588     // do not release anything here, we do that when closing the module
591 - (void)mouseButtonDown:(int)buttonNumber
593     @synchronized (self) {
594         if (_voutDisplay) {
595             if (buttonNumber == 0)
596                 vout_display_SendEventMousePressed (_voutDisplay, MOUSE_BUTTON_LEFT);
597             else if (buttonNumber == 1)
598                 vout_display_SendEventMousePressed (_voutDisplay, MOUSE_BUTTON_RIGHT);
599             else
600                 vout_display_SendEventMousePressed (_voutDisplay, MOUSE_BUTTON_CENTER);
601         }
602     }
605 - (void)mouseButtonUp:(int)buttonNumber
607     @synchronized (self) {
608         if (_voutDisplay) {
609             if (buttonNumber == 0)
610                 vout_display_SendEventMouseReleased (_voutDisplay, MOUSE_BUTTON_LEFT);
611             else if (buttonNumber == 1)
612                 vout_display_SendEventMouseReleased (_voutDisplay, MOUSE_BUTTON_RIGHT);
613             else
614                 vout_display_SendEventMouseReleased (_voutDisplay, MOUSE_BUTTON_CENTER);
615         }
616     }
619 - (void)mouseMovedToX:(double)xValue Y:(double)yValue
621     @synchronized (self) {
622         if (_voutDisplay) {
623             vout_display_SendMouseMovedDisplayCoordinates (_voutDisplay,
624                                                            ORIENT_NORMAL,
625                                                            xValue,
626                                                            yValue,
627                                                            &_voutDisplay->sys->place);
628         }
629     }
632 @end