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