]> git.sesse.net Git - ultimatescore/blobdiff - nageru/ultimate.lua
Use the new disconnect function for releasing stale streams.
[ultimatescore] / nageru / ultimate.lua
index bafd8189b96818f9e33c29e0fd4919bf5928f9fa..13b03708ad38e86100bdcb460f6f35df68e3ee72 100644 (file)
@@ -11,7 +11,8 @@ local neutral_colors = {
        {0.5, 0.5, 0.5},  -- Input 1.
        {0.5, 0.5, 0.5},  -- Input 2.
        {0.5, 0.5, 0.5},  -- Input 3.
-       {0.5, 0.5, 0.5}   -- Input 4.
+       {0.5, 0.5, 0.5},  -- Input 4.
+       {0.5, 0.5, 0.5}   -- Input 5.
 }
 
 local overlay_transition_start = -2.0
@@ -22,7 +23,7 @@ local overlay_enabled = false
 
 local live_signal_num = 0
 local preview_signal_num = 1
-local NUM_CAMERAS = 5  -- Remember to update neutral_colors, too.
+local NUM_CAMERAS = 6  -- Remember to update neutral_colors, too.
 
 -- Valid values for live_signal_num and preview_signal_num.
 local INPUT0_SIGNAL_NUM = 0
@@ -30,12 +31,14 @@ local INPUT1_SIGNAL_NUM = 1
 local INPUT2_SIGNAL_NUM = 2
 local INPUT3_SIGNAL_NUM = 3
 local INPUT4_SIGNAL_NUM = 4
+local INPUT5_SIGNAL_NUM = 5
 local SBS_SIGNAL_NUM = NUM_CAMERAS
 local STATIC_SIGNAL_NUM = NUM_CAMERAS + 1
+local VIDEO_SIGNAL_NUM = NUM_CAMERAS + 2
 
 -- Preview-only signal showing the current signal with the overlay.
 -- Not valid for live_signal_num!
-local OVERLAY_SIGNAL_NUM = NUM_CAMERAS + 2
+local OVERLAY_SIGNAL_NUM = NUM_CAMERAS + 3
 
 -- Valid values for transition_type. (Cuts are done directly, so they need no entry.)
 local NO_TRANSITION = 0
@@ -51,11 +54,19 @@ local cef_path = Nageru.THEME_PATH:match("(.*)/nageru/")
 local cef_input = HTMLInput.new("file://" .. cef_path .. "/score.html")
 cef_input:execute_javascript_async("play()")
 
+local bg_video = VideoInput.new(cef_path .. "/flow-720.mp4", Nageru.VIDEO_FORMAT_YCBCR)
+-- local iptv_video = VideoInput.new("http://10.34.129.69:9060/1/v.flv", Nageru.VIDEO_FORMAT_YCBCR)
+local iptv_video = VideoInput.new("http://home.samfundet.no/~sesse/videostuff", Nageru.VIDEO_FORMAT_YCBCR)
+
 function reload_cef()
        cef_input:reload()
        cef_input:execute_javascript_async("play()")
 end
 
+function disconnect_iptv()
+       iptv_video:disconnect()
+end
+
 -- Utility function to help creating many similar chains that can differ
 -- in a free set of chosen parameters.
 function make_cartesian_product(parms, callback)
@@ -94,14 +105,17 @@ function possibly_make_overlay(has_overlay, chain, base)
        end
 end
 
-function make_fade_input(chain, signal, live, deint, scale)
+function make_fade_input(chain, signal, live, deint, scale, video)
        local input, wb_effect, resample_effect, last
-       if live then
+       if video then
+               input = chain:add_video_input(iptv_video, false)
+               last = input
+       elseif live then
                input = chain:add_live_input(false, deint)
                input:connect_signal(signal)
                last = input
        else
-               input = chain:add_effect(ImageInput.new("tfk_pause.png"))
+               input = chain:add_effect(ImageInput.new(cef_path .. "/nageru/dsn-bg.png"))
                last = input
        end
 
