31

При подготовке потока для воспроизведения DASH точки произвольного доступа должны быть в одно и то же время исходного потока во всех потоках. Обычный способ сделать это - принудительно установить фиксированную частоту кадров и фиксированную длину GOP (то есть ключевой кадр каждые N кадров).

В FFMPEG фиксированная частота кадров проста (-r NUMBER).

Но для фиксированных местоположений ключевых кадров (длина GOP) есть три метода ... какой из них "правильный"? Документация FFMPEG разочаровывает расплывчато по этому вопросу.

Способ 1: возиться с аргументами libx264

-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1

Похоже, что есть некоторые споры, следует ли отключить сценарное отключение или нет, так как неясно, перезапускается ли "счетчик" ключевого кадра, когда происходит вырезка сцены.

Способ 2: установка фиксированного размера GOP:

-g GOP_LEN_IN_FRAMES

К сожалению, это только задокументировано в документации FFMPEG, и, следовательно, эффект этого аргумента очень неясен.

Способ 3: вставляйте ключевой кадр каждые N секунд (МОЖЕТ БЫТЬ):

-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)

Это явно документированы. Но до сих пор не ясно, запускается ли "счетчик времени" после каждого ключевого кадра. Например, в ожидаемой 5-секундной GOP, если scenecut кадр сцены вырезан через 3 секунды с помощью libx264, будет ли следующий ключевой кадр спустя 5 секунд или через 2 секунды?

На самом деле, документация FFMPEG различает эту опцию и опцию -g , но на самом деле она не говорит о том, как эти две опции как минимум отличаются друг от друга (очевидно, -g будет требовать фиксированной частоты кадров).

КАКОЙ ПРАВИЛЬНЫЙ?

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

  • соответствует спецификации GOP в H.264 (если есть)
  • он ГАРАНТИРУЕТ, что в фиксированной каденции будет ключевой кадр, независимо от ключевых кадров libx264 scenecut .

Также может показаться, что -g не может работать без принудительной установки фиксированной частоты кадров (-r), поскольку нет гарантии, что многократное выполнение ffmpeg с разными аргументами кодека обеспечит одинаковую мгновенную частоту кадров в каждом разрешении. Фиксированная частота кадров может снизить производительность сжатия (ВАЖНО в сценарии DASH!).

Наконец, метод keyint только кажется взломать. Я надеюсь против надежды, что это не правильный ответ.

Рекомендации:

Пример использования метода -force_key_frames

Пример использования метода keyint

Раздел расширенных опций видео FFMPEG

5 ответов5

23

TL; DR

Я бы порекомендовал следующее:

  • libx264: -g X -keyint_min X (и при необходимости добавить -force_key_frames "expr:gte(t,n_forced*N)")
  • libx265: -x265-params "keyint=X:min-keyint=X"
  • libvpx-vp9: -g X

где X - интервал в кадрах, а N - интервал в секундах. Например, для 2-секундного интервала с видео 30 кадров в секунду, X = 60 и N = 2.

Примечание о различных типах кадров

Чтобы правильно объяснить эту тему, мы сначала должны определить два типа I-кадров / ключевых кадров:

  • Кадры мгновенного обновления декодера (IDR): позволяют независимое декодирование следующих кадров без доступа к кадрам, предшествующим кадру IDR.
  • Не-IDR-кадры: для работы декодирования требуется предыдущий IDR-кадр. Кадры без IDR могут использоваться для вырезок сцены в середине GOP (группы изображений).

Что рекомендуется для потоковой передачи?

Для случая потоковой передачи вы хотите:

  • Убедитесь, что все кадры IDR находятся в обычных положениях (например, через 2, 4, 6, ... секунд), чтобы видео можно было разделить на сегменты одинаковой длины.
  • Включите обнаружение перехода сцены, чтобы улучшить эффективность / качество кодирования. Это означает, что I-кадры можно размещать между кадрами IDR. Вы по-прежнему можете работать с отключенным определением обрыва сцены (но это все еще часть многих руководств), но это не обязательно.

Что делают параметры?

Чтобы настроить кодировщик, мы должны понять, что делают параметры ключевого кадра. Я провел несколько тестов и обнаружил следующее для трех кодировщиков libx264 , libx265 и libvpx-vp9 в FFmpeg:

  • libx264:

    • -g устанавливает интервал ключевых кадров.
    • -keyint_min устанавливает минимальный интервал ключевых кадров.
    • -x264-params "keyint=x:min-keyint=y" совпадает с -g x -keyint_min y .
    • Примечание. При установке обоих значений на одно и то же минимальное значение внутренне устанавливается равным половине максимального интервала плюс один, как видно из кода x264:

      h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
      
  • libx265:

    • -g не реализовано.
    • -x265-params "keyint=x:min-keyint=y" работает.
  • libvpx-vp9:

    • -g устанавливает интервал ключевых кадров.
    • -keyint_min устанавливает минимальный интервал ключевых кадров
    • Примечание: из-за того, как работает FFmpeg, -keyint_min кодировщику только тогда, когда он совпадает с -g . В коде из libvpxenc.c в FFmpeg мы можем найти:

      if (avctx->keyint_min >= 0 && avctx->keyint_min == avctx->gop_size)
          enccfg.kf_min_dist = avctx->keyint_min;
      if (avctx->gop_size >= 0)
          enccfg.kf_max_dist = avctx->gop_size;
      

      Это может быть ошибкой (или отсутствием возможностей?), Поскольку libvpx определенно поддерживает установку другого значения для kf_min_dist .

Вы должны использовать -force_key_frames?

Опция -force_key_frames принудительно вставляет ключевые кадры с заданным интервалом (выражение). Это работает для всех кодировщиков, но может нарушить механизм управления скоростью. Специально для VP9 я заметил серьезные колебания качества, поэтому не могу рекомендовать его в этом случае.

11

Вот мои пятьдесят центов за дело.

Способ 1:

возиться с аргументами libx264

-c:v libx264 -x264opts keyint = GOPSIZE:min-keyint = GOPSIZE:scenecut = -1

Генерируйте фреймы только с желаемыми интервалами.

Пример 1:

ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-x264opts "keyint=48:min-keyint=48:no-scenecut" \
-c:a copy \
-y test_keyint_48.mp4

Генерация фреймов, как и ожидалось, вот так:

Iframes     Seconds
1           0
49          2
97          4
145         6
193         8
241         10
289         12
337         14
385         16
433         18
481         20
529         22
577         24
625         26
673         28
721         30
769         32
817         34
865         36
913         38
961         40
1009        42
1057        44
1105        46
1153        48
1201        50
1249        52
1297        54
1345        56
1393        58

Способ 2 амортизируется. Пропущены.

Способ 3:

вставлять ключевой кадр каждые N секунд (МОЖЕТ БЫТЬ):

-force_key_frames expr:gte (t, n_forced * GOP_LEN_IN_SECONDS)

Пример 2

ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-force_key_frames "expr:gte(t,n_forced*2)"
-c:a copy \
-y test_fkf_2.mp4

Создайте iframes немного по-другому:

Iframes     Seconds
1           0
49          2
97          4
145         6
193         8
241         10
289         12
337         14
385         16
433         18
481         20
519         21.58333333
529         22
577         24
625         26
673         28
721         30
769         32
817         34
865         36
913         38
931         38.75
941         39.16666667
961         40
1008        42
1056        44
1104        46
1152        48
1200        50
1248        52
1296        54
1305        54.375
1344        56
1367        56.95833333
1392        58
1430        59.58333333
1440        60
1475        61.45833333
1488        62
1536        64
1544        64.33333333
1584        66
1591        66.29166667
1632        68
1680        70
1728        72
1765        73.54166667
1776        74
1811        75.45833333
1824        75.95833333
1853        77.16666667
1872        77.95833333
1896        78.95833333
1920        79.95833333
1939        80.75
1968        81.95833333

