]> git.sesse.net Git - casparcg/blob - protocol/amcp/AMCPCommandsImpl.cpp
Subtree merge of old SVN "docs" folder into the "master" git branch. You can see...
[casparcg] / protocol / amcp / AMCPCommandsImpl.cpp
1 /*\r
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\r
5 *\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
10 *\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
15 *\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
18 *\r
19 * Author: Nicklas P Andersson\r
20 */\r
21 \r
22 #include "../StdAfx.h"\r
23 \r
24 #if defined(_MSC_VER)\r
25 #pragma warning (push, 1) // TODO: Legacy code, just disable warnings\r
26 #endif\r
27 \r
28 #include "AMCPCommandsImpl.h"\r
29 #include "AMCPProtocolStrategy.h"\r
30 \r
31 #include <common/env.h>\r
32 \r
33 #include <common/log/log.h>\r
34 #include <common/diagnostics/graph.h>\r
35 #include <common/os/windows/current_version.h>\r
36 #include <common/os/windows/system_info.h>\r
37 #include <common/utility/string.h>\r
38 #include <common/utility/utf8conv.h>\r
39 \r
40 #include <core/producer/frame_producer.h>\r
41 #include <core/video_format.h>\r
42 #include <core/producer/transition/transition_producer.h>\r
43 #include <core/producer/channel/channel_producer.h>\r
44 #include <core/producer/frame/frame_transform.h>\r
45 #include <core/producer/stage.h>\r
46 #include <core/producer/layer.h>\r
47 #include <core/mixer/mixer.h>\r
48 #include <core/mixer/gpu/ogl_device.h>\r
49 #include <core/consumer/output.h>\r
50 \r
51 #include <modules/bluefish/bluefish.h>\r
52 #include <modules/decklink/decklink.h>\r
53 #include <modules/ffmpeg/ffmpeg.h>\r
54 #include <modules/flash/flash.h>\r
55 #include <modules/flash/util/swf.h>\r
56 #include <modules/flash/producer/flash_producer.h>\r
57 #include <modules/flash/producer/cg_producer.h>\r
58 #include <modules/ffmpeg/producer/util/util.h>\r
59 #include <modules/image/image.h>\r
60 #include <modules/ogl/ogl.h>\r
61 \r
62 #include <algorithm>\r
63 #include <locale>\r
64 #include <fstream>\r
65 #include <memory>\r
66 #include <cctype>\r
67 #include <io.h>\r
68 \r
69 #include <boost/date_time/posix_time/posix_time.hpp>\r
70 #include <boost/lexical_cast.hpp>\r
71 #include <boost/algorithm/string.hpp>\r
72 #include <boost/filesystem.hpp>\r
73 #include <boost/filesystem/fstream.hpp>\r
74 #include <boost/regex.hpp>\r
75 #include <boost/property_tree/xml_parser.hpp>\r
76 #include <boost/locale.hpp>\r
77 #include <boost/range/adaptor/transformed.hpp>\r
78 #include <boost/range/algorithm/copy.hpp>\r
79 #include <boost/archive/iterators/base64_from_binary.hpp>\r
80 #include <boost/archive/iterators/insert_linebreaks.hpp>\r
81 #include <boost/archive/iterators/transform_width.hpp>\r
82 \r
83 #include <tbb/concurrent_unordered_map.h>\r
84 \r
85 /* Return codes\r
86 \r
87 100 [action]                    Information om att något har hänt  \r
88 101 [action]                    Information om att något har hänt, en rad data skickas  \r
89 \r
90 202 [kommando] OK               Kommandot har utförts  \r
91 201 [kommando] OK               Kommandot har utförts, och en rad data skickas tillbaka  \r
92 200 [kommando] OK               Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad  \r
93 \r
94 400 ERROR                               Kommandot kunde inte förstås  \r
95 401 [kommando] ERROR    Ogiltig kanal  \r
96 402 [kommando] ERROR    Parameter saknas  \r
97 403 [kommando] ERROR    Ogiltig parameter  \r
98 404 [kommando] ERROR    Mediafilen hittades inte  \r
99 \r
100 500 FAILED                              Internt configurationfel  \r
101 501 [kommando] FAILED   Internt configurationfel  \r
102 502 [kommando] FAILED   Oläslig mediafil  \r
103 \r
104 600 [kommando] FAILED   funktion ej implementerad\r
105 */\r
106 \r
107 namespace caspar { namespace protocol {\r
108 \r
109 using namespace core;\r
110 \r
111 std::wstring read_file_base64(const boost::filesystem::wpath& file)\r
112 {\r
113         using namespace boost::archive::iterators;\r
114 \r
115         boost::filesystem::ifstream filestream(file, std::ios::binary);\r
116 \r
117         if (!filestream)\r
118                 return L"";\r
119 \r
120         // From http://www.webbiscuit.co.uk/2012/04/02/base64-encoder-and-boost/\r
121                 \r
122         typedef\r
123                 insert_linebreaks<         // insert line breaks every 76 characters\r
124                         base64_from_binary<    // convert binary values to base64 characters\r
125                                 transform_width<   // retrieve 6 bit integers from a sequence of 8 bit bytes\r
126                                         const unsigned char *,\r
127                                         6,\r
128                                         8\r
129                                 >\r
130                         >,\r
131                         76\r
132                 >\r
133         base64_iterator; // compose all the above operations in to a new iterator\r
134         auto length = boost::filesystem::file_size(file);\r
135         std::vector<char> bytes;\r
136         bytes.resize(length);\r
137         filestream.read(bytes.data(), length);\r
138 \r
139         int padding = 0;\r
140 \r
141         while (bytes.size() % 3 != 0)\r
142         {\r
143                 ++padding;\r
144                 bytes.push_back(0x00);\r
145         }\r
146 \r
147         std::string result(base64_iterator(bytes.data()), base64_iterator(bytes.data() + length - padding));\r
148         result.insert(result.end(), padding, '=');\r
149 \r
150         return widen(result);\r
151 }\r
152 \r
153 std::wstring read_utf8_file(const boost::filesystem::wpath& file)\r
154 {\r
155         std::wstringstream result;\r
156         boost::filesystem::wifstream filestream(file);\r
157 \r
158         if (filestream) \r
159         {\r
160                 // Consume BOM first\r
161                 filestream.get();\r
162                 // read all data\r
163                 result << filestream.rdbuf();\r
164         }\r
165 \r
166         return result.str();\r
167 }\r
168 \r
169 std::wstring read_latin1_file(const boost::filesystem::wpath& file)\r
170 {\r
171         boost::locale::generator gen;\r
172         gen.locale_cache_enabled(true);\r
173         gen.categories(boost::locale::codepage_facet);\r
174 \r
175         std::stringstream result_stream;\r
176         boost::filesystem::ifstream filestream(file);\r
177         filestream.imbue(gen("en_US.ISO8859-1"));\r
178 \r
179         if (filestream)\r
180         {\r
181                 // read all data\r
182                 result_stream << filestream.rdbuf();\r
183         }\r
184 \r
185         std::string result = result_stream.str();\r
186         std::wstring widened_result;\r
187 \r
188         // The first 255 codepoints in unicode is the same as in latin1\r
189         auto from_signed_to_signed = std::function<unsigned char(char)>(\r
190                 [] (char c) { return static_cast<unsigned char>(c); }\r
191         );\r
192         boost::copy(\r
193                 result | boost::adaptors::transformed(from_signed_to_signed),\r
194                 std::back_inserter(widened_result));\r
195 \r
196         return widened_result;\r
197 }\r
198 \r
199 std::wstring read_file(const boost::filesystem::wpath& file)\r
200 {\r
201         static const uint8_t BOM[] = {0xef, 0xbb, 0xbf};\r
202 \r
203         if (!boost::filesystem::exists(file))\r
204         {\r
205                 return L"";\r
206         }\r
207 \r
208         if (boost::filesystem::file_size(file) >= 3)\r
209         {\r
210                 boost::filesystem::ifstream bom_stream(file);\r
211 \r
212                 char header[3];\r
213                 bom_stream.read(header, 3);\r
214                 bom_stream.close();\r
215 \r
216                 if (std::memcmp(BOM, header, 3) == 0)\r
217                         return read_utf8_file(file);\r
218         }\r
219 \r
220         return read_latin1_file(file);\r
221 }\r
222 \r
223 std::wstring MediaInfo(const boost::filesystem::wpath& path)\r
224 {\r
225         if(boost::filesystem::is_regular_file(path))\r
226         {\r
227                 std::wstring clipttype = TEXT(" N/A ");\r
228                 std::wstring extension = boost::to_upper_copy(path.extension());\r
229                 if(extension == TEXT(".TGA") || extension == TEXT(".COL") || extension == L".PNG" || extension == L".JPEG" || extension == L".JPG" ||\r
230                         extension == L"GIF" || extension == L"BMP")\r
231                         clipttype = TEXT(" STILL ");\r
232                 else if(extension == TEXT(".WAV") || extension == TEXT(".MP3"))\r
233                         clipttype = TEXT(" AUDIO ");\r
234                 else if(extension == TEXT(".SWF") || extension == TEXT(".CT") || \r
235                             extension == TEXT(".DV") || extension == TEXT(".MOV") || \r
236                                 extension == TEXT(".MPG") || extension == TEXT(".AVI") || \r
237                                 extension == TEXT(".MP4") || extension == TEXT(".FLV") || \r
238                                 extension == TEXT(".STGA") || \r
239                                 caspar::ffmpeg::is_valid_file(path.file_string()))\r
240                         clipttype = TEXT(" MOVIE ");\r
241 \r
242                 if(clipttype != TEXT(" N/A "))\r
243                 {               \r
244                         auto is_not_digit = [](char c){ return std::isdigit(c) == 0; };\r
245 \r
246                         auto relativePath = boost::filesystem::wpath(path.file_string().substr(env::media_folder().size()-1, path.file_string().size()));\r
247 \r
248                         auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(path)));\r
249                         writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end());\r
250                         auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
251 \r
252                         auto sizeStr = boost::lexical_cast<std::wstring>(boost::filesystem::file_size(path));\r
253                         sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end());\r
254                         auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
255                                 \r
256                         auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
257                         if(str[0] == '\\' || str[0] == '/')\r
258                                 str = std::wstring(str.begin() + 1, str.end());\r
259 \r
260                         return std::wstring() + TEXT("\"") + str +\r
261                                         + TEXT("\" ") + clipttype +\r
262                                         + TEXT(" ") + sizeStr +\r
263                                         + TEXT(" ") + writeTimeWStr +\r
264                                         + TEXT("\r\n");         \r
265                 }       \r
266         }\r
267         return L"";\r
268 }\r
269 \r
270 std::wstring ListMedia()\r
271 {       \r
272         std::wstringstream replyString;\r
273         for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr) \r
274                 replyString << MediaInfo(itr->path());\r
275         \r
276         return boost::to_upper_copy(replyString.str());\r
277 }\r
278 \r
279 std::wstring ListTemplates() \r
280 {\r
281         std::wstringstream replyString;\r
282 \r
283         for (boost::filesystem::wrecursive_directory_iterator itr(env::template_folder()), end; itr != end; ++itr)\r
284         {               \r
285                 if(boost::filesystem::is_regular_file(itr->path()) && (itr->path().extension() == L".ft" || itr->path().extension() == L".ct"))\r
286                 {\r
287                         auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::template_folder().size()-1, itr->path().file_string().size()));\r
288 \r
289                         auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(itr->path())));\r
290                         writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), [](char c){ return std::isdigit(c) == 0;}), writeTimeStr.end());\r
291                         auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
292 \r
293                         auto sizeStr = boost::lexical_cast<std::string>(boost::filesystem::file_size(itr->path()));\r
294                         sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), [](char c){ return std::isdigit(c) == 0;}), sizeStr.end());\r
295 \r
296                         auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
297 \r
298                         std::wstring dir = relativePath.parent_path().external_directory_string();\r
299                         std::wstring file = boost::to_upper_copy(relativePath.filename());\r
300                         relativePath = boost::filesystem::wpath(dir + L"/" + file);\r
301                                                 \r
302                         auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
303                         boost::trim_if(str, boost::is_any_of("\\/"));\r
304 \r
305                         replyString << TEXT("\"") << str\r
306                                                 << TEXT("\" ") << sizeWStr\r
307                                                 << TEXT(" ") << writeTimeWStr\r
308                                                 << TEXT("\r\n");                \r
309                 }\r
310         }\r
311         return replyString.str();\r
312 }\r
313 \r
314 namespace amcp {\r
315         \r
316 AMCPCommand::AMCPCommand() : channelIndex_(0), scheduling_(Default), layerIndex_(-1)\r
317 {}\r
318 \r
319 void AMCPCommand::SendReply()\r
320 {\r
321         if(!pClientInfo_) \r
322                 return;\r
323 \r
324         if(replyString_.empty())\r
325                 return;\r
326         pClientInfo_->Send(replyString_);\r
327 }\r
328 \r
329 void AMCPCommand::Clear() \r
330 {\r
331         pChannel_->stage()->clear();\r
332         pClientInfo_.reset();\r
333         channelIndex_ = 0;\r
334         _parameters.clear();\r
335 }\r
336 \r
337 bool DiagnosticsCommand::DoExecute()\r
338 {       \r
339         try\r
340         {\r
341                 diagnostics::show_graphs(true);\r
342 \r
343                 SetReplyString(TEXT("202 DIAG OK\r\n"));\r
344 \r
345                 return true;\r
346         }\r
347         catch(...)\r
348         {\r
349                 CASPAR_LOG_CURRENT_EXCEPTION();\r
350                 SetReplyString(TEXT("502 DIAG FAILED\r\n"));\r
351                 return false;\r
352         }\r
353 }\r
354 \r
355 bool ChannelGridCommand::DoExecute()\r
356 {\r
357         int index = 1;\r
358         auto self = GetChannels().back();\r
359         \r
360         std::vector<std::wstring> params;\r
361         params.push_back(L"SCREEN");\r
362         params.push_back(L"NAME");\r
363         params.push_back(L"Channel Grid Window");\r
364         auto screen = create_consumer(params);\r
365 \r
366         self->output()->add(screen);\r
367 \r
368         BOOST_FOREACH(auto channel, GetChannels())\r
369         {\r
370                 if(channel != self)\r
371                 {\r
372                         auto producer = create_channel_producer(self->mixer(), channel);                \r
373                         self->stage()->load(index, producer, false);\r
374                         self->stage()->play(index);\r
375                         index++;\r
376                 }\r
377         }\r
378 \r
379         int n = GetChannels().size()-1;\r
380         double delta = 1.0/static_cast<double>(n);\r
381         for(int x = 0; x < n; ++x)\r
382         {\r
383                 for(int y = 0; y < n; ++y)\r
384                 {\r
385                         int index = x+y*n+1;\r
386                         auto transform = [=](frame_transform transform) -> frame_transform\r
387                         {               \r
388                                 transform.fill_translation[0]   = x*delta;\r
389                                 transform.fill_translation[1]   = y*delta;\r
390                                 transform.fill_scale[0]                 = delta;\r
391                                 transform.fill_scale[1]                 = delta;\r
392                                 transform.clip_translation[0]   = x*delta;\r
393                                 transform.clip_translation[1]   = y*delta;\r
394                                 transform.clip_scale[0]                 = delta;\r
395                                 transform.clip_scale[1]                 = delta;                        \r
396                                 return transform;\r
397                         };\r
398                         self->stage()->apply_transform(index, transform);\r
399                 }\r
400         }\r
401 \r
402         return true;\r
403 }\r
404 \r
405 bool CallCommand::DoExecute()\r
406 {       \r
407         //Perform loading of the clip\r
408         try\r
409         {\r
410                 auto what = _parameters.at(0);\r
411                                 \r
412                 boost::unique_future<std::wstring> result;\r
413                 if(what == L"B" || what == L"F")\r
414                 {\r
415                         std::wstring param;\r
416                         for(auto it = std::begin(_parameters2)+1; it != std::end(_parameters2); ++it, param += L" ")\r
417                                 param += *it;\r
418                         result = GetChannel()->stage()->call(GetLayerIndex(), what == L"F", boost::trim_copy(param));\r
419                 }\r
420                 else\r
421                 {\r
422                         std::wstring param;\r
423                         for(auto it = std::begin(_parameters2); it != std::end(_parameters2); ++it, param += L" ")\r
424                                 param += *it;\r
425                         result = GetChannel()->stage()->call(GetLayerIndex(), true, boost::trim_copy(param));\r
426                 }\r
427 \r
428                 if(!result.timed_wait(boost::posix_time::seconds(2)))\r
429                         BOOST_THROW_EXCEPTION(timed_out());\r
430                                 \r
431                 std::wstringstream replyString;\r
432                 if(result.get().empty())\r
433                         replyString << TEXT("202 CALL OK\r\n");\r
434                 else\r
435                         replyString << TEXT("201 CALL OK\r\n") << result.get() << L"\r\n";\r
436                 \r
437                 SetReplyString(replyString.str());\r
438 \r
439                 return true;\r
440         }\r
441         catch(...)\r
442         {\r
443                 CASPAR_LOG_CURRENT_EXCEPTION();\r
444                 SetReplyString(TEXT("502 CALL FAILED\r\n"));\r
445                 return false;\r
446         }\r
447 }\r
448 \r
449 // UGLY HACK\r
450 tbb::concurrent_unordered_map<int, std::vector<stage::transform_tuple_t>> deferred_transforms;\r
451 \r
452 bool MixerCommand::DoExecute()\r
453 {       \r
454         //Perform loading of the clip\r
455         try\r
456         {       \r
457                 bool defer = _parameters.back() == L"DEFER";\r
458                 if(defer)\r
459                         _parameters.pop_back();\r
460 \r
461                 std::vector<stage::transform_tuple_t> transforms;\r
462 \r
463                 if(_parameters[0] == L"KEYER" || _parameters[0] == L"IS_KEY")\r
464                 {\r
465                         bool value = boost::lexical_cast<int>(_parameters.at(1));\r
466                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
467                         {\r
468                                 transform.is_key = value;\r
469                                 return transform;                                       \r
470                         }, 0, L"linear"));\r
471                 }\r
472                 else if(_parameters[0] == L"OPACITY")\r
473                 {\r
474                         int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
475                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
476 \r
477                         double value = boost::lexical_cast<double>(_parameters.at(1));\r
478                         \r
479                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
480                         {\r
481                                 transform.opacity = value;\r
482                                 return transform;                                       \r
483                         }, duration, tween));\r
484                 }\r
485                 else if(_parameters[0] == L"FILL" || _parameters[0] == L"FILL_RECT")\r
486                 {\r
487                         int duration = _parameters.size() > 5 ? boost::lexical_cast<int>(_parameters[5]) : 0;\r
488                         std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
489                         double x        = boost::lexical_cast<double>(_parameters.at(1));\r
490                         double y        = boost::lexical_cast<double>(_parameters.at(2));\r
491                         double x_s      = boost::lexical_cast<double>(_parameters.at(3));\r
492                         double y_s      = boost::lexical_cast<double>(_parameters.at(4));\r
493 \r
494                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) mutable -> frame_transform\r
495                         {\r
496                                 transform.fill_translation[0]   = x;\r
497                                 transform.fill_translation[1]   = y;\r
498                                 transform.fill_scale[0]                 = x_s;\r
499                                 transform.fill_scale[1]                 = y_s;\r
500                                 transform.clip_translation[0]   = x;\r
501                                 transform.clip_translation[1]   = y;\r
502                                 transform.clip_scale[0]                 = x_s;\r
503                                 transform.clip_scale[1]                 = y_s;\r
504                                 return transform;\r
505                         }, duration, tween));\r
506                 }\r
507                 else if(_parameters[0] == L"CLIP" || _parameters[0] == L"CLIP_RECT")\r
508                 {\r
509                         int duration = _parameters.size() > 5 ? boost::lexical_cast<int>(_parameters[5]) : 0;\r
510                         std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
511                         double x        = boost::lexical_cast<double>(_parameters.at(1));\r
512                         double y        = boost::lexical_cast<double>(_parameters.at(2));\r
513                         double x_s      = boost::lexical_cast<double>(_parameters.at(3));\r
514                         double y_s      = boost::lexical_cast<double>(_parameters.at(4));\r
515 \r
516                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
517                         {\r
518                                 transform.clip_translation[0]   = x;\r
519                                 transform.clip_translation[1]   = y;\r
520                                 transform.clip_scale[0]                 = x_s;\r
521                                 transform.clip_scale[1]                 = y_s;\r
522                                 return transform;\r
523                         }, duration, tween));\r
524                 }\r
525                 else if(_parameters[0] == L"GRID")\r
526                 {\r
527                         int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
528                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
529                         int n = boost::lexical_cast<int>(_parameters.at(1));\r
530                         double delta = 1.0/static_cast<double>(n);\r
531                         for(int x = 0; x < n; ++x)\r
532                         {\r
533                                 for(int y = 0; y < n; ++y)\r
534                                 {\r
535                                         int index = x+y*n+1;\r
536                                         transforms.push_back(stage::transform_tuple_t(index, [=](frame_transform transform) -> frame_transform\r
537                                         {               \r
538                                                 transform.fill_translation[0]   = x*delta;\r
539                                                 transform.fill_translation[1]   = y*delta;\r
540                                                 transform.fill_scale[0]                 = delta;\r
541                                                 transform.fill_scale[1]                 = delta;\r
542                                                 transform.clip_translation[0]   = x*delta;\r
543                                                 transform.clip_translation[1]   = y*delta;\r
544                                                 transform.clip_scale[0]                 = delta;\r
545                                                 transform.clip_scale[1]                 = delta;                        \r
546                                                 return transform;\r
547                                         }, duration, tween));\r
548                                 }\r
549                         }\r
550                 }\r
551                 else if(_parameters[0] == L"BLEND")\r
552                 {\r
553                         auto blend_str = _parameters.at(1);                                                             \r
554                         int layer = GetLayerIndex();\r
555                         GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str));      \r
556                 }\r
557                 else if(_parameters[0] == L"MASTERVOLUME")\r
558                 {\r
559                         float master_volume = boost::lexical_cast<float>(_parameters.at(1));\r
560                         GetChannel()->mixer()->set_master_volume(master_volume);\r
561                 }\r
562                 else if(_parameters[0] == L"BRIGHTNESS")\r
563                 {\r
564                         auto value = boost::lexical_cast<double>(_parameters.at(1));\r
565                         int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
566                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
567                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
568                         {\r
569                                 transform.brightness = value;\r
570                                 return transform;\r
571                         }, duration, tween));\r
572                 }\r
573                 else if(_parameters[0] == L"SATURATION")\r
574                 {\r
575                         auto value = boost::lexical_cast<double>(_parameters.at(1));\r
576                         int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
577                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
578                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
579                         {\r
580                                 transform.saturation = value;\r
581                                 return transform;\r
582                         }, duration, tween));   \r
583                 }\r
584                 else if(_parameters[0] == L"CONTRAST")\r
585                 {\r
586                         auto value = boost::lexical_cast<double>(_parameters.at(1));\r
587                         int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
588                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
589                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
590                         {\r
591                                 transform.contrast = value;\r
592                                 return transform;\r
593                         }, duration, tween));   \r
594                 }\r
595                 else if(_parameters[0] == L"LEVELS")\r
596                 {\r
597                         levels value;\r
598                         value.min_input  = boost::lexical_cast<double>(_parameters.at(1));\r
599                         value.max_input  = boost::lexical_cast<double>(_parameters.at(2));\r
600                         value.gamma              = boost::lexical_cast<double>(_parameters.at(3));\r
601                         value.min_output = boost::lexical_cast<double>(_parameters.at(4));\r
602                         value.max_output = boost::lexical_cast<double>(_parameters.at(5));\r
603                         int duration = _parameters.size() > 6 ? boost::lexical_cast<int>(_parameters[6]) : 0;\r
604                         std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear";\r
605 \r
606                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
607                         {\r
608                                 transform.levels = value;\r
609                                 return transform;\r
610                         }, duration, tween));\r
611                 }\r
612                 else if(_parameters[0] == L"VOLUME")\r
613                 {\r
614                         int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
615                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
616                         double value = boost::lexical_cast<double>(_parameters[1]);\r
617 \r
618                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
619                         {\r
620                                 transform.volume = value;\r
621                                 return transform;\r
622                         }, duration, tween));\r
623                 }\r
624                 else if(_parameters[0] == L"CLEAR")\r
625                 {\r
626                         int layer = GetLayerIndex(std::numeric_limits<int>::max());\r
627                         if (layer == std::numeric_limits<int>::max())\r
628                         {\r
629                                 GetChannel()->stage()->clear_transforms();\r
630                                 GetChannel()->mixer()->clear_blend_modes();\r
631                         }\r
632                         else\r
633                         {\r
634                                 GetChannel()->stage()->clear_transforms(layer);\r
635                                 GetChannel()->mixer()->clear_blend_mode(layer);\r
636                         }\r
637                 }\r
638                 else if(_parameters[0] == L"COMMIT")\r
639                 {\r
640                         transforms = std::move(deferred_transforms[GetChannelIndex()]);\r
641                 }\r
642                 else\r
643                 {\r
644                         SetReplyString(TEXT("404 MIXER ERROR\r\n"));\r
645                         return false;\r
646                 }\r
647 \r
648                 if(defer)\r
649                 {\r
650                         auto& defer_tranforms = deferred_transforms[GetChannelIndex()];\r
651                         defer_tranforms.insert(defer_tranforms.end(), transforms.begin(), transforms.end());\r
652                 }\r
653                 else\r
654                         GetChannel()->stage()->apply_transforms(transforms);\r
655         \r
656                 SetReplyString(TEXT("202 MIXER OK\r\n"));\r
657 \r
658                 return true;\r
659         }\r
660         catch(file_not_found&)\r
661         {\r
662                 CASPAR_LOG_CURRENT_EXCEPTION();\r
663                 SetReplyString(TEXT("404 MIXER ERROR\r\n"));\r
664                 return false;\r
665         }\r
666         catch(...)\r
667         {\r
668                 CASPAR_LOG_CURRENT_EXCEPTION();\r
669                 SetReplyString(TEXT("502 MIXER FAILED\r\n"));\r
670                 return false;\r
671         }\r
672 }\r
673 \r
674 bool SwapCommand::DoExecute()\r
675 {       \r
676         //Perform loading of the clip\r
677         try\r
678         {\r
679                 if(GetLayerIndex(-1) != -1)\r
680                 {\r
681                         std::vector<std::string> strs;\r
682                         boost::split(strs, _parameters[0], boost::is_any_of("-"));\r
683                         \r
684                         auto ch1 = GetChannel();\r
685                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);\r
686 \r
687                         int l1 = GetLayerIndex();\r
688                         int l2 = boost::lexical_cast<int>(strs.at(1));\r
689 \r
690                         ch1->stage()->swap_layer(l1, l2, ch2->stage());\r
691                 }\r
692                 else\r
693                 {\r
694                         auto ch1 = GetChannel();\r
695                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(_parameters[0])-1);\r
696                         ch1->stage()->swap_layers(ch2->stage());\r
697                 }\r
698                 \r
699                 SetReplyString(TEXT("202 SWAP OK\r\n"));\r
700 \r
701                 return true;\r
702         }\r
703         catch(file_not_found&)\r
704         {\r
705                 CASPAR_LOG_CURRENT_EXCEPTION();\r
706                 SetReplyString(TEXT("404 SWAP ERROR\r\n"));\r
707                 return false;\r
708         }\r
709         catch(...)\r
710         {\r
711                 CASPAR_LOG_CURRENT_EXCEPTION();\r
712                 SetReplyString(TEXT("502 SWAP FAILED\r\n"));\r
713                 return false;\r
714         }\r
715 }\r
716 \r
717 bool AddCommand::DoExecute()\r
718 {       \r
719         //Perform loading of the clip\r
720         try\r
721         {\r
722                 auto consumer = create_consumer(_parameters);\r
723                 GetChannel()->output()->add(GetLayerIndex(consumer->index()), consumer);\r
724         \r
725                 SetReplyString(TEXT("202 ADD OK\r\n"));\r
726 \r
727                 return true;\r
728         }\r
729         catch(file_not_found&)\r
730         {\r
731                 CASPAR_LOG_CURRENT_EXCEPTION();\r
732                 SetReplyString(TEXT("404 ADD ERROR\r\n"));\r
733                 return false;\r
734         }\r
735         catch(...)\r
736         {\r
737                 CASPAR_LOG_CURRENT_EXCEPTION();\r
738                 SetReplyString(TEXT("502 ADD FAILED\r\n"));\r
739                 return false;\r
740         }\r
741 }\r
742 \r
743 bool RemoveCommand::DoExecute()\r
744 {       \r
745         //Perform loading of the clip\r
746         try\r
747         {\r
748                 auto index = GetLayerIndex(std::numeric_limits<int>::min());\r
749                 if(index == std::numeric_limits<int>::min())\r
750                         index = create_consumer(_parameters)->index();\r
751 \r
752                 GetChannel()->output()->remove(index);\r
753 \r
754                 SetReplyString(TEXT("202 REMOVE OK\r\n"));\r
755 \r
756                 return true;\r
757         }\r
758         catch(file_not_found&)\r
759         {\r
760                 CASPAR_LOG_CURRENT_EXCEPTION();\r
761                 SetReplyString(TEXT("404 REMOVE ERROR\r\n"));\r
762                 return false;\r
763         }\r
764         catch(...)\r
765         {\r
766                 CASPAR_LOG_CURRENT_EXCEPTION();\r
767                 SetReplyString(TEXT("502 REMOVE FAILED\r\n"));\r
768                 return false;\r
769         }\r
770 }\r
771 \r
772 bool LoadCommand::DoExecute()\r
773 {       \r
774         //Perform loading of the clip\r
775         try\r
776         {\r
777                 _parameters[0] = _parameters[0];\r
778                 auto pFP = create_producer(GetChannel()->mixer(), _parameters);         \r
779                 GetChannel()->stage()->load(GetLayerIndex(), pFP, true);\r
780         \r
781                 SetReplyString(TEXT("202 LOAD OK\r\n"));\r
782 \r
783                 return true;\r
784         }\r
785         catch(file_not_found&)\r
786         {\r
787                 CASPAR_LOG_CURRENT_EXCEPTION();\r
788                 SetReplyString(TEXT("404 LOAD ERROR\r\n"));\r
789                 return false;\r
790         }\r
791         catch(...)\r
792         {\r
793                 CASPAR_LOG_CURRENT_EXCEPTION();\r
794                 SetReplyString(TEXT("502 LOAD FAILED\r\n"));\r
795                 return false;\r
796         }\r
797 }\r
798 \r
799 \r
800 \r
801 //std::function<std::wstring()> channel_cg_add_command::parse(const std::wstring& message, const std::vector<renderer::render_device_ptr>& channels)\r
802 //{\r
803 //      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
804 //\r
805 //      boost::wsmatch what;\r
806 //      if(!boost::regex_match(message, what, expr))\r
807 //              return nullptr;\r
808 //\r
809 //      auto info = channel_info::parse(what, channels);\r
810 //\r
811 //      int flash_layer_index = boost::lexical_cast<int>(what["FLASH_LAYER"].str());\r
812 //\r
813 //      std::wstring templatename = what["TEMPLATE"].str();\r
814 //      bool play_on_load = what["PLAY_ON_LOAD"].matched ? what["PLAY_ON_LOAD"].str() != L"0" : 0;\r
815 //      std::wstring start_label = what["START_LABEL"].str();   \r
816 //      std::wstring data = get_data(what["DATA"].str());\r
817 //      \r
818 //      boost::replace_all(templatename, "\"", "");\r
819 //\r
820 //      return [=]() -> std::wstring\r
821 //      {       \r
822 //              std::wstring fullFilename = flash::flash_producer::find_template(server::template_folder() + templatename);\r
823 //              if(fullFilename.empty())\r
824 //                      BOOST_THROW_EXCEPTION(file_not_found());\r
825 //      \r
826 //              std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
827 //              std::wstring filename = templatename;\r
828 //              filename.append(extension);\r
829 //\r
830 //              flash::flash::get_default_cg_producer(info.video_channel, std::max<int>(DEFAULT_CHANNEL_LAYER+1, info.layer_index))\r
831 //                      ->add(flash_layer_index, filename, play_on_load, start_label, data);\r
832 //\r
833 //              CASPAR_LOG(info) << L"Executed [amcp_channel_cg_add]";\r
834 //              return L"";\r
835 //      };\r
836 \r
837 bool LoadbgCommand::DoExecute()\r
838 {\r
839         transition_info transitionInfo;\r
840         \r
841         bool bLoop = false;\r
842 \r
843         // TRANSITION\r
844 \r
845         std::wstring message;\r
846         for(size_t n = 0; n < _parameters.size(); ++n)\r
847                 message += _parameters[n] + L" ";\r
848                 \r
849         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
850         boost::wsmatch what;\r
851         if(boost::regex_match(message, what, expr))\r
852         {\r
853                 auto transition = what["TRANSITION"].str();\r
854                 transitionInfo.duration = lexical_cast_or_default<size_t>(what["DURATION"].str());\r
855                 auto direction = what["DIRECTION"].matched ? what["DIRECTION"].str() : L"";\r
856                 auto tween = what["TWEEN"].matched ? what["TWEEN"].str() : L"";\r
857                 transitionInfo.tweener = get_tweener(tween);            \r
858 \r
859                 if(transition == TEXT("CUT"))\r
860                         transitionInfo.type = transition::cut;\r
861                 else if(transition == TEXT("MIX"))\r
862                         transitionInfo.type = transition::mix;\r
863                 else if(transition == TEXT("PUSH"))\r
864                         transitionInfo.type = transition::push;\r
865                 else if(transition == TEXT("SLIDE"))\r
866                         transitionInfo.type = transition::slide;\r
867                 else if(transition == TEXT("WIPE"))\r
868                         transitionInfo.type = transition::wipe;\r
869                 \r
870                 if(direction == TEXT("FROMLEFT"))\r
871                         transitionInfo.direction = transition_direction::from_left;\r
872                 else if(direction == TEXT("FROMRIGHT"))\r
873                         transitionInfo.direction = transition_direction::from_right;\r
874                 else if(direction == TEXT("LEFT"))\r
875                         transitionInfo.direction = transition_direction::from_right;\r
876                 else if(direction == TEXT("RIGHT"))\r
877                         transitionInfo.direction = transition_direction::from_left;\r
878         }\r
879         \r
880         //Perform loading of the clip\r
881         try\r
882         {\r
883                 _parameters[0] = _parameters[0];\r
884                 auto pFP = create_producer(GetChannel()->mixer(), _parameters);\r
885                 if(pFP == frame_producer::empty())\r
886                         BOOST_THROW_EXCEPTION(file_not_found() << msg_info(_parameters.size() > 0 ? narrow(_parameters[0]) : ""));\r
887 \r
888                 bool auto_play = std::find(_parameters.begin(), _parameters.end(), L"AUTO") != _parameters.end();\r
889 \r
890                 auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().field_mode, pFP, transitionInfo);\r
891                 GetChannel()->stage()->load(GetLayerIndex(), pFP2, false, auto_play ? transitionInfo.duration : -1); // TODO: LOOP\r
892         \r
893                 SetReplyString(TEXT("202 LOADBG OK\r\n"));\r
894 \r
895                 return true;\r
896         }\r
897         catch(file_not_found&)\r
898         {               \r
899                 std::wstring params2;\r
900                 for(auto it = _parameters.begin(); it != _parameters.end(); ++it)\r
901                         params2 += L" " + *it;\r
902                 CASPAR_LOG(error) << L"File not found. No match found for parameters. Check syntax:" << params2;\r
903                 SetReplyString(TEXT("404 LOADBG ERROR\r\n"));\r
904                 return false;\r
905         }\r
906         catch(...)\r
907         {\r
908                 CASPAR_LOG_CURRENT_EXCEPTION();\r
909                 SetReplyString(TEXT("502 LOADBG FAILED\r\n"));\r
910                 return false;\r
911         }\r
912 }\r
913 \r
914 bool PauseCommand::DoExecute()\r
915 {\r
916         try\r
917         {\r
918                 GetChannel()->stage()->pause(GetLayerIndex());\r
919                 SetReplyString(TEXT("202 PAUSE OK\r\n"));\r
920                 return true;\r
921         }\r
922         catch(...)\r
923         {\r
924                 SetReplyString(TEXT("501 PAUSE FAILED\r\n"));\r
925         }\r
926 \r
927         return false;\r
928 }\r
929 \r
930 bool PlayCommand::DoExecute()\r
931 {\r
932         try\r
933         {\r
934                 if(!_parameters.empty())\r
935                 {\r
936                         LoadbgCommand lbg;\r
937                         lbg.SetChannel(GetChannel());\r
938                         lbg.SetChannelIndex(GetChannelIndex());\r
939                         lbg.SetLayerIntex(GetLayerIndex());\r
940                         lbg.SetClientInfo(GetClientInfo());\r
941                         for(auto it = _parameters.begin(); it != _parameters.end(); ++it)\r
942                                 lbg.AddParameter(*it);\r
943                         if(!lbg.Execute())\r
944                                 throw std::exception();\r
945                 }\r
946 \r
947                 GetChannel()->stage()->play(GetLayerIndex());\r
948                 \r
949                 SetReplyString(TEXT("202 PLAY OK\r\n"));\r
950                 return true;\r
951         }\r
952         catch(...)\r
953         {\r
954                 SetReplyString(TEXT("501 PLAY FAILED\r\n"));\r
955         }\r
956 \r
957         return false;\r
958 }\r
959 \r
960 bool StopCommand::DoExecute()\r
961 {\r
962         try\r
963         {\r
964                 GetChannel()->stage()->stop(GetLayerIndex());\r
965                 SetReplyString(TEXT("202 STOP OK\r\n"));\r
966                 return true;\r
967         }\r
968         catch(...)\r
969         {\r
970                 SetReplyString(TEXT("501 STOP FAILED\r\n"));\r
971         }\r
972 \r
973         return false;\r
974 }\r
975 \r
976 bool ClearCommand::DoExecute()\r
977 {\r
978         int index = GetLayerIndex(std::numeric_limits<int>::min());\r
979         if(index != std::numeric_limits<int>::min())\r
980                 GetChannel()->stage()->clear(index);\r
981         else\r
982                 GetChannel()->stage()->clear();\r
983                 \r
984         SetReplyString(TEXT("202 CLEAR OK\r\n"));\r
985 \r
986         return true;\r
987 }\r
988 \r
989 bool PrintCommand::DoExecute()\r
990 {\r
991         GetChannel()->output()->add(create_consumer(boost::assign::list_of(L"IMAGE")));\r
992                 \r
993         SetReplyString(TEXT("202 PRINT OK\r\n"));\r
994 \r
995         return true;\r
996 }\r
997 \r
998 bool LogCommand::DoExecute()\r
999 {\r
1000         if(_parameters.at(0) == L"LEVEL")\r
1001                 log::set_log_level(_parameters.at(1));\r
1002 \r
1003         SetReplyString(TEXT("202 LOG OK\r\n"));\r
1004 \r
1005         return true;\r
1006 }\r
1007 \r
1008 bool CGCommand::DoExecute()\r
1009 {\r
1010         try\r
1011         {\r
1012                 std::wstring command = _parameters[0];\r
1013                 if(command == TEXT("ADD"))\r
1014                         return DoExecuteAdd();\r
1015                 else if(command == TEXT("PLAY"))\r
1016                         return DoExecutePlay();\r
1017                 else if(command == TEXT("STOP"))\r
1018                         return DoExecuteStop();\r
1019                 else if(command == TEXT("NEXT"))\r
1020                         return DoExecuteNext();\r
1021                 else if(command == TEXT("REMOVE"))\r
1022                         return DoExecuteRemove();\r
1023                 else if(command == TEXT("CLEAR"))\r
1024                         return DoExecuteClear();\r
1025                 else if(command == TEXT("UPDATE"))\r
1026                         return DoExecuteUpdate();\r
1027                 else if(command == TEXT("INVOKE"))\r
1028                         return DoExecuteInvoke();\r
1029                 else if(command == TEXT("INFO"))\r
1030                         return DoExecuteInfo();\r
1031         }\r
1032         catch(...)\r
1033         {\r
1034                 CASPAR_LOG_CURRENT_EXCEPTION();\r
1035         }\r
1036 \r
1037         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1038         return false;\r
1039 }\r
1040 \r
1041 bool CGCommand::ValidateLayer(const std::wstring& layerstring) {\r
1042         int length = layerstring.length();\r
1043         for(int i = 0; i < length; ++i) {\r
1044                 if(!_istdigit(layerstring[i])) {\r
1045                         return false;\r
1046                 }\r
1047         }\r
1048 \r
1049         return true;\r
1050 }\r
1051 \r
1052 bool CGCommand::DoExecuteAdd() {\r
1053         //CG 1 ADD 0 "template_folder/templatename" [STARTLABEL] 0/1 [DATA]\r
1054 \r
1055         int layer = 0;                          //_parameters[1]\r
1056 //      std::wstring templateName;      //_parameters[2]\r
1057         std::wstring label;             //_parameters[3]\r
1058         bool bDoStart = false;          //_parameters[3] alt. _parameters[4]\r
1059 //      std::wstring data;                      //_parameters[4] alt. _parameters[5]\r
1060 \r
1061         if(_parameters.size() < 4) \r
1062         {\r
1063                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1064                 return false;\r
1065         }\r
1066         unsigned int dataIndex = 4;\r
1067 \r
1068         if(!ValidateLayer(_parameters[1])) \r
1069         {\r
1070                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1071                 return false;\r
1072         }\r
1073 \r
1074         layer = _ttoi(_parameters[1].c_str());\r
1075 \r
1076         if(_parameters[3].length() > 1) \r
1077         {       //read label\r
1078                 label = _parameters2[3];\r
1079                 ++dataIndex;\r
1080 \r
1081                 if(_parameters.size() > 4 && _parameters[4].length() > 0)       //read play-on-load-flag\r
1082                         bDoStart = (_parameters[4][0]==TEXT('1')) ? true : false;\r
1083                 else \r
1084                 {\r
1085                         SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1086                         return false;\r
1087                 }\r
1088         }\r
1089         else if(_parameters[3].length() > 0) {  //read play-on-load-flag\r
1090                 bDoStart = (_parameters[3][0]==TEXT('1')) ? true : false;\r
1091         }\r
1092         else \r
1093         {\r
1094                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1095                 return false;\r
1096         }\r
1097 \r
1098         const TCHAR* pDataString = 0;\r
1099         std::wstringstream data;\r
1100         std::wstring dataFromFile;\r
1101         if(_parameters.size() > dataIndex) \r
1102         {       //read data\r
1103                 const std::wstring& dataString = _parameters2[dataIndex];\r
1104 \r
1105                 if(dataString[0] == TEXT('<')) //the data is an XML-string\r
1106                         pDataString = dataString.c_str();\r
1107                 else \r
1108                 {\r
1109                         //The data is not an XML-string, it must be a filename\r
1110                         std::wstring filename = env::data_folder();\r
1111                         filename.append(dataString);\r
1112                         filename.append(TEXT(".ftd"));\r
1113 \r
1114                         dataFromFile = read_file(boost::filesystem::wpath(filename));\r
1115                         pDataString = dataFromFile.c_str();\r
1116                 }\r
1117         }\r
1118 \r
1119         std::wstring fullFilename = flash::find_template(env::template_folder() + _parameters[2]);\r
1120         if(!fullFilename.empty())\r
1121         {\r
1122                 std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
1123                 std::wstring filename = _parameters[2];\r
1124                 filename.append(extension);\r
1125 \r
1126                 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
1127                 SetReplyString(TEXT("202 CG OK\r\n"));\r
1128         }\r
1129         else\r
1130         {\r
1131                 CASPAR_LOG(warning) << "Could not find template " << _parameters[2];\r
1132                 SetReplyString(TEXT("404 CG ERROR\r\n"));\r
1133         }\r
1134         return true;\r
1135 }\r
1136 \r
1137 bool CGCommand::DoExecutePlay()\r
1138 {\r
1139         if(_parameters.size() > 1)\r
1140         {\r
1141                 if(!ValidateLayer(_parameters[1])) \r
1142                 {\r
1143                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1144                         return false;\r
1145                 }\r
1146                 int layer = _ttoi(_parameters[1].c_str());\r
1147                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->play(layer);\r
1148         }\r
1149         else\r
1150         {\r
1151                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1152                 return true;\r
1153         }\r
1154 \r
1155         SetReplyString(TEXT("202 CG OK\r\n"));\r
1156         return true;\r
1157 }\r
1158 \r
1159 bool CGCommand::DoExecuteStop() \r
1160 {\r
1161         if(_parameters.size() > 1)\r
1162         {\r
1163                 if(!ValidateLayer(_parameters[1])) \r
1164                 {\r
1165                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1166                         return false;\r
1167                 }\r
1168                 int layer = _ttoi(_parameters[1].c_str());\r
1169                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->stop(layer, 0);\r
1170         }\r
1171         else \r
1172         {\r
1173                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1174                 return true;\r
1175         }\r
1176 \r
1177         SetReplyString(TEXT("202 CG OK\r\n"));\r
1178         return true;\r
1179 }\r
1180 \r
1181 bool CGCommand::DoExecuteNext()\r
1182 {\r
1183         if(_parameters.size() > 1) \r
1184         {\r
1185                 if(!ValidateLayer(_parameters[1])) \r
1186                 {\r
1187                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1188                         return false;\r
1189                 }\r
1190 \r
1191                 int layer = _ttoi(_parameters[1].c_str());\r
1192                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->next(layer);\r
1193         }\r
1194         else \r
1195         {\r
1196                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1197                 return true;\r
1198         }\r
1199 \r
1200         SetReplyString(TEXT("202 CG OK\r\n"));\r
1201         return true;\r
1202 }\r
1203 \r
1204 bool CGCommand::DoExecuteRemove() \r
1205 {\r
1206         if(_parameters.size() > 1) \r
1207         {\r
1208                 if(!ValidateLayer(_parameters[1])) \r
1209                 {\r
1210                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1211                         return false;\r
1212                 }\r
1213 \r
1214                 int layer = _ttoi(_parameters[1].c_str());\r
1215                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->remove(layer);\r
1216         }\r
1217         else \r
1218         {\r
1219                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1220                 return true;\r
1221         }\r
1222 \r
1223         SetReplyString(TEXT("202 CG OK\r\n"));\r
1224         return true;\r
1225 }\r
1226 \r
1227 bool CGCommand::DoExecuteClear() \r
1228 {\r
1229         GetChannel()->stage()->clear(GetLayerIndex(flash::cg_producer::DEFAULT_LAYER));\r
1230         SetReplyString(TEXT("202 CG OK\r\n"));\r
1231         return true;\r
1232 }\r
1233 \r
1234 bool CGCommand::DoExecuteUpdate() \r
1235 {\r
1236         try\r
1237         {\r
1238                 if(!ValidateLayer(_parameters.at(1)))\r
1239                 {\r
1240                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1241                         return false;\r
1242                 }\r
1243                                                 \r
1244                 std::wstring dataString = _parameters2.at(2);                           \r
1245                 if(dataString.at(0) != TEXT('<'))\r
1246                 {\r
1247                         //The data is not an XML-string, it must be a filename\r
1248                         std::wstring filename = env::data_folder();\r
1249                         filename.append(dataString);\r
1250                         filename.append(TEXT(".ftd"));\r
1251 \r
1252                         dataString = read_file(boost::filesystem::wpath(filename));\r
1253                 }               \r
1254 \r
1255                 int layer = _ttoi(_parameters.at(1).c_str());\r
1256                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->update(layer, dataString);\r
1257         }\r
1258         catch(...)\r
1259         {\r
1260                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1261                 return true;\r
1262         }\r
1263 \r
1264         SetReplyString(TEXT("202 CG OK\r\n"));\r
1265         return true;\r
1266 }\r
1267 \r
1268 bool CGCommand::DoExecuteInvoke() \r
1269 {\r
1270         std::wstringstream replyString;\r
1271         replyString << TEXT("201 CG OK\r\n");\r
1272 \r
1273         if(_parameters.size() > 2)\r
1274         {\r
1275                 if(!ValidateLayer(_parameters[1]))\r
1276                 {\r
1277                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1278                         return false;\r
1279                 }\r
1280                 int layer = _ttoi(_parameters[1].c_str());\r
1281                 auto result = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->invoke(layer, _parameters2[2]);\r
1282                 replyString << result << TEXT("\r\n"); \r
1283         }\r
1284         else \r
1285         {\r
1286                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1287                 return true;\r
1288         }\r
1289         \r
1290         SetReplyString(replyString.str());\r
1291         return true;\r
1292 }\r
1293 \r
1294 bool CGCommand::DoExecuteInfo() \r
1295 {\r
1296         std::wstringstream replyString;\r
1297         replyString << TEXT("201 CG OK\r\n");\r
1298 \r
1299         if(_parameters.size() > 1)\r
1300         {\r
1301                 if(!ValidateLayer(_parameters[1]))\r
1302                 {\r
1303                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1304                         return false;\r
1305                 }\r
1306 \r
1307                 int layer = _ttoi(_parameters[1].c_str());\r
1308                 auto desc = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->description(layer);\r
1309                 \r
1310                 replyString << desc << TEXT("\r\n"); \r
1311         }\r
1312         else \r
1313         {\r
1314                 auto info = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->template_host_info();\r
1315                 replyString << info << TEXT("\r\n"); \r
1316         }       \r
1317 \r
1318         SetReplyString(replyString.str());\r
1319         return true;\r
1320 }\r
1321 \r
1322 bool DataCommand::DoExecute()\r
1323 {\r
1324         std::wstring command = _parameters[0];\r
1325         if(command == TEXT("STORE"))\r
1326                 return DoExecuteStore();\r
1327         else if(command == TEXT("RETRIEVE"))\r
1328                 return DoExecuteRetrieve();\r
1329         else if(command == TEXT("REMOVE"))\r
1330                 return DoExecuteRemove();\r
1331         else if(command == TEXT("LIST"))\r
1332                 return DoExecuteList();\r
1333 \r
1334         SetReplyString(TEXT("403 DATA ERROR\r\n"));\r
1335         return false;\r
1336 }\r
1337 \r
1338 bool DataCommand::DoExecuteStore() \r
1339 {\r
1340         if(_parameters.size() < 3) \r
1341         {\r
1342                 SetReplyString(TEXT("402 DATA STORE ERROR\r\n"));\r
1343                 return false;\r
1344         }\r
1345 \r
1346         std::wstring filename = env::data_folder();\r
1347         filename.append(_parameters[1]);\r
1348         filename.append(TEXT(".ftd"));\r
1349 \r
1350         auto data_path = boost::filesystem::wpath(\r
1351                         boost::filesystem::wpath(filename).parent_path());\r
1352         \r
1353         if(!boost::filesystem::exists(data_path))\r
1354                 boost::filesystem::create_directories(data_path);\r
1355 \r
1356         std::wofstream datafile(filename.c_str());\r
1357 \r
1358         if(!datafile) \r
1359         {\r
1360                 SetReplyString(TEXT("501 DATA STORE FAILED\r\n"));\r
1361                 return false;\r
1362         }\r
1363 \r
1364         datafile << static_cast<wchar_t>(65279); // UTF-8 BOM character\r
1365         datafile << _parameters2[2] << std::flush;\r
1366         datafile.close();\r
1367 \r
1368         std::wstring replyString = TEXT("202 DATA STORE OK\r\n");\r
1369         SetReplyString(replyString);\r
1370         return true;\r
1371 }\r
1372 \r
1373 bool DataCommand::DoExecuteRetrieve() \r
1374 {\r
1375         if(_parameters.size() < 2) \r
1376         {\r
1377                 SetReplyString(TEXT("402 DATA RETRIEVE ERROR\r\n"));\r
1378                 return false;\r
1379         }\r
1380 \r
1381         std::wstring filename = env::data_folder();\r
1382         filename.append(_parameters[1]);\r
1383         filename.append(TEXT(".ftd"));\r
1384 \r
1385         std::wstring file_contents = read_file(boost::filesystem::wpath(filename));\r
1386 \r
1387         if (file_contents.empty()) \r
1388         {\r
1389                 SetReplyString(TEXT("404 DATA RETRIEVE ERROR\r\n"));\r
1390                 return false;\r
1391         }\r
1392 \r
1393         std::wstringstream reply;\r
1394         reply << TEXT("201 DATA RETRIEVE OK\r\n");\r
1395 \r
1396         std::wstringstream file_contents_stream(file_contents);\r
1397         std::wstring line;\r
1398         bool bFirstLine = true;\r
1399         while(std::getline(file_contents_stream, line))\r
1400         {\r
1401                 if(!bFirstLine)\r
1402                         reply << "\\n";\r
1403                 else\r
1404                         bFirstLine = false;\r
1405 \r
1406                 reply << line;\r
1407         }\r
1408 \r
1409         reply << "\r\n";\r
1410         SetReplyString(reply.str());\r
1411         return true;\r
1412 }\r
1413 \r
1414 bool DataCommand::DoExecuteRemove() \r
1415\r
1416         if (_parameters.size() < 2) \r
1417         {\r
1418                 SetReplyString(TEXT("402 DATA REMOVE ERROR\r\n"));\r
1419                 return false;\r
1420         }\r
1421 \r
1422         std::wstring filename = env::data_folder();\r
1423         filename.append(_parameters[1]);\r
1424         filename.append(TEXT(".ftd"));\r
1425 \r
1426         if (!boost::filesystem::exists(filename)) \r
1427         {\r
1428                 SetReplyString(TEXT("404 DATA REMOVE ERROR\r\n"));\r
1429                 return false;\r
1430         }\r
1431 \r
1432         if (!boost::filesystem::remove(filename))\r
1433         {\r
1434                 SetReplyString(TEXT("403 DATA REMOVE ERROR\r\n"));\r
1435                 return false;\r
1436         }\r
1437 \r
1438         SetReplyString(TEXT("201 DATA REMOVE OK\r\n"));\r
1439 \r
1440         return true;\r
1441 }\r
1442 \r
1443 bool DataCommand::DoExecuteList() \r
1444 {\r
1445         std::wstringstream replyString;\r
1446         replyString << TEXT("200 DATA LIST OK\r\n");\r
1447 \r
1448         for (boost::filesystem::wrecursive_directory_iterator itr(env::data_folder()), end; itr != end; ++itr)\r
1449         {                       \r
1450                 if(boost::filesystem::is_regular_file(itr->path()))\r
1451                 {\r
1452                         if(!boost::iequals(itr->path().extension(), L".ftd"))\r
1453                                 continue;\r
1454                         \r
1455                         auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::data_folder().size()-1, itr->path().file_string().size()));\r
1456                         \r
1457                         auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
1458                         if(str[0] == '\\' || str[0] == '/')\r
1459                                 str = std::wstring(str.begin() + 1, str.end());\r
1460 \r
1461                         replyString << str << TEXT("\r\n");     \r
1462                 }\r
1463         }\r
1464         \r
1465         replyString << TEXT("\r\n");\r
1466 \r
1467         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1468         return true;\r
1469 }\r
1470 \r
1471 bool ThumbnailCommand::DoExecute()\r
1472 {\r
1473         std::wstring command = _parameters[0];\r
1474 \r
1475         if (command == TEXT("RETRIEVE"))\r
1476                 return DoExecuteRetrieve();\r
1477         else if (command == TEXT("LIST"))\r
1478                 return DoExecuteList();\r
1479         else if (command == TEXT("GENERATE"))\r
1480                 return DoExecuteGenerate();\r
1481         else if (command == TEXT("GENERATE_ALL"))\r
1482                 return DoExecuteGenerateAll();\r
1483 \r
1484         SetReplyString(TEXT("403 THUMBNAIL ERROR\r\n"));\r
1485         return false;\r
1486 }\r
1487 \r
1488 bool ThumbnailCommand::DoExecuteRetrieve() \r
1489 {\r
1490         if(_parameters.size() < 2) \r
1491         {\r
1492                 SetReplyString(TEXT("402 THUMBNAIL RETRIEVE ERROR\r\n"));\r
1493                 return false;\r
1494         }\r
1495 \r
1496         std::wstring filename = env::thumbnails_folder();\r
1497         filename.append(_parameters[1]);\r
1498         filename.append(TEXT(".png"));\r
1499 \r
1500         std::wstring file_contents = read_file_base64(boost::filesystem::wpath(filename));\r
1501 \r
1502         if (file_contents.empty())\r
1503         {\r
1504                 SetReplyString(TEXT("404 THUMBNAIL RETRIEVE ERROR\r\n"));\r
1505                 return false;\r
1506         }\r
1507 \r
1508         std::wstringstream reply;\r
1509 \r
1510         reply << L"201 THUMBNAIL RETRIEVE OK\r\n";\r
1511         reply << file_contents;\r
1512         reply << L"\r\n";\r
1513         SetReplyString(reply.str());\r
1514         return true;\r
1515 }\r
1516 \r
1517 bool ThumbnailCommand::DoExecuteList()\r
1518 {\r
1519         std::wstringstream replyString;\r
1520         replyString << TEXT("200 THUMBNAIL LIST OK\r\n");\r
1521 \r
1522         for (boost::filesystem::wrecursive_directory_iterator itr(env::thumbnails_folder()), end; itr != end; ++itr)\r
1523         {                       \r
1524                 if(boost::filesystem::is_regular_file(itr->path()))\r
1525                 {\r
1526                         if(!boost::iequals(itr->path().extension(), L".png"))\r
1527                                 continue;\r
1528                         \r
1529                         auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::thumbnails_folder().size()-1, itr->path().file_string().size()));\r
1530                         \r
1531                         auto str = relativePath.replace_extension(L"").external_file_string();\r
1532                         if(str[0] == '\\' || str[0] == '/')\r
1533                                 str = std::wstring(str.begin() + 1, str.end());\r
1534 \r
1535                         auto mtime = boost::filesystem::last_write_time(itr->path());\r
1536                         auto mtime_readable = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(mtime));\r
1537                         auto file_size = boost::filesystem::file_size(itr->path());\r
1538 \r
1539                         replyString << L"\"" << str << L"\" " << widen(mtime_readable) << L" " << file_size << L"\r\n";\r
1540                 }\r
1541         }\r
1542         \r
1543         replyString << TEXT("\r\n");\r
1544 \r
1545         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1546         return true;\r
1547 }\r
1548 \r
1549 bool ThumbnailCommand::DoExecuteGenerate()\r
1550 {\r
1551         if (_parameters.size() < 2) \r
1552         {\r
1553                 SetReplyString(L"402 THUMBNAIL GENERATE ERROR\r\n");\r
1554                 return false;\r
1555         }\r
1556 \r
1557         auto thumb_gen = GetThumbGenerator();\r
1558 \r
1559         if (thumb_gen)\r
1560         {\r
1561                 thumb_gen->generate(_parameters[1]);\r
1562                 SetReplyString(L"200 THUMBNAIL GENERATE OK\r\n");\r
1563                 return true;\r
1564         }\r
1565         else\r
1566         {\r
1567                 SetReplyString(L"500 THUMBNAIL GENERATE ERROR\r\n");\r
1568                 return false;\r
1569         }\r
1570 }\r
1571 \r
1572 bool ThumbnailCommand::DoExecuteGenerateAll()\r
1573 {\r
1574         auto thumb_gen = GetThumbGenerator();\r
1575 \r
1576         if (thumb_gen)\r
1577         {\r
1578                 thumb_gen->generate_all();\r
1579                 SetReplyString(L"200 THUMBNAIL GENERATE_ALL OK\r\n");\r
1580                 return true;\r
1581         }\r
1582         else\r
1583         {\r
1584                 SetReplyString(L"500 THUMBNAIL GENERATE_ALL ERROR\r\n");\r
1585                 return false;\r
1586         }\r
1587 }\r
1588 \r
1589 bool CinfCommand::DoExecute()\r
1590 {\r
1591         std::wstringstream replyString;\r
1592         \r
1593         try\r
1594         {\r
1595                 std::wstring info;\r
1596                 for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)\r
1597                 {\r
1598                         auto path = itr->path();\r
1599                         auto file = path.replace_extension(L"").filename();\r
1600                         if(boost::iequals(file, _parameters.at(0)))\r
1601                                 info += MediaInfo(itr->path()) + L"\r\n";\r
1602                 }\r
1603 \r
1604                 if(info.empty())\r
1605                 {\r
1606                         SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
1607                         return false;\r
1608                 }\r
1609                 replyString << TEXT("200 CINF OK\r\n");\r
1610                 replyString << info << "\r\n";\r
1611         }\r
1612         catch(...)\r
1613         {\r
1614                 SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
1615                 return false;\r
1616         }\r
1617         \r
1618         SetReplyString(replyString.str());\r
1619         return true;\r
1620 }\r
1621 \r
1622 void GenerateChannelInfo(int index, const safe_ptr<core::video_channel>& pChannel, std::wstringstream& replyString)\r
1623 {\r
1624         replyString << index+1 << TEXT(" ") << pChannel->get_video_format_desc().name << TEXT(" PLAYING") << TEXT("\r\n");\r
1625 }\r
1626 \r
1627 bool InfoCommand::DoExecute()\r
1628 {\r
1629         std::wstringstream replyString;\r
1630         \r
1631         boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);\r
1632 \r
1633         try\r
1634         {\r
1635                 if(_parameters.size() >= 1 && _parameters[0] == L"TEMPLATE")\r
1636                 {               \r
1637                         replyString << L"201 INFO TEMPLATE OK\r\n";\r
1638 \r
1639                         // Needs to be extended for any file, not just flash.\r
1640 \r
1641                         auto filename = flash::find_template(env::template_folder() + _parameters.at(1));\r
1642                                                 \r
1643                         std::wstringstream str;\r
1644                         str << widen(flash::read_template_meta_info(filename));\r
1645                         boost::property_tree::wptree info;\r
1646                         boost::property_tree::xml_parser::read_xml(str, info, boost::property_tree::xml_parser::trim_whitespace | boost::property_tree::xml_parser::no_comments);\r
1647 \r
1648                         boost::property_tree::xml_parser::write_xml(replyString, info, w);\r
1649                 }\r
1650                 else if(_parameters.size() >= 1 && _parameters[0] == L"CONFIG")\r
1651                 {               \r
1652                         replyString << L"201 INFO CONFIG OK\r\n";\r
1653 \r
1654                         boost::property_tree::write_xml(replyString, caspar::env::properties(), w);\r
1655                 }\r
1656                 else if(_parameters.size() >= 1 && _parameters[0] == L"PATHS")\r
1657                 {\r
1658                         replyString << L"201 INFO PATHS OK\r\n";\r
1659 \r
1660                         boost::property_tree::wptree info;\r
1661                         info.add_child(L"paths", caspar::env::properties().get_child(L"configuration.paths"));\r
1662                         info.add(L"paths.initial-path", boost::filesystem2::initial_path<boost::filesystem2::wpath>().directory_string() + L"\\");\r
1663 \r
1664                         boost::property_tree::write_xml(replyString, info, w);\r
1665                 }\r
1666                 else if(_parameters.size() >= 1 && _parameters[0] == L"SYSTEM")\r
1667                 {\r
1668                         replyString << L"201 INFO SYSTEM OK\r\n";\r
1669                         \r
1670                         boost::property_tree::wptree info;\r
1671                         \r
1672                         info.add(L"system.name",                                        caspar::get_system_product_name());\r
1673                         info.add(L"system.windows.name",                        caspar::get_win_product_name());\r
1674                         info.add(L"system.windows.service-pack",        caspar::get_win_sp_version());\r
1675                         info.add(L"system.cpu",                                         caspar::get_cpu_info());\r
1676         \r
1677                         BOOST_FOREACH(auto device, caspar::decklink::get_device_list())\r
1678                                 info.add(L"system.caspar.decklink.device", device);\r
1679 \r
1680                         BOOST_FOREACH(auto device, caspar::bluefish::get_device_list())\r
1681                                 info.add(L"system.caspar.bluefish.device", device);\r
1682                                 \r
1683                         info.add(L"system.caspar.flash",                                        caspar::flash::get_version());\r
1684                         info.add(L"system.caspar.template-host",                        caspar::flash::get_cg_version());\r
1685                         info.add(L"system.caspar.free-image",                           caspar::image::get_version());\r
1686                         info.add(L"system.caspar.ffmpeg.avcodec",                       caspar::ffmpeg::get_avcodec_version());\r
1687                         info.add(L"system.caspar.ffmpeg.avformat",                      caspar::ffmpeg::get_avformat_version());\r
1688                         info.add(L"system.caspar.ffmpeg.avfilter",                      caspar::ffmpeg::get_avfilter_version());\r
1689                         info.add(L"system.caspar.ffmpeg.avutil",                        caspar::ffmpeg::get_avutil_version());\r
1690                         info.add(L"system.caspar.ffmpeg.swscale",                       caspar::ffmpeg::get_swscale_version());\r
1691                                                                         \r
1692                         boost::property_tree::write_xml(replyString, info, w);\r
1693                 }\r
1694                 else if(_parameters.size() >= 1 && _parameters[0] == L"SERVER")\r
1695                 {\r
1696                         replyString << L"201 INFO SERVER OK\r\n";\r
1697                         \r
1698                         boost::property_tree::wptree info;\r
1699 \r
1700                         int index = 0;\r
1701                         BOOST_FOREACH(auto channel, channels_)\r
1702                                 info.add_child(L"channels.channel", channel->info())\r
1703                                         .add(L"index", ++index);\r
1704                         \r
1705                         boost::property_tree::write_xml(replyString, info, w);\r
1706                 }\r
1707                 else // channel\r
1708                 {                       \r
1709                         if(_parameters.size() >= 1)\r
1710                         {\r
1711                                 replyString << TEXT("201 INFO OK\r\n");\r
1712                                 boost::property_tree::wptree info;\r
1713 \r
1714                                 std::vector<std::wstring> split;\r
1715                                 boost::split(split, _parameters[0], boost::is_any_of("-"));\r
1716                                         \r
1717                                 int layer = std::numeric_limits<int>::min();\r
1718                                 int channel = boost::lexical_cast<int>(split[0]) - 1;\r
1719 \r
1720                                 if(split.size() > 1)\r
1721                                         layer = boost::lexical_cast<int>(split[1]);\r
1722                                 \r
1723                                 if(layer == std::numeric_limits<int>::min())\r
1724                                 {       \r
1725                                         info.add_child(L"channel", channels_.at(channel)->info())\r
1726                                                         .add(L"index", channel);\r
1727                                 }\r
1728                                 else\r
1729                                 {\r
1730                                         if(_parameters.size() >= 2)\r
1731                                         {\r
1732                                                 if(_parameters[1] == L"B")\r
1733                                                         info.add_child(L"producer", channels_.at(channel)->stage()->background(layer).get()->info());\r
1734                                                 else\r
1735                                                         info.add_child(L"producer", channels_.at(channel)->stage()->foreground(layer).get()->info());\r
1736                                         }\r
1737                                         else\r
1738                                         {\r
1739                                                 info.add_child(L"layer", channels_.at(channel)->stage()->info(layer).get())\r
1740                                                         .add(L"index", layer);\r
1741                                         }\r
1742                                 }\r
1743                                 boost::property_tree::xml_parser::write_xml(replyString, info, w);\r
1744                         }\r
1745                         else\r
1746                         {\r
1747                                 // This is needed for backwards compatibility with old clients\r
1748                                 replyString << TEXT("200 INFO OK\r\n");\r
1749                                 for(size_t n = 0; n < channels_.size(); ++n)\r
1750                                         GenerateChannelInfo(n, channels_[n], replyString);\r
1751                         }\r
1752 \r
1753                 }\r
1754         }\r
1755         catch(...)\r
1756         {\r
1757                 SetReplyString(TEXT("403 INFO ERROR\r\n"));\r
1758                 return false;\r
1759         }\r
1760 \r
1761         replyString << TEXT("\r\n");\r
1762         SetReplyString(replyString.str());\r
1763         return true;\r
1764 }\r
1765 \r
1766 bool ClsCommand::DoExecute()\r
1767 {\r
1768 /*\r
1769                 wav = audio\r
1770                 mp3 = audio\r
1771                 swf     = movie\r
1772                 dv  = movie\r
1773                 tga = still\r
1774                 col = still\r
1775         */\r
1776         std::wstringstream replyString;\r
1777         replyString << TEXT("200 CLS OK\r\n");\r
1778         replyString << ListMedia();\r
1779         replyString << TEXT("\r\n");\r
1780         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1781         return true;\r
1782 }\r
1783 \r
1784 bool TlsCommand::DoExecute()\r
1785 {\r
1786         std::wstringstream replyString;\r
1787         replyString << TEXT("200 TLS OK\r\n");\r
1788 \r
1789         replyString << ListTemplates();\r
1790         replyString << TEXT("\r\n");\r
1791 \r
1792         SetReplyString(replyString.str());\r
1793         return true;\r
1794 }\r
1795 \r
1796 bool VersionCommand::DoExecute()\r
1797 {\r
1798         std::wstring replyString = TEXT("201 VERSION OK\r\n") + env::version() + TEXT("\r\n");\r
1799 \r
1800         if(_parameters.size() > 0)\r
1801         {\r
1802                 if(_parameters[0] == L"FLASH")\r
1803                         replyString = TEXT("201 VERSION OK\r\n") + flash::get_version() + TEXT("\r\n");\r
1804                 else if(_parameters[0] == L"TEMPLATEHOST")\r
1805                         replyString = TEXT("201 VERSION OK\r\n") + flash::get_cg_version() + TEXT("\r\n");\r
1806                 else if(_parameters[0] != L"SERVER")\r
1807                         replyString = TEXT("403 VERSION ERROR\r\n");\r
1808         }\r
1809 \r
1810         SetReplyString(replyString);\r
1811         return true;\r
1812 }\r
1813 \r
1814 bool ByeCommand::DoExecute()\r
1815 {\r
1816         GetClientInfo()->Disconnect();\r
1817         return true;\r
1818 }\r
1819 \r
1820 bool SetCommand::DoExecute()\r
1821 {\r
1822         std::wstring name = _parameters[0];\r
1823         std::transform(name.begin(), name.end(), name.begin(), toupper);\r
1824 \r
1825         std::wstring value = _parameters[1];\r
1826         std::transform(value.begin(), value.end(), value.begin(), toupper);\r
1827 \r
1828         if(name == TEXT("MODE"))\r
1829         {\r
1830                 auto format_desc = core::video_format_desc::get(value);\r
1831                 if(format_desc.format != core::video_format::invalid)\r
1832                 {\r
1833                         GetChannel()->set_video_format_desc(format_desc);\r
1834                         SetReplyString(TEXT("202 SET MODE OK\r\n"));\r
1835                 }\r
1836                 else\r
1837                         SetReplyString(TEXT("501 SET MODE FAILED\r\n"));\r
1838         }\r
1839         else\r
1840         {\r
1841                 this->SetReplyString(TEXT("403 SET ERROR\r\n"));\r
1842         }\r
1843 \r
1844         return true;\r
1845 }\r
1846 \r
1847 bool KillCommand::DoExecute()\r
1848 {\r
1849         GetShutdownServerNow().set_value(false); // False for not waiting until a key is pressed before terminating.\r
1850         return true;\r
1851 }\r
1852 \r
1853 }       //namespace amcp\r
1854 }}      //namespace caspar