[vlc-commits] ios: move caeagl implementation out of display
Alexandre Janniaux
git at videolan.org
Fri Jan 29 11:06:26 UTC 2021
vlc | branch: master | Alexandre Janniaux <ajanni at videolabs.io> | Thu Jan 28 17:57:41 2021 +0100| [d0a49a87f82e5348e1e7d0bd94d319e73b9efc2b] | committer: Alexandre Janniaux
ios: move caeagl implementation out of display
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=d0a49a87f82e5348e1e7d0bd94d319e73b9efc2b
---
modules/video_output/Makefile.am | 15 +-
modules/video_output/apple/VLCOpenGLES2VideoView.m | 495 +++++++++++++++++++++
modules/video_output/ios.m | 482 --------------------
3 files changed, 505 insertions(+), 487 deletions(-)
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
More information about the vlc-commits
mailing list