Jak podłączyć obiekt do wykresu filtra?
To, co muszę zrobić, to - uzyskać zdekodowane klatki przykładowe (jak vector<frames>
) DirectShow
, aby to zrobić. Postępuję zgodnie z tą implementacjąhttps://docs.microsoft.com/en-us/windows/win32/directshow/using-the-sample-grabber
Oto moja realizacja
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;
}
Tutaj pojawia się błąd:
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;
}
Kod błędu to - 0x8004025F
, który zgodnie z tą tabelą błędówhttps://docs.microsoft.com/en-us/windows/win32/directshow/error-and-success-codes oznacza, że
Nie można wykonać żądanej funkcji na obiekcie, którego nie ma na wykresie filtru.
Widzę, że jest przykład w doc https://docs.microsoft.com/en-us/windows/win32/directshow/using-the-sample-grabber#build-the-filter-graph
Ale to przykład użycia ścieżki do pliku jako pierwszego parametru w tej metodzie pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF)
, zamiast tego mam ścieżkę do pliku m_pMemReader = new CMemReader(m_pMemStream, m_pMediaType, &hr)
.
Pytanie brzmi - jak podłączyć obiekt do wykresu filtru, jeśli mam CMemReader
?
EDYTOWAĆ
zmienił te linie
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;
}
EDYCJA2
Aby dołączyć m_pMemReader
do wykresu dodałem tę linię
hr = m_pGraph->AddFilter(m_pMemReader, NULL);
Następnie ustawiam typ mediów, gdy MEDIATYPE_Audio
pojawia się błąd 0x80040200
- The specified media type is invalid.
jeśli spróbuję użyć typu mediów, gdy MEDIATYPE_Stream
otrzymam błąd śledzenia 0x80040266
-
Piny nie mogą się połączyć, ponieważ nie obsługują tego samego transportu. Na przykład filtr nadrzędny może wymagać interfejsu IAsyncReader, podczas gdy filtr podrzędny wymaga IMemInputPin.
Jaki jest tutaj problem? Rozumiem stąd, że mój filtr górny jest CMemReader * m_pMemReader;
, ale CMemReader
pod maską dziedziczy IBaseFilter
, to mój dolny IBaseFilter * m_pGrabberF;
jest również IBaseFilter
. Czego tu brakuje?
Odpowiedzi
DirectShow jest nieaktualny. Zamiast tego powinieneś użyć MediaFoundation. Nie testowałem tego, ale myślę, że poniższy kod zdekoduje dźwięk i pozwoli ci manipulować surowymi ramkami audio (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
}
Jest kilka problemów z kodem i ogólnie fragment kodu jest niespójny, nawet nie mówiąc, że nie można go zbudować. Prawdopodobnie powinieneś utworzyć mały projekt i przesłać go do GitHub, aby użyć go jako odniesienia.
Problemy do rozwiązania to:
- nie ma widocznego powodu, dla którego
m_pMemReader
miałby mieć odpowiedni pin, który należy do wykresu filtra - Próbka Grabber jest inicjowany z rodzaju nośnika
MEDIATYPE_Stream
, który stoi w sprzeczności do swojej intencji, że dekodowane audio (będzie trzebaMEDIATYPE_Audio
,MEDIASUBTYPE_PCM
zamiast) - generalnie nie jest dobrym pomysłem używanie
Render
metody; Zostawię to odniesienie, aby przeczytać, jakie problemy są związane z korzystaniem z DirectShow Intelligent Connect
Ponieważ wydaje się, że głównie starasz się, aby to działało po omacku, prawdopodobnie dobrym pomysłem byłoby użycie GraphStudioNext ( odpowiednik narzędzia SDK GraphEdit ) i interaktywne budowanie potoków, aby zorientować się, jaki rodzaj wykresu filtru należałoby zbudować. Uzyskasz wtedy lepszy pomysł na zbudowanie podobnej gaph z kodu.