/* Codeboosh */

How to Add Chromecast Support to Your Website Videos

I recently had to add Chromecast to a client’s website and I couldn’t find a good guide on how to do it (the Chromecast documentation is a bit rubbish), so I thought I would create one with a full working example.

Requirements For Chromecast On Websites

  1. Your website needs to be running HTTPS in order to cast. This includes your local development environment.
  2. The file to be casted must be one of the supported file formats. In this guide we are going to cast a mp4 video.
  3. The JavaScript Chromecast API only works in Chrome based browsers such as Chrome and Edge.

Note: There are separate iOS and Android frameworks for using Chromecast within mobile applications. This guide looks at the JavaScript Web Sender framework for casting to a Chromecast device.

Getting Started With Some HTML

Firstly you need to add the Web Sender framework library to your website.

<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>

Next up let’s add a simple HTML video element with a mp4 file. We will also add a cast button, play button, pause button and a stop casting button, so that we can control the video when it’s being casted.

<div class="c-video__container js-video">

	<video controls class="c-video">
		<source src="https://media.w3.org/2010/05/sintel/trailer.mp4" type='video/mp4' class="js-video" />
	</video>

	<button type="button" class="c-video__button c-video__button--cast js-cast">
		<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
			<path d="M 6 6 C 4.897 6 4 6.897 4 8 L 4 12 L 6 12 L 6 8 L 26 8 L 26 24 L 18 24 L 18 26 L 26 26 C 27.103 26 28 25.103 28 24 L 28 8 C 28 6.897 27.103 6 26 6 L 6 6 z M 4 14 L 4 16 C 9.169375 16 13.436179 19.942273 13.949219 24.978516 C 13.983421 25.314265 14 25.655375 14 26 L 16 26 C 16 19.383 10.617 14 4 14 z M 4 18 L 4 20 C 7.309 20 10 22.691 10 26 L 12 26 C 12 21.589 8.411 18 4 18 z M 4 22 L 4 26 L 8 26 C 8 23.791 6.209 22 4 22 z" fill="currentColor"/>
		</svg>
		<span class="u-visually-hidden">Cast on Chromecast</span>
	</button>
	
	<div class="c-video__casting-controls js-casting-controls" aria-hidden="false">

	<button type="button" class="c-video__button js-play">
		<svg  width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
			<path d="M6 4l20 12-20 12z" fill="currentColor"></path>
		</svg>
		<span class="u-visually-hidden">Play video</span>
	</button>

	<button type="button" class="c-video__button js-pause">
		<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
			<path d="M4 4h10v24h-10zM18 4h10v24h-10z" fill="currentColor"></path>
		</svg>
		<span class="u-visually-hidden">Pause video</span>
	</button>

	<button type="button" class="c-video__button js-stop">
		<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
			<path d="M4 4h24v24h-24z" fill="currentColor"></path>
		</svg>
		<span class="u-visually-hidden">Stop casting video</span>
	</button>
   
  </div>
  
</div>

Chromecast JavaScript

First up we check that Chromecast is available using the following. When you use this in your project it might be nice to only show the button if Chromecast is available.

function initChromecast() {
	if(typeof chrome === undefined) {
		// not chrome
		return;
	}
	var loadCastInterval = setInterval(function() {
		if (chrome.cast.isAvailable) {
			clearInterval(loadCastInterval);
			initCastApi();
			buttonEvents();
		} else {
			// not available
		}
	}, 1000);
}

Next up we initialise the Chromecast API. There are more options available listed in the Chromecast docs.

function initCastApi() {
	cast.framework.CastContext.getInstance().setOptions({
		receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
		autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED
	});
}

Next we have a function that we will use to connect to a Chromecast session. We can check if there is an existing session using cast.framework.CastContext.getInstance().requestSession() and if not create one using cast.framework.CastContext.getInstance().getCurrentSession().

function connectToSession() {
	return Promise.resolve()
	.then(() => {
		var castSession = cast.framework.CastContext.getInstance().getCurrentSession();
		if (!castSession) {
		return cast.framework.CastContext.getInstance().requestSession()
		.then(() => {
			return Promise.resolve(cast.framework.CastContext.getInstance().getCurrentSession());
		});
		}
		return Promise.resolve(castSession);
	});
}

Next up lets add a click event to the cast button to connect to the Chromecast session and load the video. I’ve also included a few events to control the cast while the video is being casted.

function buttonEvents() {
  document.querySelector('.js-video').addEventListener('click', function(event) {
    if(event.target.classList.contains('js-cast')) {
      launchApp(); 
    }
    if(event.target.classList.contains('js-play')) {
      togglePlayPause(); 
    }
    if(event.target.classList.contains('js-pause')) {
      togglePlayPause(); 
    }
    if(event.target.classList.contains('js-stop')) {
      stopApp(); 
    }
  });
}

Now lets connect to the session using the promise we created above and load the video making sure to set the correct contentType for the video.

