]> git.sesse.net Git - casparcg/commitdiff
chroma feature: MIXER 1-1 CHROMA GREEN|BLUE 0.10 0.04
authorcambell <cambell.prince@gmail.com>
Tue, 28 May 2013 19:03:45 +0000 (02:03 +0700)
committercambell <cambell.prince@gmail.com>
Tue, 28 May 2013 19:03:45 +0000 (02:03 +0700)
17 files changed:
common/exception/exceptions.h
core/mixer/gpu/shader.cpp
core/mixer/gpu/shader.h
core/mixer/image/blend_modes.cpp
core/mixer/image/blend_modes.h
core/mixer/image/image_kernel.cpp
core/mixer/image/image_kernel.h
core/mixer/image/image_mixer.cpp
core/mixer/image/image_mixer.h
core/mixer/image/shader/blending_glsl.h
core/mixer/image/shader/image_shader.cpp
core/mixer/image/shader/image_shader.h
core/mixer/mixer.cpp
core/mixer/mixer.h
core/producer/frame/frame_transform.cpp
core/producer/frame/frame_transform.h
protocol/amcp/AMCPCommandsImpl.cpp

index 2c89f14a3a8c617405afe58ca83873b5a11e8ec9..4288e1cdd491fade734eac00ff363678de75f626 100644 (file)
@@ -39,6 +39,7 @@ typedef boost::error_info<struct errinfo_nested_exception_, std::exception_ptr>
 struct caspar_exception                        : virtual boost::exception, virtual std::exception \r
 {\r
        caspar_exception(){}\r
+       virtual ~caspar_exception() throw() {}
        explicit caspar_exception(const char* msg) : std::exception(msg) {}\r
 };\r
 \r
index ffd20de2788f46fba708809cf08edfd140bfa881..b06c1580f6cda7c311fc04085257dc205db01291 100644 (file)
@@ -25,7 +25,7 @@
 \r
 #include <common/gl/gl_check.h>\r
 \r
-#include <gl/glew.h>\r
+#include <GL/glew.h>
 \r
 #include <unordered_map>\r
 \r
@@ -128,10 +128,30 @@ public:
                GL(glUniform1f(get_location(name.c_str()), value));\r
        }\r
 \r
-       void set(const std::string& name, double value)\r
+    void set(const std::string& name, float value1, float value2)
+    {
+        GL(glUniform2f(get_location(name.c_str()), value1, value2));
+    }
+
+    void set(const std::string& name, float value1, float value2, float value3)
+    {
+        GL(glUniform3f(get_location(name.c_str()), value1, value2, value3));
+    }
+
+    void set(const std::string& name, float value1, float value2, float value3, float value4)
+    {
+        GL(glUniform4f(get_location(name.c_str()), value1, value2, value3, value4));
+    }
+
+    void set(const std::string& name, double value)
        {\r
                GL(glUniform1f(get_location(name.c_str()), static_cast<float>(value)));\r
        }\r
+
+    void set(const std::string& name, double value1, double value2)
+    {
+        GL(glUniform2f(get_location(name.c_str()), static_cast<float>(value1), static_cast<float>(value2)));
+    }
 };\r
 \r
 \r
@@ -139,7 +159,11 @@ shader::shader(const std::string& vertex_source_str, const std::string& fragment
 void shader::set(const std::string& name, bool value){impl_->set(name, value);}\r
 void shader::set(const std::string& name, int value){impl_->set(name, value);}\r
 void shader::set(const std::string& name, float value){impl_->set(name, value);}\r
+void shader::set(const std::string& name, float value1, float value2){impl_->set(name, value1, value2);}
+void shader::set(const std::string& name, float value1, float value2, float value3){impl_->set(name, value1, value2, value3);}
+void shader::set(const std::string& name, float value1, float value2, float value3, float value4){impl_->set(name, value1, value2, value3, value4);}
 void shader::set(const std::string& name, double value){impl_->set(name, value);}\r
+void shader::set(const std::string& name, double value1, double value2){impl_->set(name, value1, value2);}
 int shader::id() const{return impl_->program_;}\r
 \r
-}}
\ No newline at end of file
+}}
index 4be183b6edec7c6ac15c8a741d1824d1c0077cb2..9f3e17502202566f182eb06bbef2812bb20473f6 100644 (file)
@@ -36,7 +36,11 @@ public:
        void set(const std::string& name, bool value);\r
        void set(const std::string& name, int value);\r
        void set(const std::string& name, float value);\r
