カスタム ウェブ レシーバーに主要な機能を追加する

このページでは、カスタム ウェブ レシーバー アプリで使用できるコード スニペットと機能について説明します。

  1. Web Receiver に搭載されている組み込みのプレーヤー UI を表す cast-media-player 要素
  2. さまざまな UI 要素(background-imagesplash-imagefont-family など)のスタイルを設定するための cast-media-player 要素用の、CSS に似たカスタム スタイル設定。
  3. Web Receiver フレームワークを読み込むスクリプト要素。
  4. メッセージをインターセプトしてイベントを処理する JavaScript コード。
  5. 自動再生のキュー。
  6. 再生を構成するオプション。
  7. ウェブ レシーバーのコンテキストを設定するオプション。
  8. Web Receiver アプリがサポートするコマンドを設定するオプション。
  9. Web Receiver アプリケーションを起動する JavaScript 呼び出し。

アプリケーションの構成とオプション

アプリケーションを構成する

CastReceiverContext はデベロッパーに公開される最も外側のクラスで、基盤となるライブラリの読み込みを管理し、Web Receiver SDK の初期化を処理します。SDK には、アプリ デベロッパーが CastReceiverOptions を通じて SDK を設定できる API が用意されています。これらの構成は、アプリケーションの起動ごとに 1 回評価され、start の呼び出しでオプションのパラメータを設定するときに SDK に渡されます。

次の例は、送信者の接続がまだアクティブに接続されているかどうかを検出するデフォルトの動作をオーバーライドする方法を示しています。Web Receiver が送信者と maxInactivity 秒間通信できないと、SENDER_DISCONNECTED イベントがディスパッチされます。以下の構成では、このタイムアウトがオーバーライドされます。これは、IDLE 状態で接続されている送信者がない場合に Web Receiver アプリが Chrome リモート デバッガ セッションを終了できなくなるため、問題のデバッグに役立ちます。

const context = cast.framework.CastReceiverContext.getInstance();
const options = new cast.framework.CastReceiverOptions();
options.maxInactivity = 3600; // Development only
context.start(options);

プレーヤーを構成する

Web Receiver SDK は、コンテンツを読み込む際に、cast.framework.PlaybackConfig を使用した DRM 情報、再試行構成、リクエスト ハンドラなどの再生変数を設定できます。この情報は PlayerManager によって処理され、プレーヤーの作成時に評価されます。プレーヤーは、新しい読み込みが Web Receiver SDK に渡されるたびに作成されます。プレーヤーの作成後に PlaybackConfig に加えた変更は、次回のコンテンツの読み込みで評価されます。SDK には、PlaybackConfig を変更するための次のメソッドが用意されています。

  • CastReceiverOptions.playbackConfig: CastReceiverContext の初期化時にデフォルトの構成オプションをオーバーライドします。
  • PlayerManager.getPlaybackConfig(): 現在の構成を取得します。
  • PlayerManager.setPlaybackConfig(): 現在の構成をオーバーライドします。この設定は、後続のすべての読み込みに適用されます。また、再度オーバーライドされるまで、適用されます。
  • PlayerManager.setMediaPlaybackInfoHandler(): 現在の設定の上に読み込まれるメディア アイテムに対してのみ、追加の設定を適用します。ハンドラは、プレーヤーを作成する直前に呼び出されます。ここで行った変更は永続的なものではなく、getPlaybackConfig() に対するクエリには含まれません。次のメディア アイテムが読み込まれると、このハンドラが再度呼び出されます。

次の例は、CastReceiverContext を初期化するときに PlaybackConfig を設定する方法を示しています。この構成は、マニフェストを取得するための送信リクエストをオーバーライドします。ハンドラは、Cookie や認可ヘッダーなどの認証情報を使用して CORS アクセス制御リクエストを実行するように指定します。

const playbackConfig = new cast.framework.PlaybackConfig();
playbackConfig.manifestRequestHandler = requestInfo => {
  requestInfo.withCredentials = true;
};
context.start({playbackConfig: playbackConfig});

以下の例は、PlayerManager で提供されるゲッターとセッターを使用して PlaybackConfig をオーバーライドする方法を示しています。この設定では、1 セグメントが読み込まれた後にプレーヤーがコンテンツの再生を再開するように構成されます。

