1. Introduction
This section is non-normative.
Many users want to continue consuming media while they interact with other content, sites, or applications on their device. A common UI affordance for this type of activity is Picture-in-Picture (PiP), where the video is contained in a separate miniature window that is always on top of other windows. Picture-in-Picture is a common platform-level feature among desktop and mobile OSs.
This specification aims to allow websites to initiate and control this behavior by exposing the following sets of properties to the API:
-
Notify the website when it enters and leave Picture-in-Picture mode.
-
Allow the website to trigger Picture-in-Picture via a user gesture on a video element.
-
Allow the website to exit Picture-in-Picture.
-
Allow the website to check if Picture-in-Picture can be triggered.
The proposed Picture-in-Picture API is very similar to [Fullscreen] as they
have similar properties. The API only applies on HTMLVideoElement
at the moment but is meant to be
extensible.
2. Examples
2.1. Add a custom Picture-in-Picture button
<video id="video" src="https://example.com/file.mp4"></video> <button id="pipButton"></button> <script> // Hide button if Picture-in-Picture is not supported or disabled. pipButton.hidden = !document.pictureInPictureEnabled || video.disablePictureInPicture; pipButton.addEventListener('click', function() { // If there is no element in Picture-in-Picture yet, let’s request // Picture-in-Picture for the video, otherwise leave it. if (!document.pictureInPictureElement) { video.requestPictureInPicture() .catch(error => { // Video failed to enter Picture-in-Picture mode. }); } else { document.exitPictureInPicture() .catch(error => { // Video failed to leave Picture-in-Picture mode. }); } }); </script>
2.2. Monitor video Picture-in-Picture changes
<video id="video" src="https://example.com/file.mp4"></video> <script> video.addEventListener('enterpictureinpicture', function() { // Video entered Picture-in-Picture mode. }); video.addEventListener('leavepictureinpicture', function() { // Video left Picture-in-Picture mode. }); </script>
2.3. Update video size based on Picture-in-Picture window size changes
<video id="video" src="https://example.com/file.mp4"></video> <button id="pipButton"></button> <script> pipButton.addEventListener('click', function() { video.requestPictureInPicture() .then(pipWindow => { updateVideoSize(pipWindow.width, pipWindow.height); pipWindow.addEventListener('resize', function(event) { updateVideoSize(pipWindow.width, pipWindow.height); }); }); }); function updateVideoSize(width, height) { // TODO: Update video size based on pip window width and height. } </script>
3. Concepts
3.1. Request Picture-in-Picture
When the request Picture-in-Picture algorithm with video is invoked, the user agent MUST run the following steps:
-
If Picture-in-Picture support is
false
, throw aNotSupportedError
and abort these steps. -
If document is not allowed to use the policy-controlled feature named
"picture-in-picture"
, throw aSecurityError
and abort these steps. -
OPTIONALLY, if the
disablePictureInPicture
attribute is present on video, throw aInvalidStateError
and abort these steps. -
If the algorithm is not triggered by user activation, throw a
NotAllowedError
and abort these steps. -
Set
pictureInPictureElement
to video. -
Let Picture-in-Picture window be a new instance of
PictureInPictureWindow
associated withpictureInPictureElement
. -
Queue a task to fire an event with the name
enterpictureinpicture
at the video with itsbubbles
attribute initialized to true.
It is RECOMMENDED that the video frames are not rendered in the page and in the Picture-in-Picture window at the same time but if they are, they MUST be kept in sync.
When a video is played in Picture-in-Picture, the states SHOULD transition as if it was played inline. That means that the events SHOULD fire at the same time, calling methods SHOULD have the same behaviour, etc. However, the user agent MAY transition out of Picture-in-Picture when the video element enters a state that is considered not compatible with Picture-in-Picture.
3.2. Exit Picture-in-Picture
When the exit Picture-in-Picture algorithm is invoked, the user agent MUST run the following steps:
-
If
pictureInPictureElement
is null, throw aInvalidStateError
and abort these steps. -
Run the close window algorithm with the Picture-in-Picture window associated with
pictureInPictureElement
. -
Unset
pictureInPictureElement
. -
Queue a task to fire an event with the name
leavepictureinpicture
at the video with itsbubbles
attribute initialized to true.
3.3. Disable Picture-in-Picture
Some pages may want to disable Picture-in-Picture for a video element. To
support this, a new disablePictureInPicture
attribute is added to the list
of content attributes for video elements.
A corresponding disablePictureInPicture
IDL attribute which reflects the
value of element’s disablePictureInPicture
content attribute is added to
the HTMLVideoElement
interface. The disablePictureInPicture
IDL attribute
MUST reflect the content attribute of the same name.
If the disablePictureInPicture
attribute is present on the video element,
the user agent SHOULD NOT play the video element in Picture-in-Picture or
present any UI to do so.
When the disablePictureInPicture
attribute is added to a video element,
the user agent SHOULD run these steps:
-
Reject any pending promises returned by the
requestPictureInPicture()
method withInvalidStateError
. -
If video is
pictureInPictureElement
, run the exit Picture-in-Picture algorithm.
3.4. Interaction with Remote Playback
The [Remote-Playback] specification defines a local playback device and a local playback state. For the purpose of Picture-in-Picture, the playback is local and regardless of whether it is played in page or in Picture-in-Picture.
3.5. Interaction with Media Session
The API will have to be used with the [MediaSession] API for customizing the avaiable controls on the Picture-in-Picture window.
3.6. One Picture-in-Picture window
Operating systems with a Picture-in-Picture API usually restricts
Picture-in-Picture to only one window. Whether only one window is allowed in
Picture-in-Picture will be left to the implementation and the platform.
However, because of the one Picture-in-Picture window limitation, the
specification assumes that a given Document
can only have one
Picture-in-Picture window.
What happens when there is a Picture-in-Picture request while a window is already in Picture-in-Picture will be left as an implementation details: the current Picture-in-Picture window could be closed, the Picture-in-Picture request could be rejected or even two Picture-in-Picture windows can be created. Regardless, the User Agent will have to fire the appropriate events in order to notify the website of the Picture-in-Picture status changes.
4. API
4.1. Extensions to HTMLVideoElement
partial interface HTMLVideoElement { Promise<PictureInPictureWindow>requestPictureInPicture
(); attribute EventHandleronenterpictureinpicture
; attribute EventHandleronleavepictureinpicture
; [CEReactions] attribute booleandisablePictureInPicture
; };
The requestPictureInPicture()
method, when invoked, MUST
return a new promise promise and run the following steps in
parallel:
-
Let video be the requested video.
-
Run the request Picture-in-Picture algorithm with video.
-
If the previous step threw an exception, reject promise with that exception and abort these steps.
-
Return promise with the Picture-in-Picture window associated with
pictureInPictureElement
.
4.2. Extensions to Document
partial interface Document { readonly attribute booleanpictureInPictureEnabled
; Promise<void>exitPictureInPicture
(); };
The pictureInPictureEnabled
attribute’s getter must return true
if Picture-in-Picture support is true
and
the context object is allowed to use the feature indicated by
attribute name picture-in-picture
, and false
otherwise.
Picture-in-Picture support is true
if there is no
previously-established user preference, restrictions, or platform limitation,
and false
otherwise.
The exitPictureInPicture()
method, when invoked, MUST
return a new promise promise and run the following steps in
parallel:
-
Run the exit Picture-in-Picture algorithm.
-
If the previous step threw an exception, reject promise with that exception and abort these steps.
-
Return promise.
4.3. Extension to DocumentOrShadowRoot
partial interface DocumentOrShadowRoot {
readonly attribute Element? pictureInPictureElement
;
};
The pictureInPictureElement
attribute’s getter must run these steps:
-
If the context object is not connected, return null and abort these steps.
-
Let candidate be the result of retargeting Picture-in-Picture element against the context object.
-
If candidate and the context object are in the same tree, return candidate and abort these steps.
-
Return null.
4.4. Interface PictureInPictureWindow
interfacePictureInPictureWindow
{ readonly attribute longwidth
; readonly attribute longheight
; attribute EventHandleronresize
; };
A PictureInPictureWindow
instance represents a Picture-in-Picture
window associated with an HTMLVideoElement
. When instantiated, an
instance of PictureInPictureWindow
has its state set to opened.
When the close window algorithm with an instance of PictureInPictureWindow
is invoked, its state is set to closed.
The width
attribute MUST return the width in CSS pixels of the Picture-in-Picture window associated with pictureInPictureElement
if
the state is opened. Otherwise, it MUST return 0.
The height
attribute MUST return the height in CSS pixels of the Picture-in-Picture window associated with pictureInPictureElement
if
the state is opened. Otherwise, it MUST return 0.
4.5. Event types
enterpictureinpicture
-
Fired on a
HTMLVideoElement
when it enters Picture-in-Picture. leavepictureinpicture
-
Fired on a
HTMLVideoElement
when it leaves Picture-in-Picture. resize
-
Fired on a
PictureInPictureWindow
when it changes size.
5. Security considerations
This section is non-normative.
The API applies only to HTMLVideoElement
in order to start on a minimal
viable product that has limited security issues. Later versions of this
specification may allow PIP-ing arbitrary HTML content.
5.1. Feature Policy
This specification defines a policy-controlled feature that controls
whether the request Picture-in-Picture algorithm may return a SecurityError
and whether pictureInPictureEnabled
is true
or false
.
The feature name for this feature is "picture-in-picture"
.
The default allowlist for this feature is *
.