【问题标题】:How to concat chunks of incoming binary into video (webm) file node js?如何将传入的二进制块连接到视频(webm)文件节点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 中接收它的问题,并显示到目前为止的代码。

标签: node.js buffer fs getusermedia arraybuffer


【解决方案1】:

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

  1. 我正在使用fetch,因为我不想加入axios
  2. 使用fetch时,必须确保在后端使用bodyParser
  3. 我不确定您在块中收集了多少数据(即传递给MediaRecorder 对象上的start 方法的持续时间值),但您需要确保您的后端可以处理传入的数据块的大小。我将我的设置得很高50MB,但这可能没有必要。
  4. 我从不明确关闭写入流...您可能会在您的/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!');
        })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-29
      • 2011-10-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多