cannot readsample from IMFSource in synchronous mode

0

I am having trouble with a video recording application that I am writing using Microsoft Media Foundation.

Specifically, the read/write function (which I put on a loop that lives on it's own thread) doesn't make it pass the call to ReadSample:

HRESULT WinCapture::rwFunction(void) {

    HRESULT hr;
    DWORD streamIndex, flags;
    LONGLONG llTimeStamp;
    IMFSample *pSample = NULL;

    EnterCriticalSection(&m_critsec);
    // Read another sample.
    hr = m_pReader->ReadSample(
        (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        0,
        &streamIndex,   // actual
        &flags,//NULL,   // flags
        &llTimeStamp,//NULL,   // timestamp
        &pSample    // sample
        );

    if (FAILED(hr)) { goto done; }

    hr = m_pWriter->WriteSample(0, pSample);

    goto done;

done:
    return hr;
    SafeRelease(&pSample);
    LeaveCriticalSection(&m_critsec);
}

The value of hr is an exception code: 0xc00d3704 so the code snippet skips the call to WriteSample.

It is a lot of steps, but I am fairly certain that I am setting up m_pReader (type IMFSource *) correctly.

HRESULT WinCapture::OpenMediaSource(IMFMediaSource *pSource)
{
    HRESULT hr = S_OK;

    IMFAttributes *pAttributes = NULL;

    hr = MFCreateAttributes(&pAttributes, 2);


    // use a callback
    //if (SUCCEEDED(hr))
    //{
    //  hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this);
    //}

    // set the desired format type
    DWORD dwFormatIndex = (DWORD)formatIdx;

    IMFPresentationDescriptor *pPD = NULL;
    IMFStreamDescriptor *pSD = NULL;
    IMFMediaTypeHandler *pHandler = NULL;
    IMFMediaType *pType = NULL;

    // create the source reader
    if (SUCCEEDED(hr))
    {
        hr = MFCreateSourceReaderFromMediaSource(
            pSource,
            pAttributes,
            &m_pReader
            );
    }

    // steps to set the selected format type
    hr = pSource->CreatePresentationDescriptor(&pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    BOOL fSelected;
    hr = pPD->GetStreamDescriptorByIndex(0, &fSelected, &pSD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSD->GetMediaTypeHandler(&pHandler);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pHandler->GetMediaTypeByIndex(dwFormatIndex, &pType);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pHandler->SetCurrentMediaType(pType);
    {
        goto done;
    }

    hr = m_pReader->SetCurrentMediaType(
        (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        NULL,
        pType
        );

    // set to maximum framerate?
    hr = pHandler->GetCurrentMediaType(&pType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the maximum frame rate for the selected capture format.

    // Note: To get the minimum frame rate, use the 
    // MF_MT_FRAME_RATE_RANGE_MIN attribute instead.

    PROPVARIANT var;
    if (SUCCEEDED(pType->GetItem(MF_MT_FRAME_RATE_RANGE_MAX, &var)))
    {
        hr = pType->SetItem(MF_MT_FRAME_RATE, var);

        PropVariantClear(&var);

        if (FAILED(hr))
        {
            goto done;
        }

        hr = pHandler->SetCurrentMediaType(pType);
        {
            goto done;
        }

        hr = m_pReader->SetCurrentMediaType(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            NULL,
            pType
            );
    }




    goto done;

done:
    SafeRelease(&pPD);
    SafeRelease(&pSD);
    SafeRelease(&pHandler);
    SafeRelease(&pType);
    SafeRelease(&pAttributes);
    return hr;
}

This code is all copied from Microsoft documentation pages and the SDK sample code. The variable formatIdx is 0, I get it from enumerating the camera formats and choosing the first one.

UPDATE

I have rewritten this program so that it uses callbacks instead of a blocking read/write function and I have exactly the same issue.

Here I get the device and initiate the callback method:

HRESULT WinCapture::initCapture(const WCHAR *pwszFileName, IMFMediaSource *pSource) {

    HRESULT hr = S_OK;

    EncodingParameters params;
    params.subtype = MFVideoFormat_WMV3; // TODO, paramterize this
    params.bitrate = TARGET_BIT_RATE;
    m_llBaseTime = 0;
    IMFMediaType *pType = NULL;
    DWORD sink_stream = 0;

    EnterCriticalSection(&m_critsec);
    hr = m_ppDevices[selectedDevice]->ActivateObject(IID_PPV_ARGS(&pSource));
    //m_bIsCapturing = false; // this is set externally here

    if (SUCCEEDED(hr))
        hr = OpenMediaSource(pSource); // also creates the reader


    if (SUCCEEDED(hr))
    {
        hr = m_pReader->GetCurrentMediaType(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            &pType
            );
    }


    // Create the sink writer 
    if (SUCCEEDED(hr))
    {
        hr = MFCreateSinkWriterFromURL(
            pwszFileName,
            NULL,
            NULL,
            &m_pWriter
            );
    }

    if (SUCCEEDED(hr))
        hr = ConfigureEncoder(params, pType, m_pWriter, &sink_stream);


    // kick off the recording
    if (SUCCEEDED(hr))
    {

        m_llBaseTime = 0;
        m_bIsCapturing = TRUE;

        hr = m_pReader->ReadSample(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            0,
            NULL,
            NULL,
            NULL,
            NULL
            );


    }



    SafeRelease(&pType);
    SafeRelease(&pSource);
    pType = NULL;
    LeaveCriticalSection(&m_critsec);
    return hr;
}

The OpenMediaSource method is here:

HRESULT WinCapture::OpenMediaSource(IMFMediaSource *pSource)
{
    HRESULT hr = S_OK;

    IMFAttributes *pAttributes = NULL;

    hr = MFCreateAttributes(&pAttributes, 2);


    // use a callback
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this);
    }

    // set the desired format type
    DWORD dwFormatIndex = (DWORD)formatIdx;

    IMFPresentationDescriptor *pPD = NULL;
    IMFStreamDescriptor *pSD = NULL;
    IMFMediaTypeHandler *pHandler = NULL;
    IMFMediaType *pType = NULL;

    // create the source reader
    if (SUCCEEDED(hr))
    {
        hr = MFCreateSourceReaderFromMediaSource(
            pSource,
            pAttributes,
            &m_pReader
            );
    }

    // steps to set the selected format type
    if (SUCCEEDED(hr)) hr = pSource->CreatePresentationDescriptor(&pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    BOOL fSelected;
    hr = pPD->GetStreamDescriptorByIndex(0, &fSelected, &pSD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSD->GetMediaTypeHandler(&pHandler);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pHandler->GetMediaTypeByIndex(dwFormatIndex, &pType);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pHandler->SetCurrentMediaType(pType);
    if (FAILED(hr))
    {
        goto done;
    }

    // get available framerates
    hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, &frameRate, &denominator);
    std::cout << "frameRate " << frameRate << "   denominator " << denominator << std::endl;


    hr = m_pReader->SetCurrentMediaType(
        (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        NULL,
        pType
        );

    // set to maximum framerate?
    hr = pHandler->GetCurrentMediaType(&pType);
    if (FAILED(hr))
    {
        goto done;
    }

    goto done;

done:
    SafeRelease(&pPD);
    SafeRelease(&pSD);
    SafeRelease(&pHandler);
    SafeRelease(&pType);
    SafeRelease(&pAttributes);
    return hr;
}

Here, formatIdx is a field of this class that get sets by the user via the GUI. I leave it 0 in order to test. So, I don't think I am missing any steps to get the camera going, but maybe I am.

When I inspect what applications are using the webcam (using this method) after the call to ActivateObject, I see that my application is using the webcam as expected. But, when I enter the callback routine, I see there are two instances of my application using the webcam. This is the same using a blocking method.

I don't know if that is good or bad, but when I enter my callback method:

HRESULT WinCapture::OnReadSample(
    HRESULT hrStatus,
    DWORD /*dwStreamIndex*/,
    DWORD /*dwStreamFlags*/,
    LONGLONG llTimeStamp,
    IMFSample *pSample      // Can be NULL
    )
{
    EnterCriticalSection(&m_critsec);

    if (!IsCapturing() || m_bIsCapturing == false)
    {
        LeaveCriticalSection(&m_critsec);
        return S_OK;
    }

    HRESULT hr = S_OK;

    if (FAILED(hrStatus))
    {
        hr = hrStatus;
        goto done;
    }

    if (pSample)
    {
        if (m_bFirstSample)
        {
            m_llBaseTime = llTimeStamp;
            m_bFirstSample = FALSE;
        }

        // rebase the time stamp
        llTimeStamp -= m_llBaseTime;

        hr = pSample->SetSampleTime(llTimeStamp);

        if (FAILED(hr)) { goto done; }

        hr = m_pWriter->WriteSample(0, pSample);

        if (FAILED(hr)) { goto done; }
    }

    // Read another sample.
    hr = m_pReader->ReadSample(
        (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        0,
        NULL,   // actual
        NULL,   // flags
        NULL,   // timestamp
        NULL    // sample
        );

done:
    if (FAILED(hr))
    {
        //NotifyError(hr);
    }

    LeaveCriticalSection(&m_critsec);
    return hr;
}

hrStatus is the 0x00d3704 error I was getting before, and the callback goes straight to done thus killing the callbacks.

Finally, I should say that I am modeling (read, 'copying') my code from the example MFCaptureToFile in the Windows SDK Samples and that doesn't work either. Although, there I get this weird negative number for the failed HRESULT: -1072875772.

c++
ms-media-foundation
asked on Stack Overflow Jun 23, 2016 by dmedine • edited Jul 6, 2016 by dmedine

1 Answer

-3

If you have got error [0xC00D3704] - it means that source does not initialized. Such error can be caused by mistake of initialization, busy camera by another application (process) or unsupport of the camera by UVC driver(old cameras support DirectShow driver with partly compatibleness with UVC. It is possible read some general info from old cameras via UVC as friendly name, symbolic link. However, old cameras support DirectShow models - PUSH, while camera pushes bytes into the pipeline, while Media Foundation PULL data - sends special signal and wait data). For checking your code I would like advise to research articles about capturing video from web cam on site "CodeProject" - search "videoInput" name.

answered on Stack Overflow Jun 23, 2016 by Evgeny Pereguda

User contributions licensed under CC BY-SA 3.0