맞춤 웹 수신기 빌드

1. 개요

Google Cast 로고

이 Codelab에서는 Cast 지원 기기에서 콘텐츠를 재생할 맞춤 웹 수신기 앱을 빌드하는 방법을 알아봅니다.

Google Cast란 무엇인가요?

사용자는 Google Cast를 사용하여 휴대기기의 콘텐츠를 TV로 전송할 수 있습니다. 그러면 사용자는 휴대기기 또는 데스크톱 Chrome 브라우저를 리모컨으로 사용하여 TV에서 미디어를 재생할 수 있습니다.

Google Cast SDK를 사용하면 앱에서 Google Cast 지원 기기 (예: TV 또는 사운드 시스템)를 제어할 수 있습니다. Cast SDK는 Google Cast 디자인 체크리스트에 따라 필요한 UI 구성요소를 제공합니다.

Google Cast 디자인 체크리스트는 지원되는 모든 플랫폼에서 Cast 사용자 환경을 간단하고 예측 가능하게 만들기 위해 제공됩니다. 자세히 알아보기

무엇을 빌드하게 되나요?

이 Codelab을 완료하면 Cast 지원 기기에서 동영상 콘텐츠를 표시할 수 있는 자체 맞춤 수신기 역할을 하는 HTML5 앱이 생성됩니다.

학습할 내용

  • 수신기 개발을 위해 설정하는 방법
  • Cast 애플리케이션 프레임워크에 기반한 Cast 지원 수신기의 기본사항
  • 전송된 동영상을 받는 방법
  • 디버그 로거 통합 방법
  • 수신기를 스마트 디스플레이에 최적화하는 방법

필요한 항목

경험

  • 웹 개발에 대한 사전 지식이 있어야 합니다.
  • 또한 TV 시청에 관한 사전 지식도 필요합니다. :)

본 가이드를 어떻게 사용하실 계획인가요?

읽기만 할 계획입니다 읽은 다음 연습 활동을 완료할 계획입니다

웹 앱 빌드 경험을 평가해 주세요.

초급 중급 고급

TV 시청 관련 경험을 평가해 주세요.

<ph type="x-smartling-placeholder"></ph> 초보자 중급 숙련도

2. 샘플 코드 가져오기

모든 샘플 코드를 컴퓨터에 다운로드할 수 있습니다.

그런 다음 다운로드한 ZIP 파일의 압축을 풉니다.

3. 로컬에서 수신자 배포

Cast 기기에서 웹 수신기를 사용하려면 Cast 기기가 연결할 수 있는 위치에서 호스팅되어야 합니다. https를 지원하는 서버가 이미 있는 경우 다음 안내를 건너뛰고 다음 섹션에서 필요하므로 URL을 기록해 두세요.

사용할 수 있는 서버가 없다면 Firebase 호스팅 또는 ngrok을 사용할 수 있습니다.

서버 실행

원하는 서비스를 설정한 후 app-start로 이동하여 서버를 시작합니다.

호스팅된 수신자의 URL을 기록해 둡니다. 다음 섹션에서 이 ID를 사용합니다.

4. Cast 개발자 콘솔에 애플리케이션 등록

이 Codelab에 내장된 맞춤 수신기를 Chromecast 기기에서 실행하려면 애플리케이션을 등록해야 합니다. 애플리케이션을 등록하면 발신기 애플리케이션이 API 호출(예: 수신기 애플리케이션 실행)을 수행하기 위해 사용해야 하는 애플리케이션 ID를 받게 됩니다.

&#39;새 애플리케이션 추가&#39;가 표시된 Google Cast SDK 개발자 콘솔의 이미지 강조 표시된 버튼

'새 애플리케이션 추가'를 클릭합니다.

&#39;New Receiver Application&#39;(새 수신기 애플리케이션) 이미지 &#39;맞춤 수신기&#39;가 있는 화면 강조 표시된 옵션

'맞춤 수신기'를 선택합니다. 이것이 우리가 만들고 있는 것입니다.

&#39;New Custom Receiver&#39;(새 맞춤 수신기) 이미지 누군가 &#39;수신 애플리케이션 URL&#39;에 입력하는 URL을 보여주는 화면 필드

새 수신자의 세부정보를 입력하고 최종 URL을 사용해야 합니다.

설명하겠습니다. 새 수신기에 할당된 애플리케이션 ID를 기록합니다.

또한 게시하기 전에 수신기 애플리케이션에 액세스할 수 있도록 Google Cast 기기를 등록해야 합니다. 수신기 애플리케이션을 게시하면 모든 Google Cast 기기에서 사용할 수 있습니다. 이 Codelab에서는 게시되지 않은 수신기 애플리케이션으로 작업하는 것이 좋습니다.

&#39;새 기기 추가&#39;가 표시된 Google Cast SDK 개발자 콘솔의 이미지 강조 표시된 버튼

'새 기기 추가'를 클릭합니다.

&#39;Cast 수신기 기기 추가&#39; 이미지 대화상자

Cast 기기 뒷면에 인쇄된 일련번호를 입력하고 구체적인 이름을 지정합니다. Google Cast SDK 개발자 콘솔에 액세스할 때 Chrome에서 화면을 전송하여 일련번호를 확인할 수도 있습니다.

수신기와 기기를 테스트할 준비가 되기까지 5~15분 정도 걸립니다. 5~15분 후에 Cast 기기를 재부팅해야 합니다.

5. 샘플 앱 실행

Chrome 로고

새 수신기 애플리케이션이 테스트할 준비가 될 때까지 기다리는 동안 완성된 샘플 수신기 앱이 어떤 모습인지 살펴보겠습니다. 우리가 빌드할 수신기는 적응형 비트 전송률 스트리밍을 사용하여 미디어를 재생할 수 있습니다 (HTTP 동적 적응형 스트리밍 (DASH)을 통해 인코딩된 샘플 콘텐츠를 사용함).

브라우저에서 명령어 및 제어 (CaC) 도구를 엽니다.

&#39;Cast Connect 및 로거 컨트롤 커맨드 및 제어 (CaC) 도구의 탭

  1. CaC 도구가 표시됩니다.
  2. 기본값 'CC1AD845'를 사용합니다. 샘플 수신기 ID를 입력하고 '앱 ID 설정'을 클릭하세요. 버튼을 클릭합니다.
  3. 왼쪽 상단에서 전송 버튼을 클릭하고 Google Cast 기기를 선택합니다.

&#39;Cast Connect 및 로거 컨트롤 수신기 앱에 연결되었음을 나타내는 명령 및 제어 (CaC) 도구의 탭

  1. '미디어 로드'로 이동합니다. 탭을 클릭합니다.

&#39;Load Media&#39;(미디어 로드) 이미지 명령어 및 제어 (CaC) 도구 탭

  1. '콘텐츠별 로드'를 클릭합니다 버튼을 클릭하여 샘플 동영상을 재생합니다.
  2. Google Cast 기기에서 동영상이 재생되기 시작하며 기본 수신기를 사용할 경우 수신기의 기본 기능이 어떤지 보여줍니다.

6. 시작 프로젝트 준비

다운로드한 시작 앱에 Google Cast 지원 기능을 추가해야 합니다. 다음은 이 Codelab에서 사용할 Google Cast 용어입니다.

  • 발신기 앱은 휴대기기 또는 노트북에서 실행됩니다.
  • 수신기 앱은 Google Cast 기기에서 실행됩니다.

이제 원하는 텍스트 편집기를 사용하여 시작 프로젝트 위에 빌드할 준비가 되었습니다.

  1. 샘플 코드 다운로드에서 폴더 아이콘app-start 디렉터리를 선택합니다.
  2. js/receiver.jsindex.html 열기

이 Codelab을 진행하는 동안 http-server가 변경사항을 적용해야 합니다. 업데이트되지 않으면 http-server를 종료했다가 다시 시작해 보세요.

앱 디자인

수신기 앱은 Cast 세션을 초기화하고 발신자의 LOAD 요청 (즉, 미디어 일부를 재생하는 명령어)이 도착할 때까지 대기합니다.

앱은 index.html에 정의된 기본 뷰 하나와 수신기가 작동하도록 하는 모든 로직이 포함된 js/receiver.js라는 JavaScript 파일로 구성됩니다.

index.html

이 html 파일에는 수신기 앱의 UI가 포함됩니다. 지금은 비어 있으며 Codelab을 통해 계속 추가합니다.

receiver.js

이 스크립트는 수신기 앱의 모든 로직을 관리합니다. 지금은 비어 있는 파일이지만 다음 섹션에서는 코드 몇 줄만 사용하여 완전히 작동하는 Cast 수신기로 변환해 보겠습니다.

7. 기본 Cast 수신기

기본 Cast 수신기는 시작 시 Cast 세션을 초기화합니다. 이는 연결된 모든 발신기 애플리케이션에 수신기를 불러오는 데 성공했음을 알리는 데 필요합니다. 또한 새 SDK는 적응형 비트 전송률 스트리밍 미디어 (DASH, HLS, Smooth Streaming 사용) 및 일반 MP4 파일을 즉시 처리하도록 사전 구성되어 제공됩니다. 한 번 시도해 보겠습니다.

초기화

