iosdisplay.m 8.98 KB
Newer Older
jehan's avatar
jehan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 iosdisplay.m
 Copyright (C) 2011 Belledonne Communications, Grenoble, France
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 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 General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */


#if defined(HAVE_CONFIG_H)
#include "mediastreamer-config.h"
#endif
#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/msv4l.h"
#include "mediastreamer2/mswebcam.h"
28
#include "mediastreamer2/mscommon.h"
jehan's avatar
jehan committed
29
#include "nowebcam.h"
jehan's avatar
jehan committed
30
#include "mediastreamer2/msfilter.h"
jehan's avatar
jehan committed
31 32
#include "scaler.h"

Yann Diorcet's avatar
Yann Diorcet committed
33
#import <Foundation/Foundation.h>
34
#import <AVFoundation/AVFoundation.h>
Yann Diorcet's avatar
Yann Diorcet committed
35 36 37 38 39 40 41 42 43
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/EAGLDrawable.h>
#import <OpenGLES/ES2/gl.h>

#include "opengles_display.h"

@interface IOSDisplay : UIView {
44
@public
45 46
	struct opengles_display* display_helper;
	
47
@private
48 49 50 51 52 53
	NSRecursiveLock* lock;
	EAGLContext* context;
	GLuint defaultFrameBuffer, colorRenderBuffer;
	id displayLink;
	BOOL animating;
	CGRect prevBounds;
Yann Diorcet's avatar
Yann Diorcet committed
54 55 56
}

@property (nonatomic, retain) UIView* parentView;
57 58
@property (assign) int deviceRotation;
@property (assign) int displayRotation;
59 60

@end
jehan's avatar
jehan committed
61 62 63

@implementation IOSDisplay

Yann Diorcet's avatar
Yann Diorcet committed
64
@synthesize parentView;
65 66
@synthesize deviceRotation;
@synthesize displayRotation;
67

Yann Diorcet's avatar
Yann Diorcet committed
68
- (void)initIOSDisplay {
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	self->deviceRotation = 0;
	self->lock = [[NSRecursiveLock alloc] init];
	self->display_helper = ogl_display_new();
	self->prevBounds = CGRectMake(0, 0, 0, 0);
	self->context = nil;

	// Init view
	[self setOpaque:YES];
	[self setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
	
	// Init layer
	CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
	[eaglLayer setOpaque:YES];
	[eaglLayer setDrawableProperties: [NSDictionary dictionaryWithObjectsAndKeys:
									   [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
									   kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
									   nil]];
86 87
}

88
- (id)init {
89 90 91 92 93
	self = [super init];
	if (self) {
		[self initIOSDisplay];
	}
	return self;
94 95 96
}

- (id)initWithCoder:(NSCoder *)coder {
97 98 99 100 101
	self = [super initWithCoder:coder];
	if (self) {
		[self initIOSDisplay];
	}
	return self;
102 103 104
}

- (id)initWithFrame:(CGRect)frame {
105 106 107 108 109
	self = [super initWithFrame:frame];
	if (self) {
		[self initIOSDisplay];
	}
	return self;
110 111
}

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
- (void)initOpenGL {
	// Init OpenGL context
	context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
	if (!context || ![EAGLContext setCurrentContext:context]) {
		ms_error("Opengl context failure");
		return;
	}
	
	glGenFramebuffers(1, &defaultFrameBuffer);
	glGenRenderbuffers(1, &colorRenderBuffer);
	glBindFramebuffer(GL_FRAMEBUFFER, defaultFrameBuffer);
	glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderBuffer);
	
	ogl_display_init(display_helper, prevBounds.size.width, prevBounds.size.height);
	
	// release GL context for this thread
	[EAGLContext setCurrentContext:nil];
}

- (void)drawView {	
	/* no opengl es call made when in background */ 
	if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive)
		return;
	if([lock tryLock]) {
		if(context == nil) {
			[self initOpenGL];
		}
		if (![EAGLContext setCurrentContext:context]) {
			ms_error("Failed to bind GL context");
			return;
		}
		
		if (!CGRectEqualToRect(prevBounds, [self bounds])) {
			CAEAGLLayer* layer = (CAEAGLLayer*)self.layer;
			
			if (prevBounds.size.width != 0 || prevBounds.size.height != 0) {
				// release previously allocated storage
				[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:nil];
			}
			
			prevBounds = [self bounds];
			
			// allocate storage
			if ([context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]) {
				ms_message("GL renderbuffer allocation size (layer %p frame size: %f x %f)", layer, layer.frame.size.width, layer.frame.size.height);
				ogl_display_set_size(display_helper, prevBounds.size.width, prevBounds.size.height);
				glClear(GL_COLOR_BUFFER_BIT);
			} else {
				ms_error("Error in renderbufferStorage (layer %p frame size: %f x %f)", layer, layer.frame.size.width, layer.frame.size.height);
			}
		}
		
		if (!animating) {
			glClear(GL_COLOR_BUFFER_BIT);
		} else {
168
			ogl_display_render(display_helper, deviceRotation);
169 170 171 172 173
		}
		
		[context presentRenderbuffer:GL_RENDERBUFFER];
		[lock unlock];
	}
174 175
}

Yann Diorcet's avatar
Yann Diorcet committed
176
- (void)setParentView:(UIView*)aparentView{
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
	if (parentView == aparentView) {
		return;
	}
	
	if(parentView != nil) {
		animating = FALSE;
		
		// stop schedule rendering
		[displayLink invalidate];
		displayLink = nil;
		
		[self drawView];
		
		// remove from parent
		[self removeFromSuperview];
		
		[parentView release];
		parentView = nil;
	}
	
	parentView = aparentView;
	
	if(parentView != nil) {
		[parentView retain];
		animating = TRUE;
		
		// add to new parent
		[self setFrame: [parentView bounds]];
		[parentView addSubview:self];
		
		// schedule rendering
		displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(drawView)];
		[displayLink setFrameInterval:1];
		[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
	}
212 213
}

