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 properties

Неотброшенные кадры - 0, 326, 552, 878, 1104, 1430, 1756, 1982. Дельты между ними - 326, 226, 326, 226, 226, 326, 326, 226. Это определенно заставило меня почесаться. голова...


person aggieNick02    schedule 01.02.2018    source источник
comment
Интересное наблюдение, посвященное существующему фильтру захвата, который без проблем устанавливает время мультимедиа. В установленных им временах мультимедиа никогда не бывает промежутков, даже если могут быть промежутки / перекрытия в обычных временах, установленных на образцах. Так что я думаю, что главный вывод состоит в том, что время медиа для последовательных выборок не может иметь пробелов, точка.   -  person aggieNick02    schedule 13.02.2018


Ответы (2)


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

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

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

person Roman R.    schedule 01.02.2018
comment
Спасибо за вклад, Роман. Таким образом, я получаю ~ 275 пропущенных кадров ненулевой длины между неотброшенными кадрами. Число пропущенных кадров странным образом переключается между 225 и 325 (и никак иначе). Все настроено на трек со скоростью 60 кадров в секунду, что я и получаю. Без SetMediaTime вывод 60 кадров дает мне 1-секундный клип. С SetMediaTime это дает мне прибл. 276-секундный клип, в котором присутствует каждый реальный кадр, просто куча бессмысленных пропущенных кадров между ними. - person aggieNick02; 02.02.2018
comment
Я добавил снимок экрана с дурацкими свойствами файла AVI и подробностями о том, где находятся неотброшенные кадры. Это довольно странно. - person aggieNick02; 02.02.2018
comment
Почему бы вам не увеличить текущий счетчик времени на фиксированные приращения, равные таковым в типе носителя? В отличие от 166000/166666, как в фрагменте кода. Вам все равно нужно включить распечатку типа носителя в вопрос. - person Roman R.; 02.02.2018
comment
Приносим извинения за задержку с ответом на @Roman R. - был в отпуске. Я очистил свой код выше, чтобы более точно представить, что я делаю - продолжительность кадра всегда одинакова и используется для создания CMediaType соответствующим образом - я также включил код, который использую для установки моего CMediaType. - person aggieNick02; 12.02.2018
comment
Думаю, я пропустил важную часть: если я просто удалю SetMediaTime, выходной AVI будет полностью нормальным. SetMediaTime является необязательным и очень часто просто отсутствует. Я предполагаю, что AVI mux может обрабатывать это каким-то особым образом: фильтры не требуется устанавливать время мультимедиа. Я бы удалил время мультимедиа, чтобы он соответствовал мультиплексору AVI, поскольку он реагирует на время мультимедиа таким непредсказуемым образом. - person Roman R.; 13.02.2018
comment
Спасибо @Roman R. Я только что обновил вопрос выше после того, как обнаружил, что мой код воспроизведения на самом деле не воспроизводит проблему. Мои искренние и смиренные извинения. Воспроизведение, кажется, зависит от небольшого промежутка между отметками времени выборки. Я не понимаю, почему пробел приводит к проблемам только при использовании SetMediaTime, но устранение пробела устраняет проблему. Кроме того, CMediaType avgTimePerFrame, похоже, вообще не имеет значения - в основном пробел является критическим элементом. - person aggieNick02; 13.02.2018
comment
В одном из проектов я все еще сохраняю возможность создавать файлы AVI. Я использую там специальный фильтр, подключенный до AVI Mux, фильтр, который перекомпоновывает данные, чтобы отметки времени были точными кратными числам кадров на AvgTimePerFrame. Я предполагаю, что AVI mux выполняет обратную операцию, чтобы вернуть номера кадров. Без такого фильтра подобные небольшие колебания приводили к проблемам, но, насколько мне известно, в результирующем файле мы исправляли ошибки, а не выводили бессмысленный вывод. - person Roman R.; 13.02.2018
comment
Очень интересно. Экспериментируя больше, даже разрыв в 1 ЕДИНИЦУ между образцами мультимедиа заставляет VirtualDub сообщать о пропущенных кадрах. Я думаю, что я усвоил урок: времена СМИ не могут иметь пробелов, если вы их используете. Кроме того, в то время как VirtualDub покажет файл с пропуском и пропущенными кадрами, фильтр источника avi в Graph Studio Next, похоже, вообще не отображает кадры. - person aggieNick02; 13.02.2018
comment
Действительно любопытно - вы бы порекомендовали что-нибудь помимо AVI для хранения необработанных несжатых снимков? В конце концов, я кодирую в mp4, но у меня нет времени на сжатие во время захвата. - person aggieNick02; 13.02.2018
comment
Проблема не только в AVI, но и фильтры DirectShow для AVI не идеальны. С необработанным видео проблема заключается в ограничении размера AVI. Поскольку я скептически отношусь к возможности захвата необработанного видео, я решил использовать необработанный захват в формате MP4. Там нет проблемы с отметкой времени и нет ограничения на размер файла. Версия демультиплексора / мультиплексора MP4, которую я поддерживаю здесь, предлагает поддержку типы необработанных видеоматериалов. - person Roman R.; 13.02.2018
comment
MP4 с необработанным видео может не подходить для воспроизведения в других системах / приложениях, но вы можете перекодировать контент позже, когда захотите (фильтр mp4demux будет читать такие файлы MP4 через DirectShow). Кодирование видео с аппаратным ускорением - лучший ответ на эту проблему в настоящее время. - person Roman R.; 13.02.2018

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

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

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

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

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

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

person aggieNick02    schedule 23.07.2018