]> git.sesse.net Git - casparcg/blobdiff - protocol/amcp/AMCPCommandsImpl.cpp
* Merged MIXER MIPMAP support from 2.0. Implemented as a setting in frame_transform...
[casparcg] / protocol / amcp / AMCPCommandsImpl.cpp
index 44224daeb4cc6971f73231abff510b47df85af97..ff454f79e31ceb2143a8b51feb2821e93e022de8 100644 (file)
@@ -33,6 +33,7 @@
 #include <common/log.h>
 #include <common/param.h>
 #include <common/os/system_info.h>
+#include <common/os/filesystem.h>
 #include <common/base64.h>
 
 #include <core/producer/frame_producer.h>
 #include <core/system_info_provider.h>
 
 #include <modules/reroute/producer/reroute_producer.h>
-#include <modules/flash/flash.h>
-#include <modules/flash/util/swf.h>
-#include <modules/flash/producer/flash_producer.h>
-#include <modules/flash/producer/cg_proxy.h>
 
 #include <algorithm>
 #include <locale>
@@ -106,7 +103,7 @@ namespace caspar { namespace protocol {
 
 using namespace core;
 
-std::wstring read_file_base64(const boost::filesystem::wpath& file)
+std::wstring read_file_base64(const boost::filesystem::path& file)
 {
        using namespace boost::archive::iterators;
 
@@ -124,7 +121,7 @@ std::wstring read_file_base64(const boost::filesystem::wpath& file)
        return std::wstring(result.begin(), result.end());
 }
 
-std::wstring read_utf8_file(const boost::filesystem::wpath& file)
+std::wstring read_utf8_file(const boost::filesystem::path& file)
 {
        std::wstringstream result;
        boost::filesystem::wifstream filestream(file);
@@ -140,7 +137,7 @@ std::wstring read_utf8_file(const boost::filesystem::wpath& file)
        return result.str();
 }
 
-std::wstring read_latin1_file(const boost::filesystem::wpath& file)
+std::wstring read_latin1_file(const boost::filesystem::path& file)
 {
        boost::locale::generator gen;
        gen.locale_cache_enabled(true);
@@ -168,7 +165,7 @@ std::wstring read_latin1_file(const boost::filesystem::wpath& file)
        return widened_result;
 }
 
-std::wstring read_file(const boost::filesystem::wpath& file)
+std::wstring read_file(const boost::filesystem::path& file)
 {
        static const uint8_t BOM[] = {0xef, 0xbb, 0xbf};
 
@@ -204,7 +201,7 @@ std::wstring MediaInfo(const boost::filesystem::path& path, const spl::shared_pt
 
        auto is_not_digit = [](char c){ return std::isdigit(c) == 0; };
 
-       auto relativePath = boost::filesystem::wpath(path.wstring().substr(env::media_folder().size() - 1, path.wstring().size()));
+       auto relativePath = boost::filesystem::path(path.wstring().substr(env::media_folder().size() - 1, path.wstring().size()));
 
        auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(path)));
        writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end());
@@ -237,15 +234,15 @@ std::wstring ListMedia(const spl::shared_ptr<media_info_repository>& media_info_
        return boost::to_upper_copy(replyString.str());
 }
 
-std::wstring ListTemplates(
+std::wstring ListTemplates(const spl::shared_ptr<core::cg_producer_registry>& cg_registry)
 {
        std::wstringstream replyString;
 
        for (boost::filesystem::recursive_directory_iterator itr(env::template_folder()), end; itr != end; ++itr)
        {               
-               if(boost::filesystem::is_regular_file(itr->path()) && (itr->path().extension() == L".ft" || itr->path().extension() == L".ct"))
+               if(boost::filesystem::is_regular_file(itr->path()) && cg_registry->is_cg_extension(itr->path().extension().wstring()))
                {
-                       auto relativePath = boost::filesystem::wpath(itr->path().wstring().substr(env::template_folder().size()-1, itr->path().wstring().size()));
+                       auto relativePath = boost::filesystem::path(itr->path().wstring().substr(env::template_folder().size()-1, itr->path().wstring().size()));
 
                        auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(itr->path())));
                        writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), [](char c){ return std::isdigit(c) == 0;}), writeTimeStr.end());
