dshow.cpp 77.3 KB
Newer Older
Gildas Bazin's avatar
 
Gildas Bazin committed
1
/*****************************************************************************
2
 * dshow.cpp : DirectShow access module for vlc
Gildas Bazin's avatar
 
Gildas Bazin committed
3
 *****************************************************************************
4
 * Copyright (C) 2002, 2003 the VideoLAN team
5
 * $Id$
Gildas Bazin's avatar
 
Gildas Bazin committed
6
 *
7
 * Author: Gildas Bazin <gbazin@videolan.org>
8
 *         Damien Fouilleul <damienf@videolan.org>
Gildas Bazin's avatar
 
Gildas Bazin committed
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Gildas Bazin's avatar
 
Gildas Bazin committed
23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28

Gildas Bazin's avatar
 
Gildas Bazin committed
29
#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
30 31
#include <vlc_input.h>
#include <vlc_access.h>
32
#include <vlc_demux.h>
Clément Stenac's avatar
Clément Stenac committed
33
#include <vlc_vout.h>
Clément Stenac's avatar
Round 2  
Clément Stenac committed
34
#include <vlc_interface.h>
Gildas Bazin's avatar
 
Gildas Bazin committed
35

Gildas Bazin's avatar
Gildas Bazin committed
36
#include "common.h"
Gildas Bazin's avatar
 
Gildas Bazin committed
37 38
#include "filter.h"

Gildas Bazin's avatar
 
Gildas Bazin committed
39 40 41
/*****************************************************************************
 * Access: local prototypes
 *****************************************************************************/
42
static block_t *ReadCompressed( access_t * );
43
static int AccessControl ( access_t *, int, va_list );
Gildas Bazin's avatar
 
Gildas Bazin committed
44

Gildas Bazin's avatar
Gildas Bazin committed
45 46 47 48
static int Demux       ( demux_t * );
static int DemuxControl( demux_t *, int, va_list );

static int OpenDevice( vlc_object_t *, access_sys_t *, string, vlc_bool_t );
Gildas Bazin's avatar
 
Gildas Bazin committed
49 50
static IBaseFilter *FindCaptureDevice( vlc_object_t *, string *,
                                       list<string> *, vlc_bool_t );
51
static size_t EnumDeviceCaps( vlc_object_t *, IBaseFilter *,
52 53
                              int, int, int, int, int, int,
                              AM_MEDIA_TYPE *mt, size_t );
Gildas Bazin's avatar
Gildas Bazin committed
54 55
static bool ConnectFilters( vlc_object_t *, access_sys_t *,
                            IBaseFilter *, CaptureFilter * );
Gildas Bazin's avatar
 
Gildas Bazin committed
56 57
static int FindDevicesCallback( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );
58 59
static int ConfigDevicesCallback( vlc_object_t *, char const *,
                                  vlc_value_t, vlc_value_t, void * );
Gildas Bazin's avatar
 
Gildas Bazin committed
60

61
static void ShowPropertyPage( IUnknown * );
62
static void ShowDeviceProperties( vlc_object_t *, ICaptureGraphBuilder2 *,
63
                                  IBaseFilter *, vlc_bool_t );
64
static void ShowTunerProperties( vlc_object_t *, ICaptureGraphBuilder2 *,
65
                                 IBaseFilter *, vlc_bool_t );
66 67
static void ConfigTuner( vlc_object_t *, ICaptureGraphBuilder2 *,
                         IBaseFilter * );
Gildas Bazin's avatar
 
Gildas Bazin committed
68

Gildas Bazin's avatar
 
Gildas Bazin committed
69
/*****************************************************************************
70
 * Module descriptor
Gildas Bazin's avatar
 
Gildas Bazin committed
71
 *****************************************************************************/
72 73 74 75
static const char *ppsz_vdev[] = { "", "none" };
static const char *ppsz_vdev_text[] = { N_("Default"), N_("None") };
static const char *ppsz_adev[] = { "", "none" };
static const char *ppsz_adev_text[] = { N_("Default"), N_("None") };
76
static int  pi_tuner_input[] = { 0, 1, 2 };
77
static const char *ppsz_tuner_input_text[] =
78
    {N_("Default"), N_("Cable"), N_("Antenna")};
79
static const int pi_amtuner_mode[]  = { AMTUNER_MODE_DEFAULT,
80 81 82 83
                                 AMTUNER_MODE_TV,
                                 AMTUNER_MODE_FM_RADIO,
                                 AMTUNER_MODE_AM_RADIO,
                                 AMTUNER_MODE_DSS };
84
static const char *ppsz_amtuner_mode_text[] = { N_("Default"),
85 86 87 88
                                          N_("TV"),
                                          N_("FM radio"),
                                          N_("AM radio"),
                                          N_("DSS") };
89

Gildas Bazin's avatar
 
Gildas Bazin committed
90 91
#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
92
    "Caching value for DirectShow streams. " \
Clément Stenac's avatar
Clément Stenac committed
93
    "This value should be set in millisecondss." )
94 95
#define VDEV_TEXT N_("Video device name")
#define VDEV_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
96
    "Name of the video device that will be used by the " \
97 98 99 100
    "DirectShow plugin. If you don't specify anything, the default device " \
    "will be used.")
#define ADEV_TEXT N_("Audio device name")
#define ADEV_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
101
    "Name of the audio device that will be used by the " \
102
    "DirectShow plugin. If you don't specify anything, the default device " \
