contrib: theora: do not run autoreconf
[vlc/gmpfix.git] / modules / video_output / caopengllayer.m
blob9ee27b97c130a686cc9c7b04f8a503dcb3e169ed
1 /*****************************************************************************
2  * caopengllayer.m: CAOpenGLLayer (Mac OS X) video output
3  *****************************************************************************
4  * Copyright (C) 2014 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.h"
46 /*****************************************************************************
47  * Vout interface
48  *****************************************************************************/
49 static int  Open   (vlc_object_t *);
50 static void Close  (vlc_object_t *);
52 vlc_module_begin()
53     set_description(N_("Core Animation OpenGL Layer (Mac OS X)"))
54     set_capability("vout display", 0)
55     set_category(CAT_VIDEO)
56     set_subcategory(SUBCAT_VIDEO_VOUT)
57     set_callbacks(Open, Close)
58 vlc_module_end()
61 static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count);
62 static void PictureRender   (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture);
63 static void PictureDisplay  (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture);
64 static int Control          (vout_display_t *vd, int query, va_list ap);
66 static void *OurGetProcAddress (vlc_gl_t *gl, const char *name);
67 static int OpenglLock         (vlc_gl_t *gl);
68 static void OpenglUnlock       (vlc_gl_t *gl);
69 static void OpenglSwap         (vlc_gl_t *gl);
71 @protocol VLCCoreAnimationVideoLayerEmbedding <NSObject>
72 - (void)addVoutLayer:(CALayer *)aLayer;
73 - (void)removeVoutLayer:(CALayer *)aLayer;
74 - (CGSize)currentOutputSize;
75 @end
77 @interface VLCCAOpenGLLayer : CAOpenGLLayer {
78     vout_display_t *_vd;
81 - (void)setVoutDisplay:(vout_display_t *)aVd;
82 @end
85 struct vout_display_sys_t {
87     picture_pool_t *pool;
88     picture_resource_t resource;
90     CALayer <VLCCoreAnimationVideoLayerEmbedding> *container;
91     vout_window_t *embed;
92     VLCCAOpenGLLayer *cgLayer;
94     CGLContextObj glContext;
96     vlc_gl_t gl;
97     vout_display_opengl_t *vgl;
99     vout_display_place_t place;
101     bool  b_frame_available;
104 /*****************************************************************************
105  * Open: This function allocates and initializes the OpenGL vout method.
106  *****************************************************************************/
107 static int Open (vlc_object_t *p_this)
109     vout_display_t *vd = (vout_display_t *)p_this;
110     vout_display_sys_t *sys;
112     /* Allocate structure */
113     vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
114     if (sys == NULL)
115         return VLC_EGENERIC;
117     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
119     id container = var_CreateGetAddress(vd, "drawable-nsobject");
120     if (container)
121         vout_display_DeleteWindow(vd, NULL);
122     else {
123         vout_window_cfg_t wnd_cfg;
125         memset(&wnd_cfg, 0, sizeof(wnd_cfg));
126         wnd_cfg.type = VOUT_WINDOW_TYPE_NSOBJECT;
127         wnd_cfg.x = var_InheritInteger(vd, "video-x");
128         wnd_cfg.y = var_InheritInteger(vd, "video-y");
129         wnd_cfg.height = vd->cfg->display.height;
130         wnd_cfg.width = vd->cfg->display.width;
132         sys->embed = vout_display_NewWindow(vd, &wnd_cfg);
133         if (sys->embed)
134             container = sys->embed->handle.nsobject;
136         if (!container) {
137             msg_Err(vd, "No drawable-nsobject found!");
138             goto bailout;
139         }
140     }
142     /* store for later, released in Close() */
143     sys->container = [container retain];
145     [CATransaction begin];
146     sys->cgLayer = [[VLCCAOpenGLLayer alloc] init];
147     [sys->cgLayer setVoutDisplay:vd];
149     [sys->cgLayer performSelectorOnMainThread:@selector(display) withObject:nil 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:)] || [container isKindOfClass:[CALayer class]]) {
155         msg_Dbg(vd, "container doesn't implement implicit protocol, fallback mode used");
156         [container addSublayer:sys->cgLayer];
157     } else {
158         msg_Err(vd, "Provided NSObject container isn't compatible");
159         [sys->cgLayer release];
160         sys->cgLayer = nil;
161         [CATransaction commit];
162         goto bailout;
163     }
164     [CATransaction commit];
166     if (!sys->cgLayer)
167         goto bailout;
169     if (!sys->glContext)
170         msg_Warn(vd, "we might not have an OpenGL context yet");
172     /* Initialize common OpenGL video display */
173     sys->gl.lock = OpenglLock;
174     sys->gl.unlock = OpenglUnlock;
175     sys->gl.swap = OpenglSwap;
176     sys->gl.getProcAddress = OurGetProcAddress;
177     sys->gl.sys = sys;
179     const vlc_fourcc_t *subpicture_chromas;
180     video_format_t fmt = vd->fmt;
181     sys->vgl = vout_display_opengl_New(&vd->fmt, &subpicture_chromas, &sys->gl);
182     if (!sys->vgl) {
183         msg_Err(vd, "Error while initializing opengl display.");
184         sys->gl.sys = NULL;
185         goto bailout;
186     }
188     /* setup vout display */
189     vout_display_info_t info = vd->info;
190     info.subpicture_chromas = subpicture_chromas;
191     info.has_hide_mouse = true;
192     vd->info = info;
194     vd->pool    = Pool;
195     vd->prepare = PictureRender;
196     vd->display = PictureDisplay;
197     vd->control = Control;
199     /* setup initial state */
200     CGSize outputSize;
201     if ([container respondsToSelector:@selector(currentOutputSize)])
202         outputSize = [container currentOutputSize];
203     else
204         outputSize = [sys->container visibleRect].size;
205     vout_display_SendEventFullscreen(vd, false);
206     vout_display_SendEventDisplaySize(vd, (int)outputSize.width, (int)outputSize.height, false);
208     [pool release];
209     return VLC_SUCCESS;
211 bailout:
212     [pool release];
213     Close(p_this);
214     return VLC_EGENERIC;
217 static void Close (vlc_object_t *p_this)
219     vout_display_t *vd = (vout_display_t *)p_this;
220     vout_display_sys_t *sys = vd->sys;
222     if (sys->cgLayer) {
223         if ([sys->container respondsToSelector:@selector(removeVoutLayer:)])
224             [sys->container removeVoutLayer:sys->cgLayer];
225         else
226             [sys->cgLayer removeFromSuperlayer];
227         [sys->cgLayer release];
228     }
230     if (sys->container)
231         [sys->container release];
233     if (sys->embed)
234         vout_display_DeleteWindow(vd, sys->embed);
236     if (sys->gl.sys != NULL)
237         vout_display_opengl_Delete(sys->vgl);
239     if (sys->glContext)
240         CGLReleaseContext(sys->glContext);
242     free(sys);
245 static picture_pool_t *Pool (vout_display_t *vd, unsigned count)
247     vout_display_sys_t *sys = vd->sys;
249     if (!sys->pool)
250         sys->pool = vout_display_opengl_GetPool(sys->vgl, count);
251     assert(sys->pool);
252     return sys->pool;
255 static void PictureRender (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
257     vout_display_sys_t *sys = vd->sys;
259     if (pic == NULL) {
260         msg_Warn(vd, "invalid pic, skipping frame");
261         return;
262     }
264     @synchronized (sys->cgLayer) {
265         vout_display_opengl_Prepare(sys->vgl, pic, subpicture);
266     }
269 static void PictureDisplay (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
271     vout_display_sys_t *sys = vd->sys;
273     @synchronized (sys->cgLayer) {
274         sys->b_frame_available = YES;
276         /* Calling display on the non-main thread is not officially supported, but
277          * its suggested at several places and works fine here. Flush is thread-safe
278          * and makes sure the picture is actually displayed. */
279         [sys->cgLayer display];
280         [CATransaction flush];
281     }
283     picture_Release(pic);
285     if (subpicture)
286         subpicture_Delete(subpicture);
289 static int Control (vout_display_t *vd, int query, va_list ap)
291     vout_display_sys_t *sys = vd->sys;
293     if (!vd->sys)
294         return VLC_EGENERIC;
296     switch (query)
297     {
298         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
299         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
300         case VOUT_DISPLAY_CHANGE_ZOOM:
301         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
302         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
303         case VOUT_DISPLAY_CHANGE_FULLSCREEN:
304         {
305             const vout_display_cfg_t *cfg;
306             const video_format_t *source;
308             if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
309                 source = (const video_format_t *)va_arg (ap, const video_format_t *);
310                 cfg = vd->cfg;
311             } else {
312                 source = &vd->source;
313                 cfg = (const vout_display_cfg_t*)va_arg (ap, const vout_display_cfg_t *);
314             }
316             /* we always use our current frame here */
317             vout_display_cfg_t cfg_tmp = *cfg;
318             [CATransaction lock];
319             CGRect bounds = [sys->cgLayer bounds];
320             [CATransaction unlock];
321             cfg_tmp.display.width = bounds.size.width;
322             cfg_tmp.display.height = bounds.size.height;
324             vout_display_place_t place;
325             vout_display_PlacePicture (&place, source, &cfg_tmp, false);
326             sys->place = place;
328             return VLC_SUCCESS;
329         }
331         case VOUT_DISPLAY_HIDE_MOUSE:
332         {
333             [NSCursor setHiddenUntilMouseMoves: YES];
334             return VLC_SUCCESS;
335         }
337         case VOUT_DISPLAY_GET_OPENGL:
338         {
339             vlc_gl_t **gl = va_arg (ap, vlc_gl_t **);
340             *gl = &sys->gl;
341             return VLC_SUCCESS;
342         }
343         case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
344         {
345             return VLC_SUCCESS;
346         }
348         case VOUT_DISPLAY_RESET_PICTURES:
349             assert (0);
350         default:
351             msg_Err (vd, "Unhandled request %d", query);
352             return VLC_EGENERIC;
353     }
355     return VLC_SUCCESS;
358 #pragma mark -
359 #pragma mark OpenGL callbacks
361 static int OpenglLock (vlc_gl_t *gl)
363     vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys;
365     if(!sys->glContext) {
366         return 1;
367     }
369     CGLError err = CGLLockContext(sys->glContext);
370     if (kCGLNoError == err) {
371         CGLSetCurrentContext(sys->glContext);
372         return 0;
373     }
374     return 1;
377 static void OpenglUnlock (vlc_gl_t *gl)
379     vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys;
381     if (!sys->glContext) {
382         return;
383     }
385     CGLUnlockContext(sys->glContext);
388 static void OpenglSwap (vlc_gl_t *gl)
390     glFlush();
393 static void *OurGetProcAddress (vlc_gl_t *gl, const char *name)
395     VLC_UNUSED(gl);
397     return dlsym(RTLD_DEFAULT, name);
400 #pragma mark -
401 #pragma mark CA layer
403 /*****************************************************************************
404  * @implementation VLCCAOpenGLLayer
405  *****************************************************************************/
406 @implementation VLCCAOpenGLLayer
408 - (id)init {
410     self = [super init];
411     if (self) {
412         [CATransaction lock];
413         [self setAutoresizingMask: kCALayerWidthSizable | kCALayerHeightSizable];
414         self.asynchronous = NO;
415         [CATransaction unlock];
416     }
418     return self;
421 - (void)setVoutDisplay:(vout_display_t *)aVd
423     _vd = aVd;
426 - (void)resizeWithOldSuperlayerSize:(CGSize)size
428     [super resizeWithOldSuperlayerSize: size];
430     CGSize boundsSize = self.bounds.size;
431     if (_vd)
432         vout_display_SendEventDisplaySize(_vd, boundsSize.width, boundsSize.height, _vd->cfg->is_fullscreen);
435 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
437     /* Only draw the frame if we have a frame that was previously rendered */
438     if (!_vd)
439         return false;
441     return _vd->sys->b_frame_available;
444 - (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
446     if (!_vd)
447         return;
448     vout_display_sys_t *sys = _vd->sys;
450     if (!sys->vgl)
451         return;
453     CGRect bounds = [self bounds];
454     // x / y are top left corner, but we need the lower left one
455     glViewport (sys->place.x, bounds.size.height - (sys->place.y + sys->place.height), sys->place.width, sys->place.height);
457     // flush is also done by this method, no need to call super
458     vout_display_opengl_Display (sys->vgl, &_vd->source);
459     sys->b_frame_available = NO;
462 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat
464     // Only one opengl context is allowed for the module lifetime
465     if(_vd->sys->glContext) {
466         msg_Dbg(_vd, "Return existing context: %p", _vd->sys->glContext);
467         return _vd->sys->glContext;
468     }
470     CGLContextObj context = [super copyCGLContextForPixelFormat:pixelFormat];
472     // Swap buffers only during the vertical retrace of the monitor.
473     //http://developer.apple.com/documentation/GraphicsImaging/
474     //Conceptual/OpenGL/chap5/chapter_5_section_44.html /
476     GLint params = 1;
477     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
478                      &params );
480     @synchronized (self) {
481         _vd->sys->glContext = context;
482     }
484     return context;
487 - (void)releaseCGLContext:(CGLContextObj)glContext
489     // do not release anything here, we do that when closing the module
492 - (void)mouseButtonDown:(int)buttonNumber
494     @synchronized (self) {
495         if (_vd) {
496             if (buttonNumber == 0)
497                 vout_display_SendEventMousePressed (_vd, MOUSE_BUTTON_LEFT);
498             else if (buttonNumber == 1)
499                 vout_display_SendEventMousePressed (_vd, MOUSE_BUTTON_RIGHT);
500             else
501                 vout_display_SendEventMousePressed (_vd, MOUSE_BUTTON_CENTER);
502         }
503     }
506 - (void)mouseButtonUp:(int)buttonNumber
508     @synchronized (self) {
509         if (_vd) {
510             if (buttonNumber == 0)
511                 vout_display_SendEventMouseReleased (_vd, MOUSE_BUTTON_LEFT);
512             else if (buttonNumber == 1)
513                 vout_display_SendEventMouseReleased (_vd, MOUSE_BUTTON_RIGHT);
514             else
515                 vout_display_SendEventMouseReleased (_vd, MOUSE_BUTTON_CENTER);
516         }
517     }
520 - (void)mouseMovedToX:(double)xValue Y:(double)yValue
522     @synchronized (self) {
523         if (_vd) {
524             vout_display_SendMouseMovedDisplayCoordinates (_vd,
525                                                            ORIENT_NORMAL,
526                                                            xValue,
527                                                            yValue,
528                                                            &_vd->sys->place);
529         }
530     }
533 @end