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

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

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

애플리케이션 구성 및 옵션

애플리케이션 구성

CastReceiverContext는 개발자에게 노출되는 가장 바깥쪽 클래스이며, 기본 라이브러리 로드를 관리하고 Web Receiver 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를 수정하기 위한 다음 메서드를 제공합니다.

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

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

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

이벤트 리스너

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);
    });
  • 메시지 가로채기를 사용하면 메시지를 수신 대기하고, 가로채고, 요청 데이터 자체를 수정할 수 있습니다.
  • 메시지 가로채기는 요청 데이터와 관련된 맞춤 로직을 처리하는 데 가장 적합합니다.

미디어 로드

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

  • entity는 발신자 및 수신자 앱 모두의 구현에 사용할 수 있는 속성입니다. 이 속성은 재생목록 또는 미디어 콘텐츠일 수 있는 딥 링크 URL입니다. 애플리케이션은 이 URL을 파싱하고 다른 두 필드 중 하나 이상을 채워야 합니다.
  • contentUrl은 플레이어가 콘텐츠를 로드하는 데 사용할 수 있는 URL에 해당합니다. 예를 들어 이 URL은 DASH 매니페스트를 가리킬 수 있습니다.
  • contentId은 재생 가능한 콘텐츠 URL (contentUrl 속성과 유사)이거나 로드되는 콘텐츠 또는 재생목록의 고유 식별자일 수 있습니다. 이 속성을 식별자로 사용하는 경우 애플리케이션이 contentUrl에 재생 가능한 URL을 입력해야 합니다.

entity를 사용하여 실제 ID 또는 키 매개변수를 저장하고 contentUrl을 사용하여 미디어 URL을 저장하는 것이 좋습니다. 다음 스니펫에서는 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 어시스턴트, 블루투스, 연결된 디스플레이 및 오디오 기기에 관한 지원 정보를 제공합니다.

이 메서드는 지정된 열거형 중 하나를 전달하여 해당 열거형의 기기 기능을 가져올 수 있는 객체를 반환합니다. 열거형은 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 메시지를 가로채고 요청된 작업을 확인합니다.
  • UI를 업데이트하도록 MediaInformation UserActionState를 업데이트합니다.

다음 스니펫은 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에 있습니다.

명령어 설명
재생 일시중지된 상태에서 재생하거나 다시 시작합니다.
일시중지 현재 재생 중인 콘텐츠를 일시중지합니다.
이전 미디어 대기열에서 이전 미디어 항목으로 건너뜁니다.
다음 미디어 대기열에서 다음 미디어 항목으로 건너뜁니다.
중지 현재 재생 중인 미디어를 중지합니다.
반복 안함 재생목록의 마지막 항목 재생이 완료되면 재생목록의 미디어 항목 반복을 사용 중지합니다.
싱글 반복 현재 재생 중인 미디어를 무한 반복합니다.
전체 반복 재생목록의 마지막 항목이 재생되면 재생목록의 모든 항목을 반복합니다.
모두 반복 및 셔플 재생목록의 마지막 항목 재생이 완료되면 재생목록을 셔플하고 재생목록의 모든 항목을 반복합니다.
셔플 미디어 대기열의 미디어 항목을 셔플합니다.
자막 사용 설정 / 사용 중지 미디어의 자막을 사용 설정 / 사용 중지합니다. 언어별로 사용 설정 / 사용 중지할 수도 있습니다.
절대 시간으로 탐색 지정된 절대 시간으로 건너뜁니다.
현재 시간을 기준으로 시간으로 탐색 현재 재생 시간을 기준으로 지정된 시간만큼 앞으로 또는 뒤로 이동합니다.
다시 플레이하기 현재 재생 중인 미디어를 다시 시작하거나 현재 재생 중인 항목이 없으면 마지막으로 재생한 미디어 항목을 재생합니다.
재생 속도 설정 미디어 재생 속도를 변경합니다. 이는 기본적으로 처리되어야 합니다. 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 명령어가 웹 수신기 애플리케이션에서 탐색을 트리거하지 않도록 합니다.

애플리케이션에서 지원하지 않는 미디어 명령어의 경우 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. Web Receiver 애플리케이션이 현재 미디어 상태를 저장하라는 명령어를 수신합니다.
    3. Web Receiver 애플리케이션이 종료됩니다.
  2. 대상 기기에서 다음 단계를 따릅니다.
    1. 웹 수신기 애플리케이션이 로드됩니다.
    2. Web Receiver 애플리케이션은 저장된 미디어 상태를 복원하라는 명령어를 수신합니다.
    3. 미디어 재생이 재개됩니다.

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

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

스트림 전송 사용 설정

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

  1. STREAM_TRANSFER 명령어를 사용하여 supportedMediaCommands를 업데이트합니다.
    playerManager.addSupportedMediaCommands(
    cast.framework.messages.Command.STREAM_TRANSFER, true);
  2. 원하는 경우 세션 상태 유지에 설명된 대로 SESSION_STATERESUME_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;
    });

콘텐츠 미리 로드

웹 수신기는 대기열의 현재 재생 항목 이후에 미디어 항목의 미리 로드를 지원합니다.

미리 로드 작업은 예정된 항목의 여러 세그먼트를 미리 다운로드합니다. 사양은 QueueItem 객체의 preloadTime 값에 따라 지정됩니다(제공되지 않은 경우 기본값은 20초). 시간은 현재 재생 중인 항목의 끝을 기준으로 초 단위로 표시됩니다 . 양수 값만 유효합니다. 예를 들어 값이 10초인 경우 이전 항목이 완료되기 10초 전에 이 항목이 미리 로드됩니다. 미리 로드 시간이 currentItem에 남은 시간보다 길면 가능한 한 빨리 미리 로드가 발생합니다. 따라서 queueItem에 매우 큰 preload 값이 지정되면 현재 항목을 재생할 때마다 다음 항목을 미리 로드하는 효과를 얻을 수 있습니다. 하지만 이 값은 현재 재생 중인 항목의 대역폭과 스트리밍 성능에 영향을 미칠 수 있으므로 이 설정과 선택은 개발자에게 맡깁니다.

기본적으로 HLS, DASH, Smooth 스트리밍 콘텐츠에 프리로드가 작동합니다.

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

맞춤 메시지

메시지 교환은 Web Receiver 애플리케이션의 주요 상호작용 방법입니다.

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

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

모든 네임스페이스는 문자열로 정의되며 '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의 상태를 발신자에게 알릴 수 있습니다. 웹 수신기 애플리케이션은 CastReceiverContext에서 sendCustomMessage(namespace, senderId, message)를 사용하여 메시지를 보낼 수 있습니다. 웹 수신기는 수신된 메시지에 대한 응답으로 또는 애플리케이션 상태 변경으로 인해 개별 발신자에게 메시지를 보낼 수 있습니다. 포인트 투 포인트 메시지 (64kb 제한) 외에도 웹 수신기는 연결된 모든 발신자에게 메시지를 브로드캐스트할 수 있습니다.

오디오 기기로 전송

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