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