Roku Advertising Framework (RAF) Integration
CastConductor integrates with the Roku Advertising Framework (RAF) to enable monetization through pre-roll, mid-roll, and overlay ads. This document explains the technical architecture and flow.
Overview
RAF is Roku's official advertising library. When ads are enabled in your CastConductor settings, the Roku app will:
- Request ads from the configured VAST endpoint (or Roku's default)
- Display video ads before your content starts (pre-roll)
- Optionally show ads during playback (mid-roll) or as overlays
Architecture
Separate Video Nodes
CastConductor uses separate video nodes for content and ads:
| Node | Purpose | Location |
|---|---|---|
m.videoPlayer | HLS video content / background video | MainScene.xml |
m.liveAudioPlayer | Audio streaming (MP3/AAC) | MainScene.xml |
m.adVideo | RAF video ads | AdManager.xml |
This separation ensures clean handoffs between content and ads.
How showAds() Works
RAF's showAds() function is blocking - when called, your BrightScript execution pauses completely until all ads finish or the user exits:
During ad playback: - RAF handles all rendering in the provided video node - RAF manages user input (back button, etc.) - RAF displays standard UI ("Ad 1 of 3", progress bar) - Your code is completely paused
Playback Flows
Audio-Only Mode
When your channel is playing audio (radio stations, podcasts):
┌────────────────────────────────────────────────────────┐
│ 1. Audio playing in m.liveAudioPlayer │
│ │
│ 2. Pre-roll triggers │
│ → Audio PAUSED (not muted) │
│ │
│ 3. showAds(pods, invalid, m.adVideo) called │
│ ┌────────────────────────────────────┐ │
│ │ RAF TAKES CONTROL │ │
│ │ • Video ad renders in m.adVideo │ │
│ │ • Ad audio plays through TV │ │
│ │ • Your audio is paused │ │
│ │ • User watches ad(s) │ │
│ │ • All ads complete │ │
│ └────────────────────────────────────┘ │
│ │
│ 4. showAds() returns true/false │
│ │
│ 5. Audio RESUMED │
│ → m.liveAudioPlayer.control = "play" │
└────────────────────────────────────────────────────────┘
HLS Video Mode
When your channel is playing HLS video streams:
┌────────────────────────────────────────────────────────┐
│ 1. HLS video playing in m.videoPlayer │
│ │
│ 2. Pre-roll triggers │
│ → Video PAUSED (stays visible but frozen) │
│ │
│ 3. showAds(pods, invalid, m.adVideo) called │
│ ┌────────────────────────────────────┐ │
│ │ RAF TAKES CONTROL │ │
│ │ • Ad video renders in m.adVideo │ │
│ │ • m.adVideo appears on top │ │
│ │ • m.videoPlayer still exists │ │
│ │ (paused, behind ad layer) │ │
│ │ • User watches ad(s) │ │
│ │ • All ads complete │ │
│ └────────────────────────────────────┘ │
│ │
│ 4. showAds() returns │
│ │
│ 5. Video RESUMED │
│ → m.videoPlayer.control = "play" │
│ (Live streams catch up to live edge) │
└────────────────────────────────────────────────────────┘
Key Implementation Details
Required Parameters
For SceneGraph applications, showAds() requires a Video node as the third parameter:
' WRONG - will crash on SceneGraph!
adResult = m.adFramework.showAds(adPods)
' CORRECT - pass video node for rendering
adResult = m.adFramework.showAds(adPods, invalid, m.adVideo)
Content Metadata
RAF requires content metadata for ad targeting and measurement compliance:
m.adFramework.setContentId(appInfo.GetTitle()) ' Channel name
m.adFramework.setContentLength(86400) ' 24 hours for live streams
m.adFramework.setContentGenre("music") ' Content category
m.adFramework.enableAdMeasurements(true) ' RAF 1.3 requirement
Pause/Resume Pattern
Content must be paused before ads and resumed after:
sub onAdPlayingStateChanged()
isAdPlaying = m.adManager.isAdPlaying
if isAdPlaying = true then
' PAUSE content during ad
if m.liveAudioPlayer <> invalid then
m.liveAudioPlayer.control = "pause"
end if
if m.videoPlayerMode = "livestream" then
m.videoPlayer.control = "pause"
end if
else
' RESUME content after ad
if m.liveAudioPlayer <> invalid then
m.liveAudioPlayer.control = "play"
end if
if m.videoPlayerMode = "livestream" then
m.videoPlayer.control = "play"
end if
end if
end sub
Anti-Patterns to Avoid
| ❌ Wrong | ✅ Correct |
|---|---|
| Muting audio while ad plays | Pause audio completely |
| Using same video node for ads and content | Separate video nodes |
| Playing ad audio over content audio | Pause content first |
| Hiding video visually | Just pause - RAF handles z-order |
| Forgetting showAds() view parameter | Always pass video node for RSG |
Ad Server Configuration
Default Roku Ads
When no custom VAST URL is configured, CastConductor uses Roku's default ad service. Note that default ads only serve to published channels - sideloaded/dev apps will receive "no fill".
Custom VAST
Publishers can configure custom VAST endpoints in the WordPress plugin settings to use their own ad networks (FreeWheel, Google Ad Manager, etc.).
Debugging
Enable RAF debug output to see ad requests and responses:
Debug output appears in telnet on port 8085:
[RAF] setDebugOutput(true)
[RAF] Roku_Ads Framework version 3.2021
📺📺📺 setContentId(CastConductor)
📺📺📺 setContentLength(86400)
📺📺📺 Calling getAds()...
📺📺📺 Got 1 ad pod(s)!