* configure.ac, modules/access/dshow/: brand new DirectShow input plugin.
authorGildas Bazin <gbazin@videolan.org>
Sun, 24 Aug 2003 11:17:39 +0000 (11:17 +0000)
committerGildas Bazin <gbazin@videolan.org>
Sun, 24 Aug 2003 11:17:39 +0000 (11:17 +0000)
   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.

configure.ac
modules/access/dshow/.cvsignore [new file with mode: 0644]
modules/access/dshow/Modules.am [new file with mode: 0644]
modules/access/dshow/dshow.cpp [new file with mode: 0644]
modules/access/dshow/filter.cpp [new file with mode: 0644]
modules/access/dshow/filter.h [new file with mode: 0644]

index 05c12f3..efd8c03 100644 (file)
@@ -1,5 +1,5 @@
 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
diff --git a/modules/access/dshow/.cvsignore b/modules/access/dshow/.cvsignore
new file mode 100644 (file)
index 0000000..c930ed7
--- /dev/null
@@ -0,0 +1,11 @@
+.deps
+.dirstamp
+*.lo
+*.la
+*.dll
+*.dylib
+*.sl
+*.so
+Makefile.am
+Makefile.in
+Makefile
diff --git a/modules/access/dshow/Modules.am b/modules/access/dshow/Modules.am
new file mode 100644 (file)
index 0000000..bf878b2
--- /dev/null
@@ -0,0 +1 @@
+SOURCES_dshow = dshow.cpp filter.cpp filter.h
diff --git a/modules/access/dshow/dshow.cpp b/modules/access/dshow/dshow.cpp
new file mode 100644 (file)
index 0000000..7b73d89
--- /dev/null
@@ -0,0 +1,842 @@
+/*****************************************************************************
+ * 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;
+
+            if( !i_pts )
+            {
+                /* Use our data timestamp */
+                i_pts = p_sys->sample.i_timestamp;
+            }
+
+#if 0
+            msg_Dbg( p_input, "Read() PTS: "I64Fd, i_pts );
+#endif
+
+            /* create pseudo header */
+            p_sys->i_header_size = 16;
+            p_sys->i_header_pos  = 0;
+            SetDWBE( &p_sys->p_header[0], 0 /*i_stream*/ );
+            SetDWBE( &p_sys->p_header[4], p_sys->i_data_size );
+            SetQWBE( &p_sys->p_header[8], i_pts  * 9 / 1000 );
+        }
+        else msleep( 10000 );
+    }
+
+    return i_data;
+}
+
+/****************************************************************************
+ * I. Demux Part
+ ****************************************************************************/
+static int DemuxOpen( vlc_object_t *p_this )
+{
+    input_thread_t *p_input = (input_thread_t *)p_this;
+
+    uint8_t        *p_peek;
+    int            i_streams;
+    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 v4l stream */
+    if( input_Peek( p_input, &p_peek, 8 ) < 8 )
+    {
+        msg_Warn( p_input, "v4l plugin discarded (cannot peek)" );
+        return( VLC_EGENERIC );
+    }
+
+    if( strcmp( (const char *)p_peek, ".dsh" ) ||
+        GetDWBE( &p_peek[4] ) <= 0 )
+    {
+        msg_Warn( p_input, "v4l 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 );
+    }
+
+    p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
+    p_input->stream.i_mux_rate =  0;
+
+    i_streams = GetDWBE( &p_peek[4] );
+    if( input_Peek( p_input, &p_peek, 8 + 20 * i_streams )
+        < 8 + 20 * i_streams )
+    {
+        msg_Err( p_input, "v4l plugin discarded (cannot peek)" );
+        return( VLC_EGENERIC );
+    }
+    p_peek += 8;
+
+    for( i = 0; i < i_streams; i++ )
+    {
+        es_descriptor_t *p_es;
+
+        if( !strncmp( (const char *)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
+        }
+        else if( !strncmp( (const char *)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;
+
+            msg_Dbg( p_input, "added new video es %4.4s %dx%d",
+                     (char*)&p_es->i_fourcc, bih->biWidth, bih->biHeight );
+
+            input_SelectES( p_input, p_es );
+#undef bih
+        }
+
+        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 );
+    }
+
+    p_input->pf_demux = Demux;
+    return VLC_SUCCESS;
+}
+
+static void DemuxClose( vlc_object_t *p_this )
+{
+    return;
+}
+
+static int Demux( input_thread_t *p_input )
+{
+    es_descriptor_t *p_es;
+    pes_packet_t    *p_pes;
+
+    int i_stream;
+    int i_size;
+    uint8_t *p_peek;
+    mtime_t i_pcr;
+
+    if( input_Peek( p_input, &p_peek, 16 ) < 16 )
+    {
+        msg_Warn( p_input, "cannot peek (EOF ?)" );
+        return( 0 );
+    }
+
+    i_stream = GetDWBE( &p_peek[0] );
+    i_size   = GetDWBE( &p_peek[4] );
+    i_pcr    = GetQWBE( &p_peek[8] );
+
+    //msg_Dbg( p_input, "stream=%d size=%d", i_stream, i_size );
+    //p_es = input_FindES( p_input, i_stream );
+
+    p_es = p_input->stream.p_selected_program->pp_es[i_stream];
+    if( !p_es )
+    {
+        msg_Err( p_input, "cannot find ES" );
+    }
+
+    p_pes = input_NewPES( p_input->p_method_data );
+    if( p_pes == NULL )
+    {
+        msg_Warn( p_input, "cannot allocate PES" );
+        msleep( 1000 );
+        return( 1 );
+    }
+    i_size += 16;
+    while( i_size > 0 )
+    {
+        data_packet_t   *p_data;
+        int i_read;
+
+        if( (i_read = input_SplitBuffer( p_input, &p_data,
+                                         __MIN( i_size, 10000 ) ) ) <= 0 )
+        {
+            input_DeletePES( p_input->p_method_data, p_pes );
+            return( 0 );
+        }
+        if( !p_pes->p_first )
+        {
+            p_pes->p_first = p_data;
+            p_pes->i_nb_data = 1;
+            p_pes->i_pes_size = i_read;
+        }
+        else
+        {
+            p_pes->p_last->p_next  = p_data;
+            p_pes->i_nb_data++;
+            p_pes->i_pes_size += i_read;
+        }
+        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 )
+    {
+        /* Call the pace control. */
+        input_ClockManageRef( p_input, p_input->stream.p_selected_program,
+                              i_pcr );
+
+        p_pes->i_pts = p_pes->i_dts = i_pcr <= 0 ? 0 :
+            input_ClockGetTS( p_input, p_input->stream.p_selected_program,
+                              i_pcr );
+
+        input_DecodePES( p_es->p_decoder_fifo, p_pes );
+    }
+    else
+    {
+        input_DeletePES( p_input->p_method_data, p_pes );
+    }
+
+    return 1;
+}
diff --git a/modules/access/dshow/filter.cpp b/modules/access/dshow/filter.cpp
new file mode 100644 (file)
index 0000000..fcfb55d
--- /dev/null
@@ -0,0 +1,858 @@
+/*****************************************************************************
+ * filter.c : DirectShow access module for vlc
+ *****************************************************************************
+ * Copyright (C) 2002 VideoLAN
+ * $Id: filter.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"
+
+#define DEBUG_DSHOW 1
+
+/*****************************************************************************
+ * DirectShow GUIDs.
+ * Easier to define them hear as mingw doesn't provide them all.
+ *****************************************************************************/
+const GUID CLSID_SystemDeviceEnum = {0x62be5d10, 0x60eb, 0x11d0, {0xbd, 0x3b, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86}};
+const GUID CLSID_VideoInputDeviceCategory = {0x860BB310,0x5D01,0x11d0,{0xBD,0x3B,0x00,0xA0,0xC9,0x11,0xCE,0x86}};
+const GUID IID_IPropertyBag = {0x55272A00, 0x42CB, 0x11CE, {0x81, 0x35, 0x00, 0xAA, 0x00, 0x4B, 0xB8, 0x51}};
+const GUID IID_ICreateDevEnum = {0x29840822, 0x5b84, 0x11d0, {0xbd, 0x3b, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86}};
+const GUID IID_IFilterGraph = {0x56a8689f, 0x0ad4, 0x11ce, {0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID IID_IMediaControl = {0x56a868b1, 0x0ad4, 0x11ce, {0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID CLSID_FilterGraph = {0xe436ebb3, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+
+const GUID IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46}};
+const GUID IID_IPersist = {0x0000010c, 0x0000, 0x0000, {0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46}};
+const GUID IID_IMediaFilter = {0x56a86899, 0x0ad4, 0x11ce, {0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70}};
+const GUID IID_IBaseFilter = {0x56a86895, 0x0ad4, 0x11ce, {0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID IID_IPin = {0x56a86891, 0x0ad4, 0x11ce, {0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70}};
+const GUID IID_IMemInputPin = {0x56a8689d, 0x0ad4, 0x11ce, {0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70}};
+
+const GUID IID_IEnumPins = {0x56a86892, 0x0ad4, 0x11ce, {0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70}};
+const GUID IID_IEnumMediaTypes = {0x89c31040, 0x846b, 0x11ce, {0x97,0xd3, 0x00,0xaa,0x00,0x55,0x59,0x5a}};
+
+/*
+ * MEDIATYPEs and MEDIASUBTYPEs
+ */
+const GUID MEDIATYPE_Video = {0x73646976, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+
+const GUID MEDIASUBTYPE_RGB8 = {0xe436eb7a, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID MEDIASUBTYPE_RGB565 = {0xe436eb7b, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID MEDIASUBTYPE_RGB555 = {0xe436eb7c, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID MEDIASUBTYPE_RGB24 = {0xe436eb7d, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID MEDIASUBTYPE_RGB32 = {0xe436eb7e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID MEDIASUBTYPE_ARGB32 = {0x773c9ac0, 0x3274, 0x11d0, {0xb7, 0x24, 0x0, 0xaa, 0x0, 0x6c, 0x1a, 0x1}};
+
+const GUID MEDIASUBTYPE_YUYV = {0x56595559, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID MEDIASUBTYPE_Y411 = {0x31313459, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID MEDIASUBTYPE_Y41P = {0x50313459, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID MEDIASUBTYPE_YUY2 = {0x32595559, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID MEDIASUBTYPE_YVYU = {0x55595659, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID MEDIASUBTYPE_UYVY = {0x59565955, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID MEDIASUBTYPE_Y211 = {0x31313259, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID MEDIASUBTYPE_YV12 = {0x32315659, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+
+void WINAPI FreeMediaType( AM_MEDIA_TYPE& mt )
+{
+    if( mt.cbFormat != 0 )
+    {
+        CoTaskMemFree( (PVOID)mt.pbFormat );
+        mt.cbFormat = 0;
+        mt.pbFormat = NULL;
+    }
+    if( mt.pUnk != NULL )
+    {
+        mt.pUnk->Release();
+        mt.pUnk = NULL;
+    }
+}
+
+HRESULT WINAPI CopyMediaType( AM_MEDIA_TYPE *pmtTarget,
+                              const AM_MEDIA_TYPE *pmtSource )
+{
+    *pmtTarget = *pmtSource;
+    if( pmtSource->cbFormat != 0 )
+    {
+        pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc( pmtSource->cbFormat );
+        if( pmtTarget->pbFormat == NULL )
+        {
+            pmtTarget->cbFormat = 0;
+            return E_OUTOFMEMORY;
+        }
+        else
+        {
+            CopyMemory( (PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,
+                        pmtTarget->cbFormat );
+        }
+    }
+    if( pmtTarget->pUnk != NULL )
+    {
+        pmtTarget->pUnk->AddRef();
+    }
+
+    return S_OK;
+}
+
+/****************************************************************************
+ * Implementation of our dummy directshow filter pin class
+ ****************************************************************************/
+
+CapturePin::CapturePin( input_thread_t * _p_input, CaptureFilter *_p_filter )
+  : p_input( _p_input ), p_filter( _p_filter ), p_connected_pin( NULL ),
+    i_ref( 1 )
+{
+}
+
+CapturePin::~CapturePin()
+{
+}
+
+HRESULT CapturePin::CustomGetSample( VLCMediaSample *vlc_sample )
+{
+    if( samples_queue.size() )
+    {
+        *vlc_sample = samples_queue.back();
+        samples_queue.pop_back();
+        return S_OK;
+    }
+    return S_FALSE;
+}
+
+AM_MEDIA_TYPE CapturePin::CustomGetMediaType()
+{
+    return media_type;
+}
+
+/* IUnknown methods */
+STDMETHODIMP CapturePin::QueryInterface(REFIID riid, void **ppv)
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::QueryInterface" );
+#endif
+
+    if( riid == IID_IUnknown ||
+        riid == IID_IPin )
+    {
+        *ppv = (IPin *)this;
+        return NOERROR;
+    }
+    if( riid == IID_IMemInputPin )
+    {
+        *ppv = (IMemInputPin *)this;
+        return NOERROR;
+    }
+    else
+    {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+}
+STDMETHODIMP_(ULONG) CapturePin::AddRef()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::AddRef" );
+#endif
+
+    i_ref++;
+    return NOERROR;
+};
+STDMETHODIMP_(ULONG) CapturePin::Release()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::Release" );
+#endif
+
+    i_ref--;
+    if( !i_ref ) delete this;
+
+    return NOERROR;
+};
+
+/* IPin methods */
+STDMETHODIMP CapturePin::Connect( IPin * pReceivePin,
+                                  const AM_MEDIA_TYPE *pmt )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::Connect" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::ReceiveConnection( IPin * pConnector,
+                                            const AM_MEDIA_TYPE *pmt )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::ReceiveConnection" );
+#endif
+
+    p_connected_pin = pConnector;
+    p_connected_pin->AddRef();
+    return CopyMediaType( &media_type, pmt );
+}
+STDMETHODIMP CapturePin::Disconnect()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::Disconnect" );
+#endif
+
+    p_connected_pin->Release();
+    p_connected_pin = NULL;
+    FreeMediaType( media_type );
+    return S_OK;
+}
+STDMETHODIMP CapturePin::ConnectedTo( IPin **pPin )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::ConnectedTo" );
+#endif
+
+    p_connected_pin->AddRef();
+    *pPin = p_connected_pin;
+
+    return S_OK;
+}
+STDMETHODIMP CapturePin::ConnectionMediaType( AM_MEDIA_TYPE *pmt )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::ConnectionMediaType" );
+#endif
+
+    return CopyMediaType( pmt, &media_type );
+}
+STDMETHODIMP CapturePin::QueryPinInfo( PIN_INFO * pInfo )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::QueryPinInfo" );
+#endif
+
+    pInfo->pFilter = p_filter;
+    if( p_filter ) p_filter->AddRef();
+
+    pInfo->achName[0] = L'\0';
+
+    pInfo->dir = PINDIR_INPUT;
+
+    return NOERROR;
+}
+STDMETHODIMP CapturePin::QueryDirection( PIN_DIRECTION * pPinDir )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::QueryDirection" );
+#endif
+
+    *pPinDir = PINDIR_INPUT;
+    return NOERROR;
+}
+STDMETHODIMP CapturePin::QueryId( LPWSTR * Id )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::QueryId" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::QueryAccept( const AM_MEDIA_TYPE *pmt )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::QueryAccept" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::EnumMediaTypes( IEnumMediaTypes **ppEnum )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::EnumMediaTypes" );
+#endif
+
+    *ppEnum = new CaptureEnumMediaTypes( p_input, this, NULL );
+
+    if( *ppEnum == NULL ) return E_OUTOFMEMORY;
+
+    return NOERROR;
+}
+STDMETHODIMP CapturePin::QueryInternalConnections( IPin* *apPin, ULONG *nPin )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::QueryInternalConnections" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::EndOfStream( void )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::EndOfStream" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::BeginFlush( void )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::BeginFlush" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::EndFlush( void )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::EndFlush" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::NewSegment( REFERENCE_TIME tStart,
+                                     REFERENCE_TIME tStop,
+                                     double dRate )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::NewSegment" );
+#endif
+    return E_NOTIMPL;
+}
+
+/* IMemInputPin methods */
+STDMETHODIMP CapturePin::GetAllocator( IMemAllocator **ppAllocator )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::GetAllocator" );
+#endif
+
+    return VFW_E_NO_ALLOCATOR;
+}
+STDMETHODIMP CapturePin::NotifyAllocator( IMemAllocator *pAllocator,
+                                          BOOL bReadOnly )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::NotifyAllocator" );
+#endif
+
+    return S_OK;
+}
+STDMETHODIMP CapturePin::GetAllocatorRequirements( ALLOCATOR_PROPERTIES *pProps )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::GetAllocatorRequirements" );
+#endif
+    return E_NOTIMPL;
+}
+STDMETHODIMP CapturePin::Receive( IMediaSample *pSample )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::Receive" );
+#endif
+
+    //pSample->AddRef();
+    mtime_t i_timestamp = mdate() * 10;
+    VLCMediaSample vlc_sample = {pSample, i_timestamp};
+    samples_queue.push_front( vlc_sample );
+
+    /* Make sure we don't cache too many samples */
+    if( samples_queue.size() > 10 )
+    {
+        vlc_sample = samples_queue.back();
+        samples_queue.pop_back();
+        msg_Dbg( p_input, "CapturePin::Receive trashing late input sample" );
+        // vlc_sample.p_sample->Release();
+    }
+
+    return S_OK;
+}
+STDMETHODIMP CapturePin::ReceiveMultiple( IMediaSample **pSamples,
+                                          long nSamples,
+                                          long *nSamplesProcessed )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::ReceiveMultiple" );
+#endif
+
+    HRESULT hr = S_OK;
+
+    *nSamplesProcessed = 0;
+    while( nSamples-- > 0 )
+    {
+         hr = Receive( pSamples[*nSamplesProcessed] );
+         if( hr != S_OK ) break;
+         (*nSamplesProcessed)++;
+    }
+    return hr;
+}
+STDMETHODIMP CapturePin::ReceiveCanBlock( void )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CapturePin::ReceiveCanBlock" );
+#endif
+
+    return S_FALSE; /* Thou shalt not block */
+}
+
+/****************************************************************************
+ * Implementation of our dummy directshow filter class
+ ****************************************************************************/
+
+CaptureFilter::CaptureFilter( input_thread_t * _p_input )
+  : p_input( _p_input ), p_pin( new CapturePin( _p_input, this ) ),
+    i_ref( 1 )
+{
+}
+
+CaptureFilter::~CaptureFilter()
+{
+    p_pin->Release();
+}
+
+/* IUnknown methods */
+STDMETHODIMP CaptureFilter::QueryInterface( REFIID riid, void **ppv )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::QueryInterface" );
+#endif
+
+    if( riid == IID_IUnknown )
+    {
+        *ppv = (IUnknown *)this;
+        return NOERROR;
+    }
+    if( riid == IID_IPersist )
+    {
+        *ppv = (IPersist *)this;
+        return NOERROR;
+    }
+    if( riid == IID_IMediaFilter )
+    {
+        *ppv = (IMediaFilter *)this;
+        return NOERROR;
+    }
+    if( riid == IID_IBaseFilter )
+    {
+        *ppv = (IBaseFilter *)this;
+        return NOERROR;
+    }
+    else
+    {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+};
+STDMETHODIMP_(ULONG) CaptureFilter::AddRef()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::AddRef" );
+#endif
+
+    i_ref++;
+    return NOERROR;
+};
+STDMETHODIMP_(ULONG) CaptureFilter::Release()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::Release" );
+#endif
+
+    i_ref--;
+    if( !i_ref ) delete this;
+
+    return NOERROR;
+};
+
+/* IPersist method */
+STDMETHODIMP CaptureFilter::GetClassID(CLSID *pClsID)
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::GetClassID" );
+#endif
+    return E_NOTIMPL;
+};
+
+/* IMediaFilter methods */
+STDMETHODIMP CaptureFilter::GetState(DWORD dwMSecs, FILTER_STATE *State)
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::GetStat" );
+#endif
+    return E_NOTIMPL;
+};
+STDMETHODIMP CaptureFilter::SetSyncSource(IReferenceClock *pClock)
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::SetSyncSource" );
+#endif
+
+    return NOERROR;
+};
+STDMETHODIMP CaptureFilter::GetSyncSource(IReferenceClock **pClock)
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::GetSyncSource" );
+#endif
+    return E_NOTIMPL;
+};
+STDMETHODIMP CaptureFilter::Stop()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::Stop" );
+#endif
+    return E_NOTIMPL;
+};
+STDMETHODIMP CaptureFilter::Pause()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::Pause" );
+#endif
+    return E_NOTIMPL;
+};
+STDMETHODIMP CaptureFilter::Run(REFERENCE_TIME tStart)
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::Run" );
+#endif
+    return E_NOTIMPL;
+};
+
+/* IBaseFilter methods */
+STDMETHODIMP CaptureFilter::EnumPins( IEnumPins ** ppEnum )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::EnumPins" );
+#endif
+
+    /* Create a new ref counted enumerator */
+    *ppEnum = new CaptureEnumPins( p_input, this, NULL );
+    return *ppEnum == NULL ? E_OUTOFMEMORY : NOERROR;
+};
+STDMETHODIMP CaptureFilter::FindPin( LPCWSTR Id, IPin ** ppPin )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::FindPin" );
+#endif
+    return E_NOTIMPL;
+};
+STDMETHODIMP CaptureFilter::QueryFilterInfo( FILTER_INFO * pInfo )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::QueryFilterInfo" );
+#endif
+
+    pInfo->achName[0] = L'\0';
+
+    pInfo->pGraph = p_graph;
+    if( p_graph ) p_graph->AddRef();
+
+    return NOERROR;
+};
+STDMETHODIMP CaptureFilter::JoinFilterGraph( IFilterGraph * pGraph,
+                                             LPCWSTR pName )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::JoinFilterGraph" );
+#endif
+
+    p_graph = pGraph;
+
+    return NOERROR;
+};
+STDMETHODIMP CaptureFilter::QueryVendorInfo( LPWSTR* pVendorInfo )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureFilter::QueryVendorInfo" );
+#endif
+    return E_NOTIMPL;
+};
+
+/* Custom methods */
+CapturePin *CaptureFilter::CustomGetPin()
+{
+    return p_pin;
+}
+
+/****************************************************************************
+ * Implementation of our dummy directshow enumpins class
+ ****************************************************************************/
+
+CaptureEnumPins::CaptureEnumPins( input_thread_t * _p_input,
+                                  CaptureFilter *_p_filter,
+                                  CaptureEnumPins *pEnumPins )
+  : p_input( _p_input ), p_filter( _p_filter ), i_ref( 1 )
+{
+    /* Hold a reference count on our filter */
+    p_filter->AddRef();
+
+    /* Are we creating a new enumerator */
+
+    if( pEnumPins == NULL )
+    {
+        i_position = 0;
+    }
+    else
+    {
+        i_position = pEnumPins->i_position;
+    }
+}
+
+CaptureEnumPins::~CaptureEnumPins()
+{
+    p_filter->Release();
+}
+
+/* IUnknown methods */
+STDMETHODIMP CaptureEnumPins::QueryInterface( REFIID riid, void **ppv )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumPins::QueryInterface" );
+#endif
+
+    if( riid == IID_IUnknown ||
+        riid == IID_IEnumPins )
+    {
+        *ppv = (IEnumPins *)this;
+        return NOERROR;
+    }
+    else
+    {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+};
+STDMETHODIMP_(ULONG) CaptureEnumPins::AddRef()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumPins::AddRef" );
+#endif
+
+    i_ref++;
+    return NOERROR;
+};
+STDMETHODIMP_(ULONG) CaptureEnumPins::Release()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumPins::Release" );
+#endif
+
+    i_ref--;
+    if( !i_ref ) delete this;
+
+    return NOERROR;
+};
+
+/* IEnumPins */
+STDMETHODIMP CaptureEnumPins::Next( ULONG cPins, IPin ** ppPins,
+                                    ULONG * pcFetched )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumPins::Next" );
+#endif
+
+    *pcFetched = 0;
+
+    if( i_position < 1 && cPins > 0 )
+    {
+        IPin *pPin = p_filter->CustomGetPin();
+        *ppPins = pPin;
+        pPin->AddRef();
+        *pcFetched = 1;
+        i_position++;
+        return NOERROR;
+    }
+
+    return S_FALSE;
+};
+STDMETHODIMP CaptureEnumPins::Skip( ULONG cPins )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumPins::Skip" );
+#endif
+
+    if( cPins > 1 )
+    {
+        return S_FALSE;
+    }
+
+    i_position += cPins;
+    return NOERROR;
+};
+STDMETHODIMP CaptureEnumPins::Reset()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumPins::Reset" );
+#endif
+
+    i_position = 0;
+    return S_OK;
+};
+STDMETHODIMP CaptureEnumPins::Clone( IEnumPins **ppEnum )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumPins::Clone" );
+#endif
+
+    *ppEnum = new CaptureEnumPins( p_input, p_filter, this );
+    if( *ppEnum == NULL ) return E_OUTOFMEMORY;
+
+    return NOERROR;
+};
+
+/****************************************************************************
+ * Implementation of our dummy directshow enummediatypes class
+ ****************************************************************************/
+
+CaptureEnumMediaTypes::CaptureEnumMediaTypes( input_thread_t * _p_input,
+                                  CapturePin *_p_pin,
+                                  CaptureEnumMediaTypes *pEnumMediaTypes )
+  : p_input( _p_input ), p_pin( _p_pin ), i_ref( 1 )
+{
+    /* Hold a reference count on our filter */
+    p_pin->AddRef();
+
+    /* Are we creating a new enumerator */
+
+    if( pEnumMediaTypes == NULL )
+    {
+        i_position = 0;
+    }
+    else
+    {
+        i_position = pEnumMediaTypes->i_position;
+    }
+}
+
+CaptureEnumMediaTypes::~CaptureEnumMediaTypes()
+{
+    p_pin->Release();
+}
+
+/* IUnknown methods */
+STDMETHODIMP CaptureEnumMediaTypes::QueryInterface( REFIID riid, void **ppv )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumMediaTypes::QueryInterface" );
+#endif
+
+    if( riid == IID_IUnknown ||
+        riid == IID_IEnumMediaTypes )
+    {
+        *ppv = (IEnumMediaTypes *)this;
+        return NOERROR;
+    }
+    else
+    {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+};
+STDMETHODIMP_(ULONG) CaptureEnumMediaTypes::AddRef()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumMediaTypes::AddRef" );
+#endif
+
+    i_ref++;
+    return NOERROR;
+};
+STDMETHODIMP_(ULONG) CaptureEnumMediaTypes::Release()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumMediaTypes::Release" );
+#endif
+
+    i_ref--;
+    if( !i_ref ) delete this;
+
+    return NOERROR;
+};
+
+/* IEnumMediaTypes */
+STDMETHODIMP CaptureEnumMediaTypes::Next( ULONG cMediaTypes,
+                                          AM_MEDIA_TYPE ** ppMediaTypes,
+                                          ULONG * pcFetched )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumMediaTypes::Next" );
+#endif
+
+    *pcFetched = 0;
+
+#if 0
+    if( i_position < 1 && cMediaTypes > 0 )
+    {
+        IPin *pPin = p_pin->CustomGetPin();
+        *ppMediaTypes = pPin;
+        pPin->AddRef();
+        *pcFetched = 1;
+        i_position++;
+        return NOERROR;
+    }
+#endif
+
+    return S_FALSE;
+};
+STDMETHODIMP CaptureEnumMediaTypes::Skip( ULONG cMediaTypes )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumMediaTypes::Skip" );
+#endif
+
+    if( cMediaTypes > 1 )
+    {
+        return S_FALSE;
+    }
+
+    i_position += cMediaTypes;
+    return NOERROR;
+};
+STDMETHODIMP CaptureEnumMediaTypes::Reset()
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumMediaTypes::Reset" );
+#endif
+
+    i_position = 0;
+    return S_OK;
+};
+STDMETHODIMP CaptureEnumMediaTypes::Clone( IEnumMediaTypes **ppEnum )
+{
+#ifdef DEBUG_DSHOW
+    msg_Dbg( p_input, "CaptureEnumMediaTypes::Clone" );
+#endif
+
+    *ppEnum = new CaptureEnumMediaTypes( p_input, p_pin, this );
+    if( *ppEnum == NULL ) return E_OUTOFMEMORY;
+
+    return NOERROR;
+};
diff --git a/modules/access/dshow/filter.h b/modules/access/dshow/filter.h
new file mode 100644 (file)
index 0000000..1ea95f5
--- /dev/null
@@ -0,0 +1,202 @@
+/*****************************************************************************
+ * filter.h : DirectShow access module for vlc
+ *****************************************************************************
+ * Copyright (C) 2002 VideoLAN
+ * $Id: filter.h,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 <string>
+#include <list>
+#include <deque>
+using namespace std;
+
+typedef struct VLCMediaSample
+{
+    IMediaSample *p_sample;
+    mtime_t i_timestamp;
+
+} VLCMediaSample;
+
+class CaptureFilter;
+
+void WINAPI FreeMediaType( AM_MEDIA_TYPE& mt );
+HRESULT WINAPI CopyMediaType( AM_MEDIA_TYPE *pmtTarget,
+                              const AM_MEDIA_TYPE *pmtSource );
+
+/****************************************************************************
+ * Declaration of our dummy directshow filter pin class
+ ****************************************************************************/
+class CapturePin: public IPin, public IMemInputPin
+{
+    input_thread_t * p_input;
+
+    CaptureFilter *p_filter;
+
+    IPin *p_connected_pin;
+    AM_MEDIA_TYPE media_type;
+
+    deque<VLCMediaSample> samples_queue;
+
+    int i_ref;
+
+  public:
+    CapturePin( input_thread_t * _p_input, CaptureFilter *_p_filter );
+    virtual ~CapturePin();
+
+    /* IUnknown methods */
+    STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
+    STDMETHODIMP_(ULONG) AddRef();
+    STDMETHODIMP_(ULONG) Release();
+
+    /* IPin methods */
+    STDMETHODIMP Connect( IPin * pReceivePin, const AM_MEDIA_TYPE *pmt );
+    STDMETHODIMP ReceiveConnection( IPin * pConnector,
+                                    const AM_MEDIA_TYPE *pmt );
+    STDMETHODIMP Disconnect();
+    STDMETHODIMP ConnectedTo( IPin **pPin );
+    STDMETHODIMP ConnectionMediaType( AM_MEDIA_TYPE *pmt );
+    STDMETHODIMP QueryPinInfo( PIN_INFO * pInfo );
+    STDMETHODIMP QueryDirection( PIN_DIRECTION * pPinDir );
+    STDMETHODIMP QueryId( LPWSTR * Id );
+    STDMETHODIMP QueryAccept( const AM_MEDIA_TYPE *pmt );
+    STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );
+    STDMETHODIMP QueryInternalConnections( IPin* *apPin, ULONG *nPin );
+    STDMETHODIMP EndOfStream( void );
+
+    STDMETHODIMP BeginFlush( void );
+    STDMETHODIMP EndFlush( void );
+    STDMETHODIMP NewSegment( REFERENCE_TIME tStart, REFERENCE_TIME tStop,
+                             double dRate );
+
+    /* IMemInputPin methods */
+    STDMETHODIMP GetAllocator( IMemAllocator **ppAllocator );
+    STDMETHODIMP NotifyAllocator(  IMemAllocator *pAllocator, BOOL bReadOnly );
+    STDMETHODIMP GetAllocatorRequirements( ALLOCATOR_PROPERTIES *pProps );
+    STDMETHODIMP Receive( IMediaSample *pSample );
+    STDMETHODIMP ReceiveMultiple( IMediaSample **pSamples, long nSamples,
+                                  long *nSamplesProcessed );
+    STDMETHODIMP ReceiveCanBlock( void );
+
+    /* Custom methods */
+    HRESULT CustomGetSample( VLCMediaSample * );
+    AM_MEDIA_TYPE CustomGetMediaType();
+};
+
+/****************************************************************************
+ * Declaration of our dummy directshow filter class
+ ****************************************************************************/
+class CaptureFilter : public IBaseFilter
+{
+    input_thread_t * p_input;
+    CapturePin *p_pin;
+    IFilterGraph * p_graph;
+
+    int i_ref;
+
+  public:
+    CaptureFilter( input_thread_t * _p_input );
+    virtual ~CaptureFilter();
+
+    /* IUnknown methods */
+    STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
+    STDMETHODIMP_(ULONG) AddRef();
+    STDMETHODIMP_(ULONG) Release();
+
+    /* IPersist method */
+    STDMETHODIMP GetClassID(CLSID *pClsID);
+
+    /* IMediaFilter methods */
+    STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);
+    STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
+    STDMETHODIMP GetSyncSource(IReferenceClock **pClock);
+    STDMETHODIMP Stop();
+    STDMETHODIMP Pause();
+    STDMETHODIMP Run(REFERENCE_TIME tStart);
+
+    /* IBaseFilter methods */
+    STDMETHODIMP EnumPins( IEnumPins ** ppEnum );
+    STDMETHODIMP FindPin( LPCWSTR Id, IPin ** ppPin );
+    STDMETHODIMP QueryFilterInfo( FILTER_INFO * pInfo );
+    STDMETHODIMP JoinFilterGraph( IFilterGraph * pGraph, LPCWSTR pName );
+    STDMETHODIMP QueryVendorInfo( LPWSTR* pVendorInfo );
+
+    /* Custom methods */
+    CapturePin *CustomGetPin();
+};
+
+/****************************************************************************
+ * Declaration of our dummy directshow enumpins class
+ ****************************************************************************/
+class CaptureEnumPins : public IEnumPins
+{
+    input_thread_t * p_input;
+    int i_position;
+    CaptureFilter *p_filter;
+
+    int i_ref;
+
+public:
+    CaptureEnumPins( input_thread_t * _p_input, CaptureFilter *_p_filter,
+                     CaptureEnumPins *pEnumPins );
+    virtual ~CaptureEnumPins();
+
+    // IUnknown
+    STDMETHODIMP QueryInterface( REFIID riid, void **ppv );
+    STDMETHODIMP_(ULONG) AddRef();
+    STDMETHODIMP_(ULONG) Release();
+
+    // IEnumPins
+    STDMETHODIMP Next( ULONG cPins, IPin ** ppPins, ULONG * pcFetched );
+    STDMETHODIMP Skip( ULONG cPins );
+    STDMETHODIMP Reset();
+    STDMETHODIMP Clone( IEnumPins **ppEnum );
+};
+
+/****************************************************************************
+ * Declaration of our dummy directshow enummediatypes class
+ ****************************************************************************/
+class CaptureEnumMediaTypes : public IEnumMediaTypes
+{
+    input_thread_t * p_input;
+    int i_position;
+    CapturePin *p_pin;
+
+    int i_ref;
+
+public:
+    CaptureEnumMediaTypes( input_thread_t * _p_input, CapturePin *_p_pin,
+                           CaptureEnumMediaTypes *pEnumMediaTypes );
+
+    virtual ~CaptureEnumMediaTypes();
+
+    // IUnknown
+    STDMETHODIMP QueryInterface( REFIID riid, void **ppv );
+    STDMETHODIMP_(ULONG) AddRef();
+    STDMETHODIMP_(ULONG) Release();
+
+    // IEnumMediaTypes
+    STDMETHODIMP Next( ULONG cMediaTypes, AM_MEDIA_TYPE ** ppMediaTypes,
+                       ULONG * pcFetched );
+    STDMETHODIMP Skip( ULONG cMediaTypes );
+    STDMETHODIMP Reset();
+    STDMETHODIMP Clone( IEnumMediaTypes **ppEnum );
+};