Commit b34227c1 authored by Gildas Bazin's avatar Gildas Bazin

* configure.ac, modules/access/dshow/: brand new DirectShow input plugin.
   Much work still needs to be done, like audio support, adding plenty of configuration options, etc... But the video part is already working quite well here.
parent 026eb767
dnl Autoconf settings for vlc
dnl $Id: configure.ac,v 1.67 2003/08/23 22:49:50 fenrir Exp $
dnl $Id: configure.ac,v 1.68 2003/08/24 11:17:39 gbazin Exp $
AC_INIT(vlc,0.6.3-cvs)
......@@ -1212,6 +1212,22 @@ then
fi
fi
dnl
dnl Windows DirectShow access module
dnl
AC_ARG_ENABLE(dshow,
[ --enable-dshow Win32 DirectShow support (default enabled on Win32)])
if test "${enable_dshow}" != "no"
then
if test "${SYS}" = "mingw32" -o "${SYS}" = "cygwin"
then
AC_CHECK_HEADERS(dshow.h,
[ AX_ADD_PLUGINS([dshow])
AX_ADD_CXXFLAGS([dshow],[])
AX_ADD_LDFLAGS([dshow],[-lole32 -loleaut32]) ])
fi
fi
dnl
dnl libdvbpsi ts demux/mux
dnl
......@@ -3261,6 +3277,7 @@ AC_OUTPUT([
src/Makefile
modules/access/Makefile
modules/access/dshow/Makefile
modules/access/dvb/Makefile
modules/access/dvd/Makefile
modules/access/dvdplay/Makefile
......
.deps
.dirstamp
*.lo
*.la
*.dll
*.dylib
*.sl
*.so
Makefile.am
Makefile.in
Makefile
SOURCES_dshow = dshow.cpp filter.cpp filter.h
/*****************************************************************************
* dshow.c : DirectShow access module for vlc
*****************************************************************************
* Copyright (C) 2002 VideoLAN
* $Id: dshow.cpp,v 1.1 2003/08/24 11:17:39 gbazin Exp $
*
* Author: Gildas Bazin <gbazin@netcourrier.com>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/vout.h>
#ifndef _MSC_VER
# include <wtypes.h>
# include <unknwn.h>
# include <ole2.h>
# include <limits.h>
# define _WINGDI_ 1
# define AM_NOVTABLE
# define _OBJBASE_H_
# undef _X86_
# define _I64_MAX LONG_LONG_MAX
# define LONGLONG long long
#endif
#include <dshow.h>
#include "filter.h"
/*****************************************************************************
* 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 int OpenDevice( input_thread_t *, string );
static IBaseFilter *FindCaptureDevice( vlc_object_t *, string *,
list<string> * );
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", 10 );
set_callbacks( AccessOpen, AccessClose );
add_submodule();
set_description( _("DirectShow demuxer") );
add_shortcut( "dshow" );
set_capability( "demux", 200 );
set_callbacks( DemuxOpen, DemuxClose );
vlc_module_end();
/****************************************************************************
* I. Access Part
****************************************************************************/
/*
* header:
* fcc ".dsh"
* u32 stream count
* fcc "auds"|"vids" 0
* fcc codec 4
* if vids
* u32 width 8
* u32 height 12
* u32 padding 16
* if auds
* u32 channels 12
* u32 samplerate 8
* u32 samplesize 16
*
* data:
* u32 stream number
* u32 data size
* u8 data
*/
static void SetDWBE( uint8_t *p, uint32_t dw )
{
p[0] = (dw >> 24)&0xff;
p[1] = (dw >> 16)&0xff;
p[2] = (dw >> 8)&0xff;
p[3] = (dw )&0xff;
}
static void SetQWBE( uint8_t *p, uint64_t qw )
{
SetDWBE( p, (qw >> 32)&0xffffffff );
SetDWBE( &p[4], qw&0xffffffff );
}
/****************************************************************************
* Access descriptor declaration
****************************************************************************/
struct access_sys_t
{
IFilterGraph *p_graph;
IBaseFilter *p_device_filter;
CaptureFilter *p_capture_filter;
IMediaControl *p_control;
AM_MEDIA_TYPE mt;
/* video */
char *psz_video_device;
int i_fourcc;
int i_width;
int i_height;
VIDEOINFOHEADER vid_header;
/* audio */
/* header */
int i_header_size;
int i_header_pos;
uint8_t *p_header; // at lest 8 bytes allocated
/* data */
int i_data_size;
int i_data_pos;
uint8_t *p_data;
VLCMediaSample sample;
};
/*****************************************************************************
* Open: open direct show device
*****************************************************************************/
static int AccessOpen( vlc_object_t *p_this )
{
input_thread_t *p_input = (input_thread_t *)p_this;
access_sys_t *p_sys;
#if 0
/* parse url and open device(s) */
char *psz_dup, *psz_parser;
psz_dup = strdup( p_input->psz_name );
psz_parser = psz_dup;
while( *psz_parser && *psz_parser != ':' )
{
psz_parser++;
}
#endif
p_input->pf_read = Read;
p_input->pf_seek = NULL;
p_input->pf_set_area = NULL;
p_input->pf_set_program = NULL;
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.b_pace_control = 0;
p_input->stream.b_seekable = 0;
p_input->stream.p_selected_area->i_size = 0;
p_input->stream.p_selected_area->i_tell = 0;
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;
/* Initialize OLE/COM */
CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
/* 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->psz_video_device = NULL;
p_sys->sample.p_sample = NULL;
p_sys->i_data_size = 0;
p_sys->i_data_pos = 0;
if( OpenDevice( p_input, p_input->psz_name ) != VLC_SUCCESS )
{
/* Uninitialize OLE/COM */
CoUninitialize();
free( p_sys );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* AccessClose: close device
*****************************************************************************/
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;
/* 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 );
/* Release objects */
//p_sys->p_device_filter->Release();
//p_sys->p_capture_filter->Release();
//p_sys->p_graph->Release();
/* Uninitialize OLE/COM */
CoUninitialize();
free( p_sys );
}
/****************************************************************************
* ConnectFilters
****************************************************************************/
static bool ConnectFilters( IFilterGraph *p_graph, IBaseFilter *p_filter,
IPin *p_input_pin )
{
IEnumPins *p_enumpins;
IPin *p_output_pin;
ULONG i_fetched;
if( S_OK != p_filter->EnumPins( &p_enumpins ) ) return false;
while( S_OK == p_enumpins->Next( 1, &p_output_pin, &i_fetched ) )
{
if( S_OK == p_graph->ConnectDirect( p_output_pin, p_input_pin, 0 ) )
{
p_enumpins->Release();
return true;
}
}
p_enumpins->Release();
return false;
}
static int OpenDevice( input_thread_t *p_input, string devicename )
{
access_sys_t *p_sys = p_input->p_access_data;
list<string> list_devices;
#if 1
// Enum devices and display their names
FindCaptureDevice( (vlc_object_t *)p_input, NULL, &list_devices );
list<string>::iterator iter;
for( iter = list_devices.begin(); iter != list_devices.end(); iter++ )
msg_Err( p_input, "found device: %s", iter->c_str() );
#endif
/* 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 video capture/preview device, such as a desktop USB video camera.
p_sys->p_device_filter =
FindCaptureDevice( (vlc_object_t *)p_input, &devicename, NULL );
if( p_sys->p_device_filter )
msg_Dbg( p_input, "found device: %s", devicename.c_str() );
/* Build graph */
CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,
(REFIID)IID_IFilterGraph, (void **)&p_sys->p_graph );
p_sys->p_graph->QueryInterface( IID_IMediaControl,
(void **)&p_sys->p_control );
/* Create and add our capture filter */
p_sys->p_capture_filter = new CaptureFilter( p_input );
p_sys->p_graph->AddFilter( p_sys->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_sys->p_device_filter, 0 );
// Attempt to connect one of this device's capture output pins
msg_Dbg( p_input, "connecting filters" );
if( ConnectFilters( p_sys->p_graph, p_sys->p_device_filter,
p_sys->p_capture_filter->CustomGetPin() ) )
{
/* Success */
int i_fourcc = VLC_FOURCC( ' ', ' ', ' ', ' ' );
AM_MEDIA_TYPE *pmt = &p_sys->mt;
p_sys->mt = p_sys->p_capture_filter->CustomGetPin()->
CustomGetMediaType();
if( pmt->majortype == MEDIATYPE_Video )
{
if( pmt->subtype == MEDIASUBTYPE_RGB8 )
{
i_fourcc = VLC_FOURCC( 'G', 'R', 'E', 'Y' );
}
else if( pmt->subtype == MEDIASUBTYPE_RGB555 )
{
i_fourcc = VLC_FOURCC( 'R', 'V', '1', '5' );
}
else if( pmt->subtype == MEDIASUBTYPE_RGB565 )
{
i_fourcc = VLC_FOURCC( 'R', 'V', '1', '6' );
}
else if( pmt->subtype == MEDIASUBTYPE_RGB24 )
{
i_fourcc = VLC_FOURCC( 'R', 'V', '2', '4' );
}
else if( pmt->subtype == MEDIASUBTYPE_RGB32 )
{
i_fourcc = VLC_FOURCC( 'R', 'V', '3', '2' );
}
else if( pmt->subtype == MEDIASUBTYPE_ARGB32 )
{
i_fourcc = VLC_FOURCC( 'R', 'G', 'B', 'A' );
}
else if( pmt->subtype == MEDIASUBTYPE_YUYV )
{
i_fourcc = VLC_FOURCC( 'Y', 'U', 'Y', 'V' );
}
else if( pmt->subtype == MEDIASUBTYPE_Y411 )
{
i_fourcc = VLC_FOURCC( 'I', '4', '1', 'N' );
}
else if( pmt->subtype == MEDIASUBTYPE_Y41P )
{
i_fourcc = VLC_FOURCC( 'I', '4', '1', '1' );
}
else if( pmt->subtype == MEDIASUBTYPE_YUY2 )
{
i_fourcc = VLC_FOURCC( 'Y', 'U', 'Y', '2' );
}
else if( pmt->subtype == MEDIASUBTYPE_YVYU )
{
i_fourcc = VLC_FOURCC( 'Y', 'V', 'Y', 'U' );
}
else if( pmt->subtype == MEDIASUBTYPE_Y411 )
{
i_fourcc = VLC_FOURCC( 'I', '4', '1', 'N' );
}
else if( pmt->subtype == MEDIASUBTYPE_YV12 )
{
i_fourcc = VLC_FOURCC( 'Y', 'V', '1', '2' );
}
p_sys->i_fourcc = i_fourcc;
p_sys->vid_header = *(VIDEOINFOHEADER *)pmt->pbFormat;
}
/* create header */
p_sys->i_header_size = 8 + 20;
p_sys->i_header_pos = 0;
p_sys->p_header = (uint8_t *)malloc( p_sys->i_header_size );
memcpy( &p_sys->p_header[0], ".dsh", 4 );
SetDWBE( &p_sys->p_header[4], 1 );
memcpy( &p_sys->p_header[ 8], "vids", 4 );
memcpy( &p_sys->p_header[12], &i_fourcc, 4 );
SetDWBE( &p_sys->p_header[16], p_sys->vid_header.bmiHeader.biWidth );
SetDWBE( &p_sys->p_header[20], p_sys->vid_header.bmiHeader.biHeight );
SetDWBE( &p_sys->p_header[24], 0 );
p_sys->p_control->Run();
// We're done
return VLC_SUCCESS;
}
/* Remove filters from graph */
p_sys->p_graph->RemoveFilter( p_sys->p_device_filter );
p_sys->p_graph->RemoveFilter( p_sys->p_capture_filter );
/* Release objects */
p_sys->p_device_filter->Release();
p_sys->p_capture_filter->Release();
p_sys->p_control->Release();
p_sys->p_graph->Release();
return VLC_EGENERIC;
}
static IBaseFilter *
FindCaptureDevice( vlc_object_t *p_this, string *p_devicename,
list<string> *p_listdevices )
{
IBaseFilter *pBaseFilter = NULL;
IMoniker *pMoniker = NULL;
ULONG lFetched;
HRESULT hr;
/* Create the system device enumerator */
ICreateDevEnum *pDevEnum = NULL;
hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
IID_ICreateDevEnum, (void **)&pDevEnum );
if( FAILED(hr) )
{
msg_Err( p_this, "failed to create the device enumerator (0x%x)", hr);
return NULL;
}
/* Create an enumerator for the video capture devices */
IEnumMoniker *pClassEnum = NULL;
hr = pDevEnum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory,
&pClassEnum, 0 );
if( FAILED(hr) )
{
msg_Err( p_this, "failed to create the class enumerator (0x%x)", hr );
return NULL;
}
/* If there are no enumerators for the requested type, then
* CreateClassEnumerator will succeed, but pClassEnum will be NULL */
if( pClassEnum == NULL )
{
msg_Err( p_this, "no video capture device was detected." );
return NULL;
}
/* Enumerate the devices */
/* Note that if the Next() call succeeds but there are no monikers,
* it will return S_FALSE (which is not a failure). Therefore, we check
* that the return code is S_OK instead of using SUCCEEDED() macro. */
while( pClassEnum->Next( 1, &pMoniker, &lFetched ) == S_OK )
{
/* Getting the property page to get the device name */
IPropertyBag *pBag;
hr = pMoniker->BindToStorage( 0, 0, IID_IPropertyBag,
reinterpret_cast<PVOID *>( &pBag ) );
if( SUCCEEDED(hr) )
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read( L"FriendlyName", &var, NULL );
pBag->Release();
if( SUCCEEDED(hr) )
{
int i_convert = ( lstrlenW( var.bstrVal ) + 1 ) * 2;
char *p_buf = (char *)alloca( i_convert ); p_buf[0] = 0;
WideCharToMultiByte( CP_ACP, 0, var.bstrVal, -1, p_buf,
i_convert, NULL, NULL );
SysFreeString(var.bstrVal);
if( p_listdevices ) p_listdevices->push_back( p_buf );
if( p_devicename && *p_devicename == string(p_buf) )
{
/* Bind Moniker to a filter object */
hr = pMoniker->BindToObject( 0, 0, IID_IBaseFilter,
reinterpret_cast<LPVOID *>(&pBaseFilter) );
if( FAILED(hr) )
{
msg_Err( p_this, "couldn't bind moniker to filter "
"object (0x%x)", hr );
return NULL;
}
return pBaseFilter;
}
}
}
}
return NULL;
}
/*****************************************************************************
* 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 )
{
access_sys_t *p_sys = p_input->p_access_data;
int i_data = 0;
#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
while( i_len > 0 )
{
/* First copy header if any */
if( i_len > 0 && p_sys->i_header_pos < p_sys->i_header_size )
{
int i_copy;
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;
p_buffer += i_copy;
i_len -= i_copy;
i_data += i_copy;
}
/* Then copy data */
if( i_len > 0 && p_sys->i_data_pos < p_sys->i_data_size )
{
int i_copy;
i_copy = __MIN( p_sys->i_data_size -
p_sys->i_data_pos, (int)i_len );
memcpy( p_buffer, &p_sys->p_data[p_sys->i_data_pos], i_copy );
p_sys->i_data_pos += i_copy;
p_buffer += i_copy;
i_len -= i_copy;
i_data += i_copy;
}
/* The caller got what he wanted */
if( i_len <= 0 )
{
return i_data;
}
/* Read no more than one frame at a time, otherwise we kill latency */
if( p_sys->i_data_size && i_data &&
p_sys->i_data_pos == p_sys->i_data_size )
{
p_sys->i_data_pos = 0; p_sys->i_data_size = 0;
return i_data;
}
/* Start new audio/video frame */
if( p_sys->sample.p_sample )
{
//p_sys->sample.p_sample->Release();
}
if( p_sys->p_capture_filter->CustomGetPin()
->CustomGetSample( &p_sys->sample ) == S_OK )
{
p_sys->i_data_pos = 0;
p_sys->i_data_size = p_sys->sample.p_sample->GetActualDataLength();
p_sys->sample.p_sample->GetPointer( &p_sys->p_data );
REFERENCE_TIME i_pts, i_end_date;
HRESULT hr = p_sys->sample.p_sample
->GetTime( &i_pts, &i_end_date );
if( hr != VFW_S_NO_STOP_TIME && hr != S_OK ) i_pts = 0