214
+ (Class)layerClass {
215
	return [CAEAGLLayer class];
216 217
}

218
- (void)dealloc {
219 220 221 222 223 224
	[EAGLContext setCurrentContext:context];
	
	ogl_display_uninit(display_helper, TRUE);
	ogl_display_free(display_helper);
	display_helper = NULL;
	
Yann Diorcet's avatar
Yann Diorcet committed
225 226
	glDeleteFramebuffers(1, &defaultFrameBuffer);
	glDeleteRenderbuffers(1, &colorRenderBuffer);
227 228 229 230 231 232 233 234 235
	
	[EAGLContext setCurrentContext:0];

	[context release];
	[lock release];
	
	self.parentView = nil;
	
	[super dealloc];
jehan's avatar
jehan committed
236
}
jehan's avatar
jehan committed
237

238 239 240
@end

static void iosdisplay_init(MSFilter *f) {
241 242 243
	NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
	f->data = [[IOSDisplay alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
	[loopPool drain];
244 245
}

246 247
static void iosdisplay_process(MSFilter *f) {
	IOSDisplay* thiz = (IOSDisplay*)f->data;
248

249
	mblk_t *m = ms_queue_peek_last(f->inputs[0]);
250 251 252 253 254 255 256 257 258
	
	if (thiz != nil && m != nil) {
		ogl_display_set_yuv_to_display(thiz->display_helper, m);
	}
	ms_queue_flush(f->inputs[0]);
	
	if (f->inputs[1] != NULL) {
		ms_queue_flush(f->inputs[1]);
	}
jehan's avatar
jehan committed
259 260
}

Yann Diorcet's avatar
Yann Diorcet committed
261
static void iosdisplay_uninit(MSFilter *f) {
262 263 264 265 266 267 268 269 270
	IOSDisplay* thiz = (IOSDisplay*)f->data;

	if (thiz != nil) {
		NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
		// Remove from parent view in order to release all reference to IOSDisplay
		[thiz performSelectorOnMainThread:@selector(setParentView:) withObject:nil waitUntilDone:NO];
		[thiz release];
		[loopPool drain];
	}
jehan's avatar
jehan committed
271 272 273
}

static int iosdisplay_set_native_window(MSFilter *f, void *arg) {
274 275 276 277 278 279 280 281 282
	IOSDisplay *thiz = (IOSDisplay*)f->data;
	UIView* parentView = *(UIView**)arg;
	if (thiz != nil) {
		NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
		// set current parent view
		[thiz performSelectorOnMainThread:@selector(setParentView:) withObject:parentView waitUntilDone:NO];
		[loopPool drain];
	}
	return 0;
jehan's avatar
jehan committed
283 284 285
}

static int iosdisplay_get_native_window(MSFilter *f, void *arg) {
286 287 288 289 290 291
	IOSDisplay* thiz = (IOSDisplay*)f->data;
	if (!thiz)
		return 0;
	unsigned long *winId = (unsigned long*)arg;
	*winId = (unsigned long)[thiz parentView];
	return 0;
jehan's avatar
jehan committed
292 293
}

294
static int iosdisplay_set_device_orientation(MSFilter* f, void* arg) {
295 296 297 298 299
	IOSDisplay* thiz = (IOSDisplay*)f->data;
	if (!thiz)
		return 0;
	thiz.deviceRotation = *((int*)arg);
	return 0;
Yann Diorcet's avatar
Yann Diorcet committed
300 301 302
}

static int iosdisplay_set_device_orientation_display(MSFilter* f, void* arg) {
303 304 305 306 307
	IOSDisplay* thiz = (IOSDisplay*)f->data;
	if (!thiz)
		return 0;
	thiz.displayRotation = *((int*)arg);
	return 0;
308 309
}

310
static int iosdisplay_set_zoom(MSFilter* f, void* arg) {
311 312 313 314 315
	IOSDisplay* thiz = (IOSDisplay*)f->data;
	if (!thiz)
		return 0;
	ogl_display_zoom(thiz->display_helper, arg);
	return 0;
316 317
}

318 319
static MSFilterMethod iosdisplay_methods[] = {
	{ MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID, iosdisplay_set_native_window },
320 321 322 323
	{ MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID, iosdisplay_get_native_window },
	{ MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION, iosdisplay_set_device_orientation },
	{ MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION, iosdisplay_set_device_orientation_display },
	{ MS_VIDEO_DISPLAY_ZOOM, iosdisplay_set_zoom },
324
	{ 0, NULL }
jehan's avatar
jehan committed
325
};
326 327

MSFilterDesc ms_iosdisplay_desc = {
jehan's avatar
jehan committed
328 329 330 331 332 333 334 335
	.id=MS_IOS_DISPLAY_ID, /* from Allfilters.h*/
	.name="IOSDisplay",
	.text="IOS Display filter.",
	.category=MS_FILTER_OTHER,
	.ninputs=2, /*number of inputs*/
	.noutputs=0, /*number of outputs*/
	.init=iosdisplay_init,
	.process=iosdisplay_process,
Yann Diorcet's avatar
Yann Diorcet committed
336
	.uninit=iosdisplay_uninit,
jehan's avatar
jehan committed
337 338
	.methods=iosdisplay_methods
};
jehan's avatar
jehan committed
339
MS_FILTER_DESC_EXPORT(ms_iosdisplay_desc)