]> git.sesse.net Git - casparcg/blob - protocol/amcp/AMCPCommandsImpl.cpp
2.0.2: frame_producer: Added file-nb-frames and file-frame-number.
[casparcg] / protocol / amcp / AMCPCommandsImpl.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\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 */\r
20 \r
21 #include "../StdAfx.h"\r
22 \r
23 #if defined(_MSC_VER)\r
24 #pragma warning (push, 1) // TODO: Legacy code, just disable warnings\r
25 #endif\r
26 \r
27 #include "AMCPCommandsImpl.h"\r
28 #include "AMCPProtocolStrategy.h"\r
29 \r
30 #include <common/env.h>\r
31 \r
32 #include <common/log/log.h>\r
33 #include <common/diagnostics/graph.h>\r
34 \r
35 #include <core/producer/frame_producer.h>\r
36 #include <core/video_format.h>\r
37 #include <core/producer/transition/transition_producer.h>\r
38 #include <core/producer/frame/frame_transform.h>\r
39 #include <core/producer/stage.h>\r
40 #include <core/producer/layer.h>\r
41 #include <core/mixer/mixer.h>\r
42 #include <core/consumer/output.h>\r
43 \r
44 #include <modules/flash/flash.h>\r
45 #include <modules/flash/producer/flash_producer.h>\r
46 #include <modules/flash/producer/cg_producer.h>\r
47 \r
48 #include <algorithm>\r
49 #include <locale>\r
50 #include <fstream>\r
51 #include <cctype>\r
52 #include <io.h>\r
53 \r
54 #include <boost/date_time/posix_time/posix_time.hpp>\r
55 #include <boost/lexical_cast.hpp>\r
56 #include <boost/algorithm/string.hpp>\r
57 #include <boost/filesystem.hpp>\r
58 #include <boost/regex.hpp>\r
59 \r
60 /* Return codes\r
61 \r
62 100 [action]                    Information om att något har hänt  \r
63 101 [action]                    Information om att något har hänt, en rad data skickas  \r
64 \r
65 202 [kommando] OK               Kommandot har utförts  \r
66 201 [kommando] OK               Kommandot har utförts, och en rad data skickas tillbaka  \r
67 200 [kommando] OK               Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad  \r
68 \r
69 400 ERROR                               Kommandot kunde inte förstås  \r
70 401 [kommando] ERROR    Ogiltig kanal  \r
71 402 [kommando] ERROR    Parameter saknas  \r
72 403 [kommando] ERROR    Ogiltig parameter  \r
73 404 [kommando] ERROR    Mediafilen hittades inte  \r
74 \r
75 500 FAILED                              Internt configurationfel  \r
76 501 [kommando] FAILED   Internt configurationfel  \r
77 502 [kommando] FAILED   Oläslig mediafil  \r
78 \r
79 600 [kommando] FAILED   funktion ej implementerad\r
80 */\r
81 \r
82 namespace caspar { namespace protocol {\r
83 \r
84 using namespace core;\r
85 \r
86 std::wstring MediaInfo(const boost::filesystem::wpath& path)\r
87 {\r
88         if(boost::filesystem::is_regular_file(path))\r
89         {\r
90                 std::wstring clipttype = TEXT(" N/A ");\r
91                 std::wstring extension = boost::to_upper_copy(path.extension());\r
92                 if(extension == TEXT(".TGA") || extension == TEXT(".COL") || extension == L".PNG" || extension == L".JPEG" || extension == L".JPG" ||\r
93                         extension == L"GIF" || extension == L"BMP")\r
94                         clipttype = TEXT(" STILL ");\r
95                 else if(extension == TEXT(".SWF") || extension == TEXT(".DV") || extension == TEXT(".MOV") || extension == TEXT(".MPG") || \r
96                                 extension == TEXT(".AVI") || extension == TEXT(".FLV") || extension == TEXT(".F4V") || extension == TEXT(".MP4") ||\r
97                                 extension == L".M2V" || extension == L".H264" || extension == L".MKV" || extension == L".WMV" || extension == L".DIVX" || \r
98                                 extension == L".XVID" || extension == L".OGG")\r
99                         clipttype = TEXT(" MOVIE ");\r
100                 else if(extension == TEXT(".WAV") || extension == TEXT(".MP3"))\r
101                         clipttype = TEXT(" STILL ");\r
102 \r
103                 if(clipttype != TEXT(" N/A "))\r
104                 {               \r
105                         auto is_not_digit = [](char c){ return std::isdigit(c) == 0; };\r
106 \r
107                         auto relativePath = boost::filesystem::wpath(path.file_string().substr(env::media_folder().size()-1, path.file_string().size()));\r
108 \r
109                         auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(path)));\r
110                         writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end());\r
111                         auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
112 \r
113                         auto sizeStr = boost::lexical_cast<std::wstring>(boost::filesystem::file_size(path));\r
114                         sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end());\r
115                         auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
116                                 \r
117                         auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
118                         if(str[0] == '\\' || str[0] == '/')\r
119                                 str = std::wstring(str.begin() + 1, str.end());\r
120 \r
121                         return std::wstring() + TEXT("\"") + str +\r
122                                         + TEXT("\" ") + clipttype +\r
123                                         + TEXT(" ") + sizeStr +\r
124                                         + TEXT(" ") + writeTimeWStr +\r
125                                         + TEXT("\r\n");         \r
126                 }       \r
127         }\r
128         return L"";\r
129 }\r
130 \r
131 std::wstring ListMedia()\r
132 {       \r
133         std::wstringstream replyString;\r
134         for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr) \r
135                 replyString << MediaInfo(itr->path());\r
136         \r
137         return boost::to_upper_copy(replyString.str());\r
138 }\r
139 \r
140 std::wstring ListTemplates() \r
141 {\r
142         std::wstringstream replyString;\r
143 \r
144         for (boost::filesystem::wrecursive_directory_iterator itr(env::template_folder()), end; itr != end; ++itr)\r
145         {               \r
146                 if(boost::filesystem::is_regular_file(itr->path()) && itr->path().extension() == L".ft")\r
147                 {\r
148                         auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::template_folder().size()-1, itr->path().file_string().size()));\r
149 \r
150                         auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(itr->path())));\r
151                         writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), [](char c){ return std::isdigit(c) == 0;}), writeTimeStr.end());\r
152                         auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
153 \r
154                         auto sizeStr = boost::lexical_cast<std::string>(boost::filesystem::file_size(itr->path()));\r
155                         sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), [](char c){ return std::isdigit(c) == 0;}), sizeStr.end());\r
156 \r
157                         auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
158 \r
159                         std::wstring dir = relativePath.parent_path().external_directory_string();\r
160                         std::wstring file = boost::to_upper_copy(relativePath.filename());\r
161                         relativePath = boost::filesystem::wpath(dir + L"/" + file);\r
162                                                 \r
163                         auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
164                         if(str[0] == '\\' || str[0] == '/')\r
165                                 str = std::wstring(str.begin() + 1, str.end());\r
166 \r
167                         replyString << TEXT("\"") << str\r
168                                                 << TEXT("\" ") << sizeWStr\r
169                                                 << TEXT(" ") << writeTimeWStr\r
170                                                 << TEXT("\r\n");                \r
171                 }\r
172         }\r
173         return replyString.str();\r
174 }\r
175 \r
176 namespace amcp {\r
177         \r
178 AMCPCommand::AMCPCommand() : channelIndex_(0), scheduling_(Default), layerIndex_(-1)\r
179 {}\r
180 \r
181 void AMCPCommand::SendReply()\r
182 {\r
183         if(!pClientInfo_) \r
184                 return;\r
185 \r
186         if(replyString_.empty())\r
187                 return;\r
188         pClientInfo_->Send(replyString_);\r
189 }\r
190 \r
191 void AMCPCommand::Clear() \r
192 {\r
193         pChannel_->stage()->clear();\r
194         pClientInfo_.reset();\r
195         channelIndex_ = 0;\r
196         _parameters.clear();\r
197 }\r
198 \r
199 bool DiagnosticsCommand::DoExecute()\r
200 {       \r
201         try\r
202         {\r
203                 diagnostics::show_graphs(true);\r
204 \r
205                 SetReplyString(TEXT("202 DIAG OK\r\n"));\r
206 \r
207                 return true;\r
208         }\r
209         catch(...)\r
210         {\r
211                 CASPAR_LOG_CURRENT_EXCEPTION();\r
212                 SetReplyString(TEXT("502 DIAG FAILED\r\n"));\r
213                 return false;\r
214         }\r
215 }\r
216 \r
217 bool CallCommand::DoExecute()\r
218 {       \r
219         //Perform loading of the clip\r
220         try\r
221         {\r
222                 auto what = _parameters.at(0);\r
223 \r
224                 std::wstring param = _parameters2.at(1);\r
225                 for(auto it = std::begin(_parameters2)+2; it != std::end(_parameters2); ++it)\r
226                         param += L" " + *it;\r
227                 \r
228                 boost::unique_future<std::wstring> result;\r
229                 if(what == L"B")\r
230                         result = GetChannel()->stage()->call(GetLayerIndex(), false, param);\r
231                 else if(what == L"F")\r
232                         result = GetChannel()->stage()->call(GetLayerIndex(), true, param);\r
233                 else\r
234                         result = GetChannel()->stage()->call(GetLayerIndex(), true, _parameters.at(0) + L" " + param);\r
235         \r
236                 if(!result.timed_wait(boost::posix_time::seconds(2)))\r
237                         BOOST_THROW_EXCEPTION(timed_out());\r
238 \r
239                 CASPAR_LOG(info) << "Executed call: " <<  _parameters[0] << TEXT(" successfully");\r
240                 \r
241                 std::wstringstream replyString;\r
242                 replyString << TEXT("201 CALL OK\r\n") << result.get() << L"\r\n";\r
243                 \r
244                 SetReplyString(replyString.str());\r
245 \r
246                 return true;\r
247         }\r
248         catch(...)\r
249         {\r
250                 CASPAR_LOG_CURRENT_EXCEPTION();\r
251                 SetReplyString(TEXT("502 CALL FAILED\r\n"));\r
252                 return false;\r
253         }\r
254 }\r
255 \r
256 bool MixerCommand::DoExecute()\r
257 {       \r
258         //Perform loading of the clip\r
259         try\r
260         {       \r
261                 if(_parameters[0] == L"KEYER" || _parameters[0] == L"IS_KEY")\r
262                 {\r
263                         bool value = lexical_cast_or_default(_parameters.at(1), false);\r
264                         auto transform = [=](frame_transform transform) -> frame_transform\r
265                         {\r
266                                 transform.is_key = value;\r
267                                 return transform;                                       \r
268                         };\r
269 \r
270                         int layer = GetLayerIndex();\r
271                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform);\r
272                 }\r
273                 else if(_parameters[0] == L"OPACITY")\r
274                 {\r
275                         int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
276                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
277 \r
278                         double value = boost::lexical_cast<double>(_parameters.at(1));\r
279                         \r
280                         auto transform = [=](frame_transform transform) -> frame_transform\r
281                         {\r
282                                 transform.opacity = value;\r
283                                 return transform;                                       \r
284                         };\r
285 \r
286                         int layer = GetLayerIndex();\r
287                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
288                 }\r
289                 else if(_parameters[0] == L"FILL" || _parameters[0] == L"FILL_RECT")\r
290                 {\r
291                         int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
292                         std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
293                         double x        = boost::lexical_cast<double>(_parameters.at(1));\r
294                         double y        = boost::lexical_cast<double>(_parameters.at(2));\r
295                         double x_s      = boost::lexical_cast<double>(_parameters.at(3));\r
296                         double y_s      = boost::lexical_cast<double>(_parameters.at(4));\r
297 \r
298                         auto transform = [=](frame_transform transform) mutable -> frame_transform\r
299                         {\r
300                                 transform.fill_translation[0]   = x;\r
301                                 transform.fill_translation[1]   = y;\r
302                                 transform.fill_scale[0]                 = x_s;\r
303                                 transform.fill_scale[1]                 = y_s;\r
304                                 transform.clip_translation[0]   = x;\r
305                                 transform.clip_translation[1]   = y;\r
306                                 transform.clip_scale[0]                 = x_s;\r
307                                 transform.clip_scale[1]                 = y_s;\r
308                                 return transform;\r
309                         };\r
310                                 \r
311                         int layer = GetLayerIndex();\r
312                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
313                 }\r
314                 else if(_parameters[0] == L"CLIP" || _parameters[0] == L"CLIP_RECT")\r
315                 {\r
316                         int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
317                         std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
318                         double x        = boost::lexical_cast<double>(_parameters.at(1));\r
319                         double y        = boost::lexical_cast<double>(_parameters.at(2));\r
320                         double x_s      = boost::lexical_cast<double>(_parameters.at(3));\r
321                         double y_s      = boost::lexical_cast<double>(_parameters.at(4));\r
322 \r
323                         auto transform = [=](frame_transform transform) -> frame_transform\r
324                         {\r
325                                 transform.clip_translation[0]   = x;\r
326                                 transform.clip_translation[1]   = y;\r
327                                 transform.clip_scale[0]                 = x_s;\r
328                                 transform.clip_scale[1]                 = y_s;\r
329                                 return transform;\r
330                         };\r
331                                 \r
332                         int layer = GetLayerIndex();\r
333                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
334                 }\r
335                 else if(_parameters[0] == L"GRID")\r
336                 {\r
337                         int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
338                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
339                         int n = boost::lexical_cast<int>(_parameters.at(1));\r
340                         double delta = 1.0/static_cast<double>(n);\r
341                         for(int x = 0; x < n; ++x)\r
342                         {\r
343                                 for(int y = 0; y < n; ++y)\r
344                                 {\r
345                                         int index = x+y*n+1;\r
346                                         auto transform = [=](frame_transform transform) -> frame_transform\r
347                                         {               \r
348                                                 transform.fill_translation[0]   = x*delta;\r
349                                                 transform.fill_translation[1]   = y*delta;\r
350                                                 transform.fill_scale[0]                 = delta;\r
351                                                 transform.fill_scale[1]                 = delta;\r
352                                                 transform.clip_translation[0]   = x*delta;\r
353                                                 transform.clip_translation[1]   = y*delta;\r
354                                                 transform.clip_scale[0]                 = delta;\r
355                                                 transform.clip_scale[1]                 = delta;                        \r
356                                                 return transform;\r
357                                         };\r
358                                         GetChannel()->mixer()->apply_frame_transform(index, transform, duration, tween);\r
359                                 }\r
360                         }\r
361                 }\r
362                 else if(_parameters[0] == L"BLEND")\r
363                 {\r
364                         auto blend_str = _parameters.at(1);                                                             \r
365                         int layer = GetLayerIndex();\r
366                         GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str));      \r
367                 }\r
368                 else if(_parameters[0] == L"BRIGHTNESS")\r
369                 {\r
370                         auto value = boost::lexical_cast<double>(_parameters.at(1));\r
371                         int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
372                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
373                         auto transform = [=](frame_transform transform) -> frame_transform\r
374                         {\r
375                                 transform.brightness = value;\r
376                                 return transform;\r
377                         };\r
378                                 \r
379                         int layer = GetLayerIndex();\r
380                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
381                 }\r
382                 else if(_parameters[0] == L"SATURATION")\r
383                 {\r
384                         auto value = boost::lexical_cast<double>(_parameters.at(1));\r
385                         int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
386                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
387                         auto transform = [=](frame_transform transform) -> frame_transform\r
388                         {\r
389                                 transform.saturation = value;\r
390                                 return transform;\r
391                         };\r
392                                 \r
393                         int layer = GetLayerIndex();\r
394                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
395                 }\r
396                 else if(_parameters[0] == L"CONTRAST")\r
397                 {\r
398                         auto value = boost::lexical_cast<double>(_parameters.at(1));\r
399                         int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
400                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
401                         auto transform = [=](frame_transform transform) -> frame_transform\r
402                         {\r
403                                 transform.contrast = value;\r
404                                 return transform;\r
405                         };\r
406                                 \r
407                         int layer = GetLayerIndex();\r
408                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
409                 }\r
410                 else if(_parameters[0] == L"LEVELS")\r
411                 {\r
412                         levels value;\r
413                         value.min_input  = boost::lexical_cast<double>(_parameters.at(1));\r
414                         value.max_input  = boost::lexical_cast<double>(_parameters.at(2));\r
415                         value.gamma              = boost::lexical_cast<double>(_parameters.at(3));\r
416                         value.min_output = boost::lexical_cast<double>(_parameters.at(4));\r
417                         value.max_output = boost::lexical_cast<double>(_parameters.at(5));\r
418                         int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
419                         std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear";\r
420 \r
421                         auto transform = [=](frame_transform transform) -> frame_transform\r
422                         {\r
423                                 transform.levels = value;\r
424                                 return transform;\r
425                         };\r
426                                 \r
427                         int layer = GetLayerIndex();\r
428                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);      \r
429                 }\r
430                 else if(_parameters[0] == L"VOLUME")\r
431                 {\r
432                         int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
433                         std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
434                         double value = boost::lexical_cast<double>(_parameters[1]);\r
435 \r
436                         auto transform = [=](frame_transform transform) -> frame_transform\r
437                         {\r
438                                 transform.volume = value;\r
439                                 return transform;\r
440                         };\r
441                                 \r
442                         int layer = GetLayerIndex();\r
443                         GetChannel()->mixer()->apply_frame_transform(GetLayerIndex(), transform, duration, tween);\r
444                 }\r
445                 else if(_parameters[0] == L"CLEAR")\r
446                 {\r
447                         int layer = GetLayerIndex(std::numeric_limits<int>::max());\r
448                         if(layer ==     std::numeric_limits<int>::max())\r
449                                 GetChannel()->mixer()->clear_transforms();\r
450                         else\r
451                                 GetChannel()->mixer()->clear_transforms(layer);\r
452                 }\r
453                 else\r
454                 {\r
455                         SetReplyString(TEXT("404 MIXER ERROR\r\n"));\r
456                         return false;\r
457                 }\r
458         \r
459                 SetReplyString(TEXT("202 MIXER OK\r\n"));\r
460 \r
461                 return true;\r
462         }\r
463         catch(file_not_found&)\r
464         {\r
465                 CASPAR_LOG_CURRENT_EXCEPTION();\r
466                 SetReplyString(TEXT("404 MIXER ERROR\r\n"));\r
467                 return false;\r
468         }\r
469         catch(...)\r
470         {\r
471                 CASPAR_LOG_CURRENT_EXCEPTION();\r
472                 SetReplyString(TEXT("502 MIXER FAILED\r\n"));\r
473                 return false;\r
474         }\r
475 }\r
476 \r
477 bool SwapCommand::DoExecute()\r
478 {       \r
479         //Perform loading of the clip\r
480         try\r
481         {\r
482                 if(GetLayerIndex(-1) != -1)\r
483                 {\r
484                         std::vector<std::string> strs;\r
485                         boost::split(strs, _parameters[0], boost::is_any_of("-"));\r
486                         \r
487                         auto ch1 = GetChannel();\r
488                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);\r
489 \r
490                         int l1 = GetLayerIndex();\r
491                         int l2 = boost::lexical_cast<int>(strs.at(1));\r
492 \r
493                         ch1->stage()->swap_layer(l1, l2, *ch2->stage());\r
494                 }\r
495                 else\r
496                 {\r
497                         auto ch1 = GetChannel();\r
498                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(_parameters[0])-1);\r
499                         ch1->stage()->swap(*ch2->stage());\r
500                 }\r
501 \r
502                 CASPAR_LOG(info) << "Swapped successfully";\r
503 \r
504                 SetReplyString(TEXT("202 SWAP OK\r\n"));\r
505 \r
506                 return true;\r
507         }\r
508         catch(file_not_found&)\r
509         {\r
510                 CASPAR_LOG_CURRENT_EXCEPTION();\r
511                 SetReplyString(TEXT("404 SWAP ERROR\r\n"));\r
512                 return false;\r
513         }\r
514         catch(...)\r
515         {\r
516                 CASPAR_LOG_CURRENT_EXCEPTION();\r
517                 SetReplyString(TEXT("502 SWAP FAILED\r\n"));\r
518                 return false;\r
519         }\r
520 }\r
521 \r
522 bool AddCommand::DoExecute()\r
523 {       \r
524         //Perform loading of the clip\r
525         try\r
526         {\r
527                 GetChannel()->output()->add(GetLayerIndex(), create_consumer(_parameters));\r
528         \r
529                 CASPAR_LOG(info) << "Added " <<  _parameters[0] << TEXT(" successfully");\r
530 \r
531                 SetReplyString(TEXT("202 ADD OK\r\n"));\r
532 \r
533                 return true;\r
534         }\r
535         catch(file_not_found&)\r
536         {\r
537                 CASPAR_LOG_CURRENT_EXCEPTION();\r
538                 SetReplyString(TEXT("404 ADD ERROR\r\n"));\r
539                 return false;\r
540         }\r
541         catch(...)\r
542         {\r
543                 CASPAR_LOG_CURRENT_EXCEPTION();\r
544                 SetReplyString(TEXT("502 ADD FAILED\r\n"));\r
545                 return false;\r
546         }\r
547 }\r
548 \r
549 bool RemoveCommand::DoExecute()\r
550 {       \r
551         //Perform loading of the clip\r
552         try\r
553         {\r
554                 GetChannel()->output()->remove(GetLayerIndex());\r
555 \r
556                 SetReplyString(TEXT("202 REMOVE OK\r\n"));\r
557 \r
558                 return true;\r
559         }\r
560         catch(file_not_found&)\r
561         {\r
562                 CASPAR_LOG_CURRENT_EXCEPTION();\r
563                 SetReplyString(TEXT("404 REMOVE ERROR\r\n"));\r
564                 return false;\r
565         }\r
566         catch(...)\r
567         {\r
568                 CASPAR_LOG_CURRENT_EXCEPTION();\r
569                 SetReplyString(TEXT("502 REMOVE FAILED\r\n"));\r
570                 return false;\r
571         }\r
572 }\r
573 \r
574 bool LoadCommand::DoExecute()\r
575 {       \r
576         //Perform loading of the clip\r
577         try\r
578         {\r
579                 _parameters[0] = _parameters[0];\r
580                 auto pFP = create_producer(GetChannel()->mixer(), _parameters);         \r
581                 GetChannel()->stage()->load(GetLayerIndex(), pFP, true);\r
582         \r
583                 CASPAR_LOG(info) << "Loaded " <<  _parameters[0] << TEXT(" successfully");\r
584 \r
585                 SetReplyString(TEXT("202 LOAD OK\r\n"));\r
586 \r
587                 return true;\r
588         }\r
589         catch(file_not_found&)\r
590         {\r
591                 CASPAR_LOG_CURRENT_EXCEPTION();\r
592                 SetReplyString(TEXT("404 LOAD ERROR\r\n"));\r
593                 return false;\r
594         }\r
595         catch(...)\r
596         {\r
597                 CASPAR_LOG_CURRENT_EXCEPTION();\r
598                 SetReplyString(TEXT("502 LOAD FAILED\r\n"));\r
599                 return false;\r
600         }\r
601 }\r
602 \r
603 \r
604 \r
605 //std::function<std::wstring()> channel_cg_add_command::parse(const std::wstring& message, const std::vector<renderer::render_device_ptr>& channels)\r
606 //{\r
607 //      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
608 //\r
609 //      boost::wsmatch what;\r
610 //      if(!boost::regex_match(message, what, expr))\r
611 //              return nullptr;\r
612 //\r
613 //      auto info = channel_info::parse(what, channels);\r
614 //\r
615 //      int flash_layer_index = boost::lexical_cast<int>(what["FLASH_LAYER"].str());\r
616 //\r
617 //      std::wstring templatename = what["TEMPLATE"].str();\r
618 //      bool play_on_load = what["PLAY_ON_LOAD"].matched ? what["PLAY_ON_LOAD"].str() != L"0" : 0;\r
619 //      std::wstring start_label = what["START_LABEL"].str();   \r
620 //      std::wstring data = get_data(what["DATA"].str());\r
621 //      \r
622 //      boost::replace_all(templatename, "\"", "");\r
623 //\r
624 //      return [=]() -> std::wstring\r
625 //      {       \r
626 //              std::wstring fullFilename = flash::flash_producer::find_template(server::template_folder() + templatename);\r
627 //              if(fullFilename.empty())\r
628 //                      BOOST_THROW_EXCEPTION(file_not_found());\r
629 //      \r
630 //              std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
631 //              std::wstring filename = templatename;\r
632 //              filename.append(extension);\r
633 //\r
634 //              flash::flash::get_default_cg_producer(info.video_channel, std::max<int>(DEFAULT_CHANNEL_LAYER+1, info.layer_index))\r
635 //                      ->add(flash_layer_index, filename, play_on_load, start_label, data);\r
636 //\r
637 //              CASPAR_LOG(info) << L"Executed [amcp_channel_cg_add]";\r
638 //              return L"";\r
639 //      };\r
640 \r
641 bool LoadbgCommand::DoExecute()\r
642 {\r
643         transition_info transitionInfo;\r
644         \r
645         bool bLoop = false;\r
646 \r
647         // TRANSITION\r
648 \r
649         std::wstring message;\r
650         for(size_t n = 0; n < _parameters.size(); ++n)\r
651                 message += _parameters[n] + L" ";\r
652                 \r
653         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
654         boost::wsmatch what;\r
655         if(boost::regex_match(message, what, expr))\r
656         {\r
657                 auto transition = what["TRANSITION"].str();\r
658                 transitionInfo.duration = lexical_cast_or_default<size_t>(what["DURATION"].str());\r
659                 auto direction = what["DIRECTION"].matched ? what["DIRECTION"].str() : L"";\r
660                 auto tween = what["TWEEN"].matched ? what["TWEEN"].str() : L"";\r
661                 transitionInfo.tweener = get_tweener(tween);            \r
662 \r
663                 if(transition == TEXT("CUT"))\r
664                         transitionInfo.type = transition::cut;\r
665                 else if(transition == TEXT("MIX"))\r
666                         transitionInfo.type = transition::mix;\r
667                 else if(transition == TEXT("PUSH"))\r
668                         transitionInfo.type = transition::push;\r
669                 else if(transition == TEXT("SLIDE"))\r
670                         transitionInfo.type = transition::slide;\r
671                 else if(transition == TEXT("WIPE"))\r
672                         transitionInfo.type = transition::wipe;\r
673                 \r
674                 if(direction == TEXT("FROMLEFT"))\r
675                         transitionInfo.direction = transition_direction::from_left;\r
676                 else if(direction == TEXT("FROMRIGHT"))\r
677                         transitionInfo.direction = transition_direction::from_right;\r
678                 else if(direction == TEXT("LEFT"))\r
679                         transitionInfo.direction = transition_direction::from_right;\r
680                 else if(direction == TEXT("RIGHT"))\r
681                         transitionInfo.direction = transition_direction::from_left;\r
682         }\r
683         \r
684         //Perform loading of the clip\r
685         try\r
686         {\r
687                 _parameters[0] = _parameters[0];\r
688                 auto pFP = create_producer(GetChannel()->mixer(), _parameters);\r
689                 if(pFP == frame_producer::empty())\r
690                         BOOST_THROW_EXCEPTION(file_not_found() << msg_info(_parameters.size() > 0 ? narrow(_parameters[0]) : ""));\r
691 \r
692                 bool auto_play = std::find(_parameters.begin(), _parameters.end(), L"AUTO") != _parameters.end();\r
693 \r
694                 auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().field_mode, pFP, transitionInfo);\r
695                 GetChannel()->stage()->load(GetLayerIndex(), pFP2, false, auto_play ? transitionInfo.duration : -1); // TODO: LOOP\r
696         \r
697                 CASPAR_LOG(info) << "Loaded " << _parameters[0] << TEXT(" successfully to background");\r
698                 SetReplyString(TEXT("202 LOADBG OK\r\n"));\r
699 \r
700                 return true;\r
701         }\r
702         catch(file_not_found&)\r
703         {\r
704                 CASPAR_LOG_CURRENT_EXCEPTION();\r
705                 SetReplyString(TEXT("404 LOADBG ERROR\r\n"));\r
706                 return false;\r
707         }\r
708         catch(...)\r
709         {\r
710                 CASPAR_LOG_CURRENT_EXCEPTION();\r
711                 SetReplyString(TEXT("502 LOADBG FAILED\r\n"));\r
712                 return false;\r
713         }\r
714 }\r
715 \r
716 bool PauseCommand::DoExecute()\r
717 {\r
718         try\r
719         {\r
720                 GetChannel()->stage()->pause(GetLayerIndex());\r
721                 SetReplyString(TEXT("202 PAUSE OK\r\n"));\r
722                 return true;\r
723         }\r
724         catch(...)\r
725         {\r
726                 SetReplyString(TEXT("501 PAUSE FAILED\r\n"));\r
727         }\r
728 \r
729         return false;\r
730 }\r
731 \r
732 bool PlayCommand::DoExecute()\r
733 {\r
734         try\r
735         {\r
736                 if(!_parameters.empty())\r
737                 {\r
738                         LoadbgCommand lbg;\r
739                         lbg.SetChannel(GetChannel());\r
740                         lbg.SetChannelIndex(GetChannelIndex());\r
741                         lbg.SetLayerIntex(GetLayerIndex());\r
742                         lbg.SetClientInfo(GetClientInfo());\r
743                         for(auto it = _parameters.begin(); it != _parameters.end(); ++it)\r
744                                 lbg.AddParameter(*it);\r
745                         if(!lbg.Execute())\r
746                                 CASPAR_LOG(warning) << " Failed to play.";\r
747 \r
748                         CASPAR_LOG(info) << "Playing " << _parameters[0];\r
749                 }\r
750 \r
751                 GetChannel()->stage()->play(GetLayerIndex());\r
752                 \r
753                 SetReplyString(TEXT("202 PLAY OK\r\n"));\r
754                 return true;\r
755         }\r
756         catch(...)\r
757         {\r
758                 SetReplyString(TEXT("501 PLAY FAILED\r\n"));\r
759         }\r
760 \r
761         return false;\r
762 }\r
763 \r
764 bool StopCommand::DoExecute()\r
765 {\r
766         try\r
767         {\r
768                 GetChannel()->stage()->stop(GetLayerIndex());\r
769                 SetReplyString(TEXT("202 STOP OK\r\n"));\r
770                 return true;\r
771         }\r
772         catch(...)\r
773         {\r
774                 SetReplyString(TEXT("501 STOP FAILED\r\n"));\r
775         }\r
776 \r
777         return false;\r
778 }\r
779 \r
780 bool ClearCommand::DoExecute()\r
781 {\r
782         int index = GetLayerIndex(std::numeric_limits<int>::min());\r
783         if(index != std::numeric_limits<int>::min())\r
784                 GetChannel()->stage()->clear(index);\r
785         else\r
786                 GetChannel()->stage()->clear();\r
787                 \r
788         SetReplyString(TEXT("202 CLEAR OK\r\n"));\r
789 \r
790         return true;\r
791 }\r
792 \r
793 bool PrintCommand::DoExecute()\r
794 {\r
795         GetChannel()->output()->add(99978, create_consumer(boost::assign::list_of(L"IMAGE")));\r
796                 \r
797         SetReplyString(TEXT("202 PRINT OK\r\n"));\r
798 \r
799         return true;\r
800 }\r
801 \r
802 bool StatusCommand::DoExecute()\r
803 {                               \r
804         if (GetLayerIndex() > -1)\r
805         {\r
806                 auto status = GetChannel()->stage()->get_status(GetLayerIndex());\r
807                 std::wstringstream status_text;\r
808                 status_text\r
809                         << L"201 STATUS OK\r\n"\r
810                         << L"<layer>"\r
811                         << L"\n\t<index>"                               << GetLayerIndex() << L"</index>"\r
812                         << L"\n\t<foreground>"                  << status.foreground << L"</foreground>"\r
813                         << L"\n\t<background>"                  << status.background << L"</background>"\r
814                         << L"\n\t<status>"                              << (status.is_paused ? L"paused" : L"playing") << L"</status>"\r
815                         << L"\n\t<nb-frames>"                   << (status.nb_frames == std::numeric_limits<int64_t>::max() ? 0 : status.nb_frames) << L"</nb-frames>"\r
816                         << L"\n\t<frame-number>"                << status.frame_number << L"</frame-number>"\r
817                         << L"\n\t<file-nb-frames>"              << (status.file_nb_frames == std::numeric_limits<int64_t>::max() ? 0 : status.file_nb_frames) << L"</file-nb-frames>"\r
818                         << L"\n\t<file-frame-number>"   << status.file_frame_number << L"</file-frame-number>"\r
819                         << L"\n</layer>"\r
820                         << L"\r\n";\r
821 \r
822                 SetReplyString(status_text.str());\r
823                 return true;\r
824         }\r
825         else\r
826         {\r
827                 //NOTE: Possible to extend soo that "channel" status is returned when no layer is specified.\r
828 \r
829                 SetReplyString(TEXT("403 LAYER MUST BE SPECIFIED\r\n"));\r
830                 return false;\r
831         }\r
832 }\r
833 \r
834 bool LogCommand::DoExecute()\r
835 {\r
836         if(_parameters.at(0) == L"LEVEL")\r
837                 log::set_log_level(_parameters.at(1));\r
838 \r
839         SetReplyString(TEXT("202 LOG OK\r\n"));\r
840 \r
841         return true;\r
842 }\r
843 \r
844 bool CGCommand::DoExecute()\r
845 {\r
846         std::wstring command = _parameters[0];\r
847         if(command == TEXT("ADD"))\r
848                 return DoExecuteAdd();\r
849         else if(command == TEXT("PLAY"))\r
850                 return DoExecutePlay();\r
851         else if(command == TEXT("STOP"))\r
852                 return DoExecuteStop();\r
853         else if(command == TEXT("NEXT"))\r
854                 return DoExecuteNext();\r
855         else if(command == TEXT("REMOVE"))\r
856                 return DoExecuteRemove();\r
857         else if(command == TEXT("CLEAR"))\r
858                 return DoExecuteClear();\r
859         else if(command == TEXT("UPDATE"))\r
860                 return DoExecuteUpdate();\r
861         else if(command == TEXT("INVOKE"))\r
862                 return DoExecuteInvoke();\r
863         else if(command == TEXT("INFO"))\r
864                 return DoExecuteInfo();\r
865 \r
866         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
867         return false;\r
868 }\r
869 \r
870 bool CGCommand::ValidateLayer(const std::wstring& layerstring) {\r
871         int length = layerstring.length();\r
872         for(int i = 0; i < length; ++i) {\r
873                 if(!_istdigit(layerstring[i])) {\r
874                         return false;\r
875                 }\r
876         }\r
877 \r
878         return true;\r
879 }\r
880 \r
881 bool CGCommand::DoExecuteAdd() {\r
882         //CG 1 ADD 0 "template_folder/templatename" [STARTLABEL] 0/1 [DATA]\r
883 \r
884         int layer = 0;                          //_parameters[1]\r
885 //      std::wstring templateName;      //_parameters[2]\r
886         std::wstring label;             //_parameters[3]\r
887         bool bDoStart = false;          //_parameters[3] alt. _parameters[4]\r
888 //      std::wstring data;                      //_parameters[4] alt. _parameters[5]\r
889 \r
890         if(_parameters.size() < 4) \r
891         {\r
892                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
893                 return false;\r
894         }\r
895         unsigned int dataIndex = 4;\r
896 \r
897         if(!ValidateLayer(_parameters[1])) \r
898         {\r
899                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
900                 return false;\r
901         }\r
902 \r
903         layer = _ttoi(_parameters[1].c_str());\r
904 \r
905         if(_parameters[3].length() > 1) \r
906         {       //read label\r
907                 label = _parameters2[3];\r
908                 ++dataIndex;\r
909 \r
910                 if(_parameters.size() > 4 && _parameters[4].length() > 0)       //read play-on-load-flag\r
911                         bDoStart = (_parameters[4][0]==TEXT('1')) ? true : false;\r
912                 else \r
913                 {\r
914                         SetReplyString(TEXT("402 CG ERROR\r\n"));\r
915                         return false;\r
916                 }\r
917         }\r
918         else if(_parameters[3].length() > 0) {  //read play-on-load-flag\r
919                 bDoStart = (_parameters[3][0]==TEXT('1')) ? true : false;\r
920         }\r
921         else \r
922         {\r
923                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
924                 return false;\r
925         }\r
926 \r
927         const TCHAR* pDataString = 0;\r
928         std::wstringstream data;\r
929         std::wstring dataFromFile;\r
930         if(_parameters.size() > dataIndex) \r
931         {       //read data\r
932                 const std::wstring& dataString = _parameters2[dataIndex];\r
933 \r
934                 if(dataString[0] == TEXT('<')) //the data is an XML-string\r
935                         pDataString = dataString.c_str();\r
936                 else \r
937                 {\r
938                         //The data is not an XML-string, it must be a filename\r
939                         std::wstring filename = env::data_folder();\r
940                         filename.append(dataString);\r
941                         filename.append(TEXT(".ftd"));\r
942 \r
943                         //open file\r
944                         std::wifstream datafile(filename.c_str());\r
945                         if(datafile) \r
946                         {\r
947                                 //read all data\r
948                                 data << datafile.rdbuf();\r
949                                 datafile.close();\r
950 \r
951                                 //extract data to _parameters\r
952                                 dataFromFile = data.str();\r
953                                 pDataString = dataFromFile.c_str();\r
954                         }\r
955                 }\r
956         }\r
957 \r
958         std::wstring fullFilename = flash::find_template(env::template_folder() + _parameters[2]);\r
959         if(!fullFilename.empty())\r
960         {\r
961                 std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
962                 std::wstring filename = _parameters[2];\r
963                 filename.append(extension);\r
964 \r
965                 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
966                 SetReplyString(TEXT("202 CG OK\r\n"));\r
967         }\r
968         else\r
969         {\r
970                 CASPAR_LOG(warning) << "Could not find template " << _parameters[2];\r
971                 SetReplyString(TEXT("404 CG ERROR\r\n"));\r
972         }\r
973         return true;\r
974 }\r
975 \r
976 bool CGCommand::DoExecutePlay()\r
977 {\r
978         if(_parameters.size() > 1)\r
979         {\r
980                 if(!ValidateLayer(_parameters[1])) \r
981                 {\r
982                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
983                         return false;\r
984                 }\r
985                 int layer = _ttoi(_parameters[1].c_str());\r
986                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->play(layer);\r
987         }\r
988         else\r
989         {\r
990                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
991                 return true;\r
992         }\r
993 \r
994         SetReplyString(TEXT("202 CG OK\r\n"));\r
995         return true;\r
996 }\r
997 \r
998 bool CGCommand::DoExecuteStop() \r
999 {\r
1000         if(_parameters.size() > 1)\r
1001         {\r
1002                 if(!ValidateLayer(_parameters[1])) \r
1003                 {\r
1004                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1005                         return false;\r
1006                 }\r
1007                 int layer = _ttoi(_parameters[1].c_str());\r
1008                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->stop(layer, 0);\r
1009         }\r
1010         else \r
1011         {\r
1012                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1013                 return true;\r
1014         }\r
1015 \r
1016         SetReplyString(TEXT("202 CG OK\r\n"));\r
1017         return true;\r
1018 }\r
1019 \r
1020 bool CGCommand::DoExecuteNext()\r
1021 {\r
1022         if(_parameters.size() > 1) \r
1023         {\r
1024                 if(!ValidateLayer(_parameters[1])) \r
1025                 {\r
1026                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1027                         return false;\r
1028                 }\r
1029 \r
1030                 int layer = _ttoi(_parameters[1].c_str());\r
1031                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->next(layer);\r
1032         }\r
1033         else \r
1034         {\r
1035                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1036                 return true;\r
1037         }\r
1038 \r
1039         SetReplyString(TEXT("202 CG OK\r\n"));\r
1040         return true;\r
1041 }\r
1042 \r
1043 bool CGCommand::DoExecuteRemove() \r
1044 {\r
1045         if(_parameters.size() > 1) \r
1046         {\r
1047                 if(!ValidateLayer(_parameters[1])) \r
1048                 {\r
1049                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1050                         return false;\r
1051                 }\r
1052 \r
1053                 int layer = _ttoi(_parameters[1].c_str());\r
1054                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->remove(layer);\r
1055         }\r
1056         else \r
1057         {\r
1058                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1059                 return true;\r
1060         }\r
1061 \r
1062         SetReplyString(TEXT("202 CG OK\r\n"));\r
1063         return true;\r
1064 }\r
1065 \r
1066 bool CGCommand::DoExecuteClear() \r
1067 {\r
1068         GetChannel()->stage()->clear(GetLayerIndex(flash::cg_producer::DEFAULT_LAYER));\r
1069         SetReplyString(TEXT("202 CG OK\r\n"));\r
1070         return true;\r
1071 }\r
1072 \r
1073 bool CGCommand::DoExecuteUpdate() \r
1074 {\r
1075         try\r
1076         {\r
1077                 if(!ValidateLayer(_parameters.at(1)))\r
1078                 {\r
1079                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1080                         return false;\r
1081                 }\r
1082                                                 \r
1083                 std::wstring dataString = _parameters2.at(2);                           \r
1084                 if(dataString.at(0) != TEXT('<'))\r
1085                 {\r
1086                         //The data is not an XML-string, it must be a filename\r
1087                         std::wstring filename = env::data_folder();\r
1088                         filename.append(dataString);\r
1089                         filename.append(TEXT(".ftd"));\r
1090 \r
1091                         //open file\r
1092                         std::wifstream datafile(filename.c_str());\r
1093                         if(datafile) \r
1094                         {\r
1095                                 std::wstringstream data;\r
1096                                 //read all data\r
1097                                 data << datafile.rdbuf();\r
1098                                 datafile.close();\r
1099 \r
1100                                 //extract data to _parameters\r
1101                                 dataString = data.str();\r
1102                         }\r
1103                 }               \r
1104 \r
1105                 int layer = _ttoi(_parameters.at(1).c_str());\r
1106                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->update(layer, dataString);\r
1107         }\r
1108         catch(...)\r
1109         {\r
1110                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1111                 return true;\r
1112         }\r
1113 \r
1114         SetReplyString(TEXT("202 CG OK\r\n"));\r
1115         return true;\r
1116 }\r
1117 \r
1118 bool CGCommand::DoExecuteInvoke() \r
1119 {\r
1120         std::wstringstream replyString;\r
1121         replyString << TEXT("201 CG OK\r\n");\r
1122 \r
1123         if(_parameters.size() > 2)\r
1124         {\r
1125                 if(!ValidateLayer(_parameters[1]))\r
1126                 {\r
1127                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1128                         return false;\r
1129                 }\r
1130                 int layer = _ttoi(_parameters[1].c_str());\r
1131                 auto result = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->invoke(layer, _parameters2[2]);\r
1132                 replyString << result << TEXT("\r\n"); \r
1133         }\r
1134         else \r
1135         {\r
1136                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1137                 return true;\r
1138         }\r
1139         \r
1140         SetReplyString(replyString.str());\r
1141         return true;\r
1142 }\r
1143 \r
1144 bool CGCommand::DoExecuteInfo() \r
1145 {\r
1146         std::wstringstream replyString;\r
1147         replyString << TEXT("201 CG OK\r\n");\r
1148 \r
1149         if(_parameters.size() > 1)\r
1150         {\r
1151                 if(!ValidateLayer(_parameters[1]))\r
1152                 {\r
1153                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1154                         return false;\r
1155                 }\r
1156 \r
1157                 int layer = _ttoi(_parameters[1].c_str());\r
1158                 auto desc = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->description(layer);\r
1159                 \r
1160                 replyString << desc << TEXT("\r\n"); \r
1161         }\r
1162         else \r
1163         {\r
1164                 auto info = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->info();\r
1165                 replyString << info << TEXT("\r\n"); \r
1166         }       \r
1167 \r
1168         SetReplyString(replyString.str());\r
1169         return true;\r
1170 }\r
1171 \r
1172 bool DataCommand::DoExecute()\r
1173 {\r
1174         std::wstring command = _parameters[0];\r
1175         if(command == TEXT("STORE"))\r
1176                 return DoExecuteStore();\r
1177         else if(command == TEXT("RETRIEVE"))\r
1178                 return DoExecuteRetrieve();\r
1179         else if(command == TEXT("LIST"))\r
1180                 return DoExecuteList();\r
1181 \r
1182         SetReplyString(TEXT("403 DATA ERROR\r\n"));\r
1183         return false;\r
1184 }\r
1185 \r
1186 bool DataCommand::DoExecuteStore() \r
1187 {\r
1188         if(_parameters.size() < 3) \r
1189         {\r
1190                 SetReplyString(TEXT("402 DATA STORE ERROR\r\n"));\r
1191                 return false;\r
1192         }\r
1193 \r
1194         std::wstring filename = env::data_folder();\r
1195         filename.append(_parameters[1]);\r
1196         filename.append(TEXT(".ftd"));\r
1197 \r
1198         std::wofstream datafile(filename.c_str());\r
1199         if(!datafile) \r
1200         {\r
1201                 SetReplyString(TEXT("501 DATA STORE FAILED\r\n"));\r
1202                 return false;\r
1203         }\r
1204 \r
1205         datafile << _parameters2[2];\r
1206         datafile.close();\r
1207 \r
1208         std::wstring replyString = TEXT("202 DATA STORE OK\r\n");\r
1209         SetReplyString(replyString);\r
1210         return true;\r
1211 }\r
1212 \r
1213 bool DataCommand::DoExecuteRetrieve() \r
1214 {\r
1215         if(_parameters.size() < 2) \r
1216         {\r
1217                 SetReplyString(TEXT("402 DATA RETRIEVE ERROR\r\n"));\r
1218                 return false;\r
1219         }\r
1220 \r
1221         std::wstring filename = env::data_folder();\r
1222         filename.append(_parameters[1]);\r
1223         filename.append(TEXT(".ftd"));\r
1224 \r
1225         std::wifstream datafile(filename.c_str());\r
1226         if(!datafile) \r
1227         {\r
1228                 SetReplyString(TEXT("404 DATA RETRIEVE ERROR\r\n"));\r
1229                 return false;\r
1230         }\r
1231 \r
1232         std::wstringstream reply(TEXT("201 DATA RETRIEVE OK\r\n"));\r
1233         std::wstring line;\r
1234         bool bFirstLine = true;\r
1235         while(std::getline(datafile, line))\r
1236         {\r
1237                 if(!bFirstLine)\r
1238                         reply << "\\n";\r
1239                 else\r
1240                         bFirstLine = false;\r
1241 \r
1242                 reply << line;\r
1243         }\r
1244         datafile.close();\r
1245 \r
1246         reply << "\r\n";\r
1247         SetReplyString(reply.str());\r
1248         return true;\r
1249 }\r
1250 \r
1251 bool DataCommand::DoExecuteList() \r
1252 {\r
1253         std::wstringstream replyString;\r
1254         replyString << TEXT("200 DATA LIST OK\r\n");\r
1255 \r
1256         for (boost::filesystem::wrecursive_directory_iterator itr(env::data_folder()), end; itr != end; ++itr)\r
1257         {                       \r
1258                 if(boost::filesystem::is_regular_file(itr->path()))\r
1259                 {\r
1260                         if(!boost::iequals(itr->path().extension(), L".ftd"))\r
1261                                 continue;\r
1262                         \r
1263                         auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::data_folder().size()-1, itr->path().file_string().size()));\r
1264                         \r
1265                         auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
1266                         if(str[0] == '\\' || str[0] == '/')\r
1267                                 str = std::wstring(str.begin() + 1, str.end());\r
1268 \r
1269                         replyString << str << TEXT("\r\n");     \r
1270                 }\r
1271         }\r
1272         \r
1273         replyString << TEXT("\r\n");\r
1274 \r
1275         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1276         return true;\r
1277 }\r
1278 \r
1279 bool CinfCommand::DoExecute()\r
1280 {\r
1281         std::wstringstream replyString;\r
1282         \r
1283         try\r
1284         {\r
1285                 std::wstring info;\r
1286                 for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)\r
1287                 {\r
1288                         auto path = itr->path();\r
1289                         auto file = path.replace_extension(L"").filename();\r
1290                         if(boost::iequals(file, _parameters.at(0)))\r
1291                                 info += MediaInfo(itr->path()) + L"\r\n";\r
1292                 }\r
1293 \r
1294                 if(info.empty())\r
1295                 {\r
1296                         SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
1297                         return false;\r
1298                 }\r
1299                 replyString << TEXT("200 INFO OK\r\n");\r
1300                 replyString << info << "\r\n";\r
1301         }\r
1302         catch(...)\r
1303         {\r
1304                 SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
1305                 return false;\r
1306         }\r
1307         \r
1308         SetReplyString(replyString.str());\r
1309         return true;\r
1310 }\r
1311 \r
1312 void GenerateChannelInfo(int index, const safe_ptr<core::video_channel>& pChannel, std::wstringstream& replyString)\r
1313 {\r
1314         replyString << index+1 << TEXT(" ") << pChannel->get_video_format_desc().name << TEXT(" PLAYING") << TEXT("\r\n");\r
1315 }\r
1316 \r
1317 bool InfoCommand::DoExecute()\r
1318 {\r
1319         try\r
1320         {\r
1321                 std::wstringstream replyString;\r
1322                 if(_parameters.size() >= 1)\r
1323                 {\r
1324                         int channelIndex = _ttoi(_parameters.at(0).c_str())-1;\r
1325                         replyString << TEXT("201 INFO OK\r\n");\r
1326                         GenerateChannelInfo(channelIndex, channels_.at(channelIndex), replyString);\r
1327                 }\r
1328                 else\r
1329                 {\r
1330                         replyString << TEXT("200 INFO OK\r\n");\r
1331                         for(size_t n = 0; n < channels_.size(); ++n)\r
1332                                 GenerateChannelInfo(n, channels_[n], replyString);\r
1333                         replyString << TEXT("\r\n");\r
1334                 }\r
1335                 SetReplyString(replyString.str());\r
1336         }\r
1337         catch(...)\r
1338         {\r
1339                 SetReplyString(TEXT("401 INFO ERROR\r\n"));\r
1340                 return false;\r
1341         }\r
1342 \r
1343         return true;\r
1344 }\r
1345 \r
1346 bool ClsCommand::DoExecute()\r
1347 {\r
1348 /*\r
1349                 wav = audio\r
1350                 mp3 = audio\r
1351                 swf     = movie\r
1352                 dv  = movie\r
1353                 tga = still\r
1354                 col = still\r
1355         */\r
1356         std::wstringstream replyString;\r
1357         replyString << TEXT("200 CLS OK\r\n");\r
1358         replyString << ListMedia();\r
1359         replyString << TEXT("\r\n");\r
1360         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1361         return true;\r
1362 }\r
1363 \r
1364 bool TlsCommand::DoExecute()\r
1365 {\r
1366         std::wstringstream replyString;\r
1367         replyString << TEXT("200 TLS OK\r\n");\r
1368 \r
1369         replyString << ListTemplates();\r
1370         replyString << TEXT("\r\n");\r
1371 \r
1372         SetReplyString(replyString.str());\r
1373         return true;\r
1374 }\r
1375 \r
1376 bool VersionCommand::DoExecute()\r
1377 {\r
1378         std::wstring replyString = TEXT("201 VERSION OK\r\n SERVER: ") + env::version() + TEXT("\r\n");\r
1379 \r
1380         if(_parameters.size() > 0)\r
1381         {\r
1382                 if(_parameters[0] == L"FLASH")\r
1383                         replyString = TEXT("201 VERSION OK\r\n FLASH: ") + flash::get_version() + TEXT("\r\n");\r
1384                 else if(_parameters[0] == L"TEMPLATEHOST")\r
1385                         replyString = TEXT("201 VERSION OK\r\n TEMPLATEHOST: ") + flash::get_cg_version() + TEXT("\r\n");\r
1386                 else if(_parameters[0] != L"SERVER")\r
1387                         replyString = TEXT("403 VERSION ERROR\r\n");\r
1388         }\r
1389 \r
1390         SetReplyString(replyString);\r
1391         return true;\r
1392 }\r
1393 \r
1394 bool ByeCommand::DoExecute()\r
1395 {\r
1396         GetClientInfo()->Disconnect();\r
1397         return true;\r
1398 }\r
1399 \r
1400 bool SetCommand::DoExecute()\r
1401 {\r
1402         std::wstring name = _parameters[0];\r
1403         std::transform(name.begin(), name.end(), name.begin(), toupper);\r
1404 \r
1405         std::wstring value = _parameters[1];\r
1406         std::transform(value.begin(), value.end(), value.begin(), toupper);\r
1407 \r
1408         if(name == TEXT("MODE"))\r
1409         {\r
1410                 auto format_desc = core::video_format_desc::get(value);\r
1411                 if(format_desc.format != core::video_format::invalid)\r
1412                 {\r
1413                         GetChannel()->set_video_format_desc(format_desc);\r
1414                         SetReplyString(TEXT("202 SET MODE OK\r\n"));\r
1415                 }\r
1416                 else\r
1417                         SetReplyString(TEXT("501 SET MODE FAILED\r\n"));\r
1418         }\r
1419         else\r
1420         {\r
1421                 this->SetReplyString(TEXT("403 SET ERROR\r\n"));\r
1422         }\r
1423 \r
1424         return true;\r
1425 }\r
1426 \r
1427 \r
1428 }       //namespace amcp\r
1429 }}      //namespace caspar