画中画

W3C 工作草案,

更多关于本文档的详细信息
此版本:
https://www.w3.org/TR/2025/WD-picture-in-picture-20250820/
最新发布版本:
https://www.w3.org/TR/picture-in-picture/
编辑草案:
https://w3c.github.io/picture-in-picture/
之前版本:
历史记录:
https://www.w3.org/standards/history/picture-in-picture/
反馈:
GitHub
编辑:
(Google LLC)
前任编辑:
(Google LLC)
Web 平台测试:
permissions-policy/
picture-in-picture/

摘要

本规范提供 API,允许网站创建一个始终置顶的悬浮视频窗口,使用户在与其他内容网站或设备上的应用交互时,能够持续观看媒体内容。

本文档状态

本节描述了本文档在发布时的状态。当前 W3C 发布的文档列表及该技术报告的最新修订版可在 W3C 标准与草案索引中查阅。

欢迎对本规范提出反馈和意见。关于本规范的讨论,优先使用 GitHub Issues。此外,您也可以发送邮件至 Media 工作组的邮件列表 public-media-wg@w3.org存档)。 本草案突出了一些仍待工作组讨论的未决问题,这些问题的结果尚未决定,也未判定其是否有效。

本文档由 Media 工作组 以工作草案形式发布,采用 推荐流程。 本文档旨在成为 W3C 推荐标准。

作为工作草案发布并不意味着 W3C 及其成员的认可。

这是一个草案文档,可能会随时被更新、替换或废止。除作为正在进行的工作外,不适合引用本文件。

本文档由遵循 W3C 专利政策 的组织制定。 W3C 维护着一个公开的专利披露列表,列出了与本组织交付成果相关的任何专利披露;该页面还包括披露专利的说明。任何知晓某项专利且认为其中包含必要权利要求的个人,必须根据《W3C 专利政策》第6节进行信息披露。

本文档受 2025年8月18日 W3C 流程文件 管辖。

1. 简介

本节为非规范性内容。

许多用户希望在与设备上的其他内容、站点或应用程序交互时,继续观看媒体。常见的用户界面工具是画中画(PiP),视频被包含在一个始终位于其他窗口之上的独立小窗口中。即使用户代理不可见,该窗口仍保持可见。画中画是桌面和移动操作系统中常见的平台级功能。

本规范扩展了 HTMLVideoElement, 允许网站通过暴露以下属性来启动和控制此行为:

2. 示例

2.1. 添加自定义画中画按钮

<video id="video" src="https://example.com/file.mp4"></video>

<button id="togglePipButton"></button>

<script>
  const video = document.getElementById("video");
  const togglePipButton = document.getElementById("togglePipButton");

  // 如果不支持或禁用画中画,隐藏按钮。
  togglePipButton.hidden =
    !document.pictureInPictureEnabled || video.disablePictureInPicture;

  togglePipButton.addEventListener("click", async () => {
    // 如果没有元素在画中画中,请求视频进入画中画模式,否则退出画中画。
    try {
      if (document.pictureInPictureElement) {
        await document.exitPictureInPicture();
      } else {
        await video.requestPictureInPicture();
      }
    } catch (err) {
      // 视频未能进入/退出画中画模式。
    }
  });
</script>

2.2. 监控视频画中画的变化

<video id="video" src="https://example.com/file.mp4"></video>

<script>
  const video = document.getElementById("video");

  video.addEventListener("enterpictureinpicture", (event) => {
    // 视频进入画中画模式。
    const pipWindow = event.pictureInPictureWindow;
    console.log(`画中画窗口宽度: ${pipWindow.width}`);
    console.log(`画中画窗口高度: ${pipWindow.height}`);
  });

  video.addEventListener("leavepictureinpicture", () => {
    // 视频退出画中画模式。
  });
</script>

2.3. 根据画中画窗口大小更改调整视频大小

<video id="video" src="https://example.com/file.mp4"></video>

<button id="pipButton"></button>

<script>
  const video = document.getElementById("video");
  const pipButton = document.getElementById("pipButton");

  pipButton.addEventListener("click", async () => {
    try {
      await video.requestPictureInPicture();
    } catch (error) {
      // 视频未能进入画中画模式。
    }
  });

  video.addEventListener("enterpictureinpicture", (event) => {
    // 视频进入画中画模式。
    const pipWindow = event.pictureInPictureWindow;
    updateVideoSize(pipWindow.width, pipWindow.height);
    pipWindow.addEventListener("resize", onPipWindowResize);
  });

  video.addEventListener("leavepictureinpicture", (event) => {
    // 视频退出画中画模式。
    const pipWindow = event.pictureInPictureWindow;
    pipWindow.removeEventListener("resize", onPipWindowResize);
  });

  function onPipWindowResize(event) {
    // 画中画窗口已调整大小。
    const { width, height } = event.target;
    updateVideoSize(width, height);
  }

  function updateVideoSize(width, height) {
    // TODO: 根据画中画窗口的宽度和高度调整视频大小。
  }
