1
0
mirror of https://github.com/videojs/video.js.git synced 2025-10-31 00:08:01 +02:00

feat: adds disablePictureInPicture method to the player API. (#6378)

This commit is contained in:
Grzegorz Blaszczyk
2020-04-22 18:41:21 +02:00
committed by GitHub
parent 8c66c58346
commit dbd5203a0b
9 changed files with 204 additions and 8 deletions

View File

@@ -52,7 +52,9 @@ export class VjsPlayerComponent implements OnInit, OnDestroy {
}
}
```
Don't forget to include the Video.js CSS, located at `video.js/dist/video-js.css`.
```css
/* vjs-player.component.css */
@import '~video.js/dist/video-js.css';

View File

@@ -13,6 +13,7 @@ This default value is hardcoded as a default to the localize method in the SeekB
## Status of translations
<!-- START langtable -->
| Language file | Missing translations |
| ----------------------- | ----------------------------------------------------------------------------------- |
| ar.json (missing 3) | progress bar timing: currentTime={1} duration={2} |
@@ -941,4 +942,5 @@ This default value is hardcoded as a default to the localize method in the SeekB
| | Picture-in-Picture |
| zh-TW.json (missing 2) | Exit Picture-in-Picture |
| | Picture-in-Picture |
<!-- END langtable -->

View File