const playerManager =
    cast.framework.CastReceiverContext.getInstance().getPlayerManager();
const playbackConfig = (Object.assign(
            new cast.framework.PlaybackConfig(), playerManager.getPlaybackConfig()));
playbackConfig.autoResumeNumberOfSegments = 1;
playerManager.setPlaybackConfig(playbackConfig);

以下の例は、メディア再生情報ハンドラを使用して、特定の読み込みリクエストの PlaybackConfig をオーバーライドする方法を示しています。ハンドラは、アプリに実装されたメソッド getLicenseUrlForMedia を呼び出して、現在のアイテムの contentId から licenseUrl を取得します。

playerManager.setMediaPlaybackInfoHandler((loadRequestData, playbackConfig) => {
  const mediaInformation = loadRequestData.media;
  playbackConfig.licenseUrl = getLicenseUrlForMedia(mediaInformation.contentId);

  return playbackConfig;
});

イベント リスナー

Web Receiver SDK を使用すると、Web Receiver アプリでプレーヤー イベントを処理できます。イベント リスナーは、リスナーをトリガーするイベントを指定する cast.framework.events.EventType パラメータ(またはこれらのパラメータの配列)を受け取ります。デバッグに役立つ cast.framework.events.EventType の事前構成された配列は、cast.framework.events.category にあります。イベント パラメータは、イベントに関する追加情報を提供します。

たとえば、mediaStatus の変更がいつブロードキャストされているかを知るには、次のロジックを使用してイベントを処理します。

const playerManager =
    cast.framework.CastReceiverContext.getInstance().getPlayerManager();
playerManager.addEventListener(
    cast.framework.events.EventType.MEDIA_STATUS, (event) => {
      // Write your own event handling code, for example
      // using the event.mediaStatus value
});

メッセージ インターセプト

Web Receiver SDK を使用すると、Web Receiver アプリでメッセージをインターセプトし、それらのメッセージに対してカスタムコードを実行できます。メッセージ インターセプタは、インターセプトするメッセージのタイプを指定する cast.framework.messages.MessageType パラメータを受け取ります。

インターセプタは、変更されたリクエスト、または変更されたリクエスト値で解決される Promise を返す必要があります。null を返すと、デフォルトのメッセージ ハンドラを呼び出せなくなります。詳しくは、メディアの読み込みをご覧ください。

たとえば、読み込みリクエスト データを変更する場合は、次のロジックを使用してデータをインターセプトして変更します。

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, loadRequestData => {
      const error = new cast.framework.messages.ErrorData(
                      cast.framework.messages.ErrorType.LOAD_FAILED);
      if (!loadRequestData.media) {
        error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
        return error;
      }

      if (!loadRequestData.media.entity) {
        return loadRequestData;
      }

      return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
                                          loadRequestData.credentials)
        .then(asset => {
          if (!asset) {
            throw cast.framework.messages.ErrorReason.INVALID_REQUEST;
          }

          loadRequestData.media.contentUrl = asset.url;
          loadRequestData.media.metadata = asset.metadata;
          loadRequestData.media.tracks = asset.tracks;
          return loadRequestData;
        }).catch(reason => {
          error.reason = reason; // cast.framework.messages.ErrorReason
          return error;
        });
    });

context.start();

エラー処理

メッセージ インターセプタでエラーが発生した場合、Web Receiver アプリは適切な cast.framework.messages.ErrorTypecast.framework.messages.ErrorReason を返す必要があります。

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, loadRequestData => {
      const error = new cast.framework.messages.ErrorData(
                      cast.framework.messages.ErrorType.LOAD_CANCELLED);
      if (!loadRequestData.media) {
        error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
        return error;
      }

      ...

      return fetchAssetAndAuth(loadRequestData.media.entity,
                               loadRequestData.credentials)
        .then(asset => {
          ...
          return loadRequestData;
        }).catch(reason => {
          error.reason = reason; // cast.framework.messages.ErrorReason
          return error;
        });
    });

メッセージ インターセプトとイベント リスナー

メッセージ インターセプトとイベント リスナーの主な違いは次のとおりです。

  • イベント リスナーではリクエスト データを変更できません
  • イベント リスナーは、分析またはカスタム関数をトリガーするのに最適です。
