Обзор решения Electron для потоковой передачи видео с малой задержкой

внешний интерфейс прямая трансляция FFmpeg

Давно не виделись, я не обновлял свой блог почти четыре месяца!

прошлый годпоследняя статьяВ этой статье представлены некоторые меры по оптимизации нашего настольного клиента Electron.Эта статья также связана с клиентом Electron, который мы разрабатываем. В последнее время мы предварительно изучаем решение для воспроизведения видеопотока конференции в реальном времени на странице Electron.



Интерфейс видеоконференций - последняя страница, которую не заменил веб.Он полностью разработан нативно, поэтому эффективность разработки относительно низкая.Например, очень больно разрабатывать некоторые анимационные эффекты, и сложно реагировать к изменению требований к продукту. Итак, мы думаем:Может ли сторона веб-страницы воспроизводить видеопоток, полученный базовой библиотекой WebRTC? Или почему бы не общаться напрямую через WebRTC API браузера??

Сначала ответьте на последнее, потому что логическая обработка, аудио- и видеообработка нашей видеоконференции были выделены в независимые, кросс-платформенные модули для унифицированного обслуживания; кроме того, интерфейс, предоставляемый WebRTC API браузера, очень продвинут, как черный Как и коробку, его нельзя настраивать и расширять, его сложно диагностировать и решать проблемы, ограниченные браузером. Самая большая причина в том, что изменение немного велико, и время не позволяет этого.

Поэтому в настоящее время можно выбрать только первое, то есть базовая библиотека отправляет видеопоток на страницу Electron и воспроизводит его в реальном времени на странице. До этого автор почти никогда не имел дела с разработкой аудио и видео. Я могу предположить, что базовая библиотека используется как «сторона хоста», а веб-страница используется как «сторона просмотра». аналогично прямому эфиру.


Поскольку видеопоток перенаправляется только локально, нам не нужно учитывать различные сложные условия сети и ограничения пропускной способности. Единственными требованиями являются низкая задержка, низкое потребление ресурсов:

  • В нашей видеоконференции голос и видео разделены. Есть только один смешанный голос, который передается через SIP. Видео конференции может иметь несколько каналов и использовать WebRTC для передачи. Нам не нужно обрабатывать голос (воспроизводимый непосредственно базовой библиотекой), что требует, чтобы наша задержка воспроизведения видео не была слишком большой, а голос и видео не были синхронизированы.
  • Нет необходимости учитывать совместимость браузера. Электронная версия браузера — Chrome 80.
  • Локальная переадресация, нет необходимости учитывать условия сети и ограничения пропускной способности


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



содержание



① Типичное решение для онлайн-трансляций

Существует множество вариантов веб-трансляции в прямом эфире (см. эту статью:«Прямая трансляция в Интернете, вам нужно знать это в первую очередь»):

  • RTMP (Real Time Messaging Protocol)Принадлежит Адобе. Низкая задержка и хорошая производительность в реальном времени. Тем не менее, браузер должен использовать Flash для воспроизведения; но мы также можем преобразовать его в поток HTTP/Websocket для подачиflv.jsРеализовать воспроизведение.
  • RTP (Real-time Transport Protocol) Нижний уровень WebRTC основан на RTP/RTCP.. Производительность в реальном времени очень хорошая, подходит для видеонаблюдения, видеоконференций, IP-телефонии.
  • HLS (Http Live Streaming)Протокол передачи потокового мультимедиа на основе HTTP, предложенный Apple. Safari имеет лучшую поддержку, и более высокие версии Chrome также поддерживают его, и есть несколько более зрелых сторонних решений.

Задержка HLS была слишком высока для наших требований, поэтому она была исключена в первую очередь. Я перерыл много информации, и многие из них про RTMP.Видно, насколько широко RTMP используется в Китае, поэтому мы планируем попробовать:


Во-первых, построить сервер RMTP, который может быть основан непосредственно наNode-Media-Server, код очень простой:

const NodeMediaServer = require('node-media-server')

const config = {
  // RMTP 服务器, 用于RTMP 推流和拉流
  rtmp: {
    port: 1935, // 1935 是RTMP的标准端口
    chunk_size: 0,
    gop_cache: false,
    ping: 30,
    ping_timeout: 60,
  },
  // HTTP / WebSocket 流,暴露给 flv.js
  http: {
    port: 8000,
    allow_origin: '*',
  },
}

var nms = new NodeMediaServer(config)
nms.run()


RTMP-пуш

ffmpegЭто важный артефакт для разработки аудио и видео. В этой статье он будет использоваться для захвата камеры, выполнения различных преобразований и обработки и, наконец, передачи видеопотока. Давайте посмотрим, как использовать ffmpeg для потоковой передачи RTMP.

Сначала выполните захват видео, следующая команда перечисляет все поддерживаемые типы устройств:

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

$ ffmpeg -devices
Devices:
 D. = Demuxing supported
 .E = Muxing supported
 --
 D  avfoundation    AVFoundation input device
 D  lavfi           Libavfilter virtual input device
  E sdl,sdl2        SDL2 output device

macOSобычно используется подavfoundationДля захвата устройства в следующем списке перечислены все устройства ввода, поддерживаемые текущим терминалом:

$ fmpeg -f avfoundation -list_devices true -i ""
[AVFoundation input device @ 0x7f8487425400] AVFoundation video devices:
[AVFoundation input device @ 0x7f8487425400] [0] FaceTime HD Camera
[AVFoundation input device @ 0x7f8487425400] [1] Capture screen 0
[AVFoundation input device @ 0x7f8487425400] AVFoundation audio devices:
[AVFoundation input device @ 0x7f8487425400] [0] Built-in Microphone
[AVFoundation input device @ 0x7f8487425400] [1] Boom2Device

мы будем использоватьFaceTime HD CameraЭто устройство ввода для захвата видео и передачи RTMP-потока:

$ ffmpeg -f avfoundation -r 30 -i "FaceTime HD Camera" -c:v libx264 -preset superfast -tune zerolatency -an -f flv rtmp://localhost/live/test

Чтобы немного объяснить приведенную выше команду:

  • -f avfoundation -r 30 -i "FaceTime HD Camera"означает отFaceTime HD CameraЗахват видео со скоростью 30 кадров в секунду
  • -c:v libx264Формат кодирования выходного видео — H.264, а RTMP обычно использует кодировку H.264.
  • -f flvОтносится к формату видеопакета, RTMP обычно использует формат пакета flv.
  • -anигнорировать аудиопоток
  • -preset superfast -tune zerolatencyПредустановленные параметры транскодирования H.264 и параметры настройки. Влияет на качество видео и степень сжатия

**Формат пакета (формат)иКодирование (кодек)** — это основная концепция разработки аудио и видео.
формат пакета: Эквивалентен контейнеру для хранения видеоинформации.Он объединяет закодированные аудио, видео или субтитры, сценарии и другие файлы в соответствии с соответствующими спецификациями для создания файла формата пакета. Распространенными форматами пакетов являются avi, mpeg, flv, mov и т. д.
Формат кодирования: Основная цель кодирования — сжатие. Аудио- и видеопоток, собранный с устройства, называется необработанным видеопотоком (формат rawvideo, то есть данные, которые не были закодированы и сжаты). Например: фильм 720p, 30 кадров в секунду, 60 минут, размер открытого потока: 12Bx1280x720x30x60x100 = 1,9T. Независимо от того, хранится ли он в файловой системе или передается по сети, стоимость слишком высока, поэтому нам необходимо сжатие кодирования. H264 — один из самых распространенных форматов кодирования.



Вытягивающий поток RTMP

Самый простой, мы можем использоватьffplay(один из наборов инструментов, предоставляемых ffmpeg) для проверки нормальности потоков push и pull:

$ ffplay rtmp://localhost/live/test

Flash устарел, чтобы добиться потоковой передачи RTMP на веб-страницах, нам также необходимо использоватьflv.js. Подсчитано, что все знакомы с flvjs (Кружево: что вы думаете об авторе flv.js Bilibili, чья месячная зарплата составляет менее 5000 юаней?), это flv-плеер с открытым исходным кодом на станции B. Согласно официальному введению:

flv.js works by transmuxing FLV file stream into ISO BMFF (Fragmented MP4) segments, followed by feeding mp4 segments into an HTML5 <video> element through Media Source Extensions API.


Как упоминалось выше, flv (Flash Video) — это формат видеопакета,flvjsвсе, что он делает, этоПреобразование flv в фрагментированный формат пакета MP4 (ISO BMFF), затем кормитьMedia Source Extension API, MSE, затем монтируем MSE в<video>В нее можно играть напрямую, ее структура следующая:




flvjs поддерживает извлечение двоичных видеопотоков через HTTP Streaming, WebSocket или пользовательские источники данных. Следующий пример извлекает файлы flvjsnode-media-serverВидеопоток:

    <script src="https://cdn.bootcss.com/flv.js/1.5.0/flv.min.js"></script>
    <video id="video"></video>
    <button id="play">play</button>
    <script>
      if (flvjs.isSupported()) {
        const videoElement = document.getElementById('video');
        const play = document.getElementById('play');

        const flvPlayer = flvjs.createPlayer(
          {
            type: 'flv',
            isLive: true,
            hasAudio: false,
            url: 'ws://localhost:8000/live/test.flv',
          },
          {
            enableStashBuffer: true,
          },
        );

        flvPlayer.attachMediaElement(videoElement);

        play.onclick = () => {
          flvPlayer.load();
          flvPlayer.play();
        };
      }
    </script>

Полный пример кода находится по адресуздесь



Оптимизация RTMP с низкой задержкой

Нажмите конец

ffmpegСторона push-потока может уменьшить задержку push-потока за счет некоторых параметров управления.Основным направлением оптимизации является повышение эффективности кодирования и уменьшение размера буфера.Конечно, иногда приходится жертвовать качеством кода и пропускной способностью. эта статьяТест задержки транскодирования и настройка оптимизации ffmpegНекоторые меры по оптимизации приведены для справки:

  • отключить синхронизацию-прогноз
  • Уменьшить rc-lookahead, но не менее 10, по умолчанию -1
  • Уменьшить количество потоков (например, с 12 до 6)
  • отключить rc-lookhead
  • отключить b-кадры
  • Уменьшить GOP
  • Включите параметр -preset fast/faster/verfast/superfast/ultrafast для x264.
  • Используйте параметр нулевой задержки -tune

node-media-server

NMS также может уменьшить размер буфера иЗакрыть GOP-кэшдля оптимизации задержки.


сторона flvjs

flvjs можно включитьenableStashBufferдля улучшения производительности в реальном времени. В реальном тесте у flvjs может быть феномен «кумулятивной задержки», который можно пройти черезручной поискисправлять.



После некоторого метания оптимальная задержка составляет 400 мс, а спуститься вниз нет возможности (это можно спросить у знакомых студентов). Более того, при подключении к фактическому пушу базовой библиотеки эффект воспроизведения не идеален, и возникают различные зависания и задержки. Из-за ограниченности времени и знаний нам сложно найти конкретную проблему, поэтому мы временно отказались от этого решения.



② JSMpeg и BroadwayJS

Джерри Ку написал«Прямое видео HTML5 (2)»Дал мне много вдохновенияJSMpegиBroadwayjsэти программы

Эти две библиотеки не зависят от механизма воспроизведения видео в браузере, используют чистый JS/WASM для реализации декодера видео, а затем отрисовывают их напрямую через Canvas2d или WebGL.. Broadwayjs в настоящее время не поддерживает голос, JSMpeg поддерживает голос (на основе WebAudio).


После простого теста, по сравнению с RTMP, JSMpeg и BroadwayJS имеют очень низкую задержку, что в основном соответствует нашим требованиям. Ниже приведено краткое введение в использование JSMpeg. Использование Broadwayjs аналогично, и оно будет кратко упомянуто ниже. Их основная обработка заключается в следующем:



Сервер ретрансляции

Поскольку ffmpeg не может отправить поток напрямую в Интернет, нам все равно нужно создать сервер ретрансляции для получения потока отправки видео, а затем перенаправить его на проигрыватель страниц через WebSocket.

ffmpeg поддерживает HTTP, TCP, UDP и другие методы потоковой передачи. HTTP push streaming нам удобнее обрабатывать, потому что это локальная среда, и эти сетевые протоколы не будут иметь существенных различий в производительности.

Давайте создадим HTTP-сервер для приема push-потока./push/:id:

