/**************************************************************************
 * 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"

typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID, REFIID, LPVOID *);
#define SAFE_RELEASE(p) { if(p!=NULL) { (p)->Release(); (p)=NULL; } }

struct FILTER_DATA
{
   GUID clsid;
   WCHAR *pwFileName;
};


struct RENDER_DATA
{
   WCHAR *pwFileExt;
   GUID clsid[3];
   WCHAR *pwFileName[3];
};

struct AUDIO_SUBTYPE_DATA
{
   GUID guid;
   WCHAR *pwFilterFileName;
   CLSID clsid;
};


static const GUID CLSID_NULL = { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
static const GUID CLSID_ConvertWav = { 0xefbf44b7, 0xbb1f, 0x48c8, { 0xba, 0x59, 0xeb, 0x98, 0x3, 0x57, 0x9b, 0x1e } };
static const GUID IID_IConvertWavInterface = { 0xbda4cab0, 0x2eb2, 0x42cb, { 0xa6, 0x31, 0xe8, 0x9e, 0x82, 0x35, 0x2d, 0xd1} };

static const GUID CLSID_EcoDecoInPlace  = { 0x6e94d0bb, 0x0567, 0x4ef6, { 0xac, 0x5d, 0x7b, 0xf2, 0x9b, 0xf1, 0x17, 0xe3 } };
static const GUID IID_IEcoDecoInterface = { 0x1bb79127, 0xe01d, 0x4f40, { 0x92, 0x57, 0xbb, 0xc6, 0x22, 0x43, 0x7f, 0xb7 } };

static const GUID CLSID_EcoDecoWriter         = { 0x4b1abfc2, 0xe95f, 0x4cb3, { 0xbe, 0xc8, 0x65, 0x7e, 0x6d, 0x89, 0x6a, 0x70 } };
static const GUID IID_IEcoDecoWriterInterface = { 0xfb4a5a46, 0x0bb7, 0x4c75, { 0xad, 0xa1, 0xe0, 0x24, 0x1e, 0xc9, 0x4e, 0x9e } };

static const GUID CLSID_DecodeMp3 = { 0xda178237, 0x1e6d, 0x4159, { 0xb9, 0xb7, 0xec, 0x32, 0xa, 0x4c, 0xc5, 0x14 } };
static const GUID CLSID_EncodeLame = { 0xed6dabe8, 0x2ad7, 0x465f, { 0xb2, 0xd4, 0x5b, 0x81, 0x13, 0x32, 0x95, 0x19 } };
static const GUID CLSID_EncodeFraunhofer = { 0x6997dde2, 0xcb75, 0x4d03, { 0xb8, 0x9b, 0x39, 0xc0, 0x7a, 0xf6, 0x13, 0x65 } };
static const GUID IID_IEncodeLameInterface = { 0x7ff9fbcc, 0x4270, 0x44c6, { 0x90, 0xc5, 0xd4, 0xe2, 0xd1, 0xbe, 0xbf, 0x6a } };
static const GUID IID_IEncodeFraunhoferInterface = { 0xd413bc2f, 0xf8a0, 0x4275, { 0xbe, 0x64, 0x45, 0xad, 0x9, 0x53, 0xdb, 0xa1 } };

static const GUID CLSID_DecodeVorbis = { 0x78090821, 0x8aae, 0x4e21, { 0x81, 0xb2, 0x18, 0x46, 0x82, 0x97, 0xb4, 0x9f } };
static const GUID CLSID_EncodeVorbis = { 0x84563040, 0xdd36, 0x4f00, { 0x8e, 0xea, 0x21, 0x5c, 0xbd, 0xdb, 0x18, 0x14 } };
static const GUID IID_IEncodeVorbisInterface = { 0x44895f57, 0xfe20, 0x4d78, { 0x9f, 0xe3, 0x92, 0x32, 0x61, 0x20, 0xd4, 0xba } };

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 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 CLSID_Mp4Mux = { 0x5D33564D, 0x873C, 0x47FB, { 0x90, 0xAD, 0xC6, 0xB2, 0x65, 0x7E, 0xCE, 0x1A } };

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 } };
static const int g_decodeArraySize = 12;
static const FILTER_DATA g_decodeData[] = 
{
   { CLSID_DecodeMp3,          L"EcoDecoMp3.ax"},
   { CLSID_DecodeVorbis,       L"EcoDecoVorbis.ax"},
   { CLSID_DecodeAac,          L"EcoDecoAac.ax"},
   { CLSID_DecodeAc3,          L"EcoDecoAc3.ax"},
   { CLSID_CMpegAudioCodec,    L""},
   { CLSID_DecodeFlac,         L"EcoDecoFlac.ax"},
   { CLSID_DecodeTta,          L"EcoDecoTta.ax"},
   { CLSID_DecodeWv,           L"EcoDecoWv.ax"},
   { CLSID_WMAudioDecoderDMO,  L"DMO"},
   { CLSID_WMSpeechDecoderDMO, L"DMO"},
   { CLSID_ACMWrapper,         L""},               
   { CLSID_RealAudioDecoder,   L""}
};

static const int g_sourceArraySize = 6;
static const FILTER_DATA g_sourceData[] = 
{
   { CLSID_CDDAReader,  L"cddareader.ax"},
   { CLSID_SourceApe,   L"EcoDecoApe.ax"},
   { CLSID_SourceFlac,  L"EcoDecoFlac.ax"},
   { CLSID_SourceTta,   L"EcoDecoTta.ax"},
   { CLSID_SourceWv,    L"EcoDecoWv.ax"},
   { CLSID_WMAsfReader, L""}
};

static const int g_splitterArraySize = 11;
static const FILTER_DATA g_splitterData[] = 
{
   { CLSID_MpaSplitter,        L"MpaSplitter.ax"},
   { CLSID_AviSplitter,        L""},
   { CLSID_FlvSplitter,        L"FLVSplitter.ax"},
   { CLSID_MatroskaSplitter,   L"MatroskaSplitter.ax"},
   { CLSID_MP4Splitter,        L"MP4Splitter.ax"},
   { CLSID_OggSplitter,        L"OggSplitter.ax"},
   { CLSID_WAVEParser,         L""},
   { CLSID_MPEG1Splitter,      L""},                        
   { CLSID_MPEG2Demultiplexer, L""},
   { CLSID_AC3Parser,          L""},
   { CLSID_RealMediaSplitter,  L"RealMediaSplitter.ax"}
};
static const int g_nRenderArraySize = 25;
static const RENDER_DATA g_renderData[] =
{
   {  L".aac",                      CLSID_AsyncReader, CLSID_MpaSplitter,        CLSID_DecodeAac,    NULL,               L"MpaSplitter.ax",      L"EcoDecoAac.ax"},
   {  L".ac3",                      CLSID_AsyncReader, CLSID_AC3Parser,          CLSID_DecodeAc3,    NULL,               NULL,                   L"EcoDecoAc3.ax"},
   {  L".aif.aifc.aiff.au",         CLSID_AsyncReader, CLSID_WAVEParser,         CLSID_NULL,         NULL,               NULL,                   NULL},
   {  L".avi.ivf",                  CLSID_AsyncReader, CLSID_AviSplitter,        CLSID_NULL,         NULL,               NULL,                   NULL},
   {  L".ape",                      CLSID_SourceApe,   CLSID_NULL,               CLSID_NULL,         L"EcoDecoApe.ax",   NULL,                   NULL},
   {  L".cda",                      CLSID_CDDAReader,  CLSID_WAVEParser,         CLSID_NULL,         L"cddareader.ax",   NULL,                   NULL},
   {  L".flac",                     CLSID_SourceFlac,  CLSID_NULL,               CLSID_NULL,         L"EcoDecoFlac.ax",  NULL,                   NULL},
   {  L".flv",                      CLSID_AsyncReader, CLSID_FlvSplitter,        CLSID_NULL,         NULL,               L"FLVSplitter.ax",      NULL},
   {  L".flv",                      CLSID_AsyncReader, CLSID_MP4Splitter,        CLSID_NULL,         NULL,               L"MP4Splitter.ax",      NULL},
   {  L".mkv.mka",                  CLSID_AsyncReader, CLSID_MatroskaSplitter,   CLSID_NULL,         NULL,               L"MatroskaSplitter.ax", NULL},
   {  L".mp4.mov",                  CLSID_AsyncReader, CLSID_MP4Splitter,        CLSID_NULL,         NULL,               L"MP4Splitter.ax",      NULL},
   {  L".mpg.mpeg.mpe.m1v.m2v.mpa", CLSID_AsyncReader, CLSID_MpaSplitter,        CLSID_NULL,         NULL,               L"MpaSplitter.ax",      NULL},
   {  L".mpg.mpeg.mpe.m1v.m2v.mpa", CLSID_AsyncReader, CLSID_MPEG1Splitter,      CLSID_NULL,         NULL,               NULL,                   NULL},
   {  L".mpg.mpeg.mpe.m1v.m2v.mpa", CLSID_AsyncReader, CLSID_MPEG2Demultiplexer, CLSID_NULL,         NULL,               NULL,                   NULL},
   {  L".mp3",                      CLSID_AsyncReader, CLSID_MpaSplitter,        CLSID_DecodeMp3,    NULL,               L"MpaSplitter.ax",      L"EcoDecoMp3.ax"},
   {  L".ogg",                      CLSID_AsyncReader, CLSID_OggSplitter,        CLSID_DecodeVorbis, NULL,               L"OggSplitter.ax",      L"EcoDecoVorbis.ax"},
   {  L".ogm",                      CLSID_AsyncReader, CLSID_OggSplitter,        CLSID_NULL,         NULL,               L"OggSplitter.ax",      NULL},
   {  L".rm .ram .ra ",             CLSID_AsyncReader, CLSID_RealMediaSplitter,  CLSID_NULL,         NULL,               L"RealMediaSplitter.ax",NULL},
   {  L".tta",                      CLSID_SourceTta,   CLSID_NULL,               CLSID_NULL,         L"EcoDecoTta.ax",   NULL,                   NULL},
   {  L".m2a.vob",                  CLSID_AsyncReader, CLSID_MPEG2Demultiplexer, CLSID_NULL,         NULL,               NULL,                   NULL},
   {  L".wav",                      CLSID_AsyncReader, CLSID_WAVEParser,         CLSID_NULL,         NULL,               NULL,                   NULL},
   {  L".wv",                       CLSID_SourceWv,    CLSID_NULL,               CLSID_NULL,         L"EcoDecoWv.ax",    NULL,                   NULL},
   {  L".wma.wmv.asf",              CLSID_WMAsfReader, CLSID_WMAudioDecoderDMO,  CLSID_NULL,         NULL,               L"DMO",                 NULL},
   {  L".wma.wmv.asf",              CLSID_WMAsfReader, CLSID_WMSpeechDecoderDMO, CLSID_NULL,         NULL,               L"DMO",                 NULL},
   {  L".wma.wmv.asf",              CLSID_WMAsfReader, CLSID_DecodeMp3,          CLSID_NULL,         NULL,               L"EcoDecoMp3.ax",       NULL}
};

static const int g_nAudioSubtypeArraySize = 12;
static AUDIO_SUBTYPE_DATA g_audioSubtypeList[] =
{
   { MEDIASUBTYPE_MP3,               L"EcoDecoMp3.ax",    CLSID_DecodeMp3},
   { MEDIASUBTYPE_AAC,               L"EcoDecoAac.ax",    CLSID_DecodeAac},
   { MEDIASUBTYPE_Vorbis,            L"EcoDecoVorbis.ax", CLSID_DecodeVorbis},
   { MEDIASUBTYPE_PCM,               L"",                 CLSID_ACMWrapper}, 
   { MEDIASUBTYPE_DOLBY_AC3,         L"EcoDecoAc3.ax",    CLSID_DecodeAc3},
   { MEDIASUBTYPE_MPEG2_AUDIO,       L"EcoDecoMp3.ax",    CLSID_DecodeMp3},
   { MEDIASUBTYPE_AVI_AC3,           L"EcoDecoAc3.ax",    CLSID_DecodeAc3},
   { MEDIASUBTYPE_FLAC_FRAMED,       L"EcoDecoFlac.ax",   CLSID_DecodeFlac},
   { MEDIASUBTYPE_TTA1,              L"EcoDecoTta.ax",    CLSID_DecodeTta},
   { MEDIASUBTYPE_WAVPACK4,          L"EcoDecoWv.ax",     CLSID_DecodeWv},
   { MEDIASUBTYPE_MPEG1AudioPayload, L"",                 CLSID_CMpegAudioCodec},
   { MEDIASUBTYPE_RealAudio,         L"",                 CLSID_RealAudioDecoder} 
};
CDirectShow::CDirectShow()
{  
   m_pGraphBuilder = NULL;
   m_pMediaEventEx = NULL;
   m_pMediaControl = NULL;

   m_nRawFileType = 0;
   m_llTotalBytes = 0;
   m_pEcoDecoInterface = NULL;
   m_pConvertWavInterface = NULL;

   ::ZeroMemory(&m_InputFormat, sizeof(m_InputFormat));
   ::ZeroMemory(&m_OutputFormat, sizeof(m_OutputFormat));

   for(int i=0;i<4;i++)
   {
      m_filterData[i].hDLL = NULL;
      m_filterData[i].clsid = GUID_NULL;
   }
}
CDirectShow::~CDirectShow()
{  

   Release();

   for(int i=0;i<4;i++)
   {
      if(m_filterData[i].hDLL != NULL)
      {
         ::FreeLibrary(m_filterData[i].hDLL);
         m_filterData[i].hDLL = NULL;
         m_filterData[i].clsid = GUID_NULL;
      }
   }
}
void CDirectShow::Release()
{
   if(m_pGraphBuilder == NULL)
      return;

   if(m_pMediaControl != NULL)
      m_pMediaControl->Stop();

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

   SAFE_RELEASE(m_pConvertWavInterface);
   SAFE_RELEASE(m_pEcoDecoInterface);
   SAFE_RELEASE(m_pMediaControl);

   SAFE_RELEASE(m_pGraphBuilder);

   for(int i=0;i<4;i++)
   {
      if(m_filterData[i].hDLL != NULL)
      {
         ::FreeLibrary(m_filterData[i].hDLL);
         m_filterData[i].hDLL = NULL;
         m_filterData[i].clsid = GUID_NULL;
      }
   }
}
int CDirectShow::WaitForCompletion(void)
{  
   if(m_pMediaEventEx == NULL)
      return 0;

   long lEventCode;

   m_pMediaEventEx->WaitForCompletion(INFINITE, &lEventCode);

   if(lEventCode == EC_ERRORABORT)
      return EC_ERRORABORT;

   if(lEventCode == EC_COMPLETE)
      return EC_COMPLETE;

   return 0;
}
int CDirectShow::PlayEnd(void)
{
   if(m_pMediaEventEx == NULL)
      return 0;

   long lEventCode;

   m_pMediaEventEx->WaitForCompletion(10, &lEventCode);

   if(lEventCode == EC_ERRORABORT)
      return EC_ERRORABORT;

   if(lEventCode == EC_COMPLETE)
      return EC_COMPLETE;

   return 0;
}
int CDirectShow::GetRawFileType(void)
{
   return m_nRawFileType;
}
void CDirectShow::SetWait(int nWait)
{
   if(m_pEcoDecoInterface != NULL)
      m_pEcoDecoInterface->SetWait(nWait);
}
HRESULT CDirectShow::GetReplaygainResult(double *pdPeak, double *pdTrackgain)
{
   if(m_pConvertWavInterface == NULL)
      return E_FAIL;

   m_pConvertWavInterface->GetReplaygainResult(pdPeak, pdTrackgain);
   return S_OK;
}
int CDirectShow::GetPercent(void)
{
   if(m_pEcoDecoInterface != NULL && m_llTotalBytes > 0)
   {
      LONGLONG llTransformedBytes;
      m_pEcoDecoInterface->GetTransformedBytes(&llTransformedBytes);

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

   return -1;
}
void CDirectShow::GetFormat(WAVEFORMATEX *pIn, WAVEFORMATEX *pOut)
{
   ::CopyMemory(pIn, &m_InputFormat, sizeof(m_InputFormat));
   ::CopyMemory(pOut, &m_OutputFormat, sizeof(m_OutputFormat));
}
HRESULT CDirectShow::RenderWma(IGraphBuilder *pGraphBuilder, WCHAR *pwInputFileName, IBaseFilter *pRenderFilter, bool bConvert16bit)
{  

   const GUID CLSID_WMSource           = { 0x6B6D0800, 0x9ADA, 0x11D0, { 0xA5, 0x20, 0x00, 0xA0, 0xD1, 0x01, 0x29, 0xC0 } };
   const GUID CLSID_WMAudioDecoderDMO  = { 0x2EEB4ADF, 0x4578, 0x4D10, { 0xBC, 0xA7, 0xBB, 0x95, 0x5F, 0x56, 0x32, 0x0A } };
   const GUID CLSID_WMSpeechDecoderDMO = { 0x874131CB, 0x4ECC, 0x443B, { 0x89, 0x48, 0x74, 0x6B, 0x89, 0x59, 0x5D, 0x20 } };

   int i;
   HRESULT hr;
   IBaseFilter *pSourceFilter = NULL;
   IBaseFilter *pDecodeFilter = NULL;
   IDecodeInterface *pDecodeInterface = NULL;

   hr = ::CoCreateInstance(CLSID_WMSource, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pSourceFilter);
   if(pSourceFilter != NULL)
   {
      IFileSourceFilter *pFileSourceFilter = NULL;

      pSourceFilter->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSourceFilter);
      if(pFileSourceFilter != NULL)
      {
         hr = pFileSourceFilter->Load(pwInputFileName, NULL);
         SAFE_RELEASE(pFileSourceFilter);

         if(FAILED(hr))
            goto error;
      }

      SAFE_RELEASE(pSourceFilter);
   }

   pSourceFilter = dshowTool.AddSourceFilter(pGraphBuilder, CLSID_WMAsfReader, pwInputFileName);
   if(pSourceFilter == NULL)
      goto error;

   for(i=0;i<g_decodeArraySize;i++)
   {
      if(::lstrlen(g_decodeData[i].pwFileName) == 0)
      {  
         pDecodeFilter = dshowTool.AddFilter(pGraphBuilder, g_decodeData[i].clsid);
      }
      else if(::lstrcmpi(g_decodeData[i].pwFileName, L"DMO") == 0)
      {  
         pDecodeFilter = dshowTool.AddDmoFilter(pGraphBuilder, DMOCATEGORY_AUDIO_DECODER, g_decodeData[i].clsid);
      }
      else
      {  
         pDecodeFilter = AddFilter(&m_filterData[FILTER_DECODEAUDIO], pGraphBuilder, g_decodeData[i].pwFileName, g_decodeData[i].clsid);
      }

      if(pDecodeFilter == NULL)
         continue;

      hr = dshowTool.ConnectDirect(pGraphBuilder, pSourceFilter, pDecodeFilter, NULL);

      if(SUCCEEDED(hr))
         break;

      pGraphBuilder->RemoveFilter(pDecodeFilter);
      SAFE_RELEASE(pDecodeFilter);

      if(m_filterData[FILTER_DECODEAUDIO].hDLL != NULL)
      {
         ::FreeLibrary(m_filterData[FILTER_DECODEAUDIO].hDLL);
         m_filterData[FILTER_DECODEAUDIO].hDLL = NULL;
         m_filterData[FILTER_DECODEAUDIO].clsid = GUID_NULL;
      }
   }

   if(i == g_decodeArraySize)
      goto error; 

   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 = dshowTool.ConnectDirect(pGraphBuilder, pDecodeFilter, pRenderFilter, NULL);

   if(FAILED(hr))
   {  
      pGraphBuilder->RemoveFilter(pDecodeFilter);
      SAFE_RELEASE(pDecodeFilter);

      if(m_filterData[FILTER_DECODEAUDIO].hDLL != NULL)
      {
         ::FreeLibrary(m_filterData[FILTER_DECODEAUDIO].hDLL);
         m_filterData[FILTER_DECODEAUDIO].hDLL = NULL;
         m_filterData[FILTER_DECODEAUDIO].clsid = GUID_NULL;
      }

      goto error;
   }

   SAFE_RELEASE(pDecodeFilter);
   SAFE_RELEASE(pSourceFilter);

   return S_OK;

error:

   SAFE_RELEASE(pDecodeFilter);
   SAFE_RELEASE(pSourceFilter);

   return E_FAIL;
}
HRESULT CDirectShow::RenderByAllFilter(IGraphBuilder *pGraphBuilder, WCHAR *pwFileName, IBaseFilter *pRender, bool bConvert16bit)
{  

   int i, j, k;
   HRESULT hr = E_FAIL;
   WCHAR *pwFileExt = NULL;
   IBaseFilter *pBaseFilter[3] = {NULL, NULL, NULL};
   IDecodeInterface *pDecodeInterface = NULL;

   hr = RenderWma(m_pGraphBuilder, pwFileName, pRender, bConvert16bit);
   if(SUCCEEDED(hr))
      goto success;

   pBaseFilter[0] = dshowTool.AddSourceFilter(pGraphBuilder, CLSID_AsyncReader, pwFileName);
   if(pBaseFilter[0] == NULL) goto error;

   for(i=0;i<g_splitterArraySize;i++)
   {
      if(::lstrlen(g_splitterData[i].pwFileName) == 0)
      {  
         pBaseFilter[1] = dshowTool.AddFilter(pGraphBuilder, g_splitterData[i].clsid);
      }
      else
      {  
         pBaseFilter[1] = AddFilter(&m_filterData[FILTER_SPLITTER], pGraphBuilder, g_splitterData[i].pwFileName, g_splitterData[i].clsid);
      }
  
      if(pBaseFilter[1] == NULL)
         continue;

      hr = dshowTool.ConnectDirect(pGraphBuilder, pBaseFilter[0], pBaseFilter[1], NULL);
      if(FAILED(hr))
      {
         hr = pGraphBuilder->RemoveFilter(pBaseFilter[1]);
         SAFE_RELEASE(pBaseFilter[1]);

         if(m_filterData[FILTER_SPLITTER].hDLL != NULL)
         {
            ::FreeLibrary(m_filterData[FILTER_SPLITTER].hDLL);
            m_filterData[FILTER_SPLITTER].hDLL = NULL;
            m_filterData[FILTER_SPLITTER].clsid = GUID_NULL;
         }

         continue;
      }

      hr = dshowTool.ConnectDirect(pGraphBuilder, pBaseFilter[1], pRender, NULL);

      if(SUCCEEDED(hr)) goto success;

      for(j=0;j<g_decodeArraySize;j++)
      {
         if(::lstrlen(g_decodeData[j].pwFileName) == 0)
         {  
            pBaseFilter[2] = dshowTool.AddFilter(pGraphBuilder, g_decodeData[j].clsid);
         }
         else if(::lstrcmpi(g_decodeData[j].pwFileName, L"DMO") == 0)
         {  
            pBaseFilter[2] = dshowTool.AddDmoFilter(pGraphBuilder, DMOCATEGORY_AUDIO_DECODER, g_decodeData[j].clsid);
         }
         else
         {  
            pBaseFilter[2] = AddFilter(&m_filterData[FILTER_DECODEAUDIO], pGraphBuilder, g_decodeData[j].pwFileName, g_decodeData[j].clsid);
         }

         if(pBaseFilter[2] == NULL)
            continue;

         hr = dshowTool.ConnectDirect(pGraphBuilder, pBaseFilter[1], pBaseFilter[2], NULL);
         if(SUCCEEDED(hr))
         {
            if(bConvert16bit == true)
            {  
               pBaseFilter[2]->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 = dshowTool.ConnectDirect(pGraphBuilder, pBaseFilter[2], pRender, NULL);

            if(SUCCEEDED(hr)) goto success;
         }

         pGraphBuilder->RemoveFilter(pBaseFilter[2]);
         SAFE_RELEASE(pBaseFilter[2]);

         if(m_filterData[FILTER_DECODEAUDIO].hDLL != NULL)
         {
            ::FreeLibrary(m_filterData[FILTER_DECODEAUDIO].hDLL);
            m_filterData[FILTER_DECODEAUDIO].hDLL = NULL;
            m_filterData[FILTER_DECODEAUDIO].clsid = GUID_NULL;
         }
      }

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

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

   for(i=0;i<g_sourceArraySize;i++)
   {
      if(::lstrlen(g_sourceData[i].pwFileName) == 0)
      {  
         pBaseFilter[0] = dshowTool.AddSourceFilter(pGraphBuilder, g_sourceData[i].clsid, pwFileName);
      }
      else
      {  
         pBaseFilter[0] = AddSourceFilter(&m_filterData[FILTER_SOURCE], pGraphBuilder, g_sourceData[i].pwFileName, g_sourceData[i].clsid, pwFileName);
      }

      if(pBaseFilter[0] == NULL)
         continue;

      hr = dshowTool.ConnectDirect(pGraphBuilder, pBaseFilter[0], pRender, NULL);
      if(FAILED(hr))
      {  
         hr = RenderAudio(pGraphBuilder, pBaseFilter[0], pRender, bConvert16bit);
      }

      if(SUCCEEDED(hr)) goto success;

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

      if(m_filterData[FILTER_SOURCE].hDLL != NULL)
      {
         ::FreeLibrary(m_filterData[FILTER_SOURCE].hDLL);
         m_filterData[FILTER_SOURCE].hDLL = NULL;
         m_filterData[FILTER_SOURCE].clsid = GUID_NULL;
      }
   }

   goto error;

success:

   SAFE_RELEASE(pBaseFilter[0]);
   SAFE_RELEASE(pBaseFilter[1]);
   SAFE_RELEASE(pBaseFilter[2]);

   return S_OK;


error:

   for(k=2;k>=0;k--)
   {
      if(pBaseFilter[k] != NULL)
      {
         pGraphBuilder->RemoveFilter(pBaseFilter[k]);
         SAFE_RELEASE(pBaseFilter[k]);
      }
   }

   SAFE_RELEASE(pDecodeInterface);
   SAFE_RELEASE(pBaseFilter[0]);
   SAFE_RELEASE(pBaseFilter[1]);
   SAFE_RELEASE(pBaseFilter[2]);

   return E_FAIL;
}
HRESULT CDirectShow::RenderByFileExt(IGraphBuilder *pGraphBuilder, WCHAR *pwFileName, IBaseFilter *pRender, bool bConvert16bit)
{  

   int i, j, k;
   HRESULT hr = E_FAIL;
   WCHAR *pwFileExt = NULL;
   IBaseFilter *pBaseFilter[3] = {NULL, NULL, NULL};
   IDecodeInterface *pDecodeInterface = NULL;

   pwFileExt = ::PathFindExtension(pwFileName);

   if(pwFileExt == NULL)
      return E_FAIL;


   for(i=0;i<g_nRenderArraySize;i++)
   {
      if(::StrStrI(g_renderData[i].pwFileExt, pwFileExt) != NULL)
      {
         for(j=0;j<3;j++)
         {
            if(j==0)
            {  
               if(::lstrlen(g_renderData[i].pwFileName[j]) == 0)
               {  
                  pBaseFilter[j] = dshowTool.AddSourceFilter(pGraphBuilder, g_renderData[i].clsid[j], pwFileName);
               }
               else
               {  
                  pBaseFilter[j] = AddSourceFilter(&m_filterData[FILTER_SOURCE], pGraphBuilder, g_renderData[i].pwFileName[j], g_renderData[i].clsid[j], pwFileName);
               }
            }
            else
            {
               if(::lstrlen(g_renderData[i].pwFileName[j]) == 0)
               {  
                  pBaseFilter[j] = dshowTool.AddFilter(pGraphBuilder, g_renderData[i].clsid[j]);
               }
               else if(::lstrcmpi(g_renderData[i].pwFileName[j], L"DMO") == 0)
               {  
                  pBaseFilter[j] = dshowTool.AddDmoFilter(pGraphBuilder, DMOCATEGORY_AUDIO_DECODER, g_renderData[i].clsid[j]);
               }
               else
               {  

                  if(j==1 && ::lstrlen(g_renderData[i].pwFileName[0]) == 0)
                     pBaseFilter[j] = AddFilter(&m_filterData[FILTER_SPLITTER], pGraphBuilder, g_renderData[i].pwFileName[j], g_renderData[i].clsid[j]);
                  else
                     pBaseFilter[j] = AddFilter(&m_filterData[FILTER_DECODEAUDIO], pGraphBuilder, g_renderData[i].pwFileName[j], g_renderData[i].clsid[j]);
               }
            }

            if(pBaseFilter[j] == NULL)
            {
               j = 3;

               for(k=2;k>=0;k--)
               {
                  if(pBaseFilter[k] != NULL)
                  {
                     pGraphBuilder->RemoveFilter(pBaseFilter[k]);
                     SAFE_RELEASE(pBaseFilter[k]);
                  }
               }

               continue;
            }

            if(j==0)
            {
               if(g_renderData[i].clsid[1] == CLSID_NULL)
               {  
                  hr = S_OK;
                  goto success;
               }
            }
            else 
            {
               hr = dshowTool.ConnectDirect(pGraphBuilder, pBaseFilter[j-1], pBaseFilter[j], NULL);
               if(SUCCEEDED(hr))
               {
                  if(j == 2)  
                     goto success;

                  if(g_renderData[i].clsid[j+1] == CLSID_NULL) 
                     goto success;
               }
               else 
               {
                  j = 3;

                  for(k=2;k>=0;k--)
                  {
                     if(pBaseFilter[k] != NULL)
                     {
                        pGraphBuilder->RemoveFilter(pBaseFilter[k]);
                        SAFE_RELEASE(pBaseFilter[k]);
                     }
                  }

                  continue;
               }
            }
         }

         if(SUCCEEDED(hr))
            goto success;
      }
   }

   goto error;

success:

   if(bConvert16bit == true)
   {  
      pBaseFilter[j]->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 = dshowTool.ConnectDirect(pGraphBuilder, pBaseFilter[j], pRender, NULL);
   if(FAILED(hr))
   {  
      hr = RenderAudio(pGraphBuilder, pBaseFilter[j], pRender, bConvert16bit);
      if(FAILED(hr))
         goto error;
   }

   SAFE_RELEASE(pBaseFilter[0]);
   SAFE_RELEASE(pBaseFilter[1]);
   SAFE_RELEASE(pBaseFilter[2]);

   return S_OK;

error:

   for(k=2;k>=0;k--)
   {
      if(pBaseFilter[k] != NULL)
      {
         pGraphBuilder->RemoveFilter(pBaseFilter[k]);
         SAFE_RELEASE(pBaseFilter[k]);
      }
   }

   SAFE_RELEASE(pDecodeInterface);
   SAFE_RELEASE(pBaseFilter[0]);
   SAFE_RELEASE(pBaseFilter[1]);
   SAFE_RELEASE(pBaseFilter[2]);

   return E_FAIL;
}
IBaseFilter *CDirectShow::AddSourceFilter(FilterData *pFilterData, IGraphBuilder *pGraphBuilder, WCHAR *pwFilterFileName, CLSID clsid, WCHAR *pwInputFileName)
{
   CheckPointer(pGraphBuilder, NULL);

   HRESULT hr;
   DllGetClassObjectFunc DllGetClassObject;

   IClassFactory *pClassFactory = NULL;

   IBaseFilter *pFilter = NULL;
   IFileSourceFilter *pFileSourceFilter = NULL;

   WCHAR awFileName[MAX_PATH];
   ::GetModuleFileName(NULL, awFileName, MAX_PATH);
   ::PathRemoveFileSpec(awFileName);
   ::lstrcat(awFileName, L"\\filter\\");
   ::lstrcat(awFileName, pwFilterFileName);

   if(pFilterData->hDLL != NULL && pFilterData->clsid != clsid)
   {
      ::FreeLibrary(pFilterData->hDLL);
      pFilterData->hDLL = NULL;
      pFilterData->clsid = GUID_NULL;
   }

   if(pFilterData->hDLL == NULL)
   {
      pFilterData->hDLL = ::LoadLibrary(awFileName);
      if(pFilterData->hDLL == NULL)
         goto error;

      pFilterData->clsid = clsid;
   }

   DllGetClassObject = (DllGetClassObjectFunc)::GetProcAddress(pFilterData->hDLL, "DllGetClassObject");
   if(DllGetClassObject == NULL)
      goto error;

   hr = DllGetClassObject(clsid, IID_IClassFactory, (void **)&pClassFactory);
   if(FAILED(hr))
      goto error;

   hr = pClassFactory->CreateInstance(NULL, IID_IBaseFilter, (void **)&pFilter);
   if(FAILED(hr) || pFilter == NULL)
      goto error;

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

   hr = pGraphBuilder->AddFilter(pFilter, NULL);
   if(hr != NO_ERROR)
      goto error;

   hr = pFileSourceFilter->Load(pwInputFileName, NULL);
   if(FAILED(hr))
   {
      hr = pGraphBuilder->RemoveFilter(pFilter);
      goto error;
   }

   SAFE_RELEASE(pFileSourceFilter);
   SAFE_RELEASE(pClassFactory);

   return pFilter;

error:

   SAFE_RELEASE(pFileSourceFilter);
   SAFE_RELEASE(pFilter);
   SAFE_RELEASE(pClassFactory);

   if(pFilterData->hDLL != NULL)
   {
      ::FreeLibrary(pFilterData->hDLL);
      pFilterData->hDLL = NULL;
      pFilterData->clsid = GUID_NULL;
   }

   return NULL;
}
IBaseFilter *CDirectShow::AddFilter(FilterData *pFilterData, IGraphBuilder *pGraphBuilder, WCHAR *pwFilterFileName, CLSID clsid)
{
   CheckPointer(pGraphBuilder, NULL);

   HRESULT hr;
   DllGetClassObjectFunc DllGetClassObject;

   IClassFactory *pClassFactory = NULL;
   IBaseFilter *pFilter = NULL;

   WCHAR awFileName[MAX_PATH];
   ::GetModuleFileName(NULL, awFileName, MAX_PATH);
   ::PathRemoveFileSpec(awFileName);
   ::lstrcat(awFileName, L"\\filter\\");
   ::lstrcat(awFileName, pwFilterFileName);

   if(pFilterData->hDLL != NULL && pFilterData->clsid != clsid)
   {
      ::FreeLibrary(pFilterData->hDLL);
      pFilterData->hDLL = NULL;
      pFilterData->clsid = GUID_NULL;
   }

   if(pFilterData->hDLL == NULL)
   {
      pFilterData->hDLL = ::LoadLibrary(awFileName);
      if(pFilterData->hDLL == NULL)
         goto error;

      pFilterData->clsid = clsid;
   }

   DllGetClassObject = (DllGetClassObjectFunc)::GetProcAddress(pFilterData->hDLL, "DllGetClassObject");
   if(DllGetClassObject == NULL)
      goto error;

   hr = DllGetClassObject(clsid, IID_IClassFactory, (void **)&pClassFactory);
   if(FAILED(hr))
      goto error;

   hr = pClassFactory->CreateInstance(NULL, IID_IBaseFilter, (void **)&pFilter);
   if(FAILED(hr) || pFilter == NULL)
      goto error;

   hr = pGraphBuilder->AddFilter(pFilter, NULL);
   if(hr != NO_ERROR)
      goto error;

   SAFE_RELEASE(pClassFactory);

   return pFilter;

error:

   SAFE_RELEASE(pFilter);
   SAFE_RELEASE(pClassFactory);

   if(pFilterData->hDLL != NULL)
   {
      ::FreeLibrary(pFilterData->hDLL);
      pFilterData->hDLL = NULL;
      pFilterData->clsid = GUID_NULL;
   }

   return NULL;
}
HRESULT CDirectShow::RenderAudio(IGraphBuilder *pGraphBuilder, IBaseFilter *pSplitFilter, IBaseFilter *pRenderFilter, bool bConvert16bit)
{
   CheckPointer(pGraphBuilder, NULL);

   int i, hr;
   ULONG cFetched;

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

   pOutPin = dshowTool.GetPin(pSplitFilter, PINDIR_OUTPUT, MEDIATYPE_Audio);
   if(pOutPin == NULL)
      goto error;

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

   pEnumMediaTypes->Reset();

   while(pEnumMediaTypes->Next(1, &pAm, &cFetched) == S_OK)
   {
      for(i=0;i<g_nAudioSubtypeArraySize;i++)
      {
         if(pAm->subtype == g_audioSubtypeList[i].guid)
         {
            if(::lstrlen(g_audioSubtypeList[i].pwFilterFileName) == 0)
            {  
               pDecodeFilter = dshowTool.AddFilter(pGraphBuilder, g_audioSubtypeList[i].clsid);
            }
            else
            {  
               pDecodeFilter = AddFilter(&m_filterData[FILTER_DECODEAUDIO], pGraphBuilder, g_audioSubtypeList[i].pwFilterFileName, g_audioSubtypeList[i].clsid);
            }

            if(pDecodeFilter == NULL)
               goto error;

            pInPin = dshowTool.GetPin(pDecodeFilter, PINDIR_INPUT, 0);
            if(pInPin == NULL)
               goto error;

            hr = pGraphBuilder->ConnectDirect(pOutPin, pInPin, NULL);
            if(FAILED(hr))
               goto error;

            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 = dshowTool.ConnectDirect(pGraphBuilder, pDecodeFilter, pRenderFilter, NULL);
            if(FAILED(hr))
               goto error;

            SAFE_DELETEMEDIATYPE(pAm);
            SAFE_RELEASE(pEnumMediaTypes);

            SAFE_RELEASE(pInPin);
            SAFE_RELEASE(pOutPin);

            SAFE_RELEASE(pDecodeFilter);

            return S_OK;
         }
      }

      SAFE_DELETEMEDIATYPE(pAm);
   }

error:

   SAFE_DELETEMEDIATYPE(pAm);
   SAFE_RELEASE(pEnumMediaTypes);

   SAFE_RELEASE(pInPin);
   SAFE_RELEASE(pOutPin);

   SAFE_RELEASE(pDecodeInterface);
   SAFE_RELEASE(pDecodeFilter);

   return E_FAIL;
}
bool CDirectShow::ReConvert(void)
{  
   HRESULT hr;


   IMediaSeeking *pMediaSeeking = NULL;
   m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&pMediaSeeking);

   LONGLONG ll = 0;
   pMediaSeeking->SetPositions(&ll, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
   SAFE_RELEASE(pMediaSeeking);

   hr = m_pMediaControl->Run();
   if(FAILED(hr))
      return false;

   return true;
}
bool CDirectShow::ConvertToWav(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, CONVERTDATA *pConvertData, bool bCheckMode, int nWait)
{
   HRESULT hr;
   LONGLONG llDuration;

   IBaseFilter *pConvertWav     = NULL;
   IBaseFilter *pWriter         = NULL;
   IMediaSeeking *pMediaSeeking = NULL;

   IFileSinkFilter *pFileSinkFilter = NULL;
   IEcoDecoWriterInterface *pEcoDecoWriterInterface = NULL;

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

   if(pConvertData->nContainerType == 1)
   {  

      pConvertWav = AddFilter(&m_filterData[FILTER_CONVERTWAV], m_pGraphBuilder, L"EcoDecoWav.ax", CLSID_ConvertWav);
      if(pConvertWav == NULL) goto error;

      hr = RenderByFileExt(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

      if(FAILED(hr))
         hr = RenderByAllFilter(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

      if(FAILED(hr))
         goto error;

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

      m_pConvertWavInterface->GetInFormat(&m_InputFormat);

      ::CopyMemory(&m_OutputFormat, &m_InputFormat, sizeof(m_OutputFormat));

      m_OutputFormat.cbSize = 0;

      if(m_OutputFormat.nSamplesPerSec == 176400)
         m_OutputFormat.nSamplesPerSec = 88200;
      else if(m_OutputFormat.nSamplesPerSec == 192000)
         m_OutputFormat.nSamplesPerSec = 96000;

      if(m_OutputFormat.wBitsPerSample == 64)
         m_OutputFormat.wBitsPerSample = 32;

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

      m_pConvertWavInterface->SetOutFormat(&m_OutputFormat);

      pWriter = AddFilter(&m_filterData[FILTER_WRITER], m_pGraphBuilder, L"EcoDecoWriter.ax", CLSID_EcoDecoWriter);
      if(pWriter == NULL) goto error;

      pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
      if(pFileSinkFilter == NULL) goto error;

      hr = pFileSinkFilter->SetFileName(pwOutputFileName, NULL);
      if(FAILED(hr)) goto error;

      pWriter->QueryInterface(IID_IEcoDecoWriterInterface, (void**)&pEcoDecoWriterInterface);
      if(pEcoDecoWriterInterface == NULL) goto error;

      pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW_WAV);

      hr = dshowTool.ConnectDirect(m_pGraphBuilder, pConvertWav, pWriter, NULL);
      if(FAILED(hr)) goto error;
   }
   else if(pConvertData->nGainMode == 0 && pConvertData->wf.nSamplesPerSec == 0 && pConvertData->wf.wBitsPerSample == 0 && pConvertData->wf.nChannels == 0)
   {  

      pWriter = AddFilter(&m_filterData[FILTER_WRITER], m_pGraphBuilder, L"EcoDecoWriter.ax", CLSID_EcoDecoWriter);
      if(pWriter == NULL) goto error;

      pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
      if(pFileSinkFilter == NULL) goto error;

      hr = pFileSinkFilter->SetFileName(pwOutputFileName, NULL);
      if(FAILED(hr)) goto error;

      pWriter->QueryInterface(IID_IEcoDecoWriterInterface, (void**)&pEcoDecoWriterInterface);
      if(pEcoDecoWriterInterface == NULL) goto error;

      pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW_WAV);

      hr = RenderByFileExt(m_pGraphBuilder, pwInputFileName, pWriter, true);


      if(FAILED(hr)) goto error;
   }
   else
   {
      pConvertWav = AddFilter(&m_filterData[FILTER_CONVERTWAV], m_pGraphBuilder, L"EcoDecoWav.ax", CLSID_ConvertWav);
      if(pConvertWav == NULL) goto error;

      if(pConvertData->wf.nSamplesPerSec == 0 && pConvertData->wf.wBitsPerSample == 0 && pConvertData->wf.nChannels == 0)
      {  
         hr = RenderByFileExt(m_pGraphBuilder, pwInputFileName, pConvertWav, true);

         if(FAILED(hr))
            hr = RenderByAllFilter(m_pGraphBuilder, pwInputFileName, pConvertWav, true);
      }
      else
      {
         hr = RenderByFileExt(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

         if(FAILED(hr))
            hr = RenderByAllFilter(m_pGraphBuilder, pwInputFileName, pConvertWav, false);
      }

      if(FAILED(hr)) goto error;

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

      m_pConvertWavInterface->GetInFormat(&m_InputFormat);

      ::CopyMemory(&m_OutputFormat, &pConvertData->wf, sizeof(m_OutputFormat));

      m_OutputFormat.cbSize = 0;

      if(pConvertData->wf.nSamplesPerSec == 0)
         m_OutputFormat.nSamplesPerSec = m_InputFormat.nSamplesPerSec;

      if(pConvertData->wf.nChannels == 0)
         m_OutputFormat.nChannels = m_InputFormat.nChannels;

      if(pConvertData->wf.wBitsPerSample == 0)
      {
         m_OutputFormat.wBitsPerSample = m_InputFormat.wBitsPerSample;
         m_OutputFormat.wFormatTag = m_InputFormat.wFormatTag;
      }

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

      m_pConvertWavInterface->SetOutFormat(&m_OutputFormat);

      pWriter = AddFilter(&m_filterData[FILTER_WRITER], m_pGraphBuilder, L"EcoDecoWriter.ax", CLSID_EcoDecoWriter);
      if(pWriter == NULL) goto error;

      pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
      if(pFileSinkFilter == NULL) goto error;

      hr = pFileSinkFilter->SetFileName(pwOutputFileName, NULL);
      if(FAILED(hr)) goto error;

      pWriter->QueryInterface(IID_IEcoDecoWriterInterface, (void**)&pEcoDecoWriterInterface);
      if(pEcoDecoWriterInterface == NULL) goto error;

      pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW_WAV);

      if(bCheckMode == false)
      {
         if(pConvertData->nGainMode == GAIN_NORMALIZE || pConvertData->nGainMode == GAIN_AUTOMAXIMIZE)
            m_pConvertWavInterface->SetNormalize(true, pConvertData->dNormalize);

         if(pConvertData->nGainMode == GAIN_TRACKGAIN || pConvertData->nGainMode == GAIN_ALBUMGAIN)
         {
            m_pConvertWavInterface->SetReplaygain(true);

            if(pConvertData->nGainMode == GAIN_TRACKGAIN)
               pEcoDecoWriterInterface->SetReplaygain(true, false);
            else 
               pEcoDecoWriterInterface->SetReplaygain(true, true);
         }
      }
      else
      {
         m_pConvertWavInterface->SetReplaygain(true);

         pEcoDecoWriterInterface->CheckMode(true);
      }

      hr = dshowTool.ConnectDirect(m_pGraphBuilder, pConvertWav, pWriter, NULL);
      if(FAILED(hr)) goto error;
   }

   pWriter->QueryInterface(IID_IEcoDecoInterface, (void **)&m_pEcoDecoInterface);
   if(m_pEcoDecoInterface == NULL) goto error;

   if(pConvertData->wf.nSamplesPerSec == 0 && pConvertData->wf.wBitsPerSample == 0 && pConvertData->wf.nChannels == 0)
   {  

      m_pEcoDecoInterface->GetInFormat(&m_InputFormat);

      ::CopyMemory(&m_OutputFormat, &m_InputFormat, sizeof(m_OutputFormat));
   }

   m_pEcoDecoInterface->SetWait(nWait);

   m_llTotalBytes = -1;

   m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&pMediaSeeking);
   if(pMediaSeeking != NULL)
   {
      hr = pMediaSeeking->GetDuration(&llDuration);
      if(SUCCEEDED(hr))
         m_llTotalBytes = llDuration * m_OutputFormat.nAvgBytesPerSec / 10000000;
   }

   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(pFileSinkFilter);
   SAFE_RELEASE(pEcoDecoWriterInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return true;

error:

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pEcoDecoWriterInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return false;
}
bool CDirectShow::ConvertToFraunhoferMP3(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, CONVERTDATA *pConvertData, bool bCheckMode, int nWait)
{
   int i, j;
   HRESULT hr;
   WAVEFORMATEX inFormat, outFormat;
   LONGLONG llDuration;

   IBaseFilter *pConvertWav     = NULL;
   IBaseFilter *pWriter         = NULL;
   IMediaSeeking *pMediaSeeking = NULL;

   IFileSinkFilter *pFileSinkFilter = NULL;
   IEncodeFraunhoferInterface *pEncodeFraunhoferInterface = NULL;

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

   pConvertWav = AddFilter(&m_filterData[FILTER_CONVERTWAV], m_pGraphBuilder, L"EcoDecoWav.ax", CLSID_ConvertWav);
   if(pConvertWav == NULL) goto error;

   hr = RenderByFileExt(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

   if(FAILED(hr))
      hr = RenderByAllFilter(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

   if(FAILED(hr)) goto error;

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

   m_pConvertWavInterface->GetInFormat(&inFormat);

   pWriter = AddFilter(&m_filterData[FILTER_WRITER], m_pGraphBuilder, L"EcoDecoMp3.ax", CLSID_EncodeFraunhofer);
   if(pWriter == NULL) goto error;

   pWriter->QueryInterface(IID_IEncodeFraunhoferInterface, (void **)&pEncodeFraunhoferInterface);
   if(pEncodeFraunhoferInterface == NULL) goto error;

   hr = pEncodeFraunhoferInterface->CheckOutFormat(&inFormat, &pConvertData->nABRCBR);

   if(hr == S_OK)
   {
      ::CopyMemory(&outFormat, &inFormat, sizeof(outFormat));
   }
   else
   {
      if(inFormat.wBitsPerSample != 16)
      {
         ::CopyMemory(&outFormat, &inFormat, sizeof(outFormat));
         outFormat.wFormatTag = WAVE_FORMAT_PCM;
         outFormat.wBitsPerSample = 16;
         outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
         outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;
         hr = pEncodeFraunhoferInterface->CheckOutFormat(&outFormat, &pConvertData->nABRCBR);
      }

      if(hr != S_OK)
      {
         ::CopyMemory(&outFormat, &inFormat, sizeof(outFormat));

         if(inFormat.nChannels == 2)
            outFormat.nChannels = 1;
         else
            outFormat.nChannels = 2;

         outFormat.wFormatTag = WAVE_FORMAT_PCM;
         outFormat.wBitsPerSample = 16;
         outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
         outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;
         hr = pEncodeFraunhoferInterface->CheckOutFormat(&outFormat, &pConvertData->nABRCBR);
      }
   }

   if(hr != S_OK)
   {  

      outFormat.wFormatTag = WAVE_FORMAT_PCM;
      outFormat.cbSize = 0;
      outFormat.wBitsPerSample = 16;
      outFormat.nChannels = inFormat.nChannels;
      outFormat.nSamplesPerSec = inFormat.nSamplesPerSec;
      outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
      outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;

      DWORD anSamplingRate[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000};

      for(i=0;i<2;i++)
      {
         if(i==0)
            outFormat.nChannels = inFormat.nChannels;
         else
         {
            if(inFormat.nChannels == 2)
               outFormat.nChannels = 1;
            else
               outFormat.nChannels = 2;
         }

         for(j=0;j<=8;j++)
         {  

            if(anSamplingRate[j] <= inFormat.nSamplesPerSec)
               continue;

            outFormat.nSamplesPerSec = anSamplingRate[j];
            outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
            outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;

            hr = pEncodeFraunhoferInterface->CheckOutFormat(&outFormat, &pConvertData->nABRCBR);
            if(hr == S_OK)
               break;
         }

         if(hr == S_OK)
            break;

         for(j=8;j>=0;j--)
         {  

            if(anSamplingRate[j] >= inFormat.nSamplesPerSec)
               continue;

            outFormat.nSamplesPerSec = anSamplingRate[j];
            outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
            outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;

            hr = pEncodeFraunhoferInterface->CheckOutFormat(&outFormat, &pConvertData->nABRCBR);
            if(hr == S_OK)
               break;
         }

         if(hr == S_OK)
            break;
      }
   }

   if(hr != S_OK)
      goto error;

   m_pConvertWavInterface->SetOutFormat(&outFormat);

   pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
   if(pFileSinkFilter == NULL) goto error;

   hr = pFileSinkFilter->SetFileName(pwOutputFileName, NULL);
   if(FAILED(hr)) goto error;

   hr = dshowTool.ConnectDirect(m_pGraphBuilder, pConvertWav, pWriter, NULL);
   if(FAILED(hr)) goto error;

   if(bCheckMode == false)
   {
      if(pConvertData->nGainMode == GAIN_NORMALIZE || pConvertData->nGainMode == GAIN_AUTOMAXIMIZE)
         m_pConvertWavInterface->SetNormalize(true, pConvertData->dNormalize);

      if(pConvertData->nGainMode == GAIN_TRACKGAIN || pConvertData->nGainMode == GAIN_ALBUMGAIN)
      {
         m_pConvertWavInterface->SetReplaygain(true);

         if(pConvertData->nGainMode == GAIN_TRACKGAIN)
            pEncodeFraunhoferInterface->SetReplaygain(true, false);
         else 
            pEncodeFraunhoferInterface->SetReplaygain(true, true);
      }
   }
   else
   {
      m_pConvertWavInterface->SetReplaygain(true);

      pEncodeFraunhoferInterface->CheckMode(true);
   }

   hr = pEncodeFraunhoferInterface->SetOutFormat(&pConvertData->nABRCBR);
   if(FAILED(hr)) goto error;

   pWriter->QueryInterface(IID_IEcoDecoInterface, (void **)&m_pEcoDecoInterface);
   if(m_pEcoDecoInterface == NULL) goto error;

   m_pEcoDecoInterface->SetWait(nWait);

   if(bCheckMode == true)
   {  
      pEncodeFraunhoferInterface->CheckMode(true);
   }

   m_llTotalBytes = -1;

   m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&pMediaSeeking);
   if(pMediaSeeking != NULL)
   {
      hr = pMediaSeeking->GetDuration(&llDuration);
      if(SUCCEEDED(hr))
      {
         m_llTotalBytes = llDuration * outFormat.nAvgBytesPerSec / 10000000;
      }
   }

   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;

   ::CopyMemory(&m_InputFormat, &inFormat, sizeof(m_InputFormat));
   ::CopyMemory(&m_OutputFormat, &outFormat, sizeof(m_OutputFormat));

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pEncodeFraunhoferInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return true;

error:

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pEncodeFraunhoferInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return false;
}
bool CDirectShow::ConvertToLameMP3(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, CONVERTDATA *pConvertData, bool bCheckMode, int nWait)
{
   int i, j;
   HRESULT hr;
   WAVEFORMATEX inFormat, outFormat;
   LONGLONG llDuration;

   IBaseFilter *pConvertWav     = NULL;
   IBaseFilter *pWriter         = NULL;
   IMediaSeeking *pMediaSeeking = NULL;

   IFileSinkFilter *pFileSinkFilter = NULL;
   IEncodeLameInterface *pEncodeLameInterface = NULL;

   LAME_CONFIG lameConfig;

   ::ZeroMemory(&lameConfig, sizeof(lameConfig));
   if(pConvertData->nEncodeMode == 0)
   {  
      lameConfig.nMode = 90;
      lameConfig.nQuality = 0;
      lameConfig.abr.dwBitrate = pConvertData->nABRCBR;
      lameConfig.vbr.dwMaxBitrate = 320;
      lameConfig.vbr.dwMinBitrate = 32;
      lameConfig.vbr.nVBRQuality = 0;
   }
   else if(pConvertData->nEncodeMode == 1)
   {  
      lameConfig.nMode = 91;
      lameConfig.nQuality = 0;
      lameConfig.cbr.dwBitrate = pConvertData->nABRCBR / 1000;
   }
   else
   {  
      lameConfig.nMode = 92;
      lameConfig.nQuality = 0;
      lameConfig.vbr.nVBRQuality = pConvertData->nVBR;
      lameConfig.vbr.dwMaxBitrate = 320;
      lameConfig.vbr.dwMinBitrate = 32;
   }

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

   pConvertWav = AddFilter(&m_filterData[FILTER_CONVERTWAV], m_pGraphBuilder, L"EcoDecoWav.ax", CLSID_ConvertWav);
   if(pConvertWav == NULL) goto error;

   hr = RenderByFileExt(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

   if(FAILED(hr))
      hr = RenderByAllFilter(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

   if(FAILED(hr)) goto error;

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

   m_pConvertWavInterface->GetInFormat(&inFormat);

   pWriter = AddFilter(&m_filterData[FILTER_WRITER], m_pGraphBuilder, L"EcoDecoMp3.ax", CLSID_EncodeLame);
   if(pWriter == NULL) goto error;

   pWriter->QueryInterface(IID_IEncodeLameInterface, (void **)&pEncodeLameInterface);
   if(pEncodeLameInterface == NULL) goto error;

   hr = pEncodeLameInterface->CheckOutFormat(&inFormat, &lameConfig);

   if(hr == S_OK)
   {
      ::CopyMemory(&outFormat, &inFormat, sizeof(outFormat));
   }
   else
   {  
      if(inFormat.wBitsPerSample != 16)
      {
         ::CopyMemory(&outFormat, &inFormat, sizeof(outFormat));
         outFormat.wFormatTag = WAVE_FORMAT_PCM;
         outFormat.wBitsPerSample = 16;
         outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
         outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;
         hr = pEncodeLameInterface->CheckOutFormat(&outFormat, &lameConfig);

         if(hr != S_OK && 48000 < outFormat.nSamplesPerSec)
         {
            if( (outFormat.nSamplesPerSec % 44100 ) == 0)
               outFormat.nSamplesPerSec = 44100;
            else
               outFormat.nSamplesPerSec = 48000;

            outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;
            hr = pEncodeLameInterface->CheckOutFormat(&outFormat, &lameConfig);
         }
      }
   }

   if(hr != S_OK)
   {  

      outFormat.wFormatTag = WAVE_FORMAT_PCM;
      outFormat.cbSize = 0;
      outFormat.wBitsPerSample = 16;
      outFormat.nChannels = inFormat.nChannels;
      outFormat.nSamplesPerSec = inFormat.nSamplesPerSec;
      outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
      outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;

      DWORD anSamplingRate[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000};

      for(i=0;i<2;i++)
      {
         if(i==0)
            outFormat.nChannels = inFormat.nChannels;
         else
         {
            if(inFormat.nChannels == 2)
               outFormat.nChannels = 1;
            else
               outFormat.nChannels = 2;
         }

         for(j=0;j<=8;j++)
         {  

            if(anSamplingRate[j] < outFormat.nSamplesPerSec)
               continue;

            outFormat.nSamplesPerSec = anSamplingRate[j];
            outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
            outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;

            hr = pEncodeLameInterface->CheckOutFormat(&outFormat, &lameConfig);
            if(hr == S_OK)
               break;
         }

         if(hr == S_OK)
            break;

         for(j=8;j>=0;j--)
         {  

            if(anSamplingRate[j] >= outFormat.nSamplesPerSec)
               continue;

            outFormat.nSamplesPerSec = anSamplingRate[j];
            outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
            outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;

            hr = pEncodeLameInterface->CheckOutFormat(&outFormat, &lameConfig);
            if(hr == S_OK)
               break;
         }

         if(hr == S_OK)
            break;
      }
   }

   if(hr != S_OK)
      goto error;

   m_pConvertWavInterface->SetOutFormat(&outFormat);

   if(bCheckMode == false)
   {
      if(pConvertData->nGainMode == GAIN_NORMALIZE || pConvertData->nGainMode == GAIN_AUTOMAXIMIZE)
         m_pConvertWavInterface->SetNormalize(true, pConvertData->dNormalize);

      if(pConvertData->nGainMode == GAIN_TRACKGAIN || pConvertData->nGainMode == GAIN_ALBUMGAIN)
      {
         m_pConvertWavInterface->SetReplaygain(true);

         if(pConvertData->nGainMode == GAIN_TRACKGAIN)
            pEncodeLameInterface->SetReplaygain(true, false);
         else 
            pEncodeLameInterface->SetReplaygain(true, true);
      }
   }
   else
   {
      m_pConvertWavInterface->SetReplaygain(true);

      pEncodeLameInterface->CheckMode(true);
   }

   pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
   if(pFileSinkFilter == NULL) goto error;

   hr = pFileSinkFilter->SetFileName(pwOutputFileName, NULL);
   if(FAILED(hr)) goto error;

   hr = dshowTool.ConnectDirect(m_pGraphBuilder, pConvertWav, pWriter, NULL);
   if(FAILED(hr)) goto error;

   hr = pEncodeLameInterface->SetOutFormat(&lameConfig);
   if(FAILED(hr)) goto error;

   pWriter->QueryInterface(IID_IEcoDecoInterface, (void **)&m_pEcoDecoInterface);
   if(m_pEcoDecoInterface == NULL) goto error;

   m_pEcoDecoInterface->SetWait(nWait);

   m_llTotalBytes = -1;

   m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&pMediaSeeking);
   if(pMediaSeeking != NULL)
   {
      hr = pMediaSeeking->GetDuration(&llDuration);
      if(SUCCEEDED(hr))
      {
         m_llTotalBytes = llDuration * outFormat.nAvgBytesPerSec / 10000000;
      }
   }

   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;

   ::CopyMemory(&m_InputFormat, &inFormat, sizeof(m_InputFormat));
   ::CopyMemory(&m_OutputFormat, &outFormat, sizeof(m_OutputFormat));

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pEncodeLameInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return true;

error:

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pEncodeLameInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return false;
}
bool CDirectShow::ConvertToOgg(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, CONVERTDATA *pConvertData, bool bCheckMode, int nWait)
{
   int i, j;
   HRESULT hr;
   WAVEFORMATEX inFormat, outFormat;
   LONGLONG llDuration;

   IBaseFilter *pConvertWav     = NULL;
   IBaseFilter *pWriter         = NULL;
   IMediaSeeking *pMediaSeeking = NULL;

   IFileSinkFilter *pFileSinkFilter = NULL;
   IEncodeVorbisInterface *pVorbisInterface = NULL;

   VORBISFORMAT vf;
   ::ZeroMemory(&vf, sizeof(vf));

   if(pConvertData->nEncodeMode == 0)
   {  
      vf.fQuality = -1.0f;
      vf.nMinBitsPerSec = 32000;
      vf.nAvgBitsPerSec = pConvertData->nABRCBR;
      vf.nMaxBitsPerSec = 320000;
   }
   else if(pConvertData->nEncodeMode == 1)
   {  
      vf.fQuality = -1.0f;
      vf.nMinBitsPerSec = -1;
      vf.nAvgBitsPerSec = pConvertData->nABRCBR;
      vf.nMaxBitsPerSec = -1;
   }
   else
   {  
      vf.fQuality = pConvertData->fVBR;
      vf.nMinBitsPerSec = -1;
      vf.nAvgBitsPerSec = -1;
      vf.nMaxBitsPerSec = -1;
   }

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

   pConvertWav = AddFilter(&m_filterData[FILTER_CONVERTWAV], m_pGraphBuilder, L"EcoDecoWav.ax", CLSID_ConvertWav);
   if(pConvertWav == NULL) goto error;

   hr = RenderByFileExt(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

   if(FAILED(hr))
      hr = RenderByAllFilter(m_pGraphBuilder, pwInputFileName, pConvertWav, false);

   if(FAILED(hr)) goto error;

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

   m_pConvertWavInterface->GetInFormat(&inFormat);

   pWriter = AddFilter(&m_filterData[FILTER_WRITER], m_pGraphBuilder, L"EcoDecoVorbis.ax", CLSID_EncodeVorbis);
   if(pWriter == NULL) goto error;

   pWriter->QueryInterface(IID_IEncodeVorbisInterface, (void **)&pVorbisInterface);
   if(pVorbisInterface== NULL) goto error;

   ::CopyMemory(&outFormat, &inFormat, sizeof(outFormat));

   if(outFormat.nSamplesPerSec > 48000)
   {  
      if(outFormat.nSamplesPerSec % 48000 == 0)
         outFormat.nSamplesPerSec = 48000;
      else
         outFormat.nSamplesPerSec = 44100;

      outFormat.nAvgBytesPerSec = outFormat.nBlockAlign * outFormat.nSamplesPerSec;
   }

   if(2 < outFormat.nChannels)
      outFormat.nChannels = 2;

   vf.nSamplesPerSec = outFormat.nSamplesPerSec;
   vf.nChannels = outFormat.nChannels;
   hr = pVorbisInterface->CheckOutFormat(&vf);

   if(hr != S_OK)
   {  
      DWORD anSamplingRate[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000};

      for(i=0;i<2;i++)
      {
         if(i!=0)
         {
            if(outFormat.nChannels == 2)
            {
               outFormat.nChannels = 1;
               vf.nChannels = 1;
            }
            else
            {
               outFormat.nChannels = 2;
               vf.nChannels = 2;
            }
         }

         for(j=0;j<=8;j++)
         {  

            if(anSamplingRate[j] < outFormat.nSamplesPerSec)
               continue;

            vf.nSamplesPerSec = anSamplingRate[j];
            hr = pVorbisInterface->CheckOutFormat(&vf);
            if(hr == S_OK)
            {
               outFormat.nSamplesPerSec = anSamplingRate[j];
               break;
            }
         }

         if(hr == S_OK)
            break;

         for(j=8;j>=0;j--)
         {  

            if(anSamplingRate[j] >= outFormat.nSamplesPerSec)
               continue;

            vf.nSamplesPerSec = anSamplingRate[j];
            hr = pVorbisInterface->CheckOutFormat(&vf);
            if(hr == S_OK)
            {
               outFormat.nSamplesPerSec = anSamplingRate[j];
               break;
            }
         }

         if(hr == S_OK)
            break;
      }
   }

   if(hr != S_OK)
      goto error;

   if(bCheckMode == false)
   {
      if(pConvertData->nGainMode == GAIN_NORMALIZE || pConvertData->nGainMode == GAIN_AUTOMAXIMIZE)
         m_pConvertWavInterface->SetNormalize(true, pConvertData->dNormalize);

      if(pConvertData->nGainMode == GAIN_TRACKGAIN || pConvertData->nGainMode == GAIN_ALBUMGAIN)
      {
         m_pConvertWavInterface->SetReplaygain(true);

         if(pConvertData->nGainMode == GAIN_TRACKGAIN)
            pVorbisInterface->SetReplaygain(true, false);
         else 
            pVorbisInterface->SetReplaygain(true, true);
      }
   }
   else
   {
      m_pConvertWavInterface->SetReplaygain(true);

      pVorbisInterface->CheckMode(true);
   }

   outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
   outFormat.nAvgBytesPerSec = outFormat.nSamplesPerSec * outFormat.nBlockAlign;
   m_pConvertWavInterface->SetOutFormat(&outFormat);

   hr = pVorbisInterface->SetOutFormat(&vf);
   if(FAILED(hr)) goto error;

   pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
   if(pFileSinkFilter == NULL) goto error;

   hr = pFileSinkFilter->SetFileName(pwOutputFileName, NULL);
   if(FAILED(hr)) goto error;

   hr = dshowTool.ConnectDirect(m_pGraphBuilder, pConvertWav, pWriter, NULL);
   if(FAILED(hr)) goto error;

   pWriter->QueryInterface(IID_IEcoDecoInterface, (void **)&m_pEcoDecoInterface);
   if(m_pEcoDecoInterface == NULL) goto error;

   m_pEcoDecoInterface->SetWait(nWait);

   if(bCheckMode == true)
   {  
      pVorbisInterface->CheckMode(true);
   }

   m_llTotalBytes = -1;

   m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&pMediaSeeking);
   if(pMediaSeeking != NULL)
   {
      hr = pMediaSeeking->GetDuration(&llDuration);
      if(SUCCEEDED(hr))
      {
         m_llTotalBytes = llDuration * outFormat.nAvgBytesPerSec / 10000000;
      }
   }

   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;

   ::CopyMemory(&m_InputFormat, &inFormat, sizeof(m_InputFormat));
   ::CopyMemory(&m_OutputFormat, &outFormat, sizeof(m_OutputFormat));

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pVorbisInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return true;

error:

   SAFE_RELEASE(pMediaSeeking);
   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pVorbisInterface);
   SAFE_RELEASE(pConvertWav);
   SAFE_RELEASE(pWriter);

   return false;
}
bool CDirectShow::ConvertRaw(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, int nWait)
{
   int i;
   HRESULT hr;

   IBaseFilter *pSource   = NULL;
   IBaseFilter *pSplitter = NULL;
   IBaseFilter *pMp4Mux   = NULL;
   IBaseFilter *pWriter   = NULL;

   IPin *pInPin = NULL;
   IPin *pOutPin = NULL;
   AM_MEDIA_TYPE *pAm = NULL;
   IEnumMediaTypes *pEnumMediaTypes = NULL;

   IFileSinkFilter *pFileSinkFilter = NULL;
   IEcoDecoWriterInterface *pEcoDecoWriterInterface = NULL;

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

   pSource = dshowTool.AddSourceFilter(m_pGraphBuilder, CLSID_AsyncReader, pwInputFileName);
   if(pSource == NULL) goto error;

   pWriter = AddFilter(&m_filterData[FILTER_WRITER], m_pGraphBuilder, L"EcoDecoWriter.ax", CLSID_EcoDecoWriter);
   if(pWriter == NULL) goto error;

   pWriter->QueryInterface(IID_IEcoDecoWriterInterface, (void**)&pEcoDecoWriterInterface);
   if(pEcoDecoWriterInterface == NULL) goto error;

   for(i=0;i<g_splitterArraySize;i++)
   {
      if(::lstrlen(g_splitterData[i].pwFileName) == 0)
      {  
         pSplitter = dshowTool.AddFilter(m_pGraphBuilder, g_splitterData[i].clsid);
      }
      else
      {  
         pSplitter = AddFilter(&m_filterData[FILTER_SPLITTER], m_pGraphBuilder, g_splitterData[i].pwFileName, g_splitterData[i].clsid);
      }

      if(pSplitter == NULL)
         continue;

      hr = dshowTool.ConnectDirect(m_pGraphBuilder, pSource, pSplitter, NULL);
      if(FAILED(hr))
      {
         m_pGraphBuilder->RemoveFilter(pSplitter);
         SAFE_RELEASE(pSplitter);
         continue;
      }

      break;
   }

   if(i == g_splitterArraySize)
   {  
      goto error;
   }

   pOutPin = dshowTool.GetPin(pSplitter, 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, NULL);
      if(hr != S_OK)
         goto error;

      if(pAm->subtype == MEDIASUBTYPE_AAC)
      {

         if(pMp4Mux == NULL)
         {  
            pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW_AAC);
            m_nRawFileType = CONTAINER_RAW_AAC;
            ::lstrcat(pwOutputFileName, L".aac");
         }
         else
         {  
            pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW);
            m_nRawFileType = CONTAINER_RAW_MP4;
            ::lstrcat(pwOutputFileName, L".mp4");
         }

         break;
      }
      else if(pAm->subtype == MEDIASUBTYPE_MP3)
      {
         pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW);
         m_nRawFileType = CONTAINER_RAW_MP3;
         ::lstrcat(pwOutputFileName, L".mp3");
         break;
      }
      else if(pAm->subtype == MEDIASUBTYPE_MPEG1AudioPayload)
      {
         pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW);
         m_nRawFileType = CONTAINER_RAW_MP2;
         ::lstrcat(pwOutputFileName, L".mp2");
         break;
      }
      else if(pAm->subtype == MEDIASUBTYPE_MPEG2_AUDIO)
      {
         pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW);
         m_nRawFileType = CONTAINER_RAW_MP2;
         ::lstrcat(pwOutputFileName, L".mp2");
         break;
      }
      else if(pAm->subtype == MEDIASUBTYPE_Vorbis)
      {
         pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW_OGG);
         m_nRawFileType = CONTAINER_RAW_OGG;
         ::lstrcat(pwOutputFileName, L".ogg");
         break;
      }
      else if(pAm->subtype == MEDIASUBTYPE_AVI_AC3)
      {
         pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW);
         m_nRawFileType = CONTAINER_RAW_AC3;
         ::lstrcat(pwOutputFileName, L".ac3");
         break;
      }
      else if(pAm->subtype == MEDIASUBTYPE_DOLBY_AC3)
      {
         pEcoDecoWriterInterface->SetOutputMode(CONTAINER_RAW);
         m_nRawFileType = CONTAINER_RAW_AC3;
         ::lstrcat(pwOutputFileName, L".ac3");
         break;
      }

      SAFE_DELETEMEDIATYPE(pAm);
   }

   SAFE_DELETEMEDIATYPE(pAm);
   SAFE_RELEASE(pEnumMediaTypes);

   if(pMp4Mux == NULL)
   {  

      pInPin = dshowTool.GetPin(pWriter, PINDIR_INPUT, 0);
      if(pInPin == NULL)
         goto error;

      hr = m_pGraphBuilder->ConnectDirect(pOutPin, pInPin, NULL);
      if(FAILED(hr))
         goto error;

      SAFE_RELEASE(pInPin);
      SAFE_RELEASE(pOutPin);
   }
   else
   {  

      pInPin = dshowTool.GetPin(pMp4Mux, PINDIR_INPUT, 0);
      if(pInPin == NULL)
         goto error;

      hr = m_pGraphBuilder->ConnectDirect(pOutPin, pInPin, NULL);
      if(FAILED(hr))
         goto error;

      SAFE_RELEASE(pInPin);
      SAFE_RELEASE(pOutPin);

      hr = dshowTool.ConnectDirect(m_pGraphBuilder, pMp4Mux, pWriter, NULL);
      if(FAILED(hr))
         goto error;
   }

   pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
   if(pFileSinkFilter == NULL) goto error;

   hr = pFileSinkFilter->SetFileName(pwOutputFileName, NULL);
   if(FAILED(hr)) goto error;

   m_llTotalBytes = -1;

   pWriter->QueryInterface(IID_IEcoDecoInterface, (void **)&m_pEcoDecoInterface);
   if(m_pEcoDecoInterface == NULL) goto error;

   m_pEcoDecoInterface->SetWait(nWait);

   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(pFileSinkFilter);
   SAFE_RELEASE(pEcoDecoWriterInterface);
   SAFE_RELEASE(pMp4Mux);
   SAFE_RELEASE(pWriter);
   SAFE_RELEASE(pSplitter);
   SAFE_RELEASE(pSource);

   return true;

error:

   SAFE_RELEASE(pInPin);
   SAFE_RELEASE(pOutPin);
   SAFE_DELETEMEDIATYPE(pAm);
   SAFE_RELEASE(pEnumMediaTypes);

   SAFE_RELEASE(pFileSinkFilter);
   SAFE_RELEASE(pEcoDecoWriterInterface);
   SAFE_RELEASE(pMp4Mux);
   SAFE_RELEASE(pWriter);
   SAFE_RELEASE(pSplitter);
   SAFE_RELEASE(pSource);

   return false;
}
