//==========================================================================;
//
//  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: MpgVideo.cpp
*
* Implements a prototype Mpeg Video Software codec.  It just consumes
* the passed in packets.
*
\**************************************************************************/
#include "MpgVideo.h"

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


// setup data

const AMOVIESETUP_MEDIATYPE
psudIpPinTypes[] = { { &MEDIATYPE_Video                // clsMajorType
                     , &MEDIASUBTYPE_MPEG1Packet  }    // clsMinorType
                   , { &MEDIATYPE_Video                // clsMajorType
                     , &MEDIASUBTYPE_MPEG1Payload } }; // clsMinorType

const AMOVIESETUP_MEDIATYPE
sudOpPinTypes = { &MEDIATYPE_Video      // 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
               , 2                   // 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
sudMPEGVideo = { &CLSID_CMpegFrameworkVideoCodec // clsID
               , L"MPEG Framework Audio Codec"   // strName
               , 0x00600000                      // dwMerit
               , 2                               // nPins
               , psudPins };                     // lpPin

/* -------------------------------------------------------------------------
** list of class ids and creator functions for class factory
** -------------------------------------------------------------------------
*/
CFactoryTemplate g_Templates[] =
{
    {L"MPEG Framework Video Codec"
    , &CLSID_CMpegFrameworkVideoCodec
    , CMpegVideoCodec::CreateInstance
    , NULL
    , &sudMPEGVideo }
};

int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);


const TCHAR chRegistryKey[] =
    TEXT("Software\\Microsoft\\Multimedia\\ActiveMovie Filters\\Sample MPEG Video Decoder");


/******************************Public*Routine******************************\
* GetDecoderInt
*
*
*
* History:
*
*
\**************************************************************************/
int
GetDecoderInt(
    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;
}



/* -------------------------------------------------------------------------
** CMpegVideoCodec
** -------------------------------------------------------------------------
*/
CMpegVideoCodec::CMpegVideoCodec(
    TCHAR *pName,
    LPUNKNOWN pUnk,
    HRESULT *phr
    )
    : CTransformFilter(pName, pUnk, CLSID_CMpegVideoCodec),
      m_pVideoDecoder(NULL),
      m_PtsQueue(NAME("Pts queue"), 30, FALSE, FALSE),
      m_pFrameBuff(NULL),
      m_Buffer(NULL),
      m_pOutSample(NULL)
{
    //
    // Pick up any user preferences - this should get moved to the register
    //
    m_IgnoreQualityMessage = GetDecoderInt(TEXT("IgnoreQMessages"), FALSE);

    m_dwCtrlDefault = GetDecoderInt(TEXT("VideoFramesDecoded"), DECODE_IPB);
    m_dwCtrlDefault &= 0x3F;

    m_dwQualDefault = GetDecoderInt(TEXT("VideoQuality"), 0);
    m_dwQualDefault &= 0x30000000;
    m_dwCtrl = (m_dwCtrlDefault | m_dwQualDefault);


    if (GetDecoderInt(TEXT("GreyScale"), 0)) {
        m_PaletteType = GREY_PALETTE;
        m_dwOutputFormatDib = MM_Y_DIB;
        m_dwOutputFormatDdb = MM_Y_DDB;
    }
    else {
        m_PaletteType = COLOUR_PALETTE;
        m_dwOutputFormatDib = MM_RGB8_DIB;
        m_dwOutputFormatDdb = MM_RGB8_DDB;
    }

    //
    // Reset frame stats
    //
    ZeroMemory(m_dwFramesSkipped, sizeof(m_dwFramesSkipped));
    ZeroMemory(m_dwFramesDecoded, sizeof(m_dwFramesDecoded));

    m_PerfDecode = MSR_REGISTER(TEXT("Decode Time  - Start/Stop"));
    m_QualMsg = MSR_REGISTER(TEXT("Quality Message"));
    m_FrameDrawn = MSR_REGISTER(TEXT("Frame Drawn"));
    m_FrameType = MSR_REGISTER(TEXT("Frame Type"));
}


CMpegVideoCodec::~CMpegVideoCodec(
    )
{
    CTimePosition   *pTimePos;

    DbgLog((LOG_TRACE, 2, TEXT("CMpegVideoCodec::~CMpegVideoCodec")));

    delete m_pVideoDecoder;
    m_pVideoDecoder = NULL;

    delete [] m_pFrameBuff;
    m_pFrameBuff = NULL;

    delete [] m_Buffer;
    m_Buffer = NULL;


    //
    // Purge the PTS queue.
    //
    while( (pTimePos = m_PtsQueue.RemoveHead()) != NULL) {
        delete pTimePos;
    }

    //
    // This should have been deleted in BreakConnect
    //
    ASSERT(m_Buffer == NULL);
}



/******************************Public*Routine******************************\
* CreateInstance
*
* This goes in the factory template table to create new instances
*
\**************************************************************************/
CUnknown *
CMpegVideoCodec::CreateInstance(
    LPUNKNOWN pUnk,
    HRESULT * phr
    )
{
    DbgLog((LOG_TRACE, 2, TEXT("CMpegVideoCodec::CreateInstance")));
    return new CMpegVideoCodec(TEXT("MPEG Video codec filter"), pUnk, phr);
}


/******************************Public*Routine******************************\
* NonDelegatingQueryInterface
*
* Here we would reveal ISpecifyPropertyPages and IMpegVideoDecoder if
* the framework had a property page.
*
\**************************************************************************/
STDMETHODIMP
CMpegVideoCodec::NonDelegatingQueryInterface(
    REFIID riid,
    void ** ppv
    )
{
    return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);
}


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

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

    DecodeUntilBufferEmpty();
    ResetVideoDecoder();
    return CTransformFilter::EndOfStream();
}


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


/******************************Public*Routine******************************\
* Receive
*
* Copy the input sample into our buffer.  Loop while the buffer size is
* greater than or equal to the size given in the Vbv for the next picture
* decode the picture and pass it along to the output pin for rendering.
*
\**************************************************************************/
HRESULT
CMpegVideoCodec::Receive(
    IMediaSample *pSample
    )
{
    //
    //  Make sure the pin doesn't go inactive on us
    //

    HRESULT   hr;
    CAutoLock lck(&m_csReceive);

    if (m_pVideoDecoder == NULL || m_pFrameBuff == NULL) {
        return E_UNEXPECTED;
    }

    //
    // Check for a discontinuity, if one is found decode and display any
    // frames left in the circular buffer, reset the decoder and then continue
    // processing the current sample.
    //
    if (pSample->IsDiscontinuity() == S_OK) {

        DecodeUntilBufferEmpty();
        ResetVideoDecoder();

        //
        //  Find out what the current stop time is...
        //  ... get logical duration from upstream
        //
        HRESULT hrPos;
        REFTIME dStart, dStop;
        IMediaPosition *pPosition = NULL;

        hrPos = m_pInput->GetConnected()->QueryInterface(IID_IMediaPosition,
                                                         (void **)&pPosition);

        if ( SUCCEEDED(hrPos)
          && pPosition != NULL
          && 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)));
    }

    m_pSample = NULL;
    m_LenLeftInPacket = 0L;

    //
    // 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 (pSample->IsSyncPoint() == S_OK) {

        CRefTime        tStart, tStop;
        CTimePosition   *pTimePos;

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

        pTimePos = new CTimePosition(&tStart,
                                     m_BufferCurr + m_BufferFullness >= m_BufferStart + m_VBlockSize ?
                                     m_BufferCurr + m_BufferFullness - m_VBlockSize : m_BufferCurr + m_BufferFullness);

        DbgLog(( LOG_TRACE, 4, TEXT("PTS: %s  Buffer pos: %ld"),
                 (LPCTSTR)CDisp(tStart), m_BufferCurr + m_BufferFullness ));
        m_PtsQueue.AddTail(pTimePos);

    }

    //
    // Decode and if necessary display as many frames as
    // possible until we exhaust our circular buffer.
    //

    do {

        hr = CopySampleToBuffer(pSample);

        while (SUCCEEDED(hr) && (m_BufferFullness >= m_seqInfo.lvbv)) {

            hr = DecodeNextPicture();
        }

    } while (S_OK == hr && (m_LenLeftInPacket != 0L));

    return hr;
}


