input: add an input_item_t arg to input_CreateFilename()
[vlc.git] / modules / video_output / ios.m
blobed20827884d3908ecaa37ce4e428fcb6cfad54de
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  */
54 static int Open(vlc_object_t *);
55 static void Close(vlc_object_t *);
57 static picture_pool_t* PicturePool(vout_display_t *, unsigned);
58 static void PictureRender(vout_display_t *, picture_t *, subpicture_t *, vlc_tick_t);
59 static void PictureDisplay(vout_display_t *, picture_t *, subpicture_t *);
60 static int Control(vout_display_t*, int, va_list);
62 static void *OurGetProcAddress(vlc_gl_t *, const char *);
64 static int GLESMakeCurrent(vlc_gl_t *);
65 static void GLESSwap(vlc_gl_t *);
66 static void GLESReleaseCurrent(vlc_gl_t *);
68 /**
69  * Module declaration
70  */
71 vlc_module_begin ()
72     set_shortname("iOS vout")
73     set_description("iOS OpenGL video output")
74     set_category(CAT_VIDEO)
75     set_subcategory(SUBCAT_VIDEO_VOUT)
76     set_capability("vout display", 300)
77     set_callbacks(Open, Close)
79     add_shortcut("vout_ios2", "vout_ios")
80     add_glopts()
81 vlc_module_end ()
83 @interface VLCOpenGLES2VideoView : UIView {
84     vout_display_t *_voutDisplay;
85     EAGLContext *_eaglContext;
86     CAEAGLLayer *_layer;
88     vlc_mutex_t _mutex;
89     vlc_cond_t  _gl_attached_wait;
90     BOOL        _gl_attached;
92     BOOL _bufferNeedReset;
93     BOOL _appActive;
94     BOOL _eaglEnabled;
95     BOOL _placeInvalidated;
97     UIView *_viewContainer;
98     UITapGestureRecognizer *_tapRecognizer;
100     /* Written from MT, read locked from vout */
101     vout_display_place_t _place;
102     CGSize _viewSize;
103     CGFloat _scaleFactor;
105     /* Written from vout, read locked from MT */
106     vout_display_cfg_t _cfg;
109 - (id)initWithFrameAndVd:(CGRect)frame withVd:(vout_display_t*)vd;
110 - (void)cleanAndRelease:(BOOL)flushed;
111 - (BOOL)makeCurrentWithGL:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl;
112 - (void)releaseCurrent:(EAGLContext *)previousEaglContext;
113 - (void)presentRenderbuffer;
115 - (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl;
116 - (void)getPlaceLocked:(vout_display_place_t *)place;
117 @end
119 struct vout_display_sys_t
121     VLCOpenGLES2VideoView *glESView;
123     vlc_gl_t *gl;
125     picture_pool_t *picturePool;
128 struct gl_sys
130     VLCOpenGLES2VideoView *glESView;
131     vout_display_opengl_t *vgl;
132     GLuint renderBuffer;
133     GLuint frameBuffer;
134     EAGLContext *previousEaglContext;
137 static void *OurGetProcAddress(vlc_gl_t *gl, const char *name)
139     VLC_UNUSED(gl);
141     return dlsym(RTLD_DEFAULT, name);
144 static int Open(vlc_object_t *this)
146     vout_display_t *vd = (vout_display_t *)this;
148     if (vout_display_IsWindowed(vd))
149         return VLC_EGENERIC;
151     vout_display_sys_t *sys = vlc_obj_calloc (this, 1, sizeof(*sys));
153     if (!sys)
154         return VLC_ENOMEM;
156     vd->sys = sys;
157     sys->picturePool = NULL;
158     sys->gl = NULL;
160     var_Create(vd->obj.parent, "ios-eaglcontext", VLC_VAR_ADDRESS);
162     @autoreleasepool {
163         /* setup the actual OpenGL ES view */
165         [VLCOpenGLES2VideoView performSelectorOnMainThread:@selector(getNewView:)
166                                                 withObject:[NSArray arrayWithObjects:
167                                                            [NSValue valueWithPointer:&sys->glESView],
168                                                            [NSValue valueWithPointer:vd], nil]
169                                              waitUntilDone:YES];
170         if (!sys->glESView) {
171             msg_Err(vd, "Creating OpenGL ES 2 view failed");
172             var_Destroy(vd->obj.parent, "ios-eaglcontext");
173             return VLC_EGENERIC;
174         }
176         const vlc_fourcc_t *subpicture_chromas;
177         video_format_t fmt = vd->fmt;
179         sys->gl = vlc_object_create(this, sizeof(*sys->gl));
180         if (!sys->gl)
181             goto bailout;
183         struct gl_sys *glsys = sys->gl->sys =
184             vlc_obj_malloc(this, sizeof(struct gl_sys));
185         if (unlikely(!sys->gl->sys))
186             goto bailout;
187         glsys->glESView = sys->glESView;
188         glsys->vgl = NULL;
189         glsys->renderBuffer = glsys->frameBuffer = 0;
191         /* Initialize common OpenGL video display */
192         sys->gl->makeCurrent = GLESMakeCurrent;
193         sys->gl->releaseCurrent = GLESReleaseCurrent;
194         sys->gl->swap = GLESSwap;
195         sys->gl->getProcAddress = OurGetProcAddress;
197         if (vlc_gl_MakeCurrent(sys->gl) != VLC_SUCCESS)
198             goto bailout;
200         vout_display_opengl_t *vgl = vout_display_opengl_New(&vd->fmt, &subpicture_chromas,
201                                                              sys->gl, &vd->cfg->viewpoint);
202         vlc_gl_ReleaseCurrent(sys->gl);
203         if (!vgl)
204             goto bailout;
205         glsys->vgl = vgl;
207         /* */
208         vout_display_info_t info = vd->info;
209         info.has_pictures_invalid = false;
210         info.subpicture_chromas = subpicture_chromas;
212         /* Setup vout_display_t once everything is fine */
213         vd->info = info;
215         vd->pool = PicturePool;
216         vd->prepare = PictureRender;
217         vd->display = PictureDisplay;
218         vd->control = Control;
220         return VLC_SUCCESS;
222     bailout:
223         Close(this);
224         return VLC_EGENERIC;
225     }
228 static void Close (vlc_object_t *this)
230     vout_display_t *vd = (vout_display_t *)this;
231     vout_display_sys_t *sys = vd->sys;
233     @autoreleasepool {
234         BOOL flushed = NO;
235         if (sys->gl != NULL) {
236             struct gl_sys *glsys = sys->gl->sys;
237             msg_Dbg(this, "deleting display");
239             if (likely(glsys->vgl))
240             {
241                 int ret = vlc_gl_MakeCurrent(sys->gl);
242                 vout_display_opengl_Delete(glsys->vgl);
243                 if (ret == VLC_SUCCESS)
244                 {
245                     vlc_gl_ReleaseCurrent(sys->gl);
246                     flushed = YES;
247                 }
248             }
249             vlc_object_release(sys->gl);
250         }
252         [sys->glESView cleanAndRelease:flushed];
253     }
254     var_Destroy(vd->obj.parent, "ios-eaglcontext");
257 /*****************************************************************************
258  * vout display callbacks
259  *****************************************************************************/
261 static int Control(vout_display_t *vd, int query, va_list ap)
263     vout_display_sys_t *sys = vd->sys;
264     struct gl_sys *glsys = sys->gl->sys;
266     switch (query) {
267         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
268         case VOUT_DISPLAY_CHANGE_ZOOM:
269         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
270         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
271         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
272         {
273             const vout_display_cfg_t *cfg;
275             if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT ||
276                 query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
277                 cfg = vd->cfg;
278             else
279                 cfg = (const vout_display_cfg_t*)va_arg(ap, const vout_display_cfg_t *);
281             assert(cfg);
283             [sys->glESView updateVoutCfg:cfg withVGL:glsys->vgl];
285             return VLC_SUCCESS;
286         }
288         case VOUT_DISPLAY_CHANGE_VIEWPOINT:
289             return vout_display_opengl_SetViewpoint(glsys->vgl,
290                 &va_arg (ap, const vout_display_cfg_t* )->viewpoint);
292         case VOUT_DISPLAY_RESET_PICTURES:
293             vlc_assert_unreachable ();
294         default:
295             msg_Err(vd, "Unknown request %d", query);
296             return VLC_EGENERIC;
297     }
300 static void PictureDisplay(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
302     vout_display_sys_t *sys = vd->sys;
303     struct gl_sys *glsys = sys->gl->sys;
305     if (vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
306     {
307         vout_display_opengl_Display(glsys->vgl, &vd->source);
308         vlc_gl_ReleaseCurrent(sys->gl);
309     }
311     picture_Release(pic);
313     if (subpicture)
314         subpicture_Delete(subpicture);
317 static void PictureRender(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture,
318                           vlc_tick_t date)
320     VLC_UNUSED(date);
321     vout_display_sys_t *sys = vd->sys;
322     struct gl_sys *glsys = sys->gl->sys;
324     if (vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
325     {
326         vout_display_opengl_Prepare(glsys->vgl, pic, subpicture);
327         vlc_gl_ReleaseCurrent(sys->gl);
328     }
331 static picture_pool_t *PicturePool(vout_display_t *vd, unsigned requested_count)
333     vout_display_sys_t *sys = vd->sys;
334     struct gl_sys *glsys = sys->gl->sys;
336     if (!sys->picturePool && vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
337     {
338         sys->picturePool = vout_display_opengl_GetPool(glsys->vgl, requested_count);
339         vlc_gl_ReleaseCurrent(sys->gl);
340     }
341     return sys->picturePool;
344 /*****************************************************************************
345  * vout opengl callbacks
346  *****************************************************************************/
347 static int GLESMakeCurrent(vlc_gl_t *gl)
349     struct gl_sys *sys = gl->sys;
351     if (![sys->glESView makeCurrentWithGL:&sys->previousEaglContext withGL:gl])
352         return VLC_EGENERIC;
353     return VLC_SUCCESS;
356 static void GLESReleaseCurrent(vlc_gl_t *gl)
358     struct gl_sys *sys = gl->sys;
360     [sys->glESView releaseCurrent:sys->previousEaglContext];
363 static void GLESSwap(vlc_gl_t *gl)
365     struct gl_sys *sys = gl->sys;
367     [sys->glESView presentRenderbuffer];
371 /*****************************************************************************
372  * Our UIView object
373  *****************************************************************************/
374 @implementation VLCOpenGLES2VideoView
376 + (Class)layerClass
378     return [CAEAGLLayer class];
381 + (void)getNewView:(NSArray *)value
383     id *ret = [[value objectAtIndex:0] pointerValue];
384     vout_display_t *vd = [[value objectAtIndex:1] pointerValue];
385     *ret = [[self alloc] initWithFrameAndVd:CGRectMake(0.,0.,320.,240.) withVd:vd];
388 - (id)initWithFrameAndVd:(CGRect)frame withVd:(vout_display_t*)vd
390     _appActive = ([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
391     if (unlikely(!_appActive))
392         return nil;
394     self = [super initWithFrame:frame];
395     if (!self)
396         return nil;
398     _eaglEnabled = YES;
399     _bufferNeedReset = YES;
400     _voutDisplay = vd;
401     _cfg = *_voutDisplay->cfg;
403     vlc_mutex_init(&_mutex);
404     vlc_cond_init(&_gl_attached_wait);
405     _gl_attached = YES;
407     /* the following creates a new OpenGL ES context with the API version we
408      * need if there is already an active context created by another OpenGL
409      * provider we cache it and restore analog to the
410      * makeCurrent/releaseCurrent pattern used through-out the class */
411     EAGLContext *previousEaglContext = [EAGLContext currentContext];
413     _eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
415     if (unlikely(!_eaglContext)
416      || unlikely(![EAGLContext setCurrentContext:_eaglContext]))
417     {
418         if (_eaglContext)
419             [_eaglContext release];
420         vlc_mutex_destroy(&_mutex);
421         vlc_cond_destroy(&_gl_attached_wait);
422         [super dealloc];
423         return nil;
424     }
425     [self releaseCurrent:previousEaglContext];
427     /* Set "ios-eaglcontext" to be used by cvpx fitlers/glconv */
428     var_SetAddress(_voutDisplay->obj.parent, "ios-eaglcontext", _eaglContext);
430     _layer = (CAEAGLLayer *)self.layer;
431     _layer.drawableProperties = [NSDictionary dictionaryWithObject:kEAGLColorFormatRGBA8 forKey: kEAGLDrawablePropertyColorFormat];
432     _layer.opaque = YES;
434     self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
436     if (![self fetchViewContainer])
437     {
438         vlc_mutex_destroy(&_mutex);
439         vlc_cond_destroy(&_gl_attached_wait);
440         [_eaglContext release];
441         [super dealloc];
442         return nil;
443     }
445     /* */
446     [[NSNotificationCenter defaultCenter] addObserver:self
447                                              selector:@selector(applicationStateChanged:)
448                                                  name:UIApplicationWillResignActiveNotification
449                                                object:nil];
450     [[NSNotificationCenter defaultCenter] addObserver:self
451                                              selector:@selector(applicationStateChanged:)
452                                                  name:UIApplicationDidBecomeActiveNotification
453                                                object:nil];
454     [[NSNotificationCenter defaultCenter] addObserver:self
455                                              selector:@selector(applicationStateChanged:)
456                                                  name:UIApplicationDidEnterBackgroundNotification
457                                                object:nil];
458     [[NSNotificationCenter defaultCenter] addObserver:self
459                                              selector:@selector(applicationStateChanged:)
460                                                  name:UIApplicationWillEnterForegroundNotification
461                                                object:nil];
463     return self;
466 - (BOOL)fetchViewContainer
468     @try {
469         /* get the object we will draw into */
470         UIView *viewContainer = var_InheritAddress (_voutDisplay, "drawable-nsobject");
471         if (unlikely(viewContainer == nil)) {
472             msg_Err(_voutDisplay, "provided view container is nil");
473             return NO;
474         }
476         if (unlikely(![viewContainer respondsToSelector:@selector(isKindOfClass:)])) {
477             msg_Err(_voutDisplay, "void pointer not an ObjC object");
478             return NO;
479         }
481         [viewContainer retain];
483         if (![viewContainer isKindOfClass:[UIView class]]) {
484             msg_Err(_voutDisplay, "passed ObjC object not of class UIView");
485             return NO;
486         }
488         /* This will be released in Close(), on
489          * main thread, after we are done using it. */
490         _viewContainer = viewContainer;
492         self.frame = viewContainer.bounds;
493         [self reshape];
495         [_viewContainer addSubview:self];
497         /* add tap gesture recognizer for DVD menus and stuff */
498         _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
499                                                                  action:@selector(tapRecognized:)];
500         if (_viewContainer.window
501          && _viewContainer.window.rootViewController
502          && _viewContainer.window.rootViewController.view)
503             [_viewContainer.superview addGestureRecognizer:_tapRecognizer];
504         _tapRecognizer.cancelsTouchesInView = NO;
505         return YES;
506     } @catch (NSException *exception) {
507         msg_Err(_voutDisplay, "Handling the view container failed due to an Obj-C exception (%s, %s", [exception.name UTF8String], [exception.reason UTF8String]);
508         vout_display_sys_t *sys = _voutDisplay->sys;
509         if (_tapRecognizer)
510             [_tapRecognizer release];
511         return NO;
512     }
515 - (void)cleanAndReleaseFromMainThread
517     [[NSNotificationCenter defaultCenter] removeObserver:self];
519     [_tapRecognizer.view removeGestureRecognizer:_tapRecognizer];
520     [_tapRecognizer release];
522     [self removeFromSuperview];
523     [_viewContainer release];
525     assert(!_gl_attached);
526     [_eaglContext release];
527     [self release];
530 - (void)cleanAndRelease:(BOOL)flushed
532     vlc_mutex_lock(&_mutex);
533     if (_eaglEnabled && !flushed)
534         [self flushEAGLLocked];
535     _voutDisplay = nil;
536     _eaglEnabled = NO;
537     vlc_mutex_unlock(&_mutex);
539     [self performSelectorOnMainThread:@selector(cleanAndReleaseFromMainThread)
540                            withObject:nil
541                         waitUntilDone:NO];
544 - (void)dealloc
546     vlc_mutex_destroy(&_mutex);
547     vlc_cond_destroy(&_gl_attached_wait);
548     [super dealloc];
551 - (void)didMoveToWindow
553     self.contentScaleFactor = self.window.screen.scale;
555     vlc_mutex_lock(&_mutex);
556     _bufferNeedReset = YES;
557     vlc_mutex_unlock(&_mutex);
560 - (BOOL)doResetBuffers:(vlc_gl_t *)gl
562     struct gl_sys *glsys = gl->sys;
564     if (glsys->frameBuffer != 0)
565     {
566         /* clear frame buffer */
567         glDeleteFramebuffers(1, &glsys->frameBuffer);
568         glsys->frameBuffer = 0;
569     }
571     if (glsys->renderBuffer != 0)
572     {
573         /* clear render buffer */
574         glDeleteRenderbuffers(1, &glsys->renderBuffer);
575         glsys->renderBuffer = 0;
576     }
578     glDisable(GL_DEPTH_TEST);
580     glGenFramebuffers(1, &glsys->frameBuffer);
581     glBindFramebuffer(GL_FRAMEBUFFER, glsys->frameBuffer);
583     glGenRenderbuffers(1, &glsys->renderBuffer);
584     glBindRenderbuffer(GL_RENDERBUFFER, glsys->renderBuffer);
586     [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_layer];
588     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, glsys->renderBuffer);
589     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
590     {
591         msg_Err(_voutDisplay, "Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
592         return NO;
593     }
594     return YES;
597 - (BOOL)makeCurrentWithGL:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl
599     vlc_mutex_lock(&_mutex);
600     assert(!_gl_attached);
602     if (unlikely(!_appActive))
603     {
604         vlc_mutex_unlock(&_mutex);
605         return NO;
606     }
607     assert(_eaglEnabled);
609     *previousEaglContext = [EAGLContext currentContext];
611     BOOL success = [EAGLContext setCurrentContext:_eaglContext];
612     BOOL resetBuffers = NO;
614     if (success && gl != NULL)
615     {
616         struct gl_sys *glsys = gl->sys;
618         if (unlikely(_bufferNeedReset))
619         {
620             _bufferNeedReset = NO;
621             resetBuffers = YES;
622         }
623         if (unlikely(_placeInvalidated && glsys->vgl))
624         {
625             _placeInvalidated = NO;
627             vout_display_place_t place;
628             [self getPlaceLocked: &place];
629             vout_display_opengl_SetWindowAspectRatio(glsys->vgl, (float)place.width / place.height);
631             // x / y are top left corner, but we need the lower left one
632             vout_display_opengl_Viewport(glsys->vgl, _place.x, _place.y, _place.width, _place.height);
633         }
634     }
636     if (success)
637         _gl_attached = YES;
639     vlc_mutex_unlock(&_mutex);
641     if (resetBuffers && ![self doResetBuffers:gl])
642     {
643         [self releaseCurrent:*previousEaglContext];
644         return NO;
645     }
646     return success;
649 - (void)releaseCurrent:(EAGLContext *)previousEaglContext
651     [EAGLContext setCurrentContext:previousEaglContext];
653     vlc_mutex_lock(&_mutex);
654     assert(_gl_attached);
655     _gl_attached = NO;
656     vlc_mutex_unlock(&_mutex);
657     vlc_cond_signal(&_gl_attached_wait);
660 - (void)presentRenderbuffer
662     [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
665 - (void)layoutSubviews
667     [self reshape];
669     vlc_mutex_lock(&_mutex);
670     _bufferNeedReset = YES;
671     vlc_mutex_unlock(&_mutex);
674 - (void)getPlaceLocked:(vout_display_place_t *)place
676     assert(_voutDisplay);
677     vout_display_cfg_t cfg = _cfg;
679     cfg.display.width  = _viewSize.width * _scaleFactor;
680     cfg.display.height = _viewSize.height * _scaleFactor;
682     vout_display_PlacePicture(place, &_voutDisplay->source, &cfg, false);
685 - (void)reshape
687     assert([NSThread isMainThread]);
689     vlc_mutex_lock(&_mutex);
690     if (!_voutDisplay)
691     {
692         vlc_mutex_unlock(&_mutex);
693         return;
694     }
695     _viewSize = [self bounds].size;
696     _scaleFactor = self.contentScaleFactor;
698     vout_display_place_t place;
699     [self getPlaceLocked: &place];
701     if (memcmp(&place, &_place, sizeof(vout_display_place_t)) != 0)
702     {
703         _placeInvalidated = YES;
704         _place = place;
705     }
707     vout_display_SendEventDisplaySize(_voutDisplay, _viewSize.width * _scaleFactor,
708                                       _viewSize.height * _scaleFactor);
710     vlc_mutex_unlock(&_mutex);
713 - (void)tapRecognized:(UITapGestureRecognizer *)tapRecognizer
715     vlc_mutex_lock(&_mutex);
716     if (!_voutDisplay)
717     {
718         vlc_mutex_unlock(&_mutex);
719         return;
720     }
722     UIGestureRecognizerState state = [tapRecognizer state];
723     CGPoint touchPoint = [tapRecognizer locationInView:self];
724     CGFloat scaleFactor = self.contentScaleFactor;
725     vout_display_SendMouseMovedDisplayCoordinates(_voutDisplay, ORIENT_NORMAL,
726                                                   (int)touchPoint.x * scaleFactor, (int)touchPoint.y * scaleFactor,
727                                                   &_place);
729     vout_display_SendEventMousePressed(_voutDisplay, MOUSE_BUTTON_LEFT);
730     vout_display_SendEventMouseReleased(_voutDisplay, MOUSE_BUTTON_LEFT);
732     vlc_mutex_unlock(&_mutex);
735 - (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl
737     if (memcmp(&_cfg, cfg, sizeof(vout_display_cfg_t)) == 0)
738         return;
740     vlc_mutex_lock(&_mutex);
741     _cfg = *cfg;
743     vout_display_place_t place;
744     [self getPlaceLocked: &place];
745     vout_display_opengl_SetWindowAspectRatio(vgl, (float)place.width / place.height);
747     vlc_mutex_unlock(&_mutex);
749     [self performSelectorOnMainThread:@selector(setNeedsUpdateConstraints)
750                            withObject:nil
751                         waitUntilDone:NO];
754 - (void)flushEAGLLocked
756     assert(_eaglEnabled);
758     /* Ensure that all previously submitted commands are drained from the
759      * command buffer and are executed by OpenGL ES before moving to the
760      * background.*/
761     EAGLContext *previousEaglContext = [EAGLContext currentContext];
762     if ([EAGLContext setCurrentContext:_eaglContext])
763     {
764         glFinish();
765         glFlush();
766     }
767     [EAGLContext setCurrentContext:previousEaglContext];
770 - (void)applicationStateChanged:(NSNotification *)notification
772     vlc_mutex_lock(&_mutex);
774     if ([[notification name] isEqualToString:UIApplicationWillResignActiveNotification])
775         _appActive = NO;
776     else if ([[notification name] isEqualToString:UIApplicationDidEnterBackgroundNotification])
777     {
778         _appActive = NO;
780         if (_eaglEnabled)
781         {
782             /* Wait for the vout to unlock the eagl context before releasing
783              * it. */
784             while (_gl_attached)
785                 vlc_cond_wait(&_gl_attached_wait, &_mutex);
787             [self flushEAGLLocked];
788             _eaglEnabled = NO;
789         }
790     }
791     else if ([[notification name] isEqualToString:UIApplicationWillEnterForegroundNotification])
792         _eaglEnabled = YES;
793     else
794     {
795         assert([[notification name] isEqualToString:UIApplicationDidBecomeActiveNotification]);
796         _appActive = YES;
797     }
799     vlc_mutex_unlock(&_mutex);
802 - (void)updateConstraints
804     [super updateConstraints];
805     [self reshape];
808 - (BOOL)isOpaque
810     return YES;
813 - (BOOL)acceptsFirstResponder
815     return YES;
818 @end