@@ -256,9 +253,9 @@ std::wstring ListTemplates()
 
                        auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());
 
-                       std::wstring dir = relativePath.parent_path().generic_wstring();
-                       std::wstring file = boost::to_upper_copy(relativePath.filename().wstring());
-                       relativePath = boost::filesystem::wpath(dir + L"/" + file);
+                       auto dir = relativePath.parent_path();
+                       auto file = boost::to_upper_copy(relativePath.filename().wstring());
+                       relativePath = dir / file;
                                                
                        auto str = relativePath.replace_extension(L"").generic_wstring();
                        boost::trim_if(str, boost::is_any_of("\\/"));
@@ -313,7 +310,7 @@ bool ChannelGridCommand::DoExecute()
        params.push_back(L"0");
        params.push_back(L"NAME");
        params.push_back(L"Channel Grid Window");
-       auto screen = create_consumer(params);
+       auto screen = create_consumer(params, &self->stage());
 
        self->output().add(screen);
 
@@ -388,6 +385,11 @@ bool CallCommand::DoExecute()
 
 tbb::concurrent_unordered_map<int, std::vector<stage::transform_tuple_t>> deferred_transforms;
 
+core::frame_transform MixerCommand::get_current_transform()
+{
+       return channel()->stage().get_current_transform(layer_index()).get();
+}
+
 bool MixerCommand::DoExecute()
 {      
        //Perform loading of the clip
@@ -401,6 +403,9 @@ bool MixerCommand::DoExecute()
 
                if(boost::iequals(parameters()[0], L"KEYER") || boost::iequals(parameters()[0], L"IS_KEY"))
                {
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.image_transform.is_key ? 1 : 0; });
+
                        bool value = boost::lexical_cast<int>(parameters().at(1));
                        transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
                        {
@@ -410,6 +415,9 @@ bool MixerCommand::DoExecute()
                }
                else if(boost::iequals(parameters()[0], L"OPACITY"))
                {
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.image_transform.opacity; });
+
                        int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
                        std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
 
@@ -421,8 +429,47 @@ bool MixerCommand::DoExecute()
                                return transform;                                       
                        }, duration, tween));
                }