-       void set(const std::string& name, double value);\r
+    void set(const std::string& name, float value1, float value2);
+    void set(const std::string& name, float value1, float value2, float value3);
+    void set(const std::string& name, float value1, float value2, float value3, float value4);
+    void set(const std::string& name, double value);
+       void set(const std::string& name, double value1, double value2);
 private:\r
        friend class ogl_device;\r
        struct implementation;\r
@@ -45,4 +49,4 @@ private:
        int id() const;\r
 };\r
 \r
-}}
\ No newline at end of file
+}}
index 340a2b43569787090e9a8bb59c4f76724e9b3867..a832e522eacfe8003aa42039ca296bec27d86f45 100644 (file)
@@ -27,7 +27,7 @@
 \r
 namespace caspar { namespace core {\r
                \r
-blend_mode::type get_blend_mode(const std::wstring& str)\r
+blend_mode::type get_blend_mode(const std::wstring& str)
 {\r
        if(boost::iequals(str, L"normal"))\r
                return blend_mode::normal;\r
@@ -91,4 +91,24 @@ blend_mode::type get_blend_mode(const std::wstring& str)
        return blend_mode::normal;\r
 }\r
 \r
-}}
\ No newline at end of file
+chroma::type get_chroma_mode(const std::wstring& str)
+{
+    if     (boost::iequals(str, L"none"))
+        return chroma::none;
+    else if(boost::iequals(str, L"red"))
+        return chroma::red;
+    else if(boost::iequals(str, L"yellow"))
+        return chroma::yellow;
+    else if(boost::iequals(str, L"green"))
+        return chroma::green;
+    else if(boost::iequals(str, L"torquise"))
+        return chroma::torquise;
+    else if(boost::iequals(str, L"blue"))
+        return chroma::blue;
+    else if(boost::iequals(str, L"magenta"))
+        return chroma::magenta;
+
+    return chroma::none;
+}
+
+}}
index 6690ee89cc8a9803e12b1116c2b931d9d10eb21b..aed360a18f9cf12d666ab216e046235e54ab9e03 100644 (file)
 \r
 namespace caspar { namespace core {\r
                \r
+struct chroma
+{
+    enum type
+    {
+        none     = 0x000000,
+        red      = 0xff0000,
+        yellow   = 0xffff00,
+        green    = 0x00ff00,
+        torquise = 0x00ffff,
+        blue     = 0x0000ff,
+        magenta  = 0xff00ff
+    };
+
+    unsigned int    key;
+    float           threshold,
+                    softness,
+                    spill,
+                    blur;
+    bool            show_mask;
+
+    chroma(type m=none)
+    : key(m), threshold(0.0), softness(0.0), spill(0.0),
+      blur(0.0), show_mask(false) {}
+};
+
 struct blend_mode\r
 {\r
        enum type \r
@@ -59,8 +84,15 @@ struct blend_mode
                mix,\r
                blend_mode_count \r
        };\r
-};\r
 \r
-blend_mode::type get_blend_mode(const std::wstring& str);\r
+       type    mode;
+       chroma  chroma;
 \r
-}}
\ No newline at end of file
+       blend_mode(type t = normal) : mode(t) {}
+};
+
+blend_mode::type get_blend_mode(const std::wstring& str);
+
+chroma::type get_chroma_mode(const std::wstring& str);
+
+}}
index f7e837b8b3510c83e79003191d636766b8004ba9..d25cc88fc65693b82d864057488f5a921445880f 100644 (file)
@@ -106,13 +106,27 @@ struct image_kernel::implementation : boost::noncopyable
                shader_->set("local_key",               texture_id::local_key);\r
                shader_->set("layer_key",               texture_id::layer_key);\r
                shader_->set("is_hd",                   params.pix_desc.planes.at(0).height > 700 ? 1 : 0);\r
