[vlc-devel] [PATCH 3/6] ios: move caeagl implementation out of display

Alexandre Janniaux ajanni at videolabs.io
Thu Jan 28 16:57:41 UTC 2021


---
 modules/video_output/Makefile.am              |  15 +-
 .../apple/VLCOpenGLES2VideoView.m             | 495 ++++++++++++++++++
 modules/video_output/ios.m                    | 482 -----------------
 3 files changed, 505 insertions(+), 487 deletions(-)
 create mode 100644 modules/video_output/apple/VLCOpenGLES2VideoView.m

diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 47afbbdc39..8879321dfb 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -48,27 +48,32 @@ libglinterop_cvpx_plugin_la_CFLAGS = $(AM_CFLAGS) -DUSE_OPENGL_ES2
 libglinterop_cvpx_plugin_la_LDFLAGS += -Wl,-framework,IOSurface,-framework,OpenGLES
 endif
 
-libvout_ios_plugin_la_SOURCES = video_output/ios.m
+libvout_ios_plugin_la_SOURCES = video_output/opengl/display.c
 libvout_ios_plugin_la_CFLAGS = $(AM_CFLAGS) $(OPENGL_COMMONCFLAGS) -DUSE_OPENGL_ES2
 libvout_ios_plugin_la_LIBADD = libvlc_opengles.la
-libvout_ios_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)' \
-	-Wl,-framework,Foundation,-framework,OpenGLES,-framework,QuartzCore,-framework,UIKit
 
 libuiview_window_plugin_la_SOURCES = video_output/apple/VLCVideoUIView.m
 libuiview_window_plugin_la_LDFLAGS = $(AM_LDFLAGS) \
 	-Wl,-framework,Foundation,-framework,QuartzCore,-framework,UIKit
 libuiview_window_plugin_la_OBJCFLAGS = $(AM_OBJCFLAGS) -fobjc-arc
 
+libcaeagl_ios_plugin_la_SOURCES = video_output/apple/VLCOpenGLES2VideoView.m
+libcaeagl_ios_plugin_la_LDFLAGS = $(AM_LDFLAGS) \
+	-Wl,-framework,Foundation,-framework,OpenGLES,-framework,QuartzCore,-framework,UIKit
+libcaeagl_ios_plugin_la_OBJCFLAGS = $(AM_OBJCFLAGS) -fobjc-arc
+
 if HAVE_IOS
 vout_LTLIBRARIES += libvout_ios_plugin.la \
 	libglinterop_cvpx_plugin.la \
-	libuiview_window_plugin.la
+	libuiview_window_plugin.la \
+	libcaeagl_ios_plugin.la
 endif
 if HAVE_TVOS
 vout_LTLIBRARIES += \
 	libvout_ios_plugin.la \
 	libglinterop_cvpx_plugin.la \
-	libuiview_window_plugin.la
+	libuiview_window_plugin.la \
+	libcaeagl_ios_plugin.la
 endif
 
 libglinterop_vaapi_plugin_la_SOURCES = video_output/opengl/interop_vaapi.c \