-               else if(boost::iequals(parameters()[0], L"FILL") || boost::iequals(parameters()[0], L"FILL_RECT"))
+               else if (boost::iequals(parameters()[0], L"ANCHOR"))
                {
+                       if (parameters().size() == 1)
+                       {
+                               auto transform = get_current_transform().image_transform;
+                               auto anchor = transform.anchor;
+                               SetReplyString(
+                                               L"201 MIXER OK\r\n"
+                                               + boost::lexical_cast<std::wstring>(anchor[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(anchor[1]) + L"\r\n");
+                               return true;
+                       }
+
+                       int duration = parameters().size() > 3 ? boost::lexical_cast<int>(parameters()[3]) : 0;
+                       std::wstring tween = parameters().size() > 4 ? parameters()[4] : L"linear";
+                       double x = boost::lexical_cast<double>(parameters().at(1));
+                       double y = boost::lexical_cast<double>(parameters().at(2));
+
+                       transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) mutable -> frame_transform
+                       {
+                               transform.image_transform.anchor[0] = x;
+                               transform.image_transform.anchor[1] = y;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if (boost::iequals(parameters()[0], L"FILL") || boost::iequals(parameters()[0], L"FILL_RECT"))
+               {
+                       if (parameters().size() == 1)
+                       {
+                               auto transform = get_current_transform().image_transform;
+                               auto translation = transform.fill_translation;
+                               auto scale = transform.fill_scale;
+                               SetReplyString(
+                                               L"201 MIXER OK\r\n"
+                                               + boost::lexical_cast<std::wstring>(translation[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(translation[1]) + L" "
+                                               + boost::lexical_cast<std::wstring>(scale[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(scale[1]) + L"\r\n");
+                               return true;
+                       }
+
                        int duration = parameters().size() > 5 ? boost::lexical_cast<int>(parameters()[5]) : 0;
                        std::wstring tween = parameters().size() > 6 ? parameters()[6] : L"linear";
                        double x        = boost::lexical_cast<double>(parameters().at(1));
@@ -441,6 +488,20 @@ bool MixerCommand::DoExecute()
                }
                else if(boost::iequals(parameters()[0], L"CLIP") || boost::iequals(parameters()[0], L"CLIP_RECT"))
                {
+                       if (parameters().size() == 1)
+                       {
+                               auto transform = get_current_transform().image_transform;
+                               auto translation = transform.clip_translation;
+                               auto scale = transform.clip_scale;
+                               SetReplyString(
+                                               L"201 MIXER OK\r\n"
+                                               + boost::lexical_cast<std::wstring>(translation[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(translation[1]) + L" "
+                                               + boost::lexical_cast<std::wstring>(scale[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(scale[1]) + L"\r\n");
+                               return true;
+                       }
+
                        int duration = parameters().size() > 5 ? boost::lexical_cast<int>(parameters()[5]) : 0;
                        std::wstring tween = parameters().size() > 6 ? parameters()[6] : L"linear";
                        double x        = boost::lexical_cast<double>(parameters().at(1));
@@ -457,7 +518,79 @@ bool MixerCommand::DoExecute()
                                return transform;
                        }, duration, tween));
                }
-               else if(boost::iequals(parameters()[0], L"GRID"))
+               else if (boost::iequals(parameters()[0], L"CROP"))
+               {
+                       if (parameters().size() == 1)
+                       {
+                               auto crop = get_current_transform().image_transform.crop;
+                               SetReplyString(
+                                       L"201 MIXER OK\r\n"
+                                       + boost::lexical_cast<std::wstring>(crop.ul[0]) + L" "
+                                       + boost::lexical_cast<std::wstring>(crop.ul[1]) + L" "
+                                       + boost::lexical_cast<std::wstring>(crop.lr[0]) + L" "
+                                       + boost::lexical_cast<std::wstring>(crop.lr[1]) + L"\r\n");
+                               return true;
+                       }
+
+                       int duration = parameters().size() > 5 ? boost::lexical_cast<int>(parameters()[5]) : 0;
+                       std::wstring tween = parameters().size() > 6 ? parameters()[6] : L"linear";
+                       double ul_x = boost::lexical_cast<double>(parameters().at(1));
+                       double ul_y = boost::lexical_cast<double>(parameters().at(2));
+                       double lr_x = boost::lexical_cast<double>(parameters().at(3));
+                       double lr_y = boost::lexical_cast<double>(parameters().at(4));
+
+                       transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.crop.ul[0] = ul_x;
+                               transform.image_transform.crop.ul[1] = ul_y;
+                               transform.image_transform.crop.lr[0] = lr_x;
+                               transform.image_transform.crop.lr[1] = lr_y;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if (boost::iequals(parameters()[0], L"PERSPECTIVE"))
+               {
+                       if (parameters().size() == 1)
+                       {
+                               auto perspective = get_current_transform().image_transform.perspective;
+                               SetReplyString(
+                                               L"201 MIXER OK\r\n"
+                                               + boost::lexical_cast<std::wstring>(perspective.ul[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(perspective.ul[1]) + L" "
+                                               + boost::lexical_cast<std::wstring>(perspective.ur[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(perspective.ur[1]) + L" "
+                                               + boost::lexical_cast<std::wstring>(perspective.lr[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(perspective.lr[1]) + L" "
+                                               + boost::lexical_cast<std::wstring>(perspective.ll[0]) + L" "
+                                               + boost::lexical_cast<std::wstring>(perspective.ll[1]) + L"\r\n");
+                               return true;
+                       }
+
+                       int duration = parameters().size() > 9 ? boost::lexical_cast<int>(parameters()[9]) : 0;
+                       std::wstring tween = parameters().size() > 10 ? parameters()[10] : L"linear";
+                       double ul_x = boost::lexical_cast<double>(parameters().at(1));
+                       double ul_y = boost::lexical_cast<double>(parameters().at(2));
+                       double ur_x = boost::lexical_cast<double>(parameters().at(3));
+                       double ur_y = boost::lexical_cast<double>(parameters().at(4));
+                       double lr_x = boost::lexical_cast<double>(parameters().at(5));
+                       double lr_y = boost::lexical_cast<double>(parameters().at(6));
+                       double ll_x = boost::lexical_cast<double>(parameters().at(7));
+                       double ll_y = boost::lexical_cast<double>(parameters().at(8));
+
+                       transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.perspective.ul[0] = ul_x;
+                               transform.image_transform.perspective.ul[1] = ul_y;
+                               transform.image_transform.perspective.ur[0] = ur_x;
+                               transform.image_transform.perspective.ur[1] = ur_y;
+                               transform.image_transform.perspective.lr[0] = lr_x;
+                               transform.image_transform.perspective.lr[1] = lr_y;
+                               transform.image_transform.perspective.ll[0] = ll_x;
+                               transform.image_transform.perspective.ll[1] = ll_y;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if (boost::iequals(parameters()[0], L"GRID"))
                {
                        int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
                        std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
@@ -483,19 +616,51 @@ bool MixerCommand::DoExecute()
                                }
                        }
                }
+               else if (boost::iequals(parameters()[0], L"MIPMAP"))
+               {
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.image_transform.use_mipmap ? 1 : 0; });
+
+                       bool value = boost::lexical_cast<int>(parameters().at(1));
+                       transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.use_mipmap = value;
+                               return transform;
+                       }, 0, L"linear"));
+               }
                else if(boost::iequals(parameters()[0], L"BLEND"))
                {
+                       if (parameters().size() == 1)
+                       {
+                               auto blend_mode = channel()->mixer().get_blend_mode(layer_index());
+                               SetReplyString(L"201 MIXER OK\r\n"
+                                               + boost::lexical_cast<std::wstring>(get_blend_mode(blend_mode))
+                                               + L"\r\n");
+                               return true;
+                       }
+
                        auto blend_str = parameters().at(1);                                                            
                        int layer = layer_index();
                        channel()->mixer().set_blend_mode(layer, get_blend_mode(blend_str));    
                }
                else if(boost::iequals(parameters()[0], L"MASTERVOLUME"))
                {
+                       if (parameters().size() == 1)
+                       {
+                               auto volume = channel()->mixer().get_master_volume();
+                               SetReplyString(L"201 MIXER OK\r\n"
+                                               + boost::lexical_cast<std::wstring>(volume)+L"\r\n");
+                               return true;
+                       }
+
                        float master_volume = boost::lexical_cast<float>(parameters().at(1));
                        channel()->mixer().set_master_volume(master_volume);
                }
                else if(boost::iequals(parameters()[0], L"BRIGHTNESS"))
                {
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.image_transform.brightness; });
+
                        auto value = boost::lexical_cast<double>(parameters().at(1));
                        int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
                        std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
@@ -507,6 +672,9 @@ bool MixerCommand::DoExecute()
                }
                else if(boost::iequals(parameters()[0], L"SATURATION"))
                {
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.image_transform.saturation; });
+
                        auto value = boost::lexical_cast<double>(parameters().at(1));
                        int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
                        std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
@@ -516,8 +684,11 @@ bool MixerCommand::DoExecute()
                                return transform;
                        }, duration, tween));   
                }
-               else if(parameters()[0] == L"CONTRAST")
+               else if (boost::iequals(parameters()[0], L"CONTRAST"))
                {
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.image_transform.contrast; });
+
                        auto value = boost::lexical_cast<double>(parameters().at(1));
                        int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
                        std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
@@ -527,8 +698,36 @@ bool MixerCommand::DoExecute()
                                return transform;
                        }, duration, tween));   
                }
-               else if(boost::iequals(parameters()[0], L"LEVELS"))
+               else if (boost::iequals(parameters()[0], L"ROTATION"))
+               {
+                       static const double PI = 3.141592653589793;
+
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.image_transform.angle / PI * 180.0; });
+
+                       auto value = boost::lexical_cast<double>(parameters().at(1)) * PI / 180.0;
+                       int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
+                       std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
+                       transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.angle = value;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if (boost::iequals(parameters()[0], L"LEVELS"))
                {
+                       if (parameters().size() == 1)
+                       {
+                               auto levels = get_current_transform().image_transform.levels;
+                               SetReplyString(L"201 MIXER OK\r\n"
+                                               + boost::lexical_cast<std::wstring>(levels.min_input) + L" "
+                                               + boost::lexical_cast<std::wstring>(levels.max_input) + L" "
+                                               + boost::lexical_cast<std::wstring>(levels.gamma) + L" "
+                                               + boost::lexical_cast<std::wstring>(levels.min_output) + L" "
+                                               + boost::lexical_cast<std::wstring>(levels.max_output) + L"\r\n");
+                               return true;
+                       }
+
                        levels value;
                        value.min_input  = boost::lexical_cast<double>(parameters().at(1));
                        value.max_input  = boost::lexical_cast<double>(parameters().at(2));
@@ -546,6 +745,9 @@ bool MixerCommand::DoExecute()
                }
                else if(boost::iequals(parameters()[0], L"VOLUME"))
                {
+                       if (parameters().size() == 1)
+                               return reply_value([](const frame_transform& t) { return t.audio_transform.volume; });
+
                        int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
                        std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
                        double value = boost::lexical_cast<double>(parameters()[1]);
@@ -664,7 +866,7 @@ bool AddCommand::DoExecute()
                core::diagnostics::scoped_call_context save;
                core::diagnostics::call_context::for_thread().video_channel = channel_index() + 1;
 
-               auto consumer = create_consumer(parameters());
+               auto consumer = create_consumer(parameters(), &channel()->stage());
                channel()->output().add(layer_index(consumer->index()), consumer);
        
                SetReplyString(L"202 ADD OK\r\n");
@@ -699,7 +901,7 @@ bool RemoveCommand::DoExecute()
                                boost::to_upper(str);
                        }
 
