SomeFunctionToDeleteSamplesInTheList
(1)
IPersistMediaPropertyBag
(1)
IAMStreamControl
(1)
IAMStreamSelect
(1)
IAMMediaContent
(1)
IFileSourceFilter
(1)
CBaseOutputQueue
(1)
CBaseOutputPin
(1)

CAsyncReader or CSource for file reader?

Asked By Jeremy Noring
17-Nov-09 08:20 PM
I am going to be writing an MP4 parsing filter (the actual parser will
be mp4v2), and I need to figure out the best baseclass to use as a
starting point.  In the past, I have always used CSource to write my
source filters, but the documentation implies that it is best to use
CAsyncReader for the source.  Why is this?  Looking through
CAsyncReader, it is not clear to me what advantages the pull model
offers over pushing w/r/t file reading, but maybe someone can clarify
this for me.

Also, any advice for standard interfaces to implement?  I definitely
want IMediaSeeking and IFileSourceFilter; is there any other
interfaces on a file-reader I should be sure to implement?

Have you seen this:http://www.gdcl.co.uk/mpeg4/index.

The March Hare [MVP] replied to Jeremy Noring
17-Nov-09 07:12 PM
Have you seen this:

http://www.gdcl.co.uk/mpeg4/index.htm

It could be a good starting point.

--
Please read this before replying:
1. Dshow & posting help:  http://tmhare.mvps.org/help.htm
2. Trim & respond inline (please do not top post or snip everything)
3. Benefit others:  follow up if you are helped or you found a solution

From: "Jeremy Noring"An async reader would not work at all.

Alessandro Angeli replied to Jeremy Noring
17-Nov-09 10:58 PM
From: "Jeremy Noring"


An async reader would not work at all. All filters in DS
work in push mode, including decoders, transforms,
renderers, sinks, muxers, tees, mixers, live/capture
sources, readers... The only exceptions are parsers and
async readers.

An async reader works in pull mode, so it can not connect
downstream to any filter that works in push mode.

A parser/splitter/demuxer can work in push mode on both
input and output (a demuxer) or in pull mode on the input
and push mode on the output (a parser/splitter).

So your choices are:

- a push source (possibly based on CSource+CSourceStream)
that fetches the bytes internally

- a parser/splitter, that feches the bytes from an upstream
async reader (there is no base class for this but, as TMH
suggested, take a look at the samples on www.gdcl.co.uk)

In a nutshell, a parser is a filter that has one or more
worker threads that fetch bytes and output elementary
streams. The data can be fetched internally (a push source
with 0 inputs) and from an upstream async reader (a splitter
with 1 input).

An async reader on the other hand is nothing more than a
byte fetcher, that is DS's equivalent to IStream or a file
I/O API.

it is better to have 1 thread per output, so that one slow
stream will not block the whole parser thus blocking the
whole graph and possibly leading to a deadlock (e.g. you can
use a COutputQueue per output pin and a master thread to
fetch the bytes and queue the parsed samples).


Since seeking is the responsibility of the parser, you will
have to implement IMediaSeeking on each output pin (usually
only 1 pin actually seeks, while the others ignore or fail
the calls, except the query ones).

You will need to implement IFileSourceFilter only if you
write a push source instead of a parser.

Since your source is neither live nor streaming and the
outputs have fixed formats, there is not anything else you
need to implement.

If you implement a push source, you might want to add
IAMFilterMiscFlags.

If you think your MP4 files might have mutually exclusive
streams, you might wabt to support IAMStreamSelect and
possibly IAMStreamControl on your output pins (the latter is
not required).

The metadata interfaces (IAMMediaContent and
IPersistMediaPropertyBag) are pretty useless since no player
uses them.

You might want to support IPersistStream if you want to save
your config to a GRF file (pin connections and
IFileSourceFilter info are saved automatically, so you only
need IPersistStream for additional config data).

--
// Alessandro Angeli
// MVP :: DirectShow / MediaFoundation
// mvpnews at riseoftheants dot com
// http://www.riseoftheants.com/mmx/faq.htm

Assuming that you have read the other two posts, and we are now talkingabout

Geraint Davies replied to Jeremy Noring
18-Nov-09 07:19 AM
Assuming that you have read the other two posts, and we are now talking
about why the input to your parser should be IAsyncReader (with
push-mode output), the answer is that an MP4 parser needs random
access to the source. You use the index to find the file position for
a sample, and then you read that sample from the file. Mapping this
onto a push mode source would be a difficulty that you could do
without.

