Base Class for Fixed Bitrate Content, 1-input 1-output DMOs

Purpose: many streaming issues, such as discontinuities, timestamps, and incomplete flags, can be handled completely by the base class if the following assumptions are made:

 - input and output data can each be considered a series of fixed-sized, byte-granular samples;
 - sample sizes can be derived from mediatypes and never change while streaming;
 - the size of the timestamp range that a given sample of data spans is the same for every input/output samples;
 - there is a known upper bound on lookahead/lookbehind.

With these assumtions, the following interface between the base class and a derived class is possible.  This interface is simpler than both the IMediaObject interface and our existing base class interface.

Before streaming starts (but after both mediatypes have been set), the base class calls the following method implemented by the derived class:

	HRESULT GetStreamingParams(
		DWORD *pdwInputSampleSize, // in bytes
		DWORD *pdwOutputSampleSize, // in bytes
		DWORD *pdwMaxLookahead, // in input samples, 0 means no lookahead
		DWORD *pdwMaxLookbehind, // in input samples, 0 means no lookbehind
		REFERENCE_TIME *prtSampleDuration, // same for input and output samples
		REFERENCE_TIME *prtDurationDenominator // optional, normally 1
	);

The purpose of prtDurationDenominator is to allow precise values of sample duration when it is not a whole number of REFERENCE_TIME units (e.g., 10000000 / 44100 should be expressed as 100000 / 441 rather than 227 / 1).  This is to prevent timestamp round-off errors from accumulating over time in the base class.

After streaming starts, the base class first accumulates at least ((lookahead + lookbehind + 1) times *pdwInputSampleSize) bytes of input data to ensure adequate lookahead/lookbehind.  Only then does the base class start calling Process:

	HRESULT FBRProcess( // "Fixed Bitrate Process"
		DWORD cSamplesToProcess,
		BYTE *pbInput, // points to cSamplesToProcess input samples plus lookahead/lookbehind
		BYTE *pbOutput // points to room for cSamplesToProcess output samples
	);

To ensure that the algorithm has sufficient lookahead/lookbehind, the base class takes a buffer of (lookbehind + cSamplesToProcess + lookahead) input samples and sets the pbInput pointer points to the (lookbehind + 1)th input sample.  This way for any of the cSamplesToProcess samples in the current input range, FBRProcess() is allowed to dereference samples in the input buffer up to lookahead positions ahead of the current sample and up to lookbehind positions back from the current sample, even if the current sample is at the very beginning or at the very end of the input range.  The base class makes there is valid data there.

For the purposes of computing output timestamps, the base class assumes that the first output sample in the output buffer corresponds in time to the input sample pointed to by pbInput.

Input buffers overlap by (lookahead + lookbehind) samples.  I.e., if either lookahead or lookbehind is non-zero, FBRProcess() will see some input samples more than once.  Thus FBRProcess itself does not need to remember any past input.

Output buffers are always adjacent in time and do not overlap from one FBRProcess call to the next.  Thus if FBRProcess needs previous output samples to compute the current one (e.g., an IIR audio filter), it must store them internally.

Processing efficiency increases with the number of output samples produced during each Process call, especially for objects that use lookahead or lookbehind.  Therefore the base class will attempt to invoke FBRProcess on as many output samples at a time as practical.

Mediatype negotiation with this base class happens in much the same way as with the IMediaObject interface, except the function prototypes are slightly simpler due to the 1-input/1-output restriction, and the GetXXXType (code layering) methods are handled by the base class.

Methods implemented by the derived class and called by the base class:

	// enumeration
	HRESULT GetInputType(DWORD dwTypeIndex, DMO_MEDIA_TYPE *pmt);
	HRESULT GetOutputType(DWORD dwTypeIndex, DMO_MEDIA_TYPE *pmt);

	// verification
	HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt);
	HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt);

Methods implemented by the base class and called by the derived class:

	DMO_MEDIA_TYPE *InputType();
	DMO_MEDIA_TYPE *OutputType();

When a type is set, the base class stores the type on behalf of the DMO.  The derived class accesses the currently set types by calling InputType() and OutputType().  Each returns NULL if the type has not been set.  GetInputType() may be called from CheckOutputType() and vice versa if the object has input/output type dependencies.

Other streaming methods: the base class provides the following overridables:

    virtual HRESULT Init() = 0;
    virtual HRESULT Discontinuity() = 0;

Init() is intended as a hook to allow the derived class to initialize itself prior to streaming.  Init() is guaranteed to be called at least once before streaming starts.  However, Init() may be called more than once, so the derived class should check if it has already been initialized before performing any resource allocation.

There is no corresponding Uninit method.  The derived class should perform clean up / free resources inside its destructor.

Discontinuity() is called whenever the next buffer of input data is logically unrelated to the previous one.  This can happen due to a source change, dropped data, etc.  The derived class should reset any running streaming parameters inside this method.  Discontinuity() is guaranteed to be called at least once before streaming starts.