Как вы можете видеть, он помещает фреймы каждые 2 секунды И на сцену (секунды с плавающей частью), что, на мой взгляд, важно для сложности видеопотока.

Размеры сгенерированных файлов практически одинаковы. Очень странно, что даже с большим количеством ключевых кадров в методе 3 он генерирует иногда меньше файлов, чем стандартный алгоритм библиотеки x264.

Для создания нескольких битрейтов для потока HLS мы выбираем метод три. Он отлично совмещается с 2 секундами между чанками, у них есть iframe в начале каждого чанка, и у них есть дополнительные iframe в сложных сценах, что обеспечивает лучший опыт для пользователей, которые имеют устаревшие устройства и не могут воспроизводить высокие профили x264.

Надеюсь, это поможет кому-то.

6

Поэтому ответ, кажется, таков:

  • Метод 1 проверяется на работу, но libx264 -специфический, и происходит за счет устранения очень полезный вариант scenecut в libx264
  • Метод 3 работает с версией FFMPEG от апреля 2015 года, но вы должны проверить свои результаты с помощью сценария, включенного в нижней части этого поста, так как документация FFMPEG неясна относительно действия этой опции. Если это работает, это лучший из двух вариантов.
  • НЕ ИСПОЛЬЗУЙТЕ Метод 2, -g устарела. Он не работает, не определен явно в документации, не найден в справке и не используется в коде. Проверка кода показывает, что -g вероятно, предназначена для потоков MPEG-2 (есть даже разделы кода, ссылающиеся на PAL и NTSC!).

Также:

  • Файлы, созданные с помощью метода 3, могут быть немного больше, чем метод 1, так как разрешены промежуточные I-кадры (ключевые кадры).
  • Вы должны явно установить флаг "-r" в обоих случаях, даже несмотря на то, что метод 3 помещает I-кадр в следующий временной интервал в указанное время или после него. Неспособность установить флаг "-r" ставит вас в зависимость от исходного файла, возможно, с переменной частотой кадров. Могут возникнуть несовместимые переходы DASH.
  • Несмотря на предупреждения в документации FFMPEG, метод 3 НЕ менее эффективен, чем другие. На самом деле, тесты показывают, что он может быть немного более эффективным, чем метод 1.

Скрипт для опции -force_key_frames

Вот короткая программа на PERL, которую я использовал для проверки частоты вращения I-кадров на основе вывода предложения fhprobe из команды slhck. Кажется, чтобы убедиться, что метод -force_key_frames также будет работать, и имеет дополнительное преимущество, позволяющее scenecut кадры сцены . Я абсолютно не представляю, как FFMPEG делает эту работу, или я просто так повезло, потому что мои потоки оказываются хорошо подготовленными.

В моем случае я кодировал со скоростью 30 кадров в секунду с ожидаемым размером GOP 6 секунд или 180 кадров. Я использовал 180 в качестве аргумента gopsize, чтобы эта программа проверяла I-кадр при каждом кратном 180, но установка его в 181 (или любое другое число, не кратное 180) заставила его жаловаться.

#!/usr/bin/perl
use strict;
my $gopsize = shift(@ARGV);
my $file = shift(@ARGV);
print "GOPSIZE = $gopsize\n";
my $linenum = 0;
my $expected = 0;
open my $pipe, "ffprobe -i $file -select_streams v -show_frames -of csv -show_entries frame=pict_type |"
        or die "Blah";
while (<$pipe>) {
  if ($linenum > $expected) {
    # Won't catch all the misses. But even one is good enough to fail.
    print "Missed IFrame at $expected\n";
    $expected = (int($linenum/$gopsize) + 1)*$gopsize;
  }
  if (m/,I\s*$/) {
    if ($linenum < $expected) {
      # Don't care term, just an extra I frame. Snore.
      #print "Free IFrame at $linenum\n";
    } else {
      #print "IFrame HIT at $expected\n";
      $expected += $gopsize;
    }
  }
  $linenum += 1;
}
4

