ピクチャ・イン・ピクチャ

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 Platform Tests:
permissions-policy/
picture-in-picture/

概要

この仕様は、ウェブサイトが他のウィンドウの上に常に表示されるフローティングビデオウィンドウを作成できるAPIを提供します。 これにより、ユーザーは他のコンテンツサイトやデバイス上のアプリケーションを操作しながら、メディアの視聴を続けることができます。

この文書のステータス

このセクションは、発行時点でのこの文書のステータスを説明します。現行のW3C出版物一覧やこの技術レポートの最新改訂版は、W3C標準および草案インデックスで確認できます。

この仕様へのフィードバックやコメントを歓迎します。 議論にはGitHub Issuesの利用を推奨します。 または、Media Working Groupのメーリングリストpublic-media-wg@w3.orgアーカイブ)にコメントを送ることもできます。 本草案では、ワーキンググループで今後議論予定の未解決課題をいくつか取り上げています。 これらの課題の妥当性を含め、結論はまだ出ていません。

この文書はMedia Working Groupによる 勧告トラックを用いた作業草案として公開されました。 この文書はW3C勧告となる予定です。

作業草案として公開されたことはW3Cおよびそのメンバーによる支持を意味しません。

この文書は草案であり、今後いつでも更新・置換・廃止される可能性があります。 進行中の作業以外としてこの文書を引用するのは不適切です。

この文書はW3C特許ポリシーの下で運営されているグループによって作成されました。 W3Cは、グループの成果物に関連してなされた特許開示の公開リストを管理しています。 そのページには特許開示方法の案内もあります。個人が本質的請求項を含むと信じる特許の実際の知識を持つ場合は、 W3C特許ポリシー第6節に従って情報開示しなければなりません。

この文書は2025年8月18日版W3Cプロセス文書に従い管理されています。

1. 導入

このセクションは規範的ではありません。

多くのユーザーは、他のコンテンツやサイト、またはデバイス上のアプリケーションを操作している間も、メディアの視聴を続けたいと考えています。このような活動のための一般的なUI手段が「ピクチャ・イン・ピクチャ(PiP)」です。これは、ビデオが他のウィンドウの上に常に表示される独立した小型ウィンドウ内に表示されるものです。このウィンドウはユーザーエージェントが表示されていなくても画面上に残ります。ピクチャ・イン・ピクチャは、デスクトップやモバイルOSにおける一般的なプラットフォーム機能です。

この仕様は 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: pipウィンドウの幅・高さに基づいてビデオサイズを更新する。
  }
</script>

3. 概念

3.1. 内部スロット定義

ユーザーエージェントは以下を持ちます:

  1. アクティブなピクチャ・イン・ピクチャセッションの開始元のリスト(ゼロ個以上のオリジンを含み、初期値は空です)。

注: ユーザーエージェントが複数のピクチャ・イン・ピクチャウィンドウをサポートする場合、このリストには重複が許されます。

あるオリジンがアクティブなピクチャ・イン・ピクチャセッションを持つとは、アクティブなピクチャ・イン・ピクチャセッションの開始元のいずれかが、そのオリジンと同一オリジンドメインである場合を指します。

3.2. ピクチャ・イン・ピクチャのリクエスト

request Picture-in-Picture algorithmvideoで呼び出された場合、 ユーザーエージェントは以下の手順を実行しなければなりません:

  1. ピクチャ・イン・ピクチャサポートfalseなら、 NotSupportedError を投げて、これらの手順を中断します。

  2. ドキュメントがallowed to useの状態でなく、policy-controlled feature "picture-in-picture"を利用できない場合、SecurityError を投げてこれらの手順を中断します。

  3. videoreadyState 属性がHAVE_NOTHINGなら、 InvalidStateError を投げて、これらの手順を中断します。

  4. videoにビデオトラックが存在しない場合、InvalidStateError を投げてこれらの手順を中断します。

  5. videodisablePictureInPicture がtrueの場合、 ユーザーエージェントはInvalidStateError を投げてこれらの手順を中断してもよいです。

  6. pictureInPictureElementnullかつ、relevant global objectthisに対し transient activationを持たない場合、 NotAllowedError を投げてこれらの手順を中断します。

  7. videopictureInPictureElementの場合、 これらの手順を中断します。

  8. pictureInPictureElementvideoに設定します。

  9. ピクチャ・イン・ピクチャウィンドウPictureInPictureWindow の新しいインスタンス(pictureInPictureElementに関連付ける)として作成します。

  10. relevant settings objectoriginアクティブなピクチャ・イン・ピクチャセッションの開始元リストに追加します。

  11. タスクをキューイングしてイベントを発火します。 イベント名は enterpictureinpicture で、PictureInPictureEvent を使用し、 videobubbles 属性はtruepictureInPictureWindow 属性は ピクチャ・イン・ピクチャウィンドウで初期化します。

  12. pictureInPictureElementfullscreenElementの場合、 フルスクリーンから完全に退出することが推奨されます。

ビデオフレームはページとピクチャ・イン・ピクチャウィンドウで同時描画しないことが推奨されますが、もし同時描画されるなら同期を保たなければなりません。

