今天突然想起了之前在美术宝用electron
处理直播流本来计划下一步操作,在直播内不光进行流媒体的劫持,还可以进行数据流的修改,然后讲修改过后的数据流发给服务端,劫持和修改以前做过了,写就写个直播流获取和转发的小应用吧。
前端
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>实时直播流</title>
</head>
<body>
<h1>本地摄像头直播流</h1>
<video id="video" width="640" height="480" autoplay></video>
<button id="start-button">开始直播</button>
<script>
const { ipcRenderer } = require('electron');
const videoElement = document.getElementById('video');
// 获取本地摄像头流
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
videoElement.srcObject = stream;
// 点击按钮时开始直播
document.getElementById('start-button').addEventListener('click', () => {
ipcRenderer.send('start-live-stream', stream);
});
})
.catch(error => {
console.error('获取摄像头流失败:', error);
});
</script>
</body>
</html>
在 main.js
中处理直播流并将其发送到服务器.
const ffmpeg = require('fluent-ffmpeg');
const { ipcMain } = require('electron');
ipcMain.on('start-live-stream', (event, stream) => {
const serverEndpoint = 'http://localhost:3000/api/stream';
// 使用 FFmpeg 处理直播流
ffmpeg(stream)
.inputFormat('mjpeg') // 输入格式,取决于摄像头流的格式
.outputFormat('rawvideo') // 输出格式设为 rawvideo
.videoCodec('rawvideo') // 使用原始视频编码
.format('yuv420p') // 指定 YUV 420 格式
.on('start', (commandLine) => {
console.log('FFmpeg process started with command:', commandLine);
})
.on('progress', (progress) => {
console.log('Processing:', progress);
})
.on('end', () => {
console.log('直播流处理完成');
event.reply('processing-done', '直播流处理完成!');
})
.on('error', (err) => {
console.error('发生错误:', err);
event.reply('processing-error', '直播流处理失败!');
})
.pipe(axios.post(serverEndpoint, {
responseType: 'stream', // 设定响应为流
}), { end: true }); // 结束后停止 FFmpeg
});
服务端
server.js
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const PORT = 3000;
// 创建用于保存 YUV 数据的目录
if (!fs.existsSync('uploads')) {
fs.mkdirSync('uploads');
}
// 接收流数据的路由
app.post('/api/stream', (req, res) => {
const filePath = path.join(__dirname, 'uploads', 'stream.yuv');
// 创建写入流
const writeStream = fs.createWriteStream(filePath);
req.pipe(writeStream); // 将请求流连接到写入流
writeStream.on('finish', () => {
console.log('接收到 YUV 数据并保存成功!');
res.status(200).send('YUV 数据接收成功!');
});
writeStream.on('error', (err) => {
console.error('保存 YUV 数据时发生错误:', err);
res.status(500).send('服务器错误,无法保存数据。');
});
});
// 启动服务器
app.listen(PORT, () => {
console.log(`服务器正在运行,端口:${PORT}`);
});
理论上来说,这样我们就可以在服务点保存到对应的
YUV420P
的数据了,希望以后能有机会重新搞搞视频流和直播流的处理吧,还是得有实际的业务场景才成长得快。