/*****************************************************************************
* 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
/**
* 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);
* 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);
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;
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];
[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 = YES;
/* Initialize common OpenGL video display */
sys->gl.lock = OpenglESClean;
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;
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;
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;
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");
{
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:
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 *);
} 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);
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);
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;
}
}
{
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);
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)
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
{
- (id)initWithFrame:(CGRect)frame
{
- self = [super initWithFrame:frame]; // perform selector on main thread?
+ self = [super initWithFrame:frame];
if (!self)
return nil;
[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
- (void)resetBuffers
{
if (_bufferNeedReset) {
- NSLog(@"actually resetting buffers");
[self destroyBuffers];
[self createBuffers];
_bufferNeedReset = NO;
- (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;
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);
}
}
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];