AudionAudion

Обрезка: матрица форматов, методы проверки и грабли FFmpeg

TRIM_MATRIX_RU.md

Обрезка: матрица форматов, методы проверки и грабли FFmpeg

Про интерфейс в этом документе. Точки реза здесь находят в плеере mpv,
который открывается кнопкой из раздела, и переносят в поля кнопками — либо
вводят таймкодом с клавиатуры. Форма волны и прослушивание кадра, о которых
говорится ниже, относятся к панели, отложенной до отдельного этапа: движок и
измерения от неё не зависят и остаются в силе.

Справочник по разделу «Обрезка». Всё, что здесь написано, измерено, а не взято из документации: каждая ячейка таблиц — реальная попытка записи с -c copy и чтением результата обратно, каждое число синхрона — замер по кадрам и сэмплам. Числа сняты на FFmpeg 9.0.1 и переснятые на 8.0.1 совпали до ячейки. Какая сборка попадёт в поставку — решает установщик по версии драйвера NVIDIA (на большинстве машин это ветка 8.x), поэтому обе проверены.

Замысел раздела и решения — в PLAN_TRIM_RU.md и DECISIONS.md. Здесь — фактура по обрезке; полный свод измерений, включая пересчёт частоты и пределы самого FFmpeg, — в MEASUREMENTS_RU.md.


1. Что раздел делает

Обрезка копирует потоки (-c:v copy) и не перекодирует картинку. Отсюда три свойства, которые определяют всё остальное:

(24000/1001 для 23,976 и 24/1 для честных 24,000), и ничего не домысливается — камера снимает и то, и другое;

он переписывается сэмпл-в-сэмпл без потери поколения.

РежимЧто остаётсяТочек
Убрать началовсё после INодна (IN)
Убрать конецвсё до OUTодна (OUT)
Оставить серединукусок между IN и OUTдве
Разделить надвоеоба куска, ничего не выбрасываетсяодна (CUT)

1.1. Панель: чем ищут точку

Раздел ищут не глазами по цифрам, а слухом и картинкой, поэтому панель — часть инструмента, а не оформление.

Полоса формы волны одна — на весь файл; десятисекундное окно убрано, потому что не поспевало за ней и расходилось с ней. Под полосой фейдер, под ним транспорт.

Звук под курсором. Фейдер звучит: на остановке руки проигрывается кусок ровно в один кадр, выровненный по границе кадра, — 41,7 мс на 23,976, 40 мс на 25. Так делает монтажная программа: слышен кадр, на котором стоишь, а не секунда после него. Кнопкой «Кусок прослушивания» берётся 3 кадра (125 мс), если одного мало, чтобы узнать слог.

Откуда так быстро. Звуковая дорожка декодируется в память целиком (моно 24 кГц): полчаса — 82 МБ и 2 секунды, после чего любой кусок стоит 0,84 мс и не требует ни процесса, ни обращения к диску. Выше трёх часов дорожка целиком не держится, и работают блоки по две минуты; ими же отвечают первые секунды, пока полная дорожка грузится в фоне.

Непрерывное прослушивание. ▶ играет от точки до конца файла через ffplay (без окна, только звук), ⏸ останавливает и запоминает место — следующее ▶ продолжает оттуда. Во время игры едет белая линия по волне, а фейдеры стоят: точка принадлежит оператору, а не проигрывателю. Сдвинули фейдер — запомненное место забывается, потому что звук из старого места врал бы о положении головы.

Как нарисована волна. Три правила, каждое проверено сравнением:

даёт ниточку со всплесками, sqrt — тонкую форму, log — сплошную заливку без структуры, cbrt — тело, в котором читается фраза;

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

прозрачным, чтобы пик 0 dB останавливался за 3–4 px до границы.

Скорость. Кадр превью декодируется аппаратно там, где это умеют QSV, NVDEC и AMF (h264, hevc, av1, vp9, vp8, vvc, vc1, mpeg1/2/4, mjpeg) — 4K H.264 занимает 0,48 с против 0,85 с на процессоре. Монтажным кодекам ускорение не предлагается: аппаратного пути для ProRes нет ни у одного производителя, а попытка его просить ломает цветовой обход, которым превью ProRes 4444 только и держится.


2. Контейнер × видеокодек

Пары «видео + звук, который контейнер точно принимает», запись -c copy, затем чтение обратно и сверка кодека.