-                       index = create_consumer(parameters())->index();
+                       index = create_consumer(parameters(), &channel()->stage())->index();
                }
 
                channel()->output().remove(index);
@@ -751,44 +953,6 @@ bool LoadCommand::DoExecute()
        }
 }
 
-
-
-//std::function<std::wstring()> channel_cg_add_command::parse(const std::wstring& message, const std::vector<renderer::render_device_ptr>& channels)
-//{
-//     static boost::wregex expr(L"^CG\\s(?<video_channel>\\d+)-?(?<LAYER>\\d+)?\\sADD\\s(?<FLASH_LAYER>\\d+)\\s(?<TEMPLATE>\\S+)\\s?(?<START_LABEL>\\S\\S+)?\\s?(?<PLAY_ON_LOAD>\\d)?\\s?(?<DATA>.*)?");
-//
-//     boost::wsmatch what;
-//     if(!boost::regex_match(message, what, expr))
-//             return nullptr;
-//
-//     auto info = channel_info::parse(what, channels);
-//
-//     int flash_layer_index = boost::lexical_cast<int>(what["FLASH_LAYER"].str());
-//
-//     std::wstring templatename = what["TEMPLATE"].str();
-//     bool play_on_load = what["PLAY_ON_LOAD"].matched ? what["PLAY_ON_LOAD"].str() != L"0" : 0;
-//     std::wstring start_label = what["START_LABEL"].str();   
-//     std::wstring data = get_data(what["DATA"].str());
-//     
-//     boost::replace_all(templatename, "\"", "");
-//
-//     return [=]() -> std::wstring
-//     {       
-//             std::wstring fullFilename = flash::flash_producer::find_template(server::template_folder() + templatename);
-//             if(fullFilename.empty())
-//                     CASPAR_THROW_EXCEPTION(file_not_found());
-//     
-//             std::wstring extension = boost::filesystem::wpath(fullFilename).extension();
-//             std::wstring filename = templatename;
-//             filename.append(extension);
-//
-//             flash::flash::create_cg_proxy(info.video_channel, std::max<int>(DEFAULT_CHANNEL_LAYER+1, info.layer_index))
-//                     ->add(flash_layer_index, filename, play_on_load, start_label, data);
-//
-//             CASPAR_LOG(info) << L"Executed [amcp_channel_cg_add]";
-//             return L"";
-//     };
-
 bool LoadbgCommand::DoExecute()
 {
        transition_info transitionInfo;
@@ -954,7 +1118,7 @@ bool ClearCommand::DoExecute()
 
 bool PrintCommand::DoExecute()
 {
-       channel()->output().add(create_consumer({ L"IMAGE" }));
+       channel()->output().add(create_consumer({ L"IMAGE" }, &channel()->stage()));
                
        SetReplyString(L"202 PRINT OK\r\n");
 
@@ -1067,7 +1231,7 @@ bool CGCommand::DoExecuteAdd() {
        {       //read data
                const std::wstring& dataString = parameters()[dataIndex];
 
-               if(dataString[0] == L'<') //the data is an XML-string
+               if (dataString.at(0) == L'<' || dataString.at(0) == L'{') //the data is XML or Json
                        pDataString = dataString.c_str();
                else 
                {
@@ -1076,26 +1240,29 @@ bool CGCommand::DoExecuteAdd() {
                        filename.append(dataString);
                        filename.append(L".ftd");
 
-                       dataFromFile = read_file(boost::filesystem::wpath(filename));
-                       pDataString = dataFromFile.c_str();
+                       auto found_file = find_case_insensitive(filename);
+
+                       if (found_file)
+                       {
+                               dataFromFile = read_file(boost::filesystem::path(*found_file));
+                               pDataString = dataFromFile.c_str();
+                       }
                }
        }
 
-       std::wstring fullFilename = flash::find_template(env::template_folder() + parameters()[2]);
-       if(!fullFilename.empty())
-       {
-               std::wstring extension = boost::filesystem::path(fullFilename).extension().wstring();
-               std::wstring filename = parameters()[2];
-               filename.append(extension);
+       auto filename = parameters()[2];
+       auto proxy = cg_registry_->get_or_create_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER), filename);
 
-               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).add(layer, filename, bDoStart, label, (pDataString!=0) ? pDataString : L"");
-               SetReplyString(L"202 CG OK\r\n");
-       }
-       else
+       if (proxy == core::cg_proxy::empty())
        {
                CASPAR_LOG(warning) << "Could not find template " << parameters()[2];
                SetReplyString(L"404 CG ERROR\r\n");
        }
+       else
+       {
+               proxy->add(layer, filename, bDoStart, label, (pDataString != 0) ? pDataString : L"");
+       }
+
        return true;
 }
 
