3 #include "frame_mixer_device.h"
\r
5 #include "audio/audio_mixer.h"
\r
6 #include "audio/audio_transform.h"
\r
7 #include "frame/write_frame.h"
\r
8 #include "frame/read_frame.h"
\r
9 #include "image/image_mixer.h"
\r
10 #include "image/image_transform.h"
\r
12 #include <common/exception/exceptions.h>
\r
13 #include <common/concurrency/executor.h>
\r
14 #include <common/diagnostics/graph.h>
\r
15 #include <common/utility/timer.h>
\r
16 #include <common/gl/gl_check.h>
\r
18 #include <core/video_format.h>
\r
20 #include <tbb/concurrent_queue.h>
\r
21 #include <tbb/concurrent_unordered_map.h>
\r
23 #include <boost/range/algorithm.hpp>
\r
25 #include <unordered_map>
\r
27 namespace caspar { namespace core {
\r
29 struct frame_mixer_device::implementation : boost::noncopyable
\r
31 safe_ptr<diagnostics::graph> graph_;
\r
33 timer wait_perf_timer_;
\r
35 const video_format_desc format_desc_;
\r
37 audio_mixer audio_mixer_;
\r
38 image_mixer image_mixer_;
\r
40 output_func output_;
\r
42 std::unordered_map<int, image_transform> image_transforms_;
\r
43 std::unordered_map<int, audio_transform> audio_transforms_;
\r
47 implementation(const video_format_desc& format_desc, const output_func& output)
\r
48 : graph_(diagnostics::create_graph("mixer"))
\r
49 , format_desc_(format_desc)
\r
50 , image_mixer_(format_desc)
\r
52 , executor_(L"frame_mixer_device")
\r
54 graph_->guide("frame-time", 0.5f);
\r
55 graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));
\r
56 graph_->set_color("output-wait", diagnostics::color(0.1f, 0.7f, 0.8f));
\r
57 graph_->set_color("output-buffer", diagnostics::color( 0.0f, 1.0f, 0.0f));
\r
59 executor_.set_capacity(2);
\r
64 CASPAR_LOG(info) << "Shutting down mixer-device.";
\r
67 void send(const std::vector<safe_ptr<draw_frame>>& frames)
\r
69 executor_.begin_invoke([=]
\r
71 perf_timer_.reset();
\r
72 auto image = image_mixer_.begin_pass();
\r
73 BOOST_FOREACH(auto& frame, frames)
\r
75 image_mixer_.begin(image_transforms_[frame->get_layer_index()]);
\r
76 frame->process_image(image_mixer_);
\r
79 image_mixer_.end_pass();
\r
81 auto audio = audio_mixer_.begin_pass();
\r
82 BOOST_FOREACH(auto& frame, frames)
\r
84 audio_mixer_.begin(audio_transforms_[frame->get_layer_index()]);
\r
85 frame->process_audio(audio_mixer_);
\r
88 audio_mixer_.end_pass();
\r
89 graph_->update("frame-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5));
\r
91 wait_perf_timer_.reset();
\r
92 output_(make_safe<const read_frame>(std::move(image.get()), std::move(audio)));
\r
93 graph_->update("output-wait", static_cast<float>(wait_perf_timer_.elapsed()/format_desc_.interval*0.5));
\r
95 graph_->update("output-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));
\r
98 safe_ptr<write_frame> create_frame(const pixel_format_desc& desc)
\r
100 return make_safe<write_frame>(desc, image_mixer_.create_buffers(desc));
\r
103 image_transform get_image_transform(int index)
\r
105 return executor_.invoke([&]{return image_transforms_[index];});
\r
108 audio_transform get_audio_transform(int index)
\r
110 return executor_.invoke([&]{return audio_transforms_[index];});
\r
113 void set_image_transform(int index, image_transform&& transform)
\r
115 return executor_.invoke([&]{image_transforms_[index] = std::move(transform);});
\r
118 void set_audio_transform(int index, audio_transform&& transform)
\r
120 return executor_.invoke([&]{audio_transforms_[index] = std::move(transform);});
\r
124 frame_mixer_device::frame_mixer_device(const video_format_desc& format_desc, const output_func& output) : impl_(new implementation(format_desc, output)){}
\r
125 frame_mixer_device::frame_mixer_device(frame_mixer_device&& other) : impl_(std::move(other.impl_)){}
\r
126 void frame_mixer_device::send(const std::vector<safe_ptr<draw_frame>>& frames){impl_->send(frames);}
\r
127 const video_format_desc& frame_mixer_device::get_video_format_desc() const { return impl_->format_desc_; }
\r
128 safe_ptr<write_frame> frame_mixer_device::create_frame(const pixel_format_desc& desc){ return impl_->create_frame(desc); }
\r
129 safe_ptr<write_frame> frame_mixer_device::create_frame(size_t width, size_t height, pixel_format::type pix_fmt)
\r
131 // Create bgra frame
\r
132 pixel_format_desc desc;
\r
133 desc.pix_fmt = pix_fmt;
\r
134 desc.planes.push_back(pixel_format_desc::plane(width, height, 4));
\r
135 return create_frame(desc);
\r
138 safe_ptr<write_frame> frame_mixer_device::create_frame(pixel_format::type pix_fmt)
\r
140 // Create bgra frame with output resolution
\r
141 pixel_format_desc desc;
\r
142 desc.pix_fmt = pix_fmt;
\r
143 desc.planes.push_back(pixel_format_desc::plane(get_video_format_desc().width, get_video_format_desc().height, 4));
\r
144 return create_frame(desc);
\r
146 image_transform frame_mixer_device::get_image_transform(int index){return impl_->get_image_transform(index);}
\r
147 audio_transform frame_mixer_device::get_audio_transform(int index){return impl_->get_audio_transform(index);}
\r
148 void frame_mixer_device::set_image_transform(int index, image_transform&& transform){impl_->set_image_transform(index, std::move(transform));}
\r
149 void frame_mixer_device::set_audio_transform(int index, audio_transform&& transform){impl_->set_audio_transform(index, std::move(transform));}
\r