1 /*****************************************************************************
2 * dshow.cpp : DirectShow access module for vlc
3 *****************************************************************************
4 * Copyright (C) 2002, 2003 VideoLAN
7 * Author: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc/input.h>
37 /*****************************************************************************
38 * Access: local prototypes
39 *****************************************************************************/
40 static int AccessRead ( access_t *, byte_t *, int );
41 static int ReadCompressed( access_t *, byte_t *, int );
42 static int AccessControl ( access_t *, int, va_list );
44 static int OpenDevice( access_t *, string, vlc_bool_t );
45 static IBaseFilter *FindCaptureDevice( vlc_object_t *, string *,
46 list<string> *, vlc_bool_t );
47 static size_t EnumDeviceCaps( vlc_object_t *, IBaseFilter *,
48 int, int, int, int, int, int, AM_MEDIA_TYPE *mt, size_t mt_max);
49 static bool ConnectFilters( access_t *, IBaseFilter *, CaptureFilter * );
51 static int FindDevicesCallback( vlc_object_t *, char const *,
52 vlc_value_t, vlc_value_t, void * );
53 static int ConfigDevicesCallback( vlc_object_t *, char const *,
54 vlc_value_t, vlc_value_t, void * );
56 static void PropertiesPage( vlc_object_t *, IBaseFilter *,
57 ICaptureGraphBuilder2 *, vlc_bool_t );
60 /* Debug only, use this to find out GUIDs */
62 UuidToString( (IID *)&IID_IAMBufferNegotiation, &p_st );
63 msg_Err( p_access, "BufferNegotiation: %s" , p_st );
87 /*****************************************************************************
89 *****************************************************************************/
90 static char *ppsz_vdev[] = { "", "none" };
91 static char *ppsz_vdev_text[] = { N_("Default"), N_("None") };
92 static char *ppsz_adev[] = { "", "none" };
93 static char *ppsz_adev_text[] = { N_("Default"), N_("None") };
95 #define CACHING_TEXT N_("Caching value in ms")
96 #define CACHING_LONGTEXT N_( \
97 "Allows you to modify the default caching value for DirectShow streams. " \
98 "This value should be set in milliseconds units." )
99 #define VDEV_TEXT N_("Video device name")
100 #define VDEV_LONGTEXT N_( \
101 "You can specify the name of the video device that will be used by the " \
102 "DirectShow plugin. If you don't specify anything, the default device " \
104 #define ADEV_TEXT N_("Audio device name")
105 #define ADEV_LONGTEXT N_( \
106 "You can specify the name of the audio device that will be used by the " \
107 "DirectShow plugin. If you don't specify anything, the default device " \
109 #define SIZE_TEXT N_("Video size")
110 #define SIZE_LONGTEXT N_( \
111 "You can specify the size of the video that will be displayed by the " \
112 "DirectShow plugin. If you don't specify anything the default size for " \
113 "your device will be used.")
114 #define CHROMA_TEXT N_("Video input chroma format")
115 #define CHROMA_LONGTEXT N_( \
116 "Force the DirectShow video input to use a specific chroma format " \
117 "(eg. I420 (default), RV24, etc.)")
118 #define CONFIG_TEXT N_("Device properties")
119 #define CONFIG_LONGTEXT N_( \
120 "Show the properties dialog of the selected device before starting the " \
123 static int AccessOpen ( vlc_object_t * );
124 static void AccessClose( vlc_object_t * );
126 static int DemuxOpen ( vlc_object_t * );
127 static void DemuxClose ( vlc_object_t * );
130 set_shortname( _("DirectShow") );
131 set_description( _("DirectShow input") );
132 add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL,
133 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
135 add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE);
136 change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback );
137 change_action_add( FindDevicesCallback, N_("Refresh list") );
138 change_action_add( ConfigDevicesCallback, N_("Configure") );
140 add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE);
141 change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback );
142 change_action_add( FindDevicesCallback, N_("Refresh list") );
143 change_action_add( ConfigDevicesCallback, N_("Configure") );
145 add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE);
147 add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
150 add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT,
153 add_shortcut( "dshow" );
154 set_capability( "access2", 0 );
155 set_callbacks( AccessOpen, AccessClose );
158 set_description( _("DirectShow demuxer") );
159 add_shortcut( "dshow" );
160 set_capability( "demux2", 200 );
161 set_callbacks( DemuxOpen, DemuxClose );
165 /****************************************************************************
166 * DirectShow elementary stream descriptor
167 ****************************************************************************/
168 typedef struct dshow_stream_t
171 IBaseFilter *p_device_filter;
172 CaptureFilter *p_capture_filter;
179 VIDEOINFOHEADER video;
188 /****************************************************************************
189 * Access descriptor declaration
190 ****************************************************************************/
191 #define MAX_CROSSBAR_DEPTH 10
193 typedef struct CrossbarRouteRec {
195 LONG VideoInputIndex;
196 LONG VideoOutputIndex;
197 LONG AudioInputIndex;
198 LONG AudioOutputIndex;
203 /* These 2 must be left at the beginning */
207 IFilterGraph *p_graph;
208 ICaptureGraphBuilder2 *p_capture_graph_builder2;
209 IMediaControl *p_control;
211 int i_crossbar_route_depth;
212 CrossbarRoute crossbar_routes[MAX_CROSSBAR_DEPTH];
219 /* list of elementary streams */
220 dshow_stream_t **pp_streams;
222 int i_current_stream;
224 /* misc properties */
232 /****************************************************************************
233 * DirectShow utility functions
234 ****************************************************************************/
235 static void CreateDirectShowGraph( access_sys_t *p_sys )
237 p_sys->i_crossbar_route_depth = 0;
239 /* Create directshow filter graph */
240 if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
241 (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) )
243 /* Create directshow capture graph builder if available */
244 if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0,
245 CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
246 (void **)&p_sys->p_capture_graph_builder2 ) ) )
248 p_sys->p_capture_graph_builder2->
249 SetFiltergraph((IGraphBuilder *)p_sys->p_graph);
252 p_sys->p_graph->QueryInterface( IID_IMediaControl,
253 (void **)&p_sys->p_control );
257 static void DeleteCrossbarRoutes( access_sys_t *p_sys )
259 /* Remove crossbar filters from graph */
260 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
262 p_sys->crossbar_routes[i].pXbar->Release();
264 p_sys->i_crossbar_route_depth = 0;
267 static void DeleteDirectShowGraph( access_sys_t *p_sys )
269 DeleteCrossbarRoutes( p_sys );
271 /* Remove filters from graph */
272 for( int i = 0; i < p_sys->i_streams; i++ )
274 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter );
275 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter );
276 p_sys->pp_streams[i]->p_capture_filter->Release();
277 p_sys->pp_streams[i]->p_device_filter->Release();
280 /* Release directshow objects */
281 if( p_sys->p_control )
283 p_sys->p_control->Release();
284 p_sys->p_control = NULL;
286 if( p_sys->p_capture_graph_builder2 )
288 p_sys->p_capture_graph_builder2->Release();
289 p_sys->p_capture_graph_builder2 = NULL;
294 p_sys->p_graph->Release();
295 p_sys->p_graph = NULL;
299 static void ReleaseDirectShow( access_t *p_access )
301 access_sys_t *p_sys = p_access->p_sys;
303 msg_Dbg( p_access, "Releasing DirectShow");
305 DeleteDirectShowGraph( p_sys );
307 /* Uninitialize OLE/COM */
310 free( p_sys->p_header );
311 /* Remove filters from graph */
312 for( int i = 0; i < p_sys->i_streams; i++ )
314 delete p_sys->pp_streams[i];
316 free( p_sys->pp_streams );
320 /*****************************************************************************
321 * Open: open direct show device
322 *****************************************************************************/
323 static int AccessOpen( vlc_object_t *p_this )
325 access_t *p_access = (access_t*)p_this;
329 /* Get/parse options and open device(s) */
330 string vdevname, adevname;
331 int i_width = 0, i_height = 0, i_chroma = 0;
333 var_Create( p_access, "dshow-config", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
335 var_Create( p_access, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
336 var_Get( p_access, "dshow-vdev", &val );
337 if( val.psz_string ) vdevname = string( val.psz_string );
338 if( val.psz_string ) free( val.psz_string );
340 var_Create( p_access, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
341 var_Get( p_access, "dshow-adev", &val );
342 if( val.psz_string ) adevname = string( val.psz_string );
343 if( val.psz_string ) free( val.psz_string );
345 var_Create( p_access, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
346 var_Get( p_access, "dshow-size", &val );
347 if( val.psz_string && *val.psz_string )
349 if( !strcmp( val.psz_string, "subqcif" ) )
351 i_width = 128; i_height = 96;
353 else if( !strcmp( val.psz_string, "qsif" ) )
355 i_width = 160; i_height = 120;
357 else if( !strcmp( val.psz_string, "qcif" ) )
359 i_width = 176; i_height = 144;
361 else if( !strcmp( val.psz_string, "sif" ) )
363 i_width = 320; i_height = 240;
365 else if( !strcmp( val.psz_string, "cif" ) )
367 i_width = 352; i_height = 288;
369 else if( !strcmp( val.psz_string, "vga" ) )
371 i_width = 640; i_height = 480;
377 i_width = strtol( val.psz_string, &psz_parser, 0 );
378 if( *psz_parser == 'x' || *psz_parser == 'X')
380 i_height = strtol( psz_parser + 1, &psz_parser, 0 );
382 msg_Dbg( p_access, "Width x Height %dx%d", i_width, i_height );
385 if( val.psz_string ) free( val.psz_string );
387 var_Create( p_access, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
388 var_Get( p_access, "dshow-chroma", &val );
389 if( val.psz_string && strlen( val.psz_string ) >= 4 )
391 i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1],
392 val.psz_string[2], val.psz_string[3] );
394 if( val.psz_string ) free( val.psz_string );
397 p_access->pf_read = AccessRead;
398 p_access->pf_block = NULL;
399 p_access->pf_control = AccessControl;
400 p_access->pf_seek = NULL;
401 p_access->info.i_update = 0;
402 p_access->info.i_size = 0;
403 p_access->info.i_pos = 0;
404 p_access->info.b_eof = VLC_FALSE;
405 p_access->info.i_title = 0;
406 p_access->info.i_seekpoint = 0;
407 p_access->p_sys = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
408 memset( p_sys, 0, sizeof( access_sys_t ) );
410 var_Create( p_access, "dshow-caching",
411 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
413 /* Initialize OLE/COM */
416 /* Initialize some data */
417 p_sys->i_streams = 0;
418 p_sys->pp_streams = (dshow_stream_t **)malloc( 1 );
419 p_sys->i_width = i_width;
420 p_sys->i_height = i_height;
421 p_sys->i_chroma = i_chroma;
422 p_sys->b_audio = VLC_TRUE;
426 p_sys->i_header_size = 8;
427 p_sys->p_header = (uint8_t *)malloc( p_sys->i_header_size );
428 memcpy( &p_sys->p_header[0], ".dsh", 4 );
429 SetDWBE( &p_sys->p_header[4], 1 );
430 p_sys->i_header_pos = p_sys->i_header_size;
432 p_sys->p_graph = NULL;
433 p_sys->p_capture_graph_builder2 = NULL;
434 p_sys->p_control = NULL;
436 /* Build directshow graph */
437 CreateDirectShowGraph( p_sys );
439 if( OpenDevice( p_access, vdevname, 0 ) != VLC_SUCCESS )
441 msg_Err( p_access, "can't open video");
444 if( p_sys->b_audio && OpenDevice( p_access, adevname, 1 ) != VLC_SUCCESS )
446 msg_Err( p_access, "can't open audio");
449 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
451 IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
452 LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex;
453 LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex;
454 LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex;
455 LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex;
457 if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) )
459 msg_Dbg( p_access, "Crossbar at depth %d, Routed video "
460 "ouput %ld to video input %ld", i, VideoOutputIndex,
463 if( AudioOutputIndex != -1 && AudioInputIndex != -1 )
465 if( SUCCEEDED( pXbar->Route(AudioOutputIndex,
468 msg_Dbg(p_access, "Crossbar at depth %d, Routed audio "
469 "ouput %ld to audio input %ld", i,
470 AudioOutputIndex, AudioInputIndex );
476 if( !p_sys->i_streams )
478 ReleaseDirectShow( p_access );
482 /* Initialize some data */
483 p_sys->i_current_stream = 0;
484 p_sys->i_mtu += p_sys->i_header_size + 16 /* data header size */;
485 vlc_mutex_init( p_access, &p_sys->lock );
486 vlc_cond_init( p_access, &p_sys->wait );
488 msg_Dbg( p_access, "Playing...");
490 /* Everything is ready. Let's rock baby */
491 p_sys->p_control->Run();
496 /*****************************************************************************
497 * AccessClose: close device
498 *****************************************************************************/
499 static void AccessClose( vlc_object_t *p_this )
501 access_t *p_access = (access_t *)p_this;
502 access_sys_t *p_sys = p_access->p_sys;
504 /* Stop capturing stuff */
505 p_sys->p_control->Stop();
507 ReleaseDirectShow( p_access );
510 /*****************************************************************************
512 *****************************************************************************/
513 static int AccessControl( access_t *p_access, int i_query, va_list args )
515 access_sys_t *p_sys = p_access->p_sys;
523 case ACCESS_CAN_SEEK:
524 case ACCESS_CAN_FASTSEEK:
525 case ACCESS_CAN_PAUSE:
526 case ACCESS_CAN_CONTROL_PACE:
527 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
528 *pb_bool = VLC_FALSE;
533 pi_int = (int*)va_arg( args, int * );
534 *pi_int = p_sys->i_mtu;
537 case ACCESS_GET_PTS_DELAY:
538 pi_64 = (int64_t*)va_arg( args, int64_t * );
539 *pi_64 = (int64_t)var_GetInteger( p_access, "dshow-caching" ) *
544 case ACCESS_SET_PAUSE_STATE:
545 case ACCESS_GET_TITLE_INFO:
546 case ACCESS_SET_TITLE:
547 case ACCESS_SET_SEEKPOINT:
551 msg_Err( p_access, "unimplemented query in control" );
558 /****************************************************************************
559 * RouteCrossbars (Does not AddRef the returned *Pin)
560 ****************************************************************************/
561 static HRESULT GetCrossbarIPinAtIndex( IAMCrossbar *pXbar, LONG PinIndex,
562 BOOL IsInputPin, IPin ** ppPin )
564 LONG cntInPins, cntOutPins;
566 IBaseFilter *pFilter = NULL;
570 if( !pXbar || !ppPin ) return E_POINTER;
574 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) return E_FAIL;
576 LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins;
578 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
580 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
583 while( pins->Next(1, &pP, &n) == S_OK )
598 return *ppPin ? S_OK : E_FAIL;
601 /****************************************************************************
602 * GetCrossbarIndexFromIPin: Find corresponding index of an IPin on a crossbar
603 ****************************************************************************/
604 static HRESULT GetCrossbarIndexFromIPin( IAMCrossbar * pXbar, LONG * PinIndex,
605 BOOL IsInputPin, IPin * pPin )
607 LONG cntInPins, cntOutPins;
609 IBaseFilter *pFilter = NULL;
614 if(!pXbar || !PinIndex || !pPin )
617 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) )
620 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
622 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
626 while( pins->Next(1, &pP, &n) == S_OK )
631 *PinIndex = IsInputPin ? i : i - cntInPins;
642 return fOK ? S_OK : E_FAIL;
645 /****************************************************************************
647 ****************************************************************************/
648 static HRESULT FindCrossbarRoutes( access_t *p_access, IPin *p_input_pin,
649 LONG physicalType, int depth = 0 )
651 access_sys_t *p_sys = p_access->p_sys;
652 HRESULT result = S_FALSE;
655 if( FAILED(p_input_pin->ConnectedTo(&p_output_pin)) ) return S_FALSE;
657 // It is connected, so now find out if the filter supports IAMCrossbar
659 if( FAILED(p_output_pin->QueryPinInfo(&pinInfo)) ||
660 PINDIR_OUTPUT != pinInfo.dir )
662 p_output_pin->Release ();
666 IAMCrossbar *pXbar=0;
667 if( FAILED(pinInfo.pFilter->QueryInterface(IID_IAMCrossbar,
670 pinInfo.pFilter->Release();
671 p_output_pin->Release ();
675 LONG inputPinCount, outputPinCount;
676 if( FAILED(pXbar->get_PinCounts(&outputPinCount, &inputPinCount)) )
679 pinInfo.pFilter->Release();
680 p_output_pin->Release ();
684 LONG inputPinIndexRelated, outputPinIndexRelated;
685 LONG inputPinPhysicalType, outputPinPhysicalType;
686 LONG inputPinIndex, outputPinIndex;
687 if( FAILED(GetCrossbarIndexFromIPin( pXbar, &outputPinIndex,
688 FALSE, p_output_pin )) ||
689 FAILED(pXbar->get_CrossbarPinInfo( FALSE, outputPinIndex,
690 &outputPinIndexRelated,
691 &outputPinPhysicalType )) )
694 pinInfo.pFilter->Release();
695 p_output_pin->Release ();
700 // for all input pins
702 for( inputPinIndex = 0; S_OK != result && inputPinIndex < inputPinCount;
705 if( FAILED(pXbar->get_CrossbarPinInfo( TRUE, inputPinIndex,
706 &inputPinIndexRelated, &inputPinPhysicalType )) ) continue;
708 // Is the pin a video pin?
709 if( inputPinPhysicalType != physicalType ) continue;
712 if( FAILED(pXbar->CanRoute(outputPinIndex, inputPinIndex)) ) continue;
715 if( FAILED(GetCrossbarIPinAtIndex( pXbar, inputPinIndex,
716 TRUE, &pPin)) ) continue;
718 result = FindCrossbarRoutes( p_access, pPin, physicalType, depth+1 );
719 if( S_OK == result || (S_FALSE == result &&
720 physicalType == inputPinPhysicalType &&
721 (p_sys->i_crossbar_route_depth = depth+1) < MAX_CROSSBAR_DEPTH) )
726 // remember crossbar route
727 p_sys->crossbar_routes[depth].pXbar = pXbar;
728 p_sys->crossbar_routes[depth].VideoInputIndex = inputPinIndex;
729 p_sys->crossbar_routes[depth].VideoOutputIndex = outputPinIndex;
730 p_sys->crossbar_routes[depth].AudioInputIndex = inputPinIndexRelated;
731 p_sys->crossbar_routes[depth].AudioOutputIndex = outputPinIndexRelated;
733 msg_Dbg( p_access, "Crossbar at depth %d, Found Route For "
734 "ouput %ld (type %ld) to input %ld (type %ld)", depth,
735 outputPinIndex, outputPinPhysicalType, inputPinIndex,
736 inputPinPhysicalType );
743 pinInfo.pFilter->Release();
744 p_output_pin->Release ();
749 /****************************************************************************
751 ****************************************************************************/
752 static bool ConnectFilters( access_t *p_access, IBaseFilter *p_filter,
753 CaptureFilter *p_capture_filter )
755 access_sys_t *p_sys = p_access->p_sys;
756 CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
758 AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
760 if( p_sys->p_capture_graph_builder2 )
762 if( FAILED(p_sys->p_capture_graph_builder2->
763 RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
765 (IBaseFilter *)p_capture_filter )) )
770 // Sort out all the possible video inputs
771 // The class needs to be given the capture filters ANALOGVIDEO input pin
773 if( mediaType.majortype == MEDIATYPE_Video &&
774 SUCCEEDED(p_filter->EnumPins(&pins)) )
780 IKsPropertySet *pKs=0;
784 while( !Found && (S_OK == pins->Next(1, &pP, &n)) )
786 if(S_OK == pP->QueryPinInfo(&pinInfo))
788 if(pinInfo.dir == PINDIR_INPUT)
790 // is this pin an ANALOGVIDEOIN input pin?
791 if( pP->QueryInterface(IID_IKsPropertySet,
792 (void **)&pKs) == S_OK )
794 if( pKs->Get(AMPROPSETID_Pin,
795 AMPROPERTY_PIN_CATEGORY, NULL, 0,
796 &guid, sizeof(GUID), &dw) == S_OK )
798 if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
800 // recursively search crossbar routes
801 FindCrossbarRoutes( p_access, pP,
802 PhysConn_Video_Tuner );
810 pinInfo.pFilter->Release();
820 IEnumPins *p_enumpins;
823 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
825 while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
827 PIN_DIRECTION pin_dir;
828 p_pin->QueryDirection( &pin_dir );
830 if( pin_dir == PINDIR_OUTPUT &&
831 p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
835 p_enumpins->Release();
841 p_enumpins->Release();
847 ** get fourcc priority from arbritary preference, the higher the better
849 static int GetFourCCPriority(int i_fourcc)
853 case VLC_FOURCC('I','4','2','0'):
854 case VLC_FOURCC('f','l','3','2'):
859 case VLC_FOURCC('Y','V','1','2'):
860 case VLC_FOURCC('a','r','a','w'):
865 case VLC_FOURCC('R','V','2','4'):
870 case VLC_FOURCC('Y','U','Y','2'):
871 case VLC_FOURCC('R','V','3','2'):
872 case VLC_FOURCC('R','G','B','A'):
880 #define MAX_MEDIA_TYPES 32
882 static int OpenDevice( access_t *p_access, string devicename,
885 access_sys_t *p_sys = p_access->p_sys;
887 /* See if device is already opened */
888 for( int i = 0; i < p_sys->i_streams; i++ )
890 if( p_sys->pp_streams[i]->devicename == devicename )
897 list<string> list_devices;
899 /* Enumerate devices and display their names */
900 FindCaptureDevice( (vlc_object_t *)p_access, NULL, &list_devices, b_audio );
902 if( !list_devices.size() )
905 list<string>::iterator iter;
906 for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
907 msg_Dbg( p_access, "found device: %s", iter->c_str() );
909 /* If no device name was specified, pick the 1st one */
910 if( devicename.size() == 0 )
912 devicename = *list_devices.begin();
915 // Use the system device enumerator and class enumerator to find
916 // a capture/preview device, such as a desktop USB video camera.
917 IBaseFilter *p_device_filter =
918 FindCaptureDevice( (vlc_object_t *)p_access, &devicename,
920 if( p_device_filter )
921 msg_Dbg( p_access, "using device: %s", devicename.c_str() );
924 msg_Err( p_access, "can't use device: %s, unsupported device type",
925 devicename.c_str() );
930 AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
932 size_t mt_count = EnumDeviceCaps( (vlc_object_t *)p_access,
933 p_device_filter, p_sys->i_chroma,
934 p_sys->i_width, p_sys->i_height,
935 0, 0, 0, media_types, MAX_MEDIA_TYPES );
939 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
941 // Order and copy returned media types according to arbitrary
943 for( size_t c=0; c<mt_count; c++ )
946 GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
947 size_t slot_copy = c;
948 for( size_t d=c+1; d<mt_count; d++ )
951 GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
952 if( priority > slot_priority )
954 slot_priority = priority;
960 mt[c] = media_types[slot_copy];
961 media_types[slot_copy] = media_types[c];
965 mt[c] = media_types[c];
969 else if( ! b_audio ) {
970 // Use default video media type
974 mtr.majortype = MEDIATYPE_Video;
975 mtr.subtype = MEDIASUBTYPE_I420;
976 mtr.bFixedSizeSamples = TRUE;
977 mtr.bTemporalCompression = FALSE;
980 mtr.formattype = FORMAT_VideoInfo;
981 mtr.cbFormat = sizeof(vh);
982 mtr.pbFormat = (BYTE *)&vh;
984 memset(&vh, 0, sizeof(vh));
986 vh.bmiHeader.biSize = sizeof(vh.bmiHeader);
987 vh.bmiHeader.biWidth = p_sys->i_width > 0 ? p_sys->i_width: 320;
988 vh.bmiHeader.biHeight = p_sys->i_height > 0 ? p_sys->i_height : 240;
989 vh.bmiHeader.biPlanes = 1;
990 vh.bmiHeader.biBitCount = 24;
991 vh.bmiHeader.biCompression = VLC_FOURCC('I','4','2','0');
992 vh.bmiHeader.biSizeImage = p_sys->i_width * 24 * p_sys->i_height / 8;
994 msg_Warn( p_access, "device %s using built-in video media type",
995 devicename.c_str() );
998 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
999 CopyMediaType(mt, &mtr);
1002 // Use default audio media type
1006 mtr.majortype = MEDIATYPE_Audio;
1007 mtr.subtype = MEDIASUBTYPE_PCM;
1008 mtr.bFixedSizeSamples = TRUE;
1009 mtr.bTemporalCompression = FALSE;
1010 mtr.lSampleSize = 0;
1012 mtr.formattype = FORMAT_WaveFormatEx;
1013 mtr.cbFormat = sizeof(wf);
1014 mtr.pbFormat = (BYTE *)&wf;
1016 memset(&wf, 0, sizeof(wf));
1018 wf.wFormatTag = WAVE_FORMAT_PCM;
1020 wf.nSamplesPerSec = 44100;
1021 wf.wBitsPerSample = 16;
1022 wf.nBlockAlign = wf.nSamplesPerSec * wf.wBitsPerSample / 8;
1023 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
1026 msg_Warn( p_access, "device %s using built-in audio media type",
1027 devicename.c_str() );
1030 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1031 CopyMediaType(mt, &mtr);
1034 /* Create and add our capture filter */
1035 CaptureFilter *p_capture_filter =
1036 new CaptureFilter( p_access, mt, mt_count );
1037 p_sys->p_graph->AddFilter( p_capture_filter, 0 );
1039 /* Add the device filter to the graph (seems necessary with VfW before
1040 * accessing pin attributes). */
1041 p_sys->p_graph->AddFilter( p_device_filter, 0 );
1043 /* Attempt to connect one of this device's capture output pins */
1044 msg_Dbg( p_access, "connecting filters" );
1045 if( ConnectFilters( p_access, p_device_filter, p_capture_filter ) )
1048 msg_Dbg( p_access, "filters connected successfully !" );
1050 dshow_stream_t dshow_stream;
1051 dshow_stream.b_invert = VLC_FALSE;
1052 dshow_stream.b_pts = VLC_FALSE;
1054 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1056 /* Show properties. Done here so the VLC stream is setup with the
1057 * proper parameters. */
1059 var_Get( p_access, "dshow-config", &val );
1062 PropertiesPage( VLC_OBJECT(p_access), p_device_filter,
1063 p_sys->p_capture_graph_builder2,
1064 dshow_stream.mt.majortype == MEDIATYPE_Audio );
1068 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1070 dshow_stream.i_fourcc = GetFourCCFromMediaType(dshow_stream.mt);
1071 if( 0 != dshow_stream.i_fourcc )
1073 if( dshow_stream.mt.majortype == MEDIATYPE_Video )
1075 dshow_stream.header.video =
1076 *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
1078 int i_height = dshow_stream.header.video.bmiHeader.biHeight;
1080 /* Check if the image is inverted (bottom to top) */
1081 if( dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','1') ||
1082 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','4') ||
1083 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','8') ||
1084 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','5') ||
1085 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','6') ||
1086 dshow_stream.i_fourcc == VLC_FOURCC('R','V','2','4') ||
1087 dshow_stream.i_fourcc == VLC_FOURCC('R','V','3','2') ||
1088 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','A') )
1090 if( i_height > 0 ) dshow_stream.b_invert = VLC_TRUE;
1091 else i_height = - i_height;
1094 /* Check if we are dealing with a DV stream */
1095 if( dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','l') ||
1096 dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','d') ||
1097 dshow_stream.i_fourcc == VLC_FOURCC('d','v','h','d') )
1099 p_access->pf_read = ReadCompressed;
1100 if( !p_access->psz_demux || !*p_access->psz_demux )
1102 p_access->psz_demux = strdup( "rawdv" );
1104 p_sys->b_audio = VLC_FALSE;
1107 /* Check if we are dealing with an MPEG video stream */
1108 if( dshow_stream.i_fourcc == VLC_FOURCC('m','p','2','v') )
1110 p_access->pf_read = ReadCompressed;
1111 if( !p_access->psz_demux || !*p_access->psz_demux )
1113 p_access->psz_demux = "mpgv";
1115 p_sys->b_audio = VLC_FALSE;
1118 /* Add video stream to header */
1119 p_sys->i_header_size += 20;
1120 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1121 p_sys->i_header_size );
1122 memcpy( &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
1123 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1124 &dshow_stream.i_fourcc, 4 );
1125 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1126 dshow_stream.header.video.bmiHeader.biWidth );
1127 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height );
1128 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 );
1129 p_sys->i_header_pos = p_sys->i_header_size;
1131 /* Greatly simplifies the reading routine */
1132 int i_mtu = dshow_stream.header.video.bmiHeader.biWidth *
1134 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1137 else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
1139 dshow_stream.header.audio =
1140 *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
1142 /* Add audio stream to header */
1143 p_sys->i_header_size += 20;
1144 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1145 p_sys->i_header_size );
1146 memcpy( &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
1147 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1148 &dshow_stream.i_fourcc, 4 );
1149 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1150 dshow_stream.header.audio.nChannels );
1151 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12],
1152 dshow_stream.header.audio.nSamplesPerSec );
1153 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16],
1154 dshow_stream.header.audio.wBitsPerSample );
1155 p_sys->i_header_pos = p_sys->i_header_size;
1157 /* Greatly simplifies the reading routine */
1158 IAMBufferNegotiation *p_ambuf;
1162 p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin );
1163 if( SUCCEEDED( p_pin->QueryInterface(
1164 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1166 ALLOCATOR_PROPERTIES AllocProp;
1167 memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) );
1168 p_ambuf->GetAllocatorProperties( &AllocProp );
1170 i_mtu = AllocProp.cbBuffer;
1175 i_mtu = dshow_stream.header.audio.nSamplesPerSec *
1176 dshow_stream.header.audio.nChannels *
1177 dshow_stream.header.audio.wBitsPerSample / 8;
1180 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1183 else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
1185 msg_Dbg( p_access, "MEDIATYPE_Stream" );
1187 msg_Dbg( p_access, "selected stream pin accepts format: %4.4s",
1188 (char *)&dshow_stream.i_fourcc);
1190 p_sys->b_audio = VLC_FALSE;
1191 p_sys->i_header_size = 0;
1192 p_sys->i_header_pos = 0;
1195 p_access->pf_read = ReadCompressed;
1199 msg_Dbg( p_access, "unknown stream majortype" );
1203 /* Add directshow elementary stream to our list */
1204 dshow_stream.p_device_filter = p_device_filter;
1205 dshow_stream.p_capture_filter = p_capture_filter;
1207 p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams,
1208 sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) );
1209 p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1210 *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1211 SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams );
1218 /* Remove filters from graph */
1219 p_sys->p_graph->RemoveFilter( p_device_filter );
1220 p_sys->p_graph->RemoveFilter( p_capture_filter );
1222 /* Release objects */
1223 p_device_filter->Release();
1224 p_capture_filter->Release();
1226 return VLC_EGENERIC;
1229 static IBaseFilter *
1230 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1231 list<string> *p_listdevices, vlc_bool_t b_audio )
1233 IBaseFilter *p_base_filter = NULL;
1234 IMoniker *p_moniker = NULL;
1238 /* Create the system device enumerator */
1239 ICreateDevEnum *p_dev_enum = NULL;
1241 hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1242 IID_ICreateDevEnum, (void **)&p_dev_enum );
1245 msg_Err( p_this, "failed to create the device enumerator (0x%lx)", hr);
1249 /* Create an enumerator for the video capture devices */
1250 IEnumMoniker *p_class_enum = NULL;
1252 hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1255 hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1257 p_dev_enum->Release();
1260 msg_Err( p_this, "failed to create the class enumerator (0x%lx)", hr );
1264 /* If there are no enumerators for the requested type, then
1265 * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1266 if( p_class_enum == NULL )
1268 msg_Err( p_this, "no capture device was detected" );
1272 /* Enumerate the devices */
1274 /* Note that if the Next() call succeeds but there are no monikers,
1275 * it will return S_FALSE (which is not a failure). Therefore, we check
1276 * that the return code is S_OK instead of using SUCCEEDED() macro. */
1278 while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1280 /* Getting the property page to get the device name */
1281 IPropertyBag *p_bag;
1282 hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1288 hr = p_bag->Read( L"FriendlyName", &var, NULL );
1292 int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
1293 char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
1294 WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
1295 i_convert, NULL, NULL );
1296 SysFreeString(var.bstrVal);
1298 if( p_listdevices ) p_listdevices->push_back( p_buf );
1300 if( p_devicename && *p_devicename == string(p_buf) )
1302 /* Bind Moniker to a filter object */
1303 hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1304 (void **)&p_base_filter );
1307 msg_Err( p_this, "couldn't bind moniker to filter "
1308 "object (0x%lx)", hr );
1309 p_moniker->Release();
1310 p_class_enum->Release();
1313 p_moniker->Release();
1314 p_class_enum->Release();
1315 return p_base_filter;
1320 p_moniker->Release();
1323 p_class_enum->Release();
1327 static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter,
1328 int i_fourcc, int i_width, int i_height,
1329 int i_channels, int i_samplespersec,
1330 int i_bitspersample, AM_MEDIA_TYPE *mt,
1333 IEnumPins *p_enumpins;
1335 IEnumMediaTypes *p_enummt;
1336 size_t mt_count = 0;
1338 if( FAILED(p_filter->EnumPins( &p_enumpins )) )
1340 msg_Dbg( p_this, "EnumDeviceCaps failed: no pin enumeration !");
1344 while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1348 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1350 msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1351 info.dir == PINDIR_INPUT ? "input" : "output",
1353 if( info.pFilter ) info.pFilter->Release();
1356 p_output_pin->Release();
1359 p_enumpins->Reset();
1361 while( !mt_count && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1365 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1367 if( info.pFilter ) info.pFilter->Release();
1368 if( info.dir == PINDIR_INPUT )
1370 p_output_pin->Release();
1373 msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1377 if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1379 AM_MEDIA_TYPE *p_mt;
1380 while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1382 int i_current_fourcc = GetFourCCFromMediaType(*p_mt);
1383 if( 0 != i_current_fourcc )
1385 if( p_mt->majortype == MEDIATYPE_Video )
1387 int i_current_width = p_mt->pbFormat ?
1388 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0;
1389 int i_current_height = p_mt->pbFormat ?
1390 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0;
1391 if( i_current_height < 0 )
1392 i_current_height = -i_current_height;
1394 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1395 "accepts chroma: %4.4s, width:%i, height:%i",
1396 (char *)&i_current_fourcc, i_current_width,
1399 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1400 ( !i_width || i_width == i_current_width ) &&
1401 ( !i_height || i_height == i_current_height ) &&
1402 (mt_count < mt_max) )
1405 mt[mt_count++] = *p_mt;
1409 FreeMediaType( *p_mt );
1412 else if( p_mt->majortype == MEDIATYPE_Audio )
1414 int i_current_channels =
1415 ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1416 int i_current_samplespersec =
1417 ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1418 int i_current_bitspersample =
1419 ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1421 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1422 "accepts format: %4.4s, channels:%i, "
1423 "samples/sec:%i bits/sample:%i",
1424 (char *)&i_current_fourcc, i_current_channels,
1425 i_current_samplespersec, i_current_bitspersample);
1427 if( (!i_channels || i_channels == i_current_channels) &&
1428 (!i_samplespersec ||
1429 i_samplespersec == i_current_samplespersec) &&
1430 (!i_bitspersample ||
1431 i_bitspersample == i_current_bitspersample) &&
1432 (mt_count < mt_max) )
1435 mt[mt_count++] = *p_mt;
1437 /* Pre-Configure the 1st match, Ugly */
1438 if( 1 == mt_count ) {
1439 /* Setup a few properties like the audio latency */
1440 IAMBufferNegotiation *p_ambuf;
1442 if( SUCCEEDED( p_output_pin->QueryInterface(
1443 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1445 ALLOCATOR_PROPERTIES AllocProp;
1446 AllocProp.cbAlign = -1;
1447 AllocProp.cbBuffer = i_current_channels *
1448 i_current_samplespersec *
1449 i_current_bitspersample / 8 / 10 ; /*100 ms of latency*/
1450 AllocProp.cbPrefix = -1;
1451 AllocProp.cBuffers = -1;
1452 p_ambuf->SuggestAllocatorProperties( &AllocProp );
1459 FreeMediaType( *p_mt );
1462 else if( p_mt->majortype == MEDIATYPE_Stream )
1464 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1465 (mt_count < mt_max) )
1468 mt[mt_count++] = *p_mt;
1469 i_fourcc = i_current_fourcc;
1473 FreeMediaType( *p_mt );
1479 "EnumDeviceCaps: input pin: unknown format" );
1480 FreeMediaType( *p_mt );
1486 "EnumDeviceCaps: input pin: unknown format" );
1487 FreeMediaType( *p_mt );
1489 CoTaskMemFree( (PVOID)p_mt );
1491 p_enummt->Release();
1494 p_output_pin->Release();
1497 p_enumpins->Release();
1501 /*****************************************************************************
1502 * AccessRead: reads from the device.
1503 *****************************************************************************
1504 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1506 *****************************************************************************/
1507 static int AccessRead( access_t *p_access, uint8_t *p_buffer, int i_len )
1509 access_sys_t *p_sys = p_access->p_sys;
1510 dshow_stream_t *p_stream = NULL;
1511 byte_t *p_buf_orig = p_buffer;
1512 VLCMediaSample sample;
1516 if( p_sys->i_header_pos )
1518 /* First header of the stream */
1519 memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size );
1520 p_buffer += p_sys->i_header_size;
1521 p_sys->i_header_pos = 0;
1526 /* Get new sample/frame from next elementary stream.
1527 * We first loop through all the elementary streams and if all our
1528 * fifos are empty we block until we are signaled some new data has
1530 vlc_mutex_lock( &p_sys->lock );
1533 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1535 p_stream = p_sys->pp_streams[i_stream];
1536 if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1537 p_stream->p_capture_filter &&
1538 p_stream->p_capture_filter->CustomGetPin()
1539 ->CustomGetSample( &sample ) == S_OK )
1544 if( i_stream == p_sys->i_streams )
1545 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1547 p_stream = p_sys->pp_streams[i_stream];
1548 if( p_stream->p_capture_filter &&
1549 p_stream->p_capture_filter->CustomGetPin()
1550 ->CustomGetSample( &sample ) == S_OK )
1555 if( i_stream == p_sys->i_streams )
1557 /* No data available. Wait until some data has arrived */
1558 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1559 vlc_mutex_unlock( &p_sys->lock );
1563 vlc_mutex_unlock( &p_sys->lock );
1568 i_data_size = sample.p_sample->GetActualDataLength();
1569 sample.p_sample->GetPointer( &p_data );
1571 REFERENCE_TIME i_pts, i_end_date;
1572 HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1573 if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1577 if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1579 /* Use our data timestamp */
1580 i_pts = sample.i_timestamp;
1581 p_stream->b_pts = VLC_TRUE;
1586 msg_Dbg( p_access, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts );
1589 /* Create pseudo header */
1590 SetDWBE( &p_sys->p_header[0], i_stream );
1591 SetDWBE( &p_sys->p_header[4], i_data_size );
1592 SetQWBE( &p_sys->p_header[8], i_pts / 10 );
1595 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1598 /* First copy header */
1599 memcpy( p_buffer, p_sys->p_header, 16 /* header size */ );
1600 p_buffer += 16 /* header size */;
1602 /* Then copy stream data if any */
1603 if( !p_stream->b_invert )
1605 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1606 p_buffer += i_data_size;
1610 int i_width = p_stream->header.video.bmiHeader.biWidth;
1611 int i_height = p_stream->header.video.bmiHeader.biHeight;
1612 if( i_height < 0 ) i_height = - i_height;
1614 switch( p_stream->i_fourcc )
1616 case VLC_FOURCC( 'R', 'V', '1', '5' ):
1617 case VLC_FOURCC( 'R', 'V', '1', '6' ):
1620 case VLC_FOURCC( 'R', 'V', '2', '4' ):
1623 case VLC_FOURCC( 'R', 'V', '3', '2' ):
1624 case VLC_FOURCC( 'R', 'G', 'B', 'A' ):
1629 for( int i = i_height - 1; i >= 0; i-- )
1631 p_access->p_vlc->pf_memcpy( p_buffer,
1632 &p_data[i * i_width], i_width );
1634 p_buffer += i_width;
1638 sample.p_sample->Release();
1640 /* The caller got what he wanted */
1641 return p_buffer - p_buf_orig;
1644 return 0; /* never reached */
1647 /*****************************************************************************
1648 * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1649 *****************************************************************************
1650 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1652 *****************************************************************************/
1653 static int ReadCompressed( access_t *p_access, uint8_t *p_buffer, int i_len )
1655 access_sys_t *p_sys = p_access->p_sys;
1656 dshow_stream_t *p_stream = NULL;
1657 VLCMediaSample sample;
1661 /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1663 /* There must be only 1 elementary stream to produce a valid stream
1664 * of MPEG or DV data */
1665 p_stream = p_sys->pp_streams[0];
1669 if( p_access->b_die || p_access->b_error ) return 0;
1671 /* Get new sample/frame from the elementary stream (blocking). */
1672 vlc_mutex_lock( &p_sys->lock );
1674 if( p_stream->p_capture_filter->CustomGetPin()
1675 ->CustomGetSample( &sample ) != S_OK )
1677 /* No data available. Wait until some data has arrived */
1678 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1679 vlc_mutex_unlock( &p_sys->lock );
1683 vlc_mutex_unlock( &p_sys->lock );
1688 i_data_size = sample.p_sample->GetActualDataLength();
1689 sample.p_sample->GetPointer( &p_data );
1692 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1694 i_data_size = __MIN( i_data_size, (int)i_len );
1696 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1698 sample.p_sample->Release();
1700 /* The caller got what he wanted */
1704 return 0; /* never reached */
1707 /*****************************************************************************
1708 * Demux: local prototypes
1709 *****************************************************************************/
1716 static int Demux ( demux_t * );
1717 static int DemuxControl( demux_t *, int, va_list );
1719 /****************************************************************************
1721 ****************************************************************************/
1722 static int DemuxOpen( vlc_object_t *p_this )
1724 demux_t *p_demux = (demux_t *)p_this;
1731 /* a little test to see if it's a dshow stream */
1732 if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
1734 msg_Warn( p_demux, "dshow plugin discarded (cannot peek)" );
1735 return VLC_EGENERIC;
1738 if( memcmp( p_peek, ".dsh", 4 ) ||
1739 ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
1741 msg_Warn( p_demux, "dshow plugin discarded (not a valid stream)" );
1742 return VLC_EGENERIC;
1745 p_demux->pf_demux = Demux;
1746 p_demux->pf_control = DemuxControl;
1747 p_demux->p_sys = p_sys = (demux_sys_t *)malloc( sizeof( demux_sys_t ) );
1751 if( stream_Peek( p_demux->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
1753 msg_Err( p_demux, "dshow plugin discarded (cannot peek)" );
1754 return VLC_EGENERIC;
1758 for( i = 0; i < i_es; i++ )
1762 if( !memcmp( p_peek, "auds", 4 ) )
1764 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1765 p_peek[6], p_peek[7] ) );
1767 fmt.audio.i_channels = GetDWBE( &p_peek[8] );
1768 fmt.audio.i_rate = GetDWBE( &p_peek[12] );
1769 fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
1770 fmt.audio.i_blockalign = fmt.audio.i_channels *
1771 fmt.audio.i_bitspersample / 8;
1772 fmt.i_bitrate = fmt.audio.i_channels *
1774 fmt.audio.i_bitspersample;
1776 msg_Dbg( p_demux, "new audio es %d channels %dHz",
1777 fmt.audio.i_channels, fmt.audio.i_rate );
1779 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1780 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1781 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1783 else if( !memcmp( p_peek, "vids", 4 ) )
1785 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1786 p_peek[6], p_peek[7] ) );
1787 fmt.video.i_width = GetDWBE( &p_peek[8] );
1788 fmt.video.i_height = GetDWBE( &p_peek[12] );
1790 msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
1791 (char*)&fmt.i_codec,
1792 fmt.video.i_width, fmt.video.i_height );
1794 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1795 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1796 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1803 stream_Read( p_demux->s, NULL, 8 + 20 * i_es );
1808 /****************************************************************************
1810 ****************************************************************************/
1811 static void DemuxClose( vlc_object_t *p_this )
1813 demux_t *p_demux = (demux_t *)p_this;
1814 demux_sys_t *p_sys = p_demux->p_sys;
1816 if( p_sys->i_es > 0 )
1823 /****************************************************************************
1825 ****************************************************************************/
1826 static int Demux( demux_t *p_demux )
1828 demux_sys_t *p_sys = p_demux->p_sys;
1837 if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
1839 msg_Warn( p_demux, "cannot peek (EOF ?)" );
1843 i_es = GetDWBE( &p_peek[0] );
1844 if( i_es < 0 || i_es >= p_sys->i_es )
1846 msg_Err( p_demux, "cannot find ES" );
1850 i_size = GetDWBE( &p_peek[4] );
1851 i_pts = GetQWBE( &p_peek[8] );
1853 if( ( p_block = stream_Block( p_demux->s, 16 + i_size ) ) == NULL )
1855 msg_Warn( p_demux, "cannot read data" );
1859 p_block->p_buffer += 16;
1860 p_block->i_buffer -= 16;
1862 p_block->i_dts = p_block->i_pts = i_pts;
1864 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pts > 0 ? i_pts : 0 );
1865 es_out_Send( p_demux->out, p_sys->es[i_es], p_block );
1870 /****************************************************************************
1872 ****************************************************************************/
1873 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
1875 return demux2_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
1878 /*****************************************************************************
1879 * config variable callback
1880 *****************************************************************************/
1881 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1882 vlc_value_t newval, vlc_value_t oldval, void * )
1884 module_config_t *p_item;
1885 vlc_bool_t b_audio = VLC_FALSE;
1888 p_item = config_FindConfig( p_this, psz_name );
1889 if( !p_item ) return VLC_SUCCESS;
1891 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1893 /* Clear-up the current list */
1894 if( p_item->i_list )
1896 /* Keep the 2 first entries */
1897 for( i = 2; i < p_item->i_list; i++ )
1899 free( p_item->ppsz_list[i] );
1900 free( p_item->ppsz_list_text[i] );
1902 /* TODO: Remove when no more needed */
1903 p_item->ppsz_list[i] = NULL;
1904 p_item->ppsz_list_text[i] = NULL;
1908 /* Find list of devices */
1909 list<string> list_devices;
1911 /* Initialize OLE/COM */
1914 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1916 /* Uninitialize OLE/COM */
1919 if( !list_devices.size() ) return VLC_SUCCESS;
1922 (char **)realloc( p_item->ppsz_list,
1923 (list_devices.size()+3) * sizeof(char *) );
1924 p_item->ppsz_list_text =
1925 (char **)realloc( p_item->ppsz_list_text,
1926 (list_devices.size()+3) * sizeof(char *) );
1928 list<string>::iterator iter;
1929 for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1932 p_item->ppsz_list[i] = strdup( iter->c_str() );
1933 p_item->ppsz_list_text[i] = NULL;
1936 p_item->ppsz_list[i] = NULL;
1937 p_item->ppsz_list_text[i] = NULL;
1939 /* Signal change to the interface */
1940 p_item->b_dirty = VLC_TRUE;
1945 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1946 vlc_value_t newval, vlc_value_t oldval, void * )
1948 module_config_t *p_item;
1949 vlc_bool_t b_audio = VLC_FALSE;
1951 /* Initialize OLE/COM */
1954 p_item = config_FindConfig( p_this, psz_name );
1955 if( !p_item ) return VLC_SUCCESS;
1957 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1961 if( newval.psz_string && *newval.psz_string )
1963 devicename = newval.psz_string;
1967 /* If no device name was specified, pick the 1st one */
1968 list<string> list_devices;
1970 /* Enumerate devices */
1971 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1972 if( !list_devices.size() ) return VLC_EGENERIC;
1973 devicename = *list_devices.begin();
1976 IBaseFilter *p_device_filter =
1977 FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1978 if( p_device_filter )
1980 PropertiesPage( p_this, p_device_filter, NULL, b_audio );
1984 /* Uninitialize OLE/COM */
1987 msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
1988 return VLC_EGENERIC;
1991 /* Uninitialize OLE/COM */
1997 static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid )
1999 if( cauuid->cElems > 0 )
2001 HWND hwnd_desktop = ::GetDesktopWindow();
2003 OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
2004 cauuid->cElems, cauuid->pElems, 0, 0, NULL );
2006 CoTaskMemFree( cauuid->pElems );
2010 static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter,
2011 ICaptureGraphBuilder2 *p_capture_graph,
2012 vlc_bool_t b_audio )
2016 msg_Dbg( p_this, "Configuring Device Properties" );
2019 * Video or audio capture filter page
2021 ISpecifyPropertyPages *p_spec;
2023 HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages,
2027 if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
2029 ShowPropertyPage( p_device_filter, &cauuid );
2034 msg_Dbg( p_this, "looking for WDM Configuration Pages" );
2036 if( p_capture_graph )
2037 msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" );
2042 if( p_capture_graph && b_audio )
2044 IAMStreamConfig *p_SC;
2046 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2047 &MEDIATYPE_Audio, p_device_filter,
2048 IID_IAMStreamConfig,
2052 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2056 hr = p_spec->GetPages( &cauuid );
2059 for( unsigned int c = 0; c < cauuid.cElems; c++ )
2061 ShowPropertyPage( p_SC, &cauuid );
2063 CoTaskMemFree( cauuid.pElems );
2074 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2075 &MEDIATYPE_Audio, p_device_filter,
2076 IID_IAMTVAudio, (void **)&p_TVA );
2079 hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages,
2083 if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) )
2084 ShowPropertyPage(p_TVA, &cauuid);
2095 if( p_capture_graph && !b_audio )
2097 IAMStreamConfig *p_SC;
2099 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2100 &MEDIATYPE_Interleaved,
2102 IID_IAMStreamConfig,
2106 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2109 IID_IAMStreamConfig,
2115 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2119 if( SUCCEEDED( p_spec->GetPages(&cauuid) ) )
2121 ShowPropertyPage(p_SC, &cauuid);
2129 * Video crossbar, and a possible second crossbar
2131 IAMCrossbar *p_X, *p_X2;
2134 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2135 &MEDIATYPE_Interleaved,
2137 IID_IAMCrossbar, (void **)&p_X );
2140 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2149 hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF );
2152 hr = p_X->QueryInterface( IID_ISpecifyPropertyPages,
2156 hr = p_spec->GetPages(&cauuid);
2157 if( hr == S_OK && cauuid.cElems > 0 )
2159 ShowPropertyPage( p_X, &cauuid );
2164 hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL,
2165 p_XF, IID_IAMCrossbar,
2169 hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages,
2173 hr = p_spec->GetPages( &cauuid );
2176 ShowPropertyPage( p_X2, &cauuid );
2193 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2194 &MEDIATYPE_Interleaved,
2196 IID_IAMTVTuner, (void **)&p_TV );
2199 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2208 hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages,
2212 hr = p_spec->GetPages(&cauuid);
2215 ShowPropertyPage(p_TV, &cauuid);