-               shader_->set("has_local_key",   params.local_key);\r
-               shader_->set("has_layer_key",   params.layer_key);\r
+               shader_->set("has_local_key",   bool(params.local_key));
+               shader_->set("has_layer_key",   bool(params.layer_key));
                shader_->set("pixel_format",    params.pix_desc.pix_fmt);       \r
                shader_->set("opacity",                 params.transform.is_key ? 1.0 : params.transform.opacity);      \r
+
+        shader_->set("chroma_mode",    params.blend_mode.chroma.key == chroma::green ? 1 : (params.blend_mode.chroma.key == chroma::blue ? 2 : 0));
+        shader_->set("chroma_blend",   params.blend_mode.chroma.threshold, params.blend_mode.chroma.softness);
+        shader_->set("chroma_spill",   params.blend_mode.chroma.spill);
+//        shader_->set("chroma.key",      ((params.blend_mode.chroma.key >> 24) && 0xff)/255.0f,
+//                                        ((params.blend_mode.chroma.key >> 16) && 0xff)/255.0f,
+//                                        (params.blend_mode.chroma.key & 0xff)/255.0f);
+//             if (params.blend_mode.chroma.key != chroma::none)
+//             {
+//                 shader_->set("chroma.threshold",    params.blend_mode.chroma.threshold);
+//                 shader_->set("chroma.softness",     params.blend_mode.chroma.softness);
+//            shader_->set("chroma.blur",         params.blend_mode.chroma.blur);
+//                 shader_->set("chroma.spill",        params.blend_mode.chroma.spill);
+//            shader_->set("chroma.show_mask",    params.blend_mode.chroma.show_mask);
+//             }
                \r
-               // Setup blend_func\r
-               \r
+               // Setup blend_func             
                if(params.transform.is_key)\r
                        params.blend_mode = blend_mode::normal;\r
 \r
@@ -121,7 +135,7 @@ struct image_kernel::implementation : boost::noncopyable
                        params.background->bind(6);\r
 \r
                        shader_->set("background",      texture_id::background);\r
-                       shader_->set("blend_mode",      params.blend_mode);\r
+                       shader_->set("blend_mode",      params.blend_mode.mode);
                        shader_->set("keyer",           params.keyer);\r
                }\r
                else\r
@@ -239,4 +253,4 @@ void image_kernel::draw(draw_params&& params)
        impl_->draw(std::move(params));\r
 }\r
 \r
-}}
\ No newline at end of file
+}}
index ac378fc0680fb2e495b83919b3781051197ca5cd..89a69c80e02bb4ab7431dbd99698c9fa4f8389ac 100644 (file)
@@ -49,7 +49,7 @@ struct draw_params
        pixel_format_desc                                               pix_desc;\r
        std::vector<safe_ptr<device_buffer>>    textures;\r
        frame_transform                                                 transform;\r
-       blend_mode::type                                                blend_mode;\r
+       blend_mode                                                              blend_mode;
        keyer::type                                                             keyer;\r
        std::shared_ptr<device_buffer>                  background;\r
        std::shared_ptr<device_buffer>                  local_key;\r
index 339986f5780e65bf267dfb49857d93786a707eb1..2fe7e94ed8b28c996d1e799f7aecec73d93d6e4a 100644 (file)
@@ -56,7 +56,7 @@ struct item
        frame_transform                                                 transform;\r
 };\r
 \r
