]> 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 result = GetChannel()->stage().call(GetLayerIndex(), parameters());
363                 
364                 if(!result.timed_wait(boost::posix_time::seconds(2)))
365                         CASPAR_THROW_EXCEPTION(timed_out());
366                                 
367                 std::wstringstream replyString;
368                 if(result.get().empty())
369                         replyString << TEXT("202 CALL OK\r\n");
370                 else
371                         replyString << TEXT("201 CALL OK\r\n") << result.get() << L"\r\n";
372                 
373                 SetReplyString(replyString.str());
374
375                 return true;
376         }
377         catch(...)
378         {
379                 CASPAR_LOG_CURRENT_EXCEPTION();
380                 SetReplyString(TEXT("502 CALL FAILED\r\n"));
381                 return false;
382         }
383 }
384
385 tbb::concurrent_unordered_map<int, std::vector<stage::transform_tuple_t>> deferred_transforms;
386
387 bool MixerCommand::DoExecute()
388 {       
389         //Perform loading of the clip
390         try
391         {       
392                 bool defer = boost::iequals(parameters().back(), L"DEFER");
393                 if(defer)
394                         parameters().pop_back();
395
396                 std::vector<stage::transform_tuple_t> transforms;
397
398                 if(boost::iequals(parameters()[0], L"KEYER") || boost::iequals(parameters()[0], L"IS_KEY"))
399                 {
400                         bool value = boost::lexical_cast<int>(parameters().at(1));
401                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
402                         {
403                                 transform.image_transform.is_key = value;
404                                 return transform;                                       
405                         }, 0, L"linear"));
406                 }
407                 else if(boost::iequals(parameters()[0], L"OPACITY"))
408                 {
409                         int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
410                         std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
411
412                         double value = boost::lexical_cast<double>(parameters().at(1));
413                         
414                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
415                         {
416                                 transform.image_transform.opacity = value;
417                                 return transform;                                       
418                         }, duration, tween));
419                 }
420                 else if(boost::iequals(parameters()[0], L"FILL") || boost::iequals(parameters()[0], L"FILL_RECT"))
421                 {
422                         int duration = parameters().size() > 5 ? boost::lexical_cast<int>(parameters()[5]) : 0;
423                         std::wstring tween = parameters().size() > 6 ? parameters()[6] : L"linear";
424                         double x        = boost::lexical_cast<double>(parameters().at(1));
425                         double y        = boost::lexical_cast<double>(parameters().at(2));
426                         double x_s      = boost::lexical_cast<double>(parameters().at(3));
427                         double y_s      = boost::lexical_cast<double>(parameters().at(4));
428
429                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) mutable -> frame_transform
430                         {
431                                 transform.image_transform.fill_translation[0]   = x;
432                                 transform.image_transform.fill_translation[1]   = y;
433                                 transform.image_transform.fill_scale[0]                 = x_s;
434                                 transform.image_transform.fill_scale[1]                 = y_s;
435                                 return transform;
436                         }, duration, tween));
437                 }
438                 else if(boost::iequals(parameters()[0], L"CLIP") || boost::iequals(parameters()[0], L"CLIP_RECT"))
439                 {
440                         int duration = parameters().size() > 5 ? boost::lexical_cast<int>(parameters()[5]) : 0;
441                         std::wstring tween = parameters().size() > 6 ? parameters()[6] : L"linear";
442                         double x        = boost::lexical_cast<double>(parameters().at(1));
443                         double y        = boost::lexical_cast<double>(parameters().at(2));
444                         double x_s      = boost::lexical_cast<double>(parameters().at(3));
445                         double y_s      = boost::lexical_cast<double>(parameters().at(4));
446
447                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
448                         {
449                                 transform.image_transform.clip_translation[0]   = x;
450                                 transform.image_transform.clip_translation[1]   = y;
451                                 transform.image_transform.clip_scale[0]                 = x_s;
452                                 transform.image_transform.clip_scale[1]                 = y_s;
453                                 return transform;
454                         }, duration, tween));
455                 }
456                 else if(boost::iequals(parameters()[0], L"GRID"))
457                 {
458                         int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
459                         std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
460                         int n = boost::lexical_cast<int>(parameters().at(1));
461                         double delta = 1.0/static_cast<double>(n);
462                         for(int x = 0; x < n; ++x)
463                         {
464                                 for(int y = 0; y < n; ++y)
465                                 {
466                                         int index = x+y*n+1;
467                                         transforms.push_back(stage::transform_tuple_t(index, [=](frame_transform transform) -> frame_transform
468                                         {               
469                                                 transform.image_transform.fill_translation[0]   = x*delta;
470                                                 transform.image_transform.fill_translation[1]   = y*delta;
471                                                 transform.image_transform.fill_scale[0]                 = delta;
472                                                 transform.image_transform.fill_scale[1]                 = delta;
473                                                 transform.image_transform.clip_translation[0]   = x*delta;
474                                                 transform.image_transform.clip_translation[1]   = y*delta;
475                                                 transform.image_transform.clip_scale[0]                 = delta;
476                                                 transform.image_transform.clip_scale[1]                 = delta;                        
477                                                 return transform;
478                                         }, duration, tween));
479                                 }
480                         }
481                 }
482                 else if(boost::iequals(parameters()[0], L"BLEND"))
483                 {
484                         auto blend_str = parameters().at(1);                                                            
485                         int layer = GetLayerIndex();
486                         GetChannel()->mixer().set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str));       
487                 }
488                 else if(boost::iequals(parameters()[0], L"MASTERVOLUME"))
489                 {
490                         float master_volume = boost::lexical_cast<float>(parameters().at(1));
491                         GetChannel()->mixer().set_master_volume(master_volume);
492                 }
493                 else if(boost::iequals(parameters()[0], L"BRIGHTNESS"))
494                 {
495                         auto value = boost::lexical_cast<double>(parameters().at(1));
496                         int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
497                         std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
498                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
499                         {
500                                 transform.image_transform.brightness = value;
501                                 return transform;
502                         }, duration, tween));
503                 }
504                 else if(boost::iequals(parameters()[0], L"SATURATION"))
505                 {
506                         auto value = boost::lexical_cast<double>(parameters().at(1));
507                         int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
508                         std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
509                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
510                         {
511                                 transform.image_transform.saturation = value;
512                                 return transform;
513                         }, duration, tween));   
514                 }
515                 else if(parameters()[0] == L"CONTRAST")
516                 {
517                         auto value = boost::lexical_cast<double>(parameters().at(1));
518                         int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
519                         std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
520                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
521                         {
522                                 transform.image_transform.contrast = value;
523                                 return transform;
524                         }, duration, tween));   
525                 }
526                 else if(boost::iequals(parameters()[0], L"LEVELS"))
527                 {
528                         levels value;
529                         value.min_input  = boost::lexical_cast<double>(parameters().at(1));
530                         value.max_input  = boost::lexical_cast<double>(parameters().at(2));
531                         value.gamma              = boost::lexical_cast<double>(parameters().at(3));
532                         value.min_output = boost::lexical_cast<double>(parameters().at(4));
533                         value.max_output = boost::lexical_cast<double>(parameters().at(5));
534                         int duration = parameters().size() > 6 ? boost::lexical_cast<int>(parameters()[6]) : 0;
535                         std::wstring tween = parameters().size() > 7 ? parameters()[7] : L"linear";
536
537                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
538                         {
539                                 transform.image_transform.levels = value;
540                                 return transform;
541                         }, duration, tween));
542                 }
543                 else if(boost::iequals(parameters()[0], L"VOLUME"))
544                 {
545                         int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
546                         std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
547                         double value = boost::lexical_cast<double>(parameters()[1]);
548
549                         transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
550                         {
551                                 transform.audio_transform.volume = value;
552                                 return transform;
553                         }, duration, tween));
554                 }
555                 else if(boost::iequals(parameters()[0], L"CLEAR"))
556                 {
557                         int layer = GetLayerIndex(std::numeric_limits<int>::max());
558
559                         if (layer == std::numeric_limits<int>::max())
560                         {
561                                 GetChannel()->stage().clear_transforms();
562                                 GetChannel()->mixer().clear_blend_modes();
563                         }
564                         else
565                         {
566                                 GetChannel()->stage().clear_transforms(layer);
567                                 GetChannel()->mixer().clear_blend_mode(layer);
568                         }
569                 }
570                 else if(boost::iequals(parameters()[0], L"COMMIT"))
571                 {
572                         transforms = std::move(deferred_transforms[GetChannelIndex()]);
573                 }
574                 else
575                 {
576                         SetReplyString(TEXT("404 MIXER ERROR\r\n"));
577                         return false;
578                 }
579
580                 if(defer)
581                 {
582                         auto& defer_tranforms = deferred_transforms[GetChannelIndex()];
583                         defer_tranforms.insert(defer_tranforms.end(), transforms.begin(), transforms.end());
584                 }
585                 else
586                         GetChannel()->stage().apply_transforms(transforms);
587         
588                 SetReplyString(TEXT("202 MIXER OK\r\n"));
589
590                 return true;
591         }
592         catch(file_not_found&)
593         {
594                 CASPAR_LOG_CURRENT_EXCEPTION();
595                 SetReplyString(TEXT("404 MIXER ERROR\r\n"));
596                 return false;
597         }
598         catch(...)
599         {
600                 CASPAR_LOG_CURRENT_EXCEPTION();
601                 SetReplyString(TEXT("502 MIXER FAILED\r\n"));
602                 return false;
603         }
604 }
605
606 bool SwapCommand::DoExecute()
607 {       
608         //Perform loading of the clip
609         try
610         {
611                 if(GetLayerIndex(-1) != -1)
612                 {
613                         std::vector<std::string> strs;
614                         boost::split(strs, parameters()[0], boost::is_any_of("-"));
615                         
616                         auto ch1 = GetChannel();
617                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);
618
619                         int l1 = GetLayerIndex();
620                         int l2 = boost::lexical_cast<int>(strs.at(1));
621
622                         ch1->stage().swap_layer(l1, l2, ch2->stage());
623                 }
624                 else
625                 {
626                         auto ch1 = GetChannel();
627                         auto ch2 = GetChannels().at(boost::lexical_cast<int>(parameters()[0])-1);
628                         ch1->stage().swap_layers(ch2->stage());
629                 }
630                 
631                 SetReplyString(TEXT("202 SWAP OK\r\n"));
632
633                 return true;
634         }
635         catch(file_not_found&)
636         {
637                 CASPAR_LOG_CURRENT_EXCEPTION();
638                 SetReplyString(TEXT("404 SWAP ERROR\r\n"));
639                 return false;
640         }
641         catch(...)
642         {
643                 CASPAR_LOG_CURRENT_EXCEPTION();
644                 SetReplyString(TEXT("502 SWAP FAILED\r\n"));
645                 return false;
646         }
647 }
648
649 bool AddCommand::DoExecute()
650 {       
651         //Perform loading of the clip
652         try
653         {
654                 //create_consumer still expects all parameters to be uppercase
655                 BOOST_FOREACH(std::wstring& str, parameters())
656                 {
657                         boost::to_upper(str);
658                 }
659
660                 auto consumer = create_consumer(parameters());
661                 GetChannel()->output().add(GetLayerIndex(consumer->index()), consumer);
662         
663                 SetReplyString(TEXT("202 ADD OK\r\n"));
664
665                 return true;
666         }
667         catch(file_not_found&)
668         {
669                 CASPAR_LOG_CURRENT_EXCEPTION();
670                 SetReplyString(TEXT("404 ADD ERROR\r\n"));
671                 return false;
672         }
673         catch(...)
674         {
675                 CASPAR_LOG_CURRENT_EXCEPTION();
676                 SetReplyString(TEXT("502 ADD FAILED\r\n"));
677                 return false;
678         }
679 }
680
681 bool RemoveCommand::DoExecute()
682 {       
683         //Perform loading of the clip
684         try
685         {
686                 auto index = GetLayerIndex(std::numeric_limits<int>::min());
687                 if(index == std::numeric_limits<int>::min())
688                 {
689                         //create_consumer still expects all parameters to be uppercase
690                         BOOST_FOREACH(std::wstring& str, parameters())
691                         {
692                                 boost::to_upper(str);
693                         }
694
695                         index = create_consumer(parameters())->index();
696                 }
697
698                 GetChannel()->output().remove(index);
699
700                 SetReplyString(TEXT("202 REMOVE OK\r\n"));
701
702                 return true;
703         }
704         catch(file_not_found&)
705         {
706                 CASPAR_LOG_CURRENT_EXCEPTION();
707                 SetReplyString(TEXT("404 REMOVE ERROR\r\n"));
708                 return false;
709         }
710         catch(...)
711         {
712                 CASPAR_LOG_CURRENT_EXCEPTION();
713                 SetReplyString(TEXT("502 REMOVE FAILED\r\n"));
714                 return false;
715         }
716 }
717
718 bool LoadCommand::DoExecute()
719 {       
720         //Perform loading of the clip
721         try
722         {
723                 auto pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), parameters());             
724                 GetChannel()->stage().load(GetLayerIndex(), pFP, true);
725         
726                 SetReplyString(TEXT("202 LOAD OK\r\n"));
727
728                 return true;
729         }
730         catch(file_not_found&)
731         {
732                 CASPAR_LOG_CURRENT_EXCEPTION();
733                 SetReplyString(TEXT("404 LOAD ERROR\r\n"));
734                 return false;
735         }
736         catch(...)
737         {
738                 CASPAR_LOG_CURRENT_EXCEPTION();
739                 SetReplyString(TEXT("502 LOAD FAILED\r\n"));
740                 return false;
741         }
742 }
743
744
745
746 //std::function<std::wstring()> channel_cg_add_command::parse(const std::wstring& message, const std::vector<renderer::render_device_ptr>& channels)
747 //{
748 //      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>.*)?");
749 //
750 //      boost::wsmatch what;
751 //      if(!boost::regex_match(message, what, expr))
752 //              return nullptr;
753 //
754 //      auto info = channel_info::parse(what, channels);
755 //
756 //      int flash_layer_index = boost::lexical_cast<int>(what["FLASH_LAYER"].str());
757 //
758 //      std::wstring templatename = what["TEMPLATE"].str();
759 //      bool play_on_load = what["PLAY_ON_LOAD"].matched ? what["PLAY_ON_LOAD"].str() != L"0" : 0;
760 //      std::wstring start_label = what["START_LABEL"].str();   
761 //      std::wstring data = get_data(what["DATA"].str());
762 //      
763 //      boost::replace_all(templatename, "\"", "");
764 //
765 //      return [=]() -> std::wstring
766 //      {       
767 //              std::wstring fullFilename = flash::flash_producer::find_template(server::template_folder() + templatename);
768 //              if(fullFilename.empty())
769 //                      CASPAR_THROW_EXCEPTION(file_not_found());
770 //      
771 //              std::wstring extension = boost::filesystem::wpath(fullFilename).extension();
772 //              std::wstring filename = templatename;
773 //              filename.append(extension);
774 //
775 //              flash::flash::create_cg_proxy(info.video_channel, std::max<int>(DEFAULT_CHANNEL_LAYER+1, info.layer_index))
776 //                      ->add(flash_layer_index, filename, play_on_load, start_label, data);
777 //
778 //              CASPAR_LOG(info) << L"Executed [amcp_channel_cg_add]";
779 //              return L"";
780 //      };
781
782 bool LoadbgCommand::DoExecute()
783 {
784         transition_info transitionInfo;
785         
786         bool bLoop = false;
787
788         // TRANSITION
789
790         std::wstring message;
791         for(size_t n = 0; n < parameters().size(); ++n)
792                 message += boost::to_upper_copy(parameters()[n]) + L" ";
793                 
794         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)?.*");
795         boost::wsmatch what;
796         if(boost::regex_match(message, what, expr))
797         {
798                 auto transition = what["TRANSITION"].str();
799                 transitionInfo.duration = boost::lexical_cast<size_t>(what["DURATION"].str());
800                 auto direction = what["DIRECTION"].matched ? what["DIRECTION"].str() : L"";
801                 auto tween = what["TWEEN"].matched ? what["TWEEN"].str() : L"";
802                 transitionInfo.tweener = tween;         
803
804                 if(transition == TEXT("CUT"))
805                         transitionInfo.type = transition_type::cut;
806                 else if(transition == TEXT("MIX"))
807                         transitionInfo.type = transition_type::mix;
808                 else if(transition == TEXT("PUSH"))
809                         transitionInfo.type = transition_type::push;
810                 else if(transition == TEXT("SLIDE"))
811                         transitionInfo.type = transition_type::slide;
812                 else if(transition == TEXT("WIPE"))
813                         transitionInfo.type = transition_type::wipe;
814                 
815                 if(direction == TEXT("FROMLEFT"))
816                         transitionInfo.direction = transition_direction::from_left;
817                 else if(direction == TEXT("FROMRIGHT"))
818                         transitionInfo.direction = transition_direction::from_right;
819                 else if(direction == TEXT("LEFT"))
820                         transitionInfo.direction = transition_direction::from_right;
821                 else if(direction == TEXT("RIGHT"))
822                         transitionInfo.direction = transition_direction::from_left;
823         }
824         
825         //Perform loading of the clip
826         try
827         {
828                 std::shared_ptr<core::frame_producer> pFP;
829                 
830                 static boost::wregex expr(L"\\[(?<CHANNEL>\\d+)\\]", boost::regex::icase);
831                         
832                 boost::wsmatch what;
833                 if(boost::regex_match(parameters().at(0), what, expr))
834                 {
835                         auto channel_index = boost::lexical_cast<int>(what["CHANNEL"].str());
836                         pFP = reroute::create_producer(*GetChannels().at(channel_index-1)); 
837                 }
838                 else
839                 {
840                         pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), parameters());
841                 }
842                 
843                 if(pFP == frame_producer::empty())
844                         CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(parameters().size() > 0 ? parameters()[0] : L""));
845
846                 bool auto_play = contains_param(L"AUTO", parameters());
847
848                 auto pFP2 = create_transition_producer(GetChannel()->video_format_desc().field_mode, spl::make_shared_ptr(pFP), transitionInfo);
849                 if(auto_play)
850                         GetChannel()->stage().load(GetLayerIndex(), pFP2, false, transitionInfo.duration); // TODO: LOOP
851                 else
852                         GetChannel()->stage().load(GetLayerIndex(), pFP2, false); // TODO: LOOP
853         
854                 
855                 SetReplyString(TEXT("202 LOADBG OK\r\n"));
856
857                 return true;
858         }
859         catch(file_not_found&)
860         {               
861                 CASPAR_LOG(error) << L"File not found. No match found for parameters. Check syntax.";
862                 SetReplyString(TEXT("404 LOADBG ERROR\r\n"));
863                 return false;
864         }
865         catch(...)
866         {
867                 CASPAR_LOG_CURRENT_EXCEPTION();
868                 SetReplyString(TEXT("502 LOADBG FAILED\r\n"));
869                 return false;
870         }
871 }
872
873 bool PauseCommand::DoExecute()
874 {
875         try
876         {
877                 GetChannel()->stage().pause(GetLayerIndex());
878                 SetReplyString(TEXT("202 PAUSE OK\r\n"));
879                 return true;
880         }
881         catch(...)
882         {
883                 SetReplyString(TEXT("501 PAUSE FAILED\r\n"));
884         }
885
886         return false;
887 }
888
889 bool PlayCommand::DoExecute()
890 {
891         try
892         {
893                 if(!parameters().empty())
894                 {
895                         LoadbgCommand lbg;
896                         lbg.SetChannel(GetChannel());
897                         lbg.SetChannels(GetChannels());
898                         lbg.SetChannelIndex(GetChannelIndex());
899                         lbg.SetLayerIntex(GetLayerIndex());
900                         lbg.SetClientInfo(GetClientInfo());
901                         for(auto it = parameters().begin(); it != parameters().end(); ++it)
902                                 lbg.AddParameter(*it);
903                         if(!lbg.Execute())
904                                 throw std::exception();
905                 }
906
907                 GetChannel()->stage().play(GetLayerIndex());
908                 
909                 SetReplyString(TEXT("202 PLAY OK\r\n"));
910                 return true;
911         }
912         catch(...)
913         {
914                 SetReplyString(TEXT("501 PLAY FAILED\r\n"));
915         }
916
917         return false;
918 }
919
920 bool StopCommand::DoExecute()
921 {
922         try
923         {
924                 GetChannel()->stage().stop(GetLayerIndex());
925                 SetReplyString(TEXT("202 STOP OK\r\n"));
926                 return true;
927         }
928         catch(...)
929         {
930                 SetReplyString(TEXT("501 STOP FAILED\r\n"));
931         }
932
933         return false;
934 }
935
936 bool ClearCommand::DoExecute()
937 {
938         int index = GetLayerIndex(std::numeric_limits<int>::min());
939         if(index != std::numeric_limits<int>::min())
940                 GetChannel()->stage().clear(index);
941         else
942                 GetChannel()->stage().clear();
943                 
944         SetReplyString(TEXT("202 CLEAR OK\r\n"));
945
946         return true;
947 }
948
949 bool PrintCommand::DoExecute()
950 {
951         GetChannel()->output().add(create_consumer(boost::assign::list_of(L"IMAGE")));
952                 
953         SetReplyString(TEXT("202 PRINT OK\r\n"));
954
955         return true;
956 }
957
958 bool LogCommand::DoExecute()
959 {
960         if(boost::iequals(parameters().at(0), L"LEVEL"))
961                 log::set_log_level(parameters().at(1));
962
963         SetReplyString(TEXT("202 LOG OK\r\n"));
964
965         return true;
966 }
967
968 bool CGCommand::DoExecute()
969 {
970         try
971         {
972                 std::wstring command = boost::to_upper_copy(parameters()[0]);
973                 if(command == TEXT("ADD"))
974                         return DoExecuteAdd();
975                 else if(command == TEXT("PLAY"))
976                         return DoExecutePlay();
977                 else if(command == TEXT("STOP"))
978                         return DoExecuteStop();
979                 else if(command == TEXT("NEXT"))
980                         return DoExecuteNext();
981                 else if(command == TEXT("REMOVE"))
982                         return DoExecuteRemove();
983                 else if(command == TEXT("CLEAR"))
984                         return DoExecuteClear();
985                 else if(command == TEXT("UPDATE"))
986                         return DoExecuteUpdate();
987                 else if(command == TEXT("INVOKE"))
988                         return DoExecuteInvoke();
989                 else if(command == TEXT("INFO"))
990                         return DoExecuteInfo();
991         }
992         catch(...)
993         {
994                 CASPAR_LOG_CURRENT_EXCEPTION();
995         }
996
997         SetReplyString(TEXT("403 CG ERROR\r\n"));
998         return false;
999 }
1000
1001 bool CGCommand::ValidateLayer(const std::wstring& layerstring) {
1002         int length = layerstring.length();
1003         for(int i = 0; i < length; ++i) {
1004                 if(!_istdigit(layerstring[i])) {
1005                         return false;
1006                 }
1007         }
1008
1009         return true;
1010 }
1011
1012 bool CGCommand::DoExecuteAdd() {
1013         //CG 1 ADD 0 "template_folder/templatename" [STARTLABEL] 0/1 [DATA]
1014
1015         int layer = 0;                          //_parameters[1]
1016 //      std::wstring templateName;      //_parameters[2]
1017         std::wstring label;             //_parameters[3]
1018         bool bDoStart = false;          //_parameters[3] alt. _parameters[4]
1019 //      std::wstring data;                      //_parameters[4] alt. _parameters[5]
1020
1021         if(parameters().size() < 4) 
1022         {
1023                 SetReplyString(TEXT("402 CG ERROR\r\n"));
1024                 return false;
1025         }
1026         unsigned int dataIndex = 4;
1027
1028         if(!ValidateLayer(parameters()[1])) 
1029         {
1030                 SetReplyString(TEXT("403 CG ERROR\r\n"));
1031                 return false;
1032         }
1033
1034         layer = boost::lexical_cast<int>(parameters()[1]);
1035
1036         if(parameters()[3].length() > 1) 
1037         {       //read label
1038                 label = parameters()[3];
1039                 ++dataIndex;
1040
1041                 if(parameters().size() > 4 && parameters()[4].length() > 0)     //read play-on-load-flag
1042                         bDoStart = (parameters()[4][0]==TEXT('1')) ? true : false;
1043                 else 
1044                 {
1045                         SetReplyString(TEXT("402 CG ERROR\r\n"));
1046                         return false;
1047                 }
1048         }
1049         else if(parameters()[3].length() > 0) { //read play-on-load-flag
1050                 bDoStart = (parameters()[3][0]==TEXT('1')) ? true : false;
1051         }
1052         else 
1053         {
1054                 SetReplyString(TEXT("403 CG ERROR\r\n"));
1055                 return false;
1056         }
1057
1058         const TCHAR* pDataString = 0;
1059         std::wstringstream data;
1060         std::wstring dataFromFile;
1061         if(parameters().size() > dataIndex) 
1062         {       //read data
1063                 const std::wstring& dataString = parameters()[dataIndex];
1064
1065                 if(dataString[0] == TEXT('<')) //the data is an XML-string
1066                         pDataString = dataString.c_str();
1067                 else 
1068                 {
1069                         //The data is not an XML-string, it must be a filename
1070                         std::wstring filename = env::data_folder();
1071                         filename.append(dataString);
1072                         filename.append(TEXT(".ftd"));
1073
1074                         dataFromFile = read_file(boost::filesystem::wpath(filename));
1075                         pDataString = dataFromFile.c_str();
1076                 }
1077         }
1078
1079         std::wstring fullFilename = flash::find_template(env::template_folder() + parameters()[2]);
1080         if(!fullFilename.empty())
1081         {
1082                 std::wstring extension = boost::filesystem::path(fullFilename).extension().wstring();
1083                 std::wstring filename = parameters()[2];
1084                 filename.append(extension);
1085
1086                 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(""));
1087                 SetReplyString(TEXT("202 CG OK\r\n"));
1088         }
1089         else
1090         {
1091                 CASPAR_LOG(warning) << "Could not find template " << parameters()[2];
1092                 SetReplyString(TEXT("404 CG ERROR\r\n"));
1093         }
1094         return true;
1095 }
1096
1097 bool CGCommand::DoExecutePlay()
1098 {
1099         if(parameters().size() > 1)
1100         {
1101                 if(!ValidateLayer(parameters()[1])) 
1102                 {
1103                         SetReplyString(TEXT("403 CG ERROR\r\n"));
1104                         return false;
1105                 }
1106                 int layer = boost::lexical_cast<int>(parameters()[1]);
1107                 flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).play(layer);
1108         }
1109         else
1110         {
1111                 SetReplyString(TEXT("402 CG ERROR\r\n"));
1112                 return true;
1113         }
1114
1115         SetReplyString(TEXT("202 CG OK\r\n"));
1116         return true;
1117 }
1118
1119 bool CGCommand::DoExecuteStop() 
1120 {
1121         if(parameters().size() > 1)
1122         {
1123                 if(!ValidateLayer(parameters()[1])) 
1124                 {
1125                         SetReplyString(TEXT("403 CG ERROR\r\n"));
1126                         return false;
1127                 }
1128                 int layer = boost::lexical_cast<int>(parameters()[1]);
1129                 flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).stop(layer, 0);
1130         }
1131         else 
1132         {
1133                 SetReplyString(TEXT("402 CG ERROR\r\n"));
1134                 return true;
1135         }
1136
1137         SetReplyString(TEXT("202 CG OK\r\n"));
1138         return true;
1139 }
1140
1141 bool CGCommand::DoExecuteNext()
1142 {
1143         if(parameters().size() > 1) 
1144         {
1145                 if(!ValidateLayer(parameters()[1])) 
1146                 {
1147                         SetReplyString(TEXT("403 CG ERROR\r\n"));
1148                         return false;
1149                 }
1150
1151                 int layer = boost::lexical_cast<int>(parameters()[1]);
1152                 flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).next(layer);
1153         }
1154         else 
1155         {
1156                 SetReplyString(TEXT("402 CG ERROR\r\n"));
1157                 return true;
1158         }
1159
1160         SetReplyString(TEXT("202 CG OK\r\n"));
1161         return true;
1162 }
1163
1164 bool CGCommand::DoExecuteRemove() 
1165 {
1166         if(parameters().size() > 1) 
1167         {
1168                 if(!ValidateLayer(parameters()[1])) 
1169                 {
1170                         SetReplyString(TEXT("403 CG ERROR\r\n"));
1171                         return false;
1172                 }
1173
1174                 int layer = boost::lexical_cast<int>(parameters()[1]);
1175                 flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).remove(layer);
1176         }
1177         else 
1178         {
1179                 SetReplyString(TEXT("402 CG ERROR\r\n"));
1180                 return true;
1181         }
1182
1183         SetReplyString(TEXT("202 CG OK\r\n"));
1184         return true;
1185 }
1186
1187 bool CGCommand::DoExecuteClear() 
1188 {
1189         GetChannel()->stage().clear(GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER));
1190         SetReplyString(TEXT("202 CG OK\r\n"));
1191         return true;
1192 }
1193
1194 bool CGCommand::DoExecuteUpdate() 
1195 {
1196         try
1197         {
1198                 if(!ValidateLayer(parameters().at(1)))
1199                 {
1200                         SetReplyString(TEXT("403 CG ERROR\r\n"));
1201                         return false;
1202                 }
1203                                                 
1204                 std::wstring dataString = parameters().at(2);                           
1205                 if(dataString.at(0) != TEXT('<'))
1206                 {
1207                         //The data is not an XML-string, it must be a filename
1208                         std::wstring filename = env::data_folder();
1209                         filename.append(dataString);
1210                         filename.append(TEXT(".ftd"));
1211
1212                         dataString = read_file(boost::filesystem::wpath(filename));
1213                 }               
1214
1215                 int layer = boost::lexical_cast<int>(parameters()[1]);
1216                 flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).update(layer, dataString);
1217         }
1218         catch(...)
1219         {
1220                 SetReplyString(TEXT("402 CG ERROR\r\n"));
1221                 return true;
1222         }
1223
1224         SetReplyString(TEXT("202 CG OK\r\n"));
1225         return true;
1226 }
1227
1228 bool CGCommand::DoExecuteInvoke() 
1229 {
1230         std::wstringstream replyString;
1231         replyString << TEXT("201 CG OK\r\n");
1232
1233         if(parameters().size() > 2)
1234         {
1235                 if(!ValidateLayer(parameters()[1]))
1236                 {
1237                         SetReplyString(TEXT("403 CG ERROR\r\n"));
1238                         return false;
1239                 }
1240                 int layer = boost::lexical_cast<int>(parameters()[1]);
1241                 auto result = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).invoke(layer, parameters()[2]);
1242                 replyString << result << TEXT("\r\n"); 
1243         }
1244         else 
1245         {
1246                 SetReplyString(TEXT("402 CG ERROR\r\n"));
1247                 return true;
1248         }
1249         
1250         SetReplyString(replyString.str());
1251         return true;
1252 }
1253
1254 bool CGCommand::DoExecuteInfo() 
1255 {
1256         std::wstringstream replyString;
1257         replyString << TEXT("201 CG OK\r\n");
1258
1259         if(parameters().size() > 1)
1260         {
1261                 if(!ValidateLayer(parameters()[1]))
1262                 {
1263                         SetReplyString(TEXT("403 CG ERROR\r\n"));
1264                         return false;
1265                 }
1266
1267                 int layer = boost::lexical_cast<int>(parameters()[1]);
1268                 auto desc = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).description(layer);
1269                 
1270                 replyString << desc << TEXT("\r\n"); 
1271         }
1272         else 
1273         {
1274                 auto info = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).template_host_info();
1275                 replyString << info << TEXT("\r\n"); 
1276         }       
1277
1278         SetReplyString(replyString.str());
1279         return true;
1280 }
1281
1282 bool DataCommand::DoExecute()
1283 {
1284         std::wstring command = boost::to_upper_copy(parameters()[0]);
1285         if(command == TEXT("STORE"))
1286                 return DoExecuteStore();
1287         else if(command == TEXT("RETRIEVE"))
1288                 return DoExecuteRetrieve();
1289         else if(command == TEXT("REMOVE"))
1290                 return DoExecuteRemove();
1291         else if(command == TEXT("LIST"))
1292                 return DoExecuteList();
1293
1294         SetReplyString(TEXT("403 DATA ERROR\r\n"));
1295         return false;
1296 }
1297
1298 bool DataCommand::DoExecuteStore() 
1299 {
1300         if(parameters().size() < 3) 
1301         {
1302                 SetReplyString(TEXT("402 DATA STORE ERROR\r\n"));
1303                 return false;
1304         }
1305
1306         std::wstring filename = env::data_folder();
1307         filename.append(parameters()[1]);
1308         filename.append(TEXT(".ftd"));
1309
1310         auto data_path = boost::filesystem::wpath(
1311                 boost::filesystem::wpath(filename).parent_path());
1312
1313         if(!boost::filesystem::exists(data_path))
1314                 boost::filesystem::create_directories(data_path);
1315
1316         std::wofstream datafile(filename.c_str());
1317         if(!datafile) 
1318         {
1319                 SetReplyString(TEXT("501 DATA STORE FAILED\r\n"));
1320                 return false;
1321         }
1322
1323         datafile << static_cast<wchar_t>(65279); // UTF-8 BOM character
1324         datafile << parameters()[2] << std::flush;
1325         datafile.close();
1326
1327         std::wstring replyString = TEXT("202 DATA STORE OK\r\n");
1328         SetReplyString(replyString);
1329         return true;
1330 }
1331
1332 bool DataCommand::DoExecuteRetrieve() 
1333 {
1334         if(parameters().size() < 2) 
1335         {
1336                 SetReplyString(TEXT("402 DATA RETRIEVE ERROR\r\n"));
1337                 return false;
1338         }
1339
1340         std::wstring filename = env::data_folder();
1341         filename.append(parameters()[1]);
1342         filename.append(TEXT(".ftd"));
1343
1344         std::wstring file_contents = read_file(boost::filesystem::wpath(filename));
1345
1346         if (file_contents.empty()) 
1347         {
1348                 SetReplyString(TEXT("404 DATA RETRIEVE ERROR\r\n"));
1349                 return false;
1350         }
1351
1352         std::wstringstream reply(TEXT("201 DATA RETRIEVE OK\r\n"));
1353
1354         reply << file_contents;
1355         reply << "\r\n";
1356         SetReplyString(reply.str());
1357         return true;
1358 }
1359
1360 bool DataCommand::DoExecuteRemove()
1361
1362         if (parameters().size() < 2)
1363         {
1364                 SetReplyString(TEXT("402 DATA REMOVE ERROR\r\n"));
1365                 return false;
1366         }
1367
1368         std::wstring filename = env::data_folder();
1369         filename.append(parameters()[1]);
1370         filename.append(TEXT(".ftd"));
1371
1372         if (!boost::filesystem::exists(filename))
1373         {
1374                 SetReplyString(TEXT("404 DATA REMOVE ERROR\r\n"));
1375                 return false;
1376         }
1377
1378         if (!boost::filesystem::remove(filename))
1379         {
1380                 SetReplyString(TEXT("403 DATA REMOVE ERROR\r\n"));
1381                 return false;
1382         }
1383
1384         SetReplyString(TEXT("201 DATA REMOVE OK\r\n"));
1385
1386         return true;
1387 }
1388
1389 bool DataCommand::DoExecuteList() 
1390 {
1391         std::wstringstream replyString;
1392         replyString << TEXT("200 DATA LIST OK\r\n");
1393
1394         for (boost::filesystem::recursive_directory_iterator itr(env::data_folder()), end; itr != end; ++itr)
1395         {                       
1396                 if(boost::filesystem::is_regular_file(itr->path()))
1397                 {
1398                         if(!boost::iequals(itr->path().extension().wstring(), L".ftd"))
1399                                 continue;
1400                         
1401                         auto relativePath = boost::filesystem::wpath(itr->path().wstring().substr(env::data_folder().size()-1, itr->path().wstring().size()));
1402                         
1403                         auto str = relativePath.replace_extension(TEXT("")).native();
1404                         if(str[0] == '\\' || str[0] == '/')
1405                                 str = std::wstring(str.begin() + 1, str.end());
1406
1407                         replyString << str << TEXT("\r\n");     
1408                 }
1409         }
1410         
1411         replyString << TEXT("\r\n");
1412
1413         SetReplyString(boost::to_upper_copy(replyString.str()));
1414         return true;
1415 }
1416
1417 bool CinfCommand::DoExecute()
1418 {
1419         std::wstringstream replyString;
1420         
1421         try
1422         {
1423                 std::wstring info;
1424                 for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end && info.empty(); ++itr)
1425                 {
1426                         auto path = itr->path();
1427                         auto file = path.replace_extension(L"").filename();
1428                         if(boost::iequals(file.wstring(), parameters().at(0)))
1429                                 info += MediaInfo(itr->path()) + L"\r\n";
1430                 }
1431
1432                 if(info.empty())
1433                 {
1434                         SetReplyString(TEXT("404 CINF ERROR\r\n"));
1435                         return false;
1436                 }
1437                 replyString << TEXT("200 CINF OK\r\n");
1438                 replyString << info << "\r\n";
1439         }
1440         catch(...)
1441         {
1442                 CASPAR_LOG_CURRENT_EXCEPTION();
1443                 SetReplyString(TEXT("404 CINF ERROR\r\n"));
1444                 return false;
1445         }
1446         
1447         SetReplyString(replyString.str());
1448         return true;
1449 }
1450
1451 void GenerateChannelInfo(int index, const spl::shared_ptr<core::video_channel>& pChannel, std::wstringstream& replyString)
1452 {
1453         replyString << index+1 << TEXT(" ") << pChannel->video_format_desc().name << TEXT(" PLAYING") << TEXT("\r\n");
1454 }
1455
1456 bool InfoCommand::DoExecute()
1457 {
1458         std::wstringstream replyString;
1459         
1460         boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);
1461
1462         try
1463         {
1464                 if(parameters().size() >= 1 && boost::iequals(parameters()[0], L"TEMPLATE"))
1465                 {               
1466                         replyString << L"201 INFO TEMPLATE OK\r\n";
1467
1468                         // Needs to be extended for any file, not just flash.
1469
1470                         auto filename = flash::find_template(env::template_folder() + parameters().at(1));
1471                                                 
1472                         std::wstringstream str;
1473                         str << u16(flash::read_template_meta_info(filename));
1474                         boost::property_tree::wptree info;
1475                         boost::property_tree::xml_parser::read_xml(str, info, boost::property_tree::xml_parser::trim_whitespace | boost::property_tree::xml_parser::no_comments);
1476
1477                         boost::property_tree::xml_parser::write_xml(replyString, info, w);
1478                 }
1479                 else if(parameters().size() >= 1 && boost::iequals(parameters()[0], L"CONFIG"))
1480                 {               
1481                         replyString << L"201 INFO CONFIG OK\r\n";
1482
1483                         boost::property_tree::write_xml(replyString, caspar::env::properties(), w);
1484                 }
1485                 else if(parameters().size() >= 1 && boost::iequals(parameters()[0], L"PATHS"))
1486                 {
1487                         replyString << L"201 INFO PATHS OK\r\n";
1488
1489                         boost::property_tree::wptree info;
1490                         info.add_child(L"paths", caspar::env::properties().get_child(L"configuration.paths"));
1491                         info.add(L"paths.initial-path", boost::filesystem3::initial_path().wstring() + L"\\");
1492
1493                         boost::property_tree::write_xml(replyString, info, w);
1494                 }
1495                 else if(parameters().size() >= 1 && boost::iequals(parameters()[0], L"SYSTEM"))
1496                 {
1497                         replyString << L"201 INFO SYSTEM OK\r\n";
1498                         
1499                         boost::property_tree::wptree info;
1500                         
1501                         info.add(L"system.name",                                        caspar::system_product_name());
1502                         info.add(L"system.windows.name",                        caspar::win_product_name());
1503                         info.add(L"system.windows.service-pack",        caspar::win_sp_version());
1504                         info.add(L"system.cpu",                                         caspar::cpu_info());
1505         
1506                         BOOST_FOREACH(auto device, caspar::decklink::device_list())
1507                                 info.add(L"system.decklink.device", device);
1508
1509                         BOOST_FOREACH(auto device, caspar::bluefish::device_list())
1510                                 info.add(L"system.bluefish.device", device);
1511                                 
1512                         info.add(L"system.flash",                                       caspar::flash::version());
1513                         //info.add(L"system.free-image",                                caspar::image::version());
1514                         info.add(L"system.ffmpeg.avcodec",                      caspar::ffmpeg::avcodec_version());
1515                         info.add(L"system.ffmpeg.avformat",                     caspar::ffmpeg::avformat_version());
1516                         info.add(L"system.ffmpeg.avfilter",                     caspar::ffmpeg::avfilter_version());
1517                         info.add(L"system.ffmpeg.avutil",                       caspar::ffmpeg::avutil_version());
1518                         info.add(L"system.ffmpeg.swscale",                      caspar::ffmpeg::swscale_version());
1519                                                 
1520                         boost::property_tree::write_xml(replyString, info, w);
1521                 }
1522                 else if(parameters().size() >= 1 && boost::iequals(parameters()[0], L"SERVER"))
1523                 {
1524                         replyString << L"201 INFO SERVER OK\r\n";
1525                         
1526                         boost::property_tree::wptree info;
1527
1528                         int index = 0;
1529                         BOOST_FOREACH(auto channel, channels_)
1530                                 info.add_child(L"channels.channel", channel->info())
1531                                         .add(L"index", ++index);
1532                         
1533                         boost::property_tree::write_xml(replyString, info, w);
1534                 }
1535                 else // channel
1536                 {                       
1537                         if(parameters().size() >= 1)
1538                         {
1539                                 replyString << TEXT("201 INFO OK\r\n");
1540                                 boost::property_tree::wptree info;
1541
1542                                 std::vector<std::wstring> split;
1543                                 boost::split(split, parameters()[0], boost::is_any_of("-"));
1544                                         
1545                                 int layer = std::numeric_limits<int>::min();
1546                                 int channel = boost::lexical_cast<int>(split[0]) - 1;
1547
1548                                 if(split.size() > 1)
1549                                         layer = boost::lexical_cast<int>(split[1]);
1550                                 
1551                                 if(layer == std::numeric_limits<int>::min())
1552                                 {       
1553                                         info.add_child(L"channel", channels_.at(channel)->info())
1554                                                         .add(L"index", channel);
1555                                 }
1556                                 else
1557                                 {
1558                                         if(parameters().size() >= 2)
1559                                         {
1560                                                 if(boost::iequals(parameters()[1], L"B"))
1561                                                         info.add_child(L"producer", channels_.at(channel)->stage().background(layer).get()->info());
1562                                                 else
1563                                                         info.add_child(L"producer", channels_.at(channel)->stage().foreground(layer).get()->info());
1564                                         }
1565                                         else
1566                                         {
1567                                                 info.add_child(L"layer", channels_.at(channel)->stage().info(layer).get())
1568                                                         .add(L"index", layer);
1569                                         }
1570                                 }
1571                                 boost::property_tree::xml_parser::write_xml(replyString, info, w);
1572                         }
1573                         else
1574                         {
1575                                 // This is needed for backwards compatibility with old clients
1576                                 replyString << TEXT("200 INFO OK\r\n");
1577                                 for(size_t n = 0; n < channels_.size(); ++n)
1578                                         GenerateChannelInfo(n, channels_[n], replyString);
1579                         }
1580
1581                 }
1582         }
1583         catch(...)
1584         {
1585                 CASPAR_LOG_CURRENT_EXCEPTION();
1586                 SetReplyString(TEXT("403 INFO ERROR\r\n"));
1587                 return false;
1588         }
1589
1590         replyString << TEXT("\r\n");
1591         SetReplyString(replyString.str());
1592         return true;
1593 }
1594
1595 bool ClsCommand::DoExecute()
1596 {
1597 /*
1598                 wav = audio
1599                 mp3 = audio
1600                 swf     = movie
1601                 dv  = movie
1602                 tga = still
1603                 col = still
1604         */
1605         try
1606         {
1607                 std::wstringstream replyString;
1608                 replyString << TEXT("200 CLS OK\r\n");
1609                 replyString << ListMedia();
1610                 replyString << TEXT("\r\n");
1611                 SetReplyString(boost::to_upper_copy(replyString.str()));
1612         }
1613         catch(...)
1614         {
1615                 CASPAR_LOG_CURRENT_EXCEPTION();
1616                 SetReplyString(TEXT("501 CLS FAILED\r\n"));
1617                 return false;
1618         }
1619
1620         return true;
1621 }
1622
1623 bool TlsCommand::DoExecute()
1624 {
1625         try
1626         {
1627                 std::wstringstream replyString;
1628                 replyString << TEXT("200 TLS OK\r\n");
1629
1630                 replyString << ListTemplates();
1631                 replyString << TEXT("\r\n");
1632
1633                 SetReplyString(replyString.str());
1634         }
1635         catch(...)
1636         {
1637                 CASPAR_LOG_CURRENT_EXCEPTION();
1638                 SetReplyString(TEXT("501 TLS FAILED\r\n"));
1639                 return false;
1640         }
1641         return true;
1642 }
1643
1644 bool VersionCommand::DoExecute()
1645 {
1646         std::wstring replyString = TEXT("201 VERSION OK\r\n") + env::version() + TEXT("\r\n");
1647
1648         if(parameters().size() > 0)
1649         {
1650                 if(boost::iequals(parameters()[0], L"FLASH"))
1651                         replyString = TEXT("201 VERSION OK\r\n") + flash::version() + TEXT("\r\n");
1652                 else if(boost::iequals(parameters()[0], L"TEMPLATEHOST"))
1653                         replyString = TEXT("201 VERSION OK\r\n") + flash::cg_version() + TEXT("\r\n");
1654                 else if(!boost::iequals(parameters()[0], L"SERVER"))
1655                         replyString = TEXT("403 VERSION ERROR\r\n");
1656         }
1657
1658         SetReplyString(replyString);
1659         return true;
1660 }
1661
1662 bool ByeCommand::DoExecute()
1663 {
1664         GetClientInfo()->Disconnect();
1665         return true;
1666 }
1667
1668 bool SetCommand::DoExecute()
1669 {
1670         try
1671         {
1672                 std::wstring name = boost::to_upper_copy(parameters()[0]);
1673                 std::wstring value = boost::to_upper_copy(parameters()[1]);
1674
1675                 if(name == TEXT("MODE"))
1676                 {
1677                         auto format_desc = core::video_format_desc(value);
1678                         if(format_desc.format != core::video_format::invalid)
1679                         {
1680                                 GetChannel()->video_format_desc(format_desc);
1681                                 SetReplyString(TEXT("202 SET MODE OK\r\n"));
1682                         }
1683                         else
1684                                 SetReplyString(TEXT("501 SET MODE FAILED\r\n"));
1685                 }
1686                 else
1687                 {
1688                         this->SetReplyString(TEXT("403 SET ERROR\r\n"));
1689                 }
1690         }
1691         catch(...)
1692         {
1693                 CASPAR_LOG_CURRENT_EXCEPTION();
1694                 SetReplyString(TEXT("501 SET FAILED\r\n"));
1695                 return false;
1696         }
1697
1698         return true;
1699 }
1700
1701
1702 }       //namespace amcp
1703 }}      //namespace caspar