diff --git a/modules/video_output/apple/VLCOpenGLES2VideoView.m b/modules/video_output/apple/VLCOpenGLES2VideoView.m
new file mode 100644
index 0000000000..0d617d30b6
--- /dev/null
+++ b/modules/video_output/apple/VLCOpenGLES2VideoView.m
@@ -0,0 +1,495 @@
+/*****************************************************************************
+ * VLCOpenGLES2VideoView.m: iOS OpenGL ES provider through CAEAGLLayer
+ *****************************************************************************
+ * Copyright (C) 2001-2017 VLC authors and VideoLAN
+ * Copyright (C) 2021 Videolabs
+ *
+ * Authors: Pierre d'Herbemont <pdherbemont at videolan dot org>
+ *          Felix Paul Kühne <fkuehne at videolan dot org>
+ *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
+ *          Rémi Denis-Courmont
+ *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *          Eric Petit <titer at m0k.org>
+ *          Alexandre Janniaux <ajanni at videolabs.io>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#import <UIKit/UIKit.h>
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/ES2/gl.h>
+#import <OpenGLES/ES2/glext.h>
+#import <QuartzCore/QuartzCore.h>
+#import <dlfcn.h>
+
+#ifdef HAVE_CONFIG_H
+# import "config.h"
+#endif
+
+#import <vlc_common.h>
+#import <vlc_plugin.h>
+#import <vlc_vout_display.h>
+#import <vlc_opengl.h>
+#import <vlc_dialog.h>
+#import "../opengl/vout_helper.h"
+#import "../opengl/gl_api.h"
+
+ at interface VLCOpenGLES2VideoView : UIView {
+    vlc_gl_t *_gl;
+
+    EAGLContext *_eaglContext;
+    EAGLContext *_previousEaglContext;
+    CAEAGLLayer *_layer;
+
+    vlc_mutex_t _mutex;
+    vlc_cond_t  _gl_attached_wait;
+    BOOL        _gl_attached;
+
+    BOOL _bufferNeedReset;
+    BOOL _appActive;
+    BOOL _eaglEnabled;
+
+    GLuint _renderBuffer;
+    GLuint _frameBuffer;
+
+    struct vlc_gl_api _api;
+}
+
+- (id)initWithFrame:(CGRect)frame gl:(vlc_gl_t*)gl;
+- (BOOL)makeCurrent;
+- (void)releaseCurrent;
+- (void)presentRenderbuffer;
+- (void)didMoveToWindow;
+- (void)detachFromWindow;
+ at end
+
+/*****************************************************************************
+ * vlc_gl_t callbacks
+ *****************************************************************************/
+static void *GetSymbol(vlc_gl_t *gl, const char *name)
+{
+    VLC_UNUSED(gl);
+    return dlsym(RTLD_DEFAULT, name);
+}
+
+static int MakeCurrent(vlc_gl_t *gl)
+{
+    VLCOpenGLES2VideoView *view = (__bridge VLCOpenGLES2VideoView *)gl->sys;
+
+    if (![view makeCurrent])
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+static void ReleaseCurrent(vlc_gl_t *gl)
+{
+    VLCOpenGLES2VideoView *view = (__bridge VLCOpenGLES2VideoView *)gl->sys;
+    [view releaseCurrent];
+}
+
+static void Swap(vlc_gl_t *gl)
+{
+    VLCOpenGLES2VideoView *view = (__bridge VLCOpenGLES2VideoView *)gl->sys;
+    [view presentRenderbuffer];
+}
+
+static void Resize(vlc_gl_t *gl, unsigned width, unsigned height)
+{
+    VLC_UNUSED(gl); VLC_UNUSED(width); VLC_UNUSED(height);
+    /* Use the parent frame size for now, resize is smoother and called
+     * automatically from the main thread queue. */
+}
+
+static void Close(vlc_gl_t *gl)
+{
+    /* Transfer ownership back from VLC to ARC so that it can be released. */
+    VLCOpenGLES2VideoView *view = (__bridge_transfer VLCOpenGLES2VideoView*)gl->sys;
+
+    /* We need to detach because the superview has a reference to our view. */
+    [view detachFromWindow];
+}
+
+/*****************************************************************************
+ * Our UIView object
+ *****************************************************************************/
+ at implementation VLCOpenGLES2VideoView
+
++ (Class)layerClass
+{
+    return [CAEAGLLayer class];
+}
+
+- (id)initWithFrame:(CGRect)frame gl:(vlc_gl_t*)gl
+{
+    _gl = gl;
+
+    _appActive = ([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
+    if (unlikely(!_appActive))
+        return nil;
+
+    self = [super initWithFrame:frame];
+    if (!self)
+        return nil;
+
+    _eaglEnabled = YES;
+    _bufferNeedReset = YES;
+
+    vlc_mutex_init(&_mutex);
+    vlc_cond_init(&_gl_attached_wait);
+
+    /* The following creates a new OpenGL ES context with the API version we
+     * need. If there is already an active context created by another OpenGL
+     * provider we cache it and restore analog to the
+     * makeCurrent/releaseCurrent pattern used through-out the class */
+    _eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+    _previousEaglContext = nil;
+
+    _layer = (CAEAGLLayer *)self.layer;
+    _layer.drawableProperties = [NSDictionary dictionaryWithObject:kEAGLColorFormatRGBA8 forKey: kEAGLDrawablePropertyColorFormat];
+    _layer.opaque = YES;
+
+    /* Resize is done accordingly to the parent frame directly. */
+    self.autoresizingMask = UIViewAutoresizingFlexibleWidth
+                          | UIViewAutoresizingFlexibleHeight;
+    self.contentMode = UIViewContentModeScaleToFill;
+
+    /* Connect to the parent UIView which will contain this surface.
+     * Using a parent UIView makes it easier to handle window resize and
+     * still have full control over the display object, since layers don't
+     * need to draw anything to exist. */
+    if (![self attachToWindow: _gl->surface])
+        return nil;
+
+    /* Listen application state change because we cannot use OpenGL in the
+     * background. This should probably move to the vout_window reports in
+     * the future, which could even signal that we need to disable the whole
+     * display and potentially adapt playback for that. */
+
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(applicationStateChanged:)
+                                                 name:UIApplicationWillEnterForegroundNotification
+                                               object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(applicationStateChanged:)
+                                                 name:UIApplicationDidEnterBackgroundNotification
+                                               object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(applicationStateChanged:)
+                                                 name:UIApplicationWillResignActiveNotification
+                                               object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(applicationStateChanged:)
+                                                 name:UIApplicationDidBecomeActiveNotification
+                                               object:nil];
+
+    /* Setup the usual vlc_gl_t callbacks before loading the API since we need
+     * the get_proc_address symbol and a current context. */
+    gl->make_current = MakeCurrent;
+    gl->release_current = ReleaseCurrent;
+    gl->resize = Resize;
+    gl->swap = Swap;
+    gl->get_proc_address = GetSymbol;
+    gl->destroy = Close;
+
+    return self;
+}
+
+- (BOOL)attachToWindow:(vout_window_t*)wnd
+{
+    @try {
+        UIView *viewContainer = (__bridge UIView*)wnd->handle.nsobject;
+        /* get the object we will draw into */
+        if (unlikely(viewContainer == nil)) {
+            msg_Err(_gl, "provided view container is nil");
+            return NO;
+        }
+
+        if (unlikely(![viewContainer respondsToSelector:@selector(isKindOfClass:)])) {
+            msg_Err(_gl, "void pointer not an ObjC object");
+            return NO;
+        }
+
+        if (unlikely(![viewContainer isKindOfClass:[UIView class]])) {
+            msg_Err(_gl, "passed ObjC object not of class UIView");
+            return NO;
+        }
+
+        /* Initial size setup */
+        self.frame = viewContainer.bounds;
+
+        [viewContainer addSubview:self];
+
+        return YES;
+    } @catch (NSException *exception) {
+        msg_Err(_gl, "Handling the view container failed due to an Obj-C exception (%s, %s", [exception.name UTF8String], [exception.reason UTF8String]);
+        return NO;
+    }
+}
+
+- (void)detachFromWindow
+{
+    EAGLContext *previous_context = [EAGLContext currentContext];
+    [EAGLContext setCurrentContext:_eaglContext];
+    glDeleteFramebuffers(1, &_frameBuffer);
+    glDeleteRenderbuffers(1, &_renderBuffer);
+    [EAGLContext setCurrentContext:previous_context];
+
+    /* Flush the OpenGL pipeline before leaving. */
+    vlc_mutex_lock(&_mutex);
+    if (_eaglEnabled)
+        [self flushEAGLLocked];
+    _eaglEnabled = NO;
+    vlc_mutex_unlock(&_mutex);
+
+    /* This cannot be a synchronous dispatch because player is usually running
+     * in the main thread and block the main thread unless we accept our fate
+     * and exit here. */
+    dispatch_async(dispatch_get_main_queue(), ^{
+         /* Remove the external references to the view so that
+          * dealloc can be called by ARC. */
+
+         [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                         name:UIApplicationWillResignActiveNotification
+                                                       object:nil];
+
+         [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                         name:UIApplicationDidBecomeActiveNotification
+                                                       object:nil];
+
+         [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                         name:UIApplicationWillEnterForegroundNotification
+                                                       object:nil];
+
+         [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                         name:UIApplicationDidEnterBackgroundNotification
+                                                       object:nil];
+        assert(!_gl_attached);
+        [self removeFromSuperview];
+    });
+}
+
+- (void)didMoveToWindow
+{
+    self.contentScaleFactor = self.window.screen.scale;
+
+    vlc_mutex_lock(&_mutex);
+    _bufferNeedReset = YES;
+    vlc_mutex_unlock(&_mutex);
+}
+
+- (BOOL)doResetBuffers:(vlc_gl_t *)gl
+{
+    if (_frameBuffer != 0)
+    {
+        /* clear frame buffer */
+        glDeleteFramebuffers(1, &_frameBuffer);
+        _frameBuffer = 0;
+    }
+
+    if (_renderBuffer != 0)
+    {
+        /* clear render buffer */
+        glDeleteRenderbuffers(1, &_renderBuffer);
+        _renderBuffer = 0;
+    }
+
+    glGenFramebuffers(1, &_frameBuffer);
+    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
+
+    glGenRenderbuffers(1, &_renderBuffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
+
+    [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_layer];
+
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+    {
+        msg_Err(_gl, "Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+        return NO;
+    }
+    return YES;
+}
+
+- (BOOL)makeCurrent
+{
+    vlc_mutex_lock(&_mutex);
+    assert(!_gl_attached);
+
+    if (unlikely(!_appActive))
+    {
+        vlc_mutex_unlock(&_mutex);
+        return NO;
+    }
+
+    assert(_eaglEnabled);
+    _previousEaglContext = [EAGLContext currentContext];
+
+    assert(_eaglContext);
+    if (![EAGLContext setCurrentContext:_eaglContext])
+    {
+        vlc_mutex_unlock(&_mutex);
+        return NO;
+    }
+
+    BOOL resetBuffers = NO;
+
+
+    if (unlikely(_bufferNeedReset))
+    {
+        _bufferNeedReset = NO;
+        resetBuffers = YES;
+    }
+
+    _gl_attached = YES;
+
+    vlc_mutex_unlock(&_mutex);
+
+    if (resetBuffers && ![self doResetBuffers:_gl])
+    {
+        [self releaseCurrent];
+        return NO;
+    }
+    return YES;
+}
+
+- (void)releaseCurrent
+{
+    vlc_mutex_lock(&_mutex);
+    assert(_gl_attached);
+    _gl_attached = NO;
+    [EAGLContext setCurrentContext:_previousEaglContext];
+    _previousEaglContext = nil;
+    vlc_mutex_unlock(&_mutex);
+    vlc_cond_signal(&_gl_attached_wait);
+}
+
+- (void)presentRenderbuffer
+{
+    [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
+}
+
+- (void)layoutSubviews
+{
+    vlc_mutex_lock(&_mutex);
+    _bufferNeedReset = YES;
+    vlc_mutex_unlock(&_mutex);
+}
+
+- (void)flushEAGLLocked
+{
+    assert(_eaglEnabled);
+
+    /* Ensure that all previously submitted commands are drained from the
+     * command buffer and are executed by OpenGL ES before moving to the
+     * background.*/
+    EAGLContext *previousEaglContext = [EAGLContext currentContext];
+    if ([EAGLContext setCurrentContext:_eaglContext])
+        glFinish();
+    [EAGLContext setCurrentContext:previousEaglContext];
+}
+
+- (void)applicationStateChanged:(NSNotification *)notification
+{
+    vlc_mutex_lock(&_mutex);
+
+    if ([[notification name] isEqualToString:UIApplicationWillResignActiveNotification])
+        _appActive = NO;
+    else if ([[notification name] isEqualToString:UIApplicationDidEnterBackgroundNotification])
+    {
+        _appActive = NO;
+
+        /* Wait for the vout to unlock the eagl context before releasing
+         * it. */
+        while (_gl_attached && _eaglEnabled)
+            vlc_cond_wait(&_gl_attached_wait, &_mutex);
+
+        /* _eaglEnabled can change during the vlc_cond_wait
+         * as the mutex is unlocked during that, so this check
+         * has to be done after the vlc_cond_wait! */
+        if (_eaglEnabled) {
+            [self flushEAGLLocked];
+            _eaglEnabled = NO;
+        }
+    }
+    else if ([[notification name] isEqualToString:UIApplicationWillEnterForegroundNotification])
+        _eaglEnabled = YES;
+    else
+    {
+        assert([[notification name] isEqualToString:UIApplicationDidBecomeActiveNotification]);
+        _appActive = YES;
+    }
+
+    vlc_mutex_unlock(&_mutex);
+}
+
+- (void)updateConstraints
+{
+    [super updateConstraints];
+}
+
+- (BOOL)isOpaque
+{
+    return YES;
+}
+
+- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
+{
+    /* Disable events for this view, as the vout_window view will be the one
+     * handling them. */
+    return nil;
+}
+ at end
+
+
+
+static int Open(vlc_gl_t *gl, unsigned width, unsigned height)
+{
+    vout_window_t *wnd = gl->surface;
+
+    /* We only support UIView container window. */
+    if (wnd->type != VOUT_WINDOW_TYPE_NSOBJECT)
+        return VLC_EGENERIC;
+
+   @autoreleasepool {
+        /* setup the actual OpenGL ES view */
+        dispatch_sync(dispatch_get_main_queue(), ^{
+            gl->sys = (__bridge_retained void*)[[VLCOpenGLES2VideoView alloc]
+               // TODO better rect
+               initWithFrame:CGRectMake(0.,0.,320.,240.) gl:gl];
+        });
+
+        if (gl->sys == NULL)
+        {
+            msg_Err(gl, "Creating OpenGL ES 2 view failed");
+            return VLC_EGENERIC;
+        }
+    }
+
+    return VLC_SUCCESS;
+}
+
+vlc_module_begin ()
+    set_shortname (N_("CAEAGL"))
+    set_description (N_("CAEAGL provider for OpenGL"))
+    set_category (CAT_VIDEO)
+    set_subcategory (SUBCAT_VIDEO_VOUT)
+    set_capability ("opengl es2", 50)
+    set_callback(Open)
+    add_shortcut ("caeagl")
+vlc_module_end ()
diff --git a/modules/video_output/ios.m b/modules/video_output/ios.m
index 1a67a2de72..cd2e760e58 100644
--- a/modules/video_output/ios.m
+++ b/modules/video_output/ios.m
@@ -60,12 +60,6 @@ static void PictureRender(vout_display_t *, picture_t *, subpicture_t *, vlc_tic
 static void PictureDisplay(vout_display_t *, picture_t *);
 static int Control(vout_display_t*, int);
 
-static void *OurGetProcAddress(vlc_gl_t *, const char *);
-
-static int GLESMakeCurrent(vlc_gl_t *);
-static void GLESSwap(vlc_gl_t *);
-static void GLESReleaseCurrent(vlc_gl_t *);
-
 /**
  * Module declaration
  */
@@ -83,41 +77,6 @@ vlc_module_begin ()
     add_opengl_submodule_draw()
 vlc_module_end ()
 
- at interface VLCOpenGLES2VideoView : UIView {
-    vout_display_t *_voutDisplay;
-    EAGLContext *_eaglContext;
-    CAEAGLLayer *_layer;
-
-    vlc_mutex_t _mutex;
-    vlc_cond_t  _gl_attached_wait;
-    BOOL        _gl_attached;
-
-    BOOL _bufferNeedReset;
-    BOOL _appActive;
-    BOOL _eaglEnabled;
-    BOOL _placeInvalidated;
-
-    UIView *_viewContainer;
-
-    /* Written from MT, read locked from vout */
-    vout_display_place_t _place;
-    CGSize _viewSize;
-    CGFloat _scaleFactor;
-
-    /* Written from vout, read locked from MT */
-    vout_display_cfg_t _cfg;
-}
-
-- (id)initWithFrame:(CGRect)frame andVD:(vout_display_t*)vd andWindow:(vout_window_t*)wnd;
-- (void)cleanAndRelease:(BOOL)flushed;
-- (BOOL)makeCurrent:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl;
-- (void)releaseCurrent:(EAGLContext *)previousEaglContext;
-- (void)presentRenderbuffer;
-
-- (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl;
-- (void)getPlaceLocked:(vout_display_place_t *)place;
- at end
-
 struct vout_display_sys_t
 {
     VLCOpenGLES2VideoView *glESView;
@@ -136,12 +95,6 @@ struct gl_sys
     EAGLContext *previousEaglContext;
 };
 
-static void *OurGetProcAddress(vlc_gl_t *gl, const char *name)
-{
-    VLC_UNUSED(gl);
-
-    return dlsym(RTLD_DEFAULT, name);
-}
 
 static int SetViewpoint(vout_display_t *vd, const vlc_viewpoint_t *vp)
 {
@@ -310,438 +263,3 @@ static void PictureRender(vout_display_t *vd, picture_t *pic, subpicture_t *subp
         vlc_gl_ReleaseCurrent(sys->gl);
     }
 }
-
-/*****************************************************************************
- * vout opengl callbacks
- *****************************************************************************/
-static int GLESMakeCurrent(vlc_gl_t *gl)
-{
-    struct gl_sys *sys = gl->sys;
-
-    if (![sys->glESView makeCurrent:&sys->previousEaglContext withGL:gl])
-        return VLC_EGENERIC;
-    return VLC_SUCCESS;
-}
-
-static void GLESReleaseCurrent(vlc_gl_t *gl)
-{
-    struct gl_sys *sys = gl->sys;
-
-    [sys->glESView releaseCurrent:sys->previousEaglContext];
-}
-
-static void GLESSwap(vlc_gl_t *gl)
-{
-    struct gl_sys *sys = gl->sys;
-
-    [sys->glESView presentRenderbuffer];
-}
-
-
-/*****************************************************************************
- * Our UIView object
- *****************************************************************************/
- at implementation VLCOpenGLES2VideoView
-
-+ (Class)layerClass
-{
-    return [CAEAGLLayer class];
-}
-
-+ (void)getNewView:(NSArray *)value
-{
-    vout_display_t *vd = [[value objectAtIndex:0] pointerValue];
-    vout_window_t *wnd = [[value objectAtIndex:1] pointerValue];
-
-    struct vout_display_sys_t *sys = vd->sys;
-    sys->glESView = [[self alloc] initWithFrame:CGRectMake(0.,0.,320.,240.) andVD:vd andWindow:wnd];
-}
-
-- (id)initWithFrame:(CGRect)frame andVD:(vout_display_t*)vd andWindow:(vout_window_t*)wnd
-{
-    _appActive = ([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
-    if (unlikely(!_appActive))
-        return nil;
-
-    self = [super initWithFrame:frame];
-    if (!self)
-        return nil;
-
-    _eaglEnabled = YES;
-    _bufferNeedReset = YES;
-    _voutDisplay = vd;
-    _cfg = *_voutDisplay->cfg;
-
-    vlc_mutex_init(&_mutex);
-    vlc_cond_init(&_gl_attached_wait);
-    _gl_attached = YES;
-
-    /* the following creates a new OpenGL ES context with the API version we
-     * need if there is already an active context created by another OpenGL
-     * provider we cache it and restore analog to the
-     * makeCurrent/releaseCurrent pattern used through-out the class */
-    EAGLContext *previousEaglContext = [EAGLContext currentContext];
-
-    _eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
-
-    if (unlikely(!_eaglContext)
-     || unlikely(![EAGLContext setCurrentContext:_eaglContext]))
-    {
-        [_eaglContext release];
-        [self release];
-        return nil;
-    }
-    [self releaseCurrent:previousEaglContext];
-
-    _layer = (CAEAGLLayer *)self.layer;
-    _layer.drawableProperties = [NSDictionary dictionaryWithObject:kEAGLColorFormatRGBA8 forKey: kEAGLDrawablePropertyColorFormat];
-    _layer.opaque = YES;
-
-    self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
-
-    if (![self bindToWindow: wnd])
-    {
-        [_eaglContext release];
-        [self release];
-        return nil;
-    }
-
-    /* */
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(applicationStateChanged:)
-                                                 name:UIApplicationWillResignActiveNotification
-                                               object:nil];
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(applicationStateChanged:)
-                                                 name:UIApplicationDidBecomeActiveNotification
-                                               object:nil];
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(applicationStateChanged:)
-                                                 name:UIApplicationDidEnterBackgroundNotification
-                                               object:nil];
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(applicationStateChanged:)
-                                                 name:UIApplicationWillEnterForegroundNotification
-                                               object:nil];
-
-    return self;
-}
-
-- (BOOL)bindToWindow:(vout_window_t*)wnd
-{
-    @try {
-        UIView *viewContainer = wnd->handle.nsobject;
-        /* get the object we will draw into */
-        if (unlikely(viewContainer == nil)) {
-            msg_Err(_voutDisplay, "provided view container is nil");
-            return NO;
-        }
-
-        if (unlikely(![viewContainer respondsToSelector:@selector(isKindOfClass:)])) {
-            msg_Err(_voutDisplay, "void pointer not an ObjC object");
-            return NO;
-        }
-
-        [viewContainer retain];
-
-        if (![viewContainer isKindOfClass:[UIView class]]) {
-            msg_Err(_voutDisplay, "passed ObjC object not of class UIView");
-            return NO;
-        }
-
-        /* This will be released in Close(), on
-         * main thread, after we are done using it. */
-        _viewContainer = viewContainer;
-
-        self.frame = viewContainer.bounds;
-        [self reshape];
-
-        [_viewContainer addSubview:self];
-
-        return YES;
-    } @catch (NSException *exception) {
-        msg_Err(_voutDisplay, "Handling the view container failed due to an Obj-C exception (%s, %s", [exception.name UTF8String], [exception.reason UTF8String]);
-        vout_display_sys_t *sys = _voutDisplay->sys;
-        return NO;
-    }
-}
-
-- (void)cleanAndReleaseFromMainThread
-{
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-
-    [self removeFromSuperview];
-    [_viewContainer release];
-
-    assert(!_gl_attached);
-    [_eaglContext release];
-    [self release];
-}
-
-- (void)cleanAndRelease:(BOOL)flushed
-{
-    vlc_mutex_lock(&_mutex);
-    if (_eaglEnabled && !flushed)
-        [self flushEAGLLocked];
-    _voutDisplay = nil;
-    _eaglEnabled = NO;
-    vlc_mutex_unlock(&_mutex);
-
-    [self performSelectorOnMainThread:@selector(cleanAndReleaseFromMainThread)
-                           withObject:nil
-                        waitUntilDone:NO];
-}
-
-- (void)dealloc
-{
-    [super dealloc];
-}
-
-- (void)didMoveToWindow
-{
-    self.contentScaleFactor = self.window.screen.scale;
-
-    vlc_mutex_lock(&_mutex);
-    _bufferNeedReset = YES;
-    vlc_mutex_unlock(&_mutex);
-}
-
-- (BOOL)doResetBuffers:(vlc_gl_t *)gl
-{
-    struct gl_sys *glsys = gl->sys;
-
-    if (glsys->frameBuffer != 0)
-    {
-        /* clear frame buffer */
-        glDeleteFramebuffers(1, &glsys->frameBuffer);
-        glsys->frameBuffer = 0;
-    }
-
-    if (glsys->renderBuffer != 0)
-    {
-        /* clear render buffer */
-        glDeleteRenderbuffers(1, &glsys->renderBuffer);
-        glsys->renderBuffer = 0;
-    }
-
-    glDisable(GL_DEPTH_TEST);
-
-    glGenFramebuffers(1, &glsys->frameBuffer);
-    glBindFramebuffer(GL_FRAMEBUFFER, glsys->frameBuffer);
-
-    glGenRenderbuffers(1, &glsys->renderBuffer);
-    glBindRenderbuffer(GL_RENDERBUFFER, glsys->renderBuffer);
-
-    [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_layer];
-
-    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, glsys->renderBuffer);
-    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
-    {
-        msg_Err(_voutDisplay, "Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
-        return NO;
-    }
-    return YES;
-}
-
-- (BOOL)makeCurrent:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl
-{
-    vlc_mutex_lock(&_mutex);
-    assert(!_gl_attached);
-
-    if (unlikely(!_appActive))
-    {
-        vlc_mutex_unlock(&_mutex);
-        return NO;
-    }
-
-    assert(_eaglEnabled);
-    *previousEaglContext = [EAGLContext currentContext];
-
-    if (![EAGLContext setCurrentContext:_eaglContext])
-    {
-        vlc_mutex_unlock(&_mutex);
-        return NO;
-    }
-
-    BOOL resetBuffers = NO;
-
-    if (gl != NULL)
-    {
-        struct gl_sys *glsys = gl->sys;
-
-        if (unlikely(_bufferNeedReset))
-        {
-            _bufferNeedReset = NO;
-            resetBuffers = YES;
-        }
-        if (unlikely(_placeInvalidated && glsys->vgl))
-        {
-            _placeInvalidated = NO;
-
-            vout_display_place_t place;
-            [self getPlaceLocked: &place];
-            vout_display_opengl_SetWindowAspectRatio(glsys->vgl, (float)place.width / place.height);
-
-            // x / y are top left corner, but we need the lower left one
-            vout_display_opengl_Viewport(glsys->vgl, _place.x, _place.y, _place.width, _place.height);
-        }
-    }
-
-    _gl_attached = YES;
-
-    vlc_mutex_unlock(&_mutex);
-
-    if (resetBuffers && ![self doResetBuffers:gl])
-    {
-        [self releaseCurrent:*previousEaglContext];
-        return NO;
-    }
-    return YES;
-}
-
-- (void)releaseCurrent:(EAGLContext *)previousEaglContext
-{
-    [EAGLContext setCurrentContext:previousEaglContext];
-
-    vlc_mutex_lock(&_mutex);
-    assert(_gl_attached);
-    _gl_attached = NO;
-    vlc_cond_signal(&_gl_attached_wait);
-    vlc_mutex_unlock(&_mutex);
-}
-
-- (void)presentRenderbuffer
-{
-    [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
-}
-
-- (void)layoutSubviews
-{
-    [self reshape];
-
-    vlc_mutex_lock(&_mutex);
-    _bufferNeedReset = YES;
-    vlc_mutex_unlock(&_mutex);
-}
-
-- (void)getPlaceLocked:(vout_display_place_t *)place
-{
-    assert(_voutDisplay);
-    vout_display_cfg_t cfg = _cfg;
-
-    cfg.display.width  = _viewSize.width * _scaleFactor;
-    cfg.display.height = _viewSize.height * _scaleFactor;
-
-    vout_display_PlacePicture(place, _voutDisplay->source, &cfg);
-}
-
-- (void)reshape
-{
-    assert([NSThread isMainThread]);
-
-    vlc_mutex_lock(&_mutex);
-    if (!_voutDisplay)
-    {
-        vlc_mutex_unlock(&_mutex);
-        return;
-    }
-    _viewSize = [self bounds].size;
-    _scaleFactor = self.contentScaleFactor;
-
-    vout_display_place_t place;
-    [self getPlaceLocked: &place];
-
-    if (memcmp(&place, &_place, sizeof(vout_display_place_t)) != 0)
-    {
-        _placeInvalidated = YES;
-        _place = place;
-    }
-
-    vlc_mutex_unlock(&_mutex);
-}
-
-- (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl
-{
-    if (memcmp(&_cfg, cfg, sizeof(vout_display_cfg_t)) == 0)
-        return;
-
-    vlc_mutex_lock(&_mutex);
-    _cfg = *cfg;
-
-    vout_display_place_t place;
-    [self getPlaceLocked: &place];
-    vout_display_opengl_SetWindowAspectRatio(vgl, (float)place.width / place.height);
-
-    vlc_mutex_unlock(&_mutex);
-
-    [self performSelectorOnMainThread:@selector(setNeedsUpdateConstraints)
-                           withObject:nil
-                        waitUntilDone:NO];
-}
-
-- (void)flushEAGLLocked
-{
-    assert(_eaglEnabled);
-
-    /* Ensure that all previously submitted commands are drained from the
-     * command buffer and are executed by OpenGL ES before moving to the
-     * background.*/
-    EAGLContext *previousEaglContext = [EAGLContext currentContext];
-    if ([EAGLContext setCurrentContext:_eaglContext])
-        glFinish();
-    [EAGLContext setCurrentContext:previousEaglContext];
-}
-
-- (void)applicationStateChanged:(NSNotification *)notification
-{
-    vlc_mutex_lock(&_mutex);
-
-    if ([[notification name] isEqualToString:UIApplicationWillResignActiveNotification])
-        _appActive = NO;
-    else if ([[notification name] isEqualToString:UIApplicationDidEnterBackgroundNotification])
-    {
-        _appActive = NO;
-
-        /* Wait for the vout to unlock the eagl context before releasing
-         * it. */
-        while (_gl_attached && _eaglEnabled)
-            vlc_cond_wait(&_gl_attached_wait, &_mutex);
-
-        /* _eaglEnabled can change during the vlc_cond_wait
-         * as the mutex is unlocked during that, so this check
-         * has to be done after the vlc_cond_wait! */
-        if (_eaglEnabled) {
-            [self flushEAGLLocked];
-            _eaglEnabled = NO;
-        }
-    }
-    else if ([[notification name] isEqualToString:UIApplicationWillEnterForegroundNotification])
-        _eaglEnabled = YES;
-    else
-    {
-        assert([[notification name] isEqualToString:UIApplicationDidBecomeActiveNotification]);
-        _appActive = YES;
-    }
-
-    vlc_mutex_unlock(&_mutex);
-}
-
-- (void)updateConstraints
-{
-    [super updateConstraints];
-    [self reshape];
-}
-
-- (BOOL)isOpaque
-{
-    return YES;
-}
-
-- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
-{
-    /* Disable events for this view, as the vout_window view will be the one
-     * handling them. */
-    return nil;
-}
-
- at end
-- 
2.30.0



More information about the vlc-devel mailing list