15 марта 2015 г.

Конвертировать Twitch видео в gif, mp4 онлайн

FLV - Flash Video. На данный момент этот формат видео довольно сильно распространен в интернете. Например, сервис Twitch.tv транслирует записанные показы, используя его. Видео кодируется в H.264, звук в AAC. Содержит в себе заголовок и подряд идущие теги. Теги бывают трёх видов: метаданные, видеоданные, аудиоданные. Метаданных может не быть или быть больше одного тега. Метаданные могут содержать произвольные данные, например, караоке или ID3 подобные данные. Кроме того, метаданные Твича содержат временные метки ключевых фреймов и их позиции в файле (Твич использует flvmeta для создания onMetadata в своих flv).

В данном посте я запишу, что я узнал о FLV, когда реализовывал частичную выгрузку видео с Твича с указанными временными метками начала и конца отрезка. В освоении flv мне очень помогла статья от авторов red5. Итоговый продукт для конвертации Twitch-видео в gif онлайн - tw2gif.com.

UPD: Twitch внедрил поддержку HLS-видео. Теперь tw2gif.com поддерживает ссылки вида /v/, а всё далее идущее в статье уже не актуально.

FLV Заголовок

FieldData TypeExampleDescription
Signaturebyte3"FLV"Always "FLV"
Versionuint8"\x01" (1)Currently 1 for known FLV files
Flagsuint8 bitmask"\x05" (5, audio+video)Bitmask: 4 is audio, 1 is video
Offsetuint32_be"\x00\x00\x00\x09" (9)Total size of header (always 9 for known FLV files)

Таким образом, flv Твича всегда начинается со следующих байтов:
0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x09

Теги (пакеты, фреймы)

 После заголовочной части до конца файла идут теги. По стандарту, каждый тег начинается с 4х байт, содержащих размер предыдущего тега в uint32_be. Для первого тега значение всегда 0x00, 0x00, 0x00, 0x00. Далее идёт заголовочная часть пакета.

FieldData TypeExampleDescription
Typeuint8"\x12" (0x12, META)Determines the layout of Body, see below for tag types
BodyLengthuint24_be"\x00\x00\xe0" (224)Size of Body (total tag size - 11)
Timestampuint24_be"\x00\x00\x00" (0)Timestamp of tag (in milliseconds)
TimestampExtendeduint8"\x00" (0)Timestamp extension to form a uint32_be. This field has the upper 8 bits.
StreamIduint24_be"\x00\x00\x00" (0)Always 0
BodybyteBodyLength...Dependent on the value of Type

 Первый байт определяет тип пакета:
  • 0x12 - метаданные
  • 0x09 - видеоданные
  • 0x08 - аудиоданные
Далее идут три байта длины в формате uint24_be, затем 3 байта временной метки пакета в формате uint24_be, далее один байт для расширения значения временной метки (в Твиче не используется, всегда 0), далее три постоянных нулевых байта, а затем идёт основное содержания пакета. Формат содержания зависит от типа пакета. Для метаданных это AMF0-пакет.
FLV-контейнер схематично выглядит так:
FLV-заголовок 0x00 0x00 0x00 0x00 Тег Размер предыдущего тега (4 байта) Тег Размер предыдущего тега (4 байта) ... Размер предыдущего тега (4 байта) Тег

 Параметры кодеков FLV

С форматом аудио- и видеоданных я не разбирался, но знаю, что есть два особых пакета, которые содержат информацию об используемом аудио- и видео-кодеках. Например, если видео на Твиче 1920х1080, то могут быть такие пакеты:
Аудио (0х08) пакет с кодеком (+ 4 байта его размера):
0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0xAF, 0x00, 0x11, 0x90, 0x00, 0x00, 0x00, 0x0F

 Видео (0х09) пакет с кодеком (+4 байта его размера):
0x09, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x4D, 0x40, 0x29, 0xFF, 0xE1, 
0x00, 0x1D, 0x67, 0x4D, 0x40, 0x29, 0xEC, 0xA0, 0x3C, 0x01, 0x13, 
0xF2, 0xCD, 0x40, 0x43, 0x40, 0x50, 0x00, 0x00, 0x03, 0x00, 0x10, 
0x00, 0x00, 0x03, 0x03, 0xC8, 0xF1, 0x83, 0x19, 0x60, 0x01, 0x00, 
0x04, 0x68, 0xEA, 0xEF, 0x20, 0x00, 0x00, 0x00, 0x3C

Выкачивание аудио- и видеофреймов из Твича и соединение их вместе

Метаданные Твича содержат в себе массив временных меток и их смещение в файле.
Для начала нужно распарсить AMF0 пакет этих данных. Есть готовые инструменты разбора таких пакетов. В своем случае я просто искал в пакете начало вхождения строк "times" и "filepositions" и забирал следом идущие байты данных. Сами числа представлены в виде 8 байт float64_be. Временные метки представлены в секундах.

Если мы собираемся скачать видео с 152 секунды по 160 секунду, то ищем ближайший левый индекс в массиве временных меток, например, индекс числа 150. По этому индексу находим значение в массиве смещений, например 20847292. Аналогично находим смещение для крайнего правого ключевого фрейма. Твич поддерживает http-заголовк Range, поэтому мы можем передать ему параметры смещения для возврата содержимого flv-файла.

Если сделано всё правильно, то содержимое будет начинаться с заголовка видео-тега, с его типа, то есть с 0x09. Теперь нужно прочитать и пропарсить все эти теги. После этого можно будет уже отфильтровать нужные теги по их временным меткам. Для правильного соединения в flv, фреймы должны быть с последовательными временными метками (в заголовочной части тега), начиная от 0. Поэтому в каждом теге нужно пересчитать и переписать три байта временной метки. Соединяя несколько выкаченных кусков, я добавляю первому прикрепляемому постоянное значение для видео 33, для аудио 21. Это примерно соответствует видео и аудио фреймрейтам Твича. Таким образом, все временные метки являются уникальными, упорядоченными, начинающимися с 0 и с шагом изменения около 33 и 21.

Всё, что остается сделать, это взять постоянное начало flv, добавить 2 пакета с информацией о кодеках, прикрепить медиа-фреймы с пересчитанными временными метками и сохранить. Полученный flv является валидным. С помощью ffmpeg можно извлекать видео в формат mp4 без перекодировки. Если нужно добавить метаданные, то можно использовать утилиту flvmeta, которая сама считает смещения ключевых фреймов и вставляет их в файл.

Онлайн-сервис

Одной из задач при создании онлайн-сервиса для выкачивания видео с Твича и конвертации стала задача добавления водяного знака. Так как добавление знака на mp4 видео не может быть без перекодирования видео, то сам процесс становится слишком долгим. Было решено добавлять водяной знак только на первые 2-3 секунды видео, а остальную часть соединять без перекодирования. В случае же с gif, то перекодировки всё равно не избежать, поэтому добавление водяного знака не усложняет процесс.
Другой проблемой стала нехватка оперативной памяти для выполнение перекодирования 1080p-видео даже для двух секунд. К счастью, ffmpeg имеет параметр -preset ultrafast, который частично решил эту проблему. Но нужно было организовать очередь исполнения таких команд, чтобы одновременно запущенные процессы перекодирования вмещались в объем ресурсов сервера. Поэтому был написан простой менеджер очереди для исполнения задач друг за другом.
Всё это можно попробовать на сайте http://tw2gif.com/.

Комментариев нет:

Отправить комментарий