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