]> git.sesse.net Git - casparcg/blob - modules/oal/consumer/oal_consumer.cpp
Merge branch 'master' of https://github.com/CasparCG/Server
[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 <common/exception/exceptions.h>
25 #include <common/diagnostics/graph.h>
26 #include <common/log/log.h>
27 #include <common/utility/timer.h>
28 #include <common/utility/string.h>
29 #include <common/concurrency/future_util.h>
30 #include <common/exception/win32_exception.h>
31
32 #include <core/parameters/parameters.h>
33 #include <core/consumer/frame_consumer.h>
34 #include <core/mixer/audio/audio_util.h>
35 #include <core/video_format.h>
36
37 #include <core/mixer/read_frame.h>
38
39 #include <SFML/Audio.hpp>
40
41 #include <boost/circular_buffer.hpp>
42 #include <boost/property_tree/ptree.hpp>
43 #include <boost/timer.hpp>
44 #include <boost/thread/future.hpp>
45 #include <boost/optional.hpp>
46
47 #include <tbb/atomic.h>
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                                                                            perf_timer_;
58         int                                                                                                     channel_index_;
59
60         tbb::concurrent_bounded_queue<std::pair<std::shared_ptr<core::read_frame>, std::shared_ptr<audio_buffer_16>>>   input_;
61         boost::circular_buffer<audio_buffer_16>                         container_;
62         tbb::atomic<bool>                                                                       is_running_;
63         tbb::atomic<int64_t>                                                            presentation_age_;
64         bool                                                                                            started_;
65
66         core::video_format_desc                                                         format_desc_;
67         core::channel_layout                                                            channel_layout_;
68 public:
69         oal_consumer() 
70                 : container_(16)
71                 , channel_index_(-1)
72                 , started_(false)
73                 , channel_layout_(
74                                 core::default_channel_layout_repository().get_by_name(
75                                                 L"STEREO"))
76         {
77                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
78                 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
79                 diagnostics::register_graph(graph_);
80
81                 is_running_ = true;
82                 presentation_age_ = 0;
83                 input_.set_capacity(2);
84         }
85
86         ~oal_consumer()
87         {
88                 is_running_ = false;
89                 input_.try_push(std::make_pair(std::shared_ptr<core::read_frame>(), std::make_shared<audio_buffer_16>()));
90                 input_.try_push(std::make_pair(std::shared_ptr<core::read_frame>(), std::make_shared<audio_buffer_16>()));
91                 Stop();
92                 input_.try_push(std::make_pair(std::shared_ptr<core::read_frame>(), std::make_shared<audio_buffer_16>()));
93                 input_.try_push(std::make_pair(std::shared_ptr<core::read_frame>(), std::make_shared<audio_buffer_16>()));
94
95                 CASPAR_LOG(info) << print() << L" Successfully Uninitialized."; 
96         }
97
98         // frame consumer
99
100         virtual void initialize(const core::video_format_desc& format_desc, int channel_index) override
101         {
102                 format_desc_    = format_desc;          
103                 channel_index_  = channel_index;
104                 graph_->set_text(print());
105
106                 /*if (Status() != Playing)
107                 {
108                         sf::SoundStream::Initialize(2, format_desc_.audio_sample_rate);
109                 }*/
110
111                 CASPAR_LOG(info) << print() << " Sucessfully Initialized.";
112         }
113
114         virtual int64_t presentation_frame_age_millis() const override
115         {
116                 return presentation_age_;
117         }
118
119         virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override
120         {
121                 std::shared_ptr<audio_buffer_16> buffer;
122
123                 if (core::needs_rearranging(
124                                 frame->multichannel_view(),
125                                 channel_layout_,
126                                 channel_layout_.num_channels))
127                 {
128                         core::audio_buffer downmixed;
129                         downmixed.resize(
130                                         frame->multichannel_view().num_samples() 
131                                                         * channel_layout_.num_channels,
132                                         0);
133
134                         auto dest_view = core::make_multichannel_view<int32_t>(
135                                         downmixed.begin(), downmixed.end(), channel_layout_);
136
137                         core::rearrange_or_rearrange_and_mix(
138                                         frame->multichannel_view(),
139                                         dest_view,
140                                         core::default_mix_config_repository());
141
142                         buffer = std::make_shared<audio_buffer_16>(
143                                         core::audio_32_to_16(downmixed));
144                 }
145                 else
146                 {
147                         buffer = std::make_shared<audio_buffer_16>(
148                                         core::audio_32_to_16(frame->audio_data()));
149                 }
150
151                 if (!input_.try_push(std::make_pair(frame, buffer)))
152                         graph_->set_tag("dropped-frame");
153
154                 if (Status() != Playing && !started_)
155                 {
156                         sf::SoundStream::Initialize(2, format_desc_.audio_sample_rate);
157                         Play();
158                         started_ = true;
159                 }
160
161                 return wrap_as_future(is_running_.load());
162         }
163         
164         virtual std::wstring print() const override
165         {
166                 return L"oal[" + boost::lexical_cast<std::wstring>(channel_index_) + L"|" + format_desc_.name + L"]";
167         }
168
169         virtual bool has_synchronization_clock() const override
170         {
171                 return false;
172         }
173
174         virtual boost::property_tree::wptree info() const override
175         {
176                 boost::property_tree::wptree info;
177                 info.add(L"type", L"oal-consumer");
178                 return info;
179         }
180         
181         virtual size_t buffer_depth() const override
182         {
183                 return 6;
184         }
185
186         // oal_consumer
187         
188         virtual bool OnGetData(sf::SoundStream::Chunk& data) override
189         {               
190                 win32_exception::ensure_handler_installed_for_thread(
191                                 "sfml-audio-thread");
192                 std::pair<std::shared_ptr<core::read_frame>, std::shared_ptr<audio_buffer_16>> audio_data;
193
194                 input_.pop(audio_data); // Block until available
195
196                 graph_->set_value("tick-time", perf_timer_.elapsed()*format_desc_.fps*0.5);             
197                 perf_timer_.restart();
198
199                 container_.push_back(std::move(*audio_data.second));
200                 data.Samples = container_.back().data();
201                 data.NbSamples = container_.back().size();      
202                 
203
204                 if (audio_data.first)
205                         presentation_age_ = audio_data.first->get_age_millis();
206
207                 return is_running_;
208         }
209
210         virtual int index() const override
211         {
212                 return 500;
213         }
214 };
215
216 safe_ptr<core::frame_consumer> create_consumer(const core::parameters& params)
217 {
218         if(params.size() < 1 || params[0] != L"AUDIO")
219                 return core::frame_consumer::empty();
220
221         return make_safe<oal_consumer>();
222 }
223
224 safe_ptr<core::frame_consumer> create_consumer()
225 {
226         return make_safe<oal_consumer>();
227 }
228
229 }}