Лучший подход к потоку HTTP в реальном времени для видеоконтента HTML5

Я действительно застрял, пытаясь понять лучший способ потокового вывода 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, но я не могу быть первым, и я думаю, что это должно быть в состоянии работать (как-то!).

Ответ 1

ИЗМЕНИТЬ 3: Начиная с IOS 10, HLS будет поддерживать фрагментированные файлы mp4. Ответ теперь, чтобы создать фрагментированные активы mp4, с манифестами DASH и HLS. > Pretend flash, iOS9 и ниже, а IE 10 и ниже не существует.

Все ниже этой строки устарело. Хранение его здесь для потомков.


ИЗМЕНИТЬ 2: Как указывают люди в комментариях, все меняется. Почти все браузеры будут поддерживать кодеки AVC/AAC. iOS все еще требует HLS. Но через адаптеры, такие как hls.js, вы можете играть HLS в MSE. Новый ответ - HLS + hls.js, если вам нужен iOS. или просто Фрагментированный MP4 (т.е. DASH), если вы не

Существует много причин, по которым видео и, в частности, живое видео очень сложно. (Обратите внимание, что в исходном вопросе указано, что видео в формате HTML5 является обязательным требованием, но в заявлении указано, что Flash возможен в комментариях. Поэтому немедленно этот вопрос вводит в заблуждение)

Сначала я повторю: НЕТ ОФИЦИАЛЬНОЙ ПОДДЕРЖКИ ДЛЯ ЖИВОГО ПОТОКА НА HTML5. Есть хаки, но ваш пробег может меняться.

EDIT: поскольку я написал этот ответ, расширения Media Source расширили, и теперь они очень близки к тому, чтобы стать жизнеспособным вариантом. Они поддерживаются на большинстве основных браузеров. IOS продолжает удерживаться.

Затем вам нужно понять, что видео по запросу (VOD) и живое видео очень разные. Да, они оба видео, но проблемы разные, поэтому форматы разные. Например, если часы на вашем компьютере работают на 1% быстрее, чем нужно, вы не заметите на VOD. С живым видео вы будете пытаться воспроизводить видео до его появления. Если вы хотите присоединиться к потоку видео в реальном времени, вам нужны данные, необходимые для инициализации декодера, поэтому его необходимо повторить в потоке или отправить вне диапазона. С помощью VOD вы можете прочитать начало файла, в котором они стремятся, независимо от того, что вы хотите.

Теперь отпустите немного.

Платформы:

  • IOS
  • PC
  • Mac
  • Android

Кодеки:

  • vp8/9
  • h.264
  • thora (vp3)

Общие способы доставки для видео в реальном времени в браузерах:

  • DASH (HTTP)
  • HLS (HTTP)
  • flash (RTMP)
  • flash (HDS)

Общие методы доставки для VOD в браузерах:

  • DASH (потоковое HTTP)
  • HLS (потоковое HTTP)
  • flash (RTMP)
  • flash (потоковое HTTP)
  • MP4 (потоковая передача HTTP)
  • Я не буду говорить о MKV и OOG, потому что я их не очень хорошо знаю.

html5 тег видео:

  • MP4
  • WebM
  • OGG

Давайте посмотрим, какие браузеры поддерживают какие форматы

Safari:

  • HLS (только для iOS и Mac)
  • h.264
  • MP4

Firefox

  • DASH (через MSE, но не h.264)
  • h.264 только с помощью Flash!
  • VP9
  • MP4
  • OGG
  • WebM

IE

  • Вспышка
  • DASH (только через MSE IE 11+)
  • h.264
  • MP4

Хром

  • Вспышка
  • DASH (через MSE)
  • h.264
  • VP9
  • MP4
  • WebM
  • OGG

MP4 не может использоваться для видео в реальном времени (ПРИМЕЧАНИЕ: DASH - это надмножество MP4, поэтому не путайте с этим). MP4 разбит на две части: moov и mdat. mdat содержит необработанные аудио-видео данные. Но он не индексируется, поэтому без moov это бесполезно. В moov содержится индекс всех данных в mdat. Но из-за его формата он не может быть "сплющен", пока не будут известны временные метки и размер КАЖДОГО кадра. Возможно, будет возможно создать moov, который "отображает" размеры фрейма, но он очень расточительный.

Итак, если вы хотите доставить всюду, нам нужно найти наименьший общий знаменатель. Вы увидите, что здесь нет ЖК-экрана, не прибегая к вспышке Пример:

  • iOS поддерживает только видео h.264. и он поддерживает только HLS для жизни.
  • Firefox не поддерживает h.264 вообще, если вы не используете flash
  • Flash не работает в iOS

Ближе всего к ЖК-дисплею используется HLS для доступа к вашим iOS-пользователям и для всех остальных. Мой личный фаворит - кодировать HLS, а затем использовать flash для воспроизведения HLS для всех остальных. Вы можете играть в HLS во флэш-памяти через JW-плеер 6 (или написать собственный HLS для FLV в AS3, как и я)

Вскоре самый распространенный способ сделать это будет HLS на iOS/Mac и DASH через MSE везде (это то, что Netflix будет делать в ближайшее время). Но мы все еще ждем, чтобы все обновили свои браузеры. Вам также, вероятно, понадобится отдельный DASH/VP9 для Firefox (я знаю об open264, он отстой, он не может делать видео в главном или высоком профиле, поэтому он в настоящее время бесполезен).

Ответ 2

Спасибо всем особенно szatmary, поскольку это сложный вопрос и имеет много слоев для него, все, что нужно для работы, прежде чем вы сможете транслировать живое видео. Чтобы прояснить мой оригинальный вопрос и использование видео HTML5 vs flash - мой вариант использования имеет сильное предпочтение HTML5, потому что он является универсальным, простым в реализации на клиенте и в будущем. Flash является удаленным вторым, поэтому давайте придерживаться HTML5 для этого вопроса.

Я многому научился в этом упражнении и согласен, что трансляция в прямом эфире намного сложнее, чем VOD (что хорошо работает с видео HTML5). Но я получил это, чтобы удовлетворительно работать для моего варианта использования, и решение получилось очень простым, преследуя более сложные варианты, такие как MSE, flash, сложные схемы буферизации в Node. Проблема заключалась в том, что FFMPEG искажал фрагментированный MP4, и мне приходилось настраивать параметры FFMPEG, и стандартное перенаправление потока node по HTTP, которое я использовал изначально, было всем, что было необходимо.

В MP4 есть опция "фрагментации", которая разбивает mp4 на гораздо более мелкие фрагменты, у которых есть свой собственный индекс и делает возможный вариант потоковой передачи mp4 жизнеспособным. Но невозможно вернуться в поток (ОК для моего варианта использования) и более поздние версии фрагментации поддержки FFMPEG.

Отслеживание заметок может быть проблемой, и с моим решением у меня есть отставание в диапазоне от 2 до 6 секунд, вызванное комбинацией ремуксинга (эффективно FFMPEG должен получить прямой эфир, затем отправить его на node для обслуживания через HTTP). Не так много можно сделать по этому поводу, однако в Chrome видео пытается догнать столько, сколько может, что делает видео немного нервным, но более актуальным, чем IE11 (мой предпочтительный клиент).

Вместо того, чтобы объяснять, как работает код в этом сообщении, проверьте GIST с комментариями (код клиента не включен, это стандартный тег HTML5 с адресом сервера node http). GIST находится здесь: https://gist.github.com/deandob/9240090

Мне не удалось найти похожие примеры этого варианта использования, поэтому я надеюсь, что приведенное выше объяснение и код помогают другим, тем более, что я так многому научился с этого сайта и все еще считаю себя новичком!

Хотя это ответ на мой конкретный вопрос, я выбрал ответ szatmary как принятый, поскольку он является наиболее полным.

Ответ 3

Взгляните на JSMPEG проект. Там реализована отличная идея - декодировать MPEG в браузере с помощью JavaScript. Например, байты из кодировщика (например, FFMPEG) могут быть переданы в браузер через WebSockets или Flash. Если сообщество наверстает упущенное, я думаю, это будет лучшее решение для потокового видео в реальном времени HTML5.

Ответ 4

Один из способов трансляции веб-камеры на основе RTSP на клиент HTML5 (включает повторное кодирование, поэтому ожидайте потери качества и требуйте некоторого процессора):

  • Настройка сервера icecast (может быть на том же компьютере, на котором установлен веб-сервер или на компьютере, который получает RTSP-поток с камеры)
  • На машине, принимающей поток с камеры, не используйте FFMPEG, а gstreamer. Он способен принимать и декодировать RTSP-поток, перекодировать его и передавать на сервер icecast. Пример конвейера (только видео, без звука):

    gst-launch-1.0 rtspsrc location=rtsp://192.168.1.234:554 user-id=admin user-pw=123456 ! rtph264depay ! avdec_h264 ! vp8enc threads=2 deadline=10000 ! webmmux streamable=true ! shout2send password=pass ip=<IP_OF_ICECAST_SERVER> port=12000 mount=cam.webm
    

= > Затем вы можете использовать <video> тег с URL-адресом потока icecast (http://127.0.0.1:12000/cam.webm), и он будет работать в каждом браузере и устройстве, поддерживающем webm

Ответ 5

Я написал видеопроигрыватель HTML5 вокруг широкоформатного кодека h264 (emscripten), который может воспроизводить видео в реальном времени (без задержки) h264 во всех браузерах (рабочий стол, iOS,...).

Видеопоток отправляется через websocket на клиент, декодированный кадр на кадр и отображается в банке (с помощью webgl для ускорения)

Отметьте https://github.com/131/h264-live-player в github.

Ответ 6

Как насчет использования jpeg-решения, просто позвольте серверу распространять jpeg один за другим в браузере, а затем использовать элемент canvas для рисования этих jpeg? http://thejackalofjavascript.com/rpi-live-streaming/

Ответ 7

Посмотрите это решение. Как я знаю, Flashphoner позволяет воспроизводить Live audio + видеопоток на чистой странице HTML5.

Они используют MPEG1 и G.711 кодеки для воспроизведения. Хак - это рендеринг декодированного видео в элемент холста HTML5 и воспроизведение декодированного звука через аудиоконтент HTML5.

Ответ 8

Это очень распространенное заблуждение. Нет поддержки в реальном времени HTML5 (кроме HLS на iOS и Mac Safari). Вы можете "взломать" его с помощью контейнера webm, но я бы не ожидал, что это будет поддерживаться повсеместно. То, что вы ищете, включено в расширения источников мультимедиа, где вы можете кормить фрагменты в браузере по одному за раз. но вам нужно будет написать javascript на стороне клиента.

Ответ 9

Попробуйте binaryjs. Его просто как socket.io, но только то, что он делает хорошо, это то, что он передает аудио-видео. Binaryjs google it