Chromecast - steps closer to a python native api

Fri 05 September 2014 by Fred Clift

So, after seeing this: https://gist.github.com/TheCrazyT/11263599 I got more interested in being able to speak the native chromecast api from python.

That lead me to this presentation by Sebastian Mauer: http://www.slideshare.net/mauimauer/chrome-cast-and-android-tv-add14 especially slide 20, which lead to a bit more google searching.

This lead to a couple of related projects by Vincent Bernat and Thibaut Séguy, nodecastor and node-castv2/node-castv2-client - both node.js apps that speak approximations of the same protocol that the chrome-browser plugin uses to talk to the chromecast device.

They are both based on the protobuf data format used internally and some places externally at Google. TL;DR: make a .proto file that defines a bunch of data fields and Googles protobuf code will automatically format data into and parse data out of very compact, efficient binary blobs that you can use for data serialization between various platforms and languages.

The .proto file used by chromium builds to enable the browser plugin to communicate with devices is used by nodecastor and node-castv2 to allow them to speak with chromecast devices.

To give you a flavor, here are partial contents of the cast_channel.proto file...

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package extensions.api.cast_channel;
message CastMessage {
  enum ProtocolVersion {
    CASTV2_1_0 = 0;
  }
  required ProtocolVersion protocol_version = 1;
  required string source_id = 2;
  required string destination_id = 3;
  required string namespace = 4;
  enum PayloadType {
    STRING = 0;
    BINARY = 1;
  }
  required PayloadType payload_type = 5;
  optional string payload_utf8 = 6;
  optional bytes payload_binary = 7;
}

Once you have run the google 'proto' compiler, you get a file: cast_channel_pb2.py that you can import into your programs and now have an easy way to encode your data across platforms/languages for rpc/data interchange.

In python it might look at simple as this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/env python
#properly formatted PONG response for a receiver PING request
import cast_channel_pb2
msg = cast_channel_pb2.CastMessage()
msg.protocol_version = msg.CASTV2_1_0
msg.source_id = "sender-0"
msg.destination_id = "receiver-0"
msg.payload_type = cast_channel_pb2.CastMessage.STRING;
msg.namespace=namespace['heartbeat']
msg.payload_utf8 = """{ "type": "PONG" }"""

A bunch of reverse-engineering by those guys yields a pretty good over view of the wire-level chromecast protocol.
See here.

So, I implemented a proof of concept in python using Googles protobuf python bindings, using TheCrazyTs gist as an outline. Eventually, I got the data encoded right, the lengths calculated right and presto! I can start the default media receiver and ask it to stream some media, all from native python code with no browser in the middle.

If you have an existing ssld connection to your chromecast, you could do this:

be_size = pack(">I",msg.ByteSize())
payload = be_size + msg.SerializeToString()

If you had already connected to a chromecast with a 'CONNECT' request, you might get a PING request from the receiver, which you could send this payload back to. If you want to see some actual code that can connect, get status, start the default media receiver and ask it to stream a video, see the actual code

See https://github.com/minektur/chromecast-python-poc for code and instructions to try it out yourself.

Next up: Write some 'real' networking code and integrate into pychromecast