/*****************************Private*Routine******************************\
* ResetVideoDecoder
*
*
*
\**************************************************************************/
void
CMpegVideoCodec::ResetVideoDecoder()
{
    //
    // Make him eat our sequence header
    //
    // m_BufferStart = m_BufferCurr = m_Buffer->GetPointer();
    m_BufferStart = m_BufferCurr = m_Buffer;
    m_BufferFullness = m_seqInfo.lActualHeaderLen;

    CopyMemory(m_BufferStart, m_seqInfo.RawHeader, m_seqInfo.lActualHeaderLen);

    m_TimeSinceLastSyncPoint = 0L;
    m_pOutSample = NULL;

    //
    // Don't play anything until we get a sync point
    // but don't make it too negative or we'll overflow!
    //
    m_TimeAtLastSyncPoint = (LONGLONG)-0x7FFFFFFFFFFFFF;
    m_tStopPrev = m_TimeAtLastSyncPoint;

    m_pVideoDecoder->ResetVideo();


    //
    // Purge the PTS queue.
    //
    CTimePosition *pTimePos;
    while( (pTimePos = m_PtsQueue.RemoveHead()) != NULL) {
        delete pTimePos;
    }


    //
    // Reset IP tracking
    //
    m_NextIP.Reset();

}


/*****************************Private*Routine******************************\
* DecodeUntilBufferEmpty
*
*
*
\**************************************************************************/
void
CMpegVideoCodec::DecodeUntilBufferEmpty()
{
    HRESULT hr = S_OK;

    while (SUCCEEDED(hr) && (m_BufferFullness > 0)) {

        LPBYTE pCurrentCurr = m_BufferCurr;

        DbgLog((LOG_TRACE, 3, TEXT("Forcing out a frame")));
        hr = DecodeNextPicture();

        if (S_OK != hr || m_BufferCurr == pCurrentCurr) {

            //
            // We're not making any progress!
            //
            DbgLog((LOG_ERROR, 2,
                    TEXT("CMpegVideoCodec::DecodeUntilBufferEmpty() ")
                    TEXT("- Stuck with %d bytes in buffer"),
                    m_BufferFullness));
            break;
        }
    }

    //
    // Now see if there's a frame decoded but not presented
    //
    if (m_NextIP.TimeToDraw()) {

        DbgLog((LOG_TRACE, 2, TEXT("Trying to get last frame")));

        DWORD dwCtrl = m_dwCtrl;
        m_dwCtrl &= 0xFFFF0000;
        m_dwCtrl |= DECODE_DIS;

        if (S_OK != DecodeNextPicture()) {
            DbgLog((LOG_ERROR, 2, TEXT("Failed to display last frame")));
        }

        m_dwCtrl = dwCtrl; // Restore
    }
}


/*****************************Private*Routine******************************\
* GetDecodeBufferAndFormat
*
* This function is called by the mpeg decoder when it is time to determine
* the colour conversion format and the destination buffer pointer for the
* frame.
*
\**************************************************************************/
LPBYTE
CMpegVideoCodec::GetDecodeBufferAndFormat()
{
    unsigned char   *pDst;
    AM_MEDIA_TYPE      *pmt;
    CRefTime        tStartTime(m_tStopPrev);
    CRefTime        tStopTime;

    // Don't ask for a buffer with a negative start time.
    if (tStartTime < (LONGLONG)0) {
        tStartTime = (LONGLONG)0;
    }

    //
    // this may block for an indeterminate amount of time
    //
    tStopTime = tStartTime + m_seqInfo.tPictureTime;
    hrCallback = m_pOutput->GetDeliveryBuffer(&m_pOutSample,
                                              (REFERENCE_TIME*)&tStartTime,
                                              (REFERENCE_TIME*)&tStopTime,
                                              0 // Must set proper flag
                                                // if prev frame skipped
                                              );
    if (FAILED(hrCallback)) {
        return NULL;
    }
    ASSERT(m_pOutSample);

    hrCallback = m_pOutSample->GetPointer(&pDst);
    if (FAILED(hrCallback)) {
        return NULL;
    }
    ASSERT(pDst);

    //
    // If the media type has changed then pmt is NOT NULL
    //
    m_pOutSample->GetMediaType(&pmt);
    if (pmt != NULL) {
        CMediaType cmt(*pmt);
        DeleteMediaType(pmt);
        SetOutputPinMediaType(&cmt);
    }
    return pDst;
}