Christophe Mutricy's avatar
Christophe Mutricy committed
103
    "will be used. ")
104 105
#define SIZE_TEXT N_("Video size")
#define SIZE_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
106
    "Size of the video that will be displayed by the " \
107
    "DirectShow plugin. If you don't specify anything the default size for " \
Christophe Mutricy's avatar
Christophe Mutricy committed
108
    "your device will be used. You can specify a standard size (cif, d1, ...) or <width>x<height>.")
109 110 111
#define CHROMA_TEXT N_("Video input chroma format")
#define CHROMA_LONGTEXT N_( \
    "Force the DirectShow video input to use a specific chroma format " \
112
    "(eg. I420 (default), RV24, etc.)")
113 114 115
#define FPS_TEXT N_("Video input frame rate")
#define FPS_LONGTEXT N_( \
    "Force the DirectShow video input to use a specific frame rate" \
Christophe Mutricy's avatar
Christophe Mutricy committed
116
    "(eg. 0 means default, 25, 29.97, 50, 59.94, etc.)")
117
#define CONFIG_TEXT N_("Device properties")
Gildas Bazin's avatar
 
Gildas Bazin committed
118
#define CONFIG_LONGTEXT N_( \
119 120
    "Show the properties dialog of the selected device before starting the " \
    "stream.")
121 122 123
#define TUNER_TEXT N_("Tuner properties")
#define TUNER_LONGTEXT N_( \
    "Show the tuner properties [channel selection] page." )
124 125
#define CHANNEL_TEXT N_("Tuner TV Channel")
#define CHANNEL_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
126
    "Set the TV channel the tuner will set to " \
127 128 129
    "(0 means default)." )
#define COUNTRY_TEXT N_("Tuner country code")
#define COUNTRY_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
130
    "Set the tuner country code that establishes the current " \
131 132 133
    "channel-to-frequency mapping (0 means default)." )
#define TUNER_INPUT_TEXT N_("Tuner input type")
#define TUNER_INPUT_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
134
    "Select the tuner input type (Cable/Antenna)." )
135 136
#define VIDEO_IN_TEXT N_("Video input pin")
#define VIDEO_IN_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
137
  "Select the video input source, such as composite, s-video, " \
138
  "or tuner. Since these settings are hardware-specific, you should find good " \
Clément Stenac's avatar
Clément Stenac committed
139 140
  "settings in the \"Device config\" area, and use those numbers here. -1 " \
  "means that settings will not be changed.")
141 142
#define AUDIO_IN_TEXT N_("Audio input pin")
#define AUDIO_IN_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
143
  "Select the audio input source. See the \"video input\" option." )
144 145
#define VIDEO_OUT_TEXT N_("Video output pin")
#define VIDEO_OUT_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
146
  "Select the video output type. See the \"video input\" option." )
147 148
#define AUDIO_OUT_TEXT N_("Audio output pin")
#define AUDIO_OUT_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
149
  "Select the audio output type. See the \"video input\" option." )
Gildas Bazin's avatar
 
Gildas Bazin committed
150

151
#define AMTUNER_MODE_TEXT N_("AM Tuner mode")
152
#define AMTUNER_MODE_LONGTEXT N_( \
153 154
    "AM Tuner mode. Can be one of DEFAULT, TV, AM_RADIO, FM_RADIO or DSS.")

Gildas Bazin's avatar
Gildas Bazin committed
155 156 157
static int  CommonOpen ( vlc_object_t *, access_sys_t *, vlc_bool_t );
static void CommonClose( vlc_object_t *, access_sys_t * );

158 159 160 161 162 163
static int  AccessOpen ( vlc_object_t * );
static void AccessClose( vlc_object_t * );

static int  DemuxOpen  ( vlc_object_t * );
static void DemuxClose ( vlc_object_t * );

Gildas Bazin's avatar
 
Gildas Bazin committed
164
vlc_module_begin();
165
    set_shortname( _("DirectShow") );
Gildas Bazin's avatar
 
Gildas Bazin committed
166
    set_description( _("DirectShow input") );
Clément Stenac's avatar
Clément Stenac committed
167 168
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_ACCESS );
169
    add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL,
Gildas Bazin's avatar
 
Gildas Bazin committed
170
                 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
171

172
    add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE);
173
        change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback );
174 175
        change_action_add( FindDevicesCallback, N_("Refresh list") );
        change_action_add( ConfigDevicesCallback, N_("Configure") );
176

177
    add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE);
178
        change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback );
179 180
        change_action_add( FindDevicesCallback, N_("Refresh list") );
        change_action_add( ConfigDevicesCallback, N_("Configure") );
181

182
    add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE);
183

184 185
    add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
                VLC_TRUE );
Gildas Bazin's avatar
 
Gildas Bazin committed
186

187 188 189
    add_float( "dshow-fps", 0.0f, NULL, FPS_TEXT, FPS_LONGTEXT,
                VLC_TRUE );

Gildas Bazin's avatar
 
Gildas Bazin committed
190
    add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT,
191
              VLC_TRUE );
Gildas Bazin's avatar
 
Gildas Bazin committed
192

193
    add_bool( "dshow-tuner", VLC_FALSE, NULL, TUNER_TEXT, TUNER_LONGTEXT,
194
              VLC_TRUE );
195

