WebOS Goodies

WebOS の未来を模索する、ゲームプログラマあがりの Web 開発者のブログ。

WebOS Goodies へようこそ! WebOS はインターネットの未来形。あらゆる Web サイトが繋がり、共有し、協力して創り上げる、ひとつの巨大な情報システムです。そこでは、あらゆる情報がネットワーク上に蓄積され、我々はいつでも、どこからでも、多彩なデバイスを使ってそれらにアクセスできます。 WebOS Goodies は、さまざまな情報提供やツール開発を通して、そんな世界の実現に少しでも貢献するべく活動していきます。
Subscribe       

Hangout 上でアプリケーションを構築できる Google Hangouts API の使い方

Hangouts API の仕様が大幅に変更されたため、改訂版を掲載しました。

皆さん、 Google+ の Hangout は使っていますでしょうか。 Google+ のウリの機能のひとつで、無料で複数人数のビデオチャットができる便利なサービスです。私も遠方とのミーティングなどに活用していて、 Google+ でもっとも頻繁に利用している機能かもしれません(笑)。

2 週間ほど前だと思いますが、この Hangout を拡張するための Google Hangouts API が公開されました。 Google ガジェット(OpenSocial ガジェット)に Hangout と連携するための拡張を施したもので、 Hangout と連携するアプリケーションを HTML5 / JavaScript で簡単に構築できます。まだ開発者向けプレビューの段階ですが、とても興味深い機能が多数実装されています。

そこで、本日はその Hangouts API の使い方をご紹介します。リファレンスとしても使えるように API の各メソッドの簡単な説明も掲載しているので、 Hangouts API でなにができるのか、簡単に把握できるはずです。ぜひ参考にしてください。

Hangouts API で Hello World

まずはお決まりとして、 Hangouts API で「Hello World」と表示するだけのアプリケーションを作ってみます。手順としては、最初に API Console で Hangouts API を有効にし、ガジェット XML を作成・登録して Hangout を起動する、といったものになります。 API Console は他の Google API でも利用するので、この機会になれておくといいと思います。

Hangouts API を有効にする

Hangouts API を使ってアプリケーションを作成するには、まず Google の API Console で Hangouts API を有効にしなければいけません。 API Console は Google の各種 API の管理をするために最近公開されたページで、 OAuth 2.0 のクライアントキーの発行や API トラフィックの確認・フィルタリングなどが行えます。 Hangouts API では、 API 自体の有効化とガジェット XML の登録のために API Console を利用します。

以下のリンクから、 API Console を開いてください。

https://code.google.com/apis/console

もし API Console を使うのが初めてであれば、以下の画面が表示されると思います。「Create project…」という青いボタンをクリックして、最初のプロジェクトを作成してください。もし過去に API Console を使ったことがあれば、既存のプロジェクトが使い回せるので、新たに作る必要はありません。

プロジェクトを作成したら、以下の画面が表示されるはずです。左のメニューから「Services」を選択し、「Google+ Hangouts API」の項目を ON に変更してください。

利用規約が表示されるので、「I agree to these terms.」にチェックして、「Accept」をクリックしてください。

元の画面に戻ります。「Google+ Hangouts API」が ON になり、左のメニューに「Hangouts」という項目が増えているのがわかると思います。

これで Hangouts API が有効になりました。

ガジェット XML を作成

前述のとおり、 Hangouts API は Google ガジェット(OpenSocial ガジェット)をベースにしています。したがって、アプリケーションのメインとなるソースコードはガジェット XML になります。 Hello World のガジェット XML はこんな感じになるでしょう。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Hello World">
    <Require feature="rpc"/>
    <Require feature="views"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
<!DOCTYPE html>

<script src="//hangoutsapi.appspot.com/static/hangout.js"></script>

<h1>Hello World!</h1>

    ]]>
  </Content>
</Module>

Google ガジェットの開発方法については Google のドキュメントなどを参照してください。基本的には、上記の Content タグ内に BODY タグの内容を書いておけば、それがそのまま表示されると考えておけば良いでしょう。 Content タグ内には、 hangout.js を読み込む SCRIPT タグを必ず入れるようにしてください。

