Я действительно застрял, пытаясь понять лучший способ потокового вывода ffmpeg в реальном времени для клиента HTML5 с помощью node.js, так как в игре есть несколько переменных, и у меня нет много опыт в этом пространстве, потратив много часов на различные комбинации.
Мой вариант использования:
1) IP-видеокамера RTSP H.264-поток подбирается FFMPEG и переводится в контейнер mp4, используя следующие настройки FFMPEG в node, выводимые в STDOUT. Это выполняется только при первоначальном подключении к клиенту, поэтому частичные запросы на контент не пытаются снова запустить FFMPEG.
liveFFMPEG = child_process.spawn("ffmpeg", [
"-i", "rtsp://admin:[email protected]:554" , "-vcodec", "copy", "-f",
"mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov",
"-" // output to stdout
], {detached: false});
2) Я использую сервер http node 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 с 206 частичным HTTP-сообщением HTTP) клиенту HTML5 видеофайл, ранее записанный в указанной выше командной строке FFMPEG (но сохраненный в файл вместо STDOUT), поэтому я знаю поток FFMPEG верен, и я могу даже правильно видеть потоковое видео в реальном времени в VLC при подключении к серверу HTTP node.
Однако попытка потоковой передачи из FFMPEG через node 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-камере и отправить куски на node, а события данных node также нерегулярны. Однако поточный поток должен быть точно таким же, как сохранение в файле, а HTTP должен иметь возможность обслуживать задержки.
4) При проверке сетевого журнала с HTTP-клиента при потоковой передаче файла MP4, созданного FFMPEG с камеры, я вижу, что есть 3 клиентских запроса: общий запрос GET для видео, который HTTP-сервер возвращает около 40 Кбит, затем запрос частичного содержимого с байтом диапазона для последних 10K файла, затем окончательный запрос для бит в середине не загружен. Может быть, клиент HTML5, получив первый ответ, попросит последнюю часть файла загрузить MP4 MOOV? Если это так, оно не будет работать для потоковой передачи, так как нет файла MOOV и конца файла.
5) При проверке сетевого журнала при попытке потока в реальном времени я получаю прерванный первоначальный запрос, содержащий только около 200 байтов, а затем повторный запрос снова прерывается с 200 байтами и третий запрос, длина которого составляет всего 2 КБ. Я не понимаю, почему клиент HTML5 прервал запрос, так как поточный поток точно такой же, как я могу успешно использовать при потоковой передаче из записанного файла. Похоже, что node не отправляет остальную часть потока FFMPEG клиенту, но я могу видеть данные FFMPEG в .on-событии, так что он добирается до HTTP-сервера FFMPEG node.
6) Хотя я думаю, что работа с потоком STDOUT в буфере ответа HTTP должна работать, мне нужно создать промежуточный буфер и поток, которые позволят клиентским запросам HTTP частичного контента правильно работать, как если бы он (успешно) читает файл? Я думаю, что это основная причина моих проблем, но я не совсем уверен в node, как лучше всего это настроить. И я не знаю, как обрабатывать клиентский запрос для данных в конце файла, так как нет конца файла.
7) Я нахожусь на неправильном пути, пытаясь обрабатывать 206 запросов частичного контента, и должно ли это работать с нормальными 200 ответами HTTP? Ответ HTTP 200 отлично подходит для VLC, поэтому я подозреваю, что видео-клиент HTML5 будет работать только с частичными запросами контента?
Поскольку я все еще изучаю этот материал, его трудно работать через различные уровни этой проблемы (FFMPEG, node, потоковое, HTTP, видео HTML5), поэтому любые указатели будут высоко оценены. Я потратил несколько часов на изучение этого сайта и сети, и я не встречал никого, кто смог сделать потоковое видео в режиме реального времени в node, но я не могу быть первым, и я думаю, что это должно быть в состоянии работать (как-то!).