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