]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/audio_filter.cpp
[ffmpeg] Reimplemented support for playing all audio streams in a clip and treating...
[casparcg] / modules / ffmpeg / producer / filter / audio_filter.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 "../../StdAfx.h"
23
24 #include "audio_filter.h"
25
26 #include "../../ffmpeg_error.h"
27 #include "../../ffmpeg.h"
28 #include "../util/util.h"
29
30 #include <common/assert.h>
31 #include <common/except.h>
32
33 #include <boost/algorithm/string.hpp>
34 #include <boost/thread.hpp>
35 #include <boost/format.hpp>
36 #include <boost/rational.hpp>
37
38 #include <cstdio>
39 #include <sstream>
40 #include <string>
41 #include <algorithm>
42
43 #if defined(_MSC_VER)
44 #pragma warning (push)
45 #pragma warning (disable : 4244)
46 #endif
47 extern "C"
48 {
49         #include <libavutil/avutil.h>
50         #include <libavutil/imgutils.h>
51         #include <libavutil/opt.h>
52         #include <libavfilter/avfilter.h>
53         #include <libavfilter/avcodec.h>
54         #include <libavfilter/buffersink.h>
55         #include <libavfilter/buffersrc.h>
56 }
57 #if defined(_MSC_VER)
58 #pragma warning (pop)
59 #endif
60
61 namespace caspar { namespace ffmpeg {
62
63 std::string create_sourcefilter_str(const audio_input_pad& input_pad, std::string name)
64 {
65         const auto asrc_options = (boost::format("abuffer=time_base=%1%/%2%:sample_rate=%3%:sample_fmt=%4%:channel_layout=0x%|5$x| [%6%]")
66                 % input_pad.time_base.numerator() % input_pad.time_base.denominator()
67                 % input_pad.sample_rate
68                 % av_get_sample_fmt_name(input_pad.sample_fmt)
69                 % input_pad.audio_channel_layout
70                 % name).str();
71
72         return asrc_options;
73 }
74
75 std::string create_filter_list(const std::vector<std::string>& items)
76 {
77         return boost::join(items, "|");
78 }
79
80 std::string channel_layout_to_string(uint64_t channel_layout)
81 {
82         return (boost::format("0x%|1$x|") % channel_layout).str();
83 }
84
85 std::string create_sinkfilter_str(const audio_output_pad& output_pad, std::string name)
86 {
87         const auto asink_options = (boost::format("[%4%] abuffersink")//=sample_fmts=%1%:channel_layouts=%2%:sample_rates=%3%")
88                 % create_filter_list(cpplinq::from(output_pad.sample_fmts)
89                                 .select(&av_get_sample_fmt_name)
90                                 .select([](const char* str) { return std::string(str); })
91                                 .to_vector())
92                 % create_filter_list(cpplinq::from(output_pad.sample_fmts)
93                                 .select(&channel_layout_to_string)
94                                 .to_vector())
95                 % create_filter_list(cpplinq::from(output_pad.sample_rates)
96                                 .select([](int samplerate) { return boost::lexical_cast<std::string>(samplerate); })
97                                 .to_vector())
98                 % name).str();
99
100         return asink_options;
101 }
102
103 struct audio_filter::implementation
104 {
105         std::string                                             filtergraph_;
106
107         std::shared_ptr<AVFilterGraph>  audio_graph_;
108         std::vector<AVFilterContext*>   audio_graph_inputs_;
109         std::vector<AVFilterContext*>   audio_graph_outputs_;
110
111         std::vector<audio_input_pad>    input_pads_;
112
113         implementation(
114                 std::vector<audio_input_pad> input_pads,
115                 std::vector<audio_output_pad> output_pads,
116                 const std::string& filtergraph)
117                 : filtergraph_(boost::to_lower_copy(filtergraph))
118                 , input_pads_(std::move(input_pads))
119         {
120                 if (input_pads_.empty())
121                         CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("input_pads cannot be empty"));
122
123                 if (output_pads.empty())
124                         CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("output_pads cannot be empty"));
125
126                 audio_graph_.reset(
127                         avfilter_graph_alloc(),
128                         [](AVFilterGraph* p)
129                 {
130                         avfilter_graph_free(&p);
131                 });
132
133                 std::vector<std::string> complete_filter_graph;
134
135                 {
136                         int i = 0;
137                         for (auto& input_pad : input_pads_)
138                                 complete_filter_graph.push_back(create_sourcefilter_str(input_pad, "a:" + boost::lexical_cast<std::string>(i++)));
139                 }
140
141                 if (filtergraph_.empty())
142                         complete_filter_graph.push_back("[a:0] anull [aout:0]");
143                 else
144                         complete_filter_graph.push_back(filtergraph_);
145
146                 {
147                         int i = 0;
148                         for (auto& output_pad : output_pads)
149                                 complete_filter_graph.push_back(create_sinkfilter_str(output_pad, "aout:" + boost::lexical_cast<std::string>(i++)));
150                 }
151
152                 configure_filtergraph(
153                                 *audio_graph_,
154                                 boost::join(complete_filter_graph, ";"),
155                                 audio_graph_inputs_,
156                                 audio_graph_outputs_);
157
158                 if (is_logging_quiet_for_thread())
159                         CASPAR_LOG(trace)
160                                 <<      u16(std::string("\n")
161                                         + avfilter_graph_dump(
162                                                         audio_graph_.get(),
163                                                         nullptr));
164                 else
165                         CASPAR_LOG(debug)
166                                 << u16(std::string("\n")
167                                         + avfilter_graph_dump(
168                                                 audio_graph_.get(),
169                                                 nullptr));
170         }
171
172         void configure_filtergraph(
173                         AVFilterGraph& graph,
174                         const std::string& filtergraph,
175                         std::vector<AVFilterContext*>& source_contexts,
176                         std::vector<AVFilterContext*>& sink_contexts)
177         {
178                 AVFilterInOut* outputs  = nullptr;
179                 AVFilterInOut* inputs   = nullptr;
180
181                 FF(avfilter_graph_parse2(
182                                 &graph,
183                                 filtergraph.c_str(),
184                                 &inputs,
185                                 &outputs));
186
187                 // Workaround because outputs and inputs are not filled in for some reason
188                 for (unsigned i = 0; i < graph.nb_filters; ++i)
189                 {
190                         auto filter = graph.filters[i];
191
192                         if (std::string(filter->filter->name) == "abuffer")
193                                 source_contexts.push_back(filter);
194
195                         if (std::string(filter->filter->name) == "abuffersink")
196                                 sink_contexts.push_back(filter);
197                 }
198
199                 for (AVFilterInOut* iter = inputs; iter; iter = iter->next)
200                         source_contexts.push_back(iter->filter_ctx);
201
202                 for (AVFilterInOut* iter = outputs; iter; iter = iter->next)
203                         sink_contexts.push_back(iter->filter_ctx);
204
205                 FF(avfilter_graph_config(
206                         &graph,
207                         nullptr));
208         }
209
210         void push(int input_pad_id, const std::shared_ptr<AVFrame>& src_av_frame)
211         {
212                 FF(av_buffersrc_add_frame(
213                         audio_graph_inputs_.at(input_pad_id),
214                         src_av_frame.get()));
215         }
216
217         void push(int input_pad_id, const boost::iterator_range<const int32_t*>& frame_samples)
218         {
219                 auto& input_pad                         = input_pads_.at(input_pad_id);
220                 auto num_samples                        = frame_samples.size() / av_get_channel_layout_nb_channels(input_pad.audio_channel_layout);
221                 auto input_frame                        = ffmpeg::create_frame();
222
223                 input_frame->channels           = av_get_channel_layout_nb_channels(input_pad.audio_channel_layout);
224                 input_frame->channel_layout     = input_pad.audio_channel_layout;
225                 input_frame->sample_rate                = input_pad.sample_rate;
226                 input_frame->nb_samples         = static_cast<int>(num_samples);
227                 input_frame->format                     = input_pad.sample_fmt;
228                 input_frame->pts                                = 0;
229
230                 av_samples_fill_arrays(
231                                 input_frame->extended_data,
232                                 input_frame->linesize,
233                                 reinterpret_cast<const std::uint8_t*>(frame_samples.begin()),
234                                 input_frame->channels,
235                                 input_frame->nb_samples,
236                                 static_cast<AVSampleFormat>(input_frame->format),
237                                 16);
238
239                 push(input_pad_id, input_frame);
240         }
241
242         std::shared_ptr<AVFrame> poll(int output_pad_id)
243         {
244                 auto filt_frame = create_frame();
245
246                 const auto ret = av_buffersink_get_frame(
247                         audio_graph_outputs_.at(output_pad_id),
248                         filt_frame.get());
249
250                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
251                         return nullptr;
252
253                 FF_RET(ret, "poll");
254
255                 return filt_frame;
256         }
257 };
258
259 audio_filter::audio_filter(
260                 std::vector<audio_input_pad> input_pads,
261                 std::vector<audio_output_pad> output_pads,
262                 const std::string& filtergraph)
263         : impl_(new implementation(std::move(input_pads), std::move(output_pads), filtergraph))
264 {
265 }
266 audio_filter::audio_filter(audio_filter&& other) : impl_(std::move(other.impl_)){}
267 audio_filter& audio_filter::operator=(audio_filter&& other){impl_ = std::move(other.impl_); return *this;}
268 void audio_filter::push(int input_pad_id, const std::shared_ptr<AVFrame>& frame){impl_->push(input_pad_id, frame);}
269 void audio_filter::push(int input_pad_id, const boost::iterator_range<const int32_t*>& frame_samples) { impl_->push(input_pad_id, frame_samples); }
270 std::shared_ptr<AVFrame> audio_filter::poll(int output_pad_id){return impl_->poll(output_pad_id);}
271 std::wstring audio_filter::filter_str() const{return u16(impl_->filtergraph_);}
272 std::vector<spl::shared_ptr<AVFrame>> audio_filter::poll_all(int output_pad_id)
273 {
274         std::vector<spl::shared_ptr<AVFrame>> frames;
275         for(auto frame = poll(output_pad_id); frame; frame = poll(output_pad_id))
276                 frames.push_back(spl::make_shared_ptr(frame));
277         return frames;
278 }
279
280 }}