playerManager.addEventListener(cast.framework.events.category.CORE,
    event => {
        console.log(event);
    });
  • メッセージ インターセプトを使用すると、メッセージをリッスンしてインターセプトし、リクエスト データ自体を変更できます。
  • メッセージ インターセプトは、リクエスト データに関するカスタム ロジックを処理する場合に最適です。

メディアの読み込み

MediaInformation には、cast.framework.messages.MessageType.LOAD メッセージにメディアを読み込むためのさまざまなプロパティ(entitycontentUrlcontentId など)が用意されています。

  • entity は、送信側アプリと受信側アプリの両方の実装で使用する推奨プロパティです。このプロパティは、再生リストまたはメディア コンテンツのディープリンク URL です。アプリケーションはこの URL を解析し、他の 2 つのフィールドの少なくとも 1 つに値を設定する必要があります。
  • contentUrl は、プレーヤーがコンテンツを読み込むために使用する再生アブル URL に対応しています。たとえば、この URL が DASH マニフェストを指します。
  • contentId には、再生可能なコンテンツの URL(contentUrl プロパティの URL と同様)、または読み込むコンテンツまたは再生リストの固有識別子を指定できます。 このプロパティを識別子として使用する場合は、アプリで contentUrl で再生可能な URL を入力する必要があります。

entity を使用して実際の ID または鍵パラメータを保存し、メディアの URL には contentUrl を使用することをおすすめします。この例を以下に示します。以下のスニペットでは、entityLOAD リクエストに含まれ、再生可能な contentUrl が取得されています。

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, loadRequestData => {
      ...

      if (!loadRequestData.media.entity) {
        // Copy the value from contentId for legacy reasons if needed
        loadRequestData.media.entity = loadRequestData.media.contentId;
      }

      return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
                                          loadRequestData.credentials)
        .then(asset => {
          loadRequestData.media.contentUrl = asset.url;
          ...
          return loadRequestData;
        });
    });

デバイスの機能

getDeviceCapabilities メソッドは、接続されているキャスト デバイスと、それに接続されている動画またはオーディオ デバイスのデバイス情報を提供します。getDeviceCapabilities メソッドは、Google アシスタント、Bluetooth、接続されたディスプレイやオーディオ デバイスのサポート情報を提供します。

このメソッドはオブジェクトを返します。このオブジェクトは、指定されたいずれかの列挙型を渡してその列挙型のデバイス機能を取得することでクエリできます。列挙型は cast.framework.system.DeviceCapabilities で定義されます。

この例では、ウェブ レシーバー デバイスが IS_HDR_SUPPORTED キーと IS_DV_SUPPORTED キーを使用して HDR とドルビービジョン(DV)を再生できるかどうかを確認します。

const context = cast.framework.CastReceiverContext.getInstance();
context.addEventListener(cast.framework.system.EventType.READY, () => {
  const deviceCapabilities = context.getDeviceCapabilities();
  if (deviceCapabilities &&
      deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED]) {
    // Write your own event handling code, for example
    // using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED] value
  }
  if (deviceCapabilities &&
      deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED]) {
    // Write your own event handling code, for example
    // using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED] value
  }
});
context.start();

ユーザー操作の処理

ユーザーは、送信アプリ(ウェブ、Android、iOS)、アシスタント搭載デバイスの音声コマンド、スマートディスプレイのタップ コントロール、Android TV デバイスのリモコンからウェブ レシーバー アプリを操作できます。Cast SDK にはさまざまな API が用意されています。これらの API を使用すると、Web Receiver アプリでこれらのインタラクションを処理し、ユーザー アクションの状態を通じてアプリの UI を更新できます。また、必要に応じて変更を送信してバックエンド サービスを更新できます。

サポートされているメディア コマンド

UI コントロールの状態は、iOS と Android のセンダー拡張コントローラ、タッチデバイスで実行されるレシーバ / リモコン アプリ、Android TV デバイスのレシーバアプリの MediaStatus.supportedMediaCommands によって制御されます。プロパティで特定のビット単位の Command が有効になっている場合、その操作に関連するボタンが有効になります。値が設定されていない場合、ボタンは無効になります。これらの値は、Web Receiver で次の方法で変更できます。

  1. PlayerManager.setSupportedMediaCommands を使用して特定の Commands を設定する。
  2. addSupportedMediaCommands を使用した新しいコマンドの追加
  3. removeSupportedMediaCommands を使用して既存のコマンドを削除する。
