/* ***** BEGIN LICENSE BLOCK ***** 
 * Version: RCSL 1.0/RPSL 1.0 
 *  
 * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
 *      
 * The contents of this file, and the files included with this file, are 
 * subject to the current version of the RealNetworks Public Source License 
 * Version 1.0 (the "RPSL") available at 
 * http://www.helixcommunity.org/content/rpsl unless you have licensed 
 * the file under the RealNetworks Community Source License Version 1.0 
 * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
 * in which case the RCSL will apply. You may also obtain the license terms 
 * directly from RealNetworks.  You may not use this file except in 
 * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
 * applicable to this file, the RCSL.  Please see the applicable RPSL or 
 * RCSL for the rights, obligations and limitations governing use of the 
 * contents of the file.  
 *  
 * This file is part of the Helix DNA Technology. RealNetworks is the 
 * developer of the Original Code and owns the copyrights in the portions 
 * it created. 
 *  
 * This file, and the files included with this file, is distributed and made 
 * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
 * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
 * 
 * Technology Compatibility Kit Test Suite(s) Location: 
 *    http://www.helixcommunity.org/content/tck 
 * 
 * Contributor(s): 
 *  
 * ***** END LICENSE BLOCK ***** */ 

#include "hxtypes.h"
#include "hlxclib/stdio.h"

#include "hxcom.h"              // IUnknown
#include "hxcomm.h"            // IHXCommonClassFactory
#include "ihxpckts.h"           // IHXBuffer, IHXPacket, IHXValues
#include "hxplugn.h"           // IHXPlugin
#include "hxrendr.h"           // IHXRenderer
#include "hxengin.h"           // IHXInterruptSafe
#include "hxcore.h"            // IHXStream
#include "hxausvc.h"           // Audio Services
#include "hxmon.h"             // IHXStatistics
#include "hxupgrd.h"           // IHXUpgradeCollection
#include "hxslist.h"            // CHXSimpleList
#include "carray.h"             // CHXPtrArray

#include "mpadecobj.h"          // MPEG Audio Decoder (selects fixed-pt or floating-pt based on HELIX_CONFIG_FIXEDPOINT)
#include "mp3format.h"          // MP3 formatter

#include "mp3rend.h"            // CRnMp3Ren
#include "pktparse.h"           // CPacketParser

const UINT32 CPacketParser::DEC_BUFFER_SIZE = 4096;

CPacketParser::CPacketParser() :    
    m_pBufHead(NULL),
    m_pDecBuffer(NULL),
    m_ulDecBufBytes(0),
    m_pFmt(NULL),
    m_pRenderer(NULL),
    m_pDecoder(NULL),
    m_pClassFactory(NULL),
    m_pLastPCMBuffer(NULL),
    m_dLastPCMTime(0.0),
    m_nLayer(0),
    m_ulBitRate(0),
    m_ulChannels(0),
    m_nWaveBufSize(0),
    m_dFrameTime(0.0),
    m_dNextPts(0.0),
    m_bTrustPackets(FALSE),
    m_bEndOfPackets(FALSE)
{

}

CPacketParser::~CPacketParser()
{
    HX_VECTOR_DELETE(m_pBufHead);
    HX_DELETE(m_pDecoder);
    HX_RELEASE(m_pRenderer);
    
    HX_RELEASE(m_pLastPCMBuffer);
    HX_RELEASE(m_pClassFactory);
    HX_DELETE(m_pFmt);
}

HX_RESULT
CPacketParser::Init(CRnMp3Ren* pRenderer, 
                    IHXCommonClassFactory* pClassFactory)
{
    if(!pRenderer || !pClassFactory)
    {
        return HXR_INVALID_PARAMETER;
    }
    pRenderer->AddRef();
    pClassFactory->AddRef();
    
    HX_RELEASE(m_pRenderer);
    HX_RELEASE(m_pClassFactory);
    m_pRenderer = pRenderer;
    m_pClassFactory = pClassFactory;

    if(m_pDecBuffer == NULL)
    {
        m_pBufHead = new BYTE[DEC_BUFFER_SIZE + 31];
        m_pDecBuffer = (UCHAR*)((PTR_INT)m_pBufHead+31 & ~31);
    
        if(m_pDecBuffer == NULL || m_pBufHead == NULL)
            return HXR_OUTOFMEMORY;
    }

    HX_DELETE(m_pFmt);
    m_pFmt = new CMp3Format(NULL);

    m_pFmt->SetTrustPackets(m_bTrustPackets);

    return m_pFmt ? HXR_OK : HXR_OUTOFMEMORY;
}

