]> git.sesse.net Git - vlc/blobdiff - modules/video_output/ios2.m
sftp: change item b_net
[vlc] / modules / video_output / ios2.m
index a18f8f2003f273050341a43299461171725e9a09..5039febecbe797d5697088ea2591a6434cdebedc 100644 (file)
@@ -1,19 +1,16 @@
 /*****************************************************************************
  * ios2.m: iOS OpenGL ES 2 provider
  *****************************************************************************
- * Copyright (C) 2001-2013 VLC authors and VideoLAN
+ * Copyright (C) 2001-2014 VLC authors and VideoLAN
  * $Id$
  *
- * Authors: Derk-Jan Hartman <hartman at videolan dot org>
- *          Eric Petit <titer@m0k.org>
- *          Benjamin Pracht <bigben at videolan dot org>
- *          Damien Fouilleul <damienf at videolan dot org>
- *          Pierre d'Herbemont <pdherbemont at videolan dot org>
+ * 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
- *          Juho Vähä-Herttua <juhovh at iki dot fi>
  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *          Eric Petit <titer@m0k.org>
+ *
  *
  * 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
@@ -54,8 +51,8 @@
 /**
  * Forward declarations
  */
-static int Open (vlc_object_t *);
-static void Close (vlc_object_t *);
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
 
 static picture_pool_t* PicturePool(vout_display_t *vd, unsigned requested_count);
 static void PictureRender(vout_display_t* vd, picture_t *pic, subpicture_t *subpicture);
@@ -71,51 +68,49 @@ static void OpenglESSwap(vlc_gl_t* gl);
  * Module declaration
  */
 vlc_module_begin ()
-    set_shortname ("iOS vout")
-    set_description (N_("iOS OpenGL video output"))
-    set_category (CAT_VIDEO)
-    set_subcategory (SUBCAT_VIDEO_VOUT)
-    set_capability ("vout display", 300)
-    set_callbacks (Open, Close)
-
-    add_shortcut ("vout_ios2")
+    set_shortname("iOS vout")
+    set_description(N_("iOS OpenGL video output"))
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_capability("vout display", 300)
+    set_callbacks(Open, Close)
+
+    add_shortcut("vout_ios2")
 vlc_module_end ()
 
