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

Я действительно застрял, пытаясь понять лучший способ потоковой передачи вывода ffmpeg в реальном времени клиенту HTML5 с помощью узла.js, так как в игре есть несколько переменных, и у меня нет большого опыта в этом пространстве, проведя много часов, пробуя разные комбинации.

мой вариант использования:

1) поток видеокамеры RTSP H. 264 IP выбран FFMPEG и remuxed в контейнер 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);

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

однако попытка потоковой передачи из FFMPEG через узел HTTP кажется намного сложнее, так как клиент отобразит один кадр, а затем остановится. Я подозреваю, что проблема в том, что я не настраиваю HTTP-соединение, чтобы быть совместимым с HTML5 video client. Я пробовал различные вещи, такие как использование HTTP 206 (частичное содержимое) и 200 ответов, помещая данные в буфер, а затем потоковое не повезло, поэтому мне нужно вернуться к первым принципам, чтобы убедиться, что я настраиваю это правильно.

вот мое понимание как это должно работать, поправьте меня, если я ошибаюсь:

1) FFMPEG должен быть настроен для фрагментации вывода и использования пустого Moov (флаги ffmpeg frag_keyframe и empty_moov mov). Это означает, что клиент не использует атом moov, который обычно находится в конце файла, который не имеет отношения к потоковой передаче (нет конца файла), но означает нет поиск возможного, что хорошо для моего случая использования.

2) хотя я использую фрагменты MP4 и пустой MOOV, я все равно должен использовать HTTP-частичный контент, так как HTML5-плеер будет ждать, пока весь поток не будет загружен перед воспроизведением, что с живым потоком никогда не заканчивается, поэтому не работает.

3) я не понимаю, почему передача потока STDOUT в HTTP-ответ не работает при потоковой передаче в реальном времени, если я сохраняю файл, я могу легко передать этот файл клиентам HTML5, используя подобный кодекс. Возможно, это проблема времени, поскольку для запуска ffmpeg spawn требуется секунда, подключение к IP-камере и отправка фрагментов в узел, а события данных узла также нерегулярны. Однако bytestream должен быть точно таким же, как сохранение в файл, и HTTP должен иметь возможность обслуживать задержки.

4) при проверке сетевого журнала от клиента HTTP при потоковой передаче файла MP4, созданного FFMPEG с камеры, я вижу, что есть 3 клиентских запроса: общий запрос GET для видео, которое HTTP-сервер возвращает около 40Kb, затем частичный запрос контента с диапазоном байтов для последнего 10K файла, затем окончательный запрос на биты в середине не загружен. Может быть, клиент HTML5 после получения первого ответа запрашивает последнюю часть файла для загрузки mp4 Moov atom? Если это так, он не будет работать для потоковой передачи, поскольку нет файла MOOV и нет конца файла.

5) при проверке сетевого журнала при попытке потокового видео я получаю прерванный первоначальный запрос с полученным только около 200 байтами, затем повторный запрос снова прерван с 200 байтами и третьим запросом, который составляет всего 2K. Я не понимаю, почему клиент HTML5 прерывает запрос, поскольку bytestream точно такой же, как я могу успешно использовать при потоковой передаче из записанного файла. Также кажется, что узел не отправляет остальную часть потока FFMPEG клиенту, но я вижу данные FFMPEG в .на рутине события, поэтому он попадает в узел ffmpeg HTTP сервер.

6) хотя я думаю, что передача потока STDOUT в буфер ответа HTTP должна работать, должен ли я создавать промежуточный буфер и поток, который позволит http-частичным клиентским запросам контента правильно работать, как это происходит, когда он (успешно) читает файл? Я думаю, что это основная причина моих проблем, однако я не совсем уверен в узле, как лучше всего настроить это. И я не знаю, как обрабатывать запрос клиента на данные в конце файла отсутствует конец файла.

