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