/*****************************Private*Routine******************************\
* DecodeNextPicture
*
* Decodes the next picture stored in the circular buffer.  If the picture
* is not "skipped" it is passed to the output pin.  Updates m_BufferStart,
* m_BufferCurr and m_BufferFullness to reflect any emptying of the buffer that
* has taken place.
*
\**************************************************************************/
HRESULT
CMpegVideoCodec::DecodeNextPicture()
{
    DWORD   rc;
    HRESULT hr = S_OK;

    //
    // ParthaSr.
    // trying to recover from the absence of quality messages
    // when I do a skip - so I am predictively skipping
    // frames.  - Snap back to default if I have already
    // skipped enough frames
    //
    if (m_dwLateBy < 750000L) {
        m_dwCtrl = m_dwCtrlDefault | m_dwQualDefault;
    }

    m_VideoControl.dwCtrl = m_dwCtrl;

    //
    // If we're within 1 second of the end do all IP
    //
    if (m_tStop - m_tStopPrev < CRefTime(1000L)) {

        //
        // But are we almost at the end? in which case we need to decode
        // everything
        //
        if (m_tStop - m_tStopPrev < CRefTime(m_seqInfo.tPictureTime) +
                                    CRefTime(m_seqInfo.tPictureTime)) {

            if ((m_dwCtrl & 0xFFFF) != DECODE_DIS) {
                m_VideoControl.dwCtrl = (m_dwCtrl & 0xFFFF0000) | DECODE_IPB;
            }
        }
        else {

            if ((m_dwCtrl & 0xFFFF) < DECODE_IP) {
                m_VideoControl.dwCtrl = (m_dwCtrl & 0xFFFF0000) | DECODE_IP;
            }
        }
    }
    else {

        //
        // Only do IP if we're before the start
        //
        if ((m_dwCtrl & 0x3F) > DECODE_IP) {

            if (m_tStopPrev
              + CRefTime(m_seqInfo.tPictureTime)
              + CRefTime(m_seqInfo.tPictureTime) < CRefTime(0L)) {

                m_VideoControl.dwCtrl = (m_dwCtrl & 0xFFFF0000) | DECODE_IP;
            }

        }
    }
    DbgLog((LOG_TRACE, 4, TEXT("Decode flags = %X"), m_VideoControl.dwCtrl));


    //
    // Initialize the video codec with the new video data buffer.  I am
    // assuming that there is at least one video frame in the data
    // buffer.  When this function gets called to force out the frames
    // left in the buffer it needs to guarded with a try except block.
    //
    m_VideoControl.pCmprWrite  = m_BufferCurr + m_BufferFullness;
    m_VideoControl.pCmprRead   = m_BufferCurr;

    //
    // This is where the action starts, call the codec and let it do it stuff.
    //

    //  Don't die if Windows NT takes away the DCI surface
    try {

        MSR_START(m_PerfDecode);
        rc = m_pVideoDecoder->DecodeFrame(&m_VideoControl);
        MSR_STOP(m_PerfDecode);
    }
    except(EXCEPTION_EXECUTE_HANDLER) {

        DbgLog((LOG_ERROR, 1, TEXT("Exception in decoder!")));
        rc = DECODE_ERR_DATA;
    }

    //
    // Did the frame decode OK
    //

    if (rc == DECODE_SUCCESS) {

#ifdef DEBUG
        static char *ft[4] = { "Dummy", "I", "P", "B" };
#endif

        //
        // Record frames stats
        //
        MSR_INTEGER(m_FrameType, m_VideoControl.dwFrameType);
        if (m_VideoControl.dwSkipFlag) {

            DbgLog((LOG_TRACE, 2, TEXT("%hs Frame skipped"),
                    ft[m_VideoControl.dwFrameType]));

            m_dwFramesSkipped[m_VideoControl.dwFrameType]++;

            LARGE_INTEGER li;
            li.QuadPart = m_seqInfo.tPictureTime;
            if (m_dwLateBy > li.LowPart) {
                m_dwLateBy -= li.LowPart;
            }
        }
        else {
            m_dwFramesDecoded[m_VideoControl.dwFrameType]++;
        }

        if (m_VideoControl.dwFrameType == FTYPE_I ||
            m_VideoControl.dwFrameType == FTYPE_P) {

            //
            //  Do I/P - we're actually going to draw the I/P we
            //  decoded last time we got one, not the present one
            //

            if (m_NextIP.GetTime((REFERENCE_TIME *)&m_TimeAtLastSyncPoint)) {
                m_TimeSinceLastSyncPoint = 0;
            }

            REFERENCE_TIME t;
            BOOL bIFrame = m_VideoControl.dwFrameType == FTYPE_I;

            if (UpdateTimeSyncPoint(m_VideoControl.pFrameStartPos, &t)) {
                m_NextIP.Set(m_VideoControl.dwSkipFlag, bIFrame,
                             TRUE, t, m_VideoControl.dwTemporalReference);
            }
            else {
                m_NextIP.Set(m_VideoControl.dwSkipFlag, bIFrame,
                             FALSE, t, m_VideoControl.dwTemporalReference);
            }
        }
        else {

            //
            //  Do B
            //
            m_NextIP.NextRef(m_VideoControl.dwTemporalReference);
            if ( UpdateTimeSyncPoint(m_VideoControl.pFrameStartPos,
                                     (REFERENCE_TIME*)&m_TimeAtLastSyncPoint) ) {
                m_TimeSinceLastSyncPoint = 0;
            }
        }

        CRefTime tStop(m_TimeAtLastSyncPoint + m_TimeSinceLastSyncPoint +
                       m_seqInfo.tPictureTime);

        if (!m_VideoControl.dwSkipFlag) {

            LPBITMAPINFOHEADER  lpbiDst = HEADER(m_pOutput->CurrentMediaType().Format());

            //
            // if the time is < 0, then this is preroll to get from keyframe to
            // the current frame. We decompress it into the output buffer but
            // don't deliver it.
            //
            // In order that there are no gaps we actually start the next
            // frame where we predicted it would start, rather than the
            // actual synch point
            //

            CRefTime tStart(m_tStopPrev);

            DbgLog((LOG_TRACE, 2,
                    TEXT("%hs Frame decoded - tStart = %s, tStop = %s"),
                    ft[m_VideoControl.dwFrameType],
                    (LPCTSTR)CDisp(tStart), (LPCTSTR)CDisp(tStop)));


            #pragma message (REMIND("Do Preroll right"))
            if (tStop > 0L && tStart <= m_tStop) {

                // decompressed frames are always key
                m_pOutSample->SetSyncPoint(TRUE);
                m_pOutSample->SetActualDataLength(lpbiDst->biSizeImage);
                m_pOutSample->SetTime((REFERENCE_TIME*)&tStart,
                                      (REFERENCE_TIME*)&tStop);

                DbgLog((LOG_TRACE, 2, TEXT("%hs Frame sent to next filter"),
                        ft[m_VideoControl.dwFrameType]));


                hr = m_pOutput->Deliver(m_pOutSample);
                MSR_NOTE(m_FrameDrawn);
            }
        }

        //
        // We have successfully decoded a frame.
        // Set the new position
        //
        m_tStopPrev = tStop;
        UpdateBufferPosition(m_VideoControl.pCmprRead);
        m_TimeSinceLastSyncPoint += m_seqInfo.tPictureTime;
    }
    else if (rc == DECODE_ERR_QUARTZ) {

        DbgLog((LOG_ERROR, 2,
                TEXT("Could not get buffer from down stream filter")));
        hr = hrCallback;
    }
    else if (rc == DECODE_ERR_DATA) {

        //
        // We did not have enough data available to decode the
        // current frame, so save the data for next time.
        //
        // Since this can only happen at a discontinuity, end of
        // stream or undecipherable data we'll just throw the data
        // away if it can't be eaten
        DbgLog((LOG_ERROR, 2, TEXT("Buffer underflow") ));

        UpdateBufferPosition(m_VideoControl.pCmprRead);

        //
        //  Notify the filter graph of stream errors
        //
        NotifyEvent(EC_STREAM_ERROR_STILLPLAYING, hr, 0);
    }
    else {

        //
        // Some sort of error occurred, throw the remainder of the
        // buffer away and skip this packet.
        //
        DbgLog((LOG_ERROR, 2,
                TEXT("Bad return code %d from MediaMatics video codec!"), rc ));
        UpdateBufferPosition(m_VideoControl.pCmprRead);

        //
        //  Notify the filter graph of stream errors
        //
        NotifyEvent(EC_STREAM_ERROR_STILLPLAYING, hr, 0);
    }


    //
    // release the output buffer. If the connected pin still needs it,
    // it will have addrefed it itself.
    //
    if (m_pOutSample != NULL) {
        m_pOutSample->Release();
        m_pOutSample = NULL;
    }

    return hr;
}


