blob: f1b832686fe161118b69e693ac1c3d722d95ffe6 [file] [log] [blame]
Sauw Ming6e6c2152010-12-14 13:03:10 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia-videodev/videodev_imp.h>
20#include <pj/assert.h>
21#include <pj/log.h>
22#include <pj/os.h>
23
24#if PJMEDIA_VIDEO_DEV_HAS_IOS
25#include "Availability.h"
26#ifdef __IPHONE_4_0
27
28#import <UIKit/UIKit.h>
29#import <AVFoundation/AVFoundation.h>
30
31#define THIS_FILE "ios_dev.c"
32#define DEFAULT_CLOCK_RATE 9000
33#define DEFAULT_WIDTH 480
34#define DEFAULT_HEIGHT 360
35#define DEFAULT_FPS 15
36
37typedef struct ios_fmt_info
38{
39 pjmedia_format_id pjmedia_format;
40 UInt32 ios_format;
41} ios_fmt_info;
42
43static ios_fmt_info ios_fmts[] =
44{
45 {PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA} ,
46};
47
48/* qt device info */
49struct ios_dev_info
50{
51 pjmedia_vid_dev_info info;
52};
53
54/* qt factory */
55struct ios_factory
56{
57 pjmedia_vid_dev_factory base;
58 pj_pool_t *pool;
59 pj_pool_factory *pf;
60
61 unsigned dev_count;
62 struct ios_dev_info *dev_info;
63};
64
65@interface VOutDelegate: NSObject
66 <AVCaptureVideoDataOutputSampleBufferDelegate>
67{
68@public
69 struct ios_stream *stream;
70}
71@end
72
73/* Video stream. */
74struct ios_stream
75{
76 pjmedia_vid_stream base; /**< Base stream */
77 pjmedia_vid_param param; /**< Settings */
78 pj_pool_t *pool; /**< Memory pool. */
79
80 pjmedia_vid_cb vid_cb; /**< Stream callback. */
81 void *user_data; /**< Application data. */
82
83 pjmedia_rect_size size;
84 pj_uint8_t bpp;
85 unsigned bytes_per_row;
86 unsigned frame_size;
87
88 AVCaptureSession *cap_session;
89 AVCaptureDeviceInput *dev_input;
90 AVCaptureVideoDataOutput *video_output;
91 VOutDelegate *vout_delegate;
92
93 UIImageView *imgView;
94 void *buf;
Benny Prijono349037b2011-03-17 11:25:19 +000095
96 pj_timestamp frame_ts;
97 unsigned ts_inc;
Sauw Ming6e6c2152010-12-14 13:03:10 +000098};
99
100
101/* Prototypes */
102static pj_status_t ios_factory_init(pjmedia_vid_dev_factory *f);
103static pj_status_t ios_factory_destroy(pjmedia_vid_dev_factory *f);
104static unsigned ios_factory_get_dev_count(pjmedia_vid_dev_factory *f);
105static pj_status_t ios_factory_get_dev_info(pjmedia_vid_dev_factory *f,
106 unsigned index,
107 pjmedia_vid_dev_info *info);
108static pj_status_t ios_factory_default_param(pj_pool_t *pool,
109 pjmedia_vid_dev_factory *f,
110 unsigned index,
111 pjmedia_vid_param *param);
112static pj_status_t ios_factory_create_stream(pjmedia_vid_dev_factory *f,
113 const pjmedia_vid_param *param,
114 const pjmedia_vid_cb *cb,
115 void *user_data,
116 pjmedia_vid_stream **p_vid_strm);
117
118static pj_status_t ios_stream_get_param(pjmedia_vid_stream *strm,
119 pjmedia_vid_param *param);
120static pj_status_t ios_stream_get_cap(pjmedia_vid_stream *strm,
121 pjmedia_vid_dev_cap cap,
122 void *value);
123static pj_status_t ios_stream_set_cap(pjmedia_vid_stream *strm,
124 pjmedia_vid_dev_cap cap,
125 const void *value);
126static pj_status_t ios_stream_start(pjmedia_vid_stream *strm);
127static pj_status_t ios_stream_put_frame(pjmedia_vid_stream *strm,
128 const pjmedia_frame *frame);
129static pj_status_t ios_stream_stop(pjmedia_vid_stream *strm);
130static pj_status_t ios_stream_destroy(pjmedia_vid_stream *strm);
131
132/* Operations */
133static pjmedia_vid_dev_factory_op factory_op =
134{
135 &ios_factory_init,
136 &ios_factory_destroy,
137 &ios_factory_get_dev_count,
138 &ios_factory_get_dev_info,
139 &ios_factory_default_param,
140 &ios_factory_create_stream
141};
142
143static pjmedia_vid_stream_op stream_op =
144{
145 &ios_stream_get_param,
146 &ios_stream_get_cap,
147 &ios_stream_set_cap,
148 &ios_stream_start,
149 NULL,
150 &ios_stream_put_frame,
151 &ios_stream_stop,
152 &ios_stream_destroy
153};
154
155
156/****************************************************************************
157 * Factory operations
158 */
159/*
160 * Init ios_ video driver.
161 */
162pjmedia_vid_dev_factory* pjmedia_ios_factory(pj_pool_factory *pf)
163{
164 struct ios_factory *f;
165 pj_pool_t *pool;
166
167 pool = pj_pool_create(pf, "ios video", 512, 512, NULL);
168 f = PJ_POOL_ZALLOC_T(pool, struct ios_factory);
169 f->pf = pf;
170 f->pool = pool;
171 f->base.op = &factory_op;
172
173 return &f->base;
174}
175
176
177/* API: init factory */
178static pj_status_t ios_factory_init(pjmedia_vid_dev_factory *f)
179{
180 struct ios_factory *qf = (struct ios_factory*)f;
181 struct ios_dev_info *qdi;
182 unsigned i, l;
183
184 /* Initialize input and output devices here */
185 qf->dev_info = (struct ios_dev_info*)
186 pj_pool_calloc(qf->pool, 2,
187 sizeof(struct ios_dev_info));
188
189 qf->dev_count = 0;
190 qdi = &qf->dev_info[qf->dev_count++];
191 pj_bzero(qdi, sizeof(*qdi));
192 strcpy(qdi->info.name, "iOS UIView");
193 strcpy(qdi->info.driver, "iOS");
194 qdi->info.dir = PJMEDIA_DIR_RENDER;
Sauw Mingab494302010-12-17 13:17:23 +0000195 qdi->info.has_callback = PJ_FALSE;
196 qdi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000197
198 if (NSClassFromString(@"AVCaptureSession")) {
199 qdi = &qf->dev_info[qf->dev_count++];
200 pj_bzero(qdi, sizeof(*qdi));
201 strcpy(qdi->info.name, "iOS AVCapture");
202 strcpy(qdi->info.driver, "iOS");
203 qdi->info.dir = PJMEDIA_DIR_CAPTURE;
204 qdi->info.has_callback = PJ_TRUE;
205 }
206
207 for (i = 0; i < qf->dev_count; i++) {
208 qdi = &qf->dev_info[i];
209 qdi->info.fmt_cnt = PJ_ARRAY_SIZE(ios_fmts);
Sauw Mingab494302010-12-17 13:17:23 +0000210 qdi->info.caps |= PJMEDIA_VID_DEV_CAP_FORMAT;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000211 qdi->info.fmt = (pjmedia_format*)
212 pj_pool_calloc(qf->pool, qdi->info.fmt_cnt,
213 sizeof(pjmedia_format));
214
215 for (l = 0; l < PJ_ARRAY_SIZE(ios_fmts); l++) {
216 pjmedia_format *fmt = &qdi->info.fmt[l];
217 pjmedia_format_init_video(fmt,
218 ios_fmts[l].pjmedia_format,
219 DEFAULT_WIDTH,
220 DEFAULT_HEIGHT,
221 DEFAULT_FPS, 1);
222 }
223 }
224
225 PJ_LOG(4, (THIS_FILE, "iOS video initialized with %d devices",
226 qf->dev_count));
227
228 return PJ_SUCCESS;
229}
230
231/* API: destroy factory */
232static pj_status_t ios_factory_destroy(pjmedia_vid_dev_factory *f)
233{
234 struct ios_factory *qf = (struct ios_factory*)f;
235 pj_pool_t *pool = qf->pool;
236
237 qf->pool = NULL;
238 pj_pool_release(pool);
239
240 return PJ_SUCCESS;
241}
242
243/* API: get number of devices */
244static unsigned ios_factory_get_dev_count(pjmedia_vid_dev_factory *f)
245{
246 struct ios_factory *qf = (struct ios_factory*)f;
247 return qf->dev_count;
248}
249
250/* API: get device info */
251static pj_status_t ios_factory_get_dev_info(pjmedia_vid_dev_factory *f,
252 unsigned index,
253 pjmedia_vid_dev_info *info)
254{
255 struct ios_factory *qf = (struct ios_factory*)f;
256
257 PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
258
259 pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
260
261 return PJ_SUCCESS;
262}
263
264/* API: create default device parameter */
265static pj_status_t ios_factory_default_param(pj_pool_t *pool,
266 pjmedia_vid_dev_factory *f,
267 unsigned index,
268 pjmedia_vid_param *param)
269{
270 struct ios_factory *qf = (struct ios_factory*)f;
271 struct ios_dev_info *di = &qf->dev_info[index];
272
273 PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
274
275 PJ_UNUSED_ARG(pool);
276
277 pj_bzero(param, sizeof(*param));
278 if (di->info.dir & PJMEDIA_DIR_CAPTURE_RENDER) {
279 param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
280 param->cap_id = index;
281 param->rend_id = index;
282 } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
283 param->dir = PJMEDIA_DIR_CAPTURE;
284 param->cap_id = index;
285 param->rend_id = PJMEDIA_VID_INVALID_DEV;
286 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
287 param->dir = PJMEDIA_DIR_RENDER;
288 param->rend_id = index;
289 param->cap_id = PJMEDIA_VID_INVALID_DEV;
290 } else {
291 return PJMEDIA_EVID_INVDEV;
292 }
293
294 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
295 param->clock_rate = DEFAULT_CLOCK_RATE;
296 param->frame_rate.num = DEFAULT_FPS;
297 param->frame_rate.denum = 1;
298 pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
299
300 return PJ_SUCCESS;
301}
302
303@implementation VOutDelegate
304- (void)update_image
305{
306 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
307
308 /* Create a device-dependent RGB color space */
309 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
310
311 /* Create a bitmap graphics context with the sample buffer data */
312 CGContextRef context =
313 CGBitmapContextCreate(stream->buf, stream->size.w, stream->size.h, 8,
314 stream->bytes_per_row, colorSpace,
315 kCGBitmapByteOrder32Little |
316 kCGImageAlphaPremultipliedFirst);
317
318 /**
319 * Create a Quartz image from the pixel data in the bitmap graphics
320 * context
321 */
322 CGImageRef quartzImage = CGBitmapContextCreateImage(context);
323
324 /* Free up the context and color space */
325 CGContextRelease(context);
326 CGColorSpaceRelease(colorSpace);
327
328 /* Create an image object from the Quartz image */
329 UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0
330 orientation:UIImageOrientationRight];
331
332 /* Release the Quartz image */
333 CGImageRelease(quartzImage);
334
Sauw Mingab494302010-12-17 13:17:23 +0000335 [stream->imgView performSelectorOnMainThread:@selector(setImage:)
336 withObject:image waitUntilDone:NO];
Sauw Ming6e6c2152010-12-14 13:03:10 +0000337
338 [pool release];
339}
340
341- (void)captureOutput:(AVCaptureOutput *)captureOutput
342 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
343 fromConnection:(AVCaptureConnection *)connection
344{
345 pjmedia_frame frame;
346 CVImageBufferRef imageBuffer;
347
348 if (!sampleBuffer)
349 return;
350
351 /* Get a CMSampleBuffer's Core Video image buffer for the media data */
352 imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
353
354 /* Lock the base address of the pixel buffer */
355 CVPixelBufferLockBaseAddress(imageBuffer, 0);
356
357 frame.type = PJMEDIA_TYPE_VIDEO;
358 frame.buf = CVPixelBufferGetBaseAddress(imageBuffer);
359 frame.size = stream->frame_size;
360 frame.bit_info = 0;
Benny Prijono349037b2011-03-17 11:25:19 +0000361 frame.timestamp.u64 = stream->frame_ts.u64;
362
Sauw Ming6e6c2152010-12-14 13:03:10 +0000363 if (stream->vid_cb.capture_cb)
364 (*stream->vid_cb.capture_cb)(&stream->base, stream->user_data, &frame);
365
Benny Prijono349037b2011-03-17 11:25:19 +0000366 stream->frame_ts.u64 += stream->ts_inc;
367
Sauw Ming6e6c2152010-12-14 13:03:10 +0000368 /* Unlock the pixel buffer */
369 CVPixelBufferUnlockBaseAddress(imageBuffer,0);
370}
371@end
372
373static ios_fmt_info* get_ios_format_info(pjmedia_format_id id)
374{
375 unsigned i;
376
377 for (i = 0; i < PJ_ARRAY_SIZE(ios_fmts); i++) {
378 if (ios_fmts[i].pjmedia_format == id)
379 return &ios_fmts[i];
380 }
381
382 return NULL;
383}
384
385/* API: create stream */
386static pj_status_t ios_factory_create_stream(pjmedia_vid_dev_factory *f,
387 const pjmedia_vid_param *param,
388 const pjmedia_vid_cb *cb,
389 void *user_data,
390 pjmedia_vid_stream **p_vid_strm)
391{
392 struct ios_factory *qf = (struct ios_factory*)f;
393 pj_pool_t *pool;
394 struct ios_stream *strm;
Benny Prijono349037b2011-03-17 11:25:19 +0000395 const pjmedia_video_format_detail *vfd;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000396 const pjmedia_video_format_info *vfi;
397 pj_status_t status = PJ_SUCCESS;
398 ios_fmt_info *ifi = get_ios_format_info(param->fmt.id);
399 NSError *error;
400
401 PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
402 PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
403 param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
404 PJ_EINVAL);
405
406 if (!(ifi = get_ios_format_info(param->fmt.id)))
407 return PJMEDIA_EVID_BADFORMAT;
408
409 vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
410 if (!vfi)
411 return PJMEDIA_EVID_BADFORMAT;
412
413 /* Create and Initialize stream descriptor */
414 pool = pj_pool_create(qf->pf, "ios-dev", 4000, 4000, NULL);
415 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
416
417 strm = PJ_POOL_ZALLOC_T(pool, struct ios_stream);
418 pj_memcpy(&strm->param, param, sizeof(*param));
419 strm->pool = pool;
420 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
421 strm->user_data = user_data;
422
423 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
424 pj_memcpy(&strm->size, &vfd->size, sizeof(vfd->size));
425 strm->bpp = vfi->bpp;
426 strm->bytes_per_row = strm->size.w * strm->bpp / 8;
427 strm->frame_size = strm->bytes_per_row * strm->size.h;
Benny Prijono349037b2011-03-17 11:25:19 +0000428 strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
Sauw Ming6e6c2152010-12-14 13:03:10 +0000429
430 /* Create capture stream here */
431 if (param->dir & PJMEDIA_DIR_CAPTURE) {
432 strm->cap_session = [[AVCaptureSession alloc] init];
433 if (!strm->cap_session) {
434 status = PJ_ENOMEM;
435 goto on_error;
436 }
437 strm->cap_session.sessionPreset = AVCaptureSessionPresetMedium;
438
439 /* Open video device */
440 AVCaptureDevice *videoDevice =
441 [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
442 if (!videoDevice) {
443 status = PJMEDIA_EVID_SYSERR;
444 goto on_error;
445 }
446
447 /* Add the video device to the session as a device input */
448 strm->dev_input = [AVCaptureDeviceInput
449 deviceInputWithDevice:videoDevice
450 error: &error];
451 if (!strm->dev_input) {
452 status = PJMEDIA_EVID_SYSERR;
453 goto on_error;
454 }
455 [strm->cap_session addInput:strm->dev_input];
456
457 strm->video_output = [[[AVCaptureVideoDataOutput alloc] init]
458 autorelease];
459 if (!strm->video_output) {
460 status = PJMEDIA_EVID_SYSERR;
461 goto on_error;
462 }
463 [strm->cap_session addOutput:strm->video_output];
464
Sauw Mingab494302010-12-17 13:17:23 +0000465 /* Configure the video output */
Sauw Ming6e6c2152010-12-14 13:03:10 +0000466 strm->vout_delegate = [VOutDelegate alloc];
467 strm->vout_delegate->stream = strm;
468 dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
469 [strm->video_output setSampleBufferDelegate:strm->vout_delegate
470 queue:queue];
471 dispatch_release(queue);
472
473 strm->video_output.videoSettings =
474 [NSDictionary dictionaryWithObjectsAndKeys:
475 [NSNumber numberWithInt:ifi->ios_format],
476 kCVPixelBufferPixelFormatTypeKey,
477 [NSNumber numberWithInt: vfd->size.w],
478 kCVPixelBufferWidthKey,
479 [NSNumber numberWithInt: vfd->size.h],
480 kCVPixelBufferHeightKey, nil];
481 strm->video_output.minFrameDuration = CMTimeMake(vfd->fps.denum,
482 vfd->fps.num);
483 }
484
485 /* Create renderer stream here */
486 if (param->dir & PJMEDIA_DIR_RENDER) {
487 /* Get the main window */
488 UIWindow *window = [[UIApplication sharedApplication] keyWindow];
489
Sauw Mingab494302010-12-17 13:17:23 +0000490 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW && param->window)
491 window = param->window;
492
Sauw Ming6e6c2152010-12-14 13:03:10 +0000493 pj_assert(window);
494 strm->imgView = [[UIImageView alloc] initWithFrame:[window bounds]];
495 if (!strm->imgView) {
496 status = PJ_ENOMEM;
497 goto on_error;
498 }
499 [window addSubview:strm->imgView];
500
501 if (!strm->vout_delegate) {
502 strm->vout_delegate = [VOutDelegate alloc];
503 strm->vout_delegate->stream = strm;
504 }
505
506 strm->buf = pj_pool_alloc(pool, strm->frame_size);
507 }
508
509 /* Apply the remaining settings */
510 /*
511 if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
512 ios_stream_set_cap(&strm->base,
513 PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
514 &param->fmt);
515 }
516 */
517 /* Done */
518 strm->base.op = &stream_op;
519 *p_vid_strm = &strm->base;
520
521 return PJ_SUCCESS;
522
523on_error:
524 ios_stream_destroy((pjmedia_vid_stream *)strm);
525
526 return status;
527}
528
529/* API: Get stream info. */
530static pj_status_t ios_stream_get_param(pjmedia_vid_stream *s,
531 pjmedia_vid_param *pi)
532{
533 struct ios_stream *strm = (struct ios_stream*)s;
534
535 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
536
537 pj_memcpy(pi, &strm->param, sizeof(*pi));
538
539/* if (ios_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
540 &pi->fmt.info_size) == PJ_SUCCESS)
541 {
542 pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
543 }
544*/
545 return PJ_SUCCESS;
546}
547
548/* API: get capability */
549static pj_status_t ios_stream_get_cap(pjmedia_vid_stream *s,
550 pjmedia_vid_dev_cap cap,
551 void *pval)
552{
553 struct ios_stream *strm = (struct ios_stream*)s;
554
555 PJ_UNUSED_ARG(strm);
556
557 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
558
559 if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
560 {
561 return PJMEDIA_EVID_INVCAP;
562// return PJ_SUCCESS;
563 } else {
564 return PJMEDIA_EVID_INVCAP;
565 }
566}
567
568/* API: set capability */
569static pj_status_t ios_stream_set_cap(pjmedia_vid_stream *s,
570 pjmedia_vid_dev_cap cap,
571 const void *pval)
572{
573 struct ios_stream *strm = (struct ios_stream*)s;
574
575 PJ_UNUSED_ARG(strm);
576
577 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
578
579 if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
580 {
581 return PJ_SUCCESS;
582 }
583
584 return PJMEDIA_EVID_INVCAP;
585}
586
587/* API: Start stream. */
588static pj_status_t ios_stream_start(pjmedia_vid_stream *strm)
589{
590 struct ios_stream *stream = (struct ios_stream*)strm;
591
592 PJ_UNUSED_ARG(stream);
593
594 PJ_LOG(4, (THIS_FILE, "Starting qt video stream"));
595
596 if (stream->cap_session) {
597 [stream->cap_session startRunning];
598
599 if (![stream->cap_session isRunning])
600 return PJ_EUNKNOWN;
601 }
602
603 return PJ_SUCCESS;
604}
605
606
607/* API: Put frame from stream */
608static pj_status_t ios_stream_put_frame(pjmedia_vid_stream *strm,
609 const pjmedia_frame *frame)
610{
611 struct ios_stream *stream = (struct ios_stream*)strm;
612 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
613
614 pj_assert(stream->frame_size >= frame->size);
615 pj_memcpy(stream->buf, frame->buf, frame->size);
616 /* Perform video display in a background thread */
617// [stream->vout_delegate update_image];
618 [NSThread detachNewThreadSelector:@selector(update_image)
619 toTarget:stream->vout_delegate withObject:nil];
620
621 [pool release];
622
623 return PJ_SUCCESS;
624}
625
626/* API: Stop stream. */
627static pj_status_t ios_stream_stop(pjmedia_vid_stream *strm)
628{
629 struct ios_stream *stream = (struct ios_stream*)strm;
630
631 PJ_UNUSED_ARG(stream);
632
633 PJ_LOG(4, (THIS_FILE, "Stopping qt video stream"));
634
635 if (stream->cap_session && [stream->cap_session isRunning])
636 [stream->cap_session stopRunning];
637
638 return PJ_SUCCESS;
639}
640
641
642/* API: Destroy stream. */
643static pj_status_t ios_stream_destroy(pjmedia_vid_stream *strm)
644{
645 struct ios_stream *stream = (struct ios_stream*)strm;
646
647 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
648
649 ios_stream_stop(strm);
650
651 if (stream->imgView) {
652 [stream->imgView removeFromSuperview];
653 [stream->imgView release];
654 stream->imgView = NULL;
655 }
656
657 if (stream->cap_session) {
658 [stream->cap_session release];
659 stream->cap_session = NULL;
660 }
661/* if (stream->dev_input) {
662 [stream->dev_input release];
663 stream->dev_input = NULL;
664 }
665*/
666 if (stream->vout_delegate) {
667 [stream->vout_delegate release];
668 stream->vout_delegate = NULL;
669 }
670/* if (stream->video_output) {
671 [stream->video_output release];
672 stream->video_output = NULL;
673 }
674*/
675
676 pj_pool_release(stream->pool);
677
678 return PJ_SUCCESS;
679}
680
681#endif
682#endif /* PJMEDIA_VIDEO_DEV_HAS_IOS */