전체 서비스 DAI로 설정

플랫폼 선택: HTML5 Android iOS tvOS Cast Roku

이 가이드에서는 IMA DAI SDK를 동영상 플레이어 앱에 통합하는 방법을 설명합니다. 완료된 샘플 통합을 보거나 따라 하려면 GitHub에서 기본 예를 다운로드하세요.

IMA DAI 개요

IMA DAI를 구현하려면 이 가이드에 설명된 대로 두 가지 주요 SDK 구성요소가 필요합니다.

  • StreamRequest: 스트림 요청을 정의하는 객체입니다. 스트림 요청은 VOD 또는 라이브 스트림에 대한 요청일 수 있습니다. 라이브 스트림 요청은 애셋 키를 지정하는 반면 VOD 요청은 CMS ID와 동영상 ID를 지정합니다. 두 요청 유형 모두 선택적으로 지정된 스트림에 액세스하는 데 필요한 API 키와 IMA SDK가 Google Ad Manager 설정에 지정된 대로 광고 식별자를 처리하는 데 필요한 Google Ad Manager 네트워크 코드를 포함할 수 있습니다.
  • StreamManager: 동적 광고 삽입 스트림 및 DAI 백엔드와의 상호작용을 처리하는 객체입니다. 스트림 관리자는 추적 핑을 처리하고 스트림 및 광고 이벤트를 게시자에게 전달합니다.

기본 요건

  • 호환성 페이지를 읽고 의도한 사용 사례가 지원되는지 확인합니다.
  • Roku 샘플 플레이어 코드를 다운로드합니다.
  • 샘플 플레이어 코드를 Roku 기기에 배포하여 개발 설정이 작동하는지 확인합니다.

동영상 재생

제공된 샘플 동영상 플레이어는 콘텐츠 동영상을 즉시 재생합니다. 샘플 플레이어를 Roku 플레이어에 배포하여 개발 환경이 올바르게 설정되었는지 확인합니다.

동영상 플레이어를 IMA DAI 스트림 플레이어로 전환하기

스트림 플레이어를 구현하려면 다음 단계를 따르세요.

Sdk.xml 만들기

MainScene.xml와 함께 프로젝트에 Sdk.xml라는 새 파일을 추가하고 다음 상용구를 추가합니다.

Sdk.xml

<?xml version = "1.0" encoding = "utf-8" ?>

<component name = "imasdk" extends = "Task">
<interface>
</interface>
<script type = "text/brightscript">
<![CDATA[
  ' Your code goes here.
]]>
</script>
</component>

이 가이드 전체에서 이 두 파일을 모두 수정해야 합니다.

Roku 광고 프레임워크 로드

IMA DAI SDK는 Roku 광고 프레임워크에 종속됩니다. 프레임워크를 로드하려면 manifest 및 Sdk.xml에 다음을 추가합니다.

bs_libs_required=roku_ads_lib,googleima3
Library "Roku_Ads.brs"
Library "IMA3.brs"

IMA DAI SDK 로드

IMA DAI SDK를 로드하려면 다음 단계를 따르세요.

  1. New_IMASDK() 호출로 IMA SDK를 초기화합니다.

    sub loadSdk()
      If m.sdk = invalid
        m.sdk = New_IMASDK()
      End If
      m.top.sdkLoaded = true
    End Sub
    
  2. sdkLoaded 불리언 필드를 만들어 IMA가 로드되었는지 추적합니다.

    <field id="sdkLoaded" type="Boolean" />
    
  3. 기본 runThread() 서브루틴에서 loadSdk() 서브루틴을 호출합니다.

    if not m.top.sdkLoaded
      loadSdk()
    End If
    
  4. MainScene.xml에서 loadImaSdk() 함수를 만들어 sdkTask 객체를 만들고 실행합니다.

    function loadImaSdk() as void
      m.sdkTask = createObject("roSGNode", "imasdk")
      m.sdkTask.observeField("sdkLoaded", "onSdkLoaded")
      m.sdkTask.observeField("errors", "onSdkLoadedError")
    
      ' Change to m.testLiveStream to demo live instead of VOD.
      selectedStream = m.testVodStream
      m.videoTitle = selectedStream.title
      m.sdkTask.streamData = selectedStream
    
      m.sdkTask.observeField("urlData", "urlLoadRequested")
      m.sdkTask.video = m.video
      ' Setting control to run starts the task thread.
      m.sdkTask.control = "RUN"
    end function
    
  5. init() 함수에서 loadImaSdk() 함수를 호출합니다.

  6. SDK 로드 이벤트에 응답하는 onSdkLoaded() 및 onSdkLoadedError() 리스너 서브루틴을 만듭니다.

    Sub onSdkLoaded(message as Object)
      print "----- onSdkLoaded --- control ";message
    End Sub
    
    Sub onSdkLoadedError(message as Object)
      print "----- errors in the sdk loading process --- ";message
    End Sub
    

