Node.js
ChromeCast
mDNS
castv2

[Signagify 3] Node.js で Sender アプリ作って mDNS + CastV2 で Chromecast とお話

More than 3 years have passed since last update.

この記事はファーストサーバのAdvent Calendar 2015の17日目として書きました。

http://qiita.com/advent-calendar/2015/firstserver

内容は16日目の続きです。

http://qiita.com/vanx2/items/00dcad135b9706d199a7

前回ざっくりとしたChromecastアプリの概要を書いたので、今回は実際にアプリを作ってみようと思います。いいぞもっとやれと大変不評だったので図をやめました。


やること

第一回で述べたとおり、スレート端末を使わずに複数台のChromecastへキャストをするためにはSDKを使わずに独自で実装する必要があります。1台のChromecastにキャストするだけなら提供されているSDKを使ったサンプルアプリを動かしてみてください。

https://developers.google.com/cast/docs/downloads

今回はSenderアプリをNode.jsで実装して、Default Media Receiverで動画を再生してみます。


Senderアプリ


■mDNSでChromecastを見つける

まずはネットワークに参加しているChromecastを見つけます。ChroemcastはAppleのBonjour(Zero-configuration networking)で利用されているmDNSという技術を利用しています。

mDNSはローカルネットワーク上の機器同士がサーバーを介さずにIPマルチキャストを使って名前とアドレスを交換しあう技術です。マルチキャストIPアドレス224.0.0.2515353ポートにUDPでクエリを投げると、同じローカルネットワーク内に接続されたmDNSに対応した機器がDNSのようなレコードを返します。

それでは実際にmDNSにqueryを投げて、返って来たresponseから必要な情報を抜き出してみましょう。

Node.jsのパッケージではmdnsが人気なようですが、

https://www.npmjs.com/package/mdns

外部モジュール(LinuxのmDNS実装であるAvahiやmDNSResponderなど)に依存していたので、私はpure javascriptだったmulticast-dnsを利用しました。

https://www.npmjs.com/package/multicast-dns

インストールしてー

$ npm i multicast-dns

name_googlecast._tcp.localPTRレコードをリクエストするクエリ投げるスクリプト書いてー


detect.js

var mdns = require('multicast-dns')()

mdns.on('response', function(response) {
console.log('got a response packet:', response)
})

// lets query for an PTR record for '_googlecast._tcp.local'
mdns.query({
questions:[{
name: '_googlecast._tcp.local',
type: 'PTR'
}]
})


実行するとー

$ node detect.js

got a response packet: { type: 'response',
qdcount: 0,
ancount: 1,
nscount: 0,
arcount: 3,
questions: [],
answers:
[ { name: '_googlecast._tcp.local',
type: 'PTR',
class: 1,
ttl: 120,
data: 'signage@ajito._googlecast._tcp.local' } ],
authorities: [],
additionals:
[ { name: 'signage@ajito._googlecast._tcp.local',
type: 'TXT',
class: 99999,
ttl: 4500,
data: 'id=6b2e9253d1d818dd2f2436677a98b19a' },
{ name: 've=04.md=Chromecast.ic=/setup/icon.png.fn=signage@ajito.ca=5.st=1.bs=FA8FCA57FB52.rs=signage.signage@ajito._googlecast._tcp.local',
type: 'SRV',
class: 99999,
ttl: 120,
data: [Object] },
{ name: 'signage@ajito.local',
type: 'A',
class: 99999,
ttl: 120,
data: '192.168.xxx.51' } ] }

Chromecastがこんなレコードを返してきます。他の機器もクエリを投げるので実行し続けているとChromecast以外のmDNSに対応した機器もレコードを返してきます。

返してきたPTRレコードにのりづけされたadditionalsセクションのtypeAのレコードのdataからIPアドレスが、nameからは名前が取得できます。

この例ではsignage@ajitoという名前をつけたChromecastのIPアドレスが192.168.xxx.51だということがわかります。


■Senderアプリの実装

見つけたChromecastのIPアドレスに対してcastv2プロトコルで接続し、標準で提供されているDefault media receiverを起動して動画を再生させてみます。

node-castv2-clientを利用します。

https://github.com/thibauts/node-castv2-client

インストールしてー

$ npm i castv2-client


# Windowsの場合は
C:> npm install castv2-client --no-optional
# らしい

ほぼほぼExamplesに書いてある通りですが、動画を再生して5秒後に再生時間2:00時点へと動画をスキップするように書いてみます。時代はES6だというのに牧歌的な書き方をしておりますが。。。


sender.js

var Client                = require('castv2-client').Client;

var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;

var content = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/big_buck_bunny_1080p.mp4';
var host='192.168.1.96'; // your IP address of chromecast

var client = new Client();
// connect to Chromecast
client.connect(host, function() {
console.log('connected! Launching DefaultMediaReceiver ...');
// launch Default Media Receiver app
client.launch(DefaultMediaReceiver, function(err, player) {
if(err) {console.log(err);return;}
console.log('app "%s" launched, loading media %s ...', player.session.displayName, content);

player.on('status', function(status) {
console.log('status broadcast playerState=%s', status.playerState);
});

// play a video
player.load({contentId: content}, { autoplay: true }, function(err, status) {
console.log('media loaded playerState=%s', status.playerState);

// Seek to 2 minutes after 5 seconds playing
setTimeout(function() {
player.seek(2*60, function(err, status) {
if(err) {console.log(err);return;}
console.log('skip to 2:00 playerState=%s', status.playerState);
});
}, 5000);
});
});
});
client.on('error', function(err) {
console.log('Error: %s', err.message);
client.close();
});


実行するとー

$ node sender.js

connected! Launching DefaultMediaReceiver ...
app "Default Media Receiver" launched, loading media http://commondatastorage.googleapis.com/gtv-videos-bucket/big_buck_bunny_1080p.mp4 ...
status broadcast playerState=IDLE
status broadcast playerState=BUFFERING
media loaded playerState=BUFFERING
status broadcast playerState=PLAYING
skip to 2:00 playerState=BUFFERING
status broadcast playerState=BUFFERING
status broadcast playerState=PLAYING
:

てな具合に ええ いきたいッスね いッスねー!イェー(以下略

Chromecastで動画は流れましたでしょうか。ネットワーク環境によってはバッファリングで時間がかかり、5秒以上経ってからスキップされるかもしれません。

というわけで。。。やっとmDNS触ってみましたが。。。Advent calenderの公開時間になってしまいそうなので今回はここまでとしてcastv2の詳細に関しては次回書いてみようと思います。