The goal of adding preroll video to a channel can be broken down into two tasks. First, we need to play two pieces of video content back to back as a single video experience. Second, we need to disable trick-play during the first of those two pieces of content so the user is not able to skip the preroll and jump directly to the main content. The main content should have a traditional trick-play experience consistent with most other channels.
First, let’s define the two pieces of content by building a content-meta-data structure for each of them. A content-meta-data is just a roAssociativeArray with some specific attributes used to represent a given piece of content. The only thing we need to know about the preroll video is the format it’s in and the URL to the asset, so its content-meta-data will be a lot more sparse than the main content’s. In most projects this would be done by making an API call to your cloud service and parsing the result, but for demonstration purposes, we’ll just hardcode it:
preroll = {
streamFormat: "mp4"
stream: {
url: "http://www.archive.org/download/kelloggs_variety_pak/kelloggs_variety_pak_512kb.mp4"
}
}
content ={
title: "TEDTalks : David Brooks: The social animal"
sdPosterURL: "http://images.ted.com/images/ted/78e8d94d1d2a81cd182e0626dc8e96a43c88d760_132x99.jpg"
hdPosterURL: "http://images.ted.com/images/ted/78e8d94d1d2a81cd182e0626dc8e96a43c88d760_132x99.jpg"
description: "Tapping into the findings of his latest book, NYTimes columnist David Brooks unpacks new insights into human nature from the cognitive sciences -- insights with massive implications for economics and politics as well as our own self-knowledge. In a talk full of humor, he shows how you can't hope to understand humans as separate individuals making choices based on their conscious awareness."
contentType: "episode"
streamFormat: "mp4"
stream: {
url: "http://video.ted.com/talks/podcast/DavidBrooks_2011.mp4"
}
}
Now we need to play these two pieces of content back to back. Here is a very simple subroutine that uses roVideoScreen to play a video. If you have been experimenting with the sample code in the Roku SDK, you have probably seen subroutines similar to this.
sub ShowVideoScreen(video)
port = CreateObject("roMessagePort")
screen = CreateObject("roVideoScreen")
screen.SetMessagePort(port)
screen.SetContent(video)
screen.Show()
while true
msg = wait(0, port)
if type(msg) = "roVideoScreenEvent"
if msg.isScreenClosed()
exit while
end if
end if
end while
screen.Close()
end sub
Given this subroutine, you might be tempted to do something like this:
ShowVideoScreen(preroll) ShowVideoScreen(content)
That will work. The preroll video will play and then the content video will play, but there are a few problems.
- Trick play will not be disabled during the preroll
- There may be a moment between the time when the preroll stops playing and when the content starts playing that the previous UI screen becomes visible for a fraction of a second
- If the user exits playback during the preroll, the main content will begin playing immediately. The desired behavior would be for the video experience to end and the previous UI screen to become visible
To prevent the underlying UI from flickering into view between videos, we can simply display a blank roImageCanvas before playing the videos. During that moment after the first video ends and before the second video starts, the previous screen will still become visible for a moment, but it will be a blank screen, which makes for a more seamless transition between videos and a more pleasant experience for your users.
The other two problems can be solved by using the roVideoPlayer component to play the preroll content. roVideoPlayer is a much more manual video playback mechanism than roVideoScreen. By default, it has no trick play modes and no buffer screen. These features must be added manually in BrightScript. The lack of trick play is exactly what we want, but we will have to draw our own buffer screen and handle any up or back remote key presses manually. We will do all this by building a new function for playing preroll content. This function should not only play a piece of content with trick play disabled, it should also return a value that indicates whether or not the user terminated playback by pressing up or back. We will use this return value to decide whether we should play the main content or return the user to the previous UI screen.
function ShowPreRoll(video)
' a true result indicates that playback finished without user intervention
' a false result indicates that the user pressed UP or BACK to terminate playback
result = true
canvas = CreateObject("roImageCanvas")
player = CreateObject("roVideoPlayer")
port = CreateObject("roMessagePort")
canvas.SetMessagePort(port)
' build a very simple buffer screen for our preroll video
canvas.SetLayer(0, { text: "Your program will begin after this message" })
canvas.Show()
' be sure to use the same message port for both the canvas and the player
' so we can receive events from both
player.SetMessagePort(port)
player.SetDestinationRect(canvas.GetCanvasRect())
player.AddContent(video)
player.Play()
' start our event loop
while true
' wait for an event
msg = wait(0, canvas.GetMessagePort())
if type(msg) = "roVideoPlayerEvent"
if msg.isFullResult()
' the video played to the end without user intervention
exit while
else if isRequestFailed()
' something went wrong with playback, but the user did not intervene
exit while
else if msg.isStatusMessage()
if msg.GetMessage() = "start of play"
' once the video starts, clear out the canvas so it doesn't cover the video
canvas.SetLayer(0, { color: "#00000000", CompositionMode: "Source" })
canvas.Show()
end if
end if
else if type(msg) = "roImageCanvasEvent"
if msg.isRemoteKeyPressed()
index = msg.GetIndex()
if index = 0 or index = 2
' the user pressed UP or BACK to terminate playback
result = false
exit while
end if
end if
end if
end while
player.Stop()
canvas.Close()
return result
end function
Now we have all the pieces necessary to build our video experience. Here’s how it all comes together:
' create and display a blank roImageCanvas to prevent the
' underlying UI from flickering between videos
canvas = CreateObject("roImageCanvas")
canvas.SetLayer(0, "#000000")
canvas.Show()
' play the preroll video with trick play disabled
if ShowPreroll(preroll)
' only play the main content if the preroll completed without user intervention
ShowVideoScreen(content)
end if
' close the blank canvas and return the user to the previous UI screen
canvas.Close()