@@ -129,11 +143,11 @@ end
 -- A chain to fade between two inputs, of which either can be a picture
 -- or a live input. In practice only used live, but we still support the
 -- hq parameter.
-function make_fade_chain(input0_live, input0_deint, input0_scale, input1_live, input1_deint, input1_scale, has_overlay, hq)
+function make_fade_chain(input0_live, input0_deint, input0_scale, input0_video, input1_live, input1_deint, input1_scale, input1_video, has_overlay, hq)
        local chain = EffectChain.new(16, 9)
 
-       local input0 = make_fade_input(chain, INPUT0_SIGNAL_NUM, input0_live, input0_deint, input0_scale)
-       local input1 = make_fade_input(chain, INPUT1_SIGNAL_NUM, input1_live, input1_deint, input1_scale)
+       local input0 = make_fade_input(chain, INPUT0_SIGNAL_NUM, input0_live, input0_deint, input0_scale, input0_video)
+       local input1 = make_fade_input(chain, INPUT1_SIGNAL_NUM, input1_live, input1_deint, input1_scale, input1_video)
 
        -- If fading between two live inputs, the overlay is put on top.
        -- If fading between the static picture and a live input,
@@ -169,9 +183,9 @@ end
 
 -- Chains to fade between two inputs, in various configurations.
 local fade_chains = make_cartesian_product({
-       {"static", "live", "livedeint"},  -- input0_type
+       {"video", "static", "live", "livedeint"},  -- input0_type
        {true, false},                    -- input0_scale
-       {"static", "live", "livedeint"},  -- input1_type
+       {"video", "static", "live", "livedeint"},  -- input1_type
        {true, false},                    -- input1_scale
        {true, false},                    -- has_overlay
        {true}                            -- hq
@@ -180,7 +194,9 @@ local fade_chains = make_cartesian_product({
        local input1_live = (input1_type ~= "static")
        local input0_deint = (input0_type == "livedeint")
        local input1_deint = (input1_type == "livedeint")
-       return make_fade_chain(input0_live, input0_deint, input0_scale, input1_live, input1_deint, input1_scale, has_overlay, hq)
+       local input0_video = (input0_type == "video")
+       local input1_video = (input1_type == "video")
+       return make_fade_chain(input0_live, input0_deint, input0_scale, input0_video, input1_live, input1_deint, input1_scale, input1_video, has_overlay, hq)
 end)
 
 function make_sbs_input(chain, signal, deint, has_overlay, hq)
@@ -217,13 +233,16 @@ end
 function make_sbs_chain(input0_type, input0_overlay, input1_type, hq)
        local chain = EffectChain.new(16, 9)
 
+       local bg = chain:add_video_input(bg_video, false)
+
        local input0 = make_sbs_input(chain, INPUT0_SIGNAL_NUM, input0_type == "livedeint", input0_overlay, hq)
        local input1 = make_sbs_input(chain, INPUT4_SIGNAL_NUM, input1_type == "livedeint", false, hq)
 
-       input0.padding_effect:set_vec4("border_color", 0.0, 0.0, 0.0, 1.0)
+       input0.padding_effect:set_vec4("border_color", 0.0, 0.0, 0.0, 0.0)
        input1.padding_effect:set_vec4("border_color", 0.0, 0.0, 0.0, 0.0)
 
-       chain:add_effect(OverlayEffect.new(), input0.padding_effect, input1.padding_effect)
+       local i0 = chain:add_effect(OverlayEffect.new(), bg, input0.padding_effect)
+       chain:add_effect(OverlayEffect.new(), i0, input1.padding_effect)
        chain:finalize(hq)
 
        return {
@@ -246,10 +265,15 @@ local sbs_chains = make_cartesian_product({
 end)
 
 -- A chain to show a single input on screen.
-function make_simple_chain(input_deint, input_scale, has_overlay, hq)
+function make_simple_chain(input_deint, input_video, input_scale, has_overlay, hq)
        local chain = EffectChain.new(16, 9)
 
-       local input = chain:add_live_input(false, input_deint)
+       local input;
+       if input_video then
+               input = chain:add_video_input(iptv_video, false)
+       else
+               input = chain:add_live_input(false, input_deint)
+       end
        input:connect_signal(0)  -- First input card. Can be changed whenever you want.
 
        local resample_effect, resize_effect
@@ -278,21 +302,22 @@ end
 
 -- Make all possible combinations of single-input chains.
 local simple_chains = make_cartesian_product({
-       {"live", "livedeint"},  -- input_type
+       {"video", "live", "livedeint"},  -- input_type
        {true, false},          -- input_scale
        {true, false},          -- has_overlay
        {true, false}           -- hq
 }, function(input_type, input_scale, has_overlay, hq)
        local input_deint = (input_type == "livedeint")
-       return make_simple_chain(input_deint, input_scale, has_overlay, hq)
+       local input_video = (input_type == "video")
+       return make_simple_chain(input_deint, input_video, input_scale, has_overlay, hq)
 end)
 
--- A chain to show a single static picture on screen. Never with CasparCG overlay.
+-- A chain to show a single static picture on screen. Never with HTML overlay.
 local static_chains = make_cartesian_product({
        {true, false}            -- hq
 }, function(hq)
        local chain = EffectChain.new(16, 9)
-       local chain_input = chain:add_effect(ImageInput.new("tfk_pause.png"))
+       local chain_input = chain:add_effect(ImageInput.new(cef_path .. "/nageru/dsn-bg.png"))
 
        chain:finalize(hq)
        return {
@@ -310,6 +335,8 @@ overlay_chain_lq:finalize(false)
 function get_input_type(signals, signal_num)
        if signal_num == STATIC_SIGNAL_NUM then
                return "static"
+       elseif signal_num == VIDEO_SIGNAL_NUM then
+               return "video"
        elseif signals:get_interlaced(signal_num) then
                return "livedeint"
        else
@@ -340,11 +367,11 @@ end
 -- Returns the number of outputs in addition to the live (0) and preview (1).
 -- Called only once, at the start of the program.
 function num_channels()
-       return NUM_CAMERAS + 3  -- sbs, static picture and overlay
+       return NUM_CAMERAS + 4  -- sbs, static picture, video and overlay
 end
 
 function is_plain_signal(num)
-       return num >= INPUT0_SIGNAL_NUM and num <= INPUT4_SIGNAL_NUM
+       return (num >= INPUT0_SIGNAL_NUM and num <= INPUT5_SIGNAL_NUM) or (num == VIDEO_SIGNAL_NUM)
 end
 
 -- Helper function to write e.g. “720p60”. The difference between this
@@ -405,10 +432,14 @@ function channel_name(channel)
                return "Goal R (" .. get_channel_resolution(signal_num) .. ")"
        elseif signal_num == INPUT4_SIGNAL_NUM then
                return "Commentators (" .. get_channel_resolution(signal_num) .. ")"
+       elseif signal_num == INPUT5_SIGNAL_NUM then
+               return "Laptop (" .. get_channel_resolution(signal_num) .. ")"
        elseif signal_num == SBS_SIGNAL_NUM then
                return "Side-by-side"
        elseif signal_num == STATIC_SIGNAL_NUM then
                return "Static picture"
+       elseif signal_num == VIDEO_SIGNAL_NUM then
+               return "IPTV input"
        elseif signal_num == OVERLAY_SIGNAL_NUM then
                return "Overlay"
        end
@@ -461,6 +492,9 @@ function channel_involved_in(channel, signal_num)
        if signal_num == STATIC_SIGNAL_NUM then
                return (channel == NUM_CAMERAS)
        end
+       if signal_num == VIDEO_SIGNAL_NUM then
+               return (channel == NUM_CAMERAS + 1)
+       end
        return false
 end
 
@@ -688,6 +722,8 @@ function get_sbs_chain(signals, t, width, height, input_resolution)
        return sbs_chains[input0_type][overlay_enabled][input1_type][true]
 end
 
+local last_rate = 0.0
+
 -- API ENTRY POINT
 -- Called every frame. Get the chain for displaying at input <num>,
 -- where 0 is live, 1 is preview, 2 is the first channel to display
@@ -740,6 +776,21 @@ function get_chain(num, t, width, height, signals)
        end
        last_resolution = input_resolution
 
+       -- Save some CPU time if we're not having SBS on live.
+       local new_rate
+       if live_signal_num == SBS_SIGNAL_NUM or
+          preview_signal_num == SBS_SIGNAL_NUM or
+          transition_type == ZOOM_TRANSITION then
+               new_rate = 1.0
+       else
+               new_rate = 0.0001
+       end
+       if new_rate ~= last_rate then
+               -- Avoid waking up the video thread (which may be sleeping) if the rate is the same.
+               bg_video:change_rate(new_rate)
+               last_rate = new_rate
+       end
+
        if num == 0 then  -- Live.
                -- See if we're in a transition.
                finish_transitions(t)
@@ -767,9 +818,11 @@ function get_chain(num, t, width, height, signals)
                        local overlay_really_enabled = overlay_enabled and simple_signal_has_overlay(live_signal_num)
                        local chain = simple_chains[input_type][input_scale][overlay_really_enabled][true]
                        prepare = function()
-                               chain.input:connect_signal(live_signal_num)
+                               if input_type ~= "video" then
+                                       chain.input:connect_signal(live_signal_num)
+                                       set_neutral_color_from_signal(chain.wb_effect, live_signal_num)
+                               end
                                set_scale_parameters_if_needed(chain, width, height)
-                               set_neutral_color_from_signal(chain.wb_effect, live_signal_num)
                                prepare_overlay_live(chain, t, 1.0)
                        end
                        return chain.chain, prepare
@@ -808,9 +861,11 @@ function get_chain(num, t, width, height, signals)
                local input_scale = needs_scale(signals, signal_num, width, height)
                local chain = simple_chains[input_type][input_scale][show_overlay][false]
                prepare = function()
-                       chain.input:connect_signal(signal_num)
+                       if input_type ~= "video" then
+                               chain.input:connect_signal(signal_num)
+                               set_neutral_color(chain.wb_effect, neutral_colors[signal_num + 1])
+                       end
                        set_scale_parameters_if_needed(chain, width, height)
-                       set_neutral_color(chain.wb_effect, neutral_colors[signal_num + 1])
                        prepare_overlay_static(chain, t)
                end
                return chain.chain, prepare
@@ -874,10 +929,9 @@ function prepare_sbs_chain(chain, t, transition_type, src_signal, dst_signal, sc
        set_neutral_color(chain.input0.wb_effect, neutral_colors[1])
        set_neutral_color(chain.input1.wb_effect, neutral_colors[5])
 
-       -- First input is positioned (16,48) from top-left.
-       -- Second input is positioned (16,48) from the bottom-right.
-       local pos0 = pos_from_top_left(16, 48, 848, 477, screen_width, screen_height)
-       local pos1 = pos_from_top_left(1280 - 384 - 16, 720 - 216 - 48, 384, 216, screen_width, screen_height)
+       -- Both inputs are the same size (true side-by-side).
+       local pos0 = pos_from_top_left(1280 - 616 - 16, 186, 616, 347, screen_width, screen_height)
+       local pos1 = pos_from_top_left(16, 186, 616, 347, screen_width, screen_height)
 
        local pos_fs = { x0 = 0, y0 = 0, x1 = screen_width, y1 = screen_height }
        local affine_param
@@ -1079,5 +1133,6 @@ function calc_fade_progress(t, transition_start, transition_end)
 end
 
 ThemeMenu.set(
-       { "Reload overlay", reload_cef }
+       { "Reload overlay", reload_cef },
+       { "Disconnect IPTV", disconnect_iptv }
 )