From ac0421f0a215fa9533a72fde494f3fca4d48cd2f Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Tue, 7 Apr 2015 01:47:51 +0000 Subject: Fixed #1835: Fixed crash in iOS OpenGL renderer when in background and assertion during destruction git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5047 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/src/pjmedia-videodev/ios_opengl_dev.m | 119 +++++++++++++++++++------- 1 file changed, 87 insertions(+), 32 deletions(-) diff --git a/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m b/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m index 150fcad5..2826bf4b 100644 --- a/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m +++ b/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m @@ -31,6 +31,12 @@ #define THIS_FILE "ios_opengl_dev.c" +/* If this is enabled, iOS OpenGL will not return error during creation when + * in the background. Instead, it will perform the initialization later + * during rendering. + */ +#define ALLOW_DELAYED_INITIALIZATION 0 + typedef struct iosgl_fmt_info { pjmedia_format_id pjmedia_format; @@ -60,15 +66,16 @@ struct iosgl_stream pjmedia_vid_dev_cb vid_cb; /**< Stream callback */ void *user_data; /**< Application data */ + pj_bool_t is_running; pj_status_t status; pj_timestamp frame_ts; unsigned ts_inc; pjmedia_rect_size vid_size; const pjmedia_frame *frame; - gl_buffers *gl_buf; - GLView *gl_view; - EAGLContext *ogl_context; + gl_buffers *gl_buf; + GLView *gl_view; + EAGLContext *ogl_context; }; @@ -128,7 +135,7 @@ static iosgl_fmt_info* get_iosgl_format_info(pjmedia_format_id id) return [CAEAGLLayer class]; } -- (void) init_buffers +- (void) init_gl { /* Initialize OpenGL ES 2 */ CAEAGLLayer *eagl_layer = (CAEAGLLayer *)[stream->gl_view layer]; @@ -139,11 +146,19 @@ static iosgl_fmt_info* get_iosgl_format_info(pjmedia_format_id id) kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; + /* EAGLContext initialization will crash if we are in background mode */ + if ([UIApplication sharedApplication].applicationState == + UIApplicationStateBackground) { + stream->status = PJMEDIA_EVID_INIT; + return; + } + stream->ogl_context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2]; if (!stream->ogl_context || ![EAGLContext setCurrentContext:stream->ogl_context]) { + NSLog(@"Failed in initializing EAGLContext"); stream->status = PJMEDIA_EVID_SYSERR; return; } @@ -159,13 +174,45 @@ static iosgl_fmt_info* get_iosgl_format_info(pjmedia_format_id id) stream->status = pjmedia_vid_dev_opengl_init_buffers(stream->gl_buf); } +- (void)deinit_gl +{ + if ([EAGLContext currentContext] == stream->ogl_context) + [EAGLContext setCurrentContext:nil]; + + if (stream->ogl_context) { + [stream->ogl_context release]; + stream->ogl_context = NULL; + } + + if (stream->gl_buf) { + pjmedia_vid_dev_opengl_destroy_buffers(stream->gl_buf); + stream->gl_buf = NULL; + } + + [self removeFromSuperview]; +} + - (void)render { /* Don't make OpenGLES calls while in the background */ if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) + { return; - + } + +#if ALLOW_DELAYED_INITIALIZATION + if (stream->status != PJ_SUCCESS) { + if (stream->status == PJMEDIA_EVID_INIT) { + [self init_gl]; + NSLog(@"Initializing OpenGL now %s", stream->status == PJ_SUCCESS? + "success": "failed"); + } + + return; + } +#endif + if (![EAGLContext setCurrentContext:stream->ogl_context]) { /* Failed to set context */ return; @@ -175,6 +222,13 @@ static iosgl_fmt_info* get_iosgl_format_info(pjmedia_format_id id) stream->frame->buf); } +- (void)finish_render +{ + /* Do nothing. This function is serialized in the main thread, so when + * it is called, we can be sure that render() has completed. + */ +} + @end /* API: create stream */ @@ -218,11 +272,20 @@ pjmedia_vid_dev_opengl_imp_create_stream(pj_pool_t *pool, /* Perform OpenGL buffer initializations in the main thread. */ strm->status = PJ_SUCCESS; - [strm->gl_view performSelectorOnMainThread:@selector(init_buffers) + [strm->gl_view performSelectorOnMainThread:@selector(init_gl) withObject:nil waitUntilDone:YES]; - if ((status = strm->status) != PJ_SUCCESS) { - PJ_LOG(3, (THIS_FILE, "Unable to create and init OpenGL buffers")); - goto on_error; + if ((status = strm->status) != PJ_SUCCESS) + { + if (status == PJMEDIA_EVID_INIT) { + PJ_LOG(3, (THIS_FILE, "Failed to initialize iOS OpenGL because " + "we are in background")); +#if !ALLOW_DELAYED_INITIALIZATION + goto on_error; +#endif + } else { + PJ_LOG(3, (THIS_FILE, "Unable to create and init OpenGL buffers")); + goto on_error; + } } /* Apply the remaining settings */ @@ -376,9 +439,8 @@ static pj_status_t iosgl_stream_start(pjmedia_vid_dev_stream *strm) { struct iosgl_stream *stream = (struct iosgl_stream*)strm; - PJ_UNUSED_ARG(stream); - PJ_LOG(4, (THIS_FILE, "Starting ios opengl stream")); + stream->is_running = PJ_TRUE; return PJ_SUCCESS; } @@ -389,12 +451,13 @@ static pj_status_t iosgl_stream_put_frame(pjmedia_vid_dev_stream *strm, { struct iosgl_stream *stream = (struct iosgl_stream*)strm; + if (!stream->is_running) + return PJ_EINVALIDOP; + stream->frame = frame; /* Perform OpenGL drawing in the main thread. */ [stream->gl_view performSelectorOnMainThread:@selector(render) withObject:nil waitUntilDone:YES]; - // dispatch_async(dispatch_get_main_queue(), - // ^{[stream->gl_view render];}); [stream->ogl_context presentRenderbuffer:GL_RENDERBUFFER]; @@ -406,9 +469,12 @@ static pj_status_t iosgl_stream_stop(pjmedia_vid_dev_stream *strm) { struct iosgl_stream *stream = (struct iosgl_stream*)strm; - PJ_UNUSED_ARG(stream); - PJ_LOG(4, (THIS_FILE, "Stopping ios opengl stream")); + stream->is_running = PJ_FALSE; + + /* Wait until the rendering finishes */ + [stream->gl_view performSelectorOnMainThread:@selector(finish_render) + withObject:nil waitUntilDone:YES]; return PJ_SUCCESS; } @@ -421,28 +487,17 @@ static pj_status_t iosgl_stream_destroy(pjmedia_vid_dev_stream *strm) PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); - iosgl_stream_stop(strm); - - if ([EAGLContext currentContext] == stream->ogl_context) - [EAGLContext setCurrentContext:nil]; - - if (stream->ogl_context) { - [stream->ogl_context release]; - stream->ogl_context = NULL; - } + if (stream->is_running) + iosgl_stream_stop(strm); if (stream->gl_view) { - UIView *view = stream->gl_view; - dispatch_async(dispatch_get_main_queue(), - ^{ - [view removeFromSuperview]; - [view release]; - }); + [stream->gl_view performSelectorOnMainThread:@selector(deinit_gl) + withObject:nil waitUntilDone:YES]; + + [stream->gl_view release]; stream->gl_view = NULL; } - pjmedia_vid_dev_opengl_destroy_buffers(stream->gl_buf); - pj_pool_release(stream->pool); return PJ_SUCCESS; -- cgit v1.2.3