By Janne Lahtela
We started our blog series about developing Smart TV applications in 2021 and they have been very popular. Since then, we have covered:
- How to get started
- General tips designing multiplatform TV applications and handling Problems & Solutions
- UI and UX in Smart TV apps
- Handling remote control keys
- Handling pointer events
- Building spatial navigation
- Creating a video player for Smart TV app
Let’s continue from where we got to last time. This time we are implementing a simple player user interface, which has play-pause, fast forwarding and rewinding features. I also think that we should move towards object-oriented programming, this will be useful when the project expands. If you are not familiar with object-oriented programming, I recommend getting to know it a bit before moving on to the actual code examples.
Smart TV app video player: Let’s get started!
First, let’s create a new html element for the Player UI into the index.html file.
My Awesome Smart-TV -app!
The id of the element is playerUI so we have a place where we can create all the buttons like play and pause, and maybe a progress bar if necessary. All the elements inside the playerUI are created in a playerUI.js file so our index.html stays as clean as possible.
Let’s create a playerUI.js file in the js directory, where we implement playerUI’s features and the key handler.
/**
* Player UI Constructor
* @param {object} the video player instance
*/
function playerUI(videoPlayer) {
this.videoPlayerInstance = videoPlayer;
this.playerUIonScreenTimer = null;
this.playerUIRootElement = document.getElementById("playerUI");;
this.playerUIonScreen = false;
this.playPauseButton = null;
this.init();
}
/**
* Creates the video player UI elements
*/
playerUI.prototype.init = function() {
let playPauseButton = document.createElement('div');
playPauseButton.setAttribute("id", "playPauseButton");
playPauseButton.classList.add("playing");
this.hide();
this.playerUIRootElement.appendChild(playPauseButton);
this.playPauseButton = playPauseButton;
}
/**
* Shows the player UI
*/
playerUI.prototype.show = function() {
this.playerUIonScreen = true;
this.playerUIRootElement.style.display = "block";
}
/**
* Checks is player UI on the screen
* @returns {boolean} is the player UI on the screen
*/
playerUI.prototype.isOnScreen = function() {
return this.playerUIonScreen || false;
}
/**
* Hides the player UI
*/
playerUI.prototype.hide = function() {
document.getElementById("playerUI").style.display = "none";
this.playerUIonScreen = false;
}
/**
* Handles all the key events which are coming to the player UI
* @param {object} event Key event
*/
playerUI.prototype.handleKeyPress = function(event) {
console.log("playerUI.prototype.handleKeyPress", event);
clearTimeout(this.playerUIonScreenTimer);
switch(event.keyCode) {
case RC_ENTER:
/**
* Toggles play and pause when enter is pressed
*/
this.videoPlayerInstance.playPause();
this.playPauseButton.classList.toggle("paused");
break;
case RC_LEFT:
/**
* Pressing left rewinds
*/
this.videoPlayerInstance.rewind();
break;
case RC_RIGHT:
/**
* Pressing right fast forwards
*/
this.videoPlayerInstance.fastForward();
break;
case KB_BACK:
case RC_BACK:
/**
* Pressing back stops a video stream
*/
stopVideo();
break;
default:
}
/**
* Hides player UI when there is no key events coming over three seeconds
*/
const that = this;
this.playerUIonScreenTimer = setTimeout(function() {
that.hide();
clearTimeout(that.playerUIonScreenTimer);
}, 3000);
}
/**
* Destroys the player UI after the video player stream is closed
*/
playerUI.prototype.destroy = function() {
this.hide();
document.getElementById("playerUI").innerHTML = "";
this.videoPlayerInstance = null;
}
Js/playerUI.js
DocBlocks tell where the functions are used. I recommend that you use DocBlocks so the next person who’s maintaining your code can do it easily.
Then we need to implement a couple of lines of code to the player.js so that our playpause button and fast forward+rewind works. Let’s Implement them to the end of the player.js file.
/**
* Toggles play and pause
*/
videoPlayerShaka.prototype.playPause = function() {
if(window.video.paused) {
window.video.play();
} else {
window.video.pause();
}
}
/**
* Fast forwards 5 seconds
*/
videoPlayerShaka.prototype.fastForward = function() {
window.video.currentTime += 5;
}
/**
* Rewinds 5 seconds
*/
videoPlayerShaka.prototype.rewind = function() {
window.video.currentTime -= 5;
}
Js/player.js
Now we have required changes in index.html and player.js, and we have created the playerUI.js file. The next step is to add some CSS changes to the styles.css which should be in the css folder. Instead of using images as a background let’s use SVG tags.
One easy way to toggle between play and pause is to just change a html elements class, in this case we use classes named as playing and paused.
#playerUI > #playPauseButton {
width: 280px;
height: 280px;
margin-left: 100px;
margin-top: 100px;
}
#playerUI > #playPauseButton.playing {
background-image: url('data:image/svg+xml,');
background-size: contain;
}
#playerUI > #playPauseButton.paused {
background-image: url('data:image/svg+xml,');
background-size: contain;
}
Css/styles.css
Now all we have left is the changes to the app.js file. Let’s start the changes by creating our own variable for the player UI almost at the beginning of the file just like the video player.
/**
* The video player UI
*/
let videoPlayerUI = null;
Js/app.js
Then we want to direct key events to the playerUI.js’s handleKeyPress-function. So, let’s add some way to handle this at the beginning of the app.js’s handleKeyPress-function.
/**
* Function which is called when a key is pressed
*/
function handleKeyPress(event) {
console.log('handleKeyPress', event)
const current = getFocus();
/**
* Directs key events to the player UI when a video stream is on
*/
if(videoPlayer !== null && videoPlayerUI !== null) {
if(!videoPlayerUI.isOnScreen()) {
videoPlayerUI.show();
}
videoPlayerUI.handleKeyPress(event);
return;
}
Js/app.js
Then a couple of changes left. First, playVideo-function, lets add of code for the player UI
/**
* Creates and starts video player
* @param {string} url
*/
async function playVideo(url) {
root.style.display = "none";
videoPlayer = new videoPlayerShaka(url);
videoPlayerUI = new playerUI(videoPlayer);
}
Js/app.js
Then let’s add some cleaning when a video stream is stopped. Notice videoPlayerUI variable.
/**
* Stops and destroys
*/
function stopVideo() {
if(videoPlayer !== null) {
video.pause();
video.style.display = "none";
videoPlayerUI.destroy();
videoPlayerUI = null;
video.currentTime = 0;
window.video = null;
player.unload();
player.destroy();
videoPlayer = null;
root.style.display = "block";
}
}
Js/app.js
And now you should see when you press a key, and a video stream is rolling playing-icon.
And if you press enter/ok a video stream should stop, and the icon should change to paused icon.
And if you press right a video stream should go fast forward five seconds and vice versa when you press left.
What key features we are missing here in my option are e.g progress bar and a mouse event handler, and because we have started to also learn object-oriented programming it would be wise to refactor app.js file so it’s also coded object-oriented programming style.
Now we have scratched the surface of how to implement a Smart TV application. We have created an application where you can watch videos, but we have not invested in essential things such as user experience.
Five common mistakes to avoid in the future
As you approach the final stages of development and prepare to launch your Smart TV app, there are numerous pitfalls that can easily be missed. To ensure your app’s success and deliver a seamless user experience, it’s essential to keep in mind these common oversights:
- Not supporting different ways of navigation
For example, some LG televisions have support for their own pointer-enabled remote controller called the MRCU (Magic Remote Control Unit), which is a point and click device which triggers the pointer events on the supported and registered TV just like a mouse would. Read more about Pointer events.
- Making the player slow to navigate
The Player UI should have intuitive ways to navigate quickly to different parts of the timeline with a responsive forward and rewind functions. Accelerating movement in the timeline and video thumbnails are good features to enhance the user experience.
- Seeing setting subtitles as a secondary feature
In many regions (like here in Finland) we use subtitles basically almost 100% in foreign content. If the player forgets the default subtitle language or selecting the language is difficult, user frustration is guaranteed. Read more about user frustrations in our blog: TOP 5 Frustrations in Smart TV user experience.
- Neglecting Adaptive Streaming and Buffer Management
It’s critical to focus on streaming quality. Viewers expect a smooth experience, so ensuring your video player adapts to varying network conditions and manages buffering efficiently is key. This can prevent user frustration from video lags or quality drops. Regular streaming performance testing under different network scenarios is also essential to maintain a quality user experience.
- Worst mistake: forgetting platform fragmentation and compatibility
Given the diverse range of Smart TV platforms and operating systems(link) it’s essential to select and prioritize the devices and platforms your app should support. The smooth operation of the player is the most important aspect of your application. Only way to ensure this is to test the playback and UI functions with real Smart TVs. If you need a hand, our e-zoo includes over 200 devices, enabling manual and automated testing services for app developers. Contact us to know more.