]> git.sesse.net Git - casparcg/commitdiff
Merge pull request #501 from dimitry-ishenko-casparcg/next
authorHellGore <helge.norberg@gmail.com>
Fri, 27 Jan 2017 13:21:04 +0000 (14:21 +0100)
committerGitHub <noreply@github.com>
Fri, 27 Jan 2017 13:21:04 +0000 (14:21 +0100)
Add support for IN and OUT parameters in PLAY, CALL and SEEK commands

27 files changed:
CHANGELOG
accelerator/ogl/image/blending_glsl.h
accelerator/ogl/image/image_kernel.cpp
accelerator/ogl/image/image_shader.cpp
build-scripts/build-windows.bat
common/filesystem.cpp
core/frame/frame_transform.cpp
core/frame/frame_transform.h
core/producer/framerate/framerate_producer.cpp
core/producer/scene/scene_producer.cpp
core/producer/scene/scene_producer.h
core/producer/scene/xml_scene_producer.cpp
deploy/linux/run.sh
modules/decklink/consumer/decklink_consumer.cpp
modules/ffmpeg/consumer/ffmpeg_consumer.cpp
modules/ffmpeg/producer/ffmpeg_producer.cpp
modules/ffmpeg/producer/video/video_decoder.cpp
modules/ffmpeg/producer/video/video_decoder.h
modules/html/producer/html_producer.cpp
modules/image/producer/image_producer.cpp
protocol/amcp/AMCPCommand.h
protocol/amcp/AMCPCommandsImpl.cpp
protocol/amcp/AMCPProtocolStrategy.cpp
protocol/amcp/amcp_command_repository.cpp
protocol/amcp/amcp_command_repository.h
shell/casparcg.config
shell/main.cpp

index 749f02d6fdade2c8a7ec372ed633999e79e95bdc..b62b3394eeadd2ceb7a2b578a47e37d58d3b6d70 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -17,6 +17,24 @@ General
     against INFO THREADS, ps and top.\r
   o Created automatically generated build number, so that it is easier to see\r
     whether a build is newer or older than an other.\r
+  o Changed configuration element mipmapping_default_on to mipmapping-default-on\r
+    for consistency with the rest of the configuration (Jesper Stærkær).\r
+  o Handle stdin EOF as EXIT.\r
+  o Added support for RESTART in Linux startup script run.sh.\r
+  o Copy casparcg_auto_restart.bat into Windows releases.\r
+  o Fixed bug with thumbnail generation when there are .-files in the media\r
+    folder.\r
+\r
+Consumers\r
+---------\r
+\r
+  o FFmpeg consumer:\r
+    + Fixed long overdue bug where HD material was always recorded using the\r
+      BT.601 color matrix instead of the BT.709 color matrix. RGB codecs like\r
+      qtrle was never affected but all the YCbCr based codecs were.\r
+    + Fixed bug in parsing of paths containing -.\r
+  o DeckLink consumer:\r
+    + Fixed possible dead-lock in frame queue.\r
 \r
 Producers\r
 ---------\r
@@ -24,12 +42,39 @@ Producers
   o FFmpeg producer:\r
     + Increased the max number of frames that audio/video can be badly\r
       interleaved with (Dimitry Ishenko).\r
+    + Fixed bug where decoders sometimes requires more than one video packet to\r
+      decode the first frame.\r
+  o Framerate producer:\r
+    + Fixed bug when INFO was used on a not yet playing framerate producer.\r
+  o HTML producer:\r
+    + Fixed bug where only URL:s with . in them where recognized.\r
+  o Image producer:\r
+    + Added LENGTH parameter to allow for queueing with LOADBG AUTO.\r
+\r
+Mixer\r
+-----\r
+\r
+  o Fixed bug in the contrast/saturation/brightness code where the wrong luma\r
+    coefficients was used.\r
+  o Rewrote the chroma key code to support variable hue, instead of fixed green\r
+    or blue. Threshold setting was removed in favour of separate hue width,\r
+    minimum saturation and minimum brightness constraints.\r
 \r
 AMCP\r
 ----\r
 \r
   o INFO PATHS now adds all the path elements even if they are using the default\r
     values.\r
+  o MIXER CHROMA syntax deprecated (still supported) in favour of the more\r
+    advanced syntax required by the rewritten chroma key code.\r
+  o Added special command REQ that can be prepended before any command to\r
+    identify the response with a client specified request id, allowing a client\r
+    to know exactly what asynchronous response matched a specific request.\r
+  o Added support for listing contents of a specific directory for CLS, TLS,\r
+    DATA LIST and THUMBNAIL LIST.\r
+  o Fixed bug where CINF only returned the first match.\r
+\r
+\r
 \r
 CasparCG 2.1.0 Beta 1 (w.r.t 2.0.7 Stable)\r
 ==========================================\r
index 42e95f37f83a9775a76369955d587ce49dc3c459..7f3429d7f68dfa4435d56b4cb22519b1b9ed3bfd 100644 (file)
@@ -36,7 +36,9 @@ static std::string get_adjustement_glsl()
                                const float AvgLumG = 0.5;
                                const float AvgLumB = 0.5;
 
-                               const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
+                               vec3 LumCoeff = is_hd
+                                               ? vec3(0.0722, 0.7152, 0.2126)
+                                               : vec3(0.114, 0.587, 0.299);
 
                                vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
                                vec3 brtColor = color * brt;
@@ -279,40 +281,70 @@ static std::string get_chroma_glsl()
                //      by F. van den Bergh & V. Lalioti
                // but as a pixel shader algorithm.
                //
-
-               float       chroma_blend_w = chroma_blend.y - chroma_blend.x;
-               const vec4  grey_xfer  = vec4(0.3, 0.59, 0.11, 0.0);
-
-               float fma(float a, float b, float c) { return a*b + c; }
+               vec4  grey_xfer  = is_hd
+                               ? vec4(0.2126, 0.7152, 0.0722, 0)
+                               : vec4(0.299,  0.587,  0.114, 0);
 
                // This allows us to implement the paper's alphaMap curve in software
                // rather than a largeish array
                float alpha_map(float d)
                {
-                   return 1.0-smoothstep(chroma_blend.x, chroma_blend.y, d);
+                   return 1.0 - smoothstep(1.0, chroma_softness, d);
                }
 
                vec4 supress_spill(vec4 c, float d)
                {
-                   float ds = smoothstep(chroma_spill, 1.0, d/chroma_blend.y);
+                       float ds        = smoothstep(chroma_spill, clamp(chroma_spill + 0.2, 0, 1), d / chroma_softness);
                    float gl = dot(grey_xfer, c);
-                   return mix(c, vec4(vec3(gl*gl), gl), ds);
+                   return mix(c, vec4(vec3(pow(gl, chroma_spill_darken)), gl), ds);
+               }
+
+               // http://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl
+               vec3 rgb2hsv(vec3 c)
+               {
+                       vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
+                       vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
+                       vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
+
+                       float d = q.x - min(q.w, q.y);
+                       float e = 1.0e-10;
+                       return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+               }
+
+               float AngleDiff(float angle1, float angle2)
+               {
+                       return 0.5 - abs(abs(angle1 - angle2) - 0.5);
+               }
+
+               float Distance(float actual, float target)
+               {
+                       return min(0.0, target - actual);
                }
 
-               // Key on green
-               vec4 ChromaOnGreen(vec4 c)
+               float ColorDistance(vec3 hsv)
                {
-                   float d = fma(2.0, c.g, -c.r - c.b)/2.0;
-                   c *= alpha_map(d);
-                   return supress_spill(c, d);
+                       float hueDiff                                   = AngleDiff(hsv.x, chroma_target_hue) * 2;
+                       float saturationDiff                    = Distance(hsv.y, chroma_min_saturation);
+                       float brightnessDiff                    = Distance(hsv.z, chroma_min_brightness);
+
+                       float saturationBrightnessScore = max(brightnessDiff, saturationDiff);
+                       float hueScore                                  = hueDiff - chroma_hue_width;
+
+                       return -hueScore * saturationBrightnessScore;
                }
 
-               //Key on blue
-               vec4 ChromaOnBlue(vec4 c)
+               // Key on any color
+               vec4 ChromaOnCustomColor(vec4 c)
                {
-                   float d = fma(2.0, c.b, -c.r - c.g)/2.0;
-                   c *= alpha_map(d);
-                   return supress_spill(c, d);
+                       vec3 hsv                = rgb2hsv(c.rgb);
+                       float distance  = ColorDistance(hsv);
+                       float d                 = distance * -2.0 + 1.0;
+                   vec4 suppressed     = supress_spill(c.rgba, d).rgba;
+                       float alpha             = alpha_map(d);
+
+                       suppressed *= alpha;
+
+                       return chroma_show_mask ? vec4(suppressed.a, suppressed.a, suppressed.a, 1) : suppressed;
                }
        )shader";
 
index b10accd133e0ecfe1778e6cce55bfcbd77567528..37bb44ff7552bab1d0f240de2cba5f1c49a54f46 100644 (file)
@@ -123,15 +123,15 @@ GLubyte upper_pattern[] = {
        0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00};
-               
+
 GLubyte lower_pattern[] = {
-       0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 
+       0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff};
 
 struct image_kernel::impl
