首页 > 解决方案 > 如何在 requestPictureInPicture 中播放多个视频?

问题描述

requestPictureInPicture 太棒了,但看起来它只适用于 1 个视频。

如何让 requestPictureInPicture 播放多个视频,这样我就可以同时观看两个视频?

基本上这只显示一个视频:

      video
      .requestPictureInPicture()
      .catch(error => {
        console.log(error) // Error handling
      });
      video2
      .requestPictureInPicture()
      .catch(error => {
        console.log(error) // Error handling
      });

https://codepen.io/zecheesy/pen/YzwBJMR

想法:也许我们可以在画布上放两个视频?并让pictureInPicture 同时播放两个视频?https://googlechrome.github.io/samples/picture-in-picture/audio-playlist

我不确定这是否可能。非常喜欢你的帮助!

标签: javascriptvideohtml5-canvashtml5-videopicture-in-picture

解决方案


关于同时打开两个 PictureInPitcure 窗口,规范中有一段专门针对它,他们解释说他们实际上将其保留为实现细节:

具有画中画 API 的操作系统通常将画中画模式限制为仅一个窗口。画中画模式下是否只允许一个窗口由实现和平台决定。但是,由于一个画中画窗口的限制,规范假设给定的文档只能有一个画中画窗口。

当窗口已经在画中画中时有画中画请求时会发生什么将作为实现细节留下:当前画中画窗口可以关闭,画中画请求可能会被拒绝,甚至可以创建两个画中画窗口。无论如何,用户代理必须触发适当的事件以通知网站画中画状态的变化。

所以最好的说法是你不应该期望它同时打开两个窗口。


现在,如果您真的愿意,您确实可以在画布上绘制两个视频,并将此画布传递给画中画窗口,然后将其captureStream()传送到第三个 <video>,尽管这需要使用正确的访问控制来提供两个视频 - Allow-Origin 标头,此外,它要求您的浏览器实际支持 PiP API(当前的 Firefox 具有 PiP 功能,而不是 PiP API)。

这是一个概念证明:

const vids = document.querySelectorAll( "video" );
const btn = document.querySelector( "button" );

// wait for both video has their metadata
Promise.all( [ ...vids ].map( (vid) => {
  return new Promise( (res) => vid.onloadedmetadata = () => res() );
} ) )
.then( () => {
  if( !HTMLVideoElement.prototype.requestPictureInPicture ) {
    return console.error( "Your browser doesn't support the PiP API" );
  }
  btn.onclick = async (evt) => {
    const canvas = document.createElement( "canvas" );
    // both videos share the same 16/9 ratio
    // so in this case it's really easy to draw both on the same canvas
    // to make it dynamic would require more maths
    // but I'll let it to the readers
    const height = 720;
    const width = 1280;
    canvas.height = height * 2; // vertical disposition
    canvas.width = width;
    const ctx = canvas.getContext( "2d" );
    
    const video = document.createElement( "video" );
    video.srcObject = canvas.captureStream();

    let began = false; // rPiP needs video's metadata
    anim();
    await video.play();
    began = true;
    video.requestPictureInPicture();
    
    function anim() {
      ctx.drawImage( vids[ 0 ], 0, 0, width, height );
      ctx.drawImage( vids[ 1 ], 0, height, width, height  );
      // iff we are still in PiP mode
      if( !began || document.pictureInPictureElement === video ) {
        requestAnimationFrame( anim );
      }
      else {
        // kill the stream
        video.srcObject.getTracks().forEach( track => track.stop() );
      }
    }
  }
} );
video { width: 300px }
<button>enter Picture in Picture</button><br>
<video crossorigin  muted controls autoplay loop
  src="https://upload.wikimedia.org/wikipedia/commons/2/22/Volcano_Lava_Sample.webm"></video>
<video crossorigin  muted controls autoplay loop
  src="https://upload.wikimedia.org/wikipedia/commons/a/a4/BBH_gravitational_lensing_of_gw150914.webm"></video>

请注意,由于我确实将视频静音,因此以原始视频看不见的方式滚动会使它们暂停。


推荐阅读