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,
49 AM_MEDIA_TYPE *mt, size_t );
50 static bool ConnectFilters( access_t *, IBaseFilter *, CaptureFilter * );
52 static int FindDevicesCallback( vlc_object_t *, char const *,
53 vlc_value_t, vlc_value_t, void * );
54 static int ConfigDevicesCallback( vlc_object_t *, char const *,
55 vlc_value_t, vlc_value_t, void * );
57 static void PropertiesPage( vlc_object_t *, IBaseFilter *,
58 ICaptureGraphBuilder2 *, vlc_bool_t );
61 /* Debug only, use this to find out GUIDs */
63 UuidToString( (IID *)&IID_IAMBufferNegotiation, &p_st );
64 msg_Err( p_access, "BufferNegotiation: %s" , p_st );
88 /*****************************************************************************
90 *****************************************************************************/
91 static char *ppsz_vdev[] = { "", "none" };
92 static char *ppsz_vdev_text[] = { N_("Default"), N_("None") };
93 static char *ppsz_adev[] = { "", "none" };
94 static char *ppsz_adev_text[] = { N_("Default"), N_("None") };
96 #define CACHING_TEXT N_("Caching value in ms")
97 #define CACHING_LONGTEXT N_( \
98 "Allows you to modify the default caching value for DirectShow streams. " \
99 "This value should be set in milliseconds units." )
100 #define VDEV_TEXT N_("Video device name")
101 #define VDEV_LONGTEXT N_( \
102 "You can specify the name of the video device that will be used by the " \
103 "DirectShow plugin. If you don't specify anything, the default device " \
105 #define ADEV_TEXT N_("Audio device name")
106 #define ADEV_LONGTEXT N_( \
107 "You can specify the name of the audio device that will be used by the " \
108 "DirectShow plugin. If you don't specify anything, the default device " \
110 #define SIZE_TEXT N_("Video size")
111 #define SIZE_LONGTEXT N_( \
112 "You can specify the size of the video that will be displayed by the " \
113 "DirectShow plugin. If you don't specify anything the default size for " \
114 "your device will be used.")
115 #define CHROMA_TEXT N_("Video input chroma format")
116 #define CHROMA_LONGTEXT N_( \
117 "Force the DirectShow video input to use a specific chroma format " \
118 "(eg. I420 (default), RV24, etc.)")
119 #define CONFIG_TEXT N_("Device properties")
120 #define CONFIG_LONGTEXT N_( \
121 "Show the properties dialog of the selected device before starting the " \
124 static int AccessOpen ( vlc_object_t * );
125 static void AccessClose( vlc_object_t * );
127 static int DemuxOpen ( vlc_object_t * );
128 static void DemuxClose ( vlc_object_t * );
131 set_shortname( _("DirectShow") );
132 set_description( _("DirectShow input") );
133 add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL,
134 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
136 add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE);
137 change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback );
138 change_action_add( FindDevicesCallback, N_("Refresh list") );
139 change_action_add( ConfigDevicesCallback, N_("Configure") );
141 add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE);
142 change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback );
143 change_action_add( FindDevicesCallback, N_("Refresh list") );
144 change_action_add( ConfigDevicesCallback, N_("Configure") );
146 add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE);
148 add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
151 add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT,
154 add_shortcut( "dshow" );
155 set_capability( "access2", 0 );
156 set_callbacks( AccessOpen, AccessClose );
159 set_description( _("DirectShow demuxer") );
160 add_shortcut( "dshow" );
161 set_capability( "demux2", 200 );
162 set_callbacks( DemuxOpen, DemuxClose );
166 /****************************************************************************
167 * DirectShow elementary stream descriptor
168 ****************************************************************************/
169 typedef struct dshow_stream_t
172 IBaseFilter *p_device_filter;
173 CaptureFilter *p_capture_filter;
179 VIDEOINFOHEADER video;
188 /****************************************************************************
189 * Access descriptor declaration
190 ****************************************************************************/
191 #define MAX_CROSSBAR_DEPTH 10
193 typedef struct CrossbarRouteRec
196 LONG VideoInputIndex;
197 LONG VideoOutputIndex;
198 LONG AudioInputIndex;
199 LONG AudioOutputIndex;
205 /* These 2 must be left at the beginning */
209 IFilterGraph *p_graph;
210 ICaptureGraphBuilder2 *p_capture_graph_builder2;
211 IMediaControl *p_control;
213 int i_crossbar_route_depth;
214 CrossbarRoute crossbar_routes[MAX_CROSSBAR_DEPTH];
221 /* list of elementary streams */
222 dshow_stream_t **pp_streams;
224 int i_current_stream;
226 /* misc properties */
234 /****************************************************************************
235 * DirectShow utility functions
236 ****************************************************************************/
237 static void CreateDirectShowGraph( access_sys_t *p_sys )
239 p_sys->i_crossbar_route_depth = 0;
241 /* Create directshow filter graph */
242 if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
243 (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) )
245 /* Create directshow capture graph builder if available */
246 if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0,
247 CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
248 (void **)&p_sys->p_capture_graph_builder2 ) ) )
250 p_sys->p_capture_graph_builder2->
251 SetFiltergraph((IGraphBuilder *)p_sys->p_graph);
254 p_sys->p_graph->QueryInterface( IID_IMediaControl,
255 (void **)&p_sys->p_control );
259 static void DeleteCrossbarRoutes( access_sys_t *p_sys )
261 /* Remove crossbar filters from graph */
262 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
264 p_sys->crossbar_routes[i].pXbar->Release();
266 p_sys->i_crossbar_route_depth = 0;
269 static void DeleteDirectShowGraph( access_sys_t *p_sys )
271 DeleteCrossbarRoutes( p_sys );
273 /* Remove filters from graph */
274 for( int i = 0; i < p_sys->i_streams; i++ )
276 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter );
277 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter );
278 p_sys->pp_streams[i]->p_capture_filter->Release();
279 p_sys->pp_streams[i]->p_device_filter->Release();
282 /* Release directshow objects */
283 if( p_sys->p_control )
285 p_sys->p_control->Release();
286 p_sys->p_control = NULL;
288 if( p_sys->p_capture_graph_builder2 )
290 p_sys->p_capture_graph_builder2->Release();
291 p_sys->p_capture_graph_builder2 = NULL;
296 p_sys->p_graph->Release();
297 p_sys->p_graph = NULL;
301 static void ReleaseDirectShow( access_t *p_access )
303 access_sys_t *p_sys = p_access->p_sys;
305 msg_Dbg( p_access, "Releasing DirectShow");
307 DeleteDirectShowGraph( p_sys );
309 /* Uninitialize OLE/COM */
312 free( p_sys->p_header );
313 /* Remove filters from graph */
314 for( int i = 0; i < p_sys->i_streams; i++ )
316 delete p_sys->pp_streams[i];
318 free( p_sys->pp_streams );
322 /*****************************************************************************
323 * Open: open direct show device
324 *****************************************************************************/
325 static int AccessOpen( vlc_object_t *p_this )
327 access_t *p_access = (access_t*)p_this;
331 /* Get/parse options and open device(s) */
332 string vdevname, adevname;
333 int i_width = 0, i_height = 0, i_chroma = 0;
335 var_Create( p_access, "dshow-config", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
337 var_Create( p_access, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
338 var_Get( p_access, "dshow-vdev", &val );
339 if( val.psz_string ) vdevname = string( val.psz_string );
340 if( val.psz_string ) free( val.psz_string );
342 var_Create( p_access, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
343 var_Get( p_access, "dshow-adev", &val );
344 if( val.psz_string ) adevname = string( val.psz_string );
345 if( val.psz_string ) free( val.psz_string );
347 var_Create( p_access, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
348 var_Get( p_access, "dshow-size", &val );
349 if( val.psz_string && *val.psz_string )
351 if( !strcmp( val.psz_string, "subqcif" ) )
353 i_width = 128; i_height = 96;
355 else if( !strcmp( val.psz_string, "qsif" ) )
357 i_width = 160; i_height = 120;
359 else if( !strcmp( val.psz_string, "qcif" ) )
361 i_width = 176; i_height = 144;
363 else if( !strcmp( val.psz_string, "sif" ) )
365 i_width = 320; i_height = 240;
367 else if( !strcmp( val.psz_string, "cif" ) )
369 i_width = 352; i_height = 288;
371 else if( !strcmp( val.psz_string, "vga" ) )
373 i_width = 640; i_height = 480;
379 i_width = strtol( val.psz_string, &psz_parser, 0 );
380 if( *psz_parser == 'x' || *psz_parser == 'X')
382 i_height = strtol( psz_parser + 1, &psz_parser, 0 );
384 msg_Dbg( p_access, "Width x Height %dx%d", i_width, i_height );
387 if( val.psz_string ) free( val.psz_string );
389 var_Create( p_access, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
390 var_Get( p_access, "dshow-chroma", &val );
391 if( val.psz_string && strlen( val.psz_string ) >= 4 )
393 i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1],
394 val.psz_string[2], val.psz_string[3] );
396 if( val.psz_string ) free( val.psz_string );
399 p_access->pf_read = AccessRead;
400 p_access->pf_block = NULL;
401 p_access->pf_control = AccessControl;
402 p_access->pf_seek = NULL;
403 p_access->info.i_update = 0;
404 p_access->info.i_size = 0;
405 p_access->info.i_pos = 0;
406 p_access->info.b_eof = VLC_FALSE;
407 p_access->info.i_title = 0;
408 p_access->info.i_seekpoint = 0;
409 p_access->p_sys = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
410 memset( p_sys, 0, sizeof( access_sys_t ) );
412 var_Create( p_access, "dshow-caching",
413 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
415 /* Initialize OLE/COM */
418 /* Initialize some data */
419 p_sys->i_streams = 0;
420 p_sys->pp_streams = (dshow_stream_t **)malloc( 1 );
421 p_sys->i_width = i_width;
422 p_sys->i_height = i_height;
423 p_sys->i_chroma = i_chroma;
424 p_sys->b_audio = VLC_TRUE;
428 p_sys->i_header_size = 8;
429 p_sys->p_header = (uint8_t *)malloc( p_sys->i_header_size );
430 memcpy( &p_sys->p_header[0], ".dsh", 4 );
431 SetDWBE( &p_sys->p_header[4], 1 );
432 p_sys->i_header_pos = p_sys->i_header_size;
434 p_sys->p_graph = NULL;
435 p_sys->p_capture_graph_builder2 = NULL;
436 p_sys->p_control = NULL;
438 /* Build directshow graph */
439 CreateDirectShowGraph( p_sys );
441 if( OpenDevice( p_access, vdevname, 0 ) != VLC_SUCCESS )
443 msg_Err( p_access, "can't open video");
446 if( p_sys->b_audio && OpenDevice( p_access, adevname, 1 ) != VLC_SUCCESS )
448 msg_Err( p_access, "can't open audio");
451 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
453 IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
454 LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex;
455 LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex;
456 LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex;
457 LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex;
459 if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) )
461 msg_Dbg( p_access, "Crossbar at depth %d, Routed video "
462 "ouput %ld to video input %ld", i, VideoOutputIndex,
465 if( AudioOutputIndex != -1 && AudioInputIndex != -1 )
467 if( SUCCEEDED( pXbar->Route(AudioOutputIndex,
470 msg_Dbg(p_access, "Crossbar at depth %d, Routed audio "
471 "ouput %ld to audio input %ld", i,
472 AudioOutputIndex, AudioInputIndex );
478 if( !p_sys->i_streams )
480 ReleaseDirectShow( p_access );
484 /* Initialize some data */
485 p_sys->i_current_stream = 0;
486 p_sys->i_mtu += p_sys->i_header_size + 16 /* data header size */;
487 vlc_mutex_init( p_access, &p_sys->lock );
488 vlc_cond_init( p_access, &p_sys->wait );
490 msg_Dbg( p_access, "Playing...");
492 /* Everything is ready. Let's rock baby */
493 p_sys->p_control->Run();
498 /*****************************************************************************
499 * AccessClose: close device
500 *****************************************************************************/
501 static void AccessClose( vlc_object_t *p_this )
503 access_t *p_access = (access_t *)p_this;
504 access_sys_t *p_sys = p_access->p_sys;
506 /* Stop capturing stuff */
507 p_sys->p_control->Stop();
509 ReleaseDirectShow( p_access );
512 /*****************************************************************************
514 *****************************************************************************/
515 static int AccessControl( access_t *p_access, int i_query, va_list args )
517 access_sys_t *p_sys = p_access->p_sys;
525 case ACCESS_CAN_SEEK:
526 case ACCESS_CAN_FASTSEEK:
527 case ACCESS_CAN_PAUSE:
528 case ACCESS_CAN_CONTROL_PACE:
529 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
530 *pb_bool = VLC_FALSE;
535 pi_int = (int*)va_arg( args, int * );
536 *pi_int = p_sys->i_mtu;
539 case ACCESS_GET_PTS_DELAY:
540 pi_64 = (int64_t*)va_arg( args, int64_t * );
541 *pi_64 = (int64_t)var_GetInteger( p_access, "dshow-caching" ) *
546 case ACCESS_SET_PAUSE_STATE:
547 case ACCESS_GET_TITLE_INFO:
548 case ACCESS_SET_TITLE:
549 case ACCESS_SET_SEEKPOINT:
550 case ACCESS_SET_PRIVATE_ID_STATE:
554 msg_Warn( p_access, "unimplemented query in control" );
561 /****************************************************************************
562 * RouteCrossbars (Does not AddRef the returned *Pin)
563 ****************************************************************************/
564 static HRESULT GetCrossbarIPinAtIndex( IAMCrossbar *pXbar, LONG PinIndex,
565 BOOL IsInputPin, IPin ** ppPin )
567 LONG cntInPins, cntOutPins;
569 IBaseFilter *pFilter = NULL;
573 if( !pXbar || !ppPin ) return E_POINTER;
577 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) return E_FAIL;
579 LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins;
581 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
583 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
586 while( pins->Next(1, &pP, &n) == S_OK )
601 return *ppPin ? S_OK : E_FAIL;
604 /****************************************************************************
605 * GetCrossbarIndexFromIPin: Find corresponding index of an IPin on a crossbar
606 ****************************************************************************/
607 static HRESULT GetCrossbarIndexFromIPin( IAMCrossbar * pXbar, LONG * PinIndex,
608 BOOL IsInputPin, IPin * pPin )
610 LONG cntInPins, cntOutPins;
612 IBaseFilter *pFilter = NULL;
617 if(!pXbar || !PinIndex || !pPin )
620 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) )
623 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
625 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
629 while( pins->Next(1, &pP, &n) == S_OK )
634 *PinIndex = IsInputPin ? i : i - cntInPins;
645 return fOK ? S_OK : E_FAIL;
648 /****************************************************************************
650 ****************************************************************************/
651 static HRESULT FindCrossbarRoutes( access_t *p_access, IPin *p_input_pin,
652 LONG physicalType, int depth = 0 )
654 access_sys_t *p_sys = p_access->p_sys;
655 HRESULT result = S_FALSE;
658 if( FAILED(p_input_pin->ConnectedTo(&p_output_pin)) ) return S_FALSE;
660 // It is connected, so now find out if the filter supports IAMCrossbar
662 if( FAILED(p_output_pin->QueryPinInfo(&pinInfo)) ||
663 PINDIR_OUTPUT != pinInfo.dir )
665 p_output_pin->Release ();
669 IAMCrossbar *pXbar=0;
670 if( FAILED(pinInfo.pFilter->QueryInterface(IID_IAMCrossbar,
673 pinInfo.pFilter->Release();
674 p_output_pin->Release ();
678 LONG inputPinCount, outputPinCount;
679 if( FAILED(pXbar->get_PinCounts(&outputPinCount, &inputPinCount)) )
682 pinInfo.pFilter->Release();
683 p_output_pin->Release ();
687 LONG inputPinIndexRelated, outputPinIndexRelated;
688 LONG inputPinPhysicalType, outputPinPhysicalType;
689 LONG inputPinIndex, outputPinIndex;
690 if( FAILED(GetCrossbarIndexFromIPin( pXbar, &outputPinIndex,
691 FALSE, p_output_pin )) ||
692 FAILED(pXbar->get_CrossbarPinInfo( FALSE, outputPinIndex,
693 &outputPinIndexRelated,
694 &outputPinPhysicalType )) )
697 pinInfo.pFilter->Release();
698 p_output_pin->Release ();
703 // for all input pins
705 for( inputPinIndex = 0; S_OK != result && inputPinIndex < inputPinCount;
708 if( FAILED(pXbar->get_CrossbarPinInfo( TRUE, inputPinIndex,
709 &inputPinIndexRelated, &inputPinPhysicalType )) ) continue;
711 // Is the pin a video pin?
712 if( inputPinPhysicalType != physicalType ) continue;
715 if( FAILED(pXbar->CanRoute(outputPinIndex, inputPinIndex)) ) continue;
718 if( FAILED(GetCrossbarIPinAtIndex( pXbar, inputPinIndex,
719 TRUE, &pPin)) ) continue;
721 result = FindCrossbarRoutes( p_access, pPin, physicalType, depth+1 );
722 if( S_OK == result || (S_FALSE == result &&
723 physicalType == inputPinPhysicalType &&
724 (p_sys->i_crossbar_route_depth = depth+1) < MAX_CROSSBAR_DEPTH) )
729 // remember crossbar route
730 p_sys->crossbar_routes[depth].pXbar = pXbar;
731 p_sys->crossbar_routes[depth].VideoInputIndex = inputPinIndex;
732 p_sys->crossbar_routes[depth].VideoOutputIndex = outputPinIndex;
733 p_sys->crossbar_routes[depth].AudioInputIndex = inputPinIndexRelated;
734 p_sys->crossbar_routes[depth].AudioOutputIndex = outputPinIndexRelated;
736 msg_Dbg( p_access, "Crossbar at depth %d, Found Route For "
737 "ouput %ld (type %ld) to input %ld (type %ld)", depth,
738 outputPinIndex, outputPinPhysicalType, inputPinIndex,
739 inputPinPhysicalType );
746 pinInfo.pFilter->Release();
747 p_output_pin->Release ();
752 /****************************************************************************
754 ****************************************************************************/
755 static bool ConnectFilters( access_t *p_access, IBaseFilter *p_filter,
756 CaptureFilter *p_capture_filter )
758 access_sys_t *p_sys = p_access->p_sys;
759 CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
761 AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
763 if( p_sys->p_capture_graph_builder2 )
765 if( FAILED(p_sys->p_capture_graph_builder2->
766 RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
768 (IBaseFilter *)p_capture_filter )) )
773 // Sort out all the possible video inputs
774 // The class needs to be given the capture filters ANALOGVIDEO input pin
776 if( mediaType.majortype == MEDIATYPE_Video &&
777 SUCCEEDED(p_filter->EnumPins(&pins)) )
783 IKsPropertySet *pKs=0;
787 while( !Found && (S_OK == pins->Next(1, &pP, &n)) )
789 if(S_OK == pP->QueryPinInfo(&pinInfo))
791 if(pinInfo.dir == PINDIR_INPUT)
793 // is this pin an ANALOGVIDEOIN input pin?
794 if( pP->QueryInterface(IID_IKsPropertySet,
795 (void **)&pKs) == S_OK )
797 if( pKs->Get(AMPROPSETID_Pin,
798 AMPROPERTY_PIN_CATEGORY, NULL, 0,
799 &guid, sizeof(GUID), &dw) == S_OK )
801 if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
803 // recursively search crossbar routes
804 FindCrossbarRoutes( p_access, pP,
805 PhysConn_Video_Tuner );
813 pinInfo.pFilter->Release();
823 IEnumPins *p_enumpins;
826 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
828 while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
830 PIN_DIRECTION pin_dir;
831 p_pin->QueryDirection( &pin_dir );
833 if( pin_dir == PINDIR_OUTPUT &&
834 p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
838 p_enumpins->Release();
844 p_enumpins->Release();
850 ** get fourcc priority from arbritary preference, the higher the better
852 static int GetFourCCPriority(int i_fourcc)
856 case VLC_FOURCC('I','4','2','0'):
857 case VLC_FOURCC('f','l','3','2'):
862 case VLC_FOURCC('Y','V','1','2'):
863 case VLC_FOURCC('a','r','a','w'):
868 case VLC_FOURCC('R','V','2','4'):
873 case VLC_FOURCC('Y','U','Y','2'):
874 case VLC_FOURCC('R','V','3','2'):
875 case VLC_FOURCC('R','G','B','A'):
883 #define MAX_MEDIA_TYPES 32
885 static int OpenDevice( access_t *p_access, string devicename,
888 access_sys_t *p_sys = p_access->p_sys;
890 /* See if device is already opened */
891 for( int i = 0; i < p_sys->i_streams; i++ )
893 if( p_sys->pp_streams[i]->devicename == devicename )
900 list<string> list_devices;
902 /* Enumerate devices and display their names */
903 FindCaptureDevice( (vlc_object_t *)p_access, NULL, &list_devices, b_audio );
905 if( !list_devices.size() )
908 list<string>::iterator iter;
909 for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
910 msg_Dbg( p_access, "found device: %s", iter->c_str() );
912 /* If no device name was specified, pick the 1st one */
913 if( devicename.size() == 0 )
915 devicename = *list_devices.begin();
918 // Use the system device enumerator and class enumerator to find
919 // a capture/preview device, such as a desktop USB video camera.
920 IBaseFilter *p_device_filter =
921 FindCaptureDevice( (vlc_object_t *)p_access, &devicename,
923 if( p_device_filter )
924 msg_Dbg( p_access, "using device: %s", devicename.c_str() );
927 msg_Err( p_access, "can't use device: %s, unsupported device type",
928 devicename.c_str() );
933 AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
935 size_t mt_count = EnumDeviceCaps( (vlc_object_t *)p_access,
936 p_device_filter, p_sys->i_chroma,
937 p_sys->i_width, p_sys->i_height,
938 0, 0, 0, media_types, MAX_MEDIA_TYPES );
942 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
944 // Order and copy returned media types according to arbitrary
946 for( size_t c=0; c<mt_count; c++ )
949 GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
950 size_t slot_copy = c;
951 for( size_t d=c+1; d<mt_count; d++ )
954 GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
955 if( priority > slot_priority )
957 slot_priority = priority;
963 mt[c] = media_types[slot_copy];
964 media_types[slot_copy] = media_types[c];
968 mt[c] = media_types[c];
972 else if( ! b_audio ) {
973 // Use default video media type
977 mtr.majortype = MEDIATYPE_Video;
978 mtr.subtype = MEDIASUBTYPE_I420;
979 mtr.bFixedSizeSamples = TRUE;
980 mtr.bTemporalCompression = FALSE;
983 mtr.formattype = FORMAT_VideoInfo;
984 mtr.cbFormat = sizeof(vh);
985 mtr.pbFormat = (BYTE *)&vh;
987 memset(&vh, 0, sizeof(vh));
989 vh.bmiHeader.biSize = sizeof(vh.bmiHeader);
990 vh.bmiHeader.biWidth = p_sys->i_width > 0 ? p_sys->i_width: 320;
991 vh.bmiHeader.biHeight = p_sys->i_height > 0 ? p_sys->i_height : 240;
992 vh.bmiHeader.biPlanes = 1;
993 vh.bmiHeader.biBitCount = 24;
994 vh.bmiHeader.biCompression = VLC_FOURCC('I','4','2','0');
995 vh.bmiHeader.biSizeImage = p_sys->i_width * 24 * p_sys->i_height / 8;
997 msg_Warn( p_access, "device %s using built-in video media type",
998 devicename.c_str() );
1001 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1002 CopyMediaType(mt, &mtr);
1005 // Use default audio media type
1009 mtr.majortype = MEDIATYPE_Audio;
1010 mtr.subtype = MEDIASUBTYPE_PCM;
1011 mtr.bFixedSizeSamples = TRUE;
1012 mtr.bTemporalCompression = FALSE;
1013 mtr.lSampleSize = 0;
1015 mtr.formattype = FORMAT_WaveFormatEx;
1016 mtr.cbFormat = sizeof(wf);
1017 mtr.pbFormat = (BYTE *)&wf;
1019 memset(&wf, 0, sizeof(wf));
1021 wf.wFormatTag = WAVE_FORMAT_PCM;
1023 wf.nSamplesPerSec = 44100;
1024 wf.wBitsPerSample = 16;
1025 wf.nBlockAlign = wf.nSamplesPerSec * wf.wBitsPerSample / 8;
1026 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
1029 msg_Warn( p_access, "device %s using built-in audio media type",
1030 devicename.c_str() );
1033 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1034 CopyMediaType(mt, &mtr);
1037 /* Create and add our capture filter */
1038 CaptureFilter *p_capture_filter =
1039 new CaptureFilter( p_access, mt, mt_count );
1040 p_sys->p_graph->AddFilter( p_capture_filter, 0 );
1042 /* Add the device filter to the graph (seems necessary with VfW before
1043 * accessing pin attributes). */
1044 p_sys->p_graph->AddFilter( p_device_filter, 0 );
1046 /* Attempt to connect one of this device's capture output pins */
1047 msg_Dbg( p_access, "connecting filters" );
1048 if( ConnectFilters( p_access, p_device_filter, p_capture_filter ) )
1051 msg_Dbg( p_access, "filters connected successfully !" );
1053 dshow_stream_t dshow_stream;
1054 dshow_stream.b_pts = VLC_FALSE;
1056 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1058 /* Show properties. Done here so the VLC stream is setup with the
1059 * proper parameters. */
1061 var_Get( p_access, "dshow-config", &val );
1064 PropertiesPage( VLC_OBJECT(p_access), p_device_filter,
1065 p_sys->p_capture_graph_builder2,
1066 dshow_stream.mt.majortype == MEDIATYPE_Audio );
1070 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1072 dshow_stream.i_fourcc = GetFourCCFromMediaType(dshow_stream.mt);
1073 if( 0 != dshow_stream.i_fourcc )
1075 if( dshow_stream.mt.majortype == MEDIATYPE_Video )
1077 dshow_stream.header.video =
1078 *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
1080 int i_height = dshow_stream.header.video.bmiHeader.biHeight;
1082 if( !dshow_stream.header.video.bmiHeader.biCompression )
1084 /* RGB DIB are coded from bottom to top */
1085 i_height = - i_height;
1088 /* Check if we are dealing with a DV stream */
1089 if( dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','l') ||
1090 dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','d') ||
1091 dshow_stream.i_fourcc == VLC_FOURCC('d','v','h','d') )
1093 p_access->pf_read = ReadCompressed;
1094 if( !p_access->psz_demux || !*p_access->psz_demux )
1096 p_access->psz_demux = strdup( "rawdv" );
1098 p_sys->b_audio = VLC_FALSE;
1101 /* Check if we are dealing with an MPEG video stream */
1102 if( dshow_stream.i_fourcc == VLC_FOURCC('m','p','2','v') )
1104 p_access->pf_read = ReadCompressed;
1105 if( !p_access->psz_demux || !*p_access->psz_demux )
1107 p_access->psz_demux = "mpgv";
1109 p_sys->b_audio = VLC_FALSE;
1112 /* Add video stream to header */
1113 p_sys->i_header_size += 20;
1114 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1115 p_sys->i_header_size );
1116 memcpy( &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
1117 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1118 &dshow_stream.i_fourcc, 4 );
1119 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1120 dshow_stream.header.video.bmiHeader.biWidth );
1121 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height );
1122 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 );
1123 p_sys->i_header_pos = p_sys->i_header_size;
1125 /* Greatly simplifies the reading routine */
1126 int i_mtu = dshow_stream.header.video.bmiHeader.biWidth *
1128 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1131 else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
1133 dshow_stream.header.audio =
1134 *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
1136 /* Add audio stream to header */
1137 p_sys->i_header_size += 20;
1138 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1139 p_sys->i_header_size );
1140 memcpy( &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
1141 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1142 &dshow_stream.i_fourcc, 4 );
1143 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1144 dshow_stream.header.audio.nChannels );
1145 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12],
1146 dshow_stream.header.audio.nSamplesPerSec );
1147 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16],
1148 dshow_stream.header.audio.wBitsPerSample );
1149 p_sys->i_header_pos = p_sys->i_header_size;
1151 /* Greatly simplifies the reading routine */
1152 IAMBufferNegotiation *p_ambuf;
1156 p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin );
1157 if( SUCCEEDED( p_pin->QueryInterface(
1158 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1160 ALLOCATOR_PROPERTIES AllocProp;
1161 memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) );
1162 p_ambuf->GetAllocatorProperties( &AllocProp );
1164 i_mtu = AllocProp.cbBuffer;
1169 i_mtu = dshow_stream.header.audio.nSamplesPerSec *
1170 dshow_stream.header.audio.nChannels *
1171 dshow_stream.header.audio.wBitsPerSample / 8;
1174 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1177 else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
1179 msg_Dbg( p_access, "MEDIATYPE_Stream" );
1181 msg_Dbg( p_access, "selected stream pin accepts format: %4.4s",
1182 (char *)&dshow_stream.i_fourcc);
1184 p_sys->b_audio = VLC_FALSE;
1185 p_sys->i_header_size = 0;
1186 p_sys->i_header_pos = 0;
1189 p_access->pf_read = ReadCompressed;
1193 msg_Dbg( p_access, "unknown stream majortype" );
1197 /* Add directshow elementary stream to our list */
1198 dshow_stream.p_device_filter = p_device_filter;
1199 dshow_stream.p_capture_filter = p_capture_filter;
1201 p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams,
1202 sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) );
1203 p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1204 *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1205 SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams );
1212 /* Remove filters from graph */
1213 p_sys->p_graph->RemoveFilter( p_device_filter );
1214 p_sys->p_graph->RemoveFilter( p_capture_filter );
1216 /* Release objects */
1217 p_device_filter->Release();
1218 p_capture_filter->Release();
1220 return VLC_EGENERIC;
1223 static IBaseFilter *
1224 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1225 list<string> *p_listdevices, vlc_bool_t b_audio )
1227 IBaseFilter *p_base_filter = NULL;
1228 IMoniker *p_moniker = NULL;
1232 /* Create the system device enumerator */
1233 ICreateDevEnum *p_dev_enum = NULL;
1235 hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1236 IID_ICreateDevEnum, (void **)&p_dev_enum );
1239 msg_Err( p_this, "failed to create the device enumerator (0x%lx)", hr);
1243 /* Create an enumerator for the video capture devices */
1244 IEnumMoniker *p_class_enum = NULL;
1246 hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1249 hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1251 p_dev_enum->Release();
1254 msg_Err( p_this, "failed to create the class enumerator (0x%lx)", hr );
1258 /* If there are no enumerators for the requested type, then
1259 * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1260 if( p_class_enum == NULL )
1262 msg_Err( p_this, "no capture device was detected" );
1266 /* Enumerate the devices */
1268 /* Note that if the Next() call succeeds but there are no monikers,
1269 * it will return S_FALSE (which is not a failure). Therefore, we check
1270 * that the return code is S_OK instead of using SUCCEEDED() macro. */
1272 while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1274 /* Getting the property page to get the device name */
1275 IPropertyBag *p_bag;
1276 hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1282 hr = p_bag->Read( L"FriendlyName", &var, NULL );
1286 int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
1287 char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
1288 WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
1289 i_convert, NULL, NULL );
1290 SysFreeString(var.bstrVal);
1292 if( p_listdevices ) p_listdevices->push_back( p_buf );
1294 if( p_devicename && *p_devicename == string(p_buf) )
1296 /* Bind Moniker to a filter object */
1297 hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1298 (void **)&p_base_filter );
1301 msg_Err( p_this, "couldn't bind moniker to filter "
1302 "object (0x%lx)", hr );
1303 p_moniker->Release();
1304 p_class_enum->Release();
1307 p_moniker->Release();
1308 p_class_enum->Release();
1309 return p_base_filter;
1314 p_moniker->Release();
1317 p_class_enum->Release();
1321 static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter,
1322 int i_fourcc, int i_width, int i_height,
1323 int i_channels, int i_samplespersec,
1324 int i_bitspersample, AM_MEDIA_TYPE *mt,
1327 IEnumPins *p_enumpins;
1329 IEnumMediaTypes *p_enummt;
1330 size_t mt_count = 0;
1332 if( FAILED(p_filter->EnumPins( &p_enumpins )) )
1334 msg_Dbg( p_this, "EnumDeviceCaps failed: no pin enumeration !");
1338 while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1342 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1344 msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1345 info.dir == PINDIR_INPUT ? "input" : "output",
1347 if( info.pFilter ) info.pFilter->Release();
1350 p_output_pin->Release();
1353 p_enumpins->Reset();
1355 while( !mt_count && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1359 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1361 if( info.pFilter ) info.pFilter->Release();
1362 if( info.dir == PINDIR_INPUT )
1364 p_output_pin->Release();
1367 msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1371 if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1373 AM_MEDIA_TYPE *p_mt;
1374 while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1376 int i_current_fourcc = GetFourCCFromMediaType(*p_mt);
1377 if( 0 != i_current_fourcc )
1379 if( p_mt->majortype == MEDIATYPE_Video )
1381 int i_current_width = p_mt->pbFormat ?
1382 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0;
1383 int i_current_height = p_mt->pbFormat ?
1384 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0;
1385 if( i_current_height < 0 )
1386 i_current_height = -i_current_height;
1388 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1389 "accepts chroma: %4.4s, width:%i, height:%i",
1390 (char *)&i_current_fourcc, i_current_width,
1393 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1394 ( !i_width || i_width == i_current_width ) &&
1395 ( !i_height || i_height == i_current_height ) &&
1396 (mt_count < mt_max) )
1399 mt[mt_count++] = *p_mt;
1403 FreeMediaType( *p_mt );
1406 else if( p_mt->majortype == MEDIATYPE_Audio )
1408 int i_current_channels =
1409 ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1410 int i_current_samplespersec =
1411 ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1412 int i_current_bitspersample =
1413 ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1415 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1416 "accepts format: %4.4s, channels:%i, "
1417 "samples/sec:%i bits/sample:%i",
1418 (char *)&i_current_fourcc, i_current_channels,
1419 i_current_samplespersec, i_current_bitspersample);
1421 if( (!i_channels || i_channels == i_current_channels) &&
1422 (!i_samplespersec ||
1423 i_samplespersec == i_current_samplespersec) &&
1424 (!i_bitspersample ||
1425 i_bitspersample == i_current_bitspersample) &&
1426 (mt_count < mt_max) )
1429 mt[mt_count++] = *p_mt;
1431 /* Pre-Configure the 1st match, Ugly */
1432 if( 1 == mt_count ) {
1433 /* Setup a few properties like the audio latency */
1434 IAMBufferNegotiation *p_ambuf;
1436 if( SUCCEEDED( p_output_pin->QueryInterface(
1437 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1439 ALLOCATOR_PROPERTIES AllocProp;
1440 AllocProp.cbAlign = -1;
1441 AllocProp.cbBuffer = i_current_channels *
1442 i_current_samplespersec *
1443 i_current_bitspersample / 8 / 10 ; /*100 ms of latency*/
1444 AllocProp.cbPrefix = -1;
1445 AllocProp.cBuffers = -1;
1446 p_ambuf->SuggestAllocatorProperties( &AllocProp );
1453 FreeMediaType( *p_mt );
1456 else if( p_mt->majortype == MEDIATYPE_Stream )
1458 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1459 (mt_count < mt_max) )
1462 mt[mt_count++] = *p_mt;
1463 i_fourcc = i_current_fourcc;
1467 FreeMediaType( *p_mt );
1473 "EnumDeviceCaps: input pin: unknown format" );
1474 FreeMediaType( *p_mt );
1480 "EnumDeviceCaps: input pin: unknown format" );
1481 FreeMediaType( *p_mt );
1483 CoTaskMemFree( (PVOID)p_mt );
1485 p_enummt->Release();
1488 p_output_pin->Release();
1491 p_enumpins->Release();
1495 /*****************************************************************************
1496 * AccessRead: reads from the device.
1497 *****************************************************************************
1498 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1500 *****************************************************************************/
1501 static int AccessRead( access_t *p_access, uint8_t *p_buffer, int i_len )
1503 access_sys_t *p_sys = p_access->p_sys;
1504 dshow_stream_t *p_stream = NULL;
1505 byte_t *p_buf_orig = p_buffer;
1506 VLCMediaSample sample;
1510 if( p_sys->i_header_pos )
1512 /* First header of the stream */
1513 memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size );
1514 p_buffer += p_sys->i_header_size;
1515 p_sys->i_header_pos = 0;
1520 /* Get new sample/frame from next elementary stream.
1521 * We first loop through all the elementary streams and if all our
1522 * fifos are empty we block until we are signaled some new data has
1524 vlc_mutex_lock( &p_sys->lock );
1527 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1529 p_stream = p_sys->pp_streams[i_stream];
1530 if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1531 p_stream->p_capture_filter &&
1532 p_stream->p_capture_filter->CustomGetPin()
1533 ->CustomGetSample( &sample ) == S_OK )
1538 if( i_stream == p_sys->i_streams )
1539 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1541 p_stream = p_sys->pp_streams[i_stream];
1542 if( p_stream->p_capture_filter &&
1543 p_stream->p_capture_filter->CustomGetPin()
1544 ->CustomGetSample( &sample ) == S_OK )
1549 if( i_stream == p_sys->i_streams )
1551 /* No data available. Wait until some data has arrived */
1552 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1553 vlc_mutex_unlock( &p_sys->lock );
1557 vlc_mutex_unlock( &p_sys->lock );
1562 i_data_size = sample.p_sample->GetActualDataLength();
1563 sample.p_sample->GetPointer( &p_data );
1565 REFERENCE_TIME i_pts, i_end_date;
1566 HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1567 if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1571 if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1573 /* Use our data timestamp */
1574 i_pts = sample.i_timestamp;
1575 p_stream->b_pts = VLC_TRUE;
1580 msg_Dbg( p_access, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts );
1583 /* Create pseudo header */
1584 SetDWBE( &p_sys->p_header[0], i_stream );
1585 SetDWBE( &p_sys->p_header[4], i_data_size );
1586 SetQWBE( &p_sys->p_header[8], i_pts / 10 );
1589 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1592 /* First copy header */
1593 memcpy( p_buffer, p_sys->p_header, 16 /* header size */ );
1594 p_buffer += 16 /* header size */;
1596 /* Then copy stream data if any */
1597 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1598 p_buffer += i_data_size;
1600 sample.p_sample->Release();
1602 /* The caller got what he wanted */
1603 return p_buffer - p_buf_orig;
1606 return 0; /* never reached */
1609 /*****************************************************************************
1610 * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1611 *****************************************************************************
1612 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1614 *****************************************************************************/
1615 static int ReadCompressed( access_t *p_access, uint8_t *p_buffer, int i_len )
1617 access_sys_t *p_sys = p_access->p_sys;
1618 dshow_stream_t *p_stream = NULL;
1619 VLCMediaSample sample;
1623 /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1625 /* There must be only 1 elementary stream to produce a valid stream
1626 * of MPEG or DV data */
1627 p_stream = p_sys->pp_streams[0];
1631 if( p_access->b_die || p_access->b_error ) return 0;
1633 /* Get new sample/frame from the elementary stream (blocking). */
1634 vlc_mutex_lock( &p_sys->lock );
1636 if( p_stream->p_capture_filter->CustomGetPin()
1637 ->CustomGetSample( &sample ) != S_OK )
1639 /* No data available. Wait until some data has arrived */
1640 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1641 vlc_mutex_unlock( &p_sys->lock );
1645 vlc_mutex_unlock( &p_sys->lock );
1650 i_data_size = sample.p_sample->GetActualDataLength();
1651 sample.p_sample->GetPointer( &p_data );
1654 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1656 i_data_size = __MIN( i_data_size, (int)i_len );
1658 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1660 sample.p_sample->Release();
1662 /* The caller got what he wanted */
1666 return 0; /* never reached */
1669 /*****************************************************************************
1670 * Demux: local prototypes
1671 *****************************************************************************/
1678 static int Demux ( demux_t * );
1679 static int DemuxControl( demux_t *, int, va_list );
1681 /****************************************************************************
1683 ****************************************************************************/
1684 static int DemuxOpen( vlc_object_t *p_this )
1686 demux_t *p_demux = (demux_t *)p_this;
1693 /* a little test to see if it's a dshow stream */
1694 if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
1696 msg_Warn( p_demux, "dshow plugin discarded (cannot peek)" );
1697 return VLC_EGENERIC;
1700 if( memcmp( p_peek, ".dsh", 4 ) ||
1701 ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
1703 msg_Warn( p_demux, "dshow plugin discarded (not a valid stream)" );
1704 return VLC_EGENERIC;
1707 p_demux->pf_demux = Demux;
1708 p_demux->pf_control = DemuxControl;
1709 p_demux->p_sys = p_sys = (demux_sys_t *)malloc( sizeof( demux_sys_t ) );
1713 if( stream_Peek( p_demux->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
1715 msg_Err( p_demux, "dshow plugin discarded (cannot peek)" );
1716 return VLC_EGENERIC;
1720 for( i = 0; i < i_es; i++ )
1724 if( !memcmp( p_peek, "auds", 4 ) )
1726 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1727 p_peek[6], p_peek[7] ) );
1729 fmt.audio.i_channels = GetDWBE( &p_peek[8] );
1730 fmt.audio.i_rate = GetDWBE( &p_peek[12] );
1731 fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
1732 fmt.audio.i_blockalign = fmt.audio.i_channels *
1733 fmt.audio.i_bitspersample / 8;
1734 fmt.i_bitrate = fmt.audio.i_channels *
1736 fmt.audio.i_bitspersample;
1738 msg_Dbg( p_demux, "new audio es %d channels %dHz",
1739 fmt.audio.i_channels, fmt.audio.i_rate );
1741 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1742 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1743 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1745 else if( !memcmp( p_peek, "vids", 4 ) )
1747 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1748 p_peek[6], p_peek[7] ) );
1749 fmt.video.i_width = GetDWBE( &p_peek[8] );
1750 fmt.video.i_height = GetDWBE( &p_peek[12] );
1752 msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
1753 (char*)&fmt.i_codec,
1754 fmt.video.i_width, fmt.video.i_height );
1756 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1757 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1758 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1765 stream_Read( p_demux->s, NULL, 8 + 20 * i_es );
1770 /****************************************************************************
1772 ****************************************************************************/
1773 static void DemuxClose( vlc_object_t *p_this )
1775 demux_t *p_demux = (demux_t *)p_this;
1776 demux_sys_t *p_sys = p_demux->p_sys;
1778 if( p_sys->i_es > 0 )
1785 /****************************************************************************
1787 ****************************************************************************/
1788 static int Demux( demux_t *p_demux )
1790 demux_sys_t *p_sys = p_demux->p_sys;
1799 if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
1801 msg_Warn( p_demux, "cannot peek (EOF ?)" );
1805 i_es = GetDWBE( &p_peek[0] );
1806 if( i_es < 0 || i_es >= p_sys->i_es )
1808 msg_Err( p_demux, "cannot find ES" );
1812 i_size = GetDWBE( &p_peek[4] );
1813 i_pts = GetQWBE( &p_peek[8] );
1815 if( ( p_block = stream_Block( p_demux->s, 16 + i_size ) ) == NULL )
1817 msg_Warn( p_demux, "cannot read data" );
1821 p_block->p_buffer += 16;
1822 p_block->i_buffer -= 16;
1824 p_block->i_dts = p_block->i_pts = i_pts;
1826 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pts > 0 ? i_pts : 0 );
1827 es_out_Send( p_demux->out, p_sys->es[i_es], p_block );
1832 /****************************************************************************
1834 ****************************************************************************/
1835 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
1837 return demux2_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
1840 /*****************************************************************************
1841 * config variable callback
1842 *****************************************************************************/
1843 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1844 vlc_value_t newval, vlc_value_t oldval, void * )
1846 module_config_t *p_item;
1847 vlc_bool_t b_audio = VLC_FALSE;
1850 p_item = config_FindConfig( p_this, psz_name );
1851 if( !p_item ) return VLC_SUCCESS;
1853 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1855 /* Clear-up the current list */
1856 if( p_item->i_list )
1858 /* Keep the 2 first entries */
1859 for( i = 2; i < p_item->i_list; i++ )
1861 free( p_item->ppsz_list[i] );
1862 free( p_item->ppsz_list_text[i] );
1864 /* TODO: Remove when no more needed */
1865 p_item->ppsz_list[i] = NULL;
1866 p_item->ppsz_list_text[i] = NULL;
1870 /* Find list of devices */
1871 list<string> list_devices;
1873 /* Initialize OLE/COM */
1876 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1878 /* Uninitialize OLE/COM */
1881 if( !list_devices.size() ) return VLC_SUCCESS;
1884 (char **)realloc( p_item->ppsz_list,
1885 (list_devices.size()+3) * sizeof(char *) );
1886 p_item->ppsz_list_text =
1887 (char **)realloc( p_item->ppsz_list_text,
1888 (list_devices.size()+3) * sizeof(char *) );
1890 list<string>::iterator iter;
1891 for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1894 p_item->ppsz_list[i] = strdup( iter->c_str() );
1895 p_item->ppsz_list_text[i] = NULL;
1898 p_item->ppsz_list[i] = NULL;
1899 p_item->ppsz_list_text[i] = NULL;
1901 /* Signal change to the interface */
1902 p_item->b_dirty = VLC_TRUE;
1907 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1908 vlc_value_t newval, vlc_value_t oldval, void * )
1910 module_config_t *p_item;
1911 vlc_bool_t b_audio = VLC_FALSE;
1913 /* Initialize OLE/COM */
1916 p_item = config_FindConfig( p_this, psz_name );
1917 if( !p_item ) return VLC_SUCCESS;
1919 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1923 if( newval.psz_string && *newval.psz_string )
1925 devicename = newval.psz_string;
1929 /* If no device name was specified, pick the 1st one */
1930 list<string> list_devices;
1932 /* Enumerate devices */
1933 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1934 if( !list_devices.size() ) return VLC_EGENERIC;
1935 devicename = *list_devices.begin();
1938 IBaseFilter *p_device_filter =
1939 FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1940 if( p_device_filter )
1942 PropertiesPage( p_this, p_device_filter, NULL, b_audio );
1946 /* Uninitialize OLE/COM */
1949 msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
1950 return VLC_EGENERIC;
1953 /* Uninitialize OLE/COM */
1959 static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid )
1961 if( cauuid->cElems > 0 )
1963 HWND hwnd_desktop = ::GetDesktopWindow();
1965 OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
1966 cauuid->cElems, cauuid->pElems, 0, 0, NULL );
1968 CoTaskMemFree( cauuid->pElems );
1972 static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter,
1973 ICaptureGraphBuilder2 *p_capture_graph,
1974 vlc_bool_t b_audio )
1978 msg_Dbg( p_this, "Configuring Device Properties" );
1981 * Video or audio capture filter page
1983 ISpecifyPropertyPages *p_spec;
1985 HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages,
1989 if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
1991 ShowPropertyPage( p_device_filter, &cauuid );
1996 msg_Dbg( p_this, "looking for WDM Configuration Pages" );
1998 if( p_capture_graph )
1999 msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" );
2004 if( p_capture_graph && b_audio )
2006 IAMStreamConfig *p_SC;
2008 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2009 &MEDIATYPE_Audio, p_device_filter,
2010 IID_IAMStreamConfig,
2014 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2018 hr = p_spec->GetPages( &cauuid );
2021 for( unsigned int c = 0; c < cauuid.cElems; c++ )
2023 ShowPropertyPage( p_SC, &cauuid );
2025 CoTaskMemFree( cauuid.pElems );
2036 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2037 &MEDIATYPE_Audio, p_device_filter,
2038 IID_IAMTVAudio, (void **)&p_TVA );
2041 hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages,
2045 if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) )
2046 ShowPropertyPage(p_TVA, &cauuid);
2057 if( p_capture_graph && !b_audio )
2059 IAMStreamConfig *p_SC;
2061 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2062 &MEDIATYPE_Interleaved,
2064 IID_IAMStreamConfig,
2068 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2071 IID_IAMStreamConfig,
2077 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2081 if( SUCCEEDED( p_spec->GetPages(&cauuid) ) )
2083 ShowPropertyPage(p_SC, &cauuid);
2091 * Video crossbar, and a possible second crossbar
2093 IAMCrossbar *p_X, *p_X2;
2096 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2097 &MEDIATYPE_Interleaved,
2099 IID_IAMCrossbar, (void **)&p_X );
2102 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2111 hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF );
2114 hr = p_X->QueryInterface( IID_ISpecifyPropertyPages,
2118 hr = p_spec->GetPages(&cauuid);
2119 if( hr == S_OK && cauuid.cElems > 0 )
2121 ShowPropertyPage( p_X, &cauuid );
2126 hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL,
2127 p_XF, IID_IAMCrossbar,
2131 hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages,
2135 hr = p_spec->GetPages( &cauuid );
2138 ShowPropertyPage( p_X2, &cauuid );
2155 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2156 &MEDIATYPE_Interleaved,
2158 IID_IAMTVTuner, (void **)&p_TV );
2161 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2170 hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages,
2174 hr = p_spec->GetPages(&cauuid);
2177 ShowPropertyPage(p_TV, &cauuid);