blob: d18b0cf8f3599e12abb6cebe35c3325366305daf [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;
Sauw Ming7f7c5bd2011-06-21 09:33:01 +000085 pj_pool_t *dev_pool;
Benny Prijonoc45d9512010-12-10 11:04:30 +000086 pj_pool_factory *pf;
87
88 unsigned dev_count;
89 struct dshow_dev_info *dev_info;
90};
91
92/* Video stream. */
93struct dshow_stream
94{
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000095 pjmedia_vid_dev_stream base; /**< Base stream */
96 pjmedia_vid_param param; /**< Settings */
97 pj_pool_t *pool; /**< Memory pool. */
Benny Prijonoc45d9512010-12-10 11:04:30 +000098
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000099 pjmedia_vid_cb vid_cb; /**< Stream callback. */
100 void *user_data; /**< Application data. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000101
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000102 pj_bool_t quit_flag;
103 pj_bool_t rend_thread_exited;
104 pj_bool_t cap_thread_exited;
105 pj_bool_t cap_thread_initialized;
106 pj_thread_desc cap_thread_desc;
107 pj_thread_t *cap_thread;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000108
109 struct dshow_graph
110 {
111 IFilterGraph *filter_graph;
112 IMediaFilter *media_filter;
113 SourceFilter *csource_filter;
114 IBaseFilter *source_filter;
115 IBaseFilter *rend_filter;
116 AM_MEDIA_TYPE *mediatype;
Sauw Ming83db7d62011-06-09 04:08:47 +0000117 } dgraph;
Benny Prijono349037b2011-03-17 11:25:19 +0000118
119 pj_timestamp cap_ts;
120 unsigned cap_ts_inc;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000121};
122
123
124/* Prototypes */
125static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
126static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000127static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000128static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
129static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
130 unsigned index,
131 pjmedia_vid_dev_info *info);
132static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
133 pjmedia_vid_dev_factory *f,
134 unsigned index,
135 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000136static pj_status_t dshow_factory_create_stream(
137 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000138 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000139 const pjmedia_vid_cb *cb,
140 void *user_data,
141 pjmedia_vid_dev_stream **p_vid_strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000142
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000143static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000144 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000145static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000146 pjmedia_vid_dev_cap cap,
147 void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000148static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000149 pjmedia_vid_dev_cap cap,
150 const void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000151static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
152static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000153 const pjmedia_frame *frame);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000154static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
155static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000156
157/* Operations */
158static pjmedia_vid_dev_factory_op factory_op =
159{
160 &dshow_factory_init,
161 &dshow_factory_destroy,
162 &dshow_factory_get_dev_count,
163 &dshow_factory_get_dev_info,
164 &dshow_factory_default_param,
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000165 &dshow_factory_create_stream,
166 &dshow_factory_refresh
Benny Prijonoc45d9512010-12-10 11:04:30 +0000167};
168
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000169static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000170{
171 &dshow_stream_get_param,
172 &dshow_stream_get_cap,
173 &dshow_stream_set_cap,
174 &dshow_stream_start,
175 NULL,
176 &dshow_stream_put_frame,
177 &dshow_stream_stop,
178 &dshow_stream_destroy
179};
180
181
182/****************************************************************************
183 * Factory operations
184 */
185/*
186 * Init dshow_ video driver.
187 */
188pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf)
189{
190 struct dshow_factory *f;
191 pj_pool_t *pool;
192
193 pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL);
194 f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory);
195 f->pf = pf;
196 f->pool = pool;
197 f->base.op = &factory_op;
198
199 return &f->base;
200}
201
202/* API: init factory */
203static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
204{
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000205 CoInitializeEx(NULL, COINIT_MULTITHREADED);
206
207 return dshow_factory_refresh(f);
208}
209
210/* API: destroy factory */
211static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
212{
213 struct dshow_factory *df = (struct dshow_factory*)f;
214 pj_pool_t *pool = df->pool;
215
216 df->pool = NULL;
217 if (df->dev_pool)
218 pj_pool_release(df->dev_pool);
219 if (pool)
220 pj_pool_release(pool);
221
222 CoUninitialize();
223
224 return PJ_SUCCESS;
225}
226
227/* API: refresh the list of devices */
228static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f)
229{
Benny Prijonoc45d9512010-12-10 11:04:30 +0000230 struct dshow_factory *df = (struct dshow_factory*)f;
231 struct dshow_dev_info *ddi;
232 int dev_count = 0;
233 unsigned c, i;
234 ICreateDevEnum *dev_enum = NULL;
235 IEnumMoniker *enum_cat = NULL;
236 IMoniker *moniker = NULL;
237 HRESULT hr;
238 ULONG fetched;
239
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000240 if (df->dev_pool) {
241 pj_pool_release(df->dev_pool);
242 df->dev_pool = NULL;
243 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000244
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000245 df->dev_count = 0;
246 df->dev_pool = pj_pool_create(df->pf, "dshow video", 500, 500, NULL);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000247
248 hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
249 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
250 (void**)&dev_enum);
251 if (FAILED(hr) ||
252 ICreateDevEnum_CreateClassEnumerator(dev_enum,
253 &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)
254 {
255 PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
256 if (dev_enum)
257 ICreateDevEnum_Release(dev_enum);
258 dev_count = 0;
259 } else {
260 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
261 dev_count++;
262 }
263 }
264
265 /* Add renderer device */
266 dev_count += 1;
267 df->dev_info = (struct dshow_dev_info*)
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000268 pj_pool_calloc(df->dev_pool, dev_count,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000269 sizeof(struct dshow_dev_info));
270
271 if (dev_count > 1) {
272 IEnumMoniker_Reset(enum_cat);
273 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
274 IPropertyBag *prop_bag;
275
276 hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
277 (void**)&prop_bag);
278 if (SUCCEEDED(hr)) {
279 VARIANT var_name;
280
281 VariantInit(&var_name);
282 hr = IPropertyBag_Read(prop_bag, L"FriendlyName",
283 &var_name, NULL);
284 if (SUCCEEDED(hr) && var_name.bstrVal) {
285 WCHAR *wszDisplayName = NULL;
286
287 ddi = &df->dev_info[df->dev_count++];
288 pj_bzero(ddi, sizeof(*ddi));
289 pj_unicode_to_ansi(var_name.bstrVal,
290 wcslen(var_name.bstrVal),
291 ddi->info.name,
292 sizeof(ddi->info.name));
293
294 hr = IMoniker_GetDisplayName(moniker, NULL, NULL,
295 &wszDisplayName);
296 if (hr == S_OK && wszDisplayName) {
297 pj_memcpy(ddi->display_name, wszDisplayName,
298 (wcslen(wszDisplayName)+1) * sizeof(WCHAR));
299 CoTaskMemFree(wszDisplayName);
300 }
301
302 strncpy(ddi->info.driver, "dshow",
303 sizeof(ddi->info.driver));
304 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
305 ddi->info.dir = PJMEDIA_DIR_CAPTURE;
306 ddi->info.has_callback = PJ_TRUE;
307
308 /* Set the device capabilities here */
309 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
310 // TODO: Query the default width, height, fps, and
311 // supported formats
312 }
313 VariantClear(&var_name);
314
315 IPropertyBag_Release(prop_bag);
316 }
317 IMoniker_Release(moniker);
318 }
319
320 IEnumMoniker_Release(enum_cat);
321 ICreateDevEnum_Release(dev_enum);
322 }
323
324 ddi = &df->dev_info[df->dev_count++];
325 pj_bzero(ddi, sizeof(*ddi));
Benny Prijono349037b2011-03-17 11:25:19 +0000326 pj_ansi_strncpy(ddi->info.name, "Video Mixing Renderer",
327 sizeof(ddi->info.name));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000328 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Benny Prijono349037b2011-03-17 11:25:19 +0000329 pj_ansi_strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000330 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
331 ddi->info.dir = PJMEDIA_DIR_RENDER;
332 ddi->info.has_callback = PJ_FALSE;
333 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
334
335 for (c = 0; c < df->dev_count; c++) {
336 ddi = &df->dev_info[c];
337 ddi->info.fmt_cnt = sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
338 ddi->info.fmt = (pjmedia_format*)
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000339 pj_pool_calloc(df->dev_pool, ddi->info.fmt_cnt,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000340 sizeof(pjmedia_format));
341
342 for (i = 0; i < ddi->info.fmt_cnt; i++) {
343 pjmedia_format *fmt = &ddi->info.fmt[i];
344
345 if (ddi->info.dir == PJMEDIA_DIR_RENDER && i > 0)
346 break;
347 pjmedia_format_init_video(fmt, dshow_fmts[i].pjmedia_format,
348 DEFAULT_WIDTH, DEFAULT_HEIGHT,
349 DEFAULT_FPS, 1);
350 }
351 }
352
353// TODO:
354// ddi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
355
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000356 PJ_LOG(4, (THIS_FILE, "DShow has %d devices:",
Benny Prijonoc45d9512010-12-10 11:04:30 +0000357 df->dev_count));
358 for (c = 0; c < df->dev_count; ++c) {
359 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)",
360 c,
361 df->dev_info[c].info.name,
362 df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ?
363 "capture" : "render"));
364 }
365
366 return PJ_SUCCESS;
367}
368
Benny Prijonoc45d9512010-12-10 11:04:30 +0000369/* API: get number of devices */
370static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
371{
372 struct dshow_factory *df = (struct dshow_factory*)f;
373 return df->dev_count;
374}
375
376/* API: get device info */
377static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
378 unsigned index,
379 pjmedia_vid_dev_info *info)
380{
381 struct dshow_factory *df = (struct dshow_factory*)f;
382
383 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
384
385 pj_memcpy(info, &df->dev_info[index].info, sizeof(*info));
386
387 return PJ_SUCCESS;
388}
389
390/* API: create default device parameter */
391static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
392 pjmedia_vid_dev_factory *f,
393 unsigned index,
394 pjmedia_vid_param *param)
395{
396 struct dshow_factory *df = (struct dshow_factory*)f;
397 struct dshow_dev_info *di = &df->dev_info[index];
398
399 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
400
401 PJ_UNUSED_ARG(pool);
402
403 pj_bzero(param, sizeof(*param));
Sauw Ming83db7d62011-06-09 04:08:47 +0000404 if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000405 param->dir = PJMEDIA_DIR_CAPTURE;
406 param->cap_id = index;
407 param->rend_id = PJMEDIA_VID_INVALID_DEV;
408 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
409 param->dir = PJMEDIA_DIR_RENDER;
410 param->rend_id = index;
411 param->cap_id = PJMEDIA_VID_INVALID_DEV;
412 } else {
413 return PJMEDIA_EVID_INVDEV;
414 }
415
416 /* Set the device capabilities here */
Sauw Ming8e799122010-12-30 16:31:16 +0000417 param->clock_rate = DEFAULT_CLOCK_RATE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000418 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
419
420 pjmedia_format_copy(&param->fmt, &di->info.fmt[0]);
421
422 return PJ_SUCCESS;
423}
424
425static void input_cb(void *user_data, IMediaSample *pMediaSample)
426{
427 struct dshow_stream *strm = (struct dshow_stream*)user_data;
428 unsigned char *buffer;
Benny Prijono349037b2011-03-17 11:25:19 +0000429 pjmedia_frame frame = {0};
Benny Prijonoc45d9512010-12-10 11:04:30 +0000430
431 if (strm->quit_flag) {
432 strm->cap_thread_exited = PJ_TRUE;
433 return;
434 }
435
436 if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
437 {
438 pj_status_t status;
439
440 status = pj_thread_register("ds_cap", strm->cap_thread_desc,
441 &strm->cap_thread);
442 if (status != PJ_SUCCESS)
443 return;
444 strm->cap_thread_initialized = 1;
445 PJ_LOG(5,(THIS_FILE, "Capture thread started"));
446 }
447
448 IMediaSample_GetPointer(pMediaSample, &buffer);
449
450 frame.type = PJMEDIA_TYPE_VIDEO;
451 IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
452 frame.size = IMediaSample_GetActualDataLength(pMediaSample);
453 frame.bit_info = 0;
Benny Prijono349037b2011-03-17 11:25:19 +0000454 frame.timestamp = strm->cap_ts;
455 strm->cap_ts.u64 += strm->cap_ts_inc;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000456 if (strm->vid_cb.capture_cb)
457 (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
458}
459
460/* API: Put frame from stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000461static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000462 const pjmedia_frame *frame)
463{
464 struct dshow_stream *stream = (struct dshow_stream*)strm;
Sauw Ming83db7d62011-06-09 04:08:47 +0000465 HRESULT hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000466
467 if (stream->quit_flag) {
468 stream->rend_thread_exited = PJ_TRUE;
469 return PJ_SUCCESS;
470 }
471
Sauw Ming83db7d62011-06-09 04:08:47 +0000472 hr = SourceFilter_Deliver(stream->dgraph.csource_filter,
473 frame->buf, frame->size);
474 if (FAILED(hr))
475 return hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000476
477 return PJ_SUCCESS;
478}
479
480static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
481{
482 unsigned i;
483
484 for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
485 if (dshow_fmts[i].pjmedia_format == id)
486 return &dshow_fmts[i];
487 }
488
489 return NULL;
490}
491
492static pj_status_t create_filter_graph(pjmedia_dir dir,
493 unsigned id,
Sauw Ming0531a722011-04-07 08:27:27 +0000494 pj_bool_t use_def_size,
495 pj_bool_t use_def_fps,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000496 struct dshow_factory *df,
497 struct dshow_stream *strm,
498 struct dshow_graph *graph)
499{
500 HRESULT hr;
501 IEnumPins *pEnum;
502 IPin *srcpin = NULL;
503 IPin *sinkpin = NULL;
504 AM_MEDIA_TYPE *mediatype= NULL, mtype;
505 VIDEOINFOHEADER *video_info, *vi = NULL;
506 pjmedia_video_format_detail *vfd;
507 const pjmedia_video_format_info *vfi;
508
509 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
510 strm->param.fmt.id);
511 if (!vfi)
512 return PJMEDIA_EVID_BADFORMAT;
513
514 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC,
515 &IID_IFilterGraph, (LPVOID *)&graph->filter_graph);
516 if (FAILED(hr)) {
517 goto on_error;
518 }
519
520 hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter,
521 (LPVOID *)&graph->media_filter);
522 if (FAILED(hr)) {
523 goto on_error;
524 }
525
526 if (dir == PJMEDIA_DIR_CAPTURE) {
527 IBindCtx *pbc;
528
529 hr = CreateBindCtx(0, &pbc);
530 if (SUCCEEDED (hr)) {
531 IMoniker *moniker;
532 DWORD pchEaten;
533
534 hr = MkParseDisplayName(pbc,
535 df->dev_info[id].display_name,
536 &pchEaten, &moniker);
537 if (SUCCEEDED(hr)) {
538 hr = IMoniker_BindToObject(moniker, pbc, NULL,
539 &IID_IBaseFilter,
540 (LPVOID *)&graph->source_filter);
541 IMoniker_Release(moniker);
542 }
543 IBindCtx_Release(pbc);
544 }
545 if (FAILED(hr)) {
546 goto on_error;
547 }
548 } else {
549 graph->source_filter = SourceFilter_Create(&graph->csource_filter);
550 }
551
552 hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter,
553 L"capture");
554 if (FAILED(hr)) {
555 goto on_error;
556 }
557
558 if (dir == PJMEDIA_DIR_CAPTURE) {
559 graph->rend_filter = NullRenderer_Create(input_cb, strm);
560 } else {
561 hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL,
562 CLSCTX_INPROC, &IID_IBaseFilter,
563 (LPVOID *)&graph->rend_filter);
564 if (FAILED (hr)) {
565 goto on_error;
566 }
567 }
568
569 IBaseFilter_EnumPins(graph->rend_filter, &pEnum);
570 if (SUCCEEDED(hr)) {
571 // Loop through all the pins
572 IPin *pPin = NULL;
573
574 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
575 PIN_DIRECTION pindirtmp;
576
577 hr = IPin_QueryDirection(pPin, &pindirtmp);
578 if (hr == S_OK && pindirtmp == PINDIR_INPUT) {
579 sinkpin = pPin;
580 break;
581 }
582 IPin_Release(pPin);
583 }
584 IEnumPins_Release(pEnum);
585 }
586
587 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
588
589 IBaseFilter_EnumPins(graph->source_filter, &pEnum);
590 if (SUCCEEDED(hr)) {
591 // Loop through all the pins
592 IPin *pPin = NULL;
593
594 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
595 PIN_DIRECTION pindirtmp;
596 const GUID *dshow_format;
597
598 dshow_format = get_dshow_format_info(strm->param.fmt.id)->
599 dshow_format;
600
601 hr = IPin_QueryDirection(pPin, &pindirtmp);
602 if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) {
603 if (SUCCEEDED(hr))
604 IPin_Release(pPin);
605 continue;
606 }
607
608 if (dir == PJMEDIA_DIR_CAPTURE) {
609 IAMStreamConfig *streamcaps;
610
611 hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig,
612 (LPVOID *)&streamcaps);
613 if (SUCCEEDED(hr)) {
614 VIDEO_STREAM_CONFIG_CAPS vscc;
615 int i, isize, icount;
616
617 IAMStreamConfig_GetNumberOfCapabilities(streamcaps,
618 &icount, &isize);
619
620 for (i = 0; i < icount; i++) {
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000621 RPC_STATUS rpcstatus, rpcstatus2;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000622
623 hr = IAMStreamConfig_GetStreamCaps(streamcaps, i,
624 &mediatype,
625 (BYTE *)&vscc);
626 if (FAILED (hr))
627 continue;
628
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000629 if (UuidCompare(&mediatype->subtype,
630 (UUID*)dshow_format,
631 &rpcstatus) == 0 &&
632 rpcstatus == RPC_S_OK &&
633 UuidCompare(&mediatype->formattype,
634 (UUID*)&FORMAT_VideoInfo,
635 &rpcstatus2) == 0 &&
636 rpcstatus2 == RPC_S_OK)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000637 {
638 srcpin = pPin;
639 graph->mediatype = mediatype;
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000640 break;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000641 }
642 }
643 IAMStreamConfig_Release(streamcaps);
644 }
645 } else {
646 srcpin = pPin;
647 mediatype = graph->mediatype = &mtype;
648
649 memset (mediatype, 0, sizeof(AM_MEDIA_TYPE));
650 mediatype->majortype = MEDIATYPE_Video;
651 mediatype->subtype = *dshow_format;
652 mediatype->bFixedSizeSamples = TRUE;
653 mediatype->bTemporalCompression = FALSE;
654
655 vi = (VIDEOINFOHEADER *)
656 CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
657 memset (vi, 0, sizeof(VIDEOINFOHEADER));
658 mediatype->formattype = FORMAT_VideoInfo;
659 mediatype->cbFormat = sizeof(VIDEOINFOHEADER);
660 mediatype->pbFormat = (BYTE *)vi;
661
662 vi->rcSource.bottom = vfd->size.h;
663 vi->rcSource.right = vfd->size.w;
664 vi->rcTarget.bottom = vfd->size.h;
665 vi->rcTarget.right = vfd->size.w;
666
667 vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
668 vi->bmiHeader.biPlanes = 1;
669 vi->bmiHeader.biBitCount = vfi->bpp;
670 vi->bmiHeader.biCompression = strm->param.fmt.id;
671 }
672 if (srcpin)
673 break;
674 IPin_Release(pPin);
675 }
676 IEnumPins_Release(pEnum);
677 }
678
679 if (!srcpin || !sinkpin || !mediatype) {
680 hr = S_FALSE;
681 goto on_error;
682 }
683 video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
Sauw Ming0531a722011-04-07 08:27:27 +0000684 if (!use_def_size) {
685 video_info->bmiHeader.biWidth = vfd->size.w;
686 video_info->bmiHeader.biHeight = vfd->size.h;
687 }
688 if (!use_def_fps && vfd->fps.num != 0)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000689 video_info->AvgTimePerFrame = (LONGLONG) (10000000 *
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000690 (double)vfd->fps.denum /
691 vfd->fps.num);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000692 video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
693 mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
694 if (graph->csource_filter)
Sauw Minga8e08622011-06-13 11:48:37 +0000695 SourceFilter_SetMediaType(graph->csource_filter,
696 mediatype);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000697
698 hr = IFilterGraph_AddFilter(graph->filter_graph,
699 (IBaseFilter *)graph->rend_filter,
700 L"renderer");
701 if (FAILED(hr))
702 goto on_error;
703
704 hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin,
705 mediatype);
Sauw Minge098c592011-04-08 04:14:00 +0000706 if (SUCCEEDED(hr) && (use_def_size || use_def_fps)) {
Sauw Ming0531a722011-04-07 08:27:27 +0000707 pjmedia_format_init_video(&strm->param.fmt, strm->param.fmt.id,
708 video_info->bmiHeader.biWidth,
709 video_info->bmiHeader.biHeight,
710 10000000,
711 (unsigned)video_info->AvgTimePerFrame);
712 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000713
714on_error:
715 if (srcpin)
716 IPin_Release(srcpin);
717 if (sinkpin)
718 IPin_Release(sinkpin);
719 if (vi)
720 CoTaskMemFree(vi);
Benny Prijono63a894d2011-04-07 07:22:35 +0000721 if (FAILED(hr)) {
722 char msg[80];
723 if (AMGetErrorText(hr, msg, sizeof(msg))) {
724 PJ_LOG(4,(THIS_FILE, "Error creating filter graph: %s (hr=0x%x)",
725 msg, hr));
726 }
727 return PJ_EUNKNOWN;
728 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000729
730 return PJ_SUCCESS;
731}
732
Sauw Ming0531a722011-04-07 08:27:27 +0000733static void destroy_filter_graph(struct dshow_stream * stream)
734{
Sauw Ming83db7d62011-06-09 04:08:47 +0000735 if (stream->dgraph.source_filter) {
736 IBaseFilter_Release(stream->dgraph.source_filter);
737 stream->dgraph.source_filter = NULL;
738 }
739 if (stream->dgraph.rend_filter) {
740 IBaseFilter_Release(stream->dgraph.rend_filter);
741 stream->dgraph.rend_filter = NULL;
742 }
743 if (stream->dgraph.media_filter) {
744 IMediaFilter_Release(stream->dgraph.media_filter);
745 stream->dgraph.media_filter = NULL;
746 }
747 if (stream->dgraph.filter_graph) {
748 IFilterGraph_Release(stream->dgraph.filter_graph);
749 stream->dgraph.filter_graph = NULL;
Sauw Ming0531a722011-04-07 08:27:27 +0000750 }
751}
752
Benny Prijonoc45d9512010-12-10 11:04:30 +0000753/* API: create stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000754static pj_status_t dshow_factory_create_stream(
755 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000756 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000757 const pjmedia_vid_cb *cb,
758 void *user_data,
759 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000760{
761 struct dshow_factory *df = (struct dshow_factory*)f;
762 pj_pool_t *pool;
763 struct dshow_stream *strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000764 pj_status_t status;
765
Sauw Ming83db7d62011-06-09 04:08:47 +0000766 PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE ||
767 param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
768
Benny Prijonoc45d9512010-12-10 11:04:30 +0000769 if (!get_dshow_format_info(param->fmt.id))
770 return PJMEDIA_EVID_BADFORMAT;
771
772 /* Create and Initialize stream descriptor */
773 pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL);
774 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
775
776 strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream);
777 pj_memcpy(&strm->param, param, sizeof(*param));
778 strm->pool = pool;
779 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
780 strm->user_data = user_data;
Nanang Izzuddina6efd6e2011-07-15 02:00:37 +0000781 pjmedia_event_publisher_init(&strm->base.epub, PJMEDIA_SIG_VID_DEV_DSHOW);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000782
Benny Prijonoc45d9512010-12-10 11:04:30 +0000783 if (param->dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijono349037b2011-03-17 11:25:19 +0000784 const pjmedia_video_format_detail *vfd;
785
Sauw Ming83db7d62011-06-09 04:08:47 +0000786 /* Create capture stream here */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000787 status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
Sauw Ming0531a722011-04-07 08:27:27 +0000788 PJ_FALSE, PJ_FALSE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000789 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000790 if (status != PJ_SUCCESS) {
791 destroy_filter_graph(strm);
792 /* Try to use default fps */
793 PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default fps"));
794 status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
795 PJ_FALSE, PJ_TRUE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000796 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000797
798 if (status != PJ_SUCCESS) {
799 /* Still failed, now try to use default fps and size */
800 destroy_filter_graph(strm);
801 /* Try to use default fps */
802 PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default "
803 "size & fps"));
804 status = create_filter_graph(PJMEDIA_DIR_CAPTURE,
805 param->cap_id,
806 PJ_TRUE, PJ_TRUE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000807 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000808 }
809
810 if (status != PJ_SUCCESS)
811 goto on_error;
812 pj_memcpy(param, &strm->param, sizeof(*param));
813 }
Benny Prijono349037b2011-03-17 11:25:19 +0000814
815 vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
816 strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
Sauw Ming83db7d62011-06-09 04:08:47 +0000817 } else if (param->dir & PJMEDIA_DIR_RENDER) {
818 /* Create render stream here */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000819 status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id,
Sauw Ming0531a722011-04-07 08:27:27 +0000820 PJ_FALSE, PJ_FALSE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000821 &strm->dgraph);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000822 if (status != PJ_SUCCESS)
823 goto on_error;
824 }
825
826 /* Apply the remaining settings */
827 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
828 dshow_stream_set_cap(&strm->base,
829 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
830 &param->window);
831 }
832
833 /* Done */
834 strm->base.op = &stream_op;
835 *p_vid_strm = &strm->base;
836
837 return PJ_SUCCESS;
838
839on_error:
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000840 dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
Benny Prijono63a894d2011-04-07 07:22:35 +0000841 return status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000842}
843
844/* API: Get stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000845static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000846 pjmedia_vid_param *pi)
847{
848 struct dshow_stream *strm = (struct dshow_stream*)s;
849
850 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
851
852 pj_memcpy(pi, &strm->param, sizeof(*pi));
853
854 if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
855 &pi->window) == PJ_SUCCESS)
856 {
857 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
858 }
859
860 return PJ_SUCCESS;
861}
862
863/* API: get capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000864static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000865 pjmedia_vid_dev_cap cap,
866 void *pval)
867{
868 struct dshow_stream *strm = (struct dshow_stream*)s;
869
870 PJ_UNUSED_ARG(strm);
871
872 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
873
874 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
875 {
876 *(unsigned*)pval = 0;
877 return PJ_SUCCESS;
878 } else {
879 return PJMEDIA_EVID_INVCAP;
880 }
881}
882
883/* API: set capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000884static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000885 pjmedia_vid_dev_cap cap,
886 const void *pval)
887{
888 struct dshow_stream *strm = (struct dshow_stream*)s;
889
890 PJ_UNUSED_ARG(strm);
891
892 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
893
894 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
895 {
896 // set renderer's window here
897 return PJ_SUCCESS;
898 }
899
900 return PJMEDIA_EVID_INVCAP;
901}
902
903/* API: Start stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000904static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000905{
906 struct dshow_stream *stream = (struct dshow_stream*)strm;
Sauw Ming83db7d62011-06-09 04:08:47 +0000907 HRESULT hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000908
909 stream->quit_flag = PJ_FALSE;
910 stream->cap_thread_exited = PJ_FALSE;
911 stream->rend_thread_exited = PJ_FALSE;
912
Sauw Ming83db7d62011-06-09 04:08:47 +0000913 hr = IMediaFilter_Run(stream->dgraph.media_filter, 0);
914 if (FAILED(hr)) {
915 char msg[80];
916 if (AMGetErrorText(hr, msg, sizeof(msg))) {
917 PJ_LOG(4,(THIS_FILE, "Error starting media: %s", msg));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000918 }
Sauw Ming83db7d62011-06-09 04:08:47 +0000919 return PJ_EUNKNOWN;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000920 }
921
922 PJ_LOG(4, (THIS_FILE, "Starting dshow video stream"));
923
924 return PJ_SUCCESS;
925}
926
927/* API: Stop stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000928static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000929{
930 struct dshow_stream *stream = (struct dshow_stream*)strm;
931 unsigned i;
932
933 stream->quit_flag = PJ_TRUE;
934 if (stream->cap_thread) {
935 for (i=0; !stream->cap_thread_exited && i<100; ++i)
936 pj_thread_sleep(10);
937 }
938 for (i=0; !stream->rend_thread_exited && i<100; ++i)
939 pj_thread_sleep(10);
940
Sauw Ming83db7d62011-06-09 04:08:47 +0000941 IMediaFilter_Stop(stream->dgraph.media_filter);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000942
943 PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream"));
944
945 return PJ_SUCCESS;
946}
947
Benny Prijonoc45d9512010-12-10 11:04:30 +0000948/* API: Destroy stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000949static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000950{
951 struct dshow_stream *stream = (struct dshow_stream*)strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000952
953 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
954
955 dshow_stream_stop(strm);
Sauw Ming0531a722011-04-07 08:27:27 +0000956 destroy_filter_graph(stream);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000957
958 pj_pool_release(stream->pool);
959
960 return PJ_SUCCESS;
961}
962
963#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */