X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fdshow%2Fdshow.cpp;h=d3c68ccd03bc4a49fc127af0ba51f3cc14d8f930;hb=208c33f5b91999a766ee391780606216450218f0;hp=ba842449e3edc581d41626192270db3c5723f631;hpb=6ee0c08437ad43905c1cedf1a4e701d7a9c5ccf3;p=vlc diff --git a/modules/access/dshow/dshow.cpp b/modules/access/dshow/dshow.cpp index ba842449e3..d3c68ccd03 100644 --- a/modules/access/dshow/dshow.cpp +++ b/modules/access/dshow/dshow.cpp @@ -1,10 +1,10 @@ /***************************************************************************** - * dshow.c : DirectShow access module for vlc + * dshow.cpp : DirectShow access module for vlc ***************************************************************************** - * Copyright (C) 2002 VideoLAN - * $Id: dshow.cpp,v 1.2 2003/08/25 21:45:04 gbazin Exp $ + * Copyright (C) 2002, 2003 VideoLAN + * $Id$ * - * Author: Gildas Bazin + * Author: Gildas Bazin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,69 +32,35 @@ #include #include -#ifndef _MSC_VER -# include -# include -# include -# include -# define _WINGDI_ 1 -# define AM_NOVTABLE -# define _OBJBASE_H_ -# undef _X86_ -# define _I64_MAX LONG_LONG_MAX -# define LONGLONG long long -#endif - -#include -#include - #include "filter.h" /***************************************************************************** - * Local prototypes + * Access: local prototypes *****************************************************************************/ -static int AccessOpen ( vlc_object_t * ); -static void AccessClose ( vlc_object_t * ); -static int Read ( input_thread_t *, byte_t *, size_t ); - -static int DemuxOpen ( vlc_object_t * ); -static void DemuxClose ( vlc_object_t * ); -static int Demux ( input_thread_t * ); +static ssize_t Read ( input_thread_t *, byte_t *, size_t ); +static ssize_t ReadCompressed( input_thread_t *, byte_t *, size_t ); static int OpenDevice( input_thread_t *, string, vlc_bool_t ); static IBaseFilter *FindCaptureDevice( vlc_object_t *, string *, list *, vlc_bool_t ); -static bool ConnectFilters( IFilterGraph *p_graph, IBaseFilter *p_filter, - IPin *p_input_pin ); - -/***************************************************************************** - * Module descriptior - *****************************************************************************/ -#define CACHING_TEXT N_("Caching value in ms") -#define CACHING_LONGTEXT N_( \ - "Allows you to modify the default caching value for directshow streams. " \ - "This value should be set in miliseconds units." ) - -vlc_module_begin(); - set_description( _("DirectShow input") ); - add_category_hint( N_("dshow"), NULL, VLC_TRUE ); - add_integer( "dshow-caching", DEFAULT_PTS_DELAY / 1000, NULL, - CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); - add_shortcut( "dshow" ); - set_capability( "access", 0 ); - set_callbacks( AccessOpen, AccessClose ); +static AM_MEDIA_TYPE EnumDeviceCaps( vlc_object_t *, IBaseFilter *, + int, int, int, int, int, int ); +static bool ConnectFilters( vlc_object_t *, IBaseFilter *, CaptureFilter * ); - add_submodule(); - set_description( _("DirectShow demuxer") ); - add_shortcut( "dshow" ); - set_capability( "demux", 200 ); - set_callbacks( DemuxOpen, DemuxClose ); +static int FindDevicesCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int ConfigDevicesCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); -vlc_module_end(); +static void PropertiesPage( vlc_object_t *, IBaseFilter *, + ICaptureGraphBuilder2 *, vlc_bool_t ); -/**************************************************************************** - * I. Access Part - ****************************************************************************/ +#if 0 + /* Debug only, use this to find out GUIDs */ + unsigned char p_st[]; + UuidToString( (IID *)&IID_IAMBufferNegotiation, &p_st ); + msg_Err( p_input, "BufferNegotiation: %s" , p_st ); +#endif /* * header: @@ -131,6 +97,83 @@ static void SetQWBE( uint8_t *p, uint64_t qw ) SetDWBE( &p[4], qw&0xffffffff ); } +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +static char *ppsz_vdev[] = { "", "none" }; +static char *ppsz_vdev_text[] = { N_("Default"), N_("None") }; +static char *ppsz_adev[] = { "", "none" }; +static char *ppsz_adev_text[] = { N_("Default"), N_("None") }; + +#define CACHING_TEXT N_("Caching value in ms") +#define CACHING_LONGTEXT N_( \ + "Allows you to modify the default caching value for directshow streams. " \ + "This value should be set in milliseconds units." ) +#define VDEV_TEXT N_("Video device name") +#define VDEV_LONGTEXT N_( \ + "You can specify the name of the video device that will be used by the " \ + "DirectShow plugin. If you don't specify anything, the default device " \ + "will be used.") +#define ADEV_TEXT N_("Audio device name") +#define ADEV_LONGTEXT N_( \ + "You can specify the name of the audio device that will be used by the " \ + "DirectShow plugin. If you don't specify anything, the default device " \ + "will be used.") +#define SIZE_TEXT N_("Video size") +#define SIZE_LONGTEXT N_( \ + "You can specify the size of the video that will be displayed by the " \ + "DirectShow plugin. If you don't specify anything the default size for " \ + "your device will be used.") +#define CHROMA_TEXT N_("Video input chroma format") +#define CHROMA_LONGTEXT N_( \ + "Force the DirectShow video input to use a specific chroma format " \ + "(eg. I420 (default), RV24, etc.)") +#define CONFIG_TEXT N_("Device properties") +#define CONFIG_LONGTEXT N_( \ + "Show the properties dialog of the selected device before starting the " \ + "stream.") + +static int AccessOpen ( vlc_object_t * ); +static void AccessClose( vlc_object_t * ); + +static int DemuxOpen ( vlc_object_t * ); +static void DemuxClose ( vlc_object_t * ); + +vlc_module_begin(); + set_description( _("DirectShow input") ); + add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL, + CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); + + add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, VLC_FALSE); + change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback ); + change_action_add( FindDevicesCallback, N_("Refresh list") ); + change_action_add( ConfigDevicesCallback, N_("Configure") ); + + add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE); + change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback ); + change_action_add( FindDevicesCallback, N_("Refresh list") ); + change_action_add( ConfigDevicesCallback, N_("Configure") ); + + add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE); + + add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, + VLC_TRUE ); + + add_bool( "dshow-config", VLC_FALSE, NULL, CONFIG_TEXT, CONFIG_LONGTEXT, + VLC_FALSE ); + + add_shortcut( "dshow" ); + set_capability( "access", 0 ); + set_callbacks( AccessOpen, AccessClose ); + + add_submodule(); + set_description( _("DirectShow demuxer") ); + add_shortcut( "dshow" ); + set_capability( "demux", 200 ); + set_callbacks( DemuxOpen, DemuxClose ); + +vlc_module_end(); + /**************************************************************************** * DirectShow elementary stream descriptor ****************************************************************************/ @@ -141,6 +184,7 @@ typedef struct dshow_stream_t CaptureFilter *p_capture_filter; AM_MEDIA_TYPE mt; int i_fourcc; + vlc_bool_t b_invert; union { @@ -149,20 +193,34 @@ typedef struct dshow_stream_t } header; - VLCMediaSample sample; - int i_data_size; - int i_data_pos; - uint8_t *p_data; + vlc_bool_t b_pts; } dshow_stream_t; /**************************************************************************** * Access descriptor declaration ****************************************************************************/ +#define MAX_CROSSBAR_DEPTH 10 + +typedef struct CrossbarRouteRec { + IAMCrossbar *pXbar; + LONG VideoInputIndex; + LONG VideoOutputIndex; + LONG AudioInputIndex; + LONG AudioOutputIndex; +} CrossbarRoute; + struct access_sys_t { - IFilterGraph *p_graph; - IMediaControl *p_control; + vlc_mutex_t lock; + vlc_cond_t wait; + + IFilterGraph *p_graph; + ICaptureGraphBuilder2 *p_capture_graph_builder2; + IMediaControl *p_control; + + int i_crossbar_route_depth; + CrossbarRoute crossbar_routes[MAX_CROSSBAR_DEPTH]; /* header */ int i_header_size; @@ -170,10 +228,107 @@ struct access_sys_t uint8_t *p_header; /* list of elementary streams */ - vector streams; - int i_current_stream; + dshow_stream_t **pp_streams; + int i_streams; + int i_current_stream; + + /* misc properties */ + int i_width; + int i_height; + int i_chroma; + int b_audio; }; +/**************************************************************************** + * DirectShow utility functions + ****************************************************************************/ +static void CreateDirectShowGraph( access_sys_t *p_sys ) +{ + p_sys->i_crossbar_route_depth = 0; + + /* Create directshow filter graph */ + if( SUCCEEDED( CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC, + (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph) ) ) + { + /* Create directshow capture graph builder if available */ + if( SUCCEEDED( CoCreateInstance( CLSID_CaptureGraphBuilder2, 0, + CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2, + (void **)&p_sys->p_capture_graph_builder2 ) ) ) + { + p_sys->p_capture_graph_builder2-> + SetFiltergraph((IGraphBuilder *)p_sys->p_graph); + } + + p_sys->p_graph->QueryInterface( IID_IMediaControl, + (void **)&p_sys->p_control ); + } +} + +static void DeleteCrossbarRoutes( access_sys_t *p_sys ) +{ + /* Remove crossbar filters from graph */ + for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ ) + { + p_sys->crossbar_routes[i].pXbar->Release(); + } + p_sys->i_crossbar_route_depth = 0; +} + +static void DeleteDirectShowGraph( access_sys_t *p_sys ) +{ + DeleteCrossbarRoutes( p_sys ); + + /* Remove filters from graph */ + for( int i = 0; i < p_sys->i_streams; i++ ) + { + p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_capture_filter ); + p_sys->p_graph->RemoveFilter( p_sys->pp_streams[i]->p_device_filter ); + p_sys->pp_streams[i]->p_capture_filter->Release(); + p_sys->pp_streams[i]->p_device_filter->Release(); + } + + /* Release directshow objects */ + if( p_sys->p_control ) + { + p_sys->p_control->Release(); + p_sys->p_control = NULL; + } + if( p_sys->p_capture_graph_builder2 ) + { + p_sys->p_capture_graph_builder2->Release(); + p_sys->p_capture_graph_builder2 = NULL; + } + + if( p_sys->p_graph ) + { + p_sys->p_graph->Release(); + p_sys->p_graph = NULL; + } +} + +static void ReleaseDirectShow( input_thread_t *p_input ) +{ + msg_Dbg( p_input, "Releasing DirectShow"); + + access_sys_t *p_sys = p_input->p_access_data; + + DeleteDirectShowGraph( p_sys ); + + /* Uninitialize OLE/COM */ + CoUninitialize(); + + free( p_sys->p_header ); + /* Remove filters from graph */ + for( int i = 0; i < p_sys->i_streams; i++ ) + { + delete p_sys->pp_streams[i]; + } + free( p_sys->pp_streams ); + free( p_sys ); + + p_input->p_access_data = NULL; +} + /***************************************************************************** * Open: open direct show device *****************************************************************************/ @@ -181,18 +336,74 @@ static int AccessOpen( vlc_object_t *p_this ) { input_thread_t *p_input = (input_thread_t *)p_this; access_sys_t *p_sys; + vlc_value_t val; -#if 0 - /* parse url and open device(s) */ - char *psz_dup, *psz_parser; - psz_dup = strdup( p_input->psz_name ); - psz_parser = psz_dup; + /* Get/parse options and open device(s) */ + string vdevname, adevname; + int i_width = 0, i_height = 0, i_chroma = 0; + + var_Create( p_input, "dshow-config", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + + var_Create( p_input, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + var_Get( p_input, "dshow-vdev", &val ); + if( val.psz_string ) vdevname = string( val.psz_string ); + if( val.psz_string ) free( val.psz_string ); + + var_Create( p_input, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + var_Get( p_input, "dshow-adev", &val ); + if( val.psz_string ) adevname = string( val.psz_string ); + if( val.psz_string ) free( val.psz_string ); + + var_Create( p_input, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + var_Get( p_input, "dshow-size", &val ); + if( val.psz_string && *val.psz_string ) + { + if( !strcmp( val.psz_string, "subqcif" ) ) + { + i_width = 128; i_height = 96; + } + else if( !strcmp( val.psz_string, "qsif" ) ) + { + i_width = 160; i_height = 120; + } + else if( !strcmp( val.psz_string, "qcif" ) ) + { + i_width = 176; i_height = 144; + } + else if( !strcmp( val.psz_string, "sif" ) ) + { + i_width = 320; i_height = 240; + } + else if( !strcmp( val.psz_string, "cif" ) ) + { + i_width = 352; i_height = 288; + } + else if( !strcmp( val.psz_string, "vga" ) ) + { + i_width = 640; i_height = 480; + } + else + { + /* Width x Height */ + char *psz_parser; + i_width = strtol( val.psz_string, &psz_parser, 0 ); + if( *psz_parser == 'x' || *psz_parser == 'X') + { + i_height = strtol( psz_parser + 1, &psz_parser, 0 ); + } + msg_Dbg( p_input, "Width x Height %dx%d", i_width, i_height ); + } + } + if( val.psz_string ) free( val.psz_string ); - while( *psz_parser && *psz_parser != ':' ) + var_Create( p_input, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + var_Get( p_input, "dshow-chroma", &val ); + if( val.psz_string && strlen( val.psz_string ) >= 4 ) { - psz_parser++; + i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1], + val.psz_string[2], val.psz_string[3] ); } -#endif + if( val.psz_string ) free( val.psz_string ); p_input->pf_read = Read; p_input->pf_seek = NULL; @@ -207,16 +418,24 @@ static int AccessOpen( vlc_object_t *p_this ) p_input->stream.i_method = INPUT_METHOD_FILE; vlc_mutex_unlock( &p_input->stream.stream_lock ); - /* Update default_pts to a suitable value for access */ - p_input->i_pts_delay = config_GetInt( p_input, "dshow-caching" ) * 1000; + var_Create( p_input, "dshow-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT); + var_Get( p_input, "dshow-caching", &val ); + p_input->i_pts_delay = val.i_int * 1000; /* Initialize OLE/COM */ - CoInitializeEx( 0, COINIT_APARTMENTTHREADED ); + CoInitialize( 0 ); /* create access private data */ p_input->p_access_data = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) ); - memset( p_sys, 0, sizeof( access_sys_t ) ); + + /* Initialize some data */ + p_sys->i_streams = 0; + p_sys->pp_streams = (dshow_stream_t **)malloc( 1 ); + p_sys->i_width = i_width; + p_sys->i_height = i_height; + p_sys->i_chroma = i_chroma; + p_sys->b_audio = VLC_TRUE; /* Create header */ p_sys->i_header_size = 8; @@ -225,39 +444,63 @@ static int AccessOpen( vlc_object_t *p_this ) SetDWBE( &p_sys->p_header[4], 1 ); p_sys->i_header_pos = p_sys->i_header_size; - /* Build directshow graph */ - CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC, - (REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph ); + p_sys->p_graph = NULL; + p_sys->p_capture_graph_builder2 = NULL; + p_sys->p_control = NULL; - p_sys->p_graph->QueryInterface( IID_IMediaControl, - (void **)&p_sys->p_control ); + /* Build directshow graph */ + CreateDirectShowGraph( p_sys ); - if( OpenDevice( p_input, p_input->psz_name, 0 ) != VLC_SUCCESS ) + if( OpenDevice( p_input, vdevname, 0 ) != VLC_SUCCESS ) { msg_Err( p_input, "can't open video"); } - if( OpenDevice( p_input, p_input->psz_name, 1 ) != VLC_SUCCESS ) + if( p_sys->b_audio && OpenDevice( p_input, adevname, 1 ) != VLC_SUCCESS ) { msg_Err( p_input, "can't open audio"); } - - if( p_sys->streams.empty() ) + + for( int i = 0; i < p_sys->i_crossbar_route_depth; i++ ) { - /* Uninitialize OLE/COM */ - CoUninitialize(); + IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar; + LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex; + LONG VideoOutputIndex = p_sys->crossbar_routes[i].VideoOutputIndex; + LONG AudioInputIndex = p_sys->crossbar_routes[i].AudioInputIndex; + LONG AudioOutputIndex = p_sys->crossbar_routes[i].AudioOutputIndex; - /* Release directshow objects */ - p_sys->p_control->Release(); - p_sys->p_graph->Release(); - free( p_sys->p_header ); - free( p_sys ); + if( SUCCEEDED(pXbar->Route(VideoOutputIndex, VideoInputIndex)) ) + { + msg_Dbg( p_input, "Crossbar at depth %d, Routed video ouput %d to " + "video input %d", i, VideoOutputIndex, VideoInputIndex ); + + if( AudioOutputIndex != -1 && AudioInputIndex != -1 ) + { + if( SUCCEEDED( pXbar->Route(AudioOutputIndex, + AudioInputIndex)) ) + { + msg_Dbg(p_input, "Crossbar at depth %d, Routed audio " + "ouput %d to audio input %d", i, + AudioOutputIndex, AudioInputIndex ); + } + } + } + } + + if( !p_sys->i_streams ) + { + ReleaseDirectShow( p_input ); return VLC_EGENERIC; } /* Initialize some data */ p_sys->i_current_stream = 0; - p_sys->i_header_pos = 0; + p_input->i_mtu += p_sys->i_header_size + 16 /* data header size */; + + vlc_mutex_init( p_input, &p_sys->lock ); + vlc_cond_init( p_input, &p_sys->wait ); + + msg_Dbg( p_input, "Playing..."); /* Everything is ready. Let's rock baby */ p_sys->p_control->Run(); @@ -271,138 +514,493 @@ static int AccessOpen( vlc_object_t *p_this ) static void AccessClose( vlc_object_t *p_this ) { input_thread_t *p_input = (input_thread_t *)p_this; - access_sys_t *p_sys = p_input->p_access_data; + access_sys_t *p_sys = p_input->p_access_data; /* Stop capturing stuff */ p_sys->p_control->Stop(); - p_sys->p_control->Release(); - /* Remove filters from graph */ - //p_sys->p_graph->RemoveFilter( p_sys->p_capture_filter ); - //p_sys->p_graph->RemoveFilter( p_sys->p_device_filter ); + ReleaseDirectShow(p_input); +} - /* Release objects */ - //p_sys->p_device_filter->Release(); - //p_sys->p_capture_filter->Release(); - //p_sys->p_graph->Release(); +/**************************************************************************** + * RouteCrossbars (Does not AddRef the returned *Pin) + ****************************************************************************/ +static HRESULT GetCrossbarIPinAtIndex( IAMCrossbar *pXbar, LONG PinIndex, + BOOL IsInputPin, IPin ** ppPin ) +{ + LONG cntInPins, cntOutPins; + IPin *pP = 0; + IBaseFilter *pFilter = NULL; + IEnumPins *pins=0; + ULONG n; - /* Uninitialize OLE/COM */ - CoUninitialize(); + if( !pXbar || !ppPin ) return E_POINTER; - free( p_sys ); + *ppPin = 0; + + if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) return E_FAIL; + + LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins; + + if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK ) + { + if( SUCCEEDED(pFilter->EnumPins(&pins)) ) + { + LONG i = 0; + while( pins->Next(1, &pP, &n) == S_OK ) + { + pP->Release(); + if( i == TrueIndex ) + { + *ppPin = pP; + break; + } + i++; + } + pins->Release(); + } + pFilter->Release(); + } + + return *ppPin ? S_OK : E_FAIL; } /**************************************************************************** - * ConnectFilters + * GetCrossbarIndexFromIPin: Find corresponding index of an IPin on a crossbar ****************************************************************************/ -static bool ConnectFilters( IFilterGraph *p_graph, IBaseFilter *p_filter, - IPin *p_input_pin ) +static HRESULT GetCrossbarIndexFromIPin( IAMCrossbar * pXbar, LONG * PinIndex, + BOOL IsInputPin, IPin * pPin ) { - IEnumPins *p_enumpins; - IPin *p_output_pin; - ULONG i_fetched; + LONG cntInPins, cntOutPins; + IPin *pP = 0; + IBaseFilter *pFilter = NULL; + IEnumPins *pins = 0; + ULONG n; + BOOL fOK = FALSE; - if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false; + if(!pXbar || !PinIndex || !pPin ) + return E_POINTER; - while( S_OK == p_enumpins->Next( 1, &p_output_pin, &i_fetched ) ) + if( S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins) ) + return E_FAIL; + + if( pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter) == S_OK ) { - if( S_OK == p_graph->ConnectDirect( p_output_pin, p_input_pin, 0 ) ) + if( SUCCEEDED(pFilter->EnumPins(&pins)) ) { - p_enumpins->Release(); - return true; + LONG i=0; + + while( pins->Next(1, &pP, &n) == S_OK ) + { + pP->Release(); + if( pPin == pP ) + { + *PinIndex = IsInputPin ? i : i - cntInPins; + fOK = TRUE; + break; + } + i++; + } + pins->Release(); } + pFilter->Release(); } - p_enumpins->Release(); - return false; + return fOK ? S_OK : E_FAIL; } -static int OpenDevice( input_thread_t *p_input, string devicename, - vlc_bool_t b_audio ) +/**************************************************************************** + * FindCrossbarRoutes + ****************************************************************************/ +static HRESULT FindCrossbarRoutes( vlc_object_t *p_this, IPin *p_input_pin, + LONG physicalType, int depth = 0 ) { - access_sys_t *p_sys = p_input->p_access_data; - list list_devices; - - /* Enumerate audio devices and display their names */ - FindCaptureDevice( (vlc_object_t *)p_input, NULL, &list_devices, b_audio ); + access_sys_t *p_sys = ((input_thread_t *)p_this)->p_access_data; + HRESULT result = S_FALSE; - list::iterator iter; - for( iter = list_devices.begin(); iter != list_devices.end(); iter++ ) - msg_Dbg( p_input, "found device: %s", iter->c_str() ); + IPin *p_output_pin; + if( FAILED(p_input_pin->ConnectedTo(&p_output_pin)) ) return S_FALSE; - /* If no device name was specified, pick the 1st one */ - if( devicename.size() == 0 ) + // It is connected, so now find out if the filter supports IAMCrossbar + PIN_INFO pinInfo; + if( FAILED(p_output_pin->QueryPinInfo(&pinInfo)) || + PINDIR_OUTPUT != pinInfo.dir ) { - devicename = *list_devices.begin(); + p_output_pin->Release (); + return S_FALSE; } - // Use the system device enumerator and class enumerator to find - // a capture/preview device, such as a desktop USB video camera. - IBaseFilter *p_device_filter = - FindCaptureDevice( (vlc_object_t *)p_input, &devicename, - NULL, b_audio ); - if( p_device_filter ) - msg_Dbg( p_input, "using device: %s", devicename.c_str() ); - else + IAMCrossbar *pXbar=0; + if( FAILED(pinInfo.pFilter->QueryInterface(IID_IAMCrossbar, + (void **)&pXbar)) ) { - msg_Err( p_input, "can't use device: %s", devicename.c_str() ); - return VLC_EGENERIC; + pinInfo.pFilter->Release(); + p_output_pin->Release (); + return S_FALSE; } - /* Create and add our capture filter */ - CaptureFilter *p_capture_filter = new CaptureFilter( p_input ); - p_sys->p_graph->AddFilter( p_capture_filter, 0 ); + LONG inputPinCount, outputPinCount; + if( FAILED(pXbar->get_PinCounts(&outputPinCount, &inputPinCount)) ) + { + pXbar->Release(); + pinInfo.pFilter->Release(); + p_output_pin->Release (); + return S_FALSE; + } - /* Add the device filter to the graph (seems necessary with VfW before - * accessing pin attributes). */ - p_sys->p_graph->AddFilter( p_device_filter, 0 ); + LONG inputPinIndexRelated, outputPinIndexRelated; + LONG inputPinPhysicalType, outputPinPhysicalType; + LONG inputPinIndex, outputPinIndex; + if( FAILED(GetCrossbarIndexFromIPin( pXbar, &outputPinIndex, + FALSE, p_output_pin )) || + FAILED(pXbar->get_CrossbarPinInfo( FALSE, outputPinIndex, + &outputPinIndexRelated, + &outputPinPhysicalType )) ) + { + pXbar->Release(); + pinInfo.pFilter->Release(); + p_output_pin->Release (); + return S_FALSE; + } - /* Attempt to connect one of this device's capture output pins */ - msg_Dbg( p_input, "connecting filters" ); - if( ConnectFilters( p_sys->p_graph, p_device_filter, - p_capture_filter->CustomGetPin() ) ) + // + // for all input pins + // + for( inputPinIndex = 0; S_OK != result && inputPinIndex < inputPinCount; + inputPinIndex++ ) { - /* Success */ - dshow_stream_t dshow_stream; - dshow_stream.mt = - p_capture_filter->CustomGetPin()->CustomGetMediaType(); + if( FAILED(pXbar->get_CrossbarPinInfo( TRUE, inputPinIndex, + &inputPinIndexRelated, &inputPinPhysicalType )) ) continue; + + // Is the pin a video pin? + if( inputPinPhysicalType != physicalType ) continue; + + // Can we route it? + if( FAILED(pXbar->CanRoute(outputPinIndex, inputPinIndex)) ) continue; + + IPin *pPin; + if( FAILED(GetCrossbarIPinAtIndex( pXbar, inputPinIndex, + TRUE, &pPin)) ) continue; + + result = FindCrossbarRoutes( p_this, pPin, physicalType, depth+1 ); + if( S_OK == result || (S_FALSE == result && + physicalType == inputPinPhysicalType && + (p_sys->i_crossbar_route_depth = depth+1) < MAX_CROSSBAR_DEPTH) ) + { + // hold on crossbar + pXbar->AddRef(); + + // remember crossbar route + p_sys->crossbar_routes[depth].pXbar = pXbar; + p_sys->crossbar_routes[depth].VideoInputIndex = inputPinIndex; + p_sys->crossbar_routes[depth].VideoOutputIndex = outputPinIndex; + p_sys->crossbar_routes[depth].AudioInputIndex = inputPinIndexRelated; + p_sys->crossbar_routes[depth].AudioOutputIndex = outputPinIndexRelated; + + msg_Dbg( p_this, "Crossbar at depth %d, Found Route For ouput %ld " + "(type %ld) to input %d (type %ld)", depth, + outputPinIndex, outputPinPhysicalType, inputPinIndex, + inputPinPhysicalType ); + + result = S_OK; + } + } - if( dshow_stream.mt.majortype == MEDIATYPE_Video ) + pXbar->Release(); + pinInfo.pFilter->Release(); + p_output_pin->Release (); + + return result; +} + +/**************************************************************************** + * ConnectFilters + ****************************************************************************/ +static bool ConnectFilters( vlc_object_t *p_this, IBaseFilter *p_filter, + CaptureFilter *p_capture_filter ) +{ + access_sys_t *p_sys = ((input_thread_t *)p_this)->p_access_data; + CapturePin *p_input_pin = p_capture_filter->CustomGetPin(); + + AM_MEDIA_TYPE mediaType = p_input_pin->CustomGetMediaType(); + + if( p_sys->p_capture_graph_builder2 ) + { + if( FAILED(p_sys->p_capture_graph_builder2-> + RenderStream( &PIN_CATEGORY_CAPTURE, &mediaType.majortype, + p_filter, NULL, + (IBaseFilter *)p_capture_filter )) ) { - msg_Dbg( p_input, "MEDIATYPE_Video"); + return false; + } - if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB8 ) - dshow_stream.i_fourcc = VLC_FOURCC( 'G', 'R', 'E', 'Y' ); - else if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB555 ) - dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'V', '1', '5' ); - else if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB565 ) - dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'V', '1', '6' ); - else if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB24 ) - dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'V', '2', '4' ); + // Sort out all the possible video inputs + // The class needs to be given the capture filters ANALOGVIDEO input pin + IEnumPins *pins = 0; + if( mediaType.majortype == MEDIATYPE_Video && + SUCCEEDED(p_filter->EnumPins(&pins)) ) + { + IPin *pP = 0; + ULONG n; + PIN_INFO pinInfo; + BOOL Found = FALSE; + IKsPropertySet *pKs=0; + GUID guid; + DWORD dw; + + while( !Found && (S_OK == pins->Next(1, &pP, &n)) ) + { + if(S_OK == pP->QueryPinInfo(&pinInfo)) + { + if(pinInfo.dir == PINDIR_INPUT) + { + // is this pin an ANALOGVIDEOIN input pin? + if( pP->QueryInterface(IID_IKsPropertySet, + (void **)&pKs) == S_OK ) + { + if( pKs->Get(AMPROPSETID_Pin, + AMPROPERTY_PIN_CATEGORY, NULL, 0, + &guid, sizeof(GUID), &dw) == S_OK ) + { + if( guid == PIN_CATEGORY_ANALOGVIDEOIN ) + { + // recursively search crossbar routes + FindCrossbarRoutes( p_this, pP, + PhysConn_Video_Tuner ); + // found it + Found = TRUE; + } + } + pKs->Release(); + } + } + pinInfo.pFilter->Release(); + } + pP->Release(); + } + pins->Release(); + } + return true; + } + else + { + IEnumPins *p_enumpins; + IPin *p_pin; + + if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false; + + while( S_OK == p_enumpins->Next( 1, &p_pin, NULL ) ) + { + PIN_DIRECTION pin_dir; + p_pin->QueryDirection( &pin_dir ); + + if( pin_dir == PINDIR_OUTPUT && + p_sys->p_graph->ConnectDirect( p_pin, (IPin *)p_input_pin, + 0 ) == S_OK ) + { + p_pin->Release(); + p_enumpins->Release(); + return true; + } + p_pin->Release(); + } + + p_enumpins->Release(); + return false; + } +} + +static int OpenDevice( input_thread_t *p_input, string devicename, + vlc_bool_t b_audio ) +{ + access_sys_t *p_sys = p_input->p_access_data; + + /* See if device is already opened */ + for( int i = 0; i < p_sys->i_streams; i++ ) + { + if( p_sys->pp_streams[i]->devicename == devicename ) + { + /* Already opened */ + return VLC_SUCCESS; + } + } + + list list_devices; + + /* Enumerate devices and display their names */ + FindCaptureDevice( (vlc_object_t *)p_input, NULL, &list_devices, b_audio ); + + if( !list_devices.size() ) + return VLC_EGENERIC; + + list::iterator iter; + for( iter = list_devices.begin(); iter != list_devices.end(); iter++ ) + msg_Dbg( p_input, "found device: %s", iter->c_str() ); + + /* If no device name was specified, pick the 1st one */ + if( devicename.size() == 0 ) + { + devicename = *list_devices.begin(); + } + + // Use the system device enumerator and class enumerator to find + // a capture/preview device, such as a desktop USB video camera. + IBaseFilter *p_device_filter = + FindCaptureDevice( (vlc_object_t *)p_input, &devicename, + NULL, b_audio ); + if( p_device_filter ) + msg_Dbg( p_input, "using device: %s", devicename.c_str() ); + else + { + msg_Err( p_input, "can't use device: %s", devicename.c_str() ); + return VLC_EGENERIC; + } + + AM_MEDIA_TYPE media_type = + EnumDeviceCaps( (vlc_object_t *)p_input, p_device_filter, + p_sys->i_chroma, p_sys->i_width, p_sys->i_height, + 0, 0, 0 ); + + /* Create and add our capture filter */ + CaptureFilter *p_capture_filter = new CaptureFilter( p_input, media_type ); + p_sys->p_graph->AddFilter( p_capture_filter, 0 ); + + /* Add the device filter to the graph (seems necessary with VfW before + * accessing pin attributes). */ + p_sys->p_graph->AddFilter( p_device_filter, 0 ); + + /* Attempt to connect one of this device's capture output pins */ + msg_Dbg( p_input, "connecting filters" ); + if( ConnectFilters( VLC_OBJECT(p_input), p_device_filter, + p_capture_filter ) ) + { + /* Success */ + msg_Dbg( p_input, "filters connected successfully !" ); + + dshow_stream_t dshow_stream; + dshow_stream.b_invert = VLC_FALSE; + dshow_stream.b_pts = VLC_FALSE; + dshow_stream.mt = + p_capture_filter->CustomGetPin()->CustomGetMediaType(); + + /* Show properties. Done here so the VLC stream is setup with the + * proper parameters. */ + vlc_value_t val; + var_Get( p_input, "dshow-config", &val ); + if( val.i_int ) + { + PropertiesPage( VLC_OBJECT(p_input), p_device_filter, + p_sys->p_capture_graph_builder2, + dshow_stream.mt.majortype == MEDIATYPE_Audio || + dshow_stream.mt.formattype == FORMAT_WaveFormatEx); + } + + dshow_stream.mt = + p_capture_filter->CustomGetPin()->CustomGetMediaType(); + + if( dshow_stream.mt.majortype == MEDIATYPE_Video ) + { + msg_Dbg( p_input, "MEDIATYPE_Video"); + + /* Packed RGB formats */ + if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB1 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'G', 'B', '1' ); + if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB4 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'G', 'B', '4' ); + if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB8 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'G', 'B', '8' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB555 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'V', '1', '5' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB565 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'V', '1', '6' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB24 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'V', '2', '4' ); else if( dshow_stream.mt.subtype == MEDIASUBTYPE_RGB32 ) dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'V', '3', '2' ); else if( dshow_stream.mt.subtype == MEDIASUBTYPE_ARGB32 ) dshow_stream.i_fourcc = VLC_FOURCC( 'R', 'G', 'B', 'A' ); - + + /* Packed YUV formats */ + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YVYU ) + dshow_stream.i_fourcc = VLC_FOURCC( 'Y', 'V', 'Y', 'U' ); else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YUYV ) dshow_stream.i_fourcc = VLC_FOURCC( 'Y', 'U', 'Y', 'V' ); else if( dshow_stream.mt.subtype == MEDIASUBTYPE_Y411 ) dshow_stream.i_fourcc = VLC_FOURCC( 'I', '4', '1', 'N' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_Y211 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'Y', '2', '1', '1' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YUY2 || + dshow_stream.mt.subtype == MEDIASUBTYPE_UYVY ) + dshow_stream.i_fourcc = VLC_FOURCC( 'Y', 'U', 'Y', '2' ); + + /* Planar YUV formats */ + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_I420 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'I', '4', '2', '0' ); else if( dshow_stream.mt.subtype == MEDIASUBTYPE_Y41P ) dshow_stream.i_fourcc = VLC_FOURCC( 'I', '4', '1', '1' ); - else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YUY2 ) - dshow_stream.i_fourcc = VLC_FOURCC( 'Y', 'U', 'Y', '2' ); - else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YVYU ) - dshow_stream.i_fourcc = VLC_FOURCC( 'Y', 'V', 'Y', 'U' ); - else if( dshow_stream.mt.subtype == MEDIASUBTYPE_Y411 ) - dshow_stream.i_fourcc = VLC_FOURCC( 'I', '4', '1', 'N' ); - else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YV12 ) + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YV12 || + dshow_stream.mt.subtype == MEDIASUBTYPE_IYUV ) dshow_stream.i_fourcc = VLC_FOURCC( 'Y', 'V', '1', '2' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_YVU9 ) + dshow_stream.i_fourcc = VLC_FOURCC( 'Y', 'V', 'U', '9' ); + + /* DV formats */ + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_dvsl ) + dshow_stream.i_fourcc = VLC_FOURCC( 'd', 'v', 's', 'l' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_dvsd ) + dshow_stream.i_fourcc = VLC_FOURCC( 'd', 'v', 's', 'd' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_dvhd ) + dshow_stream.i_fourcc = VLC_FOURCC( 'd', 'v', 'h', 'd' ); + + /* MPEG video formats */ + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_MPEG2_VIDEO ) + dshow_stream.i_fourcc = VLC_FOURCC( 'm', 'p', '2', 'v' ); + else goto fail; dshow_stream.header.video = *(VIDEOINFOHEADER *)dshow_stream.mt.pbFormat; + int i_height = dshow_stream.header.video.bmiHeader.biHeight; + + /* Check if the image is inverted (bottom to top) */ + if( dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', '1' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', '4' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', '8' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '1', '5' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '1', '6' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '2', '4' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'V', '3', '2' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'R', 'G', 'B', 'A' ) ) + { + if( i_height > 0 ) dshow_stream.b_invert = VLC_TRUE; + else i_height = - i_height; + } + + /* Check if we are dealing with a DV stream */ + if( dshow_stream.i_fourcc == VLC_FOURCC( 'd', 'v', 's', 'l' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'd', 'v', 's', 'd' ) || + dshow_stream.i_fourcc == VLC_FOURCC( 'd', 'v', 'h', 'd' ) ) + { + p_input->pf_read = ReadCompressed; + if( !p_input->psz_demux || !*p_input->psz_demux ) + { + p_input->psz_demux = "rawdv"; + } + p_sys->b_audio = VLC_FALSE; + } + + /* Check if we are dealing with an MPEG video stream */ + if( dshow_stream.i_fourcc == VLC_FOURCC( 'm', 'p', '2', 'v' ) ) + { + p_input->pf_read = ReadCompressed; + if( !p_input->psz_demux || !*p_input->psz_demux ) + { + p_input->psz_demux = "mpgv"; + } + p_sys->b_audio = VLC_FALSE; + } + /* Add video stream to header */ p_sys->i_header_size += 20; p_sys->p_header = (uint8_t *)realloc( p_sys->p_header, @@ -412,10 +1010,14 @@ static int OpenDevice( input_thread_t *p_input, string devicename, &dshow_stream.i_fourcc, 4 ); SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 8], dshow_stream.header.video.bmiHeader.biWidth ); - SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], - dshow_stream.header.video.bmiHeader.biHeight ); + SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 12], i_height ); SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], 0 ); p_sys->i_header_pos = p_sys->i_header_size; + + /* Greatly simplifies the reading routine */ + int i_mtu = dshow_stream.header.video.bmiHeader.biWidth * + i_height * 4; + p_input->i_mtu = __MAX( p_input->i_mtu, (unsigned int)i_mtu ); } else if( dshow_stream.mt.majortype == MEDIATYPE_Audio && @@ -425,10 +1027,8 @@ static int OpenDevice( input_thread_t *p_input, string devicename, if( dshow_stream.mt.subtype == MEDIASUBTYPE_PCM ) dshow_stream.i_fourcc = VLC_FOURCC( 'a', 'r', 'a', 'w' ); -#if 0 else if( dshow_stream.mt.subtype == MEDIASUBTYPE_IEEE_FLOAT ) dshow_stream.i_fourcc = VLC_FOURCC( 'f', 'l', '3', '2' ); -#endif else goto fail; dshow_stream.header.audio = @@ -448,18 +1048,71 @@ static int OpenDevice( input_thread_t *p_input, string devicename, SetDWBE( &p_sys->p_header[p_sys->i_header_pos + 16], dshow_stream.header.audio.wBitsPerSample ); p_sys->i_header_pos = p_sys->i_header_size; + + /* Greatly simplifies the reading routine */ + IAMBufferNegotiation *p_ambuf; + IPin *p_pin; + int i_mtu; + + p_capture_filter->CustomGetPin()->ConnectedTo( &p_pin ); + if( SUCCEEDED( p_pin->QueryInterface( + IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) ) + { + ALLOCATOR_PROPERTIES AllocProp; + memset( &AllocProp, 0, sizeof( ALLOCATOR_PROPERTIES ) ); + p_ambuf->GetAllocatorProperties( &AllocProp ); + p_ambuf->Release(); + i_mtu = AllocProp.cbBuffer; + } + else + { + /* Worst case */ + i_mtu = dshow_stream.header.audio.nSamplesPerSec * + dshow_stream.header.audio.nChannels * + dshow_stream.header.audio.wBitsPerSample / 8; + } + p_pin->Release(); + p_input->i_mtu = __MAX( p_input->i_mtu, (unsigned int)i_mtu ); + } + + else if( dshow_stream.mt.majortype == MEDIATYPE_Stream ) + { + msg_Dbg( p_input, "MEDIATYPE_Stream" ); + + if( dshow_stream.mt.subtype == MEDIASUBTYPE_MPEG2_PROGRAM ) + dshow_stream.i_fourcc = VLC_FOURCC( 'm', 'p', '2', 'p' ); + else if( dshow_stream.mt.subtype == MEDIASUBTYPE_MPEG2_TRANSPORT ) + dshow_stream.i_fourcc = VLC_FOURCC( 'm', 'p', '2', 't' ); + + msg_Dbg( p_input, "selected stream pin accepts format: %4.4s", + (char *)&dshow_stream.i_fourcc); + + p_sys->b_audio = VLC_FALSE; + p_sys->i_header_size = 0; + p_sys->i_header_pos = 0; + p_input->i_mtu = INPUT_DEFAULT_BUFSIZE; + + p_input->pf_read = ReadCompressed; + p_input->pf_set_program = input_SetProgram; + } + + else + { + msg_Dbg( p_input, "unknown stream majortype" ); + goto fail; } - else goto fail; /* Add directshow elementary stream to our list */ - dshow_stream.sample.p_sample = NULL; - dshow_stream.i_data_size = 0; - dshow_stream.i_data_pos = 0; dshow_stream.p_device_filter = p_device_filter; dshow_stream.p_capture_filter = p_capture_filter; - p_sys->streams.push_back( dshow_stream ); - SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->streams.size() ); + p_sys->pp_streams = + (dshow_stream_t **)realloc( p_sys->pp_streams, + sizeof(dshow_stream_t *) + * (p_sys->i_streams + 1) ); + p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t; + *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream; + SetDWBE( &p_sys->p_header[4], (uint32_t)p_sys->i_streams ); return VLC_SUCCESS; } @@ -511,11 +1164,11 @@ FindCaptureDevice( vlc_object_t *p_this, string *p_devicename, return NULL; } - /* If there are no enumerators for the requested type, then + /* If there are no enumerators for the requested type, then * CreateClassEnumerator will succeed, but p_class_enum will be NULL */ if( p_class_enum == NULL ) { - msg_Err( p_this, "no capture device was detected." ); + msg_Err( p_this, "no capture device was detected" ); return NULL; } @@ -574,331 +1227,965 @@ FindCaptureDevice( vlc_object_t *p_this, string *p_devicename, return NULL; } +static AM_MEDIA_TYPE EnumDeviceCaps( vlc_object_t *p_this, + IBaseFilter *p_filter, + int i_fourcc, int i_width, int i_height, + int i_channels, int i_samplespersec, + int i_bitspersample ) +{ + IEnumPins *p_enumpins; + IPin *p_output_pin; + IEnumMediaTypes *p_enummt; + int i_orig_fourcc = i_fourcc; + vlc_bool_t b_found = VLC_FALSE; + + AM_MEDIA_TYPE media_type; + media_type.majortype = GUID_NULL; + media_type.subtype = GUID_NULL; + media_type.formattype = GUID_NULL; + media_type.pUnk = NULL; + media_type.cbFormat = 0; + media_type.pbFormat = NULL; + + if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return media_type; + + while( S_OK == p_enumpins->Next( 1, &p_output_pin, NULL ) ) + { + PIN_INFO info; + + if( S_OK == p_output_pin->QueryPinInfo( &info ) ) + { + msg_Dbg( p_this, "EnumDeviceCaps: %s pin: %S", + info.dir == PINDIR_INPUT ? "input" : "output", + info.achName ); + if( info.pFilter ) info.pFilter->Release(); + } + + p_output_pin->Release(); + } + p_enumpins->Reset(); + + while( !b_found && p_enumpins->Next( 1, &p_output_pin, NULL ) == S_OK ) + { + PIN_INFO info; + + if( S_OK == p_output_pin->QueryPinInfo( &info ) ) + { + if( info.pFilter ) info.pFilter->Release(); + if( info.dir == PINDIR_INPUT ) + { + p_output_pin->Release(); + continue; + } + msg_Dbg( p_this, "EnumDeviceCaps: trying pin %S", info.achName ); + } + + /* Probe pin */ + if( SUCCEEDED( p_output_pin->EnumMediaTypes( &p_enummt ) ) ) + { + AM_MEDIA_TYPE *p_mt; + while( p_enummt->Next( 1, &p_mt, NULL ) == S_OK ) + { + + if( p_mt->majortype == MEDIATYPE_Video ) + { + int i_current_fourcc = VLC_FOURCC(' ', ' ', ' ', ' '); + + /* Packed RGB formats */ + if( p_mt->subtype == MEDIASUBTYPE_RGB1 ) + i_current_fourcc = VLC_FOURCC( 'R', 'G', 'B', '1' ); + if( p_mt->subtype == MEDIASUBTYPE_RGB4 ) + i_current_fourcc = VLC_FOURCC( 'R', 'G', 'B', '4' ); + if( p_mt->subtype == MEDIASUBTYPE_RGB8 ) + i_current_fourcc = VLC_FOURCC( 'R', 'G', 'B', '8' ); + else if( p_mt->subtype == MEDIASUBTYPE_RGB555 ) + i_current_fourcc = VLC_FOURCC( 'R', 'V', '1', '5' ); + else if( p_mt->subtype == MEDIASUBTYPE_RGB565 ) + i_current_fourcc = VLC_FOURCC( 'R', 'V', '1', '6' ); + else if( p_mt->subtype == MEDIASUBTYPE_RGB24 ) + i_current_fourcc = VLC_FOURCC( 'R', 'V', '2', '4' ); + else if( p_mt->subtype == MEDIASUBTYPE_RGB32 ) + i_current_fourcc = VLC_FOURCC( 'R', 'V', '3', '2' ); + else if( p_mt->subtype == MEDIASUBTYPE_ARGB32 ) + i_current_fourcc = VLC_FOURCC( 'R', 'G', 'B', 'A' ); + + /* Packed YUV formats */ + else if( p_mt->subtype == MEDIASUBTYPE_YVYU ) + i_current_fourcc = VLC_FOURCC( 'Y', 'V', 'Y', 'U' ); + else if( p_mt->subtype == MEDIASUBTYPE_YUYV ) + i_current_fourcc = VLC_FOURCC( 'Y', 'U', 'Y', 'V' ); + else if( p_mt->subtype == MEDIASUBTYPE_Y411 ) + i_current_fourcc = VLC_FOURCC( 'I', '4', '1', 'N' ); + else if( p_mt->subtype == MEDIASUBTYPE_Y211 ) + i_current_fourcc = VLC_FOURCC( 'Y', '2', '1', '1' ); + else if( p_mt->subtype == MEDIASUBTYPE_YUY2 || + p_mt->subtype == MEDIASUBTYPE_UYVY ) + i_current_fourcc = VLC_FOURCC( 'Y', 'U', 'Y', '2' ); + + /* MPEG2 video elementary stream */ + else if( p_mt->subtype == MEDIASUBTYPE_MPEG2_VIDEO ) + i_current_fourcc = VLC_FOURCC( 'm', 'p', '2', 'v' ); + + /* hauppauge pvr video preview */ + else if( p_mt->subtype == MEDIASUBTYPE_PREVIEW_VIDEO ) + i_current_fourcc = VLC_FOURCC( 'P', 'V', 'R', 'V' ); + + else i_current_fourcc = *((int *)&p_mt->subtype); + + int i_current_width = p_mt->pbFormat ? + ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biWidth : 0; + int i_current_height = p_mt->pbFormat ? + ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight : 0; + if( i_current_height < 0 ) + i_current_height = -i_current_height; + + msg_Dbg( p_this, "EnumDeviceCaps: input pin " + "accepts chroma: %4.4s, width:%i, height:%i", + (char *)&i_current_fourcc, i_current_width, + i_current_height ); + + if( ( !i_fourcc || i_fourcc == i_current_fourcc || + ( !i_orig_fourcc && i_current_fourcc == + VLC_FOURCC('I','4','2','0') ) ) && + ( !i_width || i_width == i_current_width ) && + ( !i_height || i_height == i_current_height ) ) + { + /* Pick the 1st match */ + media_type = *p_mt; + i_fourcc = i_current_fourcc; + i_width = i_current_width; + i_height = i_current_height; + b_found = VLC_TRUE; + } + else + { + FreeMediaType( *p_mt ); + } + } + else if( p_mt->majortype == MEDIATYPE_Audio ) + { + int i_current_fourcc; + int i_current_channels = + ((WAVEFORMATEX *)p_mt->pbFormat)->nChannels; + int i_current_samplespersec = + ((WAVEFORMATEX *)p_mt->pbFormat)->nSamplesPerSec; + int i_current_bitspersample = + ((WAVEFORMATEX *)p_mt->pbFormat)->wBitsPerSample; + + if( p_mt->subtype == MEDIASUBTYPE_PCM ) + i_current_fourcc = VLC_FOURCC( 'p', 'c', 'm', ' ' ); + else i_current_fourcc = *((int *)&p_mt->subtype); + + msg_Dbg( p_this, "EnumDeviceCaps: input pin " + "accepts format: %4.4s, channels:%i, " + "samples/sec:%i bits/sample:%i", + (char *)&i_current_fourcc, i_current_channels, + i_current_samplespersec, i_current_bitspersample); + + if( (!i_channels || i_channels == i_current_channels) && + (!i_samplespersec || + i_samplespersec == i_current_samplespersec) && + (!i_bitspersample || + i_bitspersample == i_current_bitspersample) ) + { + /* Pick the 1st match */ + media_type = *p_mt; + i_channels = i_current_channels; + i_samplespersec = i_current_samplespersec; + i_bitspersample = i_current_bitspersample; + b_found = VLC_TRUE; + + /* Setup a few properties like the audio latency */ + IAMBufferNegotiation *p_ambuf; + + if( SUCCEEDED( p_output_pin->QueryInterface( + IID_IAMBufferNegotiation, (void **)&p_ambuf ) ) ) + { + ALLOCATOR_PROPERTIES AllocProp; + AllocProp.cbAlign = -1; + AllocProp.cbBuffer = i_channels * i_samplespersec * + i_bitspersample / 8 / 10 ; /*100 ms of latency*/ + AllocProp.cbPrefix = -1; + AllocProp.cBuffers = -1; + p_ambuf->SuggestAllocatorProperties( &AllocProp ); + p_ambuf->Release(); + } + } + else + { + FreeMediaType( *p_mt ); + } + } + else if( p_mt->majortype == MEDIATYPE_Stream ) + { + msg_Dbg( p_this, "EnumDeviceCaps: MEDIATYPE_Stream" ); + + int i_current_fourcc = VLC_FOURCC(' ', ' ', ' ', ' '); + + if( p_mt->subtype == MEDIASUBTYPE_MPEG2_PROGRAM ) + i_current_fourcc = VLC_FOURCC( 'm', 'p', '2', 'p' ); + else if( p_mt->subtype == MEDIASUBTYPE_MPEG2_TRANSPORT ) + i_current_fourcc = VLC_FOURCC( 'm', 'p', '2', 't' ); + + if( ( !i_fourcc || i_fourcc == i_current_fourcc ) ) + { + /* Pick the 1st match */ + media_type = *p_mt; + i_fourcc = i_current_fourcc; + b_found = VLC_TRUE; + } + else + { + FreeMediaType( *p_mt ); + } + } + else + { + msg_Dbg( p_this, + "EnumDeviceCaps: input pin: unknown format" ); + FreeMediaType( *p_mt ); + } + + CoTaskMemFree( (PVOID)p_mt ); + } + p_enummt->Release(); + } + + p_output_pin->Release(); + } + + p_enumpins->Release(); + return media_type; +} + /***************************************************************************** * Read: reads from the device into PES packets. ***************************************************************************** * Returns -1 in case of error, 0 in case of EOF, otherwise the number of * bytes. *****************************************************************************/ -static int Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len ) +static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, + size_t i_len ) { access_sys_t *p_sys = p_input->p_access_data; - dshow_stream_t *p_stream = &p_sys->streams[p_sys->i_current_stream]; - int i_data = 0; + dshow_stream_t *p_stream = NULL; + byte_t *p_buf_orig = p_buffer; + VLCMediaSample sample; + int i_data_size; + uint8_t *p_data; -#if 0 - msg_Info( p_input, "access read data_size %i, data_pos %i", - p_sys->i_data_size, p_sys->i_data_pos ); -#endif + if( p_sys->i_header_pos ) + { + /* First header of the stream */ + memcpy( p_buffer, p_sys->p_header, p_sys->i_header_size ); + p_buffer += p_sys->i_header_size; + p_sys->i_header_pos = 0; + } - while( i_len > 0 ) + while( 1 ) { - /* First copy header if any */ - if( i_len > 0 && p_sys->i_header_pos < p_sys->i_header_size ) + /* Get new sample/frame from next elementary stream. + * We first loop through all the elementary streams and if all our + * fifos are empty we block until we are signaled some new data has + * arrived. */ + vlc_mutex_lock( &p_sys->lock ); + + int i_stream; + for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ ) + { + p_stream = p_sys->pp_streams[i_stream]; + if( p_stream->mt.majortype == MEDIATYPE_Audio && + p_stream->p_capture_filter && + p_stream->p_capture_filter->CustomGetPin() + ->CustomGetSample( &sample ) == S_OK ) + { + break; + } + } + if( i_stream == p_sys->i_streams ) + for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ ) { - int i_copy; + p_stream = p_sys->pp_streams[i_stream]; + if( p_stream->p_capture_filter && + p_stream->p_capture_filter->CustomGetPin() + ->CustomGetSample( &sample ) == S_OK ) + { + break; + } + } + if( i_stream == p_sys->i_streams ) + { + /* No data available. Wait until some data has arrived */ + vlc_cond_wait( &p_sys->wait, &p_sys->lock ); + vlc_mutex_unlock( &p_sys->lock ); + continue; + } - i_copy = __MIN( p_sys->i_header_size - - p_sys->i_header_pos, (int)i_len ); - memcpy( p_buffer, &p_sys->p_header[p_sys->i_header_pos], i_copy ); - p_sys->i_header_pos += i_copy; + vlc_mutex_unlock( &p_sys->lock ); - p_buffer += i_copy; - i_len -= i_copy; - i_data += i_copy; + /* + * We got our sample + */ + i_data_size = sample.p_sample->GetActualDataLength(); + sample.p_sample->GetPointer( &p_data ); + + REFERENCE_TIME i_pts, i_end_date; + HRESULT hr = sample.p_sample->GetTime( &i_pts, &i_end_date ); + if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0; + + if( !i_pts ) + { + if( p_stream->mt.majortype == MEDIATYPE_Video || !p_stream->b_pts ) + { + /* Use our data timestamp */ + i_pts = sample.i_timestamp; + p_stream->b_pts = VLC_TRUE; + } } +#if 0 + msg_Dbg( p_input, "Read() stream: %i PTS: "I64Fd, i_stream, i_pts ); +#endif + + /* Create pseudo header */ + SetDWBE( &p_sys->p_header[0], i_stream ); + SetDWBE( &p_sys->p_header[4], i_data_size ); + SetQWBE( &p_sys->p_header[8], i_pts * 9 / 1000 ); + +#if 0 + msg_Info( p_input, "access read %i data_size %i", i_len, i_data_size ); +#endif + + /* First copy header */ + memcpy( p_buffer, p_sys->p_header, 16 /* header size */ ); + p_buffer += 16 /* header size */; + /* Then copy stream data if any */ - if( i_len > 0 && p_stream->i_data_pos < p_stream->i_data_size ) + if( !p_stream->b_invert ) { - int i_copy = __MIN( p_stream->i_data_size - - p_stream->i_data_pos, (int)i_len ); + p_input->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size ); + p_buffer += i_data_size; + } + else + { + int i_width = p_stream->header.video.bmiHeader.biWidth; + int i_height = p_stream->header.video.bmiHeader.biHeight; + if( i_height < 0 ) i_height = - i_height; + + switch( p_stream->i_fourcc ) + { + case VLC_FOURCC( 'R', 'V', '1', '5' ): + case VLC_FOURCC( 'R', 'V', '1', '6' ): + i_width *= 2; + break; + case VLC_FOURCC( 'R', 'V', '2', '4' ): + i_width *= 3; + break; + case VLC_FOURCC( 'R', 'V', '3', '2' ): + case VLC_FOURCC( 'R', 'G', 'B', 'A' ): + i_width *= 4; + break; + } - memcpy( p_buffer, &p_stream->p_data[p_stream->i_data_pos], - i_copy ); - p_stream->i_data_pos += i_copy; + for( int i = i_height - 1; i >= 0; i-- ) + { + p_input->p_vlc->pf_memcpy( p_buffer, + &p_data[i * i_width], i_width ); - p_buffer += i_copy; - i_len -= i_copy; - i_data += i_copy; + p_buffer += i_width; + } } + sample.p_sample->Release(); + /* The caller got what he wanted */ - if( i_len <= 0 ) return i_data; + return p_buffer - p_buf_orig; + } - /* Read no more than one frame at a time, otherwise we kill latency */ - if( p_stream->i_data_size && i_data && - p_stream->i_data_pos == p_stream->i_data_size ) - { - p_stream->i_data_pos = p_stream->i_data_size = 0; - return i_data; - } + return 0; /* never reached */ +} + +/***************************************************************************** + * ReadCompressed: reads compressed (MPEG/DV) data from the device. + ***************************************************************************** + * Returns -1 in case of error, 0 in case of EOF, otherwise the number of + * bytes. + *****************************************************************************/ +static ssize_t ReadCompressed( input_thread_t * p_input, byte_t * p_buffer, + size_t i_len ) +{ + access_sys_t *p_sys = p_input->p_access_data; + dshow_stream_t *p_stream = NULL; + VLCMediaSample sample; + int i_data_size; + uint8_t *p_data; + + /* Read 1 DV/MPEG frame (they contain the video and audio data) */ + + /* There must be only 1 elementary stream to produce a valid stream + * of MPEG or DV data */ + p_stream = p_sys->pp_streams[0]; + + while( 1 ) + { + if( p_input->b_die || p_input->b_error ) return 0; + + /* Get new sample/frame from the elementary stream (blocking). */ + vlc_mutex_lock( &p_sys->lock ); - /* Get new sample/frame from next stream */ - //if( p_sream->sample.p_sample ) p_stream->sample.p_sample->Release(); - p_sys->i_current_stream = - (p_sys->i_current_stream + 1) % p_sys->streams.size(); - p_stream = &p_sys->streams[p_sys->i_current_stream]; - if( p_stream->p_capture_filter && - p_stream->p_capture_filter->CustomGetPin() - ->CustomGetSample( &p_stream->sample ) == S_OK ) + if( p_stream->p_capture_filter->CustomGetPin() + ->CustomGetSample( &sample ) != S_OK ) { - p_stream->i_data_pos = 0; - p_stream->i_data_size = - p_stream->sample.p_sample->GetActualDataLength(); - p_stream->sample.p_sample->GetPointer( &p_stream->p_data ); + /* No data available. Wait until some data has arrived */ + vlc_cond_wait( &p_sys->wait, &p_sys->lock ); + vlc_mutex_unlock( &p_sys->lock ); + continue; + } - REFERENCE_TIME i_pts, i_end_date; - HRESULT hr = - p_stream->sample.p_sample->GetTime( &i_pts, &i_end_date ); - if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0; + vlc_mutex_unlock( &p_sys->lock ); - if( !i_pts ) - { - /* Use our data timestamp */ - i_pts = p_stream->sample.i_timestamp; - } + /* + * We got our sample + */ + i_data_size = sample.p_sample->GetActualDataLength(); + sample.p_sample->GetPointer( &p_data ); #if 0 - msg_Dbg( p_input, "Read() PTS: "I64Fd, i_pts ); + msg_Info( p_input, "access read %i data_size %i", i_len, i_data_size ); #endif + i_data_size = __MIN( i_data_size, (int)i_len ); - /* Create pseudo header */ - p_sys->i_header_size = 16; - p_sys->i_header_pos = 0; - SetDWBE( &p_sys->p_header[0], p_sys->i_current_stream ); - SetDWBE( &p_sys->p_header[4], p_stream->i_data_size ); - SetQWBE( &p_sys->p_header[8], i_pts * 9 / 1000 ); - } - else msleep( 10000 ); + p_input->p_vlc->pf_memcpy( p_buffer, p_data, i_data_size ); + + sample.p_sample->Release(); + + /* The caller got what he wanted */ + return i_data_size; } - return i_data; + return 0; /* never reached */ } +/***************************************************************************** + * Demux: local prototypes + *****************************************************************************/ +struct demux_sys_t +{ + int i_es; + es_out_id_t **es; +}; + +static int Demux ( input_thread_t * ); + /**************************************************************************** - * I. Demux Part + * DemuxOpen: ****************************************************************************/ static int DemuxOpen( vlc_object_t *p_this ) { input_thread_t *p_input = (input_thread_t *)p_this; + demux_sys_t *p_sys; uint8_t *p_peek; - int i_streams; + int i_es; int i; - data_packet_t *p_pk; - - /* Initialize access plug-in structures. */ - if( p_input->i_mtu == 0 ) - { - /* Improve speed. */ - p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE ; - } - /* a little test to see if it's a dshow stream */ - if( input_Peek( p_input, &p_peek, 8 ) < 8 ) + if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 ) { msg_Warn( p_input, "dshow plugin discarded (cannot peek)" ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } - if( strcmp( (const char *)p_peek, ".dsh" ) || - GetDWBE( &p_peek[4] ) <= 0 ) + if( memcmp( p_peek, ".dsh", 4 ) || + ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 ) { msg_Warn( p_input, "dshow plugin discarded (not a valid stream)" ); return VLC_EGENERIC; } - /* create one program */ vlc_mutex_lock( &p_input->stream.stream_lock ); if( input_InitStream( p_input, 0 ) == -1) { vlc_mutex_unlock( &p_input->stream.stream_lock ); msg_Err( p_input, "cannot init stream" ); - return( VLC_EGENERIC ); - } - if( input_AddProgram( p_input, 0, 0) == NULL ) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Err( p_input, "cannot add program" ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } + p_input->stream.i_mux_rate = 0 / 50; + vlc_mutex_unlock( &p_input->stream.stream_lock ); - p_input->stream.p_selected_program = p_input->stream.pp_programs[0]; - p_input->stream.i_mux_rate = 0; + p_input->pf_demux = Demux; + p_input->pf_demux_control = demux_vaControlDefault; + p_input->p_demux_data = p_sys = + (demux_sys_t *)malloc( sizeof( demux_sys_t ) ); + p_sys->i_es = 0; + p_sys->es = NULL; - i_streams = GetDWBE( &p_peek[4] ); - if( input_Peek( p_input, &p_peek, 8 + 20 * i_streams ) - < 8 + 20 * i_streams ) + if( stream_Peek( p_input->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es ) { msg_Err( p_input, "dshow plugin discarded (cannot peek)" ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } p_peek += 8; - for( i = 0; i < i_streams; i++ ) + for( i = 0; i < i_es; i++ ) { - es_descriptor_t *p_es; + es_format_t fmt; - if( !strncmp( (const char *)p_peek, "auds", 4 ) ) + if( !memcmp( p_peek, "auds", 4 ) ) { -#define wf ((WAVEFORMATEX*)p_es->p_waveformatex) - p_es = input_AddES( p_input, p_input->stream.pp_programs[0], - i + 1, AUDIO_ES, NULL, 0 ); - p_es->i_stream_id = i + 1; - p_es->i_fourcc = - VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ); - - p_es->p_waveformatex= malloc( sizeof( WAVEFORMATEX ) ); - - wf->wFormatTag = 0;//WAVE_FORMAT_UNKNOWN; - wf->nChannels = GetDWBE( &p_peek[8] ); - wf->nSamplesPerSec = GetDWBE( &p_peek[12] ); - wf->wBitsPerSample = GetDWBE( &p_peek[16] ); - wf->nBlockAlign = wf->wBitsPerSample * wf->nChannels / 8; - wf->nAvgBytesPerSec = wf->nBlockAlign * wf->nSamplesPerSec; - wf->cbSize = 0; - - msg_Dbg( p_input, "added new audio es %d channels %dHz", - wf->nChannels, wf->nSamplesPerSec ); - - input_SelectES( p_input, p_es ); -#undef wf + es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5], + p_peek[6], p_peek[7] ) ); + + fmt.audio.i_channels = GetDWBE( &p_peek[8] ); + fmt.audio.i_rate = GetDWBE( &p_peek[12] ); + fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] ); + fmt.audio.i_blockalign = fmt.audio.i_channels * + fmt.audio.i_bitspersample / 8; + fmt.i_bitrate = fmt.audio.i_channels * + fmt.audio.i_rate * + fmt.audio.i_bitspersample; + + msg_Dbg( p_input, "new audio es %d channels %dHz", + fmt.audio.i_channels, fmt.audio.i_rate ); + + p_sys->es = (es_out_id_t **)realloc( p_sys->es, + sizeof(es_out_id_t *) * (p_sys->i_es + 1) ); + p_sys->es[p_sys->i_es++] = es_out_Add( p_input->p_es_out, &fmt ); } - else if( !strncmp( (const char *)p_peek, "vids", 4 ) ) + else if( !memcmp( p_peek, "vids", 4 ) ) { -#define bih ((BITMAPINFOHEADER*)p_es->p_bitmapinfoheader) - p_es = input_AddES( p_input, p_input->stream.pp_programs[0], - i + 1, VIDEO_ES, NULL, 0 ); - p_es->i_stream_id = i + 1; - p_es->i_fourcc = - VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ); - - p_es->p_bitmapinfoheader = malloc( sizeof( BITMAPINFOHEADER ) ); - - bih->biSize = sizeof( BITMAPINFOHEADER ); - bih->biWidth = GetDWBE( &p_peek[8] ); - bih->biHeight = GetDWBE( &p_peek[12] ); - bih->biPlanes = 0; - bih->biBitCount = 0; - bih->biCompression = 0; - bih->biSizeImage= 0; - bih->biXPelsPerMeter = 0; - bih->biYPelsPerMeter = 0; - bih->biClrUsed = 0; - bih->biClrImportant = 0; + es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5], + p_peek[6], p_peek[7] ) ); + fmt.video.i_width = GetDWBE( &p_peek[8] ); + fmt.video.i_height = GetDWBE( &p_peek[12] ); msg_Dbg( p_input, "added new video es %4.4s %dx%d", - (char*)&p_es->i_fourcc, bih->biWidth, bih->biHeight ); + (char*)&fmt.i_codec, + fmt.video.i_width, fmt.video.i_height ); - input_SelectES( p_input, p_es ); -#undef bih + p_sys->es = (es_out_id_t **)realloc( p_sys->es, + sizeof(es_out_id_t *) * (p_sys->i_es + 1) ); + p_sys->es[p_sys->i_es++] = es_out_Add( p_input->p_es_out, &fmt ); } p_peek += 20; } - p_input->stream.p_selected_program->b_is_ok = 1; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - if( input_SplitBuffer( p_input, &p_pk, 8 + i_streams * 20 ) > 0 ) - { - input_DeletePacket( p_input->p_method_data, p_pk ); - } + /* Skip header */ + stream_Read( p_input->s, NULL, 8 + 20 * i_es ); - p_input->pf_demux = Demux; return VLC_SUCCESS; } +/**************************************************************************** + * DemuxClose: + ****************************************************************************/ static void DemuxClose( vlc_object_t *p_this ) { - return; + input_thread_t *p_input = (input_thread_t *)p_this; + demux_sys_t *p_sys = p_input->p_demux_data; + + if( p_sys->i_es > 0 ) + { + free( p_sys->es ); + } + free( p_sys ); } +/**************************************************************************** + * Demux: + ****************************************************************************/ static int Demux( input_thread_t *p_input ) { - es_descriptor_t *p_es; - pes_packet_t *p_pes; + demux_sys_t *p_sys = p_input->p_demux_data; + block_t *p_block; - int i_stream; + int i_es; int i_size; + uint8_t *p_peek; mtime_t i_pcr; - if( input_Peek( p_input, &p_peek, 16 ) < 16 ) + if( stream_Peek( p_input->s, &p_peek, 16 ) < 16 ) { msg_Warn( p_input, "cannot peek (EOF ?)" ); - return( 0 ); + return 0; } - i_stream = GetDWBE( &p_peek[0] ); - i_size = GetDWBE( &p_peek[4] ); - i_pcr = GetQWBE( &p_peek[8] ); + i_es = GetDWBE( &p_peek[0] ); + if( i_es < 0 || i_es >= p_sys->i_es ) + { + msg_Err( p_input, "cannot find ES" ); + return -1; + } - //msg_Dbg( p_input, "stream=%d size=%d", i_stream, i_size ); - //p_es = input_FindES( p_input, i_stream ); + i_size = GetDWBE( &p_peek[4] ); + i_pcr = GetQWBE( &p_peek[8] ); - p_es = p_input->stream.p_selected_program->pp_es[i_stream]; - if( !p_es ) + if( ( p_block = stream_Block( p_input->s, 16 + i_size ) ) == NULL ) { - msg_Err( p_input, "cannot find ES" ); + msg_Warn( p_input, "cannot read data" ); + return 0; + } + + p_block->p_buffer += 16; + p_block->i_buffer -= 16; + + /* Call the pace control. */ + input_ClockManageRef( p_input, p_input->stream.p_selected_program, i_pcr ); + + p_block->i_dts = + p_block->i_pts = i_pcr <= 0 ? 0 : + input_ClockGetTS( p_input, p_input->stream.p_selected_program, i_pcr ); + + es_out_Send( p_input->p_es_out, p_sys->es[i_es], p_block ); + + return 1; +} + + +/***************************************************************************** + * config variable callback + *****************************************************************************/ +static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, + vlc_value_t newval, vlc_value_t oldval, void * ) +{ + module_config_t *p_item; + vlc_bool_t b_audio = VLC_FALSE; + int i; + + p_item = config_FindConfig( p_this, psz_name ); + if( !p_item ) return VLC_SUCCESS; + + if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE; + + /* Clear-up the current list */ + if( p_item->i_list ) + { + /* Keep the 2 first entries */ + for( i = 2; i < p_item->i_list; i++ ) + { + free( p_item->ppsz_list[i] ); + free( p_item->ppsz_list_text[i] ); + } + /* TODO: Remove when no more needed */ + p_item->ppsz_list[i] = NULL; + p_item->ppsz_list_text[i] = NULL; + } + p_item->i_list = 2; + + /* Find list of devices */ + list list_devices; + + /* Initialize OLE/COM */ + CoInitialize( 0 ); + + FindCaptureDevice( p_this, NULL, &list_devices, b_audio ); + + /* Uninitialize OLE/COM */ + CoUninitialize(); + + if( !list_devices.size() ) return VLC_SUCCESS; + + p_item->ppsz_list = + (char **)realloc( p_item->ppsz_list, + (list_devices.size()+3) * sizeof(char *) ); + p_item->ppsz_list_text = + (char **)realloc( p_item->ppsz_list_text, + (list_devices.size()+3) * sizeof(char *) ); + + list::iterator iter; + for( iter = list_devices.begin(), i = 2; iter != list_devices.end(); + iter++, i++ ) + { + p_item->ppsz_list[i] = strdup( iter->c_str() ); + p_item->ppsz_list_text[i] = NULL; + p_item->i_list++; } + p_item->ppsz_list[i] = NULL; + p_item->ppsz_list_text[i] = NULL; + + /* Signal change to the interface */ + p_item->b_dirty = VLC_TRUE; + + return VLC_SUCCESS; +} + +static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name, + vlc_value_t newval, vlc_value_t oldval, void * ) +{ + module_config_t *p_item; + vlc_bool_t b_audio = VLC_FALSE; - p_pes = input_NewPES( p_input->p_method_data ); - if( p_pes == NULL ) + /* Initialize OLE/COM */ + CoInitialize( 0 ); + + p_item = config_FindConfig( p_this, psz_name ); + if( !p_item ) return VLC_SUCCESS; + + if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = VLC_TRUE; + + string devicename; + + if( newval.psz_string && *newval.psz_string ) { - msg_Warn( p_input, "cannot allocate PES" ); - msleep( 1000 ); - return( 1 ); + devicename = newval.psz_string; } - i_size += 16; - while( i_size > 0 ) + else { - data_packet_t *p_data; - int i_read; + /* If no device name was specified, pick the 1st one */ + list list_devices; + + /* Enumerate devices */ + FindCaptureDevice( p_this, NULL, &list_devices, b_audio ); + if( !list_devices.size() ) return VLC_EGENERIC; + devicename = *list_devices.begin(); + } + + IBaseFilter *p_device_filter = + FindCaptureDevice( p_this, &devicename, NULL, b_audio ); + if( p_device_filter ) + { + PropertiesPage( p_this, p_device_filter, NULL, b_audio ); + } + else + { + /* Uninitialize OLE/COM */ + CoUninitialize(); + + msg_Err( p_this, "didn't find device: %s", devicename.c_str() ); + return VLC_EGENERIC; + } + + /* Uninitialize OLE/COM */ + CoUninitialize(); - if( (i_read = input_SplitBuffer( p_input, &p_data, - __MIN( i_size, 10000 ) ) ) <= 0 ) + return VLC_SUCCESS; +} + +static void ShowPropertyPage( IUnknown *obj, CAUUID *cauuid ) +{ + if( cauuid->cElems > 0 ) + { + HWND hwnd_desktop = ::GetDesktopWindow(); + + OleCreatePropertyFrame( hwnd_desktop, 30, 30, NULL, 1, &obj, + cauuid->cElems, cauuid->pElems, 0, 0, NULL ); + + CoTaskMemFree( cauuid->pElems ); + } +} + +static void PropertiesPage( vlc_object_t *p_this, IBaseFilter *p_device_filter, + ICaptureGraphBuilder2 *p_capture_graph, + vlc_bool_t b_audio ) +{ + CAUUID cauuid; + + msg_Dbg( p_this, "Configuring Device Properties" ); + + /* + * Video or audio capture filter page + */ + ISpecifyPropertyPages *p_spec; + + HRESULT hr = p_device_filter->QueryInterface( IID_ISpecifyPropertyPages, + (void **)&p_spec ); + if( SUCCEEDED(hr) ) + { + if( SUCCEEDED(p_spec->GetPages( &cauuid )) ) { - input_DeletePES( p_input->p_method_data, p_pes ); - return( 0 ); + ShowPropertyPage( p_device_filter, &cauuid ); } - if( !p_pes->p_first ) + p_spec->Release(); + } + + msg_Dbg( p_this, "looking for WDM Configuration Pages" ); + + if( p_capture_graph ) + msg_Dbg( p_this, "got capture graph for WDM Configuration Pages" ); + + /* + * Audio capture pin + */ + if( p_capture_graph && b_audio ) + { + IAMStreamConfig *p_SC; + + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Audio, p_device_filter, + IID_IAMStreamConfig, + (void **)&p_SC ); + if( SUCCEEDED(hr) ) { - p_pes->p_first = p_data; - p_pes->i_nb_data = 1; - p_pes->i_pes_size = i_read; + hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages, + (void **)&p_spec ); + if( SUCCEEDED(hr) ) + { + hr = p_spec->GetPages( &cauuid ); + if( SUCCEEDED(hr) ) + { + for( unsigned int c = 0; c < cauuid.cElems; c++ ) + { + ShowPropertyPage( p_SC, &cauuid ); + } + CoTaskMemFree( cauuid.pElems ); + } + p_spec->Release(); + } + p_SC->Release(); } - else + + /* + * TV Audio filter + */ + IAMTVAudio *p_TVA; + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Audio, p_device_filter, + IID_IAMTVAudio, (void **)&p_TVA ); + if( SUCCEEDED(hr) ) { - p_pes->p_last->p_next = p_data; - p_pes->i_nb_data++; - p_pes->i_pes_size += i_read; + hr = p_TVA->QueryInterface( IID_ISpecifyPropertyPages, + (void **)&p_spec ); + if( SUCCEEDED(hr) ) + { + if( SUCCEEDED( p_spec->GetPages( &cauuid ) ) ) + ShowPropertyPage(p_TVA, &cauuid); + + p_spec->Release(); + } + p_TVA->Release(); } - p_pes->p_last = p_data; - i_size -= i_read; } - p_pes->p_first->p_payload_start += 16; - p_pes->i_pes_size -= 16; - - if( p_es && p_es->p_decoder_fifo ) + /* + * Video capture pin + */ + if( p_capture_graph && !b_audio ) { - /* Call the pace control. */ - input_ClockManageRef( p_input, p_input->stream.p_selected_program, - i_pcr ); + IAMStreamConfig *p_SC; + + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Interleaved, + p_device_filter, + IID_IAMStreamConfig, + (void **)&p_SC ); + if( FAILED(hr) ) + { + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Video, + p_device_filter, + IID_IAMStreamConfig, + (void **)&p_SC ); + } - p_pes->i_pts = p_pes->i_dts = i_pcr <= 0 ? 0 : - input_ClockGetTS( p_input, p_input->stream.p_selected_program, - i_pcr ); + if( SUCCEEDED(hr) ) + { + hr = p_SC->QueryInterface( IID_ISpecifyPropertyPages, + (void **)&p_spec ); + if( SUCCEEDED(hr) ) + { + if( SUCCEEDED( p_spec->GetPages(&cauuid) ) ) + { + ShowPropertyPage(p_SC, &cauuid); + } + p_spec->Release(); + } + p_SC->Release(); + } - input_DecodePES( p_es->p_decoder_fifo, p_pes ); - } - else - { - input_DeletePES( p_input->p_method_data, p_pes ); - } + /* + * Video crossbar, and a possible second crossbar + */ + IAMCrossbar *p_X, *p_X2; + IBaseFilter *p_XF; + + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Interleaved, + p_device_filter, + IID_IAMCrossbar, (void **)&p_X ); + if( FAILED(hr) ) + { + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Video, + p_device_filter, + IID_IAMCrossbar, + (void **)&p_X ); + } - return 1; + if( SUCCEEDED(hr) ) + { + hr = p_X->QueryInterface( IID_IBaseFilter, (void **)&p_XF ); + if( SUCCEEDED(hr) ) + { + hr = p_X->QueryInterface( IID_ISpecifyPropertyPages, + (void **)&p_spec ); + if( SUCCEEDED(hr) ) + { + hr = p_spec->GetPages(&cauuid); + if( hr == S_OK && cauuid.cElems > 0 ) + { + ShowPropertyPage( p_X, &cauuid ); + } + p_spec->Release(); + } + + hr = p_capture_graph->FindInterface( &LOOK_UPSTREAM_ONLY, NULL, + p_XF, IID_IAMCrossbar, + (void **)&p_X2 ); + if( SUCCEEDED(hr) ) + { + hr = p_X2->QueryInterface( IID_ISpecifyPropertyPages, + (void **)&p_spec ); + if( SUCCEEDED(hr) ) + { + hr = p_spec->GetPages( &cauuid ); + if( SUCCEEDED(hr) ) + { + ShowPropertyPage( p_X2, &cauuid ); + } + p_spec->Release(); + } + p_X2->Release(); + } + + p_XF->Release(); + } + + p_X->Release(); + } + + /* + * TV Tuner + */ + IAMTVTuner *p_TV; + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Interleaved, + p_device_filter, + IID_IAMTVTuner, (void **)&p_TV ); + if( FAILED(hr) ) + { + hr = p_capture_graph->FindInterface( &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Video, + p_device_filter, + IID_IAMTVTuner, + (void **)&p_TV ); + } + + if( SUCCEEDED(hr) ) + { + hr = p_TV->QueryInterface( IID_ISpecifyPropertyPages, + (void **)&p_spec ); + if( SUCCEEDED(hr) ) + { + hr = p_spec->GetPages(&cauuid); + if( SUCCEEDED(hr) ) + { + ShowPropertyPage(p_TV, &cauuid); + } + p_spec->Release(); + } + p_TV->Release(); + } + } }