MPGAUDIO.CPP

//==========================================================================; 
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
/******************************Module*Header*******************************\
* Module Name: MpgAudio.cpp
*
* Implements a prototype Mpeg Audio Software codec. It just consume
* the passed in packets.
*
*
\**************************************************************************/

#include "MpgAudio.h"

// define the GUIDs for streams and my CLSID in this file
#include <initguid.h>

#include "MpegUids.h"

// setup data

const AMOVIESETUP_MEDIATYPE psudIpPinTypes[] =
{ { &MEDIATYPE_Audio // clsMajorType
, &MEDIASUBTYPE_MPEG1Packet } // clsMinorType
, { &MEDIATYPE_Audio // clsMajorType
, &MEDIASUBTYPE_MPEG1AudioPayload } // clsMinorType
, { &MEDIATYPE_Stream // clsMajorType
, &MEDIASUBTYPE_MPEG1Audio } }; // clsMinorType

const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
{ &MEDIATYPE_Audio // clsMajorType
, &MEDIASUBTYPE_NULL }; // clsMinorType

const AMOVIESETUP_PIN psudPins[] =
{ { L"Input" // strName
, FALSE // bRendered
, FALSE // bOutput
, FALSE // bZero
, FALSE // bMany
, &CLSID_NULL // clsConnectsToFilter
, L"Output" // strConnectsToPin
, 3 // nTypes
, psudIpPinTypes } // lpTypes
, { L"Output" // strName
, FALSE // bRendered
, TRUE // bOutput
, FALSE // bZero
, FALSE // bMany
, &CLSID_NULL // clsConnectsToFilter
, L"Input" // strConnectsToPin
, 1 // nTypes
, &sudOpPinTypes } }; // lpTypes



const AMOVIESETUP_FILTER
sudMPEGAudio = { &CLSID_CMpegFrameworkAudioCodec // clsID
, L"MPEG Framework Audio Codec" // strName
, 0x00680000 // dwMerit
, 2 // nPins
, psudPins }; // lpPin


/* -------------------------------------------------------------------------
** list of class ids and creator functions for class factory
** -------------------------------------------------------------------------
*/
CFactoryTemplate g_Templates[] = {
{ L"MPEG Framework Audio Codec"
, &CLSID_CMpegFrameworkAudioCodec
, CMpegAudioCodec::CreateInstance
, NULL
, &sudMPEGAudio }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);


/* -------------------------------------------------------------------------
** Decoder strings
** -------------------------------------------------------------------------
*/
const TCHAR chAudioChannels[] = TEXT("AudioChannels");
const TCHAR chAudioFreqDivider[] = TEXT("AudioFreqDivider");
const TCHAR chAudioQuality[] = TEXT("AudioQuality");
const TCHAR chAudioQuarterInt[] = TEXT("AudioQuarterInt");
const TCHAR chAudioBits[] = TEXT("AudioBits");
const TCHAR chRegistryKey[] =
TEXT("Software\\Microsoft\\Multimedia\\ActiveMovie Filters\\Sample MPEG Audio Decoder");



// --- CMpegAudioCodec ----------------------------------------
CMpegAudioCodec::CMpegAudioCodec(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr)
: CTransformFilter(pName, pUnk, CLSID_CMpegAudioCodec),
m_pAudioDecoder(NULL)
{
m_FreqDiv = GetDecoderInteger(chAudioFreqDivider, 4);
m_PrefChan = GetDecoderInteger(chAudioChannels, 1);
m_Quality = GetDecoderInteger(chAudioQuality, 0);
m_QuarterInt = GetDecoderInteger(chAudioQuarterInt, 0);
m_WordSize = GetDecoderInteger(chAudioBits, 16);
if (m_QuarterInt) {
m_FreqDiv = 4;
}
}

CMpegAudioCodec::~CMpegAudioCodec()
{
delete m_pAudioDecoder;
m_pAudioDecoder = NULL;
}