ВидеоMP4MOVMKVMXFTSM2TS
H.264дададададада
HEVCдададанетдада
ProResнетдададанетнет
DNxHD/DNxHRнетдададанетнет
MPEG-2дададададада
AV1данетданетнетнет
VP9данетданетнетнет

Что из этого следует для практики:

«Could not find tag for codec» и файл нулевой длины, поэтому такая пара отклоняется до запуска.

перекодирования — это отдельная операция, не обрезка.

3. Контейнер × аудиокодек

ЗвукMP4MOVMKVMXFTSM2TS
PCM 16 LEдадададанетнет
PCM 16 BE (twos, Canon)дададанетнетнет
PCM 24 LEдадададанетнет
PCM 32 floatдададанетнетнет
AACдададанетдада
AC-3дададанетдада
E-AC-3дададанетдада
MP3дададанетдада
FLACданетданетнетнет
ALACдададанетнетнет
Opusданетданетдада

Главное:

камера пишет pcm_s16be, и при выводе в MXF звук переписывается в pcm_s16le — те же сэмплы, порядок байт, который требует контейнер.

переложить; MOV возьмёт как есть.


4. Точность реза

4.1 Кадры

Проверено на восьми синтетических источниках и одном камерном. Во всех случаях число кадров совпало с планом до единицы.

ИсточникЗапросКадровРасхождение
ProRes 25p3 → 91500
HEVC long-GOP 29.9712 → 306000 (после добора по декодируемым)
H.264 all-intra 23.9763 → 91440
29.97 drop-frame5 → 153300
59.94 drop-frame3 → 93600
MPEG-TS (старт 1.44 с)2 → 83000
4 канала PCM2 → 81500
Canon HEVC 4:2:2 10 бит3 → 101660
ARRI ProRes 4444 XQ 24,0002 → 81440
VFR (30 → 17.6)4 → 16по времени+1.07 с, объявлено

4.2 Синхрон звука

Метка есть в обеих дорожках одновременно: белый кадр в начале каждой секунды и щелчок в тот же момент. Отклонение считается от исходника, а не абсолютное.

КонтейнерИзменение против исходника
MP40…2 мс
MOV0…5 мс
MKV1…3 мс
MXF0…5 мс (после перезаписи PCM; до неё было 9…18)

На камерном файле звук найден в исходнике дословно: кусок начинается на 48 сэмплов (1 мс, сороковая доля кадра) после точки реза.

4.3 Метаданные

Проверено на файле с поворотом, двумя дорожками и тегами проекта:

ЧтоMP4MOVMKV
Поворот (rotation=90)дадада
Язык дорожек (rus, eng)дадада
Теги проекта (title, artist)дадада
Камерные теги (make, model, brands)дадачастично
Таймкод как дорожка tmcdдаданет (текстовый тег)

Камерные теги переносит -movflags use_metadata_tags; без него MP4-мультиплексор пишет свои isomiso2mp41 и теряет make, model и бренд вроде mp42hvc1CAEP.


5. Грабли FFmpeg, найденные измерением

Каждая стоила отдельного разбора; ни одна не очевидна из документации.

ГрабляПроявлениеКак обойдено
1-ss до -i сбрасывает время-to после него считает от нового нуляхвост уезжает как длительность/кадры, не как абсолютная точка
2round(fps) на дробных частотах4 минуты на 29.97 превращаются в 240.27 счастота живёт как Fraction от ffprobe до ffmpeg
3Округление конца «к ближайшему»теряется кадр, стоящий на 29.9966 при запросе 30.0конец округляется вверх
4-t при копированииперебор на 1–3 кадра на потоке с B-кадрамихвост задаётся -frames:v
5-frames:v точен только с точным -ssпри -ss мимо ключевого кадра кусок короче на зазорseek всегда на вычисленный ключевой кадр
6HEVC с B-пирамидоймуксер пишет на 2–4 кадра меньше, чем просилинедобор измеряется и рез повторяется один раз с поправкой
7-avoid_negative_ts make_zeroвидео уезжает на 0.1 с от звукане используется
8start_time ≠ 0 (MPEG-TS 1.44 с)все точки смещаются на эту величинусмещение снимается с ключевых кадров и возвращается при seek
9avg_frame_rate на VFRсчёт кадров покрывает не тот отрезок (12 с → 16 с)VFR определяется и режется по времени, с предупреждением
10-n на существующий файл«already exists», код возврата 0цель проверяется до запуска
11PCM в MXF при -c copy384 лишних сэмпла в начале (8 мс)PCM переписывается сэмпл-в-сэмпл (LE)
12-map_metadata 0 в MP4камерные теги и бренды теряются-movflags use_metadata_tags
13`pan=monoc0=c1` на монопишет тишину, код возврата 0число каналов проверяется до запуска
14MXF + сжатый звук«Could not write header», код 4294967274отклоняется до запуска с объяснением
15Пакет ≠ кадрмуксер пишет 600 пакетов, декодер отдаёт 597: хвостовые B-кадры ссылаются за границу резанедобор ловится по длительности потока и добирается по декодируемым кадрам
16Камерный RAW в MXFcodec_name пуст, размеры нулевые, «dimensions not set», файл нулевой длиныотклоняется до запуска: у FFmpeg нет декодера X-OCN и ARRIRAW
17Предупреждение ломает разбор JSONstderr подмешивается в stdout, и json.loads падает на строке «could not resolve file descriptor strong ref»пробы читаются с разделёнными потоками

