roUniversalControlEvent - Pause/Play functions

Hi, I’m trying to implement the ability to Play and Pause my video when sent by ECP. I got the following code from customvideoplayer.

**How can I implement my video app to allow it to Play/Pause upon receiving a command through ECP (Posting /Keypress/Play) - When I try to it says code 13/code 113 in the debugger
I’ve code below that I’m trying to play with to get it to work but I’m not sure if I’m heading with the right approach.

Screenshot: Send video url and it plays fine
http://imgur.com/a/PcQ0v

Screenshot: Command I’m sending to try to Pause the video
http://imgur.com/a/DyPBB

Screenshot: What I’m getting back from debugger when I try to send Pause/Unpause(play) command
http://imgur.com/a/z00IC

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"
                m.paused = false
                progress% = msg.GetIndex() / 10
                if m.progress <> progress%
                    m.progress = progress%
                end if

            'Playback progress (in seconds):
            else if msg.isPlaybackPosition()
                m.position = msg.GetIndex()

            else if msg.isRemoteKeyPressed()
                index = msg.GetIndex()
                print "Remote button pressed: " + index.tostr()
             
                if index = 4 or index = 8  '<LEFT> or <REV>
                    m.position = m.position - 60
                    m.player.Seek(m.position * 1000)
                else if index = 5 or index = 9  '<RIGHT> or <FWD>
                    m.position = m.position + 60
                    m.player.Seek(m.position * 1000)
                else if index = 13  '<PAUSE/PLAY>
                    if m.player.paused m.player.Resume() else m.player.Pause()
                end if

            else if msg.isPaused()
                this.paused = true
                

            else if msg.isResumed()
                this.paused = false
                

            end if
            'Output events for debug
            print msg.GetType(); ","; msg.GetIndex(); ": "; msg.GetMessage()
            if msg.GetInfo() <> invalid print msg.GetInfo();
        end if
    end while

Or is this the right way to do it -

  'event = port.GetMessage()
       ' if (event <> invalid)
           ' if (type(event) = "roUniversalControlEvent")
              '  code = event.GetInt()
               ' print "code: " + stri(code)
               ' if code = 13
                    'if player.paused player.Resume() else player.Pause()
                       ' end if

                  ' else if msg.isPaused()
                     ' this.paused = true
                
                '  else if msg.isResumed()
                   ' this.paused = false
               
                   ' endif
               ' endif

“m” is the global associative array in Brightscript. If you wanted to use the same message port for your whole channel you would name it “m.port”, and then it will work in all of your functions. If you just name it “port” it will go out of scope when the function it’s assigned in closes.

You didn’t actually say what your problem is, if you have one. I would suggest reading this board on threads concerning port usage. A lot of it won’t make sense at first but there is literally no other way to get a handle on it short of having it explained in person or the answers you will get to specific questions here.

I’m trying to figure out how to implement pause/play when using external control protocol to send messages to control my video app, I thought atleast one of the above code should work and can’t figure out why it isn’t working

“Transparent” wrote:
I’m trying to figure out how to implement pause/play when using external control protocol to send messages to control my video app, I thought at least one of the above code should work and can’t figure out why it isn’t working

Probably because ECP and roUniversalControlEvent are different things.

heres the RTFM on ECP - https://sdkdocs.roku.com/display/sdkdoc/External+Control+Guide

Okay but if I try to play the video using ECP play command it says ‘code 13’ in the debugger, and code 13 is for Play/Pause in the roUniversalControlEvent

“Transparent” wrote:
Okay but if I try to play the video using ECP play command it says ‘code 13’ in the debugger, and code 13 is for Play/Pause in the roUniversalControlEvent

Is that a question? What exactly is the problem?

I haven’t the foggiest what does “customvideoplayer” use!
Is it roVideoPlayer? If so, use .pause()/.resume()

“EnTerr” wrote:
I haven’t the foggiest what does “customvideoplayer” use!
Is it roVideoPlayer? If so, use .pause()/.resume()

I am using .pause() and .resume(). s: if the event from using play or pause command code = 13, play or pause the video. shouldn’t this work? because Play and Pause are given defined as code 13? Seems like the right way to do it in my mind but I can’t seem to get it to work

None of what you’re saying makes any sense. Why not just post the code you’re using, and describe what happens when you say “I can’t seem to get it to work”, versus what you think should happen.

“belltown” wrote:
None of what you’re saying makes any sense. Why not just post the code you’re using, and describe what happens when you say “I can’t seem to get it to work”, versus what you think should happen.

I guess my question isn’t as my name…aye…

‘Why not just post the code you’re using’ - But I did post a segment of the code i’m trying to use, posting the whole lot of code I have would cause confusion as its a mess

‘describe what happens’ - it does not pause. It says code 13/code 113 in the debugger upon trying to play/pause as above

I’ve added pictures above to try and make it clearer what i’m doing if that helps

You appear to have multiple variables referring to whether or not the video is paused:

m.paused
m.player.paused
this.paused

You really need to clean this up. I doubt there’s any reason to have anything other than a variable just called paused.

-JT

“renojim” wrote:
You appear to have multiple variables referring to whether or not the video is paused:

m.paused
m.player.paused
this.paused

You really need to clean this up. I doubt there’s any reason to have anything other than a variable just called paused.

-JT

Hi Jim,
That code is actually from CustomVideoPlayer, supplied by Roku https://sourceforge.net/projects/rokusdkexamples/files/

“Transparent” wrote:

“renojim” wrote:
You appear to have multiple variables referring to whether or not the video is paused:

m.paused
m.player.paused
this.paused

You really need to clean this up. I doubt there’s any reason to have anything other than a variable just called paused.

-JT

Hi Jim,
That code is actually from CustomVideoPlayer, supplied by Roku https://sourceforge.net/projects/rokusdkexamples/files/
The Custom Video Player SDK example you linked to works just fine (if you use a working stream url). So if your code doesn’t handle play/pause, you must have changed something that introduced the error. I suggest you look at what you’ve changed, perhaps even go back to the original code, substitute your own Stream url, make sure the StreamFormat is correct, e.g. “ism”, and it should work.

I also noticed that the code you posted is not the same code as that in the Custom Video Player example. For instance, there’s no use of m.player.paused in the SDK example.

The Custom Video Player SDK example you linked to works just fine (if you use a working stream url). So if your code doesn’t handle play/pause, you must have changed something that introduced the error. I suggest you look at what you’ve changed, perhaps even go back to the original code, substitute your own Stream url, make sure the StreamFormat is correct, e.g. “ism”, and it should work.

I also noticed that the code you posted is not the same code as that in the Custom Video Player example. For instance, there’s no use of m.player.paused in the SDK example.

I can’t even run CustomVideoPlayer as an error pops up on the line ‘Sub Setup() As Object’, I prob have to add something, so I havn’t really seen it in action.

The pause works in that? Did you Pause it through the terminal like the command i’ve above, if not, can you please and tell me if it works? I don’t want to have the stream url used inside the program, I want to be able to send it to the channel remotely, I guess the issue I have it trying to integrate the Pausing/Playing code in CVP into my own app

Sorry Yeah you’re right (sorry Jim), m.player.paused is not in the custom video player but m.paused is, I guess I was just playing around with it trying to get it to work.

Here’s what I did:

  1. Download customvideoplayer.zip from: https://sourceforge.net/projects/rokusdkexamples/files/customvideoplayer.zip/download
  2. Un-zip customvideoplayer.zip into a new folder, customvideoplayer
  3. Remove the read-only attribute recursively from the new folder, its subfolders, and files (at least on Windows, not sure about other systems, the SDK examples are unzipped by default into a read-only folder, which must be changed in order to edit the contents)
  4. Edit customvideoplayer/source/main.brs
  5. Change the SetContentList statement in Sub Setup() to:
    this.player.SetContentList([{
        REM Stream: { url: "http://ec2-184-72-239-149.compute-1.amazonaws.com:1935/demos/smil:bigbuckbunnyiphone.smil/playlist.m3u8" }
        Stream: { url: "http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/Manifest" }
        REM StreamFormat: "hls"
        StreamFormat: "ism"
        REM SwitchingStrategy: "full-adaptation"
    }])

  1. Zip and deploy the channel to my Roku
  2. Play/pause successfully using both the Roku remote and the following curl command:
curl -d '' http://192.168.0.6:8060/keypress/Play
  1. As an added bonus, modified the code to specify the url using ECP
  2. Launched and played the video successfully using the following curl command:
curl -d '' http://192.168.0.6:8060/launch/dev?url=http%3A%2F%2Fplayready.directtaps.net%2Fsmoothstreaming%2FSSWSS720H264%2FSuperSpeedway_720.ism%2FManifest

Here’s the final version of the code.

' ********************************************************************
' ********************************************************************
' **  Roku Custom Video Player Channel (BrightScript)
' **
' **  May 2010
' **  Copyright (c) 2010 Roku Inc. All Rights Reserved.
' ********************************************************************
' ********************************************************************

Sub RunUserInterface(params As Object)
    Print "Launch url: "; params.url
    o = Setup(params.url)
    o.setup()
    o.paint()
    o.eventloop()
End Sub

Sub Setup(launchUrl As String) As Object
    this = {
        port:      CreateObject("roMessagePort")
        progress:  0 'buffering progress
        position:  0 'playback position (in seconds)
        paused:    false 'is the video currently paused?
        fonts:     CreateObject("roFontRegistry") 'global font registry
        canvas:    CreateObject("roImageCanvas") 'user interface
        player:    CreateObject("roVideoPlayer")
        setup:     SetupFramedCanvas
        paint:     PaintFramedCanvas
        eventloop: EventLoop
    }

    'Static help text:
    this.help = "Press the right or left arrow buttons on the remote control "
    this.help = this.help + "to seek forward or back through the video at "
    this.help = this.help + "approximately one minute intervals.  Press down "
    this.help = this.help + "to toggle fullscreen.  To exit the sample, press "
    this.help = this.help + "the remote control Home key."

    'Register available fonts:
    this.fonts.Register("pkg:/fonts/caps.otf")
    this.textcolor = "#406040"

    'Setup image canvas:
    this.canvas.SetMessagePort(this.port)
    this.canvas.SetLayer(0, { Color: "#000000" })
    this.canvas.Show()

    'Resolution-specific settings:
    mode = CreateObject("roDeviceInfo").GetDisplayMode()
    if mode = "720p"
        this.layout = {
            full:   this.canvas.GetCanvasRect()
            top:    { x:   0, y:   0, w:1280, h: 130 }
            left:   { x: 249, y: 177, w: 391, h: 291 }
            right:  { x: 700, y: 177, w: 350, h: 291 }
            bottom: { x: 249, y: 500, w: 780, h: 300 }
        }
        this.background = "pkg:/images/back-hd.jpg"
        this.headerfont = this.fonts.get("lmroman10 caps", 50, 50, false)
    else
        this.layout = {
            full:   this.canvas.GetCanvasRect()
            top:    { x:   0, y:   0, w: 720, h:  80 }
            left:   { x: 100, y: 100, w: 280, h: 210 }
            right:  { x: 400, y: 100, w: 220, h: 210 }
            bottom: { x: 100, y: 340, w: 520, h: 140 }
        }
        this.background = "pkg:/images/back-sd.jpg"
        this.headerfont = this.fonts.get("lmroman10 caps", 30, 50, false)
    end if

    this.player.SetMessagePort(this.port)
    this.player.SetLoop(true)
    this.player.SetPositionNotificationPeriod(1)
    this.player.SetDestinationRect(this.layout.left)
    this.player.SetContentList([{
        REM Stream: { url: "http://ec2-184-72-239-149.compute-1.amazonaws.com:1935/demos/smil:bigbuckbunnyiphone.smil/playlist.m3u8" }
        REM Stream: { url: "http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/Manifest" }
        Stream: { url: launchUrl }
        REM StreamFormat: "hls"
        StreamFormat: "ism"
        REM SwitchingStrategy: "full-adaptation"
    }])
    this.player.Play()

    return this
End Sub

Sub EventLoop()
    while true
        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"
                m.paused = false
                progress% = msg.GetIndex() / 10
                if m.progress <> progress%
                    m.progress = progress%
                    m.paint()
                end if

            'Playback progress (in seconds):
            else if msg.isPlaybackPosition()
                m.position = msg.GetIndex()
                m.paint()

            else if msg.isRemoteKeyPressed()
                index = msg.GetIndex()
                print "Remote button pressed: " + index.tostr()
                if index = 3 '<DOWN> (toggle fullscreen)
                    if m.paint = PaintFullscreenCanvas
                        m.setup = SetupFramedCanvas
                        m.paint = PaintFramedCanvas
                        rect = m.layout.left
                    else
                        m.setup = SetupFullscreenCanvas
                        m.paint = PaintFullscreenCanvas
                        rect = { x:0, y:0, w:0, h:0 } 'fullscreen
                        m.player.SetDestinationRect(0, 0, 0, 0) 'fullscreen
                    end if
                    m.setup()
                    m.player.SetDestinationRect(rect)
                else if index = 4 or index = 8  '<LEFT> or <REV>
                    m.position = m.position - 60
                    m.player.Seek(m.position * 1000)
                else if index = 5 or index = 9  '<RIGHT> or <FWD>
                    m.position = m.position + 60
                    m.player.Seek(m.position * 1000)
                else if index = 13  '<PAUSE/PLAY>
                    if m.paused m.player.Resume() else m.player.Pause()
                end if

            else if msg.isPaused()
                m.paused = true
                m.paint()

            else if msg.isResumed()
                m.paused = false
                m.paint()

            end if
            'Output events for debug
            print msg.GetType(); ","; msg.GetIndex(); ": "; msg.GetMessage()
            if msg.GetInfo() <> invalid print msg.GetInfo();
        end if
    end while
End Sub

Sub SetupFullscreenCanvas()
    m.canvas.AllowUpdates(false)
    m.paint()
    m.canvas.AllowUpdates(true)
End Sub

Sub PaintFullscreenCanvas()
    list = []

    if m.progress < 100
        color = "#000000" 'opaque black
        list.Push({
            Text: "Loading..." + m.progress.tostr() + "%"
            TextAttrs: { font: "huge" }
            TargetRect: m.layout.full
        })
    else if m.paused
        color = "#80000000" 'semi-transparent black
        list.Push({
            Text: "Paused"
            TextAttrs: { font: "huge" }
            TargetRect: m.layout.full
        })
    else
        color = "#00000000" 'fully transparent
    end if

    m.canvas.SetLayer(0, { Color: color, CompositionMode: "Source" })
    m.canvas.SetLayer(1, list)
End Sub

Sub SetupFramedCanvas()
    m.canvas.AllowUpdates(false)
    m.canvas.Clear()
    m.canvas.SetLayer(0, [
        { 'Background:
            Url: m.background
            CompositionMode: "Source"
        },
        { 'The title:
            Text: "Custom Video Player"
            TargetRect: m.layout.top
            TextAttrs: { valign: "bottom", font: m.headerfont, color: m.textcolor }
        },
        { 'Help text:
            Text: m.help
            TargetRect: m.layout.right
            TextAttrs: { halign: "left", valign: "top", color: m.textcolor }
        }
    ])
    m.paint()
    m.canvas.AllowUpdates(true)
End Sub

