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