ガジェット XML ができたら、それを公開 Web サーバー上にアップロードしておいてください。面倒なら、以下の URL で同じ内容を公開しているので、こちらを利用していただいても OK です。

https://webos-goodies.googlecode.com/svn/trunk/blo...

アプリケーションを起動する

現在のところ、 Hangouts API は開発者向けプレビューの段階なので、一般の Hangouts では使えません。 Hangouts API で構築したアプリケーションを利用するには、 API Console から特別な Hangout を作成します。再び API Console を開き、左のメニューで「Hangouts」を選択してください。

「Gadget URL」に先ほどのガジェット XML の URL を入力し、「Save」をクリックすれば、 URL が登録されて「Enter a hangout!」というリンクが表示されます。これをクリックすることで、 Hangouts API をサポートした Hangout が起動します。

ビデオチャットルームに参加すると、画面下に「アプリケーション」という見慣れないアイコンがあるのに気付くと思います。これをクリックすることで、 Hangouts API で作成したアプリケーションが起動します。以下は Hello World アプリケーションを起動したところです。

このように、メインのビデオ画面の上に被さるようにアプリケーションが表示されます。また右上にある 3 つのボタンにより、他のアプリケーションを読み込んだり(Get My Apps)、アプリケーションをリロードしたり(Reload app)、後述の共有ステートの内容をリセットしたり(Reset app state)といったことが可能です。

なお、 Hangouts API で構築したアプリケーションは API Console でチームメンバとして登録されたユーザーしか使えません。他のユーザーに参加してもらうときは、事前に API Console の「Team」タブで登録しておいてください。

API リファレンス & コード例

Hangouts API では、通常の Google ガジェットの機能に加えて Hangout と連携するための独自 API が利用できます。参加者の情報取得はもちろん、通知の表示やマイク・カメラなどの制御、リアルタイムのデータ共有など、とても興味深い機能が満載です。以降では、それらの API について簡単なリファレンスとコード例をご紹介していきます。これを読んでいただけば、 Hangouts API の使い方がほぼ理解できるはずです。

なお、記事に掲載しているコード例は JavaScript の部分のみで、対応する HTML は省略しています。それらを含めた完全なガジェットは以下の場所で公開していますので、ご利用ください。

https://webos-goodies.googlecode.com/svn/trunk/blo...

また、以降のコード例は jQuery が読み込まれていることを前提にしているので、ご了承ください。

API の初期化 (gapi.hangout)

多くの Hangouts API の機能を呼び出すためには、非同期で行われる API の初期化が完了するのを待たなければなりません。従って、アプリケーションの初期化は以下のように addApiReadyListener で指定したコールバック中で行うようにします。

gapi.hangout.addApiReadyListener(function() {
  // アプリケーションの初期化コード
});

まあ、 JavaScript ではアプリケーションの初期化を contentready 等で行うのが半ば常識ですから、それが addApiReadyListener に変わるだけです。ただし、状態変化を追跡するイベントハンドラの登録などは API の初期化を待たずに行うほうが良い場合もあるようです。詳しくは以降で説明します。

API 初期化関連のメソッドとしては、他に以下のものがあります。

isApiReady()
Hangout API が初期化されていたら true を返す。
addApiReadyListener(callback)
Hangouts API が利用可能になったときに呼び出されるコールバック関数を追加する。もし API が初期化済みであれば、コールバックは次回のイベント・ディスパッチで呼び出される。
removeApiReadyListener(callback)
addApiReadyListener によって追加されたコールバック関数を削除する。

基本機能 (gapi.hangout)

現在の Hangout の ID 、参加者の ID 、ロケール、アプリケーションといった Hangout の基本的な情報を取得するための API です。