6. Как всё это меряется

Команды, которыми получены числа выше. Их же стоит использовать при следующей проверке — иначе результаты не сравнить.

Кадры. Считать пакеты, а не декодировать: результат тот же, время — в сорок раз меньше (0.14 с против 5.7 с на четырёх минутах 59.94).

ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 FILE

Ключевые кадры вокруг точки, без сканирования всего файла:

ffprobe -v error -select_streams v:0 -skip_frame nokey -read_intervals "START%+WINDOW" -show_entries frame=pts_time -of csv=p=0 FILE

Синхрон. Нужен источник с меткой в обеих дорожках: белый кадр и щелчок в один момент. Видео читать через signalstats с -fps_mode passthrough — иначе фильтр продублирует кадры под свои часы и сдвинет времена:

ffmpeg -v info -i FILE -map 0:v:0 -vf "signalstats,metadata=print:key=lavfi.signalstats.YAVG" -fps_mode passthrough -f null -

Звук — по сэмплам, а не через silencedetect: детектор работает окнами по 20 мс, а мерить приходится единицы миллисекунд.

ffmpeg -v error -i FILE -map 0:a:0 -ac 1 -ar 48000 -f s16le -

Побитовая проверка копирования. Найти звук куска внутри звука исходника: если он там есть дословно, звук скопирован, а позиция даёт точное смещение реза.

Совместимость. Записать одну секунду -c copy в контейнер и прочитать обратно. Партнёрский поток подбирать под контейнер: MXF не примет AAC, TS не примет PCM, и без этого ячейка покажет отказ партнёра, а не проверяемого кодека.

Ловушки самих измерений

Три ошибки, на которых я уже спотыкался — стоят того, чтобы их не повторять:

  1. Сравнивать «первую найденную вспышку» с «первым найденным щелчком». Если

один поток открывается меткой, а другой нет, детекторы описывают разные события — получается «рассинхрон 107 мс», которого нет.

  1. Мерить синхрон детекторами FFmpeg. blackdetect работает целыми кадрами,

silencedetect — окнами; вместе они дают 40 мс погрешности, то есть больше, чем измеряемая величина.

  1. Проверять совместимость по коду возврата. Файл может записаться и не

читаться обратно; TS «принимает» ProRes ровно так.


6.1. Камерный RAW: чего инструмент не может

Два формата, которые FFmpeg не открывает ни в какой версии — проверено и на 8.0.1, и на 9.0.1:

ФайлЧто внутриЧто видит ffprobe
Sony BURANO X-OCN LTX-OCN в MXF OP1aпоток без имени кодека, width=0, звук pcm_s24le читается
ARRI Alexa Mini .mxfARRIRAW в MXFто же самое

Попытка простого копирования даёт «dimensions not set» и файл нулевой длины, поэтому такие исходники отклоняются до запуска с прямым объяснением. Резать их можно в родном ПО производителя (Sony Catalyst, ARRI Reference Tool) или после транскода.

Тот же материал в MOV режется полноценно: ARRI ProRes 4444 XQ (2944×2160, 12 бит, 5 дорожек PCM) прошёл кадр в кадр, вместе с таймкодом 02:00:11:11 → 02:00:13:11.


7. Чего мы не проверяли

Список открытый — чтобы никто (включая автора) не решил, что матрица закрыта:

телеметрией, AVCHD прямо с карты;

и на последних секундах — см. MEASUREMENTS_RU.md);

уходит вперёд на 0,1 с и об этом сообщается, но на слух это не проверялось.

All projects