]> git.sesse.net Git - vlc/blob - modules/access/dshow/dshow.cpp
Access/*: Second lecture (refs #438)
[vlc] / modules / access / dshow / dshow.cpp
1 /*****************************************************************************
2  * dshow.cpp : DirectShow access module for vlc
3  *****************************************************************************
4  * Copyright (C) 2002, 2003 the VideoLAN team
5  * $Id$
6  *
7  * Author: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include <vlc/vlc.h>
32 #include <vlc/input.h>
33 #include <vlc/vout.h>
34
35 #include "common.h"
36 #include "filter.h"
37
38 /*****************************************************************************
39  * Access: local prototypes
40  *****************************************************************************/
41 static block_t *ReadCompressed( access_t * );
42 static int AccessControl ( access_t *, int, va_list );
43
44 static int Demux       ( demux_t * );
45 static int DemuxControl( demux_t *, int, va_list );
46
47 static int OpenDevice( vlc_object_t *, access_sys_t *, string, vlc_bool_t );
48 static IBaseFilter *FindCaptureDevice( vlc_object_t *, string *,
49                                        list<string> *, vlc_bool_t );
50 static size_t EnumDeviceCaps( vlc_object_t *, IBaseFilter *,
51                               int, int, int, int, int, int,
52                               AM_MEDIA_TYPE *mt, size_t );
53 static bool ConnectFilters( vlc_object_t *, access_sys_t *,
54                             IBaseFilter *, CaptureFilter * );
55 static int FindDevicesCallback( vlc_object_t *, char const *,
56                                 vlc_value_t, vlc_value_t, void * );
57 static int ConfigDevicesCallback( vlc_object_t *, char const *,
58                                   vlc_value_t, vlc_value_t, void * );
59
60 static void ShowPropertyPage( IUnknown * );
61 static void ShowDeviceProperties( vlc_object_t *, ICaptureGraphBuilder2 *, 
62                                   IBaseFilter *, vlc_bool_t );
63 static void ShowTunerProperties( vlc_object_t *, ICaptureGraphBuilder2 *, 
64                                  IBaseFilter *, vlc_bool_t );
65 static void ConfigTuner( vlc_object_t *, ICaptureGraphBuilder2 *,
66                          IBaseFilter * );
67
68 /*****************************************************************************
69  * Module descriptor
70  *****************************************************************************/
71 static char *ppsz_vdev[] = { "", "none" };
72 static char *ppsz_vdev_text[] = { N_("Default"), N_("None") };
73 static char *ppsz_adev[] = { "", "none" };
74 static char *ppsz_adev_text[] = { N_("Default"), N_("None") };
75 static int  pi_tuner_input[] = { 0, 1, 2 };
76 static char *ppsz_tuner_input_text[] =
77     {N_("Default"), N_("Cable"), N_("Antenna")};
78
79 #define CACHING_TEXT N_("Caching value in ms")
80 #define CACHING_LONGTEXT N_( \
81     "Caching value for DirectShow streams. " \
82     "This value should be set in millisecondss." )
83 #define VDEV_TEXT N_("Video device name")
84 #define VDEV_LONGTEXT N_( \
85     "Name of the video device that will be used by the " \
86     "DirectShow plugin. If you don't specify anything, the default device " \
87     "will be used.")
88 #define ADEV_TEXT N_("Audio device name")
89 #define ADEV_LONGTEXT N_( \
90     "Name of the audio device that will be used by the " \
91     "DirectShow plugin. If you don't specify anything, the default device " \
92     "will be used. You can specify a standard size (cif, d1, ...) or <width>x<height>")
93 #define SIZE_TEXT N_("Video size")
94 #define SIZE_LONGTEXT N_( \
95     "Size of the video that will be displayed by the " \
96     "DirectShow plugin. If you don't specify anything the default size for " \
97     "your device will be used.")
98 #define CHROMA_TEXT N_("Video input chroma format")
99 #define CHROMA_LONGTEXT N_( \
100     "Force the DirectShow video input to use a specific chroma format " \
101     "(eg. I420 (default), RV24, etc.)")
102 #define FPS_TEXT N_("Video input frame rate")
103 #define FPS_LONGTEXT N_( \
104     "Force the DirectShow video input to use a specific frame rate" \
105     "(eg. 0 means default, 25, 29.97, 50, 59.94, etc.)"
106 #define CONFIG_TEXT N_("Device properties")
107 #define CONFIG_LONGTEXT N_( \
108     "Show the properties dialog of the selected device before starting the " \
109     "stream.")
110 #define TUNER_TEXT N_("Tuner properties")
111 #define TUNER_LONGTEXT N_( \
112     "Show the tuner properties [channel selection] page." )
113 #define CHANNEL_TEXT N_("Tuner TV Channel")
114 #define CHANNEL_LONGTEXT N_( \
115     "Set the TV channel the tuner will set to " \
116     "(0 means default)." )
117 #define COUNTRY_TEXT N_("Tuner country code")
118 #define COUNTRY_LONGTEXT N_( \
119     "Set the tuner country code that establishes the current " \
120     "channel-to-frequency mapping (0 means default)." )
121 #define TUNER_INPUT_TEXT N_("Tuner input type")
122 #define TUNER_INPUT_LONGTEXT N_( \
123     "Select the tuner input type (Cable/Antenna)." )
124 #define VIDEO_IN_TEXT N_("Video input pin")
125 #define VIDEO_IN_LONGTEXT N_( \
126   "Select the video input source, such as composite, s-video, " \
127   "or tuner. Since these settings are hardware-specfic, you should find good " \
128   "settings in the \"Device config\" area, and use those numbers here. -1 " \
129   "means that settings will not be changed.")
130 #define AUDIO_IN_TEXT N_("Audio input pin")
131 #define AUDIO_IN_LONGTEXT N_( \
132   "Select the audio input source. See the \"video input\" option." )
133 #define VIDEO_OUT_TEXT N_("Video output pin")
134 #define VIDEO_OUT_LONGTEXT N_( \
135   "Select the video output type. See the \"video input\" option." )
136 #define AUDIO_OUT_TEXT N_("Audio output pin")
137 #define AUDIO_OUT_LONGTEXT N_( \
138   "Select the audio output type. See the \"video input\" option." )
139
140 static int  CommonOpen ( vlc_object_t *, access_sys_t *, vlc_bool_t );
141 static void CommonClose( vlc_object_t *, access_sys_t * );
142
143 static int  AccessOpen ( vlc_object_t * );
144 static void AccessClose( vlc_object_t * );
145
146 static int  DemuxOpen  ( vlc_object_t * );
147 static void DemuxClose ( vlc_object_t * );
148
149 vlc_module_begin();
150     set_shortname( _("DirectShow") );
151     set_description( _("DirectShow input") );
152     set_category( CAT_INPUT );
153     set_subcategory( SUBCAT_INPUT_ACCESS );
154     add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL,
155                  CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
156
157     add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE);
158         change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback );
159         change_action_add( FindDevicesCallback, N_("Refresh list") );
160         change_action_add( ConfigDevicesCallback, N_("Configure") );
161
162     add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE);
163         change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback );
164         change_action_add( FindDevicesCallback, N_("Refresh list") );
165         change_action_add( ConfigDevicesCallback, N_("Configure") );
166
167     add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE);
168
169     add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
170                 VLC_TRUE );
171
172     add_float( "dshow-fps", 0.0f, NULL, FPS_TEXT, FPS_LONGTEXT,
173                 VLC_TRUE );
174
175     add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT,
176               VLC_TRUE );
177
178     add_bool( "dshow-tuner", VLC_FALSE, NULL, TUNER_TEXT, TUNER_LONGTEXT,
179               VLC_TRUE );
180
181     add_integer( "dshow-tuner-channel", 0, NULL, CHANNEL_TEXT,
182                  CHANNEL_LONGTEXT, VLC_TRUE );
183
184     add_integer( "dshow-tuner-country", 0, NULL, COUNTRY_TEXT,
185                  COUNTRY_LONGTEXT, VLC_TRUE );
186
187     add_integer( "dshow-tuner-input", 0, NULL, TUNER_INPUT_TEXT,
188                  TUNER_INPUT_LONGTEXT, VLC_TRUE );
189         change_integer_list( pi_tuner_input, ppsz_tuner_input_text, 0 );
190
191     add_integer( "dshow-video-input",  -1, NULL, VIDEO_IN_TEXT,
192                  VIDEO_IN_LONGTEXT, VLC_TRUE );
193
194     add_integer( "dshow-audio-input",  -1, NULL, AUDIO_IN_TEXT,
195                  AUDIO_IN_LONGTEXT, VLC_TRUE );
196
197     add_integer( "dshow-video-output", -1, NULL, VIDEO_OUT_TEXT,
198                  VIDEO_OUT_LONGTEXT, VLC_TRUE );
199
200     add_integer( "dshow-audio-output", -1, NULL, AUDIO_OUT_TEXT,
201                  AUDIO_OUT_LONGTEXT, VLC_TRUE );
202
203     add_shortcut( "dshow" );
204     set_capability( "access_demux", 0 );
205     set_callbacks( DemuxOpen, DemuxClose );
206
207     add_submodule();
208     set_description( _("DirectShow input") );
209     add_shortcut( "dshow" );
210     set_capability( "access2", 0 );
211     set_callbacks( AccessOpen, AccessClose );
212
213 vlc_module_end();
214
215 /*****************************************************************************
216  * DirectShow elementary stream descriptor
217  *****************************************************************************/
218 typedef struct dshow_stream_t
219 {
220     string          devicename;
221     IBaseFilter     *p_device_filter;
222     CaptureFilter   *p_capture_filter;
223     AM_MEDIA_TYPE   mt;
224
225     union
226     {
227       VIDEOINFOHEADER video;
228       WAVEFORMATEX    audio;
229
230     } header;
231
232     int             i_fourcc;
233     es_out_id_t     *p_es;
234
235     vlc_bool_t      b_pts;
236 } dshow_stream_t;
237
238 /*****************************************************************************
239  * DirectShow utility functions
240  *****************************************************************************/
241 static void CreateDirectShowGraph( access_sys_t *p_sys )
242 {
243     p_sys->i_crossbar_route_depth = 0;
244
245     /* Create directshow filter graph */
246     if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
247                        (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) )
248     {
249         /* Create directshow capture graph builder if available */
250         if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0,
251                          CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
252                          (void **)&p_sys->p_capture_graph_builder2 ) ) )
253         {
254             p_sys->p_capture_graph_builder2->
255                 SetFiltergraph((IGraphBuilder *)p_sys->p_graph);
256         }
257
258         p_sys->p_graph->QueryInterface( IID_IMediaControl,
259                                         (void **)&p_sys->p_control );
260     }
261 }
262
263 static void DeleteDirectShowGraph( access_sys_t *p_sys )
264 {
265     DeleteCrossbarRoutes( p_sys );
266
267     /* Remove filters from graph */
268     for( int i = 0; i < p_sys->i_streams; i++ )
269     {
270         p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter );
271         p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter );
272         p_sys->pp_streams[i]->p_capture_filter->Release();
273         p_sys->pp_streams[i]->p_device_filter->Release();
274     }
275
276     /* Release directshow objects */
277     if( p_sys->p_control )
278     {
279         p_sys->p_control->Release();
280         p_sys->p_control = NULL;
281     }
282     if( p_sys->p_capture_graph_builder2 )
283     {
284         p_sys->p_capture_graph_builder2->Release();
285         p_sys->p_capture_graph_builder2 = NULL;
286     }
287
288     if( p_sys->p_graph )
289     {
290         p_sys->p_graph->Release();
291         p_sys->p_graph = NULL;
292     }
293 }
294
295 /*****************************************************************************
296  * CommonOpen: open direct show device
297  *****************************************************************************/
298 static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys,
299                        vlc_bool_t b_access_demux )
300 {
301     vlc_value_t  val;
302     int i;
303
304     /* Get/parse options and open device(s) */
305     string vdevname, adevname;
306     int i_width = 0, i_height = 0, i_chroma = 0;
307     vlc_bool_t b_audio = VLC_TRUE;
308
309     var_Create( p_this, "dshow-config", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
310     var_Create( p_this, "dshow-tuner", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
311     var_Create( p_this, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
312     var_Get( p_this, "dshow-vdev", &val );
313     if( val.psz_string ) vdevname = string( val.psz_string );
314     if( val.psz_string ) free( val.psz_string );
315
316     var_Create( p_this, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
317     var_Get( p_this, "dshow-adev", &val );
318     if( val.psz_string ) adevname = string( val.psz_string );
319     if( val.psz_string ) free( val.psz_string );
320
321     static struct {char *psz_size; int  i_width; int  i_height;} size_table[] =
322     { { "subqcif", 128, 96 }, { "qsif", 160, 120 }, { "qcif", 176, 144 },
323       { "sif", 320, 240 }, { "cif", 352, 288 }, { "d1", 640, 480 },
324       { 0, 0, 0 },
325     };
326
327     var_Create( p_this, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
328     var_Get( p_this, "dshow-size", &val );
329     if( val.psz_string && *val.psz_string )
330     {
331         for( i = 0; size_table[i].psz_size; i++ )
332         {
333             if( !strcmp( val.psz_string, size_table[i].psz_size ) )
334             {
335                 i_width = size_table[i].i_width;
336                 i_height = size_table[i].i_height;
337                 break;
338             }
339         }
340         if( !size_table[i].psz_size ) /* Try to parse "WidthxHeight" */
341         {
342             char *psz_parser;
343             i_width = strtol( val.psz_string, &psz_parser, 0 );
344             if( *psz_parser == 'x' || *psz_parser == 'X')
345             {
346                 i_height = strtol( psz_parser + 1, &psz_parser, 0 );
347             }
348             msg_Dbg( p_this, "width x height %dx%d", i_width, i_height );
349         }
350     }
351     if( val.psz_string ) free( val.psz_string );
352
353     p_sys->b_chroma = VLC_FALSE;
354     var_Create( p_this, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
355     var_Get( p_this, "dshow-chroma", &val );
356     if( val.psz_string && strlen( val.psz_string ) >= 4 )
357     {
358         i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1],
359                                val.psz_string[2], val.psz_string[3] );
360         p_sys->b_chroma = VLC_TRUE;
361     }
362     if( val.psz_string ) free( val.psz_string );
363
364     var_Create( p_this, "dshow-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
365     var_Create( p_this, "dshow-tuner-channel",
366                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
367     var_Create( p_this, "dshow-tuner-country",
368                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
369     var_Create( p_this, "dshow-tuner-input",
370                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
371
372     var_Create( p_this, "dshow-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
373
374     var_Create( p_this, "dshow-video-input", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
375     var_Create( p_this, "dshow-audio-input", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
376     var_Create( p_this, "dshow-video-output", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
377     var_Create( p_this, "dshow-audio-output", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
378
379     /* Initialize OLE/COM */
380     CoInitialize( 0 );
381
382     /* Initialize some data */
383     p_sys->i_streams = 0;
384     p_sys->pp_streams = 0;
385     p_sys->i_width = i_width;
386     p_sys->i_height = i_height;
387     p_sys->i_chroma = i_chroma;
388
389     p_sys->p_graph = NULL;
390     p_sys->p_capture_graph_builder2 = NULL;
391     p_sys->p_control = NULL;
392
393     vlc_mutex_init( p_this, &p_sys->lock );
394     vlc_cond_init( p_this, &p_sys->wait );
395
396     /* Build directshow graph */
397     CreateDirectShowGraph( p_sys );
398
399     if( OpenDevice( p_this, p_sys, vdevname, 0 ) != VLC_SUCCESS )
400     {
401         msg_Err( p_this, "can't open video");
402     }
403     else
404     {
405         /* Check if we can handle the demuxing ourselves or need to spawn
406          * a demuxer module */
407         dshow_stream_t *p_stream = p_sys->pp_streams[p_sys->i_streams-1];
408
409         if( p_stream->mt.majortype == MEDIATYPE_Video )
410         {
411             if( /* Raw DV stream */
412                 p_stream->i_fourcc == VLC_FOURCC('d','v','s','l') ||
413                 p_stream->i_fourcc == VLC_FOURCC('d','v','s','d') ||
414                 p_stream->i_fourcc == VLC_FOURCC('d','v','h','d') ||
415                 /* Raw MPEG video stream */
416                 p_stream->i_fourcc == VLC_FOURCC('m','p','2','v') )
417             {
418                 b_audio = VLC_FALSE;
419
420                 if( b_access_demux )
421                 {
422                     /* Let the access (only) take care of that */
423                     return VLC_EGENERIC;
424                 }
425             }
426         }
427
428         if( p_stream->mt.majortype == MEDIATYPE_Stream )
429         {
430             b_audio = VLC_FALSE;
431
432             if( b_access_demux )
433             {
434                 /* Let the access (only) take care of that */
435                 return VLC_EGENERIC;
436             }
437
438             var_Get( p_this, "dshow-tuner", &val );
439             if( val.b_bool )
440             {
441                 /* FIXME: we do MEDIATYPE_Stream here so we don't do
442                  * it twice. */
443                 ShowTunerProperties( p_this, p_sys->p_capture_graph_builder2,
444                                      p_stream->p_device_filter, 0 );
445             }
446         }
447     }
448
449     if( b_audio && OpenDevice( p_this, p_sys, adevname, 1 ) != VLC_SUCCESS )
450     {
451         msg_Err( p_this, "can't open audio");
452     }
453
454     for( i = p_sys->i_crossbar_route_depth-1; i >= 0 ; --i )
455     {
456             var_Get( p_this, "dshow-video-input", &val );
457             if( val.i_int > 0 )
458                     p_sys->crossbar_routes[i].VideoInputIndex=val.i_int;
459             var_Get( p_this, "dshow-video-output", &val );
460             if( val.i_int > 0 )
461                     p_sys->crossbar_routes[i].VideoOutputIndex=val.i_int;
462             var_Get( p_this, "dshow-audio-input", &val );
463             if( val.i_int > 0 )
464                     p_sys->crossbar_routes[i].AudioInputIndex=val.i_int;
465             var_Get( p_this, "dshow-audio-output", &val );
466             if( val.i_int > 0 )
467                     p_sys->crossbar_routes[i].AudioOutputIndex=val.i_int;
468
469         IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
470         LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex;
471         LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex;
472         LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex;
473         LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex;
474
475         if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) )
476         {
477             msg_Dbg( p_this, "crossbar at depth %d, routed video "
478                      "output %ld to video input %ld", i, VideoOutputIndex,
479                      VideoInputIndex );
480
481             if( AudioOutputIndex != -1 && AudioInputIndex != -1 )
482             {
483                 if( SUCCEEDED( pXbar->Route(AudioOutputIndex,
484                                             AudioInputIndex)) )
485                 {
486                     msg_Dbg(p_this, "crossbar at depth %d, routed audio "
487                             "output %ld to audio input %ld", i,
488                             AudioOutputIndex, AudioInputIndex );
489                 }
490             }
491         }
492     }
493
494     /*
495     ** Show properties pages from other filters in graph
496     */
497     var_Get( p_this, "dshow-config", &val );
498     if( val.b_bool )
499     {
500         for( i = p_sys->i_crossbar_route_depth-1; i >= 0 ; --i )
501         {
502             IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
503             IBaseFilter *p_XF;
504
505             if( SUCCEEDED( pXbar->QueryInterface( IID_IBaseFilter,
506                                                   (void **)&p_XF ) ) )
507             {
508                 ShowPropertyPage( p_XF );
509                 p_XF->Release();
510             }
511         }
512     }
513
514     /* Initialize some data */
515     p_sys->i_current_stream = 0;
516
517     if( !p_sys->i_streams ) return VLC_EGENERIC;
518
519     return VLC_SUCCESS;
520 }
521
522 /*****************************************************************************
523  * DemuxOpen: open direct show device as an access_demux module
524  *****************************************************************************/
525 static int DemuxOpen( vlc_object_t *p_this )
526 {
527     demux_t      *p_demux = (demux_t *)p_this;
528     access_sys_t *p_sys;
529     int i;
530
531     p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
532     memset( p_sys, 0, sizeof( access_sys_t ) );
533     p_demux->p_sys = (demux_sys_t *)p_sys;
534
535     if( CommonOpen( p_this, p_sys, VLC_TRUE ) != VLC_SUCCESS )
536     {
537         CommonClose( p_this, p_sys );
538         return VLC_EGENERIC;
539     }
540
541     /* Everything is ready. Let's rock baby */
542     msg_Dbg( p_this, "Playing...");
543     p_sys->p_control->Run();
544
545     p_demux->pf_demux   = Demux;
546     p_demux->pf_control = DemuxControl;
547     p_demux->info.i_update = 0;
548     p_demux->info.i_title = 0;
549     p_demux->info.i_seekpoint = 0;
550
551     for( i = 0; i < p_sys->i_streams; i++ )
552     {
553         dshow_stream_t *p_stream = p_sys->pp_streams[i];
554         es_format_t fmt;
555
556         if( p_stream->mt.majortype == MEDIATYPE_Video )
557         {
558             es_format_Init( &fmt, VIDEO_ES, p_stream->i_fourcc );
559
560             fmt.video.i_width  = p_stream->header.video.bmiHeader.biWidth;
561             fmt.video.i_height = p_stream->header.video.bmiHeader.biHeight;
562             fmt.video.i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
563
564             if( !p_stream->header.video.bmiHeader.biCompression )
565             {
566                 /* RGB DIB are coded from bottom to top */
567                 fmt.video.i_height = (unsigned int)(-(int)fmt.video.i_height);
568             }
569
570             /* Setup rgb mask for RGB formats */
571             if( p_stream->i_fourcc == VLC_FOURCC('R','V','2','4') )
572             {
573                 /* This is in BGR format */
574                 fmt.video.i_bmask = 0x00ff0000;
575                 fmt.video.i_gmask = 0x0000ff00;
576                 fmt.video.i_rmask = 0x000000ff;
577             }
578
579             if( p_stream->header.video.AvgTimePerFrame )
580             {
581                 fmt.video.i_frame_rate = 10000000;
582                 fmt.video.i_frame_rate_base =
583                     p_stream->header.video.AvgTimePerFrame;
584             }
585         }
586         else if( p_stream->mt.majortype == MEDIATYPE_Audio )
587         {
588             es_format_Init( &fmt, AUDIO_ES, p_stream->i_fourcc );
589
590             fmt.audio.i_channels = p_stream->header.audio.nChannels;
591             fmt.audio.i_rate = p_stream->header.audio.nSamplesPerSec;
592             fmt.audio.i_bitspersample = p_stream->header.audio.wBitsPerSample;
593             fmt.audio.i_blockalign = fmt.audio.i_channels *
594                 fmt.audio.i_bitspersample / 8;
595             fmt.i_bitrate = fmt.audio.i_channels * fmt.audio.i_rate *
596                 fmt.audio.i_bitspersample;
597         }
598
599         p_stream->p_es = es_out_Add( p_demux->out, &fmt );
600     }
601
602     return VLC_SUCCESS;
603 }
604
605 /*****************************************************************************
606  * AccessOpen: open direct show device as an access module
607  *****************************************************************************/
608 static int AccessOpen( vlc_object_t *p_this )
609 {
610     access_t     *p_access = (access_t*)p_this;
611     access_sys_t *p_sys;
612
613     p_access->p_sys = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
614     memset( p_sys, 0, sizeof( access_sys_t ) );
615
616     if( CommonOpen( p_this, p_sys, VLC_FALSE ) != VLC_SUCCESS )
617     {
618         CommonClose( p_this, p_sys );
619         return VLC_EGENERIC;
620     }
621
622     dshow_stream_t *p_stream = p_sys->pp_streams[0];
623
624     /* Check if we need to force demuxers */
625     if( !p_access->psz_demux || !*p_access->psz_demux )
626     {
627         if( p_stream->i_fourcc == VLC_FOURCC('d','v','s','l') ||
628             p_stream->i_fourcc == VLC_FOURCC('d','v','s','d') ||
629             p_stream->i_fourcc == VLC_FOURCC('d','v','h','d') )
630         {
631             p_access->psz_demux = strdup( "rawdv" );
632         }
633         else if( p_stream->i_fourcc == VLC_FOURCC('m','p','2','v') )
634         {
635             p_access->psz_demux = "mpgv";
636         }
637     }
638
639     /* Setup Access */
640     p_access->pf_read = NULL;
641     p_access->pf_block = ReadCompressed;
642     p_access->pf_control = AccessControl;
643     p_access->pf_seek = NULL;
644     p_access->info.i_update = 0;
645     p_access->info.i_size = 0;
646     p_access->info.i_pos = 0;
647     p_access->info.b_eof = VLC_FALSE;
648     p_access->info.i_title = 0;
649     p_access->info.i_seekpoint = 0;
650     p_access->p_sys = p_sys;
651
652     /* Everything is ready. Let's rock baby */
653     msg_Dbg( p_this, "Playing...");
654     p_sys->p_control->Run();
655
656     return VLC_SUCCESS;
657 }
658
659 /*****************************************************************************
660  * CommonClose: close device
661  *****************************************************************************/
662 static void CommonClose( vlc_object_t *p_this, access_sys_t *p_sys )
663 {
664     msg_Dbg( p_this, "releasing DirectShow");
665
666     DeleteDirectShowGraph( p_sys );
667
668     /* Uninitialize OLE/COM */
669     CoUninitialize();
670
671     for( int i = 0; i < p_sys->i_streams; i++ ) delete p_sys->pp_streams[i];
672     if( p_sys->i_streams ) free( p_sys->pp_streams );
673
674     vlc_mutex_destroy( &p_sys->lock );
675     vlc_cond_destroy( &p_sys->wait );
676
677     free( p_sys );
678 }
679
680 /*****************************************************************************
681  * AccessClose: close device
682  *****************************************************************************/
683 static void AccessClose( vlc_object_t *p_this )
684 {
685     access_t     *p_access = (access_t *)p_this;
686     access_sys_t *p_sys    = p_access->p_sys;
687
688     /* Stop capturing stuff */
689     p_sys->p_control->Stop();
690
691     CommonClose( p_this, p_sys );
692 }
693
694 /*****************************************************************************
695  * DemuxClose: close device
696  *****************************************************************************/
697 static void DemuxClose( vlc_object_t *p_this )
698 {
699     demux_t      *p_demux = (demux_t *)p_this;
700     access_sys_t *p_sys   = (access_sys_t *)p_demux->p_sys;
701
702     /* Stop capturing stuff */
703     p_sys->p_control->Stop();
704
705     CommonClose( p_this, p_sys );
706 }
707
708 /****************************************************************************
709  * ConnectFilters
710  ****************************************************************************/
711 static bool ConnectFilters( vlc_object_t *p_this, access_sys_t *p_sys,
712                             IBaseFilter *p_filter,
713                             CaptureFilter *p_capture_filter )
714 {
715     CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
716
717     AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
718
719     if( p_sys->p_capture_graph_builder2 )
720     {
721         if( FAILED(p_sys->p_capture_graph_builder2->
722                 RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
723                               p_filter, 0, (IBaseFilter *)p_capture_filter )) )
724         {
725             return false;
726         }
727
728         // Sort out all the possible video inputs
729         // The class needs to be given the capture filters ANALOGVIDEO input pin
730         IEnumPins *pins = 0;
731         if( ( mediaType.majortype == MEDIATYPE_Video ||
732               mediaType.majortype == MEDIATYPE_Stream ) &&
733             SUCCEEDED(p_filter->EnumPins(&pins)) )
734         {
735             IPin        *pP = 0;
736             ULONG        n;
737             PIN_INFO     pinInfo;
738             BOOL         Found = FALSE;
739             IKsPropertySet *pKs=0;
740             GUID guid;
741             DWORD dw;
742
743             while( !Found && ( S_OK == pins->Next(1, &pP, &n) ) )
744             {
745                 if( S_OK == pP->QueryPinInfo(&pinInfo) )
746                 {
747                     // is this pin an ANALOGVIDEOIN input pin?
748                     if( pinInfo.dir == PINDIR_INPUT &&
749                         pP->QueryInterface( IID_IKsPropertySet,
750                                             (void **)&pKs ) == S_OK )
751                     {
752                         if( pKs->Get( AMPROPSETID_Pin,
753                                       AMPROPERTY_PIN_CATEGORY, NULL, 0,
754                                       &guid, sizeof(GUID), &dw ) == S_OK )
755                         {
756                             if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
757                             {
758                                 // recursively search crossbar routes
759                                 FindCrossbarRoutes( p_this, p_sys, pP, 0 );
760                                 // found it
761                                 Found = TRUE;
762                             }
763                         }
764                         pKs->Release();
765                     }
766                     pinInfo.pFilter->Release();
767                 }
768                 pP->Release();
769             }
770             pins->Release();
771         }
772         return true;
773     }
774     else
775     {
776         IEnumPins *p_enumpins;
777         IPin *p_pin;
778
779         if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
780
781         while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
782         {
783             PIN_DIRECTION pin_dir;
784             p_pin->QueryDirection( &pin_dir );
785
786             if( pin_dir == PINDIR_OUTPUT &&
787                 p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
788                                                0 ) == S_OK )
789             {
790                 p_pin->Release();
791                 p_enumpins->Release();
792                 return true;
793             }
794             p_pin->Release();
795         }
796
797         p_enumpins->Release();
798         return false;
799     }
800 }
801
802 /*
803  * get fourcc priority from arbritary preference, the higher the better
804  */
805 static int GetFourCCPriority( int i_fourcc )
806 {
807     switch( i_fourcc )
808     {
809     case VLC_FOURCC('I','4','2','0'):
810     case VLC_FOURCC('f','l','3','2'):
811         return 9;
812     case VLC_FOURCC('Y','V','1','2'):
813     case VLC_FOURCC('a','r','a','w'):
814         return 8;
815     case VLC_FOURCC('R','V','2','4'):
816         return 7;
817     case VLC_FOURCC('Y','U','Y','2'):
818     case VLC_FOURCC('R','V','3','2'):
819     case VLC_FOURCC('R','G','B','A'):
820         return 6;
821     }
822
823     return 0;
824 }
825
826 #define MAX_MEDIA_TYPES 32
827
828 static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys,
829                        string devicename, vlc_bool_t b_audio )
830 {
831     /* See if device is already opened */
832     for( int i = 0; i < p_sys->i_streams; i++ )
833     {
834         if( devicename.size() &&
835             p_sys->pp_streams[i]->devicename == devicename )
836         {
837             /* Already opened */
838             return VLC_SUCCESS;
839         }
840     }
841
842     list<string> list_devices;
843
844     /* Enumerate devices and display their names */
845     FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
846
847     if( !list_devices.size() )
848         return VLC_EGENERIC;
849
850     list<string>::iterator iter;
851     for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
852         msg_Dbg( p_this, "found device: %s", iter->c_str() );
853
854     /* If no device name was specified, pick the 1st one */
855     if( devicename.size() == 0 )
856     {
857         devicename = *list_devices.begin();
858     }
859
860     // Use the system device enumerator and class enumerator to find
861     // a capture/preview device, such as a desktop USB video camera.
862     IBaseFilter *p_device_filter =
863         FindCaptureDevice( p_this, &devicename, 0, b_audio );
864     if( p_device_filter )
865         msg_Dbg( p_this, "using device: %s", devicename.c_str() );
866     else
867     {
868         msg_Err( p_this, "can't use device: %s, unsupported device type",
869                  devicename.c_str() );
870         return VLC_EGENERIC;
871     }
872
873     // Retreive acceptable media types supported by device
874     AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
875     size_t media_count =
876         EnumDeviceCaps( p_this, p_device_filter, p_sys->i_chroma,
877                         p_sys->i_width, p_sys->i_height,
878                         0, 0, 0, media_types, MAX_MEDIA_TYPES );
879
880     AM_MEDIA_TYPE *mt = NULL;
881
882     if( media_count > 0 )
883     {
884         mt = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE) * media_count);
885
886         // Order and copy returned media types according to arbitrary
887         // fourcc priority
888         for( size_t c = 0; c < media_count; c++ )
889         {
890             int slot_priority =
891                 GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
892             size_t slot_copy = c;
893             for( size_t d = c+1; d < media_count; d++ )
894             {
895                 int priority =
896                     GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
897                 if( priority > slot_priority )
898                 {
899                     slot_priority = priority;
900                     slot_copy = d;
901                 }
902             }
903             if( slot_copy != c )
904             {
905                 mt[c] = media_types[slot_copy];
906                 media_types[slot_copy] = media_types[c];
907             }
908             else
909             {
910                 mt[c] = media_types[c];
911             }
912         }
913     }
914     else {
915         /* capture device */
916         msg_Err( p_this, "capture device '%s' does not support required parameters !", devicename.c_str() );
917         p_device_filter->Release();
918         return VLC_EGENERIC;
919     }
920
921     /* Create and add our capture filter */
922     CaptureFilter *p_capture_filter =
923         new CaptureFilter( p_this, p_sys, mt, media_count );
924     p_sys->p_graph->AddFilter( p_capture_filter, 0 );
925
926     /* Add the device filter to the graph (seems necessary with VfW before
927      * accessing pin attributes). */
928     p_sys->p_graph->AddFilter( p_device_filter, 0 );
929
930     /* Attempt to connect one of this device's capture output pins */
931     msg_Dbg( p_this, "connecting filters" );
932     if( ConnectFilters( p_this, p_sys, p_device_filter, p_capture_filter ) )
933     {
934         /* Success */
935         msg_Dbg( p_this, "filters connected successfully !" );
936
937         dshow_stream_t dshow_stream;
938         dshow_stream.b_pts = VLC_FALSE;
939         dshow_stream.p_es = 0;
940         dshow_stream.mt =
941             p_capture_filter->CustomGetPin()->CustomGetMediaType();
942
943         /* Show Device properties. Done here so the VLC stream is setup with
944          * the proper parameters. */
945         vlc_value_t val;
946         var_Get( p_this, "dshow-config", &val );
947         if( val.b_bool )
948         {
949             ShowDeviceProperties( p_this, p_sys->p_capture_graph_builder2,
950                                   p_device_filter, b_audio );
951         }
952
953         ConfigTuner( p_this, p_sys->p_capture_graph_builder2,
954                      p_device_filter );
955
956         var_Get( p_this, "dshow-tuner", &val );
957         if( val.b_bool && dshow_stream.mt.majortype != MEDIATYPE_Stream )
958         {
959             /* FIXME: we do MEDIATYPE_Stream later so we don't do it twice. */
960             ShowTunerProperties( p_this, p_sys->p_capture_graph_builder2,
961                                  p_device_filter, b_audio );
962         }
963
964         dshow_stream.mt =
965             p_capture_filter->CustomGetPin()->CustomGetMediaType();
966
967         dshow_stream.i_fourcc = GetFourCCFromMediaType( dshow_stream.mt );
968         if( dshow_stream.i_fourcc )
969         {
970             if( dshow_stream.mt.majortype == MEDIATYPE_Video )
971             {
972                 dshow_stream.header.video =
973                     *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
974                 msg_Dbg( p_this, "MEDIATYPE_Video" );
975                 msg_Dbg( p_this, "selected video pin accepts format: %4.4s",
976                          (char *)&dshow_stream.i_fourcc);
977             }
978             else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
979             {
980                 dshow_stream.header.audio =
981                     *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
982                 msg_Dbg( p_this, "MEDIATYPE_Audio" );
983                 msg_Dbg( p_this, "selected audio pin accepts format: %4.4s",
984                          (char *)&dshow_stream.i_fourcc);
985             }
986             else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
987             {
988                 msg_Dbg( p_this, "MEDIATYPE_Stream" );
989                 msg_Dbg( p_this, "selected stream pin accepts format: %4.4s",
990                          (char *)&dshow_stream.i_fourcc);
991             }
992             else
993             {
994                 msg_Dbg( p_this, "unknown stream majortype" );
995                 goto fail;
996             }
997
998             /* Add directshow elementary stream to our list */
999             dshow_stream.p_device_filter = p_device_filter;
1000             dshow_stream.p_capture_filter = p_capture_filter;
1001
1002             p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams,
1003                 sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) );
1004             p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1005             *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1006
1007             return VLC_SUCCESS;
1008         }
1009     }
1010
1011  fail:
1012     /* Remove filters from graph */
1013     p_sys->p_graph->RemoveFilter( p_device_filter );
1014     p_sys->p_graph->RemoveFilter( p_capture_filter );
1015
1016     /* Release objects */
1017     p_device_filter->Release();
1018     p_capture_filter->Release();
1019
1020     return VLC_EGENERIC;
1021 }
1022
1023 static IBaseFilter *
1024 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1025                    list<string> *p_listdevices, vlc_bool_t b_audio )
1026 {
1027     IBaseFilter *p_base_filter = NULL;
1028     IMoniker *p_moniker = NULL;
1029     ULONG i_fetched;
1030     HRESULT hr;
1031
1032     /* Create the system device enumerator */
1033     ICreateDevEnum *p_dev_enum = NULL;
1034
1035     hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1036                            IID_ICreateDevEnum, (void **)&p_dev_enum );
1037     if( FAILED(hr) )
1038     {
1039         msg_Err( p_this, "failed to create the device enumerator (0x%lx)", hr);
1040         return NULL;
1041     }
1042
1043     /* Create an enumerator for the video capture devices */
1044     IEnumMoniker *p_class_enum = NULL;
1045     if( !b_audio )
1046         hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1047                                                 &p_class_enum, 0 );
1048     else
1049         hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1050                                                 &p_class_enum, 0 );
1051     p_dev_enum->Release();
1052     if( FAILED(hr) )
1053     {
1054         msg_Err( p_this, "failed to create the class enumerator (0x%lx)", hr );
1055         return NULL;
1056     }
1057
1058     /* If there are no enumerators for the requested type, then
1059      * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1060     if( p_class_enum == NULL )
1061     {
1062         msg_Err( p_this, "no capture device was detected" );
1063         return NULL;
1064     }
1065
1066     /* Enumerate the devices */
1067
1068     /* Note that if the Next() call succeeds but there are no monikers,
1069      * it will return S_FALSE (which is not a failure). Therefore, we check
1070      * that the return code is S_OK instead of using SUCCEEDED() macro. */
1071
1072     while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1073     {
1074         /* Getting the property page to get the device name */
1075         IPropertyBag *p_bag;
1076         hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1077                                        (void **)&p_bag );
1078         if( SUCCEEDED(hr) )
1079         {
1080             VARIANT var;
1081             var.vt = VT_BSTR;
1082             hr = p_bag->Read( L"FriendlyName", &var, NULL );
1083             p_bag->Release();
1084             if( SUCCEEDED(hr) )
1085             {
1086                 int i_convert = WideCharToMultiByte(CP_ACP, 0, var.bstrVal,
1087                         SysStringLen(var.bstrVal), NULL, 0, NULL, NULL);
1088                 char *p_buf = (char *)alloca( i_convert+1 ); p_buf[0] = 0;
1089                 WideCharToMultiByte( CP_ACP, 0, var.bstrVal,
1090                         SysStringLen(var.bstrVal), p_buf, i_convert, NULL, NULL );
1091                 SysFreeString(var.bstrVal);
1092                 p_buf[i_convert] = '\0';
1093
1094                 if( p_listdevices ) p_listdevices->push_back( p_buf );
1095
1096                 if( p_devicename && *p_devicename == string(p_buf) )
1097                 {
1098                     /* Bind Moniker to a filter object */
1099                     hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1100                                                   (void **)&p_base_filter );
1101                     if( FAILED(hr) )
1102                     {
1103                         msg_Err( p_this, "couldn't bind moniker to filter "
1104                                  "object (0x%lx)", hr );
1105                         p_moniker->Release();
1106                         p_class_enum->Release();
1107                         return NULL;
1108                     }
1109                     p_moniker->Release();
1110                     p_class_enum->Release();
1111                     return p_base_filter;
1112                 }
1113             }
1114         }
1115
1116         p_moniker->Release();
1117     }
1118
1119     p_class_enum->Release();
1120     return NULL;
1121 }
1122
1123 static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter,
1124                               int i_fourcc, int i_width, int i_height,
1125                               int i_channels, int i_samplespersec,
1126                               int i_bitspersample, AM_MEDIA_TYPE *mt,
1127                               size_t mt_max )
1128 {
1129     IEnumPins *p_enumpins;
1130     IPin *p_output_pin;
1131     IEnumMediaTypes *p_enummt;
1132     size_t mt_count = 0;
1133
1134     LONGLONG i_AvgTimePerFrame = 0;
1135     float r_fps = var_GetFloat( p_this, "dshow-fps" );
1136     if( r_fps )
1137         i_AvgTimePerFrame = 10000000000LL/(LONGLONG)(r_fps*1000.0f);
1138
1139     if( FAILED(p_filter->EnumPins( &p_enumpins )) )
1140     {
1141         msg_Dbg( p_this, "EnumDeviceCaps failed: no pin enumeration !");
1142         return 0;
1143     }
1144
1145     while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1146     {
1147         PIN_INFO info;
1148
1149         if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1150         {
1151             msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1152                      info.dir == PINDIR_INPUT ? "input" : "output",
1153                      info.achName );
1154             if( info.pFilter ) info.pFilter->Release();
1155         }
1156
1157         p_output_pin->Release();
1158     }
1159
1160     p_enumpins->Reset();
1161
1162     while( !mt_count && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1163     {
1164         PIN_INFO info;
1165
1166         if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1167         {
1168             if( info.pFilter ) info.pFilter->Release();
1169             if( info.dir == PINDIR_INPUT )
1170             {
1171                 p_output_pin->Release();
1172                 continue;
1173             }
1174             msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1175         }
1176
1177         AM_MEDIA_TYPE *p_mt;
1178
1179         /*
1180         ** Configure pin with a default compatible media if possible
1181         */
1182
1183         IAMStreamConfig *pSC;
1184         if( SUCCEEDED(p_output_pin->QueryInterface( IID_IAMStreamConfig,
1185                                             (void **)&pSC )) )
1186         {
1187             int piCount, piSize;
1188             if( SUCCEEDED(pSC->GetNumberOfCapabilities(&piCount, &piSize)) )
1189             {
1190                 BYTE *pSCC= (BYTE *)CoTaskMemAlloc(piSize);
1191                 if( NULL != pSCC )
1192                 {
1193                     int i_priority = -1;
1194                     for( int i=0; i<piCount; ++i )
1195                     {
1196                         if( SUCCEEDED(pSC->GetStreamCaps(i, &p_mt, pSCC)) )
1197                         {
1198                             int i_current_fourcc = GetFourCCFromMediaType( *p_mt );
1199                             int i_current_priority = GetFourCCPriority(i_current_fourcc);
1200
1201                             if( !i_current_fourcc
1202                              || (i_fourcc && (i_current_fourcc != i_fourcc))
1203                              || (i_priority > i_current_priority) )
1204                             {
1205                                 // incompatible or unwanted chroma, try next media type
1206                                 FreeMediaType( *p_mt );
1207                                 CoTaskMemFree( (PVOID)p_mt );
1208                                 continue;
1209                             }
1210
1211                             if( MEDIATYPE_Video == p_mt->majortype
1212                                     && FORMAT_VideoInfo == p_mt->formattype )
1213                             {
1214                                 VIDEO_STREAM_CONFIG_CAPS *pVSCC = reinterpret_cast<VIDEO_STREAM_CONFIG_CAPS*>(pSCC);
1215                                 VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>(p_mt->pbFormat);
1216
1217                                 if( i_AvgTimePerFrame )
1218                                 {
1219                                     if( pVSCC->MinFrameInterval > i_AvgTimePerFrame
1220                                       || i_AvgTimePerFrame > pVSCC->MaxFrameInterval )
1221                                     {
1222                                         // required frame rate not compatible, try next media type
1223                                         FreeMediaType( *p_mt );
1224                                         CoTaskMemFree( (PVOID)p_mt );
1225                                         continue;
1226                                     }
1227                                     pVih->AvgTimePerFrame = i_AvgTimePerFrame;
1228                                 }
1229
1230                                 if( i_width )
1231                                 {
1232                                     if( i_width % pVSCC->OutputGranularityX
1233                                      || pVSCC->MinOutputSize.cx > i_width
1234                                      || i_width > pVSCC->MaxOutputSize.cx )
1235                                     {
1236                                         // required width not compatible, try next media type
1237                                         FreeMediaType( *p_mt );
1238                                         CoTaskMemFree( (PVOID)p_mt );
1239                                         continue;
1240                                     }
1241                                     pVih->bmiHeader.biWidth = i_width;
1242                                 }
1243
1244                                 if( i_height )
1245                                 {
1246                                     if( i_height % pVSCC->OutputGranularityY
1247                                      || pVSCC->MinOutputSize.cy > i_height 
1248                                      || i_height > pVSCC->MaxOutputSize.cy )
1249                                     {
1250                                         // required height not compatible, try next media type
1251                                         FreeMediaType( *p_mt );
1252                                         CoTaskMemFree( (PVOID)p_mt );
1253                                         continue;
1254                                     }
1255                                     pVih->bmiHeader.biHeight = i_height;
1256                                 }
1257
1258                                 // Set the sample size and image size.
1259                                 // (Round the image width up to a DWORD boundary.)
1260                                 p_mt->lSampleSize = pVih->bmiHeader.biSizeImage = 
1261                                     ((pVih->bmiHeader.biWidth + 3) & ~3) *
1262                                     pVih->bmiHeader.biHeight * (pVih->bmiHeader.biBitCount>>3);
1263
1264                                 // no cropping, use full video input buffer
1265                                 memset(&(pVih->rcSource), 0, sizeof(RECT));
1266                                 memset(&(pVih->rcTarget), 0, sizeof(RECT));
1267
1268                                 // select this format as default
1269                                 if( SUCCEEDED( pSC->SetFormat(p_mt) ) )
1270                                 {
1271                                     i_priority = i_current_priority;
1272                                     if( i_fourcc )
1273                                         // no need to check any more media types 
1274                                         i = piCount;
1275                                 }
1276                             }
1277                             else if( p_mt->majortype == MEDIATYPE_Audio
1278                                     && p_mt->formattype == FORMAT_WaveFormatEx )
1279                             {
1280                                 AUDIO_STREAM_CONFIG_CAPS *pASCC = reinterpret_cast<AUDIO_STREAM_CONFIG_CAPS*>(pSCC);
1281                                 WAVEFORMATEX *pWfx = reinterpret_cast<WAVEFORMATEX*>(p_mt->pbFormat);
1282
1283                                 if( i_channels )
1284                                 {
1285                                     if( i_channels % pASCC->ChannelsGranularity
1286                                      || (unsigned int)i_channels < pASCC->MinimumChannels
1287                                      || (unsigned int)i_channels > pASCC->MaximumChannels )
1288                                     {
1289                                         // required channels not compatible, try next media type
1290                                         FreeMediaType( *p_mt );
1291                                         CoTaskMemFree( (PVOID)p_mt );
1292                                         continue;
1293                                     }
1294                                     pWfx->nChannels = i_channels;
1295                                 }
1296
1297                                 if( i_samplespersec )
1298                                 {
1299                                     if( i_samplespersec % pASCC->BitsPerSampleGranularity
1300                                      || (unsigned int)i_samplespersec < pASCC->MinimumSampleFrequency
1301                                      || (unsigned int)i_samplespersec > pASCC->MaximumSampleFrequency )
1302                                     {
1303                                         // required sampling rate not compatible, try next media type
1304                                         FreeMediaType( *p_mt );
1305                                         CoTaskMemFree( (PVOID)p_mt );
1306                                         continue;
1307                                     }
1308                                     pWfx->nSamplesPerSec = i_samplespersec;
1309                                 }
1310
1311                                 if( i_bitspersample )
1312                                 {
1313                                     if( i_bitspersample % pASCC->BitsPerSampleGranularity
1314                                      || (unsigned int)i_bitspersample < pASCC->MinimumBitsPerSample
1315                                      || (unsigned int)i_bitspersample > pASCC->MaximumBitsPerSample )
1316                                     {
1317                                         // required sample size not compatible, try next media type
1318                                         FreeMediaType( *p_mt );
1319                                         CoTaskMemFree( (PVOID)p_mt );
1320                                         continue;
1321                                     }
1322                                     pWfx->wBitsPerSample = i_bitspersample;
1323                                 }
1324
1325                                 // select this format as default
1326                                 if( SUCCEEDED( pSC->SetFormat(p_mt) ) )
1327                                 {
1328                                     i_priority = i_current_priority;
1329                                     if( i_fourcc )
1330                                         // no need to check any more media types 
1331                                         i = piCount;
1332                                 }
1333                             }
1334                             FreeMediaType( *p_mt );
1335                             CoTaskMemFree( (PVOID)p_mt );
1336                         }
1337                     }
1338                     CoTaskMemFree( (LPVOID)pSCC );
1339                     if( i_priority >= 0 )
1340                         msg_Dbg( p_this, "EnumDeviceCaps: input pin default format configured");
1341                 }
1342             }
1343             pSC->Release();
1344         }
1345
1346         /*
1347         ** Probe pin for available medias (may be a previously configured one)
1348         */
1349
1350         if( FAILED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1351         {
1352             p_output_pin->Release();
1353             continue;
1354         }
1355
1356         while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1357         {
1358             int i_current_fourcc = GetFourCCFromMediaType( *p_mt );
1359             if( i_current_fourcc && p_mt->majortype == MEDIATYPE_Video
1360                 && p_mt->formattype == FORMAT_VideoInfo )
1361             {
1362                 int i_current_width = ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth;
1363                 int i_current_height = ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight;
1364                 LONGLONG i_current_atpf = ((VIDEOINFOHEADER *)p_mt->pbFormat)->AvgTimePerFrame;
1365
1366                 if( i_current_height < 0 )
1367                     i_current_height = -i_current_height; 
1368
1369                 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1370                          "accepts chroma: %4.4s, width:%i, height:%i, fps:%f",
1371                          (char *)&i_current_fourcc, i_current_width,
1372                          i_current_height, (10000000.0f/((float)i_current_atpf)) );
1373
1374                 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1375                     ( !i_width || i_width == i_current_width ) &&
1376                     ( !i_height || i_height == i_current_height ) &&
1377                     ( !i_AvgTimePerFrame || i_AvgTimePerFrame == i_current_atpf ) &&
1378                     mt_count < mt_max )
1379                 {
1380                     /* Pick match */
1381                     mt[mt_count++] = *p_mt;
1382                 }
1383                 else FreeMediaType( *p_mt );
1384             }
1385             else if( i_current_fourcc && p_mt->majortype == MEDIATYPE_Audio 
1386                     && p_mt->formattype == FORMAT_WaveFormatEx)
1387             {
1388                 int i_current_channels =
1389                     ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1390                 int i_current_samplespersec =
1391                     ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1392                 int i_current_bitspersample =
1393                     ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1394
1395                 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1396                          "accepts format: %4.4s, channels:%i, "
1397                          "samples/sec:%i bits/sample:%i",
1398                          (char *)&i_current_fourcc, i_current_channels,
1399                          i_current_samplespersec, i_current_bitspersample);
1400
1401                 if( (!i_channels || i_channels == i_current_channels) &&
1402                     (!i_samplespersec ||
1403                      i_samplespersec == i_current_samplespersec) &&
1404                     (!i_bitspersample ||
1405                      i_bitspersample == i_current_bitspersample) &&
1406                     mt_count < mt_max )
1407                 {
1408                     /* Pick  match */
1409                     mt[mt_count++] = *p_mt;
1410
1411                     /* Setup a few properties like the audio latency */
1412                     IAMBufferNegotiation *p_ambuf;
1413                     if( SUCCEEDED( p_output_pin->QueryInterface(
1414                           IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1415                     {
1416                         ALLOCATOR_PROPERTIES AllocProp;
1417                         AllocProp.cbAlign = -1;
1418
1419                         /* 100 ms of latency */
1420                         AllocProp.cbBuffer = i_current_channels *
1421                           i_current_samplespersec *
1422                           i_current_bitspersample / 8 / 10;
1423
1424                         AllocProp.cbPrefix = -1;
1425                         AllocProp.cBuffers = -1;
1426                         p_ambuf->SuggestAllocatorProperties( &AllocProp );
1427                         p_ambuf->Release();
1428                     }
1429                 }
1430                 else FreeMediaType( *p_mt );
1431             }
1432             else if( i_current_fourcc && p_mt->majortype == MEDIATYPE_Stream )
1433             {
1434                 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1435                          "accepts stream format: %4.4s",
1436                          (char *)&i_current_fourcc );
1437
1438                 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1439                     mt_count < mt_max )
1440                 {
1441                     /* Pick match */
1442                     mt[mt_count++] = *p_mt;
1443                     i_fourcc = i_current_fourcc;
1444                 }
1445                 else FreeMediaType( *p_mt );
1446             }
1447             else
1448             {
1449                 char *psz_type = "unknown";
1450                 if( p_mt->majortype == MEDIATYPE_Video ) psz_type = "video";
1451                 if( p_mt->majortype == MEDIATYPE_Audio ) psz_type = "audio";
1452                 if( p_mt->majortype == MEDIATYPE_Stream ) psz_type = "stream";
1453                 msg_Dbg( p_this, "EnumDeviceCaps: input pin media: unknown format "
1454                          "(%s %4.4s)", psz_type, (char *)&p_mt->subtype );
1455                 FreeMediaType( *p_mt );
1456             }
1457             CoTaskMemFree( (PVOID)p_mt );
1458         }
1459
1460         p_enummt->Release();
1461         p_output_pin->Release();
1462     }
1463
1464     p_enumpins->Release();
1465     return mt_count;
1466 }
1467
1468 /*****************************************************************************
1469  * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1470  *****************************************************************************
1471  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1472  * bytes.
1473  *****************************************************************************/
1474 static block_t *ReadCompressed( access_t *p_access )
1475 {
1476     access_sys_t   *p_sys = p_access->p_sys;
1477     dshow_stream_t *p_stream = NULL;
1478     VLCMediaSample sample;
1479
1480     /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1481
1482     /* There must be only 1 elementary stream to produce a valid stream
1483      * of MPEG or DV data */
1484     p_stream = p_sys->pp_streams[0];
1485
1486     while( 1 )
1487     {
1488         if( p_access->b_die || p_access->b_error ) return 0;
1489
1490         /* Get new sample/frame from the elementary stream (blocking). */
1491         vlc_mutex_lock( &p_sys->lock );
1492
1493         if( p_stream->p_capture_filter->CustomGetPin()
1494               ->CustomGetSample( &sample ) != S_OK )
1495         {
1496             /* No data available. Wait until some data has arrived */
1497             vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1498             vlc_mutex_unlock( &p_sys->lock );
1499             continue;
1500         }
1501
1502         vlc_mutex_unlock( &p_sys->lock );
1503
1504         /*
1505          * We got our sample
1506          */
1507         block_t *p_block;
1508         uint8_t *p_data;
1509         int i_data_size = sample.p_sample->GetActualDataLength();
1510
1511         if( !i_data_size || !(p_block = block_New( p_access, i_data_size )) )
1512         {
1513             sample.p_sample->Release();
1514             continue;
1515         }
1516
1517         sample.p_sample->GetPointer( &p_data );
1518         p_access->p_vlc->pf_memcpy( p_block->p_buffer, p_data, i_data_size );
1519         sample.p_sample->Release();
1520
1521         /* The caller got what he wanted */
1522         return p_block;
1523     }
1524
1525     return 0; /* never reached */
1526 }
1527
1528 /****************************************************************************
1529  * Demux:
1530  ****************************************************************************/
1531 static int Demux( demux_t *p_demux )
1532 {
1533     access_sys_t *p_sys = (access_sys_t *)p_demux->p_sys;
1534     dshow_stream_t *p_stream = NULL;
1535     VLCMediaSample sample;
1536     int i_data_size, i_stream;
1537     uint8_t *p_data;
1538     block_t *p_block;
1539
1540     vlc_mutex_lock( &p_sys->lock );
1541
1542     /* Try to grab an audio sample (audio has a higher priority) */
1543     for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1544     {
1545         p_stream = p_sys->pp_streams[i_stream];
1546         if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1547             p_stream->p_capture_filter &&
1548             p_stream->p_capture_filter->CustomGetPin()
1549               ->CustomGetSample( &sample ) == S_OK )
1550         {
1551             break;
1552         }
1553     }
1554     /* Try to grab a video sample */
1555     if( i_stream == p_sys->i_streams )
1556     {
1557         for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1558         {
1559             p_stream = p_sys->pp_streams[i_stream];
1560             if( p_stream->p_capture_filter &&
1561                 p_stream->p_capture_filter->CustomGetPin()
1562                     ->CustomGetSample( &sample ) == S_OK )
1563             {
1564                 break;
1565             }
1566         }
1567     }
1568
1569     vlc_mutex_unlock( &p_sys->lock );
1570
1571     if( i_stream == p_sys->i_streams )
1572     {
1573         /* Sleep so we do not consume all the cpu, 10ms seems
1574          * like a good value (100fps) */
1575         msleep( 10000 );
1576         return 1;
1577     }
1578
1579     /*
1580      * We got our sample
1581      */
1582     i_data_size = sample.p_sample->GetActualDataLength();
1583     sample.p_sample->GetPointer( &p_data );
1584
1585     REFERENCE_TIME i_pts, i_end_date;
1586     HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1587     if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1588
1589     if( !i_pts )
1590     {
1591         if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1592         {
1593             /* Use our data timestamp */
1594             i_pts = sample.i_timestamp;
1595             p_stream->b_pts = VLC_TRUE;
1596         }
1597     }
1598
1599     i_pts /= 10; /* Dshow works with 100 nano-seconds resolution */
1600
1601 #if 0
1602     msg_Dbg( p_demux, "Read() stream: %i, size: %i, PTS: "I64Fd,
1603              i_stream, i_data_size, i_pts );
1604 #endif
1605
1606     p_block = block_New( p_demux, i_data_size );
1607     p_demux->p_vlc->pf_memcpy( p_block->p_buffer, p_data, i_data_size );
1608     p_block->i_pts = p_block->i_dts = i_pts;
1609     sample.p_sample->Release();
1610
1611     es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pts > 0 ? i_pts : 0 );
1612     es_out_Send( p_demux->out, p_stream->p_es, p_block );
1613
1614     return 1;
1615 }
1616
1617 /*****************************************************************************
1618  * AccessControl:
1619  *****************************************************************************/
1620 static int AccessControl( access_t *p_access, int i_query, va_list args )
1621 {
1622     vlc_bool_t   *pb_bool;
1623     int          *pi_int;
1624     int64_t      *pi_64;
1625
1626     switch( i_query )
1627     {
1628     /* */
1629     case ACCESS_CAN_SEEK:
1630     case ACCESS_CAN_FASTSEEK:
1631     case ACCESS_CAN_PAUSE:
1632     case ACCESS_CAN_CONTROL_PACE:
1633         pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
1634         *pb_bool = VLC_FALSE;
1635         break;
1636
1637     /* */
1638     case ACCESS_GET_MTU:
1639         pi_int = (int*)va_arg( args, int * );
1640         *pi_int = 0;
1641         break;
1642
1643     case ACCESS_GET_PTS_DELAY:
1644         pi_64 = (int64_t*)va_arg( args, int64_t * );
1645         *pi_64 = (int64_t)var_GetInteger( p_access, "dshow-caching" ) * 1000;
1646         break;
1647
1648     /* */
1649     case ACCESS_SET_PAUSE_STATE:
1650     case ACCESS_GET_TITLE_INFO:
1651     case ACCESS_SET_TITLE:
1652     case ACCESS_SET_SEEKPOINT:
1653     case ACCESS_SET_PRIVATE_ID_STATE:
1654         return VLC_EGENERIC;
1655
1656     default:
1657         msg_Warn( p_access, "unimplemented query in control" );
1658         return VLC_EGENERIC;
1659     }
1660
1661     return VLC_SUCCESS;
1662 }
1663
1664 /****************************************************************************
1665  * DemuxControl:
1666  ****************************************************************************/
1667 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
1668 {
1669     vlc_bool_t *pb;
1670     int64_t    *pi64;
1671
1672     switch( i_query )
1673     {
1674     /* Special for access_demux */
1675     case DEMUX_CAN_PAUSE:
1676     case DEMUX_SET_PAUSE_STATE:
1677     case DEMUX_CAN_CONTROL_PACE:
1678         pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
1679         *pb = VLC_FALSE;
1680         return VLC_SUCCESS;
1681
1682     case DEMUX_GET_PTS_DELAY:
1683         pi64 = (int64_t*)va_arg( args, int64_t * );
1684         *pi64 = (int64_t)var_GetInteger( p_demux, "dshow-caching" ) * 1000;
1685         return VLC_SUCCESS;
1686
1687     case DEMUX_GET_TIME:
1688         pi64 = (int64_t*)va_arg( args, int64_t * );
1689         *pi64 = mdate();
1690         return VLC_SUCCESS;
1691
1692     /* TODO implement others */
1693     default:
1694         return VLC_EGENERIC;
1695     }
1696
1697     return VLC_EGENERIC;
1698 }
1699
1700 /*****************************************************************************
1701  * config variable callback
1702  *****************************************************************************/
1703 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1704                                vlc_value_t newval, vlc_value_t oldval, void * )
1705 {
1706     module_config_t *p_item;
1707     vlc_bool_t b_audio = VLC_FALSE;
1708     int i;
1709
1710     p_item = config_FindConfig( p_this, psz_name );
1711     if( !p_item ) return VLC_SUCCESS;
1712
1713     if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1714
1715     /* Clear-up the current list */
1716     if( p_item->i_list )
1717     {
1718         /* Keep the 2 first entries */
1719         for( i = 2; i < p_item->i_list; i++ )
1720         {
1721             free( p_item->ppsz_list[i] );
1722             free( p_item->ppsz_list_text[i] );
1723         }
1724         /* TODO: Remove when no more needed */
1725         p_item->ppsz_list[i] = NULL;
1726         p_item->ppsz_list_text[i] = NULL;
1727     }
1728     p_item->i_list = 2;
1729
1730     /* Find list of devices */
1731     list<string> list_devices;
1732
1733     /* Initialize OLE/COM */
1734     CoInitialize( 0 );
1735
1736     FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1737
1738     /* Uninitialize OLE/COM */
1739     CoUninitialize();
1740
1741     if( !list_devices.size() ) return VLC_SUCCESS;
1742
1743     p_item->ppsz_list =
1744         (char **)realloc( p_item->ppsz_list,
1745                           (list_devices.size()+3) * sizeof(char *) );
1746     p_item->ppsz_list_text =
1747         (char **)realloc( p_item->ppsz_list_text,
1748                           (list_devices.size()+3) * sizeof(char *) );
1749
1750     list<string>::iterator iter;
1751     for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1752          iter++, i++ )
1753     {
1754         p_item->ppsz_list[i] = strdup( iter->c_str() );
1755         p_item->ppsz_list_text[i] = NULL;
1756         p_item->i_list++;
1757     }
1758     p_item->ppsz_list[i] = NULL;
1759     p_item->ppsz_list_text[i] = NULL;
1760
1761     /* Signal change to the interface */
1762     p_item->b_dirty = VLC_TRUE;
1763
1764     return VLC_SUCCESS;
1765 }
1766
1767 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1768                                vlc_value_t newval, vlc_value_t oldval, void * )
1769 {
1770     module_config_t *p_item;
1771     vlc_bool_t b_audio = VLC_FALSE;
1772
1773     /* Initialize OLE/COM */
1774     CoInitialize( 0 );
1775
1776     p_item = config_FindConfig( p_this, psz_name );
1777     if( !p_item ) return VLC_SUCCESS;
1778
1779     if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1780
1781     string devicename;
1782
1783     if( newval.psz_string && *newval.psz_string )
1784     {
1785         devicename = newval.psz_string;
1786     }
1787     else
1788     {
1789         /* If no device name was specified, pick the 1st one */
1790         list<string> list_devices;
1791
1792         /* Enumerate devices */
1793         FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1794         if( !list_devices.size() ) return VLC_EGENERIC;
1795         devicename = *list_devices.begin();
1796     }
1797
1798     IBaseFilter *p_device_filter =
1799         FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1800     if( p_device_filter )
1801     {
1802         ShowPropertyPage( p_device_filter );
1803     }
1804     else
1805     {
1806         /* Uninitialize OLE/COM */
1807         CoUninitialize();
1808
1809         msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
1810         return VLC_EGENERIC;
1811     }
1812
1813     /* Uninitialize OLE/COM */
1814     CoUninitialize();
1815
1816     return VLC_SUCCESS;
1817 }
1818
1819 /*****************************************************************************
1820  * Properties
1821  *****************************************************************************/
1822 static void ShowPropertyPage( IUnknown *obj )
1823 {
1824     ISpecifyPropertyPages *p_spec;
1825     CAUUID cauuid;
1826
1827     HRESULT hr = obj->QueryInterface( IID_ISpecifyPropertyPages,
1828                                       (void **)&p_spec );
1829     if( FAILED(hr) ) return;
1830
1831     if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
1832     {
1833         if( cauuid.cElems > 0 )
1834         {
1835             HWND hwnd_desktop = ::GetDesktopWindow();
1836
1837             OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
1838                                     cauuid.cElems, cauuid.pElems, 0, 0, NULL );
1839
1840             CoTaskMemFree( cauuid.pElems );
1841         }
1842         p_spec->Release();
1843     }
1844 }
1845
1846 static void ShowDeviceProperties( vlc_object_t *p_this,
1847                                   ICaptureGraphBuilder2 *p_graph,
1848                                   IBaseFilter *p_device_filter,
1849                                   vlc_bool_t b_audio )
1850 {
1851     HRESULT hr;
1852     msg_Dbg( p_this, "configuring Device Properties" );
1853
1854     /*
1855      * Video or audio capture filter page
1856      */
1857     ShowPropertyPage( p_device_filter );
1858
1859     /*
1860      * Audio capture pin
1861      */
1862     if( p_graph && b_audio )
1863     {
1864         IAMStreamConfig *p_SC;
1865
1866         msg_Dbg( p_this, "showing WDM Audio Configuration Pages" );
1867
1868         hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1869                                      &MEDIATYPE_Audio, p_device_filter,
1870                                      IID_IAMStreamConfig, (void **)&p_SC );
1871         if( SUCCEEDED(hr) )
1872         {
1873             ShowPropertyPage(p_SC);
1874             p_SC->Release();
1875         }
1876
1877         /*
1878          * TV Audio filter
1879          */
1880         IAMTVAudio *p_TVA;
1881         HRESULT hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1882                                              &MEDIATYPE_Audio, p_device_filter,
1883                                              IID_IAMTVAudio, (void **)&p_TVA );
1884         if( SUCCEEDED(hr) )
1885         {
1886             ShowPropertyPage(p_TVA);
1887             p_TVA->Release();
1888         }
1889     }
1890
1891     /*
1892      * Video capture pin
1893      */
1894     if( p_graph && !b_audio )
1895     {
1896         IAMStreamConfig *p_SC;
1897
1898         msg_Dbg( p_this, "showing WDM Video Configuration Pages" );
1899
1900         hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1901                                      &MEDIATYPE_Interleaved, p_device_filter,
1902                                      IID_IAMStreamConfig, (void **)&p_SC );
1903         if( FAILED(hr) )
1904         {
1905             hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1906                                          &MEDIATYPE_Video, p_device_filter,
1907                                          IID_IAMStreamConfig, (void **)&p_SC );
1908         }
1909
1910         if( FAILED(hr) )
1911         {
1912             hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1913                                          &MEDIATYPE_Stream, p_device_filter,
1914                                          IID_IAMStreamConfig, (void **)&p_SC );
1915         }
1916
1917         if( SUCCEEDED(hr) )
1918         {
1919             ShowPropertyPage(p_SC);
1920             p_SC->Release();
1921         }
1922     }
1923 }
1924
1925 static void ShowTunerProperties( vlc_object_t *p_this,
1926                                  ICaptureGraphBuilder2 *p_graph,
1927                                  IBaseFilter *p_device_filter,
1928                                  vlc_bool_t b_audio )
1929 {
1930     HRESULT hr;
1931     msg_Dbg( p_this, "configuring Tuner Properties" );
1932
1933     if( !p_graph || b_audio ) return;
1934
1935     IAMTVTuner *p_TV;
1936     hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1937                                  &MEDIATYPE_Interleaved, p_device_filter,
1938                                  IID_IAMTVTuner, (void **)&p_TV );
1939     if( FAILED(hr) )
1940     {
1941         hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1942                                      &MEDIATYPE_Video, p_device_filter,
1943                                      IID_IAMTVTuner, (void **)&p_TV );
1944     }
1945
1946     if( FAILED(hr) )
1947     {
1948         hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1949                                      &MEDIATYPE_Stream, p_device_filter,
1950                                      IID_IAMTVTuner, (void **)&p_TV );
1951     }
1952
1953     if( SUCCEEDED(hr) )
1954     {
1955         ShowPropertyPage(p_TV);
1956         p_TV->Release();
1957     }
1958 }
1959
1960 static void ConfigTuner( vlc_object_t *p_this, ICaptureGraphBuilder2 *p_graph,
1961                          IBaseFilter *p_device_filter )
1962 {
1963     int i_channel, i_country, i_input;
1964     long l_modes = 0;
1965     IAMTVTuner *p_TV;
1966     HRESULT hr;
1967
1968     if( !p_graph ) return;
1969
1970     i_channel = var_GetInteger( p_this, "dshow-tuner-channel" );
1971     i_country = var_GetInteger( p_this, "dshow-tuner-country" );
1972     i_input = var_GetInteger( p_this, "dshow-tuner-input" );
1973
1974     if( !i_channel && !i_country && !i_input ) return; /* Nothing to do */
1975
1976     msg_Dbg( p_this, "tuner config: channel %i, country %i, input type %i",
1977              i_channel, i_country, i_input );
1978
1979     hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved,
1980                                  p_device_filter, IID_IAMTVTuner,
1981                                  (void **)&p_TV );
1982     if( FAILED(hr) )
1983     {
1984         hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
1985                                      p_device_filter, IID_IAMTVTuner,
1986                                      (void **)&p_TV );
1987     }
1988
1989     if( FAILED(hr) )
1990     {
1991         hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Stream,
1992                                      p_device_filter, IID_IAMTVTuner,
1993                                      (void **)&p_TV );
1994     }
1995
1996     if( FAILED(hr) )
1997     {
1998         msg_Dbg( p_this, "couldn't find tuner interface" );
1999         return;
2000     }
2001
2002     hr = p_TV->GetAvailableModes( &l_modes );
2003     if( SUCCEEDED(hr) && (l_modes & AMTUNER_MODE_TV) )
2004     {
2005         hr = p_TV->put_Mode( AMTUNER_MODE_TV );
2006     }
2007
2008     if( i_input == 1 ) p_TV->put_InputType( 0, TunerInputCable );
2009     else if( i_input == 2 ) p_TV->put_InputType( 0, TunerInputAntenna );
2010
2011     p_TV->put_CountryCode( i_country );
2012     p_TV->put_Channel( i_channel, AMTUNER_SUBCHAN_NO_TUNE,
2013                        AMTUNER_SUBCHAN_NO_TUNE );
2014     p_TV->Release();
2015 }