deargui-vpl/ref/virtools/Samples/Plugins/Wav Reader/DShowReader.cpp

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