isAppVisible()
メインの Hangout ウィンドウでアプリケーションが表示されていれば true を、そうでなければ false を返す。
hideApp()
アプリケーションを隠し、メインウィンドウにビデオを表示する。非表示の間もアプリケーションは動作し続ける。
addAppVisibleListener(callback)
アプリケーションの表示状態が切り替わったときに呼ばれるコールバック関数を追加する。
removeAppVisibleListener(callback)
addAppVisibleListener で追加されたコールバックを削除する。
getHangoutId()
この Hangout の ID を文字列で返す。この Hangout 専用の URL を構築するために使用できる。
getLocale()
この Hangout の参加者のロケールを返す。例 : 'en-US'
getParticipantId()
ローカル参加者のテンポラリな Hangout ID を返す。それぞれの参加者は Hangout に参加するたびに新しい Hangout ID を割り振られる。

これらの API の使用例を以下に示します。 Hangout の ID、ロケール、参加者の ID を表示し、ボタンでアプリケーションを隠すこともできるようにしています。 JS コードだけですが、必要な HTML の構造などは全体のソースコードに含まれているので、そちらをご参照ください。

gapi.hangout.addApiReadyListener(function() {
  $('#hangout-id').text(gapi.hangout.getHangoutId());
  $('#locale').text(gapi.hangout.getLocale());
  $('#participant-id').text(gapi.hangout.getParticipantId());
  $('#hide-app').click(function() { gapi.hangout.hideApp(); });
});

なお、公式リファレンスでは Hangout の ID と参加者の ID をともに「Hangout ID」と読んでいるようです。どうやら、「Hangout ID」は Hangouts API で扱う各種 ID の総称で、その中に Hangout の ID や参加者の ID が含まれる、という考え方のようです。

参加者情報の取得 (gapi.hangout)

Hangouts API には、 Hangout に参加しているユーザーの情報を取得する機能があります。取得できるユーザー情報 (Participant) は Hangouts API の People のサブセットになっていて、以下のフィールドが含まれます。ただし、常に有効なフィールドは hangoutIdhasAppInstalled のみで、他のフィールドは該当する参加者がアプリケーションを実行していないと取得できません。

フィールド名データ型説明
hangoutIdstring参加者を識別する Hangout ID 。 Google+ ID とは異なります。
hasAppInstalledbooleanもし参加者が現在のアプリケーションをインストールして実行していれば true を返す。
idstring参加者の Google+ ID 。プロフィールページの URL に含まれる ID と同じ。
displayNamestring表示に適した参加者名。
image.urlstring参加者の画像の URL 。

参加者情報を取得するためのメソッドとして、以下のものが用意されています。

getParticipants()
ハングアウト参加者を Participant の配列で返す。参加者のリストは現在のサーバーの状態を反映しているので、ローカル参加者(getParticipantId で返る)が返り値の配列の中に含まれるまでには、若干のタイムラグがある。
getParticipantById(hangoutId)
hangoutId に対応する参加者を Participant オブジェクトで返す。
addParticipantsListener(callback)
この Hangout 内の参加者に何らかの変更があったときに呼び出されるコールバックを追加する。コールバックには引数として Participant の配列が渡される。
removeParticipantsListener(callback)
addParticipantsListener で追加されたコールバックを削除する。
addParticipantAddedListener(callback)
この Hangout に新しく参加者があったときに呼び出されるコールバックを追加する。コールバックには前回イベントが発生した後に参加した参加者が Participant の配列で渡される。
removeParticipantAddedListener(callback)
addParticipantAddedListener で追加されたコールバックを削除する。
addParticipantRemovedListener(callback)
この Hangout から参加者が退出したときに呼び出されるコールバックを追加する。コールバックには前回イベントが発生した後に退出した参加者が Participant の配列で渡される。
removeParticipantRemovedListener(callback)
addParticipantRemovedListener で追加されたコールバックを削除する。

これらの API を利用して、 Hangout の参加者リストを表示する例を以下に示します。

function updateParticipants(participants) {
  var parentEl = $('#participants').empty();
  $.each(participants, function() {
    var name = this.hangoutId;
    if(this.displayName)
      name = this.displayName + ' (' + this.id + ')';
    $('<li/>').text(name).appendTo(parentEl);
  });
}
gapi.hangout.addApiReadyListener(function() {
  updateParticipants(gapi.hangout.getParticipants());
  gapi.hangout.addParticipantsListener(updateParticipants);
});

