}\r
\r
template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4>\r
-safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&&)\r
+safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4)\r
{\r
- return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P3>(p4)));\r
+ return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4)));\r
}\r
\r
}
\ No newline at end of file
{\r
return executor_.begin_invoke([=]{return layers_[index].foreground();});\r
}\r
+ \r
+ boost::unique_future<safe_ptr<frame_producer>> background(int index)\r
+ {\r
+ return executor_.begin_invoke([=]{return layers_[index].background();});\r
+ }\r
};\r
\r
frame_producer_device::frame_producer_device(const video_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
void frame_producer_device::clear(){impl_->clear();}\r
void frame_producer_device::swap_layer(int index, size_t other_index){impl_->swap_layer(index, other_index);}\r
void frame_producer_device::swap_layer(int index, size_t other_index, frame_producer_device& other){impl_->swap_layer(index, other_index, other);}\r
-boost::unique_future<safe_ptr<frame_producer>> frame_producer_device::foreground(size_t index) { return impl_->foreground(index);}\r
+boost::unique_future<safe_ptr<frame_producer>> frame_producer_device::foreground(size_t index) {return impl_->foreground(index);}\r
+boost::unique_future<safe_ptr<frame_producer>> frame_producer_device::background(size_t index) {return impl_->background(index);}\r
}}
\ No newline at end of file
void swap_layer(int index, size_t other_index);\r
void swap_layer(int index, size_t other_index, frame_producer_device& other);\r
boost::unique_future<safe_ptr<frame_producer>> foreground(size_t index);\r
+ boost::unique_future<safe_ptr<frame_producer>> background(size_t index);\r
\r
private:\r
struct implementation;\r
void play(); // nothrow\r
void pause(); // nothrow\r
void stop(); // nothrow\r
+ void param(const std::wstring& param);\r
\r
safe_ptr<frame_producer> foreground() const; // nothrow\r
safe_ptr<frame_producer> background() const; // nothrow\r
struct separated_producer : public frame_producer\r
{ \r
safe_ptr<frame_producer> fill_producer_;\r
- safe_ptr<frame_producer> key_producer;\r
+ safe_ptr<frame_producer> key_producer_;\r
safe_ptr<basic_frame> last_fill_;\r
safe_ptr<basic_frame> last_key_;\r
safe_ptr<basic_frame> last_frame_;\r
\r
explicit separated_producer(const safe_ptr<frame_producer>& fill, const safe_ptr<frame_producer>& key) \r
: fill_producer_(fill)\r
- , key_producer(key)\r
+ , key_producer_(key)\r
, last_fill_(core::basic_frame::empty())\r
, last_key_(core::basic_frame::empty())\r
, last_frame_(core::basic_frame::empty()){}\r
[&]\r
{\r
if(last_key_ == core::basic_frame::empty())\r
- last_key_ = receive_and_follow(key_producer);\r
+ last_key_ = receive_and_follow(key_producer_);\r
}\r
);\r
\r
\r
safe_ptr<basic_frame> compose(const safe_ptr<basic_frame>& dest_frame, const safe_ptr<basic_frame>& src_frame) \r
{ \r
- if(dest_frame == basic_frame::eof() && src_frame == basic_frame::eof())\r
- return basic_frame::eof();\r
-\r
if(info_.type == transition::cut) \r
return src_frame;\r
\r
<ItemGroup>\r
<ClInclude Include="consumer\ffmpeg_consumer.h" />\r
<ClInclude Include="ffmpeg.h" />\r
+ <ClInclude Include="ffmpeg_error.h" />\r
<ClInclude Include="producer\audio\audio_decoder.h" />\r
<ClInclude Include="producer\ffmpeg_producer.h" />\r
<ClInclude Include="producer\input.h" />\r
</ClInclude>\r
<ClInclude Include="StdAfx.h" />\r
<ClInclude Include="ffmpeg.h" />\r
+ <ClInclude Include="ffmpeg_error.h" />\r
</ItemGroup>\r
</Project>
\ No newline at end of file
{\r
typedef std::vector<short, tbb::cache_aligned_allocator<short>> buffer;\r
\r
- AVCodecContext* codec_context_;\r
+ AVCodecContext* codec_context_;\r
\r
buffer audio_buffer_; \r
buffer current_chunk_;\r
BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("codec_context")); \r
}\r
\r
- std::vector<std::vector<short>> execute(const aligned_buffer& audio_packet)\r
- { \r
+ std::vector<std::vector<short>> execute(const std::shared_ptr<aligned_buffer>& audio_packet)\r
+ { \r
+ if(!audio_packet)\r
+ return std::vector<std::vector<short>>();\r
+\r
+ if(audio_packet->empty()) // Need to flush\r
+ {\r
+ avcodec_flush_buffers(codec_context_);\r
+ return std::vector<std::vector<short>>();\r
+ }\r
+\r
int written_bytes = audio_buffer_.size()*2 - FF_INPUT_BUFFER_PADDING_SIZE;\r
- const int errn = avcodec_decode_audio2(codec_context_, audio_buffer_.data(), &written_bytes, audio_packet.data(), audio_packet.size());\r
+ const int errn = avcodec_decode_audio2(codec_context_, audio_buffer_.data(), &written_bytes, audio_packet->data(), audio_packet->size());\r
\r
if(errn < 0 || codec_context_->sample_rate != SAMPLE_RATE || codec_context_->channels != 2)\r
{ \r
};\r
\r
audio_decoder::audio_decoder(AVCodecContext* codec_context, double fps) : impl_(new implementation(codec_context, fps)){}\r
-std::vector<std::vector<short>> audio_decoder::execute(const aligned_buffer& audio_packet){return impl_->execute(audio_packet);}\r
+std::vector<std::vector<short>> audio_decoder::execute(const std::shared_ptr<aligned_buffer>& audio_packet){return impl_->execute(audio_packet);}\r
}
\ No newline at end of file
{\r
public:\r
explicit audio_decoder(AVCodecContext* codec_context, double fps);\r
- std::vector<std::vector<short>> execute(const aligned_buffer& audio_packet);\r
+ std::vector<std::vector<short>> execute(const std::shared_ptr<aligned_buffer>& audio_packet);\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
std::unique_ptr<video_decoder> video_decoder_;\r
std::unique_ptr<audio_decoder> audio_decoder_;\r
public:\r
- explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, bool loop) \r
+ explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, bool loop, int start_frame, int end_frame) \r
: filename_(filename)\r
, loop_(loop) \r
, graph_(diagnostics::create_graph(narrow(print())))\r
, frame_factory_(frame_factory) \r
- , input_(safe_ptr<diagnostics::graph>(graph_), filename_, loop_)\r
+ , input_(safe_ptr<diagnostics::graph>(graph_), filename_, loop_, start_frame, end_frame)\r
, video_decoder_(input_.get_video_codec_context().get() ? new video_decoder(input_.get_video_codec_context().get(), frame_factory) : nullptr)\r
, audio_decoder_(input_.get_audio_codec_context().get() ? new audio_decoder(input_.get_audio_codec_context().get(), frame_factory->get_video_format_desc().fps) : nullptr)\r
{\r
return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"]";\r
}\r
\r
- void try_decode_video_packet(const aligned_buffer& video_packet)\r
+ void try_decode_video_packet(const std::shared_ptr<aligned_buffer>& video_packet)\r
{\r
- if(!video_packet.empty() && video_decoder_) // Video Decoding.\r
+ if(video_decoder_) // Video Decoding.\r
{\r
try\r
{\r
}\r
}\r
\r
- void try_decode_audio_packet(const aligned_buffer& audio_packet)\r
+ void try_decode_audio_packet(const std::shared_ptr<aligned_buffer>& audio_packet)\r
{\r
- if(audio_chunk_buffer_.size() < 3 && !audio_packet.empty() && audio_decoder_) // Audio Decoding.\r
+ if(audio_decoder_) // Audio Decoding.\r
{\r
try\r
{\r
\r
std::wstring path = filename + L"." + *ext;\r
bool loop = std::find(params.begin(), params.end(), L"LOOP") != params.end();\r
+\r
+ static const boost::wregex expr(L"\\((?<START>\\d+)(,(?<END>\\d+)?)?\\)");//(,(?<END>\\d+))?\\]"); // boost::regex has no repeated captures?\r
+ boost::wsmatch what;\r
+ auto it = std::find_if(params.begin(), params.end(), [&](const std::wstring& str)\r
+ {\r
+ return boost::regex_match(str, what, expr);\r
+ });\r
+\r
+ int start = -1;\r
+ int end = -1;\r
+\r
+ if(it != params.end())\r
+ {\r
+ start = lexical_cast_or_default(what["START"].str(), -1);\r
+ if(what["END"].matched)\r
+ end = lexical_cast_or_default(what["END"].str(), -1);\r
+ }\r
\r
- return make_safe<ffmpeg_producer>(frame_factory, path, loop);\r
+ return make_safe<ffmpeg_producer>(frame_factory, path, loop, start, end);\r
}\r
\r
}
\ No newline at end of file
#include "..\stdafx.h"\r
\r
#include "input.h"\r
+#include "../ffmpeg_error.h"\r
\r
#include <core/video_format.h>\r
\r
#include <boost/exception/error_info.hpp>\r
#include <boost/thread/once.hpp>\r
#include <boost/thread/thread.hpp>\r
+#include <boost/regex.hpp>\r
\r
#include <errno.h>\r
#include <system_error>\r
bool loop_;\r
int video_s_index_;\r
int audio_s_index_;\r
+ const int start_frame_;\r
+ const int end_frame_;\r
+ int eof_count_;\r
\r
tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> video_packet_buffer_;\r
tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> audio_packet_buffer_;\r
std::exception_ptr exception_;\r
executor executor_;\r
public:\r
- explicit implementation(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop) \r
+ explicit implementation(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start_frame, int end_frame) \r
: graph_(graph)\r
, loop_(loop)\r
, video_s_index_(-1)\r
, audio_s_index_(-1)\r
, filename_(filename)\r
, executor_(print())\r
+ , start_frame_(std::max(start_frame, 0))\r
+ , end_frame_(end_frame)\r
+ , eof_count_(end_frame-start_frame)\r
{ \r
+ if(end_frame_ > 0 && end_frame <= start_frame_)\r
+ BOOST_THROW_EXCEPTION(\r
+ invalid_argument() << \r
+ source_info(narrow(print())) << \r
+ msg_info("End-frame cannot be lower than start-frame.")); \r
+\r
graph_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f));\r
graph_->set_color("seek", diagnostics::color(0.5f, 1.0f, 0.5f)); \r
\r
BOOST_THROW_EXCEPTION(\r
file_read_error() << \r
source_info(narrow(print())) << \r
+ msg_info(av_error_str(errn)) <<\r
boost::errinfo_api_function("av_open_input_file") <<\r
boost::errinfo_errno(AVUNERROR(errn)) <<\r
boost::errinfo_file_name(narrow(filename)));\r
BOOST_THROW_EXCEPTION(\r
file_read_error() << \r
source_info(narrow(print())) << \r
+ msg_info(av_error_str(errn)) <<\r
boost::errinfo_api_function("av_find_stream_info") <<\r
boost::errinfo_errno(AVUNERROR(errn)));\r
\r
if(!video_codec_context_ && !audio_codex_context_)\r
BOOST_THROW_EXCEPTION(\r
file_read_error() << \r
+ msg_info(av_error_str(errn)) <<\r
source_info(narrow(print())) << \r
- msg_info("No video or audio codec context found.")); \r
- \r
+ msg_info("No video or audio codec context found.")); \r
+ \r
+ if(start_frame_ != 0) \r
+ seek_frame(start_frame_);\r
+ \r
executor_.start();\r
executor_.begin_invoke([this]{read_file();});\r
CASPAR_LOG(info) << print() << " Started.";\r
\r
if(stream == streams_end) \r
return nullptr;\r
-\r
- s_index = (*stream)->index;\r
\r
auto codec = avcodec_find_decoder((*stream)->codec->codec_id); \r
if(codec == nullptr)\r
\r
if((-avcodec_open((*stream)->codec, codec)) > 0) \r
return nullptr;\r
+ \r
+ s_index = (*stream)->index;\r
\r
return std::shared_ptr<AVCodecContext>((*stream)->codec, avcodec_close);\r
}\r
+ \r
+ std::shared_ptr<AVCodecContext>& get_default_context()\r
+ {\r
+ return video_codec_context_ ? video_codec_context_ : audio_codex_context_;\r
+ }\r
+\r
+ bool is_eof(int errn)\r
+ {\r
+ if(end_frame_ != -1)\r
+ return get_default_context()->frame_number > eof_count_; \r
+\r
+ return errn == AVERROR_EOF || errn == AVERROR_IO;\r
+ }\r
\r
void read_file()\r
{ \r
safe_ptr<AVPacket> read_packet(&tmp_packet, av_free_packet); \r
\r
auto read_frame_ret = av_read_frame(format_context_.get(), read_packet.get());\r
- if(read_frame_ret == AVERROR_EOF || read_frame_ret == AVERROR_IO)\r
+ if(is_eof(read_frame_ret))\r
{\r
if(loop_)\r
{\r
- auto seek_frame_ret = av_seek_frame(format_context_.get(), -1, 0, AVSEEK_FLAG_BACKWARD);\r
- if(seek_frame_ret >= 0)\r
- graph_->add_tag("seek");\r
- else\r
- {\r
- BOOST_THROW_EXCEPTION(\r
- invalid_operation() <<\r
- boost::errinfo_api_function("av_seek_frame") <<\r
- boost::errinfo_errno(AVUNERROR(seek_frame_ret)));\r
- } \r
+ seek_frame(start_frame_, AVSEEK_FLAG_BACKWARD);\r
+ // AVCodecContext.frame_number is not reset. Increase the target frame_number.\r
+ eof_count_ += end_frame_ - start_frame_; \r
+ graph_->add_tag("seek"); \r
} \r
else\r
stop();\r
{\r
BOOST_THROW_EXCEPTION(\r
invalid_operation() <<\r
+ msg_info(av_error_str(read_frame_ret)) <<\r
+ source_info(narrow(print())) << \r
boost::errinfo_api_function("av_read_frame") <<\r
boost::errinfo_errno(AVUNERROR(read_frame_ret)));\r
}\r
else if(read_packet->stream_index == audio_s_index_) \r
audio_packet_buffer_.try_push(std::move(packet)); \r
}\r
- \r
+ \r
graph_->update_value("input-buffer", static_cast<float>(video_packet_buffer_.size())/static_cast<float>(PACKET_BUFFER_COUNT)); \r
}\r
catch(...)\r
CASPAR_LOG_CURRENT_EXCEPTION();\r
return;\r
}\r
- \r
+ \r
executor_.begin_invoke([this]{read_file();}); \r
boost::unique_lock<boost::mutex> lock(mutex_);\r
while(executor_.is_running() && audio_packet_buffer_.size() > PACKET_BUFFER_COUNT && video_packet_buffer_.size() > PACKET_BUFFER_COUNT)\r
CASPAR_LOG(info) << print() << " eof";\r
}\r
\r
- aligned_buffer get_video_packet()\r
+ std::shared_ptr<aligned_buffer> get_video_packet()\r
{\r
return get_packet(video_packet_buffer_);\r
}\r
\r
- aligned_buffer get_audio_packet()\r
+ std::shared_ptr<aligned_buffer> get_audio_packet()\r
{\r
return get_packet(audio_packet_buffer_);\r
}\r
return !video_packet_buffer_.empty() || !audio_packet_buffer_.empty();\r
}\r
\r
- aligned_buffer get_packet(tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>>& buffer)\r
+ void seek_frame(int64_t frame, int flags = 0)\r
+ { \r
+ // Convert from frames into seconds.\r
+ auto ts = frame*static_cast<int64_t>((AV_TIME_BASE*get_default_context()->time_base.num) / get_default_context()->time_base.den);\r
+\r
+ const int errn = av_seek_frame(format_context_.get(), -1, ts, flags | AVSEEK_FLAG_FRAME);\r
+\r
+ if(errn < 0)\r
+ BOOST_THROW_EXCEPTION(\r
+ invalid_operation() << \r
+ source_info(narrow(print())) << \r
+ msg_info(av_error_str(errn)) <<\r
+ boost::errinfo_api_function("seek_frame") <<\r
+ boost::errinfo_errno(AVUNERROR(errn)));\r
+ \r
+ // Notify decoders to flush buffers.\r
+ video_packet_buffer_.try_push(std::make_shared<aligned_buffer>()); \r
+ audio_packet_buffer_.try_push(std::make_shared<aligned_buffer>());\r
+ }\r
+ \r
+ std::shared_ptr<aligned_buffer> get_packet(tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>>& buffer)\r
{\r
cond_.notify_all();\r
std::shared_ptr<aligned_buffer> packet;\r
- return buffer.try_pop(packet) ? std::move(*packet) : aligned_buffer();\r
+ return buffer.try_pop(packet) ? packet : nullptr;\r
}\r
\r
- double fps() const\r
+ double fps()\r
{\r
- return static_cast<double>(video_codec_context_->time_base.den) / static_cast<double>(video_codec_context_->time_base.num);\r
+ return static_cast<double>(get_default_context()->time_base.den) / static_cast<double>(get_default_context()->time_base.num);\r
}\r
\r
std::wstring print() const\r
{\r
- return L"ffmpeg_input";\r
+ return L"ffmpeg_input[" + filename_ + L"]";\r
}\r
};\r
\r
-input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop) : impl_(new implementation(graph, filename, loop)){}\r
+input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start_frame, int end_frame) : impl_(new implementation(graph, filename, loop, start_frame, end_frame)){}\r
const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_codec_context_;}\r
const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_codex_context_;}\r
bool input::has_packet() const{return impl_->has_packet();}\r
bool input::is_running() const {return impl_->executor_.is_running();}\r
-aligned_buffer input::get_video_packet(){return impl_->get_video_packet();}\r
-aligned_buffer input::get_audio_packet(){return impl_->get_audio_packet();}\r
+std::shared_ptr<aligned_buffer> input::get_video_packet(){return impl_->get_video_packet();}\r
+std::shared_ptr<aligned_buffer> input::get_audio_packet(){return impl_->get_audio_packet();}\r
double input::fps() const { return impl_->fps(); }\r
}
\ No newline at end of file
class input : boost::noncopyable\r
{\r
public:\r
- explicit input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop);\r
+ explicit input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start_frame, int end_frame);\r
const std::shared_ptr<AVCodecContext>& get_video_codec_context() const;\r
const std::shared_ptr<AVCodecContext>& get_audio_codec_context() const;\r
\r
- aligned_buffer get_video_packet();\r
- aligned_buffer get_audio_packet();\r
+ std::shared_ptr<aligned_buffer> get_video_packet();\r
+ std::shared_ptr<aligned_buffer> get_audio_packet();\r
\r
bool has_packet() const;\r
bool is_running() const;\r
#include "../../stdafx.h"\r
\r
#include "video_decoder.h"\r
+#include "../../ffmpeg_error.h"\r
\r
#include <common/memory/memcpy.h>\r
\r
\r
struct video_decoder::implementation : boost::noncopyable\r
{ \r
- std::shared_ptr<core::frame_factory> frame_factory_;\r
- std::shared_ptr<SwsContext> sws_context_;\r
-\r
- AVCodecContext* codec_context_;\r
-\r
- const int width_;\r
- const int height_;\r
- const PixelFormat pix_fmt_;\r
- core::pixel_format_desc desc_;\r
+ std::shared_ptr<SwsContext> sws_context_;\r
+ const std::shared_ptr<core::frame_factory> frame_factory_;\r
+ AVCodecContext* codec_context_;\r
+ const int width_;\r
+ const int height_;\r
+ const PixelFormat pix_fmt_;\r
+ core::pixel_format_desc desc_;\r
\r
public:\r
explicit implementation(AVCodecContext* codec_context, const safe_ptr<core::frame_factory>& frame_factory) \r
}\r
}\r
\r
- std::shared_ptr<core::write_frame> execute(void* tag, const aligned_buffer& video_packet)\r
+ std::shared_ptr<core::write_frame> execute(void* tag, const std::shared_ptr<aligned_buffer>& video_packet)\r
{ \r
+ if(!video_packet)\r
+ return nullptr;\r
+\r
+ if(video_packet->empty()) // Need to flush\r
+ {\r
+ avcodec_flush_buffers(codec_context_);\r
+ return nullptr;\r
+ }\r
+\r
safe_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
\r
int frame_finished = 0;\r
- const int errn = avcodec_decode_video(codec_context_, decoded_frame.get(), &frame_finished, video_packet.data(), video_packet.size());\r
+ const int errn = avcodec_decode_video(codec_context_, decoded_frame.get(), &frame_finished, video_packet->data(), video_packet->size());\r
\r
if(errn < 0)\r
{\r
BOOST_THROW_EXCEPTION(\r
invalid_operation() <<\r
+ msg_info(av_error_str(errn)) <<\r
boost::errinfo_api_function("avcodec_decode_video") <<\r
boost::errinfo_errno(AVUNERROR(errn)));\r
}\r
};\r
\r
video_decoder::video_decoder(AVCodecContext* codec_context, const safe_ptr<core::frame_factory>& frame_factory) : impl_(new implementation(codec_context, frame_factory)){}\r
-std::shared_ptr<core::write_frame> video_decoder::execute(void* tag, const aligned_buffer& video_packet){return impl_->execute(tag, video_packet);}\r
+std::shared_ptr<core::write_frame> video_decoder::execute(void* tag, const std::shared_ptr<aligned_buffer>& video_packet){return impl_->execute(tag, video_packet);}\r
\r
}
\ No newline at end of file
{\r
public:\r
explicit video_decoder(AVCodecContext* codec_context, const safe_ptr<core::frame_factory>& frame_factory);\r
- std::shared_ptr<core::write_frame> execute(void* tag, const aligned_buffer& video_packet); \r
+ std::shared_ptr<core::write_frame> execute(void* tag, const std::shared_ptr<aligned_buffer>& video_packet); \r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
_parameters.clear();\r
}\r
\r
+bool ParamCommand::DoExecute()\r
+{ \r
+ //Perform loading of the clip\r
+ try\r
+ {\r
+ auto what = _parameters.at(2);\r
+ if(what == L"B")\r
+ GetChannel()->producer()->background(GetLayerIndex()).get()->param(_parameters.at(3));\r
+ else if(what == L"F")\r
+ GetChannel()->producer()->foreground(GetLayerIndex()).get()->param(_parameters.at(3));\r
+ \r
+ CASPAR_LOG(info) << "Executed param: " << _parameters[0] << TEXT(" successfully");\r
+\r
+ SetReplyString(TEXT("202 PARAM OK\r\n"));\r
+\r
+ return true;\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ SetReplyString(TEXT("502 PARAM FAILED\r\n"));\r
+ return false;\r
+ }\r
+}\r
+\r
bool MixerCommand::DoExecute()\r
{ \r
//Perform loading of the clip\r
std::wstring ListTemplates();\r
\r
namespace amcp {\r
+ \r
+class ParamCommand : public AMCPCommandBase<true, AddToQueue, 2>\r
+{\r
+ std::wstring print() const { return L"ParamCommand";}\r
+ bool DoExecute();\r
+};\r
\r
class MixerCommand : public AMCPCommandBase<true, AddToQueue, 2>\r
{\r