-@interface VLCOpenGLES2VideoView : UIView
-{
+@interface VLCOpenGLES2VideoView : UIView {
     vout_display_t *_voutDisplay;
     EAGLContext *_eaglContext;
     GLuint _renderBuffer;
     GLuint _frameBuffer;
 
     BOOL _bufferNeedReset;
+    BOOL _appActive;
 }
 @property (readwrite) vout_display_t* voutDisplay;
 @property (readonly) EAGLContext* eaglContext;
+@property (readonly) BOOL isAppActive;
 
 - (void)createBuffers;
 - (void)destroyBuffers;
 - (void)resetBuffers;
-
 @end
 
-
 struct vout_display_sys_t
 {
     VLCOpenGLES2VideoView *glESView;
     UIView* viewContainer;
+    UITapGestureRecognizer *tapRecognizer;
 
     vlc_gl_t gl;
     vout_display_opengl_t *vgl;
 
     picture_pool_t *picturePool;
-    picture_t *current;
     bool has_first_frame;
 
     vout_display_place_t place;
 };
 
-
 static void *OurGetProcAddress(vlc_gl_t *gl, const char *name)
 {
     VLC_UNUSED(gl);
@@ -126,6 +121,10 @@ static void *OurGetProcAddress(vlc_gl_t *gl, const char *name)
 static int Open(vlc_object_t *this)
 {
     vout_display_t *vd = (vout_display_t *)this;
+
+    if (vout_display_IsWindowed(vd))
+        return VLC_EGENERIC;
+
     vout_display_sys_t *sys = calloc (1, sizeof(*sys));
     NSAutoreleasePool *autoreleasePool = nil;
 
@@ -143,8 +142,6 @@ static int Open(vlc_object_t *this)
     if (!viewContainer || ![viewContainer isKindOfClass:[UIView class]])
         goto bailout;
 
-    vout_display_DeleteWindow (vd, NULL);
-
     /* This will be released in Close(), on
      * main thread, after we are done using it. */
     sys->viewContainer = [viewContainer retain];
@@ -157,7 +154,20 @@ static int Open(vlc_object_t *this)
 
     [sys->glESView setVoutDisplay:vd];
 
-    [sys->viewContainer performSelectorOnMainThread:@selector(addSubview:) withObject:sys->glESView waitUntilDone:YES];
+    [sys->viewContainer performSelectorOnMainThread:@selector(addSubview:)
+                                         withObject:sys->glESView
+                                      waitUntilDone:YES];
+
+    /* add tap gesture recognizer for DVD menus and stuff */
+    sys->tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:sys->glESView
+                                                                 action:@selector(tapRecognized:)];
+    if (sys->viewContainer.window) {
+        if (sys->viewContainer.window.rootViewController) {
+            if (sys->viewContainer.window.rootViewController.view)
+                [sys->viewContainer.superview addGestureRecognizer:sys->tapRecognizer];
+        }
+    }
+    sys->tapRecognizer.cancelsTouchesInView = NO;
 
     /* Initialize common OpenGL video display */
     sys->gl.lock = OpenglESClean;
@@ -168,7 +178,7 @@ static int Open(vlc_object_t *this)
     const vlc_fourcc_t *subpicture_chromas;
     video_format_t fmt = vd->fmt;
 
-    sys->vgl = vout_display_opengl_New (&vd->fmt, &subpicture_chromas, &sys->gl);
+    sys->vgl = vout_display_opengl_New(&vd->fmt, &subpicture_chromas, &sys->gl);
     if (!sys->vgl) {
         sys->gl.sys = NULL;
         goto bailout;
@@ -179,6 +189,7 @@ static int Open(vlc_object_t *this)
     info.has_pictures_invalid = false;
     info.has_event_thread = true;
     info.subpicture_chromas = subpicture_chromas;
+    info.has_hide_mouse = false;
 
     /* Setup vout_display_t once everything is fine */
     vd->info = info;
@@ -188,8 +199,23 @@ static int Open(vlc_object_t *this)
     vd->display = PictureDisplay;
     vd->control = Control;
 
+    /* forward our dimensions to the vout core */
+    CGSize viewSize = sys->viewContainer.frame.size;
+    vout_display_SendEventFullscreen(vd, false);
+    vout_display_SendEventDisplaySize(vd, (int)viewSize.width, (int)viewSize.height);
+
     /* */
-    [sys->glESView performSelectorOnMainThread:@selector(reshape) withObject:nil waitUntilDone:YES];
+    [[NSNotificationCenter defaultCenter] addObserver:sys->glESView
+                                             selector:@selector(applicationStateChanged:)
+                                                 name:UIApplicationWillResignActiveNotification
+                                               object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:sys->glESView
+                                             selector:@selector(applicationStateChanged:)
+                                                 name:UIApplicationDidBecomeActiveNotification
+                                               object:nil];
+    [sys->glESView performSelectorOnMainThread:@selector(reshape)
+                                    withObject:nil
+                                 waitUntilDone:YES];
 
     [autoreleasePool release];
     return VLC_SUCCESS;
@@ -205,6 +231,11 @@ void Close (vlc_object_t *this)
     vout_display_t *vd = (vout_display_t *)this;
     vout_display_sys_t *sys = vd->sys;
 
+    if (sys->tapRecognizer) {
+        [sys->tapRecognizer.view removeGestureRecognizer:sys->tapRecognizer];
+        [sys->tapRecognizer release];
+    }
+
     [sys->glESView setVoutDisplay:nil];
 
     var_Destroy (vd, "drawable-nsobject");
@@ -229,12 +260,10 @@ static int Control(vout_display_t *vd, int query, va_list ap)
 {
     vout_display_sys_t *sys = vd->sys;
 
-    switch (query)
-    {
-        case VOUT_DISPLAY_CHANGE_FULLSCREEN:
-        case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
+    switch (query) {
         case VOUT_DISPLAY_HIDE_MOUSE:
-            return VLC_SUCCESS;
+            return VLC_EGENERIC;
+
         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
         case VOUT_DISPLAY_CHANGE_ZOOM:
         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
@@ -248,7 +277,6 @@ static int Control(vout_display_t *vd, int query, va_list ap)
 
             const vout_display_cfg_t *cfg;
             const video_format_t *source;
-            bool is_forced = false;
 
             if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
                 source = (const video_format_t *)va_arg(ap, const video_format_t *);
@@ -256,25 +284,21 @@ static int Control(vout_display_t *vd, int query, va_list ap)
             } else {
                 source = &vd->source;
                 cfg = (const vout_display_cfg_t*)va_arg(ap, const vout_display_cfg_t *);
-                if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
-                    is_forced = (bool)va_arg(ap, int);
             }
 
-            if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE && is_forced
-                && (cfg->display.width != vd->cfg->display.width
-                    || cfg->display.height != vd->cfg->display.height))
+            /* we don't adapt anything here regardless of what the vout core
+             * wants since we are not in a traditional desktop window */
+            if (!cfg)
                 return VLC_EGENERIC;
 
-            /* we always use our current frame here, because we have some size constraints
-             in the ui vout provider */
             vout_display_cfg_t cfg_tmp = *cfg;
-            CGRect bounds;
-            bounds = [sys->glESView bounds];
+            CGSize viewSize;
+            viewSize = [sys->glESView bounds].size;
 
-            /* on HiDPI displays, the point bounds don't equal the actual pixel based bounds */
+            /* on HiDPI displays, the point bounds don't equal the actual pixels */
             CGFloat scaleFactor = sys->glESView.contentScaleFactor;
-            cfg_tmp.display.width = bounds.size.width * scaleFactor;
-            cfg_tmp.display.height = bounds.size.height * scaleFactor;
+            cfg_tmp.display.width = viewSize.width * scaleFactor;
+            cfg_tmp.display.height = viewSize.height * scaleFactor;
 
             vout_display_place_t place;
             vout_display_PlacePicture(&place, source, &cfg_tmp, false);
@@ -282,27 +306,19 @@ static int Control(vout_display_t *vd, int query, va_list ap)
                 sys->place = place;
             }
 
-            /* For resize, we call glViewport in reshape and not here.
-             This has the positive side effect that we avoid erratic sizing as we animate every resize. */
+            // x / y are top left corner, but we need the lower left one
             if (query != VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
-                // x / y are top left corner, but we need the lower left one
                 glViewport(place.x, cfg_tmp.display.height - (place.y + place.height), place.width, place.height);
 
             [autoreleasePool release];
             return VLC_SUCCESS;
         }
 
-        case VOUT_DISPLAY_GET_OPENGL:
-        {
-            vlc_gl_t **gl = va_arg(ap, vlc_gl_t **);
-            *gl = &sys->gl;
-            return VLC_SUCCESS;
-        }
-
         case VOUT_DISPLAY_RESET_PICTURES:
-            assert (0);
+            vlc_assert_unreachable ();
         default:
-            msg_Err(vd, "Unknown request %i in iOS ES 2 vout display", query);
+            msg_Err(vd, "Unknown request %d", query);
+        case VOUT_DISPLAY_CHANGE_FULLSCREEN:
             return VLC_EGENERIC;
     }
 }
@@ -311,7 +327,8 @@ static void PictureDisplay(vout_display_t *vd, picture_t *pic, subpicture_t *sub
 {
     vout_display_sys_t *sys = vd->sys;
     sys->has_first_frame = true;
-    vout_display_opengl_Display(sys->vgl, &vd->source);
+    if (likely([sys->glESView isAppActive]))
+        vout_display_opengl_Display(sys->vgl, &vd->source);
 
     picture_Release(pic);
 
@@ -321,10 +338,10 @@ static void PictureDisplay(vout_display_t *vd, picture_t *pic, subpicture_t *sub
 
 static void PictureRender(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
 {
-
     vout_display_sys_t *sys = vd->sys;
 
-    vout_display_opengl_Prepare(sys->vgl, pic, subpicture);
+    if (likely([sys->glESView isAppActive]))
+        vout_display_opengl_Prepare(sys->vgl, pic, subpicture);
 }
 
 static picture_pool_t *PicturePool(vout_display_t *vd, unsigned requested_count)
@@ -343,21 +360,23 @@ static picture_pool_t *PicturePool(vout_display_t *vd, unsigned requested_count)
 static int OpenglESClean(vlc_gl_t *gl)
 {
     vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys;
-    [sys->glESView resetBuffers];
+    if (likely([sys->glESView isAppActive]))
+        [sys->glESView resetBuffers];
     return 0;
 }
 
 static void OpenglESSwap(vlc_gl_t *gl)
 {
     vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys;
-    [[sys->glESView eaglContext] presentRenderbuffer:GL_RENDERBUFFER];
+    if (likely([sys->glESView isAppActive]))
+        [[sys->glESView eaglContext] presentRenderbuffer:GL_RENDERBUFFER];
 }
 
 /*****************************************************************************
  * Our UIView object
  *****************************************************************************/
 @implementation VLCOpenGLES2VideoView
-@synthesize voutDisplay = _voutDisplay, eaglContext = _eaglContext;
+@synthesize voutDisplay = _voutDisplay, eaglContext = _eaglContext, isAppActive = _appActive;
 
 + (Class)layerClass
 {
@@ -366,7 +385,7 @@ static void OpenglESSwap(vlc_gl_t *gl)
 
 - (id)initWithFrame:(CGRect)frame
 {
-    self = [super initWithFrame:frame]; // perform selector on main thread?
+    self = [super initWithFrame:frame];
 
     if (!self)
         return nil;
@@ -384,19 +403,22 @@ static void OpenglESSwap(vlc_gl_t *gl)
     [self performSelectorOnMainThread:@selector(reshape) withObject:nil waitUntilDone:NO];
     [self setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
 
+    _appActive = ([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
+
     return self;
 }
 
 - (void)dealloc
 {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
     [_eaglContext release];
     [super dealloc];
 }
 
-/* we don't get the correct scale factor if we don't overwrite this method */
-- (void)drawRect:(CGRect) rect
+- (void)didMoveToWindow
 {
-    [super drawRect:rect];
+    self.contentScaleFactor = self.window.screen.scale;
+    _bufferNeedReset = YES;
 }
 
 - (void)createBuffers
@@ -439,7 +461,6 @@ static void OpenglESSwap(vlc_gl_t *gl)
 - (void)resetBuffers
 {
     if (_bufferNeedReset) {
-        NSLog(@"actually resetting buffers");
         [self destroyBuffers];
         [self createBuffers];
         _bufferNeedReset = NO;
@@ -448,21 +469,18 @@ static void OpenglESSwap(vlc_gl_t *gl)
 
 - (void)layoutSubviews
 {
-    NSLog(@"layoutSubviews");
     /* this method is called as soon as we are resized.
      * so set a variable to re-create our buffers on the next clean event */
     _bufferNeedReset = YES;
 }
 
-/**
- * Method called by Cocoa when the view is resized.
- */
 - (void)reshape
 {
     assert([[NSThread currentThread] isMainThread]);
 
-    CGRect bounds;
-    bounds = [self bounds];
+    [EAGLContext setCurrentContext:_eaglContext];
+
+    CGSize viewSize = [self bounds].size;
 
     vout_display_place_t place;
 
@@ -471,12 +489,13 @@ static void OpenglESSwap(vlc_gl_t *gl)
             vout_display_cfg_t cfg_tmp = *(_voutDisplay->cfg);
             CGFloat scaleFactor = self.contentScaleFactor;
 
-            cfg_tmp.display.width  = bounds.size.width * scaleFactor;
-            cfg_tmp.display.height = bounds.size.height * scaleFactor;
+            cfg_tmp.display.width  = viewSize.width * scaleFactor;
+            cfg_tmp.display.height = viewSize.height * scaleFactor;
 
             vout_display_PlacePicture(&place, &_voutDisplay->source, &cfg_tmp, false);
             _voutDisplay->sys->place = place;
-            vout_display_SendEventDisplaySize(_voutDisplay, bounds.size.width * scaleFactor, bounds.size.height * scaleFactor, _voutDisplay->cfg->is_fullscreen);
+            vout_display_SendEventDisplaySize(_voutDisplay, viewSize.width * scaleFactor,
+                                              viewSize.height * scaleFactor);
         }
     }
 
@@ -484,6 +503,29 @@ static void OpenglESSwap(vlc_gl_t *gl)
     glViewport(place.x, place.y, place.width, place.height);
 }
 
+- (void)tapRecognized:(UITapGestureRecognizer *)tapRecognizer
+{
+    UIGestureRecognizerState state = [tapRecognizer state];
+    CGPoint touchPoint = [tapRecognizer locationInView:self];
+    CGFloat scaleFactor = self.contentScaleFactor;
+    vout_display_SendMouseMovedDisplayCoordinates(_voutDisplay, ORIENT_NORMAL,
+                                                  (int)touchPoint.x * scaleFactor, (int)touchPoint.y * scaleFactor,
+                                                  &_voutDisplay->sys->place);
+
+    vout_display_SendEventMousePressed(_voutDisplay, MOUSE_BUTTON_LEFT);
+    vout_display_SendEventMouseReleased(_voutDisplay, MOUSE_BUTTON_LEFT);
+}
+
+- (void)applicationStateChanged:(NSNotification *)notification
+{
+    if ([[notification name] isEqualToString:UIApplicationWillResignActiveNotification]
+        || [[notification name] isEqualToString:UIApplicationDidEnterBackgroundNotification]
+        || [[notification name] isEqualToString:UIApplicationWillTerminateNotification])
+        _appActive = NO;
+    else
+        _appActive = YES;
+}
+
 - (void)updateConstraints
 {
     [self reshape];