0

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://learn.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://learn.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://learn.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&quot;Source&quot;, &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?

Sirop4ik
  • 4,543
  • 2
  • 54
  • 121

2 Answers2

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://learn.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
}
user123
  • 2,510
  • 2
  • 6
  • 20
1

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:

  1. there is no visible reason why m_pMemReader would have a suitable pin that belongs to filter graph
  2. Sample Grabber is initialized with media type MEDIATYPE_Stream which contradicts to your intent to have decoded audio (you would need MEDIATYPE_Audio, MEDIASUBTYPE_PCM instead)
  3. 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.

Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • I edited my question, my edit related to case 2 about types. But I didn't really got your idea about case 1 and 3. About first case as I see it - I created `CMemReader` gave it a stream of source bytes than I get `IPin` and pass it to `Render`, so I assume it should start render given source stream, no? And about case 3 - but what I need to use instead? Maybe you can add a code to your answer? – Sirop4ik Nov 23 '20 at 09:19
  • First, there is nowhere to add code to - you don't have buildable project. Second, the error you get is `VFW_E_NOT_IN_GRAPH` which is exactly what I wrote above in 1. You can't `Render` something that does not even belong to filter graph. – Roman R. Nov 23 '20 at 10:34
  • I edited my question `EDIT2`, could you please take a look – Sirop4ik Nov 23 '20 at 12:39
  • To be honest I don't see how it is related to this and earlier questions. Why would you even use CMemReader here. – Roman R. Nov 23 '20 at 13:59
  • "how it is related to" you mean error, right? I don't know, but it is what I get. As you can see in question I am using `CMemReader` in order to read byte stream. I don't have a file as a source, what I have is a byte pointer and size, so for this purpose I use `CMemReader`. No? – Sirop4ik Nov 23 '20 at 14:42
  • Not really, you can't use `CMemReader` as simple as the way you outlined. – Roman R. Nov 23 '20 at 15:10
  • Hmm, I have such an example https://gist.github.com/alekseytimoshchenko/4c2d1ba77fd6e64aac0c4fd33d75ab3c where this approach works well. Maybe I am missing smth. I would like to ask you what do you think about @user123 answer? I tried to implement it looks like it works(for now from file, so I need to find a way to read from stream) – Sirop4ik Nov 23 '20 at 15:17
  • The problem with `CMemReader` is that it is not a part of this question and not even something currently documented. The way you brought it in does not add clarity to the question. @user123 mentioned newer media API Windows Media Foundation. It generally works, users might have considertions to use or not use it - but you always asked for DirectShow. – Roman R. Nov 23 '20 at 15:38
  • On DirectShow vs. Media Foundation: https://stackoverflow.com/a/38707725/868014 – Roman R. Nov 23 '20 at 15:47
  • Ok, I see, thanks. I just can't work out with `DirectShow`, but I checked `Media Foundation` example and looks like it works, but I still have a problem with read from stream as a source. Maybe you can help me here https://stackoverflow.com/q/64971831/5709159 I appreciate – Sirop4ik Nov 23 '20 at 16:04