无法在同步模式下从IMFSource读取采样

时间:2016-06-23 18:29:13

标签: c++ ms-media-foundation

我在使用Microsoft Media Foundation编写的视频录制应用程序时出现问题。

具体来说,读/写函数(我把它放在一个存在于它自己的线程上的循环)不会使它通过调用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);
}

hr的值是一个异常代码:0xc00d3704因此代码段会跳过对WriteSample的调用。

这是很多步骤,但我确信我正确设置了m_pReaderIMFSource *}类型。

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;
}

此代码全部从Microsoft文档页面和SDK示例代码中复制而来。变量formatIdx为0,我通过枚举相机格式并选择第一个来获得它。

更新

我已经重写了这个程序,以便它使用回调而不是阻塞读/写功能,我有完全相同的问题。

在这里,我获取设备并启动回调方法:

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;
}

OpenMediaSource方法在这里:

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;
}

此处,formatIdx是此类的一个字段,由用户通过GUI获取。为了测试我留下它0。所以,我不认为我错过任何让相机进入的步骤,但也许我是。

当我在调用ActivateObject后检查哪些应用程序正在使用网络摄像头(using this 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是我之前收到的0x00d3704错误,回调直接转到done,从而导致回调。

最后,我应该说我正在从Windows SDK示例中的示例MFCaptureToFile建模(读取,'复制')我的代码,但这也不起作用。虽然,我得到了失败的HRESULT的这个奇怪的负数:-1072875772。

1 个答案:

答案 0 :(得分:-3)

如果您遇到错误[0xC00D3704] - 则表示源未初始化。这种错误可能是由于初始化错误,另一个应用程序繁忙的摄像机(进程)或UVC驱动程序不支持摄像机造成的(旧摄像机支持与UVC部分兼容的DirectShow驱动程序。可以通过UVC读取旧摄像机的一些常规信息作为友好名称,符号链接。然而,旧相机支持DirectShow模型 - PUSH,而相机将字节推入管道,而Media Foundation PULL数据 - 发送特殊信号和等待数据)。 为了检查您的代码,我建议研究有关从网站摄像头捕获视频的文章“CodeProject” - 搜索“videoInput”名称。