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



/* -------------------------------------------------------------------------
** CVideoDecoder
** -------------------------------------------------------------------------
*/

/******************************Public*Routine******************************\
* CVideoDecoder
*
* Constructor and destructor
*
\**************************************************************************/
CVideoDecoder::CVideoDecoder(CMpegVideoCodec *pMpegCodec)
    : m_pCodec(pMpegCodec) {}



/******************************Public*Routine******************************\
* ResetVideo
*
*
*
\**************************************************************************/
DWORD
CVideoDecoder::ResetVideo()
{
    CAutoLock lck(this);
    return DECODE_SUCCESS;
}


/******************************Public*Routine******************************\
* DecodeFrame
*
*
*
\**************************************************************************/
DWORD
CVideoDecoder::DecodeFrame(
    VideoCtrl *pCtrl
    )
{
    CAutoLock   lck(this);
    BOOL        Done = FALSE;
    DWORD       rc = DECODE_SUCCESS;


    m_pCtrl = pCtrl;

    if ((m_pCtrl->dwCtrl & 0xffff) == DECODE_DIS) {

        LPBYTE lpOutputBuffer = m_pCodec->GetDecodeBufferAndFormat();

        if (lpOutputBuffer == NULL) {
            rc = DECODE_ERR_QUARTZ;
        }
        return rc;
    }

    m_pCtrl->dwSkipFlag = 1;
    while (!Done) {

        DWORD sc = NextStartCode();

        switch (sc) {

        case PICTURE_START_CODE:
            DbgLog((LOG_TRACE, 2, TEXT("PICTURE_START_CODE")));
            m_pCtrl->pFrameStartPos = m_pCtrl->pCmprRead;
            rc = SkipFrame();
            Done = TRUE;
            break;

        case SEQUENCE_HEADER_CODE:
            DbgLog((LOG_TRACE, 2, TEXT("SEQUENCE_HEADER_CODE")));
            Discard32Bits();
            break;

        case SEQUENCE_END_CODE:
            DbgLog((LOG_TRACE, 2, TEXT("SEQUENCE_END_CODE")));
            Discard32Bits();
            Done = TRUE;
            break;

        case GROUP_START_CODE:
            DbgLog((LOG_TRACE, 2, TEXT("GROUP_START_CODE")));
            Discard32Bits();
            break;

        case (DWORD)-1:
            //
            // We didn't find a valid start code.
            //
            rc = DECODE_ERR_DATA;
            Done = TRUE;
            break;

        default:
            DbgLog((LOG_TRACE, 2, TEXT("Unexpected start code %#X"), sc));
            Discard32Bits();
            break;
        }
    }


    if (!m_pCtrl->dwSkipFlag) {

        LPBYTE lpOutputBuffer = m_pCodec->GetDecodeBufferAndFormat();

        if (lpOutputBuffer == NULL) {
            rc = DECODE_ERR_QUARTZ;
        }
    }

    return rc;
}


/*****************************Private*Routine******************************\
* NextStartCode
*
* Returns the next start code of -1 if none found within the buffer limits.
* Advances pCmprRead to point to begining of the start code.
*
\**************************************************************************/
DWORD
CVideoDecoder::NextStartCode()
{
    LPBYTE  lpCurr = m_pCtrl->pCmprRead;
    LPBYTE  lpEnd = m_pCtrl->pCmprWrite;
    int     sm = 0;
    DWORD   StartCode = 0x00000100L;

    while ((lpCurr < lpEnd) && sm < 3) {

        switch (sm) {
        case 0:
            lpCurr = (LPBYTE)memchr(lpCurr, 0, lpEnd - lpCurr);
            if (lpCurr != NULL) lpCurr++, sm = 1;
            else lpCurr = lpEnd;
            break;

        case 1:
            if (*lpCurr++ == 0) sm = 2;
            else sm = 0;
            break;

        case 2:
            if (*lpCurr == 1) sm = 3;
            else if (*lpCurr == 0) sm = 2;
            else sm = 0;
            lpCurr++;
            break;
        }
    }

    //
    // When we get here we have either run out of buffer or found the first
    // 3 bytes of a valid start code.
    //
    // If we have the first three bytes of the start code.  The next
    // byte completes the start code.
    //
    // Don't forget to put back the start code bytes that we have just
    // read otherwise they would be lost forever.
    //
    if (lpCurr >= lpEnd) {
        StartCode = (DWORD)-1;
    }
    else {
        StartCode += *lpCurr;
    }
    lpCurr -= sm;

    m_pCtrl->pCmprRead = lpCurr;
    return StartCode;
}



/*****************************Private*Routine******************************\
* Discard32Bits
*
* Throw away the next 32 bits
*
\**************************************************************************/
void
CVideoDecoder::Discard32Bits()
{
    m_pCtrl->pCmprRead += 4;
}


/*****************************Private*Routine******************************\
* SkipFrame
*
*
*
\**************************************************************************/
DWORD
CVideoDecoder::SkipFrame()
{
    DWORD   dwX;

    //
    // If there isn't enough room for the information required then
    // then return 0L
    //
    if (m_pCtrl->pCmprRead > (m_pCtrl->pCmprWrite - 8)) {
        return DECODE_ERR_DATA;
    }

    Discard32Bits();    // Eat the picyure start code

    dwX = ByteSwap(*(UNALIGNED DWORD *)m_pCtrl->pCmprRead);
    m_pCtrl->pCmprRead += 4;

    m_pCtrl->dwTemporalReference  = (dwX & 0xFFC00000) >> 22;
    m_pCtrl->dwFrameType = (dwX & 0x00380000) >> 19;

    switch (m_pCtrl->dwCtrl & 0xffff) {
    case DECODE_NULL:
        m_pCtrl->dwSkipFlag = 1;
        break;

    case DECODE_I:
        m_pCtrl->dwSkipFlag = (m_pCtrl->dwFrameType != FTYPE_I);
        break;

    case DECODE_IP:
        m_pCtrl->dwSkipFlag = (m_pCtrl->dwFrameType == FTYPE_B);
        break;

    case DECODE_IPB:
        m_pCtrl->dwSkipFlag = 0;
        break;
    }

    for (; ; ) {

        dwX = NextStartCode();

        if (dwX == (DWORD)-1) {
            return DECODE_ERR_DATA;
        }
        else {

            BYTE Code = (BYTE)(dwX & 0xFF);

            if (Code == 0x00 || Code > 0xAF) {
                return DECODE_SUCCESS;
            }
            else {
                Discard32Bits();
            }
        }
    }
}