/******************************Public*Routine******************************\
* CreateInstance
*
* this goes in the factory template table to create new instances
*
*
\**************************************************************************/
CUnknown *
CMpegAudioCodec::CreateInstance(
LPUNKNOWN pUnk,
HRESULT *phr
)
{
DbgLog((LOG_TRACE, 2, TEXT("CMpegAudioCodec::CreateInstance")));
return new CMpegAudioCodec(TEXT("MPEG Audio Codec Filter"), pUnk, phr);
}


/******************************Public*Routine******************************\
* NonDelegatingQueryInterface
*
* Reveals ISpecifyPropertyPages
*
*
\**************************************************************************/
STDMETHODIMP
CMpegAudioCodec::NonDelegatingQueryInterface(
REFIID riid,
void ** ppv
)
{
return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);
}


/******************************Public*Routine******************************\
* EndOfStream
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::EndOfStream()
{
DbgLog((LOG_TRACE, 2, TEXT("End of stream called")));
CAutoLock lck(&m_csReceive);

//
// Here we would normally decode the remainder of the buffer. However,
// the audio codec is designed such that its buffer never contains
// any full frames of coded data, so all we have to do is reset the
// audio codec.
//

if (m_pAudioDecoder == NULL) {
return VFW_E_WRONG_STATE;
}

ResetAudioDecoder();
return CTransformFilter::EndOfStream();
}


/******************************Public*Routine******************************\
* EndFlush
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::EndFlush()
{
DbgLog((LOG_TRACE, 2, TEXT("End flush called")));
CAutoLock lck(&m_csReceive);

ResetAudioDecoder();
return CTransformFilter::EndFlush();
}


/*****************************Private*Routine******************************\
* ProcessDiscontiuity
*
*
\**************************************************************************/
void
CMpegAudioCodec::ProcessDiscontiuity(
IMediaSample *pSample
)
{
ResetAudioDecoder();

//
// Find out what the current stop time is
//

//
// Get logical duration from upstream
//
REFTIME dStart, dStop;
IMediaPosition *pPosition;

if (SUCCEEDED(m_pInput->GetConnected()->QueryInterface(IID_IMediaPosition,
(void **)&pPosition))
&& SUCCEEDED(pPosition->get_CurrentPosition(&dStart))
&& SUCCEEDED(pPosition->get_StopTime(&dStop)))
{
m_tStop = (CRefTime)(COARefTime)dStop - (CRefTime)(COARefTime)dStart;
}
else {
m_tStop = 0x7FFFFFFFFFFFFFFF;
}

if (pPosition != NULL) {
pPosition->Release();
}

DbgLog((LOG_TRACE, 2,
TEXT("Receive() : Discontinuity - setting stop time to %s"),
(LPCTSTR)CDisp(m_tStop)));
}


/*****************************Private*Routine******************************\
* ProcessSyncPoint
*
*
\**************************************************************************/
void
CMpegAudioCodec::ProcessSyncPoint(
IMediaSample *pSample,
BYTE *pSrc
)
{
CRefTime tStart, tStop;

pSample->GetTime((REFERENCE_TIME*)&tStart,
(REFERENCE_TIME*)&tStop);

m_TimeAtLastSyncPoint = tStart;
m_TimeSinceLastSyncPoint = 0;

//
// If our buffer contains a partial audio frame adjust the
// sync point time by one frame. Note that LookForSyncWord advances
// the m_lpCurr buffer pointer, if there is no sync word found
// m_lpCurr should equal m_lpEnd, if the last byte in our audio buffer
// is the first byte of the audio sync word m_lpCurr will equal
// m_lpEnd - 1.
// This can fail because sync words can be found other than at
// frame start!
//
if (LookForSyncWord()) {
m_TimeAtLastSyncPoint -= m_TimePerFrame;
}
else {

ASSERT(m_lpCurr <= m_lpEnd);

//
// Now check that there was not a pertial sync word left in our
// buffer.
//
if (m_lpCurr < m_lpEnd) {
if ((*m_lpCurr == 0xFF) && (pSrc[0] & 0xF0) == 0xF0) {
m_TimeAtLastSyncPoint -= m_TimePerFrame;
}
}
}
}