ビデオがピクチャ・イン・ピクチャモードで再生される際、状態遷移はインライン再生と同じになるべきです。つまり、イベントは同時に発火し、メソッド呼び出しの振る舞いも同じになるべきです。ただし、ユーザーエージェントはビデオ要素がピクチャ・イン・ピクチャと非互換な状態になった場合、ピクチャ・イン・ピクチャを終了させても構いません。

videoに適用されているスタイル(opacity, visibility, transform等)はピクチャ・イン・ピクチャウィンドウには適用されてはなりません。ウィンドウのアスペクト比は動画サイズに基づきます。

また、ピクチャ・イン・ピクチャウィンドウには最大・最小サイズを設けることが推奨されます。例えば画面の一辺の1/4から1/2の範囲に制限することができます。

3.3. ピクチャ・イン・ピクチャの終了

exit Picture-in-Picture algorithmが呼び出された場合、 ユーザーエージェントは以下の手順を実行しなければなりません:

  1. pictureInPictureElementnullなら、InvalidStateError を投げてこれらの手順を中断します。

  2. close window algorithmを、 pictureInPictureElement に関連付けられたピクチャ・イン・ピクチャウィンドウで実行します。

  3. タスクをキューイングしてイベントを発火します。 イベント名は leavepictureinpicture で、PictureInPictureEvent を使用し、 videobubbles 属性はtruepictureInPictureWindow 属性は ピクチャ・イン・ピクチャウィンドウpictureInPictureElementに関連付けられたもの)で初期化します。

  4. pictureInPictureElement を解除します。

  5. itemのうちrelevant settings objectoriginと一致するものを アクティブなピクチャ・イン・ピクチャセッションの開始元リストから1件削除します。

exit Picture-in-Picture algorithmが呼び出されたとき、ビデオの再生状態を変更することは推奨されません。ウェブサイトが開始した場合はウェブサイトが操作を制御するべきです。ただし、ユーザーエージェントはピクチャ・イン・ピクチャウィンドウのコントロール(例:一時停止)で再生状態を変更することができます。

ドキュメントのアンロード時クリーンアップ手順の一部として、exit Picture-in-Picture algorithmを実行します。

3.4. ピクチャ・イン・ピクチャの無効化

一部のページでは、ビデオ要素のピクチャ・イン・ピクチャモードを無効化したい場合があります。たとえば、ユーザーエージェントがピクチャ・イン・ピクチャのコンテキストメニューを提示することを防ぎたい場合などです。 これらのユースケースをサポートするために、ビデオ要素のコンテンツ属性リストに新しいdisablePictureInPicture 属性が追加されました。

disablePictureInPicture IDL属性は同名のコンテンツ属性を反映しなければなりません。

ビデオ要素にdisablePictureInPicture 属性が存在する場合、 ユーザーエージェントはそのビデオ要素をピクチャ・イン・ピクチャモードで再生できなくしたり、関連UIを表示しないようにしてもよいです。

disablePictureInPicture 属性がvideo要素に追加されたとき、 ユーザーエージェントは以下の手順を実行してもよいです:

  1. requestPictureInPicture() メソッドから返された未解決のpromiseをInvalidStateErrorで拒否します。

  2. videopictureInPictureElementの場合、 exit Picture-in-Picture algorithmを実行します。

3.5. フルスクリーンとの相互作用

pictureInPictureElementfullscreen flagが設定された場合は、 ピクチャ・イン・ピクチャ終了アルゴリズムを実行することが推奨されます。

3.6. リモート再生との相互作用

[Remote-Playback]仕様はローカル再生デバイスおよびローカル再生状態を定義しています。ピクチャ・イン・ピクチャの目的においては、 再生はローカルであり、ページ内で再生されていてもピクチャ・イン・ピクチャであっても同様です。

3.7. メディアセッションとの相互作用

このAPIは[MediaSession] APIと組み合わせて利用し、ピクチャ・イン・ピクチャウィンドウ上の利用可能なコントロールをカスタマイズする必要があります。

3.8. ページ可視性との相互作用

pictureInPictureElement が設定されている場合、ピクチャ・イン・ピクチャウィンドウは Documentがフォーカスされていなくても、非表示でも必ず表示されていなければなりません。ユーザーエージェントはユーザーがピクチャ・イン・ピクチャウィンドウを手動で閉じる手段を提供するべきです。

ピクチャ・イン・ピクチャウィンドウの可視性は、ユーザーエージェントがシステム可視性状態traversable navigableで変更されたかどうかを判断する材料として利用してはなりません。

3.9. ピクチャ・イン・ピクチャウィンドウは一つのみ

ピクチャ・イン・ピクチャAPIを持つOSでは、通常ピクチャ・イン・ピクチャモードは一つのウィンドウに制限されています。ピクチャ・イン・ピクチャモードが一つのウィンドウのみ許可されるかどうかは実装・プラットフォームの判断に委ねられます。 ただし、ピクチャ・イン・ピクチャウィンドウが一つであるという制限のため、 本仕様では、Document 毎に一つだけピクチャ・イン・ピクチャウィンドウが持てるものとしています。

