Chromecast에서 IMA DAI SDK 사용

Google Cloud Video Stitcher API에 등록된 라이브 스트림 재생

이 가이드에서는 CAF 웹 수신기용 IMA DAI SDK를 사용하여 Google Cloud Video Stitcher API에 등록된 이벤트의 라이브 스트림을 요청하고 재생하는 방법과 재생 중에 광고 시점을 삽입하는 방법을 설명합니다.

이 가이드에서는 전체 서비스 DAI의 기본 예시를 바탕으로 Google Cloud Video Stitcher API에 등록된 스트림에 대한 지원을 추가하는 방법을 설명합니다.

계속하기 전에 CAF 웹 수신기에서 스트리밍 형식을 지원하는지 확인합니다.

다른 플랫폼과 통합하거나 IMA 클라이언트 측 SDK를 사용하는 방법에 대한 자세한 내용은 양방향 미디어 광고 SDK를 참조하세요.

배경

이 가이드를 사용하기 전에 Chromecast 애플리케이션 프레임워크의 웹 수신기 프로토콜에 익숙해야 합니다.

이 가이드에서는 메시지 인터셉터, MediaInformation 객체와 같은 CAF 수신기 개념에 대한 기본 수준의 지식이 있고 전송 명령어와 제어 도구를 사용하여 CAF 발신기를 에뮬레이션한다고 가정합니다.

앱 구성요소 및 아키텍처

IMA CAF DAI SDK와 함께 Google Cloud Video Stitcher API를 사용하여 라이브 스트림 재생을 구현하려면 이 가이드의 설명대로 두 가지 주요 구성요소가 필요합니다.

  • VideoStitcherLiveStreamRequest: Google 서버에 대한 스트림 요청을 정의하는 객체입니다. 이 요청은 Cloud Video Stitcher API 인스턴스, 라이브 구성 ID, 기타 선택적 매개변수를 지정합니다.
  • StreamManager: 동영상 스트림과 IMA DAI SDK 간의 통신(예: 추적 핑 실행 및 스트림 이벤트를 게시자에게 전달)을 처리하는 객체입니다.

기본 요건

IMA SDK에는 다음 변수가 필요합니다.

  • 라이브 구성 ID: Video Stitcher API 라이브 구성을 만들 때 지정한 라이브 구성 ID입니다.

    LIVE_CONFIG_ID

  • 위치: 라이브 구성이 생성된 Google Cloud 리전입니다.

    LOCATION

  • 프로젝트 번호: Video Stitcher API를 사용하는 Google Cloud 프로젝트 번호입니다.

    PROJECT_NUMBER

  • OAuth 토큰: Video Stitcher 사용자 역할이 있는 서비스 계정의 단기 OAuth 토큰입니다. 서비스 계정의 단기 사용자 인증 정보 만들기에 대해 자세히 알아보세요.

    OAUTH_TOKEN

  • 네트워크 코드: 광고 요청에 사용되는 Google Ad Manager 네트워크 코드입니다.

    NETWORK_CODE

  • 커스텀 애셋 키: Video Stitcher API를 사용하여 라이브 스트림 이벤트 구성을 만드는 과정에서 생성된 Google Ad Manager 커스텀 애셋 키입니다.

    CUSTOM_ASSET_KEY

커스텀 전송 수신기에는 다음이 필요합니다.

  • 허용 목록에 테스트 기기가 있는 전송 개발자 콘솔 계정

  • 전송 개발자 콘솔에 등록되었으며 이 가이드에서 제공하는 코드를 호스팅하도록 수정할 수 있는 호스팅된 웹 수신기 앱

  • 웹 수신기 앱을 사용하도록 구성된 전송 앱. 이 예시를 위해 이 가이드에서는 전송 명령어 및 제어 도구를 발신기로 사용합니다.

스트림 데이터를 수신기에 전달할 발신기 준비

먼저 플랫폼의 MediaInformation 객체에 다음 필드를 포함하여 웹 수신기에 로드 요청을 보내는 발신기 앱을 구성합니다.

필드 목차
contentId 전송 참고 문서에서 정의된 이 미디어 항목의 고유 식별자입니다. 이 ID를 같은 미디어 큐의 여러 항목에 재사용하면 안 됩니다.

CONTENT_ID

contentUrl DAI 스트림이 로드할 수 없는 경우 재생할 선택적 백업 스트림 URL입니다.

BACKUP_STREAM_URL

contentType DAI 스트림이 로드할 수 없는 경우 재생할 백업 스트림 URL의 선택적 Mimetype입니다.

BACKUP_STREAM_MIMETYPE

streamType 이 값에 사용되는 문자열 리터럴이나 상수는 발신기 플랫폼에 따라 다릅니다.

LIVE

customData

customData 필드에는 추가 필수 필드의 키-값 저장소가 포함됩니다. 이 경우 customData에는 수집한 DAI 스트림 데이터가 포함됩니다.

