1
votes

I have a Directshow File Source Filter which has audio and frame output pins. It is written in C++ based on this tutorial on MSDN. My filter opens the video by using Medialooks MFormats SDK and provides raw data to output pins. Two pins are directly connecting to renderer filters when they are rendered.

The problem occurs when I run the graph, pause the video and seek to the frame number 0. After a call to ChangeStart method in output frame pin, sometimes FillBuffer is called three times and frame 1 is shown on the screen instead of 0. When it is called two times, it shows the correct frame which is the frame 0.

Output pins are inherited from CSourceStream and CSourceSeeking classes. Here is my FillBuffer and ChangeStart methods of the output frame pin;

FillBuffer Method

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

ChangeStart Method

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

1 Answers

0
votes

From the Microsoft DirectShow documentation:

The CSourceSeeking class is an abstract class for implementing seeking in source filters with one output pin.

CSourceSeeking is not recommended for a filter with more than one output pin. The main issue is that only one pin should respond to seeking requests. Typically this requires communication among the pins and the filter.

And you have two output pins in your source filter.

The CSourceSeeking class can be extended to manage more than one output pin with custom coding. When seek commands come in they'll come through both input pins so you'll need to decide which pin is controlling seeking and ignore seek commands arriving at the other input pin.