既にピクチャ・イン・ピクチャウィンドウがある状態でリクエストが発生した場合の挙動は実装依存です。現在のピクチャ・イン・ピクチャウィンドウを閉じる、リクエストを拒否する、あるいは二つのウィンドウを作成することもできます。いずれの場合も、ユーザーエージェントはウェブサイトにピクチャ・イン・ピクチャ状態の変化を通知するために適切なイベントを発火しなければなりません。

4. API

4.1. HTMLVideoElementへの拡張

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

  attribute EventHandler onenterpictureinpicture;
  attribute EventHandler onleavepictureinpicture;

  [CEReactions] attribute boolean disablePictureInPicture;
};

requestPictureInPicture() メソッドが呼び出された場合、新しいpromise promiseを返し、以下の手順を並列で実行しなければなりません:

  1. videoはこのメソッドが呼び出されたビデオ要素とします。

  2. ピクチャ・イン・ピクチャリクエストアルゴリズムvideoで実行します。

  3. 前のステップで例外が投げられた場合、promiseをその例外で拒否し、これらの手順を中断します。

  4. promiseを解決し、pictureInPictureElement に関連付けられたピクチャ・イン・ピクチャウィンドウで返します。

4.2. Documentへの拡張

partial interface Document {
  readonly attribute boolean pictureInPictureEnabled;

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

pictureInPictureEnabled 属性のgetterは、ピクチャ・イン・ピクチャサポートtrueであり、thisが 属性名picture-in-pictureで示される機能をallowed to use状態である場合はtrue、それ以外はfalseを返さなければなりません。

ピクチャ・イン・ピクチャサポートは、ユーザーの設定で無効化されている場合やプラットフォームの制限がある場合はfalseです。それ以外はtrueです。

exitPictureInPicture() メソッドが呼び出された場合、新しいpromise promiseを返し、以下の手順を並列で実行しなければなりません:

  1. ピクチャ・イン・ピクチャ終了アルゴリズムを実行します。

  2. 前のステップで例外が投げられた場合、promiseをその例外で拒否し、これらの手順を中断します。

  3. promiseを解決します。

4.3. DocumentOrShadowRootへの拡張

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

pictureInPictureElement 属性のgetterは以下の手順を実行します:

  1. thisshadow rootであり、そのhostconnectedでない場合はnullを返し、これらの手順を中断します。

  2. candidateretargeting Picture-in-Picture element against 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 に関連付けられたもの)を表します。インスタンス化されると、PictureInPictureWindowstateopened に設定されます。

close window algorithmPictureInPictureWindowのインスタンスで呼ばれた場合、stateclosedに設定されます。

width 属性は、stateopenedのとき、pictureInPictureElementに関連付けられたCSSピクセル単位のピクチャ・イン・ピクチャウィンドウの幅を返します。それ以外の場合は0を返します。

height 属性は、stateopenedのとき、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]に限定されていません。なぜなら、ユーザーエージェントが通常すべてのメディアに対してネイティブに提供している機能をウェブアプリケーションへ公開するためであり、ブラウジングコンテキストに関係なく利用可能です。

5.2. Permissions Policy

この仕様は、"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を返してこれらの手順を中断する」など)は、 アルゴリズム導入部分で用いられているキーワード("must"、"should"、"may"等)の意味で解釈されます。

アルゴリズムや特定の手順として記述された適合性要件は、最終的な結果が同等であれば、どのような方法で実装しても構いません。 特に、この仕様で定義されたアルゴリズムは理解しやすさを重視しており、パフォーマンスを意図したものではありません。 実装者は最適化を推奨します。

索引

本仕様で定義されている用語

参照によって定義されている用語

参考文献

規範参考文献

[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS値と単位モジュール レベル4。2024年3月12日。作業草案。URL: https://www.w3.org/TR/css-values-4/
[DOM]
Anne van Kesteren. DOM現行標準。URL: https://dom.spec.whatwg.org/
[FULLSCREEN]
Philip Jägenstedt. Fullscreen API標準。現行標準。URL: https://fullscreen.spec.whatwg.org/
[HTML]
Anne van Kesteren; 他。HTML現行標準。URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra現行標準。URL: https://infra.spec.whatwg.org/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy。2025年8月6日。作業草案。URL: https://www.w3.org/TR/permissions-policy-1/
[Remote-Playback]
Mark Foltz. Remote Playback API。2024年4月30日。勧告候補ドラフト。URL: https://www.w3.org/TR/remote-playback/
[RFC2119]
S. Bradner. RFCにおいて要件レベルを示すためのキーワード。1997年3月。Best Current Practice。URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. セレクター レベル4。2022年11月11日。作業草案。URL: https://www.w3.org/TR/selectors-4/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL現行標準。URL: https://webidl.spec.whatwg.org/

参考情報文献

[MediaSession]
Thomas Steimel; youenn fablet. Media Session。2025年4月30日。作業草案。URL: https://www.w3.org/TR/mediasession/
[SECURE-CONTEXTS]
Mike West. セキュアコンテキスト。2023年11月10日。勧告候補ドラフト。URL: 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;
};