In my sample mp4 demux, I use independent threads for each output pin
to read from the file. For some other environments where access to the
file is a little more constrained, I have used a single thread. In the
thread loop, I check (for each track's next sample) which sample is
earliest in the file, and read that track's sample next. This still
uses a simple random access, but reduces unnecessary seeking during
playback.

G
Thanks, that sort of helps clarify the choice.
Jeremy Noring replied to Geraint Davies
08-Dec-09 06:11 AM
Thanks, that sort of helps clarify the choice.  However, I am using
mp4v2 to perform the actual parsing, so I am not sure random access is
an issue (it has functions for accessing by sample number/time on a
per-track basis).  What I am probably going to do is have a single
thread that services all tracks and pushes samples to pins previously
associated with the tracks.  So CSource/CSourceStream might be a bit
too far down the inheritance hierarchy for me.  I will need to do some
more research.

Thanks all for the informative posts.
From: "Jeremy Noring"If mp4v2 is going to perform the byte stream I/O
Alessandro Angeli replied to Jeremy Noring
07-Dec-09 02:02 PM
From: "Jeremy Noring"


If mp4v2 is going to perform the byte stream I/O itself,
then you cannot write a parser that fetches the byte stream
from an upstream async reader and you will have to go with a
parser built into a push source filter.

It is easier to start off with CBaseFilter/CBaseOutputPin
than to adapt CSource/CSourceStream to your own threading
model. What you probably want is a CBaseOutputQueue per
output pin with its own thread (CBaseOutputQueue can spawn
it for you) and your own sample dispatcher thread (based on
CAMThread or your own code - I use my own code because I
do not like the synchronous messaged-based control mechanism
CAMThread uses, which can lead to deadlocks if the
downstream filters do not behave themselves).

--
// Alessandro Angeli
// MVP :: DirectShow / MediaFoundation
// mvpnews at riseoftheants dot com
// http://www.riseoftheants.com/mmx/faq.htm
wrote:Yeah, I came to a similar conclusion: I need to use
Jeremy Noring replied to Alessandro Angeli
08-Dec-09 06:11 AM
Yeah, I came to a similar conclusion: I need to use CBaseFilter/
CBaseOutputPin.

I really do not want dedicated threads for my output, and I have never
been a big fan of CAMThread.  Lately I have been using Qt's threading
stuff, which I vastly prefer (and the new, more permissive LGPL
license for core stuff is really nice), although occasionally I get
nailed by the perils of mixing COM and Qt.
From: "Jeremy Noring"You do not have to use them and COutputQueue was only
Alessandro Angeli replied to Jeremy Noring
07-Dec-09 05:47 PM
From: "Jeremy Noring"


You do not have to use them and COutputQueue was only a
suggestion (btw, COutputQueue can work wit or without a
worker thread). However, having separate streaming threads
for separate streams is often a good idea, so that a late
stream will not block the whole parser, in case the streams
are not tightly interleaved. In this respect, COutputQueue
simplifies your life because it will hide the dedicated
streaming threads from you.


DirectShow uses the COM interface pattern but it is free
threaded and does not use any other COM/OLE facility, so it
should not really matter what you mix it with.

--
// Alessandro Angeli
// MVP :: DirectShow / MediaFoundation
// mvpnews at riseoftheants dot com
// http://www.riseoftheants.com/mmx/faq.htm
wrote:No, the problem for me was a lot more basic than that.
Jeremy Noring replied to Alessandro Angeli
24-Dec-09 03:16 AM
No, the problem for me was a lot more basic than that.  Most of the Qt
container classes, for example, implement copy constructors.  So if
you accidentally do something like:

QList<CComPtr<IMediaSample> > someListOfSamples;
...
SomeFunctionToDeleteSamplesInTheList( QList<CComPtr<IMediaSample> >
someList );

Since the argument to SomeFunction() is not a pointer/reference, but a
value, the _entire_ list gets copied, and all of the CComPtrs have
their reference counts incremented (hurray for smart pointers...).  So
if you were counting on that function to delete objects (presumably by
popping them off the list and decrementing the reference count), you
are a C++ boob like me.

In retrospect this was incredibly stupid of me, but it was truly a
bitch to locate this reference counting leak.
Post Question To EggHeadCafe