IMA 스트림 플레이어 만들기

IMA 스트림 플레이어를 만들려면 다음 단계를 따르세요.

  1. 다음을 수행하는 setupVideoPlayer() 서브루틴을 만듭니다.

    1. createPlayer() 메서드를 사용하여 스트림 플레이어를 만듭니다.

    2. 스트림 플레이어가 loadUrl, adBreakStarted, adBreakEnded의 세 가지 콜백 메서드를 구현하도록 합니다.

    3. 스트림이 로드될 때 트릭 플레이를 사용 중지하여 광고 시점 시작 이벤트가 실행되기 전에 스트림이 시작되는 순간 사용자가 프리롤을 건너뛰지 못하도록 합니다.

    sub setupVideoPlayer()
      sdk = m.sdk
      m.player = sdk.createPlayer()
      m.player.top = m.top
      m.player.loadUrl = Function(urlData)
        ' This line prevents users from scanning during buffering
        ' or during the first second of the ad before we have a callback from roku.
        ' If there are no prerolls disabling trickplay isn't needed.
        m.top.video.enableTrickPlay = false
        m.top.urlData = urlData
      End Function
      m.player.adBreakStarted = Function(adBreakInfo as Object)
        print "---- Ad Break Started ---- "
        m.top.adPlaying = True
        m.top.video.enableTrickPlay = false
      End Function
      m.player.adBreakEnded = Function(adBreakInfo as Object)
        print "---- Ad Break Ended ---- "
        m.top.adPlaying = False
        m.top.video.enableTrickPlay = true
      End Function
      m.player.seek = Function(timeSeconds as Double)
        print "---- SDK requested seek to ----" ; timeSeconds
        m.top.video.seekMode = "accurate"
        m.top.video.seek = timeSeconds
      End Function
    End Sub
    

    seek 콜백 메서드를 추가하여 건너뛸 수 있는 광고를 지원합니다. 자세한 내용은 건너뛸 수 있는 광고 지원 추가를 참고하세요.

  2. setupVideoPlayer() 서브루틴에 사용되는 urlData, adPlaying, video 필드를 추가합니다.

    <field id="urlData" type="assocarray" />
    <field id="adPlaying" type="Boolean" />
    <field id="video" type="Node" />
    

스트림 요청 만들기 및 실행

DAI 스트림을 요청하려면 다음 단계를 따르세요.

  1. 스트림을 만들고 요청하는 loadStream() 서브루틴을 만듭니다. AdChoices 아이콘과 같은 광고 UI를 지원하려면 요청의 일부로 콘텐츠 동영상이 포함된 노드에 대한 참조도 전달해야 합니다.

    Sub loadStream()
      sdk = m.sdk
      sdk.initSdk()
      setupVideoPlayer()
    
      request = {}
      streamData = m.top.streamData
      if streamData.type = "live"
        request = sdk.CreateLiveStreamRequest(streamData.assetKey, streamData.apiKey, streamData.networkCode)
      else if streamData.type = "vod"
        request = sdk.CreateVodStreamRequest(streamData.contentSourceId, streamData.videoId, streamData.apiKey, streamData.networkCode)
      else
        request = sdk.CreateStreamRequest()
      end if
    
      request.player = m.player
      request.adUiNode = m.top.video
    
      requestResult = sdk.requestStream(request)
      If requestResult <> Invalid
        print "Error requesting stream ";requestResult
      Else
        m.streamManager = Invalid
        While m.streamManager = Invalid
          sleep(50)
          m.streamManager = sdk.getStreamManager()
        End While
        If m.streamManager = Invalid or m.streamManager["type"] <> Invalid or m.streamManager["type"] = "error"
          errors = CreateObject("roArray", 1, True)
          print "error ";m.streamManager["info"]
          errors.push(m.streamManager["info"])
          m.top.errors = errors
        Else
          m.top.streamManagerReady = True
          addCallbacks()
          m.streamManager.start()
        End If
      End If
    End Sub
    
  2. loadStream() 서브루틴에 사용되는 streamData 및 streamManagerReady 필드를 추가합니다.

    <field id="streamManagerReady" type="Boolean" />
    <field id="streamData" type="assocarray" />
    
  3. 스트림 관리자를 사용할 수 없는 경우 runThread() 서브루틴에서 loadStream() 서브루틴을 호출합니다.

    if not m.top.streamManagerReady
      loadStream()
    End If
    
  4. VOD 또는 라이브 스트림 중에서 선택합니다. 다음 예시에는 라이브 스트림과 VOD 스트림의 스트림 매개변수가 있습니다.

    m.testLiveStream = {
      title: "Live Stream",
      assetKey: "c-rArva4ShKVIAkNfy6HUQ",
      networkCode: "21775744923",
      apiKey: "",
      type: "live"
    }
    m.testVodStream = {
      title: "VOD stream"
      contentSourceId: "2548831",
      videoId: "tears-of-steel",
      networkCode: "21775744923",
      apiKey: "",
      type: "vod"
    }
    

    기본적으로 이 가이드에서는 VOD 스트림을 사용합니다. m.testVodStream 객체에서 m.testLiveStream 객체로 selectedStream 변수를 변경하여 라이브 스트림을 대신 사용할 수 있습니다.