필드 목차
liveConfigID LIVE_CONFIG_ID
region LOCATION
projectNumber PROJECT_NUMBER
oAuthToken OAUTH_TOKEN
networkCode NETWORK_CODE
customAssetKey CUSTOM_ASSET_KEY

다음은 시작하는 데 도움이 되는 몇 가지 코드 샘플입니다.

전송 웹 발신기에서 이러한 값을 구성하려면 먼저 필수 데이터가 있는 MediaInfo 객체를 만든 후 로드 요청 웹 수신기에 보냅니다.

// Create mediaInfo object
const mediaInfo = new chrome.cast.media.MediaInfo("CONTENT_ID");
mediaInfo.contentUrl = "BACKUP_STREAM_URL";
mediaInfo.contentType = "BACKUP_STREAM_MIMETYPE";
mediaInfo.streamType = chrome.cast.media.StreamType.LIVE;
mediaInfo.customData = {
liveConfigID: "LIVE_CONFIG_ID",
region: "LOCATION",
projectNumber: "PROJECT_NUMBER",
oAuthToken: "OAUTH_TOKEN",
networkCode: "NETWORK_CODE",
customAssetKey: "CUSTOM_ASSET_KEY"
};

// Make load request to cast web receiver
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
const request = new chrome.cast.media.LoadRequest(mediaInfo);
castSession.loadMedia(request).then(
  () => { console.log('Load succeed'); },
  (errorCode) => { console.log('Error code: ' + errorCode); });

Android

전송 웹 발신기에서 이러한 값을 구성하려면 먼저 필수 데이터가 있는 MediaInfo 객체를 만든 후 로드 요청 웹 수신기에 보냅니다.

JSONObject customData = new JSONObject()
  .put("liveConfigID", "LIVE_CONFIG_ID")
  .put("region", "LOCATION")
  .put("projectNumber", "PROJECT_NUMBER")
  .put("oAuthToken", "OAUTH_TOKEN")
  .put("networkCode", "NETWORK_CODE")
  .put("customAssetKey", "CUSTOM_ASSET_KEY");

MediaInfo mediaInfo = MediaInfo.Builder("CONTENT_ID")
  .setContentUrl("BACKUP_STREAM_URL")
  .setContentType("BACKUP_STREAM_MIMETYPE")
  .setStreamType(MediaInfo.STREAM_TYPE_LIVE)
  .setCustomData(customData)
  .build();

RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());

iOS(Obj-C)

전송 웹 발신기에서 이러한 값을 구성하려면 먼저 필수 데이터가 있는 GCKMediaInformation 객체를 만든 후 로드 요청 웹 수신기에 보냅니다.

NSURL url = [NSURL URLWithString:@"BACKUP_STREAM_URL"];
NSDictionary *customData = @{
  @"liveConfigID": @"LIVE_CONFIG_ID",
  @"region": @"LOCATION",
  @"projectNumber": @"PROJECT_NUMBER",
  @"oAuthToken": @"OAUTH_TOKEN",
  @"networkCode": @"NETWORK_CODE",
  @"customAssetKey": @"CUSTOM_ASSET_KEY"
};

GCKMediaInformationBuilder *mediaInfoBuilder =
  [[GCKMediaInformationBuilder alloc] initWithContentID: @"CONTENT_ID"];
mediaInfoBuilder.contentURL = url;
mediaInfoBuilder.contentType = @"BACKUP_STREAM_MIMETYPE";
mediaInfoBuilder.streamType = GCKMediaStreamTypeLive;
mediaInfoBuilder.customData = customData;
self.mediaInformation = [mediaInfoBuilder build];

GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation];
if (request != nil) {
  request.delegate = self;
}

iOS(Swift)

전송 웹 발신기에서 이러한 값을 구성하려면 먼저 필수 데이터가 있는 GCKMediaInformation 객체를 만든 후 로드 요청 웹 수신기에 보냅니다.

let url = URL.init(string: "BACKUP_STREAM_URL")
guard let mediaURL = url else {
  print("invalid mediaURL")
  return
}

let customData = [
  "liveConfigID": "LIVE_CONFIG_ID",
  "region": "LOCATION",
  "projectNumber": "PROJECT_NUMBER",
  "oAuthToken": "OAUTH_TOKEN",
  "networkCode": "NETWORK_CODE",
  "customAssetKey": "CUSTOM_ASSET_KEY"
]

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentId: "CONTENT_ID")
mediaInfoBuilder.contentURL = mediaUrl
mediaInfoBuilder.contentType = "BACKUP_STREAM_MIMETYPE"
mediaInfoBuilder.streamType = GCKMediaStreamType.Live
mediaInfoBuilder.customData = customData
mediaInformation = mediaInfoBuilder.build()

guard let mediaInfo = mediaInformation else {
  print("invalid mediaInformation")
  return
}

if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) {
  request.delegate = self
}

CAC 도구

전송 명령어 및 제어 도구에서 이러한 값을 구성하려면 미디어 로드 탭을 클릭하고 커스텀 로드 요청 유형을 LOAD로 설정합니다. 그런 다음 텍스트 영역의 JSON 데이터를 다음 JSON으로 바꿉니다.