/*****************************Private*Routine******************************\
* CopySampleToBuffer
*
* Copies the sample to the input buffer and returns the number of bytes
* present in the buffer.  Updates m_BufferStart, m_BufferCurr and
* m_BufferFullness to reflect any wrapping in the buffer
* that may have occurred.
*
\**************************************************************************/
HRESULT
CMpegVideoCodec::CopySampleToBuffer(
    IMediaSample *pSample
    )
{
    BYTE    *pSrc;
    long    LenLeftInBuffer;
    long    AmountToCopy;
    HRESULT hr;

    if (m_pSample == NULL) {

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

        // Skip pass the stream header and extract the packet length.
        //
        if (m_bPayloadOnly) {
            m_pSample = pSrc;
            m_LenLeftInPacket = pSample->GetActualDataLength();
        }
        else {
            m_pSample = SkipToPacketData(pSrc, m_LenLeftInPacket);
            if (m_pSample == NULL) {
                return E_INVALIDARG;
            }
        }
    }


    //
    // Move what remains in the video data buffer to the top of the buffer
    // and append the new video data to it.
    // Don't ever let it fill up completely or we'll have to worry about
    // full vs empty
    //
    LenLeftInBuffer = m_VBlockSize - m_BufferFullness;

    AmountToCopy = min(LenLeftInBuffer, m_LenLeftInPacket);
    CopyMemory(m_BufferCurr + m_BufferFullness, m_pSample, AmountToCopy);


    m_LenLeftInPacket -= AmountToCopy;
    m_pSample         += AmountToCopy;
    m_BufferFullness  += AmountToCopy;

    return S_OK;
}


/*****************************Private*Routine******************************\
* UpdateBufferPosition
*
* Updates m_BufferCurr from the new position passed in.
* Checks if m_BufferCurr is in the 'virtual' space at the end of the buffer
* and if it is adjusts m_BufferCurrent and m_BufferFullness down by the real
* buffer size
*
\**************************************************************************/
void
CMpegVideoCodec::UpdateBufferPosition(
    LPBYTE lpNewCurrent
    )
{
    ASSERT(lpNewCurrent >= m_BufferCurr);

    //
    // Sometimes the position gets reported as 1 too many (!)
    //
    if (lpNewCurrent > m_BufferCurr + m_BufferFullness) {
        lpNewCurrent = m_BufferCurr + m_BufferFullness;
    }
    m_BufferFullness -= lpNewCurrent - m_BufferCurr;
    if (lpNewCurrent >= (m_BufferStart + m_VBlockSize)) {
        lpNewCurrent -= m_VBlockSize;
    }

    //
    // Fix up the time code list
    //
    while (TRUE) {

        POSITION pos = m_PtsQueue.GetHeadPosition();
        if (pos == NULL) {
            break;
        }

        CTimePosition *pTimePos = m_PtsQueue.Get(pos);

        if (BuffOffset(pTimePos->m_BufferPosition) < BuffOffset(lpNewCurrent)) {
            delete pTimePos;
            m_PtsQueue.RemoveHead();
        }
        else {
            break;
        }
    }

    //
    //  Advance to the new position
    //
    // m_BufferCurr = lpNewCurrent;
    MoveMemory(m_BufferCurr, lpNewCurrent, m_BufferFullness);
}




/*****************************Private*Routine******************************\
* UpdateTimeSyncPoint
*
* Each time we get a media sample with a Pts
* time stamp we add a TIMEPOSITION entry to a queue of time positions.
* Each time we decode an I frame we record the starting and ending position
* of the I frame picture within the input buffer, this information is
* then used to find a suitable time stamp to associate with the frame.  If
* a suitable time stamp cannot be found or the frame is not an I frame we
* calculate a suitable time code by extrapolation.
*
\**************************************************************************/
BOOL
CMpegVideoCodec::UpdateTimeSyncPoint(
    LPBYTE lpPicStart,
    REFERENCE_TIME *Time
    )
{
    POSITION  pos = m_PtsQueue.GetHeadPosition();
    BOOL      bFound;

    for (bFound = FALSE; pos != NULL; ) {
        CTimePosition *pTimePos = m_PtsQueue.GetNext(pos);

        if (BuffOffset(pTimePos->m_BufferPosition) <= BuffOffset(lpPicStart)) {

            //  Buffer start time stamp could be for us (but keep looking
            //  in case there's a better one)
            //
            //  NOTE this ASSUMES there are no packets with time stamps but no
            //  start code so in fact this one should be ours

            bFound = TRUE;
            *Time  = pTimePos->m_PtsTimeStamp;
            delete pTimePos;
            m_PtsQueue.RemoveHead();
        }
        else {
            break;
        }
    }

    if (bFound) {
        DbgLog((LOG_TRACE, 3,
                TEXT("CMpegVideoCodec::UpdateTimeSyncPoint() : Found time %s"),
                (LPCTSTR)CDisp(*Time) ));
    }

    return bFound;
}



/*****************************Private*Routine******************************\
* BuffOffset
*
* Adjusts the supplied offset so that is based upon m_BufferCurr
*
\**************************************************************************/
inline ptrdiff_t
CMpegVideoCodec::BuffOffset(
    LPBYTE Offset
    )
{
    ptrdiff_t x = Offset - m_BufferCurr;
    if (x < 0) {
        x += m_VBlockSize;
    }
    return x;
}


/******************************Public*Routine******************************\

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

    //
    //  Check for native streams
    //

    if (*pmtIn->Type() == MEDIATYPE_Stream &&
        *pmtIn->Subtype() == MEDIASUBTYPE_MPEG1Video) {

        //
        //  If there's no format block we'll read the stream during
        //  CompleteConnect()
        //
        if (pmtIn->cbFormat == 0) {
            return S_OK;
        }
    }
    else {
        //
        // check this is an MPEG video format type
        //
        if (*pmtIn->FormatType() != FORMAT_MPEGVideo) {
            return E_INVALIDARG;
        }

        //
        // we only support MEDIATYPE_Video
        //
        if (*pmtIn->Type() != MEDIATYPE_Video) {
            return E_INVALIDARG;
        }

        if (*pmtIn->Subtype() != MEDIASUBTYPE_MPEG1Packet &&
            *pmtIn->Subtype() != MEDIASUBTYPE_MPEG1Payload) {
            return E_INVALIDARG;
        }
    }

    if (pmtIn->cbFormat < SIZE_VIDEOHEADER + sizeof(DWORD) ||
        pmtIn->cbFormat < SIZE_MPEG1VIDEOINFO((MPEG1VIDEOINFO *)pmtIn->pbFormat)) {
        return E_INVALIDARG;
    }

    //
    // Check the sequence header and save the info
    //

    MPEG1VIDEOINFO* videoInfo = (MPEG1VIDEOINFO *)pmtIn->pbFormat;
    if (!ParseSequenceHeader(videoInfo->bSequenceHeader,
                             videoInfo->cbSequenceHeader, &m_seqInfo)) {
        return E_INVALIDARG;
    }

    return S_OK;
}


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

    // we only output video
    if (*pmtOut->Type() != MEDIATYPE_Video) {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }

    // Check there is a format block
    if (*pmtOut->FormatType() != FORMAT_VideoInfo) {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }

    //
    // See if we can use dci/direct draw.
    // First check that there is a non empty target rectangle.
    //

    VIDEOINFO *videoInfo = (VIDEOINFO *)pmtOut->pbFormat;
    if (!IsRectEmpty(&videoInfo->rcTarget)) {

        //
        // Next, check that the source rectangle is the entire movie.
        //

        if ( videoInfo->rcSource.left   == 0
          && videoInfo->rcSource.top    == 0
          && videoInfo->rcSource.right  == m_seqInfo.lWidth
          && videoInfo->rcSource.bottom == m_seqInfo.lHeight) {

            //
            // Now check that the target rectangles size is the same as
            // the movies, that is there is no stretching or shrinking.
            //

            if ( (videoInfo->rcTarget.right - videoInfo->rcTarget.left)
                    == m_seqInfo.lWidth
              && (videoInfo->rcTarget.bottom - videoInfo->rcTarget.top)
                    == m_seqInfo.lHeight) {
#ifndef _X86_
                // On Risc machines make sure we are DWORD aligned
                if ((videoInfo->rcTarget.left & 0x03) == 0x00)
#endif
                {
                    DbgLog((LOG_TRACE, 2, TEXT("Using DCI")));
                    return S_OK;
                }
            }
        }
        DbgLog((LOG_TRACE, 2, TEXT("NOT Using DCI")));
        return E_FAIL;
    }


    return S_OK;
}


/******************************Public*Routine******************************\
* SetMediaType
*
* Overriden to know when the media type is actually set
*
\**************************************************************************/
HRESULT
CMpegVideoCodec::SetMediaType(
    PIN_DIRECTION direction,
    const CMediaType *pmt
    )
{
    DbgLog((LOG_TRACE, 2, TEXT("CMpegVideoCodec::SetMediaType")));

    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_MPEG1Video);
            m_bPayloadOnly = TRUE;
        }
    }
    else {
        SetOutputPinMediaType(pmt);
    }
    return S_OK;
}


