Hatred's Log Place

DON'T PANIC!

Oct 20, 2015 - 4 minute read -

Ускорение позиционирования в удалённом flv файле при использовании http-транспорта

Пост основан на двух вопросах на Тостере и RU.SO. На RU.SO я же накатал ответ, на основе которого и буду строить свой псто. Кроме того, рассматриваю более подробно как работает перемотка со стороны FFmpeg.

В общем, жаждущие - подкат.

У каждого формата и протокола в FFmpeg есть свои опции, которыми можно играться. Позиционирование, оно же - перемотка (seek) по времени, работает не всеми форматами и не во всех случаях оно поддерживается: к примеру, если источник - это простой HTTP сервер, типа апача или nginx, а видеофайл - простой файл на диске, то возможность непоследовательной перемотки определяется форматом файла, а так же настройками сервера: может ли он отдавать файл с произвольного места (проверить просто: при помощи wget -c начать качать файл, прервать и попробовать снова - если закачака продолжится - значит поддерживается, если начнётся заново - значит нет, дефолтные настройки Apache2 и nginx позволяют докачку без лишних телодвижений), плюс возвращает ли сервер размер файла в заголовках (возвращает - повезло, не возвращает - нет).

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

Кроме того, если сервер - стриминговый, а файл, на самом деле не файл, а каноническое имя стрима и стрим - Live - то никакой перемотки не будет. Если стрим VOD - наличие перемотки зависит от того, умеет ли это сервер для данного транспорта (http) или нет (тут верны выкладки для обычного сервера).

На этом вводное слово закончим. Перейдём к делу.

В случае передачи flv через http (псевдостриминг) работает стек форматов и протоколов (в терминологии FFmpeg): tcp -> http -> flv.

Формат flv не умеет перемотку по времени - перенаправляет её на слой ниже (на протокольную часть: к примеру, rtmp умеет обрабатывать такие запросы). Http протокол же перемотку по времени делать не может в силу того, что он на это не рассчитан, ровно как и обычный файловый “протокол” (при доступе к локальному файлу): сами подумайте, ведь через эти протоколы можно обращаться вообще к любому файлу, а насколько к вашему текстовому документу применима перемотка по времени? Но (при соблюдении условий выше) реализация умеет делать перемотку побайтово (по сути классический fseek(3p)). Собственно сама перемотка осуществляется, внутри, при помощи более высокоуровневой функции ( av_frame_seek()), которая сначала пытается сделать перемотку по времени (в случае формата flv и протокола http эта попытка фейлится), если не удалось - то перемотку при помощи индекса (по сути, индекс хранит определённые временные метки и соответствующее смещение). Если индекса нет (или по какой-то другой причине, не позволяющей его использовать) то используется последовательное чтение кадров, сравнение их DTS с заданным.

Так вот, в случае flv таблица индекса не поддерживается. Точнее на уровне FFmpeg она реализована и создаётся динамически для уже прочитанного контента, т.е. перемотку в ещё непрочитанные данные можно осуществить, в данной связке (http+flv, file+flv), только путём последовательного чтения. Прямая перемотка осуществима только в связке rtmp+flv при условии поддержки медиа-сервером (в данном случае это будет VOD стриминг, можно в качестве стримера для этого взять nginx-rtmp).

Сам flv не поддерживает, но он поддерживает метаинфорацию и один из метатегов называется keyframes, внутри такого блока располагаются пары значений times и filepositions которые и образуют эту самую индексную таблицу. Данную таблицу понимает FFmpeg и стоит индекс на основе её. Но он не умеет добавлять её (по крайней мере я не знаю способа как это сделать, ровно как и не вижу в коде данного функционала).

Метаинформация, в т.ч. и индексная таблица, может навешиваться при помощи внешних утилит, к примеру:

Пример для flvtool2:

flvtool2 -UP media-file.flv

Интернеты где-то упоминали, что yamdi самый адекватный тул.

Резюмируя: быстрая перемотка (позиционирование) flv через http-транспорт возможна только если:

  1. Http сервер умеет докачивать файлы (поддерживает заголовки Content-Length и Content-Range).
  2. Это именно файл или каноническое имя VOD-стрима (при поддержке стриминговым сервером), но не Live-стрима.
  3. Внешней утилитой на flv навешана индексная таблица.
  4. Клиент умеет пользоваться этой индексной таблицей (ffmpeg - умеет, vlc, скорее всего - тоже, насчёт web-проигрывателей на знаю).

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

Ну и полезные ссылки: