Как отправить / «стримить» видеофайл в HTML5 тег видео - PullRequest
0 голосов
/ 30 января 2020

Я пытался отправить видеофайл в HTML5 <video> -tag.
Я нашел фрагмент, который датируется 2010 годом, если не раньше.
Он реплицируется по всему Inte rnet и до сих пор распространяет его, с некоторыми незначительными различиями в стиле кода, именах, используемых Node.js версиях API или библиотеках.
У меня есть некоторые проблемы с этим.
Это фрагмент:

app.get('/video', function(req, res) {
    const path     = 'some_path/video.mp4'
    const stat     = fs.statSync(path)
    const fileSize = stat.size
    const range    = req.headers.range
    if( range ) {
        const parts     = range.replace(/bytes=/, "").split("-")
        const start     = parseInt(parts[0],10);
        const end       = parts[1] ? parseInt(parts[1],10) : fileSize-1
        const chunksize = (end-start)+1
        const file      = fs.createReadStream(path, {start, end})
        const head = {
            'Content-Range' : `bytes ${start}-${end}/${fileSize}`,
            'Accept-Ranges' : 'bytes',
            'Content-Length': chunksize,
            'Content-Type'  : 'video/mp4',
        }
        res.writeHead(206, head)
        file.pipe(res)
    } else {
        const head = {
            'Content-Length': fileSize,
            'Content-Type'  : 'video/mp4',
        }
        res.writeHead(200, head)
        fs.createReadStream(path).pipe(res)
    }
})

Вот лишь несколько источников:

  1. Потоковое видео с HTML 5 через node.js
  2. потоковое видео в nodejs через модуль фс
  3. https://github.com/davidgatti/How-to-Stream-Movies-using-NodeJS/blob/master/routes/video.js
  4. https://github.com/tsukhu/rwd-spa-alljs-app/blob/master/routes/video_streamer.js
  5. https://tewarid.github.io/2011/04/25/stream-webm-file-to-chrome-using-node.js.html

Очевидно, что этот фрагмент не готов к работе:

  1. он использует синхронную statSync() вместо асинхронной версии,
  2. он не анализирует полную грамматику заголовка Range и не обрабатывает ошибки,
  3. жестко задан,
  4. else поздний завтрак, возможно, избыточен,
  5. et c.
* 105 2 * У меня нет проблем с этим. Это код "начального уровня". Это нормально.
Но самое главное, что не работает так, как задумано ... но все еще работает .

Насколько далеко как я знаю , браузеры отправляют запросы на источники тега <video> с заголовком Range в виде

Range: bytes=12345-

и, в случае первоначального запроса, будут

Range: bytes=0-

Итак, строка

const end = parts[1] ? parseInt(parts[1],10) : fileSize-1

идентична

const end = fileSize-1

И, при первоначальном запросе, сервер отправит немалую часть видео, но общий файл. А в случае перемотки видео - действительно большой кусок от запрошенной позиции до конца.

Это определенно не будет работать так, как задумано, если вы запросите файл через Javascript. Вы будете ждать полной загрузки файла, или вам придется каким-то образом отслеживать ход выполнения запроса, что приведет к значительно более сложному клиентскому коду.
Но это работает как прелесть для <video> -tag потому что браузеры справляются с этим от вашего имени.

Мы можем исправить это, рассчитав end следующим образом:

const preferred_chunksize = 10000000 // arbitrary selected value
let end = parts[1] ? parseInt(parts[1],10) : start + preferred_chunksize
if( end > fileSize-1 ) end = fileSize-1

или, учитывая форму заголовка Range, используемую для <video> -tag, даже так:

const preferred_chunksize = 10000000 // arbitrary selected value
let end = start + preferred_chunksize
if( end > fileSize-1 ) end = fileSize-1

ОК, теперь он действительно отправляет частичные ответы ожидаемых размеров. Но

  1. эти строки более сложные, чем
    const end = fileSize-1
    
  2. , нам нужно выбрать preferred_chunksize с умом. Например, небольшие блоки размером, например, preferred_chunksize=1000, будут выдавать много запросов и работать заметно медленнее.
    Хотя, по крайней мере, с Chrome и Firefox, оригинальная версия кода транслирует видеофайлы довольно хорошо: не вижу чрезмерное использование кэша или памяти, или проблемы со скоростью. И мне не нужно беспокоиться о значении preferred_chunksize.

Поэтому мой вопрос: стоит ли мне вообще отправлять куски правильного размера (если мне просто нужно отправить видео в <video> -tag), есть ли какая-нибудь популярная библиотека браузера / клиента js, которая не сможет воспроизвести видеофайл, предоставленный с оригинальным фрагментом? Или есть какие-то скрытые проблемы с подходом этого фрагмента?

1 Ответ

0 голосов
/ 30 января 2020

Сервер не может выбрать диапазон байтов для отправки клиенту. Клиент делает запрос, и сервер ДОЛЖЕН выполнить его. Отправка чего-либо еще является ошибкой, и браузер, скорее всего, не будет воспроизводить видео

Тег видео не отправляет запрос XHR, он имеет доступ к данным ответа до завершения загрузки (так что вы через потоковое API). Браузер может (и часто делает) отменять запросы открытого диапазона (Range: bytes=0-), закрывая сеанс TCP. Когда он делает запрос на диапазон, такой как Range: bytes=12345-, это происходит потому, что он уже проанализировал заголовок видеофайла и знает, что необходимые ему данные находятся по смещению 12345.

...