アプリケーション起動時の参加者リストを表示するだけでなく、 addParticipantsListener のイベントで随時更新されるようにしています。 Hangouts API では、刻々変化する状態をこのようにイベントを使って処理するパターンが基本になります。これらのイベントは DOM イベントとはまったく別物で、ハンドラに渡される引数もイベントごとに異なるので注意してください。

アプリケーションのインストール情報 (gapi.hangout)

前述のとおり、 Hangouts API では参加者が該当アプリケーションをインストールしているかどうかによって取得できる情報が制限される場合があります。したがって、どの参加者がアプリケーションをインストールしているかは、けっこう重要な情報です。そこで、この情報をリアルタイムに追跡できる機能が用意されています。

getAppParticipants()
アプリケーションをインストールしている参加者を Participant の配列で返す。
addAppParticipantsListener(callback)
現在のアプリケーションをインストールした参加者の配列に変化があった場合に呼ばれるコールバックを追加する。コールバックには Participant の配列が渡される。
removeAppParticipantsListener
addAppParticipantsListener により追加されたコールバックを削除する。
addAppParticipantAddedListener(callback)
この Hangout の参加者が現在のアプリケーションを追加したときに呼ばれるコールバックを追加する。コールバックには前回イベントが発生した後にアプリケーションを追加したユーザーを格納した Participant 配列が渡される。
removeAppParticipantAddedListener(callback)
addAppParticipantAddedListener で追加されたコールバックを削除する。
addAppParticipantRemovedListener(callback)
参加者がこのアプリケーションを削除したときに呼ばれるコールバックを追加する。コールバックには前回イベントが発生した後にアプリケーションを削除したユーザーを格納した Participant 配列が渡される。
removeAppParticipantRemovedListener(callback)
addAppParticipantRemovedListener で追加されたコールバックを削除する。

以下はアプリケーションをインストールした参加者のみを表示する例です。

function updateAppParticipants(participants) {
  var parentEl = $('#app-participants').empty();
  $.each(participants, function() {
    if(this.hasAppInstalled) {
      $('<li/>').text(this.displayName || this.hangoutId).appendTo(parentEl);
    }
  });
}
gapi.hangout.addApiReadyListener(function() {
  updateAppParticipants(gapi.hangout.getAppParticipants());
  gapi.hangout.addAppParticipantsListener(updateAppParticipants);
});

通知 (gapi.hangout)

新たに参加者が増えた時などに、 Hangout ウィンドウの上部に通知が表示されるのを見たことがあると思います。 Hangouts API を利用すると、アプリケーションで同様の通知を表示できます。永続的な通知と一定時間で消える通知があり、通知が表示・削除された際のイベントも用意されています。しかし、私が試した時点では通知に日本語を含めるとエスケープシーケンス (\uXXXX) で表示されてしまいました。これは改善して欲しいところです。

hasNotice()
現在、通知が表示されていれば true を、そうでなければ false を返す。
displayNotice(message, opt_permanent)
Hangout ウィンドウの上部に通知を表示する。 opt_permanent が true なら、通知は dismissNotice が呼ばれるか優先度の高い通知が表示されるまで残る。 false なら、通知は一定時間で消える。
dismissNotice()
現在表示中の通知を消す。
addHasNoticeListener(callback)
通知が表示された時、もしくは消されたときに呼び出されるコールバックを追加する。コールバックには現在の通知の表示状態を示す boolean 値が渡される。
removeHasNoticeListener(callback)
addHasNoticeListener により追加されたコールバックを削除する。

以下はリンクがクリックされたときにメッセージを表示・削除するためのコードです。

gapi.hangout.addApiReadyListener(function() {
  $('#notice').click(function() {
    if(gapi.hangout.hasNotice()) {
      gapi.hangout.dismissNotice();
    } else {
      gapi.hangout.displayNotice('hogehoge', true);
    }
  });
});

アクティブスピーカーの取得・変更 (gapi.hangout)

