Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
23
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

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

この記事はファーストサーバの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の詳細に関しては次回書いてみようと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
23
Help us understand the problem. What are the problem?