~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjmedia/src/pjmedia-videodev/dshow_dev.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

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