@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Video.js Sandbox</title>
<link href="../dist/video-js.css" rel="stylesheet" type="text/css">
<script src="../dist/video.js"></script>
</head>
<body>
<div style="background-color:#eee; border: 1px solid #777; padding: 10px; margin-bottom: 20px; font-size: .8em; line-height: 1.5em; font-family: Verdana, sans-serif;">
<p>You can use /sandbox/ for writing and testing your own code. Nothing in /sandbox/ will get checked into the repo, except files that end in .example (so don't edit or add those files). To get started run `npm start` and open the index.html</p>
<pre>npm start</pre>
<pre>open http://localhost:9999/sandbox/index.html</pre>
</div>
<video-js
id="vid1"
controls
disablepictureinpicture
preload="none"
width="640"
height="264"
poster="http://vjs.zencdn.net/v/oceans.png">
<source src="http://vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
<source src="http://vjs.zencdn.net/v/oceans.webm" type="video/webm">
<source src="http://vjs.zencdn.net/v/oceans.ogv" type="video/ogg">
<track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video-js>
<p>
PiP disabled via <b>disablepictureinpicture</b> attribute.
</p>
<video-js
id="vid2"
controls
preload="auto"
width="640"
height="264"
poster="http://vjs.zencdn.net/v/oceans.png">
<source src="http://vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
<source src="http://vjs.zencdn.net/v/oceans.webm" type="video/webm">
<source src="http://vjs.zencdn.net/v/oceans.ogv" type="video/ogg">
<track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video-js>
<p>
PiP disabled via <b>disablePictureInPicture</b> player option.
</p>
<button id="PiPToggleBtn">call disablePictureInPicture(false) for the player 2</button>
<script>
var pipDisabled = true;
var vid = document.getElementById('vid1');
var player = videojs(vid);
var vid2 = document.getElementById('vid2');
var player2 = videojs(vid2, {
controlBar: {
pictureInPictureToggle: true
},
disablePictureInPicture: true
});
var pipToggle = document.getElementById('PiPToggleBtn');
pipToggle.addEventListener('click', function() {
pipToggle.innerText = 'call disablePictureInPicture(' + pipDisabled + ') for the player 2';
pipDisabled = !pipDisabled;
player2.disablePictureInPicture(pipDisabled);
});
</script>
</body>
</html>

View File

@@ -27,13 +27,10 @@ class PictureInPictureToggle extends Button {
constructor(player, options) {
super(player, options);
this.on(player, ['enterpictureinpicture', 'leavepictureinpicture'], this.handlePictureInPictureChange);
this.on(player, ['disablepictureinpicturechanged', 'loadedmetadata'], this.handlePictureInPictureEnabledChange);
// TODO: Activate button on player loadedmetadata event.
// TODO: Deactivate button on player emptied event.
// TODO: Deactivate button if disablepictureinpicture attribute is present.
if (!document.pictureInPictureEnabled) {
this.disable();
}
this.disable();
}
/**
@@ -46,6 +43,18 @@ class PictureInPictureToggle extends Button {
return `vjs-picture-in-picture-control ${super.buildCSSClass()}`;
}
/**
* Enables or disables button based on document.pictureInPictureEnabled property value
* or on value returned by player.disablePictureInPicture() method.
*/
handlePictureInPictureEnabledChange() {
if (!document.pictureInPictureEnabled || this.player_.disablePictureInPicture()) {
this.disable();
} else {
this.enable();
}
}
/**
* Handles enterpictureinpicture and leavepictureinpicture on the player and change control text accordingly.
*
@@ -62,6 +71,7 @@ class PictureInPictureToggle extends Button {
} else {
this.controlText('Picture-in-Picture');
}
this.handlePictureInPictureEnabledChange();
}
/**

View File

@@ -1119,6 +1119,7 @@ class Player extends Component {
'playsinline': this.options_.playsinline,
'preload': this.options_.preload,
'loop': this.options_.loop,
'disablePictureInPicture': this.options_.disablePictureInPicture,
'muted': this.options_.muted,
'poster': this.poster(),
'language': this.language(),
@@ -2982,6 +2983,22 @@ class Player extends Component {
this.trigger('exitFullWindow');
}
/**
* Disable Picture-in-Picture mode.
*
* @param {boolean} value
* - true will disable Picture-in-Picture mode
* - false will enable Picture-in-Picture mode
*/
disablePictureInPicture(value) {
if (value === undefined) {
return this.techGet_('disablePictureInPicture');
}
this.techCall_('setDisablePictureInPicture', value);
this.options_.disablePictureInPicture = value;
this.trigger('disablepictureinpicturechanged');
}
/**
* Check if the player is in Picture-in-Picture mode or tell the player that it
* is or is not in Picture-in-Picture mode.

View File

@@ -405,6 +405,10 @@ class Html5 extends Tech {
Dom.setAttribute(el, 'preload', this.options_.preload);
}
if (this.options_.disablePictureInPicture !== undefined) {
el.disablePictureInPicture = this.options_.disablePictureInPicture;
}
// Update specific tag settings, in case they were overridden
// `autoplay` has to be *last* so that `muted` and `playsinline` are present
// when iOS/Safari or other browsers attempt to autoplay.
@@ -1526,8 +1530,8 @@ Html5.resetMediaElement = function(el) {
// Wrap native properties with a getter
// The list is as followed
// paused, currentTime, buffered, volume, poster, preload, error, seeking
// seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
// readyState, videoWidth, videoHeight, crossOrigin
// seekable, ended, playbackRate, defaultPlaybackRate, disablePictureInPicture
// played, networkState, readyState, videoWidth, videoHeight, crossOrigin
[
/**
* Get the value of `paused` from the media element. `paused` indicates whether the media element
@@ -1699,6 +1703,19 @@ Html5.resetMediaElement = function(el) {
*/
'defaultPlaybackRate',
/**
* Get the value of 'disablePictureInPicture' from the video element.
*
* @method Html5#disablePictureInPicture
* @return {boolean} value
* - The value of `disablePictureInPicture` from the video element.
* - True indicates that the video can't be played in Picture-In-Picture mode
* - False indicates that the video can be played in Picture-In-Picture mode
*
* @see [Spec]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
*/
'disablePictureInPicture',
/**
* Get the value of `played` from the media element. `played` returns a `TimeRange`
* object representing points in the media timeline that have been played.
@@ -1796,7 +1813,8 @@ Html5.resetMediaElement = function(el) {
// Wrap native properties with a setter in this format:
// set + toTitleCase(name)
// The list is as follows:
// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate, setCrossOrigin
// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate,
// setDisablePictureInPicture, setCrossOrigin
[
/**
* Set the value of `volume` on the media element. `volume` indicates the current
@@ -1888,6 +1906,18 @@ Html5.resetMediaElement = function(el) {
*/
'defaultPlaybackRate',
/**
* Prevents the browser from suggesting a Picture-in-Picture context menu
* or to request Picture-in-Picture automatically in some cases.
*
* @method Html5#setDisablePictureInPicture
* @param {boolean} value
* The true value will disable Picture-in-Picture mode.
*
* @see [Spec]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
*/
'disablePictureInPicture',
/**
* Set the value of `crossOrigin` from the media element. `crossOrigin` indicates
* to the browser that should sent the cookies along with the requests for the

View File

@@ -788,6 +788,22 @@ class Tech extends Component {
}
}
/**
* A method to check for the presence of the 'disablePictureInPicture' <video> property.
*
* @abstract
*/
disablePictureInPicture() {
return false;
}
/**
* A method to set or unset the 'disablePictureInPicture' <video> property.
*
* @abstract
*/
setDisablePictureInPicture() {}
/**
* A method to set a poster from a `Tech`.
*

View File

@@ -39,6 +39,7 @@ QUnit.test('should be able to access expected player API methods', function(asse
assert.ok(player.textTracks, 'textTracks exists');
assert.ok(player.requestFullscreen, 'requestFullscreen exists');
assert.ok(player.exitFullscreen, 'exitFullscreen exists');
assert.ok(player.disablePictureInPicture, 'disablePictureInPicture exists');
assert.ok(player.requestPictureInPicture, 'requestPictureInPicture exists');
assert.ok(player.exitPictureInPicture, 'exitPictureInPicture exists');
assert.ok(player.playbackRate, 'playbackRate exists');

View File

@@ -169,6 +169,52 @@ QUnit.test('Picture-in-Picture control text should be correct when enterpicturei
pictureInPictureToggle.dispose();
});
QUnit.test('Picture-in-Picture control enabled property value should be correct when enterpictureinpicture and leavepictureinpicture are triggered', function(assert) {
const player = TestHelpers.makePlayer();
const pictureInPictureToggle = new PictureInPictureToggle(player);
assert.equal(pictureInPictureToggle.enabled_, false, 'pictureInPictureToggle button should be disabled after creation');
if ('pictureInPictureEnabled' in document) {
player.isInPictureInPicture(true);
player.trigger('enterpictureinpicture');
assert.equal(pictureInPictureToggle.enabled_, true, 'pictureInPictureToggle button should be enabled after triggering an enterpictureinpicture event');
player.isInPictureInPicture(false);
player.trigger('leavepictureinpicture');
assert.equal(pictureInPictureToggle.enabled_, true, 'pictureInPictureToggle button should be enabled after triggering an leavepictureinpicture event');
} else {
player.isInPictureInPicture(true);
player.trigger('enterpictureinpicture');
assert.equal(pictureInPictureToggle.enabled_, false, 'pictureInPictureToggle button should be disabled after triggering an enterpictureinpicture event');
player.isInPictureInPicture(false);
player.trigger('leavepictureinpicture');
assert.equal(pictureInPictureToggle.enabled_, false, 'pictureInPictureToggle button should be disabled after triggering an leavepictureinpicture event');
}
player.dispose();
pictureInPictureToggle.dispose();
});
QUnit.test('Picture-in-Picture control enabled property value should be correct when loadedmetadata is triggered', function(assert) {
const player = TestHelpers.makePlayer();
const pictureInPictureToggle = new PictureInPictureToggle(player);
assert.equal(pictureInPictureToggle.enabled_, false, 'pictureInPictureToggle button should be disabled after creation');
if ('pictureInPictureEnabled' in document) {
player.trigger('loadedmetadata');
assert.equal(pictureInPictureToggle.enabled_, true, 'pictureInPictureToggle button should be enabled after triggering an loadedmetadata event');
} else {
player.trigger('loadedmetadata');
assert.equal(pictureInPictureToggle.enabled_, false, 'pictureInPictureToggle button should be disabled after triggering an loadedmetadata event');
}
player.dispose();
pictureInPictureToggle.dispose();
});
QUnit.test('Fullscreen control text should be correct when fullscreenchange is triggered', function(assert) {
const player = TestHelpers.makePlayer({controlBar: false});
const fullscreentoggle = new FullscreenToggle(player);