ffmpeg показывает неверную длительность сегмента — почему и как исправить

·

5 мин чтения

Ряд циановых кадров-сегментов видео, из одного вытекает амбер-волна звука за границу кадра - метафора завышенной длительности
Паспорт генерации изображения

МодельQwen-Image 2512

КвантованиеQ6_K GGUF

Сид8

Шаги26

CFG2.5

Размер1360×768

Сэмплерeuler · simple

Время генерации20:41

Промт генерации изображения

Dark neon 'underside of progress' scene, cyberpunk workshop mood. A strip of video sliced into even segments, cold teal-cyan frames of equal width in a row; from one segment a warm amber audio-track tail leaks out past the frame edge and stretches its false length, a timestamp that lies; cold frame grid versus the amber overrun tail. Glowing waveform threads, etched frame numbers, scanline flicker, dust and rain-on-glass texture. Deep shadow, volumetric haze, warm amber and cyan color palette, shallow depth of field, cinematic film still on 35mm, intricately detailed, ultra-detailed, fine textures, rich layered background, ornate mechanical detail, sharp focus, moody atmosphere.

Записал десять минут - в метаданных семнадцать. Разбор того, как две дорожки с разным ритмом создают иллюзию лишнего времени.

Симптом проявился при настройке домашнего NVR. Задача простая: резать RTSP-поток с камеры на сегменты, чтобы архив не превратился в один бесконечный и битый файл. Но в метаданных происходила странная экспансия. Сегмент, который должен был длиться 30 секунд, по данным ffprobe закрывался с отметкой 00:00:53.92. Почти минута там, где данных на полминуты. Плеер сходит с ума: полоса прокрутки растягивается, навигация прыгает.

Записал десять минут - в теории должен получить ровно десять, а по факту коэффициент завышения держится в районе 1.7. То есть вместо десяти минут получаем семнадцать.

Поиск виноватого в муксере

Первым делом возникло подозрение на Matroska. Казалось очевидным: муксер просто игнорирует флаг -reset_timestamps 1 и не обнуляет временные метки (PTS) между кусками. В итоге каждый следующий сегмент «наследует» время предыдущего, и длительность превращается в кашу.

Проверил. Попробовал сменить контейнер, поиграть с флагами синхронизации - результат не менялся. Приборы рапортовали, что всё настроено верно, но цифры в Duration продолжали врать.

Ошибка оператора и зомби-процессы

На этом этапе в дело вступил ИИ. Я скормил ему свою команду и попросил найти, где я мог ошибиться в логике запуска. Ответ пришёл сухой и бьющий точно в цель:

«Посмотри на позицию флага -t. Он стоит после имени выходного файла. В ffmpeg порядок аргументов имеет значение: всё, что идёт после вывода, относится к следующему (несуществующему) выходному потоку. Твой лимит времени просто игнорируется».

Оказалось, я сам создал ловушку. Из-за неправильного порядка аргументов процесс не останавливался вовремя, плодил «зомби-прогоны» в фоне через SSH и портил все контрольные замеры. Перенёс -t перед -i. Сегменты стали честными по размеру, но проблема с Duration в метаданных никуда не делась. reset_timestamps был невиновен, но и решение не нашлось.

Матрица вместо гадания

Когда простые перестановки флагов перестали работать, ИИ предложил перестать гадать и построить матрицу A/B тестов. Мы развели контейнер, аудиокодек и наличие звука как отдельные переменные.

Результаты в таблице оказались отрезвляющими:

  • MKV (только видео) → Duration 00:00:30.05 (Идеально).
  • MKV (видео + аудио копия) → Врёт (+70%).
  • TS (транспортный поток, разные кодеки) → Врёт.

Стало ясно: виноват не контейнер и не конкретный флаг. Проблема в сочетании двух дорожек с разной временной сеткой. Видео идёт с частотой 20 fps, звук - 8000 Гц. При включённом reset_timestamps звуковой «хвост» неизбежно утекает за границу последнего ключевого кадра видео.

Муксер считает Duration по простейшей формуле: «последний таймстемп минус первый». Поскольку звуковой пакет оказывается чуть дальше, чем финальный кадр видео, ffmpeg записывает в метаданные общую длину этого разрыва. В итоге получаем те самые 53 секунды вместо 30.

Чистка потока

Решение оказалось радикальным, но единственно верным для сырого архива: вырезать звук на этапе нарезки. Если нам нужна точная навигация и низкая нагрузка на CPU, лишние хвосты аудиопотока только мешают.

Команда для продакшена теперь выглядит так:

ffmpeg -map 0:v:0 -c copy -f segment -segment_time 600 -segment_format mpegts -reset_timestamps 1 -strftime 1 [output_name].mp4

Проверка подтвердила: реальный десятиминутный сегмент теперь имеет Duration 00:10:00.05. Погрешность в пять сотых секунды - это уже цена честного цифрового тайминга.

Закон разных сеток

Эта история вывела меня на своего рода «закон» сегментации: как только в потоке встречаются две дорожки с разными PTS-сетками и активируется reset_timestamps, Duration закрытых сегментов будет завышен.

Это не баг конкретной версии ffmpeg и не ошибка в одном флаге. Это структурное свойство сегмент-муксера. Нельзя одновременно сохранить аудио в raw-копии, обеспечить идеальную навигацию по сегментам и оставить Duration в точности равным segment_time без перекодирования или сложного выравнивания таймстемпов.

Иногда прогресс - это не когда инструмент делает всё за тебя, а когда ты понимаешь, почему он делает именно так. И роль напарника-ИИ здесь была не в том, чтобы «починить», а в том, чтобы заставить меня перестать верить своим глазам и начать строить матрицы.

Часть темы «Умный дом без облака» - там собраны остальные разборы этого стека.

Как зашло?