/*****************************Private*Routine******************************\
* DeliverSample
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::DeliverSample(
IMediaSample *pOutSample,
CRefTime &TimeDecoded,
int nActBytesWritten
)
{
CRefTime tStart, tStop;

//
// decompressed frames are always key
//
pOutSample->SetSyncPoint(TRUE);
pOutSample->SetActualDataLength(nActBytesWritten);

//
// Extrapolate the correct time stamp.
//
tStart = m_TimeAtLastSyncPoint + m_TimeSinceLastSyncPoint;
if (tStart < (LONG)0) {

/* Only play from the start */
}

tStop = tStart + TimeDecoded;
m_TimeSinceLastSyncPoint += TimeDecoded;
pOutSample->SetTime((REFERENCE_TIME*)&tStart,
(REFERENCE_TIME*)&tStop);

DbgLog((LOG_TRACE, 3, TEXT("Writing %ld bytes with time stamp %s"),
nActBytesWritten, (LPCTSTR)CDisp(tStart) ));

return m_pOutput->Deliver(pOutSample);
}


/******************************Public*Routine******************************\
* Receive
*
* Copy the input sample into our buffer. Loop while the buffer size is
* greater than or equal to the size required to decode a frame of audio data.
* Decode the audio frame and pass it along to the output pin for rendering.
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::Receive(
IMediaSample *pSample
)
{
// Make sure the pin doesn't go inactive on us
CAutoLock lck(&m_csReceive);
if (m_pAudioDecoder == NULL) {
return E_UNEXPECTED;
}

IMediaSample *pOutSample;
BYTE *pDst;
BYTE *pSrc;
BYTE *lpPacket;
HRESULT hr;
long LenLeftInBuffer = 0L;
long LenLeftInPacket;

DbgLog((LOG_TRACE, 2, TEXT("CMpegAudioCodec::Receive")));

//
// Check for a discontinuity, if one is found just reset the decoder
// and then continue processing the current sample. We do not have to
// decode any frames left in the audio buffer because the audio codec
// always consumes all the complete audio data frames copied to
// the buffer.
//
if (pSample->IsDiscontinuity() == S_OK) {
ProcessDiscontiuity(pSample);
}

hr = pSample->GetPointer(&pSrc);
if (FAILED(hr)) {
return hr;
}

//
// Get a pointer to the actual mpeg data and determine the
// length of data supplied.
//
if (m_bPayloadOnly) {
lpPacket = pSrc;
LenLeftInPacket = pSample->GetActualDataLength();
}
else {
lpPacket = SkipToPacketData(pSrc, LenLeftInPacket);
if (lpPacket == NULL) {
NotifyEvent(EC_STREAM_ERROR_STILLPLAYING, E_INVALIDARG, 0);
return S_OK;
}
}
DbgLog((LOG_TRACE, 2, TEXT("Left in Packet %ld"), LenLeftInPacket ));


//
// If this sample is a sync point we need to update our clock and
// reset the count of samples received since the last sync point.
// If our buffer contains a partial audio frame we need to make a suitable
// adjustment to the sync point time as the time refers to the first audio
// frame in the current sample which would not necessarly be the first
// sample decoded.
//

if (pSample->IsSyncPoint() == S_OK) {
ProcessSyncPoint(pSample, pSrc);
}


//
// Now, decode the sample data passed to us.
//

do {

LPBYTE lpDstEnd;
CRefTime TimeDecoded;
DWORD rc;
int nActBytesWritten = 0;

GetNextPacketChunk(lpPacket, LenLeftInBuffer, LenLeftInPacket);

DbgLog((LOG_TRACE, 3, TEXT("Left in Buffer %ld"), LenLeftInBuffer ));
DbgLog((LOG_TRACE, 3, TEXT("Left in Packet %ld"), LenLeftInPacket ));

if (LenLeftInBuffer < m_FrameSize && LenLeftInPacket == 0L) {
break;
}

//
// this may block for an indeterminate amount of time
//
hr = m_pOutput->GetDeliveryBuffer(&pOutSample,NULL,NULL,0);
if (FAILED(hr)) {
break;
}
ASSERT(pOutSample);

hr = pOutSample->GetPointer(&pDst);
if (FAILED(hr)) {
break;
}
ASSERT(pDst);


//
// Initialize the audio control structure
//
m_AudioControl.dwNumFrames = 1;
m_AudioControl.dwOutBuffSize = m_pOutput->CurrentMediaType().GetSampleSize();
m_AudioControl.dwOutBuffUsed = 0;
m_AudioControl.pCmprRead = m_lpStart;
m_AudioControl.pCmprWrite = m_lpEnd;


//
// Determine the end of the output buffer so that we don't try to
// decode data beyond it. m_BytesPerFrame is the number of bytes
// one frame of Mpeg audio data will expand to after it has been
// decoded.
//
lpDstEnd = pDst + m_AudioControl.dwOutBuffSize - m_FrameSizeOutput;
TimeDecoded = 0;


//
// While there is an audio frame in the buffer and the frame is
// complete and there is space in the output buffer to store the
// decompressed frame, decompress it !!
//

while (LookForSyncWord()
&& (m_lpCurr + m_FrameSize + Padding() < m_lpEnd)
&& (pDst <= lpDstEnd)) {

m_AudioControl.pOutBuffer = pDst;
m_AudioControl.pCmprRead = m_lpCurr;
m_AudioControl.dwMpegError = 0;

rc = m_pAudioDecoder->DecodeAudioFrame(&m_AudioControl);
switch (rc) {

case 0:
//
// We have successfully decoded an audio frame.
//
if (m_FrameSize == 0L) {
m_FrameSize = (LPBYTE)m_AudioControl.pCmprRead -
m_lpCurr - Padding();
}
m_lpCurr = (LPBYTE)m_AudioControl.pCmprRead;
pDst += m_AudioControl.dwOutBuffUsed;
nActBytesWritten += m_AudioControl.dwOutBuffUsed;
m_AudioControl.dwOutBuffSize -= m_AudioControl.dwOutBuffUsed;
TimeDecoded += m_TimePerFrame;
break;


case 2:
//
// We did not have enough data available to decode the
// current audio frame. This is buffer underflow.
//
DbgLog((LOG_ERROR, 1, TEXT("Buffer underflow !!")));

default:
//
// Some sort of error occurred, throw the remainder of the
// buffer away and skip this packet.
//
DbgLog((LOG_ERROR, 1, TEXT("Bad return code from audio codec!")));
m_lpCurr = m_lpEnd;
NotifyEvent(EC_STREAM_ERROR_STILLPLAYING, E_INVALIDARG, 0);
hr = S_OK;
break;
}
}


//
// Have we decoded any data ? If so we need to deliver the data to
// to the filter that receives our output, usually the audio
// rendering filter.
//

if (TimeDecoded > (LONG)0) {

hr = DeliverSample(pOutSample, TimeDecoded, nActBytesWritten);
if (FAILED(hr)) {
DbgLog((LOG_ERROR, 2,
TEXT("CMpegAudioCodec::Deliver failed 0x%8.8X"), hr));
pOutSample->Release();
break;
}
}

//
// Release the output buffer. If the connected pin still needs it,
// it will have addrefed it itself.
//
pOutSample->Release();


//
// Stop decoding when we have consumed all the data in the input
// media sample.
//

} while (LenLeftInPacket != 0L);

//
// We notify the filter graph of problems but return S_FALSE
// back to the caller to notify end of stream
//

// if (hr != S_OK) {
// NotifyEvent(hr == S_FALSE ? EC_COMPLETE : EC_ERRORABORT, 0, 0);
// }
return hr;
}



/*****************************Private*Routine******************************\
* ResetAudioDecoder
*
*
\**************************************************************************/
void
CMpegAudioCodec::ResetAudioDecoder()
{
m_pAudioDecoder->ResetAudio();
m_lpStart = m_lpCurr = m_lpEnd = &m_Buffer[0];
m_FrameSize = 0L;
}