헤더의 index.html에 다음 코드를 추가합니다.

<head>
  ...

  <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
</head>

receiver.js,를 로드하는 <footer> 전에 index.html <body>에 다음 코드를 추가하여 방금 추가한 스크립트와 함께 제공되는 기본 수신기 UI를 표시할 수 있는 공간을 수신기 SDK에 제공합니다.

<cast-media-player></cast-media-player>

이제 js/receiver.js에서 다음으로 구성된 SDK를 초기화해야 합니다.

  • 전체 수신기 SDK의 기본 진입점인 CastReceiverContext 참조 획득
  • 재생을 처리하고 자체 맞춤 로직을 플러그인하는 데 필요한 모든 후크를 제공하는 객체인 PlayerManager 참조 저장
  • CastReceiverContext에서 start()를 호출하여 SDK 초기화

다음을 js/receiver.js에 추가합니다.

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

context.start();

8. 'basic' 전송 동영상 콘텐츠

이 Codelab에서는 CaC 도구를 사용하여 새로운 수신기를 사용해 봅니다.

웹브라우저에서 명령어 및 제어 (CaC) 도구를 가리킵니다.

&#39;Cast Connect 및 로거 컨트롤 명령어 및 제어 (CaC) 도구 탭

앞서 필드에 등록된 자체 앱 ID로 대체하고 '앱 ID 설정'을 클릭합니다. 이렇게 하면 Cast 세션을 시작할 때 수신기를 사용하도록 도구에 지시합니다.

미디어 전송

Cast 기기에서 미디어를 재생하려면 상위 수준에서 다음 작업이 필요합니다.

  1. 발신자는 미디어 항목을 모델링하는 Cast SDK에서 MediaInfo JSON 객체를 만듭니다.
  2. 발신기가 Cast 기기에 연결하여 수신기 애플리케이션을 실행합니다.
  3. broadcast receiver는 LOAD 요청을 통해 MediaInfo 객체를 로드하여 콘텐츠를 재생합니다.
  4. 수신기가 미디어 상태를 모니터링하고 추적합니다.
  5. 발신자는 재생 명령어를 수신기에 전송하여 발신기 앱과의 사용자 상호작용에 따라 재생을 제어합니다.

이 첫 번째 기본 시도에서는 MediaInfo에 재생 가능한 애셋 URL (MediaInfo.contentUrl에 저장됨)을 채웁니다.

실제 발신자는 MediaInfo.contentId에서 애플리케이션별 미디어 식별자를 사용합니다. 수신기는 contentId를 식별자로 사용하여 적절한 백엔드 API 호출을 수행하여 실제 애셋 URL을 확인하고 이를 MediaInfo.contentUrl.로 설정합니다. 수신기는 DRM 라이선스 획득 또는 광고 시점 관련 정보 삽입과 같은 작업도 처리합니다.

다음 섹션에서는 이와 같은 작업을 하도록 수신기를 확장합니다. 지금은 전송 아이콘을 클릭하고 기기를 선택하여 수신기를 엽니다.

&#39;Cast Connect 및 로거 컨트롤 수신기 앱에 연결되었음을 나타내는 명령 및 제어 (CaC) 도구의 탭

'미디어 로드'로 이동합니다. '콘텐츠별 로드'를 클릭합니다 버튼을 클릭합니다. 수신기에서 샘플 콘텐츠 재생을 시작해야 합니다.

&#39;Load Media&#39;(미디어 로드) 이미지 명령어 및 제어 (CaC) 도구 탭

따라서 수신기 SDK는 즉시 다음을 처리합니다.

  • Cast 세션 초기화
  • 재생 가능한 애셋이 포함된 발신기에서 수신되는 LOAD 요청 처리
  • 대형 화면에 표시할 준비가 된 기본 플레이어 UI를 제공합니다.

다음 섹션으로 넘어가기 전에 CaC 도구와 코드를 살펴보세요. 다음 섹션에서는 수신기를 확장하여 간단한 샘플 API와 통신하여 발신자로부터 수신되는 LOAD 요청을 처리합니다.

9. 외부 API와 통합

대부분의 개발자가 실제 애플리케이션에서 Cast 수신기와 상호작용하는 방식에 따라 재생 가능한 애셋 URL을 통해 전송하는 대신 API 키로 의도한 미디어 콘텐츠를 참조하는 LOAD 요청을 처리하도록 수신기를 수정합니다.

애플리케이션은 일반적으로 다음과 같은 이유로 이 작업을 실행합니다.

  • 발신자가 콘텐츠 URL을 모를 수 있습니다.
  • Cast 애플리케이션은 수신기에서 직접 인증, 기타 비즈니스 로직 또는 API 호출을 처리하도록 설계되었습니다.