@@ -1109,7 +1276,7 @@ bool CGCommand::DoExecutePlay()
                        return false;
                }
                int layer = boost::lexical_cast<int>(parameters()[1]);
-               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).play(layer);
+               cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->play(layer);
        }
        else
        {
@@ -1131,7 +1298,7 @@ bool CGCommand::DoExecuteStop()
                        return false;
                }
                int layer = boost::lexical_cast<int>(parameters()[1]);
-               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).stop(layer, 0);
+               cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->stop(layer, 0);
        }
        else 
        {
@@ -1154,7 +1321,7 @@ bool CGCommand::DoExecuteNext()
                }
 
                int layer = boost::lexical_cast<int>(parameters()[1]);
-               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).next(layer);
+               cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->next(layer);
        }
        else 
        {
@@ -1177,7 +1344,7 @@ bool CGCommand::DoExecuteRemove()
                }
 
                int layer = boost::lexical_cast<int>(parameters()[1]);
-               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).remove(layer);
+               cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->remove(layer);
        }
        else 
        {
@@ -1191,7 +1358,7 @@ bool CGCommand::DoExecuteRemove()
 
 bool CGCommand::DoExecuteClear() 
 {
-       channel()->stage().clear(layer_index(flash::cg_proxy::DEFAULT_LAYER));
+       channel()->stage().clear(layer_index(core::cg_proxy::DEFAULT_LAYER));
        SetReplyString(L"202 CG OK\r\n");
        return true;
 }
@@ -1207,18 +1374,18 @@ bool CGCommand::DoExecuteUpdate()
                }
                                                
                std::wstring dataString = parameters().at(2);                           