-typedef std::pair<blend_mode::type, std::vector<item>> layer;\r
+typedef std::pair<blend_mode, std::vector<item>> layer;
 \r
 class image_renderer\r
 {\r
@@ -144,7 +144,7 @@ private:
                std::shared_ptr<device_buffer> local_key_buffer;\r
                std::shared_ptr<device_buffer> local_mix_buffer;\r
                                \r
-               if(layer.first != blend_mode::normal)\r
+               if(layer.first.mode != blend_mode::normal || layer.first.chroma.key != chroma::none)
                {\r
                        auto layer_draw_buffer = create_mixer_buffer(4, format_desc);\r
 \r
@@ -159,7 +159,7 @@ private:
                        BOOST_FOREACH(auto& item, layer.second)         \r
                                draw_item(std::move(item), draw_buffer, layer_key_buffer, local_key_buffer, local_mix_buffer, format_desc);             \r
                                        \r
-                       draw_mixer_buffer(draw_buffer, std::move(local_mix_buffer), blend_mode::normal);\r
+                       draw_mixer_buffer(draw_buffer, std::move(local_mix_buffer), layer.first);
                }                                       \r
 \r
                layer_key_buffer = std::move(local_key_buffer);\r
@@ -213,7 +213,7 @@ private:
 \r
        void draw_mixer_buffer(safe_ptr<device_buffer>&                 draw_buffer, \r
                                                   std::shared_ptr<device_buffer>&& source_buffer, \r
-                                                  blend_mode::type                                     blend_mode = blend_mode::normal)\r
+                                                  blend_mode                                   blend_mode = blend_mode::normal)
        {\r
                if(!source_buffer)\r
                        return;\r
@@ -251,7 +251,7 @@ public:
        {\r
        }\r
 \r
-       void begin_layer(blend_mode::type blend_mode)\r
+       void begin_layer(blend_mode blend_mode)
        {\r
                layers_.push_back(std::make_pair(blend_mode, std::vector<item>()));\r
        }\r
@@ -291,7 +291,7 @@ void image_mixer::begin(basic_frame& frame){impl_->begin(frame);}
 void image_mixer::visit(write_frame& frame){impl_->visit(frame);}\r
 void image_mixer::end(){impl_->end();}\r
 boost::unique_future<safe_ptr<host_buffer>> image_mixer::operator()(const video_format_desc& format_desc){return impl_->render(format_desc);}\r
-void image_mixer::begin_layer(blend_mode::type blend_mode){impl_->begin_layer(blend_mode);}\r
+void image_mixer::begin_layer(blend_mode blend_mode){impl_->begin_layer(blend_mode);}
 void image_mixer::end_layer(){impl_->end_layer();}\r
 \r
 }}
\ No newline at end of file
index 664d7c2377639faac2f84d056acf81173964a5e3..783ef953be80fec7c27be0af0265b556db5ef422 100644 (file)
@@ -48,7 +48,7 @@ public:
        virtual void visit(core::write_frame& frame);\r
        virtual void end();\r
 \r
-       void begin_layer(blend_mode::type blend_mode);\r
+       void begin_layer(blend_mode blend_mode);
        void end_layer();\r
                \r
        boost::unique_future<safe_ptr<host_buffer>> operator()(const video_format_desc& format_desc);\r
index aab2ae4faf92bb31f60f44fc98e2d9d62b5dbd74..132fb716f01e0de003ec2bc15ffdabd835329ed5 100644 (file)
@@ -267,4 +267,55 @@ static std::string get_blend_glsl()
                ;\r
                                                                                                                                                                                                                                                                                                                                                                                                                                                   \r
                return glsl;\r
-}
\ No newline at end of file
+}
+
+static std::string get_chroma_glsl()
+{
+    static std::string glsl =
+        "// Chroma keying                                                       \n"
+        "// Author: Tim Eves <timseves@googlemail.com>                          \n"
+        "//                                                                     \n"
+        "// This implements the Chroma key algorithm described in the paper:    \n"
+        "//      'Software Chroma Keying in an Imersive Virtual Environment'    \n"
+        "//      by F. van den Bergh & V. Lalioti                               \n"
+        "// but as a pixel shader algorithm.                                    \n"
+        "//                                                                     \n"
+        "                                                                       \n"
+        "float       chroma_blend_w = chroma_blend.y - chroma_blend.x;          \n"
+        "const vec4  grey_xfer  = vec4(0.3, 0.59, 0.11, 0.0);                   \n"
+        "                                                                       \n"
+        "float fma(float a, float b, float c) { return a*b + c; }               \n"
+        "                                                                       \n"
+        "// This allows us to implement the paper's alphaMap curve in software  \n"
+        "// rather than a largeish array                                        \n"
+        "float alpha_map(float d)                                               \n"
+        "{                                                                      \n"
+        "    return 1.0-smoothstep(chroma_blend.x, chroma_blend.y, d);          \n"
+        "}                                                                      \n"
+        "                                                                       \n"
+        "vec4 supress_spill(vec4 c, float d)                                    \n"
+        "{                                                                      \n"
+        "    float ds = smoothstep(chroma_spill, 1.0, d/chroma_blend.y);        \n"
+        "    float gl = dot(grey_xfer, c);                                      \n"
+        "    return mix(c, vec4(vec3(gl*gl), gl), ds);                          \n"
+        "}                                                                      \n"
+        "                                                                       \n"
+        "// Key on green                                                        \n"
+        "vec4 ChromaOnGreen(vec4 c)                                             \n"
+        "{                                                                      \n"
+        "    float d = fma(2.0, c.g, -c.r - c.b)/2.0;                           \n"
+        "    c *= alpha_map(d);                                                 \n"
+        "    return supress_spill(c, d);                                        \n"
+        "}                                                                      \n"
+        "                                                                       \n"
+        "//Key on blue                                                          \n"
+        "vec4 ChromaOnBlue(vec4 c)                                              \n"
+        "{                                                                      \n"
+        "    float d = fma(2.0, c.b, -c.r - c.g)/2.0;                           \n"
+        "    c *= alpha_map(d);                                                 \n"
+        "    return supress_spill(c, d);                                        \n"
+        "}                                                                      \n"
+       ;
+
+        return glsl;
+}
index 28550b39a4b45f4b7701b41517286b08ded03dff..1ef8026d1e03d4fe247008f7dd8d30a58b27a83f 100644 (file)
@@ -127,6 +127,24 @@ std::string get_vertex()
        "}                                                                                                                                                                      \n";\r
 }\r
 \r
+std::string get_chroma_func()
+{
+        return get_chroma_glsl()
+
+        +
+
+               "vec4 chroma_key(vec4 c)                                                \n"
+               "{                                                                      \n"
+               "   switch (chroma_mode)                                                \n"
+               "   {                                                                   \n"
+               "   case 0: return c;                                                   \n"
+               "   case 1: return ChromaOnGreen(c.bgra).bgra;                          \n"
+               "   case 2: return ChromaOnBlue(c.bgra).bgra;                           \n"
+               "   }                                                                   \n"
+               "   return c;                                                           \n"
+               "}                                                                      \n";
+}
+
 std::string get_fragment(bool blend_modes)\r
 {\r
        return\r
@@ -157,11 +175,19 @@ std::string get_fragment(bool blend_modes)
        "uniform float          sat;                                                                                                                    \n"\r
        "uniform float          con;                                                                                                                    \n"\r
        "                                                                                                                                                                       \n"     \r
+    "uniform int        chroma_mode;                                                    \n"
+    "uniform vec2       chroma_blend;                                                   \n"
+    "uniform float      chroma_spill;                                                   \n"
 \r
        +\r
                \r
        (blend_modes ? get_blend_color_func() : get_simple_blend_color_func())\r
 \r
+
+    +
+
+    get_chroma_func()
+
        +\r
        \r
        "                                                                                                                                                                       \n"\r
@@ -234,6 +260,7 @@ std::string get_fragment(bool blend_modes)
        "void main()                                                                                                                                            \n"\r
        "{                                                                                                                                                                      \n"\r
        "       vec4 color = get_rgba_color();                                                                                                  \n"\r
+       "   color = chroma_key(color);                                                      \n"
        "   if(levels)                                                                                                                                          \n"\r
        "               color.rgb = LevelsControl(color.rgb, min_input, max_input, gamma, min_output, max_output); \n"\r
        "       if(csb)                                                                                                                                                 \n"\r
index 11dc499b19375ae6f4d3b37e133601e7b287a7ae..582bfb47bfe3824b0166a8eda218f548ece7aaaf 100644 (file)
@@ -23,6 +23,8 @@
 \r
 #include <common/memory/safe_ptr.h>\r
 \r
+#define SHADER_PROGRAM(prog)    #prog
+
 namespace caspar { namespace core {\r
 \r
 class shader;\r
index 52974744db2b79eca732fa72f88d1d3a4b2dcd0c..20ff470d61d15518a7e84cc93c71abd79357bfab 100644 (file)
@@ -70,7 +70,7 @@ struct mixer::implementation : boost::noncopyable
        audio_mixer     audio_mixer_;\r
        image_mixer image_mixer_;\r
        \r
-       std::unordered_map<int, blend_mode::type> blend_modes_;\r
+       std::unordered_map<int, blend_mode> blend_modes_;\r
                        \r
        executor executor_;\r
 \r
@@ -136,7 +136,7 @@ public:
        {\r
                executor_.begin_invoke([=]\r
                {\r
-                       blend_modes_[index] = value;\r
+                       blend_modes_[index].mode = value;\r
                }, high_priority);\r
        }\r
 \r
@@ -156,6 +156,14 @@ public:
                }, high_priority);\r
        }\r
 \r