/*****************************Private*Routine******************************\
* SetOutputPinMediaType
*
* This function is a static member function of CMpegVideoCodec, this is so that
* it can be called from SetMediaType (above) and from the static member
* function GetDecodeBufferAndFormat.  Note that we pass in the "this" pointer
* explicitly.
*
\**************************************************************************/
void
CMpegVideoCodec::SetOutputPinMediaType(
    const CMediaType *pmt
    )
{
    VIDEOINFO   *pvi;
    LONG        lStride;
    LONG        lOffset;

    if (*pmt->Subtype() == MEDIASUBTYPE_Y41P) {
        m_dwOutputFormatDib = MM_411PK;
        m_dwOutputFormatDdb = MM_411PK;
    }
    else if (*pmt->Subtype() == MEDIASUBTYPE_YUY2) {
        m_dwOutputFormatDib = MM_422PK;
        m_dwOutputFormatDdb = MM_422PK;
    }
    else if (*pmt->Subtype() == MEDIASUBTYPE_UYVY) {
        m_dwOutputFormatDib = MM_422SPK;
        m_dwOutputFormatDdb = MM_422SPK;
    }
    else if (*pmt->Subtype() == MEDIASUBTYPE_RGB24) {
        m_dwOutputFormatDib = MM_RGB24_DIB;
        m_dwOutputFormatDdb = MM_RGB24_DDB;
    }
    else if (*pmt->Subtype() == MEDIASUBTYPE_RGB565) {
        m_dwOutputFormatDib = MM_RGB565_DIB;
        m_dwOutputFormatDdb = MM_RGB565_DDB;
    }
    else if (*pmt->Subtype() == MEDIASUBTYPE_RGB555) {
        m_dwOutputFormatDib = MM_RGB555_DIB;
        m_dwOutputFormatDdb = MM_RGB555_DDB;
    }
    else {
        ASSERT(*pmt->Subtype() == MEDIASUBTYPE_RGB8);
        if (m_PaletteType == COLOUR_PALETTE) {
            m_dwOutputFormatDib = MM_RGB8_DIB;
            m_dwOutputFormatDdb = MM_RGB8_DDB;
        }
        else {
            m_dwOutputFormatDib = MM_Y_DIB;
            m_dwOutputFormatDdb = MM_Y_DDB;
        }
    }

    //
    // lStride is the distance between in bytes between a pel on the
    // screen and the pel directly underneath it.
    //

    pvi = (VIDEOINFO *)pmt->pbFormat;
    lStride = ((pvi->bmiHeader.biWidth * pvi->bmiHeader.biBitCount) + 7) / 8;
    lStride = (lStride + 3) & ~3;


    //
    // lOffset is the distance in bytes from the top corner of the
    // target bitmap to the top corner of the video image.  When we are
    // using DIBs this value allways be zero.
    //
    // When we are using DCI/DirectDraw this value will only be zero if
    // we are drawing the video image at the top left hand corner of the
    // display.
    //

    lOffset = (((pvi->rcTarget.left * pvi->bmiHeader.biBitCount) + 7) / 8) +
                (pvi->rcTarget.top * lStride);

    m_VideoControl.dwOutStride = lStride;
    m_VideoControl.dwOutOffset = lOffset;


    //
    // See what orientation we need to use when colour converting
    // the frame.
    //

    if (pvi->bmiHeader.biHeight > 0) {
        m_VideoControl.dwOutputFormat = m_dwOutputFormatDib;
    }
    else {
        m_VideoControl.dwOutputFormat = m_dwOutputFormatDdb;
    }
}


/******************************Public*Routine******************************\
* GetMediaType
*
* Return our preferred output media types (in order)
*
\**************************************************************************/
HRESULT
CMpegVideoCodec::GetMediaType(
    int iPosition,
    CMediaType *pmt
    )
{
    VIDEOINFO   *pVideoInfo;
    CMediaType  cmt;

    DbgLog((LOG_TRACE, 2, TEXT("CMpegVideoCodec::GetMediaType")));

    if (iPosition < 0) {
        return E_INVALIDARG;
    }

    //
    // Quick hack to enable greyscale for Robin
    //
    if (m_PaletteType == GREY_PALETTE) {

        if (iPosition > 0) {
            return VFW_S_NO_MORE_ITEMS;
        }

        iPosition = MT_RGB8;
    }

    //
    // We copy the proposed output format so that we can play around with
    // it all we like and still leave the original preferred format
    // untouched.  We try each of the known BITMAPINFO types in turn
    // starting off with the best quality moving through to the worst
    // (palettised) format
    //

    cmt = m_pInput->CurrentMediaType();

    if (*cmt.Type() != MEDIATYPE_Video) {
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(SIZE_PREHEADER);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }
        pVideoInfo->rcSource.top = 0;
        pVideoInfo->rcSource.left = 0;
        pVideoInfo->rcSource.right = m_seqInfo.lWidth;
        pVideoInfo->rcSource.bottom = m_seqInfo.lHeight;
        pVideoInfo->AvgTimePerFrame = m_seqInfo.tPictureTime;
        pVideoInfo->rcTarget = pVideoInfo->rcSource;
    }
    else {
        pVideoInfo = (VIDEOINFO *) cmt.Format();
        ASSERT(pVideoInfo != NULL);
    }

    //
    // Fill in the output format according to requested position, see the
    // Media Type enum in mpgvideo.h for the list of supported types and
    // their positions.
    //
    switch (iPosition) {
    case MT_Y41P:
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(SIZE_VIDEOHEADER);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }
        InitDestinationVideoInfo(pVideoInfo, MAKEFOURCC('Y','4','1','P'), 12);

        *pmt = cmt;
        pmt->SetSubtype(&MEDIASUBTYPE_Y41P);
        break;

    case MT_YUY2:
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(SIZE_VIDEOHEADER);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }
        InitDestinationVideoInfo(pVideoInfo, MAKEFOURCC('Y','U','Y','2'), 16);

        *pmt = cmt;
        pmt->SetSubtype(&MEDIASUBTYPE_YUY2);
        break;

    case MT_UYVY:
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(SIZE_VIDEOHEADER);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }
        InitDestinationVideoInfo(pVideoInfo, MAKEFOURCC('U','Y','V','Y'), 16);

        *pmt = cmt;
        pmt->SetSubtype(&MEDIASUBTYPE_UYVY);
        break;

    case MT_RGB24:
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(SIZE_VIDEOHEADER);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }
        InitDestinationVideoInfo(pVideoInfo, BI_RGB, 24);

        *pmt = cmt;
        pmt->SetSubtype(&MEDIASUBTYPE_RGB24);
        break;

    case MT_RGB565:
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(SIZE_VIDEOHEADER +
                                                          SIZE_MASKS);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }

        InitDestinationVideoInfo(pVideoInfo, BI_BITFIELDS, 16);

        DWORD *pdw;
        pdw = (DWORD *)(HEADER(pVideoInfo) + 1);
        pdw[iRED]   = bits565[iRED];
        pdw[iGREEN] = bits565[iGREEN];
        pdw[iBLUE]  = bits565[iBLUE];

        *pmt = cmt;
        pmt->SetSubtype(&MEDIASUBTYPE_RGB565);
        break;

    case MT_RGB555:
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(SIZE_VIDEOHEADER);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }
        InitDestinationVideoInfo(pVideoInfo, BI_RGB, 16);

        *pmt = cmt;
        pmt->SetSubtype(&MEDIASUBTYPE_RGB555);
        break;

    case MT_RGB8:
        pVideoInfo = (VIDEOINFO *)cmt.ReallocFormatBuffer(
                                           SIZE_VIDEOHEADER + SIZE_PALETTE);
        if (pVideoInfo == NULL) {
            return E_OUTOFMEMORY;
        }
        InitDestinationVideoInfo(pVideoInfo, BI_RGB, 8);
        InitDestinationPalette(pVideoInfo);

        *pmt = cmt;
        pmt->SetSubtype(&MEDIASUBTYPE_RGB8);
        break;

    default:
        return VFW_S_NO_MORE_ITEMS;

    }

    //
    // This block assumes that lpbi has been set up to point to a valid
    // bitmapinfoheader and that cmt has been copied into *pmt.
    // This is taken care of in the switch statement above.  This should
    // kept in mind when new formats are added.
    //
    pmt->SetType(&MEDIATYPE_Video);
    pmt->SetFormatType(&FORMAT_VideoInfo);

    //
    // we assume the output format is uncompressed
    //
    pmt->SetTemporalCompression(FALSE);
    pmt->SetSampleSize(HEADER(pVideoInfo)->biSizeImage);
    return S_OK;
}


