]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/audio_filter.cpp
c592023b3b6966bec916897eb0d4d6473f611b88
[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
41 #if defined(_MSC_VER)
42 #pragma warning (push)
43 #pragma warning (disable : 4244)
44 #endif
45 extern "C" 
46 {
47         #include <libavutil/avutil.h>
48         #include <libavutil/imgutils.h>
49         #include <libavutil/opt.h>
50         #include <libavfilter/avfilter.h>
51         #include <libavfilter/avcodec.h>
52         #include <libavfilter/buffersink.h>
53         #include <libavfilter/buffersrc.h>
54 }
55 #if defined(_MSC_VER)
56 #pragma warning (pop)
57 #endif
58
59 namespace caspar { namespace ffmpeg {
60         
61 struct audio_filter::implementation
62 {
63         std::string                                             filtergraph_;
64
65         std::shared_ptr<AVFilterGraph>  audio_graph_;   
66     AVFilterContext*                            audio_graph_in_;  
67     AVFilterContext*                            audio_graph_out_; 
68
69         implementation(
70                         boost::rational<int> in_time_base,
71                         int in_sample_rate,
72                         AVSampleFormat in_sample_fmt,
73                         std::int64_t in_audio_channel_layout,
74                         std::vector<int> out_sample_rates,
75                         std::vector<AVSampleFormat> out_sample_fmts,
76                         std::vector<std::int64_t> out_audio_channel_layouts,
77                         const std::string& filtergraph)
78                 : filtergraph_(boost::to_lower_copy(filtergraph))
79         {
80                 if (out_sample_rates.empty())
81                         out_sample_rates.push_back(48000);
82
83                 out_sample_rates.push_back(-1);
84
85                 if (out_sample_fmts.empty())
86                         out_sample_fmts.push_back(AV_SAMPLE_FMT_S32);
87
88                 out_sample_fmts.push_back(AV_SAMPLE_FMT_NONE);
89
90                 if (out_audio_channel_layouts.empty())
91                         out_audio_channel_layouts.push_back(AV_CH_LAYOUT_NATIVE);
92
93                 out_audio_channel_layouts.push_back(-1);
94
95                 audio_graph_.reset(
96                         avfilter_graph_alloc(), 
97                         [](AVFilterGraph* p)
98                         {
99                                 avfilter_graph_free(&p);
100                         });
101
102                 const auto asrc_options = (boost::format("time_base=%1%/%2%:sample_rate=%3%:sample_fmt=%4%:channel_layout=0x%|5$x|")
103                         % in_time_base.numerator() % in_time_base.denominator()
104                         % in_sample_rate
105                         % av_get_sample_fmt_name(in_sample_fmt)
106                         % in_audio_channel_layout).str();
107                                         
108                 AVFilterContext* filt_asrc = nullptr;                   
109                 FF(avfilter_graph_create_filter(
110                         &filt_asrc,
111                         avfilter_get_by_name("abuffer"), 
112                         "filter_buffer",
113                         asrc_options.c_str(),
114                         nullptr, 
115                         audio_graph_.get()));
116                                 
117                 AVFilterContext* filt_asink = nullptr;
118                 FF(avfilter_graph_create_filter(
119                         &filt_asink,
120                         avfilter_get_by_name("abuffersink"), 
121                         "filter_buffersink",
122                         nullptr, 
123                         nullptr, 
124                         audio_graph_.get()));
125                 
126 #pragma warning (push)
127 #pragma warning (disable : 4245)
128
129                 FF(av_opt_set_int_list(
130                         filt_asink,
131                         "sample_fmts",
132                         out_sample_fmts.data(),
133                         -1,
134                         AV_OPT_SEARCH_CHILDREN));
135                 FF(av_opt_set_int_list(
136                         filt_asink,
137                         "channel_layouts",
138                         out_audio_channel_layouts.data(),
139                         -1,
140                         AV_OPT_SEARCH_CHILDREN));
141                 FF(av_opt_set_int_list(
142                         filt_asink,
143                         "sample_rates",
144                         out_sample_rates.data(),
145                         -1,
146                         AV_OPT_SEARCH_CHILDREN));
147
148 #pragma warning (pop)
149                         
150                 configure_filtergraph(
151                                 *audio_graph_,
152                                 filtergraph_,
153                                 *filt_asrc,
154                                 *filt_asink);
155
156                 audio_graph_in_  = filt_asrc;
157                 audio_graph_out_ = filt_asink;
158                 
159                 if (is_logging_quiet_for_thread())
160                         CASPAR_LOG(trace)
161                                 <<      u16(std::string("\n") 
162                                         + avfilter_graph_dump(
163                                                         audio_graph_.get(), 
164                                                         nullptr));
165                 else
166                         CASPAR_LOG(debug)
167                                 << u16(std::string("\n")
168                                         + avfilter_graph_dump(
169                                                 audio_graph_.get(),
170                                                 nullptr));
171         }
172         
173         void configure_filtergraph(
174                         AVFilterGraph& graph,
175                         const std::string& filtergraph,
176                         AVFilterContext& source_ctx,
177                         AVFilterContext& sink_ctx)
178         {
179                 AVFilterInOut* outputs = nullptr;
180                 AVFilterInOut* inputs = nullptr;
181
182                 try
183                 {
184                         if(!filtergraph.empty()) 
185                         {
186                                 outputs = avfilter_inout_alloc();
187                                 inputs  = avfilter_inout_alloc();
188
189                                 CASPAR_VERIFY(outputs && inputs);
190
191                                 outputs->name       = av_strdup("in");
192                                 outputs->filter_ctx = &source_ctx;
193                                 outputs->pad_idx    = 0;
194                                 outputs->next       = nullptr;
195
196                                 inputs->name        = av_strdup("out");
197                                 inputs->filter_ctx  = &sink_ctx;
198                                 inputs->pad_idx     = 0;
199                                 inputs->next        = nullptr;
200
201                                 FF(avfilter_graph_parse(
202                                         &graph, 
203                                         filtergraph.c_str(), 
204                                         inputs,
205                                         outputs,
206                                         nullptr));
207                         } 
208                         else 
209                         {
210                                 FF(avfilter_link(
211                                         &source_ctx, 
212                                         0, 
213                                         &sink_ctx, 
214                                         0));
215                         }
216
217                         FF(avfilter_graph_config(
218                                 &graph, 
219                                 nullptr));
220                 }
221                 catch(...)
222                 {
223                         //avfilter_inout_free(&outputs);
224                         //avfilter_inout_free(&inputs);
225                         throw;
226                 }
227         }
228
229         void push(const std::shared_ptr<AVFrame>& src_av_frame)
230         {               
231                 FF(av_buffersrc_add_frame(
232                         audio_graph_in_, 
233                         src_av_frame.get()));
234         }
235
236         std::shared_ptr<AVFrame> poll()
237         {
238                 std::shared_ptr<AVFrame> filt_frame(
239                         av_frame_alloc(), 
240                         [](AVFrame* p)
241                         {
242                                 av_frame_free(&p);
243                         });
244                 
245                 const auto ret = av_buffersink_get_frame(
246                         audio_graph_out_, 
247                         filt_frame.get());
248                                 
249                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
250                         return nullptr;
251                                         
252                 FF_RET(ret, "poll");
253
254                 return filt_frame;
255         }
256 };
257
258 audio_filter::audio_filter(
259                 boost::rational<int> in_time_base,
260                 int in_sample_rate,
261                 AVSampleFormat in_sample_fmt,
262                 std::int64_t in_audio_channel_layout,
263                 std::vector<int> out_sample_rates,
264                 std::vector<AVSampleFormat> out_sample_fmts,
265                 std::vector<std::int64_t> out_audio_channel_layouts,
266                 const std::string& filtergraph)
267         : impl_(new implementation(
268                 in_time_base,
269                 in_sample_rate,
270                 in_sample_fmt,
271                 in_audio_channel_layout,
272                 std::move(out_sample_rates),
273                 std::move(out_sample_fmts),
274                 std::move(out_audio_channel_layouts),
275                 filtergraph)){}
276 audio_filter::audio_filter(audio_filter&& other) : impl_(std::move(other.impl_)){}
277 audio_filter& audio_filter::operator=(audio_filter&& other){impl_ = std::move(other.impl_); return *this;}
278 void audio_filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
279 std::shared_ptr<AVFrame> audio_filter::poll(){return impl_->poll();}
280 std::wstring audio_filter::filter_str() const{return u16(impl_->filtergraph_);}
281 std::vector<spl::shared_ptr<AVFrame>> audio_filter::poll_all()
282 {       
283         std::vector<spl::shared_ptr<AVFrame>> frames;
284         for(auto frame = poll(); frame; frame = poll())
285                 frames.push_back(spl::make_shared_ptr(frame));
286         return frames;
287 }
288
289 }}