1
/* $Id: qt_dev.m 3979 2012-03-20 08:55:33Z ming $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
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.
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.
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
19
#include <pjmedia-videodev/videodev_imp.h>
20
#include <pj/assert.h>
24
#if PJMEDIA_VIDEO_DEV_HAS_QT
26
#include <Foundation/NSAutoreleasePool.h>
27
#include <QTKit/QTKit.h>
29
#define THIS_FILE "qt_dev.c"
30
#define DEFAULT_CLOCK_RATE 90000
31
#define DEFAULT_WIDTH 640
32
#define DEFAULT_HEIGHT 480
33
#define DEFAULT_FPS 15
35
#define kCVPixelFormatType_422YpCbCr8_yuvs 'yuvs'
37
typedef struct qt_fmt_info
39
pjmedia_format_id pjmedia_format;
43
static qt_fmt_info qt_fmts[] =
45
{PJMEDIA_FORMAT_YUY2, kCVPixelFormatType_422YpCbCr8_yuvs},
46
{PJMEDIA_FORMAT_UYVY, kCVPixelFormatType_422YpCbCr8},
52
pjmedia_vid_dev_info info;
59
pjmedia_vid_dev_factory base;
65
struct qt_dev_info *dev_info;
69
typedef void (*func_ptr)(struct qt_stream *strm);
71
@interface QTDelegate: NSObject
74
struct qt_stream *strm;
84
pjmedia_vid_dev_stream base; /**< Base stream */
85
pjmedia_vid_dev_param param; /**< Settings */
86
pj_pool_t *pool; /**< Memory pool. */
88
pj_timestamp cap_frame_ts; /**< Captured frame tstamp */
89
unsigned cap_ts_inc; /**< Increment */
91
pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
92
void *user_data; /**< Application data. */
94
pj_bool_t cap_thread_exited;
95
pj_bool_t cap_thread_initialized;
96
pj_thread_desc cap_thread_desc;
97
pj_thread_t *cap_thread;
99
struct qt_factory *qf;
101
pj_bool_t is_running;
102
pj_bool_t cap_exited;
104
QTCaptureSession *cap_session;
105
QTCaptureDeviceInput *dev_input;
106
QTCaptureDecompressedVideoOutput *video_output;
107
QTDelegate *qt_delegate;
112
static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f);
113
static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f);
114
static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f);
115
static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f);
116
static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
118
pjmedia_vid_dev_info *info);
119
static pj_status_t qt_factory_default_param(pj_pool_t *pool,
120
pjmedia_vid_dev_factory *f,
122
pjmedia_vid_dev_param *param);
123
static pj_status_t qt_factory_create_stream(
124
pjmedia_vid_dev_factory *f,
125
pjmedia_vid_dev_param *param,
126
const pjmedia_vid_dev_cb *cb,
128
pjmedia_vid_dev_stream **p_vid_strm);
130
static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *strm,
131
pjmedia_vid_dev_param *param);
132
static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *strm,
133
pjmedia_vid_dev_cap cap,
135
static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *strm,
136
pjmedia_vid_dev_cap cap,
138
static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm);
139
static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm);
140
static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm);
143
static pjmedia_vid_dev_factory_op factory_op =
147
&qt_factory_get_dev_count,
148
&qt_factory_get_dev_info,
149
&qt_factory_default_param,
150
&qt_factory_create_stream,
154
static pjmedia_vid_dev_stream_op stream_op =
156
&qt_stream_get_param,
167
/****************************************************************************
171
* Init qt_ video driver.
173
pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf)
175
struct qt_factory *f;
178
pool = pj_pool_create(pf, "qt video", 4000, 4000, NULL);
179
f = PJ_POOL_ZALLOC_T(pool, struct qt_factory);
182
f->base.op = &factory_op;
188
/* API: init factory */
189
static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f)
191
return qt_factory_refresh(f);
194
/* API: destroy factory */
195
static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f)
197
struct qt_factory *qf = (struct qt_factory*)f;
198
pj_pool_t *pool = qf->pool;
201
pj_pool_release(qf->dev_pool);
204
pj_pool_release(pool);
209
/* API: refresh the list of devices */
210
static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f)
212
struct qt_factory *qf = (struct qt_factory*)f;
213
struct qt_dev_info *qdi;
214
unsigned i, dev_count = 0;
215
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
219
pj_pool_release(qf->dev_pool);
223
dev_array = [QTCaptureDevice inputDevices];
224
for (i = 0; i < [dev_array count]; i++) {
225
QTCaptureDevice *dev = [dev_array objectAtIndex:i];
226
if ([dev hasMediaType:QTMediaTypeVideo] ||
227
[dev hasMediaType:QTMediaTypeMuxed])
233
/* Initialize input and output devices here */
235
qf->dev_pool = pj_pool_create(qf->pf, "qt video", 500, 500, NULL);
237
qf->dev_info = (struct qt_dev_info*)
238
pj_pool_calloc(qf->dev_pool, dev_count,
239
sizeof(struct qt_dev_info));
240
for (i = 0; i < [dev_array count]; i++) {
241
QTCaptureDevice *dev = [dev_array objectAtIndex:i];
242
if ([dev hasMediaType:QTMediaTypeVideo] ||
243
[dev hasMediaType:QTMediaTypeMuxed])
247
qdi = &qf->dev_info[qf->dev_count++];
248
pj_bzero(qdi, sizeof(*qdi));
249
[[dev localizedDisplayName] getCString:qdi->info.name
250
maxLength:sizeof(qdi->info.name)
252
[NSString defaultCStringEncoding]];
253
[[dev uniqueID] getCString:qdi->dev_id
254
maxLength:sizeof(qdi->dev_id)
255
encoding:[NSString defaultCStringEncoding]];
256
strcpy(qdi->info.driver, "QT");
257
qdi->info.dir = PJMEDIA_DIR_CAPTURE;
258
qdi->info.has_callback = PJ_TRUE;
260
qdi->info.fmt_cnt = 0;
261
qdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
262
for (k = 0; k < [[dev formatDescriptions] count]; k++) {
264
QTFormatDescription *desc = [[dev formatDescriptions]
266
for (l = 0; l < PJ_ARRAY_SIZE(qt_fmts); l++) {
267
if ([desc formatType] == qt_fmts[l].qt_format) {
268
pjmedia_format *fmt =
269
&qdi->info.fmt[qdi->info.fmt_cnt++];
270
pjmedia_format_init_video(fmt,
271
qt_fmts[l].pjmedia_format,
280
PJ_LOG(4, (THIS_FILE, " dev_id %d: %s", i, qdi->info.name));
286
PJ_LOG(4, (THIS_FILE, "qt video has %d devices",
292
/* API: get number of devices */
293
static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f)
295
struct qt_factory *qf = (struct qt_factory*)f;
296
return qf->dev_count;
299
/* API: get device info */
300
static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
302
pjmedia_vid_dev_info *info)
304
struct qt_factory *qf = (struct qt_factory*)f;
306
PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
308
pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
313
/* API: create default device parameter */
314
static pj_status_t qt_factory_default_param(pj_pool_t *pool,
315
pjmedia_vid_dev_factory *f,
317
pjmedia_vid_dev_param *param)
319
struct qt_factory *qf = (struct qt_factory*)f;
320
struct qt_dev_info *di = &qf->dev_info[index];
322
PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
326
pj_bzero(param, sizeof(*param));
327
param->dir = PJMEDIA_DIR_CAPTURE;
328
param->cap_id = index;
329
param->rend_id = PJMEDIA_VID_INVALID_DEV;
330
param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
331
param->clock_rate = DEFAULT_CLOCK_RATE;
332
pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt));
337
static qt_fmt_info* get_qt_format_info(pjmedia_format_id id)
341
for (i = 0; i < PJ_ARRAY_SIZE(qt_fmts); i++) {
342
if (qt_fmts[i].pjmedia_format == id)
349
@implementation QTDelegate
350
- (void)captureOutput:(QTCaptureOutput *)captureOutput
351
didOutputVideoFrame:(CVImageBufferRef)videoFrame
352
withSampleBuffer:(QTSampleBuffer *)sampleBuffer
353
fromConnection:(QTCaptureConnection *)connection
355
unsigned size = [sampleBuffer lengthForAllSamples];
358
if (!strm->is_running) {
359
strm->cap_exited = PJ_TRUE;
363
if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
365
pj_thread_register("qt_cap", strm->cap_thread_desc,
367
strm->cap_thread_initialized = 1;
368
PJ_LOG(5,(THIS_FILE, "Capture thread started"));
374
frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
375
frame.buf = [sampleBuffer bytesForAllSamples];
378
frame.timestamp.u64 = strm->cap_frame_ts.u64;
380
if (strm->vid_cb.capture_cb)
381
(*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
383
strm->cap_frame_ts.u64 += strm->cap_ts_inc;
393
static void init_qt(struct qt_stream *strm)
395
const pjmedia_video_format_detail *vfd;
396
qt_fmt_info *qfi = get_qt_format_info(strm->param.fmt.id);
401
strm->status = PJMEDIA_EVID_BADFORMAT;
405
strm->cap_session = [[QTCaptureSession alloc] init];
406
if (!strm->cap_session) {
407
strm->status = PJ_ENOMEM;
411
/* Open video device */
412
QTCaptureDevice *videoDevice =
413
[QTCaptureDevice deviceWithUniqueID:
414
[NSString stringWithCString:
415
strm->qf->dev_info[strm->param.cap_id].dev_id
417
[NSString defaultCStringEncoding]]];
418
if (!videoDevice || ![videoDevice open:&error]) {
419
strm->status = PJMEDIA_EVID_SYSERR;
423
/* Add the video device to the session as a device input */
424
strm->dev_input = [[QTCaptureDeviceInput alloc]
425
initWithDevice:videoDevice];
426
success = [strm->cap_session addInput:strm->dev_input error:&error];
428
strm->status = PJMEDIA_EVID_SYSERR;
432
strm->video_output = [[QTCaptureDecompressedVideoOutput alloc] init];
433
success = [strm->cap_session addOutput:strm->video_output
436
strm->status = PJMEDIA_EVID_SYSERR;
440
vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt,
442
[strm->video_output setPixelBufferAttributes:
443
[NSDictionary dictionaryWithObjectsAndKeys:
444
[NSNumber numberWithInt:qfi->qt_format],
445
kCVPixelBufferPixelFormatTypeKey,
446
[NSNumber numberWithInt:vfd->size.w],
447
kCVPixelBufferWidthKey,
448
[NSNumber numberWithInt:vfd->size.h],
449
kCVPixelBufferHeightKey, nil]];
451
pj_assert(vfd->fps.num);
452
strm->cap_ts_inc = PJMEDIA_SPF2(strm->param.clock_rate, &vfd->fps, 1);
454
if ([strm->video_output
455
respondsToSelector:@selector(setMinimumVideoFrameInterval)])
457
[strm->video_output setMinimumVideoFrameInterval:
458
(1.0f * vfd->fps.denum / (double)vfd->fps.num)];
461
strm->qt_delegate = [[QTDelegate alloc]init];
462
strm->qt_delegate->strm = strm;
463
[strm->video_output setDelegate:strm->qt_delegate];
466
static void run_func_on_main_thread(struct qt_stream *strm, func_ptr func)
468
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
469
QTDelegate *delg = [[QTDelegate alloc] init];
473
[delg performSelectorOnMainThread:@selector(run_func)
474
withObject:nil waitUntilDone:YES];
476
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
482
/* API: create stream */
483
static pj_status_t qt_factory_create_stream(
484
pjmedia_vid_dev_factory *f,
485
pjmedia_vid_dev_param *param,
486
const pjmedia_vid_dev_cb *cb,
488
pjmedia_vid_dev_stream **p_vid_strm)
490
struct qt_factory *qf = (struct qt_factory*)f;
492
struct qt_stream *strm;
493
const pjmedia_video_format_info *vfi;
494
pj_status_t status = PJ_SUCCESS;
496
PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
497
PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
498
param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
499
param->dir == PJMEDIA_DIR_CAPTURE,
502
vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
504
return PJMEDIA_EVID_BADFORMAT;
506
/* Create and Initialize stream descriptor */
507
pool = pj_pool_create(qf->pf, "qt-dev", 4000, 4000, NULL);
508
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
510
strm = PJ_POOL_ZALLOC_T(pool, struct qt_stream);
511
pj_memcpy(&strm->param, param, sizeof(*param));
513
pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
514
strm->user_data = user_data;
517
/* Create capture stream here */
518
if (param->dir & PJMEDIA_DIR_CAPTURE) {
519
strm->status = PJ_SUCCESS;
520
run_func_on_main_thread(strm, init_qt);
521
if ((status = strm->status) != PJ_SUCCESS)
525
/* Apply the remaining settings */
527
if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
528
qt_stream_set_cap(&strm->base,
529
PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
534
strm->base.op = &stream_op;
535
*p_vid_strm = &strm->base;
540
qt_stream_destroy((pjmedia_vid_dev_stream *)strm);
545
/* API: Get stream info. */
546
static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *s,
547
pjmedia_vid_dev_param *pi)
549
struct qt_stream *strm = (struct qt_stream*)s;
551
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
553
pj_memcpy(pi, &strm->param, sizeof(*pi));
555
/* if (qt_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
556
&pi->fmt.info_size) == PJ_SUCCESS)
558
pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
564
/* API: get capability */
565
static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *s,
566
pjmedia_vid_dev_cap cap,
569
struct qt_stream *strm = (struct qt_stream*)s;
573
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
575
if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
577
return PJMEDIA_EVID_INVCAP;
578
// return PJ_SUCCESS;
580
return PJMEDIA_EVID_INVCAP;
584
/* API: set capability */
585
static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *s,
586
pjmedia_vid_dev_cap cap,
589
struct qt_stream *strm = (struct qt_stream*)s;
593
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
595
if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
600
return PJMEDIA_EVID_INVCAP;
603
static void start_qt(struct qt_stream *strm)
605
[strm->cap_session startRunning];
608
static void stop_qt(struct qt_stream *strm)
610
[strm->cap_session stopRunning];
613
/* API: Start stream. */
614
static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm)
616
struct qt_stream *stream = (struct qt_stream*)strm;
618
PJ_UNUSED_ARG(stream);
620
PJ_LOG(4, (THIS_FILE, "Starting qt video stream"));
622
if (stream->cap_session) {
623
run_func_on_main_thread(stream, start_qt);
625
if (![stream->cap_session isRunning])
626
return PJMEDIA_EVID_NOTREADY;
628
stream->is_running = PJ_TRUE;
634
/* API: Stop stream. */
635
static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm)
637
struct qt_stream *stream = (struct qt_stream*)strm;
639
PJ_UNUSED_ARG(stream);
641
PJ_LOG(4, (THIS_FILE, "Stopping qt video stream"));
643
if (stream->cap_session && [stream->cap_session isRunning]) {
646
stream->cap_exited = PJ_FALSE;
647
run_func_on_main_thread(stream, stop_qt);
649
stream->is_running = PJ_FALSE;
650
for (i = 50; i >= 0 && !stream->cap_exited; i--) {
658
static void destroy_qt(struct qt_stream *strm)
660
if (strm->dev_input && [[strm->dev_input device] isOpen])
661
[[strm->dev_input device] close];
663
if (strm->cap_session) {
664
[strm->cap_session release];
665
strm->cap_session = NULL;
667
if (strm->dev_input) {
668
[strm->dev_input release];
669
strm->dev_input = NULL;
671
if (strm->qt_delegate) {
672
[strm->qt_delegate release];
673
strm->qt_delegate = NULL;
675
if (strm->video_output) {
676
[strm->video_output release];
677
strm->video_output = NULL;
681
/* API: Destroy stream. */
682
static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm)
684
struct qt_stream *stream = (struct qt_stream*)strm;
686
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
688
qt_stream_stop(strm);
690
run_func_on_main_thread(stream, destroy_qt);
692
pj_pool_release(stream->pool);
697
#endif /* PJMEDIA_VIDEO_DEV_HAS_QT */