196 197 198 199 200 201 202 203 204 205
    add_integer( "dshow-tuner-channel", 0, NULL, CHANNEL_TEXT,
                 CHANNEL_LONGTEXT, VLC_TRUE );

    add_integer( "dshow-tuner-country", 0, NULL, COUNTRY_TEXT,
                 COUNTRY_LONGTEXT, VLC_TRUE );

    add_integer( "dshow-tuner-input", 0, NULL, TUNER_INPUT_TEXT,
                 TUNER_INPUT_LONGTEXT, VLC_TRUE );
        change_integer_list( pi_tuner_input, ppsz_tuner_input_text, 0 );

206
    add_integer( "dshow-video-input",  -1, NULL, VIDEO_IN_TEXT,
207
                 VIDEO_IN_LONGTEXT, VLC_TRUE );
208 209

    add_integer( "dshow-audio-input",  -1, NULL, AUDIO_IN_TEXT,
210
                 AUDIO_IN_LONGTEXT, VLC_TRUE );
211 212

    add_integer( "dshow-video-output", -1, NULL, VIDEO_OUT_TEXT,
213
                 VIDEO_OUT_LONGTEXT, VLC_TRUE );
214 215

    add_integer( "dshow-audio-output", -1, NULL, AUDIO_OUT_TEXT,
216
                 AUDIO_OUT_LONGTEXT, VLC_TRUE );
217

218
    add_integer( "dshow-amtuner-mode", AMTUNER_MODE_TV, NULL,
219 220 221
                AMTUNER_MODE_TEXT, AMTUNER_MODE_LONGTEXT, VLC_FALSE);
        change_integer_list( pi_amtuner_mode, ppsz_amtuner_mode_text, 0 );

Gildas Bazin's avatar
 
Gildas Bazin committed
222
    add_shortcut( "dshow" );
Gildas Bazin's avatar
Gildas Bazin committed
223 224
    set_capability( "access_demux", 0 );
    set_callbacks( DemuxOpen, DemuxClose );
Gildas Bazin's avatar
 
Gildas Bazin committed
225 226

    add_submodule();
Gildas Bazin's avatar
Gildas Bazin committed
227 228 229
    set_description( _("DirectShow input") );
    set_capability( "access2", 0 );
    set_callbacks( AccessOpen, AccessClose );
Gildas Bazin's avatar
 
Gildas Bazin committed
230 231 232

vlc_module_end();

Gildas Bazin's avatar
Gildas Bazin committed
233
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
234
 * DirectShow elementary stream descriptor
Gildas Bazin's avatar
Gildas Bazin committed
235
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249
typedef struct dshow_stream_t
{
    string          devicename;
    IBaseFilter     *p_device_filter;
    CaptureFilter   *p_capture_filter;
    AM_MEDIA_TYPE   mt;

    union
    {
      VIDEOINFOHEADER video;
      WAVEFORMATEX    audio;

    } header;

Gildas Bazin's avatar
Gildas Bazin committed
250 251 252
    int             i_fourcc;
    es_out_id_t     *p_es;

253
    vlc_bool_t      b_pts;
Gildas Bazin's avatar
 
Gildas Bazin committed
254 255
} dshow_stream_t;

Gildas Bazin's avatar
Gildas Bazin committed
256
/*****************************************************************************
257
 * DirectShow utility functions
Gildas Bazin's avatar
Gildas Bazin committed
258
 *****************************************************************************/
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
static void CreateDirectShowGraph( access_sys_t *p_sys )
{
    p_sys->i_crossbar_route_depth = 0;

    /* Create directshow filter graph */
    if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
                       (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) )
    {
        /* Create directshow capture graph builder if available */
        if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0,
                         CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
                         (void **)&p_sys->p_capture_graph_builder2 ) ) )
        {
            p_sys->p_capture_graph_builder2->
                SetFiltergraph((IGraphBuilder *)p_sys->p_graph);
        }

        p_sys->p_graph->QueryInterface( IID_IMediaControl,
                                        (void **)&p_sys->p_control );
    }
}

static void DeleteDirectShowGraph( access_sys_t *p_sys )
{
    DeleteCrossbarRoutes( p_sys );

    /* Remove filters from graph */
    for( int i = 0; i < p_sys->i_streams; i++ )
    {
        p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter );
        p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter );
        p_sys->pp_streams[i]->p_capture_filter->Release();
        p_sys->pp_streams[i]->p_device_filter->Release();
    }

    /* Release directshow objects */
    if( p_sys->p_control )
    {
        p_sys->p_control->Release();
        p_sys->p_control = NULL;
    }
    if( p_sys->p_capture_graph_builder2 )
    {
        p_sys->p_capture_graph_builder2->Release();
        p_sys->p_capture_graph_builder2 = NULL;
    }

    if( p_sys->p_graph )
    {
        p_sys->p_graph->Release();
        p_sys->p_graph = NULL;
    }
}

Gildas Bazin's avatar
 
Gildas Bazin committed
313
/*****************************************************************************
Gildas Bazin's avatar
Gildas Bazin committed
314
 * CommonOpen: open direct show device
Gildas Bazin's avatar
 
Gildas Bazin committed
315
 *****************************************************************************/
Gildas Bazin's avatar
Gildas Bazin committed
316 317
static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys,
                       vlc_bool_t b_access_demux )
Gildas Bazin's avatar
 