this.server = http
  .createServer((req, res) => {
    const url = req.url || '/'
    if (!url.startsWith('/push/')) {
      res.statusCode = 404
      // ...
      return
    }

    const id = url.slice(6)

    // 禁止超时
    res.connection.setTimeout(0)

    // 转发出去
    req.on('data', (c) => {
      this.broadcast(id, c)
    })

    req.on('end', () => {
      /* ... */
    })
  })
  .listen(port)


затем черезWebSocketЧтобы перенаправить поток, страница может пройтиws://localhost:PORT/pull/{id}Вытяните видеопоток:

/**
 * 使用 webSocket 拉取流
 */
this.wss = new ws.Server({
  server: this.server,
  // 通过 /pull/{id} 拉流
  verifyClient: (info, cb) => {
    if (info.req.url && info.req.url.startsWith('/pull')) {
      cb(true)
    } else {
      cb(false, undefined, '')
    }
  },
})

this.wss.on('connection', (client, req) => {
  const url = req.url
  const id = url.slice(6)

  console.log(`${prefix}new player attached: ${id}`)

  let buzy = false
  const listener = {
    id,
    onMessage: (data) => {
      // 推送
      if (buzy) {
        return
      }

      buzy = true
      client.send(data, { binary: true }, function ack() {
        buzy = false
      })
    },
  }

  this.attachListener(listener)

  client.on('close', () => {
    console.log(`${prefix} player dettached: ${id}`)
    this.detachListener(listener)
  })
})


толкать

Здесь снова используется ffmpeg в качестве примера push:

$ ffmpeg -f avfoundation -r 30 -i "FaceTime HD Camera" -f mpegts -codec:v mpeg1video -an  -bf 0 -b:v 1500k -maxrate 2500k http://localhost:9999/push/test

Небольшое объяснение команды ffmpeg

  • -f mpegts -codec:v mpeg1video -anУказывает использовать формат пакета MPEG-TS и использовать кодирование видео mpeg1, игнорируя звук.
  • -bf 0Декодер JSMpeg временно неправильно обрабатывает B-кадры. Таким образом, они отключают B-кадры. О том, что такое кадр I/B/P, см.статья
  • -b:v 1500k -maxrate 2500kУстановите средний битрейт и максимальный битрейт для push-стриминга. После тестирования скорость кода JSMpeg слишком высока, и экран легко выходит из строя, а массив выходит за границы.

Кроме того, JSMpeg также требует, чтобы ширина видео была кратна 2. ffmpeg может решить эту проблему путем фильтрации (filter) или установки размера видео (-s), но избыточное преобразование будет потреблять определенные ресурсы ЦП:

ffmpeg -i in.mp4 -f mpeg1video -vf "crop=iw-mod(iw\,2):ih-mod(ih\,2)" -bf 0 out.mpg


проигрывание видео

<canvas id="video-canvas"></canvas>
<script type="text/javascript" src="jsmpeg.js"></script>
<script type="text/javascript">
  const canvas = document.getElementById('video-canvas')
  const url = 'ws://localhost:9999/pull/test'
  var player = new JSMpeg.Player(url, {
    canvas: canvas,
    audio: false,
    pauseWhenHidden: false,
    videoBufferSize: 8 * 1024 * 1024,
  })
</script>

API очень простой: выше мы передаем холст в JSMpeg, отключаем звук и устанавливаем больший размер буфера, чтобы справиться с некоторыми колебаниями битрейта.


Посмотреть полный кодздесь



Многопроцессная оптимизация

В реальном тесте задержка видео JSMpeg составляет от 100 до 200 мс. Конечно, это также зависит от таких факторов, как качество видео и производительность терминала.

Ограниченный производительностью терминала и эффективностью декодера, для видеопотоков с высоким средним битрейтом (около 2000k в грубом тесте автора) JSMpeg имеет высокую вероятность иметь размытый экран или доступ к памяти за пределами допустимого диапазона.


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


Поскольку само декодирование является операцией с интенсивным использованием ЦП и выполняется браузером, загрузка ЦП по-прежнему довольно высока (у автора есть один проигрыватель на одной странице, а загрузка ЦП составляет около 16%), и после того, как JSMpeg проигрыватель ненормально аварийно завершает работу, восстановить его будет сложно.