-               if(dataString.at(0) != L'<')
+               if (dataString.at(0) != L'<' && dataString.at(0) != L'{')
                {
-                       //The data is not an XML-string, it must be a filename
+                       //The data is not XML or Json, it must be a filename
                        std::wstring filename = env::data_folder();
                        filename.append(dataString);
                        filename.append(L".ftd");
 
-                       dataString = read_file(boost::filesystem::wpath(filename));
+                       dataString = read_file(boost::filesystem::path(filename));
                }               
 
                int layer = boost::lexical_cast<int>(parameters()[1]);
-               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).update(layer, dataString);
+               cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->update(layer, dataString);
        }
        catch(...)
        {
@@ -1243,7 +1410,7 @@ bool CGCommand::DoExecuteInvoke()
                        return false;
                }
                int layer = boost::lexical_cast<int>(parameters()[1]);
-               auto result = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).invoke(layer, parameters()[2]);
+               auto result = cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->invoke(layer, parameters()[2]);
                replyString << result << L"\r\n";
        }
        else 
@@ -1270,13 +1437,13 @@ bool CGCommand::DoExecuteInfo()
                }
 
                int layer = boost::lexical_cast<int>(parameters()[1]);
-               auto desc = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).description(layer);
+               auto desc = cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->description(layer);
                
                replyString << desc << L"\r\n";
        }
        else 
        {
-               auto info = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).template_host_info();
+               auto info = cg_registry_->get_proxy(channel(), layer_index(core::cg_proxy::DEFAULT_LAYER))->template_host_info();
                replyString << info << L"\r\n";
        }       
 
@@ -1312,12 +1479,20 @@ bool DataCommand::DoExecuteStore()
        filename.append(parameters()[1]);
        filename.append(L".ftd");
 
-       auto data_path = boost::filesystem::wpath(
-               boost::filesystem::wpath(filename).parent_path());
+       auto data_path = boost::filesystem::path(filename).parent_path().wstring();
+       auto found_data_path = find_case_insensitive(data_path);
+
+       if (found_data_path)
+               data_path = *found_data_path;
 
        if(!boost::filesystem::exists(data_path))
                boost::filesystem::create_directories(data_path);
 
+       auto found_filename = find_case_insensitive(filename);
+
+       if (found_filename)
+               filename = *found_filename; // Overwrite case insensitive.
+
        boost::filesystem::wofstream datafile(filename);
        if(!datafile) 
        {
@@ -1346,7 +1521,12 @@ bool DataCommand::DoExecuteRetrieve()
        filename.append(parameters()[1]);
        filename.append(L".ftd");
 
-       std::wstring file_contents = read_file(boost::filesystem::wpath(filename));
+       std::wstring file_contents;
+
+       auto found_file = find_case_insensitive(filename);
+
+       if (found_file)
+               file_contents = read_file(boost::filesystem::path(*found_file));
 
        if (file_contents.empty()) 
        {
@@ -1416,7 +1596,7 @@ bool DataCommand::DoExecuteList()
                        if(!boost::iequals(itr->path().extension().wstring(), L".ftd"))
                                continue;
                        
-                       auto relativePath = boost::filesystem::wpath(itr->path().wstring().substr(env::data_folder().size()-1, itr->path().wstring().size()));
+                       auto relativePath = boost::filesystem::path(itr->path().wstring().substr(env::data_folder().size()-1, itr->path().wstring().size()));
                        
                        auto str = relativePath.replace_extension(L"").generic_wstring();
                        if(str[0] == L'\\' || str[0] == L'/')
@@ -1442,9 +1622,9 @@ bool CinfCommand::DoExecute()
                for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end && info.empty(); ++itr)
                {
                        auto path = itr->path();
-                       auto file = path.replace_extension(L"").filename();
-                       if(boost::iequals(file.wstring(), parameters().at(0)))
-                               info += MediaInfo(itr->path(), repo_) + L"\r\n";
+                       auto file = path.replace_extension(L"").filename().wstring();
+                       if(boost::iequals(file, parameters().at(0)))
+                               info += MediaInfo(itr->path(), system_info_repo_) + L"\r\n";
                }
 
                if(info.empty())
@@ -1483,12 +1663,10 @@ bool InfoCommand::DoExecute()
                {               
                        replyString << L"201 INFO TEMPLATE OK\r\n";
 
-                       // Needs to be extended for any file, not just flash.
-
-                       auto filename = flash::find_template(env::template_folder() + parameters().at(1));
+                       auto filename = parameters().at(1);
                                                
                        std::wstringstream str;
-                       str << u16(flash::read_template_meta_info(filename));
+                       str << u16(cg_registry_->read_meta_info(filename));
                        boost::property_tree::wptree info;
                        boost::property_tree::xml_parser::read_xml(str, info, boost::property_tree::xml_parser::trim_whitespace | boost::property_tree::xml_parser::no_comments);
 
@@ -1506,7 +1684,7 @@ bool InfoCommand::DoExecute()
 
                        boost::property_tree::wptree info;
                        info.add_child(L"paths", caspar::env::properties().get_child(L"configuration.paths"));
-                       info.add(L"paths.initial-path", boost::filesystem::initial_path().wstring() + L"\\");
+                       info.add(L"paths.initial-path", boost::filesystem::initial_path().wstring() + L"/");
 
                        boost::property_tree::write_xml(replyString, info, w);
                }
@@ -1520,7 +1698,7 @@ bool InfoCommand::DoExecute()
                        info.add(L"system.os.description",                      caspar::os_description());
                        info.add(L"system.cpu",                                         caspar::cpu_info());
 
-                       repo_->fill_information(info);
+                       system_info_repo_->fill_information(info);
                                                
                        boost::property_tree::write_xml(replyString, info, w);
                }
