d3d11: handle VLC_CODEC_D3D11_OPAQUE_10B upload/download
[vlc.git] / modules / video_output / ios.m
blob1ea2ee45aae294ef4359791e2d2f3added7fe5e3
1 /*****************************************************************************
2  * ios.m: iOS OpenGL ES provider
3  *****************************************************************************
4  * Copyright (C) 2001-2017 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Pierre d'Herbemont <pdherbemont at videolan dot org>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
9  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
10  *          Rémi Denis-Courmont
11  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
12  *          Eric Petit <titer@m0k.org>
13  *
14  * This program is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU Lesser General Public License as published by
16  * the Free Software Foundation; either version 2.1 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with this program; if not, write to the Free Software Foundation,
26  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
33 #import <UIKit/UIKit.h>
34 #import <OpenGLES/EAGL.h>
35 #import <OpenGLES/ES2/gl.h>
36 #import <OpenGLES/ES2/glext.h>
37 #import <QuartzCore/QuartzCore.h>
38 #import <dlfcn.h>
40 #ifdef HAVE_CONFIG_H
41 # import "config.h"
42 #endif
44 #import <vlc_common.h>
45 #import <vlc_plugin.h>
46 #import <vlc_vout_display.h>
47 #import <vlc_opengl.h>
48 #import <vlc_dialog.h>
49 #import "opengl/vout_helper.h"
51 /**
52  * Forward declarations
53  */
55 struct picture_sys_t {
56     CVPixelBufferRef pixelBuffer;
59 static int Open(vlc_object_t *);
60 static void Close(vlc_object_t *);
62 static picture_pool_t* PicturePool(vout_display_t *, unsigned);
63 static void PictureRender(vout_display_t *, picture_t *, subpicture_t *);
64 static void PictureDisplay(vout_display_t *, picture_t *, subpicture_t *);
65 static int Control(vout_display_t*, int, va_list);
67 static void *OurGetProcAddress(vlc_gl_t *, const char *);
69 static int GLESMakeCurrent(vlc_gl_t *);
70 static void GLESSwap(vlc_gl_t *);
71 static void GLESReleaseCurrent(vlc_gl_t *);
73 /**
74  * Module declaration
75  */
76 vlc_module_begin ()
77     set_shortname("iOS vout")
78     set_description("iOS OpenGL video output")
79     set_category(CAT_VIDEO)
80     set_subcategory(SUBCAT_VIDEO_VOUT)
81     set_capability("vout display", 300)
82     set_callbacks(Open, Close)
84     add_shortcut("vout_ios2", "vout_ios")
85     add_glopts()
86 vlc_module_end ()
88 @interface VLCOpenGLES2VideoView : UIView {
89     vout_display_t *_voutDisplay;
90     EAGLContext *_eaglContext;
91     CAEAGLLayer *_layer;
93     vlc_mutex_t _mutex;
94     vlc_cond_t  _gl_attached_wait;
95     BOOL        _gl_attached;
97     BOOL _bufferNeedReset;
98     BOOL _appActive;
99     BOOL _eaglEnabled;
100     BOOL _placeInvalidated;
102     UIView *_viewContainer;
103     UITapGestureRecognizer *_tapRecognizer;
105     /* Written from MT, read locked from vout */
106     vout_display_place_t _place;
107     CGSize _viewSize;
108     CGFloat _scaleFactor;
110     /* Written from vout, read locked from MT */
111     vout_display_cfg_t _cfg;
114 - (id)initWithFrameAndVd:(CGRect)frame withVd:(vout_display_t*)vd;
115 - (void)cleanAndRelease:(BOOL)flushed;
116 - (BOOL)makeCurrentWithGL:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl;
117 - (void)releaseCurrent:(EAGLContext *)previousEaglContext;
118 - (void)presentRenderbuffer;
120 - (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl;
121 - (void)getPlaceLocked:(vout_display_place_t *)place;
122 @end
124 struct vout_display_sys_t
126     VLCOpenGLES2VideoView *glESView;
128     vlc_gl_t *gl;
130     picture_pool_t *picturePool;
133 struct gl_sys
135     VLCOpenGLES2VideoView *glESView;
136     vout_display_opengl_t *vgl;
137     GLuint renderBuffer;
138     GLuint frameBuffer;
139     EAGLContext *previousEaglContext;
142 static void *OurGetProcAddress(vlc_gl_t *gl, const char *name)
144     VLC_UNUSED(gl);
146     return dlsym(RTLD_DEFAULT, name);
149 static int Open(vlc_object_t *this)
151     vout_display_t *vd = (vout_display_t *)this;
153     if (vout_display_IsWindowed(vd))
154         return VLC_EGENERIC;
156     vout_display_sys_t *sys = vlc_obj_calloc (this, 1, sizeof(*sys));
158     if (!sys)
159         return VLC_ENOMEM;
161     vd->sys = sys;
162     sys->picturePool = NULL;
163     sys->gl = NULL;
165     var_Create(vd->obj.parent, "ios-eaglcontext", VLC_VAR_ADDRESS);
167     @autoreleasepool {
168         /* setup the actual OpenGL ES view */
170         [VLCOpenGLES2VideoView performSelectorOnMainThread:@selector(getNewView:)
171                                                 withObject:[NSArray arrayWithObjects:
172                                                            [NSValue valueWithPointer:&sys->glESView],
173                                                            [NSValue valueWithPointer:vd], nil]
174                                              waitUntilDone:YES];
175         if (!sys->glESView) {
176             msg_Err(vd, "Creating OpenGL ES 2 view failed");
177             var_Destroy(vd->obj.parent, "ios-eaglcontext");
178             return VLC_EGENERIC;
179         }
181         const vlc_fourcc_t *subpicture_chromas;
182         video_format_t fmt = vd->fmt;
184         sys->gl = vlc_object_create(this, sizeof(*sys->gl));
185         if (!sys->gl)
186             goto bailout;
188         struct gl_sys *glsys = sys->gl->sys =
189             vlc_obj_malloc(this, sizeof(struct gl_sys));
190         if (unlikely(!sys->gl->sys))
191             goto bailout;
192         glsys->glESView = sys->glESView;
193         glsys->vgl = NULL;
194         glsys->renderBuffer = glsys->frameBuffer = 0;
196         /* Initialize common OpenGL video display */
197         sys->gl->makeCurrent = GLESMakeCurrent;
198         sys->gl->releaseCurrent = GLESReleaseCurrent;
199         sys->gl->swap = GLESSwap;
200         sys->gl->getProcAddress = OurGetProcAddress;
202         if (vlc_gl_MakeCurrent(sys->gl) != VLC_SUCCESS)
203             goto bailout;
205         vout_display_opengl_t *vgl = vout_display_opengl_New(&vd->fmt, &subpicture_chromas,
206                                                              sys->gl, &vd->cfg->viewpoint);
207         vlc_gl_ReleaseCurrent(sys->gl);
208         if (!vgl)
209             goto bailout;
210         glsys->vgl = vgl;
212         /* */
213         vout_display_info_t info = vd->info;
214         info.has_pictures_invalid = false;
215         info.subpicture_chromas = subpicture_chromas;
217         /* Setup vout_display_t once everything is fine */
218         vd->info = info;
220         vd->pool = PicturePool;
221         vd->prepare = PictureRender;
222         vd->display = PictureDisplay;
223         vd->control = Control;
225         return VLC_SUCCESS;
227     bailout:
228         Close(this);
229         return VLC_EGENERIC;
230     }
233 static void Close (vlc_object_t *this)
235     vout_display_t *vd = (vout_display_t *)this;
236     vout_display_sys_t *sys = vd->sys;
238     @autoreleasepool {
239         BOOL flushed = NO;
240         if (sys->gl != NULL) {
241             struct gl_sys *glsys = sys->gl->sys;
242             msg_Dbg(this, "deleting display");
244             if (likely(glsys->vgl))
245             {
246                 int ret = vlc_gl_MakeCurrent(sys->gl);
247                 vout_display_opengl_Delete(glsys->vgl);
248                 if (ret == VLC_SUCCESS)
249                 {
250                     vlc_gl_ReleaseCurrent(sys->gl);
251                     flushed = YES;
252                 }
253             }
254             vlc_object_release(sys->gl);
255         }
257         [sys->glESView cleanAndRelease:flushed];
258     }
259     var_Destroy(vd->obj.parent, "ios-eaglcontext");
262 /*****************************************************************************
263  * vout display callbacks
264  *****************************************************************************/
266 static int Control(vout_display_t *vd, int query, va_list ap)
268     vout_display_sys_t *sys = vd->sys;
269     struct gl_sys *glsys = sys->gl->sys;
271     switch (query) {
272         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
273         case VOUT_DISPLAY_CHANGE_ZOOM:
274         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
275         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
276         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
277         {
278             const vout_display_cfg_t *cfg;
280             if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT ||
281                 query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
282                 cfg = vd->cfg;
283             else
284                 cfg = (const vout_display_cfg_t*)va_arg(ap, const vout_display_cfg_t *);
286             assert(cfg);
288             [sys->glESView updateVoutCfg:cfg withVGL:glsys->vgl];
290             return VLC_SUCCESS;
291         }
293         case VOUT_DISPLAY_CHANGE_VIEWPOINT:
294             return vout_display_opengl_SetViewpoint(glsys->vgl,
295                 &va_arg (ap, const vout_display_cfg_t* )->viewpoint);
297         case VOUT_DISPLAY_RESET_PICTURES:
298             vlc_assert_unreachable ();
299         default:
300             msg_Err(vd, "Unknown request %d", query);
301             return VLC_EGENERIC;
302     }
305 static void PictureDisplay(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
307     vout_display_sys_t *sys = vd->sys;
308     struct gl_sys *glsys = sys->gl->sys;
310     if (vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
311     {
312         vout_display_opengl_Display(glsys->vgl, &vd->source);
313         vlc_gl_ReleaseCurrent(sys->gl);
314     }
316     picture_Release(pic);
318     if (subpicture)
319         subpicture_Delete(subpicture);
322 static void PictureRender(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
324     vout_display_sys_t *sys = vd->sys;
325     struct gl_sys *glsys = sys->gl->sys;
327     if (vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
328     {
329         vout_display_opengl_Prepare(glsys->vgl, pic, subpicture);
330         vlc_gl_ReleaseCurrent(sys->gl);
331     }
334 static picture_pool_t *PicturePool(vout_display_t *vd, unsigned requested_count)
336     vout_display_sys_t *sys = vd->sys;
337     struct gl_sys *glsys = sys->gl->sys;
339     if (!sys->picturePool && vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
340     {
341         sys->picturePool = vout_display_opengl_GetPool(glsys->vgl, requested_count);
342         vlc_gl_ReleaseCurrent(sys->gl);
343     }
344     return sys->picturePool;
347 /*****************************************************************************
348  * vout opengl callbacks
349  *****************************************************************************/
350 static int GLESMakeCurrent(vlc_gl_t *gl)
352     struct gl_sys *sys = gl->sys;
354     if (![sys->glESView makeCurrentWithGL:&sys->previousEaglContext withGL:gl])
355         return VLC_EGENERIC;
356     return VLC_SUCCESS;
359 static void GLESReleaseCurrent(vlc_gl_t *gl)
361     struct gl_sys *sys = gl->sys;
363     [sys->glESView releaseCurrent:sys->previousEaglContext];
366 static void GLESSwap(vlc_gl_t *gl)
368     struct gl_sys *sys = gl->sys;
370     [sys->glESView presentRenderbuffer];
374 /*****************************************************************************
375  * Our UIView object
376  *****************************************************************************/
377 @implementation VLCOpenGLES2VideoView
379 + (Class)layerClass
381     return [CAEAGLLayer class];
384 + (void)getNewView:(NSArray *)value
386     id *ret = [[value objectAtIndex:0] pointerValue];
387     vout_display_t *vd = [[value objectAtIndex:1] pointerValue];
388     *ret = [[self alloc] initWithFrameAndVd:CGRectMake(0.,0.,320.,240.) withVd:vd];
391 - (id)initWithFrameAndVd:(CGRect)frame withVd:(vout_display_t*)vd
393     _appActive = ([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
394     if (unlikely(!_appActive))
395         return nil;
397     self = [super initWithFrame:frame];
398     if (!self)
399         return nil;
401     _eaglEnabled = YES;
402     _bufferNeedReset = YES;
403     _voutDisplay = vd;
404     _cfg = *_voutDisplay->cfg;
406     vlc_mutex_init(&_mutex);
407     vlc_cond_init(&_gl_attached_wait);
408     _gl_attached = YES;
410     /* the following creates a new OpenGL ES context with the API version we
411      * need if there is already an active context created by another OpenGL
412      * provider we cache it and restore analog to the
413      * makeCurrent/releaseCurrent pattern used through-out the class */
414     EAGLContext *previousEaglContext = [EAGLContext currentContext];
416     _eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
418     if (unlikely(!_eaglContext)
419      || unlikely(![EAGLContext setCurrentContext:_eaglContext]))
420     {
421         if (_eaglContext)
422             [_eaglContext release];
423         vlc_mutex_destroy(&_mutex);
424         vlc_cond_destroy(&_gl_attached_wait);
425         [super dealloc];
426         return nil;
427     }
428     [self releaseCurrent:previousEaglContext];
430     /* Set "ios-eaglcontext" to be used by cvpx fitlers/glconv */
431     var_SetAddress(_voutDisplay->obj.parent, "ios-eaglcontext", _eaglContext);
433     _layer = (CAEAGLLayer *)self.layer;
434     _layer.drawableProperties = [NSDictionary dictionaryWithObject:kEAGLColorFormatRGBA8 forKey: kEAGLDrawablePropertyColorFormat];
435     _layer.opaque = YES;
437     self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
439     if (![self fetchViewContainer])
440     {
441         vlc_mutex_destroy(&_mutex);
442         vlc_cond_destroy(&_gl_attached_wait);
443         [_eaglContext release];
444         [super dealloc];
445         return nil;
446     }
448     /* */
449     [[NSNotificationCenter defaultCenter] addObserver:self
450                                              selector:@selector(applicationStateChanged:)
451                                                  name:UIApplicationWillResignActiveNotification
452                                                object:nil];
453     [[NSNotificationCenter defaultCenter] addObserver:self
454                                              selector:@selector(applicationStateChanged:)
455                                                  name:UIApplicationDidBecomeActiveNotification
456                                                object:nil];
457     [[NSNotificationCenter defaultCenter] addObserver:self
458                                              selector:@selector(applicationStateChanged:)
459                                                  name:UIApplicationDidEnterBackgroundNotification
460                                                object:nil];
461     [[NSNotificationCenter defaultCenter] addObserver:self
462                                              selector:@selector(applicationStateChanged:)
463                                                  name:UIApplicationWillEnterForegroundNotification
464                                                object:nil];
466     return self;
469 - (BOOL)fetchViewContainer
471     @try {
472         /* get the object we will draw into */
473         UIView *viewContainer = var_InheritAddress (_voutDisplay, "drawable-nsobject");
474         if (unlikely(viewContainer == nil)) {
475             msg_Err(_voutDisplay, "provided view container is nil");
476             return NO;
477         }
479         if (unlikely(![viewContainer respondsToSelector:@selector(isKindOfClass:)])) {
480             msg_Err(_voutDisplay, "void pointer not an ObjC object");
481             return NO;
482         }
484         [viewContainer retain];
486         if (![viewContainer isKindOfClass:[UIView class]]) {
487             msg_Err(_voutDisplay, "passed ObjC object not of class UIView");
488             return NO;
489         }
491         /* This will be released in Close(), on
492          * main thread, after we are done using it. */
493         _viewContainer = viewContainer;
495         self.frame = viewContainer.bounds;
496         [self reshape];
498         [_viewContainer addSubview:self];
500         /* add tap gesture recognizer for DVD menus and stuff */
501         _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
502                                                                  action:@selector(tapRecognized:)];
503         if (_viewContainer.window
504          && _viewContainer.window.rootViewController
505          && _viewContainer.window.rootViewController.view)
506             [_viewContainer.superview addGestureRecognizer:_tapRecognizer];
507         _tapRecognizer.cancelsTouchesInView = NO;
508         return YES;
509     } @catch (NSException *exception) {
510         msg_Err(_voutDisplay, "Handling the view container failed due to an Obj-C exception (%s, %s", [exception.name UTF8String], [exception.reason UTF8String]);
511         vout_display_sys_t *sys = _voutDisplay->sys;
512         if (_tapRecognizer)
513             [_tapRecognizer release];
514         return NO;
515     }
518 - (void)cleanAndReleaseFromMainThread
520     [[NSNotificationCenter defaultCenter] removeObserver:self];
522     [_tapRecognizer.view removeGestureRecognizer:_tapRecognizer];
523     [_tapRecognizer release];
525     [self removeFromSuperview];
526     [_viewContainer release];
528     assert(!_gl_attached);
529     [_eaglContext release];
530     [self release];
533 - (void)cleanAndRelease:(BOOL)flushed
535     vlc_mutex_lock(&_mutex);
536     if (_eaglEnabled && !flushed)
537         [self flushEAGLLocked];
538     _voutDisplay = nil;
539     _eaglEnabled = NO;
540     vlc_mutex_unlock(&_mutex);
542     [self performSelectorOnMainThread:@selector(cleanAndReleaseFromMainThread)
543                            withObject:nil
544                         waitUntilDone:NO];
547 - (void)dealloc
549     vlc_mutex_destroy(&_mutex);
550     vlc_cond_destroy(&_gl_attached_wait);
551     [super dealloc];
554 - (void)didMoveToWindow
556     self.contentScaleFactor = self.window.screen.scale;
558     vlc_mutex_lock(&_mutex);
559     _bufferNeedReset = YES;
560     vlc_mutex_unlock(&_mutex);
563 - (BOOL)doResetBuffers:(vlc_gl_t *)gl
565     struct gl_sys *glsys = gl->sys;
567     if (glsys->frameBuffer != 0)
568     {
569         /* clear frame buffer */
570         glDeleteFramebuffers(1, &glsys->frameBuffer);
571         glsys->frameBuffer = 0;
572     }
574     if (glsys->renderBuffer != 0)
575     {
576         /* clear render buffer */
577         glDeleteRenderbuffers(1, &glsys->renderBuffer);
578         glsys->renderBuffer = 0;
579     }
581     glDisable(GL_DEPTH_TEST);
583     glGenFramebuffers(1, &glsys->frameBuffer);
584     glBindFramebuffer(GL_FRAMEBUFFER, glsys->frameBuffer);
586     glGenRenderbuffers(1, &glsys->renderBuffer);
587     glBindRenderbuffer(GL_RENDERBUFFER, glsys->renderBuffer);
589     [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_layer];
591     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, glsys->renderBuffer);
592     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
593     {
594         msg_Err(_voutDisplay, "Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
595         return NO;
596     }
597     return YES;
600 - (BOOL)makeCurrentWithGL:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl
602     vlc_mutex_lock(&_mutex);
603     assert(!_gl_attached);
605     if (unlikely(!_appActive))
606     {
607         vlc_mutex_unlock(&_mutex);
608         return NO;
609     }
610     assert(_eaglEnabled);
612     *previousEaglContext = [EAGLContext currentContext];
614     BOOL success = [EAGLContext setCurrentContext:_eaglContext];
615     BOOL resetBuffers = NO;
617     if (success && gl != NULL)
618     {
619         struct gl_sys *glsys = gl->sys;
621         if (unlikely(_bufferNeedReset))
622         {
623             _bufferNeedReset = NO;
624             resetBuffers = YES;
625         }
626         if (unlikely(_placeInvalidated && glsys->vgl))
627         {
628             _placeInvalidated = NO;
630             vout_display_place_t place;
631             [self getPlaceLocked: &place];
632             vout_display_opengl_SetWindowAspectRatio(glsys->vgl, (float)place.width / place.height);
634             // x / y are top left corner, but we need the lower left one
635             vout_display_opengl_Viewport(glsys->vgl, _place.x, _place.y, _place.width, _place.height);
636         }
637     }
639     if (success)
640         _gl_attached = YES;
642     vlc_mutex_unlock(&_mutex);
644     if (resetBuffers && ![self doResetBuffers:gl])
645     {
646         [self releaseCurrent:*previousEaglContext];
647         return NO;
648     }
649     return success;
652 - (void)releaseCurrent:(EAGLContext *)previousEaglContext
654     [EAGLContext setCurrentContext:previousEaglContext];
656     vlc_mutex_lock(&_mutex);
657     assert(_gl_attached);
658     _gl_attached = NO;
659     vlc_mutex_unlock(&_mutex);
660     vlc_cond_signal(&_gl_attached_wait);
663 - (void)presentRenderbuffer
665     [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
668 - (void)layoutSubviews
670     [self reshape];
672     vlc_mutex_lock(&_mutex);
673     _bufferNeedReset = YES;
674     vlc_mutex_unlock(&_mutex);
677 - (void)getPlaceLocked:(vout_display_place_t *)place
679     assert(_voutDisplay);
680     vout_display_cfg_t cfg = _cfg;
682     cfg.display.width  = _viewSize.width * _scaleFactor;
683     cfg.display.height = _viewSize.height * _scaleFactor;
685     vout_display_PlacePicture(place, &_voutDisplay->source, &cfg, false);
688 - (void)reshape
690     assert([NSThread isMainThread]);
692     vlc_mutex_lock(&_mutex);
693     if (!_voutDisplay)
694     {
695         vlc_mutex_unlock(&_mutex);
696         return;
697     }
698     _viewSize = [self bounds].size;
699     _scaleFactor = self.contentScaleFactor;
701     vout_display_place_t place;
702     [self getPlaceLocked: &place];
704     if (memcmp(&place, &_place, sizeof(vout_display_place_t)) != 0)
705     {
706         _placeInvalidated = YES;
707         _place = place;
708     }
710     vout_display_SendEventDisplaySize(_voutDisplay, _viewSize.width * _scaleFactor,
711                                       _viewSize.height * _scaleFactor);
713     vlc_mutex_unlock(&_mutex);
716 - (void)tapRecognized:(UITapGestureRecognizer *)tapRecognizer
718     vlc_mutex_lock(&_mutex);
719     if (!_voutDisplay)
720     {
721         vlc_mutex_unlock(&_mutex);
722         return;
723     }
725     UIGestureRecognizerState state = [tapRecognizer state];
726     CGPoint touchPoint = [tapRecognizer locationInView:self];
727     CGFloat scaleFactor = self.contentScaleFactor;
728     vout_display_SendMouseMovedDisplayCoordinates(_voutDisplay, ORIENT_NORMAL,
729                                                   (int)touchPoint.x * scaleFactor, (int)touchPoint.y * scaleFactor,
730                                                   &_place);
732     vout_display_SendEventMousePressed(_voutDisplay, MOUSE_BUTTON_LEFT);
733     vout_display_SendEventMouseReleased(_voutDisplay, MOUSE_BUTTON_LEFT);
735     vlc_mutex_unlock(&_mutex);
738 - (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl
740     if (memcmp(&_cfg, cfg, sizeof(vout_display_cfg_t)) == 0)
741         return;
743     vlc_mutex_lock(&_mutex);
744     _cfg = *cfg;
746     vout_display_place_t place;
747     [self getPlaceLocked: &place];
748     vout_display_opengl_SetWindowAspectRatio(vgl, (float)place.width / place.height);
750     vlc_mutex_unlock(&_mutex);
752     [self performSelectorOnMainThread:@selector(setNeedsUpdateConstraints)
753                            withObject:nil
754                         waitUntilDone:NO];
757 - (void)flushEAGLLocked
759     assert(_eaglEnabled);
761     /* Ensure that all previously submitted commands are drained from the
762      * command buffer and are executed by OpenGL ES before moving to the
763      * background.*/
764     EAGLContext *previousEaglContext = [EAGLContext currentContext];
765     if ([EAGLContext setCurrentContext:_eaglContext])
766     {
767         glFinish();
768         glFlush();
769     }
770     [EAGLContext setCurrentContext:previousEaglContext];
773 - (void)applicationStateChanged:(NSNotification *)notification
775     vlc_mutex_lock(&_mutex);
777     if ([[notification name] isEqualToString:UIApplicationWillResignActiveNotification])
778         _appActive = NO;
779     else if ([[notification name] isEqualToString:UIApplicationDidEnterBackgroundNotification])
780     {
781         _appActive = NO;
783         if (_eaglEnabled)
784         {
785             /* Wait for the vout to unlock the eagl context before releasing
786              * it. */
787             while (_gl_attached)
788                 vlc_cond_wait(&_gl_attached_wait, &_mutex);
790             [self flushEAGLLocked];
791             _eaglEnabled = NO;
792         }
793     }
794     else if ([[notification name] isEqualToString:UIApplicationWillEnterForegroundNotification])
795         _eaglEnabled = YES;
796     else
797     {
798         assert([[notification name] isEqualToString:UIApplicationDidBecomeActiveNotification]);
799         _appActive = YES;
800     }
802     vlc_mutex_unlock(&_mutex);
805 - (void)updateConstraints
807     [super updateConstraints];
808     [self reshape];
811 - (BOOL)isOpaque
813     return YES;
816 - (BOOL)acceptsFirstResponder
818     return YES;
821 @end