blob: 600e419d8c91ac5b6b1de12503e1259921a3fb20 [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")
41
42#define THIS_FILE "dshow_dev.c"
Sauw Ming8e799122010-12-30 16:31:16 +000043#define DEFAULT_CLOCK_RATE 90000
Benny Prijonoc45d9512010-12-10 11:04:30 +000044#define DEFAULT_WIDTH 640
45#define DEFAULT_HEIGHT 480
46#define DEFAULT_FPS 25
47
48typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
49typedef struct NullRenderer NullRenderer;
50IBaseFilter* NullRenderer_Create(input_callback input_cb,
51 void *user_data);
52typedef struct SourceFilter SourceFilter;
53IBaseFilter* SourceFilter_Create(SourceFilter **pSrc);
54HRESULT SourceFilter_Deliver(SourceFilter *src, void *buf, long size);
55void SourceFilter_SetBufferSize(SourceFilter *src, long size);
56
57typedef struct dshow_fmt_info
58{
59 pjmedia_format_id pjmedia_format;
60 const GUID *dshow_format;
61} dshow_fmt_info;
62
63static dshow_fmt_info dshow_fmts[] =
64{
65 {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2} ,
66 {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24} ,
67 {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32} ,
Benny Prijono349037b2011-03-17 11:25:19 +000068 //{PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000069};
70
71/* dshow_ device info */
72struct dshow_dev_info
73{
74 pjmedia_vid_dev_info info;
75 unsigned dev_id;
76 WCHAR display_name[192];
77};
78
79/* dshow_ factory */
80struct dshow_factory
81{
82 pjmedia_vid_dev_factory base;
83 pj_pool_t *pool;
84 pj_pool_factory *pf;
85
86 unsigned dev_count;
87 struct dshow_dev_info *dev_info;
88};
89
90/* Video stream. */
91struct dshow_stream
92{
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000093 pjmedia_vid_dev_stream base; /**< Base stream */
94 pjmedia_vid_param param; /**< Settings */
95 pj_pool_t *pool; /**< Memory pool. */
Benny Prijonoc45d9512010-12-10 11:04:30 +000096
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000097 pjmedia_vid_cb vid_cb; /**< Stream callback. */
98 void *user_data; /**< Application data. */
Benny Prijonoc45d9512010-12-10 11:04:30 +000099
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000100 pj_bool_t quit_flag;
101 pj_bool_t rend_thread_exited;
102 pj_bool_t cap_thread_exited;
103 pj_bool_t cap_thread_initialized;
104 pj_thread_desc cap_thread_desc;
105 pj_thread_t *cap_thread;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000106
107 struct dshow_graph
108 {
109 IFilterGraph *filter_graph;
110 IMediaFilter *media_filter;
111 SourceFilter *csource_filter;
112 IBaseFilter *source_filter;
113 IBaseFilter *rend_filter;
114 AM_MEDIA_TYPE *mediatype;
115 } dgraph[2];
Benny Prijono349037b2011-03-17 11:25:19 +0000116
117 pj_timestamp cap_ts;
118 unsigned cap_ts_inc;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000119};
120
121
122/* Prototypes */
123static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
124static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
125static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
126static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
127 unsigned index,
128 pjmedia_vid_dev_info *info);
129static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
130 pjmedia_vid_dev_factory *f,
131 unsigned index,
132 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000133static pj_status_t dshow_factory_create_stream(
134 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000135 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000136 const pjmedia_vid_cb *cb,
137 void *user_data,
138 pjmedia_vid_dev_stream **p_vid_strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000139
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000140static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000141 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000142static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000143 pjmedia_vid_dev_cap cap,
144 void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000145static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000146 pjmedia_vid_dev_cap cap,
147 const void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000148static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
149static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000150 const pjmedia_frame *frame);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000151static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
152static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000153
154/* Operations */
155static pjmedia_vid_dev_factory_op factory_op =
156{
157 &dshow_factory_init,
158 &dshow_factory_destroy,
159 &dshow_factory_get_dev_count,
160 &dshow_factory_get_dev_info,
161 &dshow_factory_default_param,
162 &dshow_factory_create_stream
163};
164
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000165static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000166{
167 &dshow_stream_get_param,
168 &dshow_stream_get_cap,
169 &dshow_stream_set_cap,
170 &dshow_stream_start,
171 NULL,
172 &dshow_stream_put_frame,
173 &dshow_stream_stop,
174 &dshow_stream_destroy
175};
176
177
178/****************************************************************************
179 * Factory operations
180 */
181/*
182 * Init dshow_ video driver.
183 */
184pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf)
185{
186 struct dshow_factory *f;
187 pj_pool_t *pool;
188
189 pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL);
190 f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory);
191 f->pf = pf;
192 f->pool = pool;
193 f->base.op = &factory_op;
194
195 return &f->base;
196}
197
198/* API: init factory */
199static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
200{
201 struct dshow_factory *df = (struct dshow_factory*)f;
202 struct dshow_dev_info *ddi;
203 int dev_count = 0;
204 unsigned c, i;
205 ICreateDevEnum *dev_enum = NULL;
206 IEnumMoniker *enum_cat = NULL;
207 IMoniker *moniker = NULL;
208 HRESULT hr;
209 ULONG fetched;
210
211 df->dev_count = 0;
212
213 CoInitializeEx(NULL, COINIT_MULTITHREADED);
214
215 hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
216 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
217 (void**)&dev_enum);
218 if (FAILED(hr) ||
219 ICreateDevEnum_CreateClassEnumerator(dev_enum,
220 &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)
221 {
222 PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
223 if (dev_enum)
224 ICreateDevEnum_Release(dev_enum);
225 dev_count = 0;
226 } else {
227 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
228 dev_count++;
229 }
230 }
231
232 /* Add renderer device */
233 dev_count += 1;
234 df->dev_info = (struct dshow_dev_info*)
235 pj_pool_calloc(df->pool, dev_count,
236 sizeof(struct dshow_dev_info));
237
238 if (dev_count > 1) {
239 IEnumMoniker_Reset(enum_cat);
240 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
241 IPropertyBag *prop_bag;
242
243 hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
244 (void**)&prop_bag);
245 if (SUCCEEDED(hr)) {
246 VARIANT var_name;
247
248 VariantInit(&var_name);
249 hr = IPropertyBag_Read(prop_bag, L"FriendlyName",
250 &var_name, NULL);
251 if (SUCCEEDED(hr) && var_name.bstrVal) {
252 WCHAR *wszDisplayName = NULL;
253
254 ddi = &df->dev_info[df->dev_count++];
255 pj_bzero(ddi, sizeof(*ddi));
256 pj_unicode_to_ansi(var_name.bstrVal,
257 wcslen(var_name.bstrVal),
258 ddi->info.name,
259 sizeof(ddi->info.name));
260
261 hr = IMoniker_GetDisplayName(moniker, NULL, NULL,
262 &wszDisplayName);
263 if (hr == S_OK && wszDisplayName) {
264 pj_memcpy(ddi->display_name, wszDisplayName,
265 (wcslen(wszDisplayName)+1) * sizeof(WCHAR));
266 CoTaskMemFree(wszDisplayName);
267 }
268
269 strncpy(ddi->info.driver, "dshow",
270 sizeof(ddi->info.driver));
271 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
272 ddi->info.dir = PJMEDIA_DIR_CAPTURE;
273 ddi->info.has_callback = PJ_TRUE;
274
275 /* Set the device capabilities here */
276 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
277 // TODO: Query the default width, height, fps, and
278 // supported formats
279 }
280 VariantClear(&var_name);
281
282 IPropertyBag_Release(prop_bag);
283 }
284 IMoniker_Release(moniker);
285 }
286
287 IEnumMoniker_Release(enum_cat);
288 ICreateDevEnum_Release(dev_enum);
289 }
290
291 ddi = &df->dev_info[df->dev_count++];
292 pj_bzero(ddi, sizeof(*ddi));
Benny Prijono349037b2011-03-17 11:25:19 +0000293 pj_ansi_strncpy(ddi->info.name, "Video Mixing Renderer",
294 sizeof(ddi->info.name));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000295 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Benny Prijono349037b2011-03-17 11:25:19 +0000296 pj_ansi_strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000297 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
298 ddi->info.dir = PJMEDIA_DIR_RENDER;
299 ddi->info.has_callback = PJ_FALSE;
300 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
301
302 for (c = 0; c < df->dev_count; c++) {
303 ddi = &df->dev_info[c];
304 ddi->info.fmt_cnt = sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
305 ddi->info.fmt = (pjmedia_format*)
306 pj_pool_calloc(df->pool, ddi->info.fmt_cnt,
307 sizeof(pjmedia_format));
308
309 for (i = 0; i < ddi->info.fmt_cnt; i++) {
310 pjmedia_format *fmt = &ddi->info.fmt[i];
311
312 if (ddi->info.dir == PJMEDIA_DIR_RENDER && i > 0)
313 break;
314 pjmedia_format_init_video(fmt, dshow_fmts[i].pjmedia_format,
315 DEFAULT_WIDTH, DEFAULT_HEIGHT,
316 DEFAULT_FPS, 1);
317 }
318 }
319
320// TODO:
321// ddi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
322
323 PJ_LOG(4, (THIS_FILE, "DShow initialized, found %d devices:",
324 df->dev_count));
325 for (c = 0; c < df->dev_count; ++c) {
326 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)",
327 c,
328 df->dev_info[c].info.name,
329 df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ?
330 "capture" : "render"));
331 }
332
333 return PJ_SUCCESS;
334}
335
336/* API: destroy factory */
337static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
338{
339 struct dshow_factory *df = (struct dshow_factory*)f;
340 pj_pool_t *pool = df->pool;
341
342 df->pool = NULL;
343 pj_pool_release(pool);
344
345 CoUninitialize();
346
347 return PJ_SUCCESS;
348}
349
350/* API: get number of devices */
351static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
352{
353 struct dshow_factory *df = (struct dshow_factory*)f;
354 return df->dev_count;
355}
356
357/* API: get device info */
358static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
359 unsigned index,
360 pjmedia_vid_dev_info *info)
361{
362 struct dshow_factory *df = (struct dshow_factory*)f;
363
364 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
365
366 pj_memcpy(info, &df->dev_info[index].info, sizeof(*info));
367
368 return PJ_SUCCESS;
369}
370
371/* API: create default device parameter */
372static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
373 pjmedia_vid_dev_factory *f,
374 unsigned index,
375 pjmedia_vid_param *param)
376{
377 struct dshow_factory *df = (struct dshow_factory*)f;
378 struct dshow_dev_info *di = &df->dev_info[index];
379
380 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
381
382 PJ_UNUSED_ARG(pool);
383
384 pj_bzero(param, sizeof(*param));
385 if (di->info.dir & PJMEDIA_DIR_CAPTURE_RENDER) {
386 param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
387 param->cap_id = index;
388 param->rend_id = index;
389 } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
390 param->dir = PJMEDIA_DIR_CAPTURE;
391 param->cap_id = index;
392 param->rend_id = PJMEDIA_VID_INVALID_DEV;
393 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
394 param->dir = PJMEDIA_DIR_RENDER;
395 param->rend_id = index;
396 param->cap_id = PJMEDIA_VID_INVALID_DEV;
397 } else {
398 return PJMEDIA_EVID_INVDEV;
399 }
400
401 /* Set the device capabilities here */
Sauw Ming8e799122010-12-30 16:31:16 +0000402 param->clock_rate = DEFAULT_CLOCK_RATE;
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000403 //param->frame_rate.num = DEFAULT_FPS;
404 //param->frame_rate.denum = 1;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000405 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
406
407 pjmedia_format_copy(&param->fmt, &di->info.fmt[0]);
408
409 return PJ_SUCCESS;
410}
411
412static void input_cb(void *user_data, IMediaSample *pMediaSample)
413{
414 struct dshow_stream *strm = (struct dshow_stream*)user_data;
415 unsigned char *buffer;
Benny Prijono349037b2011-03-17 11:25:19 +0000416 pjmedia_frame frame = {0};
Benny Prijonoc45d9512010-12-10 11:04:30 +0000417
418 if (strm->quit_flag) {
419 strm->cap_thread_exited = PJ_TRUE;
420 return;
421 }
422
423 if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
424 {
425 pj_status_t status;
426
427 status = pj_thread_register("ds_cap", strm->cap_thread_desc,
428 &strm->cap_thread);
429 if (status != PJ_SUCCESS)
430 return;
431 strm->cap_thread_initialized = 1;
432 PJ_LOG(5,(THIS_FILE, "Capture thread started"));
433 }
434
435 IMediaSample_GetPointer(pMediaSample, &buffer);
436
437 frame.type = PJMEDIA_TYPE_VIDEO;
438 IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
439 frame.size = IMediaSample_GetActualDataLength(pMediaSample);
440 frame.bit_info = 0;
Benny Prijono349037b2011-03-17 11:25:19 +0000441 frame.timestamp = strm->cap_ts;
442 strm->cap_ts.u64 += strm->cap_ts_inc;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000443 if (strm->vid_cb.capture_cb)
444 (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
445}
446
447/* API: Put frame from stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000448static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000449 const pjmedia_frame *frame)
450{
451 struct dshow_stream *stream = (struct dshow_stream*)strm;
452 unsigned i;
453
454 if (stream->quit_flag) {
455 stream->rend_thread_exited = PJ_TRUE;
456 return PJ_SUCCESS;
457 }
458
459 for (i = 0; i < 2; i++) {
460 if (stream->dgraph[i].csource_filter) {
461 HRESULT hr = SourceFilter_Deliver(stream->dgraph[i].csource_filter,
Benny Prijono349037b2011-03-17 11:25:19 +0000462 frame->buf, frame->size);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000463
464 if (FAILED(hr)) {
465 return hr;
466 }
467 break;
468 }
469 }
470
471 return PJ_SUCCESS;
472}
473
474static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
475{
476 unsigned i;
477
478 for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
479 if (dshow_fmts[i].pjmedia_format == id)
480 return &dshow_fmts[i];
481 }
482
483 return NULL;
484}
485
486static pj_status_t create_filter_graph(pjmedia_dir dir,
487 unsigned id,
488 struct dshow_factory *df,
489 struct dshow_stream *strm,
490 struct dshow_graph *graph)
491{
492 HRESULT hr;
493 IEnumPins *pEnum;
494 IPin *srcpin = NULL;
495 IPin *sinkpin = NULL;
496 AM_MEDIA_TYPE *mediatype= NULL, mtype;
497 VIDEOINFOHEADER *video_info, *vi = NULL;
498 pjmedia_video_format_detail *vfd;
499 const pjmedia_video_format_info *vfi;
500
501 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
502 strm->param.fmt.id);
503 if (!vfi)
504 return PJMEDIA_EVID_BADFORMAT;
505
506 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC,
507 &IID_IFilterGraph, (LPVOID *)&graph->filter_graph);
508 if (FAILED(hr)) {
509 goto on_error;
510 }
511
512 hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter,
513 (LPVOID *)&graph->media_filter);
514 if (FAILED(hr)) {
515 goto on_error;
516 }
517
518 if (dir == PJMEDIA_DIR_CAPTURE) {
519 IBindCtx *pbc;
520
521 hr = CreateBindCtx(0, &pbc);
522 if (SUCCEEDED (hr)) {
523 IMoniker *moniker;
524 DWORD pchEaten;
525
526 hr = MkParseDisplayName(pbc,
527 df->dev_info[id].display_name,
528 &pchEaten, &moniker);
529 if (SUCCEEDED(hr)) {
530 hr = IMoniker_BindToObject(moniker, pbc, NULL,
531 &IID_IBaseFilter,
532 (LPVOID *)&graph->source_filter);
533 IMoniker_Release(moniker);
534 }
535 IBindCtx_Release(pbc);
536 }
537 if (FAILED(hr)) {
538 goto on_error;
539 }
540 } else {
541 graph->source_filter = SourceFilter_Create(&graph->csource_filter);
542 }
543
544 hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter,
545 L"capture");
546 if (FAILED(hr)) {
547 goto on_error;
548 }
549
550 if (dir == PJMEDIA_DIR_CAPTURE) {
551 graph->rend_filter = NullRenderer_Create(input_cb, strm);
552 } else {
553 hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL,
554 CLSCTX_INPROC, &IID_IBaseFilter,
555 (LPVOID *)&graph->rend_filter);
556 if (FAILED (hr)) {
557 goto on_error;
558 }
559 }
560
561 IBaseFilter_EnumPins(graph->rend_filter, &pEnum);
562 if (SUCCEEDED(hr)) {
563 // Loop through all the pins
564 IPin *pPin = NULL;
565
566 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
567 PIN_DIRECTION pindirtmp;
568
569 hr = IPin_QueryDirection(pPin, &pindirtmp);
570 if (hr == S_OK && pindirtmp == PINDIR_INPUT) {
571 sinkpin = pPin;
572 break;
573 }
574 IPin_Release(pPin);
575 }
576 IEnumPins_Release(pEnum);
577 }
578
579 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
580
581 IBaseFilter_EnumPins(graph->source_filter, &pEnum);
582 if (SUCCEEDED(hr)) {
583 // Loop through all the pins
584 IPin *pPin = NULL;
585
586 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
587 PIN_DIRECTION pindirtmp;
588 const GUID *dshow_format;
589
590 dshow_format = get_dshow_format_info(strm->param.fmt.id)->
591 dshow_format;
592
593 hr = IPin_QueryDirection(pPin, &pindirtmp);
594 if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) {
595 if (SUCCEEDED(hr))
596 IPin_Release(pPin);
597 continue;
598 }
599
600 if (dir == PJMEDIA_DIR_CAPTURE) {
601 IAMStreamConfig *streamcaps;
602
603 hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig,
604 (LPVOID *)&streamcaps);
605 if (SUCCEEDED(hr)) {
606 VIDEO_STREAM_CONFIG_CAPS vscc;
607 int i, isize, icount;
608
609 IAMStreamConfig_GetNumberOfCapabilities(streamcaps,
610 &icount, &isize);
611
612 for (i = 0; i < icount; i++) {
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000613 RPC_STATUS rpcstatus, rpcstatus2;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000614
615 hr = IAMStreamConfig_GetStreamCaps(streamcaps, i,
616 &mediatype,
617 (BYTE *)&vscc);
618 if (FAILED (hr))
619 continue;
620
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000621 if (UuidCompare(&mediatype->subtype,
622 (UUID*)dshow_format,
623 &rpcstatus) == 0 &&
624 rpcstatus == RPC_S_OK &&
625 UuidCompare(&mediatype->formattype,
626 (UUID*)&FORMAT_VideoInfo,
627 &rpcstatus2) == 0 &&
628 rpcstatus2 == RPC_S_OK)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000629 {
630 srcpin = pPin;
631 graph->mediatype = mediatype;
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000632 break;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000633 }
634 }
635 IAMStreamConfig_Release(streamcaps);
636 }
637 } else {
638 srcpin = pPin;
639 mediatype = graph->mediatype = &mtype;
640
641 memset (mediatype, 0, sizeof(AM_MEDIA_TYPE));
642 mediatype->majortype = MEDIATYPE_Video;
643 mediatype->subtype = *dshow_format;
644 mediatype->bFixedSizeSamples = TRUE;
645 mediatype->bTemporalCompression = FALSE;
646
647 vi = (VIDEOINFOHEADER *)
648 CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
649 memset (vi, 0, sizeof(VIDEOINFOHEADER));
650 mediatype->formattype = FORMAT_VideoInfo;
651 mediatype->cbFormat = sizeof(VIDEOINFOHEADER);
652 mediatype->pbFormat = (BYTE *)vi;
653
654 vi->rcSource.bottom = vfd->size.h;
655 vi->rcSource.right = vfd->size.w;
656 vi->rcTarget.bottom = vfd->size.h;
657 vi->rcTarget.right = vfd->size.w;
658
659 vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
660 vi->bmiHeader.biPlanes = 1;
661 vi->bmiHeader.biBitCount = vfi->bpp;
662 vi->bmiHeader.biCompression = strm->param.fmt.id;
663 }
664 if (srcpin)
665 break;
666 IPin_Release(pPin);
667 }
668 IEnumPins_Release(pEnum);
669 }
670
671 if (!srcpin || !sinkpin || !mediatype) {
672 hr = S_FALSE;
673 goto on_error;
674 }
675 video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
676 video_info->bmiHeader.biWidth = vfd->size.w;
677 video_info->bmiHeader.biHeight = vfd->size.h;
678 if (vfd->fps.num != 0)
679 video_info->AvgTimePerFrame = (LONGLONG) (10000000 *
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000680 (double)vfd->fps.denum /
681 vfd->fps.num);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000682 video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
683 mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
684 if (graph->csource_filter)
685 SourceFilter_SetBufferSize(graph->csource_filter,
686 mediatype->lSampleSize);
687
688 hr = IFilterGraph_AddFilter(graph->filter_graph,
689 (IBaseFilter *)graph->rend_filter,
690 L"renderer");
691 if (FAILED(hr))
692 goto on_error;
693
694 hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin,
695 mediatype);
696
697on_error:
698 if (srcpin)
699 IPin_Release(srcpin);
700 if (sinkpin)
701 IPin_Release(sinkpin);
702 if (vi)
703 CoTaskMemFree(vi);
704 if (FAILED(hr))
705 return hr;
706
707 return PJ_SUCCESS;
708}
709
710/* API: create stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000711static pj_status_t dshow_factory_create_stream(
712 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000713 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000714 const pjmedia_vid_cb *cb,
715 void *user_data,
716 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000717{
718 struct dshow_factory *df = (struct dshow_factory*)f;
719 pj_pool_t *pool;
720 struct dshow_stream *strm;
721 unsigned ngraph = 0;
722 pj_status_t status;
723
724 if (!get_dshow_format_info(param->fmt.id))
725 return PJMEDIA_EVID_BADFORMAT;
726
727 /* Create and Initialize stream descriptor */
728 pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL);
729 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
730
731 strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream);
732 pj_memcpy(&strm->param, param, sizeof(*param));
733 strm->pool = pool;
734 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
735 strm->user_data = user_data;
736
737 /* Create capture stream here */
738 if (param->dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijono349037b2011-03-17 11:25:19 +0000739 const pjmedia_video_format_detail *vfd;
740
Benny Prijonoc45d9512010-12-10 11:04:30 +0000741 status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
742 df, strm, &strm->dgraph[ngraph++]);
743 if (status != PJ_SUCCESS)
744 goto on_error;
Benny Prijono349037b2011-03-17 11:25:19 +0000745
746 vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
747 strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000748 }
749
750 /* Create render stream here */
751 if (param->dir & PJMEDIA_DIR_RENDER) {
752 status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id,
753 df, strm, &strm->dgraph[ngraph++]);
754 if (status != PJ_SUCCESS)
755 goto on_error;
756 }
757
758 /* Apply the remaining settings */
759 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
760 dshow_stream_set_cap(&strm->base,
761 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
762 &param->window);
763 }
764
765 /* Done */
766 strm->base.op = &stream_op;
767 *p_vid_strm = &strm->base;
768
769 return PJ_SUCCESS;
770
771on_error:
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000772 dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000773 return PJ_EUNKNOWN;
774}
775
776/* API: Get stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000777static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000778 pjmedia_vid_param *pi)
779{
780 struct dshow_stream *strm = (struct dshow_stream*)s;
781
782 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
783
784 pj_memcpy(pi, &strm->param, sizeof(*pi));
785
786 if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
787 &pi->window) == PJ_SUCCESS)
788 {
789 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
790 }
791
792 return PJ_SUCCESS;
793}
794
795/* API: get capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000796static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000797 pjmedia_vid_dev_cap cap,
798 void *pval)
799{
800 struct dshow_stream *strm = (struct dshow_stream*)s;
801
802 PJ_UNUSED_ARG(strm);
803
804 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
805
806 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
807 {
808 *(unsigned*)pval = 0;
809 return PJ_SUCCESS;
810 } else {
811 return PJMEDIA_EVID_INVCAP;
812 }
813}
814
815/* API: set capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000816static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000817 pjmedia_vid_dev_cap cap,
818 const void *pval)
819{
820 struct dshow_stream *strm = (struct dshow_stream*)s;
821
822 PJ_UNUSED_ARG(strm);
823
824 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
825
826 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
827 {
828 // set renderer's window here
829 return PJ_SUCCESS;
830 }
831
832 return PJMEDIA_EVID_INVCAP;
833}
834
835/* API: Start stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000836static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000837{
838 struct dshow_stream *stream = (struct dshow_stream*)strm;
839 unsigned i;
840
841 stream->quit_flag = PJ_FALSE;
842 stream->cap_thread_exited = PJ_FALSE;
843 stream->rend_thread_exited = PJ_FALSE;
844
845 for (i = 0; i < 2; i++) {
846 HRESULT hr;
847
848 if (!stream->dgraph[i].media_filter)
849 continue;
850 hr = IMediaFilter_Run(stream->dgraph[i].media_filter, 0);
851 if (FAILED(hr)) {
852 return hr;
853 }
854 }
855
856 PJ_LOG(4, (THIS_FILE, "Starting dshow video stream"));
857
858 return PJ_SUCCESS;
859}
860
861/* API: Stop stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000862static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000863{
864 struct dshow_stream *stream = (struct dshow_stream*)strm;
865 unsigned i;
866
867 stream->quit_flag = PJ_TRUE;
868 if (stream->cap_thread) {
869 for (i=0; !stream->cap_thread_exited && i<100; ++i)
870 pj_thread_sleep(10);
871 }
872 for (i=0; !stream->rend_thread_exited && i<100; ++i)
873 pj_thread_sleep(10);
874
875 for (i = 0; i < 2; i++) {
876 if (!stream->dgraph[i].media_filter)
877 continue;
878 IMediaFilter_Stop(stream->dgraph[i].media_filter);
879 }
880
881 PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream"));
882
883 return PJ_SUCCESS;
884}
885
886
887/* API: Destroy stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000888static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000889{
890 struct dshow_stream *stream = (struct dshow_stream*)strm;
891 unsigned i;
892
893 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
894
895 dshow_stream_stop(strm);
896
897 for (i = 0; i < 2; i++) {
898 if (stream->dgraph[i].source_filter) {
899 IBaseFilter_Release(stream->dgraph[i].source_filter);
900 stream->dgraph[i].source_filter = NULL;
901 }
902 if (stream->dgraph[i].rend_filter) {
903 IBaseFilter_Release(stream->dgraph[i].rend_filter);
904 stream->dgraph[i].rend_filter = NULL;
905 }
906 if (stream->dgraph[i].media_filter) {
907 IMediaFilter_Release(stream->dgraph[i].media_filter);
908 stream->dgraph[i].media_filter = NULL;
909 }
910 if (stream->dgraph[i].filter_graph) {
911 IFilterGraph_Release(stream->dgraph[i].filter_graph);
912 stream->dgraph[i].filter_graph = NULL;
913 }
914 }
915
916 pj_pool_release(stream->pool);
917
918 return PJ_SUCCESS;
919}
920
921#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */