Я действительно застрял, пытаясь понять лучший способ потоковой передачи выходных данных ffmpeg в режиме реального времени клиенту HTML5 с помощью node.js, так как есть ряд переменных, и у меня нет большого опыта в этом пространстве, потратив много часов, пробуя разные комбинации.
Мой вариант использования:
1) IP-видеокамера Поток RTSP H.264 принимается FFMPEG и повторно смешивается в контейнере mp4 с использованием следующих настроек FFMPEG в узле, выводится в STDOUT. Это выполняется только при первоначальном клиентском соединении, поэтому частичные запросы контента не пытаются снова порождать FFMPEG.
liveFFMPEG = child_process.spawn("ffmpeg", [
"-i", "rtsp://admin:12345@192.168.1.234:554" , "-vcodec", "copy", "-f",
"mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov",
"-" // output to stdout
], {detached: false});
2) Я использую http-сервер узла для захвата STDOUT и передачи его обратно клиенту по запросу клиента. Когда клиент впервые подключается, я создаю вышеупомянутую командную строку FFMPEG, а затем передаю поток STDOUT в ответ HTTP.
liveFFMPEG.stdout.pipe(resp);
Я также использовал потоковое событие для записи данных FFMPEG в ответ HTTP, но без разницы
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
Я использую следующий HTTP-заголовок (который также используется и работает при потоковой передаче предварительно записанных файлов)
var total = 999999999 // fake a large file
var partialstart = 0
var partialend = total - 1
if (range !== undefined) {
var parts = range.replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
}
var start = parseInt(partialstart, 10);
var end = partialend ? parseInt(partialend, 10) : total; // fake a large file if no range reques
var chunksize = (end-start)+1;
resp.writeHead(206, {
'Transfer-Encoding': 'chunked'
, 'Content-Type': 'video/mp4'
, 'Content-Length': chunksize // large size to fake a file
, 'Accept-Ranges': 'bytes ' + start + "-" + end + "/" + total
});
3) Клиент должен использовать теги видео HTML5.
У меня нет проблем с потоковым воспроизведением (используя fs.createReadStream с частичным содержимым HTTP 206) для клиента HTML5 видеофайл, ранее записанный с помощью указанной выше командной строки FFMPEG (но сохраненный в файл вместо STDOUT), поэтому я знаю поток FFMPEG правильно, и я даже могу правильно видеть потоковую передачу видео в VLC при подключении к серверу узла HTTP.
Однако попытка потокового вещания из FFMPEG через узел HTTP выглядит намного сложнее, так как клиент отобразит один кадр, а затем остановится. Я подозреваю, что проблема в том, что я не настраиваю соединение HTTP, чтобы быть совместимым с видео клиентом HTML5. Я пробовал разные вещи, такие как использование HTTP 206 (частичное содержимое) и 200 ответов, помещение данных в буфер, а затем потоковую передачу без удачи, поэтому мне нужно вернуться к первым принципам, чтобы убедиться, что я настроил это правильно путь.
Вот мое понимание того, как это должно работать, поправьте меня, если я ошибаюсь:
1) FFMPEG должен быть настроен для фрагментации вывода и использования пустого moov (флаги FFMPEG frag_keyframe и empty_moov mov). Это означает, что клиент не использует атом moov, который обычно находится в конце файла, который не имеет отношения к потоковой передаче (без конца файла), но означает, что поиск невозможен, что хорошо для моего варианта использования.
2) Несмотря на то, что я использую фрагменты MP4 и пустой MOOV, мне все равно нужно использовать частичное содержимое HTTP, так как проигрыватель HTML5 будет ждать, пока весь поток не загрузится, прежде чем играть, что с живым потоком никогда не заканчивается, поэтому не работает.
3) Я не понимаю, почему передача потока STDOUT в ответ HTTP не работает при потоковой передаче в реальном времени. Если я сохраню в файл, я могу легко передать этот файл клиентам HTML5, используя подобный код. Может быть, это проблема синхронизации, так как для запуска порождения FFMPEG требуется секунда, он подключается к IP-камере и отправляет порции на узел, а события данных узла также нерегулярны. Однако поток данных должен быть точно таким же, как при сохранении в файл, и HTTP должен быть способен справляться с задержками.
4) При проверке сетевого журнала от HTTP-клиента при потоковой передаче файла MP4, созданного FFMPEG с камеры, я вижу, что есть 3 клиентских запроса: общий запрос GET для видео, который HTTP-сервер возвращает около 40 КБ, затем частичный запрос контента с байтовым диапазоном для последних 10КБ файла, затем окончательный запрос для битов в середине не загружен. Может быть, клиент HTML5, получив первый ответ, запрашивает последнюю часть файла для загрузки атома MP4 MOOV? В этом случае он не будет работать для потоковой передачи, так как нет файла MOOV и нет конца файла.
5) При проверке сетевого журнала при попытке потокового вещания я получаю прерванный начальный запрос только с полученными приблизительно 200 байтами, затем повторный запрос снова прерывается с 200 байтами и третьим запросом, длина которого составляет всего 2 КБ. Я не понимаю, почему клиент HTML5 прервал запрос, поскольку поток байтов точно такой же, какой я могу успешно использовать при потоковой передаче из записанного файла. Также кажется, что узел не отправляет оставшуюся часть потока FFMPEG клиенту, но я вижу данные FFMPEG в подпрограмме события .on, поэтому он попадает на HTTP-сервер узла FFMPEG.
6) Хотя я думаю, что передача потока STDOUT в буфер ответов HTTP должна работать, мне нужно создать промежуточный буфер и поток, которые позволят клиентским запросам частичного содержимого HTTP работать должным образом, как это происходит, когда он (успешно) читает файл ? Я думаю, что это главная причина моих проблем, но я не совсем уверен, как лучше всего это настроить. И я не знаю, как обработать клиентский запрос данных в конце файла, поскольку у него нет конца файла.
7) Я ошибаюсь, пытаясь обработать 206 частичных запросов контента, и должно ли это работать с обычными 200 HTTP-ответами? HTTP 200 ответы отлично работают для VLC, поэтому я подозреваю, что видео-клиент HTML5 будет работать только с частичными запросами контента?
Так как я все еще изучаю этот материал, трудно разобраться с различными уровнями этой проблемы (FFMPEG, узел, потоковая передача, HTTP, видео HTML5), поэтому любые указатели будут высоко оценены. Я провел часы, исследуя этот сайт и сеть, и я не встречал никого, кто мог бы выполнять потоковую передачу в реальном времени на узле, но я не могу быть первым, и я думаю, что это должно сработать (каким-то образом !).
Content-Type
в голову? Вы используете чанк код? Вот где я бы начал. Кроме того, HTML5 не обязательно обеспечивает функциональность для потоковой передачи, вы можете прочитать об этом здесь . Скорее всего, вам потребуется реализовать способ буферизации и воспроизведения видеопотока своими собственными средствами ( см. Здесь ), хотя это, вероятно, не очень хорошо поддерживается. Также загляните в MediaSource API.