7) я на неправильном пути, пытаясь обработать 206 частичных запросов контента, и должен ли это работать с обычными 200 HTTP-ответами? HTTP 200 ответов отлично работает для VLC, поэтому я подозреваю, что HTML5 video client будет работать только с частичными запросами контента?

поскольку я все еще изучаю этот материал, его трудно работать через различные слои этой проблемы (FFMPEG, node, streaming, HTTP, HTML5 video), поэтому любые указатели будут высоко оценены. У меня есть потратил часы на исследование на этом сайте и в сети, и я не встречал никого, кто смог бы сделать потоковую передачу в реальном времени в узле, но я не могу быть первым, и я думаю, что это должно быть в состоянии работать (как-то!).

9 ответов


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

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


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

есть много причин, почему видео и, в частности, видео очень сложно. (Обратите внимание, что в исходном вопросе указано, что HTML5-Видео является требованием, но asker заявил, что вспышка возможна в комментариях. Так что сразу этот вопрос вводит в заблуждение)

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

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

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

теперь давайте немного покопаемся.

платформы:

  • iOS
  • PC
  • Mac
  • Android

кодеки:

  • VP8 и аудиокодек/9
  • сек.264
  • Тора (большая перспектива 3)

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

  • DASH (HTTP)
  • HLS (HTTP)
  • вспышка (RTMP)
  • вспышка (HDS)

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

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

html5 Видео тег:

  • МР4
  • формат WebM
  • ogg

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

Safari:

  • HLS (только для iOS и mac)
  • сек.264
  • МР4

Firefox

  • DASH (через MSE, но не h.264)
  • сек.264 через флеш только!
  • VP9 или
  • МР4
  • OGG
  • формат WebM

IE

  • вспышка
  • тире (только через MSE IE 11+)
  • сек.264
  • МР4

Chrome

  • вспышка
  • тире (через MSE)
  • сек.264
  • VP9 или
  • МР4
  • формат WebM
  • ogg

MP4 не может использоваться для живого видео (Примечание: DASH-это суперсет MP4, поэтому не путайте с этим). MP4 разбит на две части: moov и mdat. mdat содержит необработанные аудио-видео данные. Но он не индексируется, поэтому без moov он бесполезен. Moov содержит индекс всех данных в mdat. Но из-за его формата он не может быть "сплющен", пока не будут известны временные метки и размер каждого кадра. Возможно, можно построить moov, который "fibs" размеры кадра, но это очень расточительно пропускная способность мудрая.

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

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

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

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


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

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

In MP4 существует опция "фрагментация", которая разбивает mp4 на гораздо меньшие фрагменты, которые имеют свой собственный индекс и делают опцию mp4 live streaming жизнеспособной. Но невозможно вернуться в поток (OK для моего варианта использования), а более поздние версии ffmpeg поддерживают фрагментацию.

ПРИМЕЧАНИЕ время может быть проблемой, и с моим решением у меня есть отставание от 2 до 6 секунд, вызванное комбинацией ремуксинга (эффективно FFMPEG должен получить живой поток, remux его затем отправить это узел для обслуживания по HTTP). Не так много можно сделать об этом, однако в Chrome видео пытается догнать столько, сколько может, что делает видео немного нервным, но более актуальным, чем IE11 (мой предпочтительный клиент).

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

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

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



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

  • настройка сервера icecast (может быть на той же машине, на которой находится веб-сервер, или на машине, которая получает RTSP-поток от cam)
  • на машине, получающей поток от камеры, не используйте 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
    

=> затем вы можете использовать тег


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

видеопоток отправляется через websocket клиенту, декодируется кадр на кадр и отображается в canva (используя webgl для ускорения)

проверить https://github.com/131/h264-live-player на github.


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

Они использовать формат MPEG1 и г. 711 кодеки для воспроизведения. Хак представляет декодированное видео в HTML5 canvas element и воспроизводит декодированное аудио через HTML5 audio context.


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


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


попробуйте binaryjs. Это как розетка.io, но единственное, что он делает хорошо, - это потоковое аудио-видео. Binaryjs google это