이 기능은 주로 PlayerManager setMessageInterceptor() 메서드에서 구현됩니다. 이렇게 하면 수신 메시지를 유형별로 가로채고 SDK의 내부 메시지 핸들러에 도달하기 전에 수정할 수 있습니다. 이 섹션에서는 다음을 실행하는 LOAD 요청을 처리합니다.

  • 수신되는 LOAD 요청과 맞춤 contentId를 읽습니다.
  • API에서 GET를 호출하여 contentId로 스트리밍 가능한 애셋을 찾습니다.
  • 스트림의 URL로 LOAD 요청을 수정합니다.
  • MediaInformation 객체를 수정하여 스트림 유형 매개변수를 설정합니다.
  • 재생을 위해 요청을 SDK에 전달하거나 요청된 미디어를 찾을 수 없는 경우 명령어를 거부합니다.

제공된 샘플 API는 대부분 즉시 사용 가능한 환경에 계속 의존하면서 일반적인 수신기 작업을 맞춤설정하는 SDK의 후크를 보여줍니다.

샘플 API

브라우저에서 https://storage.googleapis.com/cpe-sample-media/content.json에 접속하고 샘플 동영상 카탈로그를 살펴보세요. 콘텐츠에는 PNG 형식의 포스터 이미지 URL과 DASH 및 HLS 스트림이 모두 포함됩니다. DASH 및 HLS 스트림은 분할된 mp4 컨테이너에 저장된 디멀티플렉스된 동영상 및 오디오 소스를 가리킵니다.

{
  "bbb": {
    "author": "The Blender Project",
    "description": "Grumpy Bunny is grumpy",
    "poster": "https://[...]/[...]/BigBuckBunny/images/screenshot1.png",
    "stream": {
      "dash": "https://[...]/[...]/BigBuckBunny/BigBuckBunny_master.mpd",
      "hls": "https://[...]/[...]/BigBuckBunny/BigBuckBunny_master.m3u8",
    "title": "Big Buck Bunny"
  },
  "fbb_ad": {
    "author": "Google Inc.",
    "description": "Introducing Chromecast. The easiest way to enjoy [...]",
    "poster": "https://[...]/[...]/ForBiggerBlazes/images/screenshot8.png",
    "stream": {
      "dash": "https://[...]/[...]/ForBiggerBlazes/ForBiggerBlazes.mpd",
      "hls": "https://[...]/[...]/ForBiggerBlazes/ForBiggerBlazes.m3u8",
    "title": "For Bigger Blazes"
  },

  [...]

}

다음 단계에서는 수신기가 LOAD 요청으로 호출된 후 각 항목의 키 (예: bbb, fbb_ad)를 스트림의 URL에 매핑합니다.

LOAD 요청 가로채기

이 단계에서는 호스팅된 JSON 파일에 XHR 요청을 하는 함수를 사용하여 로드 인터셉터를 만듭니다. JSON 파일을 가져오면 콘텐츠를 파싱하고 메타데이터를 설정합니다. 다음 섹션에서는 MediaInformation 매개변수를 맞춤설정하여 콘텐츠 유형을 지정합니다.

context.start() 호출 직전에 다음 코드를 js/receiver.js 파일에 추가합니다.

function makeRequest (method, url) {
  return new Promise(function (resolve, reject) {
    let xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
    xhr.send();
  });
}

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
        // Fetch content repository by requested contentId
        makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json').then(function (data) {
          let item = data[request.media.contentId];
          if(!item) {
            // Content could not be found in repository
            reject();
          } else {
            // Add metadata
            let metadata = new
               cast.framework.messages.GenericMediaMetadata();
            metadata.title = item.title;
            metadata.subtitle = item.author;

            request.media.metadata = metadata;

            // Resolve request
            resolve(request);
          }
        });
      });
    });

다음 섹션에서는 DASH 콘텐츠에 대한 로드 요청의 media 속성을 구성하는 방법을 간략하게 설명합니다.

샘플 API DASH 콘텐츠 사용

로드 인터셉터가 준비되었으므로 이제 수신기에 콘텐츠 유형을 지정합니다. 이 정보는 수신기에 마스터 재생목록 URL과 스트림 MIME 유형을 제공합니다. LOAD 인터셉터의 Promise()에 있는 js/receiver.js 파일에 다음 코드를 추가합니다.

...
playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
          ...
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.dash;
            request.media.contentType = 'application/dash+xml';
            ...
          }
        });
      });
    });

이 단계를 완료하면 테스트를 진행하여 DASH 콘텐츠를 로드해 볼 수 있습니다. 대신 HLS 콘텐츠로 로드를 테스트하려면 다음 단계를 확인하세요.

