Come up with an algorithm for play/pauseing a asynchronous process

I’m trying (yet again) to build a video player/explorer in Julia. Everything is looking pretty good, but one hurdle is the play/pause functionality. I want to be able to control the play/pause with an observable (which is connected to a GUI button), but I run into issues with how to accomplish that. In the following setup I use a Channel and an async:

c = Channel{Bool}(false)
@async for _ in c
    println("showing a new frame!")
    sleep(1)
    put!(c, true)
end
play = Observable(false)
on(play) do p
    if p
        put!(c, true)
    else
        take!(c)
    end
end

Then, if I

play[] = true

it start working, but the pausing part is problematic. If I “press” pause

play[] = false

the whole thing sometimes stalls and becomes unresponsive.

Is there a known way to deal with this pattern? Or am I close…?

Thanks!

Here is a short attempt at something that might do what you want. You have a “frame producer” task that is pushing frames into a channel that is being consumed in another task that shows these frames. You have another channel for changing the state of the task showing the frames so that it will stop playing which can be called from “toplevel”.

using Observables

@enum MovieState begin
    PAUSED
    PLAYING
end

const FRAME_CHANNEL = Channel{Float64}() # could buffer frames?
const STATE_CHANNEL = Channel{MovieState}()

function movie_controller()
    state = PLAYING
    while true
        if isready(STATE_CHANNEL)
            state = take!(STATE_CHANNEL)
            while state == PAUSED # wait for someone to unpause us
                state = take!(STATE_CHANNEL)
            end
        end
        if state == PLAYING
            frame = take!(FRAME_CHANNEL)
            println("Showing frame: $frame")
        end
    end
end

function frame_producer()
    while true
        v = rand() # time to produce a frame
        sleep(v)
        push!(FRAME_CHANNEL, v)
    end
end

@async movie_controller()
@async frame_producer()

play = Observable(false)
on(play) do p
    push!(STATE_CHANNEL, p ? PLAYING : PAUSED)
end
2 Likes

Totally works. I’ll admit it’s a bit more involved than I had imagined, but awesome that this is now solved! Thanks a lot!!!

using Reactive
const playbotton = Signal(false)
const video = rand(100,)
const frame = Signal(0)

framefeeder = map(_ -> begin 
        if value(playbotton)
            push!(frame, value(frame) + 1)
        end 
    end , 
    fps(60))

fetch_frame(video, frame) = frame > length(video) ? video[end] : video[max(1, frame)]
display_frame!(video, frame) = println(fetch_frame(video, frame))
frameplayer = map(frame -> display_frame!(video, frame), frame)

play!(playbotton) = push!(playbotton, true)
pause!(playbotton) = push!(playbotton, false)

play!(playbotton)

Honestly, this is the first time I try this package.