// DShowReader.cpp: implementation of the DShowReader class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "DShowReader.h" #include "Dshow.h" #include "CKGlobals.h" #define SAFERELEASE(x) { if (x) x->Release(); x = NULL; } #if _MSC_VER <1400 //not visual studio 8, old directshow sdk #pragma comment(lib, "strmiids.lib") #pragma comment(lib, "ole32.lib") #endif DShowReader::DShowReader() { m_MMStream = NULL; m_pSample = NULL; m_pAudioData = NULL; memset(&m_Wfe,0,sizeof(m_Wfe)); m_Flags = (CK_DATAREADER_FLAGS) 0; m_SubFileMem = NULL; m_GraphBuilder = NULL; m_GraphID = 0; } DShowReader::~DShowReader() { // OutputDebugString("SAFERELEASE(m_pSample)\n"); SAFERELEASE(m_pSample); // OutputDebugString("SAFERELEASE(m_pAudioData)\n"); SAFERELEASE(m_pAudioData); // OutputDebugString("SAFERELEASE(m_MMStream)\n"); SAFERELEASE(m_MMStream); } Mp3Reader::Mp3Reader() { } Mp3Reader::~Mp3Reader() { } CKERROR ExitDShowReaderInstance(CKContext* context) { DShowReader::ReleaseDirectShowFilterList(); return CK_OK; } void DShowReader::ReleaseDirectShowFilterList() { if (FilterEnumerator) { FilterEnumerator->Release(); FilterEnumerator = 0; } if (DeviceEnumerator) { DeviceEnumerator->Release(); DeviceEnumerator = 0; } } #define BAIL(hr) if(hr) goto OpenFailed; #define CODEC_NAME_LENGTH 1024 ICreateDevEnum* DShowReader::DeviceEnumerator = NULL; IEnumMoniker* DShowReader::FilterEnumerator = NULL; // Opens a file CKERROR DShowReader::OpenFile(char *file) { IAMMultiMediaStream *pAMStream = NULL; IMediaStream *pStream = NULL; IAudioMediaStream *pAudioStream = NULL; IBaseFilter *pFilter = NULL; BOOL ForcedCodecFound = FALSE; XString upversionoffile(file); HRESULT hr = CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void **)&pAMStream); BAIL(hr); WCHAR wszName[MAX_PATH + 1]; MultiByteToWideChar(CP_ACP, 0, file, -1, wszName,MAX_PATH + 1); hr = pAMStream->Initialize(STREAMTYPE_READ, 0/*AMMSF_NOGRAPHTHREAD*/, NULL); BAIL(hr); //In case of WMA and ASF, it seems that non Microsoft CODECs (ie ffdshow) decode incorrectly adding silence at the //beginbning of the sound data, making sound loops imposssible //Lets Search for the Microsoft Windows Media sound decoder : WMAudio Decoder DMO CKSTRING fileup = CKStrupr(upversionoffile.Str()); if (strstr(fileup,"WMA") || strstr(fileup,"ASF") || strstr(fileup,"MP3")) { char codecname [CODEC_NAME_LENGTH]; char searchedname1[] = "WMAudio Decoder DMO"; char searchedname2[] = "MPEG Layer-3 Decoder"; const char* searchedname = strstr(fileup,"MP3")?searchedname2:searchedname1; IBaseFilter* filter=NULL; ///////////////////////////////////////////////////////////////////// // Check the Available Audio decoders Filters if using WMA or ASF ///////////////////////////////////////////////////////////////////// // Create the System Device Enumerator. if (!DeviceEnumerator) { hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&DeviceEnumerator); if ( hr != S_OK ) { DeviceEnumerator->Release(); DeviceEnumerator = 0; goto NOENUM; } } // Obtain a class enumerator for the video compressor category. if (!FilterEnumerator) { hr = DeviceEnumerator->CreateClassEnumerator(CLSID_LegacyAmFilterCategory, &FilterEnumerator, 0); // If no success, it probably means that there are no audio decode filter. if ( hr != S_OK ) { DeviceEnumerator->Release(); DeviceEnumerator = 0; goto NOENUM; } } // Got both enumerators, store filters list. // Enumerate the monikers. ULONG Fetched; IMoniker *Moniker; while(!ForcedCodecFound && FilterEnumerator->Next(1, &Moniker, &Fetched) == S_OK) { // Get the filter name IPropertyBag *PropBag; Moniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&PropBag); // To retrieve the friendly name of the filter, do the following: VARIANT varName; VariantInit(&varName); hr = PropBag->Read(L"FriendlyName", &varName, 0); if ( SUCCEEDED(hr) ) WideCharToMultiByte(CP_ACP, 0, varName.bstrVal, -1, codecname, CODEC_NAME_LENGTH, NULL, NULL); if (!strcmp(searchedname,codecname)) { HRESULT hr; //load of wmadmod.dll hr = Moniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter); if (SUCCEEDED(hr)) { ForcedCodecFound = TRUE; // Use the filter. // Remember to release the IBaseFilter interface. } } VariantClear(&varName); // Clear property bag. PropBag->Release(); } // Release Enumerators //FilterEnumerator->Release(); //now done in ExitDShowReaderInstance // Release Enumerators //DeviceEnumerator->Release(); //now done in ExitDShowReaderInstance } NOENUM: hr = pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0/*AMMSF_ADDDEFAULTRENDERER/*we will try |AMMSF_NOSTALLalso*/ , NULL); BAIL(hr); if (ForcedCodecFound) { pAMStream->GetFilterGraph(&m_GraphBuilder); m_GraphBuilder->AddFilter(pFilter,NULL); pFilter->Release(); #ifdef _DEBUG DShowEnableGraphEdit(TRUE); #endif } hr = pAMStream->OpenFile(wszName, AMMSF_RUN|AMMSF_NOCLOCK); BAIL(hr); hr = pAMStream->QueryInterface(IID_IMultiMediaStream,(void**)&m_MMStream); BAIL(hr); pAMStream->Release(); BAIL(hr); hr = m_MMStream->GetMediaStream(MSPID_PrimaryAudio, &pStream); BAIL(hr); hr = pStream->QueryInterface(IID_IAudioMediaStream, (void **)&pAudioStream); pStream->Release(); BAIL(hr); hr = CoCreateInstance(CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER, IID_IAudioData, (void **)&m_pAudioData); BAIL(hr); hr = pAudioStream->GetFormat(&m_Wfe); BAIL(hr); m_pAudioData->SetBuffer(BUFFER_SIZE, m_Buffer, 0); BAIL(hr); m_pAudioData->SetFormat(&m_Wfe); BAIL(hr); hr = pAudioStream->CreateSample(m_pAudioData, 0, &m_pSample); BAIL(hr); m_LastDataSize = GetDataSize() % BUFFER_SIZE; if (!m_LastDataSize) m_LastDataSize = BUFFER_SIZE; pAudioStream->Release(); return CK_OK; OpenFailed: SAFERELEASE(m_MMStream); SAFERELEASE(m_pSample); SAFERELEASE(m_pAudioData); SAFERELEASE(pAMStream); SAFERELEASE(pStream); SAFERELEASE(pAudioStream); return hr; } CKERROR DShowReader::ReadMemory(void* memory, int size) { return E_FAIL; } // Decodes next chunk of data, use get data buffer to get decoded data CKERROR DShowReader::Decode() { if(!m_pSample) return E_FAIL; HRESULT hr = m_pSample->Update(0,NULL,NULL,0); if(FAILED(hr)){ return hr; } if(hr == MS_S_ENDOFSTREAM){ return CKSOUND_READER_EOF; } return S_OK; } // Gets the last decoded buffer CKERROR DShowReader::GetDataBuffer(BYTE **buf, int *size) { if(!m_pAudioData) return E_FAIL; m_pAudioData->GetInfo(NULL, NULL, (DWORD*)size); *buf = m_Buffer; if(*size != BUFFER_SIZE){ *size = XMin(m_LastDataSize,*size); } return CK_OK; } // Gets the wave format of decoded datas CKERROR DShowReader::GetWaveFormat(CKWaveFormat *wfe) { memcpy(wfe,&m_Wfe,sizeof(CKWaveFormat)); return CK_OK; } // Gets whole decoded data size int DShowReader::GetDataSize() { UINT64 d = GetDuration(); UINT64 bps = m_Wfe.nAvgBytesPerSec; UINT64 ret = ((d*bps)/1000); int iret = (int) ret; return 4*((iret+3)/4); } // Gets the play time length int DShowReader::GetDuration() { STREAM_TIME streamTime; // Duration in 100 Nanoseconds units m_MMStream->GetDuration(&streamTime); return (int)(streamTime /10000L); } CKERROR DShowReader::Seek(int pos) { if(!m_MMStream) return E_FAIL; return m_MMStream->Seek(((LONGLONG)pos)*10000L); } CKERROR DShowReader::Pause(){ return CK_OK; } CKERROR DShowReader::Resume(){ return CK_OK; } // Play CKERROR DShowReader::Play(){ return CK_OK; } // Stop CKERROR DShowReader::Stop(){ return CK_OK; } #ifdef _DEBUG void DShowReader::DShowEnableGraphEdit(CKBOOL iEnable) { //Use Graphedit.exe to see what is the actual graph. if ( iEnable ) { if ( !m_GraphBuilder ) return; IMoniker * pMoniker; IRunningObjectTable *pROT; if (FAILED(GetRunningObjectTable(0, &pROT))) { m_GraphID = 0; return; } WCHAR wsz[128]; wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) ((IGraphBuilder*)m_GraphBuilder), GetCurrentProcessId()); HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker); if (SUCCEEDED(hr)) { // Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference // to the object. Using this flag will cause the object to remain // registered until it is explicitly revoked with the Revoke() method. // // Not using this flag means that if GraphEdit remotely connects // to this graph and then GraphEdit exits, this object registration // will be deleted, causing future attempts by GraphEdit to fail until // this application is restarted or until the graph is registered again. hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, m_GraphBuilder, pMoniker, &m_GraphID); pMoniker->Release(); } pROT->Release(); if ( FAILED(hr) ) m_GraphID = 0; } /////////// else { if ( !m_GraphID ) return; IRunningObjectTable *pROT; if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) { pROT->Revoke(m_GraphID); pROT->Release(); } m_GraphID = 0; } } #endif