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

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

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

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

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

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

以下の例は、センダー接続が現在もアクティブに接続されているかどうかを検出するデフォルトの動作をオーバーライドする方法を示しています。ウェブ レシーバーが 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 Access-Control リクエストを行うよう指定します。

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 には、entitycontentUrlcontentId など、メディアを cast.framework.messages.MessageType.LOAD メッセージに読み込むためのさまざまなプロパティが用意されています。

entity は、送信側アプリと受信側アプリの両方の実装で使用する推奨プロパティです。このプロパティは、再生リストまたは特定のメディア コンテンツを指定できるディープリンク URL です。

contentUrl は再生可能な URL 用に設計されており、利用可能になったら使用できます。

contentId は、値がメディアの URL、実際の ID、カスタム検索のキーパラメータのいずれであるかのあいまいなため、非推奨になりました。

entity を使用して実際の ID またはキーパラメータを保存し、メディアの URL として contentUrl を使用することをおすすめします。次のスニペットの例では、LOAD リクエストに entity が存在し、再生可能な 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 が用意されており、ウェブ レシーバー アプリがこうしたインタラクションを処理し、ユーザーのアクション状態を通じてアプリの 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 を受け取って、MediaInformation に対して UserActionState を追加または削除します。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;
  });

音声アクティビティからのバックグラウンド実行

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

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

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, 字幕をオンにして」というコマンドに対して isSuggestedLanguagetrue に設定されます。これは、コマンドを話された言語から言語が推測されたためです。「OK Google, 英語の字幕をオンにして」のように明示的に言語がリクエストされた場合、isSuggestedLanguagefalse に設定されます。

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

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

ストリーミング転送

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

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

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

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

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

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

ウェブレシーバーにストリーム転送を実装するには:

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

セッションの状態を保持する

Web Receiver SDK にはデフォルトの実装が用意されています。Web 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;
    });

コンテンツのプリロード

Web Receiver では、キュー内の現在の再生アイテムの後にメディア アイテムをプリロードできます。

プリロード操作により、今後のアイテムの複数のセグメントが事前にダウンロードされます。この指定は、QueueItem オブジェクトの preloadTime 値で行われます(指定しない場合のデフォルトは 20 秒)。時間は、現在再生中のアイテムの終了時刻を基準とする秒数で表されます。正の値のみ有効です。たとえば、値が 10 秒の場合、このアイテムは前のアイテムが終了するまで 10 秒前にプリロードされます。プリロードの時間が currentItem の残り時間よりも長い場合、プリロードはできるだけ早く行われます。そのため、queueItem に非常に大きな値を指定すると、現在のアイテムを再生するたびに次のアイテムをプリロードしているという効果が得られます。ただし、この値は現在の再生アイテムの帯域幅やストリーミング パフォーマンスに影響する可能性があるため、設定と選択はデベロッパーに委ねられます。

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

通常の MP4 動画ファイルと MP3 などの音声ファイルはプリロードされません。キャスト デバイスがサポートしているメディア要素は 1 つのみであり、既存のコンテンツ アイテムが再生されている間はプリロードに使用できません。

カスタム メッセージ

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

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

ウェブ レシーバー アプリケーションは、指定された名前空間でメッセージをリッスンできます。これにより、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();

同様に、ウェブ レシーバー アプリケーションは、接続されたセンダーにメッセージを送信することで、ウェブ レシーバーの状態を送信者に知らせることができます。ウェブ レシーバー アプリケーションは、CastReceiverContextsendCustomMessage(namespace, senderId, message) を使用してメッセージを送信できます。ウェブレシーバーは、受信したメッセージに応じて、またはアプリケーションの状態の変化により、個々の送信者にメッセージを送信できます。ポイントツーポイント メッセージ(上限 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 を含む他の Cast プラットフォームでは、最大 1080p でページを表示することがあります。レシーバー ページがさまざまな解像度で適切にスケーリングされるようにします。