]> git.sesse.net Git - casparcg/blob - protocol/cii/CIIProtocolStrategy.cpp
- Fixed diag to work with new SFML version.
[casparcg] / protocol / cii / CIIProtocolStrategy.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Nicklas P Andersson
20 */
21
22  
23 #include "../StdAfx.h"
24
25 #include <string>
26 #include <sstream>
27 #include <algorithm>
28 #include "CIIProtocolStrategy.h"
29 #include "CIICommandsimpl.h"
30 #include <modules/flash/producer/flash_producer.h>
31 #include <core/producer/transition/transition_producer.h>
32 #include <core/mixer/mixer.h>
33 #include <core/diagnostics/call_context.h>
34 #include <common/env.h>
35
36 #include <boost/algorithm/string/replace.hpp>
37
38 #if defined(_MSC_VER)
39 #pragma warning (push, 1) // TODO: Legacy code, just disable warnings
40 #endif
41
42 namespace caspar { namespace protocol { namespace cii {
43         
44 using namespace core;
45
46 const std::wstring CIIProtocolStrategy::MessageDelimiter = L"\r\n";
47 const wchar_t CIIProtocolStrategy::TokenDelimiter = L'\\';
48
49 CIIProtocolStrategy::CIIProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels) : pChannel_(channels.at(0)), executor_(L"CIIProtocolStrategy")
50 {
51 }
52
53 //The paser method expects message to be complete messages with the delimiter stripped away.
54 //Thesefore the AMCPProtocolStrategy should be decorated with a delimiter_based_chunking_strategy
55 void CIIProtocolStrategy::Parse(const std::wstring& message, IO::ClientInfoPtr client) 
56 {
57         if(message.length() > 0)
58         {
59                 ProcessMessage(message, client);
60                 client->send(std::wstring(L"*\r\n"));
61         }
62 }
63
64 void CIIProtocolStrategy::ProcessMessage(const std::wstring& message, IO::ClientInfoPtr pClientInfo)
65 {       
66         CASPAR_LOG(info) << L"Received message from " << pClientInfo->print() << ": " << message << L"\\r\\n";
67
68         std::vector<std::wstring> tokens;
69         int tokenCount = TokenizeMessage(message, &tokens);
70
71         CIICommandPtr pCommand = Create(tokens[0]);
72         if((pCommand != 0) && (tokenCount-1) >= pCommand->GetMinimumParameters()) 
73         {
74                 pCommand->Setup(tokens);
75                 executor_.begin_invoke([=]{pCommand->Execute();});
76         }
77         else {} //report error  
78 }
79
80 int CIIProtocolStrategy::TokenizeMessage(const std::wstring& message, std::vector<std::wstring>* pTokenVector)
81 {
82         std::wstringstream currentToken;
83
84         for(unsigned int charIndex=0; charIndex<message.size(); ++charIndex) 
85         {
86                 if(message[charIndex] == TokenDelimiter) 
87                 {
88                         pTokenVector->push_back(currentToken.str());
89                         currentToken.str(TEXT(""));
90                         continue;
91                 }
92
93                 if(message[charIndex] == TEXT('\"')) 
94                         currentToken << TEXT("&quot;");         
95                 else if(message[charIndex] == TEXT('<')) 
96                         currentToken << TEXT("&lt;");           
97                 else if(message[charIndex] == TEXT('>')) 
98                         currentToken << TEXT("&gt;");           
99                 else 
100                         currentToken << message[charIndex];
101         }
102
103         if(currentToken.str().size() > 0)
104                 pTokenVector->push_back(currentToken.str());    
105
106         return (int)pTokenVector->size();
107 }
108
109 /************
110 // Examples (<X> = ASCIICHAR X)
111
112 I\25\3\VII\\                                                                    sätter outputtype till 'vii'
113 I\25\4\1\\                                                                              enablar framebuffer (ignore this)
114
115 M\C/SVTNEWS\\                                                                   pekar ut vilken grafisk profil som skall användas
116
117 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
118
119 T\7\4009.VII\A\\                                                                lägger ut skylt 4009
120
121 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)
122
123 V\5\3\1\1\namn.tga\1\\                                                  lägger ut bilden namn.tga
124 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.
125
126 *************/
127
128 /**********************
129 New Commands to support the Netupe automation system
130 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
131 Y\<27>\\                                                                                        Stop. Här kommer ett lagerID också att skickas med (<27> = ESC)
132 Y\<254>\\                                                                                       Clear Canvas. Här kommer ett lagerID också att skickas med, utan det skall allt tömmas
133 Y\<213><243>\\                                                                          Play. Här kommer ett lagerID också att skickas med
134
135 **********************/
136 CIICommandPtr CIIProtocolStrategy::Create(const std::wstring& name)
137 {
138         switch(name[0])
139         {
140                 case TEXT('M'): return std::make_shared<MediaCommand>(this);
141                 case TEXT('W'): return std::make_shared<WriteCommand>(this);
142                 case TEXT('T'): return std::make_shared<ImagestoreCommand>(this);
143                 case TEXT('V'): return std::make_shared<MiscellaneousCommand>(this);
144                 case TEXT('Y'): return std::make_shared<KeydataCommand>(this);
145                 default:                return nullptr;
146         }
147 }
148
149 void CIIProtocolStrategy::WriteTemplateData(const std::wstring& templateName, const std::wstring& titleName, const std::wstring& xmlData) 
150 {
151         std::wstring fullTemplateFilename = env::template_folder();
152         if(currentProfile_.size() > 0)
153         {
154                 fullTemplateFilename += currentProfile_;
155                 fullTemplateFilename += TEXT("\\");
156         }
157         fullTemplateFilename += templateName;
158         fullTemplateFilename = flash::find_template(fullTemplateFilename);
159         if(fullTemplateFilename.empty())
160         {
161                 CASPAR_LOG(error) << "Failed to save instance of " << templateName << TEXT(" as ") << titleName << TEXT(", template ") << fullTemplateFilename << " not found";
162                 return;
163         }
164         core::diagnostics::scoped_call_context save;
165         core::diagnostics::call_context::for_thread().video_channel = 1;
166         core::diagnostics::call_context::for_thread().layer = 0;
167
168         auto producer = flash::create_producer(this->GetChannel()->frame_factory(), this->GetChannel()->video_format_desc(), { env::template_folder() + TEXT("CG.fth") });
169
170         std::wstringstream flashParam;
171         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>");
172         std::vector<std::wstring> params;
173         params.push_back(flashParam.str());
174         producer->call(std::move(params));
175
176         CASPAR_LOG(info) << "Saved an instance of " << templateName << TEXT(" as ") << titleName ;
177
178         PutPreparedTemplate(titleName, spl::shared_ptr<core::frame_producer>(std::move(producer)));
179         
180 }
181
182 void CIIProtocolStrategy::DisplayTemplate(const std::wstring& titleName)
183 {
184         try
185         {
186                 pChannel_->stage().load(0, GetPreparedTemplate(titleName));
187                 pChannel_->stage().play(0);
188
189                 CASPAR_LOG(info) << L"Displayed title " << titleName ;
190         }
191         catch(caspar_exception&)
192         {
193                 CASPAR_LOG(error) << L"Failed to display title " << titleName;
194         }
195 }
196
197 void CIIProtocolStrategy::DisplayMediaFile(const std::wstring& filename) 
198 {
199         transition_info transition;
200         transition.type = transition_type::mix;
201         transition.duration = 12;
202
203         core::diagnostics::scoped_call_context save;
204         core::diagnostics::call_context::for_thread().video_channel = 1;
205         core::diagnostics::call_context::for_thread().layer = 0;
206
207         auto pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), filename);
208         auto pTransition = create_transition_producer(GetChannel()->video_format_desc().field_mode, pFP, transition);
209
210         try
211         {
212                 pChannel_->stage().load(0, pTransition);
213         }
214         catch(...)
215         {
216                 CASPAR_LOG_CURRENT_EXCEPTION();
217                 CASPAR_LOG(error) << L"Failed to display " << filename ;
218                 return;
219         }
220
221         pChannel_->stage().play(0);
222
223         CASPAR_LOG(info) << L"Displayed " << filename;
224 }
225
226 spl::shared_ptr<core::frame_producer> CIIProtocolStrategy::GetPreparedTemplate(const std::wstring& titleName)
227 {
228         spl::shared_ptr<core::frame_producer> result(frame_producer::empty());
229
230         TitleList::iterator it = std::find(titles_.begin(), titles_.end(), titleName);
231         if(it != titles_.end()) {
232                 CASPAR_LOG(debug) << L"Found title with name " << it->titleName;
233                 result = (*it).pframe_producer;
234         }
235         else 
236                 CASPAR_LOG(error) << L"Could not find title with name " << titleName;
237
238         return result;
239 }
240
241 void CIIProtocolStrategy::PutPreparedTemplate(const std::wstring& titleName, spl::shared_ptr<core::frame_producer>& pFP)
242 {
243         CASPAR_LOG(debug) << L"Saved title with name " << titleName;
244
245         TitleList::iterator it = std::find(titles_.begin(), titles_.end(), titleName);
246         if(it != titles_.end()) {
247                 titles_.remove((*it));
248         }
249
250         titles_.push_front(TitleHolder(titleName, pFP));
251
252         if(titles_.size() >= 6)
253                 titles_.resize(5);
254 }
255
256 bool operator==(const CIIProtocolStrategy::TitleHolder& lhs, const std::wstring& rhs) 
257 {
258         return lhs.titleName == rhs;
259 }
260
261 bool operator==(const std::wstring& lhs, const CIIProtocolStrategy::TitleHolder& rhs)
262 {
263         return lhs == rhs.titleName;
264 }
265
266 }}}