通常、 Hangout ではアクティブスピーカー(メイン画面に表示される参加者)を自動で選択します。これは便利な機能ですが、場合によっては画面が頻繁に切り替わってウザいこともありますよね。 Hangouts API を使うと、この自動選択の機能を無効にして、アクティブスピーカーを自由に指定できます。

getActiveSpeaker()
現在アクティブスピーカーの Hangout ID を返す。自動選択が有効になっている場合でも、呼び出した時点でのアクティブスピーカーが取得できる。
setActiveSpeaker(hangoutId)
hangoutId で指定された参加者をアクティブスピーカーに指定する。
clearActiveSpeaker()
setActiveSpeaker によるアクティブスピーカーの指定を解除し、自動選択に切り替える。
addActiveSpeakerListener(callback)
アクティブな参加者が変更されたときに呼ばれるコールバックを追加する。コールバックには新しくアクティブになった参加者の Hangout ID が引数として渡される。
removeActiveSpeakerListener(callback)
addActiveSpeakerListener により追加されたコールバックを削除する。

以下は現在のアクティブスピーカーを表示し、ラジオボタンで切り替えるためのコードです。

function showActiveSpeaker(hangoutId) {
  var p = gapi.hangout.getParticipantById(hangoutId);
  if(p) {
    $('#current-speaker').text(p.displayName || p.hangoutId);
  }
}

function updateActiveSpeakerSelector(participants) {
  var parentEl = $('#active-speaker').empty();
  $('<input type="radio" name="activespeaker" value="">自動<br />').appendTo(parentEl);
  $.each(participants, function() {
    parentEl.append(
      $('<input type="radio" name="activespeaker" />').attr({
        value:this.hangoutId
      }),
      $('<span />').text(this.displayName || this.hangoutId),
      $('<br />'));
  });
}

function changeActiveSpeaker() {
  var hangoutId = this.val();
  if(hangoutId) {
    gapi.hangout.setActiveSpeaker(hangoutId);
  } else {
    gapi.hangout.clearActiveSpeaker();
  }
}

gapi.hangout.addApiReadyListener(function() {
  showActiveSpeaker(gapi.hangout.getActiveSpeaker());
  gapi.hangout.addActiveSpeakerListener(showActiveSpeaker);

  updateActiveSpeakerSelector(gapi.hangout.getParticipants());
  gapi.hangout.addParticipantsListener(updateActiveSpeakerSelector);

  $('#active-speaker').change(changeActiveSpeaker);
});

AV機器の情報の取得 (gapi.hangout.av)

gapi.hangout.av 名前空間には、 Hangout で使用する AV 機器(カメラ、マイク、スピーカー)の状態取得・制御を行うメソッドが実装されています。さまざまな機能がありますが、まずは機器が利用可能かどうかを調査する API からご紹介します。

hasMicrophone()
マイクが接続されていて利用可能ならば true を、そうでなければ false を返す。
addHasMicrophoneListener(callback)
ローカルの参加者がマイクをアクティベート・デアクティベートしたときに呼ばれるコールバックを追加する。「アクティベート」はマイクが接続されていて利用可能なことを意味する(ミュート状態は関係ない)。コールバックにはマイクのアクティベート状態が boolean 値で渡される。
removeHasMicrophoneListener(callback)
addHasMicrophoneListener により追加されたコールバックを削除する。
hasCamera()
カメラが接続されていて利用可能ならば true を、そうでなければ false を返す。
addHasCameraListener(callback)
ローカル参加者がカメラをアクティベート・デアクティベートしたときに呼ばれるコールバックを追加する。「アクティベート」はカメラが接続されていて利用可能なことを意味する(ミュート状態は関係ない)。コールバックにはカメラのアクティベート状態が boolean 値で渡される。
removeHasCameraListener(callback)
addHasCameraListener により追加されたコールバックを削除する。
hasSpeakers()
スピーカーが接続されていて、利用可能ならば true を、そうでなければ false を返す。
addHasSpeakersListener(callback)
ローカル参加者がスピーカーをアクティベート・デアクティベートしたときに呼ばれるコールバックを追加する。「アクティベート」はスピーカーが接続されていて利用可能なことを意味する(ミュート状態は関係ない)。コールバックにはスピーカーのアクティベート状態が boolean 値で渡される。
removeHasSpeakersListener(callback)
addHasSpeakersListener により追加されたコールバックを削除する。

