From ba4563b293f7107519bf116cc28abfc9768bb49b Mon Sep 17 00:00:00 2001 From: ronag Date: Thu, 28 Apr 2011 13:43:58 +0000 Subject: [PATCH] 2.0.0.2: Merged mixer into core/mixer. git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.0.0.2@663 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d --- casparcg.sln | 10 - core/core.vcxproj | 74 +++- core/core.vcxproj.filters | 66 ++++ core/mixer/audio/audio_mixer.cpp | 121 +++++++ core/mixer/audio/audio_mixer.h | 46 +++ core/mixer/frame_mixer_device.cpp | 318 ++++++++++++++++++ core/mixer/frame_mixer_device.h | 78 +++++ core/mixer/gpu/device_buffer.cpp | 106 ++++++ core/mixer/gpu/device_buffer.h | 51 +++ core/mixer/gpu/gpu_read_frame.cpp | 55 +++ core/mixer/gpu/gpu_read_frame.h | 49 +++ core/mixer/gpu/gpu_write_frame.cpp | 97 ++++++ core/mixer/gpu/gpu_write_frame.h | 66 ++++ core/mixer/gpu/host_buffer.cpp | 106 ++++++ core/mixer/gpu/host_buffer.h | 51 +++ core/mixer/gpu/ogl_device.cpp | 102 ++++++ core/mixer/gpu/ogl_device.h | 74 ++++ core/mixer/image/image_kernel.cpp | 296 ++++++++++++++++ core/mixer/image/image_kernel.h | 40 +++ core/mixer/image/image_mixer.cpp | 196 +++++++++++ core/mixer/image/image_mixer.h | 55 +++ modules/ffmpeg/producer/video/video_decoder.h | 2 +- protocol/protocol.vcxproj | 3 - shell/main.cpp | 2 +- shell/shell.vcxproj | 3 - 25 files changed, 2042 insertions(+), 25 deletions(-) create mode 100644 core/mixer/audio/audio_mixer.cpp create mode 100644 core/mixer/audio/audio_mixer.h create mode 100644 core/mixer/frame_mixer_device.cpp create mode 100644 core/mixer/frame_mixer_device.h create mode 100644 core/mixer/gpu/device_buffer.cpp create mode 100644 core/mixer/gpu/device_buffer.h create mode 100644 core/mixer/gpu/gpu_read_frame.cpp create mode 100644 core/mixer/gpu/gpu_read_frame.h create mode 100644 core/mixer/gpu/gpu_write_frame.cpp create mode 100644 core/mixer/gpu/gpu_write_frame.h create mode 100644 core/mixer/gpu/host_buffer.cpp create mode 100644 core/mixer/gpu/host_buffer.h create mode 100644 core/mixer/gpu/ogl_device.cpp create mode 100644 core/mixer/gpu/ogl_device.h create mode 100644 core/mixer/image/image_kernel.cpp create mode 100644 core/mixer/image/image_kernel.h create mode 100644 core/mixer/image/image_mixer.cpp create mode 100644 core/mixer/image/image_mixer.h diff --git a/casparcg.sln b/casparcg.sln index 781e618cb..22f813e65 100644 --- a/casparcg.sln +++ b/casparcg.sln @@ -9,8 +9,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shell", "shell\shell.vcxpro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "protocol", "protocol\protocol.vcxproj", "{2040B361-1FB6-488E-84A5-38A580DA90DE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mixer", "mixer\mixer.vcxproj", "{477E12A4-1B28-4FF7-B46D-76606BDD1891}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "documentation", "documentation", "{32079541-68ED-4319-91FB-0FF041642679}" ProjectSection(SolutionItems) = preProject ..\CasparCG 2.0 Preliminary Change Log.txt = ..\CasparCG 2.0 Preliminary Change Log.txt @@ -80,14 +78,6 @@ Global {2040B361-1FB6-488E-84A5-38A580DA90DE}.Profile|Win32.Build.0 = Profile|Win32 {2040B361-1FB6-488E-84A5-38A580DA90DE}.Release|Win32.ActiveCfg = Release|Win32 {2040B361-1FB6-488E-84A5-38A580DA90DE}.Release|Win32.Build.0 = Release|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Debug|Win32.ActiveCfg = Debug|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Debug|Win32.Build.0 = Debug|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Develop|Win32.ActiveCfg = Develop|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Develop|Win32.Build.0 = Develop|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Profile|Win32.ActiveCfg = Profile|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Profile|Win32.Build.0 = Profile|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Release|Win32.ActiveCfg = Release|Win32 - {477E12A4-1B28-4FF7-B46D-76606BDD1891}.Release|Win32.Build.0 = Release|Win32 {69313D25-9F54-4FC9-9872-628A4DD79464}.Debug|Win32.ActiveCfg = Debug|Win32 {69313D25-9F54-4FC9-9872-628A4DD79464}.Debug|Win32.Build.0 = Debug|Win32 {69313D25-9F54-4FC9-9872-628A4DD79464}.Develop|Win32.ActiveCfg = Develop|Win32 diff --git a/core/core.vcxproj b/core/core.vcxproj index 6ea3fb986..c2aae767d 100644 --- a/core/core.vcxproj +++ b/core/core.vcxproj @@ -72,10 +72,10 @@ $(ProjectDir)tmp\$(Configuration)\ $(ProjectDir)tmp\$(Configuration)\ $(ProjectDir)tmp\$(Configuration)\ - ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;$(IncludePath) - ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;$(IncludePath) - ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;$(IncludePath) - ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;$(IncludePath) + ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;..\dependencies\SFML-1.6\include;..\dependencies\GLee5_4;$(IncludePath) + ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;..\dependencies\SFML-1.6\include;..\dependencies\GLee5_4;$(IncludePath) + ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;..\dependencies\SFML-1.6\include;..\dependencies\GLee5_4;$(IncludePath) + ..\;..\dependencies\boost_1_44_0\;..\dependencies\tbb30_20100406oss\include\;..\dependencies\SFML-1.6\include;..\dependencies\GLee5_4;$(IncludePath) ..\dependencies\BluefishSDK_V5_8_0_31\Lib\;..\dependencies\boost_1_44_0\stage\lib\;..\dependencies\ffmpeg 0.6\lib\;..\dependencies\FreeImage\Dist\;..\dependencies\GLee5_4\;..\dependencies\SFML-1.6\lib\;..\dependencies\tbb30_20100406oss\lib\ia32\vc10\;$(LibraryPath) ..\dependencies\BluefishSDK_V5_8_0_31\Lib\;..\dependencies\boost_1_44_0\stage\lib\;..\dependencies\ffmpeg 0.6\lib\;..\dependencies\FreeImage\Dist\;..\dependencies\GLee5_4\;..\dependencies\SFML-1.6\lib\;..\dependencies\tbb30_20100406oss\lib\ia32\vc10\;$(LibraryPath) ..\dependencies\BluefishSDK_V5_8_0_31\Lib\;..\dependencies\boost_1_44_0\stage\lib\;..\dependencies\ffmpeg 0.6\lib\;..\dependencies\FreeImage\Dist\;..\dependencies\GLee5_4\;..\dependencies\SFML-1.6\lib\;..\dependencies\tbb30_20100406oss\lib\ia32\vc10\;$(LibraryPath) @@ -251,6 +251,15 @@ + + + + + + + + + @@ -285,6 +294,60 @@ ../StdAfx.h ../StdAfx.h + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + + + ../StdAfx.h + ../StdAfx.h + ../StdAfx.h + ../StdAfx.h + + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + + + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h + ../../StdAfx.h ../../StdAfx.h @@ -351,9 +414,6 @@ {02308602-7fe0-4253-b96e-22134919f56a} - - {477e12a4-1b28-4ff7-b46d-76606bdd1891} - diff --git a/core/core.vcxproj.filters b/core/core.vcxproj.filters index 95696ccc7..847500706 100644 --- a/core/core.vcxproj.filters +++ b/core/core.vcxproj.filters @@ -19,6 +19,18 @@ {fc8d2ce0-e849-4f8e-83bf-494ab4455b80} + + {e480e128-a351-4dc6-a7ab-f58b480f6afd} + + + {0951ac32-6cd0-4a9f-865d-6f86600f12f8} + + + {1cca1d41-f9b1-4b7b-b34f-8a88dcb5b904} + + + {2d2ae7ff-03d7-4e8c-a298-fb41ee42a885} + @@ -69,6 +81,33 @@ producer\frame + + mixer + + + mixer\image + + + mixer\image + + + mixer\gpu + + + mixer\gpu + + + mixer\gpu + + + mixer\gpu + + + mixer\gpu + + + mixer\audio + @@ -104,5 +143,32 @@ producer\frame + + mixer + + + mixer\image + + + mixer\image + + + mixer\gpu + + + mixer\gpu + + + mixer\gpu + + + mixer\gpu + + + mixer\gpu + + + mixer\audio + \ No newline at end of file diff --git a/core/mixer/audio/audio_mixer.cpp b/core/mixer/audio/audio_mixer.cpp new file mode 100644 index 000000000..7ab35d110 --- /dev/null +++ b/core/mixer/audio/audio_mixer.cpp @@ -0,0 +1,121 @@ +/* +* 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 "audio_mixer.h" + +#include "../gpu/gpu_write_frame.h" + +#include + +namespace caspar { namespace mixer { + +struct audio_mixer::implementation +{ + std::vector audio_data_; + std::stack transform_stack_; + + std::map prev_audio_transforms_; + std::map next_audio_transforms_; + +public: + implementation() + { + transform_stack_.push(core::audio_transform()); + } + + void begin(const core::basic_frame& frame) + { + transform_stack_.push(transform_stack_.top()*frame.get_audio_transform()); + } + + void visit(const core::write_frame& frame) + { + if(!transform_stack_.top().get_has_audio()) + return; + + auto& audio_data = frame.audio_data(); + auto tag = frame.tag(); // Get the identifier for the audio-stream. + + if(audio_data_.empty()) + audio_data_.resize(audio_data.size(), 0); + + auto next = transform_stack_.top(); + auto prev = next; + + auto it = prev_audio_transforms_.find(tag); + if(it != prev_audio_transforms_.end()) + prev = it->second; + + next_audio_transforms_[tag] = next; // Store all active tags, inactive tags will be removed in end_pass. + + auto next_gain = next.get_gain(); + auto prev_gain = prev.get_gain(); + + if(next_gain < 0.001 && prev_gain < 0.001) + return; + + tbb::parallel_for + ( + tbb::blocked_range(0, audio_data.size()), + [&](const tbb::blocked_range& r) + { + for(size_t n = r.begin(); n < r.end(); ++n) + { + double alpha = static_cast(n)/static_cast(audio_data_.size()); + double sample_gain = prev_gain * (1.0 - alpha) + next_gain * alpha; + int sample = static_cast(audio_data[n]); + sample = (static_cast(sample_gain*static_cast(1<<15))*sample)>>15; + audio_data_[n] = static_cast((static_cast(audio_data_[n]) + sample) & 0xFFFF); + } + } + ); + } + + + void begin(const core::audio_transform& transform) + { + transform_stack_.push(transform_stack_.top()*transform); + } + + void end() + { + transform_stack_.pop(); + } + + std::vector begin_pass() + { + return std::move(audio_data_); + } + + void end_pass() + { + prev_audio_transforms_ = std::move(next_audio_transforms_); + } +}; + +audio_mixer::audio_mixer() : impl_(new implementation()){} +void audio_mixer::begin(const core::basic_frame& frame){impl_->begin(frame);} +void audio_mixer::visit(core::write_frame& frame){impl_->visit(frame);} +void audio_mixer::end(){impl_->end();} +std::vector audio_mixer::begin_pass(){return impl_->begin_pass();} +void audio_mixer::end_pass(){impl_->end_pass();} + +}} \ No newline at end of file diff --git a/core/mixer/audio/audio_mixer.h b/core/mixer/audio/audio_mixer.h new file mode 100644 index 000000000..92f20ace9 --- /dev/null +++ b/core/mixer/audio/audio_mixer.h @@ -0,0 +1,46 @@ +/* +* 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 . +* +*/ +#pragma once + +#include +#include + +#include + +namespace caspar { namespace mixer { + +class audio_mixer : public core::frame_visitor, boost::noncopyable +{ +public: + audio_mixer(); + + virtual void begin(const core::basic_frame& frame); + virtual void visit(core::write_frame& frame); + virtual void end(); + + std::vector begin_pass(); + void end_pass(); + +private: + struct implementation; + std::shared_ptr impl_; +}; + +}} \ No newline at end of file diff --git a/core/mixer/frame_mixer_device.cpp b/core/mixer/frame_mixer_device.cpp new file mode 100644 index 000000000..1d2dda838 --- /dev/null +++ b/core/mixer/frame_mixer_device.cpp @@ -0,0 +1,318 @@ +/* +* 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 "frame_mixer_device.h" + +#include "gpu/gpu_read_frame.h" +#include "gpu/gpu_write_frame.h" + +#include "audio/audio_mixer.h" +#include "image/image_mixer.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +namespace caspar { namespace mixer { + +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 frame_mixer_device::implementation : boost::noncopyable +{ + const core::video_format_desc format_desc_; + + safe_ptr diag_; + timer perf_timer_; + timer wait_perf_timer_; + + audio_mixer audio_mixer_; + image_mixer image_mixer_; + + output_t output_; + + typedef std::unordered_map> image_transforms; + typedef std::unordered_map> audio_transforms; + + boost::fusion::map, + boost::fusion::pair> transforms_; + + boost::fusion::map>, + boost::fusion::pair>> root_transforms_; + + executor executor_; +public: + implementation(const core::video_format_desc& format_desc) + : format_desc_(format_desc) + , diag_(diagnostics::create_graph(narrow(print()))) + , image_mixer_(format_desc) + , executor_(L"frame_mixer_device") + { + diag_->add_guide("frame-time", 0.5f); + diag_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f)); + diag_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f)); + diag_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f)); + executor_.start(); + executor_.set_capacity(2); + CASPAR_LOG(info) << print() << L" Successfully initialized."; + } + + boost::signals2::connection connect(const output_t::slot_type& subscriber) + { + return output_.connect(subscriber); + } + + boost::unique_future> mix_image(std::map> frames) + { + auto& root_image_transform = boost::fusion::at_key(root_transforms_); + auto& image_transforms = boost::fusion::at_key(transforms_); + + auto image = image_mixer_.begin_pass(); + BOOST_FOREACH(auto& frame, frames) + { + if(format_desc_.mode != core::video_mode::progressive) + { + auto frame1 = make_safe(frame.second); + auto frame2 = make_safe(frame.second); + + frame1->get_image_transform() = root_image_transform.fetch_and_tick(1)*image_transforms[frame.first].fetch_and_tick(1); + frame2->get_image_transform() = root_image_transform.fetch_and_tick(1)*image_transforms[frame.first].fetch_and_tick(1); + + if(frame1->get_image_transform() != frame2->get_image_transform()) + core::basic_frame::interlace(frame1, frame2, format_desc_.mode)->accept(image_mixer_); + else + frame2->accept(image_mixer_); + } + else + { + auto frame1 = make_safe(frame.second); + frame1->get_image_transform() = root_image_transform.fetch_and_tick(1)*image_transforms[frame.first].fetch_and_tick(1); + frame1->accept(image_mixer_); + } + } + image_mixer_.end_pass(); + return std::move(image); + } + + std::vector mix_audio(const std::map>& frames) + { + auto& root_audio_transform = boost::fusion::at_key(root_transforms_); + auto& audio_transforms = boost::fusion::at_key(transforms_); + + auto audio = audio_mixer_.begin_pass(); + BOOST_FOREACH(auto& frame, frames) + { + int num = format_desc_.mode == core::video_mode::progressive ? 1 : 2; + + auto frame1 = make_safe(frame.second); + frame1->get_audio_transform() = root_audio_transform.fetch_and_tick(num)*audio_transforms[frame.first].fetch_and_tick(num); + frame1->accept(audio_mixer_); + } + audio_mixer_.end_pass(); + return audio; + } + + void send(const std::map>& frames) + { + executor_.begin_invoke([=] + { + perf_timer_.reset(); + + auto image_future = mix_image(frames); + auto audio = mix_audio(frames); + auto image = image_future.get(); + + diag_->update_value("frame-time", static_cast(perf_timer_.elapsed()/format_desc_.interval*0.5)); + + output_(make_safe(std::move(image), std::move(audio))); + + diag_->update_value("tick-time", static_cast(wait_perf_timer_.elapsed()/format_desc_.interval*0.5)); + wait_perf_timer_.reset(); + + diag_->set_value("input-buffer", static_cast(executor_.size())/static_cast(executor_.capacity())); + }); + diag_->set_value("input-buffer", static_cast(executor_.size())/static_cast(executor_.capacity())); + } + + safe_ptr create_frame(void* tag, const core::pixel_format_desc& desc) + { + return make_safe(reinterpret_cast(tag), desc, image_mixer_.create_buffers(desc)); + } + + template + void set_transform(const T& transform, int mix_duration, const std::wstring& tween) + { + executor_.invoke([&] + { + auto& root = boost::fusion::at_key(root_transforms_); + + auto src = root.fetch(); + auto dst = transform; + root = tweened_transform(src, dst, mix_duration, tween); + }); + } + + template + void set_transform(int index, const T& transform, int mix_duration, const std::wstring& tween) + { + executor_.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(const std::function& transform, int mix_duration, const std::wstring& tween) + { + return executor_.invoke([&] + { + auto& root = boost::fusion::at_key(root_transforms_); + + auto src = root.fetch(); + auto dst = transform(src); + root = tweened_transform(src, dst, mix_duration, tween); + }); + } + + template + void apply_transform(int index, const std::function& transform, int mix_duration, const std::wstring& tween) + { + executor_.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); + }); + } + + template + void reset_transform(int mix_duration, const std::wstring& tween) + { + executor_.invoke([&] + { + auto& transforms = boost::fusion::at_key(transforms_); + + BOOST_FOREACH(auto& t, transforms) + t.second = tweened_transform(t.second.fetch(), T(), mix_duration, tween); + set_transform(T(), mix_duration, tween); + }); + } + + template + void reset_transform(int index, int mix_duration, const std::wstring& tween) + { + executor_.invoke([&] + { + set_transform(T(), mix_duration, tween); + }); + } + + std::wstring print() const + { + return L"frame_mixer_device"; + } +}; + +frame_mixer_device::frame_mixer_device(const core::video_format_desc& format_desc) : impl_(new implementation(format_desc)){} +frame_mixer_device::frame_mixer_device(frame_mixer_device&& other) : impl_(std::move(other.impl_)){} +boost::signals2::connection frame_mixer_device::connect(const output_t::slot_type& subscriber){return impl_->connect(subscriber);} +void frame_mixer_device::send(const std::map>& frames){impl_->send(frames);} +const core::video_format_desc& frame_mixer_device::get_video_format_desc() const { return impl_->format_desc_; } +safe_ptr frame_mixer_device::create_frame(void* tag, const core::pixel_format_desc& desc){ return impl_->create_frame(tag, desc); } +safe_ptr frame_mixer_device::create_frame(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); +} + +safe_ptr frame_mixer_device::create_frame(void* tag, core::pixel_format::type pix_fmt) +{ + // Create bgra frame with output resolution + core::pixel_format_desc desc; + desc.pix_fmt = pix_fmt; + desc.planes.push_back( core::pixel_format_desc::plane(get_video_format_desc().width, get_video_format_desc().height, 4)); + return create_frame(tag, desc); +} +void frame_mixer_device::set_image_transform(const core::image_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform(transform, mix_duration, tween);} +void frame_mixer_device::set_image_transform(int index, const core::image_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform(index, transform, mix_duration, tween);} +void frame_mixer_device::set_audio_transform(const core::audio_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform(transform, mix_duration, tween);} +void frame_mixer_device::set_audio_transform(int index, const core::audio_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform(index, transform, mix_duration, tween);} +void frame_mixer_device::apply_image_transform(const std::function& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform(transform, mix_duration, tween);} +void frame_mixer_device::apply_image_transform(int index, const std::function& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);} +void frame_mixer_device::apply_audio_transform(const std::function& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform(transform, mix_duration, tween);} +void frame_mixer_device::apply_audio_transform(int index, const std::function& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);} +void frame_mixer_device::reset_image_transform(int mix_duration, const std::wstring& tween){impl_->reset_transform(mix_duration, tween);} +void frame_mixer_device::reset_image_transform(int index, int mix_duration, const std::wstring& tween){impl_->reset_transform(index, mix_duration, tween);} +void frame_mixer_device::reset_audio_transform(int mix_duration, const std::wstring& tween){impl_->reset_transform(mix_duration, tween);} +void frame_mixer_device::reset_audio_transform(int index, int mix_duration, const std::wstring& tween){impl_->reset_transform(index, mix_duration, tween);} + +}} \ No newline at end of file diff --git a/core/mixer/frame_mixer_device.h b/core/mixer/frame_mixer_device.h new file mode 100644 index 000000000..d72c42bbe --- /dev/null +++ b/core/mixer/frame_mixer_device.h @@ -0,0 +1,78 @@ +/* +* 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 . +* +*/ +#pragma once + +#include +#include +#include +#include + +#include "image/image_mixer.h" +#include "audio/audio_mixer.h" + +#include + +#include + +#include + +namespace caspar { namespace mixer { + +class frame_mixer_device : public core::frame_factory +{ +public: + typedef boost::signals2::signal&)> output_t; + + boost::signals2::connection connect(const output_t::slot_type& subscriber); + + frame_mixer_device(const core::video_format_desc& format_desc); + frame_mixer_device(frame_mixer_device&& other); // nothrow + + void send(const std::map>& frames); // nothrow + + safe_ptr create_frame(void* tag, const core::pixel_format_desc& desc); + safe_ptr create_frame(void* tag, size_t width, size_t height, core::pixel_format::type pix_fmt = core::pixel_format::bgra); + safe_ptr create_frame(void* tag, core::pixel_format::type pix_fmt = core::pixel_format::bgra); + + const core::video_format_desc& get_video_format_desc() const; // nothrow + + void set_image_transform(const core::image_transform& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + void set_image_transform(int index, const core::image_transform& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + + void set_audio_transform(const core::audio_transform& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + void set_audio_transform(int index, const core::audio_transform& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + + void apply_image_transform(const std::function& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + void apply_image_transform(int index, const std::function& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + + void apply_audio_transform(const std::function& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + void apply_audio_transform(int index, const std::function& transform, int mix_duration = 0, const std::wstring& tween = L"linear"); + + void reset_image_transform(int mix_duration = 0, const std::wstring& tween = L"linear"); + void reset_image_transform(int index, int mix_duration = 0, const std::wstring& tween = L"linear"); + void reset_audio_transform(int mix_duration = 0, const std::wstring& tween = L"linear"); + void reset_audio_transform(int index, int mix_duration = 0, const std::wstring& tween = L"linear"); + +private: + struct implementation; + safe_ptr impl_; +}; + +}} \ No newline at end of file diff --git a/core/mixer/gpu/device_buffer.cpp b/core/mixer/gpu/device_buffer.cpp new file mode 100644 index 000000000..52c08ee01 --- /dev/null +++ b/core/mixer/gpu/device_buffer.cpp @@ -0,0 +1,106 @@ +/* +* 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 "device_buffer.h" + +#include +namespace caspar { namespace mixer { + +GLenum FORMAT[] = {0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_BGR, GL_BGRA}; +GLenum INTERNAL_FORMAT[] = {0, GL_LUMINANCE8, GL_LUMINANCE8_ALPHA8, GL_RGB8, GL_RGBA8}; + +struct device_buffer::implementation : boost::noncopyable +{ + GLuint id_; + + const size_t width_; + const size_t height_; + const size_t stride_; + +public: + implementation(size_t width, size_t height, size_t stride) + : width_(width) + , height_(height) + , stride_(stride) + { + GL(glGenTextures(1, &id_)); + GL(glBindTexture(GL_TEXTURE_2D, id_)); + GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT[stride_], width_, height_, 0, FORMAT[stride_], GL_UNSIGNED_BYTE, NULL)); + GL(glBindTexture(GL_TEXTURE_2D, 0)); + //CASPAR_LOG(trace) << "[device_buffer] allocated size:" << width*height*stride; + } + + ~implementation() + { + GL(glDeleteTextures(1, &id_)); + } + + void bind() + { + GL(glBindTexture(GL_TEXTURE_2D, id_)); + } + + void unbind() + { + GL(glBindTexture(GL_TEXTURE_2D, 0)); + } + + void read(host_buffer& source) + { + GL(glBindTexture(GL_TEXTURE_2D, id_)); + source.unmap(); + source.bind(); + GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, FORMAT[stride_], GL_UNSIGNED_BYTE, NULL)); + source.unbind(); + GL(glBindTexture(GL_TEXTURE_2D, 0)); + } + + void write(host_buffer& target) + { + GL(glBindTexture(GL_TEXTURE_2D, id_)); + target.unmap(); + target.bind(); + GL(glReadPixels(0, 0, width_, height_, FORMAT[stride_], GL_UNSIGNED_BYTE, NULL)); + target.unbind(); + GL(glBindTexture(GL_TEXTURE_2D, 0)); + } + + void attach(int index) + { + GL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, GL_TEXTURE_2D, id_, 0)); + } +}; + +device_buffer::device_buffer(size_t width, size_t height, size_t stride) : impl_(new implementation(width, height, stride)){} +size_t device_buffer::stride() const { return impl_->stride_; } +size_t device_buffer::width() const { return impl_->width_; } +size_t device_buffer::height() const { return impl_->height_; } +void device_buffer::attach(int index){impl_->attach(index);} +void device_buffer::bind(){impl_->bind();} +void device_buffer::unbind(){impl_->unbind();} +void device_buffer::read(host_buffer& source){impl_->read(source);} +void device_buffer::write(host_buffer& target){impl_->write(target);} + +}} \ No newline at end of file diff --git a/core/mixer/gpu/device_buffer.h b/core/mixer/gpu/device_buffer.h new file mode 100644 index 000000000..d64a80cd6 --- /dev/null +++ b/core/mixer/gpu/device_buffer.h @@ -0,0 +1,51 @@ +/* +* 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 . +* +*/ +#pragma once + +#include "../gpu/host_buffer.h" + +#include + +#include + +namespace caspar { namespace mixer { + +class device_buffer : boost::noncopyable +{ +public: + device_buffer(size_t width, size_t height, size_t stride); + + size_t stride() const; + size_t width() const; + size_t height() const; + + void bind(); + void unbind(); + + void attach(int index); + void read(host_buffer& source); + void write(host_buffer& target); + +private: + struct implementation; + std::shared_ptr impl_; +}; + +}} \ No newline at end of file diff --git a/core/mixer/gpu/gpu_read_frame.cpp b/core/mixer/gpu/gpu_read_frame.cpp new file mode 100644 index 000000000..65b1e2151 --- /dev/null +++ b/core/mixer/gpu/gpu_read_frame.cpp @@ -0,0 +1,55 @@ +/* +* 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 "gpu_read_frame.h" + +#include "../gpu/host_buffer.h" + +#include + +namespace caspar { namespace mixer { + +struct gpu_read_frame::implementation : boost::noncopyable +{ + safe_ptr image_data_; + std::vector audio_data_; + +public: + implementation(safe_ptr&& image_data, std::vector&& audio_data) + : image_data_(std::move(image_data)) + , audio_data_(std::move(audio_data)){} +}; + +gpu_read_frame::gpu_read_frame(safe_ptr&& image_data, std::vector&& audio_data) : impl_(new implementation(std::move(image_data), std::move(audio_data))){} + +const boost::iterator_range gpu_read_frame::image_data() const +{ + if(!impl_->image_data_->data()) + return boost::iterator_range(); + auto ptr = static_cast(impl_->image_data_->data()); + return boost::iterator_range(ptr, ptr + impl_->image_data_->size()); +} +const boost::iterator_range gpu_read_frame::audio_data() const +{ + return boost::iterator_range(impl_->audio_data_.data(), impl_->audio_data_.data() + impl_->audio_data_.size()); +} + +}} \ No newline at end of file diff --git a/core/mixer/gpu/gpu_read_frame.h b/core/mixer/gpu/gpu_read_frame.h new file mode 100644 index 000000000..4f84ecde5 --- /dev/null +++ b/core/mixer/gpu/gpu_read_frame.h @@ -0,0 +1,49 @@ +/* +* 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 . +* +*/ +#pragma once + +#include "../gpu/host_buffer.h" + +#include + +#include +#include + +#include +#include + +#include + +namespace caspar { namespace mixer { + +class gpu_read_frame : public core::read_frame +{ +public: + gpu_read_frame(safe_ptr&& image_data, std::vector&& audio_data); + + const boost::iterator_range image_data() const; + const boost::iterator_range audio_data() const; + +private: + struct implementation; + std::shared_ptr impl_; +}; + +}} \ No newline at end of file diff --git a/core/mixer/gpu/gpu_write_frame.cpp b/core/mixer/gpu/gpu_write_frame.cpp new file mode 100644 index 000000000..6e4b24711 --- /dev/null +++ b/core/mixer/gpu/gpu_write_frame.cpp @@ -0,0 +1,97 @@ +/* +* 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 "gpu_write_frame.h" + +#include + +#include "../image/image_mixer.h" +#include "../audio/audio_mixer.h" + +#include "../gpu/host_buffer.h" + +#include + +#include + +namespace caspar { namespace mixer { + +struct gpu_write_frame::implementation : boost::noncopyable +{ + std::vector> buffers_; + std::vector audio_data_; + const core::pixel_format_desc desc_; + int tag_; + +public: + implementation(int tag, const core::pixel_format_desc& desc, const std::vector>& buffers) + : desc_(desc) + , buffers_(buffers) + , tag_(tag){} + + void accept(gpu_write_frame& self, core::frame_visitor& visitor) + { + visitor.begin(self); + visitor.visit(self); + visitor.end(); + } + + boost::iterator_range image_data(size_t index) + { + if(index >= buffers_.size() || !buffers_[index]->data()) + return boost::iterator_range(); + auto ptr = static_cast(buffers_[index]->data()); + return boost::iterator_range(ptr, ptr+buffers_[index]->size()); + } + + const boost::iterator_range image_data(size_t index) const + { + if(index >= buffers_.size() || !buffers_[index]->data()) + return boost::iterator_range(); + auto ptr = static_cast(buffers_[index]->data()); + return boost::iterator_range(ptr, ptr+buffers_[index]->size()); + } +}; + +gpu_write_frame::gpu_write_frame(int tag, const core::pixel_format_desc& desc, const std::vector>& buffers) : impl_(new implementation(tag, desc, buffers)){} +gpu_write_frame::gpu_write_frame(gpu_write_frame&& other) : impl_(std::move(other.impl_)){} +void gpu_write_frame::swap(gpu_write_frame& other){impl_.swap(other.impl_);} +gpu_write_frame& gpu_write_frame::operator=(gpu_write_frame&& other) +{ + gpu_write_frame temp(std::move(other)); + temp.swap(*this); + return *this; +} +void gpu_write_frame::accept(core::frame_visitor& visitor){impl_->accept(*this, visitor);} +boost::iterator_range gpu_write_frame::image_data(size_t index){return impl_->image_data(index);} +std::vector& gpu_write_frame::audio_data() { return impl_->audio_data_; } +const boost::iterator_range gpu_write_frame::image_data(size_t index) const +{ + return boost::iterator_range(impl_->image_data(index).begin(), impl_->image_data(index).end()); +} +const boost::iterator_range gpu_write_frame::audio_data() const +{ + return boost::iterator_range(impl_->audio_data_.data(), impl_->audio_data_.data() + impl_->audio_data_.size()); +} +int gpu_write_frame::tag() const {return impl_->tag_;} +const core::pixel_format_desc& gpu_write_frame::get_pixel_format_desc() const{return impl_->desc_;} +std::vector>& gpu_write_frame::get_plane_buffers(){return impl_->buffers_;} +}} \ No newline at end of file diff --git a/core/mixer/gpu/gpu_write_frame.h b/core/mixer/gpu/gpu_write_frame.h new file mode 100644 index 000000000..a4e408edc --- /dev/null +++ b/core/mixer/gpu/gpu_write_frame.h @@ -0,0 +1,66 @@ +/* +* 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 . +* +*/ +#pragma once + +#include +#include +#include + +#include "../gpu/host_buffer.h" + +#include +#include + +#include +#include + +namespace caspar { namespace mixer { + +class gpu_write_frame : public core::write_frame +{ +public: + explicit gpu_write_frame(int tag, const core::pixel_format_desc& desc, const std::vector>& buffers); + gpu_write_frame(gpu_write_frame&& other); + gpu_write_frame& operator=(gpu_write_frame&& other); + + void swap(gpu_write_frame& other); + + const core::pixel_format_desc& get_pixel_format_desc() const; + std::vector>& get_plane_buffers(); + + // core::write_frame + virtual boost::iterator_range image_data(size_t plane_index = 0); + virtual std::vector& audio_data(); + + virtual const boost::iterator_range image_data(size_t plane_index = 0) const; + virtual const boost::iterator_range audio_data() const; + + virtual void accept(core::frame_visitor& visitor); + + virtual int tag() const; + +private: + struct implementation; + std::shared_ptr impl_; +}; +typedef std::shared_ptr gpu_write_frame_impl_ptr; + + +}} \ No newline at end of file diff --git a/core/mixer/gpu/host_buffer.cpp b/core/mixer/gpu/host_buffer.cpp new file mode 100644 index 000000000..996abbd1e --- /dev/null +++ b/core/mixer/gpu/host_buffer.cpp @@ -0,0 +1,106 @@ +/* +* 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 "../gpu/host_buffer.h" + +#include + +namespace caspar { namespace mixer { + +struct host_buffer::implementation : boost::noncopyable +{ + GLuint pbo_; + + const size_t size_; + + void* data_; + GLenum usage_; + GLenum target_; + +public: + implementation(size_t size, usage_t usage) + : size_(size) + , data_(nullptr) + , pbo_(0) + , target_(usage == write_only ? GL_PIXEL_UNPACK_BUFFER : GL_PIXEL_PACK_BUFFER) + , usage_(usage == write_only ? GL_STREAM_DRAW : GL_STREAM_READ) + { + GL(glGenBuffers(1, &pbo_)); + GL(glBindBuffer(target_, pbo_)); + GL(glBufferData(target_, size_, NULL, usage_)); + GL(glBindBuffer(target_, 0)); + + if(!pbo_) + BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Failed to allocate buffer.")); + + //CASPAR_LOG(trace) << "[host_buffer] allocated size:" << size_ << " usage: " << (usage == write_only ? "write_only" : "read_only"); + } + + ~implementation() + { + GL(glDeleteBuffers(1, &pbo_)); + } + + void map() + { + if(data_) + return; + + GL(glBindBuffer(target_, pbo_)); + data_ = glMapBuffer(target_, usage_ == GL_STREAM_DRAW ? GL_WRITE_ONLY : GL_READ_ONLY); + GL(glBindBuffer(target_, 0)); + if(!data_) + BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Failed to map target_ OpenGL Pixel Buffer Object.")); + } + + void unmap() + { + if(!data_) + return; + + GL(glBindBuffer(target_, pbo_)); + GL(glUnmapBuffer(target_)); + data_ = nullptr; + GL(glBindBuffer(target_, 0)); + } + + void bind() + { + GL(glBindBuffer(target_, pbo_)); + } + + void unbind() + { + GL(glBindBuffer(target_, 0)); + } +}; + +host_buffer::host_buffer(size_t size, usage_t usage) : impl_(new implementation(size, usage)){} +const void* host_buffer::data() const {return impl_->data_;} +void* host_buffer::data() {return impl_->data_;} +void host_buffer::map(){impl_->map();} +void host_buffer::unmap(){impl_->unmap();} +void host_buffer::bind(){impl_->bind();} +void host_buffer::unbind(){impl_->unbind();} + +size_t host_buffer::size() const { return impl_->size_; } + +}} \ No newline at end of file diff --git a/core/mixer/gpu/host_buffer.h b/core/mixer/gpu/host_buffer.h new file mode 100644 index 000000000..3d637b8f8 --- /dev/null +++ b/core/mixer/gpu/host_buffer.h @@ -0,0 +1,51 @@ +/* +* 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 . +* +*/ +#pragma once + +#include + +#include + +namespace caspar { namespace mixer { + +class host_buffer : boost::noncopyable +{ +public: + enum usage_t + { + write_only, + read_only + }; + host_buffer(size_t size, usage_t usage); + + const void* data() const; + void* data(); + size_t size() const; + + void bind(); + void unbind(); + void unmap(); + void map(); +private: + struct implementation; + std::shared_ptr impl_; +}; + +}} \ No newline at end of file diff --git a/core/mixer/gpu/ogl_device.cpp b/core/mixer/gpu/ogl_device.cpp new file mode 100644 index 000000000..692bb3a53 --- /dev/null +++ b/core/mixer/gpu/ogl_device.cpp @@ -0,0 +1,102 @@ +/* +* 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 "ogl_device.h" + +#include + +#include +#include + +#include + +namespace caspar { namespace mixer { + +ogl_device::ogl_device() : executor_(L"ogl_device") +{ + executor_.start(); + invoke([=] + { + context_.reset(new sf::Context()); + context_->SetActive(true); + }); +} + +ogl_device::~ogl_device() +{ + invoke([=] + { + BOOST_FOREACH(auto& pool, device_pools_) + pool.clear(); + BOOST_FOREACH(auto& pool, host_pools_) + pool.clear(); + }); +} + +safe_ptr ogl_device::create_device_buffer(size_t width, size_t height, size_t stride) +{ + CASPAR_VERIFY(stride > 0 && stride < 5); + CASPAR_VERIFY(width > 0 && height > 0); + auto pool = &device_pools_[stride-1][((width << 16) & 0xFFFF0000) | (height & 0x0000FFFF)]; + std::shared_ptr buffer; + if(!pool->try_pop(buffer)) + { + executor_.invoke([&] + { + buffer = std::make_shared(width, height, stride); + }); + } + + return safe_ptr(buffer.get(), [=](device_buffer*){pool->push(buffer);}); +} + +safe_ptr ogl_device::create_host_buffer(size_t size, host_buffer::usage_t usage) +{ + CASPAR_VERIFY(usage == host_buffer::write_only || usage == host_buffer::read_only); + CASPAR_VERIFY(size > 0); + auto pool = &host_pools_[usage][size]; + std::shared_ptr buffer; + if(!pool->try_pop(buffer)) + { + executor_.invoke([&] + { + buffer = std::make_shared(size, usage); + if(usage == host_buffer::write_only) + buffer->map(); + else + buffer->unmap(); + }); + } + + return safe_ptr(buffer.get(), [=](host_buffer*) + { + executor_.begin_invoke([=] + { + if(usage == host_buffer::write_only) + buffer->map(); + else + buffer->unmap(); + pool->push(buffer); + }); + }); +} + +}} \ No newline at end of file diff --git a/core/mixer/gpu/ogl_device.h b/core/mixer/gpu/ogl_device.h new file mode 100644 index 000000000..de782bae3 --- /dev/null +++ b/core/mixer/gpu/ogl_device.h @@ -0,0 +1,74 @@ +/* +* 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 . +* +*/ +#pragma once + +#include "host_buffer.h" +#include "device_buffer.h" + +#include +#include + +#include +#include + +#include + +#include + +#include + +namespace caspar { namespace mixer { + +class ogl_device +{ + std::unique_ptr context_; + + std::array>>, 4> device_pools_; + std::array>>, 2> host_pools_; + + executor executor_; + + ogl_device(); +public: + virtual ~ogl_device(); + + static safe_ptr create() + { + static safe_ptr instance(new ogl_device()); // Use the same ogl-device for all channels inorder to ensure that frames are always valid for all "context". + return instance; + } + + template + auto begin_invoke(Func&& func) -> boost::unique_future // noexcept + { + return executor_.begin_invoke(std::forward(func)); + } + + template + auto invoke(Func&& func) -> decltype(func()) + { + return executor_.invoke(std::forward(func)); + } + + safe_ptr create_device_buffer(size_t width, size_t height, size_t stride); + safe_ptr create_host_buffer(size_t size, host_buffer::usage_t usage); +}; + +}} \ No newline at end of file diff --git a/core/mixer/image/image_kernel.cpp b/core/mixer/image/image_kernel.cpp new file mode 100644 index 000000000..bd40c4a2a --- /dev/null +++ b/core/mixer/image/image_kernel.cpp @@ -0,0 +1,296 @@ +/* +* 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 "image_kernel.h" + +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +namespace caspar { namespace mixer { + +class shader_program : boost::noncopyable +{ + GLuint program_; +public: + + shader_program() : program_(0) {} + shader_program(shader_program&& other) : program_(other.program_){other.program_ = 0;} + shader_program(const std::string& vertex_source_str, const std::string& fragment_source_str) : program_(0) + { + GLint success; + + const char* vertex_source = vertex_source_str.c_str(); + + auto vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + + GL(glShaderSourceARB(vertex_shader, 1, &vertex_source, NULL)); + GL(glCompileShaderARB(vertex_shader)); + + GL(glGetObjectParameterivARB(vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); + if (success == GL_FALSE) + { + char info[2048]; + GL(glGetInfoLogARB(vertex_shader, sizeof(info), 0, info)); + GL(glDeleteObjectARB(vertex_shader)); + std::stringstream str; + str << "Failed to compile vertex shader:" << std::endl << info << std::endl; + BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); + } + + const char* fragment_source = fragment_source_str.c_str(); + + auto fragmemt_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + + GL(glShaderSourceARB(fragmemt_shader, 1, &fragment_source, NULL)); + GL(glCompileShaderARB(fragmemt_shader)); + + GL(glGetObjectParameterivARB(fragmemt_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); + if (success == GL_FALSE) + { + char info[2048]; + GL(glGetInfoLogARB(fragmemt_shader, sizeof(info), 0, info)); + GL(glDeleteObjectARB(fragmemt_shader)); + std::stringstream str; + str << "Failed to compile fragment shader:" << std::endl << info << std::endl; + BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); + } + + program_ = glCreateProgramObjectARB(); + + GL(glAttachObjectARB(program_, vertex_shader)); + GL(glAttachObjectARB(program_, fragmemt_shader)); + + GL(glLinkProgramARB(program_)); + + GL(glDeleteObjectARB(vertex_shader)); + GL(glDeleteObjectARB(fragmemt_shader)); + + GL(glGetObjectParameterivARB(program_, GL_OBJECT_LINK_STATUS_ARB, &success)); + if (success == GL_FALSE) + { + char info[2048]; + GL(glGetInfoLogARB(program_, sizeof(info), 0, info)); + GL(glDeleteObjectARB(program_)); + std::stringstream str; + str << "Failed to link shader program:" << std::endl << info << std::endl; + BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); + } + GL(glUseProgramObjectARB(program_)); + glUniform1i(glGetUniformLocation(program_, "plane[0]"), 0); + glUniform1i(glGetUniformLocation(program_, "plane[1]"), 1); + glUniform1i(glGetUniformLocation(program_, "plane[2]"), 2); + glUniform1i(glGetUniformLocation(program_, "plane[3]"), 3); + } + + GLint get_location(const char* name) + { + GLint loc = glGetUniformLocation(program_, name); + return loc; + } + + shader_program& operator=(shader_program&& other) + { + program_ = other.program_; + other.program_ = 0; + return *this; + } + + ~shader_program() + { + glDeleteProgram(program_); + } + + void use() + { + GL(glUseProgramObjectARB(program_)); + } +}; + +GLubyte progressive_pattern[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +GLubyte upper_pattern[] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}; + +GLubyte lower_pattern[] = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}; + +struct image_kernel::implementation : boost::noncopyable +{ + std::unordered_map shaders_; + +public: + std::unordered_map& shaders() + { + GL(glEnable(GL_POLYGON_STIPPLE)); + GL(glEnable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + if(shaders_.empty()) + { + std::string common_vertex = + "void main() " + "{ " + " gl_TexCoord[0] = gl_MultiTexCoord0; " + " gl_FrontColor = gl_Color; " + " gl_Position = ftransform(); " + "} "; + + std::string common_fragment = + "uniform sampler2D plane[4]; " + "uniform float gain; " + "uniform bool HD; " + + // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf + // TODO: Support for more yuv formats might be needed. + "vec4 ycbcra_to_bgra_sd(float y, float cb, float cr, float a) " + "{ " + " cb -= 0.5; " + " cr -= 0.5; " + " y = 1.164*(y-0.0625); " + " " + " vec4 color; " + " color.r = y + 1.596 * cr; " + " color.g = y - 0.813 * cr - 0.391 * cb; " + " color.b = y + 2.018 * cb; " + " color.a = a; " + " " + " return color; " + "} " + " " + + "vec4 ycbcra_to_bgra_hd(float y, float cb, float cr, float a) " + "{ " + " cb -= 0.5; " + " cr -= 0.5; " + " y = 1.164*(y-0.0625); " + " " + " vec4 color; " + " color.r = y + 1.793 * cr; " + " color.g = y - 0.534 * cr - 0.213 * cb; " + " color.b = y + 2.115 * cb; " + " color.a = a; " + " " + " return color; " + "} " + " "; + + shaders_[core::pixel_format::abgr] = shader_program(common_vertex, common_fragment + + + "void main() " + "{ " + " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); " + " gl_FragColor = abgr.argb * gain; " + "} "); + + shaders_[core::pixel_format::argb]= shader_program(common_vertex, common_fragment + + + "void main() " + "{ " + " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); " + " gl_FragColor = argb.grab * gl_Color * gain; " + "} "); + + shaders_[core::pixel_format::bgra]= shader_program(common_vertex, common_fragment + + + "void main() " + "{ " + " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); " + " gl_FragColor = bgra.rgba * gl_Color * gain; " + "} "); + + shaders_[core::pixel_format::rgba] = shader_program(common_vertex, common_fragment + + + "void main() " + "{ " + " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); " + " gl_FragColor = rgba.bgra * gl_Color * gain; " + "} "); + + shaders_[core::pixel_format::ycbcr] = shader_program(common_vertex, common_fragment + + + "void main() " + "{ " + " float y = texture2D(plane[0], gl_TexCoord[0].st).r; " + " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; " + " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; " + " float a = 1.0; " + " if(HD) " + " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;" + " else " + " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;" + "} "); + + shaders_[core::pixel_format::ycbcra] = shader_program(common_vertex, common_fragment + + + "void main() " + "{ " + " float y = texture2D(plane[0], gl_TexCoord[0].st).r; " + " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; " + " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; " + " float a = texture2D(plane[3], gl_TexCoord[0].st).r; " + " if(HD) " + " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;" + " else " + " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;" + "} "); + } + return shaders_; + } +}; + +image_kernel::image_kernel() : impl_(new implementation()){} + +void image_kernel::apply(const core::pixel_format_desc& pix_desc, const core::image_transform& transform) +{ + impl_->shaders()[pix_desc.pix_fmt].use(); + + GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast(transform.get_gain()))); + GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0)); + + if(transform.get_mode() == core::video_mode::upper) + glPolygonStipple(upper_pattern); + else if(transform.get_mode() == core::video_mode::lower) + glPolygonStipple(lower_pattern); + else + glPolygonStipple(progressive_pattern); +} + +}} \ No newline at end of file diff --git a/core/mixer/image/image_kernel.h b/core/mixer/image/image_kernel.h new file mode 100644 index 000000000..e258249a1 --- /dev/null +++ b/core/mixer/image/image_kernel.h @@ -0,0 +1,40 @@ +/* +* 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 . +* +*/ +#pragma once + +#include + +#include +#include + +namespace caspar { namespace mixer { + +class image_kernel +{ +public: + image_kernel(); + void apply(const core::pixel_format_desc& pix_desc, const core::image_transform& mode); + +private: + struct implementation; + std::shared_ptr impl_; +}; + +}} \ No newline at end of file diff --git a/core/mixer/image/image_mixer.cpp b/core/mixer/image/image_mixer.cpp new file mode 100644 index 000000000..aa9741db7 --- /dev/null +++ b/core/mixer/image/image_mixer.cpp @@ -0,0 +1,196 @@ +/* +* 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 "image_mixer.h" +#include "image_kernel.h" + +#include "../gpu/ogl_device.h" +#include "../gpu/host_buffer.h" +#include "../gpu/device_buffer.h" +#include "../gpu/gpu_write_frame.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +namespace caspar { namespace mixer { + +struct image_mixer::implementation : boost::noncopyable +{ + const core::video_format_desc format_desc_; + + std::stack transform_stack_; + + GLuint fbo_; + std::array, 2> render_targets_; + + std::shared_ptr reading_; + + image_kernel kernel_; + + safe_ptr context_; + +public: + implementation(const core::video_format_desc& format_desc) + : format_desc_(format_desc) + , context_(ogl_device::create()) + { + context_->invoke([] + { + if(!GLEE_VERSION_3_0) + BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 3.0 support.")); + }); + + context_->begin_invoke([=] + { + transform_stack_.push(core::image_transform()); + transform_stack_.top().set_mode(core::video_mode::progressive); + + GL(glEnable(GL_TEXTURE_2D)); + GL(glDisable(GL_DEPTH_TEST)); + + render_targets_[0] = context_->create_device_buffer(format_desc.width, format_desc.height, 4); + render_targets_[1] = context_->create_device_buffer(format_desc.width, format_desc.height, 4); + + GL(glGenFramebuffers(1, &fbo_)); + GL(glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_)); + GL(glReadBuffer(GL_COLOR_ATTACHMENT0_EXT)); + + reading_ = context_->create_host_buffer(format_desc_.size, host_buffer::read_only); + }); + } + + ~implementation() + { + glDeleteFramebuffersEXT(1, &fbo_); + } + + void begin(const core::basic_frame& frame) + { + transform_stack_.push(transform_stack_.top()*frame.get_image_transform()); + } + + void visit(core::write_frame& frame) + { + auto gpu_frame = boost::polymorphic_downcast(&frame); + auto& desc = gpu_frame->get_pixel_format_desc(); + auto& buffers = gpu_frame->get_plane_buffers(); + + auto transform = transform_stack_.top(); + context_->begin_invoke([=] + { + GL(glColor4d(1.0, 1.0, 1.0, transform.get_opacity())); + GL(glViewport(0, 0, format_desc_.width, format_desc_.height)); + kernel_.apply(desc, transform); + + std::vector> device_buffers; + for(size_t n = 0; n < buffers.size(); ++n) + { + auto texture = context_->create_device_buffer(desc.planes[n].width, desc.planes[n].height, desc.planes[n].channels); + texture->read(*buffers[n]); + device_buffers.push_back(texture); + } + + for(size_t n = 0; n < buffers.size(); ++n) + { + GL(glActiveTexture(GL_TEXTURE0+n)); + device_buffers[n]->bind(); + } + + auto m_p = transform.get_key_translation(); + auto m_s = transform.get_key_scale(); + double w = static_cast(format_desc_.width); + double h = static_cast(format_desc_.height); + + GL(glEnable(GL_SCISSOR_TEST)); + GL(glScissor(static_cast(m_p[0]*w), static_cast(m_p[1]*h), static_cast(m_s[0]*w), static_cast(m_s[1]*h))); + + auto f_p = transform.get_fill_translation(); + auto f_s = transform.get_fill_scale(); + + glBegin(GL_QUADS); + glTexCoord2d(0.0, 0.0); glVertex2d( f_p[0] *2.0-1.0, f_p[1] *2.0-1.0); + glTexCoord2d(1.0, 0.0); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, f_p[1] *2.0-1.0); + glTexCoord2d(1.0, 1.0); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0); + glTexCoord2d(0.0, 1.0); glVertex2d( f_p[0] *2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0); + glEnd(); + GL(glDisable(GL_SCISSOR_TEST)); + }); + } + + void end() + { + transform_stack_.pop(); + } + + boost::unique_future> begin_pass() + { + return context_->begin_invoke([=]() -> safe_ptr + { + reading_->map(); + render_targets_[0]->attach(0); + GL(glClear(GL_COLOR_BUFFER_BIT)); + return safe_ptr(reading_); + }); + } + + void end_pass() + { + context_->begin_invoke([=] + { + reading_ = context_->create_host_buffer(format_desc_.size, host_buffer::read_only); + render_targets_[0]->write(*reading_); + std::rotate(render_targets_.begin(), render_targets_.begin() + 1, render_targets_.end()); + }); + } + + std::vector> create_buffers(const core::pixel_format_desc& format) + { + std::vector> buffers; + std::transform(format.planes.begin(), format.planes.end(), std::back_inserter(buffers), [&](const core::pixel_format_desc::plane& plane) + { + return context_->create_host_buffer(plane.size, host_buffer::write_only); + }); + return buffers; + } +}; + +image_mixer::image_mixer(const core::video_format_desc& format_desc) : impl_(new implementation(format_desc)){} +void image_mixer::begin(const core::basic_frame& frame){impl_->begin(frame);} +void image_mixer::visit(core::write_frame& frame){impl_->visit(frame);} +void image_mixer::end(){impl_->end();} +boost::unique_future> image_mixer::begin_pass(){ return impl_->begin_pass();} +void image_mixer::end_pass(){impl_->end_pass();} +std::vector> image_mixer::create_buffers(const core::pixel_format_desc& format){return impl_->create_buffers(format);} + +}} \ No newline at end of file diff --git a/core/mixer/image/image_mixer.h b/core/mixer/image/image_mixer.h new file mode 100644 index 000000000..c0350fb62 --- /dev/null +++ b/core/mixer/image/image_mixer.h @@ -0,0 +1,55 @@ +/* +* 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 . +* +*/ +#pragma once + +#include +#include +#include + +#include "../gpu/host_buffer.h" + +#include +#include + +#include +#include + +namespace caspar { namespace mixer { + +class image_mixer : public core::frame_visitor, boost::noncopyable +{ +public: + image_mixer(const core::video_format_desc& format_desc); + + virtual void begin(const core::basic_frame& frame); + virtual void visit(core::write_frame& frame); + virtual void end(); + + boost::unique_future> begin_pass(); + void end_pass(); + + std::vector> create_buffers(const core::pixel_format_desc& format); + +private: + struct implementation; + std::shared_ptr impl_; +}; + +}} \ No newline at end of file diff --git a/modules/ffmpeg/producer/video/video_decoder.h b/modules/ffmpeg/producer/video/video_decoder.h index aa376d4c5..cb272220d 100644 --- a/modules/ffmpeg/producer/video/video_decoder.h +++ b/modules/ffmpeg/producer/video/video_decoder.h @@ -21,7 +21,7 @@ #include -#include +#include struct AVCodecContext; diff --git a/protocol/protocol.vcxproj b/protocol/protocol.vcxproj index 13f901ffc..f76a898e7 100644 --- a/protocol/protocol.vcxproj +++ b/protocol/protocol.vcxproj @@ -25,9 +25,6 @@ {79388c20-6499-4bf6-b8b9-d8c33d7d4ddd} - - {477e12a4-1b28-4ff7-b46d-76606bdd1891} - {816deaba-3757-4306-afe0-c27cf96c4dea} diff --git a/shell/main.cpp b/shell/main.cpp index f95cfb6fa..6c72523e0 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -38,7 +38,7 @@ #include #include -#include +#include #include diff --git a/shell/shell.vcxproj b/shell/shell.vcxproj index 63d27459f..695b53cd3 100644 --- a/shell/shell.vcxproj +++ b/shell/shell.vcxproj @@ -34,9 +34,6 @@ {79388c20-6499-4bf6-b8b9-d8c33d7d4ddd} - - {477e12a4-1b28-4ff7-b46d-76606bdd1891} - {69313d25-9f54-4fc9-9872-628a4dd79464} -- 2.39.2