summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2014-04-24 08:37:45 +0000
committerNanang Izzuddin <nanang@teluu.com>2014-04-24 08:37:45 +0000
commit9d63e832e79333fde1b4b663b68eb8c7825738c9 (patch)
tree65ce9446f273c451b7f591700354834f5a03bed1
parent199aa76e0bf487056301d3025d5f1bad84d54445 (diff)
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
-rw-r--r--pjmedia/src/pjmedia-videodev/ios_dev.m553
1 files changed, 388 insertions, 165 deletions
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 <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
+#import <QuartzCore/QuartzCore.h>
#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,
+ &param->window_hide);
+ }
+ if (param->flags & PJMEDIA_VID_DEV_CAP_ORIENTATION) {
+ ios_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_ORIENTATION,
+ &param->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,
- &param->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 */