Quiet
  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我

bajiu

  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我
Quiet主题
  • NodeJs
  • Electron
  • 流媒体

electron中使用FFmpeg处理直播流

bajiu
前端

2024-07-14 11:15:00

今天突然想起了之前在美术宝用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的数据了,希望以后能有机会重新搞搞视频流和直播流的处理吧,还是得有实际的业务场景才成长得快。

上一篇

在Express中使用JWT

下一篇

NodeJs之事件循环以及I/O多路复用

©2024 By bajiu.