Click here to Skip to main content
15,913,130 members
Articles / Programming Languages / C#
Article

DirectShow.NET

Rate me:
Please Sign up or sign in to vote.
4.94/5 (193 votes)
22 Jul 2002Public Domain3 min read 4.2M   34K   453   840
DirectShow for DVD and file playback, capture and sample grabber

Sample Image - directshownet.jpg

Disclaimer: Experimental code using DirectShow with the .NET Framework 1.0

Abstract

This experimental code shows how to use DirectShow with .NET and C#. This includes simple media playback, playing DVD discs, capturing video streams to disk and a sample picture grabber.

Note, this article doesn't save you from reading the detailed DirectShow SDK documentation! I will not explain DirectShow, only some of the used .NET Interop technologies!

DirectShow

DirectShow is a standardized Microsoft Win32 API to use any compliant movie or video device from your application. DirectShow is available with the current DirectX version 8.1(b) for Windows 98/ME/2000 and included in XP. Please install the latest version, this article doesn't support anything except 8.1 :

Again, I will not describe any DirectShow interfaces, you have to know them by installing the SDK for C++, reading the SDK documentation and understanding the SDK samples!

DirectShow is exposed as COM components and interfaces, at these two 'levels':

  • DirectShow custom interfaces - mainly for C++ programmers.
  • DirectShow VB components - designed for VB6, provides a type library.
You can use the DirectShow playback components for VB6 with .NET, as described in this CodeProject article: DirectShow MediaPlayer in C# (Daniel Strigl)

.NET Interop

While using the VB6 components with the provided type library is easy with .NET, there is no direct way to access the custom DirectShow interfaces. We have to use Interop with one of this approaches:

  • Use 'Managed Extensions for C++', as done e.g. by DirectX.NET
  • Rewrite all the interfaces from IDL to e.g. C# !
I chose the second strategy for this reasons :
  • Uses only one (managed) language (C#)
  • Most DirectShow interfaces are not very complex
  • DirectShow methods for simple playback/capturing are not time-critical
  • We can directly use the (documented) interfaces without limitations, no 'wrapper classes'
Sure, this has some drawbacks:
  • Much of initial work for rewriting the interfaces
  • You have to understand Interop to use it correctly
  • Not very .NET/OO-like
One typical rewrite of an IDL interface in C# looks like this :
// ======== IDL of ICaptureGraphBuilder2 (AXExtend.idl) ======
[
    object,
    uuid(93E5A4E0-2D50-11d2-ABFA-00A0C9C6E38D),
    pointer_default(unique)
]
interface ICaptureGraphBuilder2 : IUnknown {

    // Use this filtergraph
    HRESULT SetFiltergraph( [in] IGraphBuilder *pfg );

    // what filtergraph are you using?
    // *ppfg->Release() when you're done with it
    HRESULT GetFiltergraph( [out] IGraphBuilder **ppfg);
    ....
... using Interop attributes with C# will be translated to :
C#
// ======== C# version of ICaptureGraphBuilder2 (DsExtend.cs) ======

   [ComVisible(true), ComImport,
    Guid("93E5A4E0-2D50-11d2-ABFA-00A0C9C6E38D"),
    InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
public interface ICaptureGraphBuilder2
{
        [PreserveSig]
   int SetFiltergraph( [In] IGraphBuilder pfg );

        [PreserveSig]
   int GetFiltergraph( [Out] out IGraphBuilder ppfg );
   ....

Once we have all this interface definitions in C#, we can start calling DirectShow just like we did in C++:

// ======== C++ code to create the COM instance of Filter Graph ========

    JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                         IID_IGraphBuilder, (void **)&pGB));

    // Have the graph builder construct its the <BR>    // appropriate graph automatically
    JIF(pGB->RenderFile(wFile, NULL));

    // QueryInterface for DirectShow interfaces
    JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
    ....
... we replace CoCreateInstance with Activator.CreateInstance, and QueryInterface just is a simple cast in C#:
C#
// ======== C# code to create the COM instance of Filter Graph ========

    Type comtype = null;
    object comobj = null;
    try {
        comtype = Type.GetTypeFromCLSID( Clsid.FilterGraph );
        if( comtype == null )
            throw new NotSupportedException( <BR>                "DirectX (8.1 or higher) not installed?" );
        comobj = Activator.CreateInstance( comtype );
        graphBuilder = (IGraphBuilder) comobj; comobj = null;
        
        int hr = graphBuilder.RenderFile( clipFile, null );
        if( hr < 0 )
            Marshal.ThrowExceptionForHR( hr );

        mediaCtrl    = (IMediaControl)  graphBuilder;
     ....

Projects Structure

The download contains all this C# source code:

\DirectShow\
     \DShowNET\              // the DirectShow interface definitions :
              \DsBugWO.cs      // workaround for a bug 
              \DsControl.cs    // ported from control.odl 
              \DsCore.cs       // ported from axcore.idl 
              \DsDevice.cs     // device enumerator, helper functions 
              \DsDVD.cs        // DVD interfaces from dvdif.idl 
              \DsExtend.cs     // ported from axextend.idl 
              \DsUtils.cs      // utility classes, SDK Common sources 
              \DsUuids.cs      // UUIDs and CLSIDs from uuids.h 
              \QEdit.cs        // grabber interfaces from qedit.idl 

     \CaptureNET\            // video stream capture sample 
     \DVDPlayerNET\          // DVD player sample 
     \PlayWndNET\            // simple media file playback 
     \SampleGrabberNET\      // picture grabber 

Playback

The first sample included in the download is PlayWndNET. It plays the known video and audio file formats of DirectShow like avi, mpg, wav, mid etc.

DirectShow playback

DVD Player

For the next sample, DVDPlayerNET you must have a third-party DVD codec installed, like WinDVD or PowerDVD. Then, the C# sample uses the DirectShow DVD interfaces to watch the movie. It also supports menu navigation.

DirectShow DVD

Grab Picture

The most complex sample provided is SampleGrabberNET. It shows a live video stream from a capture device like DV cam, web cam or TV card in a preview window. By pressing the 'Grab' toolbar-button, you can capture a still picture to a 24-Bit RGB bitmap file!

DirectShow picture grabber

The sample also supports the IAMTVTuner interface of a TV card, so you can switch the TV tuner channel.

Capturing

The last sample, CaptureNET can be used to capture a live video stream to disk. Note, the few settings can only be done once at startup, and writing to the AVI file starts immediately.

DirectShow Capturing

Limitations

  • EXPERIMENTAL! don't use it in production quality code.
  • The samples only provide partial and very basic functionality.
  • I did most tests on Windows XP and few on Windows ME.
  • Tested only on a very limited set of devices with only few media formats.
    I used a Logitech QuickCam, Sony DV camcorder, Hauppauge WinTV PCI and WinDVD.
  • Get the latest driver (WDM) from manufacturer.
  • Some devices fail if you select unsupported settings in the dialogs.
  • This code will NOT help to solve any DirectShow/WDM configuration problems.
  • Get >128MB RAM, >400MHz CPU, fast & huge harddisk.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Web Developer
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: motion detection usting sampgrabber Pin
Anonymous26-Mar-04 20:47
Anonymous26-Mar-04 20:47 
General[How to fix the H-Sync and V-Sync of a video stream using DirectShow?] Pin
[Newbie_C0der]12-Nov-03 1:26
[Newbie_C0der]12-Nov-03 1:26 
GeneralGet first frame of video file by C# and DirectShow Pin
Member 4634216-Nov-03 17:28
Member 4634216-Nov-03 17:28 
Questionhow to: Change the camera input resolution Pin
robBurke5-Nov-03 14:40
robBurke5-Nov-03 14:40 
AnswerRe: how to: Change the camera input resolution Pin
Anonymous2-Dec-03 0:50
Anonymous2-Dec-03 0:50 
QuestionHow to generate a Video from Images? Pin
Marc Schneider28-Oct-03 0:33
Marc Schneider28-Oct-03 0:33 
GeneralISampleGrabber.GetCurrentSample fails! Pin
Assaf27-Oct-03 8:43
Assaf27-Oct-03 8:43 
GeneralIWMStreamConfig3 - Specified cast is not valid Pin
Assaf25-Oct-03 1:07
Assaf25-Oct-03 1:07 
hi Master, and all.

in my code, i am trying to work with Windows Media Video Encoder 9.
in the WMV docs, it says that i must configure the codec using IMStreamConfig3.

however, when i try to cast to IWMStreamConfig3, i get an exception:
"Specified cast is not valid."

here is my code:

IPin []a = new IPin[1];
DSUtil.FindPin(this.IBaseFilterVideoCompressor, PinDirection.Output, ref a, 1);
IPin p = a[0];
IAMStreamConfig asc = (IAMStreamConfig)p; // works nice
IWMStreamConfig3 wsc = (IWMStreamConfig3)p; // exception!!!!!!>>>>"Specified cast is not valid."


Please tell my why my cast fails.

here is the code for IWMStreamConfig, IWMStreamConfig2, and IWMStreamConfig3:

namespace WMFSDKWrapper
{
	///////////////////////////////////////////////////////////////////////////////
	//
	// IWMStreamConfig represents an ASF stream.
	//
	[Guid("96406BDC-2B2B-11d3-B36B-00C04F6108FF"), ComVisible(true), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	public interface IWMStreamConfig
	{
		// This interface QI's for IWMMediaProps and one of it's inheritors.
		// (IWMVideoMediaProps, for instance).

		uint GetStreamType(		[Out]		out Guid	pguidStreamType );

		uint GetStreamNumber(	[Out]		out ushort	pwStreamNum );

		uint SetStreamNumber(	[In]			ushort	wStreamNum );

		uint GetStreamName(		[Out, MarshalAs(UnmanagedType.LPWStr)]		out	string	pwszStreamName,
			[In, Out]	ref ushort	pcchStreamName );

		uint SetStreamName(		[In, MarshalAs(UnmanagedType.LPWStr)]			string	pwszStreamName );

		uint GetConnectionName( [Out, MarshalAs(UnmanagedType.LPWStr)]		out	string	pwszInputName,
			[In, Out]	ref ushort	pcchInputName );

		uint SetConnectionName( [In, MarshalAs(UnmanagedType.LPWStr)]			string	pwszInputName );

		uint GetBitrate(		[Out]		out uint	pdwBitrate );

		uint SetBitrate(		[In]			uint	pdwBitrate );

		//
		// A buffer window of -1 (0xffffffff) indicates that the buffer window
		// is unknown. On the writer side, this means the writer can use whatever
		// buffer window it chooses.
		//
		uint GetBufferWindow(	[Out]		out uint	pmsBufferWindow );

		uint SetBufferWindow(	[In]			uint	msBufferWindow );
	}

	///////////////////////////////////////////////////////////////////////////////
	[Guid("7688D8CB-FC0D-43BD-9459-5A8DEC200CFA"),ComVisible(true), ComImport] 
	public interface IWMStreamConfig2 : IWMStreamConfig
	{
		//
		// Get/set the type of data communication protocol (reliable or unreliable)
		//
		uint GetTransportType ([Out] out WMT_TRANSPORT_TYPE pnTransportType);

		uint SetTransportType([In] WMT_TRANSPORT_TYPE nTransportType);

		//
		// Add/Get data unit extension systems (for attaching user-defined
		// data to samples in the output file)
		//
		uint AddDataUnitExtension(
			[In]    Guid    guidExtensionSystemID,
			[In]    ushort    cbExtensionDataSize,
			[In,	MarshalAs(UnmanagedType.LPArray)]    byte    []pbExtensionSystemInfo,
			[In]    ushort   cbExtensionSystemInfo );

		uint GetDataUnitExtensionCount(  [Out]   out ushort pcDataUnitExtensions );

		uint GetDataUnitExtension(       
			[In]    ushort    wDataUnitExtensionNumber,
			[Out]   out Guid pguidExtensionSystemID,
			[Out]   out ushort pcbExtensionDataSize,
			[Out,	MarshalAs(UnmanagedType.LPArray)]   out byte    []pbExtensionSystemInfo,
			[In, Out]   ref uint pcbExtensionSystemInfo );

		uint RemoveAllDataUnitExtensions();
	}

	///////////////////////////////////////////////////////////////////////////////
	[Guid("CB164104-3AA9-45a7-9AC9-4DAEE131D6E1"),ComVisible(true), ComImport] 
	public interface IWMStreamConfig3 : IWMStreamConfig2
	{
		//
		// Get/set language info for this stream as an RFC1766 string.
		//

		uint GetLanguage(    [Out, MarshalAs(UnmanagedType.LPWStr)]  out string pwszLanguageString,
			[In, Out] ref ushort pcchLanguageStringLength );

		uint SetLanguage(    [In, MarshalAs(UnmanagedType.LPWStr)] string pwszLanguageString );
	}

	public enum WMT_TRANSPORT_TYPE
	{
		WMT_Transport_Type_Unreliable,
		WMT_Transport_Type_Reliable
	}
}


thanks,

assaf

assafwo01 at hotmail dot com
GeneralThe IEnumFilters.Next doen't work. Pin
bdknight19-Oct-03 21:04
bdknight19-Oct-03 21:04 
GeneralRe: The IEnumFilters.Next doen't work. Pin
NETMaster20-Oct-03 9:19
NETMaster20-Oct-03 9:19 
GeneralRe: The IEnumFilters.Next doen't work. Pin
LeTchi8-Dec-03 7:08
LeTchi8-Dec-03 7:08 
GeneralRe: The IEnumFilters.Next doen't work. Pin
neo260077714-Dec-03 19:52
neo260077714-Dec-03 19:52 
GeneralRe: The IEnumFilters.Next doen't work. Pin
letchideslandes17-Dec-03 9:09
letchideslandes17-Dec-03 9:09 
GeneralTwo Cameras Pin
smithy99917-Oct-03 0:18
smithy99917-Oct-03 0:18 
Questionwhy use wrapper ??? Pin
fguihen14-Oct-03 7:47
fguihen14-Oct-03 7:47 
AnswerRe: why use wrapper ??? Pin
NETMaster14-Oct-03 13:18
NETMaster14-Oct-03 13:18 
GeneralRe: why use wrapper ??? Pin
Assaf23-Oct-03 7:26
Assaf23-Oct-03 7:26 
GeneralRe: why use wrapper ??? Pin
NETMaster23-Oct-03 9:33
NETMaster23-Oct-03 9:33 
QuestionHow can I Restore Source Page Settings like brightness and contrast at next power up? Pin
Hakimi12-Oct-03 1:45
Hakimi12-Oct-03 1:45 
GeneralISampleGrabberCB.SampleCB gets called only the first time Pin
Assaf1-Oct-03 7:30
Assaf1-Oct-03 7:30 
GeneralCould not setup graph The parameter is incorrect Pin
Shahin774-Sep-03 11:30
Shahin774-Sep-03 11:30 
GeneralGrab Picture from Video doesn't work by me Pin
Member 4443698-Aug-03 3:45
Member 4443698-Aug-03 3:45 
GeneralRender to Direct3D Texture Pin
bernpi6-Aug-03 22:37
bernpi6-Aug-03 22:37 
QuestionHow to save and restore a filter's setting with displaying the property page? Pin
mes@ser28-Jun-03 22:16
mes@ser28-Jun-03 22:16 
Generalload directshow filtergraph Pin
Member 38646026-Jun-03 0:40
Member 38646026-Jun-03 0:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.