]> git.sesse.net Git - nageru/commitdiff
Add a static image input (fixed to 1280x720 for now, and uploaded rather inefficiently).
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 4 Nov 2015 00:53:01 +0000 (01:53 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 4 Nov 2015 00:53:01 +0000 (01:53 +0100)
Makefile
bg.jpeg [new file with mode: 0644]
image_input.cpp [new file with mode: 0644]
image_input.h [new file with mode: 0644]
theme.cpp
theme.lua

index 657f7e7419f650683322aa20263d87fcba7047c8..0b88d60bc7add19b045fae225467b963ecb42031 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,14 @@
 CXX=g++
 PKG_MODULES = Qt5Core Qt5Gui Qt5Widgets Qt5OpenGLExtensions Qt5OpenGL libusb-1.0 movit lua5.2 libmicrohttpd
 CXXFLAGS := -O2 -march=native -g -std=gnu++11 -Wall -Wno-deprecated-declarations -Werror -fPIC $(shell pkg-config --cflags $(PKG_MODULES)) -pthread -DMOVIT_SHADER_DIR=\"$(shell pkg-config --variable=shaderdir movit)\"
-LDFLAGS=$(shell pkg-config --libs $(PKG_MODULES)) -lEGL -lGL -pthread -lva -lva-drm -lva-x11 -lX11 -lavformat -lavcodec -lavutil -lzita-resampler
+LDFLAGS=$(shell pkg-config --libs $(PKG_MODULES)) -lEGL -lGL -pthread -lva -lva-drm -lva-x11 -lX11 -lavformat -lavcodec -lavutil -lswscale -lzita-resampler
 
 # Qt objects
 OBJS=glwidget.o main.o mainwindow.o vumeter.o lrameter.o vu_common.o
 OBJS += glwidget.moc.o mainwindow.moc.o vumeter.moc.o lrameter.moc.o
 
 # Mixer objects
-OBJS += h264encode.o mixer.o bmusb/bmusb.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o resampler.o httpd.o ebu_r128_proc.o flags.o
+OBJS += h264encode.o mixer.o bmusb/bmusb.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o resampler.o httpd.o ebu_r128_proc.o flags.o image_input.o
 
 %.o: %.cpp
        $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
diff --git a/bg.jpeg b/bg.jpeg
new file mode 100644 (file)
index 0000000..fdb51d9
Binary files /dev/null and b/bg.jpeg differ
diff --git a/image_input.cpp b/image_input.cpp
new file mode 100644 (file)
index 0000000..b4fc657
--- /dev/null
@@ -0,0 +1,92 @@
+#include "image_input.h"
+
+#include <movit/image_format.h>
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/imgutils.h>
+#include <libavutil/pixfmt.h>
+#include <libswscale/swscale.h>
+}
+
+using namespace std;
+
+ImageInput::ImageInput(const std::string &filename)
+       : movit::FlatInput({movit::COLORSPACE_sRGB, movit::GAMMA_sRGB}, movit::FORMAT_RGBA_POSTMULTIPLIED_ALPHA,
+                          GL_UNSIGNED_BYTE, 1280, 720)  // FIXME
+{
+       AVFormatContext *format_ctx = nullptr;
+       if (avformat_open_input(&format_ctx, filename.c_str(), nullptr, nullptr) != 0) {
+               fprintf(stderr, "%s: Error opening file\n", filename.c_str());
+               exit(1);
+       }
+
+       if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
+               fprintf(stderr, "%s: Error finding stream info\n", filename.c_str());
+               exit(1);
+       }
+
+       int stream_index = -1;
+       for (unsigned i = 0; i < format_ctx->nb_streams; ++i) {
+               if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+                       stream_index = i;
+                       break;
+               }
+       }
+       if (stream_index == -1) {
+               fprintf(stderr, "%s: No video stream found\n", filename.c_str());
+               exit(1);
+       }
+
+       AVCodecContext *codec_ctx = format_ctx->streams[stream_index]->codec;
+       AVCodec *codec = avcodec_find_decoder(codec_ctx->codec_id);
+       if (codec == nullptr) {
+               fprintf(stderr, "%s: Cannot find decoder\n", filename.c_str());
+               exit(1);
+       }
+       if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
+               fprintf(stderr, "%s: Cannot open decoder\n", filename.c_str());
+               exit(1);
+       }
+
+       // Read packets until we have a frame.
+       int frame_finished = 0;
+       AVFrame *frame = av_frame_alloc();
+       do {
+               AVPacket pkt;
+               av_init_packet(&pkt);
+               pkt.data = nullptr;
+               pkt.size = 0;
+               if (av_read_frame(format_ctx, &pkt) < 0) {
+                       fprintf(stderr, "%s: Cannot read frame\n", filename.c_str());
+                       exit(1);
+               }
+               if (pkt.stream_index != stream_index) {
+                       continue;
+               }
+
+               if (avcodec_decode_video2(codec_ctx, frame, &frame_finished, &pkt) < 0) {
+                       fprintf(stderr, "%s: Cannot decode frame\n", filename.c_str());
+                       exit(1);
+               }
+       } while (!frame_finished);
+
+       // TODO: Scale down if needed!
+       AVPicture pic;
+       avpicture_alloc(&pic, PIX_FMT_RGBA, frame->width, frame->height);
+       SwsContext *sws_ctx = sws_getContext(frame->width, frame->height,
+               (PixelFormat)frame->format, frame->width, frame->height,
+               PIX_FMT_RGBA, SWS_BICUBIC, nullptr, nullptr, nullptr);
+       if (sws_ctx == nullptr) {
+               fprintf(stderr, "%s: Could not create scaler context\n", filename.c_str());
+               exit(1);
+       }
+       sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, pic.data, pic.linesize);
+       sws_freeContext(sws_ctx);
+
+       size_t len = frame->width * frame->height * 4;
+       image_data.reset(new uint8_t[len]);
+       av_image_copy_to_buffer(image_data.get(), len, pic.data, pic.linesize, PIX_FMT_RGBA, frame->width, frame->height, 1);
+       set_pixel_data(image_data.get());
+}
diff --git a/image_input.h b/image_input.h
new file mode 100644 (file)
index 0000000..edc86c8
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _IMAGE_INPUT_H
+#define _IMAGE_INPUT_H 1
+
+#include <memory>
+#include <string>
+
+#include <movit/flat_input.h>
+
+
+// An output that takes its input from a static image, loaded with ffmpeg.
+// comes from a single 2D array with chunky pixels.
+class ImageInput : public movit::FlatInput {
+public:
+       ImageInput(const std::string &filename);
+
+       std::string effect_type_id() const override { return "ImageInput"; }
+
+private:
+       std::unique_ptr<uint8_t[]> image_data;
+};
+
+#endif // !defined(_IMAGE_INPUT_H)
index 4ea8cd73b6eaf12ea23da9e363f8b8a2220ee4cd..bd1259062780ae0859b65b4042fdfa644785d745 100644 (file)
--- a/theme.cpp
+++ b/theme.cpp
@@ -21,6 +21,8 @@
 #include <new>
 #include <utility>
 