playerManager.setSupportedMediaCommands(cast.framework.messages.Command.SEEK |
  cast.framework.messages.Command.PAUSE);

レシーバーは、更新された MediaStatus を準備するときに、supportedMediaCommands プロパティの変更を含めます。ステータスがブロードキャストされると、接続された送信アプリはそれに応じて UI のボタンを更新します。

サポートされているメディア コマンドとタッチデバイスについて詳しくは、Accessing UI controls ガイドをご覧ください。

ユーザーの操作の状態を管理する

ユーザーが UI を操作するか音声コマンドを送信すると、コンテンツの再生と、再生中のアイテムに関連するプロパティを制御できます。再生を制御するリクエストは、SDK によって自動的に処理されます。現在再生中のアイテムのプロパティを変更するリクエスト(LIKE コマンドなど)では、受信側のアプリがそれらを処理する必要があります。SDK には、これらのタイプのリクエストを処理するための一連の API が用意されています。これらのリクエストをサポートするには、次のことを行う必要があります。

  • メディア アイテムを読み込むときに、MediaInformation userActionStates にユーザーの設定を行います。
  • USER_ACTION メッセージをインターセプトし、リクエストされたアクションを特定します。
  • MediaInformation UserActionState を更新して UI を更新します。

次のスニペットは、LOAD リクエストをインターセプトし、LoadRequestDataMediaInformation にデータを入力します。この場合、ユーザーは読み込み中のコンテンツを気に入っています。

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, (loadRequestData) => {
      const userActionLike = new cast.framework.messages.UserActionState(
          cast.framework.messages.UserAction.LIKE);
      loadRequestData.media.userActionStates = [userActionLike];

      return loadRequestData;
    });

次のスニペットは、USER_ACTION メッセージをインターセプトし、リクエストされた変更でバックエンドの呼び出しを処理します。次に、レシーバの UserActionState を更新するための呼び出しを行います。

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.USER_ACTION,
  (userActionRequestData) => {
    // Obtain the media information of the current content to associate the action to.
    let mediaInfo = playerManager.getMediaInformation();

    // If there is no media info return an error and ignore the request.
    if (!mediaInfo) {
        console.error('Not playing media, user action is not supported');
        return new cast.framework.messages.ErrorData(messages.ErrorType.BAD_REQUEST);
    }

    // Reach out to backend services to store user action modifications. See sample below.
    return sendUserAction(userActionRequestData, mediaInfo)

    // Upon response from the backend, update the client's UserActionState.
    .then(backendResponse => updateUserActionStates(backendResponse))

    // If any errors occurred in the backend return them to the cast receiver.
    .catch((error) => {
      console.error(error);
      return error;
    });
});

次のスニペットは、バックエンド サービスへの呼び出しをシミュレートします。この関数は UserActionRequestData をチェックしてユーザーがリクエストした変更の種類を確認し、アクションがバックエンドでサポートされている場合にのみネットワーク呼び出しを行います。

function sendUserAction(userActionRequestData, mediaInfo) {
  return new Promise((resolve, reject) => {
    switch (userActionRequestData.userAction) {
      // Handle user action changes supported by the backend.
      case cast.framework.messages.UserAction.LIKE:
      case cast.framework.messages.UserAction.DISLIKE:
      case cast.framework.messages.UserAction.FOLLOW:
      case cast.framework.messages.UserAction.UNFOLLOW:
      case cast.framework.messages.UserAction.FLAG:
      case cast.framework.messages.UserAction.SKIP_AD:
        let backendResponse = {userActionRequestData: userActionRequestData, mediaInfo: mediaInfo};
        setTimeout(() => {resolve(backendResponse)}, 1000);
        break;
      // Reject all other user action changes.
      default:
        reject(
          new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType.INVALID_REQUEST));
    }
  });
}

次のスニペットは、UserActionRequestData を取得して、MediaInformationUserActionState を追加または削除します。MediaInformationUserActionState を更新すると、リクエストされたアクションに関連付けられているボタンの状態が変更されます。この変更は、スマートディスプレイのコントロール UI、リモコン アプリ、Android TV UI に反映されています。また、iOS と Android の送信者向けの拡張コントローラの UI を更新するために、送信 MediaStatus メッセージを通じてブロードキャストされます。

function updateUserActionStates(backendResponse) {
  // Unwrap the backend response.
  let mediaInfo = backendResponse.mediaInfo;
  let userActionRequestData = backendResponse.userActionRequestData;

  // If the current item playing has changed, don't update the UserActionState for the current item.
  if (playerManager.getMediaInformation().entity !== mediaInfo.entity) {
    return;
  }

  // Check for existing userActionStates in the MediaInformation.
  // If none, initialize a new array to populate states with.
  let userActionStates = mediaInfo.userActionStates || [];

  // Locate the index of the UserActionState that will be updated in the userActionStates array.
  let index = userActionStates.findIndex((currUserActionState) => {
    return currUserActionState.userAction == userActionRequestData.userAction;
  });

  if (userActionRequestData.clear) {
    // Remove the user action state from the array if cleared.
    if (index >= 0) {
      userActionStates.splice(index, 1);
    }
    else {
      console.warn("Could not find UserActionState to remove in MediaInformation");
    }
  } else {
    // Add the UserActionState to the array if enabled.
    userActionStates.push(
      new cast.framework.messages.UserActionState(userActionRequestData.userAction));
  }

  // Update the UserActionState array and set the new MediaInformation
  mediaInfo.userActionStates = userActionStates;
  playerManager.setMediaInformation(mediaInfo, true);
  return;
}

音声コマンド

現在、アシスタント搭載デバイス用の Web Receiver SDK では、次のメディア コマンドがサポートされています。これらのコマンドのデフォルト実装は、cast.framework.PlayerManager にあります。

コマンド 説明
Play 一時停止状態から再生または再開します。
一時停止 再生中のコンテンツを一時停止します。
前へ メディアキュー内の前のメディア アイテムにスキップします。
次へ メディアキュー内の次のメディア アイテムにスキップします。
停止 現在再生中のメディアを停止します。
繰り返しなし キューの最後のアイテムの再生が完了したら、キュー内のメディア アイテムの繰り返しを無効にします。
シングルを繰り返す 現在再生中のメディアを無限に繰り返します。
全曲リピート キューの最後のアイテムが再生されたら、キュー内のすべてのアイテムを繰り返します。
全曲リピートとシャッフル キューの最後のアイテムの再生が終わったら、キューをシャッフルして、キュー内のすべてのアイテムを繰り返します。
シャッフル メディアキュー内のメディア アイテムをシャッフルします。
字幕のオン / オフ メディアの字幕を有効または無効にします。有効化 / 無効化は言語別に設定することもできます。
絶対時間までシークする 指定された絶対時間にジャンプします。
現在の時刻を基準とした時刻シークする 現在の再生時間を基準として、指定された時間だけ前または後に移動します。
もう一度プレイ 現在再生中のメディアを再起動するか、現在何も再生中でない場合は、最後に再生したメディア アイテムを再生します。
再生速度を設定する メディアの再生速度を変える。これはデフォルトで処理されます。SET_PLAYBACK_RATE メッセージ インターセプタを使用して、受信レートのリクエストをオーバーライドできます。

音声でサポートされているメディア コマンド

アシスタント搭載デバイスで音声コマンドでメディア コマンドがトリガーされないようにするには、まず、サポートするサポート対象のメディア コマンドを設定する必要があります。次に、CastReceiverOptions.enforceSupportedCommands プロパティを有効にして、これらのコマンドを強制的に適用する必要があります。Cast SDK 送信者とタップ対応デバイスの UI は、これらの設定を反映するように変更されます。このフラグが有効になっていない場合は、受信した音声コマンドが実行されます。

たとえば、送信側のアプリとタッチ対応デバイスで PAUSE を許可する場合は、その設定を反映するようにレシーバーも構成する必要があります。構成した場合、サポートされているコマンドのリストに含まれていない受信音声コマンドは破棄されます。

