无法以同步模式从IMFSource读取样本

cannot readsample from IMFSource in synchronous mode

本文关键字:IMFSource 读取 样本 模式 同步      更新时间:2023-10-16

我在使用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);
}

人力资源的值是一个例外代码:0xc00d3704因此,代码段跳过了WriteSample的调用。

这是很多步骤,但是我可以肯定的是我正确设置m_pReader(类型IMFSource *)。

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以进行测试。因此,我认为我不会错过任何步骤来使相机继续前进,但也许我是。

当我在调用激活视频点后使用网络摄像头(使用此方法)检查哪些应用程序时,我看到我的应用程序正在按预期使用网络摄像头。但是,当我输入回调例程时,我看到使用网络摄像头的应用程序有两个实例。使用阻止方法是相同的。

我不知道那是好还是坏,但是当我输入回调方法时:

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。

如果有错误[0xc00d3704] - 这意味着源未初始化。此类错误可能是由于初始化的错误,另一个应用程序(过程)繁忙的相机或UVC驱动程序的不支持引起的(旧相机支持与UVC部分兼容的DirectShow驱动程序。可以通过UVC读取一些来自旧相机的通用信息。但是,作为友好的名称,符号链接。但是,旧相机支持直接车型模型 - 推动,而相机将字节推入管道,而媒体基金会拉数据 - 发送特殊信号并等待数据)。要检查您的代码,我想建议研究有关从网站" CodeProject" codeproject" - 搜索" videoinput"名称捕获视频的文章。