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

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

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

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

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

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

次の例は、送信者の接続がまだアクティブに接続されているかどうかを検出するデフォルトの動作をオーバーライドする方法を示しています。ウェブ受信側が送信側と maxInactivity 秒間通信できなかった場合、SENDER_DISCONNECTED イベントがディスパッチされます。次の構成は、このタイムアウトをオーバーライドします。これは、IDLE 状態の接続済み送信者がゼロの場合に、ウェブレシーバー アプリが 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 を設定する方法を示しています。この構成により、マニフェストの取得に関する送信リクエストがオーバーライドされます。ハンドラは、CORS Access-Control リクエストが Cookie や認可ヘッダーなどの認証情報を使用して行われることを指定します。

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();

エラー処理

メッセージ インターセプタでエラーが発生した場合、ウェブ レシーバー アプリは適切な 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 です。アプリケーションは、この URL を解析し、他の 2 つのフィールドの少なくとも 1 つに入力する必要があります。
  • contentUrl は、プレーヤーがコンテンツの読み込みに使用する再生可能な URL に対応しています。たとえば、この URL は DASH マニフェストを指すことができます。
  • contentId には、再生可能なコンテンツの URL(contentUrl プロパティの URL に似ている)または読み込まれるコンテンツまたは再生リストの一意の識別子を指定できます。このプロパティを識別子として使用する場合は、アプリで contentUrl に再生可能な URL を入力する必要があります。

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 と DolbyVision(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 には、ウェブ レシーバー アプリがこれらのインタラクションを処理し、ユーザー アクションの状態を通じてアプリケーション UI を更新し、必要に応じて変更を送信してバックエンド サービスを更新できるようにするさまざまな API が用意されています。

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

UI コントロールの状態は、iOS と Android の送信側の拡張コントローラ、タッチデバイスで実行されるレシーバーとリモコン アプリ、Android TV デバイスのレシーバー アプリの MediaStatus.supportedMediaCommands によって駆動されます。プロパティで特定のビット単位の Command が有効になっている場合、そのアクションに関連するボタンが有効になります。値が設定されていない場合、ボタンは無効になります。これらの値は、ウェブレシーバーで次の手順で変更できます。

  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 に反映されます。また、送信される MediaStatus メッセージを通じてブロードキャストされ、iOS と Android の送信元の展開されたコントローラの UI を更新します。

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 フラグを削除し、制限するコマンドごとに受信メッセージをインターセプトできます。ここでは、アシスタント搭載デバイスに発行された SEEK コマンドがウェブ レシーバー アプリケーションでシークをトリガーしないように、SDK から提供されたリクエストをインターセプトします。

アプリがサポートしていないメディア コマンドの場合は、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 を含む別のメッセージが送信されます。アプリケーションと再生中のメディアによっては、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, 字幕をオンにして」というコマンドでは、コマンドが話された言語から言語が推測されたため、isSuggestedLanguagetrue に設定されます。「OK Google, 英語の字幕をオンにして」など、言語が明示的にリクエストされた場合、isSuggestedLanguagefalse に設定されます。

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

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

ストリーミング転送

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

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

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

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

  • 曲、動画、メディア アイテムの特定の位置またはタイムスタンプ。
  • より大きなキュー(再生リストやアーティスト ラジオなど)内の位置。
  • 認証されたユーザー。
  • 再生ステータス(再生中、一時停止中など)。

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

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

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

セッション状態の保持

Web Receiver SDK には、現在のメディア ステータスのスナップショットを取得し、ステータスを読み込みリクエストに変換して、読み込みリクエストでセッションを再開することで、セッション状態を保持する 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 レシーバは、キュー内の現在の再生アイテムの後にメディア アイテムのプリロードをサポートしています。

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

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

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

カスタム メッセージ

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

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

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

すべての名前空間は文字列で定義され、先頭に「urn:x-cast:」を付けて、任意の文字列を続けます。例: urn:x-cast:com.example.cast.mynamespace

接続された送信者からのカスタム メッセージをリッスンするウェブ受信者のコード スニペットは次のとおりです。

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 レシーバー アプリケーションは、CastReceiverContextsendCustomMessage(namespace, senderId, message) を使用してメッセージを送信できます。ウェブ レシーバは、受信したメッセージに応答するか、アプリケーションの状態の変化により、個々の送信者にメッセージを送信できます。ウェブ受信機は、ポイントツーポイント メッセージング(64 KB の制限付き)以外にも、接続されているすべての送信者にメッセージをブロードキャストできます。

音声デバイスへのキャスト

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

Android TV

このセクションでは、Google ウェブ レシーバーが入力を再生として使用する方法と、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 で表示される場合があります。レシーバー ページがさまざまな解像度で適切にスケーリングされるようにします。