Video Signage

In this tutorial, we'll build a digital signage app for a chocolate shop that alternates between showing a web page and playing some streaming video.

It will run in a continuous loop, displaying a word with some fancy styling for ten seconds, and then a short video of some delicious chocolate until it finishes playing.

We don't need to use the Shaka Player, since we're not building an interactive app. We'll display the text in foreground mode and then stream the video in background mode by calling the Remote Player directly.

HTML

Our index.html file is very simple, and contains just one word centered on the page.

<!DOCTYPE html>
<html>
<head>
  <title>Chocolate</title>
	<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="main">
  <div id="word"></div>
</div>
</body>
<script src="https://senza-sdk.streaming.synamedia.com/latest/bundle.js"></script>
<script type="text/javascript" src="stopwatch.js"></script>
<script type="text/javascript" src="chocolate.js"></script>
</html>

And here's the beginning of our styles.css file. We'll include the Lovely Coffee font, loved by coffee shops everywhere.

@font-face {
  font-family: "LovelyCoffee";
  src: url("fonts/LovelyCoffee.woff2") format('woff2');
}

body {
  width: 1920px;
  height: 1080px;
  margin: 0px;
  background-color: black;
  overflow: hidden;
}
  
#main {
  position: relative;
  width: 1920px;
  height: 1080px;
  color: #EDE6D6;
  background-color: black;
  font: 600pt "LovelyCoffee"; 
  text-align: center;
  text-shadow: 00px 5px 5px white;

  animation-duration: 1s;
  animation-fill-mode: forwards;
  animation-timing-function: ease;
}

Text

All the code will go in the file chocolate.js. We'll start with some code for displaying a random chocolatey word, using a selection of chocolatey colors for the background, text and shadow.

const textSeconds = 8;
const words = ["chocolate", "delicious", "delightful", "dark", "rich",
  "smooth", "creamy", "velvety", "lovely", "coffee", "mocha"];
const colors = [
  "#7B3F00", // chocolate
  "#513B1C", // milk-chocolate
  "#3F000F", // dark chocolate
  "#EDE6D6", // white chocolate
  "#D2691E", // caramel
];

window.addEventListener("load", async () => {
  try {
    await senza.init();
    updateText("chocolate");
    senza.uiReady();
  } catch (error) {
    console.error(error);
  }
});

function updateText(value = null) {
  // get three unique random colors
  let someColors = shuffleArray(colors);
  main.style.backgroundColor = someColors.shift();
  main.style.color = someColors.shift();
  main.style.textShadow = "00px 7px 7px " + someColors.shift();

  word.innerHTML = value || randomObject(words);
  word.style.animationName = "dissolve-in";
  main.style.animationName = "fade-in";
  
  setTimeout(async () => {
    word.style.animationName = "dissolve-out";
    main.style.animationName = "fade-out";
    setTimeout(async () => await playVideo(), 1000);
  }, textSeconds * 1000);
}

When the text is updated, we'll dissove the word in and fade the page in.

After eight seconds, we'll dissolve the word out and fade the page out, then play a video.

Animations

We'll use CSS animations that we'll use to animate the word in and out, and fade the main div in and out. Here's the rest of our styles.css file.

#main {
  animation-duration: 1s;
  animation-fill-mode: forwards;
  animation-timing-function: ease;
}

#word {
  animation-duration: 1s;
  animation-fill-mode: forwards;
  animation-timing-function: ease;
}

@keyframes dissolve-in {
  0% { 
    transform: scale(0.7);
    filter: blur(4px);
    opacity: 0.0; 
  }
  100% { 
    transform: scale(1.0);
    filter: blur(0px);
    opacity: 1.0; 
  }
}

@keyframes dissolve-out {
  0% { 
    transform: scale(1.0);
    filter: blur(0px);
    opacity: 1.0; 
  }
  100% { 
    transform: scale(2.0);
    filter: blur(4px);
    opacity: 0.0; 
  }
}

@keyframes fade-in {
  0% { opacity: 0.0 }
  100% { opacity: 1.0 }
}

@keyframes fade-out {
  0% { opacity: 1.0 }
  100% { opacity: 0.0 }
}

Video

We'll use ten short video streams available at the link below. These are stock videos originally downloaded as MP4 files and encoded into DASH format using AWS MediaConvert. For more information, see the Encoding Video guide.

When we're ready to play a video, we'll load it into the Remote Player, play it, and use the Lifecycle object to move to background mode.

const videoLink = "https://senzadev.net/streams/chocolate/chocolate";

async function playVideo() {
  let url = videoLink + randomNumber(0, 9) + ".mpd";
  try {
    await senza.remotePlayer.load(url);
    await senza.remotePlayer.play();
    await senza.lifecycle.moveToBackground();
  } catch (e) {
    console.error("error playing", e)
  }
}

We'll upload the window's load handler with this code that listens for the video to end. When that happens, it will update the text and switch back to foreground mode.

    senza.remotePlayer.addEventListener("ended", () => {
      updateText();
      senza.lifecycle.moveToForeground();
    });

Remote

For debugging purposes, we'll add a key handler that lets you press the OK button to play the video, and the back button to return to the web view.

document.addEventListener("keydown", async function(event) {
	switch (event.key) {
    case "Enter": await playVideo(); break;
    case "Escape": senza.lifecycle.moveToForeground(); updateText(); break;
		default: return;
	}
	event.preventDefault();
});

Stopwatch

When you write an interactive app, the amount of the time the app spends in foreground versus background modes depends upon the user input. For signage apps, the app's behavior is entirely scripted so it's possible to measure the typical foreground time.

The app includes a class called Stopwatch that you can use to profile your app. All you have to do is import the stopwatch.js script, create a global variable, and instantiate an instance of the class.

let stopwatch;

window.addEventListener("load", async () => {
  try {
    await senza.init();
    ...
    stopwatch = new Stopwatch();
    ...
    senza.uiReady();
  } catch (error) {
    console.error(error);
  }
});

It will then display a box in the top left corner that shows the amount of time spent in foreground and background modes, as well as the percent of time in the foreground. It's only visible in foreground mode, but when it returns from background mode it measures the amount of time elapsed.

The app will alternate between spending nine seconds in foreground mode and then playing one of the videos, which range in length between 10 and 30 seconds. When you run the app for a whle with the stopwatch enabled, you'll see that the time in foreground stabilizes around 40%.

See the separate Stopwatch tutorial for a complete explanation of how the Stopwatch works.