SetMediaTime на фильтре CSource делает вывод AVI бессмыслицей — любая идея почему?

Обновление: код, который я первоначально разместил, фактически не воспроизводит проблему; мои искренние извинения за то, что не подтвердили это. Ключом к странному поведению является небольшая дельта (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 кадров. нет «пропущенных» кадров.
Wonky AVI свойства

Не пропущенные кадры находятся в 0, 326, 552, 878, 1104, 1430, 1756, 1982. Дельты между ними — 326, 226, 326, 226, 226, 326, 326, 226. Это определенно заставило меня поцарапать голова…

1

Решение

Индекс кадра AVI будет иметь записи для каждого кадра с фиксированной частотой кадров, определенной в заголовке потока. Например, вы создаете трек со скоростью 300 кадров в секунду, а затем сэмплы своих временных отметок с частотой 1 кадр / с. Полученный файл будет содержать ваши кадры и 299 пропущенных (нулевой длины) кадров между ними. Это то, что вы якобы получаете.

Тем не менее, ваш фрагмент кода с отметкой времени является правильным (вы делаете это простым способом). Однако важно также то, какая скорость применяется к самому потоку, и это зависит от типа носителя, который вы не включили в вопрос и который вы должны проверить.

Соответствие между типом носителя и метками времени является ключом для получения точного выходного файла AVI.

0

Другие решения

Я наткнулся на этот бит документации сегодня, и я думаю, что это на самом деле объясняет вещи в некоторой степени. От него,

При желании фильтр также может указывать время носителя для образца.
В потоке видео время мультимедиа представляет номер кадра.

Таким образом, мультиплексор ожидает мультимедиа, если оно присутствует, например 0-1,1-2,2-3. Когда время мультимедиа установлено на непрерывные порции, например 0-100000,100000-200000, я предполагаю, что мультиплексор справляется. Но когда есть пробелы, основанные на документации, предоставляемой Microsoft, я могу понять, как все рушится.

Но зная, что это на самом деле довольно мощный. Поскольку файлы AVI имеют постоянный формат частоты кадров, вы можете использовать время мультимедиа для передачи пропусков кадров, когда они вам нужны. Я начал успешно использовать их для этой цели.

К вашему сведению, несколько дней назад я попытался включить медиа-время, основанное на фактическом времени, в проект, и вместо забавных результатов график Directshow просто остановился бы с E_FAIL.

tl; dr Используйте только время мультимедиа для передачи номеров кадров, по крайней мере, на мультиплексор AVI.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector