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

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

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

애플리케이션 구성 및 옵션

애플리케이션 구성

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

아래 예는 발신자 연결이 아직 활성 상태인지 감지하는 기본 동작을 재정의하는 방법을 보여줍니다. 웹 수신기가 maxInactivity초 동안 발신자와 통신할 수 없으면 SENDER_DISCONNECTED 이벤트가 전달됩니다. 아래 구성은 이 제한 시간을 재정의합니다. 이는 IDLE 상태에 연결된 발신자가 0개인 경우 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에 의해 처리되며 플레이어가 생성될 때 평가됩니다. 웹 수신기 SDK에 새 로드가 전달될 때마다 플레이어가 생성됩니다. 플레이어가 생성된 후 PlaybackConfig의 수정사항은 다음 콘텐츠 로드에서 평가됩니다. SDK는 PlaybackConfig를 수정하는 다음 메서드를 제공합니다.

  • CastReceiverOptions.playbackConfig: CastReceiverContext를 초기화할 때 기본 구성 옵션을 재정의합니다.
  • 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;
});

이벤트 리스너

웹 수신기 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의 기기 기능을 가져오는 방식으로 쿼리할 수 있는 객체를 반환합니다. 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 기기의 리모컨을 통해 Web Receiver 애플리케이션과 상호작용할 수 있습니다. 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);

broadcast receiver가 업데이트된 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 메시지가 Web Receiver 애플리케이션에 전송됩니다. 활동이 종료되면 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 매개변수는 연결된 언어가 사용자가 제안했는지 또는 명시적으로 요청했는지 여부를 나타냅니다.

예를 들어 'Hey Google, 자막 켜 줘' 명령어에 isSuggestedLanguagetrue로 설정됩니다. 언어가 명령어가 사용된 언어로 추론되었기 때문입니다. 'Hey 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, 스무스 스트리밍 콘텐츠에서 작동합니다.

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

맞춤 메시지

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

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

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

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

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

오디오 기기용 전송

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

Android TV

이 섹션에서는 Google Web Receiver가 입력을 재생으로 사용하는 방법과 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에서 숨겨진 문제를 드러낼 수 있습니다.
  • 미디어를 업데이트할 때 timeupdate, pause, waiting와 같은 <audio>/<video> 요소에서 실행한 미디어 관련 이벤트를 사용하세요. progress, suspend, stalled와 같은 네트워킹 관련 이벤트는 플랫폼에 종속될 수 있으므로 사용하지 마세요. 수신기에서 미디어 이벤트를 처리하는 방법에 관한 자세한 내용은 미디어 이벤트를 참고하세요.
  • 수신 사이트의 HTTPS 인증서를 구성할 때 중간 CA 인증서를 포함해야 합니다. Qualsys SSL 테스트 페이지를 참조하여 확인할 수 있습니다. 사이트의 신뢰할 수 있는 인증 경로에 '추가 다운로드' 라벨이 지정된 CA 인증서가 포함되어 있으면 Android 기반 플랫폼에서 로드되지 않을 수 있습니다.
  • Chromecast는 720p 그래픽 영역에 수신기 페이지를 표시하지만 Android TV를 포함한 다른 Cast 플랫폼에서는 페이지를 최대 1080p까지 표시할 수 있습니다. 수신자 페이지가 다양한 해상도에서 적절하게 확장되는지 확인하세요.