]> git.sesse.net Git - vlc/blob - modules/access/dshow/dshow.cpp
- added last resort built-in mediatype for capture filter if card does not returns...
[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 %ld to video input %ld", 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 %ld to audio input %ld", 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 %ld (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('f','l','3','2'):
869         {
870             return 9;
871         }
872
873         case VLC_FOURCC('Y','V','1','2'):
874         case VLC_FOURCC('a','r','a','w'):
875         {
876             return 8;
877         }
878
879         case VLC_FOURCC('R','V','2','4'):
880         {
881             return 7;
882         }
883
884         case VLC_FOURCC('Y','U','Y','2'):
885         case VLC_FOURCC('R','V','3','2'):
886         case VLC_FOURCC('R','G','B','A'):
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 *mt;
944     AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
945
946     size_t mt_count = EnumDeviceCaps( (vlc_object_t *)p_access,
947                                       p_device_filter, p_sys->i_chroma,
948                                       p_sys->i_width, p_sys->i_height,
949                                       0, 0, 0, media_types, MAX_MEDIA_TYPES );
950
951     if( mt_count > 0 )
952     {
953         mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
954
955         // Order and copy returned media types according to arbitrary
956         // fourcc priority
957         for( size_t c=0; c<mt_count; c++ )
958         {
959             int slot_priority =
960                 GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
961             size_t slot_copy = c;
962             for( size_t d=c+1; d<mt_count; d++ )
963             {
964                 int priority =
965                     GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
966                 if( priority > slot_priority )
967                 {
968                     slot_priority = priority;
969                     slot_copy = d;
970                 }
971             }
972             if( slot_copy != c )
973             {
974                 mt[c] = media_types[slot_copy];
975                 media_types[slot_copy] = media_types[c];
976             }
977             else
978             {
979                 mt[c] = media_types[c];
980             }
981         }
982     }
983     else if( ! b_audio ) {
984         // Use default video media type
985         AM_MEDIA_TYPE mtr;
986         VIDEOINFOHEADER vh;
987
988         mtr.majortype            = MEDIATYPE_Video;
989         mtr.subtype              = MEDIASUBTYPE_I420;
990         mtr.bFixedSizeSamples    = TRUE;
991         mtr.bTemporalCompression = FALSE;
992         mtr.lSampleSize          = 0;
993         mtr.pUnk                 = NULL;
994         mtr.formattype           = FORMAT_VideoInfo;
995         mtr.cbFormat             = sizeof(vh);
996         mtr.pbFormat             = (BYTE *)&vh;
997
998         memset(&vh, 0, sizeof(vh));
999
1000         vh.bmiHeader.biSize        = sizeof(vh.bmiHeader);
1001         vh.bmiHeader.biWidth       = p_sys->i_width > 0 ? p_sys->i_width: 320;
1002         vh.bmiHeader.biHeight      = p_sys->i_height > 0 ? p_sys->i_height : 240;
1003         vh.bmiHeader.biPlanes      = 1;
1004         vh.bmiHeader.biBitCount    = 24;
1005         vh.bmiHeader.biCompression = VLC_FOURCC('I','4','2','0');
1006         vh.bmiHeader.biSizeImage   = p_sys->i_width * 24 * p_sys->i_height / 8;
1007
1008         msg_Warn( p_access, "device %s using built-in video media type",
1009                  devicename.c_str() );
1010
1011         mt_count = 1;
1012         mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1013         CopyMediaType(mt, &mtr);
1014     }
1015     else {
1016         // Use default audio media type
1017         AM_MEDIA_TYPE mtr;
1018         WAVEFORMATEX wf;
1019
1020         mtr.majortype            = MEDIATYPE_Audio;
1021         mtr.subtype              = MEDIASUBTYPE_PCM;
1022         mtr.bFixedSizeSamples    = TRUE;
1023         mtr.bTemporalCompression = FALSE;
1024         mtr.lSampleSize          = 0;
1025         mtr.pUnk                 = NULL;
1026         mtr.formattype           = FORMAT_WaveFormatEx;
1027         mtr.cbFormat             = sizeof(wf);
1028         mtr.pbFormat             = (BYTE *)&wf;
1029
1030         memset(&wf, 0, sizeof(wf));
1031
1032         wf.wFormatTag = WAVE_FORMAT_PCM;
1033         wf.nChannels = 2;
1034         wf.nSamplesPerSec = 44100;
1035         wf.wBitsPerSample = 16;
1036         wf.nBlockAlign = wf.nSamplesPerSec * wf.wBitsPerSample / 8;
1037         wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
1038         wf.cbSize = 0;
1039
1040         msg_Warn( p_access, "device %s using built-in audio media type",
1041                  devicename.c_str() );
1042
1043         mt_count = 1;
1044         mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1045         CopyMediaType(mt, &mtr);
1046     }
1047
1048     /* Create and add our capture filter */
1049     CaptureFilter *p_capture_filter =
1050         new CaptureFilter( p_access, mt, mt_count );
1051     p_sys->p_graph->AddFilter( p_capture_filter, 0 );
1052
1053     /* Add the device filter to the graph (seems necessary with VfW before
1054      * accessing pin attributes). */
1055     p_sys->p_graph->AddFilter( p_device_filter, 0 );
1056
1057     /* Attempt to connect one of this device's capture output pins */
1058     msg_Dbg( p_access, "connecting filters" );
1059     if( ConnectFilters( p_access, p_device_filter, p_capture_filter ) )
1060     {
1061         /* Success */
1062         msg_Dbg( p_access, "filters connected successfully !" );
1063
1064         dshow_stream_t dshow_stream;
1065         dshow_stream.b_invert = VLC_FALSE;
1066         dshow_stream.b_pts = VLC_FALSE;
1067         dshow_stream.mt =
1068             p_capture_filter->CustomGetPin()->CustomGetMediaType();
1069
1070         /* Show properties. Done here so the VLC stream is setup with the
1071          * proper parameters. */
1072         vlc_value_t val;
1073         var_Get( p_access, "dshow-config", &val );
1074         if( val.i_int )
1075         {
1076             PropertiesPage( VLC_OBJECT(p_access), p_device_filter,
1077                             p_sys->p_capture_graph_builder2,
1078                             dshow_stream.mt.majortype == MEDIATYPE_Audio );
1079         }
1080
1081         dshow_stream.mt =
1082             p_capture_filter->CustomGetPin()->CustomGetMediaType();
1083
1084         dshow_stream.i_fourcc = GetFourCCFromMediaType(dshow_stream.mt);
1085         if( 0 != dshow_stream.i_fourcc )
1086         {
1087             if( dshow_stream.mt.majortype == MEDIATYPE_Video )
1088             {
1089                 dshow_stream.header.video =
1090                     *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
1091
1092                 int i_height = dshow_stream.header.video.bmiHeader.biHeight;
1093
1094                 /* Check if the image is inverted (bottom to top) */
1095                 if( dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','1') ||
1096                     dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','4') ||
1097                     dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','8') ||
1098                     dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','5') ||
1099                     dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','6') ||
1100                     dshow_stream.i_fourcc == VLC_FOURCC('R','V','2','4') ||
1101                     dshow_stream.i_fourcc == VLC_FOURCC('R','V','3','2') ||
1102                     dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','A') )
1103                 {
1104                     if( i_height > 0 ) dshow_stream.b_invert = VLC_TRUE;
1105                     else i_height = - i_height;
1106                 }
1107
1108                 /* Check if we are dealing with a DV stream */
1109                 if( dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','l') ||
1110                     dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','d') ||
1111                     dshow_stream.i_fourcc == VLC_FOURCC('d','v','h','d') )
1112                 {
1113                     p_access->pf_read = ReadCompressed;
1114                     if( !p_access->psz_demux || !*p_access->psz_demux )
1115                     {
1116                         p_access->psz_demux = strdup( "rawdv" );
1117                     }
1118                     p_sys->b_audio = VLC_FALSE;
1119                 }
1120
1121                 /* Check if we are dealing with an MPEG video stream */
1122                 if( dshow_stream.i_fourcc == VLC_FOURCC('m','p','2','v') )
1123                 {
1124                     p_access->pf_read = ReadCompressed;
1125                     if( !p_access->psz_demux || !*p_access->psz_demux )
1126                     {
1127                         p_access->psz_demux = "mpgv";
1128                     }
1129                     p_sys->b_audio = VLC_FALSE;
1130                 }
1131
1132                 /* Add video stream to header */
1133                 p_sys->i_header_size += 20;
1134                 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1135                                                       p_sys->i_header_size );
1136                 memcpy(  &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
1137                 memcpy(  &p_sys->p_header[p_sys->i_header_pos + 4],
1138                          &dshow_stream.i_fourcc, 4 );
1139                 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1140                          dshow_stream.header.video.bmiHeader.biWidth );
1141                 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height );
1142                 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 );
1143                 p_sys->i_header_pos = p_sys->i_header_size;
1144
1145                 /* Greatly simplifies the reading routine */
1146                 int i_mtu = dshow_stream.header.video.bmiHeader.biWidth *
1147                     i_height * 4;
1148                 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1149             }
1150
1151             else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
1152             {
1153                 dshow_stream.header.audio =
1154                     *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
1155
1156                 /* Add audio stream to header */
1157                 p_sys->i_header_size += 20;
1158                 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1159                                                       p_sys->i_header_size );
1160                 memcpy(  &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
1161                 memcpy(  &p_sys->p_header[p_sys->i_header_pos + 4],
1162                          &dshow_stream.i_fourcc, 4 );
1163                 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1164                          dshow_stream.header.audio.nChannels );
1165                 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12],
1166                          dshow_stream.header.audio.nSamplesPerSec );
1167                 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16],
1168                          dshow_stream.header.audio.wBitsPerSample );
1169                 p_sys->i_header_pos = p_sys->i_header_size;
1170
1171                 /* Greatly simplifies the reading routine */
1172                 IAMBufferNegotiation *p_ambuf;
1173                 IPin *p_pin;
1174                 int i_mtu;
1175
1176                 p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin );
1177                 if( SUCCEEDED( p_pin->QueryInterface(
1178                         IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1179                 {
1180                     ALLOCATOR_PROPERTIES AllocProp;
1181                     memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) );
1182                     p_ambuf->GetAllocatorProperties( &AllocProp );
1183                     p_ambuf->Release();
1184                     i_mtu = AllocProp.cbBuffer;
1185                 }
1186                 else
1187                 {
1188                     /* Worst case */
1189                     i_mtu = dshow_stream.header.audio.nSamplesPerSec *
1190                         dshow_stream.header.audio.nChannels *
1191                         dshow_stream.header.audio.wBitsPerSample / 8;
1192                 }
1193                 p_pin->Release();
1194                 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1195             }
1196
1197             else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
1198             {
1199                 msg_Dbg( p_access, "MEDIATYPE_Stream" );
1200
1201                 msg_Dbg( p_access, "selected stream pin accepts format: %4.4s",
1202                          (char *)&dshow_stream.i_fourcc);
1203
1204                 p_sys->b_audio = VLC_FALSE;
1205                 p_sys->i_header_size = 0;
1206                 p_sys->i_header_pos = 0;
1207                 p_sys->i_mtu = 0;
1208
1209                 p_access->pf_read = ReadCompressed;
1210             }
1211             else
1212             {
1213                 msg_Dbg( p_access, "unknown stream majortype" );
1214                 goto fail;
1215             }
1216
1217             /* Add directshow elementary stream to our list */
1218             dshow_stream.p_device_filter = p_device_filter;
1219             dshow_stream.p_capture_filter = p_capture_filter;
1220
1221             p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams,
1222                 sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) );
1223             p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1224             *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1225             SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams );
1226
1227             return VLC_SUCCESS;
1228         }
1229     }
1230
1231  fail:
1232     /* Remove filters from graph */
1233     p_sys->p_graph->RemoveFilter( p_device_filter );
1234     p_sys->p_graph->RemoveFilter( p_capture_filter );
1235
1236     /* Release objects */
1237     p_device_filter->Release();
1238     p_capture_filter->Release();
1239
1240     return VLC_EGENERIC;
1241 }
1242
1243 static IBaseFilter *
1244 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1245                    list<string> *p_listdevices, vlc_bool_t b_audio )
1246 {
1247     IBaseFilter *p_base_filter = NULL;
1248     IMoniker *p_moniker = NULL;
1249     ULONG i_fetched;
1250     HRESULT hr;
1251
1252     /* Create the system device enumerator */
1253     ICreateDevEnum *p_dev_enum = NULL;
1254
1255     hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1256                            IID_ICreateDevEnum, (void **)&p_dev_enum );
1257     if( FAILED(hr) )
1258     {
1259         msg_Err( p_this, "failed to create the device enumerator (0x%lx)", hr);
1260         return NULL;
1261     }
1262
1263     /* Create an enumerator for the video capture devices */
1264     IEnumMoniker *p_class_enum = NULL;
1265     if( !b_audio )
1266         hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1267                                                 &p_class_enum, 0 );
1268     else
1269         hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1270                                                 &p_class_enum, 0 );
1271     p_dev_enum->Release();
1272     if( FAILED(hr) )
1273     {
1274         msg_Err( p_this, "failed to create the class enumerator (0x%lx)", hr );
1275         return NULL;
1276     }
1277
1278     /* If there are no enumerators for the requested type, then
1279      * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1280     if( p_class_enum == NULL )
1281     {
1282         msg_Err( p_this, "no capture device was detected" );
1283         return NULL;
1284     }
1285
1286     /* Enumerate the devices */
1287
1288     /* Note that if the Next() call succeeds but there are no monikers,
1289      * it will return S_FALSE (which is not a failure). Therefore, we check
1290      * that the return code is S_OK instead of using SUCCEEDED() macro. */
1291
1292     while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1293     {
1294         /* Getting the property page to get the device name */
1295         IPropertyBag *p_bag;
1296         hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1297                                        (void **)&p_bag );
1298         if( SUCCEEDED(hr) )
1299         {
1300             VARIANT var;
1301             var.vt = VT_BSTR;
1302             hr = p_bag->Read( L"FriendlyName", &var, NULL );
1303             p_bag->Release();
1304             if( SUCCEEDED(hr) )
1305             {
1306                 int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
1307                 char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
1308                 WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
1309                                      i_convert, NULL, NULL );
1310                 SysFreeString(var.bstrVal);
1311
1312                 if( p_listdevices ) p_listdevices->push_back( p_buf );
1313
1314                 if( p_devicename && *p_devicename == string(p_buf) )
1315                 {
1316                     /* Bind Moniker to a filter object */
1317                     hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1318                                                   (void **)&p_base_filter );
1319                     if( FAILED(hr) )
1320                     {
1321                         msg_Err( p_this, "couldn't bind moniker to filter "
1322                                  "object (0x%lx)", hr );
1323                         p_moniker->Release();
1324                         p_class_enum->Release();
1325                         return NULL;
1326                     }
1327                     p_moniker->Release();
1328                     p_class_enum->Release();
1329                     return p_base_filter;
1330                 }
1331             }
1332         }
1333
1334         p_moniker->Release();
1335     }
1336
1337     p_class_enum->Release();
1338     return NULL;
1339 }
1340
1341 static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter,
1342                               int i_fourcc, int i_width, int i_height,
1343                               int i_channels, int i_samplespersec,
1344                               int i_bitspersample, AM_MEDIA_TYPE *mt,
1345                               size_t mt_max )
1346 {
1347     IEnumPins *p_enumpins;
1348     IPin *p_output_pin;
1349     IEnumMediaTypes *p_enummt;
1350     size_t mt_count = 0;
1351
1352     if( FAILED(p_filter->EnumPins( &p_enumpins )) )
1353     {
1354         msg_Dbg( p_this, "EnumDeviceCaps failed: no pin enumeration !");
1355         return 0;
1356     }
1357
1358     while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1359     {
1360         PIN_INFO info;
1361
1362         if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1363         {
1364             msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1365                      info.dir == PINDIR_INPUT ? "input" : "output",
1366                      info.achName );
1367             if( info.pFilter ) info.pFilter->Release();
1368         }
1369
1370         p_output_pin->Release();
1371     }
1372
1373     p_enumpins->Reset();
1374
1375     while( !mt_count && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1376     {
1377         PIN_INFO info;
1378
1379         if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1380         {
1381             if( info.pFilter ) info.pFilter->Release();
1382             if( info.dir == PINDIR_INPUT )
1383             {
1384                 p_output_pin->Release();
1385                 continue;
1386             }
1387             msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1388         }
1389
1390         /* Probe pin */
1391         if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1392         {
1393             AM_MEDIA_TYPE *p_mt;
1394             while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1395             {
1396                 int i_current_fourcc = GetFourCCFromMediaType(*p_mt);
1397                 if( 0 != i_current_fourcc )
1398                 {
1399                     if( p_mt->majortype == MEDIATYPE_Video )
1400                     {
1401                         int i_current_width = p_mt->pbFormat ?
1402                                 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0;
1403                         int i_current_height = p_mt->pbFormat ?
1404                                 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0;
1405                         if( i_current_height < 0 )
1406                                 i_current_height = -i_current_height; 
1407
1408                         msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1409                                          "accepts chroma: %4.4s, width:%i, height:%i",
1410                                          (char *)&i_current_fourcc, i_current_width,
1411                                          i_current_height );
1412
1413                         if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1414                                 ( !i_width || i_width == i_current_width ) &&
1415                                 ( !i_height || i_height == i_current_height ) &&
1416                                 (mt_count < mt_max) )
1417                         {
1418                             /* Pick match */
1419                             mt[mt_count++] = *p_mt;
1420                         }
1421                         else
1422                         {
1423                             FreeMediaType( *p_mt );
1424                         }
1425                     }
1426                     else if( p_mt->majortype == MEDIATYPE_Audio )
1427                     {
1428                         int i_current_channels =
1429                                 ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1430                         int i_current_samplespersec =
1431                                 ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1432                         int i_current_bitspersample =
1433                                 ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1434
1435                         msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1436                                          "accepts format: %4.4s, channels:%i, "
1437                                          "samples/sec:%i bits/sample:%i",
1438                                          (char *)&i_current_fourcc, i_current_channels,
1439                                          i_current_samplespersec, i_current_bitspersample);
1440
1441                         if( (!i_channels || i_channels == i_current_channels) &&
1442                                 (!i_samplespersec ||
1443                                  i_samplespersec == i_current_samplespersec) &&
1444                                 (!i_bitspersample ||
1445                                  i_bitspersample == i_current_bitspersample) &&
1446                                 (mt_count < mt_max) )
1447                         {
1448                             /* Pick  match */
1449                             mt[mt_count++] = *p_mt;
1450
1451                             /* Pre-Configure the 1st match, Ugly */
1452                             if( 1 == mt_count ) {
1453                                 /* Setup a few properties like the audio latency */
1454                                 IAMBufferNegotiation *p_ambuf;
1455
1456                                 if( SUCCEEDED( p_output_pin->QueryInterface(
1457                                           IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1458                                 {
1459                                     ALLOCATOR_PROPERTIES AllocProp;
1460                                     AllocProp.cbAlign = -1;
1461                                     AllocProp.cbBuffer = i_current_channels *
1462                                       i_current_samplespersec *
1463                                       i_current_bitspersample / 8 / 10 ; /*100 ms of latency*/
1464                                     AllocProp.cbPrefix = -1;
1465                                     AllocProp.cBuffers = -1;
1466                                     p_ambuf->SuggestAllocatorProperties( &AllocProp );
1467                                     p_ambuf->Release();
1468                                 }
1469                             }
1470                         }
1471                         else
1472                         {
1473                             FreeMediaType( *p_mt );
1474                         }
1475                     }
1476                     else if( p_mt->majortype == MEDIATYPE_Stream )
1477                     {
1478                         if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1479                                 (mt_count < mt_max) )
1480                         {
1481                                 /* Pick match */
1482                                 mt[mt_count++] = *p_mt;
1483                                 i_fourcc = i_current_fourcc;
1484                         }
1485                         else
1486                         {
1487                                 FreeMediaType( *p_mt );
1488                         }
1489                     }
1490                     else
1491                     {
1492                         msg_Dbg( p_this,
1493                                          "EnumDeviceCaps: input pin: unknown format" );
1494                         FreeMediaType( *p_mt );
1495                     }
1496                 }
1497                 else
1498                 {
1499                     msg_Dbg( p_this,
1500                                      "EnumDeviceCaps: input pin: unknown format" );
1501                     FreeMediaType( *p_mt );
1502                 }
1503                 CoTaskMemFree( (PVOID)p_mt );
1504             }
1505             p_enummt->Release();
1506         }
1507
1508         p_output_pin->Release();
1509     }
1510
1511     p_enumpins->Release();
1512     return mt_count;
1513 }
1514
1515 /*****************************************************************************
1516  * AccessRead: reads from the device.
1517  *****************************************************************************
1518  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1519  * bytes.
1520  *****************************************************************************/
1521 static int AccessRead( access_t *p_access, uint8_t *p_buffer, int i_len )
1522 {
1523     access_sys_t   *p_sys = p_access->p_sys;
1524     dshow_stream_t *p_stream = NULL;
1525     byte_t         *p_buf_orig = p_buffer;
1526     VLCMediaSample  sample;
1527     int             i_data_size;
1528     uint8_t         *p_data;
1529
1530     if( p_sys->i_header_pos )
1531     {
1532         /* First header of the stream */
1533         memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size );
1534         p_buffer += p_sys->i_header_size;
1535         p_sys->i_header_pos = 0;
1536     }
1537
1538     while( 1 )
1539     {
1540         /* Get new sample/frame from next elementary stream.
1541          * We first loop through all the elementary streams and if all our
1542          * fifos are empty we block until we are signaled some new data has
1543          * arrived. */
1544         vlc_mutex_lock( &p_sys->lock );
1545
1546         int i_stream;
1547         for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1548         {
1549             p_stream = p_sys->pp_streams[i_stream];
1550             if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1551                 p_stream->p_capture_filter &&
1552                 p_stream->p_capture_filter->CustomGetPin()
1553                   ->CustomGetSample( &sample ) == S_OK )
1554             {
1555                 break;
1556             }
1557         }
1558         if( i_stream == p_sys->i_streams )
1559         for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1560         {
1561             p_stream = p_sys->pp_streams[i_stream];
1562             if( p_stream->p_capture_filter &&
1563                 p_stream->p_capture_filter->CustomGetPin()
1564                   ->CustomGetSample( &sample ) == S_OK )
1565             {
1566                 break;
1567             }
1568         }
1569         if( i_stream == p_sys->i_streams )
1570         {
1571             /* No data available. Wait until some data has arrived */
1572             vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1573             vlc_mutex_unlock( &p_sys->lock );
1574             continue;
1575         }
1576
1577         vlc_mutex_unlock( &p_sys->lock );
1578
1579         /*
1580          * We got our sample
1581          */
1582         i_data_size = sample.p_sample->GetActualDataLength();
1583         sample.p_sample->GetPointer( &p_data );
1584
1585         REFERENCE_TIME i_pts, i_end_date;
1586         HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1587         if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1588
1589         if( !i_pts )
1590         {
1591             if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1592             {
1593                 /* Use our data timestamp */
1594                 i_pts = sample.i_timestamp;
1595                 p_stream->b_pts = VLC_TRUE;
1596             }
1597         }
1598
1599 #if 0
1600         msg_Dbg( p_access, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts );
1601 #endif
1602
1603         /* Create pseudo header */
1604         SetDWBE( &p_sys->p_header[0], i_stream );
1605         SetDWBE( &p_sys->p_header[4], i_data_size );
1606         SetQWBE( &p_sys->p_header[8], i_pts / 10 );
1607
1608 #if 0
1609         msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1610 #endif
1611
1612         /* First copy header */
1613         memcpy( p_buffer, p_sys->p_header, 16 /* header size */ );
1614         p_buffer += 16 /* header size */;
1615
1616         /* Then copy stream data if any */
1617         if( !p_stream->b_invert )
1618         {
1619             p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1620             p_buffer += i_data_size;
1621         }
1622         else
1623         {
1624             int i_width = p_stream->header.video.bmiHeader.biWidth;
1625             int i_height = p_stream->header.video.bmiHeader.biHeight;
1626             if( i_height < 0 ) i_height = - i_height;
1627
1628             switch( p_stream->i_fourcc )
1629             {
1630             case VLC_FOURCC( 'R', 'V', '1', '5' ):
1631             case VLC_FOURCC( 'R', 'V', '1', '6' ):
1632                 i_width *= 2;
1633                 break;
1634             case VLC_FOURCC( 'R', 'V', '2', '4' ):
1635                 i_width *= 3;
1636                 break;
1637             case VLC_FOURCC( 'R', 'V', '3', '2' ):
1638             case VLC_FOURCC( 'R', 'G', 'B', 'A' ):
1639                 i_width *= 4;
1640                 break;
1641             }
1642
1643             for( int i = i_height - 1; i >= 0; i-- )
1644             {
1645                 p_access->p_vlc->pf_memcpy( p_buffer,
1646                      &p_data[i * i_width], i_width );
1647
1648                 p_buffer += i_width;
1649             }
1650         }
1651
1652         sample.p_sample->Release();
1653
1654         /* The caller got what he wanted */
1655         return p_buffer - p_buf_orig;
1656     }
1657
1658     return 0; /* never reached */
1659 }
1660
1661 /*****************************************************************************
1662  * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1663  *****************************************************************************
1664  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1665  * bytes.
1666  *****************************************************************************/
1667 static int ReadCompressed( access_t *p_access, uint8_t *p_buffer, int i_len )
1668 {
1669     access_sys_t   *p_sys = p_access->p_sys;
1670     dshow_stream_t *p_stream = NULL;
1671     VLCMediaSample  sample;
1672     int             i_data_size;
1673     uint8_t         *p_data;
1674
1675     /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1676
1677     /* There must be only 1 elementary stream to produce a valid stream
1678      * of MPEG or DV data */
1679     p_stream = p_sys->pp_streams[0];
1680
1681     while( 1 )
1682     {
1683         if( p_access->b_die || p_access->b_error ) return 0;
1684
1685         /* Get new sample/frame from the elementary stream (blocking). */
1686         vlc_mutex_lock( &p_sys->lock );
1687
1688         if( p_stream->p_capture_filter->CustomGetPin()
1689               ->CustomGetSample( &sample ) != S_OK )
1690         {
1691             /* No data available. Wait until some data has arrived */
1692             vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1693             vlc_mutex_unlock( &p_sys->lock );
1694             continue;
1695         }
1696
1697         vlc_mutex_unlock( &p_sys->lock );
1698
1699         /*
1700          * We got our sample
1701          */
1702         i_data_size = sample.p_sample->GetActualDataLength();
1703         sample.p_sample->GetPointer( &p_data );
1704
1705 #if 0
1706         msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1707 #endif
1708         i_data_size = __MIN( i_data_size, (int)i_len );
1709
1710         p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1711
1712         sample.p_sample->Release();
1713
1714         /* The caller got what he wanted */
1715         return i_data_size;
1716     }
1717
1718     return 0; /* never reached */
1719 }
1720
1721 /*****************************************************************************
1722  * Demux: local prototypes
1723  *****************************************************************************/
1724 struct demux_sys_t
1725 {
1726     int         i_es;
1727     es_out_id_t **es;
1728 };
1729
1730 static int  Demux      ( demux_t * );
1731 static int DemuxControl( demux_t *, int, va_list );
1732
1733 /****************************************************************************
1734  * DemuxOpen:
1735  ****************************************************************************/
1736 static int DemuxOpen( vlc_object_t *p_this )
1737 {
1738     demux_t     *p_demux = (demux_t *)p_this;
1739     demux_sys_t *p_sys;
1740
1741     uint8_t     *p_peek;
1742     int         i_es;
1743     int         i;
1744
1745     /* a little test to see if it's a dshow stream */
1746     if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
1747     {
1748         msg_Warn( p_demux, "dshow plugin discarded (cannot peek)" );
1749         return VLC_EGENERIC;
1750     }
1751
1752     if( memcmp( p_peek, ".dsh", 4 ) ||
1753         ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
1754     {
1755         msg_Warn( p_demux, "dshow plugin discarded (not a valid stream)" );
1756         return VLC_EGENERIC;
1757     }
1758
1759     p_demux->pf_demux   = Demux;
1760     p_demux->pf_control = DemuxControl;
1761     p_demux->p_sys = p_sys = (demux_sys_t *)malloc( sizeof( demux_sys_t ) );
1762     p_sys->i_es = 0;
1763     p_sys->es   = NULL;
1764
1765     if( stream_Peek( p_demux->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
1766     {
1767         msg_Err( p_demux, "dshow plugin discarded (cannot peek)" );
1768         return VLC_EGENERIC;
1769     }
1770     p_peek += 8;
1771
1772     for( i = 0; i < i_es; i++ )
1773     {
1774         es_format_t fmt;
1775
1776         if( !memcmp( p_peek, "auds", 4 ) )
1777         {
1778             es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1779                                                         p_peek[6], p_peek[7] ) );
1780
1781             fmt.audio.i_channels = GetDWBE( &p_peek[8] );
1782             fmt.audio.i_rate = GetDWBE( &p_peek[12] );
1783             fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
1784             fmt.audio.i_blockalign = fmt.audio.i_channels *
1785                                      fmt.audio.i_bitspersample / 8;
1786             fmt.i_bitrate = fmt.audio.i_channels *
1787                             fmt.audio.i_rate *
1788                             fmt.audio.i_bitspersample;
1789
1790             msg_Dbg( p_demux, "new audio es %d channels %dHz",
1791                      fmt.audio.i_channels, fmt.audio.i_rate );
1792
1793             p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1794                           sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1795             p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1796         }
1797         else if( !memcmp( p_peek, "vids", 4 ) )
1798         {
1799             es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1800                                                         p_peek[6], p_peek[7] ) );
1801             fmt.video.i_width  = GetDWBE( &p_peek[8] );
1802             fmt.video.i_height = GetDWBE( &p_peek[12] );
1803
1804             msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
1805                      (char*)&fmt.i_codec,
1806                      fmt.video.i_width, fmt.video.i_height );
1807
1808             p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1809                           sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1810             p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1811         }
1812
1813         p_peek += 20;
1814     }
1815
1816     /* Skip header */
1817     stream_Read( p_demux->s, NULL, 8 + 20 * i_es );
1818
1819     return VLC_SUCCESS;
1820 }
1821
1822 /****************************************************************************
1823  * DemuxClose:
1824  ****************************************************************************/
1825 static void DemuxClose( vlc_object_t *p_this )
1826 {
1827     demux_t     *p_demux = (demux_t *)p_this;
1828     demux_sys_t *p_sys = p_demux->p_sys;
1829
1830     if( p_sys->i_es > 0 )
1831     {
1832         free( p_sys->es );
1833     }
1834     free( p_sys );
1835 }
1836
1837 /****************************************************************************
1838  * Demux:
1839  ****************************************************************************/
1840 static int Demux( demux_t *p_demux )
1841 {
1842     demux_sys_t *p_sys = p_demux->p_sys;
1843     block_t     *p_block;
1844
1845     int i_es;
1846     int i_size;
1847
1848     uint8_t *p_peek;
1849     mtime_t i_pts;
1850
1851     if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
1852     {
1853         msg_Warn( p_demux, "cannot peek (EOF ?)" );
1854         return 0;
1855     }
1856
1857     i_es = GetDWBE( &p_peek[0] );
1858     if( i_es < 0 || i_es >= p_sys->i_es )
1859     {
1860         msg_Err( p_demux, "cannot find ES" );
1861         return -1;
1862     }
1863
1864     i_size = GetDWBE( &p_peek[4] );
1865     i_pts  = GetQWBE( &p_peek[8] );
1866
1867     if( ( p_block = stream_Block( p_demux->s, 16 + i_size ) ) == NULL )
1868     {
1869         msg_Warn( p_demux, "cannot read data" );
1870         return 0;
1871     }
1872
1873     p_block->p_buffer += 16;
1874     p_block->i_buffer -= 16;
1875
1876     p_block->i_dts = p_block->i_pts = i_pts;
1877
1878     es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pts > 0 ? i_pts : 0 );
1879     es_out_Send( p_demux->out, p_sys->es[i_es], p_block );
1880
1881     return 1;
1882 }
1883
1884 /****************************************************************************
1885  * DemuxControl:
1886  ****************************************************************************/
1887 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
1888 {
1889    return demux2_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
1890 }
1891
1892 /*****************************************************************************
1893  * config variable callback
1894  *****************************************************************************/
1895 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1896                                vlc_value_t newval, vlc_value_t oldval, void * )
1897 {
1898     module_config_t *p_item;
1899     vlc_bool_t b_audio = VLC_FALSE;
1900     int i;
1901
1902     p_item = config_FindConfig( p_this, psz_name );
1903     if( !p_item ) return VLC_SUCCESS;
1904
1905     if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1906
1907     /* Clear-up the current list */
1908     if( p_item->i_list )
1909     {
1910         /* Keep the 2 first entries */
1911         for( i = 2; i < p_item->i_list; i++ )
1912         {
1913             free( p_item->ppsz_list[i] );
1914             free( p_item->ppsz_list_text[i] );
1915         }
1916         /* TODO: Remove when no more needed */
1917         p_item->ppsz_list[i] = NULL;
1918         p_item->ppsz_list_text[i] = NULL;
1919     }
1920     p_item->i_list = 2;
1921
1922     /* Find list of devices */
1923     list<string> list_devices;
1924
1925     /* Initialize OLE/COM */
1926     CoInitialize( 0 );
1927
1928     FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1929
1930     /* Uninitialize OLE/COM */
1931     CoUninitialize();
1932
1933     if( !list_devices.size() ) return VLC_SUCCESS;
1934
1935     p_item->ppsz_list =
1936         (char **)realloc( p_item->ppsz_list,
1937                           (list_devices.size()+3) * sizeof(char *) );
1938     p_item->ppsz_list_text =
1939         (char **)realloc( p_item->ppsz_list_text,
1940                           (list_devices.size()+3) * sizeof(char *) );
1941
1942     list<string>::iterator iter;
1943     for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1944          iter++, i++ )
1945     {
1946         p_item->ppsz_list[i] = strdup( iter->c_str() );
1947         p_item->ppsz_list_text[i] = NULL;
1948         p_item->i_list++;
1949     }
1950     p_item->ppsz_list[i] = NULL;
1951     p_item->ppsz_list_text[i] = NULL;
1952
1953     /* Signal change to the interface */
1954     p_item->b_dirty = VLC_TRUE;
1955
1956     return VLC_SUCCESS;
1957 }
1958
1959 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1960                                vlc_value_t newval, vlc_value_t oldval, void * )
1961 {
1962     module_config_t *p_item;
1963     vlc_bool_t b_audio = VLC_FALSE;
1964
1965     /* Initialize OLE/COM */
1966     CoInitialize( 0 );
1967
1968     p_item = config_FindConfig( p_this, psz_name );
1969     if( !p_item ) return VLC_SUCCESS;
1970
1971     if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1972
1973     string devicename;
1974
1975     if( newval.psz_string && *newval.psz_string )
1976     {
1977         devicename = newval.psz_string;
1978     }
1979     else
1980     {
1981         /* If no device name was specified, pick the 1st one */
1982         list<string> list_devices;
1983
1984         /* Enumerate devices */
1985         FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1986         if( !list_devices.size() ) return VLC_EGENERIC;
1987         devicename = *list_devices.begin();
1988     }
1989
1990     IBaseFilter *p_device_filter =
1991         FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1992     if( p_device_filter )
1993     {
1994         PropertiesPage( p_this, p_device_filter, NULL, b_audio );
1995     }
1996     else
1997     {
1998         /* Uninitialize OLE/COM */
1999         CoUninitialize();
2000
2001         msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
2002         return VLC_EGENERIC;
2003     }
2004
2005     /* Uninitialize OLE/COM */
2006     CoUninitialize();
2007
2008     return VLC_SUCCESS;
2009 }
2010
2011 static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid )
2012 {
2013     if( cauuid->cElems > 0 )
2014     {
2015         HWND hwnd_desktop = ::GetDesktopWindow();
2016
2017         OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
2018                                 cauuid->cElems, cauuid->pElems, 0, 0, NULL );
2019
2020         CoTaskMemFree( cauuid->pElems );
2021     }
2022 }
2023
2024 static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter,
2025                             ICaptureGraphBuilder2 *p_capture_graph,
2026                             vlc_bool_t b_audio )
2027 {
2028     CAUUID cauuid;
2029
2030     msg_Dbg( p_this, "Configuring Device Properties" );
2031
2032     /*
2033      * Video or audio capture filter page
2034      */
2035     ISpecifyPropertyPages *p_spec;
2036
2037     HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages,
2038                                                   (void **)&p_spec );
2039     if( SUCCEEDED(hr) )
2040     {
2041         if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
2042         {
2043             ShowPropertyPage( p_device_filter, &cauuid );
2044         }
2045         p_spec->Release();
2046     }
2047
2048     msg_Dbg( p_this, "looking for WDM Configuration Pages" );
2049
2050     if( p_capture_graph )
2051         msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" );
2052
2053     /*
2054      * Audio capture pin
2055      */
2056     if( p_capture_graph && b_audio )
2057     {
2058         IAMStreamConfig *p_SC;
2059
2060         hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2061                                              &MEDIATYPE_Audio, p_device_filter,
2062                                              IID_IAMStreamConfig,
2063                                              (void **)&p_SC );
2064         if( SUCCEEDED(hr) )
2065         {
2066             hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2067                                        (void **)&p_spec );
2068             if( SUCCEEDED(hr) )
2069             {
2070                 hr = p_spec->GetPages( &cauuid );
2071                 if( SUCCEEDED(hr) )
2072                 {
2073                     for( unsigned int c = 0; c < cauuid.cElems; c++ )
2074                     {
2075                         ShowPropertyPage( p_SC, &cauuid );
2076                     }
2077                     CoTaskMemFree( cauuid.pElems );
2078                 }
2079                 p_spec->Release();
2080             }
2081             p_SC->Release();
2082         }
2083
2084         /*
2085          * TV Audio filter
2086          */
2087         IAMTVAudio *p_TVA;
2088         hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, 
2089                                              &MEDIATYPE_Audio, p_device_filter,
2090                                              IID_IAMTVAudio, (void **)&p_TVA );
2091         if( SUCCEEDED(hr) )
2092         {
2093             hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages,
2094                                         (void **)&p_spec );
2095             if( SUCCEEDED(hr) )
2096             {
2097                 if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) )
2098                     ShowPropertyPage(p_TVA, &cauuid);
2099
2100                 p_spec->Release();
2101             }
2102             p_TVA->Release();
2103         }
2104     }
2105
2106     /*
2107      * Video capture pin
2108      */
2109     if( p_capture_graph && !b_audio )
2110     {
2111         IAMStreamConfig *p_SC;
2112
2113         hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2114                                              &MEDIATYPE_Interleaved,
2115                                              p_device_filter,
2116                                              IID_IAMStreamConfig,
2117                                              (void **)&p_SC );
2118         if( FAILED(hr) )
2119         {
2120             hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2121                                                  &MEDIATYPE_Video,
2122                                                  p_device_filter,
2123                                                  IID_IAMStreamConfig,
2124                                                  (void **)&p_SC );
2125         }
2126
2127         if( SUCCEEDED(hr) )
2128         {
2129             hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2130                                        (void **)&p_spec );
2131             if( SUCCEEDED(hr) )
2132             {
2133                 if( SUCCEEDED( p_spec->GetPages(&cauuid) ) )
2134                 {
2135                     ShowPropertyPage(p_SC, &cauuid);
2136                 }
2137                 p_spec->Release();
2138             }
2139             p_SC->Release();
2140         }
2141
2142         /*
2143          * Video crossbar, and a possible second crossbar
2144          */
2145         IAMCrossbar *p_X, *p_X2;
2146         IBaseFilter *p_XF;
2147
2148         hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2149                                              &MEDIATYPE_Interleaved,
2150                                              p_device_filter,
2151                                              IID_IAMCrossbar, (void **)&p_X );
2152         if( FAILED(hr) )
2153         {
2154             hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2155                                                  &MEDIATYPE_Video,
2156                                                  p_device_filter,
2157                                                  IID_IAMCrossbar,
2158                                                  (void **)&p_X );
2159         }
2160
2161         if( SUCCEEDED(hr) )
2162         {
2163             hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF );
2164             if( SUCCEEDED(hr) )
2165             {
2166                 hr = p_X->QueryInterface( IID_ISpecifyPropertyPages,
2167                                           (void **)&p_spec );
2168                 if( SUCCEEDED(hr) )
2169                 {
2170                     hr = p_spec->GetPages(&cauuid);
2171                     if( hr == S_OK && cauuid.cElems > 0 )
2172                     {
2173                         ShowPropertyPage( p_X, &cauuid );
2174                     }
2175                     p_spec->Release();
2176                 }
2177
2178                 hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL,
2179                                                      p_XF, IID_IAMCrossbar,
2180                                                      (void **)&p_X2 );
2181                 if( SUCCEEDED(hr) )
2182                 {
2183                     hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages,
2184                                                (void **)&p_spec );
2185                     if( SUCCEEDED(hr) )
2186                     {
2187                         hr = p_spec->GetPages( &cauuid );
2188                         if( SUCCEEDED(hr) )
2189                         {
2190                             ShowPropertyPage( p_X2, &cauuid );
2191                         }
2192                         p_spec->Release();
2193                     }
2194                     p_X2->Release();
2195                 }
2196
2197                 p_XF->Release();
2198             }
2199
2200             p_X->Release();
2201         }
2202
2203         /*
2204          * TV Tuner
2205          */
2206         IAMTVTuner *p_TV;
2207         hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2208                                              &MEDIATYPE_Interleaved,
2209                                              p_device_filter,
2210                                              IID_IAMTVTuner, (void **)&p_TV );
2211         if( FAILED(hr) )
2212         {
2213             hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2214                                                  &MEDIATYPE_Video,
2215                                                  p_device_filter,
2216                                                  IID_IAMTVTuner,
2217                                                  (void **)&p_TV );
2218         }
2219
2220         if( SUCCEEDED(hr) )
2221         {
2222             hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages,
2223                                        (void **)&p_spec );
2224             if( SUCCEEDED(hr) )
2225             {
2226                 hr = p_spec->GetPages(&cauuid);
2227                 if( SUCCEEDED(hr) )
2228                 {
2229                     ShowPropertyPage(p_TV, &cauuid);
2230                 }
2231                 p_spec->Release();
2232             }
2233             p_TV->Release();
2234         }
2235     }
2236 }