Поток видео с Blob NodeJS - PullRequest
       174

Поток видео с Blob NodeJS

0 голосов
/ 10 октября 2018

Я записываю MediaStream на стороне клиента следующим образом:

handleStream(stream) {
    const ws = new WebSocket('ws://localhost:5432/binary');
    var recorder = new MediaRecorder(stream);
    recorder.ondataavailable = function(event) {
        ws.send(event.data);
    };
    recorder.start();
}

Эти данные принимаются на стороне сервера следующим образом:

const wss = new WebSocket.Server({ port: 5432 });
wss.on('connection', function connection(ws) {
    ws.on('message', function incoming(message) {
        writeToDisk(message, 'video.webm');
    });
});

function writeToDisk(dataURL, fileName) {
    var fileBuffer = new Buffer(dataURL, 'base64');
    fs.writeFileSync(fileName, fileBuffer);
}

Это работает как шарм, но яхочу взять буфер и сделать видео прямой эфир, обслуживаемый серверной стороной.Есть ли способ, как это сделать?

Спасибо за помощь.

1 Ответ

0 голосов
/ 04 апреля 2019

Я уже сделал это здесь .

enter image description here

Вы можете использовать класс MediaRecorder, чтобы разбить видео на куски и отправить их на сервер для широковещания.

this._mediaRecorder = new MediaRecorder(this._stream, this._streamOptions);
this._mediaRecorder.ondataavailable = e => this._videoStreamer.pushChunk(e.data);
this._mediaRecorder.start();
...
this._mediaRecorder.requestData()

Не забудьте перезапустить запись через определенные промежутки времени, чтобы новые клиенты не загружали все видео для подключения к потоку.Кроме того, при смене чанков вы должны заменить <video> на <image> или обновить постер видео, чтобы склеивание прошло гладко.

async function imageBitmapToBlob(img) {
    return new Promise(res => {
        const canvas = document.createElement('canvas');
        canvas.width = img.width;
        canvas.height = img.height;
        canvas.getContext('2d').drawImage(img,0,0);
        canvas.toBlob(res);
    });
}

...

const stream = document.querySelector('video').captureStream();

if(stream.active==true) {

    const track = stream.getVideoTracks()[0];
    const capturer = new ImageCapture(track);
    const bitmap = await imageBitmapToBlob(await capturer.grabFrame());

    URL.revokeObjectURL(this._oldPosterUrl);
    this._video.poster = this._oldPosterUrl = URL.createObjectURL(bitmap);
    track.stop();
}

Вы можете склеивать объекты Blob через их конструктор.В процессе получения нового чанка, не забудьте очистить память для старого видео с помощью URL.revokeObjectURL() и обновить текущее время видео

_updateVideo = async (newBlob = false) => {

    const stream = this._video.captureStream();

    if(stream.active==true) {

        const track = stream.getVideoTracks()[0];
        const capturer = new ImageCapture(track);
        const bitmap = await imageBitmapToBlob(await capturer.grabFrame());

        URL.revokeObjectURL(this._oldPosterUrl);
        this._video.poster = this._oldPosterUrl = URL.createObjectURL(bitmap);
        track.stop();
    }

    let data = null;
    if(newBlob === true) {
        const index = this._recordedChunks.length - 1;
        data = [this._recordedChunks[index]];
    } else {
        data = this._recordedChunks;
    }

    const blob = new Blob(data, this._options);
    const time = this._video.currentTime;

    URL.revokeObjectURL(this._oldVideoUrl);
    const url = this._oldVideoUrl = URL.createObjectURL(blob);

    if(newBlob === true) {
        this._recordedChunks = [blob];
    }

    this._size = blob.size;
    this._video.src = url;
    this._video.currentTime = time;
}

. Вы должны использовать два WebSocket для трансляции видео и два для прослушивания,Один WebSocket передает только видеоблоки, второй - только новые BLOB-объекты с заголовками видео (перезапускайте запись с интервалами).

const blobWebSocket = new WebSocket(`ws://127.0.0.1:${blobPort}/`);
blobWebSocket.onmessage = (e) => {
    console.log({blob:e.data});
    this._videoWorker.pushBlob(e.data);
}

const chunkWebSocket = new WebSocket(`ws://127.0.0.1:${chunkPort}/`);
chunkWebSocket.onmessage = (e) => {
    console.log({chunk:e.data});
    this._videoWorker.pushChunk(e.data);
}

После подключения сервер отправляет клиенту весь текущий видеоблок и начинает динамически отправлять новыекуски клиенту.

const wss = new WebSocket.Server({ port });
let buffer = new Buffer.alloc(0);

function chunkHandler(buf,isBlob=false) {

    console.log({buf,isBlob});

    if(isBlob === true) {
        //broadcast(wss,buf);
        buffer = buf;
    } else {
        const totalLenght = buffer.length + buf.length;
        buffer = Buffer.concat([buffer,buf],totalLenght);
        broadcast(wss,buf);
    }
}

wss.on('connection', function connection(ws) {
    if(buffer.length !== 0) {
        ws.send(buffer);
    }
});
...