@@ -1599,19 +1777,11 @@ bool InfoCommand::DoExecute()
 
 bool ClsCommand::DoExecute()
 {
-/*
-               wav = audio
-               mp3 = audio
-               swf     = movie
-               dv  = movie
-               tga = still
-               col = still
-       */
        try
        {
                std::wstringstream replyString;
                replyString << L"200 CLS OK\r\n";
-               replyString << ListMedia(repo_);
+               replyString << ListMedia(system_info_repo_);
                replyString << L"\r\n";
                SetReplyString(boost::to_upper_copy(replyString.str()));
        }
@@ -1632,7 +1802,7 @@ bool TlsCommand::DoExecute()
                std::wstringstream replyString;
                replyString << L"200 TLS OK\r\n";
 
-               replyString << ListTemplates();
+               replyString << ListTemplates(cg_registry_);
                replyString << L"\r\n";
 
                SetReplyString(replyString.str());
@@ -1652,7 +1822,7 @@ bool VersionCommand::DoExecute()
 
        if (parameters().size() > 0 && !boost::iequals(parameters()[0], L"SERVER"))
        {
-               auto version = repo_->get_version(parameters().at(0));
+               auto version = system_info_repo_->get_version(parameters().at(0));
 
                if (version.empty())
                        replyString = L"403 VERSION ERROR\r\n";
@@ -1858,7 +2028,12 @@ bool ThumbnailCommand::DoExecuteRetrieve()
        filename.append(parameters()[1]);
        filename.append(L".png");
 
-       std::wstring file_contents = read_file_base64(boost::filesystem::wpath(filename));
+       std::wstring file_contents;
+
+       auto found_file = find_case_insensitive(filename);
+
+       if (found_file)
+               file_contents = read_file_base64(boost::filesystem::path(*found_file));
 
        if (file_contents.empty())
        {
@@ -1887,7 +2062,7 @@ bool ThumbnailCommand::DoExecuteList()
                        if(!boost::iequals(itr->path().extension().wstring(), L".png"))
                                continue;
 
-                       auto relativePath = boost::filesystem::wpath(itr->path().wstring().substr(env::thumbnails_folder().size()-1, itr->path().wstring().size()));
+                       auto relativePath = boost::filesystem::path(itr->path().wstring().substr(env::thumbnails_folder().size()-1, itr->path().wstring().size()));
 
                        auto str = relativePath.replace_extension(L"").generic_wstring();
                        if(str[0] == '\\' || str[0] == '/')