如何将传入的二进制块连接到视频(webm)文件节点js中?

Posted

技术标签:

【中文标题】如何将传入的二进制块连接到视频(webm)文件节点js中?【英文标题】:How to concat chunks of incoming binary into video (webm) file node js? 【发布时间】:2019-11-11 13:03:31 【问题描述】:

我正在尝试将 base64 块上传到节点 js 服务器并将这些块保存到一个文件中

let chunks = [];

app.post('/api', (req, res) => 
    let blob = req.body;
    //converting chunks of base64 to buffer
    chunks.push(Buffer.from(blob, 'base64'));
    res.json(gotit:true)

);

app.post('/finish', (req, res) => 
    let buf = Buffer.concat(chunks);
    fs.writeFile('finalvideo.webm', buf, (err) => 
        console.log('Ahh....', err)
    );
    console.log('SAVED')
    res.json(save:true)
);

上面代码的问题是视频无法播放,我不知道为什么我真的做错了什么,我也尝试过可写流它也不起作用

更新 - 我

我没有发送 blob,而是实现了发送二进制文件,但即使我面临TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object. 之类的问题

client.js

 postBlob = async blob => 
       let arrayBuffer = await new Response(blob).arrayBuffer();
        let binary = new Uint8Array(arrayBuffer)
        console.log(binary) // logging typed Uint8Array
        axios.post('/api',binary)
            .then(res => 
                console.log(res)
            )

    ;

server.js

 let chunks = [];

    app.post('/api', (req, res) => 
        let binary = req.body;



        let chunkBuff = Buffer.from(binary) // This code throwing Error
        chunks.push(chunkBuff);

        console.log(chunkBuff)

         res.json(gotit:true)

    );

//Somehow combine those chunks into one file
app.post('/finish', (req, res) => 
    console.log('Combinig the files',chunks.length);

     let buf = Buffer.concat(chunks);

    console.log(buf) //empty buff
    fs.writeFile('save.webm', buf, (err) => 
        console.log('Ahh....', err)
    );

    res.json(save:true)
);

更新 - II

我能够接收二进制块并附加到流中,但在最终视频中,只有第一个块正在播放,我不知道其他块发生了什么,视频结束了。

代码

const writeMyStream = fs.createWriteStream(__dirname+'/APPENDED.webm', flags:'a', encoding:null);

app.post('/api', (req, res) => 
    let binary = req.body;
 let chunkBuff = Buffer.from(new Uint8Array(binary));
    writeMyStream.write(chunkBuff);
res.json(gotit:true)

);

更新 - III

我的客户代码 |注意:我已经尝试过其他方式来上传已注释掉的 blob

     customRecordStream = stream => 



            let recorder = new MediaStreamRecorder(stream);
            recorder.mimeType = 'video/webm;codecs=vp9';


            recorder.ondataavailable = this.postBlob 
            recorder.start(INT_REC)

        ;

 postBlob = async blob => 


        let arrayBuffer = await new Response(blob).arrayBuffer();
        let binary = new Uint8Array(arrayBuffer)


            axios.post('/api',binary)
                .then(res => 
                    console.log(res)
                )
        // let binaryUi8 = new Uint8Array(arrayBuffer);
        // let binArr = Array.from(binaryUi8);
        // // console.log(new Uint8Array(arrayBuffer))
        //
        // console.log(blob);


        // console.log(binArr)

        // let formData = new FormData();
        // formData.append('fname', 'test.webm')
        // formData.append("file", blob);
        //
        // console.log(formData,'Checjk Me',blob)
        // axios(
        //     method:'post',
        //     url:'/api',
        //     data:formData,
        //     config:  headers: 'Content-Type': 'multipart/form-data' 
        // ).then(res => 
        //     console.log(res,'FROM SERBER')
        //
        // )
        //
        //
        //     .then(res => 
        //         console.log(res)
        //     )

        // this.blobToDataURL(blob, (blobURL) => 
        //
        //     axios.post('/api',blob:blobURL)
        //         .then(res => 
        //             console.log(res)
        //         )
        // )


    ;

【问题讨论】:

为什么base-64编码这个?很有可能,您的编码/解码分段错误。只需发送二进制数据,效率会高得多! 发送二进制数据听起来不错,但我可以将这些二进制文件合并到一个视频文件中吗? 是的,最好的办法就是将流写入文件。但是如果你不能这样做,你也可以追加到一个文件,或者写一系列的缓冲区等等。 如果你发布一个例子会很棒 这有 6 个不同的部分。贴一个具体的例子。例如,发布一个问题,询问如何将二进制文件放入服务器,并显示您目前拥有的代码。然后,发布一个关于在 Node.js 中接收它的问题,并显示到目前为止的代码。 【参考方案1】:

我可以通过在前端使用FileReader api 转换为base64 编码来实现这一点。在后端,从发送的数据块中创建一个新的Buffer,并将其写入文件流。我的代码示例中的一些关键内容:

    我正在使用fetch,因为我不想加入axios。 使用fetch时,必须确保在后端使用bodyParser 我不确定您在块中收集了多少数据(即传递给MediaRecorder 对象上的start 方法的持续时间值),但您需要确保您的后端可以处理传入的数据块的大小。我将我的设置得很高50MB,但这可能没有必要。 我从不明确关闭写入流...您可能会在您的/final 路由中执行此操作。否则,createWriteStream 默认为 AutoClose,因此node 进程将自动执行此操作。

下面的完整工作示例:

前端:

const mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
let mediaRecorder;
let sourceBuffer;

function customRecordStream(stream) 
  // should actually check to see if the given mimeType is supported on the browser here.
  let options =  mimeType: 'video/webm;codecs=vp9' ;
  recorder = new MediaRecorder(window.stream, options);
  recorder.ondataavailable = postBlob 
  recorder.start(INT_REC)
;

function postBlob(event)
  if (event.data && event.data.size > 0) 
    sendBlobAsBase64(event.data);
  


function handleSourceOpen(event) 
  sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
 

function sendBlobAsBase64(blob) 
  const reader = new FileReader();

  reader.addEventListener('load', () => 
    const dataUrl = reader.result;
    const base64EncodedData = dataUrl.split(',')[1];
    console.log(base64EncodedData)
    sendDataToBackend(base64EncodedData);
  );

  reader.readAsDataURL(blob);
;

function sendDataToBackend(base64EncodedData) 
  const body = JSON.stringify(
    data: base64EncodedData
  );
  fetch('/api', 
    method: 'POST',
    headers: 
      'Content-Type': 'application/json',
    ,
    body
  ).then(res => 
    return res.json()
  ).then(json => console.log(json));
; 

后端:

const fs = require('fs');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const server = require('http').createServer(app);

app.use(bodyParser.urlencoded( extended: true ));
app.use(bodyParser.json( limit: "50MB", type:'application/json'));

app.post('/api', (req, res) => 
  try 
    const  data  = req.body;
    const dataBuffer = new Buffer(data, 'base64');
    const fileStream = fs.createWriteStream('finalvideo.webm', flags: 'a');
    fileStream.write(dataBuffer);
    console.log(dataBuffer);
    return res.json(gotit: true);
   catch (error) 
    console.log(error);
    return res.json(gotit: false);
  
);

【讨论】:

感谢您的详细回答,但问题是我用来记录 blob 的库是问题MediaStreamRecorder,我删除了它并用纯 javascript 构建它,它正在工作 太棒了!真高兴你做到了。下次,请务必包含您可能使用的任何库,以便您获得最全面的答案来解决您的问题。 我继续添加了使用 MediaRecorder 的其余客户端代码,以便其他人也可以从中受益。 @willascend,从主题来看,我假设对于单个视频,我们在特定的时间间隔将多个块文件传输到服务器,并在服务器端合并这些文件以制作组合视频文件。我能够在服务器端发送文件,但面临在服务器端的 java 应用程序中合并文件的问题。我查看了您的后端代码,并意识到我们只调用它一次。例如,我们正在录制 X 人的视频,所以我们假设获取块 C1、C2、C3 并将其推送到服务器上。 在服务器端,我们必须对这些 C1、C2、C3 进行俱乐部,并且必须准备 X 人的组合视频文件。如果您能在这个问题上指导我,那将很有帮助,因为您的服务器端代码不包含文件合并。在这里,我附上我的客户端和服务器端代码供您参考。 @nane gist.github.com/geekdiv/1d4e70ae08e8178c4ea2316a7dc5d392【参考方案2】:

灵感来自@willascend 答案:

后端:

app.use(express.raw());
app.post('/video-chunck', (req, res) => 
    fs.createWriteStream('myvideo.webm',  flags: 'a' ).write(req.body);
    res.sendStatus(200);
);

前端:

mediaRecorder.ondataavailable = event => 
    if (event.data && event.data.size > 0) 
        fetch(this.serverUrl + '/video-chunck', 
            method: 'POST',
            headers: 'Content-Type': 'application/octet-stream',
            body: event.data
        );
    
;

我的 express 版本是 4.17.1

【讨论】:

【参考方案3】:

我今天遇到了同样的问题 作为后端的解决方案,我使用了 fs.appendfile

fs.appendFile(Path, rawData, function (err) 
        if (err) throw err;
        console.log('Chunck Saved!');
    )

【讨论】:

以上是关于如何将传入的二进制块连接到视频(webm)文件节点js中?的主要内容,如果未能解决你的问题,请参考以下文章

如何将滑块连接到 MFC 中的变量

如何创建 WebM 视频文件?

如何在 iOS 视频播放器中播放 webm 文件

webm 和 mp4 十六进制文件头持续时间

JavaCV音视频开发宝典:录制vp8和vp9编码的webm格式视频,以mp4转webm为例

JavaCV音视频开发宝典:录制vp8和vp9编码的webm格式视频,以mp4转webm为例