맞춤 웹 수신기에 핵심 기능 추가

이 페이지에는 맞춤 웹 수신기 앱에서 사용할 수 있는 기능에 관한 코드 스니펫과 설명이 포함되어 있습니다.

  1. 웹 수신기와 함께 제공되는 기본 제공 플레이어 UI를 나타내는 cast-media-player 요소입니다.
  2. cast-media-player 요소의 맞춤 CSS와 같은 스타일링으로 background-image, splash-image, font-family과 같은 다양한 UI 요소의 스타일을 지정합니다.
  3. 웹 수신기 프레임워크를 로드하는 스크립트 요소입니다.
  4. 메시지를 가로채고 이벤트를 처리하는 JavaScript 코드
  5. 자동재생을 위해 현재 재생목록에 추가합니다.
  6. 재생을 구성하는 옵션
  7. 웹 수신기 컨텍스트를 설정하는 옵션입니다.
  8. 웹 수신기 앱에서 지원하는 명령어를 설정하는 옵션입니다.
  9. 웹 수신기 애플리케이션을 시작하는 JavaScript 호출입니다.

애플리케이션 구성 및 옵션

애플리케이션 구성

CastReceiverContext는 개발자에게 노출되는 최외곽 클래스이며, 기본 라이브러리의 로드를 관리하고 웹 수신기 SDK의 초기화를 처리합니다. SDK는 애플리케이션 개발자가 CastReceiverOptions를 통해 SDK를 구성할 수 있는 API를 제공합니다. 이러한 구성은 애플리케이션 실행당 한 번 평가되며 start 호출에서 선택적 매개변수를 설정할 때 SDK에 전달됩니다.

아래 예는 발신자 연결이 여전히 활성 상태인지 감지하는 기본 동작을 재정의하는 방법을 보여줍니다. 웹 수신기가 maxInactivity초 동안 발신자와 통신할 수 없는 경우 SENDER_DISCONNECTED 이벤트가 전달됩니다. 아래 구성은 이 시간 제한을 재정의합니다. 이는 IDLE 상태에서 연결된 발신자가 0명일 때 웹 수신기 앱이 Chrome 원격 디버거 세션을 닫지 못하도록 하므로 문제를 디버그할 때 유용할 수 있습니다.

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

플레이어 구성

콘텐츠를 로드할 때 웹 수신기 SDK는 cast.framework.PlaybackConfig를 사용하여 DRM 정보, 재시도 구성, 요청 핸들러와 같은 재생 변수를 구성하는 방법을 제공합니다. 이 정보는 PlayerManager에서 처리되며 플레이어가 생성될 때 평가됩니다. 플레이어는 새 로드가 웹 수신기 SDK에 전달될 때마다 만들어집니다. 플레이어가 생성된 후 PlaybackConfig를 수정하면 다음 콘텐츠 로드에서 평가됩니다. SDK는 PlaybackConfig를 수정하기 위한 다음 메서드를 제공합니다.

  • CastReceiverOptions.playbackConfig: CastReceiverContext를 초기화할 때 기본 구성 옵션을 재정의합니다.
  • PlayerManager.getPlaybackConfig()를 호출하여 현재 구성을 가져옵니다.
  • PlayerManager.setPlaybackConfig()를 사용하여 현재 구성을 재정의합니다. 이 설정은 모든 후속 로드에 적용되며 다시 재정의될 때까지 적용됩니다.
  • PlayerManager.setMediaPlaybackInfoHandler(): 현재 구성 위에 로드되는 미디어 항목에 대해서만 추가 구성을 적용합니다. 핸들러는 플레이어 생성 직전에 호출됩니다. 여기에서 변경한 사항은 영구적이지 않으며 getPlaybackConfig() 쿼리에 포함되지 않습니다. 다음 미디어 항목이 로드되면 이 핸들러가 다시 호출됩니다.

아래 예는 CastReceiverContext를 초기화할 때 PlaybackConfig를 설정하는 방법을 보여줍니다. 이 구성은 매니페스트를 가져오기 위한 발신 요청을 재정의합니다. 핸들러는 쿠키 또는 승인 헤더와 같은 사용자 인증 정보를 사용하여 CORS 액세스 제어 요청을 실행해야 한다고 지정합니다.

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

아래 예는 PlayerManager에 제공된 getter 및 setter를 사용하여 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;
});

이벤트 리스너

웹 수신기 SDK를 사용하면 웹 수신기 앱에서 플레이어 이벤트를 처리할 수 있습니다. 이벤트 리스너는 리스너를 트리거해야 하는 이벤트를 지정하는 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
});

메시지 가로채기

웹 수신기 SDK를 사용하면 웹 수신기 앱이 메시지를 가로채고 이러한 메시지에서 맞춤 코드를 실행할 수 있습니다. 메시지 인터셉터는 가로채야 할 메시지 유형을 지정하는 cast.framework.messages.MessageType 매개변수를 사용합니다.

인터셉터는 수정된 요청 또는 수정된 요청 값으로 확인되는 프로미스를 반환해야 합니다. 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);
    });
  • 메시지 가로채기를 사용하면 메시지를 수신 대기하고 가로채며 요청 데이터 자체를 수정할 수 있습니다.
  • 메시지 가로채기는 요청 데이터와 관련된 맞춤 로직을 처리하는 데 가장 적합합니다.

미디어 로드

MediaInformationentity, contentUrl, contentId를 비롯하여 cast.framework.messages.MessageType.LOAD 메시지에 미디어를 로드하는 다양한 속성을 제공합니다.

  • entity는 발신자 앱과 수신자 앱 모두의 구현에 사용할 것을 권장하는 속성입니다. 이 속성은 재생목록 또는 미디어 콘텐츠가 될 수 있는 딥 링크 URL입니다. 애플리케이션은 이 URL을 파싱하고 다른 두 필드 중 하나 이상을 채워야 합니다.
  • contentUrl는 플레이어가 콘텐츠를 로드하는 데 사용할 재생 가능한 URL에 해당합니다. 예를 들어 이 URL은 DASH 매니페스트를 가리킬 수 있습니다.
  • contentId는 재생 가능한 콘텐츠 URL (contentUrl 속성과 유사) 또는 로드 중인 콘텐츠 또는 재생목록의 고유 식별자일 수 있습니다. 이 속성을 식별자로 사용하는 경우 애플리케이션은 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 메서드는 연결된 Cast 기기와 기기에 연결된 동영상 또는 오디오 기기에 관한 기기 정보를 제공합니다. getDeviceCapabilities 메서드는 Google 어시스턴트, 블루투스, 연결된 디스플레이 및 오디오 기기에 관한 지원 정보를 제공합니다.

이 메서드는 지정된 enum 중 하나를 전달하여 쿼리하고 해당 enum의 기기 기능을 가져올 수 있는 객체를 반환합니다. 열거형은 cast.framework.system.DeviceCapabilities에 정의되어 있습니다.

이 예에서는 웹 수신기 기기가 각각 IS_HDR_SUPPORTEDIS_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;
}

음성 명령

현재 어시스턴트 지원 기기의 웹 수신기 SDK에서 다음과 같은 미디어 명령어가 지원됩니다. 이러한 명령어의 기본 구현은 cast.framework.PlayerManager에 있습니다.

명령어 설명
재생 일시중지 상태에서 재생하거나 재개합니다.
일시중지 현재 재생 중인 콘텐츠를 일시중지합니다.
이전 미디어 현재 재생목록에서 이전 미디어 항목으로 건너뜁니다.
다음 미디어 현재 재생목록에서 다음 미디어 항목으로 건너뜁니다.
중지 현재 재생 중인 미디어를 중지합니다.
반복 안함 재생목록의 마지막 항목 재생이 완료되면 재생목록의 미디어 항목 반복을 사용 중지합니다.
싱글 반복 현재 재생 중인 미디어를 무한으로 반복합니다.
모두 반복 재생목록의 마지막 항목이 재생되면 재생목록의 모든 항목을 반복합니다.
모두 반복 및 셔플 재생목록의 마지막 항목이 재생이 완료되면 재생목록을 셔플하고 재생목록의 모든 항목을 반복합니다.
셔플 미디어 현재 재생목록의 미디어 항목을 셔플합니다.
자막 켜기 / 끄기 미디어의 자막을 사용 설정 / 사용 중지합니다. 언어별로 사용 설정 / 사용 중지할 수도 있습니다.
절대 시간으로 탐색 지정된 절대 시간으로 건너뜁니다.
현재 시간에 비례하는 시간으로 이동 현재 재생 시간에 대해 지정된 시간만큼 앞뒤로 이동합니다.
다시 재생 현재 재생 중인 미디어를 다시 시작하거나 현재 재생 중인 항목이 없는 경우 마지막으로 재생한 미디어 항목을 재생합니다.
재생 속도 설정 미디어 재생 속도 변경 이는 기본적으로 처리되어야 합니다. 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가 포함된 다른 메시지가 전송됩니다. 애플리케이션과 재생 중인 미디어에 따라 메시지 유형 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 앱 또는 스마트 디스플레이를 사용하여 기기 간에 기존 오디오 및 동영상 스트림을 이동할 수 있습니다. 한 기기 (소스)에서 미디어 재생이 중지되고 다른 기기 (대상)에서 계속 재생됩니다. 최신 펌웨어가 설치된 모든 Cast 기기는 스트림 전송에서 소스 또는 대상이 될 수 있습니다.

스트림 전송의 이벤트 흐름은 다음과 같습니다.

  1. 소스 기기에서 다음 단계를 따르세요.
    1. 미디어 재생이 중지됩니다.
    2. 웹 수신기 애플리케이션은 현재 미디어 상태를 저장하는 명령어를 수신합니다.
    3. 웹 수신기 애플리케이션이 종료됩니다.
  2. 대상 기기에서 다음 단계를 따르세요.
    1. 웹 수신기 애플리케이션이 로드됩니다.
    2. 웹 수신기 애플리케이션은 저장된 미디어 상태를 복원하는 명령어를 수신합니다.
    3. 미디어 재생이 재개됩니다.

미디어 상태의 요소는 다음과 같습니다.

  • 노래, 동영상 또는 미디어 항목의 특정 위치 또는 타임스탬프입니다.
  • 더 큰 현재 재생목록 (예: 재생목록 또는 아티스트 라디오)에서의 위치입니다.
  • 인증된 사용자입니다.
  • 재생 상태 (예: 재생 중 또는 일시중지됨)

스트림 전송 사용 설정

웹 수신기의 스트림 전송을 구현하려면 다음 단계를 따르세요.

  1. STREAM_TRANSFER 명령어로 supportedMediaCommands를 업데이트합니다.
    playerManager.addSupportedMediaCommands(
    cast.framework.messages.Command.STREAM_TRANSFER, true);
  2. 원하는 경우 세션 상태 보존에 설명된 대로 SESSION_STATERESUME_SESSION 메시지 인터셉터를 재정의합니다. 맞춤 데이터를 세션 스냅샷의 일부로 저장해야 하는 경우에만 재정의합니다. 그렇지 않으면 세션 상태 보존을 위한 기본 구현에서 스트림 전송을 지원합니다.

세션 상태 유지

웹 수신기 SDK는 웹 수신기 앱이 현재 미디어 상태의 스냅샷을 찍고, 상태를 로드 요청으로 변환하고, 로드 요청으로 세션을 재개하여 세션 상태를 보존할 수 있는 기본 구현을 제공합니다.

필요한 경우 웹 수신기에서 생성된 로드 요청을 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, 원활한 스트리밍 콘텐츠에 작동합니다.

Cast 기기는 하나의 미디어 요소만 지원하며 기존 콘텐츠 항목이 재생 중인 동안 미리 로드하는 데 사용할 수 없으므로 일반 MP4 동영상 및 MP3와 같은 오디오 파일은 미리 로드되지 않습니다.

맞춤 메시지

메시지 교환은 웹 수신기 애플리케이션의 핵심 상호작용 방법입니다.

발신자는 발신자가 실행 중인 플랫폼 (Android, iOS, 웹)의 발신자 API를 사용하여 웹 수신기에 메시지를 전송합니다. 이벤트 리스너에 전달되는 이벤트 객체 (메시지의 표현)에는 데이터가 특정 이벤트 유형의 속성을 사용하는 데이터 요소 (event.data)가 있습니다.

웹 수신기 애플리케이션은 지정된 네임스페이스에서 메시지를 수신 대기하도록 선택할 수 있습니다. 이렇게 하면 웹 수신기 애플리케이션이 해당 네임스페이스 프로토콜을 지원한다고 합니다. 그러면 해당 네임스페이스에서 통신하려는 연결된 모든 발신자가 적절한 프로토콜을 사용해야 합니다.

모든 네임스페이스는 문자열로 정의되며 '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();

마찬가지로 웹 수신기 애플리케이션은 연결된 발신자에게 메시지를 전송하여 발신자에게 웹 수신기의 상태를 계속 알릴 수 있습니다. 웹 수신기 애플리케이션은 CastReceiverContext에서 sendCustomMessage(namespace, senderId, message)를 사용하여 메시지를 보낼 수 있습니다. 웹 수신기는 수신된 메시지에 대한 응답으로 또는 애플리케이션 상태 변경으로 인해 개별 발신자에게 메시지를 보낼 수 있습니다. 웹 수신기는 포인트 투 포인트 메시지 (64KB 제한) 외에도 연결된 모든 발신자에게 메시지를 브로드캐스트할 수도 있습니다.

오디오 기기용 전송

오디오 전용 재생에 관한 지원은 오디오 기기용 Google Cast 가이드를 참고하세요.

Android TV

이 섹션에서는 Google 웹 수신기가 입력을 재생으로 사용하는 방법과 Android TV 호환성을 설명합니다.

애플리케이션을 리모컨과 통합

Android TV 기기에서 실행되는 Google 웹 수신기는 기기의 제어 입력 (예: 휴대용 리모컨)의 입력을 미디어 재생 메시지에 설명된 대로 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> 요소(예: timeupdate, pause, waiting)에서 발생하는 미디어 관련 이벤트를 사용합니다. progress, suspend, stalled와 같은 네트워킹 관련 이벤트는 플랫폼에 종속되는 경향이 있으므로 사용하지 마세요. 수신기에서 미디어 이벤트를 처리하는 방법에 관한 자세한 내용은 미디어 이벤트를 참고하세요.
  • 수신자 사이트의 HTTPS 인증서를 구성할 때 중간 CA 인증서를 포함해야 합니다. Qualsys SSL 테스트 페이지를 참고하여 확인합니다. 사이트의 신뢰할 수 있는 인증 경로에 '추가 다운로드'라는 라벨이 지정된 CA 인증서가 포함된 경우 Android 기반 플랫폼에 로드되지 않을 수 있습니다.
  • Chromecast는 720p 그래픽 평면에 수신기 페이지를 표시하지만 Android TV를 비롯한 다른 Cast 플랫폼은 최대 1080p의 페이지를 표시할 수 있습니다. 수신기 페이지가 다양한 해상도에서 적절하게 조정되는지 확인합니다.