샘플 API HLS 콘텐츠 사용

샘플 API에는 HLS 콘텐츠와 DASH가 포함됩니다. 이전 단계에서 한 것처럼 contentType를 설정하는 것 외에도 샘플 API의 HLS URL을 사용하려면 로드 요청에 추가 속성이 필요합니다. 수신기가 HLS 스트림을 재생하도록 구성된 경우 예상되는 기본 컨테이너 유형은 전송 스트림 (TS)입니다. 결과적으로 수신기는 contentUrl 속성만 수정된 경우 TS 형식의 샘플 MP4 스트림을 열려고 시도합니다. 로드 요청에서 MediaInformation 객체를 추가 속성으로 수정하여 수신기가 콘텐츠가 TS 유형이 아닌 MP4 유형임을 알 수 있도록 해야 합니다. 로드 인터셉터의 js/receiver.js 파일에 다음 코드를 추가하여 contentUrlcontentType 속성을 수정합니다. 또한 HlsSegmentFormat 속성과 HlsVideoSegmentFormat 속성을 추가합니다.

...
playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
          ...
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.hls;
            request.media.contentType = 'application/x-mpegurl';
            request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
            request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
            ...
          }
        });
      });
    });

테스트하기

다시 명령어 및 제어 (CaC) 도구를 열고 앱 ID를 수신자의 앱 ID로 설정합니다. 전송 버튼을 사용하여 기기를 선택합니다.

'미디어 로드'로 이동합니다. 탭 이번에는 '콘텐츠 URL'의 텍스트를 삭제합니다. '콘텐츠별 로드' 옆의 입력란 버튼을 누르면 애플리케이션에서 미디어에 대한 contentId 참조만 포함하는 LOAD 요청을 보내도록 강제합니다.

&#39;Load Media&#39;(미디어 로드) 이미지 명령어 및 제어 (CaC) 도구 탭

수신기를 수정하여 모든 것이 잘 작동했다고 가정하면 인터셉터는 MediaInfo 객체를 SDK가 화면에서 재생할 수 있는 모양으로 만드는 작업을 처리해야 합니다.

'콘텐츠별 로드'를 클릭합니다 버튼을 클릭하여 미디어가 제대로 재생되는지 확인합니다. content.json 파일에서 Content ID를 다른 ID로 자유롭게 변경하세요.

10. 스마트 디스플레이 최적화

스마트 디스플레이는 수신기 애플리케이션이 터치 지원 컨트롤을 지원할 수 있도록 하는 터치 기능이 있는 기기입니다.

이 섹션에서는 스마트 디스플레이에서 실행될 때 수신기 애플리케이션을 최적화하는 방법과 재생 컨트롤을 맞춤설정하는 방법을 설명합니다.

UI 컨트롤 액세스

스마트 디스플레이용 UI 컨트롤 객체는 cast.framework.ui.Controls.GetInstance()를 사용하여 액세스할 수 있습니다. context.start() 위의 js/receiver.js 파일에 다음 코드를 추가합니다.

...

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();

context.start();

<cast-media-player>를 사용하지 않는 경우 요소가 있는 경우 CastReceiverOptions에서 touchScreenOptimizedApp를 설정해야 합니다. 이 Codelab에서는 <cast-media-player> 요소가 포함됩니다.

context.start({ touchScreenOptimizedApp: true });

기본 컨트롤 버튼은 MetadataTypeMediaStatus.supportedMediaCommands에 따라 각 슬롯에 할당됩니다.

동영상 컨트롤

MetadataType.MOVIE, MetadataType.TV_SHOW, MetadataType.GENERIC의 경우 스마트 디스플레이용 UI 컨트롤 객체가 아래 예와 같이 표시됩니다.

UI 컨트롤이 위에 오버레이된 상태로 재생되는 동영상 이미지

  1. --playback-logo-image
  2. MediaMetadata.subtitle
  3. MediaMetadata.title
  4. MediaStatus.currentTime
  5. MediaInformation.duration
  6. ControlsSlot.SLOT_SECONDARY_1: ControlsButton.QUEUE_PREV
  7. ControlsSlot.SLOT_PRIMARY_1: ControlsButton.SEEK_BACKWARD_30
  8. PLAY/PAUSE
  9. ControlsSlot.SLOT_PRIMARY_2: ControlsButton.SEEK_FORWARD_30
  10. ControlsSlot.SLOT_SECONDARY_2: ControlsButton.QUEUE_NEXT

오디오 컨트롤

MetadataType.MUSIC_TRACK의 경우 스마트 디스플레이용 UI 컨트롤 객체가 아래와 같이 표시됩니다.