/*****************************Private*Routine******************************\
* InitDestinationVideoInfo
*
* Fills in common video and bitmap info header fields
*
\**************************************************************************/
void
CMpegVideoCodec::InitDestinationVideoInfo(
    VIDEOINFO *pVideoInfo,
    DWORD dwComppression,
    int nBitCount
    )
{
    LPBITMAPINFOHEADER lpbi = HEADER(pVideoInfo);
    lpbi->biSize          = sizeof(BITMAPINFOHEADER);
    lpbi->biWidth         = m_seqInfo.lWidth;
    lpbi->biHeight        = m_seqInfo.lHeight;
    lpbi->biPlanes        = 1;
    lpbi->biBitCount      = nBitCount;
    lpbi->biXPelsPerMeter = 0;
    lpbi->biYPelsPerMeter = 0;
    lpbi->biCompression   = dwComppression;
    lpbi->biSizeImage     = GetBitmapSize(lpbi);

    //
    // The "bit" rate is image size in bytes times 8 (to convert to bits)
    // divided by the AvgTimePerFrame.  This result is in bits per 100 nSec,
    // so we multiply by 10000000 to convert to bits per second, this multiply
    // is combined with "times" 8 above so the calculations becomes:
    //
    // BitRate = (biSizeImage * 80000000) / AvgTimePerFrame
    //
    LARGE_INTEGER li;
    li.QuadPart = pVideoInfo->AvgTimePerFrame;
    pVideoInfo->dwBitRate = MulDiv(lpbi->biSizeImage, 80000000, li.LowPart);
    pVideoInfo->dwBitErrorRate = 0L;

}


/*****************************Private*Routine******************************\
* InitDestinationPalette
*
* Creates a standard colour palette or gray scale palette as determined
* by the PaletteType parameter.
*
\**************************************************************************/
void
CMpegVideoCodec::InitDestinationPalette(
    VIDEOINFO *pVideoInfo
    )
{
    int             i;
    int             iPalLowEnd;
    int             iPalHiStart;
    PALETTEENTRY    pal[24];
    HDC             hDC;

    if (m_PaletteType == COLOUR_PALETTE) {
        for ( i = 0; i < 256; i++ ) {
            pVideoInfo->bmiColors[i].rgbRed      = PaletteData[i].r;
            pVideoInfo->bmiColors[i].rgbBlue     = PaletteData[i].b;
            pVideoInfo->bmiColors[i].rgbGreen    = PaletteData[i].g;
            pVideoInfo->bmiColors[i].rgbReserved = 0;
        }
        iPalLowEnd  = 16;
        iPalHiStart = 232;
    }
    else {
        for ( i = 0; i < 256; i++ ) {
            pVideoInfo->bmiColors[i].rgbRed      = i;
            pVideoInfo->bmiColors[i].rgbBlue     = i;
            pVideoInfo->bmiColors[i].rgbGreen    = i;
            pVideoInfo->bmiColors[i].rgbReserved = 0;
        }
        iPalLowEnd  = 10;
        iPalHiStart = 246;
    }

    hDC = GetDC(GetDesktopWindow());
    GetSystemPaletteEntries(hDC, 0, 16, &pal[0] );
    for ( i = 0; i < iPalLowEnd; i++ ) {

        pVideoInfo->bmiColors[i].rgbRed   = pal[i].peRed;
        pVideoInfo->bmiColors[i].rgbGreen = pal[i].peGreen;
        pVideoInfo->bmiColors[i].rgbBlue  = pal[i].peBlue;
    }

    GetSystemPaletteEntries(hDC, iPalHiStart, 256 - iPalHiStart, &pal[0] );
    for ( i = iPalHiStart; i < 256; i++ ) {

        pVideoInfo->bmiColors[i].rgbRed   = pal[i - iPalHiStart].peRed;
        pVideoInfo->bmiColors[i].rgbGreen = pal[i - iPalHiStart].peGreen;
        pVideoInfo->bmiColors[i].rgbBlue  = pal[i - iPalHiStart].peBlue;
    }
    ReleaseDC(GetDesktopWindow(), hDC);


    HEADER(pVideoInfo)->biClrUsed = 256;
    HEADER(pVideoInfo)->biClrImportant = 0;
}


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

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

    pProperties->cBuffers = 1;
    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
