1 /*****************************************************************************
2 * dshow.cpp : DirectShow access module for vlc
3 *****************************************************************************
4 * Copyright (C) 2002, 2003 VideoLAN
7 * Author: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc/input.h>
37 /*****************************************************************************
38 * Access: local prototypes
39 *****************************************************************************/
40 static int AccessRead ( access_t *, byte_t *, int );
41 static int ReadCompressed( access_t *, byte_t *, int );
42 static int AccessControl ( access_t *, int, va_list );
44 static int OpenDevice( access_t *, string, vlc_bool_t );
45 static IBaseFilter *FindCaptureDevice( vlc_object_t *, string *,
46 list<string> *, vlc_bool_t );
47 static size_t EnumDeviceCaps( vlc_object_t *, IBaseFilter *,
48 int, int, int, int, int, int, AM_MEDIA_TYPE *mt, size_t mt_max);
49 static bool ConnectFilters( access_t *, IBaseFilter *, CaptureFilter * );
51 static int FindDevicesCallback( vlc_object_t *, char const *,
52 vlc_value_t, vlc_value_t, void * );
53 static int ConfigDevicesCallback( vlc_object_t *, char const *,
54 vlc_value_t, vlc_value_t, void * );
56 static void PropertiesPage( vlc_object_t *, IBaseFilter *,
57 ICaptureGraphBuilder2 *, vlc_bool_t );
60 /* Debug only, use this to find out GUIDs */
62 UuidToString( (IID *)&IID_IAMBufferNegotiation, &p_st );
63 msg_Err( p_access, "BufferNegotiation: %s" , p_st );
87 static void SetDWBE( uint8_t *p, uint32_t dw )
89 p[0] = (dw >> 24)&0xff;
90 p[1] = (dw >> 16)&0xff;
91 p[2] = (dw >> 8)&0xff;
95 static void SetQWBE( uint8_t *p, uint64_t qw )
97 SetDWBE( p, (qw >> 32)&0xffffffff );
98 SetDWBE( &p[4], qw&0xffffffff );
101 /*****************************************************************************
103 *****************************************************************************/
104 static char *ppsz_vdev[] = { "", "none" };
105 static char *ppsz_vdev_text[] = { N_("Default"), N_("None") };
106 static char *ppsz_adev[] = { "", "none" };
107 static char *ppsz_adev_text[] = { N_("Default"), N_("None") };
109 #define CACHING_TEXT N_("Caching value in ms")
110 #define CACHING_LONGTEXT N_( \
111 "Allows you to modify the default caching value for DirectShow streams. " \
112 "This value should be set in milliseconds units." )
113 #define VDEV_TEXT N_("Video device name")
114 #define VDEV_LONGTEXT N_( \
115 "You can specify the name of the video device that will be used by the " \
116 "DirectShow plugin. If you don't specify anything, the default device " \
118 #define ADEV_TEXT N_("Audio device name")
119 #define ADEV_LONGTEXT N_( \
120 "You can specify the name of the audio device that will be used by the " \
121 "DirectShow plugin. If you don't specify anything, the default device " \
123 #define SIZE_TEXT N_("Video size")
124 #define SIZE_LONGTEXT N_( \
125 "You can specify the size of the video that will be displayed by the " \
126 "DirectShow plugin. If you don't specify anything the default size for " \
127 "your device will be used.")
128 #define CHROMA_TEXT N_("Video input chroma format")
129 #define CHROMA_LONGTEXT N_( \
130 "Force the DirectShow video input to use a specific chroma format " \
131 "(eg. I420 (default), RV24, etc.)")
132 #define CONFIG_TEXT N_("Device properties")
133 #define CONFIG_LONGTEXT N_( \
134 "Show the properties dialog of the selected device before starting the " \
137 static int AccessOpen ( vlc_object_t * );
138 static void AccessClose( vlc_object_t * );
140 static int DemuxOpen ( vlc_object_t * );
141 static void DemuxClose ( vlc_object_t * );
144 set_shortname( _("DirectShow") );
145 set_description( _("DirectShow input") );
146 add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL,
147 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
149 add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE);
150 change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback );
151 change_action_add( FindDevicesCallback, N_("Refresh list") );
152 change_action_add( ConfigDevicesCallback, N_("Configure") );
154 add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE);
155 change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback );
156 change_action_add( FindDevicesCallback, N_("Refresh list") );
157 change_action_add( ConfigDevicesCallback, N_("Configure") );
159 add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE);
161 add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
164 add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT,
167 add_shortcut( "dshow" );
168 set_capability( "access2", 0 );
169 set_callbacks( AccessOpen, AccessClose );
172 set_description( _("DirectShow demuxer") );
173 add_shortcut( "dshow" );
174 set_capability( "demux2", 200 );
175 set_callbacks( DemuxOpen, DemuxClose );
179 /****************************************************************************
180 * DirectShow elementary stream descriptor
181 ****************************************************************************/
182 typedef struct dshow_stream_t
185 IBaseFilter *p_device_filter;
186 CaptureFilter *p_capture_filter;
193 VIDEOINFOHEADER video;
202 /****************************************************************************
203 * Access descriptor declaration
204 ****************************************************************************/
205 #define MAX_CROSSBAR_DEPTH 10
207 typedef struct CrossbarRouteRec {
209 LONG VideoInputIndex;
210 LONG VideoOutputIndex;
211 LONG AudioInputIndex;
212 LONG AudioOutputIndex;
217 /* These 2 must be left at the beginning */
221 IFilterGraph *p_graph;
222 ICaptureGraphBuilder2 *p_capture_graph_builder2;
223 IMediaControl *p_control;
225 int i_crossbar_route_depth;
226 CrossbarRoute crossbar_routes[MAX_CROSSBAR_DEPTH];
233 /* list of elementary streams */
234 dshow_stream_t **pp_streams;
236 int i_current_stream;
238 /* misc properties */
246 /****************************************************************************
247 * DirectShow utility functions
248 ****************************************************************************/
249 static void CreateDirectShowGraph( access_sys_t *p_sys )
251 p_sys->i_crossbar_route_depth = 0;
253 /* Create directshow filter graph */
254 if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
255 (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) )
257 /* Create directshow capture graph builder if available */
258 if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0,
259 CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
260 (void **)&p_sys->p_capture_graph_builder2 ) ) )
262 p_sys->p_capture_graph_builder2->
263 SetFiltergraph((IGraphBuilder *)p_sys->p_graph);
266 p_sys->p_graph->QueryInterface( IID_IMediaControl,
267 (void **)&p_sys->p_control );
271 static void DeleteCrossbarRoutes( access_sys_t *p_sys )
273 /* Remove crossbar filters from graph */
274 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
276 p_sys->crossbar_routes[i].pXbar->Release();
278 p_sys->i_crossbar_route_depth = 0;
281 static void DeleteDirectShowGraph( access_sys_t *p_sys )
283 DeleteCrossbarRoutes( p_sys );
285 /* Remove filters from graph */
286 for( int i = 0; i < p_sys->i_streams; i++ )
288 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter );
289 p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter );
290 p_sys->pp_streams[i]->p_capture_filter->Release();
291 p_sys->pp_streams[i]->p_device_filter->Release();
294 /* Release directshow objects */
295 if( p_sys->p_control )
297 p_sys->p_control->Release();
298 p_sys->p_control = NULL;
300 if( p_sys->p_capture_graph_builder2 )
302 p_sys->p_capture_graph_builder2->Release();
303 p_sys->p_capture_graph_builder2 = NULL;
308 p_sys->p_graph->Release();
309 p_sys->p_graph = NULL;
313 static void ReleaseDirectShow( access_t *p_access )
315 access_sys_t *p_sys = p_access->p_sys;
317 msg_Dbg( p_access, "Releasing DirectShow");
319 DeleteDirectShowGraph( p_sys );
321 /* Uninitialize OLE/COM */
324 free( p_sys->p_header );
325 /* Remove filters from graph */
326 for( int i = 0; i < p_sys->i_streams; i++ )
328 delete p_sys->pp_streams[i];
330 free( p_sys->pp_streams );
334 /*****************************************************************************
335 * Open: open direct show device
336 *****************************************************************************/
337 static int AccessOpen( vlc_object_t *p_this )
339 access_t *p_access = (access_t*)p_this;
343 /* Get/parse options and open device(s) */
344 string vdevname, adevname;
345 int i_width = 0, i_height = 0, i_chroma = 0;
347 var_Create( p_access, "dshow-config", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
349 var_Create( p_access, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
350 var_Get( p_access, "dshow-vdev", &val );
351 if( val.psz_string ) vdevname = string( val.psz_string );
352 if( val.psz_string ) free( val.psz_string );
354 var_Create( p_access, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
355 var_Get( p_access, "dshow-adev", &val );
356 if( val.psz_string ) adevname = string( val.psz_string );
357 if( val.psz_string ) free( val.psz_string );
359 var_Create( p_access, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
360 var_Get( p_access, "dshow-size", &val );
361 if( val.psz_string && *val.psz_string )
363 if( !strcmp( val.psz_string, "subqcif" ) )
365 i_width = 128; i_height = 96;
367 else if( !strcmp( val.psz_string, "qsif" ) )
369 i_width = 160; i_height = 120;
371 else if( !strcmp( val.psz_string, "qcif" ) )
373 i_width = 176; i_height = 144;
375 else if( !strcmp( val.psz_string, "sif" ) )
377 i_width = 320; i_height = 240;
379 else if( !strcmp( val.psz_string, "cif" ) )
381 i_width = 352; i_height = 288;
383 else if( !strcmp( val.psz_string, "vga" ) )
385 i_width = 640; i_height = 480;
391 i_width = strtol( val.psz_string, &psz_parser, 0 );
392 if( *psz_parser == 'x' || *psz_parser == 'X')
394 i_height = strtol( psz_parser + 1, &psz_parser, 0 );
396 msg_Dbg( p_access, "Width x Height %dx%d", i_width, i_height );
399 if( val.psz_string ) free( val.psz_string );
401 var_Create( p_access, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
402 var_Get( p_access, "dshow-chroma", &val );
403 if( val.psz_string && strlen( val.psz_string ) >= 4 )
405 i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1],
406 val.psz_string[2], val.psz_string[3] );
408 if( val.psz_string ) free( val.psz_string );
411 p_access->pf_read = AccessRead;
412 p_access->pf_block = NULL;
413 p_access->pf_control = AccessControl;
414 p_access->pf_seek = NULL;
415 p_access->info.i_update = 0;
416 p_access->info.i_size = 0;
417 p_access->info.i_pos = 0;
418 p_access->info.b_eof = VLC_FALSE;
419 p_access->info.i_title = 0;
420 p_access->info.i_seekpoint = 0;
421 p_access->p_sys = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) );
422 memset( p_sys, 0, sizeof( access_sys_t ) );
424 var_Create( p_access, "dshow-caching",
425 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
427 /* Initialize OLE/COM */
430 /* Initialize some data */
431 p_sys->i_streams = 0;
432 p_sys->pp_streams = (dshow_stream_t **)malloc( 1 );
433 p_sys->i_width = i_width;
434 p_sys->i_height = i_height;
435 p_sys->i_chroma = i_chroma;
436 p_sys->b_audio = VLC_TRUE;
440 p_sys->i_header_size = 8;
441 p_sys->p_header = (uint8_t *)malloc( p_sys->i_header_size );
442 memcpy( &p_sys->p_header[0], ".dsh", 4 );
443 SetDWBE( &p_sys->p_header[4], 1 );
444 p_sys->i_header_pos = p_sys->i_header_size;
446 p_sys->p_graph = NULL;
447 p_sys->p_capture_graph_builder2 = NULL;
448 p_sys->p_control = NULL;
450 /* Build directshow graph */
451 CreateDirectShowGraph( p_sys );
453 if( OpenDevice( p_access, vdevname, 0 ) != VLC_SUCCESS )
455 msg_Err( p_access, "can't open video");
458 if( p_sys->b_audio && OpenDevice( p_access, adevname, 1 ) != VLC_SUCCESS )
460 msg_Err( p_access, "can't open audio");
463 for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ )
465 IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar;
466 LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex;
467 LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex;
468 LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex;
469 LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex;
471 if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) )
473 msg_Dbg( p_access, "Crossbar at depth %d, Routed video "
474 "ouput %d to video input %d", i, VideoOutputIndex,
477 if( AudioOutputIndex != -1 && AudioInputIndex != -1 )
479 if( SUCCEEDED( pXbar->Route(AudioOutputIndex,
482 msg_Dbg(p_access, "Crossbar at depth %d, Routed audio "
483 "ouput %d to audio input %d", i,
484 AudioOutputIndex, AudioInputIndex );
490 if( !p_sys->i_streams )
492 ReleaseDirectShow( p_access );
496 /* Initialize some data */
497 p_sys->i_current_stream = 0;
498 p_sys->i_mtu += p_sys->i_header_size + 16 /* data header size */;
499 vlc_mutex_init( p_access, &p_sys->lock );
500 vlc_cond_init( p_access, &p_sys->wait );
502 msg_Dbg( p_access, "Playing...");
504 /* Everything is ready. Let's rock baby */
505 p_sys->p_control->Run();
510 /*****************************************************************************
511 * AccessClose: close device
512 *****************************************************************************/
513 static void AccessClose( vlc_object_t *p_this )
515 access_t *p_access = (access_t *)p_this;
516 access_sys_t *p_sys = p_access->p_sys;
518 /* Stop capturing stuff */
519 p_sys->p_control->Stop();
521 ReleaseDirectShow( p_access );
524 /*****************************************************************************
526 *****************************************************************************/
527 static int AccessControl( access_t *p_access, int i_query, va_list args )
529 access_sys_t *p_sys = p_access->p_sys;
537 case ACCESS_CAN_SEEK:
538 case ACCESS_CAN_FASTSEEK:
539 case ACCESS_CAN_PAUSE:
540 case ACCESS_CAN_CONTROL_PACE:
541 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
542 *pb_bool = VLC_FALSE;
547 pi_int = (int*)va_arg( args, int * );
548 *pi_int = p_sys->i_mtu;
551 case ACCESS_GET_PTS_DELAY:
552 pi_64 = (int64_t*)va_arg( args, int64_t * );
553 *pi_64 = (int64_t)var_GetInteger( p_access, "dshow-caching" ) *
558 case ACCESS_SET_PAUSE_STATE:
559 case ACCESS_GET_TITLE_INFO:
560 case ACCESS_SET_TITLE:
561 case ACCESS_SET_SEEKPOINT:
565 msg_Err( p_access, "unimplemented query in control" );
572 /****************************************************************************
573 * RouteCrossbars (Does not AddRef the returned *Pin)
574 ****************************************************************************/
575 static HRESULT GetCrossbarIPinAtIndex( IAMCrossbar *pXbar, LONG PinIndex,
576 BOOL IsInputPin, IPin ** ppPin )
578 LONG cntInPins, cntOutPins;
580 IBaseFilter *pFilter = NULL;
584 if( !pXbar || !ppPin ) return E_POINTER;
588 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) return E_FAIL;
590 LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins;
592 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
594 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
597 while( pins->Next(1, &pP, &n) == S_OK )
612 return *ppPin ? S_OK : E_FAIL;
615 /****************************************************************************
616 * GetCrossbarIndexFromIPin: Find corresponding index of an IPin on a crossbar
617 ****************************************************************************/
618 static HRESULT GetCrossbarIndexFromIPin( IAMCrossbar * pXbar, LONG * PinIndex,
619 BOOL IsInputPin, IPin * pPin )
621 LONG cntInPins, cntOutPins;
623 IBaseFilter *pFilter = NULL;
628 if(!pXbar || !PinIndex || !pPin )
631 if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) )
634 if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK )
636 if( SUCCEEDED(pFilter->EnumPins(&pins)) )
640 while( pins->Next(1, &pP, &n) == S_OK )
645 *PinIndex = IsInputPin ? i : i - cntInPins;
656 return fOK ? S_OK : E_FAIL;
659 /****************************************************************************
661 ****************************************************************************/
662 static HRESULT FindCrossbarRoutes( access_t *p_access, IPin *p_input_pin,
663 LONG physicalType, int depth = 0 )
665 access_sys_t *p_sys = p_access->p_sys;
666 HRESULT result = S_FALSE;
669 if( FAILED(p_input_pin->ConnectedTo(&p_output_pin)) ) return S_FALSE;
671 // It is connected, so now find out if the filter supports IAMCrossbar
673 if( FAILED(p_output_pin->QueryPinInfo(&pinInfo)) ||
674 PINDIR_OUTPUT != pinInfo.dir )
676 p_output_pin->Release ();
680 IAMCrossbar *pXbar=0;
681 if( FAILED(pinInfo.pFilter->QueryInterface(IID_IAMCrossbar,
684 pinInfo.pFilter->Release();
685 p_output_pin->Release ();
689 LONG inputPinCount, outputPinCount;
690 if( FAILED(pXbar->get_PinCounts(&outputPinCount, &inputPinCount)) )
693 pinInfo.pFilter->Release();
694 p_output_pin->Release ();
698 LONG inputPinIndexRelated, outputPinIndexRelated;
699 LONG inputPinPhysicalType, outputPinPhysicalType;
700 LONG inputPinIndex, outputPinIndex;
701 if( FAILED(GetCrossbarIndexFromIPin( pXbar, &outputPinIndex,
702 FALSE, p_output_pin )) ||
703 FAILED(pXbar->get_CrossbarPinInfo( FALSE, outputPinIndex,
704 &outputPinIndexRelated,
705 &outputPinPhysicalType )) )
708 pinInfo.pFilter->Release();
709 p_output_pin->Release ();
714 // for all input pins
716 for( inputPinIndex = 0; S_OK != result && inputPinIndex < inputPinCount;
719 if( FAILED(pXbar->get_CrossbarPinInfo( TRUE, inputPinIndex,
720 &inputPinIndexRelated, &inputPinPhysicalType )) ) continue;
722 // Is the pin a video pin?
723 if( inputPinPhysicalType != physicalType ) continue;
726 if( FAILED(pXbar->CanRoute(outputPinIndex, inputPinIndex)) ) continue;
729 if( FAILED(GetCrossbarIPinAtIndex( pXbar, inputPinIndex,
730 TRUE, &pPin)) ) continue;
732 result = FindCrossbarRoutes( p_access, pPin, physicalType, depth+1 );
733 if( S_OK == result || (S_FALSE == result &&
734 physicalType == inputPinPhysicalType &&
735 (p_sys->i_crossbar_route_depth = depth+1) < MAX_CROSSBAR_DEPTH) )
740 // remember crossbar route
741 p_sys->crossbar_routes[depth].pXbar = pXbar;
742 p_sys->crossbar_routes[depth].VideoInputIndex = inputPinIndex;
743 p_sys->crossbar_routes[depth].VideoOutputIndex = outputPinIndex;
744 p_sys->crossbar_routes[depth].AudioInputIndex = inputPinIndexRelated;
745 p_sys->crossbar_routes[depth].AudioOutputIndex = outputPinIndexRelated;
747 msg_Dbg( p_access, "Crossbar at depth %d, Found Route For "
748 "ouput %ld (type %ld) to input %d (type %ld)", depth,
749 outputPinIndex, outputPinPhysicalType, inputPinIndex,
750 inputPinPhysicalType );
757 pinInfo.pFilter->Release();
758 p_output_pin->Release ();
763 /****************************************************************************
765 ****************************************************************************/
766 static bool ConnectFilters( access_t *p_access, IBaseFilter *p_filter,
767 CaptureFilter *p_capture_filter )
769 access_sys_t *p_sys = p_access->p_sys;
770 CapturePin *p_input_pin = p_capture_filter->CustomGetPin();
772 AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType();
774 if( p_sys->p_capture_graph_builder2 )
776 if( FAILED(p_sys->p_capture_graph_builder2->
777 RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype,
779 (IBaseFilter *)p_capture_filter )) )
784 // Sort out all the possible video inputs
785 // The class needs to be given the capture filters ANALOGVIDEO input pin
787 if( mediaType.majortype == MEDIATYPE_Video &&
788 SUCCEEDED(p_filter->EnumPins(&pins)) )
794 IKsPropertySet *pKs=0;
798 while( !Found && (S_OK == pins->Next(1, &pP, &n)) )
800 if(S_OK == pP->QueryPinInfo(&pinInfo))
802 if(pinInfo.dir == PINDIR_INPUT)
804 // is this pin an ANALOGVIDEOIN input pin?
805 if( pP->QueryInterface(IID_IKsPropertySet,
806 (void **)&pKs) == S_OK )
808 if( pKs->Get(AMPROPSETID_Pin,
809 AMPROPERTY_PIN_CATEGORY, NULL, 0,
810 &guid, sizeof(GUID), &dw) == S_OK )
812 if( guid == PIN_CATEGORY_ANALOGVIDEOIN )
814 // recursively search crossbar routes
815 FindCrossbarRoutes( p_access, pP,
816 PhysConn_Video_Tuner );
824 pinInfo.pFilter->Release();
834 IEnumPins *p_enumpins;
837 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
839 while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) )
841 PIN_DIRECTION pin_dir;
842 p_pin->QueryDirection( &pin_dir );
844 if( pin_dir == PINDIR_OUTPUT &&
845 p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin,
849 p_enumpins->Release();
855 p_enumpins->Release();
861 ** get fourcc priority from arbritary preference, the higher the better
863 static int GetFourCCPriority(int i_fourcc)
867 case VLC_FOURCC('I','4','2','0'):
868 case VLC_FOURCC('a','r','a','w'):
873 case VLC_FOURCC('Y','V','1','2'):
878 case VLC_FOURCC('R','V','2','4'):
883 case VLC_FOURCC('Y','U','Y','2'):
884 case VLC_FOURCC('R','V','3','2'):
885 case VLC_FOURCC('R','G','B','A'):
886 case VLC_FOURCC('f','l','3','2'):
894 #define MAX_MEDIA_TYPES 32
896 static int OpenDevice( access_t *p_access, string devicename,
899 access_sys_t *p_sys = p_access->p_sys;
901 /* See if device is already opened */
902 for( int i = 0; i < p_sys->i_streams; i++ )
904 if( p_sys->pp_streams[i]->devicename == devicename )
911 list<string> list_devices;
913 /* Enumerate devices and display their names */
914 FindCaptureDevice( (vlc_object_t *)p_access, NULL, &list_devices, b_audio );
916 if( !list_devices.size() )
919 list<string>::iterator iter;
920 for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
921 msg_Dbg( p_access, "found device: %s", iter->c_str() );
923 /* If no device name was specified, pick the 1st one */
924 if( devicename.size() == 0 )
926 devicename = *list_devices.begin();
929 // Use the system device enumerator and class enumerator to find
930 // a capture/preview device, such as a desktop USB video camera.
931 IBaseFilter *p_device_filter =
932 FindCaptureDevice( (vlc_object_t *)p_access, &devicename,
934 if( p_device_filter )
935 msg_Dbg( p_access, "using device: %s", devicename.c_str() );
938 msg_Err( p_access, "can't use device: %s, unsupported device type",
939 devicename.c_str() );
943 AM_MEDIA_TYPE media_types[MAX_MEDIA_TYPES];
945 size_t mt_count = EnumDeviceCaps( (vlc_object_t *)p_access,
946 p_device_filter, p_sys->i_chroma,
947 p_sys->i_width, p_sys->i_height,
948 0, 0, 0, media_types, MAX_MEDIA_TYPES );
952 msg_Err( p_access, "can't use device: %s, unsupported media types",
953 devicename.c_str() );
958 (AM_MEDIA_TYPE *)malloc( sizeof(AM_MEDIA_TYPE)*mt_count );
960 // Order and copy returned media types according to arbitrary
962 for( size_t c=0; c<mt_count; c++ )
965 GetFourCCPriority(GetFourCCFromMediaType(media_types[c]));
967 for( size_t d=c+1; d<mt_count; d++ )
970 GetFourCCPriority(GetFourCCFromMediaType(media_types[d]));
971 if( priority > slot_priority )
973 slot_priority = priority;
979 mt[c] = media_types[slot_copy];
980 media_types[slot_copy] = media_types[c];
984 mt[c] = media_types[c];
988 /* Create and add our capture filter */
989 CaptureFilter *p_capture_filter =
990 new CaptureFilter( p_access, mt, mt_count );
991 p_sys->p_graph->AddFilter( p_capture_filter, 0 );
993 /* Add the device filter to the graph (seems necessary with VfW before
994 * accessing pin attributes). */
995 p_sys->p_graph->AddFilter( p_device_filter, 0 );
997 /* Attempt to connect one of this device's capture output pins */
998 msg_Dbg( p_access, "connecting filters" );
999 if( ConnectFilters( p_access, p_device_filter, p_capture_filter ) )
1002 msg_Dbg( p_access, "filters connected successfully !" );
1004 dshow_stream_t dshow_stream;
1005 dshow_stream.b_invert = VLC_FALSE;
1006 dshow_stream.b_pts = VLC_FALSE;
1008 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1010 /* Show properties. Done here so the VLC stream is setup with the
1011 * proper parameters. */
1013 var_Get( p_access, "dshow-config", &val );
1016 PropertiesPage( VLC_OBJECT(p_access), p_device_filter,
1017 p_sys->p_capture_graph_builder2,
1018 dshow_stream.mt.majortype == MEDIATYPE_Audio );
1022 p_capture_filter->CustomGetPin()->CustomGetMediaType();
1024 dshow_stream.i_fourcc = GetFourCCFromMediaType(dshow_stream.mt);
1025 if( 0 != dshow_stream.i_fourcc )
1027 if( dshow_stream.mt.majortype == MEDIATYPE_Video )
1029 msg_Dbg( p_access, "MEDIATYPE_Video");
1031 dshow_stream.header.video =
1032 *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat;
1034 int i_height = dshow_stream.header.video.bmiHeader.biHeight;
1036 /* Check if the image is inverted (bottom to top) */
1037 if( dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','1') ||
1038 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','4') ||
1039 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','8') ||
1040 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','5') ||
1041 dshow_stream.i_fourcc == VLC_FOURCC('R','V','1','6') ||
1042 dshow_stream.i_fourcc == VLC_FOURCC('R','V','2','4') ||
1043 dshow_stream.i_fourcc == VLC_FOURCC('R','V','3','2') ||
1044 dshow_stream.i_fourcc == VLC_FOURCC('R','G','B','A') )
1046 if( i_height > 0 ) dshow_stream.b_invert = VLC_TRUE;
1047 else i_height = - i_height;
1050 /* Check if we are dealing with a DV stream */
1051 if( dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','l') ||
1052 dshow_stream.i_fourcc == VLC_FOURCC('d','v','s','d') ||
1053 dshow_stream.i_fourcc == VLC_FOURCC('d','v','h','d') )
1055 p_access->pf_read = ReadCompressed;
1056 if( !p_access->psz_demux || !*p_access->psz_demux )
1058 p_access->psz_demux = strdup( "rawdv" );
1060 p_sys->b_audio = VLC_FALSE;
1063 /* Check if we are dealing with an MPEG video stream */
1064 if( dshow_stream.i_fourcc == VLC_FOURCC('m','p','2','v') )
1066 p_access->pf_read = ReadCompressed;
1067 if( !p_access->psz_demux || !*p_access->psz_demux )
1069 p_access->psz_demux = "mpgv";
1071 p_sys->b_audio = VLC_FALSE;
1074 /* Add video stream to header */
1075 p_sys->i_header_size += 20;
1076 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1077 p_sys->i_header_size );
1078 memcpy( &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
1079 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1080 &dshow_stream.i_fourcc, 4 );
1081 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1082 dshow_stream.header.video.bmiHeader.biWidth );
1083 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height );
1084 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 );
1085 p_sys->i_header_pos = p_sys->i_header_size;
1087 /* Greatly simplifies the reading routine */
1088 int i_mtu = dshow_stream.header.video.bmiHeader.biWidth *
1090 p_sys->i_mtu = __MAX( p_sys->i_mtu, (unsigned int)i_mtu );
1093 else if( dshow_stream.mt.majortype == MEDIATYPE_Audio )
1095 msg_Dbg( p_access, "MEDIATYPE_Audio");
1097 dshow_stream.header.audio =
1098 *(WAVEFORMATEX *)dshow_stream.mt.pbFormat;
1100 /* Add audio stream to header */
1101 p_sys->i_header_size += 20;
1102 p_sys->p_header = (uint8_t *)realloc( p_sys->p_header,
1103 p_sys->i_header_size );
1104 memcpy( &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
1105 memcpy( &p_sys->p_header[p_sys->i_header_pos + 4],
1106 &dshow_stream.i_fourcc, 4 );
1107 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8],
1108 dshow_stream.header.audio.nChannels );
1109 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12],
1110 dshow_stream.header.audio.nSamplesPerSec );
1111 SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16],
1112 dshow_stream.header.audio.wBitsPerSample );
1113 p_sys->i_header_pos = p_sys->i_header_size;
1115 /* Greatly simplifies the reading routine */
1116 IAMBufferNegotiation *p_ambuf;
1120 p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin );
1121 if( SUCCEEDED( p_pin->QueryInterface(
1122 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1124 ALLOCATOR_PROPERTIES AllocProp;
1125 memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) );
1126 p_ambuf->GetAllocatorProperties( &AllocProp );
1128 i_mtu = AllocProp.cbBuffer;
1133 i_mtu = dshow_stream.header.audio.nSamplesPerSec *
1134 dshow_stream.header.audio.nChannels *
1135 dshow_stream.header.audio.wBitsPerSample / 8;
1138 p_sys->i_mtu = __MAX( p_sys->i_mtu, (unsigned int)i_mtu );
1141 else if( dshow_stream.mt.majortype == MEDIATYPE_Stream )
1143 msg_Dbg( p_access, "MEDIATYPE_Stream" );
1145 msg_Dbg( p_access, "selected stream pin accepts format: %4.4s",
1146 (char *)&dshow_stream.i_fourcc);
1148 p_sys->b_audio = VLC_FALSE;
1149 p_sys->i_header_size = 0;
1150 p_sys->i_header_pos = 0;
1153 p_access->pf_read = ReadCompressed;
1157 msg_Dbg( p_access, "unknown stream majortype" );
1161 /* Add directshow elementary stream to our list */
1162 dshow_stream.p_device_filter = p_device_filter;
1163 dshow_stream.p_capture_filter = p_capture_filter;
1165 p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams,
1166 sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) );
1167 p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t;
1168 *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream;
1169 SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams );
1176 /* Remove filters from graph */
1177 p_sys->p_graph->RemoveFilter( p_device_filter );
1178 p_sys->p_graph->RemoveFilter( p_capture_filter );
1180 /* Release objects */
1181 p_device_filter->Release();
1182 p_capture_filter->Release();
1184 return VLC_EGENERIC;
1187 static IBaseFilter *
1188 FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
1189 list<string> *p_listdevices, vlc_bool_t b_audio )
1191 IBaseFilter *p_base_filter = NULL;
1192 IMoniker *p_moniker = NULL;
1196 /* Create the system device enumerator */
1197 ICreateDevEnum *p_dev_enum = NULL;
1199 hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
1200 IID_ICreateDevEnum, (void **)&p_dev_enum );
1203 msg_Err( p_this, "failed to create the device enumerator (0x%x)", hr);
1207 /* Create an enumerator for the video capture devices */
1208 IEnumMoniker *p_class_enum = NULL;
1210 hr = p_dev_enum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
1213 hr = p_dev_enum->CreateClassEnumerator( CLSID_AudioInputDeviceCategory,
1215 p_dev_enum->Release();
1218 msg_Err( p_this, "failed to create the class enumerator (0x%x)", hr );
1222 /* If there are no enumerators for the requested type, then
1223 * CreateClassEnumerator will succeed, but p_class_enum will be NULL */
1224 if( p_class_enum == NULL )
1226 msg_Err( p_this, "no capture device was detected" );
1230 /* Enumerate the devices */
1232 /* Note that if the Next() call succeeds but there are no monikers,
1233 * it will return S_FALSE (which is not a failure). Therefore, we check
1234 * that the return code is S_OK instead of using SUCCEEDED() macro. */
1236 while( p_class_enum->Next( 1, &p_moniker, &i_fetched ) == S_OK )
1238 /* Getting the property page to get the device name */
1239 IPropertyBag *p_bag;
1240 hr = p_moniker->BindToStorage( 0, 0, IID_IPropertyBag,
1246 hr = p_bag->Read( L"FriendlyName", &var, NULL );
1250 int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
1251 char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
1252 WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
1253 i_convert, NULL, NULL );
1254 SysFreeString(var.bstrVal);
1256 if( p_listdevices ) p_listdevices->push_back( p_buf );
1258 if( p_devicename && *p_devicename == string(p_buf) )
1260 /* Bind Moniker to a filter object */
1261 hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter,
1262 (void **)&p_base_filter );
1265 msg_Err( p_this, "couldn't bind moniker to filter "
1266 "object (0x%x)", hr );
1267 p_moniker->Release();
1268 p_class_enum->Release();
1271 p_moniker->Release();
1272 p_class_enum->Release();
1273 return p_base_filter;
1278 p_moniker->Release();
1281 p_class_enum->Release();
1285 static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter,
1286 int i_fourcc, int i_width, int i_height,
1287 int i_channels, int i_samplespersec,
1288 int i_bitspersample, AM_MEDIA_TYPE *mt,
1291 IEnumPins *p_enumpins;
1293 IEnumMediaTypes *p_enummt;
1294 size_t mt_count = 0;
1296 if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return 0;
1298 while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) )
1302 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1304 msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S",
1305 info.dir == PINDIR_INPUT ? "input" : "output",
1307 if( info.pFilter ) info.pFilter->Release();
1310 p_output_pin->Release();
1312 p_enumpins->Reset();
1314 while( !mt_count && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK )
1318 if( S_OK == p_output_pin->QueryPinInfo( &info ) )
1320 if( info.pFilter ) info.pFilter->Release();
1321 if( info.dir == PINDIR_INPUT )
1323 p_output_pin->Release();
1326 msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName );
1330 if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) )
1332 AM_MEDIA_TYPE *p_mt;
1333 while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK )
1335 int i_current_fourcc = GetFourCCFromMediaType(*p_mt);
1336 if( 0 != i_current_fourcc )
1338 if( p_mt->majortype == MEDIATYPE_Video )
1340 int i_current_width = p_mt->pbFormat ?
1341 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0;
1342 int i_current_height = p_mt->pbFormat ?
1343 ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0;
1344 if( i_current_height < 0 )
1345 i_current_height = -i_current_height;
1347 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1348 "accepts chroma: %4.4s, width:%i, height:%i",
1349 (char *)&i_current_fourcc, i_current_width,
1352 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1353 ( !i_width || i_width == i_current_width ) &&
1354 ( !i_height || i_height == i_current_height ) &&
1355 (mt_count < mt_max) )
1358 mt[mt_count++] = *p_mt;
1362 FreeMediaType( *p_mt );
1365 else if( p_mt->majortype == MEDIATYPE_Audio )
1367 int i_current_channels =
1368 ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels;
1369 int i_current_samplespersec =
1370 ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec;
1371 int i_current_bitspersample =
1372 ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample;
1374 msg_Dbg( p_this, "EnumDeviceCaps: input pin "
1375 "accepts format: %4.4s, channels:%i, "
1376 "samples/sec:%i bits/sample:%i",
1377 (char *)&i_current_fourcc, i_current_channels,
1378 i_current_samplespersec, i_current_bitspersample);
1380 if( (!i_channels || i_channels == i_current_channels) &&
1381 (!i_samplespersec ||
1382 i_samplespersec == i_current_samplespersec) &&
1383 (!i_bitspersample ||
1384 i_bitspersample == i_current_bitspersample) &&
1385 (mt_count < mt_max) )
1388 mt[mt_count++] = *p_mt;
1390 /* Pre-Configure the 1st match, Ugly */
1391 if( 1 == mt_count ) {
1392 /* Setup a few properties like the audio latency */
1393 IAMBufferNegotiation *p_ambuf;
1395 if( SUCCEEDED( p_output_pin->QueryInterface(
1396 IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) )
1398 ALLOCATOR_PROPERTIES AllocProp;
1399 AllocProp.cbAlign = -1;
1400 AllocProp.cbBuffer = i_current_channels *
1401 i_current_samplespersec *
1402 i_current_bitspersample / 8 / 10 ; /*100 ms of latency*/
1403 AllocProp.cbPrefix = -1;
1404 AllocProp.cBuffers = -1;
1405 p_ambuf->SuggestAllocatorProperties( &AllocProp );
1412 FreeMediaType( *p_mt );
1415 else if( p_mt->majortype == MEDIATYPE_Stream )
1417 msg_Dbg( p_this, "EnumDeviceCaps: MEDIATYPE_Stream" );
1419 if( ( !i_fourcc || i_fourcc == i_current_fourcc ) &&
1420 (mt_count < mt_max) )
1423 mt[mt_count++] = *p_mt;
1424 i_fourcc = i_current_fourcc;
1428 FreeMediaType( *p_mt );
1434 "EnumDeviceCaps: input pin: unknown format" );
1435 FreeMediaType( *p_mt );
1441 "EnumDeviceCaps: input pin: unknown format" );
1442 FreeMediaType( *p_mt );
1444 CoTaskMemFree( (PVOID)p_mt );
1446 p_enummt->Release();
1449 p_output_pin->Release();
1452 p_enumpins->Release();
1456 /*****************************************************************************
1457 * AccessRead: reads from the device.
1458 *****************************************************************************
1459 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1461 *****************************************************************************/
1462 static int AccessRead( access_t *p_access, uint8_t *p_buffer, int i_len )
1464 access_sys_t *p_sys = p_access->p_sys;
1465 dshow_stream_t *p_stream = NULL;
1466 byte_t *p_buf_orig = p_buffer;
1467 VLCMediaSample sample;
1471 if( p_sys->i_header_pos )
1473 /* First header of the stream */
1474 memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size );
1475 p_buffer += p_sys->i_header_size;
1476 p_sys->i_header_pos = 0;
1481 /* Get new sample/frame from next elementary stream.
1482 * We first loop through all the elementary streams and if all our
1483 * fifos are empty we block until we are signaled some new data has
1485 vlc_mutex_lock( &p_sys->lock );
1488 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1490 p_stream = p_sys->pp_streams[i_stream];
1491 if( p_stream->mt.majortype == MEDIATYPE_Audio &&
1492 p_stream->p_capture_filter &&
1493 p_stream->p_capture_filter->CustomGetPin()
1494 ->CustomGetSample( &sample ) == S_OK )
1499 if( i_stream == p_sys->i_streams )
1500 for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
1502 p_stream = p_sys->pp_streams[i_stream];
1503 if( p_stream->p_capture_filter &&
1504 p_stream->p_capture_filter->CustomGetPin()
1505 ->CustomGetSample( &sample ) == S_OK )
1510 if( i_stream == p_sys->i_streams )
1512 /* No data available. Wait until some data has arrived */
1513 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1514 vlc_mutex_unlock( &p_sys->lock );
1518 vlc_mutex_unlock( &p_sys->lock );
1523 i_data_size = sample.p_sample->GetActualDataLength();
1524 sample.p_sample->GetPointer( &p_data );
1526 REFERENCE_TIME i_pts, i_end_date;
1527 HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date );
1528 if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0;
1532 if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts )
1534 /* Use our data timestamp */
1535 i_pts = sample.i_timestamp;
1536 p_stream->b_pts = VLC_TRUE;
1541 msg_Dbg( p_access, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts );
1544 /* Create pseudo header */
1545 SetDWBE( &p_sys->p_header[0], i_stream );
1546 SetDWBE( &p_sys->p_header[4], i_data_size );
1547 SetQWBE( &p_sys->p_header[8], i_pts / 10 );
1550 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1553 /* First copy header */
1554 memcpy( p_buffer, p_sys->p_header, 16 /* header size */ );
1555 p_buffer += 16 /* header size */;
1557 /* Then copy stream data if any */
1558 if( !p_stream->b_invert )
1560 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1561 p_buffer += i_data_size;
1565 int i_width = p_stream->header.video.bmiHeader.biWidth;
1566 int i_height = p_stream->header.video.bmiHeader.biHeight;
1567 if( i_height < 0 ) i_height = - i_height;
1569 switch( p_stream->i_fourcc )
1571 case VLC_FOURCC( 'R', 'V', '1', '5' ):
1572 case VLC_FOURCC( 'R', 'V', '1', '6' ):
1575 case VLC_FOURCC( 'R', 'V', '2', '4' ):
1578 case VLC_FOURCC( 'R', 'V', '3', '2' ):
1579 case VLC_FOURCC( 'R', 'G', 'B', 'A' ):
1584 for( int i = i_height - 1; i >= 0; i-- )
1586 p_access->p_vlc->pf_memcpy( p_buffer,
1587 &p_data[i * i_width], i_width );
1589 p_buffer += i_width;
1593 sample.p_sample->Release();
1595 /* The caller got what he wanted */
1596 return p_buffer - p_buf_orig;
1599 return 0; /* never reached */
1602 /*****************************************************************************
1603 * ReadCompressed: reads compressed (MPEG/DV) data from the device.
1604 *****************************************************************************
1605 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
1607 *****************************************************************************/
1608 static int ReadCompressed( access_t *p_access, uint8_t *p_buffer, int i_len )
1610 access_sys_t *p_sys = p_access->p_sys;
1611 dshow_stream_t *p_stream = NULL;
1612 VLCMediaSample sample;
1616 /* Read 1 DV/MPEG frame (they contain the video and audio data) */
1618 /* There must be only 1 elementary stream to produce a valid stream
1619 * of MPEG or DV data */
1620 p_stream = p_sys->pp_streams[0];
1624 if( p_access->b_die || p_access->b_error ) return 0;
1626 /* Get new sample/frame from the elementary stream (blocking). */
1627 vlc_mutex_lock( &p_sys->lock );
1629 if( p_stream->p_capture_filter->CustomGetPin()
1630 ->CustomGetSample( &sample ) != S_OK )
1632 /* No data available. Wait until some data has arrived */
1633 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
1634 vlc_mutex_unlock( &p_sys->lock );
1638 vlc_mutex_unlock( &p_sys->lock );
1643 i_data_size = sample.p_sample->GetActualDataLength();
1644 sample.p_sample->GetPointer( &p_data );
1647 msg_Info( p_access, "access read %i data_size %i", i_len, i_data_size );
1649 i_data_size = __MIN( i_data_size, (int)i_len );
1651 p_access->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size );
1653 sample.p_sample->Release();
1655 /* The caller got what he wanted */
1659 return 0; /* never reached */
1662 /*****************************************************************************
1663 * Demux: local prototypes
1664 *****************************************************************************/
1671 static int Demux ( demux_t * );
1672 static int DemuxControl( demux_t *, int, va_list );
1674 /****************************************************************************
1676 ****************************************************************************/
1677 static int DemuxOpen( vlc_object_t *p_this )
1679 demux_t *p_demux = (demux_t *)p_this;
1686 /* a little test to see if it's a dshow stream */
1687 if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
1689 msg_Warn( p_demux, "dshow plugin discarded (cannot peek)" );
1690 return VLC_EGENERIC;
1693 if( memcmp( p_peek, ".dsh", 4 ) ||
1694 ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
1696 msg_Warn( p_demux, "dshow plugin discarded (not a valid stream)" );
1697 return VLC_EGENERIC;
1700 p_demux->pf_demux = Demux;
1701 p_demux->pf_control = DemuxControl;
1702 p_demux->p_sys = p_sys = (demux_sys_t *)malloc( sizeof( demux_sys_t ) );
1706 if( stream_Peek( p_demux->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
1708 msg_Err( p_demux, "dshow plugin discarded (cannot peek)" );
1709 return VLC_EGENERIC;
1713 for( i = 0; i < i_es; i++ )
1717 if( !memcmp( p_peek, "auds", 4 ) )
1719 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1720 p_peek[6], p_peek[7] ) );
1722 fmt.audio.i_channels = GetDWBE( &p_peek[8] );
1723 fmt.audio.i_rate = GetDWBE( &p_peek[12] );
1724 fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
1725 fmt.audio.i_blockalign = fmt.audio.i_channels *
1726 fmt.audio.i_bitspersample / 8;
1727 fmt.i_bitrate = fmt.audio.i_channels *
1729 fmt.audio.i_bitspersample;
1731 msg_Dbg( p_demux, "new audio es %d channels %dHz",
1732 fmt.audio.i_channels, fmt.audio.i_rate );
1734 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1735 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1736 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1738 else if( !memcmp( p_peek, "vids", 4 ) )
1740 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
1741 p_peek[6], p_peek[7] ) );
1742 fmt.video.i_width = GetDWBE( &p_peek[8] );
1743 fmt.video.i_height = GetDWBE( &p_peek[12] );
1745 msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
1746 (char*)&fmt.i_codec,
1747 fmt.video.i_width, fmt.video.i_height );
1749 p_sys->es = (es_out_id_t **)realloc( p_sys->es,
1750 sizeof(es_out_id_t *) * (p_sys->i_es + 1) );
1751 p_sys->es[p_sys->i_es++] = es_out_Add( p_demux->out, &fmt );
1758 stream_Read( p_demux->s, NULL, 8 + 20 * i_es );
1763 /****************************************************************************
1765 ****************************************************************************/
1766 static void DemuxClose( vlc_object_t *p_this )
1768 demux_t *p_demux = (demux_t *)p_this;
1769 demux_sys_t *p_sys = p_demux->p_sys;
1771 if( p_sys->i_es > 0 )
1778 /****************************************************************************
1780 ****************************************************************************/
1781 static int Demux( demux_t *p_demux )
1783 demux_sys_t *p_sys = p_demux->p_sys;
1792 if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
1794 msg_Warn( p_demux, "cannot peek (EOF ?)" );
1798 i_es = GetDWBE( &p_peek[0] );
1799 if( i_es < 0 || i_es >= p_sys->i_es )
1801 msg_Err( p_demux, "cannot find ES" );
1805 i_size = GetDWBE( &p_peek[4] );
1806 i_pts = GetQWBE( &p_peek[8] );
1808 if( ( p_block = stream_Block( p_demux->s, 16 + i_size ) ) == NULL )
1810 msg_Warn( p_demux, "cannot read data" );
1814 p_block->p_buffer += 16;
1815 p_block->i_buffer -= 16;
1817 p_block->i_dts = p_block->i_pts = i_pts;
1819 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pts > 0 ? i_pts : 0 );
1820 es_out_Send( p_demux->out, p_sys->es[i_es], p_block );
1825 /****************************************************************************
1827 ****************************************************************************/
1828 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
1830 return demux2_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
1833 /*****************************************************************************
1834 * config variable callback
1835 *****************************************************************************/
1836 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1837 vlc_value_t newval, vlc_value_t oldval, void * )
1839 module_config_t *p_item;
1840 vlc_bool_t b_audio = VLC_FALSE;
1843 p_item = config_FindConfig( p_this, psz_name );
1844 if( !p_item ) return VLC_SUCCESS;
1846 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1848 /* Clear-up the current list */
1849 if( p_item->i_list )
1851 /* Keep the 2 first entries */
1852 for( i = 2; i < p_item->i_list; i++ )
1854 free( p_item->ppsz_list[i] );
1855 free( p_item->ppsz_list_text[i] );
1857 /* TODO: Remove when no more needed */
1858 p_item->ppsz_list[i] = NULL;
1859 p_item->ppsz_list_text[i] = NULL;
1863 /* Find list of devices */
1864 list<string> list_devices;
1866 /* Initialize OLE/COM */
1869 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1871 /* Uninitialize OLE/COM */
1874 if( !list_devices.size() ) return VLC_SUCCESS;
1877 (char **)realloc( p_item->ppsz_list,
1878 (list_devices.size()+3) * sizeof(char *) );
1879 p_item->ppsz_list_text =
1880 (char **)realloc( p_item->ppsz_list_text,
1881 (list_devices.size()+3) * sizeof(char *) );
1883 list<string>::iterator iter;
1884 for( iter = list_devices.begin(), i = 2; iter != list_devices.end();
1887 p_item->ppsz_list[i] = strdup( iter->c_str() );
1888 p_item->ppsz_list_text[i] = NULL;
1891 p_item->ppsz_list[i] = NULL;
1892 p_item->ppsz_list_text[i] = NULL;
1894 /* Signal change to the interface */
1895 p_item->b_dirty = VLC_TRUE;
1900 static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1901 vlc_value_t newval, vlc_value_t oldval, void * )
1903 module_config_t *p_item;
1904 vlc_bool_t b_audio = VLC_FALSE;
1906 /* Initialize OLE/COM */
1909 p_item = config_FindConfig( p_this, psz_name );
1910 if( !p_item ) return VLC_SUCCESS;
1912 if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE;
1916 if( newval.psz_string && *newval.psz_string )
1918 devicename = newval.psz_string;
1922 /* If no device name was specified, pick the 1st one */
1923 list<string> list_devices;
1925 /* Enumerate devices */
1926 FindCaptureDevice( p_this, NULL, &list_devices, b_audio );
1927 if( !list_devices.size() ) return VLC_EGENERIC;
1928 devicename = *list_devices.begin();
1931 IBaseFilter *p_device_filter =
1932 FindCaptureDevice( p_this, &devicename, NULL, b_audio );
1933 if( p_device_filter )
1935 PropertiesPage( p_this, p_device_filter, NULL, b_audio );
1939 /* Uninitialize OLE/COM */
1942 msg_Err( p_this, "didn't find device: %s", devicename.c_str() );
1943 return VLC_EGENERIC;
1946 /* Uninitialize OLE/COM */
1952 static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid )
1954 if( cauuid->cElems > 0 )
1956 HWND hwnd_desktop = ::GetDesktopWindow();
1958 OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj,
1959 cauuid->cElems, cauuid->pElems, 0, 0, NULL );
1961 CoTaskMemFree( cauuid->pElems );
1965 static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter,
1966 ICaptureGraphBuilder2 *p_capture_graph,
1967 vlc_bool_t b_audio )
1971 msg_Dbg( p_this, "Configuring Device Properties" );
1974 * Video or audio capture filter page
1976 ISpecifyPropertyPages *p_spec;
1978 HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages,
1982 if( SUCCEEDED(p_spec->GetPages( &cauuid )) )
1984 ShowPropertyPage( p_device_filter, &cauuid );
1989 msg_Dbg( p_this, "looking for WDM Configuration Pages" );
1991 if( p_capture_graph )
1992 msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" );
1997 if( p_capture_graph && b_audio )
1999 IAMStreamConfig *p_SC;
2001 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2002 &MEDIATYPE_Audio, p_device_filter,
2003 IID_IAMStreamConfig,
2007 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2011 hr = p_spec->GetPages( &cauuid );
2014 for( unsigned int c = 0; c < cauuid.cElems; c++ )
2016 ShowPropertyPage( p_SC, &cauuid );
2018 CoTaskMemFree( cauuid.pElems );
2029 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2030 &MEDIATYPE_Audio, p_device_filter,
2031 IID_IAMTVAudio, (void **)&p_TVA );
2034 hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages,
2038 if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) )
2039 ShowPropertyPage(p_TVA, &cauuid);
2050 if( p_capture_graph && !b_audio )
2052 IAMStreamConfig *p_SC;
2054 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2055 &MEDIATYPE_Interleaved,
2057 IID_IAMStreamConfig,
2061 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2064 IID_IAMStreamConfig,
2070 hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages,
2074 if( SUCCEEDED( p_spec->GetPages(&cauuid) ) )
2076 ShowPropertyPage(p_SC, &cauuid);
2084 * Video crossbar, and a possible second crossbar
2086 IAMCrossbar *p_X, *p_X2;
2089 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2090 &MEDIATYPE_Interleaved,
2092 IID_IAMCrossbar, (void **)&p_X );
2095 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2104 hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF );
2107 hr = p_X->QueryInterface( IID_ISpecifyPropertyPages,
2111 hr = p_spec->GetPages(&cauuid);
2112 if( hr == S_OK && cauuid.cElems > 0 )
2114 ShowPropertyPage( p_X, &cauuid );
2119 hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL,
2120 p_XF, IID_IAMCrossbar,
2124 hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages,
2128 hr = p_spec->GetPages( &cauuid );
2131 ShowPropertyPage( p_X2, &cauuid );
2148 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2149 &MEDIATYPE_Interleaved,
2151 IID_IAMTVTuner, (void **)&p_TV );
2154 hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE,
2163 hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages,
2167 hr = p_spec->GetPages(&cauuid);
2170 ShowPropertyPage(p_TV, &cauuid);