Как объединить куски входящего двоичного файла в видео (webm) файловый узел js? - PullRequest
1 голос
/ 30 июня 2019

Я пытаюсь загрузить куски 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})
});

Проблема с приведенным выше кодом заключается в том, что видео не воспроизводится. Не знаю Почему я действительно это делаю?что-то не так, и я также пробовал записываемые потоки, он не работает либо

ОБНОВЛЕНИЕ - я

Вместо отправки больших двоичных объектов я реализовал для отправки двоичного файла, но даже при том, что я сталкиваюсь с проблемой, как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

мой код клиента |Примечание: я пробовал другие способы загрузки блобов, которые я закомментировал

     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)
        //         })
        // })


    };

1 Ответ

2 голосов
/ 05 июля 2019

Я смог заставить это работать, преобразовав кодировку base64 на интерфейс с API FileReader.На бэкэнде создайте новый Buffer из отправленного фрагмента данных и запишите его в файловый поток.Вот некоторые ключевые вещи с моим примером кода:

  1. Я использую fetch, потому что я не хотел вставлять axios.
  2. При использовании fetch вынеобходимо убедиться, что вы используете bodyParser в бэкэнде
  3. Я не уверен, сколько данных вы собираете в своих чанках (т. е. значение продолжительности, передаваемое методу start в MediaRecorderобъект), но вы захотите убедиться, что ваш бэкэнд может обрабатывать размер входящего блока данных. Я установил свой очень высокий на 50MB, но это может не быть необходимым.
  4. Я никогда не закрываюсьявный поток записи ... вы можете сделать это в вашем маршруте /final.В противном случае createWriteStream по умолчанию имеет значение AutoClose, поэтому процесс node сделает это автоматически.

Полный рабочий пример ниже:

Front End:

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));
}; 

Back End:

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});
  }
});
...