1
/* $Id: dshow_dev.c 4253 2012-09-13 08:35:24Z 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>
23
#include <pj/unicode.h>
26
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
30
# pragma warning(push, 3)
42
#pragma comment(lib, "Strmiids.lib")
43
#pragma comment(lib, "Rpcrt4.lib")
44
#pragma comment(lib, "Quartz.lib")
46
#define THIS_FILE "dshow_dev.c"
47
#define DEFAULT_CLOCK_RATE 90000
48
#define DEFAULT_WIDTH 640
49
#define DEFAULT_HEIGHT 480
50
#define DEFAULT_FPS 25
52
/* Temporarily disable DirectShow renderer (VMR) */
55
typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
56
typedef struct NullRenderer NullRenderer;
57
IBaseFilter* NullRenderer_Create(input_callback input_cb,
59
typedef struct SourceFilter SourceFilter;
60
IBaseFilter* SourceFilter_Create(SourceFilter **pSrc);
61
HRESULT SourceFilter_Deliver(SourceFilter *src, void *buf, long size);
62
void SourceFilter_SetMediaType(SourceFilter *src, AM_MEDIA_TYPE *pmt);
64
typedef struct dshow_fmt_info
66
pjmedia_format_id pjmedia_format;
67
const GUID *dshow_format;
71
static dshow_fmt_info dshow_fmts[] =
73
{PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2, PJ_FALSE} ,
74
{PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24, PJ_FALSE} ,
75
{PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32, PJ_FALSE} ,
76
{PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV, PJ_FALSE} ,
77
{PJMEDIA_FORMAT_I420, &WMMEDIASUBTYPE_I420, PJ_FALSE}
80
/* dshow_ device info */
83
pjmedia_vid_dev_info info;
85
WCHAR display_name[192];
91
pjmedia_vid_dev_factory base;
97
struct dshow_dev_info *dev_info;
103
pjmedia_vid_dev_stream base; /**< Base stream */
104
pjmedia_vid_dev_param param; /**< Settings */
105
pj_pool_t *pool; /**< Memory pool. */
107
pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
108
void *user_data; /**< Application data. */
111
pj_bool_t rend_thread_exited;
112
pj_bool_t cap_thread_exited;
113
pj_bool_t cap_thread_initialized;
114
pj_thread_desc cap_thread_desc;
115
pj_thread_t *cap_thread;
117
unsigned frm_buf_size;
121
IFilterGraph *filter_graph;
122
IMediaFilter *media_filter;
123
SourceFilter *csource_filter;
124
IBaseFilter *source_filter;
125
IBaseFilter *rend_filter;
126
AM_MEDIA_TYPE *mediatype;
135
static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
136
static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
137
static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f);
138
static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
139
static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
141
pjmedia_vid_dev_info *info);
142
static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
143
pjmedia_vid_dev_factory *f,
145
pjmedia_vid_dev_param *param);
146
static pj_status_t dshow_factory_create_stream(
147
pjmedia_vid_dev_factory *f,
148
pjmedia_vid_dev_param *param,
149
const pjmedia_vid_dev_cb *cb,
151
pjmedia_vid_dev_stream **p_vid_strm);
153
static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
154
pjmedia_vid_dev_param *param);
155
static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
156
pjmedia_vid_dev_cap cap,
158
static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
159
pjmedia_vid_dev_cap cap,
161
static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
162
static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
163
const pjmedia_frame *frame);
164
static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
165
static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
168
static pjmedia_vid_dev_factory_op factory_op =
171
&dshow_factory_destroy,
172
&dshow_factory_get_dev_count,
173
&dshow_factory_get_dev_info,
174
&dshow_factory_default_param,
175
&dshow_factory_create_stream,
176
&dshow_factory_refresh
179
static pjmedia_vid_dev_stream_op stream_op =
181
&dshow_stream_get_param,
182
&dshow_stream_get_cap,
183
&dshow_stream_set_cap,
186
&dshow_stream_put_frame,
188
&dshow_stream_destroy
192
/****************************************************************************
196
* Init dshow_ video driver.
198
pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf)
200
struct dshow_factory *f;
203
pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL);
204
f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory);
207
f->base.op = &factory_op;
212
/* API: init factory */
213
static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
215
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
216
if (hr == RPC_E_CHANGED_MODE) {
217
PJ_LOG(4,(THIS_FILE, "Failed initializing DShow: "
218
"COM library already initialized with "
219
"incompatible concurrency model"));
220
return PJMEDIA_EVID_INIT;
223
return dshow_factory_refresh(f);
226
/* API: destroy factory */
227
static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
229
struct dshow_factory *df = (struct dshow_factory*)f;
230
pj_pool_t *pool = df->pool;
234
pj_pool_release(df->dev_pool);
236
pj_pool_release(pool);
243
static HRESULT get_cap_device(struct dshow_factory *df,
245
IBaseFilter **filter)
250
hr = CreateBindCtx(0, &pbc);
251
if (SUCCEEDED (hr)) {
255
hr = MkParseDisplayName(pbc, df->dev_info[id].display_name,
256
&pchEaten, &moniker);
258
hr = IMoniker_BindToObject(moniker, pbc, NULL,
261
IMoniker_Release(moniker);
263
IBindCtx_Release(pbc);
269
static void enum_dev_cap(IBaseFilter *filter,
271
const GUID *dshow_fmt,
272
AM_MEDIA_TYPE **pMediatype,
277
AM_MEDIA_TYPE *mediatype = NULL;
282
hr = IBaseFilter_EnumPins(filter, &pEnum);
284
/* Loop through all the pins. */
287
while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
288
PIN_DIRECTION pindirtmp;
290
hr = IPin_QueryDirection(pPin, &pindirtmp);
291
if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) {
297
if (dir == PJMEDIA_DIR_CAPTURE) {
298
IAMStreamConfig *streamcaps;
300
hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig,
301
(LPVOID *)&streamcaps);
303
VIDEO_STREAM_CONFIG_CAPS vscc;
304
int i, isize, icount;
306
IAMStreamConfig_GetNumberOfCapabilities(streamcaps,
309
for (i = 0; i < icount; i++) {
311
RPC_STATUS rpcstatus, rpcstatus2;
313
hr = IAMStreamConfig_GetStreamCaps(streamcaps, i,
319
nformat = (dshow_fmt? 1:
320
sizeof(dshow_fmts)/sizeof(dshow_fmts[0]));
321
for (j = 0; j < nformat; j++) {
322
const GUID *dshow_format = dshow_fmt;
325
dshow_format = dshow_fmts[j].dshow_format;
326
if (UuidCompare(&mediatype->subtype,
329
rpcstatus == RPC_S_OK &&
330
UuidCompare(&mediatype->formattype,
331
(UUID*)&FORMAT_VideoInfo,
333
rpcstatus2 == RPC_S_OK)
336
dshow_fmts[j].enabled = PJ_TRUE;
338
sup_fmt[j] = PJ_TRUE;
341
*pMediatype = mediatype;
345
if (pSrcpin && *pSrcpin)
348
IAMStreamConfig_Release(streamcaps);
353
if (pSrcpin && *pSrcpin)
357
IEnumPins_Release(pEnum);
361
/* API: refresh the list of devices */
362
static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f)
364
struct dshow_factory *df = (struct dshow_factory*)f;
365
struct dshow_dev_info *ddi;
368
ICreateDevEnum *dev_enum = NULL;
369
IEnumMoniker *enum_cat = NULL;
370
IMoniker *moniker = NULL;
375
pj_pool_release(df->dev_pool);
380
df->dev_pool = pj_pool_create(df->pf, "dshow video", 500, 500, NULL);
382
hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
383
CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
386
ICreateDevEnum_CreateClassEnumerator(dev_enum,
387
&CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)
389
PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
391
ICreateDevEnum_Release(dev_enum);
394
while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
399
/* Add renderer device */
401
df->dev_info = (struct dshow_dev_info*)
402
pj_pool_calloc(df->dev_pool, dev_count,
403
sizeof(struct dshow_dev_info));
406
IEnumMoniker_Reset(enum_cat);
407
while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
408
IPropertyBag *prop_bag;
410
hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
415
VariantInit(&var_name);
416
hr = IPropertyBag_Read(prop_bag, L"FriendlyName",
418
if (SUCCEEDED(hr) && var_name.bstrVal) {
419
WCHAR *wszDisplayName = NULL;
422
ddi = &df->dev_info[df->dev_count++];
423
pj_bzero(ddi, sizeof(*ddi));
424
pj_unicode_to_ansi(var_name.bstrVal,
425
wcslen(var_name.bstrVal),
427
sizeof(ddi->info.name));
429
hr = IMoniker_GetDisplayName(moniker, NULL, NULL,
431
if (hr == S_OK && wszDisplayName) {
432
pj_memcpy(ddi->display_name, wszDisplayName,
433
(wcslen(wszDisplayName)+1) * sizeof(WCHAR));
434
CoTaskMemFree(wszDisplayName);
437
strncpy(ddi->info.driver, "dshow",
438
sizeof(ddi->info.driver));
439
ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
440
ddi->info.dir = PJMEDIA_DIR_CAPTURE;
441
ddi->info.has_callback = PJ_TRUE;
443
/* Set the device capabilities here */
444
ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
446
hr = get_cap_device(df, df->dev_count-1, &filter);
449
pj_bool_t sup_fmt[sizeof(dshow_fmts)/sizeof(dshow_fmts[0])];
451
pj_bzero(sup_fmt, sizeof(sup_fmt));
452
enum_dev_cap(filter, ddi->info.dir, NULL, NULL, NULL, sup_fmt);
454
ddi->info.fmt_cnt = 0;
456
j < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
461
pjmedia_format_init_video(
462
&ddi->info.fmt[ddi->info.fmt_cnt++],
463
dshow_fmts[j].pjmedia_format,
464
DEFAULT_WIDTH, DEFAULT_HEIGHT,
469
VariantClear(&var_name);
471
IPropertyBag_Release(prop_bag);
473
IMoniker_Release(moniker);
476
IEnumMoniker_Release(enum_cat);
477
ICreateDevEnum_Release(dev_enum);
481
ddi = &df->dev_info[df->dev_count++];
482
pj_bzero(ddi, sizeof(*ddi));
483
pj_ansi_strncpy(ddi->info.name, "Video Mixing Renderer",
484
sizeof(ddi->info.name));
485
ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
486
pj_ansi_strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver));
487
ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
488
ddi->info.dir = PJMEDIA_DIR_RENDER;
489
ddi->info.has_callback = PJ_FALSE;
490
ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
492
// ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
494
ddi->info.fmt_cnt = 1;
495
pjmedia_format_init_video(&ddi->info.fmt[0], dshow_fmts[0].pjmedia_format,
496
DEFAULT_WIDTH, DEFAULT_HEIGHT,
500
PJ_LOG(4, (THIS_FILE, "DShow has %d devices:",
502
for (c = 0; c < df->dev_count; ++c) {
503
PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)",
505
df->dev_info[c].info.name,
506
df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ?
507
"capture" : "render"));
513
/* API: get number of devices */
514
static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
516
struct dshow_factory *df = (struct dshow_factory*)f;
517
return df->dev_count;
520
/* API: get device info */
521
static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
523
pjmedia_vid_dev_info *info)
525
struct dshow_factory *df = (struct dshow_factory*)f;
527
PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
529
pj_memcpy(info, &df->dev_info[index].info, sizeof(*info));
534
/* API: create default device parameter */
535
static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
536
pjmedia_vid_dev_factory *f,
538
pjmedia_vid_dev_param *param)
540
struct dshow_factory *df = (struct dshow_factory*)f;
541
struct dshow_dev_info *di = &df->dev_info[index];
543
PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
547
pj_bzero(param, sizeof(*param));
548
if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
549
param->dir = PJMEDIA_DIR_CAPTURE;
550
param->cap_id = index;
551
param->rend_id = PJMEDIA_VID_INVALID_DEV;
552
} else if (di->info.dir & PJMEDIA_DIR_RENDER) {
553
param->dir = PJMEDIA_DIR_RENDER;
554
param->rend_id = index;
555
param->cap_id = PJMEDIA_VID_INVALID_DEV;
557
return PJMEDIA_EVID_INVDEV;
560
/* Set the device capabilities here */
561
param->clock_rate = DEFAULT_CLOCK_RATE;
562
param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
564
pjmedia_format_copy(¶m->fmt, &di->info.fmt[0]);
569
static void input_cb(void *user_data, IMediaSample *pMediaSample)
571
struct dshow_stream *strm = (struct dshow_stream*)user_data;
572
pjmedia_frame frame = {0};
574
if (strm->quit_flag) {
575
strm->cap_thread_exited = PJ_TRUE;
579
if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
583
status = pj_thread_register("ds_cap", strm->cap_thread_desc,
585
if (status != PJ_SUCCESS)
587
strm->cap_thread_initialized = 1;
588
PJ_LOG(5,(THIS_FILE, "Capture thread started"));
591
frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
592
IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
593
frame.size = IMediaSample_GetActualDataLength(pMediaSample);
595
frame.timestamp = strm->cap_ts;
596
strm->cap_ts.u64 += strm->cap_ts_inc;
598
if (strm->frm_buf_size) {
600
BYTE *src_buf, *dst_buf;
601
pjmedia_video_format_detail *vfd;
603
/* Image is bottom-up, convert it to top-down. */
604
src_buf = dst_buf = (BYTE *)frame.buf;
605
stride = strm->frm_buf_size;
606
vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt,
608
src_buf += (vfd->size.h - 1) * stride;
610
for (i = vfd->size.h / 2; i > 0; i--) {
611
memcpy(strm->frm_buf, dst_buf, stride);
612
memcpy(dst_buf, src_buf, stride);
613
memcpy(src_buf, strm->frm_buf, stride);
619
if (strm->vid_cb.capture_cb)
620
(*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
623
/* API: Put frame from stream */
624
static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
625
const pjmedia_frame *frame)
627
struct dshow_stream *stream = (struct dshow_stream*)strm;
630
if (stream->quit_flag) {
631
stream->rend_thread_exited = PJ_TRUE;
635
hr = SourceFilter_Deliver(stream->dgraph.csource_filter,
636
frame->buf, frame->size);
643
static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
647
for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
648
if (dshow_fmts[i].pjmedia_format == id && dshow_fmts[i].enabled)
649
return &dshow_fmts[i];
655
static pj_status_t create_filter_graph(pjmedia_dir dir,
657
pj_bool_t use_def_size,
658
pj_bool_t use_def_fps,
659
struct dshow_factory *df,
660
struct dshow_stream *strm,
661
struct dshow_graph *graph)
666
IPin *sinkpin = NULL;
667
AM_MEDIA_TYPE *mediatype= NULL, mtype;
668
VIDEOINFOHEADER *video_info, *vi = NULL;
669
pjmedia_video_format_detail *vfd;
670
const pjmedia_video_format_info *vfi;
672
vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
675
return PJMEDIA_EVID_BADFORMAT;
677
hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC,
678
&IID_IFilterGraph, (LPVOID *)&graph->filter_graph);
683
hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter,
684
(LPVOID *)&graph->media_filter);
689
if (dir == PJMEDIA_DIR_CAPTURE) {
690
hr = get_cap_device(df, id, &graph->source_filter);
695
graph->source_filter = SourceFilter_Create(&graph->csource_filter);
698
hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter,
704
if (dir == PJMEDIA_DIR_CAPTURE) {
705
graph->rend_filter = NullRenderer_Create(input_cb, strm);
707
hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL,
708
CLSCTX_INPROC, &IID_IBaseFilter,
709
(LPVOID *)&graph->rend_filter);
715
IBaseFilter_EnumPins(graph->rend_filter, &pEnum);
717
// Loop through all the pins
720
while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
721
PIN_DIRECTION pindirtmp;
723
hr = IPin_QueryDirection(pPin, &pindirtmp);
724
if (hr == S_OK && pindirtmp == PINDIR_INPUT) {
730
IEnumPins_Release(pEnum);
733
vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
735
enum_dev_cap(graph->source_filter, dir,
736
get_dshow_format_info(strm->param.fmt.id)->dshow_format,
737
&mediatype, &srcpin, NULL);
738
graph->mediatype = mediatype;
740
if (srcpin && dir == PJMEDIA_DIR_RENDER) {
741
mediatype = graph->mediatype = &mtype;
743
memset (mediatype, 0, sizeof(AM_MEDIA_TYPE));
744
mediatype->majortype = MEDIATYPE_Video;
745
mediatype->subtype = *(get_dshow_format_info(strm->param.fmt.id)->
747
mediatype->bFixedSizeSamples = TRUE;
748
mediatype->bTemporalCompression = FALSE;
750
vi = (VIDEOINFOHEADER *)
751
CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
752
memset (vi, 0, sizeof(VIDEOINFOHEADER));
753
mediatype->formattype = FORMAT_VideoInfo;
754
mediatype->cbFormat = sizeof(VIDEOINFOHEADER);
755
mediatype->pbFormat = (BYTE *)vi;
757
vi->rcSource.bottom = vfd->size.h;
758
vi->rcSource.right = vfd->size.w;
759
vi->rcTarget.bottom = vfd->size.h;
760
vi->rcTarget.right = vfd->size.w;
762
vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
763
vi->bmiHeader.biPlanes = 1;
764
vi->bmiHeader.biBitCount = vfi->bpp;
765
vi->bmiHeader.biCompression = strm->param.fmt.id;
768
if (!srcpin || !sinkpin || !mediatype) {
769
hr = VFW_E_TYPE_NOT_ACCEPTED;
772
video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
774
video_info->bmiHeader.biWidth = vfd->size.w;
775
video_info->bmiHeader.biHeight = vfd->size.h;
777
if (video_info->AvgTimePerFrame == 0 ||
778
(!use_def_fps && vfd->fps.num != 0))
780
video_info->AvgTimePerFrame = (LONGLONG) (10000000 *
781
(double)vfd->fps.denum /
784
video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
785
mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
786
if (graph->csource_filter)
787
SourceFilter_SetMediaType(graph->csource_filter,
790
hr = IFilterGraph_AddFilter(graph->filter_graph,
791
(IBaseFilter *)graph->rend_filter,
796
hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin,
799
if (use_def_size || use_def_fps) {
800
pjmedia_format_init_video(&strm->param.fmt, strm->param.fmt.id,
801
video_info->bmiHeader.biWidth,
802
video_info->bmiHeader.biHeight,
804
(unsigned)video_info->AvgTimePerFrame);
807
strm->frm_buf_size = 0;
808
if (dir == PJMEDIA_DIR_CAPTURE &&
809
video_info->bmiHeader.biCompression == BI_RGB &&
810
video_info->bmiHeader.biHeight > 0)
812
/* Allocate buffer to flip the captured image. */
813
strm->frm_buf_size = (video_info->bmiHeader.biBitCount >> 3) *
814
video_info->bmiHeader.biWidth;
815
strm->frm_buf = pj_pool_alloc(strm->pool, strm->frm_buf_size);
821
IPin_Release(srcpin);
823
IPin_Release(sinkpin);
828
if (AMGetErrorText(hr, msg, sizeof(msg))) {
829
PJ_LOG(4,(THIS_FILE, "Error creating filter graph: %s (hr=0x%x)",
838
static void destroy_filter_graph(struct dshow_stream * stream)
840
if (stream->dgraph.source_filter) {
841
IBaseFilter_Release(stream->dgraph.source_filter);
842
stream->dgraph.source_filter = NULL;
844
if (stream->dgraph.rend_filter) {
845
IBaseFilter_Release(stream->dgraph.rend_filter);
846
stream->dgraph.rend_filter = NULL;
848
if (stream->dgraph.media_filter) {
849
IMediaFilter_Release(stream->dgraph.media_filter);
850
stream->dgraph.media_filter = NULL;
852
if (stream->dgraph.filter_graph) {
853
IFilterGraph_Release(stream->dgraph.filter_graph);
854
stream->dgraph.filter_graph = NULL;
858
/* API: create stream */
859
static pj_status_t dshow_factory_create_stream(
860
pjmedia_vid_dev_factory *f,
861
pjmedia_vid_dev_param *param,
862
const pjmedia_vid_dev_cb *cb,
864
pjmedia_vid_dev_stream **p_vid_strm)
866
struct dshow_factory *df = (struct dshow_factory*)f;
868
struct dshow_stream *strm;
871
PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE ||
872
param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
874
if (!get_dshow_format_info(param->fmt.id))
875
return PJMEDIA_EVID_BADFORMAT;
877
/* Create and Initialize stream descriptor */
878
pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL);
879
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
881
strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream);
882
pj_memcpy(&strm->param, param, sizeof(*param));
884
pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
885
strm->user_data = user_data;
887
if (param->dir & PJMEDIA_DIR_CAPTURE) {
888
const pjmedia_video_format_detail *vfd;
890
/* Create capture stream here */
891
status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
892
PJ_FALSE, PJ_FALSE, df, strm,
894
if (status != PJ_SUCCESS) {
895
destroy_filter_graph(strm);
896
/* Try to use default fps */
897
PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default fps"));
898
status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
899
PJ_FALSE, PJ_TRUE, df, strm,
902
if (status != PJ_SUCCESS) {
903
/* Still failed, now try to use default fps and size */
904
destroy_filter_graph(strm);
905
/* Try to use default fps */
906
PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default "
908
status = create_filter_graph(PJMEDIA_DIR_CAPTURE,
910
PJ_TRUE, PJ_TRUE, df, strm,
914
if (status != PJ_SUCCESS)
916
pj_memcpy(param, &strm->param, sizeof(*param));
919
vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE);
920
strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
921
} else if (param->dir & PJMEDIA_DIR_RENDER) {
922
/* Create render stream here */
923
status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id,
924
PJ_FALSE, PJ_FALSE, df, strm,
926
if (status != PJ_SUCCESS)
930
/* Apply the remaining settings */
931
if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
932
dshow_stream_set_cap(&strm->base,
933
PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
938
strm->base.op = &stream_op;
939
*p_vid_strm = &strm->base;
944
dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
948
/* API: Get stream info. */
949
static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
950
pjmedia_vid_dev_param *pi)
952
struct dshow_stream *strm = (struct dshow_stream*)s;
954
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
956
pj_memcpy(pi, &strm->param, sizeof(*pi));
958
if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
959
&pi->window) == PJ_SUCCESS)
961
pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
967
/* API: get capability */
968
static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
969
pjmedia_vid_dev_cap cap,
972
struct dshow_stream *strm = (struct dshow_stream*)s;
976
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
978
if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
980
*(unsigned*)pval = 0;
983
return PJMEDIA_EVID_INVCAP;
987
/* API: set capability */
988
static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
989
pjmedia_vid_dev_cap cap,
992
struct dshow_stream *strm = (struct dshow_stream*)s;
996
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
998
if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
1000
// set renderer's window here
1004
return PJMEDIA_EVID_INVCAP;
1007
/* API: Start stream. */
1008
static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
1010
struct dshow_stream *stream = (struct dshow_stream*)strm;
1013
stream->quit_flag = PJ_FALSE;
1014
stream->cap_thread_exited = PJ_FALSE;
1015
stream->rend_thread_exited = PJ_FALSE;
1017
hr = IMediaFilter_Run(stream->dgraph.media_filter, 0);
1020
if (AMGetErrorText(hr, msg, sizeof(msg))) {
1021
PJ_LOG(4,(THIS_FILE, "Error starting media: %s", msg));
1026
PJ_LOG(4, (THIS_FILE, "Starting dshow video stream"));
1031
/* API: Stop stream. */
1032
static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
1034
struct dshow_stream *stream = (struct dshow_stream*)strm;
1037
stream->quit_flag = PJ_TRUE;
1038
if (stream->cap_thread) {
1039
for (i=0; !stream->cap_thread_exited && i<100; ++i)
1040
pj_thread_sleep(10);
1042
for (i=0; !stream->rend_thread_exited && i<100; ++i)
1043
pj_thread_sleep(10);
1045
IMediaFilter_Stop(stream->dgraph.media_filter);
1047
PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream"));
1052
/* API: Destroy stream. */
1053
static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
1055
struct dshow_stream *stream = (struct dshow_stream*)strm;
1057
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1059
dshow_stream_stop(strm);
1060
destroy_filter_graph(stream);
1062
pj_pool_release(stream->pool);
1067
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */