Building a Custom Video Player

Have you ever wanted to create your own video player for your Roku channels?  Most channels use the roVideoScreen template provided with the SDK.  But you can create your own video player using the roVideoPlayer component.  In addition, you can use this component along with the image canvas or 2D API discussed in previous articles to implement custom video players with interesting UI overlays. Complete documentation for the roVideoPlayer component can be found here.

This article will discuss how to build the sample player in the channel downloadable from this link.  Features of the video player include a custom loading screen, as well as a nice playlist overlay feature.  Both of these features are shown in the screen shots below.  When a video is playing, press the remote’s OK button to see the five videos in the playlist with the currently playing video highlighted.  You can up and down arrow in the playlist, highlighting other videos, and click OK to skip to that video.  During video playback, you can also press the right and left buttons skip to the next and previous videos in the playlist.

Figure 1 – Custom Loading Screen

Figure 2 – Playlist Overlay Text

The custom UI in the sample is all implemented using roImageCanvas. You can also use roScreen and the 2D API with the roVideoPlayer. However, it is important to note that video playback in this case needs to be limited to 720p due to memory constraints.

The roVideoPlayer Component

Most BrightScript channels use the roVideoScreen component to implement video playback.  This pre-built video component is recommended for most Roku channels.  The more you can use the Roku SDK templates for your development, the less work you need to do in terms of UI implementation and error handling. There are times, however, when you may want a greater degree of control over the user interface available for video playback.  For these purposes, the BrightScript SDK includes the roVideoPlayer component.

roVideoPlayer implements the ifVideoPlayer interface.  This interface contains all of the functions that you can call on a video player.  roVideoPlayer allows you to specify the dimensions and location of the rectangle on screen where video plays.  roVideoScreen, on the other hand, only allows for full screen video.

Here is a typical example of how to create a video player.  In this case it is created full screen, using the dimensions of an image canvas that is also created with the channel:

    canvas = CreateObject("roImageCanvas")
    player = CreateObject(“roVideoPlayer”)
    port = CreateObject(“roMessagePort”)
    targetRect = canvas.GetCanvasRect()
    canvas.SetMessagePort(port)
    this.canvas.Show()
    player.SetMessagePort(port)
    player.SetDestinationRect(targetRect)

If you instead wanted a video region that is smaller than full screen and located somewhere other than the upper left corner, you could simply change the rectangle passed to SetDestinationRect().

roVideoPlayer supports all of the same video formats as roVideoScreen. Content metadata values used to program roVideoScreen are the same as well, and are used in the same way.

One important difference between roVideoScreen and roVideoPlayer is that roVideoPlayer has a Play() function for starting video playback, unlike roVideoScreen. Since an roVideoScreen is a complete screen with full video UI, you just call Show() to display the screen, and whatever content has been set on the screen will start to play. roVideoPlayer is much more like a component that provides video, rather than a screen. As it might be embedded in other screens, it needs a function other than Show() to start playback. That’s what Play() does:

    Boolean Play(Void)

ifVideoPlayer also includes functions to stop, pause and resume video playback:

    Boolean Stop(Void)
    Boolean Pause(Void)
    Boolean Resume(Void)

There are lots of cool things that you can do with the roVideoPlayer component when it comes to customization. As with most things in life, this kind of power and flexibility comes at a price. The roVideoPlayer is the lowest level video component available in BrightScript. It basically just does video playback for you. Other UI features that you get for free when using the roVideoScreen must be implemented from scratch in your channel. This includes seeking forward and reverse, any kind of on screen playback controls or playback progress indicators. Also, there are no trick play effects with the roVideoPlayer like there are with the roVideoScreen. Let’s take a look at how to overcome some of these limitations.

roVideoPlayer Events

The roVideoPlayer receives a variety of events indicating status and state of video playback. These events are sent to the channel as roVideoPlayerEvent instances. The complete list of these events can be found in the SDK documentation at this location. Your channels can handle these events to make updates to your custom video player UI.

For example, to implement the custom loading screen in the sample channel, the BrightScript code needs to respond to the roVideoPlayerEvent isStatusMessage. This event provides information about the video download progress, which can be used to update the loading screen UI:

    list = []        
    progress_bar = {TargetRect: {x: 350, y: 500, w: 598, h: 37},
         url: "pkg:/images/progress_bar.png"}
    msg = wait(0, m.port)
    if msg <> invalid
        'If this is a startup progress status message, record progress
        'and update the UI accordingly:
        if msg.isStatusMessage() and msg.GetMessage() = "startup progress"
            progress = msg.GetIndex() / 10
            if m.progress >= 0 AND m.progress = 20 AND m.progress < 40
        progress_bar.url = "pkg:/images/progress_bar_2.png"
    …
    endif
    list.Push(progress_bar)
    m.canvas.SetLayer(0, { Color: color, CompositionMode: "Source" })
    m.canvas.SetLayer(1, list)

