This library is an experiment to provide a sender API for Google Chromecast devices using mDNS and some TLS protobuf protocol instead of the DIAL discovery protocol. Early Chromecasts made use of DIAL but Google switched away from it. While SSDP/DIAL support is still present and some applications are still using it (like YouTube), existing applications have to migrate to the new SDK using the new protocol.
This library doesn't support DIAL/SSDP (and hence the applications relying on it). See nodecast instead.
npm install nodecastor --save
You may need this on Ubuntu:
sudo apt-get install libavahi-compat-libdnssd-dev
This is still a work in progress. The API is not stable, the quality is pretty low and there are a lot of bugs.
To use the library, you first need to discover what Chromecast devices are available on the network. This is an optional step as you can also declare a Chromecast manually from its IP address.
const util = require('util');
const nodecastor = require('nodecastor');
nodecastor.scan()
.on('online', device => {
console.log('New device', util.inspect(device));
})
.on('offline', device => {
console.log('Removed device', util.inspect(device));
})
.start();
On Linux, if no device is discovered, first check that your machine is able to do mDNS address resolution. The library used for this purpose delegates this to the libc. You should have something like that in /etc/nsswitch.conf
:
hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
Both online
and offline
events will invoke the callback with a CastDevice
instance. You can also create the CastDevice
instance manually:
new nodecastor.CastDevice({
friendlyName: 'My secret Chromecast',
address: '192.168.1.27',
port: 8009
});
Once you have a CastDevice
instance, you need to wait for the connection event. Then, you can request some informations about it:
d.on('connect', () => {
d.status((err, s) => {
if (!err) {
console.log('Chromecast status', util.inspect(s));
}
});
});
You can get updated when the status of the Chromecast changes by listening to the status
event:
d.on('status', status => {
console.log('Chromecast status updated', util.inspect(status));
});
You can also request an application. This will give you a CastApplication
instance.
d.application('YouTube', (err, a) => {
if (!err) {
console.log('YouTube application', util.inspect(a));
}
});
Once you have a CastApplication
instance, you can request a CastSession
instance for this application. For that, you can either join an existing application (which should already be running) or you can run a new instance.
a.run('urn:x-cast:com.google.cast.demo.tictactoe', (err, s) => {
if (!err) {
console.log('Got a session', util.inspect(s));
}
});
a.join('urn:x-cast:com.google.cast.demo.tictactoe', (err, s) => {
if (!err) {
console.log('Joined a session', util.inspect(s));
}
});
The first parameter is the namespace you expect to run or join. Any messages sent on this session will use the given namespace.
You can then send messages and receive answers (not all messages have to be answered):
s.send({ data: 'hello' }, (err, data) => {
if (!err) {
console.log('Got an answer!', util.inspect(data));
}
});
s.on('message', data => {
console.log('Got an unexpected message', util.inspect(data));
});
Don't use callbacks for messages that you don't expect answers for. They will just leak memory...
A CastSession
object can emit a close
event when the connection is closed. A CastDevice
object can emit a disconnect
event when the connection with the device is lost and a connect
event when the connection has been established (but there is no need to wait for such an event). You can close connection to a device with .stop()
.
Any object can take as an option a logger. For example:
const c = new CastDevice({
address: '192.168.1.27',
logger: console
});
By default, reconnection is tried on disconnect. Both Scanner
and CastDevice
constructors accept a reconnect
object as an options. When set to false
, no reconnection will be retried:
const c = new CastDevice({
address: '192.168.1.27',
reconnect: false
});
Otherwise, the provided object may contain some properties to influence the reconnection:
const c = new CastDevice({
address: '192.168.1.27',
reconnect: {
maxRetries: 10,
maxDelay: 10000, // ms
initialDelay: 100 // ms
}
});
The functionality of this library can be tested with the chromecast
helper. Invoke it with chromecast -h
to get help. It embeds some simple examples too.
The Tic Tac Toe application is quite incomplete. It is expected to play against a human player. Here is how to use it:
python -mSimpleHTTPServer
if you want a quick way to get a webserver to serve the files)chromecast tictactoe 192.168.1.24
.The Dashkiosk application is a test for the Dashkiosk project.
There is no formal description of the protocol. However, you can look at channel.js
which shows how to build low-level messages. The lower-level protocol is implemented directly in Chrome and the protocol is described in cast_channel.proto
.
The high-level protocol, used by the Chromecast extension, can be discovered by modifying the extension. The following code can be appended to background_script.js
:
chromesendAndLog = (channel, data) => {
console.log('[TAP CHROMECAST send]', data);
return chrome.cast.channel.send.apply(chrome.cast.channel, arguments);
};
chrome.cast.channel.onMessage.addListener((channel, data) => {
console.log('[TAP CHROMECAST recv]', data);
});
Any occurrence of chrome.cast.channel.send
needs to be replaced by chromesendAndLog
. Monkey-patching seems to be ineffective because the whole chrome.cast.channel
seems to be erased everytime you connect/disconnect to a Chromecast. Then, filter the log messages with TAP CHROMECAST
in Chrome developper tools (click on background.html
for the Chromecast extension in chrome://extensions
).