]> git.sesse.net Git - casparcg/blobdiff - protocol/amcp/AMCPProtocolStrategy.cpp
set svn:eol-style native on .h and .cpp files
[casparcg] / protocol / amcp / AMCPProtocolStrategy.cpp
index c4d02f7c44a71d992d2cce243642c258691f9fde..2746fbaa758507f2e63c223649726e2bb746dfac 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
\r
-#include "../StdAfx.h"\r
-\r
-#include "AMCPProtocolStrategy.h"\r
-\r
-#include "../util/AsyncEventServer.h"\r
-#include "AMCPCommandsImpl.h"\r
-\r
-#include <stdio.h>\r
-#include <crtdbg.h>\r
-#include <string.h>\r
-#include <algorithm>\r
-#include <cctype>\r
-\r
-#include <boost/algorithm/string/trim.hpp>\r
-#include <boost/algorithm/string/split.hpp>\r
-#include <boost/algorithm/string/replace.hpp>\r
-#include <boost/lexical_cast.hpp>\r
-\r
-#if defined(_MSC_VER)\r
-#pragma warning (push, 1) // TODO: Legacy code, just disable warnings\r
-#endif\r
-\r
-namespace caspar { namespace protocol { namespace amcp {\r
-\r
-using IO::ClientInfoPtr;\r
-\r
-const std::wstring AMCPProtocolStrategy::MessageDelimiter = TEXT("\r\n");\r
-\r
-inline std::shared_ptr<core::video_channel> GetChannelSafe(unsigned int index, const std::vector<spl::shared_ptr<core::video_channel>>& channels)\r
-{\r
-       return index < channels.size() ? std::shared_ptr<core::video_channel>(channels[index]) : nullptr;\r
-}\r
-\r
-AMCPProtocolStrategy::AMCPProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels) : channels_(channels) {\r
-       AMCPCommandQueuePtr pGeneralCommandQueue(new AMCPCommandQueue());\r
-       commandQueues_.push_back(pGeneralCommandQueue);\r
-\r
-\r
-       std::shared_ptr<core::video_channel> pChannel;\r
-       unsigned int index = -1;\r
-       //Create a commandpump for each video_channel\r
-       while((pChannel = GetChannelSafe(++index, channels_)) != 0) {\r
-               AMCPCommandQueuePtr pChannelCommandQueue(new AMCPCommandQueue());\r
-               std::wstring title = TEXT("video_channel ");\r
-\r
-               //HACK: Perform real conversion from int to string\r
-               TCHAR num = TEXT('1')+static_cast<TCHAR>(index);\r
-               title += num;\r
-               \r
-               commandQueues_.push_back(pChannelCommandQueue);\r
-       }\r
-}\r
-\r
-AMCPProtocolStrategy::~AMCPProtocolStrategy() {\r
-}\r
-\r
-void AMCPProtocolStrategy::Parse(const TCHAR* pData, int charCount, ClientInfoPtr pClientInfo)\r
-{\r
-       size_t pos;\r
-       std::wstring recvData(pData, charCount);\r
-       std::wstring availibleData = (pClientInfo != nullptr ? pClientInfo->currentMessage_ : L"") + recvData;\r
-\r
-       while(true) {\r
-               pos = availibleData.find(MessageDelimiter);\r
-               if(pos != std::wstring::npos)\r
-               {\r
-                       std::wstring message = availibleData.substr(0,pos);\r
-\r
-                       //This is where a complete message gets taken care of\r
-                       if(message.length() > 0) {\r
-                               ProcessMessage(message, pClientInfo);\r
-                       }\r
-\r
-                       std::size_t nextStartPos = pos + MessageDelimiter.length();\r
-                       if(nextStartPos < availibleData.length())\r
-                               availibleData = availibleData.substr(nextStartPos);\r
-                       else {\r
-                               availibleData.clear();\r
-                               break;\r
-                       }\r
-               }\r
-               else\r
-               {\r
-                       break;\r
-               }\r
-       }\r
-       if(pClientInfo)\r
-               pClientInfo->currentMessage_ = availibleData;\r
-}\r
-\r
-void AMCPProtocolStrategy::ProcessMessage(const std::wstring& message, ClientInfoPtr& pClientInfo)\r
-{      \r
-       CASPAR_LOG(info) << L"Received message from " << pClientInfo->print() << ": " << message << L"\\r\\n";\r
-       \r
-       bool bError = true;\r
-       MessageParserState state = New;\r
-\r
-       AMCPCommandPtr pCommand;\r
-\r
-       pCommand = InterpretCommandString(message, &state);\r
-\r
-       if(pCommand != 0) {\r
-               pCommand->SetClientInfo(pClientInfo);   \r
-               if(QueueCommand(pCommand))\r
-                       bError = false;\r
-               else\r
-                       state = GetChannel;\r
-       }\r
-\r
-       if(bError == true) {\r
-               std::wstringstream answer;\r
-               switch(state)\r
-               {\r
-               case GetCommand:\r
-                       answer << TEXT("400 ERROR\r\n") + message << "\r\n";\r
-                       break;\r
-               case GetChannel:\r
-                       answer << TEXT("401 ERROR\r\n");\r
-                       break;\r
-               case GetParameters:\r
-                       answer << TEXT("402 ERROR\r\n");\r
-                       break;\r
-               default:\r
-                       answer << TEXT("500 FAILED\r\n");\r
-                       break;\r
-               }\r
-               pClientInfo->Send(answer.str());\r
-       }\r
-}\r
-\r
-AMCPCommandPtr AMCPProtocolStrategy::InterpretCommandString(const std::wstring& message, MessageParserState* pOutState)\r
-{\r
-       std::vector<std::wstring> tokens;\r
-       unsigned int currentToken = 0;\r
-       std::wstring commandSwitch;\r
-\r
-       AMCPCommandPtr pCommand;\r
-       MessageParserState state = New;\r
-\r
-       std::size_t tokensInMessage = TokenizeMessage(message, &tokens);\r
-\r
-       //parse the message one token at the time\r
-       while(currentToken < tokensInMessage)\r
-       {\r
-               switch(state)\r
-               {\r
-               case New:\r
-                       if(tokens[currentToken][0] == TEXT('/'))\r
-                               state = GetSwitch;\r
-                       else\r
-                               state = GetCommand;\r
-                       break;\r
-\r
-               case GetSwitch:\r
-                       commandSwitch = tokens[currentToken];\r
-                       state = GetCommand;\r
-                       ++currentToken;\r
-                       break;\r
-\r
-               case GetCommand:\r
-                       pCommand = CommandFactory(tokens[currentToken]);\r
-                       if(pCommand == 0) {\r
-                               goto ParseFinnished;\r
-                       }\r
-                       else\r
-                       {\r
-                               pCommand->SetChannels(channels_);\r
-                               //Set scheduling\r
-                               if(commandSwitch.size() > 0) {\r
-                                       transform(commandSwitch.begin(), commandSwitch.end(), commandSwitch.begin(), toupper);\r
-\r
-                                       //if(commandSwitch == TEXT("/APP"))\r
-                                       //      pCommand->SetScheduling(AddToQueue);\r
-                                       //else if(commandSwitch  == TEXT("/IMMF"))\r
-                                       //      pCommand->SetScheduling(ImmediatelyAndClear);\r
-                               }\r
-\r
-                               if(pCommand->NeedChannel())\r
-                                       state = GetChannel;\r
-                               else\r
-                                       state = GetParameters;\r
-                       }\r
-                       ++currentToken;\r
-                       break;\r
-\r
-               case GetParameters:\r
-                       {\r
-                               _ASSERTE(pCommand != 0);\r
-                               int parameterCount=0;\r
-                               while(currentToken<tokensInMessage)\r
-                               {\r
-                                       pCommand->AddParameter(tokens[currentToken++]);\r
-                                       ++parameterCount;\r
-                               }\r
-\r
-                               if(parameterCount < pCommand->GetMinimumParameters()) {\r
-                                       goto ParseFinnished;\r
-                               }\r
-\r
-                               state = Done;\r
-                               break;\r
-                       }\r
-\r
-               case GetChannel:\r
-                       {\r
-//                             assert(pCommand != 0);\r
-\r
-                               std::wstring str = boost::trim_copy(tokens[currentToken]);\r
-                               std::vector<std::wstring> split;\r
-                               boost::split(split, str, boost::is_any_of("-"));\r
-                                       \r
-                               int channelIndex = -1;\r
-                               int layerIndex = -1;\r
-                               try\r
-                               {\r
-                                       channelIndex = boost::lexical_cast<int>(split[0]) - 1;\r
-\r
-                                       if(split.size() > 1)\r
-                                               layerIndex = boost::lexical_cast<int>(split[1]);\r
-                               }\r
-                               catch(...)\r
-                               {\r
-                                       goto ParseFinnished;\r
-                               }\r
-\r
-                               std::shared_ptr<core::video_channel> pChannel = GetChannelSafe(channelIndex, channels_);\r
-                               if(pChannel == 0) {\r
-                                       goto ParseFinnished;\r
-                               }\r
-\r
-                               pCommand->SetChannel(pChannel);\r
-                               pCommand->SetChannels(channels_);\r
-                               pCommand->SetChannelIndex(channelIndex);\r
-                               pCommand->SetLayerIntex(layerIndex);\r
-\r
-                               state = GetParameters;\r
-                               ++currentToken;\r
-                               break;\r
-                       }\r
-\r
-               default:        //Done and unexpected\r
-                       goto ParseFinnished;\r
-               }\r
-       }\r
-\r
-ParseFinnished:\r
-       if(state == GetParameters && pCommand->GetMinimumParameters()==0)\r
-               state = Done;\r
-\r
-       if(state != Done) {\r
-               pCommand.reset();\r
-       }\r
-\r
-       if(pOutState != 0) {\r
-               *pOutState = state;\r
-       }\r
-\r
-       return pCommand;\r
-}\r
-\r
-bool AMCPProtocolStrategy::QueueCommand(AMCPCommandPtr pCommand) {\r
-       if(pCommand->NeedChannel()) {\r
-               unsigned int channelIndex = pCommand->GetChannelIndex() + 1;\r
-               if(commandQueues_.size() > channelIndex) {\r
-                       commandQueues_[channelIndex]->AddCommand(pCommand);\r
-               }\r
-               else\r
-                       return false;\r
-       }\r
-       else {\r
-               commandQueues_[0]->AddCommand(pCommand);\r
-       }\r
-       return true;\r
-}\r
-\r
-AMCPCommandPtr AMCPProtocolStrategy::CommandFactory(const std::wstring& str)\r
-{\r
-       std::wstring s = str;\r
-       transform(s.begin(), s.end(), s.begin(), toupper);\r
-       \r
-       if         (s == TEXT("MIXER"))                 return std::make_shared<MixerCommand>();\r
-       else if(s == TEXT("DIAG"))                      return std::make_shared<DiagnosticsCommand>();\r
-       else if(s == TEXT("CHANNEL_GRID"))      return std::make_shared<ChannelGridCommand>();\r
-       else if(s == TEXT("CALL"))                      return std::make_shared<CallCommand>();\r
-       else if(s == TEXT("SWAP"))                      return std::make_shared<SwapCommand>();\r
-       else if(s == TEXT("LOAD"))                      return std::make_shared<LoadCommand>();\r
-       else if(s == TEXT("LOADBG"))            return std::make_shared<LoadbgCommand>();\r
-       else if(s == TEXT("ADD"))                       return std::make_shared<AddCommand>();\r
-       else if(s == TEXT("REMOVE"))            return std::make_shared<RemoveCommand>();\r
-       else if(s == TEXT("PAUSE"))                     return std::make_shared<PauseCommand>();\r
-       else if(s == TEXT("PLAY"))                      return std::make_shared<PlayCommand>();\r
-       else if(s == TEXT("STOP"))                      return std::make_shared<StopCommand>();\r
-       else if(s == TEXT("CLEAR"))                     return std::make_shared<ClearCommand>();\r
-       else if(s == TEXT("PRINT"))                     return std::make_shared<PrintCommand>();\r
-       else if(s == TEXT("LOG"))                       return std::make_shared<LogCommand>();\r
-       else if(s == TEXT("CG"))                        return std::make_shared<CGCommand>();\r
-       else if(s == TEXT("DATA"))                      return std::make_shared<DataCommand>();\r
-       else if(s == TEXT("CINF"))                      return std::make_shared<CinfCommand>();\r
-       else if(s == TEXT("INFO"))                      return std::make_shared<InfoCommand>(channels_);\r
-       else if(s == TEXT("CLS"))                       return std::make_shared<ClsCommand>();\r
-       else if(s == TEXT("TLS"))                       return std::make_shared<TlsCommand>();\r
-       else if(s == TEXT("VERSION"))           return std::make_shared<VersionCommand>();\r
-       else if(s == TEXT("BYE"))                       return std::make_shared<ByeCommand>();\r
-       else if(s == TEXT("SET"))                       return std::make_shared<SetCommand>();\r
-       //else if(s == TEXT("MONITOR"))\r
-       //{\r
-       //      result = AMCPCommandPtr(new MonitorCommand());\r
-       //}\r
-       //else if(s == TEXT("KILL"))\r
-       //{\r
-       //      result = AMCPCommandPtr(new KillCommand());\r
-       //}\r
-       return nullptr;\r
-}\r
-\r
-std::size_t AMCPProtocolStrategy::TokenizeMessage(const std::wstring& message, std::vector<std::wstring>* pTokenVector)\r
-{\r
-       //split on whitespace but keep strings within quotationmarks\r
-       //treat \ as the start of an escape-sequence: the following char will indicate what to actually put in the string\r
-\r
-       std::wstring currentToken;\r
-\r
-       char inQuote = 0;\r
-       bool getSpecialCode = false;\r
-\r
-       for(unsigned int charIndex=0; charIndex<message.size(); ++charIndex)\r
-       {\r
-               if(getSpecialCode)\r
-               {\r
-                       //insert code-handling here\r
-                       switch(message[charIndex])\r
-                       {\r
-                       case TEXT('\\'):\r
-                               currentToken += TEXT("\\");\r
-                               break;\r
-                       case TEXT('\"'):\r
-                               currentToken += TEXT("\"");\r
-                               break;\r
-                       case TEXT('n'):\r
-                               currentToken += TEXT("\n");\r
-                               break;\r
-                       default:\r
-                               break;\r
-                       };\r
-                       getSpecialCode = false;\r
-                       continue;\r
-               }\r
-\r
-               if(message[charIndex]==TEXT('\\'))\r
-               {\r
-                       getSpecialCode = true;\r
-                       continue;\r
-               }\r
-\r
-               if(message[charIndex]==' ' && inQuote==false)\r
-               {\r
-                       if(currentToken.size()>0)\r
-                       {\r
-                               pTokenVector->push_back(currentToken);\r
-                               currentToken.clear();\r
-                       }\r
-                       continue;\r
-               }\r
-\r
-               if(message[charIndex]==TEXT('\"'))\r
-               {\r
-                       inQuote ^= 1;\r
-\r
-                       if(currentToken.size()>0)\r
-                       {\r
-                               pTokenVector->push_back(currentToken);\r
-                               currentToken.clear();\r
-                       }\r
-                       continue;\r
-               }\r
-\r
-               currentToken += message[charIndex];\r
-       }\r
-\r
-       if(currentToken.size()>0)\r
-       {\r
-               pTokenVector->push_back(currentToken);\r
-               currentToken.clear();\r
-       }\r
-\r
-       return pTokenVector->size();\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"
+
+#include "AMCPProtocolStrategy.h"
+
+#include "../util/AsyncEventServer.h"
+#include "AMCPCommandsImpl.h"
+
+#include <stdio.h>
+#include <crtdbg.h>
+#include <string.h>
+#include <algorithm>
+#include <cctype>
+
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/lexical_cast.hpp>
+
+#if defined(_MSC_VER)
+#pragma warning (push, 1) // TODO: Legacy code, just disable warnings
+#endif
+
+namespace caspar { namespace protocol { namespace amcp {
+
+using IO::ClientInfoPtr;
+
+const std::wstring AMCPProtocolStrategy::MessageDelimiter = TEXT("\r\n");
+
+inline std::shared_ptr<core::video_channel> GetChannelSafe(unsigned int index, const std::vector<spl::shared_ptr<core::video_channel>>& channels)
+{
+       return index < channels.size() ? std::shared_ptr<core::video_channel>(channels[index]) : nullptr;
+}
+
+AMCPProtocolStrategy::AMCPProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels) : channels_(channels) {
+       AMCPCommandQueuePtr pGeneralCommandQueue(new AMCPCommandQueue());
+       commandQueues_.push_back(pGeneralCommandQueue);
+
+
+       std::shared_ptr<core::video_channel> pChannel;
+       unsigned int index = -1;
+       //Create a commandpump for each video_channel
+       while((pChannel = GetChannelSafe(++index, channels_)) != 0) {
+               AMCPCommandQueuePtr pChannelCommandQueue(new AMCPCommandQueue());
+               std::wstring title = TEXT("video_channel ");
+
+               //HACK: Perform real conversion from int to string
+               TCHAR num = TEXT('1')+static_cast<TCHAR>(index);
+               title += num;
+               
+               commandQueues_.push_back(pChannelCommandQueue);
+       }
+}
+
+AMCPProtocolStrategy::~AMCPProtocolStrategy() {
+}
+
+void AMCPProtocolStrategy::Parse(const TCHAR* pData, int charCount, ClientInfoPtr pClientInfo)
+{
+       size_t pos;
+       std::wstring recvData(pData, charCount);
+       std::wstring availibleData = (pClientInfo != nullptr ? pClientInfo->currentMessage_ : L"") + recvData;
+
+       while(true) {
+               pos = availibleData.find(MessageDelimiter);
+               if(pos != std::wstring::npos)
+               {
+                       std::wstring message = availibleData.substr(0,pos);
+
+                       //This is where a complete message gets taken care of
+                       if(message.length() > 0) {
+                               ProcessMessage(message, pClientInfo);
+                       }
+
+                       std::size_t nextStartPos = pos + MessageDelimiter.length();
+                       if(nextStartPos < availibleData.length())
+                               availibleData = availibleData.substr(nextStartPos);
+                       else {
+                               availibleData.clear();
+                               break;
+                       }
+               }
+               else
+               {
+                       break;
+               }
+       }
+       if(pClientInfo)
+               pClientInfo->currentMessage_ = availibleData;
+}
+
+void AMCPProtocolStrategy::ProcessMessage(const std::wstring& message, ClientInfoPtr& pClientInfo)
+{      
+       CASPAR_LOG(info) << L"Received message from " << pClientInfo->print() << ": " << message << L"\\r\\n";
+       
+       bool bError = true;
+       MessageParserState state = New;
+
+       AMCPCommandPtr pCommand;
+
+       pCommand = InterpretCommandString(message, &state);
+
+       if(pCommand != 0) {
+               pCommand->SetClientInfo(pClientInfo);   
+               if(QueueCommand(pCommand))
+                       bError = false;
+               else
+                       state = GetChannel;
+       }
+
+       if(bError == true) {
+               std::wstringstream answer;
+               switch(state)
+               {
+               case GetCommand:
+                       answer << TEXT("400 ERROR\r\n") + message << "\r\n";
+                       break;
+               case GetChannel:
+                       answer << TEXT("401 ERROR\r\n");
+                       break;
+               case GetParameters:
+                       answer << TEXT("402 ERROR\r\n");
+                       break;
+               default:
+                       answer << TEXT("500 FAILED\r\n");
+                       break;
+               }
+               pClientInfo->Send(answer.str());
+       }
+}
+
+AMCPCommandPtr AMCPProtocolStrategy::InterpretCommandString(const std::wstring& message, MessageParserState* pOutState)
+{
+       std::vector<std::wstring> tokens;
+       unsigned int currentToken = 0;
+       std::wstring commandSwitch;
+
+       AMCPCommandPtr pCommand;
+       MessageParserState state = New;
+
+       std::size_t tokensInMessage = TokenizeMessage(message, &tokens);
+
+       //parse the message one token at the time
+       while(currentToken < tokensInMessage)
+       {
+               switch(state)
+               {
+               case New:
+                       if(tokens[currentToken][0] == TEXT('/'))
+                               state = GetSwitch;
+                       else
+                               state = GetCommand;
+                       break;
+
+               case GetSwitch:
+                       commandSwitch = tokens[currentToken];
+                       state = GetCommand;
+                       ++currentToken;
+                       break;
+
+               case GetCommand:
+                       pCommand = CommandFactory(tokens[currentToken]);
+                       if(pCommand == 0) {
+                               goto ParseFinnished;
+                       }
+                       else
+                       {
+                               pCommand->SetChannels(channels_);
+                               //Set scheduling
+                               if(commandSwitch.size() > 0) {
+                                       transform(commandSwitch.begin(), commandSwitch.end(), commandSwitch.begin(), toupper);
+
+                                       //if(commandSwitch == TEXT("/APP"))
+                                       //      pCommand->SetScheduling(AddToQueue);
+                                       //else if(commandSwitch  == TEXT("/IMMF"))
+                                       //      pCommand->SetScheduling(ImmediatelyAndClear);
+                               }
+
+                               if(pCommand->NeedChannel())
+                                       state = GetChannel;
+                               else
+                                       state = GetParameters;
+                       }
+                       ++currentToken;
+                       break;
+
+               case GetParameters:
+                       {
+                               _ASSERTE(pCommand != 0);
+                               int parameterCount=0;
+                               while(currentToken<tokensInMessage)
+                               {
+                                       pCommand->AddParameter(tokens[currentToken++]);
+                                       ++parameterCount;
+                               }
+
+                               if(parameterCount < pCommand->GetMinimumParameters()) {
+                                       goto ParseFinnished;
+                               }
+
+                               state = Done;
+                               break;
+                       }
+
+               case GetChannel:
+                       {
+//                             assert(pCommand != 0);
+
+                               std::wstring str = boost::trim_copy(tokens[currentToken]);
+                               std::vector<std::wstring> split;
+                               boost::split(split, str, boost::is_any_of("-"));
+                                       
+                               int channelIndex = -1;
+                               int layerIndex = -1;
+                               try
+                               {
+                                       channelIndex = boost::lexical_cast<int>(split[0]) - 1;
+
+                                       if(split.size() > 1)
+                                               layerIndex = boost::lexical_cast<int>(split[1]);
+                               }
+                               catch(...)
+                               {
+                                       goto ParseFinnished;
+                               }
+
+                               std::shared_ptr<core::video_channel> pChannel = GetChannelSafe(channelIndex, channels_);
+                               if(pChannel == 0) {
+                                       goto ParseFinnished;
+                               }
+
+                               pCommand->SetChannel(pChannel);
+                               pCommand->SetChannels(channels_);
+                               pCommand->SetChannelIndex(channelIndex);
+                               pCommand->SetLayerIntex(layerIndex);
+
+                               state = GetParameters;
+                               ++currentToken;
+                               break;
+                       }
+
+               default:        //Done and unexpected
+                       goto ParseFinnished;
+               }
+       }
+
+ParseFinnished:
+       if(state == GetParameters && pCommand->GetMinimumParameters()==0)
+               state = Done;
+
+       if(state != Done) {
+               pCommand.reset();
+       }
+
+       if(pOutState != 0) {
+               *pOutState = state;
+       }
+
+       return pCommand;
+}
+
+bool AMCPProtocolStrategy::QueueCommand(AMCPCommandPtr pCommand) {
+       if(pCommand->NeedChannel()) {
+               unsigned int channelIndex = pCommand->GetChannelIndex() + 1;
+               if(commandQueues_.size() > channelIndex) {
+                       commandQueues_[channelIndex]->AddCommand(pCommand);
+               }
+               else
+                       return false;
+       }
+       else {
+               commandQueues_[0]->AddCommand(pCommand);
+       }
+       return true;
+}
+
+AMCPCommandPtr AMCPProtocolStrategy::CommandFactory(const std::wstring& str)
+{
+       std::wstring s = str;
+       transform(s.begin(), s.end(), s.begin(), toupper);
+       
+       if         (s == TEXT("MIXER"))                 return std::make_shared<MixerCommand>();
+       else if(s == TEXT("DIAG"))                      return std::make_shared<DiagnosticsCommand>();
+       else if(s == TEXT("CHANNEL_GRID"))      return std::make_shared<ChannelGridCommand>();
+       else if(s == TEXT("CALL"))                      return std::make_shared<CallCommand>();
+       else if(s == TEXT("SWAP"))                      return std::make_shared<SwapCommand>();
+       else if(s == TEXT("LOAD"))                      return std::make_shared<LoadCommand>();
+       else if(s == TEXT("LOADBG"))            return std::make_shared<LoadbgCommand>();
+       else if(s == TEXT("ADD"))                       return std::make_shared<AddCommand>();
+       else if(s == TEXT("REMOVE"))            return std::make_shared<RemoveCommand>();
+       else if(s == TEXT("PAUSE"))                     return std::make_shared<PauseCommand>();
+       else if(s == TEXT("PLAY"))                      return std::make_shared<PlayCommand>();
+       else if(s == TEXT("STOP"))                      return std::make_shared<StopCommand>();
+       else if(s == TEXT("CLEAR"))                     return std::make_shared<ClearCommand>();
+       else if(s == TEXT("PRINT"))                     return std::make_shared<PrintCommand>();
+       else if(s == TEXT("LOG"))                       return std::make_shared<LogCommand>();
+       else if(s == TEXT("CG"))                        return std::make_shared<CGCommand>();
+       else if(s == TEXT("DATA"))                      return std::make_shared<DataCommand>();
+       else if(s == TEXT("CINF"))                      return std::make_shared<CinfCommand>();
+       else if(s == TEXT("INFO"))                      return std::make_shared<InfoCommand>(channels_);
+       else if(s == TEXT("CLS"))                       return std::make_shared<ClsCommand>();
+       else if(s == TEXT("TLS"))                       return std::make_shared<TlsCommand>();
+       else if(s == TEXT("VERSION"))           return std::make_shared<VersionCommand>();
+       else if(s == TEXT("BYE"))                       return std::make_shared<ByeCommand>();
+       else if(s == TEXT("SET"))                       return std::make_shared<SetCommand>();
+       //else if(s == TEXT("MONITOR"))
+       //{
+       //      result = AMCPCommandPtr(new MonitorCommand());
+       //}
+       //else if(s == TEXT("KILL"))
+       //{
+       //      result = AMCPCommandPtr(new KillCommand());
+       //}
+       return nullptr;
+}
+
+std::size_t AMCPProtocolStrategy::TokenizeMessage(const std::wstring& message, std::vector<std::wstring>* pTokenVector)
+{
+       //split on whitespace but keep strings within quotationmarks
+       //treat \ as the start of an escape-sequence: the following char will indicate what to actually put in the string
+
+       std::wstring currentToken;
+
+       char inQuote = 0;
+       bool getSpecialCode = false;
+
+       for(unsigned int charIndex=0; charIndex<message.size(); ++charIndex)
+       {
+               if(getSpecialCode)
+               {
+                       //insert code-handling here
+                       switch(message[charIndex])
+                       {
+                       case TEXT('\\'):
+                               currentToken += TEXT("\\");
+                               break;
+                       case TEXT('\"'):
+                               currentToken += TEXT("\"");
+                               break;
+                       case TEXT('n'):
+                               currentToken += TEXT("\n");
+                               break;
+                       default:
+                               break;
+                       };
+                       getSpecialCode = false;
+                       continue;
+               }
+
+               if(message[charIndex]==TEXT('\\'))
+               {
+                       getSpecialCode = true;
+                       continue;
+               }
+
+               if(message[charIndex]==' ' && inQuote==false)
+               {
+                       if(currentToken.size()>0)
+                       {
+                               pTokenVector->push_back(currentToken);
+                               currentToken.clear();
+                       }
+                       continue;
+               }
+
+               if(message[charIndex]==TEXT('\"'))
+               {
+                       inQuote ^= 1;
+
+                       if(currentToken.size()>0)
+                       {
+                               pTokenVector->push_back(currentToken);
+                               currentToken.clear();
+                       }
+                       continue;
+               }
+
+               currentToken += message[charIndex];
+       }
+
+       if(currentToken.size()>0)
+       {
+               pTokenVector->push_back(currentToken);
+               currentToken.clear();
+       }
+
+       return pTokenVector->size();
+}
+
+}      //namespace amcp
 }}     //namespace caspar
\ No newline at end of file