-{      
+{
        spl::shared_ptr<device> ogl_;
        spl::shared_ptr<shader> shader_;
        bool                                    blend_modes_;
@@ -148,8 +148,8 @@ struct image_kernel::impl
 
        void draw(draw_params params)
        {
-               static const double epsilon = 0.001;            
-               
+               static const double epsilon = 0.001;
+
                CASPAR_ASSERT(params.pix_desc.planes.size() == params.textures.size());
 
                if(params.textures.empty() || !params.background)
@@ -240,12 +240,12 @@ struct image_kernel::impl
 
                if(params.local_key)
                        params.local_key->bind(static_cast<int>(texture_id::local_key));
-               
+
                if(params.layer_key)
                        params.layer_key->bind(static_cast<int>(texture_id::layer_key));
-                       
+
                // Setup shader
-                                                               
+
                shader_->use();
 
                shader_->set("post_processing", false);
@@ -254,8 +254,8 @@ struct image_kernel::impl
                shader_->set("plane[2]",                texture_id::plane2);
                shader_->set("plane[3]",                texture_id::plane3);
                for (int n = 0; n < params.textures.size(); ++n)
-                       shader_->set("plane_size[" + boost::lexical_cast<std::string>(n) + "]", 
-                                                static_cast<float>(params.textures[n]->width()), 
+                       shader_->set("plane_size[" + boost::lexical_cast<std::string>(n) + "]",
+                                                static_cast<float>(params.textures[n]->width()),
                                                 static_cast<float>(params.textures[n]->height()));
 
                shader_->set("local_key",               texture_id::local_key);
@@ -263,22 +263,28 @@ struct image_kernel::impl
                shader_->set("is_hd",                   params.pix_desc.planes.at(0).height > 700 ? 1 : 0);
                shader_->set("has_local_key",   static_cast<bool>(params.local_key));
                shader_->set("has_layer_key",   static_cast<bool>(params.layer_key));
-               shader_->set("pixel_format",    params.pix_desc.format);
-               shader_->set("opacity",                 params.transform.is_key ? 1.0 : params.transform.opacity);      
+               shader_->set("pixel_format",            params.pix_desc.format);
+               shader_->set("opacity",                 params.transform.is_key ? 1.0 : params.transform.opacity);
 
-               if (params.transform.chroma.key != core::chroma::type::none)
+               if (params.transform.chroma.enable)
                {
-                       shader_->set("chroma", true);
-                       shader_->set("chroma_mode", static_cast<int>(params.transform.chroma.key));
-                       shader_->set("chroma_blend", params.transform.chroma.threshold, params.transform.chroma.softness);
-                       shader_->set("chroma_spill", params.transform.chroma.spill);
+                       shader_->set("chroma",                                          true);
+
+                       shader_->set("chroma_show_mask",                        params.transform.chroma.show_mask);
+                       shader_->set("chroma_target_hue",               params.transform.chroma.target_hue / 360.0);
+                       shader_->set("chroma_hue_width",                        params.transform.chroma.hue_width);
+                       shader_->set("chroma_min_saturation",   params.transform.chroma.min_saturation);
+                       shader_->set("chroma_min_brightness",   params.transform.chroma.min_brightness);
+                       shader_->set("chroma_softness",                 1.0 + params.transform.chroma.softness);
+                       shader_->set("chroma_spill",                            params.transform.chroma.spill);
+                       shader_->set("chroma_spill_darken",             params.transform.chroma.spill_darken);
                }
                else
                        shader_->set("chroma", false);
 
 
                // Setup blend_func
-               
+
                if(params.transform.is_key)
                        params.blend_mode = core::blend_mode::normal;
 
@@ -297,48 +303,48 @@ struct image_kernel::impl
                        switch(params.keyer)
                        {
                        case keyer::additive:
-                               GL(glBlendFunc(GL_ONE, GL_ONE));        
+                               GL(glBlendFunc(GL_ONE, GL_ONE));
                                break;
                        case keyer::linear:
-                       default:                        
+                       default:
                                GL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
-                       }               
+                       }
                }
 
                // Setup image-adjustements
-               
+
                if (params.transform.levels.min_input  > epsilon                ||
                        params.transform.levels.max_input  < 1.0-epsilon        ||
                        params.transform.levels.min_output > epsilon            ||
                        params.transform.levels.max_output < 1.0-epsilon        ||
                        std::abs(params.transform.levels.gamma - 1.0) > epsilon)
                {
-                       shader_->set("levels", true);   
-                       shader_->set("min_input",       params.transform.levels.min_input);     
+                       shader_->set("levels", true);
+                       shader_->set("min_input",       params.transform.levels.min_input);
                        shader_->set("max_input",       params.transform.levels.max_input);
                        shader_->set("min_output",      params.transform.levels.min_output);
                        shader_->set("max_output",      params.transform.levels.max_output);
                        shader_->set("gamma",           params.transform.levels.gamma);
                }
                else
-                       shader_->set("levels", false);  
+                       shader_->set("levels", false);
 
                if (std::abs(params.transform.brightness - 1.0) > epsilon ||
                        std::abs(params.transform.saturation - 1.0) > epsilon ||
                        std::abs(params.transform.contrast - 1.0)   > epsilon)
                {
-                       shader_->set("csb",     true);  
-                       
-                       shader_->set("brt", params.transform.brightness);       
+                       shader_->set("csb",     true);
+
+                       shader_->set("brt", params.transform.brightness);
                        shader_->set("sat", params.transform.saturation);
                        shader_->set("con", params.transform.contrast);
                }
                else
-                       shader_->set("csb",     false); 
-               
+                       shader_->set("csb",     false);
+
                // Setup interlacing
-               
-               if (params.transform.field_mode != core::field_mode::progressive)       
+
+               if (params.transform.field_mode != core::field_mode::progressive)
                {
                        GL(glEnable(GL_POLYGON_STIPPLE));
 
@@ -349,10 +355,10 @@ struct image_kernel::impl
                }
 
                // Setup drawing area
-               
+
                GL(glViewport(0, 0, params.background->width(), params.background->height()));
                glDisable(GL_DEPTH_TEST);
-                                                                               
+
                auto m_p = params.transform.clip_translation;
                auto m_s = params.transform.clip_scale;
 
@@ -363,18 +369,18 @@ struct image_kernel::impl
                {
                        double w = static_cast<double>(params.background->width());
                        double h = static_cast<double>(params.background->height());
-               
+
                        GL(glEnable(GL_SCISSOR_TEST));
                        glScissor(static_cast<int>(m_p[0] * w), static_cast<int>(m_p[1] * h), std::max(0, static_cast<int>(m_s[0] * w)), std::max(0, static_cast<int>(m_s[1] * h)));
                }
 
                // Synchronize and set render target
-                                                               
+
                if (blend_modes_)
                {
                        // http://www.opengl.org/registry/specs/NV/texture_barrier.txt
                        // This allows us to use framebuffer (background) both as source and target while blending.
-                       glTextureBarrierNV(); 
+                       glTextureBarrierNV();
                }
 
                params.background->attach();
@@ -451,7 +457,7 @@ struct image_kernel::impl
                default:
                        break;
                }
-               
+
                // Cleanup
                GL(glDisable(GL_SCISSOR_TEST));
                GL(glDisable(GL_POLYGON_STIPPLE));
index dfc47a0b2ef6675e79eb1e68129210ebc5032c9c..c4a5ecdc3519fe3d76fb22b699c2faf761ca9b60 100644 (file)
@@ -103,7 +103,7 @@ std::string get_blend_color_func()
                                }
                )shader";
 }
-               
+
 std::string get_simple_blend_color_func()
 {
        return
@@ -125,20 +125,13 @@ std::string get_chroma_func()
        return
 
                get_chroma_glsl()
-               
+
                +
-               
+
                R"shader(
                                vec4 chroma_key(vec4 c)
                                {
-                                       switch (chroma_mode)
-                                       {
-                                       case 0: return c;
-                                       case 1: return ChromaOnGreen(c.bgra).bgra;
-                                       case 2: return ChromaOnBlue(c.bgra).bgra;
-                                       }
-
-                                       return c;
+                                       return ChromaOnCustomColor(c.bgra).bgra;
                                }
                )shader";
 }
@@ -211,9 +204,14 @@ std::string get_fragment(bool blend_modes, bool post_processing)
                        uniform bool            straighten_alpha;
 
                        uniform bool            chroma;
-                       uniform int                     chroma_mode;
-                       uniform vec2            chroma_blend;
+                       uniform bool            chroma_show_mask;
+                       uniform float           chroma_target_hue;
+                       uniform float           chroma_hue_width;
+                       uniform float           chroma_min_saturation;
+                       uniform float           chroma_min_brightness;
+                       uniform float           chroma_softness;
                        uniform float           chroma_spill;
+                       uniform float           chroma_spill_darken;
        )shader"
 
        +
@@ -319,7 +317,7 @@ std::string get_fragment(bool blend_modes, bool post_processing)
                                        color.rgb /= color.a + 0.0000001;
 
                                return color;
-                       }       
+                       }
 
                        void main()
                        {
@@ -383,9 +381,9 @@ std::shared_ptr<shader> get_image_shader(
                                delete p;
                        });
        };
-               
+
        try
