问题
What I need to do is - get decoded sample frames (like vector<frames>
) from DirectShow
in order to do it I follow this implementation https://docs.microsoft.com/en-us/windows/win32/directshow/using-the-sample-grabber
There is my implementation
bool coAudioPlayerSampleGrabber::LoadImp(SoundDataType dataType,
unsigned char const * pData,
int64_t dataLen)
{
Cleanup();
m_bReady = false;
HRESULT hr = S_OK;
assert(pData);
assert(dataLen);
m_memBuffer.resize(dataLen);
memcpy(m_memBuffer.data(), pData, dataLen);
m_memBufferDataType = dataType;
m_pMediaType = new CMediaType();
m_pMediaType->majortype = MEDIATYPE_Stream;
switch (dataType)
{
case SoundDataType::WAV: m_pMediaType->subtype = MEDIASUBTYPE_WAVE; break;
case SoundDataType::MP3: m_pMediaType->subtype = MEDIASUBTYPE_MPEG1Audio; break;
default: return false;
}
m_pMemStream = new CMemStream((BYTE*)m_memBuffer.data(), m_memBuffer.size());
m_pMemReader = new CMemReader(m_pMemStream, m_pMediaType, &hr);
if (FAILED(hr) || m_pMemReader == NULL)
{
printf("Could not create filter - HRESULT 0x%8.8X\n", hr);
return false;
}
// Make sure we don't accidentally go away!
m_pMemReader->AddRef();
// *** Create the Filter Graph Manager
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGraph));
if (FAILED(hr))
{
if (hr == CO_E_NOTINITIALIZED)
{
printf("coAudioPlayerSampleGrabberImplementation::Load: FAILED CoCreateInstance(CLSID_FilterGraph,...) FAILED hRes: %x (CoInitialize has not been called)\n", hr);
}
else
{
printf("coAudioPlayerSampleGrabberImplementation::Load: FAILED CoCreateInstance(CLSID_FilterGraph FAILED hRes: %x\n", hr);
}
return false;
}
hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pControl));
if (FAILED(hr))
{
std::cout << "coAudioPlayerSampleGrabberImplementation::Load: FAILED QueryInterface IMediaControl" << std::endl;
return false;
}
hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pEvent));
if (FAILED(hr))
{
std::cout << "coAudioPlayerSampleGrabberImplementation::Load: FAILED QueryInterface IMediaEventEx" << std::endl;
return false;
}
// *** end Create the Filter Graph Manager
// *** Add the Sample Grabber to the Filter Graph
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_pGrabberF));
if (FAILED(hr))
{
std::cout << "coAudioPlayerSampleGrabberImplementation::Load: FAILED CoCreateInstance CLSID_SampleGrabber" << std::endl;
return false;
}
hr = m_pGraph->AddFilter(m_pGrabberF, LPCWSTR("Sample Grabber"));
if (FAILED(hr))
{
std::cout << "coAudioPlayerSampleGrabberImplementation::Load: FAILED AddFilter m_pGrabberF" << std::endl;
return false;
}
hr = m_pGrabberF->QueryInterface(IID_PPV_ARGS(&m_pGrabber));
if (FAILED(hr))
{
std::cout << "coAudioPlayerSampleGrabberImplementation::Load: FAILED QueryInterface ISampleGrabber" << std::endl;
return false;
}
// *** end Add the Sample Grabber to the Filter Graph
// *** Set the Media Type
hr = m_pGrabber->SetMediaType(m_pMediaType);
if (FAILED(hr))
{
std::cout << "coAudioPlayerSampleGrabberImplementation::Load: FAILED SetMediaType" << std::endl;
return false;
}
// *** end Set the Media Type
IPin *ppinOut = m_pMemReader->GetPin(0);
hr = m_pGraph->Render(ppinOut);
if (FAILED(hr))
{
printf("coAudioPlayerSampleGrabberImplementation::Load: FAILED to load (Render) audio file from data (hRes: %x)\n", hr);
return false;
}
m_bReady = true;
return m_bReady;
}
I get an error here :
IPin *ppinOut = m_pMemReader->GetPin(0);
hr = m_pGraph->Render(ppinOut);
if (FAILED(hr)) <------------- HERE!!
{
printf("TV_AudioPlayerSampleGrabberImplementation::Load: FAILED to load (Render) audio file from data (hRes: %x)\n", hr);
return false;
}
Error code is - 0x8004025F
, which according to this error table https://docs.microsoft.com/en-us/windows/win32/directshow/error-and-success-codes means that
Cannot perform the requested function on an object that is not in the filter graph.
I see there is an example in doc https://docs.microsoft.com/en-us/windows/win32/directshow/using-the-sample-grabber#build-the-filter-graph
But this an example use path to file as a first param in this method pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF)
, I don't have path to file instead I have m_pMemReader = new CMemReader(m_pMemStream, m_pMediaType, &hr)
.
Question is - How to connect object to the filter graph if I have CMemReader
?
EDIT
changed these lines
m_pMediaType = new CMediaType();
m_pMediaType->majortype = MEDIATYPE_Audio; <---------- First line
switch (dataType)
{
//case SoundDataType::WAV: m_pMediaType->subtype = MEDIASUBTYPE_WAVE; break;
case SoundDataType::WAV: m_pMediaType->subtype = MEDIASUBTYPE_PCM; break; <-------- Second line
case SoundDataType::MP3: m_pMediaType->subtype = MEDIASUBTYPE_MPEG1Audio; break;
default: return false;
}
EDIT2
In order to include m_pMemReader
to graph I added this line
hr = m_pGraph->AddFilter(m_pMemReader, NULL);
Then in I set media type as MEDIATYPE_Audio
I get an error 0x80040200
- The specified media type is invalid.
, if I try to use media type as MEDIATYPE_Stream
I get follow error 0x80040266
-
Pins cannot connect because they don't support the same transport. For example, the upstream filter might require the IAsyncReader interface, while the downstream filter requires IMemInputPin.
What is a problem here? What I undrstand from here is - my upstream filter is CMemReader * m_pMemReader;
, but CMemReader
under the hood it inherit from IBaseFilter
, then my downstream is IBaseFilter * m_pGrabberF;
which is also IBaseFilter
. What am I missing here?
回答1:
DirectShow is outdated. You should use MediaFoundation instead. I didn't test it but I think the following code will decode audio and let you manipulate raw audio frames (https://docs.microsoft.com/en-us/windows/win32/medfound/tutorial--decoding-audio):
IMFSourceReader *pReader = NULL;
hr = MFCreateSourceReaderFromURL(L"C:\\users\\video", NULL, &pReader);
if (FAILED(hr))
{
printf("Error opening input file");
}
IMFMediaType *type = NULL;
IMFMediaType *pPartialType = NULL;
hr = MFCreateMediaType(&pPartialType);
hr = pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
hr = pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
// Set this type on the source reader. The source reader will
// load the necessary decoder.
if (SUCCEEDED(hr))
{
hr = pReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pPartialType);
}
hr = pReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, &type);
//Do something with type which is a pointer to IMFMediaType
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
BYTE *pAudioData = NULL;
DWORD cbBuffer = 0;
while(true){
DWORD dwFlags = 0;
hr = pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &dwFlags, NULL, &pSample);
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
hr = pBuffer->Lock(&pAudioData, NULL, &cbBuffer);
//Do something with the pAudioData which is an array of unsigned chars of lenth cbBuffer
}
回答2:
There are a few problems with the code and overall the code snippet is inconsistent, not even telling that one can't build it. You should probably create a small project and upload to GitHub to use as a reference.
Problems to resolve are:
- there is no visible reason why
m_pMemReader
would have a suitable pin that belongs to filter graph - Sample Grabber is initialized with media type
MEDIATYPE_Stream
which contradicts to your intent to have decoded audio (you would needMEDIATYPE_Audio
,MEDIASUBTYPE_PCM
instead) - it is generally not a good idea to use
Render
method at all; I will leave this reference to read up on what problems are associated with use of DirectShow Intelligent Connect
Since you seem to be mostly trying to make it work blindfold, it would probably be a good idea to use GraphStudioNext (analog of SDK GraphEdit tool) and build pipelines interactively to get an idea what kind of filter graph you would need to build. You will get a better idea about building similar gaph from code then.
来源:https://stackoverflow.com/questions/64957789/how-to-connect-object-to-the-filter-graph