以下の例では、CastReceiverContext の起動時に CastReceiverOptions を指定しています。PAUSE コマンドのサポートを追加し、プレーヤーがそのコマンドのみをサポートするようにしました。これで、音声コマンドが SEEK などの別のオペレーションをリクエストすると、拒否されるようになります。コマンドがまだサポートされていないことがユーザーに通知されます。

const context = cast.framework.CastReceiverContext.getInstance();

context.start({
  enforceSupportedCommands: true,
  supportedCommands: cast.framework.messages.Command.PAUSE
});

制限するコマンドごとに個別のロジックを適用できます。enforceSupportedCommands フラグを削除し、制限するコマンドごとに受信メッセージをインターセプトします。ここでは SDK から提供されるリクエストをインターセプトし、アシスタント搭載デバイスに対して発行された SEEK コマンドによって Web Receiver アプリでシークがトリガーされないようにします。

アプリケーションでサポートされていないメディア コマンドの場合は、NOT_SUPPORTED など、適切なエラー理由を返します。

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.SEEK,
  seekData => {
    // Block seeking if the SEEK supported media command is disabled
    if (!(playerManager.getSupportedMediaCommands() & cast.framework.messages.Command.SEEK)) {
      let e = new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType
      .INVALID_REQUEST);
      e.reason = cast.framework.messages.ErrorReason.NOT_SUPPORTED;
      return e;
    }

    return seekData;
  });

音声アクティビティによるバックグラウンド処理

ユーザーの音声を聞き取る、応答するなどのアシスタント アクティビティが原因でキャスト プラットフォームがアプリの音声をバックグラウンドにする場合、アクティビティの開始時に NOT_IN_FOCUSFocusState メッセージがウェブ レシーバー アプリに送信されます。アクティビティが終了すると、IN_FOCUS を含む別のメッセージが送信されます。アプリと再生中のメディアによっては、FocusStateNOT_IN_FOCUS のときにメッセージ タイプ FOCUS_STATE をインターセプトして、メディアを一時停止できます。

たとえば、アシスタントがユーザーのクエリに応答する場合は、オーディオブックの再生を一時停止することをおすすめします。

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.FOCUS_STATE,
  focusStateRequestData => {
    // Pause content when the app is out of focus. Resume when focus is restored.
    if (focusStateRequestData.state == cast.framework.messages.FocusState.NOT_IN_FOCUS) {
      playerManager.pause();
    } else {
      playerManager.play();
    }

    return focusStateRequestData;
  });

音声指定の字幕言語

ユーザーが字幕の言語を明示的に指定しない場合、字幕に使用される言語は、コマンドを話しかけた言語と同じ言語になります。このような場合、受信メッセージの isSuggestedLanguage パラメータは、関連する言語がユーザーによって提案されたか、明示的にリクエストされたかを示します。

たとえば、「OK Google, 字幕をオンにして」というコマンドの isSuggestedLanguage は、コマンドが話しかけた言語から推定されたため true に設定されています。言語が明示的にリクエストされた場合(「OK Google, 英語の字幕をオンにして」など)、isSuggestedLanguagefalse に設定されます。

メタデータと音声のキャスト

音声コマンドはデフォルトで Web Receiver によって処理されますが、コンテンツのメタデータが完全かつ正確であることを確認してください。これにより、音声コマンドがアシスタントによって適切に処理され、Google Home アプリや Google Home Hub などのスマートディスプレイなどの新しいタイプのインターフェース全体でメタデータが正しく表示されます。

ストリーミング転送

セッション状態の保持はストリーム転送の基礎であり、ユーザーは音声コマンド、Google Home アプリ、スマートディスプレイを使用して、デバイス間で既存の音声ストリームと動画ストリームを移動できます。メディアの再生は一方のデバイス(ソース)で停止し、別のデバイス(宛先)で続行します。最新のファームウェアを搭載したキャスト デバイスは、ストリーム転送のソースまたは宛先として機能します。

