qt_dev.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. /*
  2. * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. */
  18. #include <pjmedia-videodev/videodev_imp.h>
  19. #include <pj/assert.h>
  20. #include <pj/log.h>
  21. #include <pj/os.h>
  22. #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \
  23. defined(PJMEDIA_VIDEO_DEV_HAS_QT) && PJMEDIA_VIDEO_DEV_HAS_QT != 0
  24. #include <Foundation/NSAutoreleasePool.h>
  25. #include <QTKit/QTKit.h>
  26. #define THIS_FILE "qt_dev.c"
  27. #define DEFAULT_CLOCK_RATE 90000
  28. #define DEFAULT_WIDTH 640
  29. #define DEFAULT_HEIGHT 480
  30. #define DEFAULT_FPS 15
  31. #define kCVPixelFormatType_422YpCbCr8_yuvs 'yuvs'
  32. typedef struct qt_fmt_info
  33. {
  34. pjmedia_format_id pjmedia_format;
  35. unsigned qt_format;
  36. } qt_fmt_info;
  37. static qt_fmt_info qt_fmts[] =
  38. {
  39. {PJMEDIA_FORMAT_YUY2, kCVPixelFormatType_422YpCbCr8_yuvs},
  40. {PJMEDIA_FORMAT_UYVY, kCVPixelFormatType_422YpCbCr8},
  41. };
  42. /* qt device info */
  43. struct qt_dev_info
  44. {
  45. pjmedia_vid_dev_info info;
  46. char dev_id[192];
  47. };
  48. /* qt factory */
  49. struct qt_factory
  50. {
  51. pjmedia_vid_dev_factory base;
  52. pj_pool_t *pool;
  53. pj_pool_t *dev_pool;
  54. pj_pool_factory *pf;
  55. unsigned dev_count;
  56. struct qt_dev_info *dev_info;
  57. };
  58. struct qt_stream;
  59. typedef void (*func_ptr)(struct qt_stream *strm);
  60. @interface QTDelegate: NSObject
  61. {
  62. @public
  63. struct qt_stream *strm;
  64. func_ptr func;
  65. }
  66. - (void)run_func;
  67. @end
  68. /* Video stream. */
  69. struct qt_stream
  70. {
  71. pjmedia_vid_dev_stream base; /**< Base stream */
  72. pjmedia_vid_dev_param param; /**< Settings */
  73. pj_pool_t *pool; /**< Memory pool. */
  74. pj_timestamp cap_frame_ts; /**< Captured frame tstamp */
  75. unsigned cap_ts_inc; /**< Increment */
  76. pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
  77. void *user_data; /**< Application data. */
  78. pj_bool_t cap_thread_exited;
  79. pj_bool_t cap_thread_initialized;
  80. pj_thread_desc cap_thread_desc;
  81. pj_thread_t *cap_thread;
  82. struct qt_factory *qf;
  83. pj_status_t status;
  84. pj_bool_t is_running;
  85. pj_bool_t cap_exited;
  86. QTCaptureSession *cap_session;
  87. QTCaptureDeviceInput *dev_input;
  88. QTCaptureDecompressedVideoOutput *video_output;
  89. QTDelegate *qt_delegate;
  90. };
  91. /* Prototypes */
  92. static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f);
  93. static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f);
  94. static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f);
  95. static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f);
  96. static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
  97. unsigned index,
  98. pjmedia_vid_dev_info *info);
  99. static pj_status_t qt_factory_default_param(pj_pool_t *pool,
  100. pjmedia_vid_dev_factory *f,
  101. unsigned index,
  102. pjmedia_vid_dev_param *param);
  103. static pj_status_t qt_factory_create_stream(
  104. pjmedia_vid_dev_factory *f,
  105. pjmedia_vid_dev_param *param,
  106. const pjmedia_vid_dev_cb *cb,
  107. void *user_data,
  108. pjmedia_vid_dev_stream **p_vid_strm);
  109. static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *strm,
  110. pjmedia_vid_dev_param *param);
  111. static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *strm,
  112. pjmedia_vid_dev_cap cap,
  113. void *value);
  114. static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *strm,
  115. pjmedia_vid_dev_cap cap,
  116. const void *value);
  117. static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm);
  118. static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm);
  119. static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm);
  120. /* Operations */
  121. static pjmedia_vid_dev_factory_op factory_op =
  122. {
  123. &qt_factory_init,
  124. &qt_factory_destroy,
  125. &qt_factory_get_dev_count,
  126. &qt_factory_get_dev_info,
  127. &qt_factory_default_param,
  128. &qt_factory_create_stream,
  129. &qt_factory_refresh
  130. };
  131. static pjmedia_vid_dev_stream_op stream_op =
  132. {
  133. &qt_stream_get_param,
  134. &qt_stream_get_cap,
  135. &qt_stream_set_cap,
  136. &qt_stream_start,
  137. NULL,
  138. NULL,
  139. &qt_stream_stop,
  140. &qt_stream_destroy
  141. };
  142. /****************************************************************************
  143. * Factory operations
  144. */
  145. /*
  146. * Init qt_ video driver.
  147. */
  148. pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf)
  149. {
  150. struct qt_factory *f;
  151. pj_pool_t *pool;
  152. pool = pj_pool_create(pf, "qt video", 4000, 4000, NULL);
  153. f = PJ_POOL_ZALLOC_T(pool, struct qt_factory);
  154. f->pf = pf;
  155. f->pool = pool;
  156. f->base.op = &factory_op;
  157. return &f->base;
  158. }
  159. /* API: init factory */
  160. static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f)
  161. {
  162. return qt_factory_refresh(f);
  163. }
  164. /* API: destroy factory */
  165. static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f)
  166. {
  167. struct qt_factory *qf = (struct qt_factory*)f;
  168. pj_pool_t *pool = qf->pool;
  169. if (qf->dev_pool)
  170. pj_pool_release(qf->dev_pool);
  171. qf->pool = NULL;
  172. if (pool)
  173. pj_pool_release(pool);
  174. return PJ_SUCCESS;
  175. }
  176. /* API: refresh the list of devices */
  177. static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f)
  178. {
  179. struct qt_factory *qf = (struct qt_factory*)f;
  180. struct qt_dev_info *qdi;
  181. unsigned i, dev_count = 0;
  182. NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
  183. NSArray *dev_array;
  184. if (qf->dev_pool) {
  185. pj_pool_release(qf->dev_pool);
  186. qf->dev_pool = NULL;
  187. }
  188. dev_array = [QTCaptureDevice inputDevices];
  189. for (i = 0; i < [dev_array count]; i++) {
  190. QTCaptureDevice *dev = [dev_array objectAtIndex:i];
  191. if ([dev hasMediaType:QTMediaTypeVideo] ||
  192. [dev hasMediaType:QTMediaTypeMuxed])
  193. {
  194. dev_count++;
  195. }
  196. }
  197. /* Initialize input and output devices here */
  198. qf->dev_count = 0;
  199. qf->dev_pool = pj_pool_create(qf->pf, "qt video", 500, 500, NULL);
  200. qf->dev_info = (struct qt_dev_info*)
  201. pj_pool_calloc(qf->dev_pool, dev_count,
  202. sizeof(struct qt_dev_info));
  203. for (i = 0; i < [dev_array count]; i++) {
  204. QTCaptureDevice *dev = [dev_array objectAtIndex:i];
  205. if ([dev hasMediaType:QTMediaTypeVideo] ||
  206. [dev hasMediaType:QTMediaTypeMuxed])
  207. {
  208. unsigned k;
  209. qdi = &qf->dev_info[qf->dev_count++];
  210. pj_bzero(qdi, sizeof(*qdi));
  211. [[dev localizedDisplayName] getCString:qdi->info.name
  212. maxLength:sizeof(qdi->info.name)
  213. encoding:
  214. [NSString defaultCStringEncoding]];
  215. [[dev uniqueID] getCString:qdi->dev_id
  216. maxLength:sizeof(qdi->dev_id)
  217. encoding:[NSString defaultCStringEncoding]];
  218. pj_ansi_strxcpy(qdi->info.driver, "QT", sizeof(qdi->info.driver));
  219. qdi->info.dir = PJMEDIA_DIR_CAPTURE;
  220. qdi->info.has_callback = PJ_TRUE;
  221. qdi->info.fmt_cnt = 0;
  222. qdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
  223. for (k = 0; k < [[dev formatDescriptions] count]; k++) {
  224. unsigned l;
  225. QTFormatDescription *desc = [[dev formatDescriptions]
  226. objectAtIndex:k];
  227. for (l = 0; l < PJ_ARRAY_SIZE(qt_fmts); l++) {
  228. if ([desc formatType] == qt_fmts[l].qt_format) {
  229. pjmedia_format *fmt =
  230. &qdi->info.fmt[qdi->info.fmt_cnt++];
  231. pjmedia_format_init_video(fmt,
  232. qt_fmts[l].pjmedia_format,
  233. DEFAULT_WIDTH,
  234. DEFAULT_HEIGHT,
  235. DEFAULT_FPS, 1);
  236. break;
  237. }
  238. }
  239. }
  240. PJ_LOG(4, (THIS_FILE, " dev_id %d: %s", i, qdi->info.name));
  241. }
  242. }
  243. [apool release];
  244. PJ_LOG(4, (THIS_FILE, "qt video has %d devices",
  245. qf->dev_count));
  246. return PJ_SUCCESS;
  247. }
  248. /* API: get number of devices */
  249. static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f)
  250. {
  251. struct qt_factory *qf = (struct qt_factory*)f;
  252. return qf->dev_count;
  253. }
  254. /* API: get device info */
  255. static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
  256. unsigned index,
  257. pjmedia_vid_dev_info *info)
  258. {
  259. struct qt_factory *qf = (struct qt_factory*)f;
  260. PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
  261. pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
  262. return PJ_SUCCESS;
  263. }
  264. /* API: create default device parameter */
  265. static pj_status_t qt_factory_default_param(pj_pool_t *pool,
  266. pjmedia_vid_dev_factory *f,
  267. unsigned index,
  268. pjmedia_vid_dev_param *param)
  269. {
  270. struct qt_factory *qf = (struct qt_factory*)f;
  271. struct qt_dev_info *di = &qf->dev_info[index];
  272. PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
  273. PJ_UNUSED_ARG(pool);
  274. pj_bzero(param, sizeof(*param));
  275. param->dir = PJMEDIA_DIR_CAPTURE;
  276. param->cap_id = index;
  277. param->rend_id = PJMEDIA_VID_INVALID_DEV;
  278. param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
  279. param->clock_rate = DEFAULT_CLOCK_RATE;
  280. pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
  281. return PJ_SUCCESS;
  282. }
  283. static qt_fmt_info* get_qt_format_info(pjmedia_format_id id)
  284. {
  285. unsigned i;
  286. for (i = 0; i < PJ_ARRAY_SIZE(qt_fmts); i++) {
  287. if (qt_fmts[i].pjmedia_format == id)
  288. return &qt_fmts[i];
  289. }
  290. return NULL;
  291. }
  292. @implementation QTDelegate
  293. - (void)captureOutput:(QTCaptureOutput *)captureOutput
  294. didOutputVideoFrame:(CVImageBufferRef)videoFrame
  295. withSampleBuffer:(QTSampleBuffer *)sampleBuffer
  296. fromConnection:(QTCaptureConnection *)connection
  297. {
  298. unsigned size = [sampleBuffer lengthForAllSamples];
  299. pjmedia_frame frame;
  300. if (!strm->is_running) {
  301. strm->cap_exited = PJ_TRUE;
  302. return;
  303. }
  304. if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
  305. {
  306. pj_thread_register("qt_cap", strm->cap_thread_desc,
  307. &strm->cap_thread);
  308. strm->cap_thread_initialized = 1;
  309. PJ_LOG(5,(THIS_FILE, "Capture thread started"));
  310. }
  311. if (!videoFrame)
  312. return;
  313. frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
  314. frame.buf = [sampleBuffer bytesForAllSamples];
  315. frame.size = size;
  316. frame.bit_info = 0;
  317. frame.timestamp.u64 = strm->cap_frame_ts.u64;
  318. if (strm->vid_cb.capture_cb)
  319. (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
  320. strm->cap_frame_ts.u64 += strm->cap_ts_inc;
  321. }
  322. - (void)run_func
  323. {
  324. (*func)(strm);
  325. }
  326. @end
  327. static void init_qt(struct qt_stream *strm)
  328. {
  329. const pjmedia_video_format_detail *vfd;
  330. qt_fmt_info *qfi = get_qt_format_info(strm->param.fmt.id);
  331. BOOL success = NO;
  332. NSError *error;
  333. if (!qfi) {
  334. strm->status = PJMEDIA_EVID_BADFORMAT;
  335. return;
  336. }
  337. strm->cap_session = [[QTCaptureSession alloc] init];
  338. if (!strm->cap_session) {
  339. strm->status = PJ_ENOMEM;
  340. return;
  341. }
  342. /* Open video device */
  343. QTCaptureDevice *videoDevice =
  344. [QTCaptureDevice deviceWithUniqueID:
  345. [NSString stringWithCString:
  346. strm->qf->dev_info[strm->param.cap_id].dev_id
  347. encoding:
  348. [NSString defaultCStringEncoding]]];
  349. if (!videoDevice || ![videoDevice open:&error]) {
  350. strm->status = PJMEDIA_EVID_SYSERR;
  351. return;
  352. }
  353. /* Add the video device to the session as a device input */
  354. strm->dev_input = [[QTCaptureDeviceInput alloc]
  355. initWithDevice:videoDevice];
  356. success = [strm->cap_session addInput:strm->dev_input error:&error];
  357. if (!success) {
  358. strm->status = PJMEDIA_EVID_SYSERR;
  359. return;
  360. }
  361. strm->video_output = [[QTCaptureDecompressedVideoOutput alloc] init];
  362. success = [strm->cap_session addOutput:strm->video_output
  363. error:&error];
  364. if (!success) {
  365. strm->status = PJMEDIA_EVID_SYSERR;
  366. return;
  367. }
  368. vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt,
  369. PJ_TRUE);
  370. [strm->video_output setPixelBufferAttributes:
  371. [NSDictionary dictionaryWithObjectsAndKeys:
  372. [NSNumber numberWithInt:qfi->qt_format],
  373. kCVPixelBufferPixelFormatTypeKey,
  374. [NSNumber numberWithInt:vfd->size.w],
  375. kCVPixelBufferWidthKey,
  376. [NSNumber numberWithInt:vfd->size.h],
  377. kCVPixelBufferHeightKey, nil]];
  378. pj_assert(vfd->fps.num);
  379. strm->cap_ts_inc = PJMEDIA_SPF2(strm->param.clock_rate, &vfd->fps, 1);
  380. if ([strm->video_output
  381. respondsToSelector:@selector(setMinimumVideoFrameInterval)])
  382. {
  383. [strm->video_output setMinimumVideoFrameInterval:
  384. (1.0f * vfd->fps.denum / (double)vfd->fps.num)];
  385. }
  386. strm->qt_delegate = [[QTDelegate alloc]init];
  387. strm->qt_delegate->strm = strm;
  388. [strm->video_output setDelegate:strm->qt_delegate];
  389. }
  390. static void run_func_on_main_thread(struct qt_stream *strm, func_ptr func)
  391. {
  392. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  393. QTDelegate *delg = [[QTDelegate alloc] init];
  394. delg->strm = strm;
  395. delg->func = func;
  396. [delg performSelectorOnMainThread:@selector(run_func)
  397. withObject:nil waitUntilDone:YES];
  398. CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
  399. [delg release];
  400. [pool release];
  401. }
  402. /* API: create stream */
  403. static pj_status_t qt_factory_create_stream(
  404. pjmedia_vid_dev_factory *f,
  405. pjmedia_vid_dev_param *param,
  406. const pjmedia_vid_dev_cb *cb,
  407. void *user_data,
  408. pjmedia_vid_dev_stream **p_vid_strm)
  409. {
  410. struct qt_factory *qf = (struct qt_factory*)f;
  411. pj_pool_t *pool;
  412. struct qt_stream *strm;
  413. const pjmedia_video_format_info *vfi;
  414. pj_status_t status = PJ_SUCCESS;
  415. PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
  416. PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
  417. param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
  418. param->dir == PJMEDIA_DIR_CAPTURE,
  419. PJ_EINVAL);
  420. vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
  421. if (!vfi)
  422. return PJMEDIA_EVID_BADFORMAT;
  423. /* Create and Initialize stream descriptor */
  424. pool = pj_pool_create(qf->pf, "qt-dev", 4000, 4000, NULL);
  425. PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
  426. strm = PJ_POOL_ZALLOC_T(pool, struct qt_stream);
  427. pj_memcpy(&strm->param, param, sizeof(*param));
  428. strm->pool = pool;
  429. pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
  430. strm->user_data = user_data;
  431. strm->qf = qf;
  432. /* Create capture stream here */
  433. if (param->dir & PJMEDIA_DIR_CAPTURE) {
  434. strm->status = PJ_SUCCESS;
  435. run_func_on_main_thread(strm, init_qt);
  436. if ((status = strm->status) != PJ_SUCCESS)
  437. goto on_error;
  438. }
  439. /* Apply the remaining settings */
  440. /*
  441. if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
  442. qt_stream_set_cap(&strm->base,
  443. PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
  444. &param->fmt);
  445. }
  446. */
  447. /* Done */
  448. strm->base.op = &stream_op;
  449. *p_vid_strm = &strm->base;
  450. return PJ_SUCCESS;
  451. on_error:
  452. qt_stream_destroy((pjmedia_vid_dev_stream *)strm);
  453. return status;
  454. }
  455. /* API: Get stream info. */
  456. static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *s,
  457. pjmedia_vid_dev_param *pi)
  458. {
  459. struct qt_stream *strm = (struct qt_stream*)s;
  460. PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
  461. pj_memcpy(pi, &strm->param, sizeof(*pi));
  462. /* if (qt_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
  463. &pi->fmt.info_size) == PJ_SUCCESS)
  464. {
  465. pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
  466. }
  467. */
  468. return PJ_SUCCESS;
  469. }
  470. /* API: get capability */
  471. static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *s,
  472. pjmedia_vid_dev_cap cap,
  473. void *pval)
  474. {
  475. struct qt_stream *strm = (struct qt_stream*)s;
  476. PJ_UNUSED_ARG(strm);
  477. PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
  478. if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
  479. {
  480. return PJMEDIA_EVID_INVCAP;
  481. // return PJ_SUCCESS;
  482. } else {
  483. return PJMEDIA_EVID_INVCAP;
  484. }
  485. }
  486. /* API: set capability */
  487. static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *s,
  488. pjmedia_vid_dev_cap cap,
  489. const void *pval)
  490. {
  491. struct qt_stream *strm = (struct qt_stream*)s;
  492. PJ_UNUSED_ARG(strm);
  493. PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
  494. if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
  495. {
  496. return PJ_SUCCESS;
  497. }
  498. return PJMEDIA_EVID_INVCAP;
  499. }
  500. static void start_qt(struct qt_stream *strm)
  501. {
  502. [strm->cap_session startRunning];
  503. }
  504. static void stop_qt(struct qt_stream *strm)
  505. {
  506. [strm->cap_session stopRunning];
  507. }
  508. /* API: Start stream. */
  509. static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm)
  510. {
  511. struct qt_stream *stream = (struct qt_stream*)strm;
  512. PJ_UNUSED_ARG(stream);
  513. PJ_LOG(4, (THIS_FILE, "Starting qt video stream"));
  514. if (stream->cap_session) {
  515. run_func_on_main_thread(stream, start_qt);
  516. if (![stream->cap_session isRunning])
  517. return PJMEDIA_EVID_NOTREADY;
  518. stream->is_running = PJ_TRUE;
  519. }
  520. return PJ_SUCCESS;
  521. }
  522. /* API: Stop stream. */
  523. static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm)
  524. {
  525. struct qt_stream *stream = (struct qt_stream*)strm;
  526. PJ_UNUSED_ARG(stream);
  527. PJ_LOG(4, (THIS_FILE, "Stopping qt video stream"));
  528. if (stream->cap_session && [stream->cap_session isRunning]) {
  529. int i;
  530. stream->cap_exited = PJ_FALSE;
  531. run_func_on_main_thread(stream, stop_qt);
  532. stream->is_running = PJ_FALSE;
  533. for (i = 50; i >= 0 && !stream->cap_exited; i--) {
  534. pj_thread_sleep(10);
  535. }
  536. }
  537. return PJ_SUCCESS;
  538. }
  539. static void destroy_qt(struct qt_stream *strm)
  540. {
  541. if (strm->dev_input && [[strm->dev_input device] isOpen])
  542. [[strm->dev_input device] close];
  543. if (strm->cap_session) {
  544. [strm->cap_session release];
  545. strm->cap_session = NULL;
  546. }
  547. if (strm->dev_input) {
  548. [strm->dev_input release];
  549. strm->dev_input = NULL;
  550. }
  551. if (strm->qt_delegate) {
  552. [strm->qt_delegate release];
  553. strm->qt_delegate = NULL;
  554. }
  555. if (strm->video_output) {
  556. [strm->video_output release];
  557. strm->video_output = NULL;
  558. }
  559. }
  560. /* API: Destroy stream. */
  561. static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm)
  562. {
  563. struct qt_stream *stream = (struct qt_stream*)strm;
  564. PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
  565. qt_stream_stop(strm);
  566. run_func_on_main_thread(stream, destroy_qt);
  567. pj_pool_release(stream->pool);
  568. return PJ_SUCCESS;
  569. }
  570. #endif /* PJMEDIA_VIDEO_DEV_HAS_QT */