あまり使う機会は多くないかと思いますが、特定の機器が必須の処理を行うときには、これらの API であらかじめ接続をチェックしておいたほうが良いかもしれません。以下は各機器の接続状態を表示するコードの例です。

function updateAVStatus() {
  $('#camera-status').text(gapi.hangout.av.hasCamera() ? 'あり' : 'なし');
  $('#microphone-status').text(gapi.hangout.av.hasMicrophone() ? 'あり' : 'なし');
  $('#speakers-status').text(gapi.hangout.av.hasSpeakers() ? 'あり' : 'なし');
};
gapi.hangout.addApiReadyListener(function() {
  updateAVStatus();
  gapi.hangout.av.addHasCameraListener(updateAVStatus);
  gapi.hangout.av.addHasMicrophoneListener(updateAVStatus);
  gapi.hangout.av.addHasSpeakersListener(updateAVStatus);
});

上記のコードは状態に変化があったときに表示を更新するようになっていますが、私が試した限り、例えば新たにカメラを接続しても検出されることはありませんでした(イベントが発生しないだけではなく、 Hangout 自体が認識しない)。ハードや OS 依存の現象かもしれないので、 Windows ではきちんと検出されたりするのかもしれません。

AV 機器のミュート (gapi.hangout.av)

各機器のミュートを制御することも可能です。映像や音声の送信を一時的に停止できます。

getMicrophoneMute()
ローカル参加者のマイクがミュートされていたら true を、ミュートされていなければ false を返す。
setMicrophoneMute(muted)
ローカル参加者のマイクのミュート状態を設定する。 muted に true を指定するとミュート、 false ならミュート解除。
addMicrophoneMuteLitener(callback)
ローカル参加者のマイクのミュート状態が変化したときに呼ばれるコールバックを追加する。コールバックにはミュート状態が boolean 値で渡される。
removeMicrophoneMuteListener(callback)
addMicrophoneMuteListener により追加されたコールバックを削除する。
requestParticipantMicrophoneMute(participantId)
participantId で指定された参加者にオーディオをミュートするように促す。
getCameraMute()
ローカル参加者のカメラが映像を送信していれば true を、そうでなければ false を返す。
setCameraMute(muted)
ローカル参加者から他の参加者への映像の送信を開始・中断する。 muted に true を指定すれば映像送信を中断、 false なら再開。
addCameraMuteListener(callback)
ローカル参加者が映像の送信を開始・中断したときに呼ばれるコールバックを追加する。コールバックには映像の送信状態が boolean 値で渡される。
removeCameraMuteListener(callback)
addCameraMuteListener により追加されたコールバックを削除する。

以下は、ワンクリックで映像と音声の送信を停止するエマージェンシーボタン(笑)の実装例です。

var muted = false;
gapi.hangout.addApiReadyListener(function() {
  $('#emergency').click(function(e) {
    muted = !muted;
    gapi.hangout.av.setMicrophoneMute(muted);
    gapi.hangout.av.setCameraMute(muted);
  });
});

エマージェンシーボタンをクリックすると、映像がブランクになり、音声もミュートされます。もう一度クリックするとミュートを解除するのですが、どうやらまだバグがあるらしく、それ以降 video strip でのミュートの表示が逆になってしまうようです。

AV 機器のその他の制御 (gapi.hangout.av)

それぞれの参加者のマイクへの入力レベルを取得・監視したり、映像の代わりに表示するアバターを指定することも可能です。とくにマイクレベルは固定の値ではなくリアルタイムに変化する入力レベルを監視できるので、いろいろと面白い応用ができそうです。

