Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GSoC'21] Livestreaming v2 #387

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Next Next commit
Faster livestreaming; stream desired frames
  • Loading branch information
Sov-trotter committed Aug 7, 2021
commit 9e5ab051f5fd826309ee3f29bd45d8c2cce56765
7 changes: 5 additions & 2 deletions src/Javis.jl
Original file line number Diff line number Diff line change
@@ -273,6 +273,11 @@ function render(
tempdirectory = mktempdir()
end

if streamconfig != nothing
_livestream(streamconfig, framerate, video, objects, layers, frames, tempdirectory)
return
end

filecounter = 1
@showprogress 1 "Rendering frames..." for frame in frames
frame_image = convert.(RGB, get_javis_frame(video, objects, frame; layers = layers))
@@ -310,8 +315,6 @@ function render(
@error "Currently, only gif and mp4 creation is supported. Not a $ext."
end

_livestream(streamconfig, framerate, video.width, video.height, pathname)

# even if liveview = false, show the rendered gif in the cell output
if isdefined(Main, :IJulia) && Main.IJulia.inited
display(MIME("text/html"), """<img src="$(pathname)">""")
47 changes: 33 additions & 14 deletions src/javis_viewer.jl
Original file line number Diff line number Diff line change
@@ -291,13 +291,14 @@ Sets up the livestream configuration.
**NOTE:** Twitch not fully implemented, do not use.
"""
function setup_stream(
livestreamto::Symbol = :local;
livestreamto::Symbol = :local,
frames::Union{UnitRange{Int},Symbol} = :all;
protocol::String = "udp",
address::String = "0.0.0.0",
port::Int = 14015,
twitch_key::String = "",
)
StreamConfig(livestreamto, protocol, address, port, twitch_key)
StreamConfig(livestreamto, protocol, address, port, twitch_key, frames)
end

"""
@@ -332,7 +333,7 @@ function cancel_stream()
),
),
)
return "Livestream Cancelled!"
return @info "Livestream Cancelled!"
end

"""
@@ -342,20 +343,37 @@ Internal method for livestreaming
"""
function _livestream(
streamconfig::StreamConfig,
framerate::Int,
width::Int,
height::Int,
pathname::String,
framerate,
video,
objects,
layers,
frames,
tempdirectory,
)
# kill any existing stream
cancel_stream()

livestreamto = streamconfig.livestreamto
config_frames = streamconfig.frames
twitch_key = streamconfig.twitch_key

if livestreamto == :twitch && isempty(twitch_key)
return error("Please enter your twitch stream key")
end

stream_frames = config_frames == :all ? frames : config_frames
stream_filecounter = 1
for frame in stream_frames
frame_image = convert.(RGB, get_javis_frame(video, objects, frame; layers = layers))
if !isempty(tempdirectory)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if tempdirectory is empty? It just generates the frames but can't use it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah what I mean is: it should never be empty so we don't need the if. The problem is if we have an mp4 as the pathname without a temp directory but want to stream it will fail, right?

Images.save(
"$(tempdirectory)/$(lpad(stream_filecounter, 10, "0")).png",
frame_image,
)
end
stream_filecounter += 1
end

command = [
"-stream_loop", # loop the stream -1 times i.e. indefinitely
"-1",
@@ -366,15 +384,14 @@ function _livestream(
"error",
"-re", # read input at native frame rate
"-i", # input file
"$pathname",
"$(tempdirectory)/%10d.png",
]

if livestreamto == :twitch
if isempty(twitch_key)
error("Please enter your twitch api key")
error("Please enter your twitch stream key")
end

#
twitch_cmd = [
"-f",
"flv", # force the file to flv format
@@ -399,8 +416,10 @@ end

_livestream(
streamconfig::Nothing,
framerate::Int,
width::Int,
height::Int,
pathname::String,
framerate,
video,
objects,
layers,
frames,
tempdirectory,
) = return
2 changes: 2 additions & 0 deletions src/structs/Livestream.jl
Original file line number Diff line number Diff line change
@@ -9,11 +9,13 @@ Holds the conguration for livestream, defaults to `nothing`
- `address::String` The IP address for the `:local` stream(ignored in case of `:twitch`)
- `port::Int` The port for the `:local` stream(ignored in case of `:twitch`)
- `twitch_key::String` Twitch stream key for your account
- `frames::Union{UnitRange{Int}, Symbol}` The specific frames to be livestreamed. Defaults to `:all`
"""
struct StreamConfig
livestreamto::Symbol
protocol::String
address::String
port::Int
twitch_key::String
frames::Union{UnitRange{Int},Symbol}
end
Binary file added test/refs/livestream_frames56.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/refs/livestream_frames57.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/refs/livestream_frames58.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/refs/livestream_frames59.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/refs/livestream_frames60.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion test/viewer.jl
Original file line number Diff line number Diff line change
@@ -133,6 +133,7 @@ end
@test conf_local.protocol == "udp"
@test conf_local.address == "0.0.0.0"
@test conf_local.port == 8081
@test conf_local.frames == :all

conf_twitch_err = setup_stream(:twitch)
conf_twitch = setup_stream(:twitch, twitch_key = "foo")
@@ -141,7 +142,19 @@ end
@test isempty(conf_twitch_err.twitch_key)
@test conf_twitch.twitch_key == "foo"

render(vid, streamconfig = conf_local)
render(vid, tempdirectory = "images", streamconfig = conf_local)

# can't actually test the streamed frames but let's check if they're atleast created
conf_local = setup_stream(:local, 56:60)
for i in 56:60
@test_reference "refs/livestream_frames$i.png" load(
"images/$(lpad((i-55), 10, "0")).png",
)
end

for i in 56:60
rm("images/$(lpad(i, 10, "0")).png")
end

# errors with macos; a good test to have
# test_local = run(pipeline(`lsof -i -P -n`, `grep ffmpeg`))