function launchApp() {

	// You could add an is connecting message here. E.g. Connecting to Chromecast...

	return connectToSession()
	.then((session)=> {
		var videoSrc = document.querySelector('.js-video').getAttribute('src');
		var mediaInfo = new chrome.cast.media.MediaInfo(videoSrc);
		mediaInfo.contentType = 'video/mp4';
		var request = new chrome.cast.media.LoadRequest(mediaInfo);
		request.autoplay = true;
		return session.loadMedia(request);
	})
	.then(()=> { 
		// Show controls
		document.querySelector('.js-casting-controls').setAttribute('aria-hidden', 'false');
		// Playing on Chromecast
		listenToRemote();
	})
	.catch((error)=> {	
		console.log(error);
		// TODO: Handle any errors here
	});
}

Chromecast allows you to listen to a number of events, which is super useful for controlling the cast. Here we are going to listen to any change in the player, useful for updating the state of play/pause buttons and we are going to listen to if the cast is disconnected.

function listenToRemote() {
	var player = new cast.framework.RemotePlayer();
	var playerController = new cast.framework.RemotePlayerController(player);

	playerController.addEventListener(
	cast.framework.RemotePlayerEventType.ANY_CHANGE, function() {
    	// you could update the play/pause button here or update the displayed time
		console.log(player.isPaused);
	});

	playerController.addEventListener(
	cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED, function() {
		if (!player.isConnected) {
			stopApp();
		}
	});
}

Finally, lets add some events for playing and pausing the cast with controls on your website and for stopping the cast.

function togglePlayPause() {
	var player = new cast.framework.RemotePlayer();
	var playerController = new cast.framework.RemotePlayerController(player);
	playerController.playOrPause();
}

function stopApp() {
	var castSession = cast.framework.CastContext.getInstance().getCurrentSession();
	if(castSession) {
		castSession.endSession(true);
	}
	// Hide casting controls
	document.querySelector('.js-casting-controls').setAttribute('aria-hidden', 'true');
}

Finally lets kick things off.

// kick things off
initChromecast();

Complete JavaScript Code

Below is the complete JavaScript code for our simple Chromecast example.

function initChromecast() {
    if(typeof chrome === undefined) {
      return;
    }
	var loadCastInterval = setInterval(function() {
		if (chrome.cast.isAvailable) {
			clearInterval(loadCastInterval);
			initCastApi();
			buttonEvents();
		} else {
			// not available
		}
	}, 1000);
}

function initCastApi() {
	cast.framework.CastContext.getInstance().setOptions({
		receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
		autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED
	});
}

function connectToSession() {
	return Promise.resolve()
	.then(() => {
		var castSession = cast.framework.CastContext.getInstance().getCurrentSession();
		if (!castSession) {
		return cast.framework.CastContext.getInstance().requestSession()
		.then(() => {
			return Promise.resolve(cast.framework.CastContext.getInstance().getCurrentSession());
		});
		}
		return Promise.resolve(castSession);
	});
}

function buttonEvents() {
  document.querySelector('.js-video').addEventListener('click', function(event) {
    if(event.target.classList.contains('js-cast')) {
      launchApp(); 
    }
    if(event.target.classList.contains('js-play')) {
      togglePlayPause(); 
    }
    if(event.target.classList.contains('js-pause')) {
      togglePlayPause(); 
    }
    if(event.target.classList.contains('js-stop')) {
      stopApp(); 
    }
  });
}

function launchApp() {

	// You could signal an is connecting message here. E.g. Connecting to Chromecast...
  
	return connectToSession()
	.then((session)=> {
		var videoSrc = document.querySelector('.js-video').getAttribute('src');
		var mediaInfo = new chrome.cast.media.MediaInfo(videoSrc);
		mediaInfo.contentType = 'video/mp4';
		var request = new chrome.cast.media.LoadRequest(mediaInfo);
		request.autoplay = true;
		return session.loadMedia(request);
	})
	.then(()=> { 
		// Show controls
		document.querySelector('.js-casting-controls').setAttribute('aria-hidden', 'false');
		// Playing on Chromecast
		listenToRemote();
	})
	.catch((error)=> {	
		console.log(error);
		// TODO: Handle any errors here
	});
}

function listenToRemote() {
	var player = new cast.framework.RemotePlayer();
	var playerController = new cast.framework.RemotePlayerController(player);

	playerController.addEventListener(
	cast.framework.RemotePlayerEventType.ANY_CHANGE, function() {
    	// you could update the play/pause button here or update the displayed time
		console.log(player.isPaused);
	});

	playerController.addEventListener(
	cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED, function() {
		if (!player.isConnected) {
			stopApp();
		}
	});
}

function togglePlayPause() {
	var player = new cast.framework.RemotePlayer();
	var playerController = new cast.framework.RemotePlayerController(player);
	playerController.playOrPause();
}

function stopApp() {
	var castSession = cast.framework.CastContext.getInstance().getCurrentSession();
	if(castSession) {
		castSession.endSession(true);
	}
	// Hide casting controls
   document.querySelector('.js-casting-controls').setAttribute('aria-hidden', 'true');
}

// kick things off
initChromecast();

Complete Chromecast Example

The full example can be found in this CodePen https://codepen.io/mattbegent/pen/yLvVNKB. Don’t forget in order to play with the example you need to have a Chromecast device setup on your network. Enjoy!

👍 Thanks for reading. Have a great rest of your day.