+    void set_chroma(int index, const chroma & value)\r
+    {\r
+        executor_.begin_invoke([=]\r
+        {\r
+            blend_modes_[index].chroma = value;\r
+        }, high_priority);\r
+    }\r
+\r
        void set_master_volume(float volume)\r
        {\r
                executor_.begin_invoke([=]\r
@@ -193,6 +201,7 @@ void mixer::send(const std::pair<std::map<int, safe_ptr<core::basic_frame>>, std
 core::video_format_desc mixer::get_video_format_desc() const { return impl_->get_video_format_desc(); }\r
 safe_ptr<core::write_frame> mixer::create_frame(const void* tag, const core::pixel_format_desc& desc, const channel_layout& audio_channel_layout){ return impl_->create_frame(tag, desc, audio_channel_layout); }              \r
 void mixer::set_blend_mode(int index, blend_mode::type value){impl_->set_blend_mode(index, value);}\r
+void mixer::set_chroma(int index, const chroma & value){impl_->set_chroma(index, value);}\r
 void mixer::clear_blend_mode(int index) { impl_->clear_blend_mode(index); }\r
 void mixer::clear_blend_modes() { impl_->clear_blend_modes(); }\r
 void mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); }\r
index bd0420d3c8e81d4daa2405eec97a817f767ef96c..3fd32a808728b8c578fb93b418e1f2b212cd3e8d 100644 (file)
@@ -67,7 +67,8 @@ public:
        core::video_format_desc get_video_format_desc() const; // nothrow\r
        void set_video_format_desc(const video_format_desc& format_desc);\r
        \r
-       void set_blend_mode(int index, blend_mode::type value);\r
+       void set_blend_mode(int index, blend_mode::type value);
+    void set_chroma(int index, const chroma & value);
        void clear_blend_mode(int index);\r
        void clear_blend_modes();\r
 \r
index 4532affe88702d9901bd944e68b6ae03ca0ae4a2..3a8470c470c3a787be164310507567870acf023d 100644 (file)
@@ -66,6 +66,7 @@ frame_transform& frame_transform::operator*=(const frame_transform &other)
        field_mode                               = static_cast<field_mode::type>(field_mode & other.field_mode);\r
        is_key                                  |= other.is_key;\r
        is_mix                                  |= other.is_mix;\r
+
        return *this;\r
 }\r
 \r