-       {                               
+       {
                g_blend_modes  = glTextureBarrierNV ? blend_modes_wanted : false;
                g_post_processing = straight_alpha_wanted;
                existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
@@ -394,7 +392,7 @@ std::shared_ptr<shader> get_image_shader(
        {
                CASPAR_LOG_CURRENT_EXCEPTION();
                CASPAR_LOG(warning) << "Failed to compile shader. Trying to compile without blend-modes.";
-                               
+
                g_blend_modes = false;
                existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
        }
index 8830d34a9ebfcac38a4be0951bf6f91c10323d80..a1417e442daac6c06cf8f25a873dd7c8baa67cb8 100644 (file)
@@ -45,6 +45,7 @@ echo Copying binaries...
 copy shell\*.dll "%SERVER_FOLDER%\Server" || goto :error
 copy shell\RelWithDebInfo\casparcg.exe "%SERVER_FOLDER%\Server" || goto :error
 copy shell\RelWithDebInfo\casparcg.pdb "%SERVER_FOLDER%\Server" || goto :error
+copy ..\shell\casparcg_auto_restart.bat "%SERVER_FOLDER%\Server" || goto :error
 copy shell\casparcg.config "%SERVER_FOLDER%\Server" || goto :error
 copy shell\*.ttf "%SERVER_FOLDER%\Server" || goto :error
 copy shell\*.pak "%SERVER_FOLDER%\Server" || goto :error
index 41869ad0427a48c9268a387824bf60be23001aa3..877ac7ca68f6c8310d2488d1c83ad16bf2549d7f 100644 (file)
@@ -22,6 +22,7 @@
 #include "stdafx.h"
 
 #include "filesystem.h"
+#include "except.h"
 
 #include <boost/filesystem/operations.hpp>
 #include <boost/filesystem/path.hpp>
@@ -32,9 +33,11 @@ boost::filesystem::path get_relative(
                const boost::filesystem::path& file,
                const boost::filesystem::path& relative_to)
 {
-       auto result = file.filename();
+       auto result                     = file.filename();
+       auto current_path       = file;
 
-       boost::filesystem::path current_path = file;
+       if (boost::filesystem::equivalent(current_path, relative_to))
+               return L"";
 
        while (true)
        {
@@ -44,7 +47,7 @@ boost::filesystem::path get_relative(
                        break;
 
                if (current_path.empty())
-                       throw std::runtime_error("File not relative to folder");
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("File " + file.string() + " not relative to folder " + relative_to.string()));
 
                result = current_path.filename() / result;
        }
index 4ba1899202eea6d4daca30d18753c03145785d6f..393214b69aa1326a3c6f7f6d6f27fc30ee770fca 100644 (file)
@@ -53,7 +53,7 @@ void transform_corners(corners& self, const corners& other)
 
 image_transform& image_transform::operator*=(const image_transform &other)
 {
-       opacity                                 *= other.opacity;       
+       opacity                                 *= other.opacity;
        brightness                              *= other.brightness;
        contrast                                *= other.contrast;
        saturation                              *= other.saturation;
@@ -87,14 +87,19 @@ image_transform& image_transform::operator*=(const image_transform &other)
        transform_corners(perspective, other.perspective);
 
        levels.min_input                 = std::max(levels.min_input,  other.levels.min_input);
-       levels.max_input                 = std::min(levels.max_input,  other.levels.max_input); 
+       levels.max_input                 = std::min(levels.max_input,  other.levels.max_input);
        levels.min_output                = std::max(levels.min_output, other.levels.min_output);
        levels.max_output                = std::min(levels.max_output, other.levels.max_output);
        levels.gamma                    *= other.levels.gamma;
-       chroma.key                               = std::max(chroma.key, other.chroma.key);
-       chroma.threshold                += other.chroma.threshold;
-       chroma.softness                 += other.chroma.softness;
-       chroma.spill                    += other.chroma.spill;
+       chroma.enable                   |= other.chroma.enable;
+       chroma.show_mask                |= other.chroma.show_mask;
+       chroma.target_hue                = std::max(other.chroma.target_hue, chroma.target_hue);
+       chroma.min_saturation    = std::max(other.chroma.min_saturation, chroma.min_saturation);
+       chroma.min_brightness    = std::max(other.chroma.min_brightness, chroma.min_brightness);
+       chroma.hue_width                 = std::max(other.chroma.hue_width, chroma.hue_width);
+       chroma.softness                  = std::max(other.chroma.softness, chroma.softness);
+       chroma.spill                     = std::min(other.chroma.spill, chroma.spill);
+       chroma.spill_darken              = std::max(other.chroma.spill_darken, chroma.spill_darken);
        field_mode                               = field_mode & other.field_mode;
        is_key                                  |= other.is_key;
        is_mix                                  |= other.is_mix;
@@ -137,39 +142,44 @@ void do_tween_corners(const corners& source, const corners& dest, corners& out,
 
 image_transform image_transform::tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener& tween)
 {
-       image_transform result; 
-
-       result.brightness                       = do_tween(time, source.brightness,                             dest.brightness,                        duration, tween);
-       result.contrast                         = do_tween(time, source.contrast,                               dest.contrast,                          duration, tween);
-       result.saturation                       = do_tween(time, source.saturation,                             dest.saturation,                        duration, tween);
-       result.opacity                          = do_tween(time, source.opacity,                                dest.opacity,                           duration, tween);
-       result.anchor[0]                        = do_tween(time, source.anchor[0],                              dest.anchor[0],                         duration, tween);
-       result.anchor[1]                        = do_tween(time, source.anchor[1],                              dest.anchor[1],                         duration, tween);
-       result.fill_translation[0]      = do_tween(time, source.fill_translation[0],    dest.fill_translation[0],       duration, tween);
-       result.fill_translation[1]      = do_tween(time, source.fill_translation[1],    dest.fill_translation[1],       duration, tween);
-       result.fill_scale[0]            = do_tween(time, source.fill_scale[0],                  dest.fill_scale[0],                     duration, tween);
-       result.fill_scale[1]            = do_tween(time, source.fill_scale[1],                  dest.fill_scale[1],                     duration, tween);
-       result.clip_translation[0]      = do_tween(time, source.clip_translation[0],    dest.clip_translation[0],       duration, tween);
-       result.clip_translation[1]      = do_tween(time, source.clip_translation[1],    dest.clip_translation[1],       duration, tween);
-       result.clip_scale[0]            = do_tween(time, source.clip_scale[0],                  dest.clip_scale[0],                     duration, tween);
-       result.clip_scale[1]            = do_tween(time, source.clip_scale[1],                  dest.clip_scale[1],                     duration, tween);
-       result.angle                            = do_tween(time, source.angle,                                  dest.angle,                                     duration, tween);
-       result.levels.max_input         = do_tween(time, source.levels.max_input,               dest.levels.max_input,          duration, tween);
-       result.levels.min_input         = do_tween(time, source.levels.min_input,               dest.levels.min_input,          duration, tween);
-       result.levels.max_output        = do_tween(time, source.levels.max_output,              dest.levels.max_output,         duration, tween);
-       result.levels.min_output        = do_tween(time, source.levels.min_output,              dest.levels.min_output,         duration, tween);
-       result.levels.gamma                     = do_tween(time, source.levels.gamma,                   dest.levels.gamma,                      duration, tween);
-       result.chroma.threshold         = do_tween(time, source.chroma.threshold,               dest.chroma.threshold,          duration, tween);
-       result.chroma.softness          = do_tween(time, source.chroma.softness,                dest.chroma.softness,           duration, tween);
-       result.chroma.spill                     = do_tween(time, source.chroma.spill,                   dest.chroma.spill,                      duration, tween);
-       result.chroma.key                       = dest.chroma.key;
-       result.field_mode                       = source.field_mode & dest.field_mode;
-       result.is_key                           = source.is_key | dest.is_key;
-       result.is_mix                           = source.is_mix | dest.is_mix;
-       result.is_still                         = source.is_still | dest.is_still;
-       result.use_mipmap                       = source.use_mipmap | dest.use_mipmap;
-       result.blend_mode                       = std::max(source.blend_mode, dest.blend_mode);
-       result.layer_depth                      = dest.layer_depth;
+       image_transform result;
+
+       result.brightness                               = do_tween(time, source.brightness,                             dest.brightness,                        duration, tween);
+       result.contrast                                 = do_tween(time, source.contrast,                               dest.contrast,                          duration, tween);
+       result.saturation                               = do_tween(time, source.saturation,                             dest.saturation,                        duration, tween);
+       result.opacity                                  = do_tween(time, source.opacity,                                dest.opacity,                           duration, tween);
+       result.anchor[0]                                = do_tween(time, source.anchor[0],                              dest.anchor[0],                         duration, tween);
+       result.anchor[1]                                = do_tween(time, source.anchor[1],                              dest.anchor[1],                         duration, tween);
+       result.fill_translation[0]              = do_tween(time, source.fill_translation[0],    dest.fill_translation[0],       duration, tween);
+       result.fill_translation[1]              = do_tween(time, source.fill_translation[1],    dest.fill_translation[1],       duration, tween);
+       result.fill_scale[0]                    = do_tween(time, source.fill_scale[0],                  dest.fill_scale[0],                     duration, tween);
+       result.fill_scale[1]                    = do_tween(time, source.fill_scale[1],                  dest.fill_scale[1],                     duration, tween);
+       result.clip_translation[0]              = do_tween(time, source.clip_translation[0],    dest.clip_translation[0],       duration, tween);
+       result.clip_translation[1]              = do_tween(time, source.clip_translation[1],    dest.clip_translation[1],       duration, tween);
+       result.clip_scale[0]                    = do_tween(time, source.clip_scale[0],                  dest.clip_scale[0],                     duration, tween);
+       result.clip_scale[1]                    = do_tween(time, source.clip_scale[1],                  dest.clip_scale[1],                     duration, tween);
+       result.angle                                    = do_tween(time, source.angle,                                  dest.angle,                                     duration, tween);
+       result.levels.max_input                 = do_tween(time, source.levels.max_input,               dest.levels.max_input,          duration, tween);
+       result.levels.min_input                 = do_tween(time, source.levels.min_input,               dest.levels.min_input,          duration, tween);
+       result.levels.max_output                = do_tween(time, source.levels.max_output,              dest.levels.max_output,         duration, tween);
+       result.levels.min_output                = do_tween(time, source.levels.min_output,              dest.levels.min_output,         duration, tween);
+       result.levels.gamma                             = do_tween(time, source.levels.gamma,                   dest.levels.gamma,                      duration, tween);
+       result.chroma.target_hue                = do_tween(time, source.chroma.target_hue,              dest.chroma.target_hue,         duration, tween);
+       result.chroma.hue_width                 = do_tween(time, source.chroma.hue_width,               dest.chroma.hue_width,          duration, tween);
+       result.chroma.min_saturation    = do_tween(time, source.chroma.min_saturation,  dest.chroma.min_saturation,     duration, tween);
+       result.chroma.min_brightness    = do_tween(time, source.chroma.min_brightness,  dest.chroma.min_brightness,     duration, tween);
+       result.chroma.softness                  = do_tween(time, source.chroma.softness,                dest.chroma.softness,           duration, tween);
+       result.chroma.spill                             = do_tween(time, source.chroma.spill,                   dest.chroma.spill,                      duration, tween);
+       result.chroma.spill_darken              = do_tween(time, source.chroma.spill_darken,    dest.chroma.spill_darken,       duration, tween);
+       result.chroma.enable                    = dest.chroma.enable;
+       result.chroma.show_mask                 = dest.chroma.show_mask;
+       result.field_mode                               = source.field_mode & dest.field_mode;
+       result.is_key                                   = source.is_key | dest.is_key;
+       result.is_mix                                   = source.is_mix | dest.is_mix;
+       result.is_still                                 = source.is_still | dest.is_still;
+       result.use_mipmap                               = source.use_mipmap | dest.use_mipmap;
+       result.blend_mode                               = std::max(source.blend_mode, dest.blend_mode);
+       result.layer_depth                              = dest.layer_depth;
 
        do_tween_rectangle(source.crop, dest.crop, result.crop, time, duration, tween);
        do_tween_corners(source.perspective, dest.perspective, result.perspective, time, duration, tween);
@@ -200,7 +210,7 @@ bool operator==(const rectangle& lhs, const rectangle& rhs)
 
 bool operator==(const image_transform& lhs, const image_transform& rhs)
 {
-       return 
+       return
                eq(lhs.opacity, rhs.opacity) &&
                eq(lhs.contrast, rhs.contrast) &&
                eq(lhs.brightness, rhs.brightness) &&
@@ -218,6 +228,15 @@ bool operator==(const image_transform& lhs, const image_transform& rhs)
                lhs.use_mipmap == rhs.use_mipmap &&
                lhs.blend_mode == rhs.blend_mode &&
                lhs.layer_depth == rhs.layer_depth &&
+               lhs.chroma.enable == rhs.chroma.enable &&
+               lhs.chroma.show_mask == rhs.chroma.show_mask &&
+               eq(lhs.chroma.target_hue, rhs.chroma.target_hue) &&
+               eq(lhs.chroma.hue_width, rhs.chroma.hue_width) &&
+               eq(lhs.chroma.min_saturation, rhs.chroma.min_saturation) &&
+               eq(lhs.chroma.min_brightness, rhs.chroma.min_brightness) &&
+               eq(lhs.chroma.softness, rhs.chroma.softness) &&
+               eq(lhs.chroma.spill, rhs.chroma.spill) &&
+               eq(lhs.chroma.spill_darken, rhs.chroma.spill_darken) &&
                lhs.crop == rhs.crop &&
                lhs.perspective == rhs.perspective;
 }
@@ -231,7 +250,7 @@ bool operator!=(const image_transform& lhs, const image_transform& rhs)
 
 audio_transform& audio_transform::operator*=(const audio_transform &other)
 {
-       volume   *= other.volume;       
+       volume   *= other.volume;
        is_still |= other.is_still;
        return *this;
 }
@@ -242,11 +261,11 @@ audio_transform audio_transform::operator*(const audio_transform &other) const
 }
 
 audio_transform audio_transform::tween(double time, const audio_transform& source, const audio_transform& dest, double duration, const tweener& tween)
-{      
+{
        audio_transform result;
        result.is_still                 = source.is_still | dest.is_still;
        result.volume                   = do_tween(time, source.volume,                         dest.volume,                    duration, tween);
-       
+
        return result;
 }
 
@@ -287,7 +306,7 @@ frame_transform frame_transform::tween(double time, const frame_transform& sourc
 
 bool operator==(const frame_transform& lhs, const frame_transform& rhs)
 {
-       return  lhs.image_transform == rhs.image_transform && 
+       return  lhs.image_transform == rhs.image_transform &&
                        lhs.audio_transform == rhs.audio_transform;
 }
 
@@ -297,31 +316,16 @@ bool operator!=(const frame_transform& lhs, const frame_transform& rhs)
 }
 
 
-core::chroma::type get_chroma_mode(const std::wstring& str)
+boost::optional<core::chroma::legacy_type> get_chroma_mode(const std::wstring& str)
 {
        if (boost::iequals(str, L"none"))
-               return core::chroma::type::none;
+               return core::chroma::legacy_type::none;
        else if (boost::iequals(str, L"green"))
-               return core::chroma::type::green;
+               return core::chroma::legacy_type::green;
        else if (boost::iequals(str, L"blue"))
-               return core::chroma::type::blue;
+               return core::chroma::legacy_type::blue;
        else
-               CASPAR_THROW_EXCEPTION(user_error() << msg_info("chroma mode has to be one of none, green or blue"));
-}
-
-std::wstring get_chroma_mode(core::chroma::type type)
-{
-       switch (type)
-       {
-       case core::chroma::type::none:
-               return L"none";
-       case core::chroma::type::green:
-               return L"green";
-       case core::chroma::type::blue:
-               return L"blue";
-       default:
-               CASPAR_THROW_EXCEPTION(programming_error() << msg_info("Unhandled enum constant"));
-       };
+               return boost::none;
 }
 
 namespace detail {
index bf2cdb35f550a12e299a90b6c05f70446482d751..724d35cd1f9b92396f9d01be98227392b718594a 100644 (file)
 #include <core/mixer/image/blend_modes.h>
 
 #include <boost/array.hpp>
+#include <boost/optional.hpp>
 #include <boost/property_tree/ptree.hpp>
 
 namespace caspar { namespace core {
 
 struct chroma
 {
-       enum class type
+       enum class legacy_type
        {
                none,
                green,
                blue
        };
 
-       type    key                     = type::none;
-       double  threshold       = 0.0;
-       double  softness        = 0.0;
-       double  spill           = 0.0;
+       bool            enable                          = false;
+       bool            show_mask                       = false;
+       double          target_hue                      = 0.0;
+       double          hue_width                       = 0.0;
+       double          min_saturation          = 0.0;
+       double          min_brightness          = 0.0;
+       double          softness                        = 0.0;
+       double          spill                           = 1.0;
+       double          spill_darken            = 0.0;
 };
 
 struct levels final
@@ -98,7 +104,7 @@ struct image_transform final
        bool                                    use_mipmap                      = false;
        core::blend_mode                blend_mode                      = core::blend_mode::normal;
        int                                             layer_depth                     = 0;
-       
+
        image_transform& operator*=(const image_transform &other);
        image_transform operator*(const image_transform &other) const;
 
@@ -112,7 +118,7 @@ struct audio_transform final
 {
        double  volume          = 1.0;
        bool    is_still        = false;
-       
+
        audio_transform& operator*=(const audio_transform &other);
        audio_transform operator*(const audio_transform &other) const;
 
@@ -122,17 +128,17 @@ struct audio_transform final
 bool operator==(const audio_transform& lhs, const audio_transform& rhs);
 bool operator!=(const audio_transform& lhs, const audio_transform& rhs);
 
-//__declspec(align(16)) 
+//__declspec(align(16))
 struct frame_transform final
 {
 public:
        frame_transform();
-       
+
        core::image_transform image_transform;
        core::audio_transform audio_transform;
 
        //char padding[(sizeof(core::image_transform) + sizeof(core::audio_transform)) % 16];
-       
+
        frame_transform& operator*=(const frame_transform &other);
        frame_transform operator*(const frame_transform &other) const;
 
@@ -149,12 +155,12 @@ class tweened_transform
        int duration_;
        int time_;
        tweener tweener_;
-public:        
+public:
        tweened_transform()
                : duration_(0)
                , time_(0)
        {
-               dest_.image_transform.use_mipmap = env::properties().get(L"configuration.mixer.mipmapping_default_on", false);
+               dest_.image_transform.use_mipmap = env::properties().get(L"configuration.mixer.mipmapping-default-on", false);
        }
 
        tweened_transform(const frame_transform& source, const frame_transform& dest, int duration, const tweener& tween)
@@ -170,21 +176,20 @@ public:
        {
                return dest_;
        }
-       
+
        frame_transform fetch()
        {
                return time_ == duration_ ? dest_ : frame_transform::tween(static_cast<double>(time_), source_, dest_, static_cast<double>(duration_), tweener_);
        }
 
        frame_transform fetch_and_tick(int num)
-       {                                               
+       {
                time_ = std::min(time_+num, duration_);
                return fetch();
        }
 };
 
-chroma::type get_chroma_mode(const std::wstring& str);
-std::wstring get_chroma_mode(chroma::type type);
+boost::optional<chroma::legacy_type> get_chroma_mode(const std::wstring& str);
 
 namespace detail {
 
index b6141cc0b1762fd87ca1967b63fb6b1a67fb0004..5125ebae3baeb7391775a0fdb6b0de5327dd8d50 100644 (file)
@@ -313,6 +313,9 @@ public:
 
        uint32_t nb_frames() const override
        {
+               if (!is_initialized())
+                       return std::numeric_limits<uint32_t>::max();
+
                auto source_nb_frames = source_->nb_frames();
                auto multiple = boost::rational_cast<double>(1 / get_speed() * (output_repeat_ != 0 ? 2 : 1));
 
@@ -321,6 +324,9 @@ public:
 
        uint32_t frame_number() const override
        {
+               if (!is_initialized())
+                       return 0;
+
                auto source_frame_number = source_->frame_number() - 1; // next frame already received
                auto multiple = boost::rational_cast<double>(1 / get_speed() * (output_repeat_ != 0 ? 2 : 1));
 
@@ -332,6 +338,11 @@ public:
                return source_->pixel_constraints();
        }
 private:
+       bool is_initialized() const
+       {
+               return source_framerate_ != -1;
+       }
+
        draw_frame do_render_progressive_frame(bool sound)
        {
                user_speed_.fetch_and_tick();
index fd99bb96b299f327fc221c3480f5c5dd37551f39..f5f2a625edf0b0257f329578f703799009d78183 100644 (file)
@@ -149,7 +149,7 @@ struct scene_producer::impl
                auto speed_variable = std::make_shared<core::variable_impl<double>>(L"1.0", true, 1.0);
                store_variable(L"scene_speed", speed_variable);
                speed_ = speed_variable->value();
-               
+
                auto frame_variable = std::make_shared<core::variable_impl<double>>(L"-1", true, -1);
                store_variable(L"frame", frame_variable);
                frame_number_ = frame_variable->value();
@@ -256,14 +256,18 @@ struct scene_producer::impl
 
                angle           = layer.rotation.get() * PI / 180.0;
 
-               transform.image_transform.opacity                       = layer.adjustments.opacity.get();
-               transform.image_transform.is_key                        = layer.is_key.get();
-               transform.image_transform.use_mipmap            = layer.use_mipmap.get();
-               transform.image_transform.blend_mode            = layer.blend_mode.get();
-               transform.image_transform.chroma.key            = layer.chroma_key.key.get();
-               transform.image_transform.chroma.threshold      = layer.chroma_key.threshold.get();
-               transform.image_transform.chroma.softness       = layer.chroma_key.softness.get();
-               transform.image_transform.chroma.spill          = layer.chroma_key.spill.get();
+               transform.image_transform.opacity                               = layer.adjustments.opacity.get();
+               transform.image_transform.is_key                                = layer.is_key.get();
+               transform.image_transform.use_mipmap                    = layer.use_mipmap.get();
+               transform.image_transform.blend_mode                    = layer.blend_mode.get();
+               transform.image_transform.chroma.enable                 = layer.chroma_key.enable.get();
+               transform.image_transform.chroma.target_hue             = layer.chroma_key.target_hue.get();
+               transform.image_transform.chroma.hue_width              = layer.chroma_key.hue_width.get();
+               transform.image_transform.chroma.min_saturation = layer.chroma_key.min_saturation.get();
+               transform.image_transform.chroma.min_brightness = layer.chroma_key.min_brightness.get();
+               transform.image_transform.chroma.softness               = layer.chroma_key.softness.get();
+               transform.image_transform.chroma.spill                  = layer.chroma_key.spill.get();
+               transform.image_transform.chroma.spill_darken   = layer.chroma_key.spill_darken.get();
 
                // Mark as sublayer, so it will be composited separately by the mixer.
                transform.image_transform.layer_depth = 1;
@@ -316,9 +320,9 @@ struct scene_producer::impl
                {
                        prec_timer timer;
                        timer.tick_millis(0);
-                       
+
                        auto field1 = render_progressive_frame();
-                       
+
                        timer.tick(0.5 / format_desc_.fps);
 
                        auto field2 = render_progressive_frame();
@@ -432,7 +436,7 @@ struct scene_producer::impl
                return boost::optional<interaction_target>();
        }
 
-       std::future<std::wstring> call(const std::vector<std::wstring>& params) 
+       std::future<std::wstring> call(const std::vector<std::wstring>& params)
        {
                if (!params.empty() && boost::ends_with(params.at(0), L"()"))
                        return make_ready_future(handle_call(params));
@@ -531,7 +535,7 @@ struct scene_producer::impl
        {
                return producer_name_;
        }
-       
+
        boost::property_tree::wptree info() const
        {
                boost::property_tree::wptree info;
@@ -636,7 +640,7 @@ boost::property_tree::wptree scene_producer::info() const
        return impl_->info();
 }
 
-std::future<std::wstring> scene_producer::call(const std::vector<std::wstring>& params) 
+std::future<std::wstring> scene_producer::call(const std::vector<std::wstring>& params)
 {
        return impl_->call(params);
 }
index ccfad7fe9c8c2a742272543428754123fa276a64..aca30f6b60fb720b191d178d585e36d19e95815c 100644 (file)
@@ -60,10 +60,14 @@ struct adjustments
 
 struct chroma_key
 {
-       binding<core::chroma::type>     key;
-       binding<double>                         threshold;
+       binding<bool>                           enable;
+       binding<double>                         target_hue;
+       binding<double>                         hue_width;
+       binding<double>                         min_saturation;
+       binding<double>                         min_brightness;
        binding<double>                         softness;
        binding<double>                         spill;
+       binding<double>                         spill_darken;
 };
 
 struct layer
@@ -195,7 +199,7 @@ public:
                                                        static_cast<double>(duration)));
 
                                        to_affect.set(tweened);
-                                       
+
                                        //CASPAR_LOG(info) << relative_frame << L" " << *start_value << L" " << duration << L" " << tweened;
                                };
 
index 5eacb6c368f92359bdfe35703f16d27b6e57ae4f..49f1ee4d83648aaf1e0800dcbc649f36e24da5dc 100644 (file)
@@ -170,10 +170,14 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                layer.is_key                                    = scene->create_variable<bool>(variable_prefix + L"is_key", false, elem.second.get(L"is_key", L"false"));
                layer.use_mipmap                                = scene->create_variable<bool>(variable_prefix + L"use_mipmap", false, elem.second.get(L"use_mipmap", L"false"));
                layer.blend_mode                                = scene->create_variable<std::wstring>(variable_prefix + L"blend_mode", false, elem.second.get(L"blend_mode", L"normal")).transformed([](const std::wstring& b) { return get_blend_mode(b); });
-               layer.chroma_key.key                    = scene->create_variable<std::wstring>(variable_prefix + L"chroma_key.key", false, elem.second.get(L"chroma_key.key", L"none")).transformed([](const std::wstring& k) { return get_chroma_mode(k); });
-               layer.chroma_key.threshold              = scene->create_variable<double>(variable_prefix + L"chroma_key.threshold", false, elem.second.get(L"chroma_key.threshold", L"0.0"));
+               layer.chroma_key.enable                 = scene->create_variable<bool>(variable_prefix + L"chroma_key.enable", false, elem.second.get(L"chroma_key.enable", L"false"));
+               layer.chroma_key.target_hue             = scene->create_variable<double>(variable_prefix + L"chroma_key.target_hue", false, elem.second.get(L"chroma_key.target_hue", L"120.0"));
+               layer.chroma_key.hue_width              = scene->create_variable<double>(variable_prefix + L"chroma_key.hue_width", false, elem.second.get(L"chroma_key.hue_width", L"0.1"));
+               layer.chroma_key.min_saturation = scene->create_variable<double>(variable_prefix + L"chroma_key.min_saturation", false, elem.second.get(L"chroma_key.min_saturation", L"0.0"));
+               layer.chroma_key.min_brightness = scene->create_variable<double>(variable_prefix + L"chroma_key.min_brightness", false, elem.second.get(L"chroma_key.min_brightness", L"0.0"));
                layer.chroma_key.softness               = scene->create_variable<double>(variable_prefix + L"chroma_key.softness", false, elem.second.get(L"chroma_key.softness", L"0.0"));
-               layer.chroma_key.spill                  = scene->create_variable<double>(variable_prefix + L"chroma_key.spill", false, elem.second.get(L"chroma_key.spill", L"0.0"));
+               layer.chroma_key.spill                  = scene->create_variable<double>(variable_prefix + L"chroma_key.spill", false, elem.second.get(L"chroma_key.spill", L"1.0"));
+               layer.chroma_key.spill_darken   = scene->create_variable<double>(variable_prefix + L"chroma_key.spill_darken", false, elem.second.get(L"chroma_key.spill_darken", L"2.0"));
 
                scene->create_variable<double>(variable_prefix + L"width", false) = layer.producer.get()->pixel_constraints().width;
                scene->create_variable<double>(variable_prefix + L"height", false) = layer.producer.get()->pixel_constraints().height;
@@ -234,7 +238,7 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
 
        auto repo = [&scene](const std::wstring& name) -> variable&
        {
-               return scene->get_variable(name); 
+               return scene->get_variable(name);
        };
 
        for (auto& var_name : scene->get_variables())
index 185a73608730ea122f01f63f58f212ae2febe54c..72a66bf16f29dd7f0c7f793d0a02bd5415f996e6 100755 (executable)
@@ -1,4 +1,10 @@
 #!/bin/sh
 
-LD_LIBRARY_PATH=lib bin/casparcg "$@"
+RET=5
+
+while [ $RET -eq 5 ]
+do
+  LD_LIBRARY_PATH=lib bin/casparcg "$@"
+  RET=$?
+done
 
index ca847727eb8b6849834fdb989763757482749a1f..39dcde7f40804edf19f80e550f301898d39199aa 100644 (file)
@@ -647,10 +647,13 @@ public:
                if(!is_running_)
                        CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
 
+               boost::lock_guard<boost::mutex> lock(send_completion_mutex_);
+
                if (frame_buffer_.try_push(frame))
+               {
+                       send_completion_ = std::packaged_task<bool()>();
                        return make_ready_future(true);
-
-               boost::lock_guard<boost::mutex> lock(send_completion_mutex_);
+               }
 
                send_completion_ = std::packaged_task<bool ()>([frame, this] () mutable -> bool
                {
index e6f1ded380c494182a1ef8e95407bd8f5b861676..188fa418878652b424ef1b324ae24d80e87943ec 100644 (file)
@@ -247,30 +247,37 @@ public:
        {
                if(oc_)
                {
-                       video_encoder_executor_.begin_invoke([&] { encode_video(core::const_frame::empty(), nullptr); });
-                       audio_encoder_executor_.begin_invoke([&] { encode_audio(core::const_frame::empty(), nullptr); });
+                       try
+                       {
+                               video_encoder_executor_.begin_invoke([&] { encode_video(core::const_frame::empty(), nullptr); });
+                               audio_encoder_executor_.begin_invoke([&] { encode_audio(core::const_frame::empty(), nullptr); });
 
-                       video_encoder_executor_.stop();
-                       audio_encoder_executor_.stop();
-                       video_encoder_executor_.join();
-                       audio_encoder_executor_.join();
+                               video_encoder_executor_.stop();
+                               audio_encoder_executor_.stop();
+                               video_encoder_executor_.join();
+                               audio_encoder_executor_.join();
 
-                       video_graph_.reset();
-                       audio_filter_.reset();
-                       video_st_.reset();
-                       audio_sts_.clear();
+                               video_graph_.reset();
+                               audio_filter_.reset();
+                               video_st_.reset();
+                               audio_sts_.clear();
 
-                       write_packet(nullptr, nullptr);
+                               write_packet(nullptr, nullptr);
 
-                       write_executor_.stop();
-                       write_executor_.join();
+                               write_executor_.stop();
+                               write_executor_.join();
 
-                       FF(av_write_trailer(oc_.get()));
+                               FF(av_write_trailer(oc_.get()));
 
-                       if (!(oc_->oformat->flags & AVFMT_NOFILE) && oc_->pb)
-                               avio_close(oc_->pb);
+                               if (!(oc_->oformat->flags & AVFMT_NOFILE) && oc_->pb)
+                                       avio_close(oc_->pb);
 
-                       oc_.reset();
+                               oc_.reset();
+                       }
+                       catch (...)
+                       {
+                               CASPAR_LOG_CURRENT_EXCEPTION();
+                       }
                }
        }
 
@@ -702,6 +709,11 @@ private:
 
                adjust_video_filter(codec, in_video_format_, filt_vsink, filtergraph);
 
+               if (in_video_format_.width < 1280)
+                       video_graph_->scale_sws_opts = "out_color_matrix=bt601";
+               else
+                       video_graph_->scale_sws_opts = "out_color_matrix=bt709";
+
                configure_filtergraph(
                                *video_graph_,
                                filtergraph,
@@ -1298,6 +1310,15 @@ spl::shared_ptr<core::frame_consumer> create_ffmpeg_consumer(
        bool mono_streams               = get_and_consume_flag(L"MONO_STREAMS", params2);
        auto compatibility_mode = boost::iequals(params.at(0), L"FILE");
        auto path                               = u8(params2.size() > 1 ? params2.at(1) : L"");
+
+       // remove FILE or STREAM
+       params2.erase(params2.begin());
+
+       // remove path
+       if (!path.empty())
+               params2.erase(params2.begin());
+
+       // join only the args
        auto args                               = u8(boost::join(params2, L" "));
 
        return spl::make_shared<ffmpeg_consumer_proxy>(path, args, separate_key, mono_streams, compatibility_mode);
index b71e36a8fa89559882b6b343ab06fb87bb07eee5..216559156fda5435fbc2ae174aad78257d55ff23 100644 (file)
@@ -559,8 +559,17 @@ public:
                tbb::parallel_invoke(
                [&]
                {
-                       if (!muxer_->video_ready() && video_decoder_)
-                               video = video_decoder_->poll();
+                       do
+                       {
+                               if (!muxer_->video_ready() && video_decoder_)
+                               {
+                                       video = video_decoder_->poll();
+                                       if (video)
+                                               break;
+                               }
+                               else
+                                       break;
+                       } while (!video_decoder_->empty());
                },
                [&]
                {
@@ -599,7 +608,8 @@ public:
                file_frame_number = std::max(file_frame_number, video_decoder_ ? video_decoder_->file_frame_number() : 0);
 
                for (auto frame = muxer_->poll(); frame != core::draw_frame::empty(); frame = muxer_->poll())
-                       frame_buffer_.push(std::make_pair(frame, file_frame_number));
+                       if (frame != core::draw_frame::empty())
+                               frame_buffer_.push(std::make_pair(frame, file_frame_number));
        }
 
        bool audio_only() const
index 9322baad4ba1db6a67669e8df3c184ace5eaef85..8f8e60abfa86c46b701542c7dad8c6f3a9db6f47 100644 (file)
@@ -148,6 +148,11 @@ public:
                return packets_.size() >= 8;
        }
 
+       bool empty() const
+       {
+               return packets_.empty();
+       }
+
        uint32_t nb_frames() const
        {
                return std::max(nb_frames_, static_cast<uint32_t>(file_frame_number_));
@@ -163,6 +168,7 @@ video_decoder::video_decoder(const spl::shared_ptr<AVFormatContext>& context) :
 void video_decoder::push(const std::shared_ptr<AVPacket>& packet){impl_->push(packet);}
 std::shared_ptr<AVFrame> video_decoder::poll(){return impl_->poll();}
 bool video_decoder::ready() const{return impl_->ready();}
+bool video_decoder::empty() const { return impl_->empty(); }
 int video_decoder::width() const{return impl_->width_;}
 int video_decoder::height() const{return impl_->height_;}
 uint32_t video_decoder::nb_frames() const{return impl_->nb_frames();}
index d954dc05b05610a1d8b24d26b8cd03b7a75e573a..5947b8ea4b1ef88ea4fc67758d207bb3512a3aad 100644 (file)
@@ -42,11 +42,12 @@ class video_decoder : boost::noncopyable
 {
 public:
        explicit video_decoder(const spl::shared_ptr<AVFormatContext>& context);
-       
+
        bool                                            ready() const;
+       bool                                            empty() const;
        void                                            push(const std::shared_ptr<AVPacket>& packet);
        std::shared_ptr<AVFrame>        poll();
-       
+
        int                                                     width() const;
        int                                                     height() const;
 
@@ -61,4 +62,4 @@ private:
        spl::shared_ptr<implementation> impl_;
 };
 
-}}
\ No newline at end of file
+}}
index 78ca18fc7be036b8bfd073925316ea79aa108ef1..241c2f41f158a613a2ac30de96072414338770d4 100644 (file)
@@ -73,7 +73,7 @@
 #pragma comment (lib, "libcef_dll_wrapper.lib")
 
 namespace caspar { namespace html {
-               
+
 class html_client
        : public CefClient
        , public CefRenderHandler
@@ -459,7 +459,7 @@ public:
                        window_info.SetTransparentPainting(true);
                        window_info.SetAsOffScreen(nullptr);
                        //window_info.SetAsWindowless(nullptr, true);
-                                       
+
                        CefBrowserSettings browser_settings;
                        browser_settings.web_security = cef_state_t::STATE_DISABLED;
                        CefBrowserHost::CreateBrowser(window_info, client_.get(), url, browser_settings, nullptr);
@@ -599,17 +599,18 @@ spl::shared_ptr<core::frame_producer> create_producer(
                const core::frame_producer_dependencies& dependencies,
                const std::vector<std::wstring>& params)
 {
-       const auto filename = env::template_folder() + params.at(0) + L".html";
-       const auto found_filename = find_case_insensitive(filename);
+       const auto filename                     = env::template_folder() + params.at(0) + L".html";
+       const auto found_filename       = find_case_insensitive(filename);
+       const auto html_prefix          = boost::iequals(params.at(0), L"[HTML]");
 
-       if (!found_filename && !boost::iequals(params.at(0), L"[HTML]"))
+       if (!found_filename && !html_prefix)
                return core::frame_producer::empty();
 
-       const auto url = found_filename 
+       const auto url = found_filename
                ? L"file://" + *found_filename
                : params.at(1);
-               
-       if (!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA"))
+
+       if (!html_prefix && (!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA")))
                return core::frame_producer::empty();
 
        return core::create_destroy_proxy(spl::make_shared<html_producer>(
index 112922ac5f68fe450de2feec264398800f94066f..b1857407b9b726f47ea7e6544224468277ba546d 100644 (file)
@@ -40,6 +40,7 @@
 #include <common/log.h>
 #include <common/array.h>
 #include <common/base64.h>
+#include <common/param.h>
 #include <common/os/filesystem.h>
 
 #include <boost/filesystem.hpp>
@@ -79,12 +80,14 @@ struct image_producer : public core::frame_producer_base
        core::monitor::subject                                          monitor_subject_;
        const std::wstring                                                      description_;
        const spl::shared_ptr<core::frame_factory>      frame_factory_;
+       const uint32_t                                                          length_;
        core::draw_frame                                                        frame_                          = core::draw_frame::empty();
        core::constraints                                                       constraints_;
        
-       image_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const std::wstring& description, bool thumbnail_mode
+       image_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const std::wstring& description, bool thumbnail_mode, uint32_t length)
                : description_(description)
                , frame_factory_(frame_factory)
+               , length_(length)
        {
                load(load_image(description_));
 
@@ -94,9 +97,10 @@ struct image_producer : public core::frame_producer_base
                        CASPAR_LOG(info) << print() << L" Initialized";
        }
 
-       image_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const void* png_data, size_t size
+       image_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const void* png_data, size_t size, uint32_t length)
                : description_(L"png from memory")
                , frame_factory_(frame_factory)
+               , length_(length)
        {
                load(load_png_from_memory(png_data, size));
 
@@ -131,6 +135,11 @@ struct image_producer : public core::frame_producer_base
                return frame_;
        }
 
+       uint32_t nb_frames() const override
+       {
+               return length_;
+       }
+
        core::constraints& pixel_constraints() override
        {
                return constraints_;
@@ -178,11 +187,19 @@ public:
 void describe_producer(core::help_sink& sink, const core::help_repository& repo)
 {
        sink.short_description(L"Loads a still image.");
-       sink.syntax(L"{[image_file:string]},{[PNG_BASE64] [encoded:string]}");
+       sink.syntax(L"{[image_file:string]},{[PNG_BASE64] [encoded:string]} {LENGTH [length:int]}");
        sink.para()->text(L"Loads a still image, either from disk or via a base64 encoded image submitted via AMCP.");
+       sink.para()->text(L"The ")->code(L"length")->text(L" parameter can be used to limit the number of frames that the image will be shown for.");
        sink.para()->text(L"Examples:");
        sink.example(L">> PLAY 1-10 image_file", L"Plays an image from the media folder.");
        sink.example(L">> PLAY 1-10 [PNG_BASE64] data...", L"Plays a PNG image transferred as a base64 encoded string.");
+       sink.example(
+                               L">> PLAY 1-10 slide_show1 LENGTH 100\n"
+                               L">> LOADBG 1-10 slide_show2 LENGTH 100 MIX 20 AUTO\n"
+                               L"delay until foreground layer becomes slide_show2 and then\n"
+                               L">> LOADBG 1-10 slide_show3 LENGTH 100 MIX 20 AUTO\n"
+                               L"delay until foreground layer becomes slide_show3 and then\n"
+                               L">> LOADBG 1-10 EMPTY MIX 20 AUTO\n", L"Plays a slide show of 3 images for 100 frames each and fades to black.");
 }
 
 static const auto g_extensions = {
@@ -202,6 +219,7 @@ static const auto g_extensions = {
 
 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
 {
+       auto length = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());
 
        if (boost::iequals(params.at(0), L"[IMG_SEQUENCE]"))
        {
@@ -258,7 +276,7 @@ spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer
 
                auto png_data = from_base64(std::string(params.at(1).begin(), params.at(1).end()));
 
-               return spl::make_shared<image_producer>(dependencies.frame_factory, png_data.data(), png_data.size());
+               return spl::make_shared<image_producer>(dependencies.frame_factory, png_data.data(), png_data.size(), length);
        }
 
        std::wstring filename = env::media_folder() + params.at(0);
@@ -273,7 +291,7 @@ spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer
        if(ext == g_extensions.end())
                return core::frame_producer::empty();
 
-       return spl::make_shared<image_producer>(dependencies.frame_factory, *caspar::find_case_insensitive(filename + *ext), false);
+       return spl::make_shared<image_producer>(dependencies.frame_factory, *caspar::find_case_insensitive(filename + *ext), false, length);
 }
 
 
@@ -294,7 +312,8 @@ core::draw_frame create_thumbnail(const core::frame_producer_dependencies& depen
        spl::shared_ptr<core::frame_producer> producer = spl::make_shared<image_producer>(
                        dependencies.frame_factory,
                        *caspar::find_case_insensitive(filename + *ext),
-                       true);
+                       true,
+                       1);
        
        return producer->receive();
 }
index 0eb9a2855bff2297c9d4629aa4e09e1b6c858744..7c6d56af797a3468746c928a1515177e95d23c2b 100644 (file)
@@ -95,6 +95,7 @@ namespace amcp {
                int                                     min_num_params_;
                std::wstring            name_;
                std::wstring            replyString_;
+               std::wstring            request_id_;
        public:
                AMCPCommand(const command_context& ctx, const amcp_command_func& command, int min_num_params, const std::wstring& name)
                        : ctx_(ctx)
@@ -134,9 +135,17 @@ namespace amcp {
                        return name_;
                }
 
+               void set_request_id(std::wstring request_id)
+               {
+                       request_id_ = std::move(request_id);
+               }
+
                void SetReplyString(const std::wstring& str)
                {
-                       replyString_ = str;
+                       if (request_id_.empty())
+                               replyString_ = str;
+                       else
+                               replyString_ = L"RES " + request_id_ + L" " + str;
                }
        };
 }}}
index a490da5d714ffb71db4de3eff5a9ac36d1c51e6d..45382b452119b6360bee81a18ec1707e99020ae6 100644 (file)
@@ -233,20 +233,33 @@ std::wstring MediaInfo(const boost::filesystem::path& path, const spl::shared_pt
                + L"\r\n";
 }
 
-std::wstring ListMedia(const spl::shared_ptr<media_info_repository>& media_info_repo)
+std::wstring get_sub_directory(const std::wstring& base_folder, const std::wstring& sub_directory)
+{
+       if (sub_directory.empty())
+               return base_folder;
+
+       auto found = find_case_insensitive(base_folder + L"/" + sub_directory);
+
+       if (!found)
+               CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Sub directory " + sub_directory + L" not found."));
+
+       return *found;
+}
+
+std::wstring ListMedia(const spl::shared_ptr<media_info_repository>& media_info_repo, const std::wstring& sub_directory = L"")
 {
        std::wstringstream replyString;
-       for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)
+       for (boost::filesystem::recursive_directory_iterator itr(get_sub_directory(env::media_folder(), sub_directory)), end; itr != end; ++itr)
                replyString << MediaInfo(itr->path(), media_info_repo);
 
        return boost::to_upper_copy(replyString.str());
 }
 
-std::wstring ListTemplates(const spl::shared_ptr<core::cg_producer_registry>& cg_registry)
+std::wstring ListTemplates(const spl::shared_ptr<core::cg_producer_registry>& cg_registry, const std::wstring& sub_directory = L"")
 {
        std::wstringstream replyString;
 
-       for (boost::filesystem::recursive_directory_iterator itr(env::template_folder()), end; itr != end; ++itr)
+       for (boost::filesystem::recursive_directory_iterator itr(get_sub_directory(env::template_folder(), sub_directory)), end; itr != end; ++itr)
        {
                if(boost::filesystem::is_regular_file(itr->path()) && cg_registry->is_cg_extension(itr->path().extension().wstring()))
                {
@@ -871,16 +884,24 @@ std::wstring data_retrieve_command(command_context& ctx)
 void data_list_describer(core::help_sink& sink, const core::help_repository& repo)
 {
        sink.short_description(L"List stored datasets.");
-       sink.syntax(L"DATA LIST");
-       sink.para()->text(L"Returns a list of all stored datasets.");
+       sink.syntax(L"DATA LIST {[sub_directory:string]}");
+       sink.para()->text(L"Returns a list of stored datasets.");
+       sink.para()
+               ->text(L"if the optional ")->code(L"sub_directory")
+               ->text(L" is specified only the datasets in that sub directory will be returned.");
 }
 
 std::wstring data_list_command(command_context& ctx)
 {
+       std::wstring sub_directory;
+
+       if (!ctx.parameters.empty())
+               sub_directory = ctx.parameters.at(0);
+
        std::wstringstream replyString;
        replyString << L"200 DATA LIST OK\r\n";
 
-       for (boost::filesystem::recursive_directory_iterator itr(env::data_folder()), end; itr != end; ++itr)
+       for (boost::filesystem::recursive_directory_iterator itr(get_sub_directory(env::data_folder(), sub_directory)), end; itr != end; ++itr)
        {
                if (boost::filesystem::is_regular_file(itr->path()))
                {
@@ -1272,16 +1293,33 @@ std::wstring ANIMATION_SYNTAX = L" {[duration:int] {[tween:string]|linear}|0 lin
 void mixer_chroma_describer(core::help_sink& sink, const core::help_repository& repo)
 {
        sink.short_description(L"Enable chroma keying on a layer.");
-       sink.syntax(L"MIXER [video_channel:int]{-[layer:int]|-0} CHROMA {[color:none,green,blue] {[threshold:float] [softness:float] {[spill:float]}}" + ANIMATION_SYNTAX);
+       sink.syntax(L"MIXER [video_channel:int]{-[layer:int]|-0} CHROMA {[enable:0,1] {[target_hue:float] [hue_width:float] [min_saturation:float] [min_brightness:float] [softness:float] [spill:float] [spill_darken:float] [show_mask:0,1]}}" + ANIMATION_SYNTAX);
        sink.para()
                ->text(L"Enables or disables chroma keying on the specified video layer. Giving no parameters returns the current chroma settings.");
-       sink.para()->text(L"Examples:");
-       sink.example(L">> MIXER 1-1 CHROMA green 0.10 0.20 25 easeinsine");
-       sink.example(L">> MIXER 1-1 CHROMA none");
+       sink.para()->text(L"The chroma keying is done in the HSB/HSV color space.");
+       sink.para()->text(L"Parameters:");
+       sink.definitions()
+               ->item(L"enable", L"0 to disable chroma keying on layer. The rest of the parameters should not be given when disabling.")
+               ->item(L"target_hue", L"The hue in degrees between 0-360 where the center of the hue window will open up.")
+               ->item(L"hue_width", L"The width of the hue window within 0.0-1.0 where 1.0 means 100% of 360 degrees around target_hue.")
+               ->item(L"min_saturation", L"The minimum saturation within 0.0-1.0 required for a color to be within the chroma window.")
+               ->item(L"min_brightness", L"The minimum brightness within 0.0-1.0 required for a color to be within the chroma window.")
+               ->item(L"softness", L"The softness of the chroma keying window.")
+               ->item(L"spill", L"Controls the amount of spill. A value of 1.0 does not suppress any spill. A lower value gradually turns the spill into grayscale and more transparent.")
+               ->item(L"spill_darken", L"Controls the shade of gray that the spill suppression is done towards. Lower values goes towards white and higher values goes towards black.")
+               ->item(L"show_mask", L"If enabled, only shows the mask. Useful while editing the chroma key settings.")
+               ;
+       sink.example(L">> MIXER 1-1 CHROMA 1 120 0.1 0 0 0.1 1 2 0", L"for enabling chroma keying centered around a hue of 120 degrees (green) and with a 10% hue width");
+       sink.example(L">> MIXER 1-1 CHROMA 0", L"for disabling chroma keying");
        sink.example(
-               L">> MIXER 1-1 BLEND\n"
-               L"<< 201 MIXER OK\n"
-               L"<< SCREEN", L"for getting the current blend mode");
+               L">> MIXER 1-1 CHROMA 0\n"
+               L"<< 202 MIXER OK\n"
+               L"<< 1 120 0.1 0 0 0.1 1 2 0", L"for getting the current chroma key mode");
+       sink.para()->text(L"Deprecated legacy syntax:");
+       sink.syntax(L"MIXER [video_channel:int]{-[layer:int]|-0} CHROMA {[color:none,green,blue] {[threshold:float] [softness:float] [spill:float]}}" + ANIMATION_SYNTAX);
+       sink.para()->text(L"Deprecated legacy examples:");
+       sink.example(L">> MIXER 1-1 CHROMA green 0.10 0.20 1.0 25 easeinsine");
+       sink.example(L">> MIXER 1-1 CHROMA none");
 }
 
 std::wstring mixer_chroma_command(command_context& ctx)
@@ -1290,25 +1328,71 @@ std::wstring mixer_chroma_command(command_context& ctx)
        {
                auto chroma = get_current_transform(ctx).image_transform.chroma;
                return L"201 MIXER OK\r\n"
-                       + core::get_chroma_mode(chroma.key) + L" "
-                       + boost::lexical_cast<std::wstring>(chroma.threshold) + L" "
+                       + std::wstring(chroma.enable ? L"1 " : L"0 ")
+                       + boost::lexical_cast<std::wstring>(chroma.target_hue) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.hue_width) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.min_saturation) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.min_brightness) + L" "
                        + boost::lexical_cast<std::wstring>(chroma.softness) + L" "
-                       + boost::lexical_cast<std::wstring>(chroma.spill) + L"\r\n";
+                       + boost::lexical_cast<std::wstring>(chroma.spill) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.spill_darken) + L" "
+                       + std::wstring(chroma.show_mask ? L"1" : L"0") + L"\r\n";
        }
 
        transforms_applier transforms(ctx);
-       int duration = ctx.parameters.size() > 4 ? boost::lexical_cast<int>(ctx.parameters.at(4)) : 0;
-       std::wstring tween = ctx.parameters.size() > 5 ? ctx.parameters.at(5) : L"linear";
-
        core::chroma chroma;
-       chroma.key = get_chroma_mode(ctx.parameters.at(0));
 
-       if (chroma.key != core::chroma::type::none)
+       int duration;
+       std::wstring tween;
+
+       auto legacy_mode = core::get_chroma_mode(ctx.parameters.at(0));
+
+       if (legacy_mode)
        {
-               chroma.threshold = boost::lexical_cast<double>(ctx.parameters.at(1));
-               chroma.softness = boost::lexical_cast<double>(ctx.parameters.at(2));
-               chroma.spill = ctx.parameters.size() > 3 ? boost::lexical_cast<double>(ctx.parameters.at(3)) : 0.0;
+
+               duration = ctx.parameters.size() > 4 ? boost::lexical_cast<int>(ctx.parameters.at(4)) : 0;
+               tween = ctx.parameters.size() > 5 ? ctx.parameters.at(5) : L"linear";
+
+               if (*legacy_mode == chroma::legacy_type::none)
+               {
+                       chroma.enable = false;
+               }
+               else
+               {
+                       chroma.enable = true;
+                       chroma.hue_width = 0.5 - boost::lexical_cast<double>(ctx.parameters.at(1)) * 0.5;
+                       chroma.min_brightness = boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.min_saturation = boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.softness = boost::lexical_cast<double>(ctx.parameters.at(2)) - boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.spill = boost::lexical_cast<double>(ctx.parameters.at(3));
+                       chroma.spill_darken = 2;
+
+                       if (*legacy_mode == chroma::legacy_type::green)
+                               chroma.target_hue = 120;
+                       else if (*legacy_mode == chroma::legacy_type::blue)
+                               chroma.target_hue = 240;
+               }
        }
+       else
+       {
+               duration = ctx.parameters.size() > 9 ? boost::lexical_cast<int>(ctx.parameters.at(9)) : 0;
+               tween = ctx.parameters.size() > 10 ? ctx.parameters.at(10) : L"linear";
+
+               chroma.enable = ctx.parameters.at(0) == L"1";
+
+               if (chroma.enable)
+               {
+                       chroma.target_hue               = boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.hue_width                = boost::lexical_cast<double>(ctx.parameters.at(2));
+                       chroma.min_saturation   = boost::lexical_cast<double>(ctx.parameters.at(3));
+                       chroma.min_brightness   = boost::lexical_cast<double>(ctx.parameters.at(4));
+                       chroma.softness                 = boost::lexical_cast<double>(ctx.parameters.at(5));
+                       chroma.spill                    = boost::lexical_cast<double>(ctx.parameters.at(6));
+                       chroma.spill_darken             = boost::lexical_cast<double>(ctx.parameters.at(7));
+                       chroma.show_mask                = boost::lexical_cast<double>(ctx.parameters.at(8));
+               }
+       }
+
 
        transforms.add(stage::transform_tuple_t(ctx.layer_index(), [=](frame_transform transform) -> frame_transform
        {
@@ -2083,9 +2167,12 @@ std::wstring channel_grid_command(command_context& ctx)
 
 void thumbnail_list_describer(core::help_sink& sink, const core::help_repository& repo)
 {
-       sink.short_description(L"List all thumbnails.");
-       sink.syntax(L"THUMBNAIL LIST");
-       sink.para()->text(L"Lists all thumbnails.");
+       sink.short_description(L"List thumbnails.");
+       sink.syntax(L"THUMBNAIL LIST {[sub_directory:string]}");
+       sink.para()->text(L"Lists thumbnails.");
+       sink.para()
+               ->text(L"if the optional ")->code(L"sub_directory")
+               ->text(L" is specified only the thumbnails in that sub directory will be returned.");
        sink.para()->text(L"Examples:");
        sink.example(
                L">> THUMBNAIL LIST\n"
@@ -2096,10 +2183,15 @@ void thumbnail_list_describer(core::help_sink& sink, const core::help_repository
 
 std::wstring thumbnail_list_command(command_context& ctx)
 {
+       std::wstring sub_directory;
+
+       if (!ctx.parameters.empty())
+               sub_directory = ctx.parameters.at(0);
+
        std::wstringstream replyString;
        replyString << L"200 THUMBNAIL LIST OK\r\n";
 
-       for (boost::filesystem::recursive_directory_iterator itr(env::thumbnail_folder()), end; itr != end; ++itr)
+       for (boost::filesystem::recursive_directory_iterator itr(get_sub_directory(env::thumbnail_folder(), sub_directory)), end; itr != end; ++itr)
        {
                if (boost::filesystem::is_regular_file(itr->path()))
                {
@@ -2204,15 +2296,16 @@ void cinf_describer(core::help_sink& sink, const core::help_repository& repo)
        sink.short_description(L"Get information about a media file.");
        sink.syntax(L"CINF [filename:string]");
        sink.para()->text(L"Returns information about a media file.");
+       sink.para()->text(L"If a file with the same name exist in multiple directories, all of them are returned.");
 }
 
 std::wstring cinf_command(command_context& ctx)
 {
        std::wstring info;
-       for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end && info.empty(); ++itr)
+       for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)
        {
                auto path = itr->path();
-               auto file = path.replace_extension(L"").filename().wstring();
+               auto file = path.stem().wstring();
                if (boost::iequals(file, ctx.parameters.at(0)))
                        info += MediaInfo(itr->path(), ctx.media_info_repo);
        }
@@ -2229,18 +2322,26 @@ std::wstring cinf_command(command_context& ctx)
 
 void cls_describer(core::help_sink& sink, const core::help_repository& repo)
 {
-       sink.short_description(L"List all media files.");
-       sink.syntax(L"CLS");
+       sink.short_description(L"List media files.");
+       sink.syntax(L"CLS {[sub_directory:string]}");
        sink.para()
-               ->text(L"Lists all media files in the ")->code(L"media")->text(L" folder. Use the command ")
+               ->text(L"Lists media files in the ")->code(L"media")->text(L" folder. Use the command ")
                ->see(L"INFO PATHS")->text(L" to get the path to the ")->code(L"media")->text(L" folder.");
+       sink.para()
+               ->text(L"if the optional ")->code(L"sub_directory")
+               ->text(L" is specified only the media files in that sub directory will be returned.");
 }
 
 std::wstring cls_command(command_context& ctx)
 {
+       std::wstring sub_directory;
+
+       if (!ctx.parameters.empty())
+               sub_directory = ctx.parameters.at(0);
+
        std::wstringstream replyString;
        replyString << L"200 CLS OK\r\n";
-       replyString << ListMedia(ctx.media_info_repo);
+       replyString << ListMedia(ctx.media_info_repo, sub_directory);
        replyString << L"\r\n";
        return boost::to_upper_copy(replyString.str());
 }
@@ -2270,19 +2371,27 @@ std::wstring fls_command(command_context& ctx)
 
 void tls_describer(core::help_sink& sink, const core::help_repository& repo)
 {
-       sink.short_description(L"List all templates.");
-       sink.syntax(L"TLS");
+       sink.short_description(L"List templates.");
+       sink.syntax(L"TLS {[sub_directory:string]}");
        sink.para()
-               ->text(L"Lists all template files in the ")->code(L"templates")->text(L" folder. Use the command ")
+               ->text(L"Lists template files in the ")->code(L"templates")->text(L" folder. Use the command ")
                ->see(L"INFO PATHS")->text(L" to get the path to the ")->code(L"templates")->text(L" folder.");
+       sink.para()
+               ->text(L"if the optional ")->code(L"sub_directory")
+               ->text(L" is specified only the template files in that sub directory will be returned.");
 }
 
 std::wstring tls_command(command_context& ctx)
 {
+       std::wstring sub_directory;
+
+       if (!ctx.parameters.empty())
+               sub_directory = ctx.parameters.at(0);
+
        std::wstringstream replyString;
        replyString << L"200 TLS OK\r\n";
 
-       replyString << ListTemplates(ctx.cg_registry);
+       replyString << ListTemplates(ctx.cg_registry, sub_directory);
        replyString << L"\r\n";
 
        return replyString.str();
@@ -2885,6 +2994,21 @@ std::wstring lock_command(command_context& ctx)
        CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Unknown LOCK command " + command));
 }
 
+void req_describer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Perform any command with an additional request id identifying the response.");
+       sink.syntax(L"REQ [request_id:string] COMMAND...");
+       sink.para()
+               ->text(L"This special command modifies the AMCP protocol a little bit to prepend ")
+               ->code(L"RES request_id")->text(L" to the response, in order to see what asynchronous response matches what request.");
+       sink.para()->text(L"Examples:");
+       sink.example(L"REQ unique PLAY 1-0 AMB\n");
+       sink.example(
+               L">> REQ unique PLAY 1-0 AMB\n"
+               L"<< RES unique 202 PLAY OK");
+}
+
+
 void register_commands(amcp_command_repository& repo)
 {
        repo.register_channel_command(  L"Basic Commands",              L"LOADBG",                                              loadbg_describer,                                       loadbg_command,                                 1);
@@ -2971,6 +3095,8 @@ void register_commands(amcp_command_repository& repo)
        repo.register_command(                  L"Query Commands",              L"HELP",                                                help_describer,                                         help_command,                                   0);
        repo.register_command(                  L"Query Commands",              L"HELP PRODUCER",                               help_producer_describer,                        help_producer_command,                  0);
        repo.register_command(                  L"Query Commands",              L"HELP CONSUMER",                               help_consumer_describer,                        help_consumer_command,                  0);
+
+       repo.help_repo()->register_item({ L"AMCP", L"Protocol Commands" }, L"REQ", req_describer);
 }
 
 }      //namespace amcp
index 871f25c918d17d7d9133925f28d8e0d43c825028..6009b3e930c73199656270718f288ac327ed0ee2 100644 (file)
@@ -19,7 +19,7 @@
 * Author: Nicklas P Andersson
 */
 
+
 #include "../StdAfx.h"
 
 #include "AMCPProtocolStrategy.h"
@@ -106,7 +106,7 @@ public:
        void Parse(const std::wstring& message, ClientInfoPtr client)
        {
                CASPAR_LOG_COMMUNICATION(info) << L"Received message from " << client->address() << ": " << message << L"\\r\\n";
-       
+
                command_interpreter_result result;
                if(interpret_command_string(message, result, client))
                {
@@ -115,7 +115,7 @@ public:
                        else
                                result.queue->AddCommand(result.command);
                }
-               
+
                if (result.error != error_state::no_error)
                {
                        std::wstringstream answer;
@@ -157,6 +157,22 @@ private:
                        if (!tokens.empty() && tokens.front().at(0) == L'/')
                                tokens.pop_front();
 
+                       std::wstring request_id;
+
+                       if (!tokens.empty() && boost::iequals(tokens.front(), L"REQ"))
+                       {
+                               tokens.pop_front();
+
+                               if (tokens.empty())
+                               {
+                                       result.error = error_state::parameters_error;
+                                       return false;
+                               }
+
+                               request_id = tokens.front();
+                               tokens.pop_front();
+                       }
+
                        // Fail if no more tokens.
                        if (tokens.empty())
                        {
@@ -234,6 +250,9 @@ private:
                                if (result.command->parameters().size() < result.command->minimum_parameters())
                                        result.error = error_state::parameters_error;
                        }
+
+                       if (result.command)
+                               result.command->set_request_id(std::move(request_id));
                }
                catch (std::out_of_range&)
                {
index 06249814def23a49a9bbaed5bc2229f9c7d68b3f..2457ec72d5b0f847897fe5b204ef65b53f02e27c 100644 (file)
@@ -225,4 +225,9 @@ void amcp_command_repository::register_channel_command(
        self.channel_commands.insert(std::make_pair(std::move(name), std::make_pair(std::move(command), min_num_params)));
 }
 
+spl::shared_ptr<core::help_repository> amcp_command_repository::help_repo() const
+{
+       return impl_->help_repo;
+}
+
 }}}
index 778c99845dcf1e20d420255d94f316d30fc3a364..2cd065fbba5a09be25eb796a2cc49537d68ce6d3 100644 (file)
@@ -60,6 +60,7 @@ public:
 
        void register_command(std::wstring category, std::wstring name, core::help_item_describer describer, amcp_command_func command, int min_num_params);
        void register_channel_command(std::wstring category, std::wstring name, core::help_item_describer describer, amcp_command_func command, int min_num_params);
+       spl::shared_ptr<core::help_repository> help_repo() const;
 private:
        struct impl;
        spl::shared_ptr<impl> impl_;
index d9973b14a39ba1de946421cd381e40a342896d79..1734424857b28495cb910ffc73a49a53c6779484 100644 (file)
@@ -41,7 +41,7 @@
 <channel-grid>        false [true|false]</channel-grid>\r
 <mixer>\r
     <blend-modes>          false [true|false]</blend-modes>\r
-    <mipmapping_default_on>false [true|false]</mipmapping_default_on>\r
+    <mipmapping-default-on>false [true|false]</mipmapping-default-on>\r
     <straight-alpha>       false [true|false]</straight-alpha>\r
 </mixer>\r
 <accelerator>auto [cpu|gpu|auto]</accelerator>\r
index 7db474e7e21f990dcbc663cd2b3788f5f9b069cd..b90bd40018219abba98dc17097a27fa220b08756 100644 (file)
@@ -118,9 +118,8 @@ void do_run(
        std::wstring wcmd;
        while(true)
        {
-               std::getline(std::wcin, wcmd); // TODO: It's blocking...
-
-               //boost::to_upper(wcmd);
+               if (!std::getline(std::wcin, wcmd))             // TODO: It's blocking...
+                       wcmd = L"EXIT";                                         // EOF, handle as EXIT
 
                if(boost::iequals(wcmd, L"EXIT") || boost::iequals(wcmd, L"Q") || boost::iequals(wcmd, L"QUIT") || boost::iequals(wcmd, L"BYE"))
                {