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