X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fvideo_channel.cpp;h=a0ac2c2012fd249ac76d0fcd85a8483826ee7e86;hb=9e4b08cde6c6de9e83a3fff42d90affc3cd8e5bc;hp=10b4d5864b6d39810dd30e66e07f8e41b0353865;hpb=65199f18ae5ec831d809ea73b6e9dd01f530b4c5;p=casparcg diff --git a/core/video_channel.cpp b/core/video_channel.cpp index 10b4d5864..a0ac2c201 100644 --- a/core/video_channel.cpp +++ b/core/video_channel.cpp @@ -1,163 +1,293 @@ -/* -* copyright (c) 2010 Sveriges Television AB -* -* This file is part of CasparCG. -* -* 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 . -* -*/ - -#include "StdAfx.h" - -#include "video_channel.h" - -#include "video_channel_context.h" -#include "video_format.h" - -#include "consumer/output.h" -#include "mixer/mixer.h" -#include "producer/stage.h" - -#include -#include - -#include "mixer/gpu/ogl_device.h" - -#include - -#ifdef _MSC_VER -#pragma warning(disable : 4355) -#endif - -namespace caspar { namespace core { - -struct video_channel::implementation : boost::noncopyable -{ - video_channel_context context_; - - safe_ptr output_; - std::shared_ptr mixer_; - safe_ptr stage_; - - safe_ptr diag_; - boost::timer frame_timer_; - boost::timer tick_timer_; - boost::timer output_timer_; - -public: - implementation(int index, const video_format_desc& format_desc, ogl_device& ogl) - : context_(index, ogl, format_desc) - , output_(new caspar::core::output(context_, [this]{restart();})) - , mixer_(new caspar::core::mixer(context_)) - , stage_(new caspar::core::stage(context_)) - { - diag_->add_guide("produce-time", 0.5f); - diag_->set_color("produce-time", diagnostics::color(0.0f, 1.0f, 0.0f)); - diag_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); - diag_->set_color("output-time", diagnostics::color(1.0f, 0.5f, 0.0f)); - diag_->set_color("mix-time", diagnostics::color(1.0f, 1.0f, 0.9f)); - diag_->set_text(print()); - diagnostics::register_graph(diag_); - - CASPAR_LOG(info) << print() << " Successfully Initialized."; - context_.execution().begin_invoke([this]{tick();}); - } - - ~implementation() - { - // Stop context before destroying devices. - context_.execution().stop(); - context_.execution().join(); - context_.destruction().stop(); - context_.destruction().join(); - } - - void tick() - { - try - { - // Produce - - frame_timer_.restart(); - - auto simple_frames = stage_->execute(); - - diag_->update_value("produce-time", frame_timer_.elapsed()*context_.get_format_desc().fps*0.5); - - // Mix - - frame_timer_.restart(); - - auto finished_frame = mixer_->execute(simple_frames); - - diag_->update_value("mix-time", frame_timer_.elapsed()*context_.get_format_desc().fps*0.5); - - // Consume - - output_timer_.restart(); - - output_->execute(finished_frame); - - diag_->update_value("output-time", frame_timer_.elapsed()*context_.get_format_desc().fps*0.5); - - - diag_->update_value("tick-time", tick_timer_.elapsed()*context_.get_format_desc().fps*0.5); - tick_timer_.restart(); - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - CASPAR_LOG(error) << context_.print() << L" Unexpected exception. Clearing stage and freeing memory"; - restart(); - } - - context_.execution().begin_invoke([this]{tick();}); - } - - void restart() - { - stage_->clear(); - context_.ogl().gc().wait(); - - mixer_ = nullptr; - mixer_.reset(new caspar::core::mixer(context_)); - } - - std::wstring print() const - { - return context_.print(); - } - - void set_video_format_desc(const video_format_desc& format_desc) - { - context_.execution().begin_invoke([=] - { - stage_->clear(); - context_.ogl().gc().wait(); - context_.set_format_desc(format_desc); - }); - } -}; - -video_channel::video_channel(int index, const video_format_desc& format_desc, ogl_device& ogl) : impl_(new implementation(index, format_desc, ogl)){} -video_channel::video_channel(video_channel&& other) : impl_(std::move(other.impl_)){} -safe_ptr video_channel::stage() { return impl_->stage_;} -safe_ptr video_channel::mixer() { return make_safe_ptr(impl_->mixer_);} -safe_ptr video_channel::output() { return impl_->output_;} -video_format_desc video_channel::get_video_format_desc() const{return impl_->context_.get_format_desc();} -void video_channel::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);} -std::wstring video_channel::print() const { return impl_->print();} -video_channel_context& video_channel::context(){return impl_->context_;} - -}} \ No newline at end of file +/* +* 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: Robert Nagy, ronag89@gmail.com +*/ + +#include "StdAfx.h" + +#include "video_channel.h" + +#include "video_format.h" + +#include "producer/stage.h" +#include "mixer/mixer.h" +#include "consumer/output.h" +#include "frame/frame.h" +#include "frame/draw_frame.h" +#include "frame/frame_factory.h" +#include "frame/audio_channel_layout.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +namespace caspar { namespace core { + +struct video_channel::impl final +{ + spl::shared_ptr monitor_subject_; + + const int index_; + + mutable tbb::spin_mutex format_desc_mutex_; + core::video_format_desc format_desc_; + mutable tbb::spin_mutex channel_layout_mutex_; + core::audio_channel_layout channel_layout_; + + const spl::shared_ptr graph_ = [](int index) + { + core::diagnostics::scoped_call_context save; + core::diagnostics::call_context::for_thread().video_channel = index; + return spl::make_shared(); + }(index_); + + caspar::core::output output_; + std::future output_ready_for_frame_ = make_ready_future(); + spl::shared_ptr image_mixer_; + caspar::core::mixer mixer_; + caspar::core::stage stage_; + + mutable tbb::spin_mutex tick_listeners_mutex_; + int64_t last_tick_listener_id = 0; + std::unordered_map> tick_listeners_; + + executor executor_ { L"video_channel " + boost::lexical_cast(index_) }; +public: + impl( + int index, + const core::video_format_desc& format_desc, + const core::audio_channel_layout& channel_layout, + std::unique_ptr image_mixer) + : monitor_subject_(spl::make_shared( + "/channel/" + boost::lexical_cast(index))) + , index_(index) + , format_desc_(format_desc) + , channel_layout_(channel_layout) + , output_(graph_, format_desc, channel_layout, index) + , image_mixer_(std::move(image_mixer)) + , mixer_(index, graph_, image_mixer_) + , stage_(index, graph_) + { + graph_->set_color("tick-time", caspar::diagnostics::color(0.0f, 0.6f, 0.9f)); + graph_->set_text(print()); + caspar::diagnostics::register_graph(graph_); + + output_.monitor_output().attach_parent(monitor_subject_); + mixer_.monitor_output().attach_parent(monitor_subject_); + stage_.monitor_output().attach_parent(monitor_subject_); + + executor_.begin_invoke([=]{tick();}); + + CASPAR_LOG(info) << print() << " Successfully Initialized."; + } + + ~impl() + { + CASPAR_LOG(info) << print() << " Uninitializing."; + } + + core::video_format_desc video_format_desc() const + { + return lock(format_desc_mutex_, [&] + { + return format_desc_; + }); + } + + void video_format_desc(const core::video_format_desc& format_desc) + { + lock(format_desc_mutex_, [&] + { + format_desc_ = format_desc; + stage_.clear(); + }); + } + + core::audio_channel_layout audio_channel_layout() const + { + return lock(channel_layout_mutex_, [&] + { + return channel_layout_; + }); + } + + void audio_channel_layout(const core::audio_channel_layout& channel_layout) + { + lock(channel_layout_mutex_, [&] + { + channel_layout_ = channel_layout; + stage_.clear(); + }); + } + + void invoke_tick_listeners() + { + auto listeners = lock(tick_listeners_mutex_, [=] { return tick_listeners_; }); + + for (auto listener : listeners) + { + try + { + listener.second(); + } + catch (...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + } + } + } + + void tick() + { + try + { + invoke_tick_listeners(); + + auto format_desc = video_format_desc(); + auto channel_layout = audio_channel_layout(); + + caspar::timer frame_timer; + + // Produce + + auto stage_frames = stage_(format_desc); + + // Mix + + auto mixed_frame = mixer_(std::move(stage_frames), format_desc, channel_layout); + + // Consume + + output_ready_for_frame_ = output_(std::move(mixed_frame), format_desc, channel_layout); + output_ready_for_frame_.get(); + + auto frame_time = frame_timer.elapsed()*format_desc.fps*0.5; + graph_->set_value("tick-time", frame_time); + + *monitor_subject_ << monitor::message("/profiler/time") % frame_timer.elapsed() % (1.0/ video_format_desc().fps) + << monitor::message("/format") % format_desc.name; + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + } + + if (executor_.is_running()) + executor_.begin_invoke([=]{tick();}); + } + + std::wstring print() const + { + return L"video_channel[" + boost::lexical_cast(index_) + L"|" + video_format_desc().name + L"]"; + } + + int index() const + { + return index_; + } + + boost::property_tree::wptree info() const + { + boost::property_tree::wptree info; + + auto stage_info = stage_.info(); + auto mixer_info = mixer_.info(); + auto output_info = output_.info(); + + info.add(L"video-mode", video_format_desc().name); + info.add(L"audio-channel-layout", audio_channel_layout().print()); + info.add_child(L"stage", stage_info.get()); + info.add_child(L"mixer", mixer_info.get()); + info.add_child(L"output", output_info.get()); + + return info; + } + + boost::property_tree::wptree delay_info() const + { + boost::property_tree::wptree info; + + auto stage_info = stage_.delay_info(); + auto mixer_info = mixer_.delay_info(); + auto output_info = output_.delay_info(); + + // TODO: because of std::async deferred timed waiting does not work so for now we have to block + info.add_child(L"layers", stage_info.get()); + info.add_child(L"mix-time", mixer_info.get()); + info.add_child(L"output", output_info.get()); + + return info; + } + + std::shared_ptr add_tick_listener(std::function listener) + { + return lock(tick_listeners_mutex_, [&] + { + auto tick_listener_id = last_tick_listener_id++; + tick_listeners_.insert(std::make_pair(tick_listener_id, listener)); + + return std::shared_ptr(nullptr, [=](void*) + { + lock(tick_listeners_mutex_, [&] + { + tick_listeners_.erase(tick_listener_id); + }); + }); + }); + } +}; + +video_channel::video_channel( + int index, + const core::video_format_desc& format_desc, + const core::audio_channel_layout& channel_layout, + std::unique_ptr image_mixer) : impl_(new impl(index, format_desc, channel_layout, std::move(image_mixer))){} +video_channel::~video_channel(){} +const stage& video_channel::stage() const { return impl_->stage_;} +stage& video_channel::stage() { return impl_->stage_;} +const mixer& video_channel::mixer() const{ return impl_->mixer_;} +mixer& video_channel::mixer() { return impl_->mixer_;} +const output& video_channel::output() const { return impl_->output_;} +output& video_channel::output() { return impl_->output_;} +spl::shared_ptr video_channel::frame_factory() { return impl_->image_mixer_;} +core::video_format_desc video_channel::video_format_desc() const{return impl_->video_format_desc();} +void core::video_channel::video_format_desc(const core::video_format_desc& format_desc){impl_->video_format_desc(format_desc);} +core::audio_channel_layout video_channel::audio_channel_layout() const { return impl_->audio_channel_layout(); } +void core::video_channel::audio_channel_layout(const core::audio_channel_layout& channel_layout) { impl_->audio_channel_layout(channel_layout); } +boost::property_tree::wptree video_channel::info() const{return impl_->info();} +boost::property_tree::wptree video_channel::delay_info() const { return impl_->delay_info(); } +int video_channel::index() const { return impl_->index(); } +monitor::subject& video_channel::monitor_output(){ return *impl_->monitor_subject_; } +std::shared_ptr video_channel::add_tick_listener(std::function listener) { return impl_->add_tick_listener(std::move(listener)); } + +}}