From 9d63e832e79333fde1b4b663b68eb8c7825738c9 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 24 Apr 2014 08:37:45 +0000 Subject: Re #1762: - enumerate all capture devices - fast switch between capture devices - enhance simple renderer (a bit simpler, add capabilities) git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4824 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/src/pjmedia-videodev/ios_dev.m | 553 +++++++++++++++++++++++---------- 1 file changed, 388 insertions(+), 165 deletions(-) (limited to 'pjmedia/src/pjmedia-videodev/ios_dev.m') diff --git a/pjmedia/src/pjmedia-videodev/ios_dev.m b/pjmedia/src/pjmedia-videodev/ios_dev.m index 307a445d..929f8d93 100644 --- a/pjmedia/src/pjmedia-videodev/ios_dev.m +++ b/pjmedia/src/pjmedia-videodev/ios_dev.m @@ -29,11 +29,12 @@ #import #import +#import #define THIS_FILE "ios_dev.c" #define DEFAULT_CLOCK_RATE 90000 -#define DEFAULT_WIDTH 480 -#define DEFAULT_HEIGHT 360 +#define DEFAULT_WIDTH 352 +#define DEFAULT_HEIGHT 288 #define DEFAULT_FPS 15 typedef struct ios_fmt_info @@ -44,13 +45,14 @@ typedef struct ios_fmt_info static ios_fmt_info ios_fmts[] = { - {PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA} , + { PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA } }; /* qt device info */ struct ios_dev_info { pjmedia_vid_dev_info info; + AVCaptureDevice *dev; }; /* qt factory */ @@ -78,6 +80,7 @@ struct ios_stream pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool */ + struct ios_factory *factory; /**< Factory */ pjmedia_vid_dev_cb vid_cb; /**< Stream callback */ void *user_data; /**< Application data */ @@ -92,12 +95,17 @@ struct ios_stream AVCaptureVideoDataOutput *video_output; VOutDelegate *vout_delegate; - UIImageView *imgView; - void *buf; - dispatch_queue_t render_queue; + void *render_buf; + pj_size_t render_buf_size; + CGDataProviderRef render_data_provider; + UIView *render_view; pj_timestamp frame_ts; unsigned ts_inc; + + pj_bool_t thread_initialized; + pj_thread_desc thread_desc; + pj_thread_t *thread; }; @@ -185,31 +193,62 @@ static pj_status_t ios_factory_init(pjmedia_vid_dev_factory *f) { struct ios_factory *qf = (struct ios_factory*)f; struct ios_dev_info *qdi; - unsigned i, l; + unsigned i, l, first_idx, front_idx = -1; + enum { MAX_DEV_COUNT = 8 }; /* Initialize input and output devices here */ qf->dev_info = (struct ios_dev_info*) - pj_pool_calloc(qf->pool, 2, + pj_pool_calloc(qf->pool, MAX_DEV_COUNT, sizeof(struct ios_dev_info)); - qf->dev_count = 0; + + /* Init output device */ qdi = &qf->dev_info[qf->dev_count++]; pj_bzero(qdi, sizeof(*qdi)); - strcpy(qdi->info.name, "iOS UIView"); - strcpy(qdi->info.driver, "iOS"); + pj_ansi_strncpy(qdi->info.name, "UIView", sizeof(qdi->info.name)); + pj_ansi_strncpy(qdi->info.driver, "iOS", sizeof(qdi->info.driver)); qdi->info.dir = PJMEDIA_DIR_RENDER; qdi->info.has_callback = PJ_FALSE; qdi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW; + /* Init input device */ + first_idx = qf->dev_count; if (NSClassFromString(@"AVCaptureSession")) { - qdi = &qf->dev_info[qf->dev_count++]; - pj_bzero(qdi, sizeof(*qdi)); - strcpy(qdi->info.name, "iOS AVCapture"); - strcpy(qdi->info.driver, "iOS"); - qdi->info.dir = PJMEDIA_DIR_CAPTURE; - qdi->info.has_callback = PJ_TRUE; + for (AVCaptureDevice *device in [AVCaptureDevice devices]) { + if (![device hasMediaType:AVMediaTypeVideo] || + qf->dev_count >= MAX_DEV_COUNT) + { + continue; + } + + if (front_idx == -1 && + [device position] == AVCaptureDevicePositionFront) + { + front_idx = qf->dev_count; + } + + qdi = &qf->dev_info[qf->dev_count++]; + pj_bzero(qdi, sizeof(*qdi)); + pj_ansi_strncpy(qdi->info.name, [[device localizedName] UTF8String], + sizeof(qdi->info.name)); + pj_ansi_strncpy(qdi->info.driver, "iOS", sizeof(qdi->info.driver)); + qdi->info.dir = PJMEDIA_DIR_CAPTURE; + qdi->info.has_callback = PJ_TRUE; + qdi->info.caps = PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW | + PJMEDIA_VID_DEV_CAP_SWITCH | + PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW; + qdi->dev = device; + } + } + + /* Set front camera to be the first input device (as default dev) */ + if (front_idx != -1 && front_idx != first_idx) { + struct ios_dev_info tmp_dev_info = qf->dev_info[first_idx]; + qf->dev_info[first_idx] = qf->dev_info[front_idx]; + qf->dev_info[front_idx] = tmp_dev_info; } + /* Set supported formats */ for (i = 0; i < qf->dev_count; i++) { qdi = &qf->dev_info[i]; qdi->info.fmt_cnt = PJ_ARRAY_SIZE(ios_fmts); @@ -225,9 +264,15 @@ static pj_status_t ios_factory_init(pjmedia_vid_dev_factory *f) } } - PJ_LOG(4, (THIS_FILE, "iOS video initialized with %d devices", + PJ_LOG(4, (THIS_FILE, "iOS video initialized with %d devices:", qf->dev_count)); - + for (i = 0; i < qf->dev_count; i++) { + qdi = &qf->dev_info[i]; + PJ_LOG(4, (THIS_FILE, "%2d: [%s] %s - %s", i, + (qdi->info.dir==PJMEDIA_DIR_CAPTURE? "Capturer":"Renderer"), + qdi->info.driver, qdi->info.name)); + } + return PJ_SUCCESS; } @@ -278,11 +323,12 @@ static pj_status_t ios_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_param *param) { struct ios_factory *qf = (struct ios_factory*)f; - struct ios_dev_info *di = &qf->dev_info[index]; + struct ios_dev_info *di; PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV); - PJ_UNUSED_ARG(pool); + + di = &qf->dev_info[index]; pj_bzero(param, sizeof(*param)); if (di->info.dir & PJMEDIA_DIR_CAPTURE) { @@ -308,41 +354,18 @@ static pj_status_t ios_factory_default_param(pj_pool_t *pool, - (void)update_image { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - /* Create a device-dependent RGB color space */ - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - - /* Create a bitmap graphics context with the sample buffer data */ - CGContextRef context = - CGBitmapContextCreate(stream->buf, stream->size.w, stream->size.h, 8, - stream->bytes_per_row, colorSpace, - kCGBitmapByteOrder32Little | - kCGImageAlphaPremultipliedFirst); - - /** - * Create a Quartz image from the pixel data in the bitmap graphics - * context - */ - CGImageRef quartzImage = CGBitmapContextCreateImage(context); - - /* Free up the context and color space */ - CGContextRelease(context); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGImageRef cgIm = CGImageCreate(stream->size.w, stream->size.h, + 8, 32, stream->bytes_per_row, colorSpace, + kCGImageAlphaFirst | + kCGBitmapByteOrder32Little, + stream->render_data_provider, 0, + false, kCGRenderingIntentDefault); CGColorSpaceRelease(colorSpace); - /* Create an image object from the Quartz image */ - UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 - orientation:UIImageOrientationRight]; - - /* Release the Quartz image */ - CGImageRelease(quartzImage); - - dispatch_async(dispatch_get_main_queue(), - ^{[stream->imgView setImage:image];}); - /* - [stream->imgView performSelectorOnMainThread:@selector(setImage:) - withObject:image waitUntilDone:NO]; - */ - + stream->render_view.layer.contents = (__bridge id)(cgIm); + CGImageRelease(cgIm); + [pool release]; } @@ -360,7 +383,7 @@ static pj_status_t ios_factory_default_param(pj_pool_t *pool, imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); /* Lock the base address of the pixel buffer */ - CVPixelBufferLockBaseAddress(imageBuffer, 0); + CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly); frame.type = PJMEDIA_FRAME_TYPE_VIDEO; frame.buf = CVPixelBufferGetBaseAddress(imageBuffer); @@ -368,13 +391,22 @@ static pj_status_t ios_factory_default_param(pj_pool_t *pool, frame.bit_info = 0; frame.timestamp.u64 = stream->frame_ts.u64; - if (stream->vid_cb.capture_cb) + if (stream->vid_cb.capture_cb) { + if (stream->thread_initialized == 0 || !pj_thread_is_registered()) + { + pj_bzero(stream->thread_desc, sizeof(pj_thread_desc)); + pj_thread_register("ios_vdev", stream->thread_desc, + &stream->thread); + stream->thread_initialized = 1; + } + (*stream->vid_cb.capture_cb)(&stream->base, stream->user_data, &frame); + } stream->frame_ts.u64 += stream->ts_inc; /* Unlock the pixel buffer */ - CVPixelBufferUnlockBaseAddress(imageBuffer,0); + CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly); } @end @@ -390,6 +422,44 @@ static ios_fmt_info* get_ios_format_info(pjmedia_format_id id) return NULL; } + +static pj_status_t ios_init_view(struct ios_stream *strm) +{ + pjmedia_vid_dev_param *param = &strm->param; + CGRect view_rect = CGRectMake(0, 0, param->fmt.det.vid.size.w, + param->fmt.det.vid.size.h); + + if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) { + view_rect.size.width = param->disp_size.w; + view_rect.size.height = param->disp_size.h; + } + + if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) { + view_rect.origin.x = param->window_pos.x; + view_rect.origin.y = param->window_pos.y; + } + + strm->render_view = [[UIView alloc] initWithFrame:view_rect]; + strm->param.window.info.ios.window = strm->render_view; + + if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { + PJ_ASSERT_RETURN(param->window.info.ios.window, PJ_EINVAL); + ios_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW, + param->window.info.ios.window); + } + if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) { + ios_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, + ¶m->window_hide); + } + if (param->flags & PJMEDIA_VID_DEV_CAP_ORIENTATION) { + ios_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_ORIENTATION, + ¶m->orient); + } + + return PJ_SUCCESS; +} + + /* API: create stream */ static pj_status_t ios_factory_create_stream( pjmedia_vid_dev_factory *f, @@ -401,11 +471,10 @@ static pj_status_t ios_factory_create_stream( struct ios_factory *qf = (struct ios_factory*)f; pj_pool_t *pool; struct ios_stream *strm; - const pjmedia_video_format_detail *vfd; + pjmedia_video_format_detail *vfd; const pjmedia_video_format_info *vfi; pj_status_t status = PJ_SUCCESS; ios_fmt_info *ifi = get_ios_format_info(param->fmt.id); - NSError *error; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && @@ -430,7 +499,8 @@ static pj_status_t ios_factory_create_stream( strm->pool = pool; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; - + strm->factory = qf; + vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE); pj_memcpy(&strm->size, &vfd->size, sizeof(vfd->size)); strm->bpp = vfi->bpp; @@ -445,19 +515,34 @@ static pj_status_t ios_factory_create_stream( status = PJ_ENOMEM; goto on_error; } - strm->cap_session.sessionPreset = AVCaptureSessionPresetMedium; - - /* Open video device */ - AVCaptureDevice *videoDevice = - [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; - if (!videoDevice) { - status = PJMEDIA_EVID_SYSERR; - goto on_error; - } - - /* Add the video device to the session as a device input */ - strm->dev_input = [AVCaptureDeviceInput - deviceInputWithDevice:videoDevice + + /* Just hardcode to always capture 352x288 for now */ + strm->cap_session.sessionPreset = AVCaptureSessionPreset352x288; + vfd->size.w = 352; + vfd->size.h = 288; + strm->size = vfd->size; + strm->bytes_per_row = strm->size.w * strm->bpp / 8; + strm->frame_size = strm->bytes_per_row * strm->size.h; + + /* Update param as output */ + param->fmt = strm->param.fmt; + + /* Set frame rate, this may only work on iOS 7 or later */ + AVCaptureDevice *dev = qf->dev_info[param->cap_id].dev; + if ([dev respondsToSelector:@selector(activeVideoMinFrameDuration)] && + [dev lockForConfiguration:NULL]) + { + dev.activeVideoMinFrameDuration = CMTimeMake(vfd->fps.denum, + vfd->fps.num); + dev.activeVideoMaxFrameDuration = CMTimeMake(vfd->fps.denum, + vfd->fps.num); + [dev unlockForConfiguration]; + } + + /* Add the video device to the session as a device input */ + NSError *error; + strm->dev_input = [AVCaptureDeviceInput + deviceInputWithDevice:dev error: &error]; if (!strm->dev_input) { status = PJMEDIA_EVID_SYSERR; @@ -471,6 +556,8 @@ static pj_status_t ios_factory_create_stream( status = PJMEDIA_EVID_SYSERR; goto on_error; } + + strm->video_output.alwaysDiscardsLateVideoFrames = YES; [strm->cap_session addOutput:strm->video_output]; /* Configure the video output */ @@ -478,57 +565,57 @@ static pj_status_t ios_factory_create_stream( strm->vout_delegate->stream = strm; dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL); [strm->video_output setSampleBufferDelegate:strm->vout_delegate - queue:queue]; - dispatch_release(queue); + queue:queue]; + dispatch_release(queue); strm->video_output.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:ifi->ios_format], - kCVPixelBufferPixelFormatTypeKey, - [NSNumber numberWithInt: vfd->size.w], - kCVPixelBufferWidthKey, - [NSNumber numberWithInt: vfd->size.h], - kCVPixelBufferHeightKey, nil]; - strm->video_output.minFrameDuration = CMTimeMake(vfd->fps.denum, - vfd->fps.num); + kCVPixelBufferPixelFormatTypeKey, nil]; + + /* Native preview */ + if ((param->flags & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW) && + param->native_preview) + { + /* Preview layer instantiation should be in main thread! */ + dispatch_async(dispatch_get_main_queue(), ^{ + /* Create view */ + ios_init_view(strm); + + /* Create preview layer */ + AVCaptureVideoPreviewLayer *previewLayer = + [AVCaptureVideoPreviewLayer layerWithSession: strm->cap_session]; + + /* Attach preview layer to a UIView */ + CGRect r = strm->render_view.bounds; + previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + previewLayer.frame = r; + [[strm->render_view layer] addSublayer:previewLayer]; + + NSLog(@"Native preview initialized."); + }); + } + } else if (param->dir & PJMEDIA_DIR_RENDER) { + /* Create renderer stream here */ - /* Get the main window */ - UIWindow *window = [[UIApplication sharedApplication] keyWindow]; - - if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW && - param->window.info.ios.window) - window = (UIWindow *)param->window.info.ios.window; - - pj_assert(window); - strm->imgView = [[UIImageView alloc] initWithFrame:[window bounds]]; - if (!strm->imgView) { - status = PJ_ENOMEM; - goto on_error; - } - [window addSubview:strm->imgView]; - + + status = ios_init_view(strm); + if (status != PJ_SUCCESS) + goto on_error; + if (!strm->vout_delegate) { strm->vout_delegate = [VOutDelegate alloc]; strm->vout_delegate->stream = strm; } - strm->render_queue = dispatch_queue_create("com.pjsip.render_queue", - NULL); - if (!strm->render_queue) - goto on_error; - - strm->buf = pj_pool_alloc(pool, strm->frame_size); - } + strm->render_buf = pj_pool_alloc(pool, strm->frame_size); + strm->render_buf_size = strm->frame_size; + strm->render_data_provider = CGDataProviderCreateWithData(NULL, + strm->render_buf, strm->frame_size, + NULL); + } - /* Apply the remaining settings */ - /* - if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { - ios_stream_set_cap(&strm->base, - PJMEDIA_VID_DEV_CAP_INPUT_SCALE, - ¶m->fmt); - } - */ /* Done */ strm->base.op = &stream_op; *p_vid_strm = &strm->base; @@ -551,12 +638,6 @@ static pj_status_t ios_stream_get_param(pjmedia_vid_dev_stream *s, pj_memcpy(pi, &strm->param, sizeof(*pi)); -/* if (ios_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE, - &pi->fmt.info_size) == PJ_SUCCESS) - { - pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE; - } -*/ return PJ_SUCCESS; } @@ -566,18 +647,23 @@ static pj_status_t ios_stream_get_cap(pjmedia_vid_dev_stream *s, void *pval) { struct ios_stream *strm = (struct ios_stream*)s; - - PJ_UNUSED_ARG(strm); - + PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); - if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) - { - return PJMEDIA_EVID_INVCAP; -// return PJ_SUCCESS; - } else { - return PJMEDIA_EVID_INVCAP; + switch (cap) { + case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW: + { + pjmedia_vid_dev_hwnd *hwnd = (pjmedia_vid_dev_hwnd*) pval; + hwnd->type = PJMEDIA_VID_DEV_HWND_TYPE_NONE; + hwnd->info.ios.window = (void*)strm->render_view; + return PJ_SUCCESS; + } + + default: + break; } + + return PJMEDIA_EVID_INVCAP; } /* API: set capability */ @@ -587,13 +673,149 @@ static pj_status_t ios_stream_set_cap(pjmedia_vid_dev_stream *s, { struct ios_stream *strm = (struct ios_stream*)s; - PJ_UNUSED_ARG(strm); - PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); - if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) - { - return PJ_SUCCESS; + switch (cap) { + /* Fast switch */ + case PJMEDIA_VID_DEV_CAP_SWITCH: + { + if (!strm->cap_session) return PJ_EINVAL; + + NSError *error; + struct ios_dev_info* di = strm->factory->dev_info; + pjmedia_vid_dev_switch_param *p = + (pjmedia_vid_dev_switch_param*)pval; + + /* Verify target capture ID */ + if (p->target_id < 0 || p->target_id >= strm->factory->dev_count) + return PJ_EINVAL; + + if (di[p->target_id].info.dir != PJMEDIA_DIR_CAPTURE || + !di[p->target_id].dev) + { + return PJ_EINVAL; + } + + /* Just return if current and target device are the same */ + if (strm->param.cap_id == p->target_id) + return PJ_SUCCESS; + + /* Ok, let's do the switch */ + AVCaptureDeviceInput *cur_dev_input = strm->dev_input; + //[AVCaptureDeviceInput + // deviceInputWithDevice:di[strm->param.cap_id].dev + // error:&error]; + AVCaptureDeviceInput *new_dev_input = + [AVCaptureDeviceInput + deviceInputWithDevice:di[p->target_id].dev + error:&error]; + + [strm->cap_session beginConfiguration]; + [strm->cap_session removeInput:cur_dev_input]; + [strm->cap_session addInput:new_dev_input]; + [strm->cap_session commitConfiguration]; + + strm->dev_input = new_dev_input; + strm->param.cap_id = p->target_id; + + return PJ_SUCCESS; + } + + case PJMEDIA_VID_DEV_CAP_FORMAT: + { + const pjmedia_video_format_info *vfi; + pjmedia_video_format_detail *vfd; + pjmedia_format *fmt = (pjmedia_format *)pval; + ios_fmt_info *ifi; + + if (!(ifi = get_ios_format_info(fmt->id))) + return PJMEDIA_EVID_BADFORMAT; + + vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(), + fmt->id); + if (!vfi) + return PJMEDIA_EVID_BADFORMAT; + + pjmedia_format_copy(&strm->param.fmt, fmt); + + vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE); + pj_memcpy(&strm->size, &vfd->size, sizeof(vfd->size)); + strm->bytes_per_row = strm->size.w * strm->bpp / 8; + strm->frame_size = strm->bytes_per_row * strm->size.h; + if (strm->render_buf_size < strm->frame_size) { + strm->render_buf = pj_pool_alloc(strm->pool, strm->frame_size); + strm->render_buf_size = strm->frame_size; + CGDataProviderRelease(strm->render_data_provider); + strm->render_data_provider = CGDataProviderCreateWithData(NULL, + strm->render_buf, strm->frame_size, + NULL); + } + + return PJ_SUCCESS; + } + + case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW: + { + UIView *view = (UIView *)pval; + strm->param.window.info.ios.window = (void *)pval; + dispatch_async(dispatch_get_main_queue(), + ^{[view addSubview:strm->render_view];}); + return PJ_SUCCESS; + } + + case PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE: + { + pj_memcpy(&strm->param.disp_size, pval, + sizeof(strm->param.disp_size)); + CGRect r = strm->render_view.bounds; + r.size = CGSizeMake(strm->param.disp_size.w, + strm->param.disp_size.h); + dispatch_async(dispatch_get_main_queue(), ^{ + strm->render_view.bounds = r; + }); + return PJ_SUCCESS; + } + + case PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION: + { + pj_memcpy(&strm->param.window_pos, pval, + sizeof(strm->param.window_pos)); + dispatch_async(dispatch_get_main_queue(), ^{ + strm->render_view.center = + CGPointMake(strm->param.window_pos.x + + strm->param.disp_size.w/2.0, + strm->param.window_pos.y + + strm->param.disp_size.h/2.0); + }); + return PJ_SUCCESS; + } + + case PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE: + { + dispatch_async(dispatch_get_main_queue(), ^{ + strm->render_view.hidden = (BOOL)(*((pj_bool_t *)pval)); + }); + return PJ_SUCCESS; + } + + /* TODO: orientation for capture device */ + case PJMEDIA_VID_DEV_CAP_ORIENTATION: + { + pj_memcpy(&strm->param.orient, pval, + sizeof(strm->param.orient)); + if (strm->param.orient == PJMEDIA_ORIENT_UNKNOWN) + return PJ_SUCCESS; + + dispatch_async(dispatch_get_main_queue(), ^{ + strm->render_view.transform = + CGAffineTransformMakeRotation( + ((int)strm->param.orient-1) * -M_PI_2); + }); + return PJ_SUCCESS; + } + + default: + break; } return PJMEDIA_EVID_INVCAP; @@ -606,7 +828,7 @@ static pj_status_t ios_stream_start(pjmedia_vid_dev_stream *strm) PJ_UNUSED_ARG(stream); - PJ_LOG(4, (THIS_FILE, "Starting ios video stream")); + PJ_LOG(4, (THIS_FILE, "Starting iOS video stream")); if (stream->cap_session) { [stream->cap_session startRunning]; @@ -624,20 +846,18 @@ static pj_status_t ios_stream_put_frame(pjmedia_vid_dev_stream *strm, const pjmedia_frame *frame) { struct ios_stream *stream = (struct ios_stream*)strm; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + //NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (stream->frame_size >= frame->size) + pj_memcpy(stream->render_buf, frame->buf, frame->size); + else + pj_memcpy(stream->render_buf, frame->buf, stream->frame_size); - pj_assert(stream->frame_size >= frame->size); - pj_memcpy(stream->buf, frame->buf, frame->size); /* Perform video display in a background thread */ -/* - [stream->vout_delegate update_image]; - [NSThread detachNewThreadSelector:@selector(update_image) - toTarget:stream->vout_delegate withObject:nil]; -*/ - dispatch_async(stream->render_queue, + dispatch_async(dispatch_get_main_queue(), ^{[stream->vout_delegate update_image];}); - - [pool release]; + + //[pool release]; return PJ_SUCCESS; } @@ -649,7 +869,7 @@ static pj_status_t ios_stream_stop(pjmedia_vid_dev_stream *strm) PJ_UNUSED_ARG(stream); - PJ_LOG(4, (THIS_FILE, "Stopping ios video stream")); + PJ_LOG(4, (THIS_FILE, "Stopping iOS video stream")); if (stream->cap_session && [stream->cap_session isRunning]) [stream->cap_session stopRunning]; @@ -667,33 +887,36 @@ static pj_status_t ios_stream_destroy(pjmedia_vid_dev_stream *strm) ios_stream_stop(strm); - if (stream->imgView) { - [stream->imgView removeFromSuperview]; - [stream->imgView release]; - stream->imgView = NULL; - } - if (stream->cap_session) { + [stream->cap_session removeInput:stream->dev_input]; + [stream->cap_session removeOutput:stream->video_output]; [stream->cap_session release]; - stream->cap_session = NULL; + stream->cap_session = nil; } -/* if (stream->dev_input) { - [stream->dev_input release]; - stream->dev_input = NULL; + if (stream->dev_input) { + stream->dev_input = nil; } -*/ + if (stream->vout_delegate) { [stream->vout_delegate release]; - stream->vout_delegate = NULL; + stream->vout_delegate = nil; } -/* if (stream->video_output) { - [stream->video_output release]; - stream->video_output = NULL; + if (stream->video_output) { + stream->video_output = nil; } -*/ - if (stream->render_queue) { - dispatch_release(stream->render_queue); - stream->render_queue = NULL; + + if (stream->render_view) { + dispatch_async(dispatch_get_main_queue(), + ^{ + [stream->render_view removeFromSuperview]; + [stream->render_view release]; + }); + stream->render_view = NULL; + } + + if (stream->render_data_provider) { + CGDataProviderRelease(stream->render_data_provider); + stream->render_data_provider = NULL; } pj_pool_release(stream->pool); @@ -701,5 +924,5 @@ static pj_status_t ios_stream_destroy(pjmedia_vid_dev_stream *strm) return PJ_SUCCESS; } -#endif +#endif /* __IPHONE_4_0 */ #endif /* PJMEDIA_VIDEO_DEV_HAS_IOS */ -- cgit v1.2.3