Обновление: код, который я изначально опубликовал, на самом деле не воспроизводил проблему; мои искренние извинения за то, что не подтвердили это. Ключ к странному поведению - небольшая разница (300 ЕДИНИЦ = 30 микросекунд) между окончанием одного кадра и началом следующего. По какой-то причине оборудование захвата, которое я использую, сообщает другую частоту кадров, чем то, что оно показывает на самом деле, когда оно предоставляет захваченные кадры и их временные метки. Я обновил приведенный ниже источник, чтобы дать пример того, как имитировать такое поведение.
Я написал простой фильтр источника «поддельного» изображения для directshow, полученный из CSource. Это работает хорошо. Но я заметил кое-что странное, чего не могу объяснить. Мой FillBuffer выглядит так:
const REFERENCE_TIME TIME_PER_FRAME = 166000;
HRESULT MyFilterOutputPin::FillBuffer(IMediaSample *pms)
{
//fill the bytes of the image media sample
static REFERENCE_TIME currentTime = 0;
REFERENCE_TIME startTime = currentTime;
REFERENCE_TIME endTime = currentTime + TIME_PER_FRAME; //60Hz video
// The +300 below is an update not in the original question, and is the
// key to reproducing the behavior.
currentTime += TIME_PER_FRAME + 300;
pms->SetTime(&startTime, &endTime);
pms->SetMediaTime(&startTime, &endTime);
return S_OK;
}
и мой CMediaType устанавливается путем вызова
SetCMediaTypeForBitmap(1920,1080,TIME_PER_FRAME,&cmt);
где эта функция реализована как
void SetCMediaTypeForBitmap(unsigned long width, unsigned long height, REFERENCE_TIME averageTimePerFrame, CMediaType *pmt)
{
CMediaType mt;
mt.SetType(&MEDIATYPE_Video);
mt.SetSubtype(&MEDIASUBTYPE_RGB24);
mt.SetFormatType(&FORMAT_VideoInfo);
mt.SetSampleSize(GetBitmapBufferSize(width, height, BIT_COUNT));
auto pvi = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
pvi->rcSource.left = pvi->rcSource.top = 0;
pvi->rcSource.right = width;
pvi->rcSource.bottom = height;
pvi->rcTarget = pvi->rcSource;
pvi->dwBitErrorRate = 0;
pvi->AvgTimePerFrame = averageTimePerFrame;
pvi->bmiHeader.biSize = 40;
pvi->bmiHeader.biWidth = width;
pvi->bmiHeader.biHeight = height;
pvi->bmiHeader.biPlanes = 1;
pvi->bmiHeader.biBitCount = BIT_COUNT;
pvi->bmiHeader.biCompression = 0;
pvi->bmiHeader.biSizeImage = mt.lSampleSize;
pvi->dwBitRate = (DWORD)(((uint64_t)mt.lSampleSize) * 8 / pvi->AvgTimePerFrame * UNITS);
pvi->bmiHeader.biXPelsPerMeter = pvi->bmiHeader.biYPelsPerMeter = pvi->bmiHeader.biClrUsed = pvi->bmiHeader.biClrImportant = 0;
*pmt = mt;
}
Если я попытаюсь установить время мультимедиа для своих образцов в моем переопределении MyFilterOutputPin :: FillBuffer, а затем запишу вывод в файл AVI, файл AVI, согласно VirtualDub, будет иметь в 300 раз больше кадров, чем он должен. Он перечисляет большинство кадров как отброшенные и периодически имеет реальный кадр.
Если я просто удалю SetMediaTime, выходной AVI будет полностью нормальным.
Я экспериментировал с разными способами установки времени мультимедиа. Я могу указать время относительно m_pStart фильтра, время на эталонных часах и т. Д. Это не имеет значения - просто присутствие MediaTime взрывает AVI.
Я видел правильные фильтры захвата directshow, которые отлично настраивают MediaTime, поэтому я предполагаю, что я что-то не могу сделать. Есть мысли / идеи?
Вот скриншот моих свойств файла примерно за 2 секунды захвата. На самом деле было выведено 138 кадров, но AVI считает, что у него ~ 40000 кадров, или в 290 раз больше истинного числа. Если я запустил тот же код без SetMediaTime, AVI будет длиться 2 секунды с 138 кадрами и. никаких "выпавших" кадров.
Неотброшенные кадры - 0, 326, 552, 878, 1104, 1430, 1756, 1982. Дельты между ними - 326, 226, 326, 226, 226, 326, 326, 226. Это определенно заставило меня почесаться. голова...