Using ShakaPlayer with LightningJS
In this blog, we’ll take a look at extending the default media player in Lightning, and using it with ShakaPlayer to manage our playback.
Introduction
Now, you may be wondering why you would need any other player at all. The answer to that is rather simple. Lightning by default comes with support for regular HTML5 playback. That means you’ll be fine with playing simple mp4 files for example. However, once you get into the more complex territory of video playback through the likes of HLS and MPEG-Dash, and of course DRM, you’ll have to rely on a different player to handle playback. Lightning underwater simply uses a video tag for playback, so they’ll never be able to support those use cases in the current state.
In order to be able to support those more complex use cases, we’ll have to integrate an external player. For the sake of this blog, I’ve chosen ShakaPlayer. This has proven to work well on SmartTVs, and can be configured to a high degree. There are of course different players you can consider too. If you are interested in an easy overview of the players available, I’d recommend reading my blog on open-source video players.
Step 1: Initial setup
Before we get started, we’ll need to have a Lightning project ready and available to write code in. If you already have a project ready, you can continue to step 2.
To create a Lightning project, we first have to install the Lightning CLI:
npm install -g @lightningjs/cli
Once we have the Lightning CLI installed, we can scaffold a project with the following command:
lng create
You can walk through the options given by the CLI and decide for yourself what your preferred setup looks like. For the sake of this blog, it’s not too important which options you choose. I did pick Typescript, so that’s also the code you’ll see in this blog — feel free to decide for yourself of course!
After your project has been scaffolded, make sure to have the node modules installed via running npm i
in your command line.
Now that the project is set up, we can continue with the next step towards getting Shakaplayer running in your Lightning app.
Step 2: Creating a Player page.
As we generally don’t run players on the homepage of our apps, we’ll make a dedicated page for the player. This also means we’ll have to include routing into our application, for which we can use Lightning’s built-in Router. I’ll not go through all the steps of creating a Router, as that’s a whole beast in itself. For that, I’d recommend following the steps as defined in Lightning's own documentation.
We do need to create our Player page. This page of course needs to be filled in with your full user interface, but for the sake of this blog, we’ll two simple elements to this page. It starts with a single wrapper element that contains all the UI elements. We could use this for smoothly showing and hiding the interface. And inside this wrapper, we will implement a Play/Pause button that we will use for toggling the playback state. Any other UI elements would of course be placed in the wrapper element, but we won’t implement any others for this blog.
import {Lightning} from "@lightningjs/sdk";
import {RouterPage} from "./RouterPage"; // Defines isPage:true in the TypeConfig.
export default class Player extends Lightning.Component<Lightning.Component.TemplateSpecLoose, RouterPage> {
static override _template(): Lightning.Component.Template<Lightning.Component.TemplateSpecLoose> {
return {
Wrapper: {
alpha: 1
PlayPause: {
mount: 0.5,
x: 1920 / 2,
y: 1080 / 2,
rect: true,
w: 250,
h: 250,
color: 0x00000000,
Background: {
rect: true,
w: 250,
h: 250,
color: 0xffababab,
shader: {
type: Lightning.shaders.RoundedRectangle,
radius: 125
},
},
Text: {
mount: 0.5,
x: 125,
y: 125,
text: {
textColor: 0xffffffff,
text: 'Pause'
}
}
}
}
}
}
}
Step 3: Installing and importing ShakaPlayer
Rather simple of a step, we’ll need to get a reference to ShakaPlayer in our project. Now, there are several ways to achieve that. You could use a CDN reference and include that script in your index.html, but in the context of the Metrological Appstore, we actually don’t have access to that index. You could inject the code by manually creating a script tag with Javascript, but that’s also not the preferred method by Metrological. Rather, we’ll include it as an NPM package to be built into our application files. This is the preferred method as it will better allow Metrological to manage the resources used by the app in the context of their Appstore.
We start by installing the package from NPM, I personally used npm for this but use any package manager you want:
npm i shaka-player
Then we’ll import the ShakaPlayer reference at the top of our Player page:
import shaka from 'shaka-player';
Step 4: Connect Lightning with ShakaPlayer
Lightning has its own integration with video playback, so it is preferred to hook into this behaviour. Luckily Lightning makes this extremely easy to handle. We start by setting up the VideoPlayer element to work with our Player page:
import {VideoPlayer} from "@lightningjs/sdk";
override _firstActive() {
VideoPlayer.consumer(this);
VideoPlayer.loader(this._loadPlayback);
VideoPlayer.unloader(this._unloadPlayback);
}
In the above code, we execute three functions on the VideoPlayer. The first, consumer
, is there to allow our Player page to handle playback events (described here, we’ll not implement them in this tutorial). The loader
and unloader
functions we will use to handle the playback with ShakaPlayer. So let’s implement those now. You can see a very easy setup in the below example.
_setupShakaPlayer (videoEl: HTMLVideoElement) {
videoEl.autoplay = true;
this._player = new shaka.Player(videoEl);
}
async _loadPlayback (url: string, videoEl: HTMLVideoElement) {
this._setupShakaPlayer(videoEl);
await this._player.load(url);
}
async _unloadPlayback () {
await this._player.unload();
}
All we do is pass along the video element and playback URL to our newly created ShakaPlayer instance. You can see that we do set videoEl.autoplay = true
, this is used to immediately start playback. If you don’t want playback to immediately start, the autoplay
can of course be removed. Then, when the video is getting unloaded again, we tell ShakaPlayer to unload too.
Step 5: Starting Playback
Almost done! Our next step is rather simple. We have ShakaPlayer set up to work with the VideoPlayer object of Lightning. So all we need to do next is tell the VideoPlayer to start playing a video. For that, we’ll use the open
function, that we can call from for example the _active
life-cycle method:
override _active() {
VideoPlayer.open('https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.mpd')
}
In my example, I’ve used an MPEG-Dash stream, without DRM, which I’ve taken from the demo URLs page of Unified Streaming. And, if you’ve implemented the same code and opened up the Player page in your browser, you should have working playback!
Step 6: Playing and Pausing
The video playback part of this blog is now done. So that means you should be in a good place to get going with the rest of your video player. I do want to give you a quick example of how you can implement a working Play/Pause button, to give you some inspiration.
In the Player page setup, we had already created our UI element for the Play/Pause button. For our next step, we’ll hook it up some event handlers to let button clicks interact with the player. Let’s start by extracting our PlayPause button into a separate Lightning Component so that it can receive focus, and add some methods for changing the button state:
// Our Player page component.
static override _template(): Lightning.Component.Template<Lightning.Component.TemplateSpecLoose> {
return {
Wrapper: {
alpha: 1,
PlayPause: {
type: PlayPause
}
}
}
}
override _getFocused(): Lightning.Component {
return this.tag('PlayPause');
}
// Our new PlayPause component.
import {Lightning} from "@lightningjs/sdk";
export default class PlayPause extends Lightning.Component {
// @ts-ignore
_isPlaying: boolean
override _setup() {
this._isPlaying = true;
}
static override _template () {
return {
mount: 0.5,
x: 1920 / 2,
y: 1080 / 2,
rect: true,
w: 250,
h: 250,
color: 0x00000000,
Background: {
rect: true,
w: 250,
h: 250,
color: 0xffababab,
shader: {
type: Lightning.shaders.RoundedRectangle,
radius: 125
},
},
Text: {
mount: 0.5,
x: 125,
y: 125,
text: {
textColor: 0xffffffff,
text: 'Pause'
}
}
}
}
set isPlaying(isPlaying: boolean) {
this._isPlaying = isPlaying;
this.tag('Text').patch({
text: {
text: isPlaying ? 'Pause' : 'Play'
}
});
}
get isPlaying(): boolean {
return this._isPlaying;
}
}
The code above extracts our button into a separate component and adds the isPlaying
setter. We’ll use this to signify the play or paused state, which changes the button text to either ‘Play’ or ‘Pause’. In a real scenario, you would likely replace these with play and pause icons.
Now that we have our button set up, we can attach it to an event handler for the ‘enter’ button. Then, depending on the state of the button, we will either pause or resume the video playback:
override _handleEnter () {
const button = this.tag('PlayPause');
button.isPlaying = !button.isPlaying;
button.isPlaying ? VideoPlayer.play() : VideoPlayer.pause();
}
And we’re off to the races! With the above implementation added, clicking OK on the TV-Remote (or Enter on your keyboard) toggles between the play and pause states. That makes our player ready for use!
Step 7: Next steps
Okay, we’re not done yet, or rather, you are not done yet! Because this is obviously a very small implementation of what’s needed for a player. You’ll need a full UI that you can show and hide, controls for rewinding and fast-forwarding, and possibly the use of DRM. But the basics are there, you’ve just learned how to use ShakaPlayer with Lightning!
For your next steps, I recommend reading up on the documentation of ShakaPlayer itself. That gives you great insights into how to deal with the different events coming from the player (for example time updates or errors), you’ll definitely use some of them to make your UI elements work as expected.
I do hope this blog was helpful and has given you some quick insights into integrating ShakaPlayer into your Lightning application. If you do need additional help or have any questions, by all means, reach out! I can also recommend reading my other blogs about video playback, Lightning, development, and SmartTV development in general. They should give you some pointers for when you’re stuck during your development. You can find a bunch of those blogs down below, or visit my Medium profile for a complete overview.
PS. The code used in this blog is available on Github, if you’re interested!