/**************************************************************************
 * 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 "EcoDecoTooL.h"


static const GUID IID_IDecodeInterface = { 0x51296c64, 0x6a98, 0x462c, { 0x81, 0x14, 0x22, 0x59, 0xc0, 0x56, 0xc9, 0x6c } };
static const GUID CLSID_DecodeAac =         { 0x723b8a44, 0x86b6, 0x4497, { 0x9c, 0x77, 0x38, 0x14, 0xd7, 0x6a, 0x2f, 0xa6 } };
static const GUID CLSID_DecodeAc3 =         { 0x38321b5e, 0xc287, 0x4c1a, { 0x91, 0x43, 0x79, 0x01, 0x7c, 0x33, 0x9a, 0x3f } };
static const GUID CLSID_DecodeFlac =        { 0xac0a8547, 0x178a, 0x49be, { 0xb2, 0x66, 0x3d, 0x6f, 0x8e, 0x32, 0x38, 0x3a } };
static const GUID CLSID_DecodeTta =         { 0xb1c3b86e, 0xd75e, 0x4e19, { 0xa3, 0x14, 0x7b, 0xb6, 0x07, 0x3e, 0xef, 0xea } };
static const GUID CLSID_DecodeWv =          { 0x9b4644d6, 0xec42, 0x4c49, { 0xbc, 0x77, 0x93, 0x82, 0x9a, 0x22, 0xc0, 0x17 } };
static const GUID CLSID_RealAudioDecoder  = { 0x941A4793, 0xA705, 0x4312, { 0x8D, 0xFC, 0xC1, 0x1C, 0xA0, 0x5F, 0x39, 0x7E } };
static const GUID CLSID_WMAudioDecoderDMO  = { 0x2EEB4ADF, 0x4578, 0x4D10, { 0xBC, 0xA7, 0xBB, 0x95, 0x5F, 0x56, 0x32, 0x0A } };
static const GUID CLSID_WMSpeechDecoderDMO = { 0x874131CB, 0x4ECC, 0x443B, { 0x89, 0x48, 0x74, 0x6B, 0x89, 0x59, 0x5D, 0x20 } };
static const GUID WMMEDIASUBTYPE_WMAudioV8 = { 0x00000161, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; 
static const GUID WMMEDIASUBTYPE_WMAudioV9 = { 0x00000162, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; 
static const GUID WMMEDIASUBTYPE_WMSP1     = { 0x0000000A, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } };
static const GUID MEDIASUBTYPE_ADPCM =       { 0x00000002, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };


static const GUID CLSID_SourceApe =  { 0x71f2e335, 0x44cd, 0x4ad5, { 0x94, 0x67, 0xd0, 0xb9, 0x16, 0x9c, 0x5b, 0xa5 } };
static const GUID CLSID_SourceFlac = { 0x11971e60, 0x0f06, 0x4c4f, { 0xb8, 0xf8, 0x48, 0x7f, 0x60, 0x4c, 0x16, 0x75 } };
static const GUID CLSID_SourceTta =  { 0xe2fa064b, 0xcf4d, 0x4e7f, { 0xb9, 0x8e, 0x24, 0x28, 0xb9, 0x0b, 0xcf, 0xe8 } };
static const GUID CLSID_SourceWv =   { 0x4786fc41, 0x7568, 0x4b2b, { 0x81, 0xe1, 0x85, 0xe9, 0x49, 0x00, 0xc7, 0x53 } };
static const GUID CLSID_CDDAReader = { 0x54A35221, 0x2C8D, 0x4A31, { 0xA5, 0xDF, 0x6D, 0x80, 0x98, 0x47, 0xE3, 0x93 } };


static const GUID CLSID_AC3Parser         = { 0x280a3020, 0x86cf, 0x11d1, { 0xab, 0xe6, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75 } };
static const GUID CLSID_WAVEParser        = { 0xD51BD5A1, 0x7548, 0x11CF, { 0xA5, 0x20, 0x00, 0x80, 0xC7, 0x7E, 0xF5, 0x8A } };
static const GUID CLSID_FlvSplitter       = { 0x47E792CF, 0x0BBE, 0x4F7A, { 0x85, 0x9C, 0x19, 0x4B, 0x07, 0x68, 0x65, 0x0A } };
static const GUID CLSID_MatroskaSplitter  = { 0x149D2E01, 0xC32E, 0x4939, { 0x80, 0xF6, 0xC0, 0x7B, 0x81, 0x01, 0x5A, 0x7A } };
static const GUID CLSID_MP4Splitter       = { 0x61F47056, 0xE400, 0x43D3, { 0xAF, 0x1E, 0xAB, 0x7D, 0xFF, 0xD4, 0xC4, 0xAD } };
static const GUID CLSID_MpaSplitter       = { 0x0E9D4BF7, 0xCBCB, 0x46C7, { 0xBD, 0x80, 0x4E, 0xF2, 0x23, 0xA3, 0xDC, 0x2B } };
static const GUID CLSID_OggSplitter       = { 0x9FF48807, 0xE133, 0x40AA, { 0x82, 0x6F, 0x9B, 0x29, 0x59, 0xE5, 0x23, 0x2D } };
static const GUID CLSID_RealMediaSplitter = { 0xE21BE468, 0x5C18, 0x43EB, { 0xB0, 0xCC, 0xDB, 0x93, 0xA8, 0x47, 0xD7, 0x69 } };


static const GUID MEDIASUBTYPE_AAC    =      { 0x000000ff, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID MEDIASUBTYPE_MP3    =      { 0x00000055, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID MEDIASUBTYPE_Vorbis =      { 0xcddca2d5, 0x6d75, 0x4f98, { 0x84, 0x0e, 0x73, 0x7b, 0xed, 0xd5, 0xc6, 0x3b } };
static const GUID MEDIASUBTYPE_AVI_AC3 =     { 0x00002000, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID MEDIASUBTYPE_FLAC_FRAMED = { 0x1541c5c0, 0xcddf, 0x477d, { 0xbc, 0x0a, 0x86, 0xf8, 0xae, 0x7f, 0x83, 0x54 } };
static const GUID MEDIASUBTYPE_TTA1 =        { 0x000077A1, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID MEDIASUBTYPE_WAVPACK4 =    { 0x00005756, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID MEDIASUBTYPE_RealAudio =   { 0x52504953, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } };


struct DECODE_FILTER_DATA
{
   GUID subtype;
   GUID clsid;
};

static const DECODE_FILTER_DATA g_audioDecodeFilterData[] =
{
   { MEDIASUBTYPE_AAC,               CLSID_DecodeAac},
   { MEDIASUBTYPE_MP3,               CLSID_DecodeMp3},
   { MEDIASUBTYPE_MPEG2_AUDIO,       CLSID_DecodeMp3},
   { MEDIASUBTYPE_Vorbis,            CLSID_DecodeVorbis},
   { MEDIASUBTYPE_DOLBY_AC3,         CLSID_DecodeAc3},
   { MEDIASUBTYPE_AVI_AC3,           CLSID_DecodeAc3},
   { MEDIASUBTYPE_FLAC_FRAMED,       CLSID_DecodeFlac},
   { MEDIASUBTYPE_TTA1,              CLSID_DecodeTta},
   { MEDIASUBTYPE_WAVPACK4,          CLSID_DecodeWv},
   { WMMEDIASUBTYPE_WMAudioV8,       CLSID_WMAudioDecoderDMO},
   { WMMEDIASUBTYPE_WMAudioV9,       CLSID_WMAudioDecoderDMO},
   { WMMEDIASUBTYPE_WMSP1,           CLSID_WMSpeechDecoderDMO},
   { MEDIASUBTYPE_MPEG1Packet,       CLSID_CMpegAudioCodec},
   { MEDIASUBTYPE_MPEG1Payload,      CLSID_CMpegAudioCodec},
   { MEDIASUBTYPE_MPEG1AudioPayload, CLSID_CMpegAudioCodec},
   { MEDIASUBTYPE_ADPCM,             CLSID_ACMWrapper},
   { MEDIASUBTYPE_RealAudio,         CLSID_RealAudioDecoder},
};


struct SOURCE_FILTER_DATA
{
   WCHAR *pwFileExt;
   GUID clsid[2];
};

static const SOURCE_FILTER_DATA g_sourceFilterData[] =
{


   {  L".wma.wmv.asf",              CLSID_WMAsfReader, CLSID_NULL}, 
   {  L".wma.wmv.asf",              CLSID_WMAsfReader, CLSID_NULL},
   {  L".wma.wmv.asf",              CLSID_WMAsfReader, CLSID_NULL},
   {  L".aac",                      CLSID_AsyncReader, CLSID_MpaSplitter},
   {  L".ac3",                      CLSID_AsyncReader, CLSID_AC3Parser},
   {  L".aif.aifc.aiff.au",         CLSID_AsyncReader, CLSID_WAVEParser},
   {  L".avi.ivf",                  CLSID_AsyncReader, CLSID_AviSplitter},
   {  L".ape",                      CLSID_SourceApe,   CLSID_NULL},
   {  L".cda",                      CLSID_CDDAReader,  CLSID_WAVEParser},
   {  L".flac",                     CLSID_SourceFlac,  CLSID_NULL},
   {  L".flv",                      CLSID_AsyncReader, CLSID_FlvSplitter},
   {  L".flv",                      CLSID_AsyncReader, CLSID_MP4Splitter},
   {  L".mkv.mka",                  CLSID_AsyncReader, CLSID_MatroskaSplitter},
   {  L".mp4.mov",                  CLSID_AsyncReader, CLSID_MP4Splitter},
   {  L".mpg.mpeg.mpe.m1v.m2v.mpa", CLSID_AsyncReader, CLSID_MpaSplitter},
   {  L".mpg.mpeg.mpe.m1v.m2v.mpa", CLSID_AsyncReader, CLSID_MPEG1Splitter},
   {  L".mpg.mpeg.mpe.m1v.m2v.mpa", CLSID_AsyncReader, CLSID_MPEG2Demultiplexer},
   {  L".mp3",                      CLSID_AsyncReader, CLSID_MpaSplitter},
   {  L".ogg",                      CLSID_AsyncReader, CLSID_OggSplitter},
   {  L".ogm",                      CLSID_AsyncReader, CLSID_OggSplitter},
   {  L".rm .ram .ra ",             CLSID_AsyncReader, CLSID_RealMediaSplitter},
   {  L".tta",                      CLSID_SourceTta,   CLSID_NULL},
   {  L".m2a.vob",                  CLSID_AsyncReader, CLSID_MPEG2Demultiplexer},
   {  L".wav",                      CLSID_AsyncReader, CLSID_WAVEParser},
   {  L".wv",                       CLSID_SourceWv,    CLSID_NULL}
};

CConvertDirectShow::CConvertDirectShow()
{
   m_pGraphBuilder = NULL;
   m_pMediaEventEx = NULL;
   m_pMediaControl = NULL;
   m_pConvertWavInterface = NULL;
}

CConvertDirectShow::~CConvertDirectShow()
{




}

void CConvertDirectShow::ReleaseDirectShow(void)
{
   if(m_pMediaControl != NULL)
      m_pMediaControl->Stop();

   if(m_pMediaEventEx != NULL)
      m_pMediaEventEx->SetNotifyWindow(NULL, 0, 0);

   Release();

   
   SAFE_RELEASE(m_pConvertWavInterface);
   SAFE_RELEASE(m_pMediaEventEx);
   SAFE_RELEASE(m_pMediaControl);
   SAFE_RELEASE(m_pGraphBuilder);
}

HRESULT CConvertDirectShow::GetDirectShowFormat(WAVEFORMATEX *pInputFormat, WAVEFORMATEX *pOutputFormat)
{
   if(m_pConvertWavInterface == NULL)
      return E_FAIL;

   
   m_pConvertWavInterface->GetInFormat(pInputFormat);
   m_pConvertWavInterface->GetOutFormat(pOutputFormat);

   return S_OK;
}

void CConvertDirectShow::SetWait(int nWait)
{
   if(m_pConvertWavInterface == NULL)
      return;

   m_pConvertWavInterface->SetWait(nWait);
}

HRESULT CConvertDirectShow::GetOutputFileName(WCHAR *pwOutputFileName, WCHAR *pwInputFileName, WCHAR *pwOutputPathName, WCHAR *pwFileExtension, bool bDelete)
{  

   ::lstrcpy(pwOutputFileName, pwOutputPathName);

   if(pwOutputFileName[::lstrlen(pwOutputFileName) - 1] != '\\')
      ::lstrcat(pwOutputFileName, L"\\");

   ::lstrcat(pwOutputFileName, ::PathFindFileName(pwInputFileName));
   ::PathRenameExtension(pwOutputFileName, pwFileExtension);

   if(::lstrcmpi(pwInputFileName, pwOutputFileName) == 0)
   {  
      ::PathRemoveExtension(pwOutputFileName);
      ::lstrcat(pwOutputFileName, L"_");
      ::PathAddExtension(pwOutputFileName, pwFileExtension);
   }

   if(bDelete == true)
   {
      if(::PathFileExists(pwOutputFileName) != FALSE)
      {  

         
         if(::DeleteFile(pwOutputFileName) == FALSE)
            return E_FAIL;
      }
   }

   return S_OK;
}

HRESULT CConvertDirectShow::Render(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, CONVERTDATA2 *pConvertData, int nWait)
{  

   HRESULT hr;
   IBaseFilter *pFilter = NULL;
   IMediaSeeking *pMediaSeeking = NULL;

   
   ::CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGraphBuilder);
   if(m_pGraphBuilder == NULL) goto error;

  
   pFilter = AddFilter(m_pGraphBuilder, CLSID_ConvertWav);
   if(pFilter == NULL) goto error;

   
   pFilter->QueryInterface(IID_IConvertWavInterface, (void **)&m_pConvertWavInterface);
   if(m_pConvertWavInterface == NULL) goto error;

   
   hr = RenderGraph(m_pGraphBuilder, pwInputFileName, pwOutputFileName, pFilter, m_pConvertWavInterface, pConvertData);
   if(FAILED(hr)) goto error;

   
   if( (pConvertData->nGainMode == GAIN_TRACK_NORMALIZE || pConvertData->nGainMode == GAIN_ALBUM_NORMALIZE) && pConvertData->bNormalize == false)
      m_pConvertWavInterface->SetNormalize(pConvertData->dNormalize);

   
   m_pConvertWavInterface->SetWait(nWait);

   
   m_llTotalBytes = -1;

   m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&pMediaSeeking);
   if(pMediaSeeking != NULL)
   {
      LONGLONG llDuration;
      hr = pMediaSeeking->GetDuration(&llDuration);
      if(SUCCEEDED(hr))
      {
         WAVEFORMATEX wf;

         
         m_pConvertWavInterface->GetInFormat(&wf);

         
         m_llTotalBytes = (LONGLONG)((double)llDuration * (double)wf.nAvgBytesPerSec / 10000000.0);
      }
   }

   
   m_pGraphBuilder->QueryInterface(IID_IMediaEventEx, (void **)&m_pMediaEventEx);
   if(m_pMediaEventEx == NULL) goto error;

   if(hWnd != NULL)
   {
      hr = m_pMediaEventEx->SetNotifyWindow((OAHWND)hWnd, WM_GRAPHNOTIFY, 0);
      if(hr != NO_ERROR) goto error;
   }

   
   m_pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&m_pMediaControl);
   if(m_pMediaControl == NULL) goto error;

   
   hr = m_pMediaControl->Run();
   if(FAILED(hr)) goto error;

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFilter);
   return S_OK;

error:

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFilter);

   ReleaseDirectShow();

   return E_FAIL;
}

int CConvertDirectShow::GetPercent(void)
{  
   if(m_pConvertWavInterface == NULL || m_llTotalBytes == -1)
      return -1;

   LONGLONG llTransformedBytes;
   m_pConvertWavInterface->GetTransformedBytes(&llTransformedBytes);

   return (int)(llTransformedBytes * 100 / m_llTotalBytes);
}

int CConvertDirectShow::GraphNotify(void)
{
   if(m_pMediaEventEx == NULL)
      return E_FAIL;

   
   long lEventCode;
   m_pMediaEventEx->WaitForCompletion(100, &lEventCode);

   if(lEventCode == EC_COMPLETE)
      return EC_COMPLETE;

   if(lEventCode == EC_ERRORABORT)
      return EC_ERRORABORT;

   if(lEventCode == EC_USERABORT)
      return EC_USERABORT;

   return S_OK;
}













HRESULT CConvertDirectShow::ConnectDirect(IGraphBuilder *pGraphBuilder, IBaseFilter *pFilter1, IBaseFilter *pFilter2, AM_MEDIA_TYPE *pAmMediaType)
{
   CheckPointer(pGraphBuilder, E_POINTER);
   CheckPointer(pFilter1, E_POINTER);
   CheckPointer(pFilter2, E_POINTER);


   HRESULT hr;
   int nInputPinCount, nOutputPinCount;
   IPin *pInPin = NULL;
   IPin *pOutPin = NULL;

   
   nOutputPinCount = this->GetPinCount(pFilter1, PINDIR_OUTPUT);
   if(nOutputPinCount == 0) return E_FAIL;

   
   nInputPinCount  = this->GetPinCount(pFilter2, PINDIR_INPUT);
   if(nInputPinCount == 0) return E_FAIL;

   for(int i=0;i<nOutputPinCount;i++)
   {
      for(int j=0;j<nInputPinCount;j++)
      {
         pOutPin = this->GetPin(pFilter1, PINDIR_OUTPUT, i);
         if(pOutPin == NULL) return E_FAIL;

         pInPin = this->GetPin(pFilter2, PINDIR_INPUT, j);
         if(pInPin == NULL)
         {
            SAFE_RELEASE(pOutPin);
            return E_FAIL;
         }

         hr = pGraphBuilder->ConnectDirect(pOutPin, pInPin, pAmMediaType);

         SAFE_RELEASE(pInPin);
         SAFE_RELEASE(pOutPin);

         if (hr == S_OK)
           return S_OK;
      }
   }

   SAFE_RELEASE(pInPin);
   SAFE_RELEASE(pOutPin);

   return VFW_E_CANNOT_CONNECT;
}





int CConvertDirectShow::GetPinCount(IBaseFilter *pFilter, PIN_DIRECTION pinDirection)
{
   CheckPointer(pFilter, 0);

   HRESULT hr;
   int nPinCount;
   IEnumPins *pEnumPins = NULL;
   IPin *pPin = NULL;

   nPinCount = 0;

   hr = pFilter->EnumPins(&pEnumPins);
   if (hr != NO_ERROR) return 0;

   hr = pEnumPins->Reset();

   while(pEnumPins->Next(1, &pPin, 0) == S_OK)
   {
      PIN_DIRECTION dir;
      pPin->QueryDirection(&dir);

      if (dir == pinDirection)
         nPinCount++;

      SAFE_RELEASE(pPin);
   }

   SAFE_RELEASE(pPin);
   SAFE_RELEASE(pEnumPins);

   return nPinCount;
}





IPin* CConvertDirectShow::GetPin(IBaseFilter *pFilter, PIN_DIRECTION pinDirection, int nPinNumber)
{
   CheckPointer(pFilter, NULL);

   int hr, count;
   IEnumPins *pEnumPins = NULL;
   IPin *pPin = NULL;

   count = 0;

   hr = pFilter->EnumPins(&pEnumPins);
   if (hr != NO_ERROR) return NULL;

   pEnumPins->Reset();

   while(pEnumPins->Next(1, &pPin, 0) == S_OK)
   {
      PIN_DIRECTION dir;
      pPin->QueryDirection(&dir);

      if (dir == pinDirection)
      {
         if(count == nPinNumber)
         {
            SAFE_RELEASE(pEnumPins);
            return pPin;
         }

         count++;
      }

      SAFE_RELEASE(pPin);
   }

   SAFE_RELEASE(pPin);
   SAFE_RELEASE(pEnumPins);

   return NULL;
}





IPin* CConvertDirectShow::GetPin(IBaseFilter *pFilter, PIN_DIRECTION pinDirection, GUID majortype)
{
   CheckPointer(pFilter, NULL);

   int hr;
   ULONG cFetched;

   IPin *pPin = NULL;
   AM_MEDIA_TYPE *pAm = NULL;
   IEnumPins *pEnumPins = NULL;
   IEnumMediaTypes *pEnumMediaTypes = NULL;

   hr = pFilter->EnumPins(&pEnumPins);
   if (hr != NO_ERROR) return NULL;

   pEnumPins->Reset();

   while(pEnumPins->Next(1, &pPin, 0) == S_OK)
   {
      PIN_DIRECTION dir;
      pPin->QueryDirection(&dir);

      if (dir == pinDirection)
      {
         pPin->EnumMediaTypes(&pEnumMediaTypes);
         if(pEnumMediaTypes == NULL)
            goto error;

         pEnumMediaTypes->Reset();

         while(pEnumMediaTypes->Next(1, &pAm, &cFetched) == S_OK)
         {
            if(pAm->majortype == majortype)
            {
               SAFE_DELETEMEDIATYPE(pAm);
               SAFE_RELEASE(pEnumMediaTypes);
               SAFE_RELEASE(pEnumPins);

               return pPin;
            }

            SAFE_DELETEMEDIATYPE(pAm);
         }

         SAFE_RELEASE(pEnumMediaTypes);
      }
   }

error:

   SAFE_DELETEMEDIATYPE(pAm);
   SAFE_RELEASE(pEnumMediaTypes);
   SAFE_RELEASE(pPin);
   SAFE_RELEASE(pEnumPins);

   return NULL;
}





IBaseFilter* CConvertDirectShow::AddFilter(IGraphBuilder *pGraphBuilder, CLSID clsid)
{
   CheckPointer(pGraphBuilder, NULL);

   HRESULT hr;
   IBaseFilter *pBaseFilter = NULL;

   hr = ::CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pBaseFilter);
   if(pBaseFilter == NULL) return NULL;

   hr = pGraphBuilder->AddFilter(pBaseFilter, NULL);
   if(hr != NO_ERROR)
   {
      SAFE_RELEASE(pBaseFilter);
      return NULL;
   }

   return pBaseFilter;
}





IBaseFilter* CConvertDirectShow::AddDmoFilter(IGraphBuilder *pGraphBuilder, CLSID clsid_Category, CLSID clsid)
{
   CheckPointer(pGraphBuilder, NULL);

   HRESULT hr;
   IBaseFilter *pDmoFilter = NULL;
   IDMOWrapperFilter *pDmoWrapper = NULL;

   hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pDmoFilter));
   if(FAILED(hr))
      return NULL;

   hr = pDmoFilter->QueryInterface(IID_IDMOWrapperFilter, reinterpret_cast<void**>(&pDmoWrapper));
   if(FAILED(hr))
   {
      SAFE_RELEASE(pDmoFilter);
      return NULL;
   }

   hr = pDmoWrapper->Init(clsid, clsid_Category); 
   SAFE_RELEASE(pDmoWrapper);

   if(FAILED(hr))
   {
      SAFE_RELEASE(pDmoFilter);
      return NULL;
   }

   hr = pGraphBuilder->AddFilter(pDmoFilter, NULL);
   if(FAILED(hr))
   {
      SAFE_RELEASE(pDmoFilter);
      return NULL;
   }

   return pDmoFilter;
}





IBaseFilter* CConvertDirectShow::AddSourceFilter(IGraphBuilder *pGraphBuilder, CLSID clsid, WCHAR *wpFileName)
{
   CheckPointer(pGraphBuilder, NULL);

   HRESULT hr;
   IBaseFilter *pBaseFilter = NULL;
   IFileSourceFilter *pFileSourceFilter = NULL;

   hr = ::CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pBaseFilter);
   if(pBaseFilter == NULL) return NULL;

   pBaseFilter->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSourceFilter);
   if(pFileSourceFilter == NULL)
   {
      SAFE_RELEASE(pBaseFilter);
      return NULL;
   }

   hr = pGraphBuilder->AddFilter(pBaseFilter, NULL);
   if(hr != NO_ERROR)
   {
      SAFE_RELEASE(pBaseFilter);
      return NULL;
   }

   hr = pFileSourceFilter->Load(wpFileName, NULL);
   SAFE_RELEASE(pFileSourceFilter);
   if(FAILED(hr))
   {
      pGraphBuilder->RemoveFilter(pBaseFilter);
      SAFE_RELEASE(pBaseFilter);
      return NULL;
   }

   return pBaseFilter;
}





IBaseFilter* CConvertDirectShow::AddWriteFilter(IGraphBuilder *pGraphBuilder, CLSID clsid, WCHAR *wpFileName)
{
   CheckPointer(pGraphBuilder, NULL);

   HRESULT hr;
   IBaseFilter *pBaseFilter = NULL;
   IFileSinkFilter *pFileSinkFilter = NULL;

   pBaseFilter = this->AddFilter(pGraphBuilder, clsid);
   if(pBaseFilter == NULL)
      return NULL;

   pBaseFilter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
   if(pFileSinkFilter == NULL)
   {
      pGraphBuilder->RemoveFilter(pBaseFilter);
      SAFE_RELEASE(pBaseFilter);
      return NULL;
   }

   hr = pFileSinkFilter->SetFileName(wpFileName, NULL);
   if(FAILED(hr))
   {
      SAFE_RELEASE(pFileSinkFilter);
      pGraphBuilder->RemoveFilter(pBaseFilter);
      SAFE_RELEASE(pBaseFilter);
      return NULL;
   }

   SAFE_RELEASE(pFileSinkFilter);

   return pBaseFilter;
}

HRESULT CConvertDirectShow::ConnectWithDecoder(IGraphBuilder *pGraphBuilder, IBaseFilter *pFilter1, IBaseFilter *pFilter2, bool bConvert16bit)
{  

   HRESULT hr;
   int i, nArraySize;

   IPin *pOutPin = NULL;
   AM_MEDIA_TYPE *pAm = NULL;
   IBaseFilter *pDecodeFilter = NULL;
   IEnumMediaTypes *pEnumMediaTypes = NULL;
   IDecodeInterface *pDecodeInterface = NULL;

   
   pOutPin = GetPin(pFilter1, PINDIR_OUTPUT, MEDIATYPE_Audio);
   if(pOutPin == NULL)
      goto error;

   
   pOutPin->EnumMediaTypes(&pEnumMediaTypes);
   if(pEnumMediaTypes == NULL)
      goto error;

   pEnumMediaTypes->Reset();

   
   while(true)
   {
      hr = pEnumMediaTypes->Next(1, &pAm, 0);
      if(hr != S_OK)
         goto error;

      
      if(pAm->subtype == MEDIASUBTYPE_PCM)
      {
         if(pAm->formattype == FORMAT_WaveFormatEx)
         {
            if(((WAVEFORMATEX *)pAm->pbFormat)->wFormatTag == WAVE_FORMAT_PCM || ((WAVEFORMATEX *)pAm->pbFormat)->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
            {
               
               hr = ConnectDirect(pGraphBuilder, pFilter1, pFilter2, NULL);
               if(SUCCEEDED(hr))
               {
                  SAFE_DELETEMEDIATYPE(pAm);
                  SAFE_RELEASE(pEnumMediaTypes);
                  SAFE_RELEASE(pOutPin);
                  SAFE_RELEASE(pDecodeFilter);
                  return S_OK;
               }
            }
         }
      }

      
      nArraySize = sizeof(g_audioDecodeFilterData) / sizeof(g_audioDecodeFilterData[0]);

      for(i=0;i<nArraySize;i++)
      {
         if(g_audioDecodeFilterData[i].subtype == pAm->subtype)
         {
            if(g_audioDecodeFilterData[i].subtype == WMMEDIASUBTYPE_WMAudioV8 || g_audioDecodeFilterData[i].subtype == WMMEDIASUBTYPE_WMAudioV9 || g_audioDecodeFilterData[i].subtype == WMMEDIASUBTYPE_WMSP1)
               pDecodeFilter = AddDmoFilter(pGraphBuilder, DMOCATEGORY_AUDIO_DECODER, g_audioDecodeFilterData[i].clsid); 
            else
               pDecodeFilter = AddFilter(pGraphBuilder, g_audioDecodeFilterData[i].clsid);

            if(pDecodeFilter != NULL)
               break;
         }
      }

      SAFE_DELETEMEDIATYPE(pAm);

      if(pDecodeFilter == NULL)
         continue;

      
      hr = ConnectDirect(pGraphBuilder, pFilter1, pDecodeFilter, NULL);
      if(FAILED(hr))
      {
         pGraphBuilder->RemoveFilter(pDecodeFilter);
         SAFE_RELEASE(pDecodeFilter);
         continue;
      }

      if(bConvert16bit == true)
      {  
         pDecodeFilter->QueryInterface(IID_IDecodeInterface, (void **)&pDecodeInterface);
         if(pDecodeInterface != NULL)
         {
            WAVEFORMATEX wf;
            pDecodeInterface->GetOutFormat(&wf);

            wf.wFormatTag = WAVE_FORMAT_PCM;
            wf.wBitsPerSample = 16;
            wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8);
            wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;

            pDecodeInterface->SetOutFormat(&wf);
            SAFE_RELEASE(pDecodeInterface);
         }
      }

      
      hr = ConnectDirect(pGraphBuilder, pDecodeFilter, pFilter2, NULL);
      if(FAILED(hr))
      {
         pGraphBuilder->RemoveFilter(pDecodeFilter);
         SAFE_RELEASE(pDecodeFilter);
         continue;
      }

      SAFE_RELEASE(pEnumMediaTypes);
      SAFE_RELEASE(pOutPin);
      SAFE_RELEASE(pDecodeFilter);

      return S_OK;
   }

error:

   SAFE_RELEASE(pDecodeInterface);
   SAFE_DELETEMEDIATYPE(pAm);
   SAFE_RELEASE(pEnumMediaTypes);
   SAFE_RELEASE(pOutPin);
   SAFE_RELEASE(pDecodeFilter);

   return E_FAIL;
}

HRESULT CConvertDirectShow::RenderByFileExt(IGraphBuilder *pGraphBuilder, WCHAR *pwInputFileName, IBaseFilter *pRender, bool bConvert16bit)
{  

   HRESULT hr;
   int i, nArraySize;
   WCHAR *pwFileExt = NULL;
   IBaseFilter *pFilter[2] = {NULL, NULL};

   
   pwFileExt = ::PathFindExtension(pwInputFileName);

   if(pwFileExt == NULL)
      return E_FAIL;

   nArraySize = sizeof(g_sourceFilterData) / sizeof(g_sourceFilterData[0]);

   for(i=0;i<nArraySize;i++)
   {
      if(::StrStrI(g_sourceFilterData[i].pwFileExt, pwFileExt) != NULL)
      {
         pFilter[0] = AddSourceFilter(pGraphBuilder, g_sourceFilterData[i].clsid[0], pwInputFileName);
         if(pFilter[0] == NULL)
            continue;

         
         if(g_sourceFilterData[i].clsid[1] == GUID_NULL)
            break;

         
         
         pFilter[1] = AddFilter(pGraphBuilder, g_sourceFilterData[i].clsid[1]);
         if(pFilter[1] == NULL)
         {
            pGraphBuilder->RemoveFilter(pFilter[0]);
            SAFE_RELEASE(pFilter[0]);
            continue;
         }

         
         hr = ConnectDirect(pGraphBuilder, pFilter[0], pFilter[1], NULL);
         if(SUCCEEDED(hr))
            break;

         pGraphBuilder->RemoveFilter(pFilter[0]);
         SAFE_RELEASE(pFilter[0]);

         pGraphBuilder->RemoveFilter(pFilter[1]);
         SAFE_RELEASE(pFilter[1]);
      }
   }

   if(pFilter[0] == NULL)
   {  
      goto error;
   }

   
   if(pFilter[1] == NULL)
      hr = ConnectWithDecoder(pGraphBuilder, pFilter[0], pRender, bConvert16bit);
   else
      hr = ConnectWithDecoder(pGraphBuilder, pFilter[1], pRender, bConvert16bit);

   if(FAILED(hr))
      goto error;

   SAFE_RELEASE(pFilter[0]);
   SAFE_RELEASE(pFilter[1]);

   return S_OK;

error:

   SAFE_RELEASE(pFilter[0]);
   SAFE_RELEASE(pFilter[1]);

   return E_FAIL;
}

HRESULT CConvertDirectShow::RenderByAllFilter(IGraphBuilder *pGraphBuilder, WCHAR *pwInputFileName, IBaseFilter *pRender, bool bConvert16bit)
{  
   HRESULT hr;
   int i, nArraySize;
   WCHAR *pwFileExt = NULL;
   IBaseFilter *pFilter[2] = {NULL, NULL};

   nArraySize = sizeof(g_sourceFilterData) / sizeof(g_sourceFilterData[0]);

   for(i=0;i<nArraySize;i++)
   {
if(i==19)
{
int ii = 0;
}

      pFilter[0] = AddSourceFilter(pGraphBuilder, g_sourceFilterData[i].clsid[0], pwInputFileName);
      if(pFilter[0] == NULL)
         continue;

      
      if(g_sourceFilterData[i].clsid[1] == GUID_NULL)
      {
         
         hr = ConnectWithDecoder(pGraphBuilder, pFilter[0], pRender, bConvert16bit);

         if(SUCCEEDED(hr))
         {
            SAFE_RELEASE(pFilter[0]);
            SAFE_RELEASE(pFilter[1]);
            return S_OK;
         }

         pGraphBuilder->RemoveFilter(pFilter[0]);
         SAFE_RELEASE(pFilter[0]);

         continue;
      }

      
      
      pFilter[1] = AddFilter(pGraphBuilder, g_sourceFilterData[i].clsid[1]);
      if(pFilter[1] == NULL)
      {
         pGraphBuilder->RemoveFilter(pFilter[0]);
         SAFE_RELEASE(pFilter[0]);
         continue;
      }

      
      hr = ConnectDirect(pGraphBuilder, pFilter[0], pFilter[1], NULL);
      if(SUCCEEDED(hr))
      {
         
         hr = ConnectWithDecoder(pGraphBuilder, pFilter[1], pRender, bConvert16bit);

         if(SUCCEEDED(hr))
         {
            SAFE_RELEASE(pFilter[0]);
            SAFE_RELEASE(pFilter[1]);
            return S_OK;
         }
      }

      pGraphBuilder->RemoveFilter(pFilter[0]);
      SAFE_RELEASE(pFilter[0]);

      pGraphBuilder->RemoveFilter(pFilter[1]);
      SAFE_RELEASE(pFilter[1]);
   }

   SAFE_RELEASE(pFilter[0]);
   SAFE_RELEASE(pFilter[1]);

   return E_FAIL;
}

