Как использовать ffmpeg в JavaScript для декодирования кадров H.264 в кадры RGB

Я пытаюсь скомпилировать ffmpeg в javascript, чтобы я мог декодировать видеопотоки H.264, используя node. Потоки представляют собой кадры H.264, упакованные в RTP NALU, поэтому любое решение должно иметь возможность принимать кадры H.264, а не целое имя файла. Эти кадры не могут быть в контейнере, таком как MP4 или AVI, потому что тогда демультиплексору требуется отметка времени каждого кадра, прежде чем может произойти демультиплексирование, но я имею дело с потоком в реальном времени, без контейнеров.

Потоковая передача H.264 по RTP

Ниже приведен базовый код, который я использую для прослушивания сокета udp. Внутри обратного вызова «сообщение» пакет данных представляет собой дейтаграмму RTP. Часть данных граммы данных представляет собой кадр H.264 (P-кадры и I-кадры).

var PORT = 33333;
var HOST = '127.0.0.1';

var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
    var address = server.address();
    console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

server.on('message', function (message, remote) {
    console.log(remote.address + ':' + remote.port +' - ' + message);
    frame = parse_rtp(message);

    rgb_frame = some_library.decode_h264(frame); // This is what I need.

});

server.bind(PORT, HOST);  

Я нашел библиотеку Broadway.js, но не смог заставить ее работать, и она не обрабатывает P-кадры, которые мне нужны. Я также нашел ffmpeg.js, но смог заставить его работать, и для этого нужен целый файл, а не поток. Аналогично, fluent-ffmpeg не поддерживает файловые потоки; все примеры показывают, что конструктору передается имя файла. Поэтому я решил написать свой собственный API.

Моя текущая попытка решения

Мне удалось скомпилировать ffmpeg в один большой файл js, но я не могу использовать его таким образом. Я хочу написать API вокруг ffmpeg, а затем предоставить эти функции для JS. Итак, мне кажется, что мне нужно сделать следующее:

  1. Скомпилируйте компоненты ffmpeg (avcodec, avutil и т. д.) в битовый код llvm.
  2. Напишите оболочку C, которая предоставляет функциональность декодирования и использует EMSCRIPTEN_KEEPALIVE.
  3. Используйте emcc, чтобы скомпилировать оболочку и связать ее с битовым кодом, созданным на шаге 1.

Я нашел WASM+ffmpeg, но он на китайском, и некоторые шаги непонятны. . В частности, есть этот шаг:

emcc web.c process.c ../lib/libavformat.bc ../lib/libavcodec.bc ../lib/libswscale.bc ../lib/libswresample.bc ../lib/libavutil.bc \

