mkv source filter

Feb 16, 2010 at 8:23 PM

I'm currently working on an MKV source filter.  It's only guaranteed to be to consume what the mkvmux filter can produce, but that will at least allow you to render the MKV files you make with VOB2MKV, using DirectShow-based players such as Windows Media Player.

Most of the heavy lifting has been completed.  What's left to do is parse clusters, and from there to synthesize a media sample that we pass downstream.

An interesting problem is in what order to send frames downstream.  I-frames and P-frames have a decode order (DTS) that corresponds to their presentation order (PTS), but the B-frame case is different.  An MKV file specifies that frames be in decode order (implying the B-frames must follow the I- or P-frames they reference), but DirectShow requires that media sample time-stamps be monotonically increasing (PTS). 

There are a couple of possibilities.  Either we can pass the B-frames downstream in decode order, but not set the time-stamp of the sample; I think this gets around the DirectShow rule. The other possibility is to pass the B-frames in time-stamp order (allowing us to specify the time on the sample), but then downstream filters will have to cache samples.

The issue with caching samples is whether the downstream filter holds onto the media sample object, or it copies the data and immediately releases the sample.  In the former case, this can lock up a graph if all samples originally negotiated during IPin::Connect are held in cache.  In the mkvmux filter we copy all of the data and immediately release the samples, because we don't have a guarantee that media sample buffers correspond exactly to mpeg-2 (or ac-3) frames, so maybe that's what other filters do too.

All this copying is a pity, since DirectShow was designed specifically to avoid having to copy data.  (Of course, you don't have to copy data.  You could create a secondary abstraction that synthesizes a frame from media samples held in a cache, but you still run into the problem of locking up the graph when there are no more samples available.  You could work around that problem by comparing media samples held in cache to the buffer count in the allocator properties, but that only works if you're not sharing an allocator with another filter, as is the case when using the tee filter.)  Maybe the StreamBuffer filter solves this problem.  I don't know because I haven't studied it very closely.

In any event, I'm targeting the mkv source filter for inclusion in the 1.0.2 release, which should be in the next few days.  In order to release it sooner rather than later, it probably won't support IMediaSeeking in its initial release.

After that, I plan to write an MKV splitter filter, which will work when streaming an MKV file over a network.  The code for the source filter (the mkvparser library especially) was designed to work with async file sources, so adapting it for use with a splitter should be relatively painless.




Feb 25, 2010 at 12:24 PM
Edited Feb 25, 2010 at 3:44 PM

The source filter is complete.  It has been included in release v1.0.2.

I was incorrect in my last post - the source filter does indeed support IMediaSeeking on its outpins.  The MKVMUX filter writes entries for each cluster in the (only) SeekHead, that is present at the beginning of the file.  Seeking is implemented by performing a binary search over the clusters (each cluster has its own timecode).

The way the MKVMUX filter writes the SeekHead is not optimal for network downloads, since all of the cluster entries are described at the beginning of the file.  But this is not useful for a network download, because the clusters aren't available anyway, so it means playback of the file takes longer that it would otherwise (because you have to download more data before you get to a cluster).

My plan for the next release is to change the MKVMUX filter to write the cluster entries in a second SeekHead, placed at the end of the file, after the clusters.  The MKVSOURCE filter will have to be modified to read the cluster entries in the second SeekHead.  My tentative plan is to also include a splitter filter.

One major difference between a source filter and a splitter filter is that in a source filter all of the file is immediately available, so you can seek anywhere in the file.  This is not true of a splitter filter, which only allows seeking over the portion of the file that has been downloaded.