]> git.sesse.net Git - casparcg/blobdiff - protocol/amcp/AMCPCommandsImpl.cpp
set svn:eol-style native on .h and .cpp files
[casparcg] / protocol / amcp / AMCPCommandsImpl.cpp
index 2026b852a7d4748352aff3b98a46fd550afb6416..eaa3f35c6f85079f7076609aa3dda7dd341a6f16 100644 (file)
-/*\r
-* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
-*\r
-* This file is part of CasparCG (www.casparcg.com).\r
-*\r
-* CasparCG is free software: you can redistribute it and/or modify\r
-* it under the terms of the GNU General Public License as published by\r
-* the Free Software Foundation, either version 3 of the License, or\r
-* (at your option) any later version.\r
-*\r
-* CasparCG is distributed in the hope that it will be useful,\r
-* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-* GNU General Public License for more details.\r
-*\r
-* You should have received a copy of the GNU General Public License\r
-* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
-*\r
-* Author: Nicklas P Andersson\r
-*/\r
-\r
-#include "../StdAfx.h"\r
-\r
-#if defined(_MSC_VER)\r
-#pragma warning (push, 1) // TODO: Legacy code, just disable warnings\r
-#endif\r
-\r
-#include "AMCPCommandsImpl.h"\r
-#include "AMCPProtocolStrategy.h"\r
-\r
-#include <common/env.h>\r
-\r
-#include <common/log/log.h>\r
-#include <common/diagnostics/graph.h>\r
-#include <common/os/windows/current_version.h>\r
-#include <common/os/windows/system_info.h>\r
-\r
-#include <core/producer/frame_producer.h>\r
-#include <core/video_format.h>\r
-#include <core/producer/transition/transition_producer.h>\r
-#include <core/producer/frame/frame_transform.h>\r
-#include <core/producer/stage.h>\r
-#include <core/producer/layer.h>\r
-#include <core/mixer/mixer.h>\r
-#include <core/consumer/output.h>\r
-\r
-#include <modules/bluefish/bluefish.h>\r
-#include <modules/decklink/decklink.h>\r
-#include <modules/ffmpeg/ffmpeg.h>\r
-#include <modules/flash/flash.h>\r
-#include <modules/flash/util/swf.h>\r
-#include <modules/flash/producer/flash_producer.h>\r
-#include <modules/flash/producer/cg_producer.h>\r
-#include <modules/ffmpeg/producer/util/util.h>\r
-#include <modules/image/image.h>\r
-\r
-#include <algorithm>\r
-#include <locale>\r
-#include <fstream>\r
-#include <cctype>\r
-#include <io.h>\r
-\r
-#include <boost/date_time/posix_time/posix_time.hpp>\r
-#include <boost/lexical_cast.hpp>\r
-#include <boost/algorithm/string.hpp>\r
-#include <boost/filesystem.hpp>\r
-#include <boost/regex.hpp>\r
-#include <boost/property_tree/xml_parser.hpp>\r
-\r
-/* Return codes\r
-\r
-100 [action]                   Information om att något har hänt  \r
-101 [action]                   Information om att något har hänt, en rad data skickas  \r
-\r
-202 [kommando] OK              Kommandot har utförts  \r
-201 [kommando] OK              Kommandot har utförts, och en rad data skickas tillbaka  \r
-200 [kommando] OK              Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad  \r
-\r
-400 ERROR                              Kommandot kunde inte förstås  \r
-401 [kommando] ERROR   Ogiltig kanal  \r
-402 [kommando] ERROR   Parameter saknas  \r
-403 [kommando] ERROR   Ogiltig parameter  \r
-404 [kommando] ERROR   Mediafilen hittades inte  \r
-\r
-500 FAILED                             Internt configurationfel  \r
-501 [kommando] FAILED  Internt configurationfel  \r
-502 [kommando] FAILED  Oläslig mediafil  \r
-\r
-600 [kommando] FAILED  funktion ej implementerad\r
-*/\r
-\r
-namespace caspar { namespace protocol {\r
-\r
-using namespace core;\r
-\r
-std::wstring MediaInfo(const boost::filesystem::wpath& path)\r
-{\r
-       if(boost::filesystem::is_regular_file(path))\r
-       {\r
-               std::wstring clipttype = TEXT(" N/A ");\r
-               std::wstring extension = boost::to_upper_copy(path.extension());\r
-               if(extension == TEXT(".TGA") || extension == TEXT(".COL") || extension == L".PNG" || extension == L".JPEG" || extension == L".JPG" ||\r
-                       extension == L"GIF" || extension == L"BMP")\r
-                       clipttype = TEXT(" STILL ");\r
-               else if(extension == TEXT(".WAV") || extension == TEXT(".MP3"))\r
-                       clipttype = TEXT(" STILL ");\r
-               else if(caspar::ffmpeg::is_valid_file(path.file_string()) || extension == L".CT")\r
-                       clipttype = TEXT(" MOVIE ");\r
-\r
-               if(clipttype != TEXT(" N/A "))\r
-               {               \r
-                       auto is_not_digit = [](char c){ return std::isdigit(c) == 0; };\r
-\r
-                       auto relativePath = boost::filesystem::wpath(path.file_string().substr(env::media_folder().size()-1, path.file_string().size()));\r
-\r
-                       auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(path)));\r
-                       writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end());\r
-                       auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
-\r
-                       auto sizeStr = boost::lexical_cast<std::wstring>(boost::filesystem::file_size(path));\r
-                       sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end());\r
-                       auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
-                               \r
-                       auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
-                       if(str[0] == '\\' || str[0] == '/')\r
-                               str = std::wstring(str.begin() + 1, str.end());\r
-\r
-                       return std::wstring() + TEXT("\"") + str +\r
-                                       + TEXT("\" ") + clipttype +\r
-                                       + TEXT(" ") + sizeStr +\r
-                                       + TEXT(" ") + writeTimeWStr +\r
-                                       + TEXT("\r\n");         \r
-               }       \r
-       }\r
-       return L"";\r
-}\r
-\r
-std::wstring ListMedia()\r
-{      \r
-       std::wstringstream replyString;\r
-       for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr) \r
-               replyString << MediaInfo(itr->path());\r
-       \r
-       return boost::to_upper_copy(replyString.str());\r
-}\r
-\r
-std::wstring ListTemplates() \r
-{\r
-       std::wstringstream replyString;\r
-\r
-       for (boost::filesystem::wrecursive_directory_iterator itr(env::template_folder()), end; itr != end; ++itr)\r
-       {               \r
-               if(boost::filesystem::is_regular_file(itr->path()) && (itr->path().extension() == L".ft" || itr->path().extension() == L".ct"))\r
-               {\r
-                       auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::template_folder().size()-1, itr->path().file_string().size()));\r
-\r
-                       auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(itr->path())));\r
-                       writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), [](char c){ return std::isdigit(c) == 0;}), writeTimeStr.end());\r
-                       auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
-\r
-                       auto sizeStr = boost::lexical_cast<std::string>(boost::filesystem::file_size(itr->path()));\r
-                       sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), [](char c){ return std::isdigit(c) == 0;}), sizeStr.end());\r
-\r
-                       auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
-\r
-                       std::wstring dir = relativePath.parent_path().external_directory_string();\r
-                       std::wstring file = boost::to_upper_copy(relativePath.filename());\r
-                       relativePath = boost::filesystem::wpath(dir + L"/" + file);\r
-                                               \r
-                       auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
-                       if(str[0] == '\\' || str[0] == '/')\r
-                               str = std::wstring(str.begin() + 1, str.end());\r
-\r
-                       replyString << TEXT("\"") << str\r
-                                               << TEXT("\" ") << sizeWStr\r
-                                               << TEXT(" ") << writeTimeWStr\r
-                                               << TEXT("\r\n");                \r
-               }\r
-       }\r
-       return replyString.str();\r
-}\r
-\r
-namespace amcp {\r
-       \r
-AMCPCommand::AMCPCommand() : channelIndex_(0), scheduling_(Default), layerIndex_(-1)\r
-{}\r
-\r
-void AMCPCommand::SendReply()\r
-{\r
-       if(!pClientInfo_) \r
-               return;\r
-\r
-       if(replyString_.empty())\r
-               return;\r
-       pClientInfo_->Send(replyString_);\r
-}\r
-\r
-void AMCPCommand::Clear() \r
-{\r
-       pChannel_->stage()->clear();\r
-       pClientInfo_.reset();\r
-       channelIndex_ = 0;\r
-       _parameters.clear();\r
-}\r
-\r
-bool DiagnosticsCommand::DoExecute()\r
-{      \r
-       try\r
-       {\r
-               diagnostics::show_graphs(true);\r
-\r
-               SetReplyString(TEXT("202 DIAG OK\r\n"));\r
-\r
-               return true;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 DIAG FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-bool CallCommand::DoExecute()\r
-{      \r
-       //Perform loading of the clip\r
-       try\r
-       {\r
-               auto what = _parameters.at(0);\r
-\r
-               std::wstring param = _parameters2.at(1);\r
-               for(auto it = std::begin(_parameters2)+2; it != std::end(_parameters2); ++it)\r
-                       param += L" " + *it;\r
-               \r
-               boost::unique_future<std::wstring> result;\r
-               if(what == L"B")\r
-                       result = GetChannel()->stage()->call(GetLayerIndex(), false, param);\r
-               else if(what == L"F")\r
-                       result = GetChannel()->stage()->call(GetLayerIndex(), true, param);\r
-               else\r
-                       result = GetChannel()->stage()->call(GetLayerIndex(), true, _parameters.at(0) + L" " + param);\r
-       \r
-               if(!result.timed_wait(boost::posix_time::seconds(2)))\r
-                       BOOST_THROW_EXCEPTION(timed_out());\r
-\r
-               CASPAR_LOG(info) << "Executed call: " <<  _parameters[0] << TEXT(" successfully");\r
-               \r
-               std::wstringstream replyString;\r
-               if(result.get().empty())\r
-                       replyString << TEXT("202 CALL OK\r\n");\r
-               else\r
-                       replyString << TEXT("201 CALL OK\r\n") << result.get() << L"\r\n";\r
-               \r
-               SetReplyString(replyString.str());\r
-\r
-               return true;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 CALL FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-bool MixerCommand::DoExecute()\r
-{      \r
-       //Perform loading of the clip\r
-       try\r
-       {       \r
-               if(_parameters[0] == L"KEYER" || _parameters[0] == L"IS_KEY")\r
-               {\r
-                       bool value = lexical_cast_or_default(_parameters.at(1), false);\r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.is_key = value;\r
-                               return transform;                                       \r
-                       };\r
-\r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform);\r
-               }\r
-               else if(_parameters[0] == L"OPACITY")\r
-               {\r
-                       int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
-\r
-                       double value = boost::lexical_cast<double>(_parameters.at(1));\r
-                       \r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.opacity = value;\r
-                               return transform;                                       \r
-                       };\r
-\r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
-               }\r
-               else if(_parameters[0] == L"FILL" || _parameters[0] == L"FILL_RECT")\r
-               {\r
-                       int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
-                       double x        = boost::lexical_cast<double>(_parameters.at(1));\r
-                       double y        = boost::lexical_cast<double>(_parameters.at(2));\r
-                       double x_s      = boost::lexical_cast<double>(_parameters.at(3));\r
-                       double y_s      = boost::lexical_cast<double>(_parameters.at(4));\r
-\r
-                       auto transform = [=](frame_transform transform) mutable -> frame_transform\r
-                       {\r
-                               transform.fill_translation[0]   = x;\r
-                               transform.fill_translation[1]   = y;\r
-                               transform.fill_scale[0]                 = x_s;\r
-                               transform.fill_scale[1]                 = y_s;\r
-                               transform.clip_translation[0]   = x;\r
-                               transform.clip_translation[1]   = y;\r
-                               transform.clip_scale[0]                 = x_s;\r
-                               transform.clip_scale[1]                 = y_s;\r
-                               return transform;\r
-                       };\r
-                               \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
-               }\r
-               else if(_parameters[0] == L"CLIP" || _parameters[0] == L"CLIP_RECT")\r
-               {\r
-                       int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
-                       double x        = boost::lexical_cast<double>(_parameters.at(1));\r
-                       double y        = boost::lexical_cast<double>(_parameters.at(2));\r
-                       double x_s      = boost::lexical_cast<double>(_parameters.at(3));\r
-                       double y_s      = boost::lexical_cast<double>(_parameters.at(4));\r
-\r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.clip_translation[0]   = x;\r
-                               transform.clip_translation[1]   = y;\r
-                               transform.clip_scale[0]                 = x_s;\r
-                               transform.clip_scale[1]                 = y_s;\r
-                               return transform;\r
-                       };\r
-                               \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
-               }\r
-               else if(_parameters[0] == L"GRID")\r
-               {\r
-                       int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
-                       int n = boost::lexical_cast<int>(_parameters.at(1));\r
-                       double delta = 1.0/static_cast<double>(n);\r
-                       for(int x = 0; x < n; ++x)\r
-                       {\r
-                               for(int y = 0; y < n; ++y)\r
-                               {\r
-                                       int index = x+y*n+1;\r
-                                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                                       {               \r
-                                               transform.fill_translation[0]   = x*delta;\r
-                                               transform.fill_translation[1]   = y*delta;\r
-                                               transform.fill_scale[0]                 = delta;\r
-                                               transform.fill_scale[1]                 = delta;\r
-                                               transform.clip_translation[0]   = x*delta;\r
-                                               transform.clip_translation[1]   = y*delta;\r
-                                               transform.clip_scale[0]                 = delta;\r
-                                               transform.clip_scale[1]                 = delta;                        \r
-                                               return transform;\r
-                                       };\r
-                                       GetChannel()->stage()->apply_frame_transform(index, transform, duration, tween);\r
-                               }\r
-                       }\r
-               }\r
-               else if(_parameters[0] == L"BLEND")\r
-               {\r
-                       auto blend_str = _parameters.at(1);                                                             \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str));      \r
-               }\r
-               else if(_parameters[0] == L"BRIGHTNESS")\r
-               {\r
-                       auto value = boost::lexical_cast<double>(_parameters.at(1));\r
-                       int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.brightness = value;\r
-                               return transform;\r
-                       };\r
-                               \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
-               }\r
-               else if(_parameters[0] == L"SATURATION")\r
-               {\r
-                       auto value = boost::lexical_cast<double>(_parameters.at(1));\r
-                       int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.saturation = value;\r
-                               return transform;\r
-                       };\r
-                               \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
-               }\r
-               else if(_parameters[0] == L"CONTRAST")\r
-               {\r
-                       auto value = boost::lexical_cast<double>(_parameters.at(1));\r
-                       int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.contrast = value;\r
-                               return transform;\r
-                       };\r
-                               \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
-               }\r
-               else if(_parameters[0] == L"LEVELS")\r
-               {\r
-                       levels value;\r
-                       value.min_input  = boost::lexical_cast<double>(_parameters.at(1));\r
-                       value.max_input  = boost::lexical_cast<double>(_parameters.at(2));\r
-                       value.gamma              = boost::lexical_cast<double>(_parameters.at(3));\r
-                       value.min_output = boost::lexical_cast<double>(_parameters.at(4));\r
-                       value.max_output = boost::lexical_cast<double>(_parameters.at(5));\r
-                       int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear";\r
-\r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.levels = value;\r
-                               return transform;\r
-                       };\r
-                               \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
-               }\r
-               else if(_parameters[0] == L"VOLUME")\r
-               {\r
-                       int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                       std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
-                       double value = boost::lexical_cast<double>(_parameters[1]);\r
-\r
-                       auto transform = [=](frame_transform transform) -> frame_transform\r
-                       {\r
-                               transform.volume = value;\r
-                               return transform;\r
-                       };\r
-                               \r
-                       int layer = GetLayerIndex();\r
-                       GetChannel()->stage()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
-               }\r
-               else if(_parameters[0] == L"CLEAR")\r
-               {\r
-                       int layer = GetLayerIndex(std::numeric_limits<int>::max());\r
-                       if(layer ==     std::numeric_limits<int>::max())\r
-                               GetChannel()->stage()->clear_transforms();\r
-                       else\r
-                               GetChannel()->stage()->clear_transforms(layer);\r
-               }\r
-               else\r
-               {\r
-                       SetReplyString(TEXT("404 MIXER ERROR\r\n"));\r
-                       return false;\r
-               }\r
-       \r
-               SetReplyString(TEXT("202 MIXER OK\r\n"));\r
-\r
-               return true;\r
-       }\r
-       catch(file_not_found&)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("404 MIXER ERROR\r\n"));\r
-               return false;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 MIXER FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-bool SwapCommand::DoExecute()\r
-{      \r
-       //Perform loading of the clip\r
-       try\r
-       {\r
-               if(GetLayerIndex(-1) != -1)\r
-               {\r
-                       std::vector<std::string> strs;\r
-                       boost::split(strs, _parameters[0], boost::is_any_of("-"));\r
-                       \r
-                       auto ch1 = GetChannel();\r
-                       auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);\r
-\r
-                       int l1 = GetLayerIndex();\r
-                       int l2 = boost::lexical_cast<int>(strs.at(1));\r
-\r
-                       ch1->stage()->swap_layer(l1, l2, ch2->stage());\r
-               }\r
-               else\r
-               {\r
-                       auto ch1 = GetChannel();\r
-                       auto ch2 = GetChannels().at(boost::lexical_cast<int>(_parameters[0])-1);\r
-                       ch1->stage()->swap_layers(ch2->stage());\r
-               }\r
-\r
-               CASPAR_LOG(info) << "Swapped successfully";\r
-\r
-               SetReplyString(TEXT("202 SWAP OK\r\n"));\r
-\r
-               return true;\r
-       }\r
-       catch(file_not_found&)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("404 SWAP ERROR\r\n"));\r
-               return false;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 SWAP FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-bool AddCommand::DoExecute()\r
-{      \r
-       //Perform loading of the clip\r
-       try\r
-       {\r
-               auto consumer = create_consumer(_parameters);\r
-               GetChannel()->output()->add(GetLayerIndex(consumer->index()), consumer);\r
-       \r
-               CASPAR_LOG(info) << "Added " <<  _parameters[0] << TEXT(" successfully");\r
-\r
-               SetReplyString(TEXT("202 ADD OK\r\n"));\r
-\r
-               return true;\r
-       }\r
-       catch(file_not_found&)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("404 ADD ERROR\r\n"));\r
-               return false;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 ADD FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-bool RemoveCommand::DoExecute()\r
-{      \r
-       //Perform loading of the clip\r
-       try\r
-       {\r
-               auto index = GetLayerIndex(std::numeric_limits<int>::min());\r
-               if(index == std::numeric_limits<int>::min())\r
-                       index = create_consumer(_parameters)->index();\r
-\r
-               GetChannel()->output()->remove(index);\r
-\r
-               SetReplyString(TEXT("202 REMOVE OK\r\n"));\r
-\r
-               return true;\r
-       }\r
-       catch(file_not_found&)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("404 REMOVE ERROR\r\n"));\r
-               return false;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 REMOVE FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-bool LoadCommand::DoExecute()\r
-{      \r
-       //Perform loading of the clip\r
-       try\r
-       {\r
-               _parameters[0] = _parameters[0];\r
-               auto pFP = create_producer(GetChannel()->mixer(), _parameters);         \r
-               GetChannel()->stage()->load(GetLayerIndex(), pFP, true);\r
-       \r
-               CASPAR_LOG(info) << "Loaded " <<  _parameters[0] << TEXT(" successfully");\r
-\r
-               SetReplyString(TEXT("202 LOAD OK\r\n"));\r
-\r
-               return true;\r
-       }\r
-       catch(file_not_found&)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("404 LOAD ERROR\r\n"));\r
-               return false;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 LOAD FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-\r
-\r
-//std::function<std::wstring()> channel_cg_add_command::parse(const std::wstring& message, const std::vector<renderer::render_device_ptr>& channels)\r
-//{\r
-//     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>.*)?");\r
-//\r
-//     boost::wsmatch what;\r
-//     if(!boost::regex_match(message, what, expr))\r
-//             return nullptr;\r
-//\r
-//     auto info = channel_info::parse(what, channels);\r
-//\r
-//     int flash_layer_index = boost::lexical_cast<int>(what["FLASH_LAYER"].str());\r
-//\r
-//     std::wstring templatename = what["TEMPLATE"].str();\r
-//     bool play_on_load = what["PLAY_ON_LOAD"].matched ? what["PLAY_ON_LOAD"].str() != L"0" : 0;\r
-//     std::wstring start_label = what["START_LABEL"].str();   \r
-//     std::wstring data = get_data(what["DATA"].str());\r
-//     \r
-//     boost::replace_all(templatename, "\"", "");\r
-//\r
-//     return [=]() -> std::wstring\r
-//     {       \r
-//             std::wstring fullFilename = flash::flash_producer::find_template(server::template_folder() + templatename);\r
-//             if(fullFilename.empty())\r
-//                     BOOST_THROW_EXCEPTION(file_not_found());\r
-//     \r
-//             std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
-//             std::wstring filename = templatename;\r
-//             filename.append(extension);\r
-//\r
-//             flash::flash::get_default_cg_producer(info.video_channel, std::max<int>(DEFAULT_CHANNEL_LAYER+1, info.layer_index))\r
-//                     ->add(flash_layer_index, filename, play_on_load, start_label, data);\r
-//\r
-//             CASPAR_LOG(info) << L"Executed [amcp_channel_cg_add]";\r
-//             return L"";\r
-//     };\r
-\r
-bool LoadbgCommand::DoExecute()\r
-{\r
-       transition_info transitionInfo;\r
-       \r
-       bool bLoop = false;\r
-\r
-       // TRANSITION\r
-\r
-       std::wstring message;\r
-       for(size_t n = 0; n < _parameters.size(); ++n)\r
-               message += _parameters[n] + L" ";\r
-               \r
-       static const boost::wregex expr(L".*(?<TRANSITION>CUT|PUSH|SLIDE|WIPE|MIX)\\s*(?<DURATION>\\d+)\\s*(?<TWEEN>(LINEAR)|(EASE[^\\s]*))?\\s*(?<DIRECTION>FROMLEFT|FROMRIGHT|LEFT|RIGHT)?.*");\r
-       boost::wsmatch what;\r
-       if(boost::regex_match(message, what, expr))\r
-       {\r
-               auto transition = what["TRANSITION"].str();\r
-               transitionInfo.duration = lexical_cast_or_default<size_t>(what["DURATION"].str());\r
-               auto direction = what["DIRECTION"].matched ? what["DIRECTION"].str() : L"";\r
-               auto tween = what["TWEEN"].matched ? what["TWEEN"].str() : L"";\r
-               transitionInfo.tweener = get_tweener(tween);            \r
-\r
-               if(transition == TEXT("CUT"))\r
-                       transitionInfo.type = transition::cut;\r
-               else if(transition == TEXT("MIX"))\r
-                       transitionInfo.type = transition::mix;\r
-               else if(transition == TEXT("PUSH"))\r
-                       transitionInfo.type = transition::push;\r
-               else if(transition == TEXT("SLIDE"))\r
-                       transitionInfo.type = transition::slide;\r
-               else if(transition == TEXT("WIPE"))\r
-                       transitionInfo.type = transition::wipe;\r
-               \r
-               if(direction == TEXT("FROMLEFT"))\r
-                       transitionInfo.direction = transition_direction::from_left;\r
-               else if(direction == TEXT("FROMRIGHT"))\r
-                       transitionInfo.direction = transition_direction::from_right;\r
-               else if(direction == TEXT("LEFT"))\r
-                       transitionInfo.direction = transition_direction::from_right;\r
-               else if(direction == TEXT("RIGHT"))\r
-                       transitionInfo.direction = transition_direction::from_left;\r
-       }\r
-       \r
-       //Perform loading of the clip\r
-       try\r
-       {\r
-               _parameters[0] = _parameters[0];\r
-               auto pFP = create_producer(GetChannel()->mixer(), _parameters);\r
-               if(pFP == frame_producer::empty())\r
-                       BOOST_THROW_EXCEPTION(file_not_found() << msg_info(_parameters.size() > 0 ? u8(_parameters[0]) : ""));\r
-\r
-               bool auto_play = std::find(_parameters.begin(), _parameters.end(), L"AUTO") != _parameters.end();\r
-\r
-               auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().field_mode, pFP, transitionInfo);\r
-               GetChannel()->stage()->load(GetLayerIndex(), pFP2, false, auto_play ? transitionInfo.duration : -1); // TODO: LOOP\r
-       \r
-               CASPAR_LOG(info) << "Loaded " << _parameters[0] << TEXT(" successfully to background");\r
-               SetReplyString(TEXT("202 LOADBG OK\r\n"));\r
-\r
-               return true;\r
-       }\r
-       catch(file_not_found&)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("404 LOADBG ERROR\r\n"));\r
-               return false;\r
-       }\r
-       catch(...)\r
-       {\r
-               CASPAR_LOG_CURRENT_EXCEPTION();\r
-               SetReplyString(TEXT("502 LOADBG FAILED\r\n"));\r
-               return false;\r
-       }\r
-}\r
-\r
-bool PauseCommand::DoExecute()\r
-{\r
-       try\r
-       {\r
-               GetChannel()->stage()->pause(GetLayerIndex());\r
-               SetReplyString(TEXT("202 PAUSE OK\r\n"));\r
-               return true;\r
-       }\r
-       catch(...)\r
-       {\r
-               SetReplyString(TEXT("501 PAUSE FAILED\r\n"));\r
-       }\r
-\r
-       return false;\r
-}\r
-\r
-bool PlayCommand::DoExecute()\r
-{\r
-       try\r
-       {\r
-               if(!_parameters.empty())\r
-               {\r
-                       LoadbgCommand lbg;\r
-                       lbg.SetChannel(GetChannel());\r
-                       lbg.SetChannelIndex(GetChannelIndex());\r
-                       lbg.SetLayerIntex(GetLayerIndex());\r
-                       lbg.SetClientInfo(GetClientInfo());\r
-                       for(auto it = _parameters.begin(); it != _parameters.end(); ++it)\r
-                               lbg.AddParameter(*it);\r
-                       if(!lbg.Execute())\r
-                               CASPAR_LOG(warning) << " Failed to play.";\r
-\r
-                       CASPAR_LOG(info) << "Playing " << _parameters[0];\r
-               }\r
-\r
-               GetChannel()->stage()->play(GetLayerIndex());\r
-               \r
-               SetReplyString(TEXT("202 PLAY OK\r\n"));\r
-               return true;\r
-       }\r
-       catch(...)\r
-       {\r
-               SetReplyString(TEXT("501 PLAY FAILED\r\n"));\r
-       }\r
-\r
-       return false;\r
-}\r
-\r
-bool StopCommand::DoExecute()\r
-{\r
-       try\r
-       {\r
-               GetChannel()->stage()->stop(GetLayerIndex());\r
-               SetReplyString(TEXT("202 STOP OK\r\n"));\r
-               return true;\r
-       }\r
-       catch(...)\r
-       {\r
-               SetReplyString(TEXT("501 STOP FAILED\r\n"));\r
-       }\r
-\r
-       return false;\r
-}\r
-\r
-bool ClearCommand::DoExecute()\r
-{\r
-       int index = GetLayerIndex(std::numeric_limits<int>::min());\r
-       if(index != std::numeric_limits<int>::min())\r
-               GetChannel()->stage()->clear(index);\r
-       else\r
-               GetChannel()->stage()->clear();\r
-               \r
-       SetReplyString(TEXT("202 CLEAR OK\r\n"));\r
-\r
-       return true;\r
-}\r
-\r
-bool PrintCommand::DoExecute()\r
-{\r
-       GetChannel()->output()->add(create_consumer(boost::assign::list_of(L"IMAGE")));\r
-               \r
-       SetReplyString(TEXT("202 PRINT OK\r\n"));\r
-\r
-       return true;\r
-}\r
-\r
-bool LogCommand::DoExecute()\r
-{\r
-       if(_parameters.at(0) == L"LEVEL")\r
-               log::set_log_level(_parameters.at(1));\r
-\r
-       SetReplyString(TEXT("202 LOG OK\r\n"));\r
-\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecute()\r
-{\r
-       std::wstring command = _parameters[0];\r
-       if(command == TEXT("ADD"))\r
-               return DoExecuteAdd();\r
-       else if(command == TEXT("PLAY"))\r
-               return DoExecutePlay();\r
-       else if(command == TEXT("STOP"))\r
-               return DoExecuteStop();\r
-       else if(command == TEXT("NEXT"))\r
-               return DoExecuteNext();\r
-       else if(command == TEXT("REMOVE"))\r
-               return DoExecuteRemove();\r
-       else if(command == TEXT("CLEAR"))\r
-               return DoExecuteClear();\r
-       else if(command == TEXT("UPDATE"))\r
-               return DoExecuteUpdate();\r
-       else if(command == TEXT("INVOKE"))\r
-               return DoExecuteInvoke();\r
-       else if(command == TEXT("INFO"))\r
-               return DoExecuteInfo();\r
-\r
-       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-       return false;\r
-}\r
-\r
-bool CGCommand::ValidateLayer(const std::wstring& layerstring) {\r
-       int length = layerstring.length();\r
-       for(int i = 0; i < length; ++i) {\r
-               if(!_istdigit(layerstring[i])) {\r
-                       return false;\r
-               }\r
-       }\r
-\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteAdd() {\r
-       //CG 1 ADD 0 "template_folder/templatename" [STARTLABEL] 0/1 [DATA]\r
-\r
-       int layer = 0;                          //_parameters[1]\r
-//     std::wstring templateName;      //_parameters[2]\r
-       std::wstring label;             //_parameters[3]\r
-       bool bDoStart = false;          //_parameters[3] alt. _parameters[4]\r
-//     std::wstring data;                      //_parameters[4] alt. _parameters[5]\r
-\r
-       if(_parameters.size() < 4) \r
-       {\r
-               SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-               return false;\r
-       }\r
-       unsigned int dataIndex = 4;\r
-\r
-       if(!ValidateLayer(_parameters[1])) \r
-       {\r
-               SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-               return false;\r
-       }\r
-\r
-       layer = _ttoi(_parameters[1].c_str());\r
-\r
-       if(_parameters[3].length() > 1) \r
-       {       //read label\r
-               label = _parameters2[3];\r
-               ++dataIndex;\r
-\r
-               if(_parameters.size() > 4 && _parameters[4].length() > 0)       //read play-on-load-flag\r
-                       bDoStart = (_parameters[4][0]==TEXT('1')) ? true : false;\r
-               else \r
-               {\r
-                       SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-       }\r
-       else if(_parameters[3].length() > 0) {  //read play-on-load-flag\r
-               bDoStart = (_parameters[3][0]==TEXT('1')) ? true : false;\r
-       }\r
-       else \r
-       {\r
-               SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-               return false;\r
-       }\r
-\r
-       const TCHAR* pDataString = 0;\r
-       std::wstringstream data;\r
-       std::wstring dataFromFile;\r
-       if(_parameters.size() > dataIndex) \r
-       {       //read data\r
-               const std::wstring& dataString = _parameters2[dataIndex];\r
-\r
-               if(dataString[0] == TEXT('<')) //the data is an XML-string\r
-                       pDataString = dataString.c_str();\r
-               else \r
-               {\r
-                       //The data is not an XML-string, it must be a filename\r
-                       std::wstring filename = env::data_folder();\r
-                       filename.append(dataString);\r
-                       filename.append(TEXT(".ftd"));\r
-\r
-                       //open file\r
-                       std::wifstream datafile(filename.c_str());\r
-                       if(datafile) \r
-                       {\r
-                               //read all data\r
-                               data << datafile.rdbuf();\r
-                               datafile.close();\r
-\r
-                               //extract data to _parameters\r
-                               dataFromFile = data.str();\r
-                               pDataString = dataFromFile.c_str();\r
-                       }\r
-               }\r
-       }\r
-\r
-       std::wstring fullFilename = flash::find_template(env::template_folder() + _parameters[2]);\r
-       if(!fullFilename.empty())\r
-       {\r
-               std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
-               std::wstring filename = _parameters[2];\r
-               filename.append(extension);\r
-\r
-               flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->add(layer, filename, bDoStart, label, (pDataString!=0) ? pDataString : TEXT(""));\r
-               SetReplyString(TEXT("202 CG OK\r\n"));\r
-       }\r
-       else\r
-       {\r
-               CASPAR_LOG(warning) << "Could not find template " << _parameters[2];\r
-               SetReplyString(TEXT("404 CG ERROR\r\n"));\r
-       }\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecutePlay()\r
-{\r
-       if(_parameters.size() > 1)\r
-       {\r
-               if(!ValidateLayer(_parameters[1])) \r
-               {\r
-                       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-               int layer = _ttoi(_parameters[1].c_str());\r
-               flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->play(layer);\r
-       }\r
-       else\r
-       {\r
-               SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-               return true;\r
-       }\r
-\r
-       SetReplyString(TEXT("202 CG OK\r\n"));\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteStop() \r
-{\r
-       if(_parameters.size() > 1)\r
-       {\r
-               if(!ValidateLayer(_parameters[1])) \r
-               {\r
-                       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-               int layer = _ttoi(_parameters[1].c_str());\r
-               flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->stop(layer, 0);\r
-       }\r
-       else \r
-       {\r
-               SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-               return true;\r
-       }\r
-\r
-       SetReplyString(TEXT("202 CG OK\r\n"));\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteNext()\r
-{\r
-       if(_parameters.size() > 1) \r
-       {\r
-               if(!ValidateLayer(_parameters[1])) \r
-               {\r
-                       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-\r
-               int layer = _ttoi(_parameters[1].c_str());\r
-               flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->next(layer);\r
-       }\r
-       else \r
-       {\r
-               SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-               return true;\r
-       }\r
-\r
-       SetReplyString(TEXT("202 CG OK\r\n"));\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteRemove() \r
-{\r
-       if(_parameters.size() > 1) \r
-       {\r
-               if(!ValidateLayer(_parameters[1])) \r
-               {\r
-                       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-\r
-               int layer = _ttoi(_parameters[1].c_str());\r
-               flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->remove(layer);\r
-       }\r
-       else \r
-       {\r
-               SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-               return true;\r
-       }\r
-\r
-       SetReplyString(TEXT("202 CG OK\r\n"));\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteClear() \r
-{\r
-       GetChannel()->stage()->clear(GetLayerIndex(flash::cg_producer::DEFAULT_LAYER));\r
-       SetReplyString(TEXT("202 CG OK\r\n"));\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteUpdate() \r
-{\r
-       try\r
-       {\r
-               if(!ValidateLayer(_parameters.at(1)))\r
-               {\r
-                       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-                                               \r
-               std::wstring dataString = _parameters2.at(2);                           \r
-               if(dataString.at(0) != TEXT('<'))\r
-               {\r
-                       //The data is not an XML-string, it must be a filename\r
-                       std::wstring filename = env::data_folder();\r
-                       filename.append(dataString);\r
-                       filename.append(TEXT(".ftd"));\r
-\r
-                       //open file\r
-                       std::wifstream datafile(filename.c_str());\r
-                       if(datafile) \r
-                       {\r
-                               std::wstringstream data;\r
-                               //read all data\r
-                               data << datafile.rdbuf();\r
-                               datafile.close();\r
-\r
-                               //extract data to _parameters\r
-                               dataString = data.str();\r
-                       }\r
-               }               \r
-\r
-               int layer = _ttoi(_parameters.at(1).c_str());\r
-               flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->update(layer, dataString);\r
-       }\r
-       catch(...)\r
-       {\r
-               SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-               return true;\r
-       }\r
-\r
-       SetReplyString(TEXT("202 CG OK\r\n"));\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteInvoke() \r
-{\r
-       std::wstringstream replyString;\r
-       replyString << TEXT("201 CG OK\r\n");\r
-\r
-       if(_parameters.size() > 2)\r
-       {\r
-               if(!ValidateLayer(_parameters[1]))\r
-               {\r
-                       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-               int layer = _ttoi(_parameters[1].c_str());\r
-               auto result = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->invoke(layer, _parameters2[2]);\r
-               replyString << result << TEXT("\r\n"); \r
-       }\r
-       else \r
-       {\r
-               SetReplyString(TEXT("402 CG ERROR\r\n"));\r
-               return true;\r
-       }\r
-       \r
-       SetReplyString(replyString.str());\r
-       return true;\r
-}\r
-\r
-bool CGCommand::DoExecuteInfo() \r
-{\r
-       std::wstringstream replyString;\r
-       replyString << TEXT("201 CG OK\r\n");\r
-\r
-       if(_parameters.size() > 1)\r
-       {\r
-               if(!ValidateLayer(_parameters[1]))\r
-               {\r
-                       SetReplyString(TEXT("403 CG ERROR\r\n"));\r
-                       return false;\r
-               }\r
-\r
-               int layer = _ttoi(_parameters[1].c_str());\r
-               auto desc = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->description(layer);\r
-               \r
-               replyString << desc << TEXT("\r\n"); \r
-       }\r
-       else \r
-       {\r
-               auto info = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->template_host_info();\r
-               replyString << info << TEXT("\r\n"); \r
-       }       \r
-\r
-       SetReplyString(replyString.str());\r
-       return true;\r
-}\r
-\r
-bool DataCommand::DoExecute()\r
-{\r
-       std::wstring command = _parameters[0];\r
-       if(command == TEXT("STORE"))\r
-               return DoExecuteStore();\r
-       else if(command == TEXT("RETRIEVE"))\r
-               return DoExecuteRetrieve();\r
-       else if(command == TEXT("LIST"))\r
-               return DoExecuteList();\r
-\r
-       SetReplyString(TEXT("403 DATA ERROR\r\n"));\r
-       return false;\r
-}\r
-\r
-bool DataCommand::DoExecuteStore() \r
-{\r
-       if(_parameters.size() < 3) \r
-       {\r
-               SetReplyString(TEXT("402 DATA STORE ERROR\r\n"));\r
-               return false;\r
-       }\r
-\r
-       std::wstring filename = env::data_folder();\r
-       filename.append(_parameters[1]);\r
-       filename.append(TEXT(".ftd"));\r
-\r
-       std::wofstream datafile(filename.c_str());\r
-       if(!datafile) \r
-       {\r
-               SetReplyString(TEXT("501 DATA STORE FAILED\r\n"));\r
-               return false;\r
-       }\r
-\r
-       datafile << _parameters2[2];\r
-       datafile.close();\r
-\r
-       std::wstring replyString = TEXT("202 DATA STORE OK\r\n");\r
-       SetReplyString(replyString);\r
-       return true;\r
-}\r
-\r
-bool DataCommand::DoExecuteRetrieve() \r
-{\r
-       if(_parameters.size() < 2) \r
-       {\r
-               SetReplyString(TEXT("402 DATA RETRIEVE ERROR\r\n"));\r
-               return false;\r
-       }\r
-\r
-       std::wstring filename = env::data_folder();\r
-       filename.append(_parameters[1]);\r
-       filename.append(TEXT(".ftd"));\r
-\r
-       std::wifstream datafile(filename.c_str());\r
-       if(!datafile) \r
-       {\r
-               SetReplyString(TEXT("404 DATA RETRIEVE ERROR\r\n"));\r
-               return false;\r
-       }\r
-\r
-       std::wstringstream reply(TEXT("201 DATA RETRIEVE OK\r\n"));\r
-       std::wstring line;\r
-       bool bFirstLine = true;\r
-       while(std::getline(datafile, line))\r
-       {\r
-               if(!bFirstLine)\r
-                       reply << "\\n";\r
-               else\r
-                       bFirstLine = false;\r
-\r
-               reply << line;\r
-       }\r
-       datafile.close();\r
-\r
-       reply << "\r\n";\r
-       SetReplyString(reply.str());\r
-       return true;\r
-}\r
-\r
-bool DataCommand::DoExecuteList() \r
-{\r
-       std::wstringstream replyString;\r
-       replyString << TEXT("200 DATA LIST OK\r\n");\r
-\r
-       for (boost::filesystem::wrecursive_directory_iterator itr(env::data_folder()), end; itr != end; ++itr)\r
-       {                       \r
-               if(boost::filesystem::is_regular_file(itr->path()))\r
-               {\r
-                       if(!boost::iequals(itr->path().extension(), L".ftd"))\r
-                               continue;\r
-                       \r
-                       auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::data_folder().size()-1, itr->path().file_string().size()));\r
-                       \r
-                       auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
-                       if(str[0] == '\\' || str[0] == '/')\r
-                               str = std::wstring(str.begin() + 1, str.end());\r
-\r
-                       replyString << str << TEXT("\r\n");     \r
-               }\r
-       }\r
-       \r
-       replyString << TEXT("\r\n");\r
-\r
-       SetReplyString(boost::to_upper_copy(replyString.str()));\r
-       return true;\r
-}\r
-\r
-bool CinfCommand::DoExecute()\r
-{\r
-       std::wstringstream replyString;\r
-       \r
-       try\r
-       {\r
-               std::wstring info;\r
-               for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)\r
-               {\r
-                       auto path = itr->path();\r
-                       auto file = path.replace_extension(L"").filename();\r
-                       if(boost::iequals(file, _parameters.at(0)))\r
-                               info += MediaInfo(itr->path()) + L"\r\n";\r
-               }\r
-\r
-               if(info.empty())\r
-               {\r
-                       SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
-                       return false;\r
-               }\r
-               replyString << TEXT("200 CINF OK\r\n");\r
-               replyString << info << "\r\n";\r
-       }\r
-       catch(...)\r
-       {\r
-               SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
-               return false;\r
-       }\r
-       \r
-       SetReplyString(replyString.str());\r
-       return true;\r
-}\r
-\r
-void GenerateChannelInfo(int index, const safe_ptr<core::video_channel>& pChannel, std::wstringstream& replyString)\r
-{\r
-       replyString << index+1 << TEXT(" ") << pChannel->get_video_format_desc().name << TEXT(" PLAYING") << TEXT("\r\n");\r
-}\r
-\r
-bool InfoCommand::DoExecute()\r
-{\r
-       std::wstringstream replyString;\r
-       \r
-       boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);\r
-\r
-       try\r
-       {\r
-               if(_parameters.size() >= 1 && _parameters[0] == L"TEMPLATE")\r
-               {               \r
-                       replyString << L"201 INFO TEMPLATE OK\r\n";\r
-\r
-                       // Needs to be extended for any file, not just flash.\r
-\r
-                       auto filename = flash::find_template(env::template_folder() + _parameters.at(1));\r
-                                               \r
-                       std::wstringstream str;\r
-                       str << u16(flash::read_template_meta_info(filename));\r
-                       boost::property_tree::wptree info;\r
-                       boost::property_tree::xml_parser::read_xml(str, info, boost::property_tree::xml_parser::trim_whitespace | boost::property_tree::xml_parser::no_comments);\r
-\r
-                       boost::property_tree::xml_parser::write_xml(replyString, info, w);\r
-               }\r
-               else if(_parameters.size() >= 1 && _parameters[0] == L"CONFIG")\r
-               {               \r
-                       replyString << L"201 INFO CONFIG OK\r\n";\r
-\r
-                       boost::property_tree::write_xml(replyString, caspar::env::properties(), w);\r
-               }\r
-               else if(_parameters.size() >= 1 && _parameters[0] == L"PATHS")\r
-               {\r
-                       replyString << L"201 INFO PATHS OK\r\n";\r
-\r
-                       boost::property_tree::wptree info;\r
-                       info.add_child(L"paths", caspar::env::properties().get_child(L"configuration.paths"));\r
-                       info.add(L"paths.initial-path", boost::filesystem2::initial_path<boost::filesystem2::wpath>().directory_string() + L"\\");\r
-\r
-                       boost::property_tree::write_xml(replyString, info, w);\r
-               }\r
-               else if(_parameters.size() >= 1 && _parameters[0] == L"SYSTEM")\r
-               {\r
-                       replyString << L"201 INFO SYSTEM OK\r\n";\r
-                       \r
-                       boost::property_tree::wptree info;\r
-                       \r
-                       info.add(L"system.name",                                        caspar::get_system_product_name());\r
-                       info.add(L"system.windows.name",                        caspar::get_win_product_name());\r
-                       info.add(L"system.windows.service-pack",        caspar::get_win_sp_version());\r
-                       info.add(L"system.cpu",                                         caspar::get_cpu_info());\r
-       \r
-                       BOOST_FOREACH(auto device, caspar::decklink::get_device_list())\r
-                               info.add(L"system.decklink.device", device);\r
-\r
-                       BOOST_FOREACH(auto device, caspar::bluefish::get_device_list())\r
-                               info.add(L"system.bluefish.device", device);\r
-                               \r
-                       info.add(L"system.flash",                                       caspar::flash::get_version());\r
-                       info.add(L"system.free-image",                          caspar::image::get_version());\r
-                       info.add(L"system.ffmpeg.avcodec",                      caspar::ffmpeg::get_avcodec_version());\r
-                       info.add(L"system.ffmpeg.avformat",                     caspar::ffmpeg::get_avformat_version());\r
-                       info.add(L"system.ffmpeg.avfilter",                     caspar::ffmpeg::get_avfilter_version());\r
-                       info.add(L"system.ffmpeg.avutil",                       caspar::ffmpeg::get_avutil_version());\r
-                       info.add(L"system.ffmpeg.swscale",                      caspar::ffmpeg::get_swscale_version());\r
-                                               \r
-                       boost::property_tree::write_xml(replyString, info, w);\r
-               }\r
-               else if(_parameters.size() >= 1 && _parameters[0] == L"SERVER")\r
-               {\r
-                       replyString << L"201 INFO SYSTEM OK\r\n";\r
-                       \r
-                       boost::property_tree::wptree info;\r
-\r
-                       int index = 0;\r
-                       BOOST_FOREACH(auto channel, channels_)\r
-                               info.add_child(L"channels.channel", channel->info())\r
-                                       .add(L"index", ++index);\r
-                       \r
-                       boost::property_tree::write_xml(replyString, info, w);\r
-               }\r
-               else // channel\r
-               {                       \r
-                       if(_parameters.size() >= 1)\r
-                       {\r
-                               replyString << TEXT("201 INFO OK\r\n");\r
-                               boost::property_tree::wptree info;\r
-\r
-                               std::vector<std::wstring> split;\r
-                               boost::split(split, _parameters[0], boost::is_any_of("-"));\r
-                                       \r
-                               int layer = std::numeric_limits<int>::min();\r
-                               int channel = boost::lexical_cast<int>(split[0]) - 1;\r
-\r
-                               if(split.size() > 1)\r
-                                       layer = boost::lexical_cast<int>(split[1]);\r
-                               \r
-                               if(layer == std::numeric_limits<int>::min())\r
-                               {       \r
-                                       info.add_child(L"channel", channels_.at(channel)->info())\r
-                                                       .add(L"index", channel);\r
-                               }\r
-                               else\r
-                               {\r
-                                       if(_parameters.size() >= 2)\r
-                                       {\r
-                                               if(_parameters[1] == L"B")\r
-                                                       info.add_child(L"producer", channels_.at(channel)->stage()->background(layer).get()->info());\r
-                                               else\r
-                                                       info.add_child(L"producer", channels_.at(channel)->stage()->foreground(layer).get()->info());\r
-                                       }\r
-                                       else\r
-                                       {\r
-                                               info.add_child(L"layer", channels_.at(channel)->stage()->info(layer).get())\r
-                                                       .add(L"index", layer);\r
-                                       }\r
-                               }\r
-                               boost::property_tree::xml_parser::write_xml(replyString, info, w);\r
-                       }\r
-                       else\r
-                       {\r
-                               // This is needed for backwards compatibility with old clients\r
-                               replyString << TEXT("200 INFO OK\r\n");\r
-                               for(size_t n = 0; n < channels_.size(); ++n)\r
-                                       GenerateChannelInfo(n, channels_[n], replyString);\r
-                       }\r
-\r
-               }\r
-       }\r
-       catch(...)\r
-       {\r
-               SetReplyString(TEXT("403 INFO ERROR\r\n"));\r
-               return false;\r
-       }\r
-\r
-       replyString << TEXT("\r\n");\r
-       SetReplyString(replyString.str());\r
-       return true;\r
-}\r
-\r
-bool ClsCommand::DoExecute()\r
-{\r
-/*\r
-               wav = audio\r
-               mp3 = audio\r
-               swf     = movie\r
-               dv  = movie\r
-               tga = still\r
-               col = still\r
-       */\r
-       std::wstringstream replyString;\r
-       replyString << TEXT("200 CLS OK\r\n");\r
-       replyString << ListMedia();\r
-       replyString << TEXT("\r\n");\r
-       SetReplyString(boost::to_upper_copy(replyString.str()));\r
-       return true;\r
-}\r
-\r
-bool TlsCommand::DoExecute()\r
-{\r
-       std::wstringstream replyString;\r
-       replyString << TEXT("200 TLS OK\r\n");\r
-\r
-       replyString << ListTemplates();\r
-       replyString << TEXT("\r\n");\r
-\r
-       SetReplyString(replyString.str());\r
-       return true;\r
-}\r
-\r
-bool VersionCommand::DoExecute()\r
-{\r
-       std::wstring replyString = TEXT("201 VERSION OK\r\n") + env::version() + TEXT("\r\n");\r
-\r
-       if(_parameters.size() > 0)\r
-       {\r
-               if(_parameters[0] == L"FLASH")\r
-                       replyString = TEXT("201 VERSION OK\r\n") + flash::get_version() + TEXT("\r\n");\r
-               else if(_parameters[0] == L"TEMPLATEHOST")\r
-                       replyString = TEXT("201 VERSION OK\r\n") + flash::get_cg_version() + TEXT("\r\n");\r
-               else if(_parameters[0] != L"SERVER")\r
-                       replyString = TEXT("403 VERSION ERROR\r\n");\r
-       }\r
-\r
-       SetReplyString(replyString);\r
-       return true;\r
-}\r
-\r
-bool ByeCommand::DoExecute()\r
-{\r
-       GetClientInfo()->Disconnect();\r
-       return true;\r
-}\r
-\r
-bool SetCommand::DoExecute()\r
-{\r
-       std::wstring name = _parameters[0];\r
-       std::transform(name.begin(), name.end(), name.begin(), toupper);\r
-\r
-       std::wstring value = _parameters[1];\r
-       std::transform(value.begin(), value.end(), value.begin(), toupper);\r
-\r
-       if(name == TEXT("MODE"))\r
-       {\r
-               auto format_desc = core::video_format_desc::get(value);\r
-               if(format_desc.format != core::video_format::invalid)\r
-               {\r
-                       GetChannel()->set_video_format_desc(format_desc);\r
-                       SetReplyString(TEXT("202 SET MODE OK\r\n"));\r
-               }\r
-               else\r
-                       SetReplyString(TEXT("501 SET MODE FAILED\r\n"));\r
-       }\r
-       else\r
-       {\r
-               this->SetReplyString(TEXT("403 SET ERROR\r\n"));\r
-       }\r
-\r
-       return true;\r
-}\r
-\r
-\r
-}      //namespace amcp\r
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Nicklas P Andersson
+*/
+
+#include "../StdAfx.h"
+
+#if defined(_MSC_VER)
+#pragma warning (push, 1) // TODO: Legacy code, just disable warnings
+#endif
+
+#include "AMCPCommandsImpl.h"
+#include "AMCPProtocolStrategy.h"
+
+#include <common/env.h>
+
+#include <common/log.h>
+#include <common/diagnostics/graph.h>
+#include <common/os/windows/current_version.h>
+#include <common/os/windows/system_info.h>
+
+#include <core/producer/frame_producer.h>
+#include <core/video_format.h>
+#include <core/producer/transition/transition_producer.h>
+#include <core/frame/frame_transform.h>
+#include <core/producer/stage.h>
+#include <core/producer/layer.h>
+#include <core/mixer/mixer.h>
+#include <core/consumer/output.h>
+
+#include <modules/reroute/producer/reroute_producer.h>
+#include <modules/bluefish/bluefish.h>
+#include <modules/decklink/decklink.h>
+#include <modules/ffmpeg/ffmpeg.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 <modules/ffmpeg/producer/util/util.h>
+#include <modules/image/image.h>
+#include <modules/screen/screen.h>
+#include <modules/reroute/producer/reroute_producer.h>
+
+#include <algorithm>
+#include <locale>
+#include <fstream>
+#include <memory>
+#include <cctype>
+#include <io.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/regex.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+#include <boost/locale.hpp>
+#include <boost/range/adaptor/transformed.hpp>
+#include <boost/range/algorithm/copy.hpp>
+
+#include <tbb/concurrent_unordered_map.h>
+
+/* Return codes
+
+100 [action]                   Information om att något har hänt  
+101 [action]                   Information om att något har hänt, en rad data skickas  
+
+202 [kommando] OK              Kommandot har utförts  
+201 [kommando] OK              Kommandot har utförts, och en rad data skickas tillbaka  
+200 [kommando] OK              Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad  
+
+400 ERROR                              Kommandot kunde inte förstås  
+401 [kommando] ERROR   Ogiltig kanal  
+402 [kommando] ERROR   Parameter saknas  
+403 [kommando] ERROR   Ogiltig parameter  
+404 [kommando] ERROR   Mediafilen hittades inte  
+
+500 FAILED                             Internt configurationfel  
+501 [kommando] FAILED  Internt configurationfel  
+502 [kommando] FAILED  Oläslig mediafil  
+
+600 [kommando] FAILED  funktion ej implementerad
+*/
+
+namespace caspar { namespace protocol {
+
+using namespace core;
+
+std::wstring read_utf8_file(const boost::filesystem::wpath& file)
+{
+       std::wstringstream result;
+       boost::filesystem::wifstream filestream(file);
+
+       if (filestream) 
+       {
+               // Consume BOM first
+               filestream.get();
+               // read all data
+               result << filestream.rdbuf();
+       }
+
+       return result.str();
+}
+
+std::wstring read_latin1_file(const boost::filesystem::wpath& file)
+{
+       boost::locale::generator gen;
+       gen.locale_cache_enabled(true);
+       gen.categories(boost::locale::codepage_facet);
+
+       std::stringstream result_stream;
+       boost::filesystem::ifstream filestream(file);
+       filestream.imbue(gen("en_US.ISO8859-1"));
+
+       if (filestream)
+       {
+               // read all data
+               result_stream << filestream.rdbuf();
+       }
+
+       std::string result = result_stream.str();
+       std::wstring widened_result;
+
+       // The first 255 codepoints in unicode is the same as in latin1
+       auto from_signed_to_signed = std::function<unsigned char(char)>(
+               [] (char c) { return static_cast<unsigned char>(c); }
+       );
+       boost::copy(
+               result | boost::adaptors::transformed(from_signed_to_signed),
+               std::back_inserter(widened_result));
+
+       return widened_result;
+}
+
+std::wstring read_file(const boost::filesystem::wpath& file)
+{
+       static const uint8_t BOM[] = {0xef, 0xbb, 0xbf};
+
+       if (!boost::filesystem::exists(file))
+       {
+               return L"";
+       }
+
+       if (boost::filesystem::file_size(file) >= 3)
+       {
+               boost::filesystem::ifstream bom_stream(file);
+
+               char header[3];
+               bom_stream.read(header, 3);
+               bom_stream.close();
+
+               if (std::memcmp(BOM, header, 3) == 0)
+                       return read_utf8_file(file);
+       }
+
+       return read_latin1_file(file);
+}
+
+std::wstring MediaInfo(const boost::filesystem::path& path)
+{
+       if(boost::filesystem::is_regular_file(path))
+       {
+               std::wstring clipttype = TEXT("N/A");
+               std::wstring extension = boost::to_upper_copy(path.extension().wstring());
+               if(extension == TEXT(".TGA") || extension == TEXT(".COL") || extension == L".PNG" || extension == L".JPEG" || extension == L".JPG" ||
+                       extension == L"GIF" || extension == L"BMP")
+                       clipttype = TEXT("STILL");
+               else if(extension == TEXT(".WAV") || extension == TEXT(".MP3"))
+                       clipttype = TEXT("STILL");
+               else if(extension == TEXT("SWF") || extension == TEXT("CT") || extension == TEXT("DV") || extension == TEXT("MOV") || extension == TEXT("MPG") || extension == TEXT("AVI") || caspar::ffmpeg::is_valid_file(path.wstring()))
+                       clipttype = TEXT("MOVIE");
+
+               if(clipttype != TEXT("N/A"))
+               {               
+                       auto is_not_digit = [](char c){ return std::isdigit(c) == 0; };
+
+                       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());
+                       auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());
+
+                       auto sizeStr = boost::lexical_cast<std::wstring>(boost::filesystem::file_size(path));
+                       sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end());
+                       auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());
+                               
+                       auto str = relativePath.replace_extension(TEXT("")).native();
+                       while(str.size() > 0 && (str[0] == '\\' || str[0] == '/'))
+                               str = std::wstring(str.begin() + 1, str.end());
+
+                       return std::wstring() + TEXT("\"") + str +
+                                       + TEXT("\" ") + clipttype +
+                                       + TEXT(" ") + sizeStr +
+                                       + TEXT(" ") + writeTimeWStr +
+                                       + TEXT("\r\n");         
+               }       
+       }
+       return L"";
+}
+
+std::wstring ListMedia()
+{      
+       std::wstringstream replyString;
+       for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)  
+               replyString << MediaInfo(itr->path());
+       
+       return boost::to_upper_copy(replyString.str());
+}
+
+std::wstring ListTemplates() 
+{
+       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"))
+               {
+                       auto relativePath = boost::filesystem::wpath(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());
+                       auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());
+
+                       auto sizeStr = boost::lexical_cast<std::string>(boost::filesystem::file_size(itr->path()));
+                       sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), [](char c){ return std::isdigit(c) == 0;}), sizeStr.end());
+
+                       auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());
+
+                       std::wstring dir = relativePath.parent_path().native();
+                       std::wstring file = boost::to_upper_copy(relativePath.filename().wstring());
+                       relativePath = boost::filesystem::wpath(dir + L"/" + file);
+                                               
+                       auto str = relativePath.replace_extension(TEXT("")).native();
+                       boost::trim_if(str, boost::is_any_of("\\/"));
+
+                       replyString << TEXT("\"") << str
+                                               << TEXT("\" ") << sizeWStr
+                                               << TEXT(" ") << writeTimeWStr
+                                               << TEXT("\r\n");                
+               }
+       }
+       return replyString.str();
+}
+
+namespace amcp {
+       
+AMCPCommand::AMCPCommand() : channelIndex_(0), layerIndex_(-1)
+{}
+
+void AMCPCommand::SendReply()
+{
+       if(!pClientInfo_) 
+               return;
+
+       if(replyString_.empty())
+               return;
+       pClientInfo_->Send(replyString_);
+}
+
+void AMCPCommand::Clear() 
+{
+       pChannel_->stage().clear();
+       pClientInfo_.reset();
+       channelIndex_ = 0;
+       _parameters.clear();
+}
+
+bool DiagnosticsCommand::DoExecute()
+{      
+       try
+       {
+               diagnostics::show_graphs(true);
+
+               SetReplyString(TEXT("202 DIAG OK\r\n"));
+
+               return true;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 DIAG FAILED\r\n"));
+               return false;
+       }
+}
+
+bool ChannelGridCommand::DoExecute()
+{
+       CASPAR_THROW_EXCEPTION(not_implemented());
+
+       //int index = 1;
+       //auto self = GetChannels().back();
+       //
+       //std::vector<std::wstring> params;
+       //params.push_back(L"SCREEN");
+       //params.push_back(L"NAME");
+       //params.push_back(L"Channel Grid Window");
+       //auto screen = create_consumer(params);
+
+       //self->output().add(screen);
+
+       //BOOST_FOREACH(auto channel, GetChannels())
+       //{
+       //      if(channel != self)
+       //      {
+       //              auto producer = reroute::create_producer(self->frame_factory(), *channel);              
+       //              self->stage().load(index, producer, false);
+       //              self->stage().play(index);
+       //              index++;
+       //      }
+       //}
+
+       //int n = GetChannels().size()-1;
+       //double delta = 1.0/static_cast<double>(n);
+       //for(int x = 0; x < n; ++x)
+       //{
+       //      for(int y = 0; y < n; ++y)
+       //      {
+       //              int index = x+y*n+1;
+       //              auto transform = [=](frame_transform transform) -> frame_transform
+       //              {               
+       //                      transform.image_transform.fill_translation[0]   = x*delta;
+       //                      transform.image_transform.fill_translation[1]   = y*delta;
+       //                      transform.image_transform.fill_scale[0]                 = delta;
+       //                      transform.image_transform.fill_scale[1]                 = delta;
+       //                      transform.image_transform.clip_translation[0]   = x*delta;
+       //                      transform.image_transform.clip_translation[1]   = y*delta;
+       //                      transform.image_transform.clip_scale[0]                 = delta;
+       //                      transform.image_transform.clip_scale[1]                 = delta;                        
+       //                      return transform;
+       //              };
+       //              self->stage().apply_transform(index, transform);
+       //      }
+       //}
+
+       //return true;
+}
+
+bool CallCommand::DoExecute()
+{      
+       //Perform loading of the clip
+       try
+       {
+               auto what = _parameters.at(0);
+                               
+               std::wstring param;
+               for(auto it = std::begin(_parameters2); it != std::end(_parameters2); ++it, param += L" ")
+                       param += *it;
+
+               auto result = GetChannel()->stage().call(GetLayerIndex(), boost::trim_copy(param));
+               
+               if(!result.timed_wait(boost::posix_time::seconds(2)))
+                       CASPAR_THROW_EXCEPTION(timed_out());
+                               
+               std::wstringstream replyString;
+               if(result.get().empty())
+                       replyString << TEXT("202 CALL OK\r\n");
+               else
+                       replyString << TEXT("201 CALL OK\r\n") << result.get() << L"\r\n";
+               
+               SetReplyString(replyString.str());
+
+               return true;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 CALL FAILED\r\n"));
+               return false;
+       }
+}
+
+tbb::concurrent_unordered_map<int, std::vector<stage::transform_tuple_t>> deferred_transforms;
+
+bool MixerCommand::DoExecute()
+{      
+       //Perform loading of the clip
+       try
+       {       
+               bool defer = _parameters.back() == L"DEFER";
+               if(defer)
+                       _parameters.pop_back();
+
+               std::vector<stage::transform_tuple_t> transforms;
+
+               if(_parameters[0] == L"KEYER" || _parameters[0] == L"IS_KEY")
+               {
+                       bool value = boost::lexical_cast<int>(_parameters.at(1));
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.is_key = value;
+                               return transform;                                       
+                       }, 0, L"linear"));
+               }
+               else if(_parameters[0] == L"OPACITY")
+               {
+                       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.at(1));
+                       
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.opacity = value;
+                               return transform;                                       
+                       }, duration, tween));
+               }
+               else if(_parameters[0] == L"FILL" || _parameters[0] == L"FILL_RECT")
+               {
+                       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));
+                       double y        = boost::lexical_cast<double>(_parameters.at(2));
+                       double x_s      = boost::lexical_cast<double>(_parameters.at(3));
+                       double y_s      = boost::lexical_cast<double>(_parameters.at(4));
+
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) mutable -> frame_transform
+                       {
+                               transform.image_transform.fill_translation[0]   = x;
+                               transform.image_transform.fill_translation[1]   = y;
+                               transform.image_transform.fill_scale[0]                 = x_s;
+                               transform.image_transform.fill_scale[1]                 = y_s;
+                               transform.image_transform.clip_translation[0]   = x;
+                               transform.image_transform.clip_translation[1]   = y;
+                               transform.image_transform.clip_scale[0]                 = x_s;
+                               transform.image_transform.clip_scale[1]                 = y_s;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if(_parameters[0] == L"CLIP" || _parameters[0] == L"CLIP_RECT")
+               {
+                       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));
+                       double y        = boost::lexical_cast<double>(_parameters.at(2));
+                       double x_s      = boost::lexical_cast<double>(_parameters.at(3));
+                       double y_s      = boost::lexical_cast<double>(_parameters.at(4));
+
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.clip_translation[0]   = x;
+                               transform.image_transform.clip_translation[1]   = y;
+                               transform.image_transform.clip_scale[0]                 = x_s;
+                               transform.image_transform.clip_scale[1]                 = y_s;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if(_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";
+                       int n = boost::lexical_cast<int>(_parameters.at(1));
+                       double delta = 1.0/static_cast<double>(n);
+                       for(int x = 0; x < n; ++x)
+                       {
+                               for(int y = 0; y < n; ++y)
+                               {
+                                       int index = x+y*n+1;
+                                       transforms.push_back(stage::transform_tuple_t(index, [=](frame_transform transform) -> frame_transform
+                                       {               
+                                               transform.image_transform.fill_translation[0]   = x*delta;
+                                               transform.image_transform.fill_translation[1]   = y*delta;
+                                               transform.image_transform.fill_scale[0]                 = delta;
+                                               transform.image_transform.fill_scale[1]                 = delta;
+                                               transform.image_transform.clip_translation[0]   = x*delta;
+                                               transform.image_transform.clip_translation[1]   = y*delta;
+                                               transform.image_transform.clip_scale[0]                 = delta;
+                                               transform.image_transform.clip_scale[1]                 = delta;                        
+                                               return transform;
+                                       }, duration, tween));
+                               }
+                       }
+               }
+               else if(_parameters[0] == L"BLEND")
+               {
+                       auto blend_str = _parameters.at(1);                                                             
+                       int layer = GetLayerIndex();
+                       GetChannel()->mixer().set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str));       
+               }
+               else if(_parameters[0] == L"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";
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.brightness = value;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if(_parameters[0] == L"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";
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.saturation = value;
+                               return transform;
+                       }, duration, tween));   
+               }
+               else if(_parameters[0] == L"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";
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.contrast = value;
+                               return transform;
+                       }, duration, tween));   
+               }
+               else if(_parameters[0] == L"LEVELS")
+               {
+                       levels value;
+                       value.min_input  = boost::lexical_cast<double>(_parameters.at(1));
+                       value.max_input  = boost::lexical_cast<double>(_parameters.at(2));
+                       value.gamma              = boost::lexical_cast<double>(_parameters.at(3));
+                       value.min_output = boost::lexical_cast<double>(_parameters.at(4));
+                       value.max_output = boost::lexical_cast<double>(_parameters.at(5));
+                       int duration = _parameters.size() > 6 ? boost::lexical_cast<int>(_parameters[6]) : 0;
+                       std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear";
+
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.levels = value;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if(_parameters[0] == L"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]);
+
+                       transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.audio_transform.volume = value;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if(_parameters[0] == L"CLEAR")
+               {
+                       int layer = GetLayerIndex(std::numeric_limits<int>::max());
+                       if(layer ==     std::numeric_limits<int>::max())
+                               GetChannel()->stage().clear_transforms();
+                       else
+                               GetChannel()->stage().clear_transforms(layer);
+               }
+               else if(_parameters[0] == L"COMMIT")
+               {
+                       transforms = std::move(deferred_transforms[GetChannelIndex()]);
+               }
+               else
+               {
+                       SetReplyString(TEXT("404 MIXER ERROR\r\n"));
+                       return false;
+               }
+
+               if(defer)
+               {
+                       auto& defer_tranforms = deferred_transforms[GetChannelIndex()];
+                       defer_tranforms.insert(defer_tranforms.end(), transforms.begin(), transforms.end());
+               }
+               else
+                       GetChannel()->stage().apply_transforms(transforms);
+       
+               SetReplyString(TEXT("202 MIXER OK\r\n"));
+
+               return true;
+       }
+       catch(file_not_found&)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("404 MIXER ERROR\r\n"));
+               return false;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 MIXER FAILED\r\n"));
+               return false;
+       }
+}
+
+bool SwapCommand::DoExecute()
+{      
+       //Perform loading of the clip
+       try
+       {
+               if(GetLayerIndex(-1) != -1)
+               {
+                       std::vector<std::string> strs;
+                       boost::split(strs, _parameters[0], boost::is_any_of("-"));
+                       
+                       auto ch1 = GetChannel();
+                       auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);
+
+                       int l1 = GetLayerIndex();
+                       int l2 = boost::lexical_cast<int>(strs.at(1));
+
+                       ch1->stage().swap_layer(l1, l2, ch2->stage());
+               }
+               else
+               {
+                       auto ch1 = GetChannel();
+                       auto ch2 = GetChannels().at(boost::lexical_cast<int>(_parameters[0])-1);
+                       ch1->stage().swap_layers(ch2->stage());
+               }
+               
+               SetReplyString(TEXT("202 SWAP OK\r\n"));
+
+               return true;
+       }
+       catch(file_not_found&)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("404 SWAP ERROR\r\n"));
+               return false;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 SWAP FAILED\r\n"));
+               return false;
+       }
+}
+
+bool AddCommand::DoExecute()
+{      
+       //Perform loading of the clip
+       try
+       {
+               auto consumer = create_consumer(_parameters);
+               GetChannel()->output().add(GetLayerIndex(consumer->index()), consumer);
+       
+               SetReplyString(TEXT("202 ADD OK\r\n"));
+
+               return true;
+       }
+       catch(file_not_found&)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("404 ADD ERROR\r\n"));
+               return false;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 ADD FAILED\r\n"));
+               return false;
+       }
+}
+
+bool RemoveCommand::DoExecute()
+{      
+       //Perform loading of the clip
+       try
+       {
+               auto index = GetLayerIndex(std::numeric_limits<int>::min());
+               if(index == std::numeric_limits<int>::min())
+                       index = create_consumer(_parameters)->index();
+
+               GetChannel()->output().remove(index);
+
+               SetReplyString(TEXT("202 REMOVE OK\r\n"));
+
+               return true;
+       }
+       catch(file_not_found&)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("404 REMOVE ERROR\r\n"));
+               return false;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 REMOVE FAILED\r\n"));
+               return false;
+       }
+}
+
+bool LoadCommand::DoExecute()
+{      
+       //Perform loading of the clip
+       try
+       {
+               _parameters[0] = _parameters[0];
+               auto pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), _parameters);              
+               GetChannel()->stage().load(GetLayerIndex(), pFP, true);
+       
+               SetReplyString(TEXT("202 LOAD OK\r\n"));
+
+               return true;
+       }
+       catch(file_not_found&)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("404 LOAD ERROR\r\n"));
+               return false;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 LOAD FAILED\r\n"));
+               return false;
+       }
+}
+
+
+
+//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;
+       
+       bool bLoop = false;
+
+       // TRANSITION
+
+       std::wstring message;
+       for(size_t n = 0; n < _parameters.size(); ++n)
+               message += _parameters[n] + L" ";
+               
+       static const boost::wregex expr(L".*(?<TRANSITION>CUT|PUSH|SLIDE|WIPE|MIX)\\s*(?<DURATION>\\d+)\\s*(?<TWEEN>(LINEAR)|(EASE[^\\s]*))?\\s*(?<DIRECTION>FROMLEFT|FROMRIGHT|LEFT|RIGHT)?.*");
+       boost::wsmatch what;
+       if(boost::regex_match(message, what, expr))
+       {
+               auto transition = what["TRANSITION"].str();
+               transitionInfo.duration = boost::lexical_cast<size_t>(what["DURATION"].str());
+               auto direction = what["DIRECTION"].matched ? what["DIRECTION"].str() : L"";
+               auto tween = what["TWEEN"].matched ? what["TWEEN"].str() : L"";
+               transitionInfo.tweener = tween;         
+
+               if(transition == TEXT("CUT"))
+                       transitionInfo.type = transition_type::cut;
+               else if(transition == TEXT("MIX"))
+                       transitionInfo.type = transition_type::mix;
+               else if(transition == TEXT("PUSH"))
+                       transitionInfo.type = transition_type::push;
+               else if(transition == TEXT("SLIDE"))
+                       transitionInfo.type = transition_type::slide;
+               else if(transition == TEXT("WIPE"))
+                       transitionInfo.type = transition_type::wipe;
+               
+               if(direction == TEXT("FROMLEFT"))
+                       transitionInfo.direction = transition_direction::from_left;
+               else if(direction == TEXT("FROMRIGHT"))
+                       transitionInfo.direction = transition_direction::from_right;
+               else if(direction == TEXT("LEFT"))
+                       transitionInfo.direction = transition_direction::from_right;
+               else if(direction == TEXT("RIGHT"))
+                       transitionInfo.direction = transition_direction::from_left;
+       }
+       
+       //Perform loading of the clip
+       try
+       {
+               std::shared_ptr<core::frame_producer> pFP;
+               
+               static boost::wregex expr(L"\\[(?<CHANNEL>\\d+)\\]", boost::regex::icase);
+                       
+               boost::wsmatch what;
+               if(boost::regex_match(_parameters.at(0), what, expr))
+               {
+                       auto channel_index = boost::lexical_cast<int>(what["CHANNEL"].str());
+                       pFP = reroute::create_producer(*GetChannels().at(channel_index-1)); 
+               }
+               else
+                       pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), _parameters);
+               
+               if(pFP == frame_producer::empty())
+                       CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(_parameters.size() > 0 ? _parameters[0] : L""));
+
+               bool auto_play = std::find(_parameters.begin(), _parameters.end(), L"AUTO") != _parameters.end();
+
+               auto pFP2 = create_transition_producer(GetChannel()->video_format_desc().field_mode, spl::make_shared_ptr(pFP), transitionInfo);
+               if(auto_play)
+                       GetChannel()->stage().load(GetLayerIndex(), pFP2, false, transitionInfo.duration); // TODO: LOOP
+               else
+                       GetChannel()->stage().load(GetLayerIndex(), pFP2, false); // TODO: LOOP
+       
+               
+               SetReplyString(TEXT("202 LOADBG OK\r\n"));
+
+               return true;
+       }
+       catch(file_not_found&)
+       {               
+               std::wstring params2;
+               for(auto it = _parameters.begin(); it != _parameters.end(); ++it)
+                       params2 += L" " + *it;
+               CASPAR_LOG(error) << L"File not found. No match found for parameters. Check syntax:" << params2;
+               SetReplyString(TEXT("404 LOADBG ERROR\r\n"));
+               return false;
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("502 LOADBG FAILED\r\n"));
+               return false;
+       }
+}
+
+bool PauseCommand::DoExecute()
+{
+       try
+       {
+               GetChannel()->stage().pause(GetLayerIndex());
+               SetReplyString(TEXT("202 PAUSE OK\r\n"));
+               return true;
+       }
+       catch(...)
+       {
+               SetReplyString(TEXT("501 PAUSE FAILED\r\n"));
+       }
+
+       return false;
+}
+
+bool PlayCommand::DoExecute()
+{
+       try
+       {
+               if(!_parameters.empty())
+               {
+                       LoadbgCommand lbg;
+                       lbg.SetChannel(GetChannel());
+                       lbg.SetChannels(GetChannels());
+                       lbg.SetChannelIndex(GetChannelIndex());
+                       lbg.SetLayerIntex(GetLayerIndex());
+                       lbg.SetClientInfo(GetClientInfo());
+                       for(auto it = _parameters.begin(); it != _parameters.end(); ++it)
+                               lbg.AddParameter(*it);
+                       if(!lbg.Execute())
+                               throw std::exception();
+               }
+
+               GetChannel()->stage().play(GetLayerIndex());
+               
+               SetReplyString(TEXT("202 PLAY OK\r\n"));
+               return true;
+       }
+       catch(...)
+       {
+               SetReplyString(TEXT("501 PLAY FAILED\r\n"));
+       }
+
+       return false;
+}
+
+bool StopCommand::DoExecute()
+{
+       try
+       {
+               GetChannel()->stage().stop(GetLayerIndex());
+               SetReplyString(TEXT("202 STOP OK\r\n"));
+               return true;
+       }
+       catch(...)
+       {
+               SetReplyString(TEXT("501 STOP FAILED\r\n"));
+       }
+
+       return false;
+}
+
+bool ClearCommand::DoExecute()
+{
+       int index = GetLayerIndex(std::numeric_limits<int>::min());
+       if(index != std::numeric_limits<int>::min())
+               GetChannel()->stage().clear(index);
+       else
+               GetChannel()->stage().clear();
+               
+       SetReplyString(TEXT("202 CLEAR OK\r\n"));
+
+       return true;
+}
+
+bool PrintCommand::DoExecute()
+{
+       GetChannel()->output().add(create_consumer(boost::assign::list_of(L"IMAGE")));
+               
+       SetReplyString(TEXT("202 PRINT OK\r\n"));
+
+       return true;
+}
+
+bool LogCommand::DoExecute()
+{
+       if(_parameters.at(0) == L"LEVEL")
+               log::set_log_level(_parameters.at(1));
+
+       SetReplyString(TEXT("202 LOG OK\r\n"));
+
+       return true;
+}
+
+bool CGCommand::DoExecute()
+{
+       try
+       {
+               std::wstring command = _parameters[0];
+               if(command == TEXT("ADD"))
+                       return DoExecuteAdd();
+               else if(command == TEXT("PLAY"))
+                       return DoExecutePlay();
+               else if(command == TEXT("STOP"))
+                       return DoExecuteStop();
+               else if(command == TEXT("NEXT"))
+                       return DoExecuteNext();
+               else if(command == TEXT("REMOVE"))
+                       return DoExecuteRemove();
+               else if(command == TEXT("CLEAR"))
+                       return DoExecuteClear();
+               else if(command == TEXT("UPDATE"))
+                       return DoExecuteUpdate();
+               else if(command == TEXT("INVOKE"))
+                       return DoExecuteInvoke();
+               else if(command == TEXT("INFO"))
+                       return DoExecuteInfo();
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+       }
+
+       SetReplyString(TEXT("403 CG ERROR\r\n"));
+       return false;
+}
+
+bool CGCommand::ValidateLayer(const std::wstring& layerstring) {
+       int length = layerstring.length();
+       for(int i = 0; i < length; ++i) {
+               if(!_istdigit(layerstring[i])) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+bool CGCommand::DoExecuteAdd() {
+       //CG 1 ADD 0 "template_folder/templatename" [STARTLABEL] 0/1 [DATA]
+
+       int layer = 0;                          //_parameters[1]
+//     std::wstring templateName;      //_parameters[2]
+       std::wstring label;             //_parameters[3]
+       bool bDoStart = false;          //_parameters[3] alt. _parameters[4]
+//     std::wstring data;                      //_parameters[4] alt. _parameters[5]
+
+       if(_parameters.size() < 4) 
+       {
+               SetReplyString(TEXT("402 CG ERROR\r\n"));
+               return false;
+       }
+       unsigned int dataIndex = 4;
+
+       if(!ValidateLayer(_parameters[1])) 
+       {
+               SetReplyString(TEXT("403 CG ERROR\r\n"));
+               return false;
+       }
+
+       layer = _ttoi(_parameters[1].c_str());
+
+       if(_parameters[3].length() > 1) 
+       {       //read label
+               label = _parameters2[3];
+               ++dataIndex;
+
+               if(_parameters.size() > 4 && _parameters[4].length() > 0)       //read play-on-load-flag
+                       bDoStart = (_parameters[4][0]==TEXT('1')) ? true : false;
+               else 
+               {
+                       SetReplyString(TEXT("402 CG ERROR\r\n"));
+                       return false;
+               }
+       }
+       else if(_parameters[3].length() > 0) {  //read play-on-load-flag
+               bDoStart = (_parameters[3][0]==TEXT('1')) ? true : false;
+       }
+       else 
+       {
+               SetReplyString(TEXT("403 CG ERROR\r\n"));
+               return false;
+       }
+
+       const TCHAR* pDataString = 0;
+       std::wstringstream data;
+       std::wstring dataFromFile;
+       if(_parameters.size() > dataIndex) 
+       {       //read data
+               const std::wstring& dataString = _parameters2[dataIndex];
+
+               if(dataString[0] == TEXT('<')) //the data is an XML-string
+                       pDataString = dataString.c_str();
+               else 
+               {
+                       //The data is not an XML-string, it must be a filename
+                       std::wstring filename = env::data_folder();
+                       filename.append(dataString);
+                       filename.append(TEXT(".ftd"));
+
+                       dataFromFile = read_file(boost::filesystem::wpath(filename));
+                       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);
+
+               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).add(layer, filename, bDoStart, label, (pDataString!=0) ? pDataString : TEXT(""));
+               SetReplyString(TEXT("202 CG OK\r\n"));
+       }
+       else
+       {
+               CASPAR_LOG(warning) << "Could not find template " << _parameters[2];
+               SetReplyString(TEXT("404 CG ERROR\r\n"));
+       }
+       return true;
+}
+
+bool CGCommand::DoExecutePlay()
+{
+       if(_parameters.size() > 1)
+       {
+               if(!ValidateLayer(_parameters[1])) 
+               {
+                       SetReplyString(TEXT("403 CG ERROR\r\n"));
+                       return false;
+               }
+               int layer = _ttoi(_parameters[1].c_str());
+               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).play(layer);
+       }
+       else
+       {
+               SetReplyString(TEXT("402 CG ERROR\r\n"));
+               return true;
+       }
+
+       SetReplyString(TEXT("202 CG OK\r\n"));
+       return true;
+}
+
+bool CGCommand::DoExecuteStop() 
+{
+       if(_parameters.size() > 1)
+       {
+               if(!ValidateLayer(_parameters[1])) 
+               {
+                       SetReplyString(TEXT("403 CG ERROR\r\n"));
+                       return false;
+               }
+               int layer = _ttoi(_parameters[1].c_str());
+               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).stop(layer, 0);
+       }
+       else 
+       {
+               SetReplyString(TEXT("402 CG ERROR\r\n"));
+               return true;
+       }
+
+       SetReplyString(TEXT("202 CG OK\r\n"));
+       return true;
+}
+
+bool CGCommand::DoExecuteNext()
+{
+       if(_parameters.size() > 1) 
+       {
+               if(!ValidateLayer(_parameters[1])) 
+               {
+                       SetReplyString(TEXT("403 CG ERROR\r\n"));
+                       return false;
+               }
+
+               int layer = _ttoi(_parameters[1].c_str());
+               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).next(layer);
+       }
+       else 
+       {
+               SetReplyString(TEXT("402 CG ERROR\r\n"));
+               return true;
+       }
+
+       SetReplyString(TEXT("202 CG OK\r\n"));
+       return true;
+}
+
+bool CGCommand::DoExecuteRemove() 
+{
+       if(_parameters.size() > 1) 
+       {
+               if(!ValidateLayer(_parameters[1])) 
+               {
+                       SetReplyString(TEXT("403 CG ERROR\r\n"));
+                       return false;
+               }
+
+               int layer = _ttoi(_parameters[1].c_str());
+               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).remove(layer);
+       }
+       else 
+       {
+               SetReplyString(TEXT("402 CG ERROR\r\n"));
+               return true;
+       }
+
+       SetReplyString(TEXT("202 CG OK\r\n"));
+       return true;
+}
+
+bool CGCommand::DoExecuteClear() 
+{
+       GetChannel()->stage().clear(GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER));
+       SetReplyString(TEXT("202 CG OK\r\n"));
+       return true;
+}
+
+bool CGCommand::DoExecuteUpdate() 
+{
+       try
+       {
+               if(!ValidateLayer(_parameters.at(1)))
+               {
+                       SetReplyString(TEXT("403 CG ERROR\r\n"));
+                       return false;
+               }
+                                               
+               std::wstring dataString = _parameters2.at(2);                           
+               if(dataString.at(0) != TEXT('<'))
+               {
+                       //The data is not an XML-string, it must be a filename
+                       std::wstring filename = env::data_folder();
+                       filename.append(dataString);
+                       filename.append(TEXT(".ftd"));
+
+                       dataString = read_file(boost::filesystem::wpath(filename));
+               }               
+
+               int layer = _ttoi(_parameters.at(1).c_str());
+               flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).update(layer, dataString);
+       }
+       catch(...)
+       {
+               SetReplyString(TEXT("402 CG ERROR\r\n"));
+               return true;
+       }
+
+       SetReplyString(TEXT("202 CG OK\r\n"));
+       return true;
+}
+
+bool CGCommand::DoExecuteInvoke() 
+{
+       std::wstringstream replyString;
+       replyString << TEXT("201 CG OK\r\n");
+
+       if(_parameters.size() > 2)
+       {
+               if(!ValidateLayer(_parameters[1]))
+               {
+                       SetReplyString(TEXT("403 CG ERROR\r\n"));
+                       return false;
+               }
+               int layer = _ttoi(_parameters[1].c_str());
+               auto result = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).invoke(layer, _parameters2[2]);
+               replyString << result << TEXT("\r\n"); 
+       }
+       else 
+       {
+               SetReplyString(TEXT("402 CG ERROR\r\n"));
+               return true;
+       }
+       
+       SetReplyString(replyString.str());
+       return true;
+}
+
+bool CGCommand::DoExecuteInfo() 
+{
+       std::wstringstream replyString;
+       replyString << TEXT("201 CG OK\r\n");
+
+       if(_parameters.size() > 1)
+       {
+               if(!ValidateLayer(_parameters[1]))
+               {
+                       SetReplyString(TEXT("403 CG ERROR\r\n"));
+                       return false;
+               }
+
+               int layer = _ttoi(_parameters[1].c_str());
+               auto desc = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).description(layer);
+               
+               replyString << desc << TEXT("\r\n"); 
+       }
+       else 
+       {
+               auto info = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).template_host_info();
+               replyString << info << TEXT("\r\n"); 
+       }       
+
+       SetReplyString(replyString.str());
+       return true;
+}
+
+bool DataCommand::DoExecute()
+{
+       std::wstring command = _parameters[0];
+       if(command == TEXT("STORE"))
+               return DoExecuteStore();
+       else if(command == TEXT("RETRIEVE"))
+               return DoExecuteRetrieve();
+       else if(command == TEXT("LIST"))
+               return DoExecuteList();
+
+       SetReplyString(TEXT("403 DATA ERROR\r\n"));
+       return false;
+}
+
+bool DataCommand::DoExecuteStore() 
+{
+       if(_parameters.size() < 3) 
+       {
+               SetReplyString(TEXT("402 DATA STORE ERROR\r\n"));
+               return false;
+       }
+
+       std::wstring filename = env::data_folder();
+       filename.append(_parameters[1]);
+       filename.append(TEXT(".ftd"));
+
+       std::wofstream datafile(filename.c_str());
+       if(!datafile) 
+       {
+               SetReplyString(TEXT("501 DATA STORE FAILED\r\n"));
+               return false;
+       }
+
+       datafile << static_cast<wchar_t>(65279); // UTF-8 BOM character
+       datafile << _parameters2[2] << std::flush;
+       datafile.close();
+
+       std::wstring replyString = TEXT("202 DATA STORE OK\r\n");
+       SetReplyString(replyString);
+       return true;
+}
+
+bool DataCommand::DoExecuteRetrieve() 
+{
+       if(_parameters.size() < 2) 
+       {
+               SetReplyString(TEXT("402 DATA RETRIEVE ERROR\r\n"));
+               return false;
+       }
+
+       std::wstring filename = env::data_folder();
+       filename.append(_parameters[1]);
+       filename.append(TEXT(".ftd"));
+
+       std::wstring file_contents = read_file(boost::filesystem::wpath(filename));
+
+       if (file_contents.empty()) 
+       {
+               SetReplyString(TEXT("404 DATA RETRIEVE ERROR\r\n"));
+               return false;
+       }
+
+       std::wstringstream reply(TEXT("201 DATA RETRIEVE OK\r\n"));
+
+       reply << file_contents;
+       reply << "\r\n";
+       SetReplyString(reply.str());
+       return true;
+}
+
+bool DataCommand::DoExecuteList() 
+{
+       std::wstringstream replyString;
+       replyString << TEXT("200 DATA LIST OK\r\n");
+
+       for (boost::filesystem::recursive_directory_iterator itr(env::data_folder()), end; itr != end; ++itr)
+       {                       
+               if(boost::filesystem::is_regular_file(itr->path()))
+               {
+                       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 str = relativePath.replace_extension(TEXT("")).native();
+                       if(str[0] == '\\' || str[0] == '/')
+                               str = std::wstring(str.begin() + 1, str.end());
+
+                       replyString << str << TEXT("\r\n");     
+               }
+       }
+       
+       replyString << TEXT("\r\n");
+
+       SetReplyString(boost::to_upper_copy(replyString.str()));
+       return true;
+}
+
+bool CinfCommand::DoExecute()
+{
+       std::wstringstream replyString;
+       
+       try
+       {
+               std::wstring info;
+               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()) + L"\r\n";
+               }
+
+               if(info.empty())
+               {
+                       SetReplyString(TEXT("404 CINF ERROR\r\n"));
+                       return false;
+               }
+               replyString << TEXT("200 CINF OK\r\n");
+               replyString << info << "\r\n";
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("404 CINF ERROR\r\n"));
+               return false;
+       }
+       
+       SetReplyString(replyString.str());
+       return true;
+}
+
+void GenerateChannelInfo(int index, const spl::shared_ptr<core::video_channel>& pChannel, std::wstringstream& replyString)
+{
+       replyString << index+1 << TEXT(" ") << pChannel->video_format_desc().name << TEXT(" PLAYING") << TEXT("\r\n");
+}
+
+bool InfoCommand::DoExecute()
+{
+       std::wstringstream replyString;
+       
+       boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);
+
+       try
+       {
+               if(_parameters.size() >= 1 && _parameters[0] == L"TEMPLATE")
+               {               
+                       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));
+                                               
+                       std::wstringstream str;
+                       str << u16(flash::read_template_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);
+
+                       boost::property_tree::xml_parser::write_xml(replyString, info, w);
+               }
+               else if(_parameters.size() >= 1 && _parameters[0] == L"CONFIG")
+               {               
+                       replyString << L"201 INFO CONFIG OK\r\n";
+
+                       boost::property_tree::write_xml(replyString, caspar::env::properties(), w);
+               }
+               else if(_parameters.size() >= 1 && _parameters[0] == L"PATHS")
+               {
+                       replyString << L"201 INFO PATHS OK\r\n";
+
+                       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::filesystem3::initial_path().wstring() + L"\\");
+
+                       boost::property_tree::write_xml(replyString, info, w);
+               }
+               else if(_parameters.size() >= 1 && _parameters[0] == L"SYSTEM")
+               {
+                       replyString << L"201 INFO SYSTEM OK\r\n";
+                       
+                       boost::property_tree::wptree info;
+                       
+                       info.add(L"system.name",                                        caspar::system_product_name());
+                       info.add(L"system.windows.name",                        caspar::win_product_name());
+                       info.add(L"system.windows.service-pack",        caspar::win_sp_version());
+                       info.add(L"system.cpu",                                         caspar::cpu_info());
+       
+                       BOOST_FOREACH(auto device, caspar::decklink::device_list())
+                               info.add(L"system.decklink.device", device);
+
+                       BOOST_FOREACH(auto device, caspar::bluefish::device_list())
+                               info.add(L"system.bluefish.device", device);
+                               
+                       info.add(L"system.flash",                                       caspar::flash::version());
+                       //info.add(L"system.free-image",                                caspar::image::version());
+                       info.add(L"system.ffmpeg.avcodec",                      caspar::ffmpeg::avcodec_version());
+                       info.add(L"system.ffmpeg.avformat",                     caspar::ffmpeg::avformat_version());
+                       info.add(L"system.ffmpeg.avfilter",                     caspar::ffmpeg::avfilter_version());
+                       info.add(L"system.ffmpeg.avutil",                       caspar::ffmpeg::avutil_version());
+                       info.add(L"system.ffmpeg.swscale",                      caspar::ffmpeg::swscale_version());
+                                               
+                       boost::property_tree::write_xml(replyString, info, w);
+               }
+               else if(_parameters.size() >= 1 && _parameters[0] == L"SERVER")
+               {
+                       replyString << L"201 INFO SERVER OK\r\n";
+                       
+                       boost::property_tree::wptree info;
+
+                       int index = 0;
+                       BOOST_FOREACH(auto channel, channels_)
+                               info.add_child(L"channels.channel", channel->info())
+                                       .add(L"index", ++index);
+                       
+                       boost::property_tree::write_xml(replyString, info, w);
+               }
+               else // channel
+               {                       
+                       if(_parameters.size() >= 1)
+                       {
+                               replyString << TEXT("201 INFO OK\r\n");
+                               boost::property_tree::wptree info;
+
+                               std::vector<std::wstring> split;
+                               boost::split(split, _parameters[0], boost::is_any_of("-"));
+                                       
+                               int layer = std::numeric_limits<int>::min();
+                               int channel = boost::lexical_cast<int>(split[0]) - 1;
+
+                               if(split.size() > 1)
+                                       layer = boost::lexical_cast<int>(split[1]);
+                               
+                               if(layer == std::numeric_limits<int>::min())
+                               {       
+                                       info.add_child(L"channel", channels_.at(channel)->info())
+                                                       .add(L"index", channel);
+                               }
+                               else
+                               {
+                                       if(_parameters.size() >= 2)
+                                       {
+                                               if(_parameters[1] == L"B")
+                                                       info.add_child(L"producer", channels_.at(channel)->stage().background(layer).get()->info());
+                                               else
+                                                       info.add_child(L"producer", channels_.at(channel)->stage().foreground(layer).get()->info());
+                                       }
+                                       else
+                                       {
+                                               info.add_child(L"layer", channels_.at(channel)->stage().info(layer).get())
+                                                       .add(L"index", layer);
+                                       }
+                               }
+                               boost::property_tree::xml_parser::write_xml(replyString, info, w);
+                       }
+                       else
+                       {
+                               // This is needed for backwards compatibility with old clients
+                               replyString << TEXT("200 INFO OK\r\n");
+                               for(size_t n = 0; n < channels_.size(); ++n)
+                                       GenerateChannelInfo(n, channels_[n], replyString);
+                       }
+
+               }
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("403 INFO ERROR\r\n"));
+               return false;
+       }
+
+       replyString << TEXT("\r\n");
+       SetReplyString(replyString.str());
+       return true;
+}
+
+bool ClsCommand::DoExecute()
+{
+/*
+               wav = audio
+               mp3 = audio
+               swf     = movie
+               dv  = movie
+               tga = still
+               col = still
+       */
+       try
+       {
+               std::wstringstream replyString;
+               replyString << TEXT("200 CLS OK\r\n");
+               replyString << ListMedia();
+               replyString << TEXT("\r\n");
+               SetReplyString(boost::to_upper_copy(replyString.str()));
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("501 CLS FAILED\r\n"));
+               return false;
+       }
+
+       return true;
+}
+
+bool TlsCommand::DoExecute()
+{
+       try
+       {
+               std::wstringstream replyString;
+               replyString << TEXT("200 TLS OK\r\n");
+
+               replyString << ListTemplates();
+               replyString << TEXT("\r\n");
+
+               SetReplyString(replyString.str());
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("501 TLS FAILED\r\n"));
+               return false;
+       }
+       return true;
+}
+
+bool VersionCommand::DoExecute()
+{
+       std::wstring replyString = TEXT("201 VERSION OK\r\n") + env::version() + TEXT("\r\n");
+
+       if(_parameters.size() > 0)
+       {
+               if(_parameters[0] == L"FLASH")
+                       replyString = TEXT("201 VERSION OK\r\n") + flash::version() + TEXT("\r\n");
+               else if(_parameters[0] == L"TEMPLATEHOST")
+                       replyString = TEXT("201 VERSION OK\r\n") + flash::cg_version() + TEXT("\r\n");
+               else if(_parameters[0] != L"SERVER")
+                       replyString = TEXT("403 VERSION ERROR\r\n");
+       }
+
+       SetReplyString(replyString);
+       return true;
+}
+
+bool ByeCommand::DoExecute()
+{
+       GetClientInfo()->Disconnect();
+       return true;
+}
+
+bool SetCommand::DoExecute()
+{
+       try
+       {
+               std::wstring name = _parameters[0];
+               std::transform(name.begin(), name.end(), name.begin(), toupper);
+
+               std::wstring value = _parameters[1];
+               std::transform(value.begin(), value.end(), value.begin(), toupper);
+
+               if(name == TEXT("MODE"))
+               {
+                       auto format_desc = core::video_format_desc(value);
+                       if(format_desc.format != core::video_format::invalid)
+                       {
+                               GetChannel()->video_format_desc(format_desc);
+                               SetReplyString(TEXT("202 SET MODE OK\r\n"));
+                       }
+                       else
+                               SetReplyString(TEXT("501 SET MODE FAILED\r\n"));
+               }
+               else
+               {
+                       this->SetReplyString(TEXT("403 SET ERROR\r\n"));
+               }
+       }
+       catch(...)
+       {
+               CASPAR_LOG_CURRENT_EXCEPTION();
+               SetReplyString(TEXT("501 SET FAILED\r\n"));
+               return false;
+       }
+
+       return true;
+}
+
+
+}      //namespace amcp
 }}     //namespace caspar
\ No newline at end of file