Custom UI example
This example demonstrates how you can build a custom UI based on our API calls.
There are some important technical informations about a custom UI implementation:
- there is no progress event from the player, so you should query the progress periodically (using the `playing` event to disable unnecessary querying will increase performance)
- `progress` and `duration` is in seconds, so every percentage related progress data (like a seekbar) should be calculated from this two data
- if you would like to build a full custom UI on the player, use the `controls=false&showtitle=false` url params to hide the default IBM Video Streaming UI components
- autoplay permission must be delegated on cross-origin iframes since Chrome 66 for video elements by policy. Therefore, the iframe tag must contain an `allow="autoplay"` feature policy attribute to allow play calls from a cross-orgin parent to the player iframe. For more information visit https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#iframe
Custom UI pieces:
Volume:
HTML
<div class="player-container">
<iframe id="UstreamPlayer" src="//www.ustream.tv/embed/12762028?html5ui" frameborder="0" allow="autoplay" allowfullscreen webkitallowfullscreen scrolling="no"></iframe>
</div>
<div class="custom-ui-container">
<span class="button play">Play</span>
<span class="button pause">Pause</span>
<div class="volume-container">
Volume:
<div class="volume">
<div class="slider"></div>
</div>
</div>
<div class="seekbar">
<span class="timeLabel currentTime">00:00</span>
<span class="timeLabel duration">--:--</span>
<div class="progress"></div>
</div>
</div>
Javascript
//IBM Video Streaming Embed API instance
var embedApi = UstreamEmbed('UstreamPlayer');
//video duration will be stored in this variable
var videoDuration = 0;
//we will query the progress with an interval, we store it to be able to cancel it any time
var progressInterval = false;
var seekbarElement = document.querySelector('.seekbar');
var seekProgressElement = document.querySelector('.seekbar .progress');
var volumeElement = document.querySelector('.volume');
var currentTimeElement = document.querySelector('.currentTime');
var durationElement = document.querySelector('.duration');
/**
* API Methods
*/
// PLAY
document.querySelector('.play').addEventListener('click', function () {
embedApi.callMethod('play');
});
// PAUSE
document.querySelector('.pause').addEventListener('click', function () {
embedApi.callMethod('pause');
});
//VOLUME
document.querySelector('.volume').addEventListener('click', function (e) {
//Calculate the desired volume based on the click event position (this is just an example solution)
var volumePercentage = e.offsetX / volumeElement.offsetWidth * 100;
embedApi.callMethod('volume', volumePercentage);
document.querySelector('.volume .slider').style.width = volumePercentage + '%';
});
// SEEK
seekbarElement.addEventListener('click', function (e) {
//Calculate the desired progress based on the click event position (this is just an example solution)
var progressPercentage = e.layerX / seekbarElement.offsetWidth;
embedApi.callMethod('seek', videoDuration * progressPercentage);
});
/**
* API Events
*/
//duration is needed to calculate % progress
embedApi.addListener('duration', function (eventType, duration) {
videoDuration = duration;
durationElement.innerHTML = formatTime(videoDuration);
});
//this is not mandatory, but its unnecessary to query the progress when the playback is stopped.
embedApi.addListener('playing', function (eventType, playing) {
if (playing) {
startProgressInterval();
} else {
stopProgressInterval();
}
});
// Progress is not fired with an event from the player because of performance optimization purposes
// you have to query it with the interval you like.
function startProgressInterval() {
stopProgressInterval();
progressInterval = setInterval(function () {
embedApi.getProperty('progress', function (progress) {
updateSeekbar(progress);
});
}, 200);
}
function stopProgressInterval () {
clearInterval(progressInterval);
}
function updateSeekbar (progress) {
if (!videoDuration) return;
seekProgressElement.style.width = (progress / videoDuration) * 100 + '%';
currentTimeElement.innerHTML = formatTime(progress);
}
//format seconds to hh:mm:ss
function formatTime (sec) {
var time = new Date(sec * 1000),
hour = 60*60;
time = time.toISOString();
time = time.substr(11, 8);
if (+sec < hour) {
time = time.substr(3);
}
if (+sec > 24*hour) {
time = Math.floor(sec / hour) + time.substr(2);
}
return time;
}
CSS
.player-container {
//this padding-bottom workaround is the 16:9 (9/16) ratio's value for responsive support
padding-bottom: 56.25%;
position: relative;
width: 100%;
margin: 0 0 20px;
}
.player-container iframe {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
}
.custom-ui-container {
margin: 5px 0;
color: #fff;
font-size: 12px;
line-height: 30px;
}
.custom-ui-container .seekbar {
background: rgba(22, 22, 22, 0.8);
height: 30px;
margin: 5px 0;
user-select: none;
cursor: pointer;
}
.custom-ui-container .timeLabel {
float: left;
padding: 0 10px;
}
.custom-ui-container .timeLabel.duration {
float: right;
}
.custom-ui-container .progress {
background: #38f;
width: 0;
height: 100%;
}
.custom-ui-container .volume-container {
display: inline-block;
padding-left: 20px;
color: #2d2d2d;
}
.custom-ui-container .volume {
display: inline-block;
width: 80px;
height: 20px;
vertical-align: middle;
background: rgba(22, 22, 22, 0.8);
cursor: pointer;
}
.custom-ui-container .volume .slider {
width: 50%;
height: 100%;
background: #38f;
}