getParticipantVolume(participantId)
participantId で指定した参加者のマイクへの入力レベルを 0 から 5 までの数値で返す。
getVolumes()
すべての参加者の入力レベルを取得する。返り値はキーに参加者の ID 、値に参加者のボリュームを格納したオブジェクト。
addVolumesChangedListener(callback)
いずれかの参加者の入力レベルが変化したときに呼ばれるコールバックを追加する。コールバックにはすべての参加者の新しいボリュームを格納したオブジェクトが渡される。
removeVolumesChangedListener(callback)
addVolumesChangedListener により追加されたコールバックを削除する。
isParticipantVisible(participantId)
participantId で指定した参加者の映像がローカル参加者に見えているなら true を、そうでなければ false を返す。
setParticipantVisible(participantId, visible)
participantId で指定した参加者の可視・不可視を設定する。 visible に true を指定すれば可視に、 false なら不可視になる。
getAvater(participantId)
participantId で指定した参加者のアバター画像の URL を返す。もし参加者にアバター画像が指定されていなければ undefined を返す。
setAvater(participantId, imageUrl)
participantId で指定した参加者の映像を imageUrl で指定したアバター画像に差し替える。
clearAvater(participantId)
participantId で指定された参加者の映像を復帰させる。

以下は、各参加者のマイクへの入力レベルを表示する例です。

var volumeElementMap = {};
function updateVolume(volumeInfo) {
  for(id in (volumeInfo || [])) {
    var span = volumeElementMap[id];
    if(span)
      span.text(volumeInfo[id]);
  }
}

function updateVolumeParticipants(participants) {
  volumeElementMap = {};
  var parentEl = $('#volume').empty();
  $.each(participants, function() {
    var span = $('<span />');
    var name = (this.displayName || this.id) + ' : ';
    $('<li/>').text(name).append(span).appendTo(parentEl);
    volumeElementMap[this.hangoutId] = span;
  });
}

gapi.hangout.addApiReadyListener(function() {
  updateVolumeParticipants(gapi.hangout.getParticipants());
  console.log(gapi.hangout.av.getVolumes());
  updateVolume(gapi.hangout.av.getVolumes());
  gapi.hangout.addParticipantsListener(updateVolumeParticipants);
  gapi.hangout.av.addVolumesChangedListener(updateVolume);
});

それぞれの参加者のマイクに入力されている信号レベルがリアルタイムで表示されます。これと前述のアクティブスピーカーの指定の機能を組み合わせれば、アクティブスピーカーの自動選択のアルゴリズムをカスタマイズ版を作ることもできそうですね。

共有ステート (gapi.hangout.data)

gapi.hangout.data 名前空間には、 Hangout への参加者間でリアルタイムにデータ共有するための API が実装されています。共有するデータ(共有ステート)はキーと値の双方が文字列の key/value データで、値の追加・更新・削除がほぼリアルタイムで他の参加者の共有ステートに反映されます。共有ステートを利用することで、すべての参加者が編集できるホワイトボードなどといったコラボレーションが簡単に実現できます。

getState()
現在の共有ステートをキーと値をオブジェクトで返す。
getStateMetadata()
現在の共有ステートのキーとメタデータをオブジェクトで返す。返り値のキーは getState と同じだが、値は MetaData 型のオブジェクトになる。
submitDelta(opt_updates, opt_removes)
共有ステートオブジェクトを更新する。 opt_updates には追加もしくは上書きする共有ステートのキーと値を格納したオブジェクトを指定する。値は必ず文字列でなければならない。 opt_removes には削除するキーを格納した配列を指定する。
addStateChangeListener(callback)
新しいバージョンの共有オブジェクトをサーバーから受け取ったときに呼ばれるコールバックを追加する。コールバックには 4 つの引数が渡される。第一引数は新たに追加された値を格納した MetaData 型の配列。第二引数は削除されたキーの文字列配列。第三引数は現在の共有ステートのキーと値を格納したオブジェクト。第四引数は現在の共有ステートのキーと MetaData を格納したオブジェクト。
removeStateChangeListener(callback)
addStateChangeListener により追加されたコールバックを削除する。

