1 /*****************************************************************************
2 * dshow.cpp : DirectShow access module for vlc
3 *****************************************************************************
4 * Copyright (C) 2002, 2003 VideoLAN
7 * Author: Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc/input.h>
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 );
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 * );
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 * );
56 static void PropertiesPage( vlc_object_t *, IBaseFilter *,
57 ICaptureGraphBuilder2 *, vlc_bool_t );
60 /* Debug only, use this to find out GUIDs */
62 UuidToString( (IID *)&IID_IAMBufferNegotiation, &p_st );
63 msg_Err( p_access, "BufferNegotiation: %s" , p_st );
87 static void SetDWBE( uint8_t *p, uint32_t dw )
89 p[0] = (dw >> 24)&0xff;
90 p[1] = (dw >> 16)&0xff;
91 p[2] = (dw >> 8)&0xff;
95 static void SetQWBE( uint8_t *p, uint64_t qw )
97 SetDWBE( p, (qw >> 32)&0xffffffff );
98 SetDWBE( &p[4], qw&0xffffffff );
101 /*****************************************************************************
103 *****************************************************************************/
104 static char *ppsz_vdev[] = { "", "none" };
105 static char *ppsz_vdev_text[] = { N_("Default"), N_("None") };
106 static char *ppsz_adev[] = { "", "none" };
107 static char *ppsz_adev_text[] = { N_("Default"), N_("None") };
109 #define CACHING_TEXT N_("Caching value in ms")
110 #define CACHING_LONGTEXT N_( \
111 "Allows you to modify the default caching value for DirectShow streams. " \
112 "This value should be set in milliseconds units." )
113 #define VDEV_TEXT N_("Video device name")
114 #define VDEV_LONGTEXT N_( \
115 "You can specify the name of the video device that will be used by the " \
116 "DirectShow plugin. If you don't specify anything, the default device " \
118 #define ADEV_TEXT N_("Audio device name")
119 #define ADEV_LONGTEXT N_( \
120 "You can specify the name of the audio device that will be used by the " \
121 "DirectShow plugin. If you don't specify anything, the default device " \
123 #define SIZE_TEXT N_("Video size")
124 #define SIZE_LONGTEXT N_( \
125 "You can specify the size of the video that will be displayed by the " \
126 "DirectShow plugin. If you don't specify anything the default size for " \
127 "your device will be used.")
128 #define CHROMA_TEXT N_("Video input chroma format")
129 #define CHROMA_LONGTEXT N_( \
130 "Force the DirectShow video input to use a specific chroma format " \
131 "(eg. I420 (default), RV24, etc.)")
132 #define CONFIG_TEXT N_("Device properties")
133 #define CONFIG_LONGTEXT N_( \
134 "Show the properties dialog of the selected device before starting the " \
137 static int AccessOpen ( vlc_object_t * );
138 static void AccessClose( vlc_object_t * );
140 static int DemuxOpen ( vlc_object_t * );
141 static void DemuxClose ( vlc_object_t * );
144 set_shortname( _("DirectShow") );
145 set_description( _("DirectShow input") );
146 add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL,
147 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
149 add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE);
150 change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback );
151 change_action_add( FindDevicesCallback, N_("Refresh list") );
152 change_action_add( ConfigDevicesCallback, N_("Configure") );
154 add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE);
155 change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback );
156 change_action_add( FindDevicesCallback, N_("Refresh list") );
157 change_action_add( ConfigDevicesCallback, N_("Configure") );
159 add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE);
161 add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
164 add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT,
167 add_shortcut( "dshow" );
168 set_capability( "access2", 0 );
169 set_callbacks( AccessOpen, AccessClose );
172 set_description( _("DirectShow demuxer") );
173 add_shortcut( "dshow" );
174 set_capability( "demux2", 200 );
175 set_callbacks( DemuxOpen, DemuxClose );
179 /****************************************************************************
180 * DirectShow elementary stream descriptor
181 ****************************************************************************/
182 typedef struct dshow_stream_t
185 IBaseFilter *p_device_filter;
186 CaptureFilter *p_capture_filter;
193 VIDEOINFOHEADER video;
202 /****************************************************************************
203 * Access descriptor declaration
204 ****************************************************************************/
205 #define MAX_CROSSBAR_DEPTH 10
207 typedef struct CrossbarRouteRec {
209 LONG VideoInputIndex;
210 LONG VideoOutputIndex;
211 LONG AudioInputIndex;
212 LONG AudioOutputIndex;
217 /* These 2 must be left at the beginning */
221 IFilterGraph *p_graph;
222 ICaptureGraphBuilder2 *p_capture_graph_builder2;
223 IMediaControl *p_control;
225 int i_crossbar_route_depth;
226 CrossbarRoute crossbar_routes[MAX_CROSSBAR_DEPTH];
233 /* list of elementary streams */
234 dshow_stream_t **pp_streams;
236 int i_current_stream;
238 /* misc properties */
246 /****************************************************************************
247 * DirectShow utility functions
248 ****************************************************************************/
249 static void CreateDirectShowGraph( access_sys_t *p_sys )
251 p_sys->i_crossbar_route_depth = 0;
253 /* Create directshow filter graph */
254 if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
255 (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) )
257 /* Create directshow capture graph builder if available */
258 if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0,
259 CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
260 (void **)&p_sys->p_capture_graph_builder2 ) ) )
262 p_sys->p_capture_graph_builder2->
263 SetFiltergraph((IGraphBuilder *)p_sys->p_graph);
266 p_sys->p_graph->QueryInterface( IID_IMediaControl,
267 (void **)&p_sys->p_control );
271 static void DeleteCrossbarRoutes( access_sys_t *p_sys )
273 /* Remove crossbar filters from graph */
274 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
276 p_sys->crossbar_routes[i].pXbar->Release();
278 p_sys->i_crossbar_route_depth = 0;
281 static void DeleteDirectShowGraph( access_sys_t *p_sys )
283 DeleteCrossbarRoutes( p_sys );
285 /* Remove filters from graph */
286 for( int i = 0; i < p_sys->i_streams; i++ )
288 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter );
289 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter );
290 p_sys->pp_streams[i]->p_capture_filter->Release();
291 p_sys->pp_streams[i]->p_device_filter->Release();
294 /* Release directshow objects */
295 if( p_sys->p_control )
297 p_sys->p_control->Release();
298 p_sys->p_control = NULL;
300 if( p_sys->p_capture_graph_builder2 )
302 p_sys->p_capture_graph_builder2->Release();
303 p_sys->p_capture_graph_builder2 = NULL;
308 p_sys->p_graph->Release();
309 p_sys->p_graph = NULL;
313 static void ReleaseDirectShow( access_t *p_access )
315 access_sys_t *p_sys = p_access->p_sys;
317 msg_Dbg( p_access, "Releasing DirectShow");
319 DeleteDirectShowGraph( p_sys );
321 /* Uninitialize OLE/COM */
324 free( p_sys->p_header );
325 /* Remove filters from graph */
326 for( int i = 0; i < p_sys->i_streams; i++ )
328 delete p_sys->pp_streams[i];
330 free( p_sys->pp_streams );
334 /*****************************************************************************
335 * Open: open direct show device
336 *****************************************************************************/
337 static int AccessOpen( vlc_object_t *p_this )
339 access_t *p_access = (access_t*)p_this;
343 /* Get/parse options and open device(s) */
344 string vdevname, adevname;
345 int i_width = 0, i_height = 0, i_chroma = 0;
347 var_Create( p_access, "dshow-config", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
349 var_Create( p_access, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
350 var_Get( p_access, "dshow-vdev", &val );
351 if( val.psz_string ) vdevname = string( val.psz_string );
352 if( val.psz_string ) free( val.psz_string );
354 var_Create( p_access, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
355 var_Get( p_access, "dshow-adev", &val );
356 if( val.psz_string ) adevname = string( val.psz_string );
357 if( val.psz_string ) free( val.psz_string );
359 var_Create( p_access, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
360 var_Get( p_access, "dshow-size", &val );
361 if( val.psz_string && *val.psz_string )
363 if( !strcmp( val.psz_string, "subqcif" ) )
365 i_width = 128; i_height = 96;
367 else if( !strcmp( val.psz_string, "qsif" ) )
369 i_width = 160; i_height = 120;
371 else if( !strcmp( val.psz_string, "qcif" ) )
373 i_width = 176; i_height = 144;
375 else if( !strcmp( val.psz_string, "sif" ) )
377 i_width = 320; i_height = 240;
379 else if( !strcmp( val.psz_string, "cif" ) )
381 i_width = 352; i_height = 288;
383 else if( !strcmp( val.psz_string, "vga" ) )
385 i_width = 640; i_height = 480;
391 i_width = strtol( val.psz_string, &psz_parser, 0 );
392 if( *psz_parser == 'x' || *psz_parser == 'X')
394 i_height = strtol( psz_parser + 1, &psz_parser, 0 );
396 msg_Dbg( p_access, "Width x Height %dx%d", i_width, i_height );
399 if( val.psz_string ) free( val.psz_string );
401 var_Create( p_access, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
402 var_Get( p_access, "dshow-chroma", &val );
403 if( val.psz_string && strlen( val.psz_string ) >= 4 )
405 i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1],
406 val.psz_string[2], val.psz_string[3] );
408 if( val.psz_string ) free( val.psz_string );
411 p_access->pf_read = AccessRead;
412 p_access->pf_block = NULL;
413 p_access->pf_control = AccessControl;
414 p_access->pf_seek = NULL;
415 p_access->info.i_update = 0;
416 p_access->info.i_size = 0;
417 p_access->info.i_pos = 0;
418 p_access->info.b_eof = VLC_FALSE;
419 p_access->info.i_title = 0;
420 p_access->info.i_seekpoint = 0;
421 p_access->p_sys = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
422 memset( p_sys, 0, sizeof( access_sys_t ) );
424 var_Create( p_access, "dshow-caching",
425 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
427 /* Initialize OLE/COM */
430 /* Initialize some data */
431 p_sys->i_streams = 0;
432 p_sys->pp_streams = (dshow_stream_t **)malloc( 1 );
433 p_sys->i_width = i_width;
434 p_sys->i_height = i_height;
435 p_sys->i_chroma = i_chroma;
436 p_sys->b_audio = VLC_TRUE;
440 p_sys->i_header_size = 8;
441 p_sys->p_header = (uint8_t *)malloc( p_sys->i_header_size );
442 memcpy( &p_sys->p_header[0], ".dsh", 4 );
443 SetDWBE( &p_sys->p_header[4], 1 );
444 p_sys->i_header_pos = p_sys->i_header_size;
446 p_sys->p_graph = NULL;
447 p_sys->p_capture_graph_builder2 = NULL;
448 p_sys->p_control = NULL;
450 /* Build directshow graph */
451 CreateDirectShowGraph( p_sys );
453 if( OpenDevice( p_access, vdevname, 0 ) != VLC_SUCCESS )
455 msg_Err( p_access, "can't open video");
458 if( p_sys->b_audio && OpenDevice( p_access, adevname, 1 ) != VLC_SUCCESS )
460 msg_Err( p_access, "can't open audio");
463 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
465 IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
466 LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex;
467 LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex;
468 LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex;
469 LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex;
471 if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) )
473 msg_Dbg( p_access, "Crossbar at depth %d, Routed video "
474 "ouput %ld to video input %ld", i, VideoOutputIndex,
477 if( AudioOutputIndex != -1 && AudioInputIndex != -1 )
479 if( SUCCEEDED( pXbar->Route(AudioOutputIndex,
482 msg_Dbg(p_access, "Crossbar at depth %d, Routed audio "
483 "ouput %ld to audio input %ld", i,
484 AudioOutputIndex, AudioInputIndex );
490 if( !p_sys->i_streams )
492 ReleaseDirectShow( p_access );
496 /* Initialize some data */
497 p_sys->i_current_stream = 0;
498 p_sys->i_mtu += p_sys->i_header_size + 16 /* data header size */;
499 vlc_mutex_init( p_access, &p_sys->lock );
500 vlc_cond_init( p_access, &p_sys->wait );
502 msg_Dbg( p_access, "Playing...");
504 /* Everything is ready. Let's rock baby */
505 p_sys->p_control->Run();
510 /*****************************************************************************
511 * AccessClose: close device
512 *****************************************************************************/
513 static void AccessClose( vlc_object_t *p_this )
515 access_t *p_access = (access_t *)p_this;
516 access_sys_t *p_sys = p_access->p_sys;
518 /* Stop capturing stuff */
519 p_sys->p_control->Stop();
521 ReleaseDirectShow( p_access );
524 /*****************************************************************************
526 *****************************************************************************/
527 static int AccessControl( access_t *p_access, int i_query, va_list args )
529 access_sys_t *p_sys = p_access->p_sys;
537 case ACCESS_CAN_SEEK:
538 case ACCESS_CAN_FASTSEEK:
539 case ACCESS_CAN_PAUSE:
540 case ACCESS_CAN_CONTROL_PACE:
541 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
542 *pb_bool = VLC_FALSE;
547 pi_int = (int*)va_arg( args, int * );
548 *pi_int = p_sys->i_mtu;
551 case ACCESS_GET_PTS_DELAY:
552 pi_64 = (int64_t*)va_arg( args, int64_t * );
553 *pi_64 = (int64_t)var_GetInteger( p_access, "dshow-caching" ) *
558 case ACCESS_SET_PAUSE_STATE:
559 case ACCESS_GET_TITLE_INFO:
560 case ACCESS_SET_TITLE:
561 case ACCESS_SET_SEEKPOINT:
565 msg_Err( p_access, "unimplemented query in control" );
572 /****************************************************************************
573 * RouteCrossbars (Does not AddRef the returned *Pin)
574 ****************************************************************************/
575 static HRESULT GetCrossbarIPinAtIndex( IAMCrossbar *pXbar, LONG PinIndex,
576 BOOL IsInputPin, IPin ** ppPin )
578 LONG cntInPins, cntOutPins;
580 IBaseFilter *pFilter = NULL;
584 if( !pXbar || !ppPin ) return E_POINTER;
588 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) return E_FAIL;
590 LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins;
592 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
594 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
597 while( pins->Next(1, &pP, &n) == S_OK )
612 return *ppPin ? S_OK : E_FAIL;
615 /****************************************************************************
616 * GetCrossbarIndexFromIPin: Find corresponding index of an IPin on a crossbar
617 ****************************************************************************/
618 static HRESULT GetCrossbarIndexFromIPin( IAMCrossbar * pXbar, LONG * PinIndex,
619 BOOL IsInputPin, IPin * pPin )
621 LONG cntInPins, cntOutPins;
623 IBaseFilter *pFilter = NULL;
628 if(!pXbar || !PinIndex || !pPin )
631 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) )
634 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
636 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
640 while( pins->Next(1, &pP, &n) == S_OK )
645 *PinIndex = IsInputPin ? i : i - cntInPins;
656 return fOK ? S_OK : E_FAIL;
659 /****************************************************************************
661 ****************************************************************************/
662 static HRESULT FindCrossbarRoutes( access_t *p_access, IPin *p_input_pin,
663 LONG physicalType, int depth = 0 )
665 access_sys_t *p_sys = p_access->p_sys;
666 HRESULT result = S_FALSE;
669 if( FAILED(p_input_pin->ConnectedTo(&p_output_pin)) ) return S_FALSE;
671 // It is connected, so now find out if the filter supports IAMCrossbar
673 if( FAILED(p_output_pin->QueryPinInfo(&pinInfo)) ||
674 PINDIR_OUTPUT != pinInfo.dir )
676 p_output_pin->Release ();
680 IAMCrossbar *pXbar=0;
681 if( FAILED(pinInfo.pFilter->QueryInterface(IID_IAMCrossbar,
684 pinInfo.pFilter->Release();
685 p_output_pin->Release ();
689 LONG inputPinCount, outputPinCount;
690 if( FAILED(pXbar->get_PinCounts(&outputPinCount, &inputPinCount)) )
693 pinInfo.pFilter->Release();
694 p_output_pin->Release ();
698 LONG inputPinIndexRelated, outputPinIndexRelated;
699 LONG inputPinPhysicalType, outputPinPhysicalType;
700 LONG inputPinIndex, outputPinIndex;
701 if( FAILED(GetCrossbarIndexFromIPin( pXbar, &outputPinIndex,
702 FALSE, p_output_pin )) ||
703 FAILED(pXbar->get_CrossbarPinInfo( FALSE, outputPinIndex,
704 &outputPinIndexRelated,
705 &outputPinPhysicalType )) )
708 pinInfo.pFilter->Release();
709 p_output_pin->Release ();
714 // for all input pins
716 for( inputPinIndex = 0; S_OK != result && inputPinIndex < inputPinCount;
719 if( FAILED(pXbar->get_CrossbarPinInfo( TRUE, inputPinIndex,
720 &inputPinIndexRelated, &inputPinPhysicalType )) ) continue;
722 // Is the pin a video pin?
723 if( inputPinPhysicalType != physicalType ) continue;
726 if( FAILED(pXbar->CanRoute(outputPinIndex, inputPinIndex)) ) continue;
729 if( FAILED(GetCrossbarIPinAtIndex( pXbar, inputPinIndex,
730 TRUE, &pPin)) ) continue;
732 result = FindCrossbarRoutes( p_access, pPin, physicalType, depth+1 );
733 if( S_OK == result || (S_FALSE == result &&
734 physicalType == inputPinPhysicalType &&
735 (p_sys->i_crossbar_route_depth = depth+1) < MAX_CROSSBAR_DEPTH) )
740 // remember crossbar route
741 p_sys->crossbar_routes[depth].pXbar = pXbar;
742 p_sys->crossbar_routes[depth].VideoInputIndex = inputPinIndex;
743 p_sys->crossbar_routes[depth].VideoOutputIndex = outputPinIndex;
744 p_sys->crossbar_routes[depth].AudioInputIndex = inputPinIndexRelated;
745 p_sys->crossbar_routes[depth].AudioOutputIndex = outputPinIndexRelated;
747 msg_Dbg( p_access, "Crossbar at depth %d, Found Route For "
748 "ouput %ld (type %ld) to input %ld (type %ld)", depth,
749 outputPinIndex, outputPinPhysicalType, inputPinIndex,
750 inputPinPhysicalType );
757 pinInfo.pFilter->Release();
758 p_output_pin->Release ();
763 /****************************************************************************
765 ****************************************************************************/
766 static bool ConnectFilters( access_t *p_access, IBaseFilter *p_filter,
767 CaptureFilter *p_capture_filter )
769 access_sys_t *p_sys = p_access->p_sys;
770 CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
772 AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
774 if( p_sys->p_capture_graph_builder2 )
776 if( FAILED(p_sys->p_capture_graph_builder2->
777 RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
779 (IBaseFilter *)p_capture_filter )) )
784 // Sort out all the possible video inputs
785 // The class needs to be given the capture filters ANALOGVIDEO input pin
787 if( mediaType.majortype == MEDIATYPE_Video &&
788 SUCCEEDED(p_filter->EnumPins(&pins)) )
794 IKsPropertySet *pKs=0;
798 while( !Found && (S_OK == pins->Next(1, &pP, &n)) )
800 if(S_OK == pP->QueryPinInfo(&pinInfo))
802 if(pinInfo.dir == PINDIR_INPUT)
804 // is this pin an ANALOGVIDEOIN input pin?
805 if( pP->QueryInterface(IID_IKsPropertySet,
806 (void **)&pKs) == S_OK )
808 if( pKs->Get(AMPROPSETID_Pin,
809 AMPROPERTY_PIN_CATEGORY, NULL, 0,
810 &guid, sizeof(GUID), &dw) == S_OK )
812 if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
814 // recursively search crossbar routes
815 FindCrossbarRoutes( p_access, pP,
816 PhysConn_Video_Tuner );
824 pinInfo.pFilter->Release();
834 IEnumPins *p_enumpins;
837 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
839 while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
841 PIN_DIRECTION pin_dir;
842 p_pin->QueryDirection( &pin_dir );
844 if( pin_dir == PINDIR_OUTPUT &&
845 p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
849 p_enumpins->Release();
855 p_enumpins->Release();
861 ** get fourcc priority from arbritary preference, the higher the better
863 static int GetFourCCPriority(int i_fourcc)
867 case VLC_FOURCC('I','4','2','0'):
868 case VLC_FOURCC('f','l','3','2'):
873 case VLC_FOURCC('Y','V','1','2'):
874 case VLC_FOURCC('a','r','a','w'):
879 case VLC_FOURCC('R','V','2','4'):
884 case VLC_FOURCC('Y','U','Y','2'):
885 case VLC_FOURCC('R','V','3','2'):
886 case VLC_FOURCC('R','G','B','A'):
894 #define MAX_MEDIA_TYPES 32
896 static int OpenDevice( access_t *p_access, string devicename,
899 access_sys_t *p_sys = p_access->p_sys;
901 /* See if device is already opened */
902 for( int i = 0; i < p_sys->i_streams; i++ )
904 if( p_sys->pp_streams[i]->devicename == devicename )
911 list<string> list_devices;
913 /* Enumerate devices and display their names */
914 FindCaptureDevice( (vlc_object_t *)p_access, NULL, &list_devices, b_audio );
916 if( !list_devices.size() )
919 list<string>::iterator iter;
920 for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
921 msg_Dbg( p_access, "found device: %s", iter->c_str() );
923 /* If no device name was specified, pick the 1st one */
924 if( devicename.size() == 0 )
926 devicename = *list_devices.begin();
929 // Use the system device enumerator and class enumerator to find
930 // a capture/preview device, such as a desktop USB video camera.
931 IBaseFilter *p_device_filter =
932 FindCaptureDevice( (vlc_object_t *)p_access, &devicename,
934 if( p_device_filter )
935 msg_Dbg( p_access, "using device: %s", devicename.c_str() );
938 msg_Err( p_access, "can't use device: %s, unsupported device type",
939 devicename.c_str() );
944 AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
946 size_t mt_count = EnumDeviceCaps( (vlc_object_t *)p_access,
947 p_device_filter, p_sys->i_chroma,
948 p_sys->i_width, p_sys->i_height,
949 0, 0, 0, media_types, MAX_MEDIA_TYPES );
953 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
955 // Order and copy returned media types according to arbitrary
957 for( size_t c=0; c<mt_count; c++ )
960 GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
961 size_t slot_copy = c;
962 for( size_t d=c+1; d<mt_count; d++ )
965 GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
966 if( priority > slot_priority )
968 slot_priority = priority;
974 mt[c] = media_types[slot_copy];
975 media_types[slot_copy] = media_types[c];
979 mt[c] = media_types[c];
983 else if( ! b_audio ) {
984 // Use default video media type
988 mtr.majortype = MEDIATYPE_Video;
989 mtr.subtype = MEDIASUBTYPE_I420;
990 mtr.bFixedSizeSamples = TRUE;
991 mtr.bTemporalCompression = FALSE;
994 mtr.formattype = FORMAT_VideoInfo;
995 mtr.cbFormat = sizeof(vh);
996 mtr.pbFormat = (BYTE *)&vh;
998 memset(&vh, 0, sizeof(vh));
1000 vh.bmiHeader.biSize = sizeof(vh.bmiHeader);
1001 vh.bmiHeader.biWidth = p_sys->i_width > 0 ? p_sys->i_width: 320;
1002 vh.bmiHeader.biHeight = p_sys->i_height > 0 ? p_sys->i_height : 240;
1003 vh.bmiHeader.biPlanes = 1;
1004 vh.bmiHeader.biBitCount = 24;
1005 vh.bmiHeader.biCompression = VLC_FOURCC('I','4','2','0');
1006 vh.bmiHeader.biSizeImage = p_sys->i_width * 24 * p_sys->i_height / 8;
1008 msg_Warn( p_access, "device %s using built-in video media type",
1009 devicename.c_str() );
1012 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1013 CopyMediaType(mt, &mtr);
1016 // Use default audio media type
1020 mtr.majortype = MEDIATYPE_Audio;
1021 mtr.subtype = MEDIASUBTYPE_PCM;
1022 mtr.bFixedSizeSamples = TRUE;
1023 mtr.bTemporalCompression = FALSE;
1024 mtr.lSampleSize = 0;
1026 mtr.formattype = FORMAT_WaveFormatEx;
1027 mtr.cbFormat = sizeof(wf);
1028 mtr.pbFormat = (BYTE *)&wf;
1030 memset(&wf, 0, sizeof(wf));
1032 wf.wFormatTag = WAVE_FORMAT_PCM;
1034 wf.nSamplesPerSec = 44100;
1035 wf.wBitsPerSample = 16;
1036 wf.nBlockAlign = wf.nSamplesPerSec * wf.wBitsPerSample / 8;
1037 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
1040 msg_Warn( p_access, "device %s using built-in audio media type",
1041 devicename.c_str() );
1044 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1045 CopyMediaType(mt, &mtr);
1048 /* Create and add our capture filter */
1049 CaptureFilter *p_capture_filter =
1050 new CaptureFilter( p_access, mt, mt_count );
1051 p_sys->p_graph->AddFilter( p_capture_filter, 0 );
1053 /* Add the device filter to the graph (seems necessary with VfW before
1054 * accessing pin attributes). */
1055 p_sys->p_graph->AddFilter( p_device_filter, 0 );
1057 /* Attempt to connect one of this device's capture output pins */
1058 msg_Dbg( p_access, "connecting filters" );
1059 if( ConnectFilters( p_access, p_device_filter, p_capture_filter ) )
1062 msg_Dbg( p_access, "filters connected successfully !" );
1064 dshow_stream_t dshow_stream;
1065 dshow_stream.b_invert = VLC_FALSE;
1066 dshow_stream.b_pts = VLC_FALSE;
1068 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1070 /* Show properties. Done here so the VLC stream is setup with the
1071 * proper parameters. */
1073 var_Get( p_access, "dshow-config", &val );
1076 PropertiesPage( VLC_OBJECT(p_access), p_device_filter,
1077 p_sys->p_capture_graph_builder2,
1078 dshow_stream.mt.majortype == MEDIATYPE_Audio );
1082 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1084 dshow_stream.i_fourcc = GetFourCCFromMediaType(dshow_stream.mt);
1085 if( 0 != dshow_stream.i_fourcc )
1087 if( dshow_stream.mt.majortype == MEDIATYPE_Video )
1089 dshow_stream.header.video =
1090 *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
1092 int i_height = dshow_stream.header.video.bmiHeader.biHeight;
1094 /* Check if the image is inverted (bottom to top) */
1095 if( dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','1') ||
1096 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','4') ||
1097 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','8') ||
1098 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','5') ||
1099 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','6') ||
1100 dshow_stream.i_fourcc == VLC_FOURCC('R','V','2','4') ||
1101 dshow_stream.i_fourcc == VLC_FOURCC('R','V','3','2') ||
1102 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','A') )
1104 if( i_height > 0 ) dshow_stream.b_invert = VLC_TRUE;
1105 else i_height = - i_height;
1108 /* Check if we are dealing with a DV stream */
1109 if( dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','l') ||
1110 dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','d') ||
1111 dshow_stream.i_fourcc == VLC_FOURCC('d','v','h','d') )
1113 p_access->pf_read = ReadCompressed;
1114 if( !p_access->psz_demux || !*p_access->psz_demux )
1116 p_access->psz_demux = strdup( "rawdv" );
1118 p_sys->b_audio = VLC_FALSE;
1121 /* Check if we are dealing with an MPEG video stream */
1122 if( dshow_stream.i_fourcc == VLC_FOURCC('m','p','2','v') )
1124 p_access->pf_read = ReadCompressed;
1125 if( !p_access->psz_demux || !*p_access->psz_demux )
1127 p_access->psz_demux = "mpgv";
1129 p_sys->b_audio = VLC_FALSE;
1132 /* Add video stream to header */
1133 p_sys->i_header_size += 20;
1134 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1135 p_sys->i_header_size );
1136 memcpy( &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
1137 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1138 &dshow_stream.i_fourcc, 4 );
1139 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1140 dshow_stream.header.video.bmiHeader.biWidth );
1141 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height );
1142 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 );
1143 p_sys->i_header_pos = p_sys->i_header_size;
1145 /* Greatly simplifies the reading routine */
1146 int i_mtu = dshow_stream.header.video.bmiHeader.biWidth *
1148 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1151 else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
1153 dshow_stream.header.audio =
1154 *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
1156 /* Add audio stream to header */
1157 p_sys->i_header_size += 20;
1158 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1159 p_sys->i_header_size );
1160 memcpy( &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
1161 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1162 &dshow_stream.i_fourcc, 4 );
1163 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1164 dshow_stream.header.audio.nChannels );
1165 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12],
1166 dshow_stream.header.audio.nSamplesPerSec );
1167 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16],
1168 dshow_stream.header.audio.wBitsPerSample );
1169 p_sys->i_header_pos = p_sys->i_header_size;
1171 /* Greatly simplifies the reading routine */
1172 IAMBufferNegotiation *p_ambuf;
1176 p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin );
1177 if( SUCCEEDED( p_pin->QueryInterface(
1178 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1180 ALLOCATOR_PROPERTIES AllocProp;
1181 memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) );
1182 p_ambuf->GetAllocatorProperties( &AllocProp );
1184 i_mtu = AllocProp.cbBuffer;
1189 i_mtu = dshow_stream.header.audio.nSamplesPerSec *
1190 dshow_stream.header.audio.nChannels *
1191 dshow_stream.header.audio.wBitsPerSample / 8;
1194 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1197 else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
1199 msg_Dbg( p_access, "MEDIATYPE_Stream" );
1201 msg_Dbg( p_access, "selected stream pin accepts format: %4.4s",
1202 (char *)&dshow_stream.i_fourcc);
1204 p_sys->b_audio = VLC_FALSE;
1205 p_sys->i_header_size = 0;
1206 p_sys->i_header_pos = 0;
1209 p_access->pf_read = ReadCompressed;
1213 msg_Dbg( p_access, "unknown stream majortype" );
1217 /* Add directshow elementary stream to our list */
1218 dshow_stream.p_device_filter = p_device_filter;
1219 dshow_stream.p_capture_filter = p_capture_filter;
1221 p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams,
1222 sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) );
1223 p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1224 *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1225 SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams );
1232 /* Remove filters from graph */
1233 p_sys->p_graph->RemoveFilter( p_device_filter );
1234 p_sys->p_graph->RemoveFilter( p_capture_filter );
1236 /* Release objects */
1237 p_device_filter->Release();
1238 p_capture_filter->Release();
1240 return VLC_EGENERIC;
1243 static IBaseFilter *
1244 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1245 list<string> *p_listdevices, vlc_bool_t b_audio )
1247 IBaseFilter *p_base_filter = NULL;
1248 IMoniker *p_moniker = NULL;
1252 /* Create the system device enumerator */
1253 ICreateDevEnum *p_dev_enum = NULL;
1255 hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1256 IID_ICreateDevEnum, (void **)&p_dev_enum );
1259 msg_Err( p_this, "failed to create the device enumerator (0x%lx)", hr);
1263 /* Create an enumerator for the video capture devices */
1264 IEnumMoniker *p_class_enum = NULL;
1266 hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1269 hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1271 p_dev_enum->Release();
1274 msg_Err( p_this, "failed to create the class enumerator (0x%lx)", hr );
1278 /* If there are no enumerators for the requested type, then
1279 * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1280 if( p_class_enum == NULL )
1282 msg_Err( p_this, "no capture device was detected" );
1286 /* Enumerate the devices */
1288 /* Note that if the Next() call succeeds but there are no monikers,
1289 * it will return S_FALSE (which is not a failure). Therefore, we check
1290 * that the return code is S_OK instead of using SUCCEEDED() macro. */
1292 while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1294 /* Getting the property page to get the device name */
1295 IPropertyBag *p_bag;
1296 hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1302 hr = p_bag->Read( L"FriendlyName", &var, NULL );
1306 int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
1307 char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
1308 WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
1309 i_convert, NULL, NULL );
1310 SysFreeString(var.bstrVal);
1312 if( p_listdevices ) p_listdevices->push_back( p_buf );
1314 if( p_devicename && *p_devicename == string(p_buf) )
1316 /* Bind Moniker to a filter object */
1317 hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1318 (void **)&p_base_filter );
1321 msg_Err( p_this, "couldn't bind moniker to filter "
1322 "object (0x%lx)", hr );
1323 p_moniker->Release();
1324 p_class_enum->Release();
1327 p_moniker->Release();
1328 p_class_enum->Release();
1329 return p_base_filter;
1334 p_moniker->Release();
1337 p_class_enum->Release();
1341 static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter,
1342 int i_fourcc, int i_width, int i_height,
1343 int i_channels, int i_samplespersec,
1344 int i_bitspersample, AM_MEDIA_TYPE *mt,
1347 IEnumPins *p_enumpins;
1349 IEnumMediaTypes *p_enummt;
1350 size_t mt_count = 0;
1352 if( FAILED(p_filter->EnumPins( &p_enumpins )) )
1354 msg_Dbg( p_this, "EnumDeviceCaps failed: no pin enumeration !");
1358 while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1362 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1364 msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1365 info.dir == PINDIR_INPUT ? "input" : "output",
1367 if( info.pFilter ) info.pFilter->Release();
1370 p_output_pin->Release();
1373 p_enumpins->Reset();
1375 while( !mt_count && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1379 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1381 if( info.pFilter ) info.pFilter->Release();
1382 if( info.dir == PINDIR_INPUT )
1384 p_output_pin->Release();
1387 msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1391 if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1393 AM_MEDIA_TYPE *p_mt;
1394 while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1396 int i_current_fourcc = GetFourCCFromMediaType(*p_mt);
1397 if( 0 != i_current_fourcc )
1399 if( p_mt->majortype == MEDIATYPE_Video )
1401 int i_current_width = p_mt->pbFormat ?
1402 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0;
1403 int i_current_height = p_mt->pbFormat ?
1404 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0;
1405 if( i_current_height < 0 )
1406 i_current_height = -i_current_height;
1408 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1409 "accepts chroma: %4.4s, width:%i, height:%i",
1410 (char *)&i_current_fourcc, i_current_width,
1413 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1414 ( !i_width || i_width == i_current_width ) &&
1415 ( !i_height || i_height == i_current_height ) &&
1416 (mt_count < mt_max) )
1419 mt[mt_count++] = *p_mt;
1423 FreeMediaType( *p_mt );
1426 else if( p_mt->majortype == MEDIATYPE_Audio )
1428 int i_current_channels =
1429 ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1430 int i_current_samplespersec =
1431 ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1432 int i_current_bitspersample =
1433 ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1435 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1436 "accepts format: %4.4s, channels:%i, "
1437 "samples/sec:%i bits/sample:%i",
1438 (char *)&i_current_fourcc, i_current_channels,
1439 i_current_samplespersec, i_current_bitspersample);
1441 if( (!i_channels || i_channels == i_current_channels) &&
1442 (!i_samplespersec ||
1443 i_samplespersec == i_current_samplespersec) &&
1444 (!i_bitspersample ||
1445 i_bitspersample == i_current_bitspersample) &&
1446 (mt_count < mt_max) )
1449 mt[mt_count++] = *p_mt;
1451 /* Pre-Configure the 1st match, Ugly */
1452 if( 1 == mt_count ) {
1453 /* Setup a few properties like the audio latency */
1454 IAMBufferNegotiation *p_ambuf;
1456 if( SUCCEEDED( p_output_pin->QueryInterface(
1457 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1459 ALLOCATOR_PROPERTIES AllocProp;
1460 AllocProp.cbAlign = -1;
1461 AllocProp.cbBuffer = i_current_channels *
1462 i_current_samplespersec *
1463 i_current_bitspersample / 8 / 10 ; /*100 ms of latency*/
1464 AllocProp.cbPrefix = -1;
1465 AllocProp.cBuffers = -1;
1466 p_ambuf->SuggestAllocatorProperties( &AllocProp );
1473 FreeMediaType( *p_mt );
1476 else if( p_mt->majortype == MEDIATYPE_Stream )
1478 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1479 (mt_count < mt_max) )
1482 mt[mt_count++] = *p_mt;
1483 i_fourcc = i_current_fourcc;
1487 FreeMediaType( *p_mt );
1493 "EnumDeviceCaps: input pin: unknown format" );
1494 FreeMediaType( *p_mt );
1500 "EnumDeviceCaps: input pin: unknown format" );
1501 FreeMediaType( *p_mt );
1503 CoTaskMemFree( (PVOID)p_mt );
1505 p_enummt->Release();
1508 p_output_pin->Release();
1511 p_enumpins->Release();
1515 /*****************************************************************************
1516 * AccessRead: reads from the device.
1517 *****************************************************************************
1518 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1520 *****************************************************************************/
1521 static int AccessRead( access_t *p_access, uint8_t *p_buffer, int i_len )
1523 access_sys_t *p_sys = p_access->p_sys;
1524 dshow_stream_t *p_stream = NULL;
1525 byte_t *p_buf_orig = p_buffer;
1526 VLCMediaSample sample;
1530 if( p_sys->i_header_pos )
1532 /* First header of the stream */
1533 memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size );
1534 p_buffer += p_sys->i_header_size;
1535 p_sys->i_header_pos = 0;
1540 /* Get new sample/frame from next elementary stream.
1541 * We first loop through all the elementary streams and if all our
1542 * fifos are empty we block until we are signaled some new data has
1544 vlc_mutex_lock( &p_sys->lock );
1547 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1549 p_stream = p_sys->pp_streams[i_stream];
1550 if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1551 p_stream->p_capture_filter &&
1552 p_stream->p_capture_filter->CustomGetPin()
1553 ->CustomGetSample( &sample ) == S_OK )
1558 if( i_stream == p_sys->i_streams )
1559 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1561 p_stream = p_sys->pp_streams[i_stream];
1562 if( p_stream->p_capture_filter &&
1563 p_stream->p_capture_filter->CustomGetPin()
1564 ->CustomGetSample( &sample ) == S_OK )
1569 if( i_stream == p_sys->i_streams )
1571 /* No data available. Wait until some data has arrived */
1572 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1573 vlc_mutex_unlock( &p_sys->lock );
1577 vlc_mutex_unlock( &p_sys->lock );
1582 i_data_size = sample.p_sample->GetActualDataLength();
1583 sample.p_sample->GetPointer( &p_data );
1585 REFERENCE_TIME i_pts, i_end_date;
1586 HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1587 if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1591 if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1593 /* Use our data timestamp */
1594 i_pts = sample.i_timestamp;
1595 p_stream->b_pts = VLC_TRUE;
1600 msg_Dbg( p_access, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts );
1603 /* Create pseudo header */
1604 SetDWBE( &p_sys->p_header[0], i_stream );
1605 SetDWBE( &p_sys->p_header[4], i_data_size );
1606 SetQWBE( &p_sys->p_header[8], i_pts / 10 );
1609 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1612 /* First copy header */
1613 memcpy( p_buffer, p_sys->p_header, 16 /* header size */ );
1614 p_buffer += 16 /* header size */;
1616 /* Then copy stream data if any */
1617 if( !p_stream->b_invert )
1619 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1620 p_buffer += i_data_size;
1624 int i_width = p_stream->header.video.bmiHeader.biWidth;
1625 int i_height = p_stream->header.video.bmiHeader.biHeight;
1626 if( i_height < 0 ) i_height = - i_height;
1628 switch( p_stream->i_fourcc )
1630 case VLC_FOURCC( 'R', 'V', '1', '5' ):
1631 case VLC_FOURCC( 'R', 'V', '1', '6' ):
1634 case VLC_FOURCC( 'R', 'V', '2', '4' ):
1637 case VLC_FOURCC( 'R', 'V', '3', '2' ):
1638 case VLC_FOURCC( 'R', 'G', 'B', 'A' ):
1643 for( int i = i_height - 1; i >= 0; i-- )
1645 p_access->p_vlc->pf_memcpy( p_buffer,
1646 &p_data[i * i_width], i_width );
1648 p_buffer += i_width;
1652 sample.p_sample->Release();
1654 /* The caller got what he wanted */
1655 return p_buffer - p_buf_orig;
1658 return 0; /* never reached */
1661 /*****************************************************************************
1662 * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1663 *****************************************************************************
1664 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1666 *****************************************************************************/
1667 static int ReadCompressed( access_t *p_access, uint8_t *p_buffer, int i_len )
1669 access_sys_t *p_sys = p_access->p_sys;
1670 dshow_stream_t *p_stream = NULL;
1671 VLCMediaSample sample;
1675 /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1677 /* There must be only 1 elementary stream to produce a valid stream
1678 * of MPEG or DV data */
1679 p_stream = p_sys->pp_streams[0];
1683 if( p_access->b_die || p_access->b_error ) return 0;
1685 /* Get new sample/frame from the elementary stream (blocking). */
1686 vlc_mutex_lock( &p_sys->lock );
1688 if( p_stream->p_capture_filter->CustomGetPin()
1689 ->CustomGetSample( &sample ) != S_OK )
1691 /* No data available. Wait until some data has arrived */
1692 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1693 vlc_mutex_unlock( &p_sys->lock );
1697 vlc_mutex_unlock( &p_sys->lock );
1702 i_data_size = sample.p_sample->GetActualDataLength();
1703 sample.p_sample->GetPointer( &p_data );
1706 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1708 i_data_size = __MIN( i_data_size, (int)i_len );
1710 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1712 sample.p_sample->Release();
1714 /* The caller got what he wanted */
1718 return 0; /* never reached */
1721 /*****************************************************************************
1722 * Demux: local prototypes
1723 *****************************************************************************/
1730 static int Demux ( demux_t * );
1731 static int DemuxControl( demux_t *, int, va_list );
1733 /****************************************************************************
1735 ****************************************************************************/
1736 static int DemuxOpen( vlc_object_t *p_this )
1738 demux_t *p_demux = (demux_t *)p_this;
1745 /* a little test to see if it's a dshow stream */
1746 if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
1748 msg_Warn( p_demux, "dshow plugin discarded (cannot peek)" );
1749 return VLC_EGENERIC;
1752 if( memcmp( p_peek, ".dsh", 4 ) ||
1753 ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
1755 msg_Warn( p_demux, "dshow plugin discarded (not a valid stream)" );
1756 return VLC_EGENERIC;
1759 p_demux->pf_demux = Demux;
1760 p_demux->pf_control = DemuxControl;
1761 p_demux->p_sys = p_sys = (demux_sys_t *)malloc( sizeof( demux_sys_t ) );
1765 if( stream_Peek( p_demux->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
1767 msg_Err( p_demux, "dshow plugin discarded (cannot peek)" );
1768 return VLC_EGENERIC;
1772 for( i = 0; i < i_es; i++ )
1776 if( !memcmp( p_peek, "auds", 4 ) )
1778 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1779 p_peek[6], p_peek[7] ) );
1781 fmt.audio.i_channels = GetDWBE( &p_peek[8] );
1782 fmt.audio.i_rate = GetDWBE( &p_peek[12] );
1783 fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
1784 fmt.audio.i_blockalign = fmt.audio.i_channels *
1785 fmt.audio.i_bitspersample / 8;
1786 fmt.i_bitrate = fmt.audio.i_channels *
1788 fmt.audio.i_bitspersample;
1790 msg_Dbg( p_demux, "new audio es %d channels %dHz",
1791 fmt.audio.i_channels, fmt.audio.i_rate );
1793 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1794 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1795 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1797 else if( !memcmp( p_peek, "vids", 4 ) )
1799 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1800 p_peek[6], p_peek[7] ) );
1801 fmt.video.i_width = GetDWBE( &p_peek[8] );
1802 fmt.video.i_height = GetDWBE( &p_peek[12] );
1804 msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
1805 (char*)&fmt.i_codec,
1806 fmt.video.i_width, fmt.video.i_height );
1808 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1809 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1810 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1817 stream_Read( p_demux->s, NULL, 8 + 20 * i_es );
1822 /****************************************************************************
1824 ****************************************************************************/
1825 static void DemuxClose( vlc_object_t *p_this )
1827 demux_t *p_demux = (demux_t *)p_this;
1828 demux_sys_t *p_sys = p_demux->p_sys;
1830 if( p_sys->i_es > 0 )
1837 /****************************************************************************
1839 ****************************************************************************/
1840 static int Demux( demux_t *p_demux )
1842 demux_sys_t *p_sys = p_demux->p_sys;
1851 if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
1853 msg_Warn( p_demux, "cannot peek (EOF ?)" );
1857 i_es = GetDWBE( &p_peek[0] );
1858 if( i_es < 0 || i_es >= p_sys->i_es )
1860 msg_Err( p_demux, "cannot find ES" );
1864 i_size = GetDWBE( &p_peek[4] );
1865 i_pts = GetQWBE( &p_peek[8] );
1867 if( ( p_block = stream_Block( p_demux->s, 16 + i_size ) ) == NULL )
1869 msg_Warn( p_demux, "cannot read data" );
1873 p_block->p_buffer += 16;
1874 p_block->i_buffer -= 16;
1876 p_block->i_dts = p_block->i_pts = i_pts;
1878 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pts > 0 ? i_pts : 0 );
1879 es_out_Send( p_demux->out, p_sys->es[i_es], p_block );
1884 /****************************************************************************
1886 ****************************************************************************/
1887 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
1889 return demux2_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
1892 /*****************************************************************************
1893 * config variable callback
1894 *****************************************************************************/
1895 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1896 vlc_value_t newval, vlc_value_t oldval, void * )
1898 module_config_t *p_item;
1899 vlc_bool_t b_audio = VLC_FALSE;
1902 p_item = config_FindConfig( p_this, psz_name );
1903 if( !p_item ) return VLC_SUCCESS;
1905 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1907 /* Clear-up the current list */
1908 if( p_item->i_list )
1910 /* Keep the 2 first entries */
1911 for( i = 2; i < p_item->i_list; i++ )
1913 free( p_item->ppsz_list[i] );
1914 free( p_item->ppsz_list_text[i] );
1916 /* TODO: Remove when no more needed */
1917 p_item->ppsz_list[i] = NULL;
1918 p_item->ppsz_list_text[i] = NULL;
1922 /* Find list of devices */
1923 list<string> list_devices;
1925 /* Initialize OLE/COM */
1928 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1930 /* Uninitialize OLE/COM */
1933 if( !list_devices.size() ) return VLC_SUCCESS;
1936 (char **)realloc( p_item->ppsz_list,
1937 (list_devices.size()+3) * sizeof(char *) );
1938 p_item->ppsz_list_text =
1939 (char **)realloc( p_item->ppsz_list_text,
1940 (list_devices.size()+3) * sizeof(char *) );
1942 list<string>::iterator iter;
1943 for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1946 p_item->ppsz_list[i] = strdup( iter->c_str() );
1947 p_item->ppsz_list_text[i] = NULL;
1950 p_item->ppsz_list[i] = NULL;
1951 p_item->ppsz_list_text[i] = NULL;
1953 /* Signal change to the interface */
1954 p_item->b_dirty = VLC_TRUE;
1959 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1960 vlc_value_t newval, vlc_value_t oldval, void * )
1962 module_config_t *p_item;
1963 vlc_bool_t b_audio = VLC_FALSE;
1965 /* Initialize OLE/COM */
1968 p_item = config_FindConfig( p_this, psz_name );
1969 if( !p_item ) return VLC_SUCCESS;
1971 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1975 if( newval.psz_string && *newval.psz_string )
1977 devicename = newval.psz_string;
1981 /* If no device name was specified, pick the 1st one */
1982 list<string> list_devices;
1984 /* Enumerate devices */
1985 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1986 if( !list_devices.size() ) return VLC_EGENERIC;
1987 devicename = *list_devices.begin();
1990 IBaseFilter *p_device_filter =
1991 FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1992 if( p_device_filter )
1994 PropertiesPage( p_this, p_device_filter, NULL, b_audio );
1998 /* Uninitialize OLE/COM */
2001 msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
2002 return VLC_EGENERIC;
2005 /* Uninitialize OLE/COM */
2011 static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid )
2013 if( cauuid->cElems > 0 )
2015 HWND hwnd_desktop = ::GetDesktopWindow();
2017 OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
2018 cauuid->cElems, cauuid->pElems, 0, 0, NULL );
2020 CoTaskMemFree( cauuid->pElems );
2024 static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter,
2025 ICaptureGraphBuilder2 *p_capture_graph,
2026 vlc_bool_t b_audio )
2030 msg_Dbg( p_this, "Configuring Device Properties" );
2033 * Video or audio capture filter page
2035 ISpecifyPropertyPages *p_spec;
2037 HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages,
2041 if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
2043 ShowPropertyPage( p_device_filter, &cauuid );
2048 msg_Dbg( p_this, "looking for WDM Configuration Pages" );
2050 if( p_capture_graph )
2051 msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" );
2056 if( p_capture_graph && b_audio )
2058 IAMStreamConfig *p_SC;
2060 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2061 &MEDIATYPE_Audio, p_device_filter,
2062 IID_IAMStreamConfig,
2066 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2070 hr = p_spec->GetPages( &cauuid );
2073 for( unsigned int c = 0; c < cauuid.cElems; c++ )
2075 ShowPropertyPage( p_SC, &cauuid );
2077 CoTaskMemFree( cauuid.pElems );
2088 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2089 &MEDIATYPE_Audio, p_device_filter,
2090 IID_IAMTVAudio, (void **)&p_TVA );
2093 hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages,
2097 if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) )
2098 ShowPropertyPage(p_TVA, &cauuid);
2109 if( p_capture_graph && !b_audio )
2111 IAMStreamConfig *p_SC;
2113 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2114 &MEDIATYPE_Interleaved,
2116 IID_IAMStreamConfig,
2120 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2123 IID_IAMStreamConfig,
2129 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2133 if( SUCCEEDED( p_spec->GetPages(&cauuid) ) )
2135 ShowPropertyPage(p_SC, &cauuid);
2143 * Video crossbar, and a possible second crossbar
2145 IAMCrossbar *p_X, *p_X2;
2148 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2149 &MEDIATYPE_Interleaved,
2151 IID_IAMCrossbar, (void **)&p_X );
2154 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2163 hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF );
2166 hr = p_X->QueryInterface( IID_ISpecifyPropertyPages,
2170 hr = p_spec->GetPages(&cauuid);
2171 if( hr == S_OK && cauuid.cElems > 0 )
2173 ShowPropertyPage( p_X, &cauuid );
2178 hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL,
2179 p_XF, IID_IAMCrossbar,
2183 hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages,
2187 hr = p_spec->GetPages( &cauuid );
2190 ShowPropertyPage( p_X2, &cauuid );
2207 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2208 &MEDIATYPE_Interleaved,
2210 IID_IAMTVTuner, (void **)&p_TV );
2213 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2222 hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages,
2226 hr = p_spec->GetPages(&cauuid);
2229 ShowPropertyPage(p_TV, &cauuid);