</script>

3. 概念

3.1. 内部槽位定义

一个用户代理拥有:

  1. 一个活动画中画会话的发起者列表,包含零个或多个来源,该列表初始为空。

注意:如果用户代理支持多个画中画窗口,则该列表允许重复。

如果在活动画中画会话的发起者中的任何来源与原始来源同源域,则该来源被认为有一个活动的画中画会话。

3.2. 请求画中画

当带有video请求画中画算法被调用时, 用户代理必须运行以下步骤:

  1. 如果画中画支持false,则抛出NotSupportedError并中止这些步骤。

  2. 如果文档不被允许使用名为受政策控制的功能"picture-in-picture",则抛出SecurityError并中止这些步骤。

  3. 如果videoreadyState属性为HAVE_NOTHING,则抛出InvalidStateError并中止这些步骤。

  4. 如果video没有视频轨道,则抛出InvalidStateError并中止这些步骤。

  5. 如果videodisablePictureInPicturetrue,则用户代理可以抛出InvalidStateError并中止这些步骤。

  6. 如果pictureInPictureElementnull,且相关全局对象this不具有瞬时激活,则抛出NotAllowedError并中止这些步骤。

  7. 如果videopictureInPictureElement,则中止这些步骤。

  8. pictureInPictureElement设置为video

  9. 画中画窗口设为一个与pictureInPictureElement关联的PictureInPictureWindow新实例。

  10. 相关设置对象来源附加到活动画中画会话的发起者

  11. 排队一个任务触发一个事件,该事件名为enterpictureinpicture,使用PictureInPictureEvent,其bubbles属性初始化为truepictureInPictureWindow属性初始化为画中画窗口

  12. 如果pictureInPictureElement全屏元素,建议退出全屏

建议不要同时在页面和画中画窗口中渲染视频帧,但如果渲染,则必须保持同步。

当视频在画中画模式中播放时,状态应过渡为类似于内嵌播放。这意味着事件应该同时触发,调用方法应具有相同的行为,等等。然而,当视频元素进入与画中画不兼容的状态时,用户代理可能会退出画中画模式。

应用于video的样式(例如不透明度、可见性、变换等)不得应用于画中画窗口。其纵横比基于视频大小。

还建议画中画窗口具有最大和最小尺寸。例如,它可以被限制为屏幕尺寸的四分之一到二分之一之间。

3.3. 退出画中画

当调用退出画中画算法时, 用户代理必须执行以下步骤:

  1. 如果pictureInPictureElementnull,则抛出InvalidStateError并中止这些步骤。

  2. 使用与pictureInPictureElement关联的画中画窗口,运行关闭窗口算法

  3. 排队一个任务,以触发一个事件,该事件名为leavepictureinpicture,使用PictureInPictureEvent,并将其bubbles属性初始化为true,其pictureInPictureWindow属性初始化为与pictureInPictureElement关联的画中画窗口

  4. 取消设置pictureInPictureElement

  5. 列表项中删除一个匹配相关设置对象来源,从活动画中画会话的发起者列表中移除。

不建议在调用退出画中画算法时更改视频播放状态。如果由网站发起,网站应控制体验。然而,用户代理可以暴露画中画窗口控件以更改视频播放状态(例如暂停)。

作为卸载文档清理步骤的一部分,运行退出画中画算法

3.4. 禁用画中画

某些页面可能希望禁用视频元素的画中画模式;例如,它们可能希望在某些情况下阻止用户代理建议画中画上下文菜单。为了支持这些用例,视频元素的内容属性列表中添加了一个新属性disablePictureInPicture

disablePictureInPictureID属性必须反映相同名称的内容属性。

如果视频元素上存在disablePictureInPicture属性,用户代理可以阻止视频元素在画中画模式下播放或阻止任何相关的用户界面。

disablePictureInPicture属性被添加到video元素时,用户代理可以运行以下步骤:

  1. 拒绝requestPictureInPicture()方法返回的所有待处理承诺,并抛出InvalidStateError

  2. 如果videopictureInPictureElement,运行退出画中画算法

3.5. 与全屏的交互

建议在设置pictureInPictureElement全屏标志时,运行退出画中画算法

