javascript - ExpressJS 视频流有烦人的音频问题
问题描述
我一直在尝试运行一个 ExpressJS Web 服务器来提供来自我的文件系统的视频。出于某种原因,每当播放视频时,都会有持续的爆裂声,最终(3-10 分钟后)音频完全中断。重新加载页面将带回音频,但不会停止弹出。
我有 2 种不同的方法来提供视频,但在运行时只使用了 1 种。它们都用于此功能:
app.get(`/${VIDTYPE}/:path`, (req, res) => {
let p = decode(req.params.path)
let dir = path.dirname(p)
let name = path.parse(p).name
let ext = '.vtt'
let track = path.resolve(`${dir}/${name}${ext}`)
console.log({track})
res.send(
`<video id="videoplayer" controls width="90%" height="90%">` +
`<source src="/video/${req.params.path}"/>` +
`<track default kind="subtitles" label="en" src="/track/${encode(track)}"/>` +
`</video>`
)
})
第一种方法是从网上抄来的,感觉比较复杂。
app.get("/video/:path", (req, res) => {
let decoded = decode(req.params.path)
console.log("video", {decoded})
let stat = fs.statSync(decoded)
let fileSize = stat.size
let 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(decoded, {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)
}
})
第二种方法要简单得多,而且功能似乎相同。他们俩都有音频问题,质量没有明显差异。
app.get("/video2/:path", (req, res) => {
res.sendFile(decode(req.params.path))
})
完整的代码库将在评论中。所有 mp4 文件都是使用 ffmpeg 从 mkv 转换的,所以这可能会导致问题,但是直接播放时没有一个视频有相同的音频问题。我也将问题隔离到移动设备上。
关于我哪里出错的任何想法?
解决方案
代码库:
const { json } = require('body-parser')
const { dir } = require('console')
const { response } = require('express')
const express = require('express')
const fs = require("fs")
const path = require("path")
const port = 80
const app = express()
const vid_ext = [
'.mp4',
'.webm'
]
const VIDTYPE = "vid"
const DIRTYPE = "dir"
const getAllFiles = function(dirPath) {
console.log({dirPath});
files = fs.readdirSync(dirPath)
arrayOfFiles = [{
path: path.join(dirPath + "/.."),
title: "go up(beta)",
type: DIRTYPE
}]
files.forEach(function(file) {
let p = path.resolve(path.join(dirPath, "/", file))
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles.push({
path: p,
title: file,
type: DIRTYPE
})
} else {
if (vid_ext.includes(path.extname(p))) {
arrayOfFiles.push({
path: p,
title: file,
type: VIDTYPE
})
}
}
})
return arrayOfFiles
}
app.get('/', (request, response) => {
response.redirect(`/${DIRTYPE}/${encode("../stuff")}`);
});
app.get(`/${DIRTYPE}/:path`, (req, res) => {
let decoded = decode(req.params.path)
console.log(DIRTYPE, decoded)
res.send(dirtable(decoded))
})
app.get("/video/:path", (req, res) => {
let decoded = decode(req.params.path)
console.log("video", {decoded})
let stat = fs.statSync(decoded)
let fileSize = stat.size
let 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(decoded, {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)
}
})
app.get("/video2/:path", (req, res) => {
res.sendFile(decode(req.params.path))
})
app.get(`/${VIDTYPE}/:path`, (req, res) => {
let p = decode(req.params.path)
let dir = path.dirname(p)
let name = path.parse(p).name
let ext = '.vtt'
let track = path.resolve(`${dir}/${name}${ext}`)
console.log({track})
res.send(
`<video id="videoplayer" controls width="90%" height="90%">` +
`<source src="/video/${req.params.path}"/>` +
`<track default kind="subtitles" label="en" src="/track/${encode(track)}"/>` +
`</video>`
)
//res.redirect(`/video2/${req.params.path}`)
})
app.get("/track/:path", (req, res) => {
res.sendFile(decode(req.params.path))
})
function dirtable(a) {
let files = getAllFiles(a)
return (
"<table><tbody>" +
files.map(x => `<td>${linkfrom(x)}</td><td>${x.type}</td>`).reduce((acc, x) => acc + `<tr>${x}</tr>`) +
"</tbody></table>"
)
}
function linkfrom(a) {
return `<a href="/${a.type}/${encode(a.path)}">${a.title}</a>`
}
function encode(h) {
return Buffer.from(h).toString('base64')
}
function decode(h) {
return Buffer.from(h, "base64").toString()
}
app.listen(port, console.log(`App Listening to port ${port}`));
推荐阅读
- r - 在这种情况下,您将如何计算 30 个观测值的样本?
- excel - Excel:按年份对多行求和
- java - 裁剪和重新缩放背景是 java 游戏的好解决方案吗?
- regex - regex.exec 无法从电子邮件正文中提取表格
- c++ - 在这种情况下如何从结构中输入字符串数组?
- javascript - 如何在点击时让 div 滚动到页面顶部?
- asp.net - ASP.net MVC 实体数据模型类序列化问题
- python - 在 Tkinter 中为单独的按钮创建矩形
- reactjs - 如何在 Leaflet 中创建具有绘制多边形功能的按钮?
- jquery - Jest - 在特定元素上测试 jquery fadeIn/fadeOut