ストリーム転送のイベントフローは次のとおりです。

  1. ソースデバイスで次の操作を行います。
    1. メディアの再生が停止します。
    2. ウェブ レシーバー アプリケーションが、現在のメディア状態を保存するコマンドを受信します。
    3. Web Receiver アプリケーションがシャットダウンされます。
  2. 移行先デバイスで次の操作を行います。
    1. Web Receiver アプリケーションが読み込まれます。
    2. ウェブ レシーバー アプリケーションが、保存されたメディアの状態を復元するコマンドを受信します。
    3. メディアの再生を再開します。

メディア状態の要素には次のものがあります。

  • 曲、動画、メディア アイテムの特定の位置またはタイムスタンプ。
  • より幅広いキュー(プレイリストやアーティスト ラジオなど)に入れられます。
  • 認証されたユーザー。
  • 再生状態(再生、一時停止など)。

ストリーム転送を有効にする

Web Receiver にストリーム転送を実装するには:

  1. STREAM_TRANSFER コマンドで supportedMediaCommands を更新します。
    playerManager.addSupportedMediaCommands(
    cast.framework.messages.Command.STREAM_TRANSFER, true);
  2. 必要に応じて、セッション状態の保持で説明されているように、SESSION_STATERESUME_SESSION のメッセージ インターセプタをオーバーライドします。これらをオーバーライドするのは、カスタムデータをセッション スナップショットの一部として保存する必要がある場合に限ります。それ以外の場合は、セッション状態を保持するデフォルトの実装でストリーム転送がサポートされます。

セッション状態の保持

Web Receiver SDK には、現在のメディア ステータスのスナップショットを取得し、ステータスを読み込みリクエストに変換して、読み込みリクエストでセッションを再開することにより、セッション状態を保持するウェブ Receiver アプリ向けのデフォルトの実装が用意されています。

Web Receiver によって生成された読み込みリクエストは、必要に応じて SESSION_STATE メッセージ インターセプタでオーバーライドできます。読み込みリクエストにカスタムデータを追加する場合は、loadRequestData.customData に配置することをおすすめします。

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.SESSION_STATE,
    function (sessionState) {
        // Override sessionState.loadRequestData if needed.
        const newCredentials = updateCredentials_(sessionState.loadRequestData.credentials);
        sessionState.loadRequestData.credentials = newCredentials;

        // Add custom data if needed.
        sessionState.loadRequestData.customData = {
            'membership': 'PREMIUM'
        };

        return sessionState;
    });

カスタムデータは、RESUME_SESSION メッセージ インターセプタの loadRequestData.customData から取得できます。

let cred_ = null;
let membership_ = null;

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.RESUME_SESSION,
    function (resumeSessionRequest) {
        let sessionState = resumeSessionRequest.sessionState;

        // Modify sessionState.loadRequestData if needed.
        cred_ = sessionState.loadRequestData.credentials;

        // Retrieve custom data.
        membership_ = sessionState.loadRequestData.customData.membership;

        return resumeSessionRequest;
    });

コンテンツのプリロード

ウェブ レシーバーは、キュー内の現在の再生アイテムの後にメディア アイテムをプリロードできます。

プリロード オペレーションでは、追加されるアイテムのいくつかのセグメントがプリロードされます。指定は、QueueItem オブジェクトの preloadTime 値に対して行われます(指定しない場合のデフォルトは 20 秒です)。時間は秒単位で、現在再生中のアイテムの終了時間からの相対時間で表されます。正の値のみ有効です。たとえば、値が 10 秒の場合、このアイテムは前のアイテムが完了する 10 秒前にプリロードされます。プリロードの時間が currentItem の残り時間よりも長い場合、プリロードは可能な限り早く行われます。そのため、queueItem でプリロードの値が非常に大きいと、現在のアイテムを再生するときはいつでも次のアイテムをプリロードするという効果を実現できます。ただし、この値は現在再生中のアイテムの帯域幅やストリーミング パフォーマンスに影響する可能性があるため、設定と選択はデベロッパーが行います。

プリロードは、HLS、DASH、スムーズ ストリーミング コンテンツでデフォルトで機能します。

キャスト デバイスは 1 つのメディア要素のみをサポートし、既存のコンテンツ アイテムの再生中にプリロードできないため、通常の MP4 動画や音声ファイル(MP3 など)はプリロードされません。

カスタム メッセージ

メッセージ交換は、Web Receiver アプリケーションの重要なインタラクション方法です。

