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