В нашем реальном сценарии приложения страница может воспроизводить несколько видео. Если все видео декодируются и отображаются в основном процессе браузера, работа страницы будет плохой. Так что лучше разделить JSMpeg на Worker,Во-первых, чтобы основной процесс мог реагировать на действия пользователя, а во-вторых, сбои JSMpeg не повлияют на основной процесс..

К счастью, в Worker легко поставить JSMpeg: Worker поддерживает независимые запросы WebSocket, а Canvas пропускаетtransferControlToOffscreen()создание методаOffscreenCanvasОбъект передается в Worker для реализации внеэкранного рендеринга холста.

Первый взглядworker.js, похож на приведенный выше код, в основном добавляя рабочую связь:

importScripts('./jsmpeg.js')

this.window = this

this.addEventListener('message', (evt) => {
  const data = evt.data

  switch (data.type) {
    // 创建播放器
    case 'create':
      const { url, canvas, ...config } = data.data
      this.id = url
      this.player = new JSMpeg.Player(url, {
        canvas,
        audio: false,
        pauseWhenHidden: false,
        videoBufferSize: 10 * 1024 * 1024,
        ...config,
      })

      break

    // 销毁播放器
    case 'destroy':
      try {
        if (this.player) {
          this.player.destroy()
        }
        this.postMessage({ type: 'destroyed' })
      } catch (err) {
        console.log(LOGGER_FREFIX + '销毁失败: ', global.id, err)
        this.postMessage({
          type: 'fatal',
          data: err,
        })
      }

      break
  }
})

// 就绪
this.postMessage({ type: 'ready', data: {} })


Давайте еще раз посмотрим на основной процесс,transferControlToOffscreen()Создайте холст рендеринга вне экрана, чтобы JSMpeg можно было легко перенести в Worker:

const video = document.getElementById('video')
const wk = new Worker('./jsmpeg.worker.js')

wk.onmessage = (evt) => {
  const data = evt.data
  switch (data.type) {
    case 'ready':
      // 创建 OffscreenCanvas 对象
      const oc = video.transferControlToOffscreen()

      wk.postMessage(
        {
          type: 'create',
          data: {
            canvas: oc,
            url: 'ws://localhost:9999/pull/test',
          },
        },
        [oc] // 注意这里
      )

      break
  }
}


Кратко о Broadway.js

Также есть решение, похожее на JSMpeg ------Broadwayjs. этоH.264декодер, черезEmscriptenИнструмент преобразован из декодера Android H.264. Он поддерживает прием потоков H.264 без покрытия, но есть некоторые ограничения: нет поддержкиweighted prediction for P-frames & CABAC entropy encoding.


Пример отправки:

$ ffmpeg -f avfoundation  -r 30 -i "FaceTime HD Camera"  -f rawvideo -c:v libx264 -pix_fmt yuv420p -vprofile baseline -tune zerolatency -coder 0 -bf 0 -flags -loop -wpredp 0 -an  http://localhost:9999/push/test


Пример клиента:

const video = document.getElementById('video')
const url = `ws://localhost:9999/pull/test`
const player = new Player({
  canvas: video,
})
const ws = new WebSocket(url)
ws.binaryType = 'arraybuffer'

ws.onmessage = function (evt) {
  var data = evt.data
  if (typeof data !== 'string') {
    player.decode(new Uint8Array(data))
  } else {
    console.log('get command from server: ', data)
  }
}

увидеть полный кодздесь

После тестирования одинаковое качество и размер видеопотоков JSMpeg и Broadway потребляют одинаковое количество процессора. Но потоковое видео Бродвея не ограничено битрейтом, нет размытия и вылетов. Конечно, для видео высокого качества, преобразования ffmpeg и воспроизведения Broadway потребление ресурсов ошеломляет.


Другие подобные программы:

  • wfs html5 player for raw h.264 streams.


③ Прямой рендеринг YUV

Возвращаясь к началу статьи, на самом деле то, что базовая библиотека получает от WebRTC, — это исходный видеопоток YUV, представляющий собой покадровое изображение, которое не было закодировано и сжато. Решения, представленные выше, имеют дополнительные процессы распаковки и декодирования, а конечным выходом также является видеокадр в формате YUV.Их последний шаг — преобразование этих видеокадров формата YUV в формат RGB и их рендеринг в Canvas..

