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