3.6. 与远程播放的交互

[Remote-Playback]规范定义了本地播放设备本地播放状态。对于画中画,播放是本地的,无论它是在页面中播放还是在画中画中播放。

3.7. 与媒体会话的交互

该API需要与[MediaSession] API一起使用,以便定制画中画窗口中的可用控件。

3.8. 与页面可见性的交互

当设置了pictureInPictureElement时,即使文档不在焦点或处于隐藏状态,画中画窗口也必须是可见的。用户代理应提供一种方式让用户手动关闭画中画窗口。

用户代理不得将画中画窗口的可见性考虑在内,用以确定系统可见性状态是否已更改。

3.9. 单个画中画窗口

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

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

4. API

4.1. 扩展 HTMLVideoElement

partial interface HTMLVideoElement {
  [NewObject] Promise<PictureInPictureWindow> requestPictureInPicture();

  attribute EventHandler onenterpictureinpicture;
  attribute EventHandler onleavepictureinpicture;

  [CEReactions] attribute boolean disablePictureInPicture;
};

requestPictureInPicture() 方法在调用时,必须返回一个新的承诺promise并并行运行以下步骤:

  1. video为调用该方法的视频元素。

  2. 运行请求画中画算法,使用video

  3. 如果上一步抛出异常,使用该异常拒绝promise,并中止这些步骤。

  4. 使用与pictureInPictureElement关联的画中画窗口解析promise

4.2. 扩展 Document

partial interface Document {
  readonly attribute boolean pictureInPictureEnabled;

  [NewObject] Promise<undefined> exitPictureInPicture();
};

pictureInPictureEnabled 属性的getter必须返回true,如果画中画支持true,且this允许使用由属性名称picture-in-picture指示的功能,否则返回false

画中画支持false,如果存在禁用它的用户偏好或平台限制。否则为true

exitPictureInPicture() 方法在调用时,必须返回一个新的承诺promise并并行运行以下步骤:

  1. 运行退出画中画算法

  2. 如果上一步抛出异常,使用该异常拒绝promise,并中止这些步骤。

  3. 解析promise

4.3. 扩展 DocumentOrShadowRoot

partial interface mixin DocumentOrShadowRoot {
  readonly attribute Element? pictureInPictureElement;
};

pictureInPictureElement 属性的getter必须运行以下步骤:

  1. 如果this是一个影子根,并且它的宿主没有连接,则返回null并中止这些步骤。

  2. candidate设为重定向画中画元素对this的结果。

  3. 如果candidatethis在相同的中,返回candidate并中止这些步骤。

  4. 返回null

4.4. 接口 PictureInPictureWindow

[Exposed=Window]
interface PictureInPictureWindow : EventTarget {
  readonly attribute long width;
  readonly attribute long height;

  attribute EventHandler onresize;
};

一个 PictureInPictureWindow 实例表示与 HTMLVideoElement 关联的 画中画窗口。当实例化时,PictureInPictureWindow 实例的 state 被设置为 opened

当调用包含 PictureInPictureWindow 实例的 关闭窗口算法 时,其 state 被设置为 closed

如果 stateopened,则 width 属性必须返回与 pictureInPictureElement 关联的 画中画窗口 的宽度(单位为 CSS 像素)。否则,必须返回 0。

如果 stateopened,则 height 属性必须返回与 pictureInPictureElement 关联的 画中画窗口 的高度(单位为 CSS 像素)。否则,必须返回 0。

当与 pictureInPictureElement 关联的 画中画窗口 的大小发生变化时,用户代理必须 排队任务,以 触发 名为 resize 的事件,并将其应用于 pictureInPictureElement

4.5. 事件类型

[Exposed=Window]
interface PictureInPictureEvent : Event {
    constructor(DOMString type, PictureInPictureEventInit eventInitDict);
    [SameObject] readonly attribute PictureInPictureWindow pictureInPictureWindow;
};

dictionary PictureInPictureEventInit : EventInit {
    required PictureInPictureWindow pictureInPictureWindow;
};
enterpictureinpicture

HTMLVideoElement进入画中画模式时触发。

leavepictureinpicture

HTMLVideoElement离开画中画模式时触发。

resize

PictureInPictureWindow改变大小时触发。

4.6. 任务源

任务源对于本规范中排队的所有任务是相关视频元素的媒体元素事件任务源

4.7. CSS伪类

:picture-in-picture 伪类必须匹配画中画元素。它不同于pictureInPictureElement,因为它不适用于影子宿主链。

5. 安全性考量

本节为非规范性内容。

