Skip to content

Commit

Permalink
feat: support seeking during live playback via liveui option (#5511)
Browse files Browse the repository at this point in the history
When liveui is enabled, allow seeking during the live window, add button that allows you to seek to the live edge and that indicates whether you are at the live edge or not.
  • Loading branch information
brandonocasey authored and gkatsev committed Dec 3, 2018
1 parent db1369a commit 2974ad3
Show file tree
Hide file tree
Showing 17 changed files with 912 additions and 46 deletions.
28 changes: 19 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions sandbox/live.html.example
@@ -0,0 +1,30 @@
<!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 make a copy of index.html.example and rename it to index.html.</p>
<pre>cp sandbox/index.html.example sandbox/index.html</pre>
<pre>npm run start</pre>
<pre>open http://localhost:9999/sandbox/index.html</pre>
</div>

<video-js id="vid1" controls preload="auto" width="640" height="264">
<source src="https://akamai-axtest.akamaized.net/routes/lapd-v1-acceptance/www_c4/Manifest.m3u8" type="application/x-mpegURL">
<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>

<script>
// fake a livestream for easy testing
var vid = document.getElementById('vid1');

var player = videojs(vid);
</script>

</body>
</html>
34 changes: 34 additions & 0 deletions sandbox/liveui.html.example
@@ -0,0 +1,34 @@
<!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 make a copy of index.html.example and rename it to index.html.</p>
<pre>cp sandbox/index.html.example sandbox/index.html</pre>
<pre>npm run start</pre>
<pre>open http://localhost:9999/sandbox/index.html</pre>
</div>

<video-js id="vid1" controls preload="auto" width="640" height="264">
<source src="https://akamai-axtest.akamaized.net/routes/lapd-v1-acceptance/www_c4/Manifest.m3u8" type="application/x-mpegURL">
<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>

<script>
// fake a livestream for easy testing
var vid = document.getElementById('vid1');
var liveui = true

if (videojs.browser.IS_ANDROID) {
liveui = false;
}
var player = videojs(vid, {liveui: liveui});
</script>

</body>
</html>
53 changes: 51 additions & 2 deletions src/css/components/_live.scss
@@ -1,5 +1,4 @@
// We are assuming there is no progress bar and using the live display
// to fill in the middle space. Live+DVR will need to adjust this.
// css for the old live ui, assumes that the progress bar is hidden
.video-js .vjs-live-control {
@include display-flex(flex-start);
@include flex(auto);
Expand All @@ -12,3 +11,53 @@
width: auto;
text-align: left;
}

// hide the LiveDisplay when not live or when
// the new liveui is in use
.video-js:not(.vjs-live) .vjs-live-control,
.video-js.vjs-liveui .vjs-live-control {
display: none;
}

// css for the new live ui below
.video-js .vjs-seek-to-live-control {
cursor: pointer;
@include flex(none);
display: inline-flex;
height: 100%;
padding-left: 0.5em;
padding-right: 0.5em;
font-size: 1em;
line-height: 3em;
width: auto;
min-width: 4em;
}

.vjs-no-flex .vjs-seek-to-live-control {
display: table-cell;
width: auto;
text-align: left;
}

// hide the SeekToLive button when not live and
// when the liveui is not in use
.video-js.vjs-live:not(.vjs-liveui) .vjs-seek-to-live-control,
.video-js:not(.vjs-live) .vjs-seek-to-live-control {
display: none;
}

// only show as a pointer when we will seek to live edge
.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge {
cursor: auto;
}

.vjs-seek-to-live-control .vjs-seek-to-live-circle {
margin-right: 0.5em;
@extend .vjs-icon-circle;
color: #888;
}

// make the live circle red when at the live edge
.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-seek-to-live-circle {
color: red;
}
4 changes: 4 additions & 0 deletions src/css/components/_progress.scss
Expand Up @@ -17,6 +17,10 @@
display: none;
}

.vjs-liveui .vjs-progress-control {
@include display-flex(center);
}

