X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=protocol%2Famcp%2FAMCPCommandsImpl.cpp;h=eaa3f35c6f85079f7076609aa3dda7dd341a6f16;hb=8c61df76c4b35ab58316f35b6a09a3f977efd596;hp=8f62d463dbc092c183f709d000cba4e3c248e04b;hpb=435cf4b385c5099270bee44f89c3e2615af30521;p=casparcg diff --git a/protocol/amcp/AMCPCommandsImpl.cpp b/protocol/amcp/AMCPCommandsImpl.cpp index 8f62d463d..eaa3f35c6 100644 --- a/protocol/amcp/AMCPCommandsImpl.cpp +++ b/protocol/amcp/AMCPCommandsImpl.cpp @@ -1,1573 +1,1653 @@ -/* -* Copyright (c) 2011 Sveriges Television AB -* -* This file is part of CasparCG (www.casparcg.com). -* -* CasparCG is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* CasparCG is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with CasparCG. If not, see . -* -* Author: Nicklas P Andersson -*/ - -#include "../StdAfx.h" - -#if defined(_MSC_VER) -#pragma warning (push, 1) // TODO: Legacy code, just disable warnings -#endif - -#include "AMCPCommandsImpl.h" -#include "AMCPProtocolStrategy.h" - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -/* Return codes - -100 [action] Information om att något har hänt -101 [action] Information om att något har hänt, en rad data skickas - -202 [kommando] OK Kommandot har utförts -201 [kommando] OK Kommandot har utförts, och en rad data skickas tillbaka -200 [kommando] OK Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad - -400 ERROR Kommandot kunde inte förstås -401 [kommando] ERROR Ogiltig kanal -402 [kommando] ERROR Parameter saknas -403 [kommando] ERROR Ogiltig parameter -404 [kommando] ERROR Mediafilen hittades inte - -500 FAILED Internt configurationfel -501 [kommando] FAILED Internt configurationfel -502 [kommando] FAILED Oläslig mediafil - -600 [kommando] FAILED funktion ej implementerad -*/ - -namespace caspar { namespace protocol { - -using namespace core; - -std::wstring MediaInfo(const boost::filesystem::path& path) -{ - if(boost::filesystem::is_regular_file(path)) - { - std::wstring clipttype = TEXT(" N/A "); - std::wstring extension = boost::to_upper_copy(path.extension().wstring()); - if(extension == TEXT(".TGA") || extension == TEXT(".COL") || extension == L".PNG" || extension == L".JPEG" || extension == L".JPG" || - extension == L"GIF" || extension == L"BMP") - clipttype = TEXT(" STILL "); - else if(extension == TEXT(".WAV") || extension == TEXT(".MP3")) - clipttype = TEXT(" STILL "); - else if(caspar::ffmpeg::is_valid_file(path.wstring()) || extension == L".CT") - clipttype = TEXT(" MOVIE "); - - if(clipttype != TEXT(" N/A ")) - { - auto is_not_digit = [](char c){ return std::isdigit(c) == 0; }; - - auto relativePath = boost::filesystem::path(path.wstring().substr(env::media_folder().size()-1, path.wstring().size())); - - auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(path))); - writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end()); - auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end()); - - auto sizeStr = boost::lexical_cast(boost::filesystem::file_size(path)); - sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end()); - auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end()); - - auto str = relativePath.replace_extension(TEXT("")).native(); - while(str.size() > 0 && (str[0] == '\\' || str[0] == '/')) - str = std::wstring(str.begin() + 1, str.end()); - - return std::wstring() + TEXT("\"") + str + - + TEXT("\" ") + clipttype + - + TEXT(" ") + sizeStr + - + TEXT(" ") + writeTimeWStr + - + TEXT("\r\n"); - } - } - return L""; -} - -std::wstring ListMedia() -{ - std::wstringstream replyString; - for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr) - replyString << MediaInfo(itr->path()); - - return boost::to_upper_copy(replyString.str()); -} - -std::wstring ListTemplates() -{ - std::wstringstream replyString; - - for (boost::filesystem::recursive_directory_iterator itr(env::template_folder()), end; itr != end; ++itr) - { - if(boost::filesystem::is_regular_file(itr->path()) && (itr->path().extension() == L".ft" || itr->path().extension() == L".ct")) - { - auto relativePath = boost::filesystem::wpath(itr->path().wstring().substr(env::template_folder().size()-1, itr->path().wstring().size())); - - auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(itr->path()))); - writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), [](char c){ return std::isdigit(c) == 0;}), writeTimeStr.end()); - auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end()); - - auto sizeStr = boost::lexical_cast(boost::filesystem::file_size(itr->path())); - sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), [](char c){ return std::isdigit(c) == 0;}), sizeStr.end()); - - auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end()); - - std::wstring dir = relativePath.parent_path().native(); - std::wstring file = boost::to_upper_copy(relativePath.filename().wstring()); - relativePath = boost::filesystem::wpath(dir + L"/" + file); - - auto str = relativePath.replace_extension(TEXT("")).native(); - while(str.size() > 0 && (str[0] == '\\' || str[0] == '/')) - str = std::wstring(str.begin() + 1, str.end()); - - replyString << TEXT("\"") << str - << TEXT("\" ") << sizeWStr - << TEXT(" ") << writeTimeWStr - << TEXT("\r\n"); - } - } - return replyString.str(); -} - -namespace amcp { - -AMCPCommand::AMCPCommand() : channelIndex_(0), scheduling_(Default), layerIndex_(-1) -{} - -void AMCPCommand::SendReply() -{ - if(!pClientInfo_) - return; - - if(replyString_.empty()) - return; - pClientInfo_->Send(replyString_); -} - -void AMCPCommand::Clear() -{ - pChannel_->stage().clear(); - pClientInfo_.reset(); - channelIndex_ = 0; - _parameters.clear(); -} - -bool DiagnosticsCommand::DoExecute() -{ - try - { - diagnostics::show_graphs(true); - - SetReplyString(TEXT("202 DIAG OK\r\n")); - - return true; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("502 DIAG FAILED\r\n")); - return false; - } -} - -bool ChannelGridCommand::DoExecute() -{ - int index = 1; - auto self = GetChannels().back(); - - std::vector params; - params.push_back(L"SCREEN"); - params.push_back(L"NAME"); - params.push_back(L"Channel Grid Window"); - auto screen = create_consumer(params); - - self->output().add(screen); - - BOOST_FOREACH(auto channel, GetChannels()) - { - if(channel != self) - { - auto producer = reroute::create_producer(self->frame_factory(), *channel); - self->stage().load(index, producer, false); - self->stage().play(index); - index++; - } - } - - int n = GetChannels().size()-1; - double delta = 1.0/static_cast(n); - for(int x = 0; x < n; ++x) - { - for(int y = 0; y < n; ++y) - { - int index = x+y*n+1; - auto transform = [=](frame_transform transform) -> frame_transform - { - transform.image_transform.fill_translation[0] = x*delta; - transform.image_transform.fill_translation[1] = y*delta; - transform.image_transform.fill_scale[0] = delta; - transform.image_transform.fill_scale[1] = delta; - transform.image_transform.clip_translation[0] = x*delta; - transform.image_transform.clip_translation[1] = y*delta; - transform.image_transform.clip_scale[0] = delta; - transform.image_transform.clip_scale[1] = delta; - return transform; - }; - self->stage().apply_transform(index, transform); - } - } - - return true; -} - -bool CallCommand::DoExecute() -{ - //Perform loading of the clip - try - { - auto what = _parameters.at(0); - - boost::unique_future result; - if(what == L"B" || what == L"F") - { - std::wstring param; - for(auto it = std::begin(_parameters2)+1; it != std::end(_parameters2); ++it, param += L" ") - param += *it; - result = (what == L"F" ? GetChannel()->stage().foreground(GetLayerIndex()) : GetChannel()->stage().background(GetLayerIndex())).get()->call(boost::trim_copy(param)); - } - else - { - std::wstring param; - for(auto it = std::begin(_parameters2); it != std::end(_parameters2); ++it, param += L" ") - param += *it; - result = GetChannel()->stage().foreground(GetLayerIndex()).get()->call(boost::trim_copy(param)); - } - - if(!result.timed_wait(boost::posix_time::seconds(2))) - BOOST_THROW_EXCEPTION(timed_out()); - - std::wstringstream replyString; - if(result.get().empty()) - replyString << TEXT("202 CALL OK\r\n"); - else - replyString << TEXT("201 CALL OK\r\n") << result.get() << L"\r\n"; - - SetReplyString(replyString.str()); - - return true; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("502 CALL FAILED\r\n")); - return false; - } -} - -tbb::concurrent_unordered_map> deferred_transforms; - -bool MixerCommand::DoExecute() -{ - //Perform loading of the clip - try - { - bool defer = _parameters.back() == L"DEFER"; - if(defer) - _parameters.pop_back(); - - std::vector transforms; - - if(_parameters[0] == L"KEYER" || _parameters[0] == L"IS_KEY") - { - bool value = boost::lexical_cast(_parameters.at(1)); - transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.image_transform.is_key = value; - return transform; - }, 0, L"linear")); - } - else if(_parameters[0] == L"OPACITY") - { - int duration = _parameters.size() > 2 ? boost::lexical_cast(_parameters[2]) : 0; - std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear"; - - double value = boost::lexical_cast(_parameters.at(1)); - - transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.image_transform.opacity = value; - return transform; - }, duration, tween)); - } - else if(_parameters[0] == L"FILL" || _parameters[0] == L"FILL_RECT") - { - int duration = _parameters.size() > 5 ? boost::lexical_cast(_parameters[5]) : 0; - std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear"; - double x = boost::lexical_cast(_parameters.at(1)); - double y = boost::lexical_cast(_parameters.at(2)); - double x_s = boost::lexical_cast(_parameters.at(3)); - double y_s = boost::lexical_cast(_parameters.at(4)); - - transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) mutable -> frame_transform - { - transform.image_transform.fill_translation[0] = x; - transform.image_transform.fill_translation[1] = y; - transform.image_transform.fill_scale[0] = x_s; - transform.image_transform.fill_scale[1] = y_s; - transform.image_transform.clip_translation[0] = x; - transform.image_transform.clip_translation[1] = y; - transform.image_transform.clip_scale[0] = x_s; - transform.image_transform.clip_scale[1] = y_s; - return transform; - }, duration, tween)); - } - else if(_parameters[0] == L"CLIP" || _parameters[0] == L"CLIP_RECT") - { - int duration = _parameters.size() > 5 ? boost::lexical_cast(_parameters[5]) : 0; - std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear"; - double x = boost::lexical_cast(_parameters.at(1)); - double y = boost::lexical_cast(_parameters.at(2)); - double x_s = boost::lexical_cast(_parameters.at(3)); - double y_s = boost::lexical_cast(_parameters.at(4)); - - transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.image_transform.clip_translation[0] = x; - transform.image_transform.clip_translation[1] = y; - transform.image_transform.clip_scale[0] = x_s; - transform.image_transform.clip_scale[1] = y_s; - return transform; - }, duration, tween)); - } - else if(_parameters[0] == L"GRID") - { - int duration = _parameters.size() > 2 ? boost::lexical_cast(_parameters[2]) : 0; - std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear"; - int n = boost::lexical_cast(_parameters.at(1)); - double delta = 1.0/static_cast(n); - for(int x = 0; x < n; ++x) - { - for(int y = 0; y < n; ++y) - { - int index = x+y*n+1; - transforms.push_back(stage::transform_tuple_t(index, [=](frame_transform transform) -> frame_transform - { - transform.image_transform.fill_translation[0] = x*delta; - transform.image_transform.fill_translation[1] = y*delta; - transform.image_transform.fill_scale[0] = delta; - transform.image_transform.fill_scale[1] = delta; - transform.image_transform.clip_translation[0] = x*delta; - transform.image_transform.clip_translation[1] = y*delta; - transform.image_transform.clip_scale[0] = delta; - transform.image_transform.clip_scale[1] = delta; - return transform; - }, duration, tween)); - } - } - } - else if(_parameters[0] == L"BLEND") - { - auto blend_str = _parameters.at(1); - int layer = GetLayerIndex(); - GetChannel()->mixer().set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str)); - } - else if(_parameters[0] == L"BRIGHTNESS") - { - auto value = boost::lexical_cast(_parameters.at(1)); - int duration = _parameters.size() > 2 ? boost::lexical_cast(_parameters[2]) : 0; - std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear"; - auto transform = stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.image_transform.brightness = value; - return transform; - }, duration, tween); - } - else if(_parameters[0] == L"SATURATION") - { - auto value = boost::lexical_cast(_parameters.at(1)); - int duration = _parameters.size() > 2 ? boost::lexical_cast(_parameters[2]) : 0; - std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear"; - auto transform = stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.image_transform.saturation = value; - return transform; - }, duration, tween); - } - else if(_parameters[0] == L"CONTRAST") - { - auto value = boost::lexical_cast(_parameters.at(1)); - int duration = _parameters.size() > 2 ? boost::lexical_cast(_parameters[2]) : 0; - std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear"; - auto transform = stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.image_transform.contrast = value; - return transform; - }, duration, tween); - } - else if(_parameters[0] == L"LEVELS") - { - levels value; - value.min_input = boost::lexical_cast(_parameters.at(1)); - value.max_input = boost::lexical_cast(_parameters.at(2)); - value.gamma = boost::lexical_cast(_parameters.at(3)); - value.min_output = boost::lexical_cast(_parameters.at(4)); - value.max_output = boost::lexical_cast(_parameters.at(5)); - int duration = _parameters.size() > 6 ? boost::lexical_cast(_parameters[6]) : 0; - std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear"; - - auto transform = stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.image_transform.levels = value; - return transform; - }, duration, tween); - } - else if(_parameters[0] == L"VOLUME") - { - int duration = _parameters.size() > 2 ? boost::lexical_cast(_parameters[2]) : 0; - std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear"; - double value = boost::lexical_cast(_parameters[1]); - - auto transform = stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform - { - transform.audio_transform.volume = value; - return transform; - }, duration, tween); - } - else if(_parameters[0] == L"CLEAR") - { - int layer = GetLayerIndex(std::numeric_limits::max()); - if(layer == std::numeric_limits::max()) - GetChannel()->stage().clear_transforms(); - else - GetChannel()->stage().clear_transforms(layer); - } - else if(_parameters[0] == L"COMMIT") - { - transforms = std::move(deferred_transforms[GetChannelIndex()]); - } - else - { - SetReplyString(TEXT("404 MIXER ERROR\r\n")); - return false; - } - - if(defer) - { - auto& defer_tranforms = deferred_transforms[GetChannelIndex()]; - defer_tranforms.insert(defer_tranforms.end(), transforms.begin(), transforms.end()); - } - else - GetChannel()->stage().apply_transforms(transforms); - - SetReplyString(TEXT("202 MIXER OK\r\n")); - - return true; - } - catch(file_not_found&) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("404 MIXER ERROR\r\n")); - return false; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("502 MIXER FAILED\r\n")); - return false; - } -} - -bool SwapCommand::DoExecute() -{ - //Perform loading of the clip - try - { - if(GetLayerIndex(-1) != -1) - { - std::vector strs; - boost::split(strs, _parameters[0], boost::is_any_of("-")); - - auto ch1 = GetChannel(); - auto ch2 = GetChannels().at(boost::lexical_cast(strs.at(0))-1); - - int l1 = GetLayerIndex(); - int l2 = boost::lexical_cast(strs.at(1)); - - ch1->stage().swap_layer(l1, l2, ch2->stage()); - } - else - { - auto ch1 = GetChannel(); - auto ch2 = GetChannels().at(boost::lexical_cast(_parameters[0])-1); - ch1->stage().swap_layers(ch2->stage()); - } - - SetReplyString(TEXT("202 SWAP OK\r\n")); - - return true; - } - catch(file_not_found&) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("404 SWAP ERROR\r\n")); - return false; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("502 SWAP FAILED\r\n")); - return false; - } -} - -bool AddCommand::DoExecute() -{ - //Perform loading of the clip - try - { - auto consumer = create_consumer(_parameters); - GetChannel()->output().add(GetLayerIndex(consumer->index()), consumer); - - SetReplyString(TEXT("202 ADD OK\r\n")); - - return true; - } - catch(file_not_found&) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("404 ADD ERROR\r\n")); - return false; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("502 ADD FAILED\r\n")); - return false; - } -} - -bool RemoveCommand::DoExecute() -{ - //Perform loading of the clip - try - { - auto index = GetLayerIndex(std::numeric_limits::min()); - if(index == std::numeric_limits::min()) - index = create_consumer(_parameters)->index(); - - GetChannel()->output().remove(index); - - SetReplyString(TEXT("202 REMOVE OK\r\n")); - - return true; - } - catch(file_not_found&) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("404 REMOVE ERROR\r\n")); - return false; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("502 REMOVE FAILED\r\n")); - return false; - } -} - -bool LoadCommand::DoExecute() -{ - //Perform loading of the clip - try - { - _parameters[0] = _parameters[0]; - auto pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), _parameters); - GetChannel()->stage().load(GetLayerIndex(), pFP, true); - - SetReplyString(TEXT("202 LOAD OK\r\n")); - - return true; - } - catch(file_not_found&) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("404 LOAD ERROR\r\n")); - return false; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - SetReplyString(TEXT("502 LOAD FAILED\r\n")); - return false; - } -} - - - -//std::function channel_cg_add_command::parse(const std::wstring& message, const std::vector& channels) -//{ -// static boost::wregex expr(L"^CG\\s(?\\d+)-?(?\\d+)?\\sADD\\s(?\\d+)\\s(?