Sub PaintFramedCanvas()
    list = []
    if m.progress < 100  'Video is currently buffering
        list.Push({
            Color: "#80000000"
            TargetRect: m.layout.left
        })
        list.Push({
            Text: "Loading..." + m.progress.tostr() + "%"
            TargetRect: m.layout.left
        })
    else  'Video is currently playing
        if m.paused
            list.Push({
                Color: "#80000000"
                TargetRect: m.layout.left
                CompositionMode: "Source"
            })
            list.Push({
                Text: "Paused"
                TargetRect: m.layout.left
            })
        else  'not paused
            list.Push({
                Color: "#00000000"
                TargetRect: m.layout.left
                CompositionMode: "Source"
            })
        end if
        list.Push({
            Text: "Current position: " + m.position.tostr() + " seconds"
            TargetRect: m.layout.bottom
            TextAttrs: { halign: "left", valign: "top", color: m.textcolor }
        })
    end if
    m.canvas.SetLayer(1, list)
End Sub

That should get you started.

Wow belltown, thanks a lot! That helps so much! Really do appreciate the time you put into modifying that
I’ve one question though, that error I had earlier at

Sub Setup(launchUrl As String) As Object

still pops up, is that an issue? As I can still upload the channel and it runs fine, seems odd - should it be ignored?

Syntax error: unexpected token "As", expected one of <INTID>, <FLOATID>, <DOUBLEID>, <STRINGID>, <INTLITERAL>, <HEXLITERAL>, <FLOATLITERAL>, <DOUBLELITERAL>, <STRINGLITERAL>, "True", "False", "Invalid", "(", "Type", "Rnd", "GetGlobalAA", "Box", "Run", "Eval", "GetLastCompileError", "GetLastRuntimeError", "Sleep", "Wait", "CreateObject", "GetInterface", "UpTime", "RebootSystem", "ListDir", "ReadAsciiFile", "WriteAsciiFile", "CopyFile", "MatchFiles", "DeleteFile", "DeleteDirectory", "CreateDirectory", "FormatDrive", "strtoi", "Ucase", "LCase", "Asc", "Chr", "Instr", "Left", "Len", "Mid", "Right", "Str", "Stri", "String", "Stringi", "Val", "Tr", "Abs", "Atn", "Cos", "Csng", "Cdbl", "Exp", "Fix", "Int", "Log", "Sgn", "Sin", "Sqr", "Tan", <ID>, "Component", "Interface", "Event", "Exit", "Print", "?", "Goto", "Return", "Stop", <NEWLINE>, or ":"

Interesting. I don’t get an error on my Roku for that code, nor for the Roku SDK customvideoplayer example code, and I’ve never seen that particular error message before. I’m surprised the code still runs with a syntax error like that. Maybe it’s something new. Just curious what Roku model number and OS version are you running?

What I think it’s saying though is that a “Sub” cannot have a non-void return type, so change:

Sub Setup(launchUrl As String) As Object

to:

Function Setup(launchUrl As String) As Object

and change the corresponding:

End Sub

statement to:

End Function

Model: 3500X - Roku Stick
Version: 7.2.0 Build 4100 - 09

Yeap, no more errors exist now and the channel runs fine, thanks!

Edit: Was only trying to do Pause/Play but you also gave me ffw and rwd…loving it

“Transparent” wrote:
Model: 3500X - Roku Stick
Version: 7.2.0 Build 4100 - 09

Yeap, no more errors exist now and the channel runs fine, thanks!
Curious. I have a Model 3100X with version 7.2.0 • build 4100-02, and I didn’t get the error message when specifying a non-void return value on a Sub declaration. I wonder if Roku changed something in the -03 .. -09 builds.

Unless.. That was an error you saw in the Roku Debugger on port 8085 right, rather than somewhere else (like Eclipse?)

Would be nice if sub can be used in lieu of function because of less typing!

Now maybe it’s important for function to nag if there is no return statement (i am ambivalent if it should or not). But there is no good design justification for sub complaining if there is non-void return (besides nit-picking :slightly_smiling_face: - i said “good” reason!). Quick check… no, function does non ensure there is return (probably futile to try that compile-time), so sub may as well be turned to synonym of function.