UI 컨트롤이 위에 오버레이된 상태로 재생되는 음악 이미지

  1. --playback-logo-image
  2. MusicTrackMediaMetadata.albumName
  3. MusicTrackMediaMetadata.title
  4. MusicTrackMediaMetadata.albumArtist
  5. MusicTrackMediaMetadata.images[0]
  6. MediaStatus.currentTime
  7. MediaInformation.duration
  8. ControlsSlot.SLOT_SECONDARY_1: ControlsButton.NO_BUTTON
  9. ControlsSlot.SLOT_PRIMARY_1: ControlsButton.QUEUE_PREV
  10. PLAY/PAUSE
  11. ControlsSlot.SLOT_PRIMARY_2: ControlsButton.QUEUE_NEXT
  12. ControlsSlot.SLOT_SECONDARY_2: ControlsButton.NO_BUTTON

지원되는 미디어 명령어 업데이트

UI 컨트롤 객체는 MediaStatus.supportedMediaCommands를 기반으로 ControlsButton의 표시 여부도 결정합니다.

supportedMediaCommands 값이 ALL_BASIC_MEDIA와 같으면 기본 컨트롤 레이아웃은 아래와 같이 표시됩니다.

미디어 플레이어 컨트롤 이미지: 진행률 표시줄, &#39;재생&#39; 버튼, &#39;앞으로 건너뛰기&#39; &#39;뒤로 건너뛰기&#39;를 선택하면 버튼 사용 설정됨

supportedMediaCommands 값이 ALL_BASIC_MEDIA | QUEUE_PREV | QUEUE_NEXT와 같으면 기본 컨트롤 레이아웃은 아래와 같이 표시됩니다.

미디어 플레이어 컨트롤 이미지: 진행률 표시줄, &#39;재생&#39; 버튼, &#39;앞으로 건너뛰기&#39; &#39;뒤로 건너뛰기&#39;를 선택하면 버튼, &#39;현재 재생목록&#39; &#39;Queue next(다음 대기열)&#39; 버튼 사용 설정됨

supportedMediaCommands 값이 PAUSE | QUEUE_PREV | QUEUE_NEXT와 같으면 기본 컨트롤 레이아웃은 아래와 같이 표시됩니다.

미디어 플레이어 컨트롤 이미지: 진행률 표시줄, &#39;재생&#39; &#39;이전 대기열&#39; 버튼, &#39;Queue next(다음 대기열)&#39; 버튼 사용 설정됨

텍스트 트랙을 사용할 수 있는 경우 자막 버튼이 항상 SLOT_1에 표시됩니다.

미디어 플레이어 컨트롤 이미지: 진행률 표시줄, &#39;재생&#39; 버튼, &#39;앞으로 건너뛰기&#39; &#39;뒤로 건너뛰기&#39;를 선택하면 버튼, &#39;이전 대기열&#39; &#39;Queue next&#39;(다음 대기열) 버튼, &#39;선택 캡션&#39; 버튼 사용 설정됨

수신자 컨텍스트를 시작한 후 supportedMediaCommands 값을 동적으로 변경하려면 PlayerManager.setSupportedMediaCommands를 호출하여 값을 재정의하면 됩니다. 또한 addSupportedMediaCommands를 사용하여 새 명령어를 추가하거나 removeSupportedMediaCommands를 사용하여 기존 명령어를 삭제할 수 있습니다.

컨트롤 버튼 맞춤설정

PlayerDataBinder를 사용하여 컨트롤을 맞춤설정할 수 있습니다. touchControls 아래의 js/receiver.js 파일에 다음 코드를 추가하여 컨트롤의 첫 번째 슬롯을 설정합니다.

...

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);

playerDataBinder.addEventListener(
  cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
  (e) => {
    if (!e.value) return;

    // Clear default buttons and re-assign
    touchControls.clearDefaultSlotAssignments();
    touchControls.assignButton(
      cast.framework.ui.ControlsSlot.SLOT_PRIMARY_1,
      cast.framework.ui.ControlsButton.SEEK_BACKWARD_30
    );
  });

context.start();

11. 스마트 디스플레이에서 미디어 탐색 구현

미디어 탐색은 사용자가 터치 기기에서 추가 콘텐츠를 탐색할 수 있는 CAF 수신기 기능입니다. 이를 구현하려면 PlayerDataBinder를 사용하여 BrowseContent UI를 설정합니다. 그런 다음 표시하려는 콘텐츠에 따라 BrowseItems로 채울 수 있습니다.

BrowseContent

다음은 BrowseContent UI 및 그 속성의 예입니다.