@@ -103,7 +104,6 @@ frame_transform tween(double time, const frame_transform& source, const frame_tr
        result.field_mode                       = static_cast<field_mode::type>(source.field_mode & dest.field_mode);\r
        result.is_key                           = source.is_key | dest.is_key;\r
        result.is_mix                           = source.is_mix | dest.is_mix;\r
-       \r
        return result;\r
 }\r
 \r
index e2547b7e458eb18a84acf4146758058e7c5b558a..363df57172c6699502e39919ee80dac67cbccf26 100644 (file)
@@ -79,4 +79,4 @@ bool operator<(const frame_transform& lhs, const frame_transform& rhs);
 bool operator==(const frame_transform& lhs, const frame_transform& rhs);\r
 bool operator!=(const frame_transform& lhs, const frame_transform& rhs);\r
 \r
-}}
\ No newline at end of file
+}}
index 8b27a5777cfecb4b28bd0aae2ecb7b1bd100d486..68a9dd074fdf3a5416ce4c0b2770203d47678606 100644 (file)
 \r
 /* Return codes\r
 \r
-100 [action]                   Information om att något har hänt  \r
-101 [action]                   Information om att något har hänt, en rad data skickas  \r
+100 [action]                   Information om att n�got har h�nt  \r
+101 [action]                   Information om att n�got har h�nt, en rad data skickas  \r
 \r
-202 [kommando] OK              Kommandot har utförts  \r
-201 [kommando] OK              Kommandot har utförts, och en rad data skickas tillbaka  \r
-200 [kommando] OK              Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad  \r
+202 [kommando] OK              Kommandot har utfrts  \r
+201 [kommando] OK              Kommandot har utfrts, och en rad data skickas tillbaka  \r
+200 [kommando] OK              Kommandot har utfrts, och flera rader data skickas tillbaka. Avslutas med tomrad  \r
 \r
-400 ERROR                              Kommandot kunde inte förstås  \r
+400 ERROR                              Kommandot kunde inte f�rst�s  \r
 401 [kommando] ERROR   Ogiltig kanal  \r
 402 [kommando] ERROR   Parameter saknas  \r
 403 [kommando] ERROR   Ogiltig parameter  \r
 \r
 500 FAILED                             Internt configurationfel  \r
 501 [kommando] FAILED  Internt configurationfel  \r
-502 [kommando] FAILED  Oläslig mediafil  \r
+502 [kommando] FAILED  Olslig mediafil  \r
 \r
 600 [kommando] FAILED  funktion ej implementerad\r
 */\r
@@ -528,8 +528,21 @@ bool MixerCommand::DoExecute()
                {\r
                        auto blend_str = _parameters.at(1);                                                             \r
                        int layer = GetLayerIndex();\r
-                       GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str));      \r
+                       blend_mode::type && blend = get_blend_mode(blend_str);\r
+                       GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), blend);  \r
                }\r
+        else if(_parameters[0] == L"CHROMA")\r
+        {\r
+            int layer = GetLayerIndex();\r
+            chroma  chroma;\r
+            chroma.key          = get_chroma_mode(_parameters[1]);\r
+            chroma.threshold    = boost::lexical_cast<double>(_parameters[2]);\r
+            chroma.softness     = boost::lexical_cast<double>(_parameters[3]);\r
+            chroma.spill        = _parameters.size() > 4 ? boost::lexical_cast<double>(_parameters[4]) : 0.0f;\r
+            chroma.blur         = _parameters.size() > 5 ? boost::lexical_cast<double>(_parameters[5]) : 0.0f;\r
+            chroma.show_mask    = _parameters.size() > 6 ? bool(boost::lexical_cast<int>(_parameters[6])) : false;\r
+            GetChannel()->mixer()->set_chroma(GetLayerIndex(), chroma);\r
+        }\r
                else if(_parameters[0] == L"MASTERVOLUME")\r
                {\r
                        float master_volume = boost::lexical_cast<float>(_parameters.at(1));\r
@@ -1834,4 +1847,4 @@ bool RestartCommand::DoExecute()
 }\r
 \r
 }      //namespace amcp\r
-}}     //namespace caspar
\ No newline at end of file
+}}     //namespace caspar\r