스트림 시작

스트림 데이터를 수신 대기하고 playStream() 서브루틴을 호출하는 urlLoadRequested() 서브루틴을 만듭니다.

Sub urlLoadRequested(message as Object)
  print "Url Load Requested ";message
  data = message.getData()

  playStream(data.manifest, data.format)
End Sub

playStream()를 만들어 스트림 재생을 시작합니다.

Sub playStream(url as String, format as String)
  vidContent = createObject("RoSGNode", "ContentNode")
  vidContent.url = url
  vidContent.title = m.videoTitle
  vidContent.streamformat = format
  m.video.content = vidContent
  m.video.setFocus(true)
  m.video.visible = true
  m.video.control = "play"
  m.video.EnableCookies()
End Sub

스트림 메타데이터 수신 대기

스트림 재생 중에 실행할 while 루프를 사용하여 runLoop() 서브루틴을 만들고 StreamManager.onMessage()를 사용하여 스트림 메타데이터를 IMA에 전송합니다.

Sub runLoop()
  ' Forward all timed metadata events.
  m.top.video.timedMetaDataSelectionKeys = ["*"]

  ' Cycle through all the fields and just listen to them all.
  m.port = CreateObject("roMessagePort")
  fields = m.top.video.getFields()
  for each field in fields
    m.top.video.observeField(field, m.port)
  end for

  while True
    msg = wait(1000, m.port)
    if m.top.video = invalid
      print "exiting"
      exit while
    end if

    m.streamManager.onMessage(msg)
    currentTime = m.top.video.position
    ' Only enable trickplay after a few seconds, in case we start with an ad,
    ' to prevent users from skipping through that ad.
    If currentTime > 3 And not m.top.adPlaying
       m.top.video.enableTrickPlay = true
    End If
  end while
End Sub

광고 이벤트 수신 대기

이제 스트림 메타데이터를 IMA에 전달하므로 IMA는 광고 시점 중에 광고 이벤트를 내보낼 수 있습니다. 광고 이벤트에 응답하는 데 필요한 광고 이벤트 리스너를 만듭니다.

Function addCallbacks() as Void
  m.streamManager.addEventListener(m.sdk.AdEvent.ERROR, errorCallback)
  m.streamManager.addEventListener(m.sdk.AdEvent.START, startCallback)
  m.streamManager.addEventListener(m.sdk.AdEvent.FIRST_QUARTILE, firstQuartileCallback)
  m.streamManager.addEventListener(m.sdk.AdEvent.MIDPOINT, midpointCallback)
  m.streamManager.addEventListener(m.sdk.AdEvent.THIRD_QUARTILE, thirdQuartileCallback)
  m.streamManager.addEventListener(m.sdk.AdEvent.COMPLETE, completeCallback)
End Function

Function startCallback(ad as Object) as Void
  print "Callback from SDK -- Start called - "
End Function

Function firstQuartileCallback(ad as Object) as Void
  print "Callback from SDK -- First quartile called - "
End Function

Function midpointCallback(ad as Object) as Void
  print "Callback from SDK -- Midpoint called - "
End Function

Function thirdQuartileCallback(ad as Object) as Void
  print "Callback from SDK -- Third quartile called - "
End Function

Function completeCallback(ad as Object) as Void
  print "Callback from SDK -- Complete called - "
End Function

Function errorCallback(error as Object) as Void
  print "Callback from SDK -- Error called - "; error
  ' errors are critical and should terminate the stream.
  m.errorState = True
End Function

건너뛸 수 있는 광고 지원 추가 (선택사항)

건너뛸 수 있는 광고를 지원하려면 IMA DAI SDK의 player 객체에 seek 메서드를 추가해야 합니다. 이 메서드는 동영상을 지정된 위치로 프로그래매틱 방식으로 탐색합니다(부동 소수점 초 단위).

건너뛸 수 있는 광고를 지원하려면 요청에서 adUiNode도 설정해야 합니다.

m.player.seek = Function(timeSeconds as Double)
  print "---- SDK requested seek to ----" ; timeSeconds
  m.top.video.seekMode = "accurate"
  m.top.video.seek = timeSeconds
End Function