:( Где, я думаю, я застрял

Я не понимаю, как все компоненты ffmpeg компилируются в отдельные файлы *.bc. Я следовал командам emmake из этой статьи и получил один большой файл .bc.

2 вопроса

1. Кто-нибудь знает шаги по компиляции ffmpeg с использованием emscripten, чтобы я мог открыть некоторые API для javascript?
2. Есть ли лучший способ (с достойной документацией/примерами) для декодирования видеопотоков h264 с помощью узла?


person noel    schedule 17.09.2018    source источник
comment
попробуйте взглянуть на свободный API: npmjs.com/package/fluent-ffmpeg   -  person Robert Rowntree    schedule 18.09.2018
comment
Судя по их примерам, @RobertRowntree fluent-ffmpeg не поддерживает файловый поток. Я обновил свой вопрос.   -  person noel    schedule 18.09.2018
comment
В документации говорится, что вы можете передать конструктору имя входного файла или читаемый поток, объект конфигурации или и то, и другое. Что вы подразумеваете под «файловым потоком»?   -  person Alex Taylor    schedule 18.09.2018
comment
github.com/fluent-ffmpeg/ node-fluent-ffmpeg/blob/master/ выглядит как входной поток для меня   -  person Robert Rowntree    schedule 18.09.2018
comment
Я обновил свой пост, чтобы показать пример типа потока, о котором я говорю.   -  person noel    schedule 18.09.2018
comment
Вы можете открыть дочерний процесс для запуска ffmpeg, а затем передать свои RTP-сообщения в стандартный ввод этого дочернего процесса и прочитать свои rgb_frames из стандартного вывода дочернего процесса.   -  person GroovyDotCom    schedule 26.09.2018
comment
Если вы хотите еще проще и вам не нужно самостоятельно анализировать RTP, вы можете позволить ffmpeg также обрабатывать транспорт RTP, а дочерняя команда будет выглядеть примерно так: ffmpeg -i rtp://127.0.0.1:9999 -f rawvideo -pix_fmt rgb24 -s 320x240 -vcodec rawvideo. Каждые 320x240x3 байта, которые вы читаете от дочернего элемента, будут одним кадром RGB.   -  person GroovyDotCom    schedule 27.09.2018
comment
Пожалуйста, опишите ваши кадры H.264, упакованные в RTP NALU, более подробно - без этого невозможно сказать, как с этим можно работать. Например. вы можете показать минимальный воспроизводимый пример, который их генерирует. Например. как принимающая сторона должна выяснить метаданные? временные метки? соединять взаимозависимые фреймы вместе?   -  person ivan_pozdeev    schedule 27.09.2018
comment
Заключительный комментарий: мне кажется, вы ошибочно убедили себя, что ваше решение не должно вызывать какой-то подпроцесс, а должно фактически раскрывать API ffmpeg. Интерфейс командной строки ffmpeg довольно хорошо раскрывает функциональные возможности FFMPEG таким образом, который, вероятно, проще и эффективнее вызывать из Javascript.   -  person GroovyDotCom    schedule 27.09.2018
comment
Я согласен с этим. Меня вдохновил Broadway.js и др. но я думаю, что нашел решение, которое прекрасно работает с использованием веб-сокетов. Может опубликовать MCVE на будущее. Я все еще хотел бы выяснить, как использовать ffmpeg с wasm.   -  person noel    schedule 27.09.2018


Ответы (2)


К вопросу 1: просто следуйте официальному документу.

Рассмотрим случай, когда вы обычно строите со следующими командами:

./configure
make

Для сборки с помощью Emscripten вместо этого вы должны использовать следующие команды:

# Run emconfigure with the normal configure command as an argument.
./emconfigure ./configure

# Run emmake with the normal make to generate linked LLVM bitcode.
./emmake make

# Compile the linked bitcode generated by make (project.bc) to JavaScript.
# 'project.bc' should be replaced with the make output for your project (e.g. 'yourproject.so')
#  [-Ox] represents build optimisations (discussed in the next section).
./emcc [-Ox] project.bc -o project.js

На вопрос 2: библиотеки c/c++ можно вызывать в среде узла. Вы можете написать некоторый связующий код c/c++ или использовать модуль прокси-узла, такой как node-ffi.

Использование node-ffi для вызова существующих библиотек может быть проще. Да поможет :)

person Dragonite    schedule 30.11.2018

Самый простой способ, особенно если вам нужно запустить его в веб-браузерах, — использовать расширение Media Source. Мне удалось это сделать всего за 3 дня. Более того, он автоматически использует аппаратное ускорение графического процессора (Cuda, Intel Qsv, ...), а также встроенную поддержку браузера. Важно, если он работает в приложении реального мира. Вчера я протестировал всего 5% процессора моей старой машины i7 для декодирования 4K (в 4 раза больше, чем 1080p) IP-камеры H.264 nal в прямом эфире. Я не уверен, что это на стороне сервера js, как и nodejs, но я ожидаю, что результат будет аналогичным. Свяжитесь со мной, если вам нужно больше. Насчет H.265/HEVC нужно emscripten частично из ffmpeg или x265 или OpenH265 аналогично, с минимальным размером bc (менее 1,2,3 Мб в зависимости от конфига). Удачи...

person HOSUNG CHANG    schedule 17.06.2020