/******************************Public*Routine******************************\
* GetNextPacketChunk
*
*
\**************************************************************************/
void
CMpegAudioCodec::GetNextPacketChunk(
LPBYTE &lpPacket,
long &LenLeftInBuffer,
long &LenLeftInPacket
)
{
long AmountToCopy;

LenLeftInBuffer = m_lpEnd - m_lpCurr;

//
// Move what remains in the audio data buffer to the top of
// the buffer and append the new audio data to it.
//
AmountToCopy = min( (AUDIO_BUFF_SIZE - LenLeftInBuffer), LenLeftInPacket);

MoveMemory(m_lpStart, m_lpCurr, LenLeftInBuffer);
CopyMemory(m_lpStart + LenLeftInBuffer, lpPacket, AmountToCopy);

LenLeftInPacket -= AmountToCopy;
lpPacket += AmountToCopy;


//
// Adjust the buffer pointers so that m_lpCurr points to
// the start of the buffer and m_lpEnd points to the last
// valid audio byte in buffer.
//
m_lpCurr = m_lpStart;
m_lpEnd = m_lpStart + LenLeftInBuffer + AmountToCopy;
LenLeftInBuffer = m_lpEnd - m_lpCurr;
}


/******************************Public*Routine******************************\
* LookForSyncWord
*
*
\**************************************************************************/
BOOL
CMpegAudioCodec::LookForSyncWord()
{
/* now look for sync */
int sm = 0;

while (m_lpCurr < m_lpEnd && sm < 2) {

switch (sm) {
case 0:
sm = (*m_lpCurr == 0xff);
break;

case 1:
if ((*m_lpCurr & 0xf0) == 0xf0) sm = 2; /* sync found */
else sm = (*m_lpCurr == 0xff);
break;
}
m_lpCurr++;
}

//
// When we get here we have either run out of buffer or found the first
// "sm" bytes of a valid sync word.
//
// Don't forget to put back the sync word bytes that we have just
// read otherwise they would be lost forever.
//
m_lpCurr -= sm;

if (sm < 2) {
return FALSE; // sync not found.
}

return TRUE;
}