getStateMetaData などで取得できる MetaData 型(これは私が勝手に付けた名前なのでご注意くだだい)には、以下のフィールドがあります。こちらには更新された時刻もあるので、上書きの制御などもある程度可能でしょう。

フィールド名説明
keyデータの名前
value与えられたキーの値。値は常に文字列
timestampkey/value が最も最近更新されたサーバータイム
timediffサーバータイムとローカルアプリケーションの時間の差

以下は、共有ステートを使った「へぇーボタン」の実装例です。それぞれの参加者がへぇーボタンを押した回数がリアルタイムで表示されます。

var countElementMap = {};
function updateCount(adds, removes, state, metadata) {
  for(key in (state || {})) {
    if(countElementMap[key]) {
      countElementMap[key].text(state[key]);
    }
  }
};

function updateCountParticipants(participants) {
  var parentEl = $('#count');
  $.each(participants, function() {
    if(this.hasAppInstalled && !countElementMap[this.hangoutId]) {
      var span = $('<span />').text('0');
      var name = (this.displayName || this.hangoutId) + ' : ';
      $('<li/>').text(name).append(span).appendTo(parentEl);
      countElementMap[this.hangoutId] = span;
    }
  });
};

gapi.hangout.addApiReadyListener(function() {
  updateCountParticipants(gapi.hangout.getAppParticipants());
  gapi.hangout.addAppParticipantsListener(updateCountParticipants);
  gapi.hangout.data.addStateChangeListener(updateCount);
  $('#countup').click(function() {
    var id     = gapi.hangout.getParticipantId();
    var state  = gapi.hangout.data.getState() || {};
    var update = {}
    update[id] = ''+(((state[id] || 0) | 0) + 1);
    gapi.hangout.data.submitDelta(update);
  });
});

チャットウィンドウの制御 (gapi.hangout.layout)

gapi.hangout.layout 名前空間には、チャットの表示・非表示や参加者のハイライトなど、ガジェット外の表示を制御するための API が実装されています。

setChatPaneVisible(visible)
チャットパネルの表示・非表示を切り替える。 visible に true を指定するとチャットパネルを表示、 false なら非表示。
isChatPaneVisible()
チャットパネルが表示されていれば true 、そうでなければ false を返す。|
addChatPaneVisibleListener(callback)
チャットパネルの表示状態が変化したときに呼ばれるコールバックを追加する。コールバックにはチャットパネルの状態が boolean 値で渡される。
removeChatPaneVisibleListener(callback)
addChatPaneVisibleListener により追加されたコールバックを削除する。
getParticipantHighlight(participantId)
participantId で指定した参加者の video strip に表示がハイライトされていれば true を、そうでなければ false を返す。
setParticipantHighlight(participantId)
participantId で指定した参加者の video strip での表示をハイライトする。
clearParticipantHighlight(participantId)
participantId で指定した参加者のハイライト表示を解除する。

使ってみた感想

Hangouts API を使ってみて、思っていた以上に多くの機能があるのに驚きました。とくにマイクレベルの取得や共有ステートなど、リアルタイムに更新されるデータが多いのは新しいですね。 Google+ のユーザー ID が取れるので Google+ API とも連携できますし、これまでとはまったく違うアプリケーションが生まれそうな気がします。

しかし、それだけに API Console に登録した限られたユーザーしか使えないのは残念です。現状ではアプリケーションを作っても使いものにならないのが実情でしょう。

Gmail や Google Calendar のガジェットもそうなのですが、 Google はどうも API を用意するだけで、それを使ったアプリケーションのエコシステムを構築することにはまったく興味がないように思えます。これでは、以下に可能性のある API でもアプリケーションを作りたいとは思えません。

どうやら Google+ は社運をかけたプロジェクトのようですし、 Hangout はそのなかでも最も特徴的な機能です。今回はぜひ、そのあたりも含めて力を入れてほしいところです。

関連記事

この記事にコメントする

Recommendations
Books
「Closure Library」の入門書です。
詳しくはこちらの記事をどうぞ!
Categories
Recent Articles