Я хотел добавить сюда некоторую информацию, так как мой поиск в Google потянул это обсуждение довольно много в моем стремлении найти информацию о попытке найти способ сегментировать мою DASH-кодировку так, как я хотел, и ни одна из найденных мной сведений не была полностью правильной.

Первые несколько заблуждений, чтобы избавиться от:

  1. Не все I-кадры одинаковы. Есть большие рамки "я" и маленькие рамки "я". Или использовать правильную терминологию, I-кадры IDR и I-кадры не-IDR. I-кадры IDR (иногда называемые "ключевыми кадрами") создадут новую GOP. Кадры без IDR не будут. Их удобно иметь внутри GOP, где происходит смена сцены.

  2. -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE ← Это не делает то, что вы думаете, что делает. Это заняло у меня некоторое время, чтобы понять. Оказывается, min-keyint ограничен в коде. Нельзя превышать (keyint / 2) + 1 . Таким образом, присвоение одного и того же значения этим двум переменным приводит к тому, что значение min-keyint сбито наполовину при кодировании.

Вот в чем дело: вырезка сцены действительно великолепна, особенно в видео, где есть быстрые переходы. Он сохраняет его красивым и четким, поэтому я не хочу его отключать, но в то же время я не мог получить фиксированный размер GOP, пока он был включен. Я хотел включить вырезку сцены, но использовать только I-кадры без IDR. Но это не сработало. Пока я не выяснил (из большого количества чтения) о заблуждении № 2.

Оказывается, мне нужно было установить keyint чтобы удвоить желаемый размер GOP. Это означает, что min-keyint может быть установлен на мой желаемый размер GOP (без внутреннего кода, обрезающего его пополам), что предотвращает обнаружение среза сцены при использовании I-кадров IDR внутри размера GOP, поскольку количество кадров с момента последнего IDR I -Фрейм всегда меньше, чем min-keyinit .

И, наконец, установка параметра force_key_frame переопределяет двойной размер keyint . Итак, вот что работает:

Я предпочитаю сегменты по 2 секунды, поэтому мой GOPSIZE = Framerate * 2

ffmpeg <other_options> -force_key_frames "expr:eq(mod(n,<GOPSIZE>),0)" -x264opts rc-lookahead=<GOPSIZE>:keyint=<GOPSIZE * 2>:min-keyint=<GOPSIZE> <other_options>

Вы можете проверить, используя ffprobe:

ffprobe <SRC_FLE> -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type > frames.csv

В сгенерированном CSV-файле каждая строка сообщит вам: frame, [is_an_IDR_?], [frame_type], [frame_number]:

frame,1,I,60  <-- frame 60, is I frame, 1 means is an IDR I-frame (aka KeyFrame)
frame,0,I,71  <-- frame 71, is I frame, 0 means not an IDR I_frame

В результате вы должны видеть I-кадры IDR только с фиксированными интервалами GOPSIZE , в то время как все другие I-кадры являются I-кадрами не IDR, вставленными так, как необходимо для обнаружения обрыва сцены.

0

Кажется, что этот синтаксис не всегда работает .. Я довольно много тестировал наш VOD-контент, а также живой контент (дампы файлов), и иногда scenecut не работает и запускает промежуточный iframe:

Синтаксис для преобразования с повышением частоты i50-> p50, 2-х секундный переход / сегмент, IDR в начале, промежуточные фреймы при необходимости

ffmpeg.exe -loglevel verbose -i avc_50i.ts -pix_fmt yuv420p -filter_complex yadif = 1, scale = 1920:1080 -vcodec libx264 -preset fast -x264-params "rc-lookahead = 100:ключ = 200:min-keyint = 100:hrd = 1:vbv_maxrate = 12000:vbv_bufsize = 12000:no-open-gop = 1 "-r 50 -crf 22 -force_key_frames" expr:eq(mod(n, 100), 0)"-кодек:aac -b:128 тыс. -й target.ts

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .