DirectShow CSourceStream::FillBuffer 暂停和查找后对第一帧的不可预测的调用数

DirectShow CSourceStream::FillBuffer unpredictable number of calls after Pause and Seek to the first frame

本文关键字:一帧 不可预测 调用 暂停 FillBuffer CSourceStream 查找 DirectShow      更新时间:2023-10-16

我有一个Directshow文件源过滤器,它有音频和帧输出引脚。它是根据 MSDN 上的本教程以C++编写的。我的筛选器使用 Medialook MFormats SDK 打开视频,并向输出引脚提供原始数据。两个引脚在渲染时直接连接到渲染器筛选器。

当我运行图形,暂停视频并查找帧号0时,出现问题。在输出帧引脚中调用ChangeStart方法后,有时会调用 FillBuffer 三次,并在屏幕上显示帧 1 而不是 0。当它被调用两次时,它显示正确的帧,即帧 0。

输出引脚继承自CSourceStream类和CSourceSeeking类。这是我对输出帧引脚的FillBufferChangeStart方法;

填充缓冲液方法

HRESULT frame_pin::FillBuffer(IMediaSample *sample)
{
     CheckPointer(sample, E_POINTER);
    BYTE *frame_buffer;
    sample->GetPointer(&frame_buffer);
    // Check if the downstream filter is changing the format.
    CMediaType *mt;
    HRESULT hr = sample->GetMediaType(reinterpret_cast<AM_MEDIA_TYPE**>(&mt));
    if (hr == S_OK)
    {
        auto new_width = reinterpret_cast<VIDEOINFOHEADER2*>(mt->pbFormat)->bmiHeader.biWidth;
        auto old_witdh = reinterpret_cast<VIDEOINFOHEADER2*>(m_mt.pbFormat)->bmiHeader.biWidth;
        if(new_width != old_witdh)
            format_changed_ = true;
        SetMediaType(mt);
        DeleteMediaType(mt);
    }
    ASSERT(m_mt.formattype == FORMAT_VideoInfo2);
    VIDEOINFOHEADER2 *vih = reinterpret_cast<VIDEOINFOHEADER2*>(m_mt.pbFormat);
    CComPtr<IMFFrame> mf_frame;
    {
        CAutoLock lock(&shared_state_);
        if (source_time_ >= m_rtStop)
            return S_FALSE;
        // mf_reader_ is a member external SDK instance which gets the frame data with this function call
        hr = mf_reader_->SourceFrameConvertedGetByNumber(&av_props_, frame_number_, -1, &mf_frame, CComBSTR(L""));
        if (FAILED(hr))
            return hr;
        REFERENCE_TIME start, stop = 0;
        start = stream_time_;
        stop = static_cast<REFERENCE_TIME>(tc_.get_stop_time() / m_dRateSeeking);
        sample->SetTime(&start, &stop);
        stream_time_ = stop;
        source_time_ += (stop - start);
        frame_number_++;
    }
    if (format_changed_)
    {
        CComPtr<IMFFrame> mf_frame_resized;
        mf_frame->MFResize(eMFCC_YUY2, std::abs(vih->bmiHeader.biWidth), std::abs(vih->bmiHeader.biHeight), 0, &mf_frame_resized, CComBSTR(L""), CComBSTR(L""));
        mf_frame = mf_frame_resized;
    }

    MF_FRAME_INFO mf_frame_info;
    mf_frame->MFAllGet(&mf_frame_info);
    memcpy(frame_buffer, reinterpret_cast<BYTE*>(mf_frame_info.lpVideo), mf_frame_info.cbVideo);
    sample->SetActualDataLength(static_cast<long>(mf_frame_info.cbVideo));
    sample->SetSyncPoint(TRUE);
    sample->SetPreroll(FALSE);
    if (discontinuity_)
    {
        sample->SetDiscontinuity(TRUE);
        discontinuity_ = FALSE;
    }
    return S_OK;
}

更改启动方法

HRESULT frame_pin::ChangeStart()
{
    {
        CAutoLock lock(CSourceSeeking::m_pLock);
        tc_.reset();
        stream_time_ = 0;
        source_time_ = m_rtStart;
        frame_number_ = static_cast<int>(m_rtStart / frame_lenght_);
    }
    update_from_seek();
    return S_OK;
}

来自 Microsoft DirectShow 文档:

CSourceSeeking类是一个用于实现的抽象类 在具有一个输出引脚的源滤波器中查找。

不建议将 CSourceSeek 用于具有多个筛选器的筛选器 输出引脚。主要问题是只有一个引脚应该响应 寻求请求。通常,这需要引脚之间的通信 和过滤器。

源筛选器中有两个输出引脚。

可以扩展 CSourceSeeking 类,以使用自定义编码管理多个输出引脚。当搜索命令传入时,它们将通过两个输入引脚,因此您需要确定哪个引脚控制查找并忽略到达另一个输入引脚的查找命令。