Index: /pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_dev.m
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_dev.m	(revision 4823)
+++ /pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_dev.m	(revision 4824)
@@ -30,9 +30,10 @@
 #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
 
@@ -45,5 +46,5 @@
 static ios_fmt_info ios_fmts[] =
 {
-    {PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA} ,
+    { PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA }
 };
 
@@ -52,4 +53,5 @@
 {
     pjmedia_vid_dev_info	 info;
+    AVCaptureDevice             *dev;
 };
 
@@ -79,4 +81,5 @@
     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   */
@@ -93,10 +96,15 @@
     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;
 };
 
@@ -186,29 +194,60 @@
     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];
@@ -226,7 +265,13 @@
     }
     
-    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;
 }
@@ -279,9 +324,10 @@
 {
     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));
@@ -309,39 +355,16 @@
 {    
     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];
 }    
@@ -361,5 +384,5 @@
     
     /* Lock the base address of the pixel buffer */
-    CVPixelBufferLockBaseAddress(imageBuffer, 0); 
+    CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
     
     frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
@@ -369,11 +392,20 @@
     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,4 +422,42 @@
     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 */
@@ -402,9 +472,8 @@
     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);
@@ -431,5 +500,6 @@
     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));
@@ -446,17 +516,32 @@
 	    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) {
@@ -472,4 +557,6 @@
 	    goto on_error;
 	}
+        
+        strm->video_output.alwaysDiscardsLateVideoFrames = YES;
 	[strm->cap_session addOutput:strm->video_output];
 	
@@ -479,34 +566,43 @@
 	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];
@@ -514,20 +610,11 @@
 	}
         
-        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);
-    }    
-    
-    /* 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);
-     }
-     */
+	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);
+    }
+    
     /* Done */
     strm->base.op = &stream_op;
@@ -552,10 +639,4 @@
     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;
 }
@@ -567,16 +648,21 @@
 {
     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;
 }
 
@@ -588,11 +674,147 @@
     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;
     }
 
@@ -607,5 +829,5 @@
     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) {
@@ -625,18 +847,16 @@
 {
     struct ios_stream *stream = (struct ios_stream*)strm;
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    
-    pj_assert(stream->frame_size >= frame->size);
-    pj_memcpy(stream->buf, frame->buf, frame->size);
+    //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);
+    
     /* 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;
@@ -650,5 +870,5 @@
     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])
@@ -668,31 +888,34 @@
     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;
-    }
-/*    if (stream->video_output) {
-	[stream->video_output release];
-	stream->video_output = NULL;
-    }
-*/
-    if (stream->render_queue) {
-        dispatch_release(stream->render_queue);
-        stream->render_queue = NULL;
+	stream->vout_delegate = nil;
+    }
+    if (stream->video_output) {
+        stream->video_output = nil;
+    }
+
+    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;
     }
 
@@ -702,4 +925,4 @@
 }
 
-#endif
+#endif  /* __IPHONE_4_0 */
 #endif	/* PJMEDIA_VIDEO_DEV_HAS_IOS */
