node后端使用express搭建两个接口
一个是获取视频流,一个是上传视频并进行切片
实现的效果:
上传视频接口
import express from "express";
import fs, { existsSync } from "fs";
import multer from "multer";
import { nanoid } from "nanoid";
import ffmpeg from "fluent-ffmpeg";
import path from "path";
import cors from 'cors';
// 创建一个服务器,监听 3300 端口
const server = express();
server.listen(3300);
console.log("Server started.");
server.use(cors())
/*
* 定义一个处理上传表单文件
*/
const copeUpload = multer({
dest: "uploads-temp/",
filename: function (req, file, cb) {
const uniqueSuffix = nanoid();
cb(null, file.fieldname + "-" + uniqueSuffix);
},
}).single("video");
// 定义上传的 API 路由,并且使用上面的中间件
server.post("/upload", copeUpload, function (req, res, next) {
const tempFilePath = path.resolve(req.file.path); // 视频上传后的临时文件位置
const videoId = nanoid(); // 为视频资源创建唯一 ID
const storageDirectory = path.resolve("storage", videoId); //为视频创建储存位置
fs.mkdirSync(storageDirectory);
ffmpeg(tempFilePath)
.videoCodec("libx264")
.audioCodec("aac")
.addOption("-hls_time", 10)
.addOption("-hls_segment_type", "mpegts")
.addOption("-hls_list_size", 0)
.format("hls")
.addOption("-max_muxing_queue_size", 1024)
.output(`${storageDirectory}/video.m3u8`)
.on("start", function () {
console.log("开始为视频切片");
})
.on("end", function () {
fs.rmSync(tempFilePath); // 删除上传的临时文件
console.log("切片完成");
})
.on("error", function (err) {
fs.rmSync(tempFilePath); // 删除上传的临时文件
console.error("切片失败:", err);
})
.run();
res.json(`http://localhost:3300/play/${videoId}/video.m3u8`); //返回播放地址
});
获取播放接口
server.get("/play/:videoId/:filename", (req, res) => {
const videoId = req.params["videoId"]; // 从 URL 中获取视频 ID
const storageDirectory = path.resolve("storage", videoId); // 视频切片和清单的储存位置
if (!existsSync(storageDirectory)) {
// 若目标视频记录不存在则返回 404
res.status(404).send();
}
const filename = req.params["filename"]; //请求的文件
const filepath = path.join(storageDirectory, filename);
if (!existsSync(filepath)) {
// 若目标文件不存在则返回 404
res.status(404).send();
}
const data = fs.readFileSync(filepath); // 读取目标文件
res.send(data);
});
前端使用视频播放器
<label for="m3u8-url">请输入视频的m3u8地址:</label>
<input type="text" id="m3u8-url" name="m3u8-url" placeholder="例如:https://example.com/video.m3u8" />
<!-- 视频播放器 -->
<video id="my-video" class="video-js" controls preload="auto" width="640" height="360" data-setup="{}">
<source src="" type="application/x-mpegURL" />
</video>
写一个方法进行视频流播放
function playVideo () {
// 拿到输入框的m3u8地址
var m3u8Url = document.getElementById("m3u8-url").value;
console.log("🚀 ~ playVideo ~ m3u8Url:", m3u8Url)
// 设置视频源为输入的m3u8地址
var videoPlayer = videojs("my-video");
videoPlayer.src({
src: m3u8Url,
type: "application/x-mpegURL",
});
// 播放视频
videoPlayer.play();
}
使用的是video.js进行播放
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
前端上传视频
<form id="uploadForm" method="POST" enctype="multipart/form-data">
<input type="file" name="video" accept="video/*" />
<button type="submit">上传</button>
</form>
<div id="response"></div>
<script>
// 获取表单元素
const form = document.getElementById("uploadForm");
// 监听表单提交事件
form.addEventListener("submit", function (event) {
event.preventDefault(); // 阻止默认提交行为
// 创建 FormData 对象,用于将表单数据发送到服务器
const formData = new FormData(form);
// 发送表单数据到服务器
fetch("http://localhost:3300/upload", {
method: "POST",
body: formData,
})
.then((response) => response.text()) // 将响应转换为文本格式
.then((data) => {
// 将服务器返回的文本数据显示在页面上
document.getElementById("response").innerText =
"上传成功,视频地址是:" + data;
})
.catch((error) => {
console.error("请求错误:", error);
});
});
</script>