]> git.sesse.net Git - casparcg/blob - protocol/amcp/AMCPCommandsImpl.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[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 ParamCommand::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()->param(GetLayerIndex(), false, param);\r
231                 else if(what == L"F")\r
232                         result = GetChannel()->stage()->param(GetLayerIndex(), true, param);\r
233                 else\r
234                         result = GetChannel()->stage()->param(GetLayerIndex(), true, _parameters.at(0) + L" " + param);\r
235         \r
236                 CASPAR_LOG(info) << "Executed param: " <<  _parameters[0] << TEXT(" successfully");\r
237                 \r
238                 std::wstringstream replyString;\r
239                 replyString << TEXT("201 PARAM 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 PARAM 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"200 STATUS OK\r\n"\r
807                         << L"FOREGROUND:"               << status.foreground << L"\r\n"\r
808                         << L"BACKGROUND:"               << status.background << L"\r\n"\r
809                         << L"STATUS:"                   << (status.is_paused ? L"PAUSED" : L"PLAYING") << L"\r\n"\r
810                         << L"TOTAL FRAMES:"             << (status.total_frames == std::numeric_limits<int64_t>::max() ? 0 : status.total_frames) << L"\r\n"\r
811                         << L"CURRENT FRAME:"    << status.current_frame << L"\r\n\r\n";\r
812 \r
813                 SetReplyString(status_text.str());\r
814                 return true;\r
815         }\r
816         else\r
817         {\r
818                 //NOTE: Possible to extend soo that "channel" status is returned when no layer is specified.\r
819 \r
820                 SetReplyString(TEXT("403 LAYER MUST BE SPECIFIED\r\n"));\r
821                 return false;\r
822         }\r
823 }\r
824 \r
825 bool LogCommand::DoExecute()\r
826 {\r
827         if(_parameters.at(0) == L"LEVEL")\r
828                 log::set_log_level(_parameters.at(1));\r
829 \r
830         SetReplyString(TEXT("202 LOG OK\r\n"));\r
831 \r
832         return true;\r
833 }\r
834 \r
835 bool CGCommand::DoExecute()\r
836 {\r
837         std::wstring command = _parameters[0];\r
838         if(command == TEXT("ADD"))\r
839                 return DoExecuteAdd();\r
840         else if(command == TEXT("PLAY"))\r
841                 return DoExecutePlay();\r
842         else if(command == TEXT("STOP"))\r
843                 return DoExecuteStop();\r
844         else if(command == TEXT("NEXT"))\r
845                 return DoExecuteNext();\r
846         else if(command == TEXT("REMOVE"))\r
847                 return DoExecuteRemove();\r
848         else if(command == TEXT("CLEAR"))\r
849                 return DoExecuteClear();\r
850         else if(command == TEXT("UPDATE"))\r
851                 return DoExecuteUpdate();\r
852         else if(command == TEXT("INVOKE"))\r
853                 return DoExecuteInvoke();\r
854         else if(command == TEXT("INFO"))\r
855                 return DoExecuteInfo();\r
856 \r
857         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
858         return false;\r
859 }\r
860 \r
861 bool CGCommand::ValidateLayer(const std::wstring& layerstring) {\r
862         int length = layerstring.length();\r
863         for(int i = 0; i < length; ++i) {\r
864                 if(!_istdigit(layerstring[i])) {\r
865                         return false;\r
866                 }\r
867         }\r
868 \r
869         return true;\r
870 }\r
871 \r
872 bool CGCommand::DoExecuteAdd() {\r
873         //CG 1 ADD 0 "template_folder/templatename" [STARTLABEL] 0/1 [DATA]\r
874 \r
875         int layer = 0;                          //_parameters[1]\r
876 //      std::wstring templateName;      //_parameters[2]\r
877         std::wstring label;             //_parameters[3]\r
878         bool bDoStart = false;          //_parameters[3] alt. _parameters[4]\r
879 //      std::wstring data;                      //_parameters[4] alt. _parameters[5]\r
880 \r
881         if(_parameters.size() < 4) \r
882         {\r
883                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
884                 return false;\r
885         }\r
886         unsigned int dataIndex = 4;\r
887 \r
888         if(!ValidateLayer(_parameters[1])) \r
889         {\r
890                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
891                 return false;\r
892         }\r
893 \r
894         layer = _ttoi(_parameters[1].c_str());\r
895 \r
896         if(_parameters[3].length() > 1) \r
897         {       //read label\r
898                 label = _parameters2[3];\r
899                 ++dataIndex;\r
900 \r
901                 if(_parameters.size() > 4 && _parameters[4].length() > 0)       //read play-on-load-flag\r
902                         bDoStart = (_parameters[4][0]==TEXT('1')) ? true : false;\r
903                 else \r
904                 {\r
905                         SetReplyString(TEXT("402 CG ERROR\r\n"));\r
906                         return false;\r
907                 }\r
908         }\r
909         else if(_parameters[3].length() > 0) {  //read play-on-load-flag\r
910                 bDoStart = (_parameters[3][0]==TEXT('1')) ? true : false;\r
911         }\r
912         else \r
913         {\r
914                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
915                 return false;\r
916         }\r
917 \r
918         const TCHAR* pDataString = 0;\r
919         std::wstringstream data;\r
920         std::wstring dataFromFile;\r
921         if(_parameters.size() > dataIndex) \r
922         {       //read data\r
923                 const std::wstring& dataString = _parameters2[dataIndex];\r
924 \r
925                 if(dataString[0] == TEXT('<')) //the data is an XML-string\r
926                         pDataString = dataString.c_str();\r
927                 else \r
928                 {\r
929                         //The data is not an XML-string, it must be a filename\r
930                         std::wstring filename = env::data_folder();\r
931                         filename.append(dataString);\r
932                         filename.append(TEXT(".ftd"));\r
933 \r
934                         //open file\r
935                         std::wifstream datafile(filename.c_str());\r
936                         if(datafile) \r
937                         {\r
938                                 //read all data\r
939                                 data << datafile.rdbuf();\r
940                                 datafile.close();\r
941 \r
942                                 //extract data to _parameters\r
943                                 dataFromFile = data.str();\r
944                                 pDataString = dataFromFile.c_str();\r
945                         }\r
946                 }\r
947         }\r
948 \r
949         std::wstring fullFilename = flash::find_template(env::template_folder() + _parameters[2]);\r
950         if(!fullFilename.empty())\r
951         {\r
952                 std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
953                 std::wstring filename = _parameters[2];\r
954                 filename.append(extension);\r
955 \r
956                 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
957                 SetReplyString(TEXT("202 CG OK\r\n"));\r
958         }\r
959         else\r
960         {\r
961                 CASPAR_LOG(warning) << "Could not find template " << _parameters[2];\r
962                 SetReplyString(TEXT("404 CG ERROR\r\n"));\r
963         }\r
964         return true;\r
965 }\r
966 \r
967 bool CGCommand::DoExecutePlay()\r
968 {\r
969         if(_parameters.size() > 1)\r
970         {\r
971                 if(!ValidateLayer(_parameters[1])) \r
972                 {\r
973                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
974                         return false;\r
975                 }\r
976                 int layer = _ttoi(_parameters[1].c_str());\r
977                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->play(layer);\r
978         }\r
979         else\r
980         {\r
981                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
982                 return true;\r
983         }\r
984 \r
985         SetReplyString(TEXT("202 CG OK\r\n"));\r
986         return true;\r
987 }\r
988 \r
989 bool CGCommand::DoExecuteStop() \r
990 {\r
991         if(_parameters.size() > 1)\r
992         {\r
993                 if(!ValidateLayer(_parameters[1])) \r
994                 {\r
995                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
996                         return false;\r
997                 }\r
998                 int layer = _ttoi(_parameters[1].c_str());\r
999                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->stop(layer, 0);\r
1000         }\r
1001         else \r
1002         {\r
1003                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1004                 return true;\r
1005         }\r
1006 \r
1007         SetReplyString(TEXT("202 CG OK\r\n"));\r
1008         return true;\r
1009 }\r
1010 \r
1011 bool CGCommand::DoExecuteNext()\r
1012 {\r
1013         if(_parameters.size() > 1) \r
1014         {\r
1015                 if(!ValidateLayer(_parameters[1])) \r
1016                 {\r
1017                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1018                         return false;\r
1019                 }\r
1020 \r
1021                 int layer = _ttoi(_parameters[1].c_str());\r
1022                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->next(layer);\r
1023         }\r
1024         else \r
1025         {\r
1026                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1027                 return true;\r
1028         }\r
1029 \r
1030         SetReplyString(TEXT("202 CG OK\r\n"));\r
1031         return true;\r
1032 }\r
1033 \r
1034 bool CGCommand::DoExecuteRemove() \r
1035 {\r
1036         if(_parameters.size() > 1) \r
1037         {\r
1038                 if(!ValidateLayer(_parameters[1])) \r
1039                 {\r
1040                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1041                         return false;\r
1042                 }\r
1043 \r
1044                 int layer = _ttoi(_parameters[1].c_str());\r
1045                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->remove(layer);\r
1046         }\r
1047         else \r
1048         {\r
1049                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1050                 return true;\r
1051         }\r
1052 \r
1053         SetReplyString(TEXT("202 CG OK\r\n"));\r
1054         return true;\r
1055 }\r
1056 \r
1057 bool CGCommand::DoExecuteClear() \r
1058 {\r
1059         GetChannel()->stage()->clear(GetLayerIndex(flash::cg_producer::DEFAULT_LAYER));\r
1060         SetReplyString(TEXT("202 CG OK\r\n"));\r
1061         return true;\r
1062 }\r
1063 \r
1064 bool CGCommand::DoExecuteUpdate() \r
1065 {\r
1066         try\r
1067         {\r
1068                 if(!ValidateLayer(_parameters.at(1)))\r
1069                 {\r
1070                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1071                         return false;\r
1072                 }\r
1073                                                 \r
1074                 std::wstring dataString = _parameters2.at(2);                           \r
1075                 if(dataString.at(0) != TEXT('<'))\r
1076                 {\r
1077                         //The data is not an XML-string, it must be a filename\r
1078                         std::wstring filename = env::data_folder();\r
1079                         filename.append(dataString);\r
1080                         filename.append(TEXT(".ftd"));\r
1081 \r
1082                         //open file\r
1083                         std::wifstream datafile(filename.c_str());\r
1084                         if(datafile) \r
1085                         {\r
1086                                 std::wstringstream data;\r
1087                                 //read all data\r
1088                                 data << datafile.rdbuf();\r
1089                                 datafile.close();\r
1090 \r
1091                                 //extract data to _parameters\r
1092                                 dataString = data.str();\r
1093                         }\r
1094                 }               \r
1095 \r
1096                 int layer = _ttoi(_parameters.at(1).c_str());\r
1097                 flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->update(layer, dataString);\r
1098         }\r
1099         catch(...)\r
1100         {\r
1101                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1102                 return true;\r
1103         }\r
1104 \r
1105         SetReplyString(TEXT("202 CG OK\r\n"));\r
1106         return true;\r
1107 }\r
1108 \r
1109 bool CGCommand::DoExecuteInvoke() \r
1110 {\r
1111         std::wstringstream replyString;\r
1112         replyString << TEXT("201 CG OK\r\n");\r
1113 \r
1114         if(_parameters.size() > 2)\r
1115         {\r
1116                 if(!ValidateLayer(_parameters[1]))\r
1117                 {\r
1118                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1119                         return false;\r
1120                 }\r
1121                 int layer = _ttoi(_parameters[1].c_str());\r
1122                 auto result = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->invoke(layer, _parameters2[2]);\r
1123                 replyString << result << TEXT("\r\n"); \r
1124         }\r
1125         else \r
1126         {\r
1127                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
1128                 return true;\r
1129         }\r
1130         \r
1131         SetReplyString(replyString.str());\r
1132         return true;\r
1133 }\r
1134 \r
1135 bool CGCommand::DoExecuteInfo() \r
1136 {\r
1137         std::wstringstream replyString;\r
1138         replyString << TEXT("201 CG OK\r\n");\r
1139 \r
1140         if(_parameters.size() > 1)\r
1141         {\r
1142                 if(!ValidateLayer(_parameters[1]))\r
1143                 {\r
1144                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
1145                         return false;\r
1146                 }\r
1147 \r
1148                 int layer = _ttoi(_parameters[1].c_str());\r
1149                 auto desc = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->description(layer);\r
1150                 \r
1151                 replyString << desc << TEXT("\r\n"); \r
1152         }\r
1153         else \r
1154         {\r
1155                 auto info = flash::get_default_cg_producer(safe_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_producer::DEFAULT_LAYER))->info();\r
1156                 replyString << info << TEXT("\r\n"); \r
1157         }       \r
1158 \r
1159         SetReplyString(replyString.str());\r
1160         return true;\r
1161 }\r
1162 \r
1163 bool DataCommand::DoExecute()\r
1164 {\r
1165         std::wstring command = _parameters[0];\r
1166         if(command == TEXT("STORE"))\r
1167                 return DoExecuteStore();\r
1168         else if(command == TEXT("RETRIEVE"))\r
1169                 return DoExecuteRetrieve();\r
1170         else if(command == TEXT("LIST"))\r
1171                 return DoExecuteList();\r
1172 \r
1173         SetReplyString(TEXT("403 DATA ERROR\r\n"));\r
1174         return false;\r
1175 }\r
1176 \r
1177 bool DataCommand::DoExecuteStore() \r
1178 {\r
1179         if(_parameters.size() < 3) \r
1180         {\r
1181                 SetReplyString(TEXT("402 DATA STORE ERROR\r\n"));\r
1182                 return false;\r
1183         }\r
1184 \r
1185         std::wstring filename = env::data_folder();\r
1186         filename.append(_parameters[1]);\r
1187         filename.append(TEXT(".ftd"));\r
1188 \r
1189         std::wofstream datafile(filename.c_str());\r
1190         if(!datafile) \r
1191         {\r
1192                 SetReplyString(TEXT("501 DATA STORE FAILED\r\n"));\r
1193                 return false;\r
1194         }\r
1195 \r
1196         datafile << _parameters2[2];\r
1197         datafile.close();\r
1198 \r
1199         std::wstring replyString = TEXT("202 DATA STORE OK\r\n");\r
1200         SetReplyString(replyString);\r
1201         return true;\r
1202 }\r
1203 \r
1204 bool DataCommand::DoExecuteRetrieve() \r
1205 {\r
1206         if(_parameters.size() < 2) \r
1207         {\r
1208                 SetReplyString(TEXT("402 DATA RETRIEVE ERROR\r\n"));\r
1209                 return false;\r
1210         }\r
1211 \r
1212         std::wstring filename = env::data_folder();\r
1213         filename.append(_parameters[1]);\r
1214         filename.append(TEXT(".ftd"));\r
1215 \r
1216         std::wifstream datafile(filename.c_str());\r
1217         if(!datafile) \r
1218         {\r
1219                 SetReplyString(TEXT("404 DATA RETRIEVE ERROR\r\n"));\r
1220                 return false;\r
1221         }\r
1222 \r
1223         std::wstringstream reply(TEXT("201 DATA RETRIEVE OK\r\n"));\r
1224         std::wstring line;\r
1225         bool bFirstLine = true;\r
1226         while(std::getline(datafile, line))\r
1227         {\r
1228                 if(!bFirstLine)\r
1229                         reply << "\\n";\r
1230                 else\r
1231                         bFirstLine = false;\r
1232 \r
1233                 reply << line;\r
1234         }\r
1235         datafile.close();\r
1236 \r
1237         reply << "\r\n";\r
1238         SetReplyString(reply.str());\r
1239         return true;\r
1240 }\r
1241 \r
1242 bool DataCommand::DoExecuteList() \r
1243 {\r
1244         std::wstringstream replyString;\r
1245         replyString << TEXT("200 DATA LIST OK\r\n");\r
1246 \r
1247         for (boost::filesystem::wrecursive_directory_iterator itr(env::data_folder()), end; itr != end; ++itr)\r
1248         {                       \r
1249                 if(boost::filesystem::is_regular_file(itr->path()))\r
1250                 {\r
1251                         if(!boost::iequals(itr->path().extension(), L".ftd"))\r
1252                                 continue;\r
1253                         \r
1254                         auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::data_folder().size()-1, itr->path().file_string().size()));\r
1255                         \r
1256                         auto str = relativePath.replace_extension(TEXT("")).external_file_string();\r
1257                         if(str[0] == '\\' || str[0] == '/')\r
1258                                 str = std::wstring(str.begin() + 1, str.end());\r
1259 \r
1260                         replyString << str << TEXT("\r\n");     \r
1261                 }\r
1262         }\r
1263         \r
1264         replyString << TEXT("\r\n");\r
1265 \r
1266         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1267         return true;\r
1268 }\r
1269 \r
1270 bool CinfCommand::DoExecute()\r
1271 {\r
1272         std::wstringstream replyString;\r
1273         \r
1274         try\r
1275         {\r
1276                 std::wstring info;\r
1277                 for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)\r
1278                 {\r
1279                         auto path = itr->path();\r
1280                         auto file = path.replace_extension(L"").filename();\r
1281                         if(boost::iequals(file, _parameters.at(0)))\r
1282                                 info += MediaInfo(itr->path()) + L"\r\n";\r
1283                 }\r
1284 \r
1285                 if(info.empty())\r
1286                 {\r
1287                         SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
1288                         return false;\r
1289                 }\r
1290                 replyString << TEXT("200 INFO OK\r\n");\r
1291                 replyString << info << "\r\n";\r
1292         }\r
1293         catch(...)\r
1294         {\r
1295                 SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
1296                 return false;\r
1297         }\r
1298         \r
1299         SetReplyString(replyString.str());\r
1300         return true;\r
1301 }\r
1302 \r
1303 void GenerateChannelInfo(int index, const safe_ptr<core::video_channel>& pChannel, std::wstringstream& replyString)\r
1304 {\r
1305         replyString << index+1 << TEXT(" ") << pChannel->get_video_format_desc().name << TEXT(" PLAYING") << TEXT("\r\n");\r
1306 }\r
1307 \r
1308 bool InfoCommand::DoExecute()\r
1309 {\r
1310         try\r
1311         {\r
1312                 std::wstringstream replyString;\r
1313                 if(_parameters.size() >= 1)\r
1314                 {\r
1315                         int channelIndex = _ttoi(_parameters.at(0).c_str())-1;\r
1316                         replyString << TEXT("201 INFO OK\r\n");\r
1317                         GenerateChannelInfo(channelIndex, channels_.at(channelIndex), replyString);\r
1318                 }\r
1319                 else\r
1320                 {\r
1321                         replyString << TEXT("200 INFO OK\r\n");\r
1322                         for(size_t n = 0; n < channels_.size(); ++n)\r
1323                                 GenerateChannelInfo(n, channels_[n], replyString);\r
1324                         replyString << TEXT("\r\n");\r
1325                 }\r
1326                 SetReplyString(replyString.str());\r
1327         }\r
1328         catch(...)\r
1329         {\r
1330                 SetReplyString(TEXT("401 INFO ERROR\r\n"));\r
1331                 return false;\r
1332         }\r
1333 \r
1334         return true;\r
1335 }\r
1336 \r
1337 bool ClsCommand::DoExecute()\r
1338 {\r
1339 /*\r
1340                 wav = audio\r
1341                 mp3 = audio\r
1342                 swf     = movie\r
1343                 dv  = movie\r
1344                 tga = still\r
1345                 col = still\r
1346         */\r
1347         std::wstringstream replyString;\r
1348         replyString << TEXT("200 CLS OK\r\n");\r
1349         replyString << ListMedia();\r
1350         replyString << TEXT("\r\n");\r
1351         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1352         return true;\r
1353 }\r
1354 \r
1355 bool TlsCommand::DoExecute()\r
1356 {\r
1357         std::wstringstream replyString;\r
1358         replyString << TEXT("200 TLS OK\r\n");\r
1359 \r
1360         replyString << ListTemplates();\r
1361         replyString << TEXT("\r\n");\r
1362 \r
1363         SetReplyString(replyString.str());\r
1364         return true;\r
1365 }\r
1366 \r
1367 bool VersionCommand::DoExecute()\r
1368 {\r
1369         std::wstring replyString = TEXT("201 VERSION OK\r\n SERVER: ") + env::version() + TEXT("\r\n");\r
1370 \r
1371         if(_parameters.size() > 0)\r
1372         {\r
1373                 if(_parameters[0] == L"FLASH")\r
1374                         replyString = TEXT("201 VERSION OK\r\n FLASH: ") + flash::get_version() + TEXT("\r\n");\r
1375                 else if(_parameters[0] == L"TEMPLATEHOST")\r
1376                         replyString = TEXT("201 VERSION OK\r\n TEMPLATEHOST: ") + flash::get_cg_version() + TEXT("\r\n");\r
1377                 else if(_parameters[0] != L"SERVER")\r
1378                         replyString = TEXT("403 VERSION ERROR\r\n");\r
1379         }\r
1380 \r
1381         SetReplyString(replyString);\r
1382         return true;\r
1383 }\r
1384 \r
1385 bool ByeCommand::DoExecute()\r
1386 {\r
1387         GetClientInfo()->Disconnect();\r
1388         return true;\r
1389 }\r
1390 \r
1391 bool SetCommand::DoExecute()\r
1392 {\r
1393         std::wstring name = _parameters[0];\r
1394         std::transform(name.begin(), name.end(), name.begin(), toupper);\r
1395 \r
1396         std::wstring value = _parameters[1];\r
1397         std::transform(value.begin(), value.end(), value.begin(), toupper);\r
1398 \r
1399         if(name == TEXT("MODE"))\r
1400         {\r
1401                 auto format_desc = core::video_format_desc::get(value);\r
1402                 if(format_desc.format != core::video_format::invalid)\r
1403                 {\r
1404                         GetChannel()->set_video_format_desc(format_desc);\r
1405                         SetReplyString(TEXT("202 SET MODE OK\r\n"));\r
1406                 }\r
1407                 else\r
1408                         SetReplyString(TEXT("501 SET MODE FAILED\r\n"));\r
1409         }\r
1410         else\r
1411         {\r
1412                 this->SetReplyString(TEXT("403 SET ERROR\r\n"));\r
1413         }\r
1414 \r
1415         return true;\r
1416 }\r
1417 \r
1418 \r
1419 }       //namespace amcp\r
1420 }}      //namespace caspar