Gildas Bazin committed
318
{
319
    vlc_value_t  val;
Gildas Bazin's avatar
Gildas Bazin committed
320
    int i;
Gildas Bazin's avatar
 
Gildas Bazin committed
321

322
    /* Get/parse options and open device(s) */
Gildas Bazin's avatar
 
Gildas Bazin committed
323
    string vdevname, adevname;
324
    int i_width = 0, i_height = 0, i_chroma = 0;
Gildas Bazin's avatar
Gildas Bazin committed
325
    vlc_bool_t b_audio = VLC_TRUE;
Gildas Bazin's avatar
 
Gildas Bazin committed
326

327 328
    var_Create( p_this, "dshow-config", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_this, "dshow-tuner", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
Gildas Bazin's avatar
Gildas Bazin committed
329 330
    var_Create( p_this, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_this, "dshow-vdev", &val );
331 332
    if( val.psz_string ) vdevname = string( val.psz_string );
    if( val.psz_string ) free( val.psz_string );
Gildas Bazin's avatar
 
Gildas Bazin committed
333

Gildas Bazin's avatar
Gildas Bazin committed
334 335
    var_Create( p_this, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_this, "dshow-adev", &val );
336 337 338
    if( val.psz_string ) adevname = string( val.psz_string );
    if( val.psz_string ) free( val.psz_string );

Gildas Bazin's avatar
Gildas Bazin committed
339 340
    static struct {char *psz_size; int  i_width; int  i_height;} size_table[] =
    { { "subqcif", 128, 96 }, { "qsif", 160, 120 }, { "qcif", 176, 144 },
341
      { "sif", 320, 240 }, { "cif", 352, 288 }, { "d1", 640, 480 },
Gildas Bazin's avatar
Gildas Bazin committed
342 343 344 345 346
      { 0, 0, 0 },
    };

    var_Create( p_this, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_this, "dshow-size", &val );
347
    if( val.psz_string && *val.psz_string )
Gildas Bazin's avatar
 
Gildas Bazin committed
348
    {
Gildas Bazin's avatar
Gildas Bazin committed
349
        for( i = 0; size_table[i].psz_size; i++ )
350
        {
Gildas Bazin's avatar
Gildas Bazin committed
351 352 353 354 355 356
            if( !strcmp( val.psz_string, size_table[i].psz_size ) )
            {
                i_width = size_table[i].i_width;
                i_height = size_table[i].i_height;
                break;
            }
357
        }
Gildas Bazin's avatar
Gildas Bazin committed
358
        if( !size_table[i].psz_size ) /* Try to parse "WidthxHeight" */
359 360 361 362
        {
            char *psz_parser;
            i_width = strtol( val.psz_string, &psz_parser, 0 );
            if( *psz_parser == 'x' || *psz_parser == 'X')
Gildas Bazin's avatar
 
Gildas Bazin committed
363
            {
364
                i_height = strtol( psz_parser + 1, &psz_parser, 0 );
Gildas Bazin's avatar
 
Gildas Bazin committed
365
            }
Clément Stenac's avatar
Clément Stenac committed
366
            msg_Dbg( p_this, "width x height %dx%d", i_width, i_height );
Gildas Bazin's avatar
 
Gildas Bazin committed
367 368
        }
    }
369
    if( val.psz_string ) free( val.psz_string );
Gildas Bazin's avatar
 
Gildas Bazin committed
370

371
    p_sys->b_chroma = VLC_FALSE;
Gildas Bazin's avatar
Gildas Bazin committed
372 373
    var_Create( p_this, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_this, "dshow-chroma", &val );
374 375 376 377
    if( val.psz_string && strlen( val.psz_string ) >= 4 )
    {
        i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1],
                               val.psz_string[2], val.psz_string[3] );
378
        p_sys->b_chroma = VLC_TRUE;
379 380
    }
    if( val.psz_string ) free( val.psz_string );
Gildas Bazin's avatar
 
Gildas Bazin committed
381