/******************************Public*Routine******************************\
* Padding
*
* Returns 1 if the padding bit is set, zero otherwise
*
*
\**************************************************************************/
int
CMpegAudioCodec::Padding()
{
DWORD dw1 = *(UNALIGNED DWORD *)m_lpCurr;

//
// Determine the audio layer for the given frame
//
switch (dw1 & 0x600) {

case 0x200: // Layer 1
return (dw1 & 0x20000) ? 4 : 0;

case 0x400:
case 0x600: // Layer 2 or Layer 3
return (dw1 & 0x20000) ? 1 : 0;

default: // Error !!
return 0;
}
}




/******************************Public*Routine******************************\
* CheckInputType
*
*
* check if you can support mtIn
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::CheckInputType(
const CMediaType* pmtIn
)
{
DbgLog((LOG_TRACE, 2, TEXT("CMpegAudioCodec::CheckInputType")));

// Check for native streams
if (*pmtIn->Type() == MEDIATYPE_Stream &&
*pmtIn->Subtype() == MEDIASUBTYPE_MPEG1Audio) {

/* This will be checked a bit more in Connect() */
return S_OK;
}


// check this is an MPEG audio format type
if (*pmtIn->FormatType() != FORMAT_WaveFormatEx) {
DbgLog((LOG_ERROR, 2, TEXT("\tFormat not FORMAT_WaveFormatEx")));
return E_INVALIDARG;
}

// we only support MEDIATYPE_Audio
if (*pmtIn->Type() != MEDIATYPE_Audio) {
DbgLog((LOG_ERROR, 2, TEXT("\tNot MEDIATYPE_Audio")));
return E_INVALIDARG;
}

if (*pmtIn->Subtype() != MEDIASUBTYPE_MPEG1Packet &&
*pmtIn->Subtype() != MEDIASUBTYPE_MPEG1Payload &&
*pmtIn->Subtype() != MEDIASUBTYPE_MPEG1AudioPayload ) {
DbgLog((LOG_ERROR, 2, TEXT("\tNot MEDIASUBTYPE_MPEG1Packet or Payload")));
return E_INVALIDARG;
}

//
// Make sure that we have not been given layer III data, we don't know
// how to decode layer III.
//
if (((LPMPEG1WAVEFORMAT)pmtIn->pbFormat)->fwHeadLayer == ACM_MPEG_LAYER3) {
DbgLog((LOG_ERROR, 2, TEXT("\tCan't decode layer III data")));
return E_INVALIDARG;
}