为了限制通过欺骗行为的潜在滥用,API 仅适用于HTMLVideoElement。 对画中画窗口的用户交互有意限制为仅影响画中画窗口本身或正在播放的媒体。

5.1. 安全上下文

该 API 不仅限于[SECURE-CONTEXTS],因为它为 Web 应用程序提供了用户代理通常在所有媒体上原生提供的功能,无论浏览上下文如何。

5.2. 权限策略

本规范定义了一个名为"picture-in-picture"策略控制的功能,用于控制请求画中画算法 是否会返回SecurityError,以及pictureInPictureEnabledtrue还是false

该功能的默认允许列表*

6. 致谢

感谢 Jennifer Apacible、Zouhir Chahoud、Marcos Cáceres、Philip Jägenstedt、Jeremy Jones、Chris Needham、Jer Noble、Justin Uberti、Yoav Weiss 和 Eckhart Wörner 对本文档的贡献。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语的组合来表达。 在本文档的规范部分中,关键字“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按照 RFC 2119 中的描述进行解释。 然而,为了提高可读性,本规范中这些词不会全部大写。

除非明确标记为非规范性的章节、示例和注释,本规范的所有文本都是规范性的。[RFC2119]

本规范中的示例以“例如”开头或通过 class="example" 与规范文本区分开,如下所示:

这是一个说明性示例。

说明性注释以“注”字开头,并通过 class="note" 与规范文本区分开,如下所示:

注,这是一个说明性注释。

一致性算法

作为算法一部分的命令式要求(例如“去除任何前导空格字符”或“返回 false 并中止这些步骤”) 应按照引入算法时使用的关键字(如“必须”、“应该”、“可以”等)的含义进行解释。

以算法或特定步骤措辞的一致性要求可以通过任何方式实现,只要最终结果等效即可。 特别是,本规范中定义的算法旨在易于理解,而非高效执行。鼓励实现者进行优化。

索引

本规范定义的术语

通过引用定义的术语

参考文献

规范性引用

[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS数值与单位模块第4级。2024年3月12日。工作草案。链接:https://www.w3.org/TR/css-values-4/
[DOM]
Anne van Kesteren. DOM标准。动态标准。链接:https://dom.spec.whatwg.org/
[FULLSCREEN]
Philip Jägenstedt. 全屏API标准。动态标准。链接:https://fullscreen.spec.whatwg.org/
[HTML]
Anne van Kesteren; 等。HTML标准。动态标准。链接:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. 基础设施标准。动态标准。链接:https://infra.spec.whatwg.org/
[PERMISSIONS-POLICY-1]
Ian Clelland. 权限策略。2025年8月6日。工作草案。链接:https://www.w3.org/TR/permissions-policy-1/
[Remote-Playback]
Mark Foltz. 远程播放API。2024年4月30日。候选推荐草案。链接:https://www.w3.org/TR/remote-playback/
[RFC2119]
S. Bradner. RFC中用于指示需求级别的关键词。1997年3月。最佳当前实践。链接:https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. 选择器第4级。2022年11月11日。工作草案。链接:https://www.w3.org/TR/selectors-4/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL标准。动态标准。链接:https://webidl.spec.whatwg.org/

参考性引用

[MediaSession]
Thomas Steimel; youenn fablet. 媒体会话。2025年4月30日。工作草案。链接:https://www.w3.org/TR/mediasession/
[SECURE-CONTEXTS]
Mike West. 安全上下文。2023年11月10日。候选推荐草案。链接:https://www.w3.org/TR/secure-contexts/

IDL 索引

partial interface HTMLVideoElement {
  [NewObject] Promise<PictureInPictureWindow> requestPictureInPicture();

  attribute EventHandler onenterpictureinpicture;
  attribute EventHandler onleavepictureinpicture;

  [CEReactions] attribute boolean disablePictureInPicture;
};

partial interface Document {
  readonly attribute boolean pictureInPictureEnabled;

  [NewObject] Promise<undefined> exitPictureInPicture();
};

partial interface mixin DocumentOrShadowRoot {
  readonly attribute Element? pictureInPictureElement;
};

[Exposed=Window]
interface PictureInPictureWindow : EventTarget {
  readonly attribute long width;
  readonly attribute long height;

  attribute EventHandler onresize;
};

[Exposed=Window]
interface PictureInPictureEvent : Event {
    constructor(DOMString type, PictureInPictureEventInit eventInitDict);
    [SameObject] readonly attribute PictureInPictureWindow pictureInPictureWindow;
};

dictionary PictureInPictureEventInit : EventInit {
    required PictureInPictureWindow pictureInPictureWindow;
};