IMA SDK を使用すると、ウェブサイトやアプリにマルチメディア広告を簡単に統合できます。IMA SDK は、VAST 準拠の任意の広告サーバーから広告をリクエストし、アプリ内の広告再生を管理できます。IMA DAI SDK を使用すると、アプリは広告とコンテンツ動画(VOD またはライブ コンテンツ)のストリーム リクエストを行います。SDK は統合された動画ストリームを返すため、アプリ内で広告動画とコンテンツ動画を切り替える必要はありません。
関心のある DAI ソリューションを選択する
このガイドでは、Roku 用の IMA DAI SDK を使用して、DAI Pod Serving のライブ ストリームまたは VOD ストリームを再生する方法について説明します。
IMA DAI Pod Serving の概要
IMA DAI を使用して連続配信広告を実装するには、このガイドで説明しているように、次の 2 つの主要な SDK コンポーネントが必要です。
StreamRequest.createPodLiveStreamRequest()
/StreamRequest.createPodVodStreamRequest()
: Google の広告サーバーのストリーム リクエストを定義するオブジェクトを作成します。これらのリクエストではネットワーク コードを指定します。Pod Liveima.StreamRequest
には、カスタム アセットキーとオプションの API キーも必要です。StreamManager
: 動画ストリームと IMA DAI SDK 間の通信を処理するオブジェクト(トラッキング ピングの発行、ストリーム イベントのパブリッシャーへの転送など)。
また、マニフェスト操作サーバーにリクエストを送信して、アプリに表示するストリーミング マニフェストを取得する必要があります。具体的な手順は動画技術パートナー(VTP)によって異なります。
前提条件
- 互換性に関するページを読み、目的のユースケースがサポートされていることを確認してください。
- Roku のサンプル プレーヤー コードをダウンロードします。
- サンプル プレーヤー コードを Roku デバイスにデプロイして、開発設定が機能していることを確認します。
動画の再生
提供されているサンプル動画プレーヤーは、コンテンツ動画をすぐに再生できます。サンプル プレーヤーを Roku デバイスにデプロイして、開発環境が正しく設定されていることを確認します。
動画プレーヤーを IMA DAI ストリーム プレーヤーに変換する
ストリーミング プレーヤーを実装する手順は次のとおりです。
Sdk.xml を作成する
Sdk.xml
という MainScene.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>
このガイドでは、これらのファイル(MainScene.xml
と Sdk.xml
)の両方を編集する必要があります。
IMA DAI SDK フレームワークを読み込む
フレームワークを読み込むには、manifest
と Sdk.xml
に以下を追加します。
マニフェスト
bs_libs_required=googleima3
Sdk.xml
<?xml version = "1.0" encoding = "utf-8" ?>
<component name = "imasdk" extends = "Task">
<interface>
</interface>
<script type = "text/brightscript">
<![CDATA[
Library "IMA3.brs"
]]>
</script>
</component>
IMA DAI SDK を初期化する
IMA ダイナミック広告挿入ストリームを読み込む最初のステップは、IMA DAI SDK を読み込んで初期化することです。以下では、IMA DAI SDK スクリプトを初期化します。
Sdk.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="IMASDKTask" extends="Task">
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
</interface>
<script type = "text/brightscript">
<![CDATA[
Library "IMA3.brs"
sub init()
m.top.functionName = "runThread"
end sub
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
end sub
sub initializeIMASDK()
if m.sdk = invalid
m.sdk = New_IMASDK()
end if
m.top.IMASDKInitialized = true
end sub
]]>
</script>
</component>
次に、MainScene.xml
でこのタスクを開始し、コンテンツ ストリームを読み込む呼び出しを削除します。
MainScene.xml
<?xml version="1.0" encoding="utf-8" ?>
<component extends="Scene" initialFocus="myVideo" name="MainScene">
<script type="text/brightscript">
<![CDATA[
function init()
m.video = m.top.findNode("myVideo")
m.video.notificationinterval = 1
runIMASDKTask()
end function
function runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.IMASDKTask.control = "RUN"
end function
sub handleIMASDKInitialized()
' Follow your manifest manipulator (VTP) documentation to register a user
' streaming session if needed.
end sub
sub handleIMASDKErrors(message as object)
print "------ IMA DAI SDK failed ------"
if message <> invalid and message.getData() <> invalid
print "IMA DAI SDK Error ";message.getData()
end if
end sub
]]>
</script>
<children>
<Video height="720" id="myVideo" visible="false" width="1280"/>
</children>
</component>
IMA ストリーム プレーヤーを作成する
次に、既存の roVideoScreen
を使用して IMA ストリーム プレーヤーを作成する必要があります。
ライブ配信の連続配信広告
ライブ配信の場合、このストリーム プレーヤーは streamInitialized
、adBreakStarted
、adBreakEnded
の 3 つのコールバック メソッドを実装します。
ストリームの読み込み時にトリックプレイも無効にする。これにより、ミッドロール挿入点の開始イベントがトリガーされる前に、ユーザーがプレロールの開始直後にスキップするのを防ぐことができます。
Sdk.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="IMASDKTask" extends="Task">
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
<field id="urlData" type="assocarray" />
<field id="adPlaying" type="Boolean" />
<field id="videoNode" type="Node" />
</interface>
<script type="text/brightscript">
...
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
end sub
...
sub initializeIMASDK()
if m.ima = invalid
ima = New_IMASDK()
ima.initSdk()
m.ima = ima
end if
m.top.IMASDKInitialized = true
end sub
sub setupPlayerCallbacks()
m.player = m.ima.createPlayer()
m.player.top = m.top
m.player.streamInitialized = function(urlData)
m.top.videoNode.enableTrickPlay = false
m.top.urlData = urlData
end function
m.player.adBreakStarted = function(adBreakInfo)
print "------ Ad break started ------"
m.top.adPlaying = true
m.top.videoNode.enableTrickPlay = false
end function
m.player.adBreakEnded = function(adBreakInfo)
print "------ Ad break ended ------"
m.top.adPlaying = false
m.top.videoNode.enableTrickPlay = true
end function
end sub
</script>
...
</component>
VOD ストリーム Pod サービング
VOD ストリームの場合、このストリーム プレーヤーは streamInitialized
、loadUrl
、adBreakStarted
、adBreakEnded
の 4 つのコールバック メソッドを実装します。streamInitialized
コールバックで、必ず StreamManager.loadThirdPartyStream()
を呼び出します。これを行わないと、SDK は loadUrl
関数をトリガーしません。
このステップでは、loadAdPodStream()
で取得したストリーム ID を使用して、動画技術パートナー(VTP)にストリーム URL をリクエストします。次に、連続配信広告のマニフェストと VTP から返された字幕を使用して StreamManager.loadThirdPartyStream()
を呼び出します。
ストリームの読み込み時にトリックプレイも無効にする。これにより、ミッドロール挿入点の開始イベントが発生する前に、ユーザーがプレロールの開始と同時にスキップするのを防ぐことができます。
Sdk.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="IMASDKTask" extends="Task">
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
<field id="adStitchedStreamInfo" type="assocarray" />
<field id="adPlaying" type="Boolean" />
<field id="videoNode" type="Node" />
<field id="streamParameters" type="assocarray" />
</interface>
<script type="text/brightscript">
...
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
end sub
...
sub initializeIMASDK()
if m.ima = invalid
ima = New_IMASDK()
ima.initSdk()
m.ima = ima
end if
m.top.IMASDKInitialized = true
end sub
sub loadThirdPartyStream(adStitchedManifest as string, subtitleConfig as dynamic)
m.streamManager.loadThirdPartyStream(adStitchedManifest, subtitleConfig)
end sub
sub setupPlayerCallbacks()
m.player = m.ima.createPlayer()
m.player.top = m.top
m.player.streamInitialized = function(urlData)
adStitchedManifest = m.top.streamParameters.VTPManifest.replace("[[STREAMID]]", urlData.streamId)
loadThirdPartyStream(adStitchedManifest, m.top.streamParameters.subtitleConfig)
end function
m.player.loadUrl = function(streamInfo)
m.top.adStitchedStreamInfo = streamInfo
end function
m.player.adBreakStarted = function(adBreakInfo)
print "------ Ad break started ------"
m.top.adPlaying = true
m.top.videoNode.enableTrickPlay = false
end function
m.player.adBreakEnded = function(adBreakInfo)
print "------ Ad break ended ------"
m.top.adPlaying = false
m.top.videoNode.enableTrickPlay = true
end function
end sub
</script>
...
</component>
ライブまたは VOD の Pod Serving ストリーム リクエストを作成して実行する
ストリーム プレーヤーを作成したら、ストリーム リクエストを作成して実行できます。この例では、m.testPodServingStream
にストリーミングを提供する Pod のデータがあります。
ライブ配信ポッドの配信
m.testPodServingStream
オブジェクトには、ネットワーク コードやカスタム アセットキーなど、Google アド マネージャーで該当のストリームを識別するために必要なパラメータを保存します。また、マニフェスト操作サーバーにアクセスするために使用されるマニフェスト URL も保存します。この場合、マニフェスト URL には、ストリーム リクエストが返された後に Google ストリーム ID を追加する必要があります。
adChoices アイコンなどの AdUI をサポートできるようにするには、リクエストの一部として、コンテンツ動画を含むノードへの参照も渡す必要があります。
MainScene.xml
function init()
m.video = m.top.findNode("myVideo")
m.video.notificationinterval = 1
m.testPodServingStream = {
title: "Test live stream for DAI Pod Serving",
assetKey: "test-live-stream",
networkCode: "your-network-code",
manifest: "https://.../master.m3u8?stream_id=[[STREAMID]]",
apiKey: ""
}
runIMASDKTask()
end function
function runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.IMASDKTask.control = "RUN"
end function
Sdk.xml
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
<field id="urlData" type="assocarray" />
<field id="adPlaying" type="Boolean" />
<field id="videoNode" type="Node" />
<field id="streamParameters" type="assocarray" />
</interface>
...
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
end sub
sub loadAdPodStream()
request = m.ima.CreatePodLiveStreamRequest(m.top.streamParameters.assetKey, m.top.streamParameters.networkCode, m.top.streamParameters.apiKey)
' Set the player object so that the request can trigger the player's
' callbacks at stream initialization or playback events.
request.player = m.player
' Set the video node for the IMA DAI SDK to create ad UI as its child nodes.
request.adUiNode = m.top.video
requestResult = m.ima.requestStream(request)
if requestResult <> invalid
print "Error requesting stream ";requestResult
return
end if
m.streamManager = invalid
while m.streamManager = invalid
sleep(50)
m.streamManager = m.ima.getStreamManager()
end while
if m.streamManager = invalid
errors = CreateObject("roArray", 1, True)
invalidStreamManagerError = "Invalid stream manager"
print invalidStreamManagerError
errors.push(invalidStreamManagerError)
m.top.errors = errors
return
end if
if m.streamManager["type"] <> invalid and m.streamManager["type"] = "error"
errors = CreateObject("roArray", 1, True)
print "Stream request returns an error. " ; m.streamManager["info"]
errors.push(m.streamManager["info"])
m.top.errors = errors
return
end if
setupStreamManager()
m.streamManager.start()
end sub
VOD ストリーム Pod サービング
m.testPodServingStream
オブジェクトに、ストリーミング リクエストで使用されるネットワーク コードを保存します。これにより、Google アド マネージャーはストリーム ID を提供できます。また、ユーザー固有のマニフェストへのアクセスに使用するマニフェスト URL をマニフェスト操作サーバーに格納します。
AdUI(AdChoices アイコンなど)をサポートするには、リクエストの一部としてコンテンツ動画を含むノードの参照も渡す必要があります。
MainScene.xml
sub init()
m.video = m.top.findNode("myVideo")
m.video.notificationinterval = 1
m.testPodServingStream = {
title: "Pod Serving VOD Stream",
networkCode: "your-network-code",
VTPManifest: "https://.../manifest.m3u8?gam-stream-id=[[STREAMID]]",
subtitleConfig: []
}
runIMASDKTask()
end sub
sub runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.IMASDKTask.control = "RUN"
end sub
Sdk.xml
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
end sub
sub loadAdPodStream()
request = m.ima.CreatePodVodStreamRequest(m.top.streamParameters.networkCode)
' Set the player object so that the request can trigger the player
' callbacks at stream initialization or playback events.
request.player = m.player
' Set the video node for the IMA DAI SDK to create ad UI as its child nodes.
request.adUiNode = m.top.video
requestResult = m.ima.requestStream(request)
if requestResult <> invalid
print "Error requesting stream ";requestResult
return
end if
m.streamManager = invalid
while m.streamManager = invalid
sleep(50)
m.streamManager = m.ima.getStreamManager()
end while
if m.streamManager = invalid
errors = CreateObject("roArray", 1, True)
invalidStreamManagerError = "Invalid stream manager"
print invalidStreamManagerError
errors.push(invalidStreamManagerError)
m.top.errors = errors
return
end if
if m.streamManager["type"] <> invalid and m.streamManager["type"] = "error"
errors = CreateObject("roArray", 1, True)
print "Stream request returns an error. " ; m.streamManager["info"]
errors.push(m.streamManager["info"])
m.top.errors = errors
return
end if
setupStreamManager()
m.streamManager.start()
end sub
イベント リスナーを追加してストリームを開始する
ライブ配信ポッドの配信
ストリームをリクエストした後、残りの作業は、広告の進行状況を追跡するイベント リスナーを追加して、Roku メッセージを SDK に転送するといういくつかの作業のみです。広告の正しい再生を確実に行うには、すべてのメッセージを SDK に転送することが重要です。これを行わないと、広告の視聴回数が正しくレポートされません。
このステップでは、[[STREAMID]]
マクロをストリーム ID に置き換え、完成したマニフェスト リクエスト URL を動画プレーヤーに渡す関数も追加します。この実装では、このステップでストリーム ID を取得しますが、VTP の統合によっては、このステップの前に使用できる場合があります。
MainScene.xml
function runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.sdkTask.observeField("adStitchedStreamInfo", "loadAdStitchedStream")
m.sdkTask.control = "RUN"
end function
sub loadAdStitchedStream(message as object)
print "Ad pod stream information ";message
adPodStreamInfo = message.getData()
manifest = m.testPodservingStream.manifest.Replace("[[STREAMID]]", adPodStreamInfo.streamId)
playStream(manifest, adPodStreamInfo.format)
end sub
sub playStream(url as string, format as string)
vidContent = createObject("RoSGNode", "ContentNode")
vidContent.url = url
vidContent.title = m.testPodservingStream.title
vidContent.streamformat = format
m.video.content = vidContent
m.video.setFocus(true)
m.video.visible = true
m.video.control = "play"
m.video.EnableCookies()
end sub
Sdk.xml
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
if m.streamManager <> invalid
runLoop()
end if
end sub
sub runLoop()
m.top.videoNode.timedMetaDataSelectionKeys = ["*"]
' IMPORTANT: Failure to listen to the position and timedmetadata fields
' could result in ad impressions not being reported.
m.port = CreateObject("roMessagePort")
m.top.videoNode.observeField("position", m.port)
m.top.videoNode.observeField("timedMetaData", m.port)
m.top.videoNode.observeField("timedMetaData2", m.port)
m.top.videoNode.observeField("state", m.port)
while True
msg = wait(1000, m.port)
if m.top.videoNode = invalid
print "exiting"
exit while
end if
m.streamManager.onMessage(msg)
currentTime = m.top.videoNode.position
if currentTime > 3 And not m.top.adPlaying
m.top.videoNode.enableTrickPlay = true
end if
end while
end sub
sub setupStreamManager()
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 sub
sub startCallback(ad as object)
print "Callback from SDK -- Start called - "
end sub
sub firstQuartileCallback(ad as object)
print "Callback from SDK -- First quartile called - "
end sub
sub midpointCallback(ad as object)
print "Callback from SDK -- Midpoint called - "
end sub
sub thirdQuartileCallback(ad as object)
print "Callback from SDK -- Third quartile called - "
end sub
sub completeCallback(ad as object)
print "Callback from SDK -- Complete called - "
end sub
function errorCallback(error as object)
print "Callback from SDK -- Error called - " ; error
m.errorState = True
end function
VOD ストリーム Pod サービング
ストリーミングをリクエストしたら、あとはイベント リスナーを追加して広告の進行状況を追跡し、Roku メッセージを SDK に転送するだけです。広告が適切に再生されるように、すべてのメッセージを SDK に転送することが重要です。設定しないと、広告の視聴回数が正しく報告されません。
MainScene.xml
sub runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.sdkTask.observeField("adStitchedStreamInfo", "loadAdStitchedStream")
m.sdkTask.control = "RUN"
end sub
sub loadAdStitchedStream(message as object)
print "Ad pod stream information ";message
adPodStreamInfo = message.getData()
end sub
sub playStream(url as string, format as string, subtitleConfig as object)
vidContent = createObject("RoSGNode", "ContentNode")
vidContent.title = m.testPodservingStream.title
vidContent.url = url
vidContent.subtitleConfig = subtitleConfig
vidContent.streamformat = format
m.video.content = vidContent
m.video.setFocus(true)
m.video.visible = true
m.video.control = "play"
m.video.EnableCookies()
end sub
Sdk.xml
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
if m.streamManager <> invalid
runLoop()
end if
end sub
sub runLoop()
m.top.videoNode.timedMetaDataSelectionKeys = ["*"]
' IMPORTANT: Failure to listen to the position and timedmetadata fields
' could result in ad impressions not being reported.
m.port = CreateObject("roMessagePort")
m.top.videoNode.observeField("position", m.port)
m.top.videoNode.observeField("timedMetaData", m.port)
m.top.videoNode.observeField("timedMetaData2", m.port)
m.top.videoNode.observeField("state", m.port)
while True
msg = wait(1000, m.port)
if m.top.videoNode = invalid
exit while
end if
m.streamManager.onMessage(msg)
currentTime = m.top.videoNode.position
if currentTime > 3 and not m.top.adPlaying
m.top.videoNode.enableTrickPlay = true
end if
end while
end sub
sub setupStreamManager()
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 sub
sub startCallback(ad as object)
print "Callback from SDK -- Start called - "
end sub
sub firstQuartileCallback(ad as object)
print "Callback from SDK -- First quartile called - "
end sub
sub midpointCallback(ad as object)
print "Callback from SDK -- Midpoint called - "
end sub
sub thirdQuartileCallback(ad as object)
print "Callback from SDK -- Third quartile called - "
end sub
sub completeCallback(ad as object)
print "Callback from SDK -- Complete called - "
end sub
sub errorCallback(error as object)
print "Callback from SDK -- Error called - " ; error
m.errorState = True
end sub