+#include "image_input.h"
+
 namespace movit {
 class ResourcePool;
 }  // namespace movit
@@ -63,7 +65,8 @@ Effect *get_effect(lua_State *L, int idx)
            luaL_testudata(L, idx, "IntegralPaddingEffect") ||
            luaL_testudata(L, idx, "OverlayEffect") ||
            luaL_testudata(L, idx, "ResizeEffect") ||
-           luaL_testudata(L, idx, "MixEffect")) {
+           luaL_testudata(L, idx, "MixEffect") ||
+           luaL_testudata(L, idx, "ImageInput")) {
                return (Effect *)lua_touserdata(L, idx);
        }
        luaL_error(L, "Error: Index #%d was not an Effect type\n", idx);
@@ -76,6 +79,13 @@ bool checkbool(lua_State* L, int idx)
        return lua_toboolean(L, idx);
 }
 
+std::string checkstdstring(lua_State *L, int index)
+{
+       size_t len;
+       const char* cstr = lua_tolstring(L, index, &len);
+       return std::string(cstr, len);
+}
+
 int EffectChain_new(lua_State* L)
 {
        assert(lua_gettop(L) == 2);
@@ -102,7 +112,11 @@ int EffectChain_add_effect(lua_State* L)
        // TODO: Better error reporting.
        Effect *effect = get_effect(L, 2);
        if (lua_gettop(L) == 2) {
-               chain->add_effect(effect);
+               if (effect->num_inputs() == 0) {
+                       chain->add_input((Input *)effect);
+               } else {
+                       chain->add_effect(effect);
+               }
        } else {
                vector<Effect *> inputs;
                for (int idx = 3; idx <= lua_gettop(L); ++idx) {
@@ -179,6 +193,13 @@ int LiveInputWrapper_connect_signal(lua_State* L)
        return 0;
 }
 
+int ImageInput_new(lua_State* L)
+{
+       assert(lua_gettop(L) == 1);
+       std::string filename = checkstdstring(L, 1);
+       return wrap_lua_object<ImageInput>(L, "ImageInput", filename);
+}
+
 int WhiteBalanceEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
@@ -225,12 +246,10 @@ int Effect_set_float(lua_State *L)
 {
        assert(lua_gettop(L) == 3);
        Effect *effect = (Effect *)get_effect(L, 1);
-       size_t len;
-       const char* cstr = lua_tolstring(L, 2, &len);
-       std::string key(cstr, len);
+       std::string key = checkstdstring(L, 2);
        float value = luaL_checknumber(L, 3);
        if (!effect->set_float(key, value)) {
-               luaL_error(L, "Effect refused set_float(\"%s\", %d) (invalid key?)", cstr, int(value));
+               luaL_error(L, "Effect refused set_float(\"%s\", %d) (invalid key?)", key.c_str(), int(value));
        }
        return 0;
 }
@@ -239,12 +258,10 @@ int Effect_set_int(lua_State *L)
 {
        assert(lua_gettop(L) == 3);
        Effect *effect = (Effect *)get_effect(L, 1);
-       size_t len;
-       const char* cstr = lua_tolstring(L, 2, &len);
-       std::string key(cstr, len);
+       std::string key = checkstdstring(L, 2);
        float value = luaL_checknumber(L, 3);
        if (!effect->set_int(key, value)) {
-               luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", cstr, int(value));
+               luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", key.c_str(), int(value));
        }
        return 0;
 }
@@ -253,15 +270,13 @@ int Effect_set_vec3(lua_State *L)
 {
        assert(lua_gettop(L) == 5);
        Effect *effect = (Effect *)get_effect(L, 1);
-       size_t len;
-       const char* cstr = lua_tolstring(L, 2, &len);
-       std::string key(cstr, len);
+       std::string key = checkstdstring(L, 2);
        float v[3];
        v[0] = luaL_checknumber(L, 3);
        v[1] = luaL_checknumber(L, 4);
        v[2] = luaL_checknumber(L, 5);
        if (!effect->set_vec3(key, v)) {
-               luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", cstr,
+               luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", key.c_str(),
                        v[0], v[1], v[2]);
        }
        return 0;
@@ -271,16 +286,14 @@ int Effect_set_vec4(lua_State *L)
 {
        assert(lua_gettop(L) == 6);
        Effect *effect = (Effect *)get_effect(L, 1);
-       size_t len;
-       const char* cstr = lua_tolstring(L, 2, &len);
-       std::string key(cstr, len);
+       std::string key = checkstdstring(L, 2);
        float v[4];
        v[0] = luaL_checknumber(L, 3);
        v[1] = luaL_checknumber(L, 4);
        v[2] = luaL_checknumber(L, 5);
        v[3] = luaL_checknumber(L, 6);
        if (!effect->set_vec4(key, v)) {
-               luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", cstr,
+               luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", key.c_str(),
                        v[0], v[1], v[2], v[3]);
        }
        return 0;
@@ -299,6 +312,15 @@ const luaL_Reg LiveInputWrapper_funcs[] = {
        { NULL, NULL }
 };
 
+const luaL_Reg ImageInput_funcs[] = {
+       { "new", ImageInput_new },
+       { "set_float", Effect_set_float },
+       { "set_int", Effect_set_int },
+       { "set_vec3", Effect_set_vec3 },
+       { "set_vec4", Effect_set_vec4 },
+       { NULL, NULL }
+};
+
 const luaL_Reg WhiteBalanceEffect_funcs[] = {
        { "new", WhiteBalanceEffect_new },
        { "set_float", Effect_set_float },
@@ -414,6 +436,7 @@ Theme::Theme(const char *filename, ResourcePool *resource_pool, unsigned num_car
 
        register_class("EffectChain", EffectChain_funcs); 
        register_class("LiveInputWrapper", LiveInputWrapper_funcs); 
+       register_class("ImageInput", ImageInput_funcs);
        register_class("WhiteBalanceEffect", WhiteBalanceEffect_funcs);
        register_class("ResampleEffect", ResampleEffect_funcs);
        register_class("PaddingEffect", PaddingEffect_funcs);
index 93f79ff80df21c096926fe06eabe218f2140a27d..abba2202250b34bb5e428bb3c3eab7eded453d16 100644 (file)
--- a/theme.lua
+++ b/theme.lua
@@ -25,10 +25,12 @@ local preview_signal_num = 1
 local INPUT0_SIGNAL_NUM = 0
 local INPUT1_SIGNAL_NUM = 1
 local SBS_SIGNAL_NUM = 2
+local STATIC_SIGNAL_NUM = 3
 
--- A “fake” signal number that signifies that we are fading from one input
+-- “fake” signal numbers that signifies that we are fading from one input
 -- to the next.
-local FADE_SIGNAL_NUM = 3
+local FADE_VTV_SIGNAL_NUM = 4  -- Video to/from video.
+local FADE_VTP_SIGNAL_NUM = 5  -- Video to/from static picture.
 
 -- The main live chain.
 function make_sbs_chain(hq)
@@ -86,15 +88,24 @@ local main_chain_hq = make_sbs_chain(true)
 local main_chain_lq = make_sbs_chain(false)
 
 -- A chain to fade between two inputs (live chain only)
-local fade_chain_hq = EffectChain.new(16, 9)
-local fade_chain_hq_input0 = fade_chain_hq:add_live_input(true)
-local fade_chain_hq_wb0_effect = fade_chain_hq:add_effect(WhiteBalanceEffect.new())
-local fade_chain_hq_input1 = fade_chain_hq:add_live_input(true)
-local fade_chain_hq_wb1_effect = fade_chain_hq:add_effect(WhiteBalanceEffect.new())
-fade_chain_hq_input0:connect_signal(0)
-fade_chain_hq_input1:connect_signal(1)
-local fade_chain_mix_effect = fade_chain_hq:add_effect(MixEffect.new(), fade_chain_hq_wb0_effect, fade_chain_hq_wb1_effect)
-fade_chain_hq:finalize(true)
+local fade_vtv_chain_hq = EffectChain.new(16, 9)
+local fade_vtv_chain_hq_input0 = fade_vtv_chain_hq:add_live_input(true)
+local fade_vtv_chain_hq_wb0_effect = fade_vtv_chain_hq:add_effect(WhiteBalanceEffect.new())
+local fade_vtv_chain_hq_input1 = fade_vtv_chain_hq:add_live_input(true)
+local fade_vtv_chain_hq_wb1_effect = fade_vtv_chain_hq:add_effect(WhiteBalanceEffect.new())
+fade_vtv_chain_hq_input0:connect_signal(0)
+fade_vtv_chain_hq_input1:connect_signal(1)
+local fade_vtv_chain_mix_effect = fade_vtv_chain_hq:add_effect(MixEffect.new(), fade_vtv_chain_hq_wb0_effect, fade_vtv_chain_hq_wb1_effect)
+fade_vtv_chain_hq:finalize(true)
+
+-- A chain to fade between an inputs and a picture (live chain only)
+local fade_vtp_chain_hq = EffectChain.new(16, 9)
+local fade_vtp_chain_hq_input0 = fade_vtp_chain_hq:add_live_input(true)
+local fade_vtp_chain_hq_wb0_effect = fade_vtp_chain_hq:add_effect(WhiteBalanceEffect.new())
+local fade_vtp_chain_hq_input1 = fade_vtp_chain_hq:add_effect(ImageInput.new("bg.jpeg"))
+fade_vtp_chain_hq_input0:connect_signal(0)
+local fade_vtp_chain_mix_effect = fade_vtp_chain_hq:add_effect(MixEffect.new(), fade_vtp_chain_hq_wb0_effect, fade_vtp_chain_hq_input1)
+fade_vtp_chain_hq:finalize(true)
 
 -- A chain to show a single input on screen (HQ version).
 local simple_chain_hq = EffectChain.new(16, 9)
@@ -110,10 +121,20 @@ simple_chain_lq_input:connect_signal(0)  -- First input card. Can be changed whe
 local simple_chain_lq_wb_effect = simple_chain_lq:add_effect(WhiteBalanceEffect.new())
 simple_chain_lq:finalize(false)
 
+-- A chain to show a single static picture on screen (HQ version).
+local static_chain_hq = EffectChain.new(16, 9)
+local static_chain_hq_input = static_chain_hq:add_effect(ImageInput.new("bg.jpeg"))
+static_chain_hq:finalize(true)
+
+-- A chain to show a single static picture on screen (LQ version).
+local static_chain_lq = EffectChain.new(16, 9)
+local static_chain_lq_input = static_chain_lq:add_effect(ImageInput.new("bg.jpeg"))
+static_chain_lq:finalize(false)
+
 -- 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 3
+       return 4
 end
 
 -- Returns the name for each additional channel (starting from 2).
@@ -125,6 +146,8 @@ function channel_name(channel)
                return "Input 2"
        elseif channel == 4 then
                return "Side-by-side"
+       elseif channel == 5 then
+               return "Static picture"
        end
 end
 
@@ -151,7 +174,7 @@ function finish_transitions(t)
        end
 
        -- If live is fade but de-facto single, make it so.
-       if live_signal_num == FADE_SIGNAL_NUM and t >= transition_end then
+       if (live_signal_num == FADE_VTV_SIGNAL_NUM or live_signal_num == FADE_VTP_SIGNAL_NUM) and t >= transition_end then
                live_signal_num = fade_dst_signal
        end
 end
@@ -169,8 +192,12 @@ function get_transitions(t)
                return {"Cut"}
        end
 
-       if (live_signal_num == INPUT0_SIGNAL_NUM and preview_signal_num == INPUT1_SIGNAL_NUM) or
-          (live_signal_num == INPUT1_SIGNAL_NUM and preview_signal_num == INPUT0_SIGNAL_NUM) then
+       if (live_signal_num == INPUT0_SIGNAL_NUM or
+           live_signal_num == INPUT1_SIGNAL_NUM or
+           live_signal_num == STATIC_SIGNAL_NUM) and
+          (preview_signal_num == INPUT0_SIGNAL_NUM or
+           preview_signal_num == INPUT1_SIGNAL_NUM or
+           preview_signal_num == STATIC_SIGNAL_NUM) then
                return {"Cut", "", "Fade"}
        end
 
@@ -189,7 +216,7 @@ end
 function transition_clicked(num, t)
        if num == 0 then
                -- Cut.
-               if live_signal_num == FADE_SIGNAL_NUM then
+               if live_signal_num == FADE_VTV_SIGNAL_NUM or live_signal_num == FADE_VTP_SIGNAL_NUM then
                        -- Ongoing fade; finish it immediately.
                        finish_transitions(transition_end)
                end
@@ -256,7 +283,17 @@ function transition_clicked(num, t)
                        fade_src_signal = live_signal_num
                        fade_dst_signal = preview_signal_num
                        preview_signal_num = live_signal_num
-                       live_signal_num = FADE_SIGNAL_NUM
+                       live_signal_num = FADE_VTV_SIGNAL_NUM
+               elseif ((live_signal_num == INPUT0_SIGNAL_NUM or live_signal_num == INPUT1_SIGNAL_NUM) and
+                       preview_signal_num == STATIC_SIGNAL_NUM) or
+                      ((preview_signal_num == INPUT0_SIGNAL_NUM or preview_signal_num == INPUT1_SIGNAL_NUM) and
+                       live_signal_num == STATIC_SIGNAL_NUM) then
+                       transition_start = t
+                       transition_end = t + 1.0
+                       fade_src_signal = live_signal_num
+                       fade_dst_signal = preview_signal_num
+                       preview_signal_num = live_signal_num
+                       live_signal_num = FADE_VTP_SIGNAL_NUM
                else
                        -- Fades involving SBS are ignored (we have no chain for it).
                end
@@ -293,28 +330,38 @@ function get_chain(num, t, width, height)
                                set_neutral_color_from_signal(simple_chain_hq_wb_effect, live_signal_num)
                        end
                        return simple_chain_hq, prepare
-               elseif live_signal_num == FADE_SIGNAL_NUM then  -- Fade.
+               elseif live_signal_num == STATIC_SIGNAL_NUM then  -- Static picture.
                        prepare = function()
-                               fade_chain_hq_input0:connect_signal(fade_src_signal)
-                               set_neutral_color_from_signal(fade_chain_hq_wb0_effect, fade_src_signal)
-                               fade_chain_hq_input1:connect_signal(fade_dst_signal)
-                               set_neutral_color_from_signal(fade_chain_hq_wb1_effect, fade_dst_signal)
-                               local tt = (t - transition_start) / (transition_end - transition_start)
-                               if tt < 0.0 then
-                                       tt = 0.0
-                               elseif tt > 1.0 then
-                                       tt = 1.0
+                       end
+                       return static_chain_hq, prepare
+               elseif live_signal_num == FADE_VTV_SIGNAL_NUM then  -- Fade video-to-video.
+                       prepare = function()
+                               fade_vtv_chain_hq_input0:connect_signal(fade_src_signal)
+                               set_neutral_color_from_signal(fade_vtv_chain_hq_wb0_effect, fade_src_signal)
+                               fade_vtv_chain_hq_input1:connect_signal(fade_dst_signal)
+                               set_neutral_color_from_signal(fade_vtv_chain_hq_wb1_effect, fade_dst_signal)
+                               local tt = calc_fade_progress(t, transition_start, transition_end)
+
+                               fade_vtv_chain_mix_effect:set_float("strength_first", 1.0 - tt)
+                               fade_vtv_chain_mix_effect:set_float("strength_second", tt)
+                       end
+                       return fade_vtv_chain_hq, prepare
+               elseif live_signal_num == FADE_VTP_SIGNAL_NUM then  -- Fade video-to-picture (or picture-to-video).
+                       prepare = function()
+                               local tt
+                               if fade_src_signal == STATIC_SIGNAL_NUM then
+                                       fade_vtp_chain_hq_input0:connect_signal(fade_dst_signal)
+                                       set_neutral_color_from_signal(fade_vtp_chain_hq_wb0_effect, fade_dst_signal)
+                                       tt = 1.0 - calc_fade_progress(t, transition_start, transition_end)
+                               else
+                                       fade_vtp_chain_hq_input0:connect_signal(fade_src_signal)
+                                       set_neutral_color_from_signal(fade_vtp_chain_hq_wb0_effect, fade_src_signal)
+                                       tt = calc_fade_progress(t, transition_start, transition_end)
                                end
-
-                               -- Make the fade look maybe a tad more natural, by pumping it
-                               -- through a sigmoid function.
-                               tt = 10.0 * tt - 5.0
-                               tt = 1.0 / (1.0 + math.exp(-tt))
-
-                               fade_chain_mix_effect:set_float("strength_first", 1.0 - tt)
-                               fade_chain_mix_effect:set_float("strength_second", tt)
+                               fade_vtp_chain_mix_effect:set_float("strength_first", 1.0 - tt)
+                               fade_vtp_chain_mix_effect:set_float("strength_second", tt)
                        end
-                       return fade_chain_hq, prepare
+                       return fade_vtp_chain_hq, prepare
                end
 
                -- SBS code (live_signal_num == SBS_SIGNAL_NUM).
@@ -363,6 +410,11 @@ function get_chain(num, t, width, height)
                end
                return main_chain_lq.chain, prepare
        end
+       if num == STATIC_SIGNAL_NUM + 2 then
+               prepare = function()
+               end
+               return static_chain_lq, prepare
+       end
 end
 
 function place_rectangle(resample_effect, resize_effect, padding_effect, x0, y0, x1, y1, screen_width, screen_height, input_width, input_height)
@@ -526,3 +578,19 @@ function set_neutral_color_from_signal(effect, signal)
                set_neutral_color(effect, input1_neutral_color)
        end
 end
+
+function calc_fade_progress(t, transition_start, transition_end)
+       local tt = (t - transition_start) / (transition_end - transition_start)
+       if tt < 0.0 then
+               tt = 0.0
+       elseif tt > 1.0 then
+               tt = 1.0
+       end
+
+       -- Make the fade look maybe a tad more natural, by pumping it
+       -- through a sigmoid function.
+       tt = 10.0 * tt - 5.0
+       tt = 1.0 / (1.0 + math.exp(-tt))
+
+       return tt
+end