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