2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG (www.casparcg.com).
\r
6 * CasparCG is free software: you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation, either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * CasparCG is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
\r
19 * Author: Nicklas P Andersson
\r
23 #include "../StdAfx.h"
\r
27 #include <algorithm>
\r
28 #include "CIIProtocolStrategy.h"
\r
29 #include "CIICommandsimpl.h"
\r
30 #include <modules/flash/producer/flash_producer.h>
\r
31 #include <core/producer/transition/transition_producer.h>
\r
32 #include <core/mixer/mixer.h>
\r
33 #include <common/env.h>
\r
35 #include <boost/algorithm/string/replace.hpp>
\r
37 #if defined(_MSC_VER)
\r
38 #pragma warning (push, 1) // TODO: Legacy code, just disable warnings
\r
41 namespace caspar { namespace protocol { namespace cii {
\r
43 using namespace core;
\r
45 const std::wstring CIIProtocolStrategy::MessageDelimiter = TEXT("\r\n");
\r
46 const TCHAR CIIProtocolStrategy::TokenDelimiter = TEXT('\\');
\r
48 CIIProtocolStrategy::CIIProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels) : pChannel_(channels.at(0)), executor_(L"CIIProtocolStrategy")
\r
52 void CIIProtocolStrategy::Parse(const TCHAR* pData, int charCount, IO::ClientInfoPtr pClientInfo)
\r
55 std::wstring msg(pData, charCount);
\r
56 std::wstring availibleData = currentMessage_ + msg;
\r
60 pos = availibleData.find(MessageDelimiter);
\r
61 if(pos != std::wstring::npos)
\r
63 std::wstring message = availibleData.substr(0,pos);
\r
65 if(message.length() > 0) {
\r
66 ProcessMessage(message, pClientInfo);
\r
67 if(pClientInfo != 0)
\r
68 pClientInfo->Send(TEXT("*\r\n"));
\r
71 std::size_t nextStartPos = pos + MessageDelimiter.length();
\r
72 if(nextStartPos < availibleData.length())
\r
73 availibleData = availibleData.substr(nextStartPos);
\r
76 availibleData.clear();
\r
83 currentMessage_ = availibleData;
\r
86 void CIIProtocolStrategy::ProcessMessage(const std::wstring& message, IO::ClientInfoPtr pClientInfo)
\r
88 CASPAR_LOG(info) << L"Received message from " << pClientInfo->print() << ": " << message << L"\\r\\n";
\r
90 std::vector<std::wstring> tokens;
\r
91 int tokenCount = TokenizeMessage(message, &tokens);
\r
93 CIICommandPtr pCommand = Create(tokens[0]);
\r
94 if((pCommand != 0) && (tokenCount-1) >= pCommand->GetMinimumParameters())
\r
96 pCommand->Setup(tokens);
\r
97 executor_.begin_invoke([=]{pCommand->Execute();});
\r
99 else {} //report error
\r
102 int CIIProtocolStrategy::TokenizeMessage(const std::wstring& message, std::vector<std::wstring>* pTokenVector)
\r
104 std::wstringstream currentToken;
\r
106 for(unsigned int charIndex=0; charIndex<message.size(); ++charIndex)
\r
108 if(message[charIndex] == TokenDelimiter)
\r
110 pTokenVector->push_back(currentToken.str());
\r
111 currentToken.str(TEXT(""));
\r
115 if(message[charIndex] == TEXT('\"'))
\r
116 currentToken << TEXT(""");
\r
117 else if(message[charIndex] == TEXT('<'))
\r
118 currentToken << TEXT("<");
\r
119 else if(message[charIndex] == TEXT('>'))
\r
120 currentToken << TEXT(">");
\r
122 currentToken << message[charIndex];
\r
125 if(currentToken.str().size() > 0)
\r
126 pTokenVector->push_back(currentToken.str());
\r
128 return (int)pTokenVector->size();
\r
132 // Examples (<X> = ASCIICHAR X)
\r
134 I\25\3\VII\\ sätter outputtype till 'vii'
\r
135 I\25\4\1\\ enablar framebuffer (ignore this)
\r
137 M\C/SVTNEWS\\ pekar ut vilken grafisk profil som skall användas
\r
139 W\4009\4067\Jonas Björkman\\ Skriver "Jonas Björkman" till första textfältet i template 4067 och sparar den färdiga skylten som 4009
\r
141 T\7\4009.VII\A\\ lägger ut skylt 4009
\r
143 Y\<205><247><202><196><192><192><200><248>\\ lägger ut skylten 4008 (<205><247><202><196><192><192><200><248> = "=g:4008h" om man drar bort 144 från varje asciivärde)
\r
145 V\5\3\1\1\namn.tga\1\\ lägger ut bilden namn.tga
\r
146 V\0\1\D\C\10\0\0\0\\ gör någon inställning som har med föregående kommando att göra.
\r
150 /**********************
\r
151 New Commands to support the Netupe automation system
\r
152 V\5\13\1\1\Template\0\TabField1\TabField2...\\ Build. Ettan före Template indikerar vilket lager den nya templaten skall laddas in i. OBS. Skall inte visas efter det här steget
\r
153 Y\<27>\\ Stop. Här kommer ett lagerID också att skickas med (<27> = ESC)
\r
154 Y\<254>\\ Clear Canvas. Här kommer ett lagerID också att skickas med, utan det skall allt tömmas
\r
155 Y\<213><243>\\ Play. Här kommer ett lagerID också att skickas med
\r
157 **********************/
\r
158 CIICommandPtr CIIProtocolStrategy::Create(const std::wstring& name)
\r
162 case TEXT('M'): return std::make_shared<MediaCommand>(this);
\r
163 case TEXT('W'): return std::make_shared<WriteCommand>(this);
\r
164 case TEXT('T'): return std::make_shared<ImagestoreCommand>(this);
\r
165 case TEXT('V'): return std::make_shared<MiscellaneousCommand>(this);
\r
166 case TEXT('Y'): return std::make_shared<KeydataCommand>(this);
\r
167 default: return nullptr;
\r
171 void CIIProtocolStrategy::WriteTemplateData(const std::wstring& templateName, const std::wstring& titleName, const std::wstring& xmlData)
\r
173 std::wstring fullTemplateFilename = env::template_folder();
\r
174 if(currentProfile_.size() > 0)
\r
176 fullTemplateFilename += currentProfile_;
\r
177 fullTemplateFilename += TEXT("\\");
\r
179 fullTemplateFilename += templateName;
\r
180 fullTemplateFilename = flash::find_template(fullTemplateFilename);
\r
181 if(fullTemplateFilename.empty())
\r
183 CASPAR_LOG(error) << "Failed to save instance of " << templateName << TEXT(" as ") << titleName << TEXT(", template ") << fullTemplateFilename << " not found";
\r
187 auto producer = flash::create_producer(this->GetChannel()->frame_factory(), this->GetChannel()->video_format_desc(), boost::assign::list_of(env::template_folder()+TEXT("CG.fth")));
\r
189 std::wstringstream flashParam;
\r
190 flashParam << TEXT("<invoke name=\"Add\" returntype=\"xml\"><arguments><number>1</number><string>") << currentProfile_ << '/' << templateName << TEXT("</string><number>0</number><true/><string> </string><string><![CDATA[ ") << xmlData << TEXT(" ]]></string></arguments></invoke>");
\r
191 producer->call(flashParam.str());
\r
193 CASPAR_LOG(info) << "Saved an instance of " << templateName << TEXT(" as ") << titleName ;
\r
195 PutPreparedTemplate(titleName, spl::shared_ptr<core::frame_producer>(std::move(producer)));
\r
199 void CIIProtocolStrategy::DisplayTemplate(const std::wstring& titleName)
\r
203 pChannel_->stage().load(0, GetPreparedTemplate(titleName));
\r
204 pChannel_->stage().play(0);
\r
206 CASPAR_LOG(info) << L"Displayed title " << titleName ;
\r
208 catch(caspar_exception&)
\r
210 CASPAR_LOG(error) << L"Failed to display title " << titleName;
\r
214 void CIIProtocolStrategy::DisplayMediaFile(const std::wstring& filename)
\r
216 transition_info transition;
\r
217 transition.type = transition_type::mix;
\r
218 transition.duration = 12;
\r
220 auto pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), filename);
\r
221 auto pTransition = create_transition_producer(GetChannel()->video_format_desc().field_mode, pFP, transition);
\r
225 pChannel_->stage().load(0, pTransition);
\r
229 CASPAR_LOG_CURRENT_EXCEPTION();
\r
230 CASPAR_LOG(error) << L"Failed to display " << filename ;
\r
234 pChannel_->stage().play(0);
\r
236 CASPAR_LOG(info) << L"Displayed " << filename;
\r
239 spl::shared_ptr<core::frame_producer> CIIProtocolStrategy::GetPreparedTemplate(const std::wstring& titleName)
\r
241 spl::shared_ptr<core::frame_producer> result(frame_producer::empty());
\r
243 TitleList::iterator it = std::find(titles_.begin(), titles_.end(), titleName);
\r
244 if(it != titles_.end()) {
\r
245 CASPAR_LOG(debug) << L"Found title with name " << it->titleName;
\r
246 result = (*it).pframe_producer;
\r
249 CASPAR_LOG(error) << L"Could not find title with name " << titleName;
\r
254 void CIIProtocolStrategy::PutPreparedTemplate(const std::wstring& titleName, spl::shared_ptr<core::frame_producer>& pFP)
\r
256 CASPAR_LOG(debug) << L"Saved title with name " << titleName;
\r
258 TitleList::iterator it = std::find(titles_.begin(), titles_.end(), titleName);
\r
259 if(it != titles_.end()) {
\r
260 titles_.remove((*it));
\r
263 titles_.push_front(TitleHolder(titleName, pFP));
\r
265 if(titles_.size() >= 6)
\r
269 bool operator==(const CIIProtocolStrategy::TitleHolder& lhs, const std::wstring& rhs)
\r
271 return lhs.titleName == rhs;
\r
274 bool operator==(const std::wstring& lhs, const CIIProtocolStrategy::TitleHolder& rhs)
\r
276 return lhs == rhs.titleName;
\r