123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 |
- /*
- * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <pjmedia-videodev/videodev_imp.h>
- #include <pj/assert.h>
- #include <pj/log.h>
- #include <pj/os.h>
- #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \
- defined(PJMEDIA_VIDEO_DEV_HAS_QT) && PJMEDIA_VIDEO_DEV_HAS_QT != 0
- #include <Foundation/NSAutoreleasePool.h>
- #include <QTKit/QTKit.h>
- #define THIS_FILE "qt_dev.c"
- #define DEFAULT_CLOCK_RATE 90000
- #define DEFAULT_WIDTH 640
- #define DEFAULT_HEIGHT 480
- #define DEFAULT_FPS 15
- #define kCVPixelFormatType_422YpCbCr8_yuvs 'yuvs'
- typedef struct qt_fmt_info
- {
- pjmedia_format_id pjmedia_format;
- unsigned qt_format;
- } qt_fmt_info;
- static qt_fmt_info qt_fmts[] =
- {
- {PJMEDIA_FORMAT_YUY2, kCVPixelFormatType_422YpCbCr8_yuvs},
- {PJMEDIA_FORMAT_UYVY, kCVPixelFormatType_422YpCbCr8},
- };
- /* qt device info */
- struct qt_dev_info
- {
- pjmedia_vid_dev_info info;
- char dev_id[192];
- };
- /* qt factory */
- struct qt_factory
- {
- pjmedia_vid_dev_factory base;
- pj_pool_t *pool;
- pj_pool_t *dev_pool;
- pj_pool_factory *pf;
- unsigned dev_count;
- struct qt_dev_info *dev_info;
- };
- struct qt_stream;
- typedef void (*func_ptr)(struct qt_stream *strm);
- @interface QTDelegate: NSObject
- {
- @public
- struct qt_stream *strm;
- func_ptr func;
- }
- - (void)run_func;
- @end
- /* Video stream. */
- struct qt_stream
- {
- pjmedia_vid_dev_stream base; /**< Base stream */
- pjmedia_vid_dev_param param; /**< Settings */
- pj_pool_t *pool; /**< Memory pool. */
- pj_timestamp cap_frame_ts; /**< Captured frame tstamp */
- unsigned cap_ts_inc; /**< Increment */
-
- pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
- void *user_data; /**< Application data. */
- pj_bool_t cap_thread_exited;
- pj_bool_t cap_thread_initialized;
- pj_thread_desc cap_thread_desc;
- pj_thread_t *cap_thread;
-
- struct qt_factory *qf;
- pj_status_t status;
- pj_bool_t is_running;
- pj_bool_t cap_exited;
-
- QTCaptureSession *cap_session;
- QTCaptureDeviceInput *dev_input;
- QTCaptureDecompressedVideoOutput *video_output;
- QTDelegate *qt_delegate;
- };
- /* Prototypes */
- static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f);
- static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f);
- static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f);
- static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f);
- static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_info *info);
- static pj_status_t qt_factory_default_param(pj_pool_t *pool,
- pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_param *param);
- static pj_status_t qt_factory_create_stream(
- pjmedia_vid_dev_factory *f,
- pjmedia_vid_dev_param *param,
- const pjmedia_vid_dev_cb *cb,
- void *user_data,
- pjmedia_vid_dev_stream **p_vid_strm);
- static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *strm,
- pjmedia_vid_dev_param *param);
- static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *strm,
- pjmedia_vid_dev_cap cap,
- void *value);
- static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *strm,
- pjmedia_vid_dev_cap cap,
- const void *value);
- static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm);
- static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm);
- static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm);
- /* Operations */
- static pjmedia_vid_dev_factory_op factory_op =
- {
- &qt_factory_init,
- &qt_factory_destroy,
- &qt_factory_get_dev_count,
- &qt_factory_get_dev_info,
- &qt_factory_default_param,
- &qt_factory_create_stream,
- &qt_factory_refresh
- };
- static pjmedia_vid_dev_stream_op stream_op =
- {
- &qt_stream_get_param,
- &qt_stream_get_cap,
- &qt_stream_set_cap,
- &qt_stream_start,
- NULL,
- NULL,
- &qt_stream_stop,
- &qt_stream_destroy
- };
- /****************************************************************************
- * Factory operations
- */
- /*
- * Init qt_ video driver.
- */
- pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf)
- {
- struct qt_factory *f;
- pj_pool_t *pool;
- pool = pj_pool_create(pf, "qt video", 4000, 4000, NULL);
- f = PJ_POOL_ZALLOC_T(pool, struct qt_factory);
- f->pf = pf;
- f->pool = pool;
- f->base.op = &factory_op;
- return &f->base;
- }
- /* API: init factory */
- static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f)
- {
- return qt_factory_refresh(f);
- }
- /* API: destroy factory */
- static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f)
- {
- struct qt_factory *qf = (struct qt_factory*)f;
- pj_pool_t *pool = qf->pool;
- if (qf->dev_pool)
- pj_pool_release(qf->dev_pool);
- qf->pool = NULL;
- if (pool)
- pj_pool_release(pool);
- return PJ_SUCCESS;
- }
- /* API: refresh the list of devices */
- static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f)
- {
- struct qt_factory *qf = (struct qt_factory*)f;
- struct qt_dev_info *qdi;
- unsigned i, dev_count = 0;
- NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
- NSArray *dev_array;
-
- if (qf->dev_pool) {
- pj_pool_release(qf->dev_pool);
- qf->dev_pool = NULL;
- }
-
- dev_array = [QTCaptureDevice inputDevices];
- for (i = 0; i < [dev_array count]; i++) {
- QTCaptureDevice *dev = [dev_array objectAtIndex:i];
- if ([dev hasMediaType:QTMediaTypeVideo] ||
- [dev hasMediaType:QTMediaTypeMuxed])
- {
- dev_count++;
- }
- }
-
- /* Initialize input and output devices here */
- qf->dev_count = 0;
- qf->dev_pool = pj_pool_create(qf->pf, "qt video", 500, 500, NULL);
-
- qf->dev_info = (struct qt_dev_info*)
- pj_pool_calloc(qf->dev_pool, dev_count,
- sizeof(struct qt_dev_info));
- for (i = 0; i < [dev_array count]; i++) {
- QTCaptureDevice *dev = [dev_array objectAtIndex:i];
- if ([dev hasMediaType:QTMediaTypeVideo] ||
- [dev hasMediaType:QTMediaTypeMuxed])
- {
- unsigned k;
-
- qdi = &qf->dev_info[qf->dev_count++];
- pj_bzero(qdi, sizeof(*qdi));
- [[dev localizedDisplayName] getCString:qdi->info.name
- maxLength:sizeof(qdi->info.name)
- encoding:
- [NSString defaultCStringEncoding]];
- [[dev uniqueID] getCString:qdi->dev_id
- maxLength:sizeof(qdi->dev_id)
- encoding:[NSString defaultCStringEncoding]];
- pj_ansi_strxcpy(qdi->info.driver, "QT", sizeof(qdi->info.driver));
- qdi->info.dir = PJMEDIA_DIR_CAPTURE;
- qdi->info.has_callback = PJ_TRUE;
-
- qdi->info.fmt_cnt = 0;
- qdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
- for (k = 0; k < [[dev formatDescriptions] count]; k++) {
- unsigned l;
- QTFormatDescription *desc = [[dev formatDescriptions]
- objectAtIndex:k];
- for (l = 0; l < PJ_ARRAY_SIZE(qt_fmts); l++) {
- if ([desc formatType] == qt_fmts[l].qt_format) {
- pjmedia_format *fmt =
- &qdi->info.fmt[qdi->info.fmt_cnt++];
- pjmedia_format_init_video(fmt,
- qt_fmts[l].pjmedia_format,
- DEFAULT_WIDTH,
- DEFAULT_HEIGHT,
- DEFAULT_FPS, 1);
- break;
- }
- }
- }
-
- PJ_LOG(4, (THIS_FILE, " dev_id %d: %s", i, qdi->info.name));
- }
- }
-
- [apool release];
-
- PJ_LOG(4, (THIS_FILE, "qt video has %d devices",
- qf->dev_count));
-
- return PJ_SUCCESS;
- }
- /* API: get number of devices */
- static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f)
- {
- struct qt_factory *qf = (struct qt_factory*)f;
- return qf->dev_count;
- }
- /* API: get device info */
- static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_info *info)
- {
- struct qt_factory *qf = (struct qt_factory*)f;
- PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
- pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
- return PJ_SUCCESS;
- }
- /* API: create default device parameter */
- static pj_status_t qt_factory_default_param(pj_pool_t *pool,
- pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_param *param)
- {
- struct qt_factory *qf = (struct qt_factory*)f;
- struct qt_dev_info *di = &qf->dev_info[index];
- PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
- PJ_UNUSED_ARG(pool);
- pj_bzero(param, sizeof(*param));
- param->dir = PJMEDIA_DIR_CAPTURE;
- param->cap_id = index;
- param->rend_id = PJMEDIA_VID_INVALID_DEV;
- param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
- param->clock_rate = DEFAULT_CLOCK_RATE;
- pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt));
- return PJ_SUCCESS;
- }
- static qt_fmt_info* get_qt_format_info(pjmedia_format_id id)
- {
- unsigned i;
-
- for (i = 0; i < PJ_ARRAY_SIZE(qt_fmts); i++) {
- if (qt_fmts[i].pjmedia_format == id)
- return &qt_fmts[i];
- }
-
- return NULL;
- }
- @implementation QTDelegate
- - (void)captureOutput:(QTCaptureOutput *)captureOutput
- didOutputVideoFrame:(CVImageBufferRef)videoFrame
- withSampleBuffer:(QTSampleBuffer *)sampleBuffer
- fromConnection:(QTCaptureConnection *)connection
- {
- unsigned size = [sampleBuffer lengthForAllSamples];
- pjmedia_frame frame;
- if (!strm->is_running) {
- strm->cap_exited = PJ_TRUE;
- return;
- }
-
- if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
- {
- pj_thread_register("qt_cap", strm->cap_thread_desc,
- &strm->cap_thread);
- strm->cap_thread_initialized = 1;
- PJ_LOG(5,(THIS_FILE, "Capture thread started"));
- }
-
- if (!videoFrame)
- return;
-
- frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
- frame.buf = [sampleBuffer bytesForAllSamples];
- frame.size = size;
- frame.bit_info = 0;
- frame.timestamp.u64 = strm->cap_frame_ts.u64;
-
- if (strm->vid_cb.capture_cb)
- (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
-
- strm->cap_frame_ts.u64 += strm->cap_ts_inc;
- }
- - (void)run_func
- {
- (*func)(strm);
- }
- @end
- static void init_qt(struct qt_stream *strm)
- {
- const pjmedia_video_format_detail *vfd;
- qt_fmt_info *qfi = get_qt_format_info(strm->param.fmt.id);
- BOOL success = NO;
- NSError *error;
-
- if (!qfi) {
- strm->status = PJMEDIA_EVID_BADFORMAT;
- return;
- }
-
- strm->cap_session = [[QTCaptureSession alloc] init];
- if (!strm->cap_session) {
- strm->status = PJ_ENOMEM;
- return;
- }
-
- /* Open video device */
- QTCaptureDevice *videoDevice =
- [QTCaptureDevice deviceWithUniqueID:
- [NSString stringWithCString:
- strm->qf->dev_info[strm->param.cap_id].dev_id
- encoding:
- [NSString defaultCStringEncoding]]];
- if (!videoDevice || ![videoDevice open:&error]) {
- strm->status = PJMEDIA_EVID_SYSERR;
- return;
- }
-
- /* Add the video device to the session as a device input */
- strm->dev_input = [[QTCaptureDeviceInput alloc]
- initWithDevice:videoDevice];
- success = [strm->cap_session addInput:strm->dev_input error:&error];
- if (!success) {
- strm->status = PJMEDIA_EVID_SYSERR;
- return;
- }
-
- strm->video_output = [[QTCaptureDecompressedVideoOutput alloc] init];
- success = [strm->cap_session addOutput:strm->video_output
- error:&error];
- if (!success) {
- strm->status = PJMEDIA_EVID_SYSERR;
- return;
- }
-
- vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt,
- PJ_TRUE);
- [strm->video_output setPixelBufferAttributes:
- [NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithInt:qfi->qt_format],
- kCVPixelBufferPixelFormatTypeKey,
- [NSNumber numberWithInt:vfd->size.w],
- kCVPixelBufferWidthKey,
- [NSNumber numberWithInt:vfd->size.h],
- kCVPixelBufferHeightKey, nil]];
-
- pj_assert(vfd->fps.num);
- strm->cap_ts_inc = PJMEDIA_SPF2(strm->param.clock_rate, &vfd->fps, 1);
-
- if ([strm->video_output
- respondsToSelector:@selector(setMinimumVideoFrameInterval)])
- {
- [strm->video_output setMinimumVideoFrameInterval:
- (1.0f * vfd->fps.denum / (double)vfd->fps.num)];
- }
-
- strm->qt_delegate = [[QTDelegate alloc]init];
- strm->qt_delegate->strm = strm;
- [strm->video_output setDelegate:strm->qt_delegate];
- }
- static void run_func_on_main_thread(struct qt_stream *strm, func_ptr func)
- {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- QTDelegate *delg = [[QTDelegate alloc] init];
-
- delg->strm = strm;
- delg->func = func;
- [delg performSelectorOnMainThread:@selector(run_func)
- withObject:nil waitUntilDone:YES];
-
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
- [delg release];
- [pool release];
- }
- /* API: create stream */
- static pj_status_t qt_factory_create_stream(
- pjmedia_vid_dev_factory *f,
- pjmedia_vid_dev_param *param,
- const pjmedia_vid_dev_cb *cb,
- void *user_data,
- pjmedia_vid_dev_stream **p_vid_strm)
- {
- struct qt_factory *qf = (struct qt_factory*)f;
- pj_pool_t *pool;
- struct qt_stream *strm;
- const pjmedia_video_format_info *vfi;
- pj_status_t status = PJ_SUCCESS;
- PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
- PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
- param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
- param->dir == PJMEDIA_DIR_CAPTURE,
- PJ_EINVAL);
- vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
- if (!vfi)
- return PJMEDIA_EVID_BADFORMAT;
- /* Create and Initialize stream descriptor */
- pool = pj_pool_create(qf->pf, "qt-dev", 4000, 4000, NULL);
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
- strm = PJ_POOL_ZALLOC_T(pool, struct qt_stream);
- pj_memcpy(&strm->param, param, sizeof(*param));
- strm->pool = pool;
- pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
- strm->user_data = user_data;
- strm->qf = qf;
- /* Create capture stream here */
- if (param->dir & PJMEDIA_DIR_CAPTURE) {
- strm->status = PJ_SUCCESS;
- run_func_on_main_thread(strm, init_qt);
- if ((status = strm->status) != PJ_SUCCESS)
- goto on_error;
- }
-
- /* Apply the remaining settings */
- /*
- if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
- qt_stream_set_cap(&strm->base,
- PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
- ¶m->fmt);
- }
- */
- /* Done */
- strm->base.op = &stream_op;
- *p_vid_strm = &strm->base;
-
- return PJ_SUCCESS;
-
- on_error:
- qt_stream_destroy((pjmedia_vid_dev_stream *)strm);
-
- return status;
- }
- /* API: Get stream info. */
- static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *s,
- pjmedia_vid_dev_param *pi)
- {
- struct qt_stream *strm = (struct qt_stream*)s;
- PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
- pj_memcpy(pi, &strm->param, sizeof(*pi));
- /* if (qt_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;
- }
- /* API: get capability */
- static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *s,
- pjmedia_vid_dev_cap cap,
- void *pval)
- {
- struct qt_stream *strm = (struct qt_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;
- }
- }
- /* API: set capability */
- static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *s,
- pjmedia_vid_dev_cap cap,
- const void *pval)
- {
- struct qt_stream *strm = (struct qt_stream*)s;
- PJ_UNUSED_ARG(strm);
- PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
- if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
- {
- return PJ_SUCCESS;
- }
- return PJMEDIA_EVID_INVCAP;
- }
- static void start_qt(struct qt_stream *strm)
- {
- [strm->cap_session startRunning];
- }
- static void stop_qt(struct qt_stream *strm)
- {
- [strm->cap_session stopRunning];
- }
- /* API: Start stream. */
- static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm)
- {
- struct qt_stream *stream = (struct qt_stream*)strm;
- PJ_UNUSED_ARG(stream);
- PJ_LOG(4, (THIS_FILE, "Starting qt video stream"));
- if (stream->cap_session) {
- run_func_on_main_thread(stream, start_qt);
-
- if (![stream->cap_session isRunning])
- return PJMEDIA_EVID_NOTREADY;
-
- stream->is_running = PJ_TRUE;
- }
- return PJ_SUCCESS;
- }
- /* API: Stop stream. */
- static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm)
- {
- struct qt_stream *stream = (struct qt_stream*)strm;
- PJ_UNUSED_ARG(stream);
- PJ_LOG(4, (THIS_FILE, "Stopping qt video stream"));
- if (stream->cap_session && [stream->cap_session isRunning]) {
- int i;
-
- stream->cap_exited = PJ_FALSE;
- run_func_on_main_thread(stream, stop_qt);
-
- stream->is_running = PJ_FALSE;
- for (i = 50; i >= 0 && !stream->cap_exited; i--) {
- pj_thread_sleep(10);
- }
- }
-
- return PJ_SUCCESS;
- }
- static void destroy_qt(struct qt_stream *strm)
- {
- if (strm->dev_input && [[strm->dev_input device] isOpen])
- [[strm->dev_input device] close];
-
- if (strm->cap_session) {
- [strm->cap_session release];
- strm->cap_session = NULL;
- }
- if (strm->dev_input) {
- [strm->dev_input release];
- strm->dev_input = NULL;
- }
- if (strm->qt_delegate) {
- [strm->qt_delegate release];
- strm->qt_delegate = NULL;
- }
- if (strm->video_output) {
- [strm->video_output release];
- strm->video_output = NULL;
- }
- }
- /* API: Destroy stream. */
- static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm)
- {
- struct qt_stream *stream = (struct qt_stream*)strm;
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
- qt_stream_stop(strm);
- run_func_on_main_thread(stream, destroy_qt);
-
- pj_pool_release(stream->pool);
- return PJ_SUCCESS;
- }
- #endif /* PJMEDIA_VIDEO_DEV_HAS_QT */
|