/**************************************************************************
 * Copyright (C) 2008 Cocha                                               *
 * http://sourceforge.jp/projects/ecodecotool/                            *
 *                                                                        *
 *  This Program is free software; you can redistribute it and/or modify  *
 *  it under the terms of the GNU General Public License as published by  *
 *  the Free Software Foundation; either version 2, or (at your option)   *
 *  any later version.                                                    *
 *                                                                        *
 *  This Program is distributed in the hope that it will be useful,       *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
 *  GNU General Public License for more details.                          *
 *                                                                        *
 *  You should have received a copy of the GNU General Public License     *
 *  along with GNU Make; see the file COPYING.  If not, write to          *
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *
 *                                                                        *
 **************************************************************************/

#include "DecodeAac.h"

CDecodeAac::CDecodeAac(IUnknown *pUnk, HRESULT *phr)
   : CSimpleTransform(L"Aac Decoder", pUnk, CLSID_DecodeAac, phr)
{  

   ::ZeroMemory(&m_outFormat, sizeof(m_outFormat));

   m_outFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   m_outFormat.wBitsPerSample = 32;

   m_decHandle = NULL;
   m_pszExtData = NULL;
   m_nExtDataLength = 0;

   m_rtCurrent = 0;
}
CDecodeAac::~CDecodeAac()  
{  

   if(m_pszExtData != NULL)
   {
      delete []m_pszExtData;
      m_pszExtData = NULL;
      m_nExtDataLength = 0;
   }

   if(m_decHandle != NULL)
   {
      faacDecClose(m_decHandle);
      m_decHandle = NULL;
   }
}  
HRESULT CDecodeAac::GetOutFormat(void *pFormat)
{  
   CheckPointer(pFormat, E_POINTER);
   ::CopyMemory(pFormat, &m_outFormat, sizeof(m_outFormat));

   return S_OK;
}
HRESULT CDecodeAac::SetOutFormat(void *pFormat)
{  

   CheckPointer(pFormat, E_POINTER);
   WAVEFORMATEX *pwf = (WAVEFORMATEX *)pFormat;

   if(pwf->wFormatTag == WAVE_FORMAT_PCM && pwf->wBitsPerSample == 16)
   {
      ::CopyMemory(&m_outFormat, pFormat, sizeof(m_outFormat));
      return S_OK;
   }
   else if(pwf->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && pwf->wBitsPerSample == 32)
   {
      ::CopyMemory(&m_outFormat, pFormat, sizeof(m_outFormat));
      return S_OK;
   }

   return E_FAIL;
}
HRESULT CDecodeAac::OnStart(void)
{  
   m_rtCurrent = 0;
   return S_OK;
}
HRESULT CDecodeAac::OnSeek(void)
{  
   m_rtCurrent = 0;
   return S_OK;
}
HRESULT CDecodeAac::OnTransform(IMediaSample *pInSample, IMediaSample *pOutSample)
{  

   int nOutLength = 0;
   BYTE *pbInBuffer = NULL;
   BYTE *pbOutBuffer = NULL;
   void *pTmpOutBuffer = NULL;
   faacDecFrameInfo frameInfo;

   pInSample->GetPointer(&pbInBuffer);

   pOutSample->GetPointer(&pbOutBuffer);

   pTmpOutBuffer = faacDecDecode(m_decHandle, &frameInfo, pbInBuffer, (unsigned long)pInSample->GetActualDataLength());

   if(frameInfo.error != 0 || pTmpOutBuffer == NULL)
      return E_FAIL;

   if(frameInfo.samples == 0)
   {
      pOutSample->SetActualDataLength(0);
      pOutSample->SetTime(&m_rtCurrent, &m_rtCurrent);
      return S_OK;
   }

   if(frameInfo.channels == 2)
   {  

      if(m_outFormat.wBitsPerSample == 32)
      {
         ::CopyMemory((float *)pbOutBuffer, (float *)pTmpOutBuffer, frameInfo.samples * 4);
         nOutLength = frameInfo.samples * 4;
      }
      else 
      {
         ::CopyMemory((short *)pbOutBuffer, (short *)pTmpOutBuffer, frameInfo.samples * 2);
         nOutLength = frameInfo.samples * 2;
      }
   }
   else if(frameInfo.channels == 1)
   {  

      if(m_outFormat.wBitsPerSample == 32)
      {
         float *pfTmpBuffer = (float *)pTmpOutBuffer;
         float *pfOutBuffer = (float *)pbOutBuffer;

         for(unsigned long i=0;i<frameInfo.samples;i+=2)
         {
            *pfOutBuffer = *pfTmpBuffer;
            pfOutBuffer++;

            pfTmpBuffer += 2;
            nOutLength += 4;
         }
      }
      else 
      {
         short *psTmpBuffer = (short *)pTmpOutBuffer;
         short *psOutBuffer = (short *)pbOutBuffer;

         for(unsigned long i=0;i<frameInfo.samples;i+=2)
         {
            *psOutBuffer = *psTmpBuffer;
            psOutBuffer++;

            psTmpBuffer += 2;
            nOutLength += 2;
         }
      }
   }
   else
   {

      if(m_outFormat.wBitsPerSample == 32)
      {
         float *pfTmpBuffer = (float *)pTmpOutBuffer;
         float *pfOutBuffer = (float *)pbOutBuffer;

         for(unsigned long i=0;i<frameInfo.samples;i+=frameInfo.channels)
         {
            *pfOutBuffer = pfTmpBuffer[1];
            pfOutBuffer++;

            *pfOutBuffer = pfTmpBuffer[2];
            pfOutBuffer++;

            pfTmpBuffer += frameInfo.channels;
            nOutLength += 8;
         }
      }
      else 
      {
         short *psTmpBuffer = (short *)pTmpOutBuffer;
         short *psOutBuffer = (short *)pbOutBuffer;

         for(unsigned long i=0;i<frameInfo.samples;i+=frameInfo.channels)
         {
            *psOutBuffer = psTmpBuffer[1];
            psOutBuffer++;

            *psOutBuffer = psTmpBuffer[2];
            psOutBuffer++;

            psTmpBuffer += frameInfo.channels;
            nOutLength += 4;
         }
      }
   }

   pOutSample->SetSyncPoint(TRUE);
   pOutSample->SetDiscontinuity(FALSE);
   pOutSample->SetPreroll(FALSE);
   pOutSample->SetActualDataLength(nOutLength);

   REFERENCE_TIME rtStop;

   rtStop = m_rtCurrent + (REFERENCE_TIME)((LONGLONG)nOutLength * ONE_SECOND / m_outFormat.nAvgBytesPerSec);
   pOutSample->SetTime(&m_rtCurrent, &rtStop);
   m_rtCurrent = rtStop;

   return S_OK;
}
HRESULT CDecodeAac::OnConnectInPin(const CMediaType *pmtIn)
{  

   if(pmtIn->majortype != MEDIATYPE_Audio)
      return S_FALSE;

   if(pmtIn->subtype != MEDIASUBTYPE_Aac)
      return S_FALSE;

   if(pmtIn->formattype != FORMAT_WaveFormatEx)
      return S_FALSE;

   WAVEFORMATEX *pwf = (WAVEFORMATEX *)pmtIn->Format();

   if(pwf->cbSize < 2)
      return S_FALSE;

   if(m_pszExtData != NULL)
   {
      delete []m_pszExtData;
      m_pszExtData = NULL;
      m_nExtDataLength = 0;
   }

   m_nExtDataLength = pwf->cbSize;
   m_pszExtData = new unsigned char[m_nExtDataLength];
   ::CopyMemory(m_pszExtData, (unsigned char *)pmtIn->pbFormat + sizeof(WAVEFORMATEX), m_nExtDataLength);


   m_outFormat.cbSize = 0;
   m_outFormat.nSamplesPerSec = pwf->nSamplesPerSec;

   if(pwf->nChannels <= 2)
      m_outFormat.nChannels = pwf->nChannels;
   else
      m_outFormat.nChannels = 2;

   m_outFormat.nBlockAlign = m_outFormat.nChannels * (m_outFormat.wBitsPerSample / 8);
   m_outFormat.nAvgBytesPerSec = m_outFormat.nSamplesPerSec * m_outFormat.nBlockAlign;

   return S_OK;
}
HRESULT CDecodeAac::OnConnectOutPin(const CMediaType *pmtIn, int nInBufferSize, CMediaType *pmtOut, int *pnOutBufferSize)
{  

   char result;
   unsigned char Channels = 0;
   unsigned long SamplesPerSec = 0;

   if(m_decHandle != NULL)
   {
      faacDecClose(m_decHandle);
      m_decHandle = NULL;
   }

   m_decHandle = faacDecOpen();
   if(m_decHandle == NULL)
      return S_FALSE;

   result = faacDecInit2(m_decHandle, m_pszExtData, m_nExtDataLength, &SamplesPerSec, &Channels);

   if(result < 0)
   {
      faacDecClose(m_decHandle);
      m_decHandle = NULL;
      return S_FALSE;
   }

   m_outFormat.nSamplesPerSec = SamplesPerSec;
   m_outFormat.nChannels = Channels;
   m_outFormat.nBlockAlign = m_outFormat.nChannels * (m_outFormat.wBitsPerSample / 8);
   m_outFormat.nAvgBytesPerSec = m_outFormat.nSamplesPerSec * m_outFormat.nBlockAlign;


   faacDecConfigurationPtr	pConfig;
   pConfig = faacDecGetCurrentConfiguration(m_decHandle);

   pConfig->defObjectType = 0;

   if(m_outFormat.wBitsPerSample == 16)
      pConfig->outputFormat = FAAD_FMT_16BIT;
   else
      pConfig->outputFormat = FAAD_FMT_FLOAT;

   faacDecSetConfiguration(m_decHandle, pConfig);

   pmtOut->SetType(&MEDIATYPE_Audio);
   pmtOut->SetSubtype(&MEDIASUBTYPE_PCM);
   pmtOut->SetTemporalCompression(FALSE);
   pmtOut->SetSampleSize(0);
   pmtOut->SetFormatType(&FORMAT_WaveFormatEx);
   pmtOut->SetFormat((BYTE*)&m_outFormat, sizeof(m_outFormat));

   *pnOutBufferSize = 2048 * m_outFormat.nBlockAlign;

   return S_OK;
}
STDMETHODIMP CDecodeAac::OnQueryInterface(REFIID riid, void ** ppv)
{
   if(riid == IID_IDecodeInterface)
      return GetInterface((IDecodeInterface *)this, ppv);

   return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);
}
CUnknown * WINAPI CDecodeAac::CreateInstance(LPUNKNOWN punk, HRESULT *phr)
{  

   ASSERT(phr);
    
   CDecodeAac *pNewObject = new CDecodeAac(punk, phr);
   if (pNewObject == NULL)
   {
      if(phr != NULL)
         *phr = E_OUTOFMEMORY;
   }

   return dynamic_cast<CUnknown *>(pNewObject);
}