382
    var_Create( p_this, "dshow-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
383 384 385 386 387 388 389
    var_Create( p_this, "dshow-tuner-channel",
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_this, "dshow-tuner-country",
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_this, "dshow-tuner-input",
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );

390 391 392
    var_Create( p_this, "dshow-amtuner-mode",
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );

Gildas Bazin's avatar
Gildas Bazin committed
393
    var_Create( p_this, "dshow-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
Gildas Bazin's avatar
 
Gildas Bazin committed
394

395 396 397 398 399
    var_Create( p_this, "dshow-video-input", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_this, "dshow-audio-input", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_this, "dshow-video-output", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_this, "dshow-audio-output", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );

Gildas Bazin's avatar
 
Gildas Bazin committed
400
    /* Initialize OLE/COM */
401
    CoInitialize( 0 );
Gildas Bazin's avatar
 
Gildas Bazin committed
402

Gildas Bazin's avatar
 
Gildas Bazin committed
403 404
    /* Initialize some data */
    p_sys->i_streams = 0;
405
    p_sys->pp_streams = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
406 407 408
    p_sys->i_width = i_width;
    p_sys->i_height = i_height;
    p_sys->i_chroma = i_chroma;
Gildas Bazin's avatar
 
Gildas Bazin committed
409

410 411 412
    p_sys->p_graph = NULL;
    p_sys->p_capture_graph_builder2 = NULL;
    p_sys->p_control = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
413

414 415 416
    vlc_mutex_init( p_this, &p_sys->lock );
    vlc_cond_init( p_this, &p_sys->wait );

417 418
    /* Build directshow graph */
    CreateDirectShowGraph( p_sys );
Gildas Bazin's avatar
 
Gildas Bazin committed
419

Gildas Bazin's avatar
Gildas Bazin committed
420
    if( OpenDevice( p_this, p_sys, vdevname, 0 ) != VLC_SUCCESS )
Gildas Bazin's avatar
 
Gildas Bazin committed
421
    {
Gildas Bazin's avatar
Gildas Bazin committed
422
        msg_Err( p_this, "can't open video");
Gildas Bazin's avatar
 
Gildas Bazin committed
423
    }
Gildas Bazin's avatar
Gildas Bazin committed
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
    else
    {
        /* Check if we can handle the demuxing ourselves or need to spawn
         * a demuxer module */
        dshow_stream_t *p_stream = p_sys->pp_streams[p_sys->i_streams-1];

        if( p_stream->mt.majortype == MEDIATYPE_Video )
        {
            if( /* Raw DV stream */
                p_stream->i_fourcc == VLC_FOURCC('d','v','s','l') ||
                p_stream->i_fourcc == VLC_FOURCC('d','v','s','d') ||
                p_stream->i_fourcc == VLC_FOURCC('d','v','h','d') ||
                /* Raw MPEG video stream */
                p_stream->i_fourcc == VLC_FOURCC('m','p','2','v') )
            {
                b_audio = VLC_FALSE;
440 441 442 443

                if( b_access_demux )
                {
                    /* Let the access (only) take care of that */
444
                    return VLC_EGENERIC;
445
                }
Gildas Bazin's avatar
Gildas Bazin committed
446 447
            }
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
448

449 450 451 452 453 454 455 456 457
        if( p_stream->mt.majortype == MEDIATYPE_Stream )
        {
            b_audio = VLC_FALSE;

            if( b_access_demux )
            {
                /* Let the access (only) take care of that */
                return VLC_EGENERIC;
            }
458 459 460 461 462 463 464 465 466

            var_Get( p_this, "dshow-tuner", &val );
            if( val.b_bool )
            {
                /* FIXME: we do MEDIATYPE_Stream here so we don't do
                 * it twice. */
                ShowTunerProperties( p_this, p_sys->p_capture_graph_builder2,
                                     p_stream->p_device_filter, 0 );
            }
467
        }
Gildas Bazin's avatar
Gildas Bazin committed
468 469 470
    }

    if( b_audio && OpenDevice( p_this, p_sys, adevname, 1 ) != VLC_SUCCESS )
Gildas Bazin's avatar
 
Gildas Bazin committed
471
    {
Gildas Bazin's avatar
Gildas Bazin committed
472
        msg_Err( p_this, "can't open audio");
Gildas Bazin's avatar
 
Gildas Bazin committed
473
    }
Gildas Bazin's avatar
Gildas Bazin committed
474

475
    for( i = p_sys->i_crossbar_route_depth-1; i >= 0 ; --i )
Gildas Bazin's avatar
 
Gildas Bazin committed
476
    {
477
            var_Get( p_this, "dshow-video-input", &val );
478
            if( val.i_int >= 0 )
479 480
                    p_sys->crossbar_routes[i].VideoInputIndex=val.i_int;
            var_Get( p_this, "dshow-video-output", &val );
481
            if( val.i_int >= 0 )
482 483
                    p_sys->crossbar_routes[i].VideoOutputIndex=val.i_int;
            var_Get( p_this, "dshow-audio-input", &val );
484
            if( val.i_int >= 0 )
485 486
                    p_sys->crossbar_routes[i].AudioInputIndex=val.i_int;
            var_Get( p_this, "dshow-audio-output", &val );
487
            if( val.i_int >= 0 )
488 489
                    p_sys->crossbar_routes[i].AudioOutputIndex=val.i_int;

490 491 492 493 494
        IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
        LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex;
        LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex;
        LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex;
        LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex;
Gildas Bazin's avatar
 
Gildas Bazin committed
495

496 497
        if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) )
        {
Clément Stenac's avatar
Clément Stenac committed
498 499
            msg_Dbg( p_this, "crossbar at depth %d, routed video "
                     "output %ld to video input %ld", i, VideoOutputIndex,
500
                     VideoInputIndex );
Gildas Bazin's avatar
 
Gildas Bazin committed
501

502 503 504 505 506
            if( AudioOutputIndex != -1 && AudioInputIndex != -1 )
            {
                if( SUCCEEDED( pXbar->Route(AudioOutputIndex,
                                            AudioInputIndex)) )
                {
Clément Stenac's avatar
Clément Stenac committed
507 508
                    msg_Dbg(p_this, "crossbar at depth %d, routed audio "
                            "output %ld to audio input %ld", i,
509 510 511 512 513 514
                            AudioOutputIndex, AudioInputIndex );
                }
            }
        }
    }

515 516 517 518
    /*
    ** Show properties pages from other filters in graph
    */
    var_Get( p_this, "dshow-config", &val );
519
    if( val.b_bool )
520 521 522 523 524
    {
        for( i = p_sys->i_crossbar_route_depth-1; i >= 0 ; --i )
        {
            IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
            IBaseFilter *p_XF;
525 526 527

            if( SUCCEEDED( pXbar->QueryInterface( IID_IBaseFilter,
                                                  (void **)&p_XF ) ) )
528 529 530 531
            {
                ShowPropertyPage( p_XF );
                p_XF->Release();
            }
532
        }
533 534
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
535 536
    /* Initialize some data */
    p_sys->i_current_stream = 0;
537

Gildas Bazin's avatar
Gildas Bazin committed
538
    if( !p_sys->i_streams ) return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
539

Gildas Bazin's avatar
 
Gildas Bazin committed
540 541 542 543
    return VLC_SUCCESS;
}

/*****************************************************************************
Gildas Bazin's avatar
Gildas Bazin committed
544
 * DemuxOpen: open direct show device as an access_demux module
Gildas Bazin's avatar
 
Gildas Bazin committed
545
 *****************************************************************************/
Gildas Bazin's avatar
Gildas Bazin committed
546
static int DemuxOpen( vlc_object_t *p_this )
Gildas Bazin's avatar
 
Gildas Bazin committed
547
{
Gildas Bazin's avatar
Gildas Bazin committed
548 549 550
    demux_t      *p_demux = (demux_t *)p_this;
    access_sys_t *p_sys;
    int i;
Gildas Bazin's avatar
 
Gildas Bazin committed
551

Gildas Bazin's avatar
Gildas Bazin committed
552 553 554
    p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
    memset( p_sys, 0, sizeof( access_sys_t ) );
    p_demux->p_sys = (demux_sys_t *)p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
555

Gildas Bazin's avatar
Gildas Bazin committed
556 557 558 559 560
    if( CommonOpen( p_this, p_sys, VLC_TRUE ) != VLC_SUCCESS )
    {
        CommonClose( p_this, p_sys );
        return VLC_EGENERIC;
    }
561

Gildas Bazin's avatar
Gildas Bazin committed
562 563 564
    /* Everything is ready. Let's rock baby */
    msg_Dbg( p_this, "Playing...");
    p_sys->p_control->Run();
565

Gildas Bazin's avatar
Gildas Bazin committed
566 567 568 569 570 571 572
    p_demux->pf_demux   = Demux;
    p_demux->pf_control = DemuxControl;
    p_demux->info.i_update = 0;
    p_demux->info.i_title = 0;
    p_demux->info.i_seekpoint = 0;

    for( i = 0; i < p_sys->i_streams; i++ )
573
    {
Gildas Bazin's avatar
Gildas Bazin committed
574 575
        dshow_stream_t *p_stream = p_sys->pp_streams[i];
        es_format_t fmt;
576

Gildas Bazin's avatar
Gildas Bazin committed
577 578 579
        if( p_stream->mt.majortype == MEDIATYPE_Video )
        {
            es_format_Init( &fmt, VIDEO_ES, p_stream->i_fourcc );
580

Gildas Bazin's avatar
Gildas Bazin committed
581 582 583
            fmt.video.i_width  = p_stream->header.video.bmiHeader.biWidth;
            fmt.video.i_height = p_stream->header.video.bmiHeader.biHeight;
            fmt.video.i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
584

Gildas Bazin's avatar
Gildas Bazin committed
585 586 587 588 589
            if( !p_stream->header.video.bmiHeader.biCompression )
            {
                /* RGB DIB are coded from bottom to top */
                fmt.video.i_height = (unsigned int)(-(int)fmt.video.i_height);
            }
590

Gildas Bazin's avatar
Gildas Bazin committed
591 592 593 594
            /* Setup rgb mask for RGB formats */
            if( p_stream->i_fourcc == VLC_FOURCC('R','V','2','4') )
            {
                /* This is in BGR format */
595 596 597
                fmt.video.i_bmask = 0x00ff0000;
                fmt.video.i_gmask = 0x0000ff00;
                fmt.video.i_rmask = 0x000000ff;
Gildas Bazin's avatar
Gildas Bazin committed
598
            }
599 600 601 602 603 604 605

            if( p_stream->header.video.AvgTimePerFrame )
            {
                fmt.video.i_frame_rate = 10000000;
                fmt.video.i_frame_rate_base =
                    p_stream->header.video.AvgTimePerFrame;
            }
Gildas Bazin's avatar
Gildas Bazin committed
606 607 608 609
        }
        else if( p_stream->mt.majortype == MEDIATYPE_Audio )
        {
            es_format_Init( &fmt, AUDIO_ES, p_stream->i_fourcc );
610

Gildas Bazin's avatar
Gildas Bazin committed
611 612 613 614 615 616 617 618 619 620
            fmt.audio.i_channels = p_stream->header.audio.nChannels;
            fmt.audio.i_rate = p_stream->header.audio.nSamplesPerSec;
            fmt.audio.i_bitspersample = p_stream->header.audio.wBitsPerSample;
            fmt.audio.i_blockalign = fmt.audio.i_channels *
                fmt.audio.i_bitspersample / 8;
            fmt.i_bitrate = fmt.audio.i_channels * fmt.audio.i_rate *
                fmt.audio.i_bitspersample;
        }

        p_stream->p_es = es_out_Add( p_demux->out, &fmt );
621
    }
Gildas Bazin's avatar
Gildas Bazin committed
622

623
    return VLC_SUCCESS;
624 625
}

Gildas Bazin's avatar
Gildas Bazin committed
626 627 628 629
/*****************************************************************************
 * AccessOpen: open direct show device as an access module
 *****************************************************************************/
static int AccessOpen( vlc_object_t *p_this )
630
{
Gildas Bazin's avatar
Gildas Bazin committed
631 632
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys;
633

Gildas Bazin's avatar
Gildas Bazin committed
634 635
    p_access->p_sys = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
    memset( p_sys, 0, sizeof( access_sys_t ) );
636

637
    if( CommonOpen( p_this, p_sys, VLC_FALSE ) != VLC_SUCCESS )
Gildas Bazin's avatar
Gildas Bazin committed
638 639 640 641
    {
        CommonClose( p_this, p_sys );
        return VLC_EGENERIC;
    }
642

Gildas Bazin's avatar
Gildas Bazin committed
643
    dshow_stream_t *p_stream = p_sys->pp_streams[0];
644

Gildas Bazin's avatar
Gildas Bazin committed
645 646
    /* Check if we need to force demuxers */
    if( !p_access->psz_demux || !*p_access->psz_demux )
Gildas Bazin's avatar
 
Gildas Bazin committed
647
    {
Gildas Bazin's avatar
Gildas Bazin committed
648 649 650
        if( p_stream->i_fourcc == VLC_FOURCC('d','v','s','l') ||
            p_stream->i_fourcc == VLC_FOURCC('d','v','s','d') ||
            p_stream->i_fourcc == VLC_FOURCC('d','v','h','d') )
651
        {
Gildas Bazin's avatar
Gildas Bazin committed
652 653 654 655 656
            p_access->psz_demux = strdup( "rawdv" );
        }
        else if( p_stream->i_fourcc == VLC_FOURCC('m','p','2','v') )
        {
            p_access->psz_demux = "mpgv";
657
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
658
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
659

Gildas Bazin's avatar
Gildas Bazin committed
660
    /* Setup Access */
661 662
    p_access->pf_read = NULL;
    p_access->pf_block = ReadCompressed;
Gildas Bazin's avatar
Gildas Bazin committed
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
    p_access->pf_control = AccessControl;
    p_access->pf_seek = NULL;
    p_access->info.i_update = 0;
    p_access->info.i_size = 0;
    p_access->info.i_pos = 0;
    p_access->info.b_eof = VLC_FALSE;
    p_access->info.i_title = 0;
    p_access->info.i_seekpoint = 0;
    p_access->p_sys = p_sys;

    /* Everything is ready. Let's rock baby */
    msg_Dbg( p_this, "Playing...");
    p_sys->p_control->Run();

    return VLC_SUCCESS;
678
}
Gildas Bazin's avatar
 
Gildas Bazin committed
679

Gildas Bazin's avatar
Gildas Bazin committed
680 681 682 683
/*****************************************************************************
 * CommonClose: close device
 *****************************************************************************/
static void CommonClose( vlc_object_t *p_this, access_sys_t *p_sys )
684
{
Clément Stenac's avatar
Clément Stenac committed
685
    msg_Dbg( p_this, "releasing DirectShow");
686

Gildas Bazin's avatar
Gildas Bazin committed
687
    DeleteDirectShowGraph( p_sys );
688

Gildas Bazin's avatar
Gildas Bazin committed
689 690
    /* Uninitialize OLE/COM */
    CoUninitialize();
691

692 693
    for( int i = 0; i < p_sys->i_streams; i++ ) delete p_sys->pp_streams[i];
    if( p_sys->i_streams ) free( p_sys->pp_streams );
Gildas Bazin's avatar
Gildas Bazin committed
694 695 696

    vlc_mutex_destroy( &p_sys->lock );
    vlc_cond_destroy( &p_sys->wait );
697

Gildas Bazin's avatar
Gildas Bazin committed
698
    free( p_sys );
699 700
}

Gildas Bazin's avatar
Gildas Bazin committed
701 702 703 704
/*****************************************************************************
 * AccessClose: close device
 *****************************************************************************/
static void AccessClose( vlc_object_t *p_this )
705
{
Gildas Bazin's avatar
Gildas Bazin committed
706 707
    access_t     *p_access = (access_t *)p_this;
    access_sys_t *p_sys    = p_access->p_sys;
708

Gildas Bazin's avatar
Gildas Bazin committed
709 710
    /* Stop capturing stuff */
    p_sys->p_control->Stop();
711

Gildas Bazin's avatar
Gildas Bazin committed
712 713
    CommonClose( p_this, p_sys );
}
714

Gildas Bazin's avatar
Gildas Bazin committed
715 716 717 718 719 720 721
/*****************************************************************************
 * DemuxClose: close device
 *****************************************************************************/
static void DemuxClose( vlc_object_t *p_this )
{
    demux_t      *p_demux = (demux_t *)p_this;
    access_sys_t *p_sys   = (access_sys_t *)p_demux->p_sys;
722

Gildas Bazin's avatar
Gildas Bazin committed
723 724
    /* Stop capturing stuff */
    p_sys->p_control->Stop();
725

Gildas Bazin's avatar
Gildas Bazin committed
726
    CommonClose( p_this, p_sys );
Gildas Bazin's avatar
 
Gildas Bazin committed
727 728 729 730 731
}

/****************************************************************************
 * ConnectFilters
 ****************************************************************************/
Gildas Bazin's avatar
Gildas Bazin committed
732 733
static bool ConnectFilters( vlc_object_t *p_this, access_sys_t *p_sys,
                            IBaseFilter *p_filter,
734
                            CaptureFilter *p_capture_filter )
Gildas Bazin's avatar
 
Gildas Bazin committed
735
{
736
    CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
Gildas Bazin's avatar
 
Gildas Bazin committed
737

738
    AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
Gildas Bazin's avatar
 
Gildas Bazin committed
739

740
    if( p_sys->p_capture_graph_builder2 )
Gildas Bazin's avatar
 
Gildas Bazin committed
741
    {
742
        if( FAILED(p_sys->p_capture_graph_builder2->
Gildas Bazin's avatar
Gildas Bazin committed
743 744
                RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
                              p_filter, 0, (IBaseFilter *)p_capture_filter )) )
745 746 747
        {
            return false;
        }
748

749 750 751
        // Sort out all the possible video inputs
        // The class needs to be given the capture filters ANALOGVIDEO input pin
        IEnumPins *pins = 0;
752 753
        if( ( mediaType.majortype == MEDIATYPE_Video ||
              mediaType.majortype == MEDIATYPE_Stream ) &&
754
            SUCCEEDED(p_filter->EnumPins(&pins)) )
Gildas Bazin's avatar
 
Gildas Bazin committed
755
        {
756 757 758 759 760 761 762 763
            IPin        *pP = 0;
            ULONG        n;
            PIN_INFO     pinInfo;
            BOOL         Found = FALSE;
            IKsPropertySet *pKs=0;
            GUID guid;
            DWORD dw;

Gildas Bazin's avatar
Gildas Bazin committed
764
            while( !Found && ( S_OK == pins->Next(1, &pP, &n) ) )
765
            {
Gildas Bazin's avatar
Gildas Bazin committed
766
                if( S_OK == pP->QueryPinInfo(&pinInfo) )
767
                {
Gildas Bazin's avatar
Gildas Bazin committed
768 769 770 771
                    // is this pin an ANALOGVIDEOIN input pin?
                    if( pinInfo.dir == PINDIR_INPUT &&
                        pP->QueryInterface( IID_IKsPropertySet,
                                            (void **)&pKs ) == S_OK )
772
                    {
Gildas Bazin's avatar
Gildas Bazin committed
773 774 775
                        if( pKs->Get( AMPROPSETID_Pin,
                                      AMPROPERTY_PIN_CATEGORY, NULL, 0,
                                      &guid, sizeof(GUID), &dw ) == S_OK )
776
                        {
Gildas Bazin's avatar
Gildas Bazin committed
777
                            if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
778
                            {
Gildas Bazin's avatar
Gildas Bazin committed
779
                                // recursively search crossbar routes
780
                                FindCrossbarRoutes( p_this, p_sys, pP, 0 );
Gildas Bazin's avatar
Gildas Bazin committed
781 782
                                // found it
                                Found = TRUE;
783 784
                            }
                        }
Gildas Bazin's avatar
Gildas Bazin committed
785
                        pKs->Release();
786 787 788 789 790 791
                    }
                    pinInfo.pFilter->Release();
                }
                pP->Release();
            }
            pins->Release();
Gildas Bazin's avatar
 
Gildas Bazin committed
792
        }
793
        return true;
Gildas Bazin's avatar
 
Gildas Bazin committed
794
    }
795 796 797 798
    else
    {
        IEnumPins *p_enumpins;
        IPin *p_pin;
Gildas Bazin's avatar
 
Gildas Bazin committed
799

800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
        if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;

        while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
        {
            PIN_DIRECTION pin_dir;
            p_pin->QueryDirection( &pin_dir );

            if( pin_dir == PINDIR_OUTPUT &&
                p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
                                               0 ) == S_OK )
            {
                p_pin->Release();
                p_enumpins->Release();
                return true;
            }
            p_pin->Release();
        }

        p_enumpins->Release();
        return false;
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
821 822
}

823
/*
Gildas Bazin's avatar
Gildas Bazin committed
824 825 826
 * get fourcc priority from arbritary preference, the higher the better
 */
static int GetFourCCPriority( int i_fourcc )
827 828 829
{
    switch( i_fourcc )
    {
Gildas Bazin's avatar
Gildas Bazin committed
830 831 832 833 834 835 836 837 838 839 840 841
    case VLC_FOURCC('I','4','2','0'):
    case VLC_FOURCC('f','l','3','2'):
        return 9;
    case VLC_FOURCC('Y','V','1','2'):
    case VLC_FOURCC('a','r','a','w'):
        return 8;
    case VLC_FOURCC('R','V','2','4'):
        return 7;
    case VLC_FOURCC('Y','U','Y','2'):
    case VLC_FOURCC('R','V','3','2'):
    case VLC_FOURCC('R','G','B','A'):
        return 6;
842
    }
Gildas Bazin's avatar
Gildas Bazin committed
843

844 845 846 847 848
    return 0;
}

#define MAX_MEDIA_TYPES 32

Gildas Bazin's avatar
Gildas Bazin committed
849 850
static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys,
                       string devicename, vlc_bool_t b_audio )
Gildas Bazin's avatar
 
Gildas Bazin committed
851
{
852 853 854
    /* See if device is already opened */
    for( int i = 0; i < p_sys->i_streams; i++ )
    {
Gildas Bazin's avatar
Gildas Bazin committed
855 856
        if( devicename.size() &&
            p_sys->pp_streams[i]->devicename == devicename )
857 858 859 860 861 862
        {
            /* Already opened */
            return VLC_SUCCESS;
        }
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
863 864
    list<string> list_devices;

865
    /* Enumerate devices and display their names */
Gildas Bazin's avatar
Gildas Bazin committed
866
    FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
Gildas Bazin's avatar
 
Gildas Bazin committed
867

868 869 870
    if( !list_devices.size() )
        return VLC_EGENERIC;

Gildas Bazin's avatar
 
Gildas Bazin committed
871 872
    list<string>::iterator iter;
    for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
Gildas Bazin's avatar
Gildas Bazin committed
873
        msg_Dbg( p_this, "found device: %s", iter->c_str() );
Gildas Bazin's avatar