Sunday, October 27, 2013

A Simple Chromecast Media Streamer

The Google Chromecast became available over the summer to great anticipation and excitement.

I decided to buy one for a couple of reasons :
  1. I needed a way to play Netflix on one of my TV's that didn't currently have that ability.
  2. I wanted a way to cheaply and easily play videos that I had sitting on a NAS.
For 2. I had actually made some attempts via alternative methods using a Raspberry Pi running a number of different versions of XMBC. While initially things would look promising, when it actually came to using it anger things fell apart. After reading up on the Chromecast SDK  it seemed like I could write my own media streamer fairly simply.

So what follows is details of my attempt. Overall it was an interesting investigation into what can be achieved with the Chromecast with a small amount of code. Some things work well and other things not so much. Particularly the media status listener support seemed somewhat flaky.

The source code for the application is available here. The majority of the chromecast interaction can be found in  chromecast.js.

To try it out you must
  1. Checkout the app from github and run npm install
  2. Get your URL(s) whitelisted 
  3. Modify receiver.html and chromecast.js setting your application id
  4. Point your whitelisted URL at your modified receiver.html
  5. Add the domain you plan to run the sender web application on to the Chrome browser Chromecast setttings. See the "Whitelisting Chrome Apps" section here 
  6. Run the server via "node lib/server.js [port] [path to media]"
  7. Point your Chromecast enabled browser at http://[host]:[port] to load the Media List Page
I decided (at least initially) to go the Google Chrome route for the application.


For the receiver I ended up using the sample one from cast-chrome-sender-helloworld. I just had to hook it up to my whitelisted URL and host it somewhere. In the end I just put it on a Jetty instance running on the Raspberry Pi. To make it available I setup a port forward in my home networks router pointing at the Raspberry Pi jetty instance.


The sender part of the application runs in the Chrome Browser.
  • It provides access to the list of media available on the NAS (actually this can be any filesystem directory accesible to the web application). 
  • It enables the media to be played, started, stop and paused. It also enables the play position to be set
I wrote the sender in javascript running on Node.js. It's hosted within my home network running on my Raspberry Pi. This part did not have to visible outside of my home and that included the URL's for the media. I was happy to find that I could pass internally based URL's to the receiver that pointed to the node.js server and they would load fine.

The backend part of sender application simply provided a rest handler to :
  1. Get a list of available media
  2. Get and set the currently playing media
  3. Get and set the current activity id so that page reloads of the sender application can pick up on what the receiver is currently doing.
It also used a Connect static registration to enable loading the media from the NAS/filesystem.

For the frontend I decided to use a mobile friendly setup with the hopes that the Chrome browser in IOS and Android will eventually get cast support (neither of them currently do). It uses jQuery, jQuery Mobile, Backbone.js and Underscore.js with all modules in AMD format. My Zazl Optimizer is also used to provide dynamically built and optimized javascript to the frontend.

The media list page looks like this :

You can select one of the list media files to play or navigate to the Playing page.

The playing page looks like this :
You can select the Chromecast Receiver to play on, the current position by setting the slider value, and start, pause and stop the media itself.

Wednesday, August 28, 2013

Zazl AMD Optimizer and Node.js

When I first started writing the Zazl Optimizer I focused on providing a Java HTTP layer that could be used to support dynamic analysis and optimization of AMD based Web Applications. One of the  core analyzers within the Zazl AMD Optimizer is written in javascript, so with that in mind it made sense to also provide an HTTP layer written to run in Node.js.
Node.js has a number of static HTML libraries available. If one of those is combined with the Zazl AMD Optimizer you have an environment where you can write your AMD based application and serve up an optimized (concatenated and compressed) version of it to your browser based clients.

One of the most well known and used static HTML libraries is Connect. It's actually used within a large number of middleware Node.js based libraries such as Express. For the Zazl Optimizer's  purposes connect is used to serve up any static resource that is not handled by the optimizer. The packaging of the Zazl Optimizer for Node.js provides a Connect based server frontend. The section that starts up the http server looks like this (from the file found here):

    var connectOptimizer = zazloptimizer.createConnectOptimizer(appdir, compress);

    var app = connect()
        .use("/_javascript", connectOptimizer)


When creating the optimizer you give it the path to where the JavaScript resources reside and also whether to turn on compression. Also,  you can see above that the typical approach is taken to initialize the connect environment. The path is first checked for "_javascript" and directed to the optimizer to be handled if matched. Otherwise a Connect static handler for the specified application directory is search and also one to handle finding Zazl's AMD loader that the application code references. To take advantage of the AMD loader handler simply reference it as follows in the HTML file :

    <script type="text/javascript" src="loader/amd/zazl.js"></script>

That's more or less all there is to setting up usage. You can see more in two sample github repositories, one with Dojo samples and one with JQuery samples. Also both are hosted here and here.  

Note: The hosting site (Heroku) puts both apps to sleep after being idle for 1hour. Don't be surprised if the first load(s) takes some time. Subsequent loads will demonstrate the full potential. Alternatively you can download the source from the repositories and run them yourself.