]> git.sesse.net Git - casparcg/blob - modules/oal/consumer/oal_consumer.cpp
Improved read_fps.
[casparcg] / modules / oal / consumer / oal_consumer.cpp
1 /*
2 * Copyright 2013 Sveriges Television AB http://casparcg.com/
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "oal_consumer.h"
23
24 #include <iterator>
25
26 #include <common/exception/exceptions.h>
27 #include <common/diagnostics/graph.h>
28 #include <common/log/log.h>
29 #include <common/utility/timer.h>
30 #include <common/utility/string.h>
31 #include <common/concurrency/future_util.h>
32 #include <common/exception/win32_exception.h>
33
34 #include <core/parameters/parameters.h>
35 #include <core/consumer/frame_consumer.h>
36 #include <core/mixer/audio/audio_util.h>
37 #include <core/video_format.h>
38
39 #include <core/mixer/read_frame.h>
40
41 #include <SFML/Audio.hpp>
42
43 #include <boost/property_tree/ptree.hpp>
44 #include <boost/timer.hpp>
45 #include <boost/thread/future.hpp>
46 #include <boost/optional.hpp>
47
48 #include <tbb/concurrent_queue.h>
49
50 namespace caspar { namespace oal {
51
52 typedef std::vector<int16_t, tbb::cache_aligned_allocator<int16_t>> audio_buffer_16;
53
54 struct oal_consumer : public core::frame_consumer,  public sf::SoundStream
55 {
56         safe_ptr<diagnostics::graph>                                            graph_;
57         boost::timer                                                                            tick_timer_;
58         int                                                                                                     channel_index_;
59
60         tbb::concurrent_bounded_queue<std::shared_ptr<std::vector<audio_buffer_16>>>    input_;
61         std::shared_ptr<std::vector<audio_buffer_16>>           chunk_builder_;
62         audio_buffer_16                                                                         container_;
63         tbb::atomic<bool>                                                                       is_running_;
64
65         core::video_format_desc                                                         format_desc_;
66         core::channel_layout                                                            channel_layout_;
67         size_t                                                                                          frames_to_buffer_;
68 public:
69         oal_consumer() 
70                 : channel_index_(-1)
71                 , channel_layout_(
72                                 core::default_channel_layout_repository().get_by_name(
73                                                 L"STEREO"))
74         {
75                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
76                 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
77                 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
78                 diagnostics::register_graph(graph_);
79
80                 is_running_ = true;
81                 input_.set_capacity(1);
82         }
83
84         ~oal_consumer()
85         {
86                 is_running_ = false;
87                 input_.try_push(std::make_shared<std::vector<audio_buffer_16>>());
88                 input_.try_push(std::make_shared<std::vector<audio_buffer_16>>());
89                 Stop();
90                 input_.try_push(std::make_shared<std::vector<audio_buffer_16>>());
91                 input_.try_push(std::make_shared<std::vector<audio_buffer_16>>());
92
93                 CASPAR_LOG(info) << print() << L" Successfully Uninitialized."; 
94         }
95
96         // frame consumer
97
98         virtual void initialize(const core::video_format_desc& format_desc, int channel_index) override
99         {
100                 format_desc_    = format_desc;          
101                 channel_index_  = channel_index;
102                 graph_->set_text(print());
103
104                 if(Status() != Playing)
105                 {
106                         sf::SoundStream::Initialize(2, format_desc.audio_sample_rate);
107
108                         // Each time OnGetData is called it seems to no longer be enough
109                         // with the samples of one frame (since the change to statically
110                         // linked SFML 1.6), so the samples of a few frames is needed to be
111                         // collected.
112                         static const double SAMPLES_NEEDED_BY_SFML_MULTIPLE = 0.1;
113                         int min_num_samples_in_chunk = static_cast<int>(format_desc.audio_sample_rate * SAMPLES_NEEDED_BY_SFML_MULTIPLE);
114                         int min_num_samples_in_frame = *std::min_element(format_desc.audio_cadence.begin(), format_desc.audio_cadence.end());
115                         int min_frames_to_buffer = min_num_samples_in_chunk / min_num_samples_in_frame + (min_num_samples_in_chunk % min_num_samples_in_frame ? 1 : 0);
116                         frames_to_buffer_ = min_frames_to_buffer;
117
118                         Play();
119                 }
120                 CASPAR_LOG(info) << print() << " Sucessfully Initialized.";
121         }
122
123         virtual int64_t presentation_frame_age_millis() const override
124         {
125                 return 0;
126         }
127
128         virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override
129         {
130                 audio_buffer_16 buffer;
131
132                 if (core::needs_rearranging(
133                                 frame->multichannel_view(),
134                                 channel_layout_,
135                                 channel_layout_.num_channels))
136                 {
137                         core::audio_buffer downmixed;
138                         downmixed.resize(
139                                         frame->multichannel_view().num_samples() 
140                                                         * channel_layout_.num_channels,
141                                         0);
142
143                         auto dest_view = core::make_multichannel_view<int32_t>(
144                                         downmixed.begin(), downmixed.end(), channel_layout_);
145
146                         core::rearrange_or_rearrange_and_mix(
147                                         frame->multichannel_view(),
148                                         dest_view,
149                                         core::default_mix_config_repository());
150
151                         buffer = core::audio_32_to_16(downmixed);
152                 }
153                 else
154                 {
155                         buffer = core::audio_32_to_16(frame->audio_data());
156                 }
157
158                 if (!chunk_builder_)
159                 {
160                         chunk_builder_.reset(new std::vector<audio_buffer_16>);
161                         chunk_builder_->push_back(std::move(buffer));
162                 }
163                 else
164                 {
165                         chunk_builder_->push_back(std::move(buffer));
166                 }
167
168                 if (chunk_builder_->size() == frames_to_buffer_)
169                 {
170                         if (!input_.try_push(chunk_builder_))
171                         {
172                                 graph_->set_tag("dropped-frame");
173                                 chunk_builder_->pop_back();
174                         }
175                         else
176                         {
177                                 chunk_builder_.reset();
178                         }
179                 }
180
181                 return wrap_as_future(is_running_.load());
182         }
183         
184         virtual std::wstring print() const override
185         {
186                 return L"oal[" + boost::lexical_cast<std::wstring>(channel_index_) + L"|" + format_desc_.name + L"]";
187         }
188
189         virtual bool has_synchronization_clock() const override
190         {
191                 return false;
192         }
193
194         virtual boost::property_tree::wptree info() const override
195         {
196                 boost::property_tree::wptree info;
197                 info.add(L"type", L"oal-consumer");
198                 return info;
199         }
200         
201         virtual size_t buffer_depth() const override
202         {
203                 return 6;
204         }
205
206         // oal_consumer
207         
208         virtual bool OnGetData(sf::SoundStream::Chunk& data) override
209         {
210                 win32_exception::ensure_handler_installed_for_thread(
211                                 "sfml-audio-thread");
212                 std::shared_ptr<std::vector<audio_buffer_16>> audio_data;               
213
214                 if (!input_.try_pop(audio_data))
215                 {
216                         graph_->set_tag("late-frame");
217                         input_.pop(audio_data); // Block until available
218                 }
219
220                 container_.clear();
221                 
222                 // Concatenate to one large buffer.
223                 BOOST_FOREACH(auto& buffer, *audio_data)
224                 {
225                         std::copy(buffer.begin(), buffer.end(), std::back_inserter(container_));
226                 }
227
228                 data.Samples = container_.data();
229                 data.NbSamples = container_.size();     
230                 
231                 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5 / frames_to_buffer_);         
232                 tick_timer_.restart();
233
234                 return is_running_;
235         }
236
237         virtual int index() const override
238         {
239                 return 500;
240         }
241 };
242
243 safe_ptr<core::frame_consumer> create_consumer(const core::parameters& params)
244 {
245         if(params.size() < 1 || params[0] != L"AUDIO")
246                 return core::frame_consumer::empty();
247
248         return make_safe<oal_consumer>();
249 }
250
251 safe_ptr<core::frame_consumer> create_consumer()
252 {
253         return make_safe<oal_consumer>();
254 }
255
256 }}