X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fmixer%2Fmixer.cpp;h=4f269b144b528eb54cb3b03fbc69e16025e11b5d;hb=19a5caa33bc347ce2cdba576ac90a9359c9e5087;hp=c663d7d2250cdb222a3f35b585b102dcbd361c4e;hpb=1c1d1f8ace8c4128d192390afb2dc5f489b98d01;p=casparcg diff --git a/core/mixer/mixer.cpp b/core/mixer/mixer.cpp index c663d7d22..4f269b144 100644 --- a/core/mixer/mixer.cpp +++ b/core/mixer/mixer.cpp @@ -1,264 +1,161 @@ -/* -* 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 "mixer.h" - -#include "read_frame.h" -#include "write_frame.h" - -#include "audio/audio_mixer.h" -#include "image/image_mixer.h" - -#include "../video_channel_context.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include - -namespace caspar { namespace core { - -template -class tweened_transform -{ - T source_; - T dest_; - int duration_; - int time_; - tweener_t tweener_; -public: - tweened_transform() - : duration_(0) - , time_(0) - , tweener_(get_tweener(L"linear")){} - tweened_transform(const T& source, const T& dest, int duration, const std::wstring& tween = L"linear") - : source_(source) - , dest_(dest) - , duration_(duration) - , time_(0) - , tweener_(get_tweener(tween)){} - - T fetch() - { - return time_ == duration_ ? dest_ : tween(static_cast(time_), source_, dest_, static_cast(duration_), tweener_); - } - - T fetch_and_tick(int num) - { - time_ = std::min(time_+num, duration_); - return fetch(); - } -}; - -struct mixer::implementation : boost::noncopyable -{ - video_channel_context& channel_; - - audio_mixer audio_mixer_; - image_mixer image_mixer_; - - typedef std::unordered_map> image_transforms; - typedef std::unordered_map> audio_transforms; - - boost::fusion::map, - boost::fusion::pair> transforms_; - - std::unordered_map blend_modes_; - - std::queue>, std::vector>> buffer_; - - const size_t buffer_size_; - -public: - implementation(video_channel_context& video_channel) - : channel_(video_channel) - , audio_mixer_(channel_.get_format_desc()) - , image_mixer_(channel_) - , buffer_size_(env::properties().get("configuration.producers.buffer-depth", 1)) - { - CASPAR_LOG(info) << print() << L" Successfully initialized . Buffer-depth: " << buffer_size_; - } - - safe_ptr execute(const std::map>& frames) - { - try - { - decltype(mix_image(frames)) image; - decltype(mix_audio(frames)) audio; - - tbb::parallel_invoke - ( - [&]{image = mix_image(frames);}, - [&]{audio = mix_audio(frames);} - ); - - buffer_.push(std::make_pair(std::move(image), audio)); - - if(buffer_.size()-1 < buffer_size_) - return make_safe(); - - auto res = std::move(buffer_.front()); - buffer_.pop(); - - return make_safe(channel_.ogl(), channel_.get_format_desc().size, std::move(res.first.get()), std::move(res.second)); - } - catch(...) - { - CASPAR_LOG(error) << L"[mixer] Error detected."; - throw; - } - } - - safe_ptr create_frame(const void* tag, const core::pixel_format_desc& desc) - { - return image_mixer_.create_frame(tag, desc); - } - - void reset_transforms() - { - channel_.execution().invoke([&] - { - boost::fusion::at_key(transforms_).clear(); - boost::fusion::at_key(transforms_).clear(); - }); - } - - template - void set_transform(int index, const T& transform, unsigned int mix_duration, const std::wstring& tween) - { - channel_.execution().invoke([&] - { - auto& transforms = boost::fusion::at_key(transforms_); - - auto src = transforms[index].fetch(); - auto dst = transform; - transforms[index] = tweened_transform(src, dst, mix_duration, tween); - }); - } - - template - void apply_transform(int index, const std::function& transform, unsigned int mix_duration, const std::wstring& tween) - { - channel_.execution().invoke([&] - { - auto& transforms = boost::fusion::at_key(transforms_); - - auto src = transforms[index].fetch(); - auto dst = transform(src); - transforms[index] = tweened_transform(src, dst, mix_duration, tween); - }); - } - - void set_blend_mode(int index, blend_mode::type value) - { - blend_modes_[index] = value; - } - - std::wstring print() const - { - return L"mixer"; - } - -private: - - boost::unique_future> mix_image(std::map> frames) - { - auto& image_transforms = boost::fusion::at_key(transforms_); - - BOOST_FOREACH(auto& frame, frames) - { - auto blend_it = blend_modes_.find(frame.first); - image_mixer_.begin_layer(blend_it != blend_modes_.end() ? blend_it->second : blend_mode::normal); - - auto frame1 = make_safe(frame.second); - frame1->get_image_transform() = image_transforms[frame.first].fetch_and_tick(1); - - if(channel_.get_format_desc().mode != core::field_mode::progressive) - { - auto frame2 = make_safe(frame.second); - frame2->get_image_transform() = image_transforms[frame.first].fetch_and_tick(1); - if(frame1->get_image_transform() != frame2->get_image_transform()) - frame1 = core::basic_frame::interlace(frame1, frame2, channel_.get_format_desc().mode); - } - - frame1->accept(image_mixer_); - - image_mixer_.end_layer(); - } - - return image_mixer_.render(); - } - - std::vector mix_audio(const std::map>& frames) - { - auto& audio_transforms = boost::fusion::at_key(transforms_); - - BOOST_FOREACH(auto& frame, frames) - { - const unsigned int num = channel_.get_format_desc().mode == core::field_mode::progressive ? 1 : 2; - - auto frame1 = make_safe(frame.second); - frame1->get_audio_transform() = audio_transforms[frame.first].fetch_and_tick(num); - frame1->accept(audio_mixer_); - } - - return audio_mixer_.mix(); - } -}; - -mixer::mixer(video_channel_context& video_channel) : impl_(new implementation(video_channel)){} -safe_ptr mixer::execute(const std::map>& frames){ return impl_->execute(frames);} -core::video_format_desc mixer::get_video_format_desc() const { return impl_->channel_.get_format_desc(); } -safe_ptr mixer::create_frame(const void* tag, const core::pixel_format_desc& desc){ return impl_->create_frame(tag, desc); } -safe_ptr mixer::create_frame(const void* tag, size_t width, size_t height, core::pixel_format::type pix_fmt) -{ - // Create bgra frame - core::pixel_format_desc desc; - desc.pix_fmt = pix_fmt; - desc.planes.push_back( core::pixel_format_desc::plane(width, height, 4)); - return create_frame(tag, desc); -} -void mixer::reset_transforms(){impl_->reset_transforms();} -void mixer::set_image_transform(int index, const core::image_transform& transform, unsigned int mix_duration, const std::wstring& tween){impl_->set_transform(index, transform, mix_duration, tween);} -void mixer::set_audio_transform(int index, const core::audio_transform& transform, unsigned int mix_duration, const std::wstring& tween){impl_->set_transform(index, transform, mix_duration, tween);} -void mixer::apply_image_transform(int index, const std::function& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);} -void mixer::apply_audio_transform(int index, const std::function& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);} -void mixer::set_blend_mode(int index, blend_mode::type value){impl_->set_blend_mode(index, value);} -}} \ 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 "mixer.h" + +#include "../frame/frame.h" + +#include "audio/audio_mixer.h" +#include "image/image_mixer.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +namespace caspar { namespace core { + +struct mixer::impl : boost::noncopyable +{ + spl::shared_ptr graph_; + audio_mixer audio_mixer_; + spl::shared_ptr image_mixer_; + + std::unordered_map blend_modes_; + + executor executor_ { L"mixer" }; + +public: + impl(spl::shared_ptr graph, spl::shared_ptr image_mixer) + : graph_(std::move(graph)) + , image_mixer_(std::move(image_mixer)) + { + graph_->set_color("mix-time", diagnostics::color(1.0f, 0.0f, 0.9f, 0.8f)); + } + + const_frame operator()(std::map frames, const video_format_desc& format_desc) + { + caspar::timer frame_timer; + + auto frame = executor_.invoke([=]() mutable -> const_frame + { + try + { + for (auto& frame : frames) + { + auto blend_it = blend_modes_.find(frame.first); + image_mixer_->begin_layer(blend_it != blend_modes_.end() ? blend_it->second : blend_mode::normal); + + frame.second.accept(audio_mixer_); + frame.second.accept(*image_mixer_); + + image_mixer_->end_layer(); + } + + auto image = (*image_mixer_)(format_desc); + auto audio = audio_mixer_(format_desc); + + auto desc = core::pixel_format_desc(core::pixel_format::bgra); + desc.planes.push_back(core::pixel_format_desc::plane(format_desc.width, format_desc.height, 4)); + return const_frame(std::move(image), std::move(audio), this, desc); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + return const_frame::empty(); + } + }); + + graph_->set_value("mix-time", frame_timer.elapsed()*format_desc.fps*0.5); + + return frame; + } + + void set_blend_mode(int index, blend_mode value) + { + executor_.begin_invoke([=] + { + auto it = blend_modes_.find(index); + if(it == blend_modes_.end()) + blend_modes_.insert(std::make_pair(index, value)); + else + it->second = value; + }, task_priority::high_priority); + } + + void clear_blend_mode(int index) + { + executor_.begin_invoke([=] + { + blend_modes_.erase(index); + }, task_priority::high_priority); + } + + void clear_blend_modes() + { + executor_.begin_invoke([=] + { + blend_modes_.clear(); + }, task_priority::high_priority); + } + + void set_master_volume(float volume) + { + executor_.begin_invoke([=] + { + audio_mixer_.set_master_volume(volume); + }, task_priority::high_priority); + } + + std::future info() const + { + return make_ready_future(boost::property_tree::wptree()); + } +}; + +mixer::mixer(spl::shared_ptr graph, spl::shared_ptr image_mixer) + : impl_(new impl(std::move(graph), std::move(image_mixer))){} +void mixer::set_blend_mode(int index, blend_mode value){impl_->set_blend_mode(index, value);} +void mixer::clear_blend_mode(int index) { impl_->clear_blend_mode(index); } +void mixer::clear_blend_modes() { impl_->clear_blend_modes(); } +void mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); } +std::future mixer::info() const{return impl_->info();} +const_frame mixer::operator()(std::map frames, const struct video_format_desc& format_desc){return (*impl_)(std::move(frames), format_desc);} +mutable_frame mixer::create_frame(const void* tag, const core::pixel_format_desc& desc) {return impl_->image_mixer_->create_frame(tag, desc);} +}}