동영상 썸네일 2개와 세 번째 썸네일의 일부를 보여주는 BrowseContent UI 이미지

  1. BrowseContent.title
  2. BrowseContent.items

가로세로 비율

targetAspectRatio property를 사용하여 이미지 확장 소재에 가장 적합한 가로세로 비율을 선택합니다. CAF 수신기 SDK에서는 세 가지 가로세로 비율(SQUARE_1_TO_1, PORTRAIT_2_TO_3, LANDSCAPE_16_TO_9)을 지원합니다.

BrowseItem

BrowseItem를 사용하여 각 항목의 제목, 부제목, 길이, 이미지를 표시합니다.

동영상 썸네일 2개와 세 번째 썸네일의 일부를 보여주는 BrowseContent UI 이미지

  1. BrowseItem.image
  2. BrowseItem.duration
  3. BrowseItem.title
  4. BrowseItem.subtitle

미디어 탐색 데이터 설정

setBrowseContent를 호출하여 탐색할 미디어 콘텐츠 목록을 제공할 수 있습니다. playerDataBinder 아래 및 MEDIA_CHANGED 이벤트 리스너에서 js/receiver.js 파일에 다음 코드를 추가하여 제목이 'Up Next'인 탐색 항목을 설정합니다.

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);

...

let browseItems = getBrowseItems();

function getBrowseItems() {
  let browseItems = [];
  makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json')
  .then(function (data) {
    for (let key in data) {
      let item = new cast.framework.ui.BrowseItem();
      item.entity = key;
      item.title = data[key].title;
      item.subtitle = data[key].description;
      item.image = new cast.framework.messages.Image(data[key].poster);
      item.imageType = cast.framework.ui.BrowseImageType.MOVIE;
      browseItems.push(item);
    }
  });
  return browseItems;
}

let browseContent = new cast.framework.ui.BrowseContent();
browseContent.title = 'Up Next';
browseContent.items = browseItems;
browseContent.targetAspectRatio = cast.framework.ui.BrowseImageAspectRatio.LANDSCAPE_16_TO_9;

playerDataBinder.addEventListener(
  cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
  (e) => {
    if (!e.value) return;

    ....

    // Media browse
    touchControls.setBrowseContent(browseContent);
  });

미디어 탐색 항목을 클릭하면 LOAD 인터셉터가 트리거됩니다. 다음 코드를 LOAD 인터셉터에 추가하여 미디어 탐색 항목에서 request.media.contentIdrequest.media.entity에 매핑합니다.

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      ...

      // Map contentId to entity
      if (request.media && request.media.entity) {
        request.media.contentId = request.media.entity;
      }

      return new Promise((resolve, reject) => {
            ...
        });
    });

BrowseContent 객체를 null로 설정하여 미디어 탐색 UI를 삭제할 수도 있습니다.

12. 수신기 앱 디버깅

Cast 수신기 SDK는 개발자가 CastDebugLogger API 및 함께 제공되는 명령어 및 제어 (CaC) 도구를 사용하여 로그를 캡처하여 수신기 앱을 쉽게 디버그할 수 있는 또 다른 옵션을 제공합니다.

초기화

API를 통합하려면 index.html 파일에 CastDebugLogger 소스 스크립트를 추가합니다. 소스는 <head> 태그 사이에 있어야 합니다.

<head>
  ...
  <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
  <!-- Cast Debug Logger -->
  <script src="//www.gstatic.com/cast/sdk/libs/devtools/debug_layer/caf_receiver_logger.js"></script>
</head>

파일 상단과 playerManager 아래에 있는 js/receiver.js에서 다음 코드를 추가하여 CastDebugLogger 인스턴스를 검색하고 로거를 사용 설정합니다.

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const LOG_TAG = 'MyAPP.LOG';

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
context.addEventListener(cast.framework.system.EventType.READY, () => {
  if (!castDebugLogger.debugOverlayElement_) {
      castDebugLogger.setEnabled(true);
  }
});

디버그 로거가 사용 설정되면 DEBUG MODE를 표시하는 오버레이가 수신기에 표시됩니다.

&#39;디버그 모드&#39;로 재생되는 동영상 이미지 프레임의 왼쪽 상단 모서리에 나타나는 빨간색 배경에 표시되는 메시지

플레이어 이벤트 기록

CastDebugLogger를 사용하면 CAF 수신기 SDK에서 발생한 플레이어 이벤트를 쉽게 로깅하고 다양한 로거 수준을 사용하여 이벤트 데이터를 기록할 수 있습니다. loggerLevelByEvents 구성은 cast.framework.events.EventTypecast.framework.events.category를 사용하여 로깅할 이벤트를 지정합니다.

