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