У меня есть фильтр источника файла Directshow, который имеет вывод звука и вывод кадров. Это написано в C ++ на основе этого руководство на MSDN. Мой фильтр открывает видео с помощью Medialooks MFormats SDK и предоставляет необработанные данные для выходных контактов. Два вывода напрямую подключаются к фильтрам рендеринга, когда они отображаются.
Проблема возникает, когда я запускаю график, приостанавливаю видео и ищу кадр № 0. После звонка ChangeStart
метод в выводе кадра, иногда FillBuffer
вызывается три раза, а кадр 1 отображается на экране вместо 0. Когда он вызывается два раза, он показывает правильный кадр, который является кадром 0.
Выходные контакты наследуются от CSourceStream
а также CSourceSeeking
классы. Вот мой FillBuffer
а также ChangeStart
методы вывода кадра на вывод;
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 класс является абстрактным классом для реализации
поиск в исходных фильтрах с один выходной контакт.CSourceSeeking не рекомендуется для фильтра с более чем одним
выходной контакт. Основная проблема заключается в том, что только один контакт должен реагировать на
ищет запросы. Как правило, это требует общения между контактами
и фильтр.
И у тебя есть два выходные выводы в вашем исходном фильтре.
Класс CSourceSeeking может быть расширен для управления несколькими выходными выводами с помощью пользовательского кодирования. Когда поступают команды поиска, они проходят через оба входных контакта, поэтому вам необходимо решить, какой из них управляет поиском, и игнорировать команды поиска, поступающие на другой входной контакт.
Других решений пока нет …