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:
548 case ACCESS_SET_PRIVATE_ID_STATE:
552 msg_Warn( p_access, "unimplemented query in control" );
559 /****************************************************************************
560 * RouteCrossbars (Does not AddRef the returned *Pin)
561 ****************************************************************************/
562 static HRESULT GetCrossbarIPinAtIndex( IAMCrossbar *pXbar, LONG PinIndex,
563 BOOL IsInputPin, IPin ** ppPin )
565 LONG cntInPins, cntOutPins;
567 IBaseFilter *pFilter = NULL;
571 if( !pXbar || !ppPin ) return E_POINTER;
575 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) return E_FAIL;
577 LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins;
579 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
581 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
584 while( pins->Next(1, &pP, &n) == S_OK )
599 return *ppPin ? S_OK : E_FAIL;
602 /****************************************************************************
603 * GetCrossbarIndexFromIPin: Find corresponding index of an IPin on a crossbar
604 ****************************************************************************/
605 static HRESULT GetCrossbarIndexFromIPin( IAMCrossbar * pXbar, LONG * PinIndex,
606 BOOL IsInputPin, IPin * pPin )
608 LONG cntInPins, cntOutPins;
610 IBaseFilter *pFilter = NULL;
615 if(!pXbar || !PinIndex || !pPin )
618 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) )
621 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
623 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
627 while( pins->Next(1, &pP, &n) == S_OK )
632 *PinIndex = IsInputPin ? i : i - cntInPins;
643 return fOK ? S_OK : E_FAIL;
646 /****************************************************************************
648 ****************************************************************************/
649 static HRESULT FindCrossbarRoutes( access_t *p_access, IPin *p_input_pin,
650 LONG physicalType, int depth = 0 )
652 access_sys_t *p_sys = p_access->p_sys;
653 HRESULT result = S_FALSE;
656 if( FAILED(p_input_pin->ConnectedTo(&p_output_pin)) ) return S_FALSE;
658 // It is connected, so now find out if the filter supports IAMCrossbar
660 if( FAILED(p_output_pin->QueryPinInfo(&pinInfo)) ||
661 PINDIR_OUTPUT != pinInfo.dir )
663 p_output_pin->Release ();
667 IAMCrossbar *pXbar=0;
668 if( FAILED(pinInfo.pFilter->QueryInterface(IID_IAMCrossbar,
671 pinInfo.pFilter->Release();
672 p_output_pin->Release ();
676 LONG inputPinCount, outputPinCount;
677 if( FAILED(pXbar->get_PinCounts(&outputPinCount, &inputPinCount)) )
680 pinInfo.pFilter->Release();
681 p_output_pin->Release ();
685 LONG inputPinIndexRelated, outputPinIndexRelated;
686 LONG inputPinPhysicalType, outputPinPhysicalType;
687 LONG inputPinIndex, outputPinIndex;
688 if( FAILED(GetCrossbarIndexFromIPin( pXbar, &outputPinIndex,
689 FALSE, p_output_pin )) ||
690 FAILED(pXbar->get_CrossbarPinInfo( FALSE, outputPinIndex,
691 &outputPinIndexRelated,
692 &outputPinPhysicalType )) )
695 pinInfo.pFilter->Release();
696 p_output_pin->Release ();
701 // for all input pins
703 for( inputPinIndex = 0; S_OK != result && inputPinIndex < inputPinCount;
706 if( FAILED(pXbar->get_CrossbarPinInfo( TRUE, inputPinIndex,
707 &inputPinIndexRelated, &inputPinPhysicalType )) ) continue;
709 // Is the pin a video pin?
710 if( inputPinPhysicalType != physicalType ) continue;
713 if( FAILED(pXbar->CanRoute(outputPinIndex, inputPinIndex)) ) continue;
716 if( FAILED(GetCrossbarIPinAtIndex( pXbar, inputPinIndex,
717 TRUE, &pPin)) ) continue;
719 result = FindCrossbarRoutes( p_access, pPin, physicalType, depth+1 );
720 if( S_OK == result || (S_FALSE == result &&
721 physicalType == inputPinPhysicalType &&
722 (p_sys->i_crossbar_route_depth = depth+1) < MAX_CROSSBAR_DEPTH) )
727 // remember crossbar route
728 p_sys->crossbar_routes[depth].pXbar = pXbar;
729 p_sys->crossbar_routes[depth].VideoInputIndex = inputPinIndex;
730 p_sys->crossbar_routes[depth].VideoOutputIndex = outputPinIndex;
731 p_sys->crossbar_routes[depth].AudioInputIndex = inputPinIndexRelated;
732 p_sys->crossbar_routes[depth].AudioOutputIndex = outputPinIndexRelated;
734 msg_Dbg( p_access, "Crossbar at depth %d, Found Route For "
735 "ouput %ld (type %ld) to input %ld (type %ld)", depth,
736 outputPinIndex, outputPinPhysicalType, inputPinIndex,
737 inputPinPhysicalType );
744 pinInfo.pFilter->Release();
745 p_output_pin->Release ();
750 /****************************************************************************
752 ****************************************************************************/
753 static bool ConnectFilters( access_t *p_access, IBaseFilter *p_filter,
754 CaptureFilter *p_capture_filter )
756 access_sys_t *p_sys = p_access->p_sys;
757 CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
759 AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
761 if( p_sys->p_capture_graph_builder2 )
763 if( FAILED(p_sys->p_capture_graph_builder2->
764 RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
766 (IBaseFilter *)p_capture_filter )) )
771 // Sort out all the possible video inputs
772 // The class needs to be given the capture filters ANALOGVIDEO input pin
774 if( mediaType.majortype == MEDIATYPE_Video &&
775 SUCCEEDED(p_filter->EnumPins(&pins)) )
781 IKsPropertySet *pKs=0;
785 while( !Found && (S_OK == pins->Next(1, &pP, &n)) )
787 if(S_OK == pP->QueryPinInfo(&pinInfo))
789 if(pinInfo.dir == PINDIR_INPUT)
791 // is this pin an ANALOGVIDEOIN input pin?
792 if( pP->QueryInterface(IID_IKsPropertySet,
793 (void **)&pKs) == S_OK )
795 if( pKs->Get(AMPROPSETID_Pin,
796 AMPROPERTY_PIN_CATEGORY, NULL, 0,
797 &guid, sizeof(GUID), &dw) == S_OK )
799 if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
801 // recursively search crossbar routes
802 FindCrossbarRoutes( p_access, pP,
803 PhysConn_Video_Tuner );
811 pinInfo.pFilter->Release();
821 IEnumPins *p_enumpins;
824 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
826 while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
828 PIN_DIRECTION pin_dir;
829 p_pin->QueryDirection( &pin_dir );
831 if( pin_dir == PINDIR_OUTPUT &&
832 p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
836 p_enumpins->Release();
842 p_enumpins->Release();
848 ** get fourcc priority from arbritary preference, the higher the better
850 static int GetFourCCPriority(int i_fourcc)
854 case VLC_FOURCC('I','4','2','0'):
855 case VLC_FOURCC('f','l','3','2'):
860 case VLC_FOURCC('Y','V','1','2'):
861 case VLC_FOURCC('a','r','a','w'):
866 case VLC_FOURCC('R','V','2','4'):
871 case VLC_FOURCC('Y','U','Y','2'):
872 case VLC_FOURCC('R','V','3','2'):
873 case VLC_FOURCC('R','G','B','A'):
881 #define MAX_MEDIA_TYPES 32
883 static int OpenDevice( access_t *p_access, string devicename,
886 access_sys_t *p_sys = p_access->p_sys;
888 /* See if device is already opened */
889 for( int i = 0; i < p_sys->i_streams; i++ )
891 if( p_sys->pp_streams[i]->devicename == devicename )
898 list<string> list_devices;
900 /* Enumerate devices and display their names */
901 FindCaptureDevice( (vlc_object_t *)p_access, NULL, &list_devices, b_audio );
903 if( !list_devices.size() )
906 list<string>::iterator iter;
907 for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
908 msg_Dbg( p_access, "found device: %s", iter->c_str() );
910 /* If no device name was specified, pick the 1st one */
911 if( devicename.size() == 0 )
913 devicename = *list_devices.begin();
916 // Use the system device enumerator and class enumerator to find
917 // a capture/preview device, such as a desktop USB video camera.
918 IBaseFilter *p_device_filter =
919 FindCaptureDevice( (vlc_object_t *)p_access, &devicename,
921 if( p_device_filter )
922 msg_Dbg( p_access, "using device: %s", devicename.c_str() );
925 msg_Err( p_access, "can't use device: %s, unsupported device type",
926 devicename.c_str() );
931 AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
933 size_t mt_count = EnumDeviceCaps( (vlc_object_t *)p_access,
934 p_device_filter, p_sys->i_chroma,
935 p_sys->i_width, p_sys->i_height,
936 0, 0, 0, media_types, MAX_MEDIA_TYPES );
940 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
942 // Order and copy returned media types according to arbitrary
944 for( size_t c=0; c<mt_count; c++ )
947 GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
948 size_t slot_copy = c;
949 for( size_t d=c+1; d<mt_count; d++ )
952 GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
953 if( priority > slot_priority )
955 slot_priority = priority;
961 mt[c] = media_types[slot_copy];
962 media_types[slot_copy] = media_types[c];
966 mt[c] = media_types[c];
970 else if( ! b_audio ) {
971 // Use default video media type
975 mtr.majortype = MEDIATYPE_Video;
976 mtr.subtype = MEDIASUBTYPE_I420;
977 mtr.bFixedSizeSamples = TRUE;
978 mtr.bTemporalCompression = FALSE;
981 mtr.formattype = FORMAT_VideoInfo;
982 mtr.cbFormat = sizeof(vh);
983 mtr.pbFormat = (BYTE *)&vh;
985 memset(&vh, 0, sizeof(vh));
987 vh.bmiHeader.biSize = sizeof(vh.bmiHeader);
988 vh.bmiHeader.biWidth = p_sys->i_width > 0 ? p_sys->i_width: 320;
989 vh.bmiHeader.biHeight = p_sys->i_height > 0 ? p_sys->i_height : 240;
990 vh.bmiHeader.biPlanes = 1;
991 vh.bmiHeader.biBitCount = 24;
992 vh.bmiHeader.biCompression = VLC_FOURCC('I','4','2','0');
993 vh.bmiHeader.biSizeImage = p_sys->i_width * 24 * p_sys->i_height / 8;
995 msg_Warn( p_access, "device %s using built-in video media type",
996 devicename.c_str() );
999 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1000 CopyMediaType(mt, &mtr);
1003 // Use default audio media type
1007 mtr.majortype = MEDIATYPE_Audio;
1008 mtr.subtype = MEDIASUBTYPE_PCM;
1009 mtr.bFixedSizeSamples = TRUE;
1010 mtr.bTemporalCompression = FALSE;
1011 mtr.lSampleSize = 0;
1013 mtr.formattype = FORMAT_WaveFormatEx;
1014 mtr.cbFormat = sizeof(wf);
1015 mtr.pbFormat = (BYTE *)&wf;
1017 memset(&wf, 0, sizeof(wf));
1019 wf.wFormatTag = WAVE_FORMAT_PCM;
1021 wf.nSamplesPerSec = 44100;
1022 wf.wBitsPerSample = 16;
1023 wf.nBlockAlign = wf.nSamplesPerSec * wf.wBitsPerSample / 8;
1024 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
1027 msg_Warn( p_access, "device %s using built-in audio media type",
1028 devicename.c_str() );
1031 mt = (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
1032 CopyMediaType(mt, &mtr);
1035 /* Create and add our capture filter */
1036 CaptureFilter *p_capture_filter =
1037 new CaptureFilter( p_access, mt, mt_count );
1038 p_sys->p_graph->AddFilter( p_capture_filter, 0 );
1040 /* Add the device filter to the graph (seems necessary with VfW before
1041 * accessing pin attributes). */
1042 p_sys->p_graph->AddFilter( p_device_filter, 0 );
1044 /* Attempt to connect one of this device's capture output pins */
1045 msg_Dbg( p_access, "connecting filters" );
1046 if( ConnectFilters( p_access, p_device_filter, p_capture_filter ) )
1049 msg_Dbg( p_access, "filters connected successfully !" );
1051 dshow_stream_t dshow_stream;
1052 dshow_stream.b_invert = VLC_FALSE;
1053 dshow_stream.b_pts = VLC_FALSE;
1055 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1057 /* Show properties. Done here so the VLC stream is setup with the
1058 * proper parameters. */
1060 var_Get( p_access, "dshow-config", &val );
1063 PropertiesPage( VLC_OBJECT(p_access), p_device_filter,
1064 p_sys->p_capture_graph_builder2,
1065 dshow_stream.mt.majortype == MEDIATYPE_Audio );
1069 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1071 dshow_stream.i_fourcc = GetFourCCFromMediaType(dshow_stream.mt);
1072 if( 0 != dshow_stream.i_fourcc )
1074 if( dshow_stream.mt.majortype == MEDIATYPE_Video )
1076 dshow_stream.header.video =
1077 *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
1079 int i_height = dshow_stream.header.video.bmiHeader.biHeight;
1081 /* Check if the image is inverted (bottom to top) */
1082 if( dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','1') ||
1083 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','4') ||
1084 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','8') ||
1085 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','5') ||
1086 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','6') ||
1087 dshow_stream.i_fourcc == VLC_FOURCC('R','V','2','4') ||
1088 dshow_stream.i_fourcc == VLC_FOURCC('R','V','3','2') ||
1089 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','A') )
1091 if( i_height > 0 ) dshow_stream.b_invert = VLC_TRUE;
1092 else i_height = - i_height;
1095 /* Check if we are dealing with a DV stream */
1096 if( dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','l') ||
1097 dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','d') ||
1098 dshow_stream.i_fourcc == VLC_FOURCC('d','v','h','d') )
1100 p_access->pf_read = ReadCompressed;
1101 if( !p_access->psz_demux || !*p_access->psz_demux )
1103 p_access->psz_demux = strdup( "rawdv" );
1105 p_sys->b_audio = VLC_FALSE;
1108 /* Check if we are dealing with an MPEG video stream */
1109 if( dshow_stream.i_fourcc == VLC_FOURCC('m','p','2','v') )
1111 p_access->pf_read = ReadCompressed;
1112 if( !p_access->psz_demux || !*p_access->psz_demux )
1114 p_access->psz_demux = "mpgv";
1116 p_sys->b_audio = VLC_FALSE;
1119 /* Add video stream to header */
1120 p_sys->i_header_size += 20;
1121 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1122 p_sys->i_header_size );
1123 memcpy( &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
1124 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1125 &dshow_stream.i_fourcc, 4 );
1126 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1127 dshow_stream.header.video.bmiHeader.biWidth );
1128 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height );
1129 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 );
1130 p_sys->i_header_pos = p_sys->i_header_size;
1132 /* Greatly simplifies the reading routine */
1133 int i_mtu = dshow_stream.header.video.bmiHeader.biWidth *
1135 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1138 else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
1140 dshow_stream.header.audio =
1141 *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
1143 /* Add audio stream to header */
1144 p_sys->i_header_size += 20;
1145 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1146 p_sys->i_header_size );
1147 memcpy( &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
1148 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1149 &dshow_stream.i_fourcc, 4 );
1150 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1151 dshow_stream.header.audio.nChannels );
1152 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12],
1153 dshow_stream.header.audio.nSamplesPerSec );
1154 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16],
1155 dshow_stream.header.audio.wBitsPerSample );
1156 p_sys->i_header_pos = p_sys->i_header_size;
1158 /* Greatly simplifies the reading routine */
1159 IAMBufferNegotiation *p_ambuf;
1163 p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin );
1164 if( SUCCEEDED( p_pin->QueryInterface(
1165 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1167 ALLOCATOR_PROPERTIES AllocProp;
1168 memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) );
1169 p_ambuf->GetAllocatorProperties( &AllocProp );
1171 i_mtu = AllocProp.cbBuffer;
1176 i_mtu = dshow_stream.header.audio.nSamplesPerSec *
1177 dshow_stream.header.audio.nChannels *
1178 dshow_stream.header.audio.wBitsPerSample / 8;
1181 p_sys->i_mtu = __MAX( p_sys->i_mtu, i_mtu );
1184 else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
1186 msg_Dbg( p_access, "MEDIATYPE_Stream" );
1188 msg_Dbg( p_access, "selected stream pin accepts format: %4.4s",
1189 (char *)&dshow_stream.i_fourcc);
1191 p_sys->b_audio = VLC_FALSE;
1192 p_sys->i_header_size = 0;
1193 p_sys->i_header_pos = 0;
1196 p_access->pf_read = ReadCompressed;
1200 msg_Dbg( p_access, "unknown stream majortype" );
1204 /* Add directshow elementary stream to our list */
1205 dshow_stream.p_device_filter = p_device_filter;
1206 dshow_stream.p_capture_filter = p_capture_filter;
1208 p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams,
1209 sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) );
1210 p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1211 *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1212 SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams );
1219 /* Remove filters from graph */
1220 p_sys->p_graph->RemoveFilter( p_device_filter );
1221 p_sys->p_graph->RemoveFilter( p_capture_filter );
1223 /* Release objects */
1224 p_device_filter->Release();
1225 p_capture_filter->Release();
1227 return VLC_EGENERIC;
1230 static IBaseFilter *
1231 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1232 list<string> *p_listdevices, vlc_bool_t b_audio )
1234 IBaseFilter *p_base_filter = NULL;
1235 IMoniker *p_moniker = NULL;
1239 /* Create the system device enumerator */
1240 ICreateDevEnum *p_dev_enum = NULL;
1242 hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1243 IID_ICreateDevEnum, (void **)&p_dev_enum );
1246 msg_Err( p_this, "failed to create the device enumerator (0x%lx)", hr);
1250 /* Create an enumerator for the video capture devices */
1251 IEnumMoniker *p_class_enum = NULL;
1253 hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1256 hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1258 p_dev_enum->Release();
1261 msg_Err( p_this, "failed to create the class enumerator (0x%lx)", hr );
1265 /* If there are no enumerators for the requested type, then
1266 * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1267 if( p_class_enum == NULL )
1269 msg_Err( p_this, "no capture device was detected" );
1273 /* Enumerate the devices */
1275 /* Note that if the Next() call succeeds but there are no monikers,
1276 * it will return S_FALSE (which is not a failure). Therefore, we check
1277 * that the return code is S_OK instead of using SUCCEEDED() macro. */
1279 while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1281 /* Getting the property page to get the device name */
1282 IPropertyBag *p_bag;
1283 hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1289 hr = p_bag->Read( L"FriendlyName", &var, NULL );
1293 int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
1294 char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
1295 WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
1296 i_convert, NULL, NULL );
1297 SysFreeString(var.bstrVal);
1299 if( p_listdevices ) p_listdevices->push_back( p_buf );
1301 if( p_devicename && *p_devicename == string(p_buf) )
1303 /* Bind Moniker to a filter object */
1304 hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1305 (void **)&p_base_filter );
1308 msg_Err( p_this, "couldn't bind moniker to filter "
1309 "object (0x%lx)", hr );
1310 p_moniker->Release();
1311 p_class_enum->Release();
1314 p_moniker->Release();
1315 p_class_enum->Release();
1316 return p_base_filter;
1321 p_moniker->Release();
1324 p_class_enum->Release();
1328 static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter,
1329 int i_fourcc, int i_width, int i_height,
1330 int i_channels, int i_samplespersec,
1331 int i_bitspersample, AM_MEDIA_TYPE *mt,
1334 IEnumPins *p_enumpins;
1336 IEnumMediaTypes *p_enummt;
1337 size_t mt_count = 0;
1339 if( FAILED(p_filter->EnumPins( &p_enumpins )) )
1341 msg_Dbg( p_this, "EnumDeviceCaps failed: no pin enumeration !");
1345 while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1349 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1351 msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1352 info.dir == PINDIR_INPUT ? "input" : "output",
1354 if( info.pFilter ) info.pFilter->Release();
1357 p_output_pin->Release();
1360 p_enumpins->Reset();
1362 while( !mt_count && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1366 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1368 if( info.pFilter ) info.pFilter->Release();
1369 if( info.dir == PINDIR_INPUT )
1371 p_output_pin->Release();
1374 msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1378 if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1380 AM_MEDIA_TYPE *p_mt;
1381 while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1383 int i_current_fourcc = GetFourCCFromMediaType(*p_mt);
1384 if( 0 != i_current_fourcc )
1386 if( p_mt->majortype == MEDIATYPE_Video )
1388 int i_current_width = p_mt->pbFormat ?
1389 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0;
1390 int i_current_height = p_mt->pbFormat ?
1391 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0;
1392 if( i_current_height < 0 )
1393 i_current_height = -i_current_height;
1395 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1396 "accepts chroma: %4.4s, width:%i, height:%i",
1397 (char *)&i_current_fourcc, i_current_width,
1400 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1401 ( !i_width || i_width == i_current_width ) &&
1402 ( !i_height || i_height == i_current_height ) &&
1403 (mt_count < mt_max) )
1406 mt[mt_count++] = *p_mt;
1410 FreeMediaType( *p_mt );
1413 else if( p_mt->majortype == MEDIATYPE_Audio )
1415 int i_current_channels =
1416 ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1417 int i_current_samplespersec =
1418 ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1419 int i_current_bitspersample =
1420 ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1422 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1423 "accepts format: %4.4s, channels:%i, "
1424 "samples/sec:%i bits/sample:%i",
1425 (char *)&i_current_fourcc, i_current_channels,
1426 i_current_samplespersec, i_current_bitspersample);
1428 if( (!i_channels || i_channels == i_current_channels) &&
1429 (!i_samplespersec ||
1430 i_samplespersec == i_current_samplespersec) &&
1431 (!i_bitspersample ||
1432 i_bitspersample == i_current_bitspersample) &&
1433 (mt_count < mt_max) )
1436 mt[mt_count++] = *p_mt;
1438 /* Pre-Configure the 1st match, Ugly */
1439 if( 1 == mt_count ) {
1440 /* Setup a few properties like the audio latency */
1441 IAMBufferNegotiation *p_ambuf;
1443 if( SUCCEEDED( p_output_pin->QueryInterface(
1444 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1446 ALLOCATOR_PROPERTIES AllocProp;
1447 AllocProp.cbAlign = -1;
1448 AllocProp.cbBuffer = i_current_channels *
1449 i_current_samplespersec *
1450 i_current_bitspersample / 8 / 10 ; /*100 ms of latency*/
1451 AllocProp.cbPrefix = -1;
1452 AllocProp.cBuffers = -1;
1453 p_ambuf->SuggestAllocatorProperties( &AllocProp );
1460 FreeMediaType( *p_mt );
1463 else if( p_mt->majortype == MEDIATYPE_Stream )
1465 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1466 (mt_count < mt_max) )
1469 mt[mt_count++] = *p_mt;
1470 i_fourcc = i_current_fourcc;
1474 FreeMediaType( *p_mt );
1480 "EnumDeviceCaps: input pin: unknown format" );
1481 FreeMediaType( *p_mt );
1487 "EnumDeviceCaps: input pin: unknown format" );
1488 FreeMediaType( *p_mt );
1490 CoTaskMemFree( (PVOID)p_mt );
1492 p_enummt->Release();
1495 p_output_pin->Release();
1498 p_enumpins->Release();
1502 /*****************************************************************************
1503 * AccessRead: reads from the device.
1504 *****************************************************************************
1505 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1507 *****************************************************************************/
1508 static int AccessRead( access_t *p_access, uint8_t *p_buffer, int i_len )
1510 access_sys_t *p_sys = p_access->p_sys;
1511 dshow_stream_t *p_stream = NULL;
1512 byte_t *p_buf_orig = p_buffer;
1513 VLCMediaSample sample;
1517 if( p_sys->i_header_pos )
1519 /* First header of the stream */
1520 memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size );
1521 p_buffer += p_sys->i_header_size;
1522 p_sys->i_header_pos = 0;
1527 /* Get new sample/frame from next elementary stream.
1528 * We first loop through all the elementary streams and if all our
1529 * fifos are empty we block until we are signaled some new data has
1531 vlc_mutex_lock( &p_sys->lock );
1534 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1536 p_stream = p_sys->pp_streams[i_stream];
1537 if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1538 p_stream->p_capture_filter &&
1539 p_stream->p_capture_filter->CustomGetPin()
1540 ->CustomGetSample( &sample ) == S_OK )
1545 if( i_stream == p_sys->i_streams )
1546 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1548 p_stream = p_sys->pp_streams[i_stream];
1549 if( p_stream->p_capture_filter &&
1550 p_stream->p_capture_filter->CustomGetPin()
1551 ->CustomGetSample( &sample ) == S_OK )
1556 if( i_stream == p_sys->i_streams )
1558 /* No data available. Wait until some data has arrived */
1559 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1560 vlc_mutex_unlock( &p_sys->lock );
1564 vlc_mutex_unlock( &p_sys->lock );
1569 i_data_size = sample.p_sample->GetActualDataLength();
1570 sample.p_sample->GetPointer( &p_data );
1572 REFERENCE_TIME i_pts, i_end_date;
1573 HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1574 if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1578 if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1580 /* Use our data timestamp */
1581 i_pts = sample.i_timestamp;
1582 p_stream->b_pts = VLC_TRUE;
1587 msg_Dbg( p_access, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts );
1590 /* Create pseudo header */
1591 SetDWBE( &p_sys->p_header[0], i_stream );
1592 SetDWBE( &p_sys->p_header[4], i_data_size );
1593 SetQWBE( &p_sys->p_header[8], i_pts / 10 );
1596 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1599 /* First copy header */
1600 memcpy( p_buffer, p_sys->p_header, 16 /* header size */ );
1601 p_buffer += 16 /* header size */;
1603 /* Then copy stream data if any */
1604 if( !p_stream->b_invert )
1606 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1607 p_buffer += i_data_size;
1611 int i_width = p_stream->header.video.bmiHeader.biWidth;
1612 int i_height = p_stream->header.video.bmiHeader.biHeight;
1613 if( i_height < 0 ) i_height = - i_height;
1615 switch( p_stream->i_fourcc )
1617 case VLC_FOURCC( 'R', 'V', '1', '5' ):
1618 case VLC_FOURCC( 'R', 'V', '1', '6' ):
1621 case VLC_FOURCC( 'R', 'V', '2', '4' ):
1624 case VLC_FOURCC( 'R', 'V', '3', '2' ):
1625 case VLC_FOURCC( 'R', 'G', 'B', 'A' ):
1630 for( int i = i_height - 1; i >= 0; i-- )
1632 p_access->p_vlc->pf_memcpy( p_buffer,
1633 &p_data[i * i_width], i_width );
1635 p_buffer += i_width;
1639 sample.p_sample->Release();
1641 /* The caller got what he wanted */
1642 return p_buffer - p_buf_orig;
1645 return 0; /* never reached */
1648 /*****************************************************************************
1649 * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1650 *****************************************************************************
1651 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1653 *****************************************************************************/
1654 static int ReadCompressed( access_t *p_access, uint8_t *p_buffer, int i_len )
1656 access_sys_t *p_sys = p_access->p_sys;
1657 dshow_stream_t *p_stream = NULL;
1658 VLCMediaSample sample;
1662 /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1664 /* There must be only 1 elementary stream to produce a valid stream
1665 * of MPEG or DV data */
1666 p_stream = p_sys->pp_streams[0];
1670 if( p_access->b_die || p_access->b_error ) return 0;
1672 /* Get new sample/frame from the elementary stream (blocking). */
1673 vlc_mutex_lock( &p_sys->lock );
1675 if( p_stream->p_capture_filter->CustomGetPin()
1676 ->CustomGetSample( &sample ) != S_OK )
1678 /* No data available. Wait until some data has arrived */
1679 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1680 vlc_mutex_unlock( &p_sys->lock );
1684 vlc_mutex_unlock( &p_sys->lock );
1689 i_data_size = sample.p_sample->GetActualDataLength();
1690 sample.p_sample->GetPointer( &p_data );
1693 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1695 i_data_size = __MIN( i_data_size, (int)i_len );
1697 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1699 sample.p_sample->Release();
1701 /* The caller got what he wanted */
1705 return 0; /* never reached */
1708 /*****************************************************************************
1709 * Demux: local prototypes
1710 *****************************************************************************/
1717 static int Demux ( demux_t * );
1718 static int DemuxControl( demux_t *, int, va_list );
1720 /****************************************************************************
1722 ****************************************************************************/
1723 static int DemuxOpen( vlc_object_t *p_this )
1725 demux_t *p_demux = (demux_t *)p_this;
1732 /* a little test to see if it's a dshow stream */
1733 if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
1735 msg_Warn( p_demux, "dshow plugin discarded (cannot peek)" );
1736 return VLC_EGENERIC;
1739 if( memcmp( p_peek, ".dsh", 4 ) ||
1740 ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
1742 msg_Warn( p_demux, "dshow plugin discarded (not a valid stream)" );
1743 return VLC_EGENERIC;
1746 p_demux->pf_demux = Demux;
1747 p_demux->pf_control = DemuxControl;
1748 p_demux->p_sys = p_sys = (demux_sys_t *)malloc( sizeof( demux_sys_t ) );
1752 if( stream_Peek( p_demux->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
1754 msg_Err( p_demux, "dshow plugin discarded (cannot peek)" );
1755 return VLC_EGENERIC;
1759 for( i = 0; i < i_es; i++ )
1763 if( !memcmp( p_peek, "auds", 4 ) )
1765 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1766 p_peek[6], p_peek[7] ) );
1768 fmt.audio.i_channels = GetDWBE( &p_peek[8] );
1769 fmt.audio.i_rate = GetDWBE( &p_peek[12] );
1770 fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
1771 fmt.audio.i_blockalign = fmt.audio.i_channels *
1772 fmt.audio.i_bitspersample / 8;
1773 fmt.i_bitrate = fmt.audio.i_channels *
1775 fmt.audio.i_bitspersample;
1777 msg_Dbg( p_demux, "new audio es %d channels %dHz",
1778 fmt.audio.i_channels, fmt.audio.i_rate );
1780 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1781 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1782 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1784 else if( !memcmp( p_peek, "vids", 4 ) )
1786 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1787 p_peek[6], p_peek[7] ) );
1788 fmt.video.i_width = GetDWBE( &p_peek[8] );
1789 fmt.video.i_height = GetDWBE( &p_peek[12] );
1791 msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
1792 (char*)&fmt.i_codec,
1793 fmt.video.i_width, fmt.video.i_height );
1795 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1796 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1797 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1804 stream_Read( p_demux->s, NULL, 8 + 20 * i_es );
1809 /****************************************************************************
1811 ****************************************************************************/
1812 static void DemuxClose( vlc_object_t *p_this )
1814 demux_t *p_demux = (demux_t *)p_this;
1815 demux_sys_t *p_sys = p_demux->p_sys;
1817 if( p_sys->i_es > 0 )
1824 /****************************************************************************
1826 ****************************************************************************/
1827 static int Demux( demux_t *p_demux )
1829 demux_sys_t *p_sys = p_demux->p_sys;
1838 if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
1840 msg_Warn( p_demux, "cannot peek (EOF ?)" );
1844 i_es = GetDWBE( &p_peek[0] );
1845 if( i_es < 0 || i_es >= p_sys->i_es )
1847 msg_Err( p_demux, "cannot find ES" );
1851 i_size = GetDWBE( &p_peek[4] );
1852 i_pts = GetQWBE( &p_peek[8] );
1854 if( ( p_block = stream_Block( p_demux->s, 16 + i_size ) ) == NULL )
1856 msg_Warn( p_demux, "cannot read data" );
1860 p_block->p_buffer += 16;
1861 p_block->i_buffer -= 16;
1863 p_block->i_dts = p_block->i_pts = i_pts;
1865 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pts > 0 ? i_pts : 0 );
1866 es_out_Send( p_demux->out, p_sys->es[i_es], p_block );
1871 /****************************************************************************
1873 ****************************************************************************/
1874 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
1876 return demux2_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
1879 /*****************************************************************************
1880 * config variable callback
1881 *****************************************************************************/
1882 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1883 vlc_value_t newval, vlc_value_t oldval, void * )
1885 module_config_t *p_item;
1886 vlc_bool_t b_audio = VLC_FALSE;
1889 p_item = config_FindConfig( p_this, psz_name );
1890 if( !p_item ) return VLC_SUCCESS;
1892 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1894 /* Clear-up the current list */
1895 if( p_item->i_list )
1897 /* Keep the 2 first entries */
1898 for( i = 2; i < p_item->i_list; i++ )
1900 free( p_item->ppsz_list[i] );
1901 free( p_item->ppsz_list_text[i] );
1903 /* TODO: Remove when no more needed */
1904 p_item->ppsz_list[i] = NULL;
1905 p_item->ppsz_list_text[i] = NULL;
1909 /* Find list of devices */
1910 list<string> list_devices;
1912 /* Initialize OLE/COM */
1915 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1917 /* Uninitialize OLE/COM */
1920 if( !list_devices.size() ) return VLC_SUCCESS;
1923 (char **)realloc( p_item->ppsz_list,
1924 (list_devices.size()+3) * sizeof(char *) );
1925 p_item->ppsz_list_text =
1926 (char **)realloc( p_item->ppsz_list_text,
1927 (list_devices.size()+3) * sizeof(char *) );
1929 list<string>::iterator iter;
1930 for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1933 p_item->ppsz_list[i] = strdup( iter->c_str() );
1934 p_item->ppsz_list_text[i] = NULL;
1937 p_item->ppsz_list[i] = NULL;
1938 p_item->ppsz_list_text[i] = NULL;
1940 /* Signal change to the interface */
1941 p_item->b_dirty = VLC_TRUE;
1946 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1947 vlc_value_t newval, vlc_value_t oldval, void * )
1949 module_config_t *p_item;
1950 vlc_bool_t b_audio = VLC_FALSE;
1952 /* Initialize OLE/COM */
1955 p_item = config_FindConfig( p_this, psz_name );
1956 if( !p_item ) return VLC_SUCCESS;
1958 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1962 if( newval.psz_string && *newval.psz_string )
1964 devicename = newval.psz_string;
1968 /* If no device name was specified, pick the 1st one */
1969 list<string> list_devices;
1971 /* Enumerate devices */
1972 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1973 if( !list_devices.size() ) return VLC_EGENERIC;
1974 devicename = *list_devices.begin();
1977 IBaseFilter *p_device_filter =
1978 FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1979 if( p_device_filter )
1981 PropertiesPage( p_this, p_device_filter, NULL, b_audio );
1985 /* Uninitialize OLE/COM */
1988 msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
1989 return VLC_EGENERIC;
1992 /* Uninitialize OLE/COM */
1998 static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid )
2000 if( cauuid->cElems > 0 )
2002 HWND hwnd_desktop = ::GetDesktopWindow();
2004 OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
2005 cauuid->cElems, cauuid->pElems, 0, 0, NULL );
2007 CoTaskMemFree( cauuid->pElems );
2011 static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter,
2012 ICaptureGraphBuilder2 *p_capture_graph,
2013 vlc_bool_t b_audio )
2017 msg_Dbg( p_this, "Configuring Device Properties" );
2020 * Video or audio capture filter page
2022 ISpecifyPropertyPages *p_spec;
2024 HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages,
2028 if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
2030 ShowPropertyPage( p_device_filter, &cauuid );
2035 msg_Dbg( p_this, "looking for WDM Configuration Pages" );
2037 if( p_capture_graph )
2038 msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" );
2043 if( p_capture_graph && b_audio )
2045 IAMStreamConfig *p_SC;
2047 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2048 &MEDIATYPE_Audio, p_device_filter,
2049 IID_IAMStreamConfig,
2053 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2057 hr = p_spec->GetPages( &cauuid );
2060 for( unsigned int c = 0; c < cauuid.cElems; c++ )
2062 ShowPropertyPage( p_SC, &cauuid );
2064 CoTaskMemFree( cauuid.pElems );
2075 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2076 &MEDIATYPE_Audio, p_device_filter,
2077 IID_IAMTVAudio, (void **)&p_TVA );
2080 hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages,
2084 if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) )
2085 ShowPropertyPage(p_TVA, &cauuid);
2096 if( p_capture_graph && !b_audio )
2098 IAMStreamConfig *p_SC;
2100 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2101 &MEDIATYPE_Interleaved,
2103 IID_IAMStreamConfig,
2107 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2110 IID_IAMStreamConfig,
2116 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2120 if( SUCCEEDED( p_spec->GetPages(&cauuid) ) )
2122 ShowPropertyPage(p_SC, &cauuid);
2130 * Video crossbar, and a possible second crossbar
2132 IAMCrossbar *p_X, *p_X2;
2135 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2136 &MEDIATYPE_Interleaved,
2138 IID_IAMCrossbar, (void **)&p_X );
2141 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2150 hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF );
2153 hr = p_X->QueryInterface( IID_ISpecifyPropertyPages,
2157 hr = p_spec->GetPages(&cauuid);
2158 if( hr == S_OK && cauuid.cElems > 0 )
2160 ShowPropertyPage( p_X, &cauuid );
2165 hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL,
2166 p_XF, IID_IAMCrossbar,
2170 hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages,
2174 hr = p_spec->GetPages( &cauuid );
2177 ShowPropertyPage( p_X2, &cauuid );
2194 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2195 &MEDIATYPE_Interleaved,
2197 IID_IAMTVTuner, (void **)&p_TV );
2200 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2209 hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages,
2213 hr = p_spec->GetPages(&cauuid);
2216 ShowPropertyPage(p_TV, &cauuid);