送信者は、送信者が実行しているプラットフォーム(Android、iOS、ウェブ)用の送信者 API を使用して、Web Receiver にメッセージを発行します。イベント リスナーに渡されるイベント オブジェクト(メッセージのマニフェスト)には、データが特定のイベントタイプのプロパティを受け取るデータ要素(event.data)があります。

Web Receiver アプリケーションは、指定した名前空間でメッセージをリッスンできます。これにより、Web Receiver アプリケーションはこの名前空間プロトコルをサポートすることになります。その後、その Namespace で通信することを希望する接続送信者が、適切なプロトコルを使用します。

すべての名前空間は文字列で定義され、「urn:x-cast:」で始まり、その後に任意の文字列が続く必要があります。例: urn:x-cast:com.example.cast.mynamespace

次のコード スニペットは、Web Receiver で接続されている送信者からのカスタム メッセージをリッスンします。

const context = cast.framework.CastReceiverContext.getInstance();

const CUSTOM_CHANNEL = 'urn:x-cast:com.example.cast.mynamespace';
context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) {
  // handle customEvent.
});

context.start();

同様に、Web Receiver アプリケーションは、接続されている送信者にメッセージを送信することで、Web Receiver の状態を送信者に常に通知できます。Web Receiver アプリケーションは、CastReceiverContextsendCustomMessage(namespace, senderId, message) を使用してメッセージを送信できます。Web Receiver は、受信メッセージまたはアプリケーションの状態変化に応じて、個々の送信者にメッセージを送信できます。Web Receiver は、ポイントツーポイント メッセージング(上限は 64 KB)だけでなく、接続されているすべての送信者にメッセージをブロードキャストすることもできます。

オーディオ機器のキャスト

音声のみの再生のサポートについては、オーディオ デバイス用 Google Cast ガイドをご覧ください。

Android TV

このセクションでは、Google Web Receiver が入力を再生として使用する方法と、Android TV の互換性について説明します。

アプリケーションとリモコンの統合

Android TV デバイスで実行される Google Web Receiver は、デバイスのコントロール入力(ハンドヘルド リモコン)からの入力を、urn:x-cast:com.google.cast.media 名前空間で定義されたメディア再生メッセージに変換します(メディア再生メッセージを参照)。Android TV のコントロール入力から基本的な再生コントロールを行えるようにするには、アプリでこれらのメッセージをサポートし、アプリのメディア再生を制御する必要があります。

Android TV の互換性に関するガイドライン

ここでは、アプリを Android TV に対応させるためのおすすめの方法と、よくある問題をいくつか紹介します。

  • ユーザー エージェント文字列には「Android」と「CrKey」の両方が含まれることに注意してください。サイトによっては、「Android」ラベルを検出しているために、モバイル専用サイトにリダイレクトされる場合があります。ユーザー エージェント文字列の「Android」が、常にモバイル ユーザーを示しているとは限りません。
  • Android のメディア スタックは、データの取得に透明な GZIP を使用することがあります。メディアデータが Accept-Encoding: gzip に応答できることを確認してください。
  • Android TV の HTML5 メディア イベントは、Chromecast とは異なるタイミングでトリガーされる場合があるため、Chromecast で隠れていた問題が明らかになる場合があります。
  • メディアを更新するときは、<audio>/<video> 要素(timeupdatepausewaiting など)によって起動されるメディア関連のイベントを使用します。progresssuspendstalled などのネットワーク関連のイベントは、プラットフォームに依存する傾向があるため、使用しないでください。レシーバーでのメディア イベントの処理について詳しくは、メディア イベントをご覧ください。
  • 受信側のサイトの HTTPS 証明書を構成する場合は、必ず中間 CA 証明書を含めてください。確認するには、Qualsys SSL テストページをご覧ください。サイトの信頼できる認証パスに「追加ダウンロード」というラベルが付いた CA 証明書が含まれている場合、Android ベースのプラットフォームで読み込まれない場合があります。
  • Chromecast ではレシーバー ページが 720p のグラフィックス プレーンに表示されますが、Android TV などの他のキャスト プラットフォームでは、最大 1080p でページを表示できます。レシーバーページがさまざまな解像度で適切にスケーリングされるようにします。