.vjs-no-flex .vjs-progress-control {
width: auto;
}
Expand Down
2 changes: 2 additions & 0 deletions src/js/control-bar/control-bar.js
Expand Up @@ -10,6 +10,7 @@ import './time-controls/duration-display.js';
import './time-controls/time-divider.js';
import './time-controls/remaining-time-display.js';
import './live-display.js';
import './seek-to-live.js';
import './progress-control/progress-control.js';
import './fullscreen-toggle.js';
import './volume-panel.js';
Expand Down Expand Up @@ -58,6 +59,7 @@ ControlBar.prototype.options_ = {
'durationDisplay',
'progressControl',
'liveDisplay',
'seekToLive',
'remainingTimeDisplay',
'customControlSpacer',
'playbackRateMenuButton',
Expand Down
3 changes: 2 additions & 1 deletion src/js/control-bar/progress-control/load-progress-bar.js
Expand Up @@ -54,8 +54,9 @@ class LoadProgressBar extends Component {
* @listens Player#progress
*/
update(event) {
const liveTracker = this.player_.liveTracker;
const buffered = this.player_.buffered();
const duration = this.player_.duration();
const duration = liveTracker.isLive() ? liveTracker.seekableEnd() : this.player_.duration();
const bufferedEnd = this.player_.bufferedEnd();
const children = this.partEls_;

Expand Down
13 changes: 2 additions & 11 deletions src/js/control-bar/progress-control/mouse-time-display.js
Expand Up @@ -3,7 +3,6 @@
*/
import Component from '../../component.js';
import * as Fn from '../../utils/fn.js';
import formatTime from '../../utils/format-time.js';

import './time-tooltip';

Expand Down Expand Up @@ -55,18 +54,10 @@ class MouseTimeDisplay extends Component {
* from the left edge of the {@link SeekBar}
*/
update(seekBarRect, seekBarPoint) {
const time = seekBarPoint * this.player_.duration();

// If there is an existing rAF ID, cancel it so we don't over-queue.
if (this.rafId_) {
this.cancelAnimationFrame(this.rafId_);
}

this.rafId_ = this.requestAnimationFrame(() => {
const duration = this.player_.duration();
const content = formatTime(seekBarPoint * duration, duration);

this.getChild('timeTooltip').updateTime(seekBarRect, seekBarPoint, time, () => {
this.el_.style.left = `${seekBarRect.width * seekBarPoint}px`;
this.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
});
}
}
Expand Down
22 changes: 7 additions & 15 deletions src/js/control-bar/progress-control/play-progress-bar.js
Expand Up @@ -3,7 +3,6 @@
*/
import Component from '../../component.js';
import {IS_IOS, IS_ANDROID} from '../../utils/browser.js';
import formatTime from '../../utils/format-time.js';

import './time-tooltip';

Expand Down Expand Up @@ -40,24 +39,17 @@ class PlayProgressBar extends Component {
* from the left edge of the {@link SeekBar}
*/
update(seekBarRect, seekBarPoint) {
const timeTooltip = this.getChild('timeTooltip');

// If there is an existing rAF ID, cancel it so we don't over-queue.
if (this.rafId_) {
this.cancelAnimationFrame(this.rafId_);
if (!timeTooltip) {
return;
}

this.rafId_ = this.requestAnimationFrame(() => {
const time = (this.player_.scrubbing()) ?
this.player_.getCache().currentTime :
this.player_.currentTime();
const time = (this.player_.scrubbing()) ?
this.player_.getCache().currentTime :
this.player_.currentTime();

const content = formatTime(time, this.player_.duration());
const timeTooltip = this.getChild('timeTooltip');

if (timeTooltip) {
timeTooltip.update(seekBarRect, seekBarPoint, content);
}
});
timeTooltip.updateTime(seekBarRect, seekBarPoint, time);
}
}

Expand Down

0 comments on commit 2974ad3

Please sign in to comment.