vlc | branch: master | Alexandre Janniaux <[email protected]> | 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 <[email protected]> + * Alexandre Janniaux <[email protected]> + * + * 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" + +@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; +@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 + *****************************************************************************/ +@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; +} +@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 () -@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; -@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 - *****************************************************************************/ -@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; -} - -@end _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
