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