This code snippet demonstrates the basic technique for tracking video download progress. When the event loop receives a startup progress event, it reads the download progress value from the event’s GetIndex() value and then loads and renders a progress bar image accordingly. A channel could also render text instead of images to indicate progress. The choice is up to you and to the capabilities of the roImageCanvas component that does the drawing. isStatusMessage() can indicate other statuses as well, such as “start of play” (indicating that video playback has started), “end of stream” and “end of playlist”.

Another event commonly used when implementing custom video players is the isPlaybackPosition event. This event is sent to roVideoPlayer to indicate the current position, in seconds, of video playback. This event can be used to trigger the update of playback progress controls.

    if msg.isPlaybackPosition()
        position = msg.GetIndex()
    endif

You can specify how frequently this event is sent by calling the SetPositionNotificationPeriod() function:

    Void SetPositionNotificationPeriod(Integer period)

The period argument specifies the frequency, in seconds, of the notification. Please note as well that the position playback event will not be sent to the event loop if the notification period value is not set.

Error Handling

Another category of work that you will need to do from scratch when using roVideoPlayer is video playback error handling. When using the SDK roVideoScreen template, all of this is implemented for you. A typical example of the kind of error handling that you need to implement with roVideoPlayer is underrun detection. The roVideoPlayerEvent isStreamStarted event sends information about the stream that is started. The Info associative array sent with this message contains the following information:

    Info = {
        url: (Url of stream)
        StreamBitrate: (Bitrate of stream in kbps)
        MeasuredBitrate: (Actual bitrate of video transfer in kbps)
        IsUnderrun: (true if stream started as the result of an underrun)
    }

With this information, conditions such as underruns, playback quality issues related to network bandwidth and the like can be detected.

Building Playlists

Another unique feature of the roVideoPlayer is the ability to create playlist experiences. ifVideoPlayer includes a function called SetContentList():

    Void SetContentList(Array contentList)

This function takes an array of video stream specifications. When you call Play() the video player will start playing the first video in the array. After each video completes, it starts the next video in the list automatically. Once the last video has completed, the player will loop back to the first video if so specified by this function:

    Void SetLoop(Boolean loop)

You can also set the next video to play by calling SetNext():

    Void SetNext(Integer item)

The item argument specifies the 0-based index of the video in the content list to play next.

As an example, here is how you might load a playlist from a JSON file and then populate the video player content list.

  player = CreateObject(“roVideoPlayer”)    
  jsonAsString = ReadAsciiFile("pkg:/json/sample.json")
  feedData = ParseJSON(jsonAsString)
  contentList = []
  for each video in feedData.Videos
      contentList.Push({
        Stream: { url: video.url }
        StreamFormat: "mp4"
      })
  end for    
  player.SetContentList(contentList)
  player.Play()

Grab the sample channel from this link and experiment around, and you’ll have your own custom video players running in no time.

About Robert Burdick

Roku Developer Support Manager
This entry was posted in Uncategorized. Bookmark the permalink.
  • http://www.facebook.com/lee.wright.75286100 Lee Wright

    very nice artwork. How each picture represents another concept is simply brilliant.

  • http://blog.atomicreach.com/ Atomic Reach

    Hi Robert,

    I’m going to keep this short and sweet. I think it’s really great what you are doing with your programming blog, and you’ve got great insights in your field. As an expert in topics such as software dev and resources for the API community, I’m are reaching out to you to participate in an API community we’re building.

    If you decide to join, we will publish the title of your posts and the first few sentences of each post on an API website. If readers want to read the full story, they’ll be pushed to your blog. The benefits are straightforward: increased exposure and more traffic to your site.

    If you’re interested or have any questions, please send me an email with “API” in the subject line and I’ll send you a link to activate your account (or an answer questions).

    I look forward to you joining our community!

    Kindest regards,
    Tina Jin

  • http://www.facebook.com/cy.mu.1 Cy Mu

    if i build a layer above the video play screen according

    roVideoPlayer Events, the layer is a list. Could I use the remote control to focus one item? Need I encode to define the remote control action(like up or down)?

  • jason

    hello i have issue with json playlist not scrolling can any one help

  • RIDGELINETV

    This is a great example. Is it possible to parse a playlist from an external JSON or XML file? This would be most helpful in creating playlists which could be easily updated.

  • brian b.

    Im just curious I really like the functionality of roVideoPlayer. Is it possible to launch roVideoPlayer/roImageCanvas from an roGridScreen or roListScreen?