Я пытался отправить видеофайл в 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)
}
})
Вот лишь несколько источников:
- Потоковое видео с HTML 5 через node.js
- потоковое видео в nodejs через модуль фс
- https://github.com/davidgatti/How-to-Stream-Movies-using-NodeJS/blob/master/routes/video.js
- https://github.com/tsukhu/rwd-spa-alljs-app/blob/master/routes/video_streamer.js
- https://tewarid.github.io/2011/04/25/stream-webm-file-to-chrome-using-node.js.html
Очевидно, что этот фрагмент не готов к работе:
- он использует синхронную
statSync()
вместо асинхронной версии, - он не анализирует полную грамматику заголовка
Range
и не обрабатывает ошибки, - жестко задан,
else
поздний завтрак, возможно, избыточен, - 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
ОК, теперь он действительно отправляет частичные ответы ожидаемых размеров. Но
- эти строки более сложные, чем
const end = fileSize-1
- , нам нужно выбрать
preferred_chunksize
с умом. Например, небольшие блоки размером, например, preferred_chunksize=1000
, будут выдавать много запросов и работать заметно медленнее.
Хотя, по крайней мере, с Chrome и Firefox, оригинальная версия кода транслирует видеофайлы довольно хорошо: не вижу чрезмерное использование кэша или памяти, или проблемы со скоростью. И мне не нужно беспокоиться о значении preferred_chunksize
.
Поэтому мой вопрос: стоит ли мне вообще отправлять куски правильного размера (если мне просто нужно отправить видео в <video>
-tag), есть ли какая-нибудь популярная библиотека браузера / клиента js, которая не сможет воспроизвести видеофайл, предоставленный с оригинальным фрагментом? Или есть какие-то скрытые проблемы с подходом этого фрагмента?