castDebugLogger 선언 아래에 다음 코드를 추가하여 플레이어 CORE 이벤트가 트리거되거나 mediaStatus 변경사항이 브로드캐스트될 때 로깅합니다.

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
context.addEventListener(cast.framework.system.EventType.READY, () => {
  if (!castDebugLogger.debugOverlayElement_) {
      castDebugLogger.setEnabled(true);
  }
});

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
}

로그 메시지 및 커스텀 태그

CastDebugLogger API를 사용하면 수신기 디버그 오버레이에 다양한 색상으로 표시되는 로그 메시지를 만들 수 있습니다. 다음과 같은 로그 메서드를 사용할 수 있으며, 가장 높은 우선순위에서 가장 낮은 우선순위로 나열되어 있습니다.

  • castDebugLogger.error(custom_tag, message);
  • castDebugLogger.warn(custom_tag, message);
  • castDebugLogger.info(custom_tag, message);
  • castDebugLogger.debug(custom_tag, message);

각 로그 메서드에서 첫 번째 매개변수는 맞춤 태그입니다. 의미 있는 식별 문자열을 사용할 수 있습니다. CastDebugLogger는 태그를 사용하여 로그를 필터링합니다. 태그 사용은 아래에 자세히 설명되어 있습니다. 두 번째 매개변수는 로그 메시지입니다.

실행 중인 로그를 표시하려면 LOAD 인터셉터에 로그를 추가합니다.

playerManager.setMessageInterceptor(
  cast.framework.messages.MessageType.LOAD,
  request => {
    castDebugLogger.info(LOG_TAG, 'Intercepting LOAD request');

    // Map contentId to entity
    if (request.media && request.media.entity) {
      request.media.contentId = request.media.entity;
    }

    return new Promise((resolve, reject) => {
      // Fetch content repository by requested contentId
      makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json')
        .then(function (data) {
          let item = data[request.media.contentId];
          if(!item) {
            // Content could not be found in repository
            castDebugLogger.error(LOG_TAG, 'Content not found');
            reject();
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.dash;
            request.media.contentType = 'application/dash+xml';
            castDebugLogger.warn(LOG_TAG, 'Playable URL:', request.media.contentUrl);

            // Add metadata
            let metadata = new cast.framework.messages.MovieMediaMetadata();
            metadata.metadataType = cast.framework.messages.MetadataType.MOVIE;
            metadata.title = item.title;
            metadata.subtitle = item.author;

            request.media.metadata = metadata;

            // Resolve request
            resolve(request);
          }
      });
    });
  });

각 맞춤 태그의 loggerLevelByTags에서 로그 수준을 설정하여 디버그 오버레이에 표시할 메시지를 제어할 수 있습니다. 예를 들어 로그 수준 cast.framework.LoggerLevel.DEBUG로 맞춤 태그를 사용 설정하면 오류, 경고, 정보, 디버그 로그 메시지와 함께 추가된 모든 메시지가 표시됩니다. WARNING 수준의 맞춤 태그를 사용 설정하면 오류 및 경고 로그 메시지만 표시됩니다.

loggerLevelByTags 구성은 선택사항입니다. 맞춤 태그가 로거 수준에 구성되지 않은 경우 모든 로그 메시지가 디버그 오버레이에 표시됩니다.

CORE 이벤트 로거 아래에 다음 코드를 추가합니다.

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
}

// Set verbosity level for custom tags.
castDebugLogger.loggerLevelByTags = {
    [LOG_TAG]: cast.framework.LoggerLevel.DEBUG,
};

디버그 오버레이

Cast 디버그 로거는 수신기에 디버그 오버레이를 제공하여 Cast 기기에 맞춤 로그 메시지를 표시합니다. showDebugLogs를 사용하여 디버그 오버레이를 전환하고 clearDebugLogs를 사용하여 오버레이의 로그 메시지를 지웁니다.

수신기에서 디버그 오버레이를 미리 보려면 다음 코드를 추가합니다.

context.addEventListener(cast.framework.system.EventType.READY, () => {
  if (!castDebugLogger.debugOverlayElement_) {
      // Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
      castDebugLogger.setEnabled(true);

      // Show debug overlay
      castDebugLogger.showDebugLogs(true);

      // Clear log messages on debug overlay
      castDebugLogger.clearDebugLogs();
  }
});

동영상 프레임 위의 반투명 배경에 디버그 로그 메시지 목록인 디버그 오버레이를 보여주는 이미지

13. 축하합니다

지금까지 Cast 웹 수신기 SDK를 사용하여 맞춤 웹 수신기 애플리케이션을 만드는 방법을 알아봤습니다.

자세한 내용은 웹 수신기 개발자 가이드를 참고하세요.