CopyMemory(&m_Format, pmtIn->pbFormat, sizeof(MPEG1WAVEFORMAT));
return S_OK;
}



/******************************Public*Routine******************************\
* CheckTransform
*
* check if you can support the transform from this input to this output
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::CheckTransform(
const CMediaType* pmtIn,
const CMediaType* pmtOut
)
{
DbgLog((LOG_TRACE, 2, TEXT("CMpegAudioCodec::CheckTransform")));

// we only output audio
if (*pmtOut->Type() != MEDIATYPE_Audio) {
return S_FALSE;
}

return S_OK;
}


/******************************Public*Routine******************************\
* SetMediaType
*
* overriden to know when the media type is actually set
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::SetMediaType(
PIN_DIRECTION direction,
const CMediaType *pmt
)
{
if (direction == PINDIR_INPUT) {

//
// Get the data type
//
if (*pmt->Subtype() == MEDIASUBTYPE_MPEG1Packet) {
m_bPayloadOnly = FALSE;
}
else {
ASSERT(*pmt->Subtype() == MEDIASUBTYPE_MPEG1Payload ||
*pmt->Subtype() == MEDIASUBTYPE_MPEG1Audio ||
*pmt->Subtype() == MEDIASUBTYPE_MPEG1AudioPayload);
m_bPayloadOnly = TRUE;
}
}
else {

LPWAVEFORMATEX lpWf = (LPWAVEFORMATEX)pmt->pbFormat;
int SamplesPerFrame;

if (m_QuarterInt) {
m_dwCtrl = DECODE_QUART_INT | DECODE_QUARTER;
}
else {
switch (m_FreqDiv) {
case 1:
m_dwCtrl = DECODE_FULL;
break;

case 2:
m_dwCtrl = DECODE_HALF;
break;

case 4:
m_dwCtrl = DECODE_QUARTER;
break;
}
}

switch (lpWf->wBitsPerSample) {
case 16:
m_dwCtrl |= DECODE_16BIT;
break;

case 8:
m_dwCtrl |= DECODE_8BIT;
break;
}

switch (m_Quality) {
case DECODE_HALF_FULLQ:
m_dwCtrl |= DECODE_HALF_FULLQ;
break;

case DECODE_HALF_HIQ:
m_dwCtrl |= DECODE_HALF_HIQ;
break;
}

switch (lpWf->nChannels) {
case 2:
m_dwCtrl |= DECODE_STEREO;
break;

case 1:
m_dwCtrl |= DECODE_MONO;
break;
}

if (m_Format.fwHeadLayer == ACM_MPEG_LAYER1) {
SamplesPerFrame = 384;
}
else {
SamplesPerFrame = 1152;
}

m_TimePerFrame = MulDiv(SamplesPerFrame, 10000000,
m_Format.wfx.nSamplesPerSec);

m_FrameSizeOutput = MulDiv(MulDiv(SamplesPerFrame,
lpWf->wBitsPerSample, 8),
lpWf->nChannels, m_FreqDiv);
}
return S_OK;
}




/******************************Public*Routine******************************\
* GetMediaType
*
* Return our preferred output media types (in order)
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::GetMediaType(
int iPosition,
CMediaType *pmt
)
{
LPWAVEFORMATEX lpWf;
LPWAVEFORMATEX lpWfIn;
LPMPEG1WAVEFORMAT lpWfInMpeg;
CMediaType cmt;
int SamplesPerFrame;
WORD nChannels;
WORD nBitsPerSample;

if (iPosition < 0) {
return E_INVALIDARG;
}

//
// If the requested output word size is 8 bits do not reveal the 16 bits
// per sample option.
//
if (m_WordSize == 8) {
if (iPosition > 0) {
return VFW_S_NO_MORE_ITEMS;
}
iPosition = 1;
}

switch (iPosition) {
case 0:
nBitsPerSample = 16;
break;

case 1:
nBitsPerSample = 8;
break;

default:
return VFW_S_NO_MORE_ITEMS;
}

lpWf = (LPWAVEFORMATEX)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
if (lpWf == NULL) {
return E_OUTOFMEMORY;
}
if (*m_pInput->CurrentMediaType().Type() == MEDIATYPE_Stream) {
lpWfIn = &m_Format.wfx;
lpWfInMpeg = &m_Format;
} else {
lpWfIn = (LPWAVEFORMATEX)m_pInput->CurrentMediaType().pbFormat;
lpWfInMpeg = (LPMPEG1WAVEFORMAT)m_pInput->CurrentMediaType().pbFormat;
}
ASSERT(lpWfIn != NULL);


//
// Dump the input format
//
DbgLog((LOG_TRACE, 2, TEXT("nSamplesPerSec = %ld"), lpWfIn->nSamplesPerSec));
DbgLog((LOG_TRACE, 2, TEXT("nChannels = %hd"), lpWfIn->nChannels));
DbgLog((LOG_TRACE, 2, TEXT("Layer = %hd"), lpWfInMpeg->fwHeadLayer));

//
// If the number of channels available matches the number preferred
// we decode all the channels available. Otherwise we decode the
// minimum of the chanels available and the channels preferred.
//
if (lpWfIn->nChannels == m_PrefChan) {
nChannels = m_PrefChan;
}
else {
nChannels = min(m_PrefChan, lpWfIn->nChannels);
}

lpWf->wFormatTag = WAVE_FORMAT_PCM;
lpWf->nChannels = nChannels;
lpWf->nSamplesPerSec = lpWfIn->nSamplesPerSec / m_FreqDiv;
lpWf->nBlockAlign = (nBitsPerSample * nChannels) / 8;
lpWf->nAvgBytesPerSec = lpWf->nSamplesPerSec * lpWf->nBlockAlign;
lpWf->wBitsPerSample = nBitsPerSample;
lpWf->cbSize = 0;

//
// Dump the ouput format
//
DbgLog((LOG_TRACE, 2, TEXT("!!nSamplesPerSec = %ld"), lpWf->nSamplesPerSec));
DbgLog((LOG_TRACE, 2, TEXT("!!nChannels = %hd"), lpWf->nChannels));
DbgLog((LOG_TRACE, 2, TEXT("!!nBlockAlign = %hd"), lpWf->nBlockAlign));
DbgLog((LOG_TRACE, 2, TEXT("!!wBitsPerSample = %hd"), lpWf->wBitsPerSample));
DbgLog((LOG_TRACE, 2, TEXT("!!nAvgBytesPerSec= %ld\n"), lpWf->nAvgBytesPerSec));

//
// we assume the output format is uncompressed
//
pmt->SetType(&MEDIATYPE_Audio);
pmt->SetSubtype(&GUID_NULL);
pmt->SetFormatType(&FORMAT_WaveFormatEx);
pmt->SetTemporalCompression(FALSE);

//
// The time per frame and output sample size depend on the layer
// of mpeg audio data being compressed.
//
if (lpWfInMpeg->fwHeadLayer == ACM_MPEG_LAYER1) {
SamplesPerFrame = 384;
}
else {
SamplesPerFrame = 1152;

} 

m_TimePerFrame = MulDiv(SamplesPerFrame, 10000000, lpWfIn->nSamplesPerSec);
m_FrameSizeOutput = MulDiv(MulDiv(SamplesPerFrame, nBitsPerSample, 8),
nChannels, m_FreqDiv);

pmt->SetSampleSize(m_FrameSizeOutput * MAX_FRAMES_PER_OUTPUT_SAMPLE);
return S_OK;
}


/******************************Public*Routine******************************\
* DecideBufferSize
*
*
* Called from CBaseOutputPin to prepare the allocator's count
* of buffers and sizes
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::DecideBufferSize(
IMemAllocator *pAllocator,
ALLOCATOR_PROPERTIES * pProperties
)
{
DbgLog((LOG_TRACE, 2, TEXT("CMpegAudioCodec::DecideBufferSize")));

ASSERT(pAllocator);
ASSERT(pProperties);
HRESULT hr = NOERROR;

pProperties->cBuffers = 8;
pProperties->cbBuffer = m_pOutput->CurrentMediaType().GetSampleSize();

ASSERT(pProperties->cbBuffer);
DbgLog((LOG_TRACE, 2, TEXT("Sample size = %ld\n"), pProperties->cbBuffer));

// Ask the allocator to reserve us some sample memory, NOTE the function
// can succeed (that is return NOERROR) but still not have allocated the
// memory that we requested, so we must check we got whatever we wanted

ALLOCATOR_PROPERTIES Actual;
hr = pAllocator->SetProperties(pProperties,&Actual);
if (FAILED(hr)) {
return hr;
}

ASSERT(Actual.cbAlign == 1);
ASSERT(Actual.cbPrefix == 0);

if (Actual.cbBuffer < pProperties->cbBuffer ||
Actual.cBuffers < pProperties->cBuffers) {

// can't use this allocator
return E_INVALIDARG;
}
return S_OK;
}


/******************************Public*Routine******************************\
* StartStreaming
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::StartStreaming()
{
CAutoLock lock(&m_csFilter);

DbgLog((LOG_TRACE, 2, TEXT("CMpegAudioCodec::StartStreaming")));

m_pAudioDecoder = new CAudioDecoder(this);
if (m_pAudioDecoder == NULL) {
return E_OUTOFMEMORY;
}

ResetAudioDecoder();
return S_OK;
}


/******************************Public*Routine******************************\
* StopStreaming
*
*
\**************************************************************************/
HRESULT
CMpegAudioCodec::StopStreaming()
{
CAutoLock lock(&m_csFilter);
CAutoLock lck(&m_csReceive);
DbgLog((LOG_TRACE, 2, TEXT("CMpegAudioCodec::StopStreaming")));

ASSERT(m_pAudioDecoder != NULL);

delete m_pAudioDecoder;
m_pAudioDecoder = NULL;

return S_OK;
}