CMpegVideoCodec::StartStreaming(
    void
    )
{
    CAutoLock   lock(&m_csFilter);
    long        Size;
    // HRESULT     hr;

    ASSERT(MEDIASUBTYPE_RGB8   == *m_pOutput->CurrentMediaType().Subtype() ||
           MEDIASUBTYPE_RGB555 == *m_pOutput->CurrentMediaType().Subtype() ||
           MEDIASUBTYPE_RGB565 == *m_pOutput->CurrentMediaType().Subtype() ||
           MEDIASUBTYPE_Y41P   == *m_pOutput->CurrentMediaType().Subtype() ||
           MEDIASUBTYPE_YUY2   == *m_pOutput->CurrentMediaType().Subtype() ||
           MEDIASUBTYPE_UYVY   == *m_pOutput->CurrentMediaType().Subtype() ||
           MEDIASUBTYPE_RGB24  == *m_pOutput->CurrentMediaType().Subtype());

    ASSERT(m_pFrameBuff == NULL);
    ASSERT(m_pVideoDecoder == NULL);
    ASSERT(m_Buffer == NULL);

    //
    // Create the video codec - if we fail to open the codec it is
    // probably because the codec was unable to allocate memory for its
    // decoding tables.
    //
    m_pVideoDecoder = new CVideoDecoder(this);
    if (m_pVideoDecoder == NULL) {
        return E_OUTOFMEMORY;
    }


    //
    // Allocate some space for the codecs I and P frame buffer store
    //
    Size = m_seqInfo.lWidth * m_seqInfo.lHeight;
    m_pFrameBuff = new BYTE[ 3 * (Size + (2 * (Size / 4)))];
    if (m_pFrameBuff == NULL) {

        delete m_pVideoDecoder;
        m_pVideoDecoder = NULL;

        return E_OUTOFMEMORY;
    }

    m_VBlockSize = m_seqInfo.lvbv;
    m_VBlockSize +=  ((1 << 16) - 1);
    m_VBlockSize &= ~((1 << 16) - 1);

    // m_Buffer = new CCircularBuffer(m_VBlockSize, m_VBlockSize, hr);
    m_Buffer = new BYTE[m_VBlockSize];
    if (m_Buffer == NULL) {

        delete [] m_pFrameBuff;
        m_pFrameBuff = NULL;

        delete m_pVideoDecoder;
        m_pVideoDecoder = NULL;

        return E_OUTOFMEMORY;
    }

    //
    // Initialize the mpeg video codec
    //
    m_dwLateBy = 0;
    m_dwCtrl = (m_dwCtrlDefault | m_dwQualDefault);
    m_VideoControl.dwCtrl = m_dwCtrl;
    m_VideoControl.dwYStride = m_seqInfo.lWidth;
    m_VideoControl.dwYLines = m_seqInfo.lHeight;

    ZeroMemory(m_pFrameBuff, Size);
    m_VideoControl.pFrameBuff = m_pFrameBuff;


    //
    // Now reset the mpeg video decoder
    //
    ResetVideoDecoder();


    //
    // Reset frame stats
    //
    ZeroMemory(m_dwFramesSkipped, sizeof(m_dwFramesSkipped));
    ZeroMemory(m_dwFramesDecoded, sizeof(m_dwFramesDecoded));

    return S_OK;
}


/******************************Public*Routine******************************\
* StopStreaming
*
*
*
\**************************************************************************/
HRESULT
CMpegVideoCodec::StopStreaming(
    void
    )
{
    CAutoLock       lock(&m_csFilter);
    CAutoLock       lck(&m_csReceive);

    CTimePosition   *pTimePos;

    ASSERT(m_pFrameBuff != NULL);
    ASSERT(m_pVideoDecoder != NULL);
    ASSERT(m_Buffer != NULL);

    delete m_pVideoDecoder;
    m_pVideoDecoder = NULL;

    delete [] m_pFrameBuff;
    m_pFrameBuff = NULL;

    delete [] m_Buffer;
    m_Buffer = NULL;


    //
    // Purge the PTS queue.
    //
    DbgLog((LOG_TRACE, 2,
            TEXT("Freeing %d time entries"), m_PtsQueue.GetCount()));

    while( (pTimePos = m_PtsQueue.RemoveHead()) != NULL) {
        delete pTimePos;
    }

    return S_OK;
}

/******************************Public*Routine******************************\
* Notify
*
* Handle quality control notifications sent to us
*
\**************************************************************************/
HRESULT
CMpegVideoCodec::AlterQuality(
    Quality q
    )
{
    DWORD dwNewCtrl;
    LARGE_INTEGER li;
    li.QuadPart = q.Late;
    DWORD dwTimeLate = li.LowPart;

    MSR_INTEGER(m_QualMsg, dwTimeLate);
    li.QuadPart = m_seqInfo.tPictureTime;

    DbgLog((LOG_TRACE, 2,
            TEXT("Q = %s Prop = %4.4ld Late = %8.8ld Fram = %8.8ld"),
            q.Type == Famine ? TEXT("Famine") : TEXT("Flood "),
            q.Proportion, dwTimeLate,
            li.LowPart));

    //
    // See if the user has overidden quality messages
    //
    if (m_IgnoreQualityMessage) {
        return S_OK;
    }

    //
    // Turn off the old option and then bring in the new.
    //

    m_dwLateBy = dwTimeLate;

    if (dwTimeLate <= 750000L) {
        DbgLog((LOG_TRACE, 2, TEXT("Default QL")));
        dwNewCtrl = m_dwCtrlDefault;
    }
    else if (dwTimeLate <= (7L * 330000L)) {
        DbgLog((LOG_TRACE, 2, TEXT("IP")));
        dwNewCtrl = DECODE_IP;
    }
    else {
        DbgLog((LOG_TRACE, 2, TEXT("I")));
        dwNewCtrl = DECODE_I;
    }

    m_dwCtrl = (m_dwQualDefault | dwNewCtrl);

    return S_OK;
}


// -------------------------------------------------------------------------
// IP Tracking class
// -------------------------------------------------------------------------
//
inline CMpegVideoCodec::CNextIP::CNextIP()
{
    Reset();
}

inline void
CMpegVideoCodec::CNextIP::Set(
    DWORD dwSkipFlag,
    BOOL bIFrame,
    BOOL bTimeSet,
    REFERENCE_TIME t,
    DWORD dwTemporalReference
    )
{
    if (m_bGotFirst) {

        m_bTimeToDraw = FALSE;
        NextRef(dwTemporalReference); // Might already be time if no Bs
    }
    else {
        if (bIFrame) {
            m_bGotFirst   = TRUE;
            m_bTimeToDraw = TRUE;
        }
    }
    m_dwSkipFlag          = dwSkipFlag;
    m_bTimeSet            = bTimeSet;
    m_t                   = t;
    m_dwTemporalReference = dwTemporalReference;

}

inline BOOL
CMpegVideoCodec::CNextIP::GotFirst()
{
    return m_bGotFirst;
}

inline void
CMpegVideoCodec::CNextIP::NextRef(
    DWORD dwTemporalReference
    )
{
    //  See if we're next after this one
    //  Don't set m_bTimeToDraw = FALSE here because in the case
    //  where we're stepping through undrawable B-frames before the
    //  first I-Frame we've already set m_bTimeToDraw = TRUE
    if (((dwTemporalReference + 1) & 1023) == m_dwTemporalReference) {
        m_bTimeToDraw = TRUE;
    }
    DbgLog((LOG_TRACE, 3,
            TEXT("New Temporal Reference %d, NextIP = %d, TimeToDraw = %d"),
            dwTemporalReference, m_dwTemporalReference,
            m_bTimeToDraw));
}


inline BOOL
CMpegVideoCodec::CNextIP::GetTime(
    REFERENCE_TIME *pt
    )
{
    if (m_bTimeSet) {
        *pt = m_t;
    }
    return m_bTimeSet;
}

inline BOOL
CMpegVideoCodec::CNextIP::TimeToDraw()  const
{
    return !m_dwSkipFlag && m_bTimeToDraw;
}

inline void
CMpegVideoCodec::CNextIP::Reset()
{
    m_bGotFirst = FALSE;
    m_bTimeSet = FALSE;
    m_dwTemporalReference = 0;
    m_bTimeToDraw = FALSE;
}





