Video playback with Blits (Lightning3)

Matthijs Langendijk
6 min readDec 20, 2023

One of the most important parts of any TV application is the video player. In this blog we’ll dive into what it takes to implement a video player with Lightning 3, using the Blits framework.

Getting started

First and foremost we’ll need the basics of a Blits application. You can go to the next step if you already have your basic application created.

For the setup, we’ll use the instructions as defined in the Blits documentation. There are no other specific setup steps for our basic application, as the default application comes with everything we need (like a router).

Installation

  1. Open your terminal or command prompt.
  2. Run the following command to create a new Lightning 3 App project:
npx @lightningjs/blits@latest my_lightning3_app

This will generate the necessary boilerplate code in the folder my_lightning3_app for you to start building your own Lightning 3 App.

Navigate to the newly created project directory and install the project dependencies:

cd my_lightning3_app
npm install

Installing the required player dependencies

Now that we have our default application, we’ll need to install any dependencies of the video player we want to use. As the used video player heavily depends on your use case, the dependencies you’ll need to install also differ. In this blog we will make use of shaka player; if you’re interested in learning more about different video players you can take a look here (click).

So for our case, we’ll install shaka player and then continue with the next step:

npm install shaka-player

Creating a player manager

Something that I personally always aim to do, is create a middleman between the actual player and the user interface. That’s of course very good for the separation of concerns and adds an additional benefit of being able to implement different players at the same time and be able to switch between them on the fly. So we’ll create a ‘managers’ folder (you can name this a service, interface, or something different too, for that matter), and create our PlayerManager.js file.

Inside this file we’ll create some basic methods for setting up, configuring and controlling shakaplayer. As this player-manager will be a singleton instance that can be used from multiple places (useful if you want playback from multiple places), we’ll simply return a set of functions and properties that can be used. We’ll start simple in this blog, for more advanced functions you can of course use a similar approach.

The basic set of functions we need will roughly be the following, so let’s create the skeleton of this setup:

// src/managers/PlayerManager.js
import shaka from 'shaka-player'

let player
let videoElement

/**
* Initialises the player.
* @param {HTMLElement} [videoElement]. - The video element used to initiate playback in.
* @returns {Promise<void>}
*/
const init = async (element) => {
shaka.polyfill.installAll() // polyfilling for devices that need it.

videoElement = element

if (!videoElement) {
videoElement = document.createElement('video')

// Position our video element in the background.
Object.assign(videoElement.style, { position: 'absolute', left: 0, top: 0, zIndex: 0 })
videoElement.id = 'video-player'
videoElement.autoplay = true

// Position our app on top of the video element.
Object.assign(document.getElementById('app').style,{ position: 'absolute', left: 0, top: 0, zIndex: 1 })
document.body.insertBefore(videoElement, document.getElementById('app'))
}

player = new shaka.Player()
await player.attach(videoElement)
}

/**
* Loads the player.
* @param {Object} config - The player configuration.
* @returns {Promise<void>}
*/
const load = async (config) => {
if (!player || !videoElement) {
throw 'Player not initialised yet'
}

await player.load(config.streamUrl)
}

const getCurrentTime = () => {
return videoElement?.currentTime
}

const play = () => {
return videoElement?.play()
}

const pause = () => {
return videoElement?.pause()
}

export default {
init,
load,
getCurrentTime,
play,
pause
}

The above is a simple implementation of using shaka player, with some easy-to-use methods to initialise, load and control the player behaviour. You can also get the current time for displaying in your player UI with the getCurrentTime method.

Setting up a player route

Now that we have our player manager, we can start worrying about the user interface of it all. We’ll start by setting up a simple player route that we’ll use for displaying our video player and player user interface; so create a ‘Player.js’ file inside of our ‘pages’ folder. Next, you’ll need to register it as a route inside your App.js file:

// src/App.js
import Blits from '@lightningjs/blits'

import Home from './pages/Home.js'
import Player from './pages/Player.js'

export default Blits.Application({
template: `
<Element>
<RouterView />
</Element>
`,
routes: [
{ path: '/', component: Home },
{ path: 'player', component: Player },
],
})

Then, in order to reach our player route, we’ll have to navigate to it from any place in our application that has access to the $router object; for example inside an input event inside a page:

input: {
enter () {
this.$router.to('player')
}
}

Playing video and next steps

We’re ready to start playing video! With our route available and the player manager in a state to receive video, we can start working on displaying video on the screen. Inside our route, we’ll import the player manager and call the init and load functions with a stream URL to start playing video:

// src/pages/Player.js
import Blits from '@lightningjs/blits'
import PlayerManager from '../managers/PlayerManager.js'

export default Blits.Component('Player', {
template: `
<Element>
<Text color="black">Our awesome player</Text>
</Element>
`,

hooks: {
async init () {
await PlayerManager.init()
},
async ready () {
await PlayerManager.load({
streamUrl: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8'
})
}
}
})

And… that’s all there is to it! You should now be seeing a simple video UI (it only displays a black text), with the video playing behind it. This means we are now ready to start building our video UI, which I’ll totally leave up to you. You could add a play/pause button to work with the play and pause functions we had created, or maybe a progress bar to display the current time — the sky is the limit!

Don’t forget to destroy

Before I send you off on your adventure to play around with the implementation and build a user interface for your player, we should not forget one of the most important things — destroying. Memory management is incredibly important. Wouldn’t it be a shame if you left memory on the table for video you’re not actively using? After all, Lightning is the king of memory management, so let’s follow that principle and make sure we clean up our resources after we don’t need them anymore.

First, we’ll create a destroy method inside our player manager, and fill it with any cleanup we need:

// src/managers/PlayerManager.js
const destroy = async () => {
await player.destroy()

player = null
videoElement.remove()
videoElement = null
}

export default {
init,
load,
getCurrentTime,
play,
pause,
destroy
}

And then make sure to call the destroy function in the destroy hook of your player route:

// src/pages/Player.js
hooks: {
async destroy () {
await PlayerManager.destroy()
}
}

Concluding

That’s all! You’re now good to go and continue with your player implementation in your Lightning 3 app with Blits. Wasn’t that easy? To be fair, the beginning is always easy. The next step for you will be implementing a simple user interface to control the player with play, pause and maybe fast-forwarding and rewinding. And, of course, displaying relevant information like the current time, duration and maybe some metadata about the video that you’re watching. If you ever struggle with that, by all means, reach out to me personally; or you can always join the Lightning Discord where a lot of people can help you out. Regardless, a fun video playback development journey is ahead!

Oh and…

Want to learn more about Lightning, or SmartTV development in general? You could try one of these blogs, with a whole lot more useful information:

--

--