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