UINT32
CPacketParser::DecodeAndRender(UCHAR* pDec, UINT32 ulSize, double dTime, 
                               BOOL bPacketLoss)
{
    // Dynamic format change - must reinit
    if (DidSourceChange(pDec, ulSize) && !InitDecoder(pDec, ulSize, TRUE))
    {
        return 0;
    }

    // Create our PCM buffer
    IHXBuffer *pPCMBuffer = NULL;
    m_pClassFactory->CreateInstance(CLSID_IHXBuffer, (void**) &pPCMBuffer);
    if (!pPCMBuffer)
    {
        return 0;
    }

    HX_RESULT 
    retVal = pPCMBuffer->SetSize(m_nWaveBufSize);
    if (FAILED(retVal) || pPCMBuffer->GetSize() != m_nWaveBufSize)
    {
        pPCMBuffer->Release();
        return 0;
    }

    UINT32 dwPCM = pPCMBuffer->GetSize();

    // Decode the frame
    UINT32 ulDec = bPacketLoss ? (UINT32)-1 : ulSize;
    m_pDecoder->DecodeFrame_v(pDec,(unsigned long*)&ulDec,
                               pPCMBuffer->GetBuffer(), 
                               (unsigned long*)&dwPCM);

    if(bPacketLoss)
    {
        ulDec = ulSize;
    }

    if(ulDec)
    {
        m_pRenderer->Render(pPCMBuffer, dTime);
    }

    // Store the pcm buffer of the last decoded frame
    HX_RELEASE(m_pLastPCMBuffer);

    m_pLastPCMBuffer = pPCMBuffer;
    m_pLastPCMBuffer->AddRef();
    m_dLastPCMTime = dTime;

    pPCMBuffer->Release();

    return ulDec;
}

BOOL 
CPacketParser::DidSourceChange(UCHAR *pHeader, UINT32 ulSize)
{
    unsigned long lPCMSampleRate = 0;
    int nChannels = 0;
    int nBitsPerSample = 0;

    // Get existing header info
    m_pDecoder->GetPCMInfo_v(lPCMSampleRate,
                              nChannels,
                              nBitsPerSample);

    UINT32  ulBitRate = 0;
    UINT32  ulSampRate = 0;

    int     nSrcChannels = 0;
    int     nLayer = 0;
    int     nSamplesPerFrame = 0;

    // Get new header values
    m_pFmt->GetEncodeInfo(pHeader, ulSize,
                          ulBitRate, ulSampRate,
                          nSrcChannels, nLayer, nSamplesPerFrame);

    // Allow layer3 bitrates to change
    if (nChannels != nSrcChannels ||
        lPCMSampleRate != ulSampRate ||
        m_pDecoder->GetSamplesPerFrame_n() != nSamplesPerFrame ||
        (m_ulBitRate != ulBitRate && (nLayer != 3 || m_nLayer != 3)))
        return TRUE;
    else
        return FALSE;
}

BOOL
CPacketParser::InitDecoder(UCHAR *pBuffer, UINT32 ulSize, BOOL bReInit)
{
    if (!m_pDecoder)
    {
        // Create and init the decoder
        m_pDecoder = new CMpaDecObj();
        if (m_pDecoder) m_pDecoder->SetTrustPackets(m_bTrustPackets);
    }

    if (!m_pDecoder->Init_n(pBuffer, ulSize, m_bReformatted))
    {
        delete m_pDecoder;
        m_pDecoder = NULL;

        return FALSE;
    }

    // Create and init the PCM device
    unsigned long lPCMSampRate = 0;

    int     nPCMChannels = 0,
            nBitsPerSample = 0;

    m_pDecoder->GetPCMInfo_v(lPCMSampRate,
                              nPCMChannels,
                              nBitsPerSample);

    m_nWaveBufSize = m_pDecoder->GetSamplesPerFrame_n() *
                     (nBitsPerSample>>3) * nPCMChannels;

    // Get the time per frame
    m_dFrameTime = m_pDecoder->GetSamplesPerFrame_n() * 1000.0 / lPCMSampRate;

    m_ulChannels = nPCMChannels;

    // Init the audio stream
    HXAudioFormat audioFmt;
    audioFmt.uChannels = nPCMChannels;
    audioFmt.uBitsPerSample = nBitsPerSample;
    audioFmt.ulSamplesPerSec = lPCMSampRate;
    audioFmt.uMaxBlockSize = m_nWaveBufSize;
    
    HX_ASSERT(m_pRenderer);
    if(bReInit ? !m_pRenderer->ReInitAudioStream(audioFmt) :
       !m_pRenderer->InitAudioStream(audioFmt))
    {
        return FALSE;      
    }
    
    HX_ASSERT(m_pFmt);
    m_pFmt->Init(pBuffer, ulSize);    

    UINT32 ulSampRate = 0;
    int nChannels = 0;
    int nSamplesPerFrame = 0;

    m_pFmt->GetEncodeInfo(pBuffer, ulSize,
                          m_ulBitRate, ulSampRate,
                          nChannels, m_nLayer, nSamplesPerFrame);

    return TRUE;
}

void	
CPacketParser::OverrideFactory(IHXCommonClassFactory* pCommonClassFactory)
{
    HX_RELEASE(m_pClassFactory);
    m_pClassFactory = pCommonClassFactory;
    m_pClassFactory->AddRef();
}