{
  "media": {
    "contentId": "CONTENT_ID",
    "contentUrl": "BACKUP_STREAM_URL",
    "contentType": "BACKUP_STREAM_MIMETYPE",
    "streamType": "LIVE",
    "customData": {
      "liveConfigID": "LIVE_CONFIG_ID",
      "region": "LOCATION",
      "projectNumber": "PROJECT_NUMBER",
      "oAuthToken": "OAUTH_TOKEN",
      "networkCode": "NETWORK_CODE",
      "customAssetKey": "CUSTOM_ASSET_KEY"
    }
  }
}

이 커스텀 부하 요청을 수신기에게 전송하여 나머지 단계를 테스트할 수 있습니다.

커스텀 CAF 웹 수신기 만들기

CAF SDK 커스텀 웹 수신기 가이드와 같이 커스텀 웹 수신기를 만듭니다.

수신기 코드는 다음과 같이 표시됩니다.

<html>
<head>
  <script
      src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js">
  </script>
</head>
<body>
  <cast-media-player></cast-media-player>
  <script>
    const castContext = cast.framework.CastReceiverContext.getInstance()
    castContext.start();
  </script>
</body>
</html>

IMA DAI SDK 가져오기 및 플레이어 관리자 가져오기

스크립트에서 CAF를 로드한 직후에 스크립트 태그를 추가하여 CAF용 IMA DAI SDK를 웹 수신기로 가져옵니다. 그런 다음 수신기를 시작하기 전에 뒤에 오는 스크립트 태그에 수신기 컨텍스트와 플레이어 관리자를 상수로 저장합니다.

<html>
<head>
  <script
      src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
  <script src="//imasdk.googleapis.com/js/sdkloader/cast_dai.js"></script>
</head>
<body>
  <cast-media-player></cast-media-player>
  <script>
    const castContext = cast.framework.CastReceiverContext.getInstance();
    const playerManager = castContext.getPlayerManager();

    castContext.start();
  </script>
</body>
</html>

IMA 스트림 관리자 초기화

IMA 스트림 관리자를 초기화합니다.

<html>
<head>
  <script type="text/javascript"
      src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
  <script src="//imasdk.googleapis.com/js/sdkloader/cast_dai.js"></script>
</head>
<body>
  <cast-media-player></cast-media-player>
  <script>
    const castContext = cast.framework.CastReceiverContext.getInstance();
    const playerManager = castContext.getPlayerManager();
    const streamManager = new google.ima.cast.dai.api.StreamManager();

    castContext.start();
  </script>
</body>
</html>

스트림 관리자 로드 인터셉터 만들기

미디어 항목이 CAF로 전달되기 전에 LOAD 메시지 인터셉터에서 스트림 요청을 만듭니다.

    const castContext = cast.framework.CastReceiverContext.getInstance();
    const playerManager = castContext.getPlayerManager();
    const streamManager = new google.ima.cast.dai.api.StreamManager();

    /**
     * Creates a livestream request object for the Video Stitcher API.
     * @param {!LoadRequestData} castRequest The request object from the cast sender
     * @return {StreamRequest} an IMA stream request
     */
    const createStreamRequest = (castRequest) => { /* ... */};

    /**
     * Initates a DAI stream request for the final stream manifest.
     * @param {!LoadRequestData} castRequest The request object from the cast sender
     * @return {Promise<LoadRequestData>} a promise that resolves to an updated castRequest, containing the DAI stream manifest
     */
    const createDAICastRequest = (castRequest) => {
        return streamManager.requestStream(castRequest, createStreamRequest(castRequest))
          .then((castRequestWithStreamData) => {
            console.log('Successfully made DAI stream request.');
            return castRequestWithStreamData;
          })
          .catch((error) => {
            console.log('Failed to make DAI stream request.');
            // CAF will automatically fallback to the content URL
            // that it can read from the castRequest object.
            return castRequest;
          });
    };

    playerManager.setMessageInterceptor(
        cast.framework.messages.MessageType.LOAD, createDAICastRequest);

    castContext.start();

스트림 요청 만들기

createStreamRequest 함수를 완료하여 CAF 로드 요청에 따라 Video Stitcher API 라이브 스트림 요청을 만듭니다.

    /**
     * Creates a livestream request object for the Video Stitcher API.
     * @param {!LoadRequestData} castRequest The request object from the cast sender
     * @return {StreamRequest} an IMA stream request
     */
    const createStreamRequest = (castRequest) => {
      const streamRequest = new google.ima.cast.dai.api.VideoStitcherLiveStreamRequest();
      const customData = castRequest.media.customData;

      streamRequest.liveStreamEventId = customData.liveConfigID;
      streamRequest.region = customData.region;
      streamRequest.projectNumber = customData.projectNumber;
      streamRequest.oAuthToken = customData.oAuthToken;
      streamRequest.networkCode = customData.networkCode;
      streamRequest.customAssetKey = customData.customAssetKey;

      return streamRequest;
    };