]> git.sesse.net Git - casparcg/blob - protocol/amcp/AMCPCommandsImpl.cpp
2.0.0.2: Mayor solution reconfiguration.
[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 #include "AMCPCommandsImpl.h"\r
24 #include "AMCPProtocolStrategy.h"\r
25 \r
26 #include <common/env.h>\r
27 \r
28 #include <core/producer/frame_producer.h>\r
29 #include <core/video_format.h>\r
30 #include <modules/flash/flash.h>\r
31 #include <modules/flash/producer/flash_producer.h>\r
32 #include <modules/flash/producer/cg_producer.h>\r
33 #include <core/producer/transition/transition_producer.h>\r
34 \r
35 #include <mixer/image/image_transform.h>\r
36 #include <mixer/audio/audio_transform.h>\r
37 \r
38 #include <algorithm>\r
39 #include <locale>\r
40 #include <fstream>\r
41 #include <cctype>\r
42 #include <io.h>\r
43 \r
44 #include <boost/date_time/posix_time/posix_time.hpp>\r
45 #include <boost/lexical_cast.hpp>\r
46 #include <boost/algorithm/string.hpp>\r
47 #include <boost/filesystem.hpp>\r
48 \r
49 #if defined(_MSC_VER)\r
50 #pragma warning (push, 1) // TODO: Legacy code, just disable warnings\r
51 #endif\r
52 \r
53 /* Return codes\r
54 \r
55 100 [action]                    Information om att något har hänt  \r
56 101 [action]                    Information om att något har hänt, en rad data skickas  \r
57 \r
58 202 [kommando] OK               Kommandot har utförts  \r
59 201 [kommando] OK               Kommandot har utförts, och en rad data skickas tillbaka  \r
60 200 [kommando] OK               Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad  \r
61 \r
62 400 ERROR                               Kommandot kunde inte förstås  \r
63 401 [kommando] ERROR    Ogiltig kanal  \r
64 402 [kommando] ERROR    Parameter saknas  \r
65 403 [kommando] ERROR    Ogiltig parameter  \r
66 404 [kommando] ERROR    Mediafilen hittades inte  \r
67 \r
68 500 FAILED                              Internt configurationfel  \r
69 501 [kommando] FAILED   Internt configurationfel  \r
70 502 [kommando] FAILED   Oläslig mediafil  \r
71 \r
72 600 [kommando] FAILED   funktion ej implementerad\r
73 */\r
74 \r
75 namespace caspar { namespace protocol {\r
76 \r
77 using namespace core;\r
78 \r
79 std::wstring ListMedia()\r
80 {       \r
81         std::wstringstream replyString;\r
82         for (boost::filesystem::wrecursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr)\r
83         {                       \r
84                 if(boost::filesystem::is_regular_file(itr->path()))\r
85                 {\r
86                         std::wstring clipttype = TEXT(" N/A ");\r
87                         std::wstring extension = boost::to_upper_copy(itr->path().extension());\r
88                         if(extension == TEXT(".TGA") || extension == TEXT(".COL"))\r
89                                 clipttype = TEXT(" STILL ");\r
90                         else if(extension == TEXT(".SWF") || extension == TEXT(".DV") || extension == TEXT(".MOV") || extension == TEXT(".MPG") || \r
91                                         extension == TEXT(".AVI") || extension == TEXT(".FLV") || extension == TEXT(".F4V") || extension == TEXT(".MP4"))\r
92                                 clipttype = TEXT(" MOVIE ");\r
93                         else if(extension == TEXT(".WAV") || extension == TEXT(".MP3"))\r
94                                 clipttype = TEXT(" STILL ");\r
95 \r
96                         if(clipttype != TEXT(" N/A "))\r
97                         {               \r
98                                 auto is_not_digit = [](char c){ return std::isdigit(c) == 0; };\r
99 \r
100                                 auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::media_folder().size()-1, itr->path().file_string().size()));\r
101 \r
102                                 auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(itr->path())));\r
103                                 writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end());\r
104                                 auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
105 \r
106                                 auto sizeStr = boost::lexical_cast<std::wstring>(boost::filesystem::file_size(itr->path()));\r
107                                 sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end());\r
108                                 auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
109 \r
110                                 replyString << TEXT("\"") << relativePath.replace_extension(TEXT(""))\r
111                                                         << TEXT("\" ") << clipttype \r
112                                                         << TEXT(" ") << sizeStr\r
113                                                         << TEXT(" ") << writeTimeWStr\r
114                                                         << TEXT("\r\n");\r
115                         }       \r
116                 }\r
117         }\r
118         return boost::to_upper_copy(replyString.str());\r
119 }\r
120 \r
121 std::wstring ListTemplates() \r
122 {\r
123         std::wstringstream replyString;\r
124 \r
125         for (boost::filesystem::wrecursive_directory_iterator itr(env::template_folder()), end; itr != end; ++itr)\r
126         {               \r
127                 if(boost::filesystem::is_regular_file(itr->path()) && itr->path().extension() == L".ft")\r
128                 {\r
129                         auto relativePath = boost::filesystem::wpath(itr->path().file_string().substr(env::template_folder().size()-1, itr->path().file_string().size()));\r
130 \r
131                         auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(itr->path())));\r
132                         writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), [](char c){ return std::isdigit(c) == 0;}), writeTimeStr.end());\r
133                         auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end());\r
134 \r
135                         auto sizeStr = boost::lexical_cast<std::string>(boost::filesystem::file_size(itr->path()));\r
136                         sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), [](char c){ return std::isdigit(c) == 0;}), sizeStr.end());\r
137 \r
138                         auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end());\r
139                         \r
140                         replyString << TEXT("\"") << relativePath.replace_extension(TEXT(""))\r
141                                                 << TEXT("\" ") << sizeWStr\r
142                                                 << TEXT(" ") << writeTimeWStr\r
143                                                 << TEXT("\r\n");                \r
144                 }\r
145         }\r
146         return boost::to_upper_copy(replyString.str());\r
147 }\r
148 \r
149 namespace amcp {\r
150         \r
151 AMCPCommand::AMCPCommand() : channelIndex_(0), scheduling_(Default), layerIndex_(-1)\r
152 {}\r
153 \r
154 void AMCPCommand::SendReply()\r
155 {\r
156         if(!pClientInfo_) \r
157                 return;\r
158 \r
159         if(replyString_.empty())\r
160                 return;\r
161         pClientInfo_->Send(replyString_);\r
162 }\r
163 \r
164 void AMCPCommand::Clear() \r
165 {\r
166         pChannel_->producer().clear();\r
167         pClientInfo_.reset();\r
168         channelIndex_ = 0;\r
169         _parameters.clear();\r
170 }\r
171 \r
172 bool MixerCommand::DoExecute()\r
173 {       \r
174         //Perform loading of the clip\r
175         try\r
176         {       \r
177                 if(_parameters[0] == L"VIDEO")\r
178                 {\r
179                         if(_parameters[1] == L"OPACITY")\r
180                         {\r
181                                 int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
182                                 double value = boost::lexical_cast<double>(_parameters.at(2));\r
183                         \r
184                                 auto transform = [=](image_transform transform) -> image_transform\r
185                                 {\r
186                                         transform.set_opacity(value);\r
187                                         return transform;                                       \r
188                                 };\r
189 \r
190                                 int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
191                                 if(layer != std::numeric_limits<int>::min())                                    \r
192                                         GetChannel()->mixer().apply_image_transform(GetLayerIndex(), transform, duration);\r
193                                 else\r
194                                         GetChannel()->mixer().apply_image_transform(transform, duration);\r
195                         }\r
196                         else if(_parameters[1] == L"GAIN")\r
197                         {\r
198                                 int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
199                                 double value = boost::lexical_cast<double>(_parameters.at(2));\r
200                                 \r
201                                 auto transform = [=](image_transform transform) -> image_transform\r
202                                 {\r
203                                         transform.set_gain(value);\r
204                                         return transform;                                       \r
205                                 };\r
206 \r
207                                 int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
208                                 if(layer != std::numeric_limits<int>::min())\r
209                                         GetChannel()->mixer().apply_image_transform(GetLayerIndex(), transform, duration);\r
210                                 else\r
211                                         GetChannel()->mixer().apply_image_transform(transform, duration);\r
212                         }\r
213                         else if(_parameters[1] == L"FILL_RECT")\r
214                         {\r
215                                 int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
216                                 double x        = boost::lexical_cast<double>(_parameters.at(2));\r
217                                 double y        = boost::lexical_cast<double>(_parameters.at(3));\r
218                                 double x_s      = boost::lexical_cast<double>(_parameters.at(4));\r
219                                 double y_s      = boost::lexical_cast<double>(_parameters.at(5));\r
220 \r
221                                 auto transform = [=](image_transform transform) -> image_transform\r
222                                 {\r
223                                         transform.set_fill_translation(x, y);\r
224                                         transform.set_fill_scale(x_s, y_s);\r
225                                         transform.set_key_translation(x, y);\r
226                                         transform.set_key_scale(x_s, y_s);\r
227                                         return transform;\r
228                                 };\r
229 \r
230                                 int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
231                                 if(layer != std::numeric_limits<int>::min())\r
232                                         GetChannel()->mixer().apply_image_transform(GetLayerIndex(), transform, duration);\r
233                                 else\r
234                                         GetChannel()->mixer().apply_image_transform(transform, duration);\r
235                         }\r
236                         else if(_parameters[1] == L"KEY_RECT")\r
237                         {\r
238                                 int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
239                                 double x        = boost::lexical_cast<double>(_parameters.at(2));\r
240                                 double y        = boost::lexical_cast<double>(_parameters.at(3));\r
241                                 double x_s      = boost::lexical_cast<double>(_parameters.at(4));\r
242                                 double y_s      = boost::lexical_cast<double>(_parameters.at(5));\r
243 \r
244                                 auto transform = [=](image_transform transform) -> image_transform\r
245                                 {\r
246                                         transform.set_key_translation(x, y);\r
247                                         transform.set_key_scale(x_s, y_s);\r
248                                         return transform;\r
249                                 };\r
250 \r
251                                 int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
252                                 if(layer != std::numeric_limits<int>::min())\r
253                                         GetChannel()->mixer().apply_image_transform(GetLayerIndex(), transform, duration);\r
254                                 else\r
255                                         GetChannel()->mixer().apply_image_transform(transform, duration);\r
256                         }\r
257                         else if(_parameters[1] == L"GRID")\r
258                         {\r
259                                 int n = boost::lexical_cast<int>(_parameters.at(2));\r
260                                 double delta = 1.0/static_cast<double>(n);\r
261                                 for(int x = 0; x < n; ++x)\r
262                                 {\r
263                                         for(int y = 0; y < n; ++y)\r
264                                         {\r
265                                                 int index = x+y*n;\r
266                                                 auto transform = [=](image_transform transform) -> image_transform\r
267                                                 {                               \r
268                                                         transform.set_fill_translation(x*delta, y*delta);\r
269                                                         transform.set_fill_scale(delta, delta);                 \r
270                                                         transform.set_key_translation(x*delta, y*delta);\r
271                                                         transform.set_key_scale(delta, delta);\r
272                                                         return transform;\r
273                                                 };\r
274                                                 GetChannel()->mixer().apply_image_transform(index, transform, 0);\r
275                                         }\r
276                                 }\r
277                         }\r
278                         else if(_parameters[1] == L"RESET")\r
279                         {\r
280                                 GetChannel()->mixer().set_image_transform(GetLayerIndex(), image_transform());\r
281                         }\r
282                 }\r
283                 else if(_parameters[0] == L"AUDIO")\r
284                 {\r
285                         if(_parameters[1] == L"GAIN")\r
286                         {\r
287                                 int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
288                                 double value = boost::lexical_cast<double>(_parameters[2]);\r
289 \r
290                                 auto transform = [=](audio_transform transform) -> audio_transform\r
291                                 {\r
292                                         transform.set_gain(value);\r
293                                         return transform;\r
294                                 };\r
295                                 \r
296                                 int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
297                                 if(layer != std::numeric_limits<int>::min())\r
298                                         GetChannel()->mixer().apply_audio_transform(GetLayerIndex(), transform, duration);\r
299                                 else\r
300                                         GetChannel()->mixer().apply_audio_transform(transform, duration);\r
301                         }\r
302                         else if(_parameters[1] == L"RESET")\r
303                         {\r
304                                 GetChannel()->mixer().set_audio_transform(GetLayerIndex(), audio_transform());\r
305                         }\r
306                 }\r
307                 else if(_parameters[0] == L"RESET")\r
308                 {\r
309                         GetChannel()->mixer().set_image_transform(GetLayerIndex(), image_transform());\r
310                         GetChannel()->mixer().set_audio_transform(GetLayerIndex(), audio_transform());\r
311                 }\r
312         \r
313                 SetReplyString(TEXT("202 MIXER OK\r\n"));\r
314 \r
315                 return true;\r
316         }\r
317         catch(file_not_found&)\r
318         {\r
319                 CASPAR_LOG_CURRENT_EXCEPTION();\r
320                 SetReplyString(TEXT("404 MIXER ERROR\r\n"));\r
321                 return false;\r
322         }\r
323         catch(...)\r
324         {\r
325                 CASPAR_LOG_CURRENT_EXCEPTION();\r
326                 SetReplyString(TEXT("502 MIXER FAILED\r\n"));\r
327                 return false;\r
328         }\r
329 }\r
330 \r
331 bool SwapCommand::DoExecute()\r
332 {       \r
333         //Perform loading of the clip\r
334         try\r
335         {\r
336                 if(GetLayerIndex(-1) != -1)\r
337                 {\r
338                         std::vector<std::string> strs;\r
339                         boost::split(strs, _parameters[0], boost::is_any_of("-"));\r
340                         \r
341                         auto ch1 = GetChannel();\r
342                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);\r
343 \r
344                         int l1 = GetLayerIndex();\r
345                         int l2 = boost::lexical_cast<int>(strs.at(1));\r
346 \r
347                         ch1->producer().swap_layer(l1, l2, ch2->producer());\r
348                 }\r
349                 else\r
350                 {\r
351                         auto ch1 = GetChannel();\r
352                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(_parameters[0])-1);\r
353                         ch1->producer().swap(ch2->producer());\r
354                 }\r
355 \r
356                 CASPAR_LOG(info) << "Swapped successfully";\r
357 \r
358                 SetReplyString(TEXT("202 SWAP OK\r\n"));\r
359 \r
360                 return true;\r
361         }\r
362         catch(file_not_found&)\r
363         {\r
364                 CASPAR_LOG_CURRENT_EXCEPTION();\r
365                 SetReplyString(TEXT("404 SWAP ERROR\r\n"));\r
366                 return false;\r
367         }\r
368         catch(...)\r
369         {\r
370                 CASPAR_LOG_CURRENT_EXCEPTION();\r
371                 SetReplyString(TEXT("502 SWAP FAILED\r\n"));\r
372                 return false;\r
373         }\r
374 }\r
375 \r
376 bool AddCommand::DoExecute()\r
377 {       \r
378         //Perform loading of the clip\r
379         try\r
380         {\r
381                 GetChannel()->consumer().add(GetLayerIndex(), create_consumer(_parameters));\r
382         \r
383                 CASPAR_LOG(info) << "Added " <<  _parameters[0] << TEXT(" successfully");\r
384 \r
385                 SetReplyString(TEXT("202 ADD OK\r\n"));\r
386 \r
387                 return true;\r
388         }\r
389         catch(file_not_found&)\r
390         {\r
391                 CASPAR_LOG_CURRENT_EXCEPTION();\r
392                 SetReplyString(TEXT("404 ADD ERROR\r\n"));\r
393                 return false;\r
394         }\r
395         catch(...)\r
396         {\r
397                 CASPAR_LOG_CURRENT_EXCEPTION();\r
398                 SetReplyString(TEXT("502 ADD FAILED\r\n"));\r
399                 return false;\r
400         }\r
401 }\r
402 \r
403 bool RemoveCommand::DoExecute()\r
404 {       \r
405         //Perform loading of the clip\r
406         try\r
407         {\r
408                 GetChannel()->consumer().remove(GetLayerIndex());\r
409 \r
410                 SetReplyString(TEXT("202 REMOVE OK\r\n"));\r
411 \r
412                 return true;\r
413         }\r
414         catch(file_not_found&)\r
415         {\r
416                 CASPAR_LOG_CURRENT_EXCEPTION();\r
417                 SetReplyString(TEXT("404 REMOVE ERROR\r\n"));\r
418                 return false;\r
419         }\r
420         catch(...)\r
421         {\r
422                 CASPAR_LOG_CURRENT_EXCEPTION();\r
423                 SetReplyString(TEXT("502 REMOVE FAILED\r\n"));\r
424                 return false;\r
425         }\r
426 }\r
427 \r
428 bool LoadCommand::DoExecute()\r
429 {       \r
430         //Perform loading of the clip\r
431         try\r
432         {\r
433                 _parameters[0] = _parameters[0];\r
434                 auto pFP = create_producer(_parameters);                \r
435                 GetChannel()->producer().load(GetLayerIndex(), pFP, false, true);\r
436         \r
437                 CASPAR_LOG(info) << "Loaded " <<  _parameters[0] << TEXT(" successfully");\r
438 \r
439                 SetReplyString(TEXT("202 LOAD OK\r\n"));\r
440 \r
441                 return true;\r
442         }\r
443         catch(file_not_found&)\r
444         {\r
445                 CASPAR_LOG_CURRENT_EXCEPTION();\r
446                 SetReplyString(TEXT("404 LOADBG ERROR\r\n"));\r
447                 return false;\r
448         }\r
449         catch(...)\r
450         {\r
451                 CASPAR_LOG_CURRENT_EXCEPTION();\r
452                 SetReplyString(TEXT("502 LOADBG FAILED\r\n"));\r
453                 return false;\r
454         }\r
455 }\r
456 \r
457 bool LoadbgCommand::DoExecute()\r
458 {\r
459         transition_info transitionInfo;\r
460         \r
461         bool bLoop = false;\r
462         unsigned short transitionParameterIndex = 1;\r
463 \r
464         if(_parameters.size() > 1 && _parameters[1] == TEXT("LOOP"))\r
465                 ++transitionParameterIndex;\r
466 \r
467         //Setup transition info\r
468         if(_parameters.size() > transitionParameterIndex)       //type\r
469         {\r
470                 std::wstring transitionType = _parameters[transitionParameterIndex];\r
471 \r
472                 if(transitionType == TEXT("CUT"))\r
473                         transitionInfo.type = transition::cut;\r
474                 else if(transitionType == TEXT("MIX"))\r
475                         transitionInfo.type = transition::mix;\r
476                 else if(transitionType == TEXT("PUSH"))\r
477                         transitionInfo.type = transition::push;\r
478                 else if(transitionType == TEXT("SLIDE"))\r
479                         transitionInfo.type = transition::slide;\r
480                 else if(transitionType == TEXT("WIPE"))\r
481                         transitionInfo.type = transition::wipe;\r
482 \r
483                 if(_parameters.size() > static_cast<unsigned short>(transitionParameterIndex+1))        //duration\r
484                 {\r
485                         int duration = _ttoi(_parameters[transitionParameterIndex+1].c_str());\r
486                         if(duration > 0)\r
487                                 transitionInfo.duration = duration;\r
488 \r
489                         if(_parameters.size() > static_cast<unsigned short>(transitionParameterIndex+2))        //direction\r
490                         {\r
491                                 std::wstring direction = _parameters[transitionParameterIndex+2];\r
492 \r
493                                 if(direction == TEXT("FROMLEFT"))\r
494                                         transitionInfo.direction = transition_direction::from_left;\r
495                                 else if(direction == TEXT("FROMRIGHT"))\r
496                                         transitionInfo.direction = transition_direction::from_right;\r
497                                 else if(direction == TEXT("LEFT"))\r
498                                         transitionInfo.direction = transition_direction::from_right;\r
499                                 else if(direction == TEXT("RIGHT"))\r
500                                         transitionInfo.direction = transition_direction::from_left;\r
501                         }\r
502                 }\r
503         }\r
504 \r
505         //Perform loading of the clip\r
506         try\r
507         {\r
508                 _parameters[0] = _parameters[0];\r
509                 auto pFP = create_producer(_parameters);\r
510                 if(pFP == frame_producer::empty())\r
511                         BOOST_THROW_EXCEPTION(file_not_found() << msg_info(_parameters.size() > 0 ? narrow(_parameters[0]) : ""));\r
512 \r
513                 auto pFP2 = make_safe<core::transition_producer>(pFP, transitionInfo);\r
514                 bool autoPlay = std::find(_parameters.begin(), _parameters.end(), TEXT("AUTOPLAY")) != _parameters.end();\r
515                 GetChannel()->producer().load(GetLayerIndex(), pFP2, autoPlay); // TODO: LOOP\r
516         \r
517                 CASPAR_LOG(info) << "Loaded " << _parameters[0] << TEXT(" successfully to background");\r
518                 SetReplyString(TEXT("202 LOADBG OK\r\n"));\r
519 \r
520                 return true;\r
521         }\r
522         catch(file_not_found&)\r
523         {\r
524                 CASPAR_LOG_CURRENT_EXCEPTION();\r
525                 SetReplyString(TEXT("404 LOADBG ERROR\r\n"));\r
526                 return false;\r
527         }\r
528         catch(...)\r
529         {\r
530                 CASPAR_LOG_CURRENT_EXCEPTION();\r
531                 SetReplyString(TEXT("502 LOADBG FAILED\r\n"));\r
532                 return false;\r
533         }\r
534 }\r
535 \r
536 bool PauseCommand::DoExecute()\r
537 {\r
538         try\r
539         {\r
540                 GetChannel()->producer().pause(GetLayerIndex());\r
541                 SetReplyString(TEXT("202 PAUSE OK\r\n"));\r
542                 return true;\r
543         }\r
544         catch(...)\r
545         {\r
546                 SetReplyString(TEXT("501 PAUSE FAILED\r\n"));\r
547         }\r
548 \r
549         return false;\r
550 }\r
551 \r
552 bool PlayCommand::DoExecute()\r
553 {\r
554         try\r
555         {\r
556                 GetChannel()->producer().play(GetLayerIndex());\r
557                 SetReplyString(TEXT("202 PLAY OK\r\n"));\r
558                 return true;\r
559         }\r
560         catch(...)\r
561         {\r
562                 SetReplyString(TEXT("501 PLAY FAILED\r\n"));\r
563         }\r
564 \r
565         return false;\r
566 }\r
567 \r
568 bool StopCommand::DoExecute()\r
569 {\r
570         try\r
571         {\r
572                 GetChannel()->producer().stop(GetLayerIndex());\r
573                 SetReplyString(TEXT("202 STOP OK\r\n"));\r
574                 return true;\r
575         }\r
576         catch(...)\r
577         {\r
578                 SetReplyString(TEXT("501 STOP FAILED\r\n"));\r
579         }\r
580 \r
581         return false;\r
582 }\r
583 \r
584 bool ClearCommand::DoExecute()\r
585 {\r
586         int index = GetLayerIndex(std::numeric_limits<int>::min());\r
587         if(index != std::numeric_limits<int>::min())\r
588                 GetChannel()->producer().clear(index);\r
589         else\r
590                 GetChannel()->producer().clear();\r
591                 \r
592         SetReplyString(TEXT("202 CLEAR OK\r\n"));\r
593 \r
594         return true;\r
595 }\r
596 \r
597 bool CGCommand::DoExecute()\r
598 {\r
599         std::wstring command = _parameters[0];\r
600         if(command == TEXT("ADD"))\r
601                 return DoExecuteAdd();\r
602         else if(command == TEXT("PLAY"))\r
603                 return DoExecutePlay();\r
604         else if(command == TEXT("STOP"))\r
605                 return DoExecuteStop();\r
606         else if(command == TEXT("NEXT"))\r
607                 return DoExecuteNext();\r
608         else if(command == TEXT("REMOVE"))\r
609                 return DoExecuteRemove();\r
610         else if(command == TEXT("CLEAR"))\r
611                 return DoExecuteClear();\r
612         else if(command == TEXT("UPDATE"))\r
613                 return DoExecuteUpdate();\r
614         else if(command == TEXT("INVOKE"))\r
615                 return DoExecuteInvoke();\r
616         else if(command == TEXT("INFO"))\r
617                 return DoExecuteInfo();\r
618 \r
619         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
620         return false;\r
621 }\r
622 \r
623 bool CGCommand::ValidateLayer(const std::wstring& layerstring) {\r
624         int length = layerstring.length();\r
625         for(int i = 0; i < length; ++i) {\r
626                 if(!_istdigit(layerstring[i])) {\r
627                         return false;\r
628                 }\r
629         }\r
630 \r
631         return true;\r
632 }\r
633 \r
634 bool CGCommand::DoExecuteAdd() {\r
635         //CG 1 ADD 0 "template_folder/templatename" [STARTLABEL] 0/1 [DATA]\r
636 \r
637         int layer = 0;                          //_parameters[1]\r
638 //      std::wstring templateName;      //_parameters[2]\r
639         std::wstring label;             //_parameters[3]\r
640         bool bDoStart = false;          //_parameters[3] alt. _parameters[4]\r
641 //      std::wstring data;                      //_parameters[4] alt. _parameters[5]\r
642 \r
643         if(_parameters.size() < 4) \r
644         {\r
645                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
646                 return false;\r
647         }\r
648         unsigned int dataIndex = 4;\r
649 \r
650         if(!ValidateLayer(_parameters[1])) \r
651         {\r
652                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
653                 return false;\r
654         }\r
655 \r
656         layer = _ttoi(_parameters[1].c_str());\r
657 \r
658         if(_parameters[3].length() > 1) \r
659         {       //read label\r
660                 label = _parameters[3];\r
661                 ++dataIndex;\r
662 \r
663                 if(_parameters.size() > 4 && _parameters[4].length() > 0)       //read play-on-load-flag\r
664                         bDoStart = (_parameters[4][0]==TEXT('1')) ? true : false;\r
665                 else \r
666                 {\r
667                         SetReplyString(TEXT("402 CG ERROR\r\n"));\r
668                         return false;\r
669                 }\r
670         }\r
671         else if(_parameters[3].length() > 0) {  //read play-on-load-flag\r
672                 bDoStart = (_parameters[3][0]==TEXT('1')) ? true : false;\r
673         }\r
674         else \r
675         {\r
676                 SetReplyString(TEXT("403 CG ERROR\r\n"));\r
677                 return false;\r
678         }\r
679 \r
680         const TCHAR* pDataString = 0;\r
681         std::wstringstream data;\r
682         std::wstring dataFromFile;\r
683         if(_parameters.size() > dataIndex) \r
684         {       //read data\r
685                 const std::wstring& dataString = _parameters[dataIndex];\r
686 \r
687                 if(dataString[0] == TEXT('<')) //the data is an XML-string\r
688                         pDataString = dataString.c_str();\r
689                 else \r
690                 {\r
691                         //The data is not an XML-string, it must be a filename\r
692                         std::wstring filename = env::data_folder();\r
693                         filename.append(dataString);\r
694                         filename.append(TEXT(".ftd"));\r
695 \r
696                         //open file\r
697                         std::wifstream datafile(filename.c_str());\r
698                         if(datafile) \r
699                         {\r
700                                 //read all data\r
701                                 data << datafile.rdbuf();\r
702                                 datafile.close();\r
703 \r
704                                 //extract data to _parameters\r
705                                 dataFromFile = data.str();\r
706                                 pDataString = dataFromFile.c_str();\r
707                         }\r
708                 }\r
709         }\r
710 \r
711         std::wstring fullFilename = flash_producer::find_template(env::template_folder() + _parameters[2]);\r
712         if(!fullFilename.empty())\r
713         {\r
714                 std::wstring extension = boost::filesystem::wpath(fullFilename).extension();\r
715                 std::wstring filename = _parameters[2];\r
716                 filename.append(extension);\r
717 \r
718                 get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->add(layer, filename, bDoStart, label, (pDataString!=0) ? pDataString : TEXT(""));\r
719                 SetReplyString(TEXT("202 CG OK\r\n"));\r
720         }\r
721         else\r
722         {\r
723                 CASPAR_LOG(warning) << "Could not find template " << _parameters[2];\r
724                 SetReplyString(TEXT("404 CG ERROR\r\n"));\r
725         }\r
726         return true;\r
727 }\r
728 \r
729 bool CGCommand::DoExecutePlay()\r
730 {\r
731         if(_parameters.size() > 1)\r
732         {\r
733                 if(!ValidateLayer(_parameters[1])) \r
734                 {\r
735                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
736                         return false;\r
737                 }\r
738                 int layer = _ttoi(_parameters[1].c_str());\r
739                 get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->play(layer);\r
740         }\r
741         else\r
742         {\r
743                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
744                 return true;\r
745         }\r
746 \r
747         SetReplyString(TEXT("202 CG OK\r\n"));\r
748         return true;\r
749 }\r
750 \r
751 bool CGCommand::DoExecuteStop() \r
752 {\r
753         if(_parameters.size() > 1)\r
754         {\r
755                 if(!ValidateLayer(_parameters[1])) \r
756                 {\r
757                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
758                         return false;\r
759                 }\r
760                 int layer = _ttoi(_parameters[1].c_str());\r
761                 get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->stop(layer, 0);\r
762         }\r
763         else \r
764         {\r
765                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
766                 return true;\r
767         }\r
768 \r
769         SetReplyString(TEXT("202 CG OK\r\n"));\r
770         return true;\r
771 }\r
772 \r
773 bool CGCommand::DoExecuteNext()\r
774 {\r
775         if(_parameters.size() > 1) \r
776         {\r
777                 if(!ValidateLayer(_parameters[1])) \r
778                 {\r
779                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
780                         return false;\r
781                 }\r
782                 int layer = _ttoi(_parameters[1].c_str());\r
783                 get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->next(layer);\r
784         }\r
785         else \r
786         {\r
787                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
788                 return true;\r
789         }\r
790 \r
791         SetReplyString(TEXT("202 CG OK\r\n"));\r
792         return true;\r
793 }\r
794 \r
795 bool CGCommand::DoExecuteRemove() \r
796 {\r
797         if(_parameters.size() > 1) \r
798         {\r
799                 if(!ValidateLayer(_parameters[1])) \r
800                 {\r
801                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
802                         return false;\r
803                 }\r
804                 int layer = _ttoi(_parameters[1].c_str());\r
805                 get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->remove(layer);\r
806         }\r
807         else \r
808         {\r
809                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
810                 return true;\r
811         }\r
812 \r
813         SetReplyString(TEXT("202 CG OK\r\n"));\r
814         return true;\r
815 }\r
816 \r
817 bool CGCommand::DoExecuteClear() \r
818 {\r
819         get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->clear();\r
820         SetReplyString(TEXT("202 CG OK\r\n"));\r
821         return true;\r
822 }\r
823 \r
824 bool CGCommand::DoExecuteUpdate() \r
825 {\r
826         if(_parameters.size() > 2) \r
827         {\r
828                 if(!ValidateLayer(_parameters[1]))\r
829                 {\r
830                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
831                         return false;\r
832                 }\r
833                 int layer = _ttoi(_parameters[1].c_str());\r
834                 //TODO: Implement indirect data loading from file. Same as in Add\r
835                 get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->update(layer, _parameters[2]);\r
836         }\r
837         else \r
838         {\r
839                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
840                 return true;\r
841         }\r
842 \r
843         SetReplyString(TEXT("202 CG OK\r\n"));\r
844         return true;\r
845 }\r
846 \r
847 bool CGCommand::DoExecuteInvoke() \r
848 {\r
849         if(_parameters.size() > 2)\r
850         {\r
851                 if(!ValidateLayer(_parameters[1]))\r
852                 {\r
853                         SetReplyString(TEXT("403 CG ERROR\r\n"));\r
854                         return false;\r
855                 }\r
856                 int layer = _ttoi(_parameters[1].c_str());\r
857                 get_default_cg_producer(safe_ptr<core::channel>(GetChannel()), GetLayerIndex(cg_producer::DEFAULT_LAYER))->invoke(layer, _parameters[2]);\r
858         }\r
859         else \r
860         {\r
861                 SetReplyString(TEXT("402 CG ERROR\r\n"));\r
862                 return true;\r
863         }\r
864 \r
865         SetReplyString(TEXT("202 CG OK\r\n"));\r
866         return true;\r
867 }\r
868 \r
869 bool CGCommand::DoExecuteInfo() \r
870 {\r
871         // TODO\r
872         //get_default_cg_producer(GetChannel())->Info();\r
873         SetReplyString(TEXT("600 CG FAILED\r\n"));\r
874         return true;\r
875 }\r
876 \r
877 bool DataCommand::DoExecute()\r
878 {\r
879         std::wstring command = _parameters[0];\r
880         if(command == TEXT("STORE"))\r
881                 return DoExecuteStore();\r
882         else if(command == TEXT("RETRIEVE"))\r
883                 return DoExecuteRetrieve();\r
884         else if(command == TEXT("LIST"))\r
885                 return DoExecuteList();\r
886 \r
887         SetReplyString(TEXT("403 DATA ERROR\r\n"));\r
888         return false;\r
889 }\r
890 \r
891 bool DataCommand::DoExecuteStore() \r
892 {\r
893         if(_parameters.size() < 3) \r
894         {\r
895                 SetReplyString(TEXT("402 DATA STORE ERROR\r\n"));\r
896                 return false;\r
897         }\r
898 \r
899         std::wstring filename = env::data_folder();\r
900         filename.append(_parameters[1]);\r
901         filename.append(TEXT(".ftd"));\r
902 \r
903         std::wofstream datafile(filename.c_str());\r
904         if(!datafile) \r
905         {\r
906                 SetReplyString(TEXT("501 DATA STORE FAILED\r\n"));\r
907                 return false;\r
908         }\r
909 \r
910         datafile << _parameters[2];\r
911         datafile.close();\r
912 \r
913         std::wstring replyString = TEXT("202 DATA STORE OK\r\n");\r
914         SetReplyString(replyString);\r
915         return true;\r
916 }\r
917 \r
918 bool DataCommand::DoExecuteRetrieve() \r
919 {\r
920         if(_parameters.size() < 2) \r
921         {\r
922                 SetReplyString(TEXT("402 DATA RETRIEVE ERROR\r\n"));\r
923                 return false;\r
924         }\r
925 \r
926         std::wstring filename = env::data_folder();\r
927         filename.append(_parameters[1]);\r
928         filename.append(TEXT(".ftd"));\r
929 \r
930         std::wifstream datafile(filename.c_str());\r
931         if(!datafile) \r
932         {\r
933                 SetReplyString(TEXT("404 DATA RETRIEVE ERROR\r\n"));\r
934                 return false;\r
935         }\r
936 \r
937         std::wstringstream reply(TEXT("201 DATA RETRIEVE OK\r\n"));\r
938         std::wstring line;\r
939         bool bFirstLine = true;\r
940         while(std::getline(datafile, line))\r
941         {\r
942                 if(!bFirstLine)\r
943                         reply << "\\n";\r
944                 else\r
945                         bFirstLine = false;\r
946 \r
947                 reply << line;\r
948         }\r
949         datafile.close();\r
950 \r
951         reply << "\r\n";\r
952         SetReplyString(reply.str());\r
953         return true;\r
954 }\r
955 \r
956 bool DataCommand::DoExecuteList() \r
957 {\r
958         std::wstringstream replyString;\r
959         replyString << TEXT("200 DATA LIST OK\r\n");\r
960         replyString << ListMedia();\r
961         replyString << TEXT("\r\n");\r
962 \r
963         SetReplyString(boost::to_upper_copy(replyString.str()));\r
964         return true;\r
965 }\r
966 \r
967 bool CinfCommand::DoExecute()\r
968 {\r
969         std::wstringstream replyString;\r
970 \r
971         std::wstring filename = _parameters[0];\r
972 \r
973         // TODO:\r
974 \r
975         //FileInfo fileInfo;\r
976 \r
977         //MediaManagerPtr pMediaManager = GetApplication()->FindMediaFile(filename, &fileInfo);\r
978         //if(pMediaManager != 0 && fileInfo.filetype.length() >0)       //File was found\r
979         //{\r
980         //      if(pMediaManager->getFileInfo(&fileInfo))\r
981         //      {\r
982         //              TCHAR numBuffer[32];\r
983         //              _ui64tot_s(fileInfo.size, numBuffer, 32, 10);\r
984 \r
985         //              replyString << TEXT("201 CINF OK\r\n\"") << fileInfo.filename << TEXT("\" ") << fileInfo.type << TEXT("/") << fileInfo.filetype << TEXT("/") << fileInfo.encoding << TEXT(" ") << numBuffer << TEXT("\r\n");\r
986 \r
987         //              SetReplyString(replyString.str());\r
988         //              return true;\r
989         //      }\r
990         //}\r
991 \r
992         SetReplyString(TEXT("404 CINF ERROR\r\n"));\r
993         return false;\r
994 }\r
995 \r
996 void GenerateChannelInfo(int index, const safe_ptr<core::channel>& pChannel, std::wstringstream& replyString)\r
997 {\r
998         replyString << index+1 << TEXT(" ") << pChannel->get_video_format_desc().name << TEXT(" PLAYING") << TEXT("\r\n");\r
999 }\r
1000 \r
1001 bool InfoCommand::DoExecute()\r
1002 {\r
1003         std::wstringstream replyString;\r
1004 \r
1005         if(_parameters.size() >= 1)\r
1006         {\r
1007                 int channelIndex = _ttoi(_parameters[0].c_str())-1;\r
1008 \r
1009                 if(channelIndex < channels_.size())\r
1010                 {\r
1011                         replyString << TEXT("201 INFO OK\r\n");\r
1012                         GenerateChannelInfo(channelIndex, channels_[channelIndex], replyString);\r
1013                 }\r
1014                 else\r
1015                 {\r
1016                         SetReplyString(TEXT("401 INFO ERROR\r\n"));\r
1017                         return false;\r
1018                 }\r
1019         }\r
1020         else\r
1021         {\r
1022                 replyString << TEXT("200 INFO OK\r\n");\r
1023                 for(size_t n = 0; n < channels_.size(); ++n)\r
1024                         GenerateChannelInfo(n, channels_[n], replyString);\r
1025                 replyString << TEXT("\r\n");\r
1026         }\r
1027 \r
1028         SetReplyString(replyString.str());\r
1029         return true;\r
1030 }\r
1031 \r
1032 bool ClsCommand::DoExecute()\r
1033 {\r
1034 /*\r
1035                 wav = audio\r
1036                 mp3 = audio\r
1037                 swf     = movie\r
1038                 dv  = movie\r
1039                 tga = still\r
1040                 col = still\r
1041         */\r
1042         std::wstringstream replyString;\r
1043         replyString << TEXT("200 CLS OK\r\n");\r
1044         replyString << ListMedia();\r
1045         replyString << TEXT("\r\n");\r
1046         SetReplyString(boost::to_upper_copy(replyString.str()));\r
1047         return true;\r
1048 }\r
1049 \r
1050 bool TlsCommand::DoExecute()\r
1051 {\r
1052         std::wstringstream replyString;\r
1053         replyString << TEXT("200 TLS OK\r\n");\r
1054 \r
1055         replyString << ListTemplates();\r
1056         replyString << TEXT("\r\n");\r
1057 \r
1058         SetReplyString(replyString.str());\r
1059         return true;\r
1060 }\r
1061 \r
1062 bool VersionCommand::DoExecute()\r
1063 {\r
1064         std::wstring replyString = TEXT("201 VERSION OK\r\n SERVER: ") + env::version() + TEXT("\r\n");\r
1065 \r
1066         if(_parameters.size() > 0)\r
1067         {\r
1068                 if(_parameters[0] == L"FLASH")\r
1069                         replyString = TEXT("201 VERSION OK\r\n FLASH: ") + get_flash_version() + TEXT("\r\n");\r
1070                 else if(_parameters[0] == L"TEMPLATEHOST")\r
1071                         replyString = TEXT("201 VERSION OK\r\n TEMPLATEHOST: ") + get_cg_version() + TEXT("\r\n");\r
1072                 else if(_parameters[0] != L"SERVER")\r
1073                         replyString = TEXT("403 VERSION ERROR\r\n");\r
1074         }\r
1075 \r
1076         SetReplyString(replyString);\r
1077         return true;\r
1078 }\r
1079 \r
1080 bool ByeCommand::DoExecute()\r
1081 {\r
1082         GetClientInfo()->Disconnect();\r
1083         return true;\r
1084 }\r
1085 \r
1086 bool SetCommand::DoExecute()\r
1087 {\r
1088         std::wstring name = _parameters[0];\r
1089         std::transform(name.begin(), name.end(), name.begin(), toupper);\r
1090 \r
1091         std::wstring value = _parameters[1];\r
1092         std::transform(value.begin(), value.end(), value.begin(), toupper);\r
1093 \r
1094         if(name == TEXT("MODE"))\r
1095         {\r
1096                 //if(this->GetChannel()->consumer().SetVideoFormat(value)) TODO\r
1097                 //      this->SetReplyString(TEXT("202 SET MODE OK\r\n"));\r
1098                 //else\r
1099                         this->SetReplyString(TEXT("501 SET MODE FAILED\r\n"));\r
1100         }\r
1101         else\r
1102         {\r
1103                 this->SetReplyString(TEXT("403 SET ERROR\r\n"));\r
1104         }\r
1105 \r
1106         return true;\r
1107 }\r
1108 \r
1109 \r
1110 }       //namespace amcp\r
1111 }}      //namespace caspar