/******************************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;
}



#ifdef DEBUG
LPCTSTR PictureTypes[8] = {
    TEXT("forbidden frame type"),
    TEXT("I-Frame"),
    TEXT("P-Frame"),
    TEXT("B-Frame"),
    TEXT("D-Frame"),
    TEXT("Reserved frame type"),
    TEXT("Reserved frame type"),
    TEXT("Reserved frame type")
};

LPCTSTR PelAspectRatios[16] = {
    TEXT("Forbidden"),
    TEXT("1.0000 - VGA etc"),
    TEXT("0.6735"),
    TEXT("0.7031 - 16:9, 625 line"),
    TEXT("0.7615"),
    TEXT("0.8055"),
    TEXT("0.8437 - 16:9, 525 line"),
    TEXT("0.8935"),
    TEXT("0.9375 - CCIR601, 625 line"),
    TEXT("0.9815"),
    TEXT("1.0255"),
    TEXT("1.0695"),
    TEXT("1.1250 - CCIR601, 525 line"),
    TEXT("1.1575"),
    TEXT("1.2015"),
    TEXT("Reserved")
};

LPCTSTR PictureRates[16] = {
    TEXT("Forbidden"),
    TEXT("23.976"),
    TEXT("24"),
    TEXT("25"),
    TEXT("29.97"),
    TEXT("30"),
    TEXT("50"),
    TEXT("59.94"),
    TEXT("60"),
    TEXT("Reserved"),
    TEXT("Reserved"),
    TEXT("Reserved"),
    TEXT("Reserved"),
    TEXT("Reserved"),
    TEXT("Reserved"),
    TEXT("Reserved")
};
#endif // DEBUG

const LONG PictureTimes[16] = {
    0,
    (LONG)((double)10000000 / 23.976),
    (LONG)((double)10000000 / 24),
    (LONG)((double)10000000 / 25),
    (LONG)((double)10000000 / 29.97),
    (LONG)((double)10000000 / 30),
    (LONG)((double)10000000 / 50),
    (LONG)((double)10000000 / 59.94),
    (LONG)((double)10000000 / 60)
};

const LONG AspectRatios[16] = {
    0,
    393700,
    (LONG)(393700.0 * 0.6735),
    (LONG)(393700.0 * 0.7031),
    (LONG)(393700.0 * 0.7615),
    (LONG)(393700.0 * 0.8055),
    (LONG)(393700.0 * 0.8437),
    (LONG)(393700.0 * 0.8935),
    (LONG)(393700.0 * 0.9375),
    (LONG)(393700.0 * 0.9815),
    (LONG)(393700.0 * 1.0255),
    (LONG)(393700.0 * 1.0695),
    (LONG)(393700.0 * 1.1250),
    (LONG)(393700.0 * 1.1575),
    (LONG)(393700.0 * 1.2015),
    0
};

/******************************Public*Routine******************************\
* ParseSequenceHeader
*
*
*
\**************************************************************************/
BOOL
CMpegVideoCodec::ParseSequenceHeader(
    const BYTE *pbData,
    LONG lData,
    SEQHDR_INFO *pInfo
    )
{
    ASSERT(*(UNALIGNED DWORD *)pbData == ByteSwap(SEQUENCE_HEADER_CODE));

    //
    // Check random marker bit
    //
    if (!(pbData[10] & 0x20)) {
        DbgLog((LOG_ERROR, 2, TEXT("Sequence header invalid marker bit")));
        return FALSE;
    }

    DWORD dwWidthAndHeight = ((DWORD)pbData[4] << 16) + ((DWORD)pbData[5] << 8) +
                             ((DWORD)pbData[6]);

    pInfo->lWidth = dwWidthAndHeight >> 12;
    pInfo->lHeight = dwWidthAndHeight & 0xFFF;

    DbgLog((LOG_TRACE, 2, TEXT("Width = %d, Height = %d"),
            pInfo->lWidth, pInfo->lHeight));

    //
    // the '8' bit is the scramble flag used by sigma designs - ignore
    //
    BYTE PelAspectRatioAndPictureRate = pbData[7];

    if ((PelAspectRatioAndPictureRate & 0x0F) > 8) {
        PelAspectRatioAndPictureRate &= 0xF7;
    }

    DbgLog((LOG_TRACE, 2, TEXT("Pel Aspect Ratio = %s"),
        PelAspectRatios[PelAspectRatioAndPictureRate >> 4]));
    DbgLog((LOG_TRACE, 2, TEXT("Picture Rate = %s"),
        PictureRates[PelAspectRatioAndPictureRate & 0x0F]));

    if ((PelAspectRatioAndPictureRate & 0xF0) == 0 ||
        (PelAspectRatioAndPictureRate & 0x0F) == 0) {

        DbgLog((LOG_ERROR, 2, TEXT("Sequence header invalid ratio/rate")));
        return FALSE;
    }

    pInfo->tPictureTime =
            (LONGLONG)PictureTimes[PelAspectRatioAndPictureRate & 0x0F];

    pInfo->lTimePerFrame =
            MulDiv((LONG)pInfo->tPictureTime, 9, 1000);

    /*  Pull out the bit rate and aspect ratio for the type */
    pInfo->dwBitRate = ((((DWORD)pbData[8] << 16) + ((DWORD)pbData[9] << 8) +
                          (DWORD)pbData[10]) >> 6);

    if (pInfo->dwBitRate == 0x3FFFF) {

        DbgLog((LOG_TRACE, 2, TEXT("Variable video bit rate")));
        pInfo->dwBitRate = 0;
    }
    else {

        pInfo->dwBitRate *= 400;
        DbgLog((LOG_TRACE, 2, TEXT("Video bit rate is %d bits per second"),
               pInfo->dwBitRate));
    }

    //
    // Get a DC
    //
    HDC hdc = GetDC(GetDesktopWindow());
    ASSERT(hdc != NULL);

    //
    //  Guess (randomly) 39.37 inches per meter
    //
    LONG lNotionalPelsPerMeter =
            MulDiv((LONG)GetDeviceCaps(hdc, LOGPIXELSX), 3937, 100);

    pInfo->lXPelsPerMeter = lNotionalPelsPerMeter;

    pInfo->lYPelsPerMeter = MulDiv(
                              lNotionalPelsPerMeter,
                              AspectRatios[PelAspectRatioAndPictureRate >> 4],
                              10000);
    //
    // Pull out the vbv
    //
    pInfo->lvbv = ((((LONG)pbData[10] & 0x1F) << 5) |
                    ((LONG)pbData[11] >> 3)) * 2048;

    DbgLog((LOG_TRACE, 2, TEXT("vbv size is %d bytes"), pInfo->lvbv));

    //
    // Check constrained parameter stuff
    //
    if (pbData[11] & 0x04) {

        DbgLog((LOG_TRACE, 2, TEXT("Constrained parameter video stream")));

        if (pInfo->lvbv > 40960) {

            DbgLog((LOG_ERROR, 1,
                    TEXT("Invalid vbv (%d) for Constrained stream"),
                    pInfo->lvbv));

            //
            //  Have to let this through too!  bisp.mpg has this
            //  But constrain it since it might be random
            //
            pInfo->lvbv = 40960;

        }

    }
    else {
        DbgLog((LOG_TRACE, 2, TEXT("Non-Constrained parameter video stream")));
    }

    pInfo->lActualHeaderLen = lData;
    CopyMemory((PVOID)pInfo->RawHeader, (PVOID)pbData, pInfo->lActualHeaderLen);
    return TRUE;
}
/******************************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 );
}


