]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/audio_filter.cpp
* Ported thread local disabling of ffmpeg producer logging from 2.0 and move some...
[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_disabled_for_thread())
160                         CASPAR_LOG(info)
161                                 <<      u16(std::string("\n") 
162                                         + avfilter_graph_dump(
163                                                         audio_graph_.get(), 
164                                                         nullptr));
165         }
166         
167         void configure_filtergraph(
168                         AVFilterGraph& graph,
169                         const std::string& filtergraph,
170                         AVFilterContext& source_ctx,
171                         AVFilterContext& sink_ctx)
172         {
173                 AVFilterInOut* outputs = nullptr;
174                 AVFilterInOut* inputs = nullptr;
175
176                 try
177                 {
178                         if(!filtergraph.empty()) 
179                         {
180                                 outputs = avfilter_inout_alloc();
181                                 inputs  = avfilter_inout_alloc();
182
183                                 CASPAR_VERIFY(outputs && inputs);
184
185                                 outputs->name       = av_strdup("in");
186                                 outputs->filter_ctx = &source_ctx;
187                                 outputs->pad_idx    = 0;
188                                 outputs->next       = nullptr;
189
190                                 inputs->name        = av_strdup("out");
191                                 inputs->filter_ctx  = &sink_ctx;
192                                 inputs->pad_idx     = 0;
193                                 inputs->next        = nullptr;
194
195                                 FF(avfilter_graph_parse(
196                                         &graph, 
197                                         filtergraph.c_str(), 
198                                         inputs,
199                                         outputs,
200                                         nullptr));
201                         } 
202                         else 
203                         {
204                                 FF(avfilter_link(
205                                         &source_ctx, 
206                                         0, 
207                                         &sink_ctx, 
208                                         0));
209                         }
210
211                         FF(avfilter_graph_config(
212                                 &graph, 
213                                 nullptr));
214                 }
215                 catch(...)
216                 {
217                         //avfilter_inout_free(&outputs);
218                         //avfilter_inout_free(&inputs);
219                         throw;
220                 }
221         }
222
223         void push(const std::shared_ptr<AVFrame>& src_av_frame)
224         {               
225                 FF(av_buffersrc_add_frame(
226                         audio_graph_in_, 
227                         src_av_frame.get()));
228         }
229
230         std::shared_ptr<AVFrame> poll()
231         {
232                 std::shared_ptr<AVFrame> filt_frame(
233                         av_frame_alloc(), 
234                         [](AVFrame* p)
235                         {
236                                 av_frame_free(&p);
237                         });
238                 
239                 const auto ret = av_buffersink_get_frame(
240                         audio_graph_out_, 
241                         filt_frame.get());
242                                 
243                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
244                         return nullptr;
245                                         
246                 FF_RET(ret, "poll");
247
248                 return filt_frame;
249         }
250 };
251
252 audio_filter::audio_filter(
253                 boost::rational<int> in_time_base,
254                 int in_sample_rate,
255                 AVSampleFormat in_sample_fmt,
256                 std::int64_t in_audio_channel_layout,
257                 std::vector<int> out_sample_rates,
258                 std::vector<AVSampleFormat> out_sample_fmts,
259                 std::vector<std::int64_t> out_audio_channel_layouts,
260                 const std::string& filtergraph)
261         : impl_(new implementation(
262                 in_time_base,
263                 in_sample_rate,
264                 in_sample_fmt,
265                 in_audio_channel_layout,
266                 std::move(out_sample_rates),
267                 std::move(out_sample_fmts),
268                 std::move(out_audio_channel_layouts),
269                 filtergraph)){}
270 audio_filter::audio_filter(audio_filter&& other) : impl_(std::move(other.impl_)){}
271 audio_filter& audio_filter::operator=(audio_filter&& other){impl_ = std::move(other.impl_); return *this;}
272 void audio_filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
273 std::shared_ptr<AVFrame> audio_filter::poll(){return impl_->poll();}
274 std::wstring audio_filter::filter_str() const{return u16(impl_->filtergraph_);}
275 std::vector<spl::shared_ptr<AVFrame>> audio_filter::poll_all()
276 {       
277         std::vector<spl::shared_ptr<AVFrame>> frames;
278         for(auto frame = poll(); frame; frame = poll())
279                 frames.push_back(spl::make_shared_ptr(frame));
280         return frames;
281 }
282
283 }}