эффективный способ потоковой передачи содержимого холста html5? - PullRequest
0 голосов
/ 10 сентября 2018

Я пытаюсь транслировать содержимое html5 canvas в режиме реального времени, используя websockets и nodejs.

Содержимое холста html5 - это просто видео.

То, что я сделал до сих пор:

Я преобразую холст в blob, затем получаю blob URL и отправляю этот URL на мой сервер nodejs с помощью веб-сокетов.

Я получаю URL-адрес большого двоичного объекта следующим образом:

canvas.toBlob(function(blob) {
   url = window.URL.createObjectURL(blob);
});

URL-адреса BLOB-объектов создаются для каждого видеокадра (если быть точным, 20 кадров в секунду) и выглядят примерно так:

blob:null/e3e8888e-98da-41aa-a3c0-8fe3f44frt53

Затем я получаю URL-адрес большого двоичного объекта с сервера через веб-сокеты, чтобы я мог использовать его, чтобы НАПИСАТЬ его на другой холст, чтобы его могли увидеть другие пользователи.

Я искал, как рисовать на холст из блоб-URL, но не смог найти ничего похожего на то, что я пытаюсь сделать.

Итак, у меня есть следующие вопросы:

  1. Это правильный способ делать то, что я пытаюсь достичь? любой плюсы и минусы будут оценены.

  2. Есть ли другой, более эффективный способ сделать это, или я прав путь

Заранее спасибо.

EDIT:

Я должен был упомянуть, что я не могу использовать WebRTC в этом проекте, и я должен делать все это с тем, что у меня есть.

, чтобы облегчить задачу всем, где я сейчас нахожусь, таким образом я пытался отобразить URL-адреса больших двоичных объектов, которые я упомянул выше, на холсте с помощью веб-сокетов:

websocket.onopen = function(event) {

        websocket.onmessage = function(evt) {
            var val = evt.data;
            console.log("new data "+val);
            var canvas2 = document.querySelector('.canvMotion2');
            var ctx2 = canvas2.getContext('2d');
            var img = new Image();     

            img.onload = function(){
                ctx2.drawImage(img, 0, 0)
            }
            img.src = val;
        };

        // Listen for socket closes
        websocket.onclose = function(event) {

        };

        websocket.onerror = function(evt) {

        };

};

Проблема в том, что когда я запускаю этот код в FireFox, холст всегда пуст / пуст, но я вижу URL-адреса больших двоичных объектов в консоли, что заставляет меня думать, что я делаю неправильно.

и в Google Chrome я получаю Not allowed to load local resource: blob: ошибку.

ВТОРОЕ РЕДАКТИРОВАНИЕ:

Вот где я сейчас нахожусь.

Первый вариант

Я попытался отправить целые объекты через веб-сокеты, и мне это удалось. Однако по какой-то странной причине я не смог прочитать его на стороне клиента!

когда я просматривал консоль моего сервера nodejs, я мог видеть что-то подобное для каждого большого двоичного объекта, отправляемого на сервер:

<buffer fd67676 hdsjuhsd8 sjhjs....

Второй вариант:

Таким образом, вышеприведенная опция не удалась, и я подумал о другом, который превращает каждый кадр холста в base64 (jpeg) и отправляет его на сервер через веб-сокеты, а затем отображает / рисует изображение base64 на холсте на стороне клиента.

Я отправляю на сервер 24 кадра в секунду.

Это сработало. НО холст на стороне клиента, где эти изображения base64 отображаются снова, очень медленный и похож на рисование 1 кадра в секунду. и это проблема, которая у меня есть на данный момент.

Третий вариант:

Я также пытался использовать видео без холста. Итак, используя WebRTC, я получил video Stream как одиночный Blob . но я не совсем уверен, как его использовать и отправить на сторону клиента, чтобы люди могли его увидеть.

ВАЖНО: эта система, над которой я работаю, не является одноранговым соединением. Это всего лишь один из способов потокового вещания, которого я пытаюсь достичь.

1 Ответ

0 голосов
/ 11 сентября 2018

Самый естественный способ потоковой передачи содержимого холста: WebRTC

ОП дал понять, что они не могут его использовать, и это может иметь место для многих, потому что,

  1. Поддержка браузеров все еще не так уж велика.
  2. Это означает, что MediaServer работает (по крайней мере, ICE + STUN / TURN, и, возможно, шлюз, если вы хотите выполнять потоковую передачу более чем одному узлу).

Но все же, если вы можете себе это позволить, все, что вам нужно, чтобы получить MediaStream из вашего элемента canvas, это

const canvas_stream = canvas.captureStream(minimumFrameRate);

и тогда вам просто нужно добавить его в свой RTCPeerConnection:

pc.addTrack(stream.getVideoTracks()[0], stream);

В приведенном ниже примере просто отобразится MediaStream для элемента <video>.

let x = 0;
const ctx = canvas.getContext('2d');
draw();
startStream();

function startStream() {
  // grab our MediaStream
  const stream = canvas.captureStream(30);
  // feed the <video>
  vid.srcObject = stream;
  vid.play();
}
function draw() {
  x = (x + 1) % (canvas.width + 50);
  ctx.fillStyle = 'white';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  ctx.fillStyle = 'red';
  ctx.beginPath();
  ctx.arc(x - 25, 75, 25, 0, Math.PI*2);
  ctx.fill();
  requestAnimationFrame(draw);
}
video,canvas{border:1px solid}
<canvas id="canvas">75</canvas>
<video id="vid" controls></video>

Самый эффективный способ потокового рисования на холсте: потоковые операции рисования.

Еще раз, OP сказал, что они не хотят этого решения, потому что их настройки не совпадают, но могут быть полезны для многих читателей:

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

Но у этого подхода есть свои предостережения:

  • Вам нужно будет написать свой собственный кодер / декодер для передачи команд.
  • В некоторых случаях может быть сложно делиться (например, внешний носитель должен был бы делиться и предварительно загружаться одинаково для всех пиров, а в худшем случае - рисование другого холста, где вам также нужно было бы поделиться своим собственным рисунком. процесс).
  • Возможно, вы захотите избежать интенсивной обработки изображений (например, манипуляции с ImageData) для всех пиров.

Итак, третий, явно менее производительный способ сделать это, как пытался сделать OP:

Загрузка кадров с регулярным интервалом.

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

Вместо этого я сосредоточусь на , почему код OP не работает?

Во-первых, было бы неплохо иметь небольшое напоминание о том, что такое Blob (вещь, которая предоставляется в обратном вызове canvas.toBlob(callback)).

Blob - это специальный объект JavaScript, который представляет двоичные данные, обычно хранящиеся в памяти браузера или, по крайней мере, на диске пользователя, доступные браузеру.
Эти двоичные данные не доступны напрямую для JavaScript. Чтобы получить к нему доступ, нам нужно либо прочитать этот BLOB-объект (через FileReader или объект Response), либо , чтобы создать BlobURI , который является поддельным URI, позволяющим большинству API указывать на двоичный файл. данные, как если бы они хранились на реальном сервере, хотя двоичные данные все еще находятся в выделенной памяти браузера.

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

Все это говорит о том, что то, что должно было быть отправлено в WebSocket, напрямую относится к BLOB-объектам, а не к BlobURI.

Вы должны создавать BlobURI только на стороне потребителей, чтобы они могли загружать эти изображения из двоичных данных Blob, которые теперь находятся в их выделенной памяти.

Сторона излучателя:

canvas.toBlob(blob=>ws.send(blob));

Потребительская сторона:

ws.onmessage = function(evt) {
  const blob = evt.data;
  const url = URL.createObjectURL(blob);
  img.src = url;
};

Но на самом деле, чтобы еще лучше ответить на проблему OP, окончательное решение, которое, вероятно, является лучшим в этом сценарии,

Поделитесь видео потоком, который нарисован на холсте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...