397 lines
9.1 KiB
C++
397 lines
9.1 KiB
C++
// 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
|