blob: 30fd0ccbb2b43a1e996745ab48f41427d84281e2 [file] [log] [blame]
Benny Prijonoc45d9512010-12-10 11:04:30 +00001/* $Id$ */
2/*
Nanang Izzuddinfad6f692011-08-19 09:35:25 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijonoc45d9512010-12-10 11:04:30 +00004 *
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
Nanang Izzuddin63b3c132011-07-19 11:11:07 +000025
26#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
27
Benny Prijonoc45d9512010-12-10 11:04:30 +000028
29#ifdef _MSC_VER
30# pragma warning(push, 3)
31#endif
32
33#include <windows.h>
34#define COBJMACROS
35#include <DShow.h>
36
37#ifdef _MSC_VER
38# pragma warning(pop)
39#endif
40
41#pragma comment(lib, "Strmiids.lib")
42#pragma comment(lib, "Rpcrt4.lib")
Benny Prijono63a894d2011-04-07 07:22:35 +000043#pragma comment(lib, "Quartz.lib")
Benny Prijonoc45d9512010-12-10 11:04:30 +000044
45#define THIS_FILE "dshow_dev.c"
Sauw Ming8e799122010-12-30 16:31:16 +000046#define DEFAULT_CLOCK_RATE 90000
Benny Prijonoc45d9512010-12-10 11:04:30 +000047#define DEFAULT_WIDTH 640
48#define DEFAULT_HEIGHT 480
49#define DEFAULT_FPS 25
50
Nanang Izzuddin030aa2b2011-07-15 04:36:06 +000051/* Temporarily disable DirectShow renderer (VMR) */
52#define HAS_VMR 0
53
Benny Prijonoc45d9512010-12-10 11:04:30 +000054typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
55typedef struct NullRenderer NullRenderer;
56IBaseFilter* NullRenderer_Create(input_callback input_cb,
57 void *user_data);
58typedef struct SourceFilter SourceFilter;
59IBaseFilter* SourceFilter_Create(SourceFilter **pSrc);
60HRESULT SourceFilter_Deliver(SourceFilter *src, void *buf, long size);
Sauw Minga8e08622011-06-13 11:48:37 +000061void SourceFilter_SetMediaType(SourceFilter *src, AM_MEDIA_TYPE *pmt);
Benny Prijonoc45d9512010-12-10 11:04:30 +000062
63typedef struct dshow_fmt_info
64{
65 pjmedia_format_id pjmedia_format;
66 const GUID *dshow_format;
67} dshow_fmt_info;
68
69static dshow_fmt_info dshow_fmts[] =
70{
71 {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2} ,
72 {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24} ,
Sauw Minga8e08622011-06-13 11:48:37 +000073 {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32} ,
74 {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000075};
76
77/* dshow_ device info */
78struct dshow_dev_info
79{
80 pjmedia_vid_dev_info info;
81 unsigned dev_id;
82 WCHAR display_name[192];
83};
84
85/* dshow_ factory */
86struct dshow_factory
87{
88 pjmedia_vid_dev_factory base;
89 pj_pool_t *pool;
Sauw Ming7f7c5bd2011-06-21 09:33:01 +000090 pj_pool_t *dev_pool;
Benny Prijonoc45d9512010-12-10 11:04:30 +000091 pj_pool_factory *pf;
92
93 unsigned dev_count;
94 struct dshow_dev_info *dev_info;
95};
96
97/* Video stream. */
98struct dshow_stream
99{
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000100 pjmedia_vid_dev_stream base; /**< Base stream */
Sauw Ming5291a2d2011-07-15 07:52:44 +0000101 pjmedia_vid_dev_param param; /**< Settings */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000102 pj_pool_t *pool; /**< Memory pool. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000103
Sauw Ming5291a2d2011-07-15 07:52:44 +0000104 pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000105 void *user_data; /**< Application data. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000106
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000107 pj_bool_t quit_flag;
108 pj_bool_t rend_thread_exited;
109 pj_bool_t cap_thread_exited;
110 pj_bool_t cap_thread_initialized;
111 pj_thread_desc cap_thread_desc;
112 pj_thread_t *cap_thread;
Sauw Mingcec804e2011-10-07 05:41:37 +0000113 void *frm_buf;
114 unsigned frm_buf_size;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000115
116 struct dshow_graph
117 {
118 IFilterGraph *filter_graph;
119 IMediaFilter *media_filter;
120 SourceFilter *csource_filter;
121 IBaseFilter *source_filter;
122 IBaseFilter *rend_filter;
123 AM_MEDIA_TYPE *mediatype;
Sauw Ming83db7d62011-06-09 04:08:47 +0000124 } dgraph;
Benny Prijono349037b2011-03-17 11:25:19 +0000125
126 pj_timestamp cap_ts;
127 unsigned cap_ts_inc;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000128};
129
130
131/* Prototypes */
132static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
133static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000134static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000135static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
136static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
137 unsigned index,
138 pjmedia_vid_dev_info *info);
139static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
140 pjmedia_vid_dev_factory *f,
141 unsigned index,
Sauw Ming5291a2d2011-07-15 07:52:44 +0000142 pjmedia_vid_dev_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000143static pj_status_t dshow_factory_create_stream(
144 pjmedia_vid_dev_factory *f,
Sauw Ming5291a2d2011-07-15 07:52:44 +0000145 pjmedia_vid_dev_param *param,
146 const pjmedia_vid_dev_cb *cb,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000147 void *user_data,
148 pjmedia_vid_dev_stream **p_vid_strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000149
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000150static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
Sauw Ming5291a2d2011-07-15 07:52:44 +0000151 pjmedia_vid_dev_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000152static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000153 pjmedia_vid_dev_cap cap,
154 void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000155static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000156 pjmedia_vid_dev_cap cap,
157 const void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000158static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
159static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000160 const pjmedia_frame *frame);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000161static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
162static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000163
164/* Operations */
165static pjmedia_vid_dev_factory_op factory_op =
166{
167 &dshow_factory_init,
168 &dshow_factory_destroy,
169 &dshow_factory_get_dev_count,
170 &dshow_factory_get_dev_info,
171 &dshow_factory_default_param,
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000172 &dshow_factory_create_stream,
173 &dshow_factory_refresh
Benny Prijonoc45d9512010-12-10 11:04:30 +0000174};
175
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000176static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000177{
178 &dshow_stream_get_param,
179 &dshow_stream_get_cap,
180 &dshow_stream_set_cap,
181 &dshow_stream_start,
182 NULL,
183 &dshow_stream_put_frame,
184 &dshow_stream_stop,
185 &dshow_stream_destroy
186};
187
188
189/****************************************************************************
190 * Factory operations
191 */
192/*
193 * Init dshow_ video driver.
194 */
195pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf)
196{
197 struct dshow_factory *f;
198 pj_pool_t *pool;
199
200 pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL);
201 f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory);
202 f->pf = pf;
203 f->pool = pool;
204 f->base.op = &factory_op;
205
206 return &f->base;
207}
208
209/* API: init factory */
210static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
211{
Sauw Ming6f888b92012-01-04 02:39:13 +0000212 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
213 if (hr == RPC_E_CHANGED_MODE) {
214 PJ_LOG(4,(THIS_FILE, "Failed initializing DShow: "
215 "COM library already initialized with "
216 "incompatible concurrency model"));
217 return PJMEDIA_EVID_INIT;
218 }
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000219
220 return dshow_factory_refresh(f);
221}
222
223/* API: destroy factory */
224static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
225{
226 struct dshow_factory *df = (struct dshow_factory*)f;
227 pj_pool_t *pool = df->pool;
228
229 df->pool = NULL;
230 if (df->dev_pool)
231 pj_pool_release(df->dev_pool);
232 if (pool)
233 pj_pool_release(pool);
234
235 CoUninitialize();
236
237 return PJ_SUCCESS;
238}
239
Sauw Mingaa08ef62011-07-15 06:42:11 +0000240static HRESULT get_cap_device(struct dshow_factory *df,
241 unsigned id,
242 IBaseFilter **filter)
243{
244 IBindCtx *pbc;
245 HRESULT hr;
246
247 hr = CreateBindCtx(0, &pbc);
248 if (SUCCEEDED (hr)) {
249 IMoniker *moniker;
250 DWORD pchEaten;
251
252 hr = MkParseDisplayName(pbc, df->dev_info[id].display_name,
253 &pchEaten, &moniker);
254 if (SUCCEEDED(hr)) {
255 hr = IMoniker_BindToObject(moniker, pbc, NULL,
256 &IID_IBaseFilter,
257 (LPVOID *)filter);
258 IMoniker_Release(moniker);
259 }
260 IBindCtx_Release(pbc);
261 }
262
263 return hr;
264}
265
266static void enum_dev_cap(IBaseFilter *filter,
267 pjmedia_dir dir,
Sauw Ming8350e7e2011-10-06 07:11:15 +0000268 const GUID *dshow_fmt,
Sauw Mingaa08ef62011-07-15 06:42:11 +0000269 AM_MEDIA_TYPE **pMediatype,
270 IPin **pSrcpin,
271 pj_bool_t *sup_fmt)
272{
273 IEnumPins *pEnum;
274 AM_MEDIA_TYPE *mediatype = NULL;
275 HRESULT hr;
276
277 if (pSrcpin)
278 *pSrcpin = NULL;
279 hr = IBaseFilter_EnumPins(filter, &pEnum);
280 if (SUCCEEDED(hr)) {
281 /* Loop through all the pins. */
282 IPin *pPin = NULL;
283
284 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
285 PIN_DIRECTION pindirtmp;
286
287 hr = IPin_QueryDirection(pPin, &pindirtmp);
288 if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) {
289 if (SUCCEEDED(hr))
290 IPin_Release(pPin);
291 continue;
292 }
293
294 if (dir == PJMEDIA_DIR_CAPTURE) {
295 IAMStreamConfig *streamcaps;
296
297 hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig,
298 (LPVOID *)&streamcaps);
299 if (SUCCEEDED(hr)) {
300 VIDEO_STREAM_CONFIG_CAPS vscc;
301 int i, isize, icount;
302
303 IAMStreamConfig_GetNumberOfCapabilities(streamcaps,
304 &icount, &isize);
305
306 for (i = 0; i < icount; i++) {
307 unsigned j, nformat;
308 RPC_STATUS rpcstatus, rpcstatus2;
309
310 hr = IAMStreamConfig_GetStreamCaps(streamcaps, i,
311 &mediatype,
312 (BYTE *)&vscc);
313 if (FAILED (hr))
314 continue;
315
Sauw Ming8350e7e2011-10-06 07:11:15 +0000316 nformat = (dshow_fmt? 1:
Sauw Mingaa08ef62011-07-15 06:42:11 +0000317 sizeof(dshow_fmts)/sizeof(dshow_fmts[0]));
318 for (j = 0; j < nformat; j++) {
Sauw Ming8350e7e2011-10-06 07:11:15 +0000319 const GUID *dshow_format = dshow_fmt;
320
321 if (!dshow_format)
Sauw Mingaa08ef62011-07-15 06:42:11 +0000322 dshow_format = dshow_fmts[j].dshow_format;
323 if (UuidCompare(&mediatype->subtype,
324 (UUID*)dshow_format,
325 &rpcstatus) == 0 &&
326 rpcstatus == RPC_S_OK &&
327 UuidCompare(&mediatype->formattype,
328 (UUID*)&FORMAT_VideoInfo,
329 &rpcstatus2) == 0 &&
330 rpcstatus2 == RPC_S_OK)
331 {
332 if (sup_fmt)
333 sup_fmt[j] = PJ_TRUE;
334 if (pSrcpin) {
335 *pSrcpin = pPin;
336 *pMediatype = mediatype;
337 }
338 }
339 }
340 if (pSrcpin && *pSrcpin)
341 break;
342 }
343 IAMStreamConfig_Release(streamcaps);
344 }
345 } else {
346 *pSrcpin = pPin;
347 }
348 if (pSrcpin && *pSrcpin)
349 break;
350 IPin_Release(pPin);
351 }
352 IEnumPins_Release(pEnum);
353 }
354}
355
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000356/* API: refresh the list of devices */
357static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f)
358{
Benny Prijonoc45d9512010-12-10 11:04:30 +0000359 struct dshow_factory *df = (struct dshow_factory*)f;
360 struct dshow_dev_info *ddi;
361 int dev_count = 0;
Nanang Izzuddin030aa2b2011-07-15 04:36:06 +0000362 unsigned c;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000363 ICreateDevEnum *dev_enum = NULL;
364 IEnumMoniker *enum_cat = NULL;
365 IMoniker *moniker = NULL;
366 HRESULT hr;
367 ULONG fetched;
368
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000369 if (df->dev_pool) {
370 pj_pool_release(df->dev_pool);
371 df->dev_pool = NULL;
372 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000373
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000374 df->dev_count = 0;
375 df->dev_pool = pj_pool_create(df->pf, "dshow video", 500, 500, NULL);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000376
377 hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
378 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
379 (void**)&dev_enum);
380 if (FAILED(hr) ||
381 ICreateDevEnum_CreateClassEnumerator(dev_enum,
382 &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)
383 {
384 PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
385 if (dev_enum)
386 ICreateDevEnum_Release(dev_enum);
387 dev_count = 0;
388 } else {
389 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
390 dev_count++;
391 }
392 }
393
394 /* Add renderer device */
395 dev_count += 1;
396 df->dev_info = (struct dshow_dev_info*)
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000397 pj_pool_calloc(df->dev_pool, dev_count,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000398 sizeof(struct dshow_dev_info));
399
400 if (dev_count > 1) {
401 IEnumMoniker_Reset(enum_cat);
402 while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
403 IPropertyBag *prop_bag;
404
405 hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
406 (void**)&prop_bag);
407 if (SUCCEEDED(hr)) {
408 VARIANT var_name;
409
410 VariantInit(&var_name);
411 hr = IPropertyBag_Read(prop_bag, L"FriendlyName",
412 &var_name, NULL);
413 if (SUCCEEDED(hr) && var_name.bstrVal) {
414 WCHAR *wszDisplayName = NULL;
Sauw Mingaa08ef62011-07-15 06:42:11 +0000415 IBaseFilter *filter;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000416
417 ddi = &df->dev_info[df->dev_count++];
418 pj_bzero(ddi, sizeof(*ddi));
419 pj_unicode_to_ansi(var_name.bstrVal,
420 wcslen(var_name.bstrVal),
421 ddi->info.name,
422 sizeof(ddi->info.name));
423
424 hr = IMoniker_GetDisplayName(moniker, NULL, NULL,
425 &wszDisplayName);
426 if (hr == S_OK && wszDisplayName) {
427 pj_memcpy(ddi->display_name, wszDisplayName,
428 (wcslen(wszDisplayName)+1) * sizeof(WCHAR));
429 CoTaskMemFree(wszDisplayName);
430 }
431
432 strncpy(ddi->info.driver, "dshow",
433 sizeof(ddi->info.driver));
434 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
435 ddi->info.dir = PJMEDIA_DIR_CAPTURE;
436 ddi->info.has_callback = PJ_TRUE;
437
438 /* Set the device capabilities here */
439 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
Sauw Mingaa08ef62011-07-15 06:42:11 +0000440
441 hr = get_cap_device(df, df->dev_count-1, &filter);
442 if (SUCCEEDED(hr)) {
443 unsigned j;
444 pj_bool_t sup_fmt[sizeof(dshow_fmts)/sizeof(dshow_fmts[0])];
445
446 pj_bzero(sup_fmt, sizeof(sup_fmt));
447 enum_dev_cap(filter, ddi->info.dir, NULL, NULL, NULL, sup_fmt);
448
449 ddi->info.fmt_cnt = 0;
Sauw Mingaa08ef62011-07-15 06:42:11 +0000450 for (j = 0;
451 j < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
452 j++)
453 {
454 if (!sup_fmt[j])
455 continue;
456 pjmedia_format_init_video(
457 &ddi->info.fmt[ddi->info.fmt_cnt++],
458 dshow_fmts[j].pjmedia_format,
459 DEFAULT_WIDTH, DEFAULT_HEIGHT,
460 DEFAULT_FPS, 1);
461 }
462 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000463 }
464 VariantClear(&var_name);
465
466 IPropertyBag_Release(prop_bag);
467 }
468 IMoniker_Release(moniker);
469 }
470
471 IEnumMoniker_Release(enum_cat);
472 ICreateDevEnum_Release(dev_enum);
473 }
474
Nanang Izzuddin030aa2b2011-07-15 04:36:06 +0000475#if HAS_VMR
Benny Prijonoc45d9512010-12-10 11:04:30 +0000476 ddi = &df->dev_info[df->dev_count++];
477 pj_bzero(ddi, sizeof(*ddi));
Benny Prijono349037b2011-03-17 11:25:19 +0000478 pj_ansi_strncpy(ddi->info.name, "Video Mixing Renderer",
479 sizeof(ddi->info.name));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000480 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Benny Prijono349037b2011-03-17 11:25:19 +0000481 pj_ansi_strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000482 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
483 ddi->info.dir = PJMEDIA_DIR_RENDER;
484 ddi->info.has_callback = PJ_FALSE;
485 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000486// TODO:
Sauw Mingaa08ef62011-07-15 06:42:11 +0000487// ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
488
489 ddi->info.fmt_cnt = 1;
Sauw Mingaa08ef62011-07-15 06:42:11 +0000490 pjmedia_format_init_video(&ddi->info.fmt[0], dshow_fmts[0].pjmedia_format,
491 DEFAULT_WIDTH, DEFAULT_HEIGHT,
492 DEFAULT_FPS, 1);
493#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000494
Sauw Ming7f7c5bd2011-06-21 09:33:01 +0000495 PJ_LOG(4, (THIS_FILE, "DShow has %d devices:",
Benny Prijonoc45d9512010-12-10 11:04:30 +0000496 df->dev_count));
497 for (c = 0; c < df->dev_count; ++c) {
498 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)",
499 c,
500 df->dev_info[c].info.name,
501 df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ?
502 "capture" : "render"));
503 }
504
505 return PJ_SUCCESS;
506}
507
Benny Prijonoc45d9512010-12-10 11:04:30 +0000508/* API: get number of devices */
509static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
510{
511 struct dshow_factory *df = (struct dshow_factory*)f;
512 return df->dev_count;
513}
514
515/* API: get device info */
516static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
517 unsigned index,
518 pjmedia_vid_dev_info *info)
519{
520 struct dshow_factory *df = (struct dshow_factory*)f;
521
522 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
523
524 pj_memcpy(info, &df->dev_info[index].info, sizeof(*info));
525
526 return PJ_SUCCESS;
527}
528
529/* API: create default device parameter */
530static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
531 pjmedia_vid_dev_factory *f,
532 unsigned index,
Sauw Ming5291a2d2011-07-15 07:52:44 +0000533 pjmedia_vid_dev_param *param)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000534{
535 struct dshow_factory *df = (struct dshow_factory*)f;
536 struct dshow_dev_info *di = &df->dev_info[index];
537
538 PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
539
540 PJ_UNUSED_ARG(pool);
541
542 pj_bzero(param, sizeof(*param));
Sauw Ming83db7d62011-06-09 04:08:47 +0000543 if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000544 param->dir = PJMEDIA_DIR_CAPTURE;
545 param->cap_id = index;
546 param->rend_id = PJMEDIA_VID_INVALID_DEV;
547 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
548 param->dir = PJMEDIA_DIR_RENDER;
549 param->rend_id = index;
550 param->cap_id = PJMEDIA_VID_INVALID_DEV;
551 } else {
552 return PJMEDIA_EVID_INVDEV;
553 }
554
555 /* Set the device capabilities here */
Sauw Ming8e799122010-12-30 16:31:16 +0000556 param->clock_rate = DEFAULT_CLOCK_RATE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000557 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
558
559 pjmedia_format_copy(&param->fmt, &di->info.fmt[0]);
560
561 return PJ_SUCCESS;
562}
563
564static void input_cb(void *user_data, IMediaSample *pMediaSample)
565{
566 struct dshow_stream *strm = (struct dshow_stream*)user_data;
Benny Prijono349037b2011-03-17 11:25:19 +0000567 pjmedia_frame frame = {0};
Benny Prijonoc45d9512010-12-10 11:04:30 +0000568
569 if (strm->quit_flag) {
570 strm->cap_thread_exited = PJ_TRUE;
571 return;
572 }
573
574 if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
575 {
576 pj_status_t status;
577
578 status = pj_thread_register("ds_cap", strm->cap_thread_desc,
579 &strm->cap_thread);
580 if (status != PJ_SUCCESS)
581 return;
582 strm->cap_thread_initialized = 1;
583 PJ_LOG(5,(THIS_FILE, "Capture thread started"));
584 }
585
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +0000586 frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000587 IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
588 frame.size = IMediaSample_GetActualDataLength(pMediaSample);
589 frame.bit_info = 0;
Benny Prijono349037b2011-03-17 11:25:19 +0000590 frame.timestamp = strm->cap_ts;
591 strm->cap_ts.u64 += strm->cap_ts_inc;
Sauw Mingcec804e2011-10-07 05:41:37 +0000592
593 if (strm->frm_buf_size) {
594 unsigned i, stride;
595 BYTE *src_buf, *dst_buf;
596 pjmedia_video_format_detail *vfd;
597
598 /* Image is bottom-up, convert it to top-down. */
599 src_buf = dst_buf = (BYTE *)frame.buf;
600 stride = strm->frm_buf_size;
601 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt,
602 PJ_TRUE);
603 src_buf += (vfd->size.h - 1) * stride;
604
605 for (i = vfd->size.h / 2; i > 0; i--) {
606 memcpy(strm->frm_buf, dst_buf, stride);
607 memcpy(dst_buf, src_buf, stride);
608 memcpy(src_buf, strm->frm_buf, stride);
609 dst_buf += stride;
610 src_buf -= stride;
611 }
612 }
613
Benny Prijonoc45d9512010-12-10 11:04:30 +0000614 if (strm->vid_cb.capture_cb)
615 (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
616}
617
618/* API: Put frame from stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000619static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000620 const pjmedia_frame *frame)
621{
622 struct dshow_stream *stream = (struct dshow_stream*)strm;
Sauw Ming83db7d62011-06-09 04:08:47 +0000623 HRESULT hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000624
625 if (stream->quit_flag) {
626 stream->rend_thread_exited = PJ_TRUE;
627 return PJ_SUCCESS;
628 }
629
Sauw Ming83db7d62011-06-09 04:08:47 +0000630 hr = SourceFilter_Deliver(stream->dgraph.csource_filter,
631 frame->buf, frame->size);
632 if (FAILED(hr))
633 return hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000634
635 return PJ_SUCCESS;
636}
637
638static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
639{
640 unsigned i;
641
642 for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
643 if (dshow_fmts[i].pjmedia_format == id)
644 return &dshow_fmts[i];
645 }
646
647 return NULL;
648}
649
650static pj_status_t create_filter_graph(pjmedia_dir dir,
651 unsigned id,
Sauw Ming0531a722011-04-07 08:27:27 +0000652 pj_bool_t use_def_size,
653 pj_bool_t use_def_fps,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000654 struct dshow_factory *df,
655 struct dshow_stream *strm,
656 struct dshow_graph *graph)
657{
658 HRESULT hr;
659 IEnumPins *pEnum;
660 IPin *srcpin = NULL;
661 IPin *sinkpin = NULL;
662 AM_MEDIA_TYPE *mediatype= NULL, mtype;
663 VIDEOINFOHEADER *video_info, *vi = NULL;
664 pjmedia_video_format_detail *vfd;
665 const pjmedia_video_format_info *vfi;
666
667 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
668 strm->param.fmt.id);
669 if (!vfi)
670 return PJMEDIA_EVID_BADFORMAT;
671
672 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC,
673 &IID_IFilterGraph, (LPVOID *)&graph->filter_graph);
674 if (FAILED(hr)) {
675 goto on_error;
676 }
677
678 hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter,
679 (LPVOID *)&graph->media_filter);
680 if (FAILED(hr)) {
681 goto on_error;
682 }
683
684 if (dir == PJMEDIA_DIR_CAPTURE) {
Sauw Mingaa08ef62011-07-15 06:42:11 +0000685 hr = get_cap_device(df, id, &graph->source_filter);
686 if (FAILED(hr)) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000687 goto on_error;
688 }
689 } else {
690 graph->source_filter = SourceFilter_Create(&graph->csource_filter);
691 }
692
693 hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter,
694 L"capture");
695 if (FAILED(hr)) {
696 goto on_error;
697 }
698
699 if (dir == PJMEDIA_DIR_CAPTURE) {
700 graph->rend_filter = NullRenderer_Create(input_cb, strm);
701 } else {
702 hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL,
703 CLSCTX_INPROC, &IID_IBaseFilter,
704 (LPVOID *)&graph->rend_filter);
705 if (FAILED (hr)) {
706 goto on_error;
707 }
708 }
709
710 IBaseFilter_EnumPins(graph->rend_filter, &pEnum);
711 if (SUCCEEDED(hr)) {
712 // Loop through all the pins
713 IPin *pPin = NULL;
714
715 while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
716 PIN_DIRECTION pindirtmp;
717
718 hr = IPin_QueryDirection(pPin, &pindirtmp);
719 if (hr == S_OK && pindirtmp == PINDIR_INPUT) {
720 sinkpin = pPin;
721 break;
722 }
723 IPin_Release(pPin);
724 }
725 IEnumPins_Release(pEnum);
726 }
727
728 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
729
Sauw Mingaa08ef62011-07-15 06:42:11 +0000730 enum_dev_cap(graph->source_filter, dir,
731 get_dshow_format_info(strm->param.fmt.id)->dshow_format,
732 &mediatype, &srcpin, NULL);
733 graph->mediatype = mediatype;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000734
Sauw Mingaa08ef62011-07-15 06:42:11 +0000735 if (srcpin && dir == PJMEDIA_DIR_RENDER) {
736 mediatype = graph->mediatype = &mtype;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000737
Sauw Mingaa08ef62011-07-15 06:42:11 +0000738 memset (mediatype, 0, sizeof(AM_MEDIA_TYPE));
739 mediatype->majortype = MEDIATYPE_Video;
740 mediatype->subtype = *(get_dshow_format_info(strm->param.fmt.id)->
741 dshow_format);
742 mediatype->bFixedSizeSamples = TRUE;
743 mediatype->bTemporalCompression = FALSE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000744
Sauw Mingaa08ef62011-07-15 06:42:11 +0000745 vi = (VIDEOINFOHEADER *)
746 CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
747 memset (vi, 0, sizeof(VIDEOINFOHEADER));
748 mediatype->formattype = FORMAT_VideoInfo;
749 mediatype->cbFormat = sizeof(VIDEOINFOHEADER);
750 mediatype->pbFormat = (BYTE *)vi;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000751
Sauw Mingaa08ef62011-07-15 06:42:11 +0000752 vi->rcSource.bottom = vfd->size.h;
753 vi->rcSource.right = vfd->size.w;
754 vi->rcTarget.bottom = vfd->size.h;
755 vi->rcTarget.right = vfd->size.w;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000756
Sauw Mingaa08ef62011-07-15 06:42:11 +0000757 vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
758 vi->bmiHeader.biPlanes = 1;
759 vi->bmiHeader.biBitCount = vfi->bpp;
760 vi->bmiHeader.biCompression = strm->param.fmt.id;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000761 }
762
763 if (!srcpin || !sinkpin || !mediatype) {
Nanang Izzuddinfd600af2011-07-15 02:12:25 +0000764 hr = VFW_E_TYPE_NOT_ACCEPTED;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000765 goto on_error;
766 }
767 video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
Sauw Ming0531a722011-04-07 08:27:27 +0000768 if (!use_def_size) {
769 video_info->bmiHeader.biWidth = vfd->size.w;
770 video_info->bmiHeader.biHeight = vfd->size.h;
771 }
772 if (!use_def_fps && vfd->fps.num != 0)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000773 video_info->AvgTimePerFrame = (LONGLONG) (10000000 *
Nanang Izzuddin63b01ab2011-03-22 09:46:04 +0000774 (double)vfd->fps.denum /
775 vfd->fps.num);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000776 video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
777 mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
778 if (graph->csource_filter)
Sauw Minga8e08622011-06-13 11:48:37 +0000779 SourceFilter_SetMediaType(graph->csource_filter,
780 mediatype);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000781
782 hr = IFilterGraph_AddFilter(graph->filter_graph,
783 (IBaseFilter *)graph->rend_filter,
784 L"renderer");
785 if (FAILED(hr))
786 goto on_error;
787
788 hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin,
789 mediatype);
Sauw Mingcec804e2011-10-07 05:41:37 +0000790 if (SUCCEEDED(hr)) {
791 if (use_def_size || use_def_fps) {
792 pjmedia_format_init_video(&strm->param.fmt, strm->param.fmt.id,
793 video_info->bmiHeader.biWidth,
794 video_info->bmiHeader.biHeight,
795 10000000,
796 (unsigned)video_info->AvgTimePerFrame);
797 }
798
799 strm->frm_buf_size = 0;
800 if (dir == PJMEDIA_DIR_CAPTURE &&
801 video_info->bmiHeader.biCompression == BI_RGB &&
802 video_info->bmiHeader.biHeight > 0)
803 {
804 /* Allocate buffer to flip the captured image. */
805 strm->frm_buf_size = (video_info->bmiHeader.biBitCount >> 3) *
806 video_info->bmiHeader.biWidth;
807 strm->frm_buf = pj_pool_alloc(strm->pool, strm->frm_buf_size);
808 }
Sauw Ming0531a722011-04-07 08:27:27 +0000809 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000810
811on_error:
812 if (srcpin)
813 IPin_Release(srcpin);
814 if (sinkpin)
815 IPin_Release(sinkpin);
816 if (vi)
817 CoTaskMemFree(vi);
Benny Prijono63a894d2011-04-07 07:22:35 +0000818 if (FAILED(hr)) {
819 char msg[80];
820 if (AMGetErrorText(hr, msg, sizeof(msg))) {
821 PJ_LOG(4,(THIS_FILE, "Error creating filter graph: %s (hr=0x%x)",
822 msg, hr));
823 }
824 return PJ_EUNKNOWN;
825 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000826
827 return PJ_SUCCESS;
828}
829
Sauw Ming0531a722011-04-07 08:27:27 +0000830static void destroy_filter_graph(struct dshow_stream * stream)
831{
Sauw Ming83db7d62011-06-09 04:08:47 +0000832 if (stream->dgraph.source_filter) {
833 IBaseFilter_Release(stream->dgraph.source_filter);
834 stream->dgraph.source_filter = NULL;
835 }
836 if (stream->dgraph.rend_filter) {
837 IBaseFilter_Release(stream->dgraph.rend_filter);
838 stream->dgraph.rend_filter = NULL;
839 }
840 if (stream->dgraph.media_filter) {
841 IMediaFilter_Release(stream->dgraph.media_filter);
842 stream->dgraph.media_filter = NULL;
843 }
844 if (stream->dgraph.filter_graph) {
845 IFilterGraph_Release(stream->dgraph.filter_graph);
846 stream->dgraph.filter_graph = NULL;
Sauw Ming0531a722011-04-07 08:27:27 +0000847 }
848}
849
Benny Prijonoc45d9512010-12-10 11:04:30 +0000850/* API: create stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000851static pj_status_t dshow_factory_create_stream(
852 pjmedia_vid_dev_factory *f,
Sauw Ming5291a2d2011-07-15 07:52:44 +0000853 pjmedia_vid_dev_param *param,
854 const pjmedia_vid_dev_cb *cb,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000855 void *user_data,
856 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000857{
858 struct dshow_factory *df = (struct dshow_factory*)f;
859 pj_pool_t *pool;
860 struct dshow_stream *strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000861 pj_status_t status;
862
Sauw Ming83db7d62011-06-09 04:08:47 +0000863 PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE ||
864 param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
865
Benny Prijonoc45d9512010-12-10 11:04:30 +0000866 if (!get_dshow_format_info(param->fmt.id))
867 return PJMEDIA_EVID_BADFORMAT;
868
869 /* Create and Initialize stream descriptor */
870 pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL);
871 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
872
873 strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream);
874 pj_memcpy(&strm->param, param, sizeof(*param));
875 strm->pool = pool;
876 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
877 strm->user_data = user_data;
878
Benny Prijonoc45d9512010-12-10 11:04:30 +0000879 if (param->dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijono349037b2011-03-17 11:25:19 +0000880 const pjmedia_video_format_detail *vfd;
881
Sauw Ming83db7d62011-06-09 04:08:47 +0000882 /* Create capture stream here */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000883 status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
Sauw Ming0531a722011-04-07 08:27:27 +0000884 PJ_FALSE, PJ_FALSE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000885 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000886 if (status != PJ_SUCCESS) {
887 destroy_filter_graph(strm);
888 /* Try to use default fps */
889 PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default fps"));
890 status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
891 PJ_FALSE, PJ_TRUE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000892 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000893
894 if (status != PJ_SUCCESS) {
895 /* Still failed, now try to use default fps and size */
896 destroy_filter_graph(strm);
897 /* Try to use default fps */
898 PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default "
899 "size & fps"));
900 status = create_filter_graph(PJMEDIA_DIR_CAPTURE,
901 param->cap_id,
902 PJ_TRUE, PJ_TRUE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000903 &strm->dgraph);
Sauw Ming0531a722011-04-07 08:27:27 +0000904 }
905
906 if (status != PJ_SUCCESS)
907 goto on_error;
908 pj_memcpy(param, &strm->param, sizeof(*param));
909 }
Benny Prijono349037b2011-03-17 11:25:19 +0000910
911 vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
912 strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
Sauw Ming83db7d62011-06-09 04:08:47 +0000913 } else if (param->dir & PJMEDIA_DIR_RENDER) {
914 /* Create render stream here */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000915 status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id,
Sauw Ming0531a722011-04-07 08:27:27 +0000916 PJ_FALSE, PJ_FALSE, df, strm,
Sauw Ming83db7d62011-06-09 04:08:47 +0000917 &strm->dgraph);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000918 if (status != PJ_SUCCESS)
919 goto on_error;
920 }
921
922 /* Apply the remaining settings */
923 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
924 dshow_stream_set_cap(&strm->base,
925 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
926 &param->window);
927 }
928
929 /* Done */
930 strm->base.op = &stream_op;
931 *p_vid_strm = &strm->base;
932
933 return PJ_SUCCESS;
934
935on_error:
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000936 dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
Benny Prijono63a894d2011-04-07 07:22:35 +0000937 return status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000938}
939
940/* API: Get stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000941static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
Sauw Ming5291a2d2011-07-15 07:52:44 +0000942 pjmedia_vid_dev_param *pi)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000943{
944 struct dshow_stream *strm = (struct dshow_stream*)s;
945
946 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
947
948 pj_memcpy(pi, &strm->param, sizeof(*pi));
949
950 if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
951 &pi->window) == PJ_SUCCESS)
952 {
953 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
954 }
955
956 return PJ_SUCCESS;
957}
958
959/* API: get capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000960static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000961 pjmedia_vid_dev_cap cap,
962 void *pval)
963{
964 struct dshow_stream *strm = (struct dshow_stream*)s;
965
966 PJ_UNUSED_ARG(strm);
967
968 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
969
970 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
971 {
972 *(unsigned*)pval = 0;
973 return PJ_SUCCESS;
974 } else {
975 return PJMEDIA_EVID_INVCAP;
976 }
977}
978
979/* API: set capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000980static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000981 pjmedia_vid_dev_cap cap,
982 const void *pval)
983{
984 struct dshow_stream *strm = (struct dshow_stream*)s;
985
986 PJ_UNUSED_ARG(strm);
987
988 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
989
990 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
991 {
992 // set renderer's window here
993 return PJ_SUCCESS;
994 }
995
996 return PJMEDIA_EVID_INVCAP;
997}
998
999/* API: Start stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001000static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001001{
1002 struct dshow_stream *stream = (struct dshow_stream*)strm;
Sauw Ming83db7d62011-06-09 04:08:47 +00001003 HRESULT hr;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001004
1005 stream->quit_flag = PJ_FALSE;
1006 stream->cap_thread_exited = PJ_FALSE;
1007 stream->rend_thread_exited = PJ_FALSE;
1008
Sauw Ming83db7d62011-06-09 04:08:47 +00001009 hr = IMediaFilter_Run(stream->dgraph.media_filter, 0);
1010 if (FAILED(hr)) {
1011 char msg[80];
1012 if (AMGetErrorText(hr, msg, sizeof(msg))) {
1013 PJ_LOG(4,(THIS_FILE, "Error starting media: %s", msg));
Benny Prijonoc45d9512010-12-10 11:04:30 +00001014 }
Sauw Ming83db7d62011-06-09 04:08:47 +00001015 return PJ_EUNKNOWN;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001016 }
1017
1018 PJ_LOG(4, (THIS_FILE, "Starting dshow video stream"));
1019
1020 return PJ_SUCCESS;
1021}
1022
1023/* API: Stop stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001024static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001025{
1026 struct dshow_stream *stream = (struct dshow_stream*)strm;
1027 unsigned i;
1028
1029 stream->quit_flag = PJ_TRUE;
1030 if (stream->cap_thread) {
1031 for (i=0; !stream->cap_thread_exited && i<100; ++i)
1032 pj_thread_sleep(10);
1033 }
1034 for (i=0; !stream->rend_thread_exited && i<100; ++i)
1035 pj_thread_sleep(10);
1036
Sauw Ming83db7d62011-06-09 04:08:47 +00001037 IMediaFilter_Stop(stream->dgraph.media_filter);
Benny Prijonoc45d9512010-12-10 11:04:30 +00001038
1039 PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream"));
1040
1041 return PJ_SUCCESS;
1042}
1043
Benny Prijonoc45d9512010-12-10 11:04:30 +00001044/* API: Destroy stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001045static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001046{
1047 struct dshow_stream *stream = (struct dshow_stream*)strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001048
1049 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1050
1051 dshow_stream_stop(strm);
Sauw Ming0531a722011-04-07 08:27:27 +00001052 destroy_filter_graph(stream);
Benny Prijonoc45d9512010-12-10 11:04:30 +00001053
1054 pj_pool_release(stream->pool);
1055
1056 return PJ_SUCCESS;
1057}
1058
1059#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */