Можно ли добавить поток в качестве источника к элементу html-холста как к элементу html-видео?

Согласно МДН:

Интерфейс HTMLMediaElement добавляет к HTMLElement свойства и методы, необходимые для поддерживают основные возможности, связанные с мультимедиа, которые являются общими для аудио и видео.

HTMLMediaElement.captureStream(). Его можно использовать с элементами <video> и <canvas> для захвата их потока.

И наоборот, можно добавить видеопоток как srcObject к элементу <video>, тогда он его покажет. Возможно ли и для элемента <canvas>?

Можно ли добавить поток в качестве источника к элементу html <canvas>?


person sçuçu    schedule 11.05.2019    source источник
comment
Возможно, повторное использование «ctx.drawImage(video, ...)» делает это, но существует ли абстракция на основе потока, которая отражает метод «captureStream» элемента html «video»   -  person sçuçu    schedule 12.05.2019


Ответы (2)


Нет, ни в одном из Canvas API нет ничего, что могло бы потреблять MediaStream.

API-интерфейсы холста работают только с необработанными пикселями и не содержат никаких декодеров. Вы должны использовать либо объекты javascript, которые могут выполнять это декодирование (например, ImageBitmap), либо HTMLElements.

Таким образом, в случае MediaStream в настоящее время единственным объектом, способным декодировать его видеоконтент, будет HTMLVideoElement, который вы сможете легко рисовать на холсте.


обновление 2021 г.

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

Этот API предлагает новый интерфейс под названием VideoFrame, который будет скоро станет частью CanvasImageSource, что означает, что мы можем использовать его непосредственно с drawImage, texImage2D и везде, где может использоваться такой CanvasImageSource.
href="https://github.com/w3c/mediacapture-transform" rel="nofollow noreferrer">MediaCapture Transform группа W3C разработала MediaStreamTrackProcessor, который возвращает такие видеокадры из видео MediaStreamTrack.

Итак, теперь у нас есть более прямой способ рендеринга MediaStream на холст, который в настоящее время работает только в браузере Chrome с включенным флагом #enable-experimental-web-platform-features...

if( window.MediaStreamTrackProcessor ) {
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  const track = getCanvasTrack(); // MediaStream.getVideoTracks()[0]
  const processor = new MediaStreamTrackProcessor( track );
  const reader = processor.readable.getReader();
  readChunk();
  function readChunk() {
    reader.read().then( ({ done, value }) => {
      // the MediaStream video can have dynamic size
      if( canvas.width !== value.displayWidth || canvas.height !== value.displayHeight ) {
        canvas.width = value.displayWidth;
        canvas.height = value.displayHeight;
      }
      ctx.clearRect( 0, 0, canvas.width, canvas.height );
      // value is a VideoFrame
      ctx.drawImage( value, 0, 0 );
      value.close(); // close the VideoFrame when we're done with it
      if( !done ) {
        readChunk();
      }
    });
  }
}
else {
  console.error("Your browser doesn't support this API yet");
}

// We can't use getUserMedia in StackSnippets
// So here we use a simple canvas as source
// for our MediaStream.
function getCanvasTrack() {
  // just some noise...
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  const img = new ImageData(300, 150);
  const data = new Uint32Array(img.data.buffer);
  const track = canvas.captureStream().getVideoTracks()[0];

  anim();
  
  return track;
  
  function anim() {
    for( let i=0; i<data.length;i++ ) {
      data[i] = Math.random() * 0xFFFFFF + 0xFF000000;
    }
    ctx.putImageData(img, 0, 0);
    if( track.readyState === "live" ) {
      requestAnimationFrame(anim);
    }
  }
  
}
<canvas></canvas>

Как глючный проект (источник), используя камеру в качестве источника.

person Kaiido    schedule 12.05.2019
comment
Вы имеете в виду, что я должен использовать либо объекты javascript, либо HTMLElements, которые могут декодировать из потоков (из MediaStream) - person sçuçu; 13.05.2019
comment
Да, вы должны использовать HTMLVideoElement для декодирования видеокомпонента MediaStream во внешнем интерфейсе. Только из этого вы сможете использовать один из методов контекста холста для его рисования (например, drawImage для 2d-контекста) - person Kaiido; 14.05.2019
comment
Другой мой пост был посвящен показу как потока камеры, так и холста 2d или объектов, нарисованных webgl, в элементе video или canvas с использованием MediaStream. Оказалось неправильный подход. В связи с этим, возможно ли это, если использовать drawImage() контекста canvas для размещения видео с камеры на canvas, накладывая его на другие виртуальные объекты, созданные с помощью canvas 2d или webgl? - person sçuçu; 14.05.2019
comment
Да, просто проверьте ссылку, которую я разместил в своем ответе. - person Kaiido; 14.05.2019
comment
Я не смог найти там ваше имя пользователя, оно там другое и это принятый ответ? - person sçuçu; 14.05.2019
comment
Нет, я не ответил там, но все ответы, которые были опубликованы, показывают, как именно рисовать видео на холсте. То, что это происходит из MediaStream, ничем не отличается: вам нужно воспроизвести его, а затем использовать ctx.drawImage(video,x,y) - person Kaiido; 14.05.2019
comment
Ах, извините, я неправильно понял ваш комментарий. Я видел ответ. Спасибо, теперь я понимаю. Я надеюсь, что то же самое возможно и при использовании контекста canvas webgl. Хотя API контекста webgl будет использоваться везде, где это возможно. Мне просто нужно, чтобы это было возможно, я выясню, если не смогу, я поищу, а затем спрошу еще раз. - person sçuçu; 14.05.2019
comment
Да, вы также можете передать HTMLVideo как текстуру в webgl. Есть много примеров. - person Kaiido; 14.05.2019

@Kaiido прав в том, что нет никакого способа сделать это напрямую. Итак, вот что вы должны сделать:

function onFrame() {
  window.requestAnimationFrame(onFrame);
  canvasContext.drawImage(video, 0, 0);
}
onFrame();

Пара ошибок, с которыми вы столкнетесь:

  • Ваше исходное видео может изменить разрешение в середине потока. Это очень распространено в вызовах WebRTC, когда источник может масштабировать фактическое разрешение в пикселях из-за ограничений пропускной способности или ЦП. Один из способов обойти это — проверять размер видео в каждом кадре, который вы рисуете, и соответствующим образом масштабировать его на холсте.
  • Этот цикл кадров не работает со скоростью, когда вкладка не имеет фокуса. Если вы также полагаетесь на captureStream из этого холста, из-за политик регулирования он не будет работать, если вкладка не имеет фокуса.
  • Буфер холста не обновляется, когда вкладка не имеет фокуса, поэтому, даже если вы обойдете проблему с таймером с помощью узла аудиоскрипта или чего-то еще, это не сработает, если вы также хотите использовать captureStream из холста.
  • Помните, что здесь нет «генлока». Для каждого кадра, который вы копируете на холст, в видео может пройти произвольное количество кадров (возможно, ноль!) Это может не иметь значения для вашей ситуации.
person Brad    schedule 12.05.2019
comment
Хорошо, что исходное разрешение может меняться, однако существует событие onresize, которое вы можете прослушивать на потребителе вместо проверки каждого кадра: jsfiddle.net/df83jbyx Кроме того, даже если это хорошие моменты, я не думаю, что OP будет использовать captureStream, если они правильно поняли, они говорили об этом только для того, чтобы заявить, что API MediaElement и CanvasElement имеют отношения с MediaStream и, таким образом, надеялись, что они смогут использовать его напрямую. - person Kaiido; 13.05.2019