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 ssize_t Read ( input_thread_t *, byte_t *, size_t );
41 static ssize_t ReadCompressed( input_thread_t *, byte_t *, size_t );
43 static int OpenDevice( input_thread_t *, string, vlc_bool_t );
44 static IBaseFilter *FindCaptureDevice( vlc_object_t *, string *,
45 list<string> *, vlc_bool_t );
46 static size_t EnumDeviceCaps( vlc_object_t *, IBaseFilter *,
47 int, int, int, int, int, int, AM_MEDIA_TYPE *mt, size_t mt_max);
48 static bool ConnectFilters( vlc_object_t *, IBaseFilter *, CaptureFilter * );
50 static int FindDevicesCallback( vlc_object_t *, char const *,
51 vlc_value_t, vlc_value_t, void * );
52 static int ConfigDevicesCallback( vlc_object_t *, char const *,
53 vlc_value_t, vlc_value_t, void * );
55 static void PropertiesPage( vlc_object_t *, IBaseFilter *,
56 ICaptureGraphBuilder2 *, vlc_bool_t );
59 /* Debug only, use this to find out GUIDs */
61 UuidToString( (IID *)&IID_IAMBufferNegotiation, &p_st );
62 msg_Err( p_input, "BufferNegotiation: %s" , p_st );
86 static void SetDWBE( uint8_t *p, uint32_t dw )
88 p[0] = (dw >> 24)&0xff;
89 p[1] = (dw >> 16)&0xff;
90 p[2] = (dw >> 8)&0xff;
94 static void SetQWBE( uint8_t *p, uint64_t qw )
96 SetDWBE( p, (qw >> 32)&0xffffffff );
97 SetDWBE( &p[4], qw&0xffffffff );
100 /*****************************************************************************
102 *****************************************************************************/
103 static char *ppsz_vdev[] = { "", "none" };
104 static char *ppsz_vdev_text[] = { N_("Default"), N_("None") };
105 static char *ppsz_adev[] = { "", "none" };
106 static char *ppsz_adev_text[] = { N_("Default"), N_("None") };
108 #define CACHING_TEXT N_("Caching value in ms")
109 #define CACHING_LONGTEXT N_( \
110 "Allows you to modify the default caching value for DirectShow streams. " \
111 "This value should be set in milliseconds units." )
112 #define VDEV_TEXT N_("Video device name")
113 #define VDEV_LONGTEXT N_( \
114 "You can specify the name of the video device that will be used by the " \
115 "DirectShow plugin. If you don't specify anything, the default device " \
117 #define ADEV_TEXT N_("Audio device name")
118 #define ADEV_LONGTEXT N_( \
119 "You can specify the name of the audio device that will be used by the " \
120 "DirectShow plugin. If you don't specify anything, the default device " \
122 #define SIZE_TEXT N_("Video size")
123 #define SIZE_LONGTEXT N_( \
124 "You can specify the size of the video that will be displayed by the " \
125 "DirectShow plugin. If you don't specify anything the default size for " \
126 "your device will be used.")
127 #define CHROMA_TEXT N_("Video input chroma format")
128 #define CHROMA_LONGTEXT N_( \
129 "Force the DirectShow video input to use a specific chroma format " \
130 "(eg. I420 (default), RV24, etc.)")
131 #define CONFIG_TEXT N_("Device properties")
132 #define CONFIG_LONGTEXT N_( \
133 "Show the properties dialog of the selected device before starting the " \
136 static int AccessOpen ( vlc_object_t * );
137 static void AccessClose( vlc_object_t * );
139 static int DemuxOpen ( vlc_object_t * );
140 static void DemuxClose ( vlc_object_t * );
143 set_shortname( _("DirectShow") );
144 set_description( _("DirectShow input") );
145 add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL,
146 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
148 add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE);
149 change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback );
150 change_action_add( FindDevicesCallback, N_("Refresh list") );
151 change_action_add( ConfigDevicesCallback, N_("Configure") );
153 add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE);
154 change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback );
155 change_action_add( FindDevicesCallback, N_("Refresh list") );
156 change_action_add( ConfigDevicesCallback, N_("Configure") );
158 add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE);
160 add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
163 add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT,
166 add_shortcut( "dshow" );
167 set_capability( "access", 0 );
168 set_callbacks( AccessOpen, AccessClose );
171 set_description( _("DirectShow demuxer") );
172 add_shortcut( "dshow" );
173 set_capability( "demux", 200 );
174 set_callbacks( DemuxOpen, DemuxClose );
178 /****************************************************************************
179 * DirectShow elementary stream descriptor
180 ****************************************************************************/
181 typedef struct dshow_stream_t
184 IBaseFilter *p_device_filter;
185 CaptureFilter *p_capture_filter;
192 VIDEOINFOHEADER video;
201 /****************************************************************************
202 * Access descriptor declaration
203 ****************************************************************************/
204 #define MAX_CROSSBAR_DEPTH 10
206 typedef struct CrossbarRouteRec {
208 LONG VideoInputIndex;
209 LONG VideoOutputIndex;
210 LONG AudioInputIndex;
211 LONG AudioOutputIndex;
219 IFilterGraph *p_graph;
220 ICaptureGraphBuilder2 *p_capture_graph_builder2;
221 IMediaControl *p_control;
223 int i_crossbar_route_depth;
224 CrossbarRoute crossbar_routes[MAX_CROSSBAR_DEPTH];
231 /* list of elementary streams */
232 dshow_stream_t **pp_streams;
234 int i_current_stream;
236 /* misc properties */
243 /****************************************************************************
244 * DirectShow utility functions
245 ****************************************************************************/
246 static void CreateDirectShowGraph( access_sys_t *p_sys )
248 p_sys->i_crossbar_route_depth = 0;
250 /* Create directshow filter graph */
251 if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
252 (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) )
254 /* Create directshow capture graph builder if available */
255 if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0,
256 CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
257 (void **)&p_sys->p_capture_graph_builder2 ) ) )
259 p_sys->p_capture_graph_builder2->
260 SetFiltergraph((IGraphBuilder *)p_sys->p_graph);
263 p_sys->p_graph->QueryInterface( IID_IMediaControl,
264 (void **)&p_sys->p_control );
268 static void DeleteCrossbarRoutes( access_sys_t *p_sys )
270 /* Remove crossbar filters from graph */
271 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
273 p_sys->crossbar_routes[i].pXbar->Release();
275 p_sys->i_crossbar_route_depth = 0;
278 static void DeleteDirectShowGraph( access_sys_t *p_sys )
280 DeleteCrossbarRoutes( p_sys );
282 /* Remove filters from graph */
283 for( int i = 0; i < p_sys->i_streams; i++ )
285 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter );
286 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter );
287 p_sys->pp_streams[i]->p_capture_filter->Release();
288 p_sys->pp_streams[i]->p_device_filter->Release();
291 /* Release directshow objects */
292 if( p_sys->p_control )
294 p_sys->p_control->Release();
295 p_sys->p_control = NULL;
297 if( p_sys->p_capture_graph_builder2 )
299 p_sys->p_capture_graph_builder2->Release();
300 p_sys->p_capture_graph_builder2 = NULL;
305 p_sys->p_graph->Release();
306 p_sys->p_graph = NULL;
310 static void ReleaseDirectShow( input_thread_t *p_input )
312 msg_Dbg( p_input, "Releasing DirectShow");
314 access_sys_t *p_sys = p_input->p_access_data;
316 DeleteDirectShowGraph( p_sys );
318 /* Uninitialize OLE/COM */
321 free( p_sys->p_header );
322 /* Remove filters from graph */
323 for( int i = 0; i < p_sys->i_streams; i++ )
325 delete p_sys->pp_streams[i];
327 free( p_sys->pp_streams );
330 p_input->p_access_data = NULL;
333 /*****************************************************************************
334 * Open: open direct show device
335 *****************************************************************************/
336 static int AccessOpen( vlc_object_t *p_this )
338 input_thread_t *p_input = (input_thread_t *)p_this;
342 /* Get/parse options and open device(s) */
343 string vdevname, adevname;
344 int i_width = 0, i_height = 0, i_chroma = 0;
346 var_Create( p_input, "dshow-config", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
348 var_Create( p_input, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
349 var_Get( p_input, "dshow-vdev", &val );
350 if( val.psz_string ) vdevname = string( val.psz_string );
351 if( val.psz_string ) free( val.psz_string );
353 var_Create( p_input, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
354 var_Get( p_input, "dshow-adev", &val );
355 if( val.psz_string ) adevname = string( val.psz_string );
356 if( val.psz_string ) free( val.psz_string );
358 var_Create( p_input, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
359 var_Get( p_input, "dshow-size", &val );
360 if( val.psz_string && *val.psz_string )
362 if( !strcmp( val.psz_string, "subqcif" ) )
364 i_width = 128; i_height = 96;
366 else if( !strcmp( val.psz_string, "qsif" ) )
368 i_width = 160; i_height = 120;
370 else if( !strcmp( val.psz_string, "qcif" ) )
372 i_width = 176; i_height = 144;
374 else if( !strcmp( val.psz_string, "sif" ) )
376 i_width = 320; i_height = 240;
378 else if( !strcmp( val.psz_string, "cif" ) )
380 i_width = 352; i_height = 288;
382 else if( !strcmp( val.psz_string, "vga" ) )
384 i_width = 640; i_height = 480;
390 i_width = strtol( val.psz_string, &psz_parser, 0 );
391 if( *psz_parser == 'x' || *psz_parser == 'X')
393 i_height = strtol( psz_parser + 1, &psz_parser, 0 );
395 msg_Dbg( p_input, "Width x Height %dx%d", i_width, i_height );
398 if( val.psz_string ) free( val.psz_string );
400 var_Create( p_input, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
401 var_Get( p_input, "dshow-chroma", &val );
402 if( val.psz_string && strlen( val.psz_string ) >= 4 )
404 i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1],
405 val.psz_string[2], val.psz_string[3] );
407 if( val.psz_string ) free( val.psz_string );
409 p_input->pf_read = Read;
410 p_input->pf_seek = NULL;
411 p_input->pf_set_area = NULL;
412 p_input->pf_set_program = NULL;
414 vlc_mutex_lock( &p_input->stream.stream_lock );
415 p_input->stream.b_pace_control = 0;
416 p_input->stream.b_seekable = 0;
417 p_input->stream.p_selected_area->i_size = 0;
418 p_input->stream.p_selected_area->i_tell = 0;
419 p_input->stream.i_method = INPUT_METHOD_FILE;
420 vlc_mutex_unlock( &p_input->stream.stream_lock );
422 var_Create( p_input, "dshow-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
423 var_Get( p_input, "dshow-caching", &val );
424 p_input->i_pts_delay = val.i_int * 1000;
426 /* Initialize OLE/COM */
429 /* create access private data */
430 p_input->p_access_data = p_sys =
431 (access_sys_t *)malloc( sizeof( access_sys_t ) );
433 /* Initialize some data */
434 p_sys->i_streams = 0;
435 p_sys->pp_streams = (dshow_stream_t **)malloc( 1 );
436 p_sys->i_width = i_width;
437 p_sys->i_height = i_height;
438 p_sys->i_chroma = i_chroma;
439 p_sys->b_audio = VLC_TRUE;
442 p_sys->i_header_size = 8;
443 p_sys->p_header = (uint8_t *)malloc( p_sys->i_header_size );
444 memcpy( &p_sys->p_header[0], ".dsh", 4 );
445 SetDWBE( &p_sys->p_header[4], 1 );
446 p_sys->i_header_pos = p_sys->i_header_size;
448 p_sys->p_graph = NULL;
449 p_sys->p_capture_graph_builder2 = NULL;
450 p_sys->p_control = NULL;
452 /* Build directshow graph */
453 CreateDirectShowGraph( p_sys );
455 if( OpenDevice( p_input, vdevname, 0 ) != VLC_SUCCESS )
457 msg_Err( p_input, "can't open video");
460 if( p_sys->b_audio && OpenDevice( p_input, adevname, 1 ) != VLC_SUCCESS )
462 msg_Err( p_input, "can't open audio");
465 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
467 IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
468 LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex;
469 LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex;
470 LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex;
471 LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex;
473 if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) )
475 msg_Dbg( p_input, "Crossbar at depth %d, Routed video ouput %d to "
476 "video input %d", i, VideoOutputIndex, VideoInputIndex );
478 if( AudioOutputIndex != -1 && AudioInputIndex != -1 )
480 if( SUCCEEDED( pXbar->Route(AudioOutputIndex,
483 msg_Dbg(p_input, "Crossbar at depth %d, Routed audio "
484 "ouput %d to audio input %d", i,
485 AudioOutputIndex, AudioInputIndex );
491 if( !p_sys->i_streams )
493 ReleaseDirectShow( p_input );
497 /* Initialize some data */
498 p_sys->i_current_stream = 0;
499 p_input->i_mtu += p_sys->i_header_size + 16 /* data header size */;
501 vlc_mutex_init( p_input, &p_sys->lock );
502 vlc_cond_init( p_input, &p_sys->wait );
504 msg_Dbg( p_input, "Playing...");
506 /* Everything is ready. Let's rock baby */
507 p_sys->p_control->Run();
512 /*****************************************************************************
513 * AccessClose: close device
514 *****************************************************************************/
515 static void AccessClose( vlc_object_t *p_this )
517 input_thread_t *p_input = (input_thread_t *)p_this;
518 access_sys_t *p_sys = p_input->p_access_data;
520 /* Stop capturing stuff */
521 p_sys->p_control->Stop();
523 ReleaseDirectShow(p_input);
526 /****************************************************************************
527 * RouteCrossbars (Does not AddRef the returned *Pin)
528 ****************************************************************************/
529 static HRESULT GetCrossbarIPinAtIndex( IAMCrossbar *pXbar, LONG PinIndex,
530 BOOL IsInputPin, IPin ** ppPin )
532 LONG cntInPins, cntOutPins;
534 IBaseFilter *pFilter = NULL;
538 if( !pXbar || !ppPin ) return E_POINTER;
542 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) return E_FAIL;
544 LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins;
546 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
548 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
551 while( pins->Next(1, &pP, &n) == S_OK )
566 return *ppPin ? S_OK : E_FAIL;
569 /****************************************************************************
570 * GetCrossbarIndexFromIPin: Find corresponding index of an IPin on a crossbar
571 ****************************************************************************/
572 static HRESULT GetCrossbarIndexFromIPin( IAMCrossbar * pXbar, LONG * PinIndex,
573 BOOL IsInputPin, IPin * pPin )
575 LONG cntInPins, cntOutPins;
577 IBaseFilter *pFilter = NULL;
582 if(!pXbar || !PinIndex || !pPin )
585 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) )
588 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
590 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
594 while( pins->Next(1, &pP, &n) == S_OK )
599 *PinIndex = IsInputPin ? i : i - cntInPins;
610 return fOK ? S_OK : E_FAIL;
613 /****************************************************************************
615 ****************************************************************************/
616 static HRESULT FindCrossbarRoutes( vlc_object_t *p_this, IPin *p_input_pin,
617 LONG physicalType, int depth = 0 )
619 access_sys_t *p_sys = ((input_thread_t *)p_this)->p_access_data;
620 HRESULT result = S_FALSE;
623 if( FAILED(p_input_pin->ConnectedTo(&p_output_pin)) ) return S_FALSE;
625 // It is connected, so now find out if the filter supports IAMCrossbar
627 if( FAILED(p_output_pin->QueryPinInfo(&pinInfo)) ||
628 PINDIR_OUTPUT != pinInfo.dir )
630 p_output_pin->Release ();
634 IAMCrossbar *pXbar=0;
635 if( FAILED(pinInfo.pFilter->QueryInterface(IID_IAMCrossbar,
638 pinInfo.pFilter->Release();
639 p_output_pin->Release ();
643 LONG inputPinCount, outputPinCount;
644 if( FAILED(pXbar->get_PinCounts(&outputPinCount, &inputPinCount)) )
647 pinInfo.pFilter->Release();
648 p_output_pin->Release ();
652 LONG inputPinIndexRelated, outputPinIndexRelated;
653 LONG inputPinPhysicalType, outputPinPhysicalType;
654 LONG inputPinIndex, outputPinIndex;
655 if( FAILED(GetCrossbarIndexFromIPin( pXbar, &outputPinIndex,
656 FALSE, p_output_pin )) ||
657 FAILED(pXbar->get_CrossbarPinInfo( FALSE, outputPinIndex,
658 &outputPinIndexRelated,
659 &outputPinPhysicalType )) )
662 pinInfo.pFilter->Release();
663 p_output_pin->Release ();
668 // for all input pins
670 for( inputPinIndex = 0; S_OK != result && inputPinIndex < inputPinCount;
673 if( FAILED(pXbar->get_CrossbarPinInfo( TRUE, inputPinIndex,
674 &inputPinIndexRelated, &inputPinPhysicalType )) ) continue;
676 // Is the pin a video pin?
677 if( inputPinPhysicalType != physicalType ) continue;
680 if( FAILED(pXbar->CanRoute(outputPinIndex, inputPinIndex)) ) continue;
683 if( FAILED(GetCrossbarIPinAtIndex( pXbar, inputPinIndex,
684 TRUE, &pPin)) ) continue;
686 result = FindCrossbarRoutes( p_this, pPin, physicalType, depth+1 );
687 if( S_OK == result || (S_FALSE == result &&
688 physicalType == inputPinPhysicalType &&
689 (p_sys->i_crossbar_route_depth = depth+1) < MAX_CROSSBAR_DEPTH) )
694 // remember crossbar route
695 p_sys->crossbar_routes[depth].pXbar = pXbar;
696 p_sys->crossbar_routes[depth].VideoInputIndex = inputPinIndex;
697 p_sys->crossbar_routes[depth].VideoOutputIndex = outputPinIndex;
698 p_sys->crossbar_routes[depth].AudioInputIndex = inputPinIndexRelated;
699 p_sys->crossbar_routes[depth].AudioOutputIndex = outputPinIndexRelated;
701 msg_Dbg( p_this, "Crossbar at depth %d, Found Route For ouput %ld "
702 "(type %ld) to input %d (type %ld)", depth,
703 outputPinIndex, outputPinPhysicalType, inputPinIndex,
704 inputPinPhysicalType );
711 pinInfo.pFilter->Release();
712 p_output_pin->Release ();
717 /****************************************************************************
719 ****************************************************************************/
720 static bool ConnectFilters( vlc_object_t *p_this, IBaseFilter *p_filter,
721 CaptureFilter *p_capture_filter )
723 access_sys_t *p_sys = ((input_thread_t *)p_this)->p_access_data;
724 CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
726 AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
728 if( p_sys->p_capture_graph_builder2 )
730 if( FAILED(p_sys->p_capture_graph_builder2->
731 RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
733 (IBaseFilter *)p_capture_filter )) )
738 // Sort out all the possible video inputs
739 // The class needs to be given the capture filters ANALOGVIDEO input pin
741 if( mediaType.majortype == MEDIATYPE_Video &&
742 SUCCEEDED(p_filter->EnumPins(&pins)) )
748 IKsPropertySet *pKs=0;
752 while( !Found && (S_OK == pins->Next(1, &pP, &n)) )
754 if(S_OK == pP->QueryPinInfo(&pinInfo))
756 if(pinInfo.dir == PINDIR_INPUT)
758 // is this pin an ANALOGVIDEOIN input pin?
759 if( pP->QueryInterface(IID_IKsPropertySet,
760 (void **)&pKs) == S_OK )
762 if( pKs->Get(AMPROPSETID_Pin,
763 AMPROPERTY_PIN_CATEGORY, NULL, 0,
764 &guid, sizeof(GUID), &dw) == S_OK )
766 if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
768 // recursively search crossbar routes
769 FindCrossbarRoutes( p_this, pP,
770 PhysConn_Video_Tuner );
778 pinInfo.pFilter->Release();
788 IEnumPins *p_enumpins;
791 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
793 while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
795 PIN_DIRECTION pin_dir;
796 p_pin->QueryDirection( &pin_dir );
798 if( pin_dir == PINDIR_OUTPUT &&
799 p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
803 p_enumpins->Release();
809 p_enumpins->Release();
815 ** get fourcc priority from arbritary preference, the higher the better
817 static int GetFourCCPriority(int i_fourcc)
821 case VLC_FOURCC('I','4','2','0'):
822 case VLC_FOURCC('a','r','a','w'):
827 case VLC_FOURCC('Y','V','1','2'):
832 case VLC_FOURCC('R','V','2','4'):
837 case VLC_FOURCC('Y','U','Y','2'):
838 case VLC_FOURCC('R','V','3','2'):
839 case VLC_FOURCC('R','G','B','A'):
840 case VLC_FOURCC('f','l','3','2'):
848 #define MAX_MEDIA_TYPES 32
850 static int OpenDevice( input_thread_t *p_input, string devicename,
853 access_sys_t *p_sys = p_input->p_access_data;
855 /* See if device is already opened */
856 for( int i = 0; i < p_sys->i_streams; i++ )
858 if( p_sys->pp_streams[i]->devicename == devicename )
865 list<string> list_devices;
867 /* Enumerate devices and display their names */
868 FindCaptureDevice( (vlc_object_t *)p_input, NULL, &list_devices, b_audio );
870 if( !list_devices.size() )
873 list<string>::iterator iter;
874 for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
875 msg_Dbg( p_input, "found device: %s", iter->c_str() );
877 /* If no device name was specified, pick the 1st one */
878 if( devicename.size() == 0 )
880 devicename = *list_devices.begin();
883 // Use the system device enumerator and class enumerator to find
884 // a capture/preview device, such as a desktop USB video camera.
885 IBaseFilter *p_device_filter =
886 FindCaptureDevice( (vlc_object_t *)p_input, &devicename,
888 if( p_device_filter )
889 msg_Dbg( p_input, "using device: %s", devicename.c_str() );
892 msg_Err( p_input, "can't use device: %s, unsupported device type", devicename.c_str() );
896 AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
898 size_t mt_count = EnumDeviceCaps( (vlc_object_t *)p_input, p_device_filter,
899 p_sys->i_chroma, p_sys->i_width, p_sys->i_height,
900 0, 0, 0, media_types, MAX_MEDIA_TYPES );
904 msg_Err( p_input, "can't use device: %s, unsupported media types", devicename.c_str() );
908 AM_MEDIA_TYPE *mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
910 //order and copy returned media types according to arbitrary fourcc priority
911 for( size_t c=0; c<mt_count; c++ )
913 int slot_priority = GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
915 for( size_t d=c+1; d<mt_count; d++ )
917 int priority = GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
918 if( priority > slot_priority )
920 slot_priority = priority;
926 mt[c] = media_types[slot_copy];
927 media_types[slot_copy] = media_types[c];
931 mt[c] = media_types[c];
935 /* Create and add our capture filter */
936 CaptureFilter *p_capture_filter = new CaptureFilter( p_input, mt, mt_count );
937 p_sys->p_graph->AddFilter( p_capture_filter, 0 );
939 /* Add the device filter to the graph (seems necessary with VfW before
940 * accessing pin attributes). */
941 p_sys->p_graph->AddFilter( p_device_filter, 0 );
943 /* Attempt to connect one of this device's capture output pins */
944 msg_Dbg( p_input, "connecting filters" );
945 if( ConnectFilters( VLC_OBJECT(p_input), p_device_filter,
949 msg_Dbg( p_input, "filters connected successfully !" );
951 dshow_stream_t dshow_stream;
952 dshow_stream.b_invert = VLC_FALSE;
953 dshow_stream.b_pts = VLC_FALSE;
955 p_capture_filter->CustomGetPin()->CustomGetMediaType();
957 /* Show properties. Done here so the VLC stream is setup with the
958 * proper parameters. */
960 var_Get( p_input, "dshow-config", &val );
963 PropertiesPage( VLC_OBJECT(p_input), p_device_filter,
964 p_sys->p_capture_graph_builder2,
965 dshow_stream.mt.majortype == MEDIATYPE_Audio );
969 p_capture_filter->CustomGetPin()->CustomGetMediaType();
971 dshow_stream.i_fourcc = GetFourCCFromMediaType(dshow_stream.mt);
972 if( 0 != dshow_stream.i_fourcc )
974 if( dshow_stream.mt.majortype == MEDIATYPE_Video )
976 msg_Dbg( p_input, "MEDIATYPE_Video");
978 dshow_stream.header.video =
979 *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
981 int i_height = dshow_stream.header.video.bmiHeader.biHeight;
983 /* Check if the image is inverted (bottom to top) */
984 if( dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', '1' ) ||
985 dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', '4' ) ||
986 dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', '8' ) ||
987 dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '1', '5' ) ||
988 dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '1', '6' ) ||
989 dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '2', '4' ) ||
990 dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '3', '2' ) ||
991 dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', 'A' ) )
993 if( i_height > 0 ) dshow_stream.b_invert = VLC_TRUE;
994 else i_height = - i_height;
997 /* Check if we are dealing with a DV stream */
998 if( dshow_stream.i_fourcc == VLC_FOURCC( 'd', 'v', 's', 'l' ) ||
999 dshow_stream.i_fourcc == VLC_FOURCC( 'd', 'v', 's', 'd' ) ||
1000 dshow_stream.i_fourcc == VLC_FOURCC( 'd', 'v', 'h', 'd' ) )
1002 p_input->pf_read = ReadCompressed;
1003 if( !p_input->psz_demux || !*p_input->psz_demux )
1005 p_input->psz_demux = "rawdv";
1007 p_sys->b_audio = VLC_FALSE;
1010 /* Check if we are dealing with an MPEG video stream */
1011 if( dshow_stream.i_fourcc == VLC_FOURCC( 'm', 'p', '2', 'v' ) )
1013 p_input->pf_read = ReadCompressed;
1014 if( !p_input->psz_demux || !*p_input->psz_demux )
1016 p_input->psz_demux = "mpgv";
1018 p_sys->b_audio = VLC_FALSE;
1021 /* Add video stream to header */
1022 p_sys->i_header_size += 20;
1023 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1024 p_sys->i_header_size );
1025 memcpy( &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
1026 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1027 &dshow_stream.i_fourcc, 4 );
1028 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1029 dshow_stream.header.video.bmiHeader.biWidth );
1030 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height );
1031 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 );
1032 p_sys->i_header_pos = p_sys->i_header_size;
1034 /* Greatly simplifies the reading routine */
1035 int i_mtu = dshow_stream.header.video.bmiHeader.biWidth *
1037 p_input->i_mtu = __MAX( p_input->i_mtu, (unsigned int)i_mtu );
1040 else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
1042 msg_Dbg( p_input, "MEDIATYPE_Audio");
1044 dshow_stream.header.audio =
1045 *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
1047 /* Add audio stream to header */
1048 p_sys->i_header_size += 20;
1049 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1050 p_sys->i_header_size );
1051 memcpy( &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
1052 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1053 &dshow_stream.i_fourcc, 4 );
1054 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1055 dshow_stream.header.audio.nChannels );
1056 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12],
1057 dshow_stream.header.audio.nSamplesPerSec );
1058 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16],
1059 dshow_stream.header.audio.wBitsPerSample );
1060 p_sys->i_header_pos = p_sys->i_header_size;
1062 /* Greatly simplifies the reading routine */
1063 IAMBufferNegotiation *p_ambuf;
1067 p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin );
1068 if( SUCCEEDED( p_pin->QueryInterface(
1069 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1071 ALLOCATOR_PROPERTIES AllocProp;
1072 memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) );
1073 p_ambuf->GetAllocatorProperties( &AllocProp );
1075 i_mtu = AllocProp.cbBuffer;
1080 i_mtu = dshow_stream.header.audio.nSamplesPerSec *
1081 dshow_stream.header.audio.nChannels *
1082 dshow_stream.header.audio.wBitsPerSample / 8;
1085 p_input->i_mtu = __MAX( p_input->i_mtu, (unsigned int)i_mtu );
1088 else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
1090 msg_Dbg( p_input, "MEDIATYPE_Stream" );
1092 msg_Dbg( p_input, "selected stream pin accepts format: %4.4s",
1093 (char *)&dshow_stream.i_fourcc);
1095 p_sys->b_audio = VLC_FALSE;
1096 p_sys->i_header_size = 0;
1097 p_sys->i_header_pos = 0;
1098 p_input->i_mtu = INPUT_DEFAULT_BUFSIZE;
1100 p_input->pf_read = ReadCompressed;
1101 p_input->pf_set_program = input_SetProgram;
1105 msg_Dbg( p_input, "unknown stream majortype" );
1109 /* Add directshow elementary stream to our list */
1110 dshow_stream.p_device_filter = p_device_filter;
1111 dshow_stream.p_capture_filter = p_capture_filter;
1114 (dshow_stream_t **)realloc( p_sys->pp_streams,
1115 sizeof(dshow_stream_t *)
1116 * (p_sys->i_streams + 1) );
1117 p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1118 *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1119 SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams );
1126 /* Remove filters from graph */
1127 p_sys->p_graph->RemoveFilter( p_device_filter );
1128 p_sys->p_graph->RemoveFilter( p_capture_filter );
1130 /* Release objects */
1131 p_device_filter->Release();
1132 p_capture_filter->Release();
1134 return VLC_EGENERIC;
1137 static IBaseFilter *
1138 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1139 list<string> *p_listdevices, vlc_bool_t b_audio )
1141 IBaseFilter *p_base_filter = NULL;
1142 IMoniker *p_moniker = NULL;
1146 /* Create the system device enumerator */
1147 ICreateDevEnum *p_dev_enum = NULL;
1149 hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1150 IID_ICreateDevEnum, (void **)&p_dev_enum );
1153 msg_Err( p_this, "failed to create the device enumerator (0x%x)", hr);
1157 /* Create an enumerator for the video capture devices */
1158 IEnumMoniker *p_class_enum = NULL;
1160 hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1163 hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1165 p_dev_enum->Release();
1168 msg_Err( p_this, "failed to create the class enumerator (0x%x)", hr );
1172 /* If there are no enumerators for the requested type, then
1173 * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1174 if( p_class_enum == NULL )
1176 msg_Err( p_this, "no capture device was detected" );
1180 /* Enumerate the devices */
1182 /* Note that if the Next() call succeeds but there are no monikers,
1183 * it will return S_FALSE (which is not a failure). Therefore, we check
1184 * that the return code is S_OK instead of using SUCCEEDED() macro. */
1186 while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1188 /* Getting the property page to get the device name */
1189 IPropertyBag *p_bag;
1190 hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1196 hr = p_bag->Read( L"FriendlyName", &var, NULL );
1200 int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
1201 char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
1202 WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
1203 i_convert, NULL, NULL );
1204 SysFreeString(var.bstrVal);
1206 if( p_listdevices ) p_listdevices->push_back( p_buf );
1208 if( p_devicename && *p_devicename == string(p_buf) )
1210 /* Bind Moniker to a filter object */
1211 hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1212 (void **)&p_base_filter );
1215 msg_Err( p_this, "couldn't bind moniker to filter "
1216 "object (0x%x)", hr );
1217 p_moniker->Release();
1218 p_class_enum->Release();
1221 p_moniker->Release();
1222 p_class_enum->Release();
1223 return p_base_filter;
1228 p_moniker->Release();
1231 p_class_enum->Release();
1235 static size_t EnumDeviceCaps( vlc_object_t *p_this,
1236 IBaseFilter *p_filter,
1237 int i_fourcc, int i_width, int i_height,
1238 int i_channels, int i_samplespersec,
1239 int i_bitspersample, AM_MEDIA_TYPE *mt, size_t mt_max )
1241 IEnumPins *p_enumpins;
1243 IEnumMediaTypes *p_enummt;
1244 size_t mt_count = 0;
1246 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return 0;
1248 while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1252 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1254 msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1255 info.dir == PINDIR_INPUT ? "input" : "output",
1257 if( info.pFilter ) info.pFilter->Release();
1260 p_output_pin->Release();
1262 p_enumpins->Reset();
1264 while( (0 == mt_count) && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1268 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1270 if( info.pFilter ) info.pFilter->Release();
1271 if( info.dir == PINDIR_INPUT )
1273 p_output_pin->Release();
1276 msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1280 if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1282 AM_MEDIA_TYPE *p_mt;
1283 while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1285 int i_current_fourcc = GetFourCCFromMediaType(*p_mt);
1286 if( 0 != i_current_fourcc )
1288 if( p_mt->majortype == MEDIATYPE_Video )
1290 int i_current_width = p_mt->pbFormat ?
1291 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0;
1292 int i_current_height = p_mt->pbFormat ?
1293 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0;
1294 if( i_current_height < 0 )
1295 i_current_height = -i_current_height;
1297 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1298 "accepts chroma: %4.4s, width:%i, height:%i",
1299 (char *)&i_current_fourcc, i_current_width,
1302 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1303 ( !i_width || i_width == i_current_width ) &&
1304 ( !i_height || i_height == i_current_height ) &&
1305 (mt_count < mt_max) )
1308 mt[mt_count++] = *p_mt;
1312 FreeMediaType( *p_mt );
1315 else if( p_mt->majortype == MEDIATYPE_Audio )
1317 int i_current_channels =
1318 ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1319 int i_current_samplespersec =
1320 ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1321 int i_current_bitspersample =
1322 ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1324 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1325 "accepts format: %4.4s, channels:%i, "
1326 "samples/sec:%i bits/sample:%i",
1327 (char *)&i_current_fourcc, i_current_channels,
1328 i_current_samplespersec, i_current_bitspersample);
1330 if( (!i_channels || i_channels == i_current_channels) &&
1331 (!i_samplespersec ||
1332 i_samplespersec == i_current_samplespersec) &&
1333 (!i_bitspersample ||
1334 i_bitspersample == i_current_bitspersample) &&
1335 (mt_count < mt_max) )
1338 mt[mt_count++] = *p_mt;
1340 /* Pre-Configure the 1st match, Ugly */
1341 if( 1 == mt_count ) {
1342 /* Setup a few properties like the audio latency */
1343 IAMBufferNegotiation *p_ambuf;
1345 if( SUCCEEDED( p_output_pin->QueryInterface(
1346 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1348 ALLOCATOR_PROPERTIES AllocProp;
1349 AllocProp.cbAlign = -1;
1350 AllocProp.cbBuffer = i_current_channels *
1351 i_current_samplespersec *
1352 i_current_bitspersample / 8 / 10 ; /*100 ms of latency*/
1353 AllocProp.cbPrefix = -1;
1354 AllocProp.cBuffers = -1;
1355 p_ambuf->SuggestAllocatorProperties( &AllocProp );
1362 FreeMediaType( *p_mt );
1365 else if( p_mt->majortype == MEDIATYPE_Stream )
1367 msg_Dbg( p_this, "EnumDeviceCaps: MEDIATYPE_Stream" );
1369 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1370 (mt_count < mt_max) )
1373 mt[mt_count++] = *p_mt;
1374 i_fourcc = i_current_fourcc;
1378 FreeMediaType( *p_mt );
1384 "EnumDeviceCaps: input pin: unknown format" );
1385 FreeMediaType( *p_mt );
1391 "EnumDeviceCaps: input pin: unknown format" );
1392 FreeMediaType( *p_mt );
1394 CoTaskMemFree( (PVOID)p_mt );
1396 p_enummt->Release();
1399 p_output_pin->Release();
1402 p_enumpins->Release();
1406 /*****************************************************************************
1407 * Read: reads from the device into PES packets.
1408 *****************************************************************************
1409 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1411 *****************************************************************************/
1412 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer,
1415 access_sys_t *p_sys = p_input->p_access_data;
1416 dshow_stream_t *p_stream = NULL;
1417 byte_t *p_buf_orig = p_buffer;
1418 VLCMediaSample sample;
1422 if( p_sys->i_header_pos )
1424 /* First header of the stream */
1425 memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size );
1426 p_buffer += p_sys->i_header_size;
1427 p_sys->i_header_pos = 0;
1432 /* Get new sample/frame from next elementary stream.
1433 * We first loop through all the elementary streams and if all our
1434 * fifos are empty we block until we are signaled some new data has
1436 vlc_mutex_lock( &p_sys->lock );
1439 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1441 p_stream = p_sys->pp_streams[i_stream];
1442 if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1443 p_stream->p_capture_filter &&
1444 p_stream->p_capture_filter->CustomGetPin()
1445 ->CustomGetSample( &sample ) == S_OK )
1450 if( i_stream == p_sys->i_streams )
1451 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1453 p_stream = p_sys->pp_streams[i_stream];
1454 if( p_stream->p_capture_filter &&
1455 p_stream->p_capture_filter->CustomGetPin()
1456 ->CustomGetSample( &sample ) == S_OK )
1461 if( i_stream == p_sys->i_streams )
1463 /* No data available. Wait until some data has arrived */
1464 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1465 vlc_mutex_unlock( &p_sys->lock );
1469 vlc_mutex_unlock( &p_sys->lock );
1474 i_data_size = sample.p_sample->GetActualDataLength();
1475 sample.p_sample->GetPointer( &p_data );
1477 REFERENCE_TIME i_pts, i_end_date;
1478 HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1479 if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1483 if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1485 /* Use our data timestamp */
1486 i_pts = sample.i_timestamp;
1487 p_stream->b_pts = VLC_TRUE;
1492 msg_Dbg( p_input, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts );
1495 /* Create pseudo header */
1496 SetDWBE( &p_sys->p_header[0], i_stream );
1497 SetDWBE( &p_sys->p_header[4], i_data_size );
1498 SetQWBE( &p_sys->p_header[8], i_pts * 9 / 1000 );
1501 msg_Info( p_input, "access read %i data_size %i", i_len, i_data_size );
1504 /* First copy header */
1505 memcpy( p_buffer, p_sys->p_header, 16 /* header size */ );
1506 p_buffer += 16 /* header size */;
1508 /* Then copy stream data if any */
1509 if( !p_stream->b_invert )
1511 p_input->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1512 p_buffer += i_data_size;
1516 int i_width = p_stream->header.video.bmiHeader.biWidth;
1517 int i_height = p_stream->header.video.bmiHeader.biHeight;
1518 if( i_height < 0 ) i_height = - i_height;
1520 switch( p_stream->i_fourcc )
1522 case VLC_FOURCC( 'R', 'V', '1', '5' ):
1523 case VLC_FOURCC( 'R', 'V', '1', '6' ):
1526 case VLC_FOURCC( 'R', 'V', '2', '4' ):
1529 case VLC_FOURCC( 'R', 'V', '3', '2' ):
1530 case VLC_FOURCC( 'R', 'G', 'B', 'A' ):
1535 for( int i = i_height - 1; i >= 0; i-- )
1537 p_input->p_vlc->pf_memcpy( p_buffer,
1538 &p_data[i * i_width], i_width );
1540 p_buffer += i_width;
1544 sample.p_sample->Release();
1546 /* The caller got what he wanted */
1547 return p_buffer - p_buf_orig;
1550 return 0; /* never reached */
1553 /*****************************************************************************
1554 * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1555 *****************************************************************************
1556 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1558 *****************************************************************************/
1559 static ssize_t ReadCompressed( input_thread_t * p_input, byte_t * p_buffer,
1562 access_sys_t *p_sys = p_input->p_access_data;
1563 dshow_stream_t *p_stream = NULL;
1564 VLCMediaSample sample;
1568 /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1570 /* There must be only 1 elementary stream to produce a valid stream
1571 * of MPEG or DV data */
1572 p_stream = p_sys->pp_streams[0];
1576 if( p_input->b_die || p_input->b_error ) return 0;
1578 /* Get new sample/frame from the elementary stream (blocking). */
1579 vlc_mutex_lock( &p_sys->lock );
1581 if( p_stream->p_capture_filter->CustomGetPin()
1582 ->CustomGetSample( &sample ) != S_OK )
1584 /* No data available. Wait until some data has arrived */
1585 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1586 vlc_mutex_unlock( &p_sys->lock );
1590 vlc_mutex_unlock( &p_sys->lock );
1595 i_data_size = sample.p_sample->GetActualDataLength();
1596 sample.p_sample->GetPointer( &p_data );
1599 msg_Info( p_input, "access read %i data_size %i", i_len, i_data_size );
1601 i_data_size = __MIN( i_data_size, (int)i_len );
1603 p_input->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1605 sample.p_sample->Release();
1607 /* The caller got what he wanted */
1611 return 0; /* never reached */
1614 /*****************************************************************************
1615 * Demux: local prototypes
1616 *****************************************************************************/
1623 static int Demux ( input_thread_t * );
1625 /****************************************************************************
1627 ****************************************************************************/
1628 static int DemuxOpen( vlc_object_t *p_this )
1630 input_thread_t *p_input = (input_thread_t *)p_this;
1637 /* a little test to see if it's a dshow stream */
1638 if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 )
1640 msg_Warn( p_input, "dshow plugin discarded (cannot peek)" );
1641 return VLC_EGENERIC;
1644 if( memcmp( p_peek, ".dsh", 4 ) ||
1645 ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
1647 msg_Warn( p_input, "dshow plugin discarded (not a valid stream)" );
1648 return VLC_EGENERIC;
1651 vlc_mutex_lock( &p_input->stream.stream_lock );
1652 if( input_InitStream( p_input, 0 ) == -1)
1654 vlc_mutex_unlock( &p_input->stream.stream_lock );
1655 msg_Err( p_input, "cannot init stream" );
1656 return VLC_EGENERIC;
1658 p_input->stream.i_mux_rate = 0 / 50;
1659 vlc_mutex_unlock( &p_input->stream.stream_lock );
1661 p_input->pf_demux = Demux;
1662 p_input->pf_demux_control = demux_vaControlDefault;
1663 p_input->p_demux_data = p_sys =
1664 (demux_sys_t *)malloc( sizeof( demux_sys_t ) );
1668 if( stream_Peek( p_input->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
1670 msg_Err( p_input, "dshow plugin discarded (cannot peek)" );
1671 return VLC_EGENERIC;
1675 for( i = 0; i < i_es; i++ )
1679 if( !memcmp( p_peek, "auds", 4 ) )
1681 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1682 p_peek[6], p_peek[7] ) );
1684 fmt.audio.i_channels = GetDWBE( &p_peek[8] );
1685 fmt.audio.i_rate = GetDWBE( &p_peek[12] );
1686 fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
1687 fmt.audio.i_blockalign = fmt.audio.i_channels *
1688 fmt.audio.i_bitspersample / 8;
1689 fmt.i_bitrate = fmt.audio.i_channels *
1691 fmt.audio.i_bitspersample;
1693 msg_Dbg( p_input, "new audio es %d channels %dHz",
1694 fmt.audio.i_channels, fmt.audio.i_rate );
1696 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1697 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1698 p_sys->es[p_sys->i_es++] = es_out_Add( p_input->p_es_out, &fmt );
1700 else if( !memcmp( p_peek, "vids", 4 ) )
1702 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1703 p_peek[6], p_peek[7] ) );
1704 fmt.video.i_width = GetDWBE( &p_peek[8] );
1705 fmt.video.i_height = GetDWBE( &p_peek[12] );
1707 msg_Dbg( p_input, "added new video es %4.4s %dx%d",
1708 (char*)&fmt.i_codec,
1709 fmt.video.i_width, fmt.video.i_height );
1711 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1712 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1713 p_sys->es[p_sys->i_es++] = es_out_Add( p_input->p_es_out, &fmt );
1720 stream_Read( p_input->s, NULL, 8 + 20 * i_es );
1725 /****************************************************************************
1727 ****************************************************************************/
1728 static void DemuxClose( vlc_object_t *p_this )
1730 input_thread_t *p_input = (input_thread_t *)p_this;
1731 demux_sys_t *p_sys = p_input->p_demux_data;
1733 if( p_sys->i_es > 0 )
1740 /****************************************************************************
1742 ****************************************************************************/
1743 static int Demux( input_thread_t *p_input )
1745 demux_sys_t *p_sys = p_input->p_demux_data;
1754 if( stream_Peek( p_input->s, &p_peek, 16 ) < 16 )
1756 msg_Warn( p_input, "cannot peek (EOF ?)" );
1760 i_es = GetDWBE( &p_peek[0] );
1761 if( i_es < 0 || i_es >= p_sys->i_es )
1763 msg_Err( p_input, "cannot find ES" );
1767 i_size = GetDWBE( &p_peek[4] );
1768 i_pcr = GetQWBE( &p_peek[8] );
1770 if( ( p_block = stream_Block( p_input->s, 16 + i_size ) ) == NULL )
1772 msg_Warn( p_input, "cannot read data" );
1776 p_block->p_buffer += 16;
1777 p_block->i_buffer -= 16;
1779 /* Call the pace control. */
1780 input_ClockManageRef( p_input, p_input->stream.p_selected_program, i_pcr );
1783 p_block->i_pts = i_pcr <= 0 ? 0 :
1784 input_ClockGetTS( p_input, p_input->stream.p_selected_program, i_pcr );
1786 es_out_Send( p_input->p_es_out, p_sys->es[i_es], p_block );
1792 /*****************************************************************************
1793 * config variable callback
1794 *****************************************************************************/
1795 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1796 vlc_value_t newval, vlc_value_t oldval, void * )
1798 module_config_t *p_item;
1799 vlc_bool_t b_audio = VLC_FALSE;
1802 p_item = config_FindConfig( p_this, psz_name );
1803 if( !p_item ) return VLC_SUCCESS;
1805 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1807 /* Clear-up the current list */
1808 if( p_item->i_list )
1810 /* Keep the 2 first entries */
1811 for( i = 2; i < p_item->i_list; i++ )
1813 free( p_item->ppsz_list[i] );
1814 free( p_item->ppsz_list_text[i] );
1816 /* TODO: Remove when no more needed */
1817 p_item->ppsz_list[i] = NULL;
1818 p_item->ppsz_list_text[i] = NULL;
1822 /* Find list of devices */
1823 list<string> list_devices;
1825 /* Initialize OLE/COM */
1828 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1830 /* Uninitialize OLE/COM */
1833 if( !list_devices.size() ) return VLC_SUCCESS;
1836 (char **)realloc( p_item->ppsz_list,
1837 (list_devices.size()+3) * sizeof(char *) );
1838 p_item->ppsz_list_text =
1839 (char **)realloc( p_item->ppsz_list_text,
1840 (list_devices.size()+3) * sizeof(char *) );
1842 list<string>::iterator iter;
1843 for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1846 p_item->ppsz_list[i] = strdup( iter->c_str() );
1847 p_item->ppsz_list_text[i] = NULL;
1850 p_item->ppsz_list[i] = NULL;
1851 p_item->ppsz_list_text[i] = NULL;
1853 /* Signal change to the interface */
1854 p_item->b_dirty = VLC_TRUE;
1859 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1860 vlc_value_t newval, vlc_value_t oldval, void * )
1862 module_config_t *p_item;
1863 vlc_bool_t b_audio = VLC_FALSE;
1865 /* Initialize OLE/COM */
1868 p_item = config_FindConfig( p_this, psz_name );
1869 if( !p_item ) return VLC_SUCCESS;
1871 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1875 if( newval.psz_string && *newval.psz_string )
1877 devicename = newval.psz_string;
1881 /* If no device name was specified, pick the 1st one */
1882 list<string> list_devices;
1884 /* Enumerate devices */
1885 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1886 if( !list_devices.size() ) return VLC_EGENERIC;
1887 devicename = *list_devices.begin();
1890 IBaseFilter *p_device_filter =
1891 FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1892 if( p_device_filter )
1894 PropertiesPage( p_this, p_device_filter, NULL, b_audio );
1898 /* Uninitialize OLE/COM */
1901 msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
1902 return VLC_EGENERIC;
1905 /* Uninitialize OLE/COM */
1911 static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid )
1913 if( cauuid->cElems > 0 )
1915 HWND hwnd_desktop = ::GetDesktopWindow();
1917 OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
1918 cauuid->cElems, cauuid->pElems, 0, 0, NULL );
1920 CoTaskMemFree( cauuid->pElems );
1924 static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter,
1925 ICaptureGraphBuilder2 *p_capture_graph,
1926 vlc_bool_t b_audio )
1930 msg_Dbg( p_this, "Configuring Device Properties" );
1933 * Video or audio capture filter page
1935 ISpecifyPropertyPages *p_spec;
1937 HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages,
1941 if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
1943 ShowPropertyPage( p_device_filter, &cauuid );
1948 msg_Dbg( p_this, "looking for WDM Configuration Pages" );
1950 if( p_capture_graph )
1951 msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" );
1956 if( p_capture_graph && b_audio )
1958 IAMStreamConfig *p_SC;
1960 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1961 &MEDIATYPE_Audio, p_device_filter,
1962 IID_IAMStreamConfig,
1966 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
1970 hr = p_spec->GetPages( &cauuid );
1973 for( unsigned int c = 0; c < cauuid.cElems; c++ )
1975 ShowPropertyPage( p_SC, &cauuid );
1977 CoTaskMemFree( cauuid.pElems );
1988 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
1989 &MEDIATYPE_Audio, p_device_filter,
1990 IID_IAMTVAudio, (void **)&p_TVA );
1993 hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages,
1997 if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) )
1998 ShowPropertyPage(p_TVA, &cauuid);
2009 if( p_capture_graph && !b_audio )
2011 IAMStreamConfig *p_SC;
2013 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2014 &MEDIATYPE_Interleaved,
2016 IID_IAMStreamConfig,
2020 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2023 IID_IAMStreamConfig,
2029 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2033 if( SUCCEEDED( p_spec->GetPages(&cauuid) ) )
2035 ShowPropertyPage(p_SC, &cauuid);
2043 * Video crossbar, and a possible second crossbar
2045 IAMCrossbar *p_X, *p_X2;
2048 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2049 &MEDIATYPE_Interleaved,
2051 IID_IAMCrossbar, (void **)&p_X );
2054 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2063 hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF );
2066 hr = p_X->QueryInterface( IID_ISpecifyPropertyPages,
2070 hr = p_spec->GetPages(&cauuid);
2071 if( hr == S_OK && cauuid.cElems > 0 )
2073 ShowPropertyPage( p_X, &cauuid );
2078 hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL,
2079 p_XF, IID_IAMCrossbar,
2083 hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages,
2087 hr = p_spec->GetPages( &cauuid );
2090 ShowPropertyPage( p_X2, &cauuid );
2107 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2108 &MEDIATYPE_Interleaved,
2110 IID_IAMTVTuner, (void **)&p_TV );
2113 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2122 hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages,
2126 hr = p_spec->GetPages(&cauuid);
2129 ShowPropertyPage(p_TV, &cauuid);