/******************************Public*Routine******************************\
* GetDecoderInteger
*
*
\**************************************************************************/
int
GetDecoderInteger(
const TCHAR *pKey,
int iDefault
)
{
HKEY hKey;
LONG lRet;
int iRet = iDefault;

lRet = RegOpenKey(HKEY_CURRENT_USER, chRegistryKey, &hKey);
if (lRet == ERROR_SUCCESS) {

DWORD dwType, dwLen;

dwLen = sizeof(iRet);
if (ERROR_SUCCESS != RegQueryValueEx(hKey, pKey, 0L, &dwType,
(LPBYTE)&iRet, &dwLen)) {
iRet = iDefault;
}
RegCloseKey(hKey);
}
return iRet;
}

/******************************Public*Routine******************************\
* SkipToPacketData
*
*
\**************************************************************************/
LPBYTE
SkipToPacketData(
LPBYTE pSrc,
long &LenLeftInPacket
)
{
LPBYTE lpPacketStart;
DWORD bData;
long Length;


//
// Skip the stream ID and extract the packet length
//
pSrc += 4;
bData = *pSrc++;
Length = (long)((bData << 8) + *pSrc++);
DbgLog((LOG_TRACE, 3, TEXT("Packet length %ld"), Length ));


//
// Record position of first byte after packet length
//
lpPacketStart = pSrc;


//
// Remove stuffing bytes
//
for (; ; ) {
bData = *pSrc++;
if (!(bData & 0x80)) {
break;
}
}

if ((bData & 0xC0) == 0x40) {
pSrc++;
bData = *pSrc++;
}

switch (bData & 0xF1) {

case 0x21:
pSrc += 4;
break;

case 0x31:
pSrc += 9;
break;

default:
if (bData != 0x0F) {
DbgLog((LOG_TRACE, 2, TEXT("Invalid packet - 0x%2.2X\n"), bData));
return NULL;
}
}

//
// The length left in the packet is the original length of the packet
// less those bytes that we have just skipped over.
//
LenLeftInPacket = Length - (pSrc - lpPacketStart);
return pSrc;
}


/******************************Public*Routine******************************\
* exported entry points for registration and
* unregistration (in this case they only call
* through to default implmentations).
*
*
*
* History:
*
\**************************************************************************/
STDAPI
DllRegisterServer()
{
return AMovieDllRegisterServer2( TRUE );
}

STDAPI
DllUnregisterServer()
{
return AMovieDllRegisterServer2( FALSE );
}