From 05071d371e26fe60a94dc8574d898a54aa199fc7 Mon Sep 17 00:00:00 2001 From: dannykng Date: Tue, 28 Mar 2017 18:32:51 -0700 Subject: [PATCH] initial commit --- README.md | 25 ++++- components/EntryScene.brs | 78 ++++++++++++++++ components/EntryScene.xml | 11 +++ components/Player.brs | 68 ++++++++++++++ components/Player.xml | 17 ++++ components/PlayerTask.brs | 134 +++++++++++++++++++++++++++ components/PlayerTask.xml | 12 +++ components/VideoContent.xml | 11 +++ images/MainMenu_Icon_Center_HD.png | Bin 0 -> 11384 bytes images/MainMenu_Icon_Center_SD43.png | Bin 0 -> 9454 bytes images/splash_hd.jpg | Bin 0 -> 33546 bytes images/splash_sd.jpg | Bin 0 -> 18990 bytes manifest | 11 +++ source/main.brs | 24 +++++ 14 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 components/EntryScene.brs create mode 100644 components/EntryScene.xml create mode 100644 components/Player.brs create mode 100644 components/Player.xml create mode 100644 components/PlayerTask.brs create mode 100644 components/PlayerTask.xml create mode 100644 components/VideoContent.xml create mode 100644 images/MainMenu_Icon_Center_HD.png create mode 100644 images/MainMenu_Icon_Center_SD43.png create mode 100644 images/splash_hd.jpg create mode 100644 images/splash_sd.jpg create mode 100644 manifest create mode 100644 source/main.brs diff --git a/README.md b/README.md index b52f7c7..bc5d97b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ -# RAF4RSG-sample -sample demonstrating the Roku Advertising Framework in SceneGraph +# RAF4RSG Sample + +This sample code demonstrates the use of Roku Advertising Framework in a Roku SceneGraph app. With v2 of RAF, the preferred way of use is by invoking the library from a Task node and passing a 3rd argument (`view`) to .ShowAds, which specifies where exactly in scene graph the ad content (video and interactive ad UI) should be attached. + +## How does it work + +main() creates an EntryScene (extends Scene), which only has a simplistic menu (LabelList). Selecting from there configures a VideoContent node with the video and ad information and starts playing it via a Player child node. + +Player (extends Group) in turn creates a Video node child, sets the main content in it and delegates running the video and ad play to a PlayerTask. + +PlayerTask (extends Task) in this sample is the only one "tasked" (pun intended) with playing the main content and the corresponding ads. It is also the only one that has to include the RAF library (`Library "Roku_Ads.brs"`). The runner function in PlayerTask - playContentWithAds() - makes the necessary library calls, the most notable being `.getAds()` to fetch ad pods and `.showAds()` to display them. Note that in RAF2 .showAds() has now a new, 3rd parameter - `view`, that is used to provide a renderable node in Roku Scene Graph, under which RAF should temporary attach its UI (incl. an ad video player node). + +PlayContentWithAds() has an event loop, in which it keeps track of `position` events during playback of the main content, so that it could interrupt and insert mid-roll ads as appropriate (determined by repeat calls to .getAds()) - and when that is done, it resumes the main video. The event loop also handles other player state changes besides "position", for example it latches onto the video "finished" event for a chance to display post-roll ads. + +## Pre-roll, mid-roll and post-roll ads + +The code can handle pre-roll, mid-roll and post-roll ads on the same video content - subject to the ad server returning the pods configured that way during the .getAds() call. + +Note that if invoked without a custom URL (as the sample does), only a single pre-roll ad will be shown, because that is the Roku ad server's default configuration. To change that behavior, .setAdUrl() pointing to customer's ad server has to be used (this falls under the "inventory split" model, see "Revenue Share" section in the documentation). + +## Caveats + +Notice this wrinkle - of PlayerTask having to manually set back the input focus after playing ads, since RAF UI has had the focus to be able to handle remote button presses. diff --git a/components/EntryScene.brs b/components/EntryScene.brs new file mode 100644 index 0000000..8e3cd58 --- /dev/null +++ b/components/EntryScene.brs @@ -0,0 +1,78 @@ +'********************************************************************* +'** (c) 2016-2017 Roku, Inc. All content herein is protected by U.S. +'** copyright and other applicable intellectual property laws and may +'** not be copied without the express permission of Roku, Inc., which +'** reserves all rights. Reuse of any of this content for any purpose +'** without the permission of Roku, Inc. is strictly prohibited. +'********************************************************************* + +sub init() + 'we use a simple LabelList for a menu + m.list = m.top.FindNode("list") + m.list.observeField("itemSelected", "onItemSelected") + m.list.SetFocus(true) + + 'descriptor for the menu items + itemList = [ + { + title: "Roku ad server (default server URL, single pre-roll)" + url: "" 'point to your own ad server if doing "inventory split" revenue share + } + ] + + ' compile into a ContentNode structure + listNode = CreateObject("roSGNode", "ContentNode") + for each item in itemList: + nod = CreateObject("roSGNode", "ContentNode") + nod.setFields(item) + listNode.appendChild(nod) + next + m.list.content = listNode + +end sub + +sub onItemSelected() + m.list.SetFocus(false) ' un-set focus to avoid creating multiple players on user tapping twice + menuItem = m.list.content.getChild(m.list.itemSelected) + + videoContent = { + + streamFormat: "hls", + titleSeason: "Art21 Season 3", + title: "Place", + url: "http://ga.video.cdn.pbs.org/videos/art-21/fe42d468-e90f-489b-ade3-a242f196dba2/60600/hd-mezzanine-16x9/ARTF101_iPad-1240k.m3u8", + + 'used for raf.setContentGenre(). For ads provided by the Roku ad service, see docs on 'Roku Genre Tags' + categories: ["Documentary"] + + 'Roku mandates that all channels enable Nielsen DAR + nielsen_app_id: "P2871BBFF-1A28-44AA-AF68-C7DE4B148C32" 'required, put "P2871BBFF-1A28-44AA-AF68-C7DE4B148C32", Roku's default appId if not having ID from Nielsen + nielsen_genre: "DO" 'required, put "GV" if dynamic genre or special circumstances (e.g. games) + nielsen_program_id: "Art21" 'movie title or series name + length: 3220 'in seconds; + + } + ' compile into a VideoContent node + content = CreateObject("roSGNode", "VideoContent") + content.setFields(videoContent) + content.ad_url = menuItem.url + + if m.Player = invalid: + m.Player = m.top.CreateChild("Player") + m.Player.observeField("state", "PlayerStateChanged") + end if + + 'start the player + m.Player.content = content + m.Player.visible = true + m.Player.control = "play" +end sub + +sub PlayerStateChanged() + print "EntryScene: PlayerStateChanged(), state = "; m.Player.state + if m.Player.state = "done" or m.Player.state = "stop" + m.Player.visible = false + m.list.setFocus(true) 'NB. the player took the focus away, so get it back + end if +end sub + diff --git a/components/EntryScene.xml b/components/EntryScene.xml new file mode 100644 index 0000000..b7c8a56 --- /dev/null +++ b/components/EntryScene.xml @@ -0,0 +1,11 @@ + + + + +