Можете ли вы напрямую передать исходный видеокадр YUV и отрендерить его прямо на Cavans?? Промежуточный процесс декодирования будет удален, каков эффект? Попробуйте.


Были предыдущие статьи, пытающиеся сделать это:《IVWEB Plays WASM Series - WEBGL YUV Rendering Image Practice》. Давайте сделаем один со ссылкой на него.

Что касается того, чтоYUV, науку не знаю, ищу сам. Размер кадра ЮВ можно рассчитать по такой формуле:(width * height * 3) >> 1, которыйYUV420p1,5 байта на пиксель.

Поэтому нам нужно знать только размер видео, мы можем нарезать видеопоток и разделить видеокадры. Затем создайте новый сервер передачи для приема push-потока.Здесь пустой поток YUV разрезается на покадровые данные изображения и отправляется в браузер:


this.server = http.createServer((req, res) => {
  // ...
  const parsed = new URL('http://host' + url)
  let id = parsed.searchParams.get('id'),
    width = parsed.searchParams.get('width'),
    height = parsed.searchParams.get('height')

  const nwidth = parseInt(width)
  const nheight = parseInt(height)

  const frameSize = (nwidth * nheight * 3) >> 1

  // 按照字节大小切割流
  const stream = req.pipe(new Splitter(frameSize))

  stream.on('data', (c) => {
    this.broadcast(id, c)
  })
  // ...
})

SplitterОбрежьте буфер в соответствии с фиксированным размером в байтах.


Если рендерить ЮВ? может относиться кРендерер JSMpeg WebGL, Визуализатор Broadway.js WebGL. Конкретный рендеринг не будет расширен. Далее будет непосредственно описан Broadway.jsYUVCanvas.jsВозьмите его прямо и используйте его:

const renderer = new YUVCanvas({
  canvas: video,
  type: 'yuv420',
  width: width,
  height: height,
})

// 通过 WebSocket 接收 YUV 帧. 并抽取出 YUV 分量
function onData(data) {
  const ylen = width * height
  const uvlen = (width / 2) * (height / 2)

  renderer.render(
    buff.subarray(0, ylen),
    buff.subarray(ylen, ylen + uvlen),
    buff.subarray(ylen + uvlen, ylen + uvlen + uvlen),
    true
  )
}


Следует отметить, что для рендеринга JSMpeg и Broadway Canvas требуется, чтобы ширина видео была кратна 8. Если это требование не выполняется, будет сообщено об ошибке.《IVWEB Plays WASM Series - WEBGL YUV Rendering Image Practice》занимался этим вопросом.


Наконец, взгляните на пример push-уведомления ffmpeg:

$ ffmpeg -f avfoundation -r 30 -i "FaceTime HD Camera" -f rawvideo -c:v rawvideo -pix_fmt yuv420p "http://localhost:9999/push?id=test&width=320&height=240"

увидеть полный кодздесь



Давайте взглянем на простое сравнение потребления ресурсов. Аппаратура автора 15 Macboook pro, источник видео собран с камеры, разрешение 320х240, формат пикселей уйвы422, частота кадров 30.

Следующая таблицаJвыражатьJSMpeg,BвыражатьBroadway,YвыражатьYUV

CPU (J/B/Y) Память (J/B/Y) Средняя скорость передачи данных (J/B/Y)
ffmpeg 9% / 9% / 5% 12MB / 12MB / 9MB 1600k / 200k / 27000k
сервер 0.6% / 0.6% /1.4% 18MB / 18MB / 42MB N/A
игрок 16% / 13% / 8% 70MB / 200MB / 50MB N/A

Из результатов прямой рендеринг синтеза YUV требует наименьшего количества ресурсов. Поскольку он не сжат, скорость передачи данных также очень высока, но локальная среда не ограничена пропускной способностью, поэтому эта проблема невелика. мы также можем использоватьrequestAnimationFrameБраузер должен планировать скорость воспроизведения, удалять накопленные кадры и поддерживать воспроизведение с низкой задержкой.

Следовать за. Если используется решение yuv, нагрузка на сервер ретрансляции для пересылки Websocket будет больше.



Конец этой статьи

Расширенное чтение