blob: ff31b3f24a01f9c0e9d310edbaa82dfd0cb5a003 [file] [log] [blame]
Benny Prijonoc45d9512010-12-10 11:04:30 +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#include <pj/unicode.h>
24
25#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
26
27#ifdef _MSC_VER
28# pragma warning(push, 3)
29#endif
30
31#include <windows.h>
32#define COBJMACROS
33#include <DShow.h>
34
35#ifdef _MSC_VER
36# pragma warning(pop)
37#endif
38
39#pragma comment(lib, "Strmiids.lib")
40#pragma comment(lib, "Rpcrt4.lib")
Benny Prijono63a894d2011-04-07 07:22:35 +000041#pragma comment(lib, "Quartz.lib")
Benny Prijonoc45d9512010-12-10 11:04:30 +000042
43#define THIS_FILE "dshow_dev.c"
Sauw Ming8e799122010-12-30 16:31:16 +000044#define DEFAULT_CLOCK_RATE 90000
Benny Prijonoc45d9512010-12-10 11:04:30 +000045#define DEFAULT_WIDTH 640
46#define DEFAULT_HEIGHT 480
47#define DEFAULT_FPS 25
48
49typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
50typedef struct NullRenderer NullRenderer;
51IBaseFilter* NullRenderer_Create(input_callback input_cb,
52 void *user_data);
53typedef struct SourceFilter SourceFilter;
54IBaseFilter* SourceFilter_Create(SourceFilter **pSrc);
55HRESULT SourceFilter_Deliver(SourceFilter *src, void *buf, long size);
Sauw Minga8e08622011-06-13 11:48:37 +000056void SourceFilter_SetMediaType(SourceFilter *src, AM_MEDIA_TYPE *pmt);
Benny Prijonoc45d9512010-12-10 11:04:30 +000057
58typedef struct dshow_fmt_info
59{
60 pjmedia_format_id pjmedia_format;
61 const GUID *dshow_format;
62} dshow_fmt_info;
63
64static dshow_fmt_info dshow_fmts[] =
65{
66 {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2} ,
67 {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24} ,
Sauw Minga8e08622011-06-13 11:48:37 +000068 {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32} ,
69 {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000070};
71
72/* dshow_ device info */
73struct dshow_dev_info
74{
75 pjmedia_vid_dev_info info;
76 unsigned dev_id;
77 WCHAR display_name[192];
78};
79
80/* dshow_ factory */
81struct dshow_factory
82{
83 pjmedia_vid_dev_factory base;
84 pj_pool_t *pool;
85 pj_pool_factory *pf;
86
87 unsigned dev_count;
88 struct dshow_dev_info *dev_info;
89};
90
91/* Video stream. */
92struct dshow_stream
93{
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000094 pjmedia_vid_dev_stream base; /**< Base stream */
95 pjmedia_vid_param param; /**< Settings */
96 pj_pool_t *pool; /**< Memory pool. */
Benny Prijonoc45d9512010-12-10 11:04:30 +000097
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000098 pjmedia_vid_cb vid_cb; /**< Stream callback. */
99 void *user_data; /**< Application data. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000100
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000101 pj_bool_t quit_flag;
102 pj_bool_t rend_thread_exited;
103 pj_bool_t cap_thread_exited;
104 pj_bool_t cap_thread_initialized;
105 pj_thread_desc cap_thread_desc;
106 pj_thread_t *cap_thread;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000107
108 struct dshow_graph
109 {
110 IFilterGraph *filter_graph;
111 IMediaFilter *media_filter;
112 SourceFilter *csource_filter;
113 IBaseFilter *source_filter;
114 IBaseFilter *rend_filter;
115 AM_MEDIA_TYPE *mediatype;
Sauw Ming83db7d62011-06-09 04:08:47 +0000116 } dgraph;
Benny Prijono349037b2011-03-17 11:25:19 +0000117
118 pj_timestamp cap_ts;
119 unsigned cap_ts_inc;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000120};
121
122
123/* Prototypes */
124static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
125static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
126static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
127static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
128 unsigned index,
129 pjmedia_vid_dev_info *info);
130static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
131 pjmedia_vid_dev_factory *f,
132 unsigned index,
133 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000134static pj_status_t dshow_factory_create_stream(
135 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000136 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000137 const pjmedia_vid_cb *cb,
138 void *user_data,
139 pjmedia_vid_dev_stream **p_vid_strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000140
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000141static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000142 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000143static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000144 pjmedia_vid_dev_cap cap,
145 void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000146static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000147 pjmedia_vid_dev_cap cap,
148 const void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000149static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
150static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000151 const pjmedia_frame *frame);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000152static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
153static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000154
155/* Operations */
156static pjmedia_vid_dev_factory_op factory_op =
157{
158 &dshow_factory_init,
159 &dshow_factory_destroy,
160 &dshow_factory_get_dev_count,
161 &dshow_factory_get_dev_info,
162 &dshow_factory_default_param,
163 &dshow_factory_create_stream
164};
165
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000166static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000167{
168 &dshow_stream_get_param,
169 &dshow_stream_get_cap,
170 &dshow_stream_set_cap,
171 &dshow_stream_start,
172 NULL,
173 &dshow_stream_put_frame,
174 &dshow_stream_stop,
175 &dshow_stream_destroy
176};
177
178
179/****************************************************************************
180 * Factory operations
181 */
182/*
183 * Init dshow_ video driver.
184 */
185pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf)
186{
187 struct dshow_factory *f;
188 pj_pool_t *pool;
189
190 pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL);
191 f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory);
192 f->pf = pf;
193 f->pool = pool;
194 f->base.op = &factory_op;
195
196 return &f->base;
197}
198
199/* API: init factory */
200static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
201{
202 struct dshow_factory *df = (struct dshow_factory*)f;
203 struct dshow_dev_info *ddi;
204 int dev_count = 0;
205 unsigned c, i;
206 ICreateDevEnum *dev_enum = NULL;
207 IEnumMoniker *enum_cat = NULL;
208 IMoniker *moniker = NULL;
209 HRESULT hr;
210 ULONG fetched;
211
212 df->dev_count = 0;
213
214 CoInitializeEx(NULL, COINIT_MULTITHREADED);
215
216 hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
217 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
218 (void**)&dev_enum);
219 if (FAILED(hr) ||
220 ICreateDevEnum_CreateClassEnumerator(dev_enum,
221 &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)
222 {
223 PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
224 if (dev_enum)
225 ICreateDevEnum_Release(dev_enum);
226 dev_count = 0;
227 } else {
228 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
229 dev_count++;
230 }
231 }
232
233 /* Add renderer device */
234 dev_count += 1;
235 df->dev_info = (struct dshow_dev_info*)
236 pj_pool_calloc(df->pool, dev_count,
237 sizeof(struct dshow_dev_info));
238
239 if (dev_count > 1) {
240 IEnumMoniker_Reset(enum_cat);
241 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
242 IPropertyBag *prop_bag;
243
244 hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
245 (void**)&prop_bag);
246 if (SUCCEEDED(hr)) {
247 VARIANT var_name;
248
249 VariantInit(&var_name);
250 hr = IPropertyBag_Read(prop_bag, L"FriendlyName",
251 &var_name, NULL);
252 if (SUCCEEDED(hr) && var_name.bstrVal) {
253 WCHAR *wszDisplayName = NULL;
254
255 ddi = &df->dev_info[df->dev_count++];
256 pj_bzero(ddi, sizeof(*ddi));
257 pj_unicode_to_ansi(var_name.bstrVal,
258 wcslen(var_name.bstrVal),
259 ddi->info.name,
260 sizeof(ddi->info.name));
261
262 hr = IMoniker_GetDisplayName(moniker, NULL, NULL,
263 &wszDisplayName);
264 if (hr == S_OK && wszDisplayName) {
265 pj_memcpy(ddi->display_name, wszDisplayName,
266 (wcslen(wszDisplayName)+1) * sizeof(WCHAR));
267 CoTaskMemFree(wszDisplayName);
268 }
269
270 strncpy(ddi->info.driver, "dshow",
271 sizeof(ddi->info.driver));
272 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
273 ddi->info.dir = PJMEDIA_DIR_CAPTURE;
274 ddi->info.has_callback = PJ_TRUE;
275
276 /* Set the device capabilities here */
277 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
278 // TODO: Query the default width, height, fps, and
279 // supported formats
280 }
281 VariantClear(&var_name);
282
283 IPropertyBag_Release(prop_bag);
284 }
285 IMoniker_Release(moniker);
286 }
287
288 IEnumMoniker_Release(enum_cat);
289 ICreateDevEnum_Release(dev_enum);
290 }
291
292 ddi = &df->dev_info[df->dev_count++];
293 pj_bzero(ddi, sizeof(*ddi));
Benny Prijono349037b2011-03-17 11:25:19 +0000294 pj_ansi_strncpy(ddi->info.name, "Video Mixing Renderer",
295 sizeof(ddi->info.name));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000296 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Benny Prijono349037b2011-03-17 11:25:19 +0000297 pj_ansi_strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000298 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
299 ddi->info.dir = PJMEDIA_DIR_RENDER;
300 ddi->info.has_callback = PJ_FALSE;
301 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
302
303 for (c = 0; c < df->dev_count; c++) {
304 ddi = &df->dev_info[c];
305 ddi->info.fmt_cnt = sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
306 ddi->info.fmt = (pjmedia_format*)
307 pj_pool_calloc(df->pool, ddi->info.fmt_cnt,
308 sizeof(pjmedia_format));
309
310 for (i = 0; i < ddi->info.fmt_cnt; i++) {
311 pjmedia_format *fmt = &ddi->info.fmt[i];
312
313 if (ddi->info.dir == PJMEDIA_DIR_RENDER && i > 0)
314 break;
315 pjmedia_format_init_video(fmt, dshow_fmts[i].pjmedia_format,
316 DEFAULT_WIDTH, DEFAULT_HEIGHT,
317 DEFAULT_FPS, 1);
318 }
319 }
320
321// TODO:
322// ddi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
323
324 PJ_LOG(4, (THIS_FILE, "DShow initialized, found %d devices:",
325 df->dev_count));
326 for (c = 0; c < df->dev_count; ++c) {
327 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)",
328 c,
329 df->dev_info[c].info.name,
330 df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ?
331 "capture" : "render"));
332 }
333
334 return PJ_SUCCESS;
335}
336
337/* API: destroy factory */
338static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
339{
340 struct dshow_factory *df = (struct dshow_factory*)f;
341 pj_pool_t *pool = df->pool;
342
343 df->pool = NULL;
344 pj_pool_release(pool);
345
346 CoUninitialize();
347
348 return PJ_SUCCESS;
349}
350
351/* API: get number of devices */
352static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
353{
354 struct dshow_factory *df = (struct dshow_factory*)f;
355 return df->dev_count;
356}
357
358/* API: get device info */
359static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
360 unsigned index,
361 pjmedia_vid_dev_info *info)
362{
363 struct dshow_factory *df = (struct dshow_factory*)f;
364
365 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
366
367 pj_memcpy(info, &df->dev_info[index].info, sizeof(*info));
368
369 return PJ_SUCCESS;
370}
371
372/* API: create default device parameter */
373static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
374 pjmedia_vid_dev_factory *f,
375 unsigned index,
376 pjmedia_vid_param *param)
377{
378 struct dshow_factory *df = (struct dshow_factory*)f;
379 struct dshow_dev_info *di = &df->dev_info[index];
380
381 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
382
383 PJ_UNUSED_ARG(pool);
384
385 pj_bzero(param, sizeof(*param));
Sauw Ming83db7d62011-06-09 04:08:47 +0000386 if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000387 param->dir = PJMEDIA_DIR_CAPTURE;
388 param->cap_id = index;
389 param->rend_id = PJMEDIA_VID_INVALID_DEV;
390 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
391 param->dir = PJMEDIA_DIR_RENDER;
392 param->rend_id = index;
393 param->cap_id = PJMEDIA_VID_INVALID_DEV;
394 } else {
395 return PJMEDIA_EVID_INVDEV;
396 }
397
398 /* Set the device capabilities here */
Sauw Ming8e799122010-12-30 16:31:16 +0000399 param->clock_rate = DEFAULT_CLOCK_RATE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000400 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
401
402 pjmedia_format_copy(&param->fmt, &di->info.fmt[0]);
403
404 return PJ_SUCCESS;
405}
406
407static void input_cb(void *user_data, IMediaSample *pMediaSample)
408{
409 struct dshow_stream *strm = (struct dshow_stream*)user_data;
410 unsigned char *buffer;
Benny Prijono349037b2011-03-17 11:25:19 +0000411 pjmedia_frame frame = {0};
Benny Prijonoc45d9512010-12-10 11:04:30 +0000412
413 if (strm->quit_flag) {
414 strm->cap_thread_exited = PJ_TRUE;
415 return;
416 }
417
418 if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
419 {
420 pj_status_t status;
421
422 status = pj_thread_register("ds_cap", strm->cap_thread_desc,
423 &strm->cap_thread);
424 if (status != PJ_SUCCESS)
425 return;
426 strm->cap_thread_initialized = 1;
427 PJ_LOG(5,(THIS_FILE, "Capture thread started"));
428 }
429
430 IMediaSample_GetPointer(pMediaSample, &buffer);
431
432 frame.type = PJMEDIA_TYPE_VIDEO;
433 IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
434 frame.size = IMediaSample_GetActualDataLength(pMediaSample);
435 frame.bit_info = 0;
Benny Prijono349037b2011-03-17 11:25:19 +0000436 frame.timestamp = strm->cap_ts;
437 strm->cap_ts.u64 += strm->cap_ts_inc;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000438 if (strm->vid_cb.capture_cb)
439 (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
440}
441
442/* API: Put frame from stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000443static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000444 const pjmedia_frame *frame)
445{
446 struct dshow_stream *stream = (struct dshow_stream*)strm;
Sauw Ming83db7d62011-06-09 04:08:47 +0000447 HRESULT hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000448
449 if (stream->quit_flag) {
450 stream->rend_thread_exited = PJ_TRUE;
451 return PJ_SUCCESS;
452 }
453
Sauw Ming83db7d62011-06-09 04:08:47 +0000454 hr = SourceFilter_Deliver(stream->dgraph.csource_filter,
455 frame->buf, frame->size);
456 if (FAILED(hr))
457 return hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000458
459 return PJ_SUCCESS;
460}
461
462static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
463{
464 unsigned i;
465
466 for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
467 if (dshow_fmts[i].pjmedia_format == id)
468 return &dshow_fmts[i];
469 }
470
471 return NULL;
472}
473
474static pj_status_t create_filter_graph(pjmedia_dir dir,
475 unsigned id,
Sauw Ming0531a722011-04-07 08:27:27 +0000476 pj_bool_t use_def_size,
477 pj_bool_t use_def_fps,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000478 struct dshow_factory *df,
479 struct dshow_stream *strm,
480 struct dshow_graph *graph)
481{
482 HRESULT hr;
483 IEnumPins *pEnum;
484 IPin *srcpin = NULL;
485 IPin *sinkpin = NULL;
486 AM_MEDIA_TYPE *mediatype= NULL, mtype;
487 VIDEOINFOHEADER *video_info, *vi = NULL;
488 pjmedia_video_format_detail *vfd;
489 const pjmedia_video_format_info *vfi;
490
491 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
492 strm->param.fmt.id);
493 if (!vfi)
494 return PJMEDIA_EVID_BADFORMAT;
495
496 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC,
497 &IID_IFilterGraph, (LPVOID *)&graph->filter_graph);
498 if (FAILED(hr)) {
499 goto on_error;
500 }
501
502 hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter,
503 (LPVOID *)&graph->media_filter);
504 if (FAILED(hr)) {
505 goto on_error;
506 }
507
508 if (dir == PJMEDIA_DIR_CAPTURE) {
509 IBindCtx *pbc;
510
511 hr = CreateBindCtx(0, &pbc);
512 if (SUCCEEDED (hr)) {
513 IMoniker *moniker;
514 DWORD pchEaten;
515
516 hr = MkParseDisplayName(pbc,
517 df->dev_info[id].display_name,
518 &pchEaten, &moniker);
519 if (SUCCEEDED(hr)) {
520 hr = IMoniker_BindToObject(moniker, pbc, NULL,
521 &IID_IBaseFilter,
522 (LPVOID *)&graph->source_filter);
523 IMoniker_Release(moniker);
524 }
525 IBindCtx_Release(pbc);
526 }
527 if (FAILED(hr)) {
528 goto on_error;
529 }
530 } else {
531 graph->source_filter = SourceFilter_Create(&graph->csource_filter);
532 }
533
534 hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter,
535 L"capture");
536 if (FAILED(hr)) {
537 goto on_error;
538 }
539
540 if (dir == PJMEDIA_DIR_CAPTURE) {
541 graph->rend_filter = NullRenderer_Create(input_cb, strm);
542 } else {
543 hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL,
544 CLSCTX_INPROC, &IID_IBaseFilter,
545 (LPVOID *)&graph->rend_filter);
546 if (FAILED (hr)) {
547 goto on_error;
548 }
549 }
550
551 IBaseFilter_EnumPins(graph->rend_filter, &pEnum);
552 if (SUCCEEDED(hr)) {
553 // Loop through all the pins
554 IPin *pPin = NULL;
555
556 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
557 PIN_DIRECTION pindirtmp;
558
559 hr = IPin_QueryDirection(pPin, &pindirtmp);
560 if (hr == S_OK && pindirtmp == PINDIR_INPUT) {
561 sinkpin = pPin;
562 break;
563 }
564 IPin_Release(pPin);
565 }
566 IEnumPins_Release(pEnum);
567 }
568
569 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
570
571 IBaseFilter_EnumPins(graph->source_filter, &pEnum);
572 if (SUCCEEDED(hr)) {
573 // Loop through all the pins
574 IPin *pPin = NULL;
575
576 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
577 PIN_DIRECTION pindirtmp;
578 const GUID *dshow_format;
579
580 dshow_format = get_dshow_format_info(strm->param.fmt.id)->
581 dshow_format;
582
583 hr = IPin_QueryDirection(pPin, &pindirtmp);
584 if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) {
585 if (SUCCEEDED(hr))
586 IPin_Release(pPin);
587 continue;
588 }
589
590 if (dir == PJMEDIA_DIR_CAPTURE) {
591 IAMStreamConfig *streamcaps;
592
593 hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig,
594 (LPVOID *)&streamcaps);
595 if (SUCCEEDED(hr)) {
596 VIDEO_STREAM_CONFIG_CAPS vscc;
597 int i, isize, icount;
598
599 IAMStreamConfig_GetNumberOfCapabilities(streamcaps,
600 &icount, &isize);
601
602 for (i = 0; i < icount; i++) {
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000603 RPC_STATUS rpcstatus, rpcstatus2;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000604
605 hr = IAMStreamConfig_GetStreamCaps(streamcaps, i,
606 &mediatype,
607 (BYTE *)&vscc);
608 if (FAILED (hr))
609 continue;
610
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000611 if (UuidCompare(&mediatype->subtype,
612 (UUID*)dshow_format,
613 &rpcstatus) == 0 &&
614 rpcstatus == RPC_S_OK &&
615 UuidCompare(&mediatype->formattype,
616 (UUID*)&FORMAT_VideoInfo,
617 &rpcstatus2) == 0 &&
618 rpcstatus2 == RPC_S_OK)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000619 {
620 srcpin = pPin;
621 graph->mediatype = mediatype;
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000622 break;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000623 }
624 }
625 IAMStreamConfig_Release(streamcaps);
626 }
627 } else {
628 srcpin = pPin;
629 mediatype = graph->mediatype = &mtype;
630
631 memset (mediatype, 0, sizeof(AM_MEDIA_TYPE));
632 mediatype->majortype = MEDIATYPE_Video;
633 mediatype->subtype = *dshow_format;
634 mediatype->bFixedSizeSamples = TRUE;
635 mediatype->bTemporalCompression = FALSE;
636
637 vi = (VIDEOINFOHEADER *)
638 CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
639 memset (vi, 0, sizeof(VIDEOINFOHEADER));
640 mediatype->formattype = FORMAT_VideoInfo;
641 mediatype->cbFormat = sizeof(VIDEOINFOHEADER);
642 mediatype->pbFormat = (BYTE *)vi;
643
644 vi->rcSource.bottom = vfd->size.h;
645 vi->rcSource.right = vfd->size.w;
646 vi->rcTarget.bottom = vfd->size.h;
647 vi->rcTarget.right = vfd->size.w;
648
649 vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
650 vi->bmiHeader.biPlanes = 1;
651 vi->bmiHeader.biBitCount = vfi->bpp;
652 vi->bmiHeader.biCompression = strm->param.fmt.id;
653 }
654 if (srcpin)
655 break;
656 IPin_Release(pPin);
657 }
658 IEnumPins_Release(pEnum);
659 }
660
661 if (!srcpin || !sinkpin || !mediatype) {
662 hr = S_FALSE;
663 goto on_error;
664 }
665 video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
Sauw Ming0531a722011-04-07 08:27:27 +0000666 if (!use_def_size) {
667 video_info->bmiHeader.biWidth = vfd->size.w;
668 video_info->bmiHeader.biHeight = vfd->size.h;
669 }
670 if (!use_def_fps && vfd->fps.num != 0)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000671 video_info->AvgTimePerFrame = (LONGLONG) (10000000 *
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000672 (double)vfd->fps.denum /
673 vfd->fps.num);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000674 video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
675 mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
676 if (graph->csource_filter)
Sauw Minga8e08622011-06-13 11:48:37 +0000677 SourceFilter_SetMediaType(graph->csource_filter,
678 mediatype);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000679
680 hr = IFilterGraph_AddFilter(graph->filter_graph,
681 (IBaseFilter *)graph->rend_filter,
682 L"renderer");
683 if (FAILED(hr))
684 goto on_error;
685
686 hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin,
687 mediatype);
Sauw Minge098c592011-04-08 04:14:00 +0000688 if (SUCCEEDED(hr) && (use_def_size || use_def_fps)) {
Sauw Ming0531a722011-04-07 08:27:27 +0000689 pjmedia_format_init_video(&strm->param.fmt, strm->param.fmt.id,
690 video_info->bmiHeader.biWidth,
691 video_info->bmiHeader.biHeight,
692 10000000,
693 (unsigned)video_info->AvgTimePerFrame);
694 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000695
696on_error:
697 if (srcpin)
698 IPin_Release(srcpin);
699 if (sinkpin)
700 IPin_Release(sinkpin);
701 if (vi)
702 CoTaskMemFree(vi);
Benny Prijono63a894d2011-04-07 07:22:35 +0000703 if (FAILED(hr)) {
704 char msg[80];
705 if (AMGetErrorText(hr, msg, sizeof(msg))) {
706 PJ_LOG(4,(THIS_FILE, "Error creating filter graph: %s (hr=0x%x)",
707 msg, hr));
708 }
709 return PJ_EUNKNOWN;
710 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000711
712 return PJ_SUCCESS;
713}
714
Sauw Ming0531a722011-04-07 08:27:27 +0000715static void destroy_filter_graph(struct dshow_stream * stream)
716{
Sauw Ming83db7d62011-06-09 04:08:47 +0000717 if (stream->dgraph.source_filter) {
718 IBaseFilter_Release(stream->dgraph.source_filter);
719 stream->dgraph.source_filter = NULL;
720 }
721 if (stream->dgraph.rend_filter) {
722 IBaseFilter_Release(stream->dgraph.rend_filter);
723 stream->dgraph.rend_filter = NULL;
724 }
725 if (stream->dgraph.media_filter) {
726 IMediaFilter_Release(stream->dgraph.media_filter);
727 stream->dgraph.media_filter = NULL;
728 }
729 if (stream->dgraph.filter_graph) {
730 IFilterGraph_Release(stream->dgraph.filter_graph);
731 stream->dgraph.filter_graph = NULL;
Sauw Ming0531a722011-04-07 08:27:27 +0000732 }
733}
734
Benny Prijonoc45d9512010-12-10 11:04:30 +0000735/* API: create stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000736static pj_status_t dshow_factory_create_stream(
737 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000738 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000739 const pjmedia_vid_cb *cb,
740 void *user_data,
741 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000742{
743 struct dshow_factory *df = (struct dshow_factory*)f;
744 pj_pool_t *pool;
745 struct dshow_stream *strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000746 pj_status_t status;
747
Sauw Ming83db7d62011-06-09 04:08:47 +0000748 PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE ||
749 param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
750
Benny Prijonoc45d9512010-12-10 11:04:30 +0000751 if (!get_dshow_format_info(param->fmt.id))
752 return PJMEDIA_EVID_BADFORMAT;
753
754 /* Create and Initialize stream descriptor */
755 pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL);
756 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
757
758 strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream);
759 pj_memcpy(&strm->param, param, sizeof(*param));
760 strm->pool = pool;
761 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
762 strm->user_data = user_data;
763
Benny Prijonoc45d9512010-12-10 11:04:30 +0000764 if (param->dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijono349037b2011-03-17 11:25:19 +0000765 const pjmedia_video_format_detail *vfd;
766
Sauw Ming83db7d62011-06-09 04:08:47 +0000767 /* Create capture stream here */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000768 status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
Sauw Ming0531a722011-04-07 08:27:27 +0000769 PJ_FALSE, PJ_FALSE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000770 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000771 if (status != PJ_SUCCESS) {
772 destroy_filter_graph(strm);
773 /* Try to use default fps */
774 PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default fps"));
775 status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
776 PJ_FALSE, PJ_TRUE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000777 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000778
779 if (status != PJ_SUCCESS) {
780 /* Still failed, now try to use default fps and size */
781 destroy_filter_graph(strm);
782 /* Try to use default fps */
783 PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default "
784 "size & fps"));
785 status = create_filter_graph(PJMEDIA_DIR_CAPTURE,
786 param->cap_id,
787 PJ_TRUE, PJ_TRUE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000788 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000789 }
790
791 if (status != PJ_SUCCESS)
792 goto on_error;
793 pj_memcpy(param, &strm->param, sizeof(*param));
794 }
Benny Prijono349037b2011-03-17 11:25:19 +0000795
796 vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
797 strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
Sauw Ming83db7d62011-06-09 04:08:47 +0000798 } else if (param->dir & PJMEDIA_DIR_RENDER) {
799 /* Create render stream here */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000800 status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id,
Sauw Ming0531a722011-04-07 08:27:27 +0000801 PJ_FALSE, PJ_FALSE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000802 &strm->dgraph);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000803 if (status != PJ_SUCCESS)
804 goto on_error;
805 }
806
807 /* Apply the remaining settings */
808 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
809 dshow_stream_set_cap(&strm->base,
810 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
811 &param->window);
812 }
813
814 /* Done */
815 strm->base.op = &stream_op;
816 *p_vid_strm = &strm->base;
817
818 return PJ_SUCCESS;
819
820on_error:
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000821 dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
Benny Prijono63a894d2011-04-07 07:22:35 +0000822 return status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000823}
824
825/* API: Get stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000826static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000827 pjmedia_vid_param *pi)
828{
829 struct dshow_stream *strm = (struct dshow_stream*)s;
830
831 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
832
833 pj_memcpy(pi, &strm->param, sizeof(*pi));
834
835 if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
836 &pi->window) == PJ_SUCCESS)
837 {
838 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
839 }
840
841 return PJ_SUCCESS;
842}
843
844/* API: get capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000845static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000846 pjmedia_vid_dev_cap cap,
847 void *pval)
848{
849 struct dshow_stream *strm = (struct dshow_stream*)s;
850
851 PJ_UNUSED_ARG(strm);
852
853 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
854
855 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
856 {
857 *(unsigned*)pval = 0;
858 return PJ_SUCCESS;
859 } else {
860 return PJMEDIA_EVID_INVCAP;
861 }
862}
863
864/* API: set capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000865static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000866 pjmedia_vid_dev_cap cap,
867 const void *pval)
868{
869 struct dshow_stream *strm = (struct dshow_stream*)s;
870
871 PJ_UNUSED_ARG(strm);
872
873 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
874
875 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
876 {
877 // set renderer's window here
878 return PJ_SUCCESS;
879 }
880
881 return PJMEDIA_EVID_INVCAP;
882}
883
884/* API: Start stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000885static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000886{
887 struct dshow_stream *stream = (struct dshow_stream*)strm;
Sauw Ming83db7d62011-06-09 04:08:47 +0000888 HRESULT hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000889
890 stream->quit_flag = PJ_FALSE;
891 stream->cap_thread_exited = PJ_FALSE;
892 stream->rend_thread_exited = PJ_FALSE;
893
Sauw Ming83db7d62011-06-09 04:08:47 +0000894 hr = IMediaFilter_Run(stream->dgraph.media_filter, 0);
895 if (FAILED(hr)) {
896 char msg[80];
897 if (AMGetErrorText(hr, msg, sizeof(msg))) {
898 PJ_LOG(4,(THIS_FILE, "Error starting media: %s", msg));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000899 }
Sauw Ming83db7d62011-06-09 04:08:47 +0000900 return PJ_EUNKNOWN;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000901 }
902
903 PJ_LOG(4, (THIS_FILE, "Starting dshow video stream"));
904
905 return PJ_SUCCESS;
906}
907
908/* API: Stop stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000909static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000910{
911 struct dshow_stream *stream = (struct dshow_stream*)strm;
912 unsigned i;
913
914 stream->quit_flag = PJ_TRUE;
915 if (stream->cap_thread) {
916 for (i=0; !stream->cap_thread_exited && i<100; ++i)
917 pj_thread_sleep(10);
918 }
919 for (i=0; !stream->rend_thread_exited && i<100; ++i)
920 pj_thread_sleep(10);
921
Sauw Ming83db7d62011-06-09 04:08:47 +0000922 IMediaFilter_Stop(stream->dgraph.media_filter);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000923
924 PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream"));
925
926 return PJ_SUCCESS;
927}
928
Benny Prijonoc45d9512010-12-10 11:04:30 +0000929/* API: Destroy stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000930static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000931{
932 struct dshow_stream *stream = (struct dshow_stream*)strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000933
934 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
935
936 dshow_stream_stop(strm);
Sauw Ming0531a722011-04-07 08:27:27 +0000937 destroy_filter_graph(stream);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000938
939 pj_pool_release(stream->pool);
940
941 return PJ_SUCCESS;
942}
943
944#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */