Jeff Griffin

DirectShow with WPF MediaKit

Posted on April 25, 2011

Lately, I have been tweaking and old video file transcoding application, so I can post it here.  I'll get into the program itself in more detail once I'm done adding some functionality.

For those of us who still have some unfinished business in the world of DirectShow, as well as those building new software with Media Foundation, Jeremiah Morrill's work with the WPF MediaKit can be a helpful tool.  The intent of this library seems to be to provide pluggable media presenters for a number of use cases like DVD content, generic URI content, video capture devices, Direct3d renders, etc. If these cases fit your needs, dropping the appropriate element into your XAML will be a negligible task. In my case, the bulk of the graph is built in code that isn't concerned with the UI, and all I needed was a surface allocated renderer to plug into my existing pipeline architecture.  Once I found the right way to use the aspects of the Media Kit that I needed, the code to expose them was simple.

The first thing I had to understand was that Jeremiah's player classes inherited from a WorkDispatcherObject that encapsulated his own WorkDispatcher class, which handles delegate queuing and invoking and hosts a message pump.  It's a nice implementation, so I had no problem incorporating it into my model code, which handles the pipeline's topology.  Allowing two WorkDispatcherObjects to share a dispatcher required a small modification to the kit: The Dispatcher property on WorkDispatcherObject needed to be made protected, rather than private. Changing the dispatcher of a WorkDispatcherObject that has already started its dispatcher's thread can have some unintended results. It wouldn't be too tough to write a WorkDispatcherObject that could support setting the dispatcher more gracefully, but this works for me as long as I'm careful about when I set it.  Since the player object is instantiated as a result of UI initialization and immediately starts a thread on the dispatcher, it was much simpler to allow the ViewModel code to borrow the View's dispatcher, using a OneWayToSource binding.


<Local:GraphPlayerElement GraphPlayer="{Binding GraphPlayer, Mode=OneWayToSource}"/>

The entire player is bound, rather than just the WorkDispatcher, because the ViewModel/Model will need it to call CreateRenderer.  Here is the code for the player and the element used to host it:

public class GraphPlayerElement : MediaElementBase

	protected override WPFMediaKit.DirectShow.MediaPlayers.MediaPlayerBase OnRequestMediaPlayer()
	{
		return new GraphPlayer();
	}

	public static readonly DependencyProperty GraphPlayerProperty =
		DependencyProperty.Register("GraphPlayer", typeof(GraphPlayer),
		typeof(GraphPlayerElement), new PropertyMetadata());

	public GraphPlayer GraphPlayer
	{
		get { return (GraphPlayer)GetValue(GraphPlayerProperty); }
		set { SetValue(GraphPlayerProperty, value); }
	}

	protected override void OnInitialized(EventArgs e)
	{
       	base.OnInitialized(e);
		GraphPlayer = (GraphPlayer)MediaPlayerBase;
	}
}

public class GraphPlayer : WPFMediaKit.DirectShow.MediaPlayers.MediaPlayerBase
{
	public GraphPlayer()
	{
		//Using STA, because my application pops property windows.
		EnsureThread(System.Threading.ApartmentState.STA);
	}

	/// <summary>
	/// This method exposes a surface allocated renderer to the caller
	/// </summary>
	public IBaseFilter CreateRenderer(IGraphBuilder graph)
	{
		if (!CheckAccess())
		{
			Dispatcher.BeginInvoke((Action)(()=>CreateRenderer(graph)));
		}

		return CreateVideoRenderer(
			WPFMediaKit.DirectShow.MediaPlayers.VideoRendererType.VideoMixingRenderer9,
			graph);
	}
}

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.