2 * Copyright 2013 Sveriges Television AB http://casparcg.com/
4 * This file is part of CasparCG (www.casparcg.com).
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.
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.
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/>.
19 * Author: Robert Nagy, ronag89@gmail.com
22 #include "../../StdAfx.h"
24 #include "audio_filter.h"
26 #include "../../ffmpeg_error.h"
27 #include "../../ffmpeg.h"
29 #include <common/assert.h>
30 #include <common/except.h>
32 #include <boost/algorithm/string.hpp>
33 #include <boost/thread.hpp>
34 #include <boost/format.hpp>
35 #include <boost/rational.hpp>
43 #pragma warning (push)
44 #pragma warning (disable : 4244)
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>
60 namespace caspar { namespace ffmpeg {
62 std::string create_sourcefilter_str(const audio_input_pad& input_pad, std::string name)
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
74 std::string create_filter_list(const std::vector<std::string>& items)
76 return boost::join(items, "|");
79 std::string channel_layout_to_string(int64_t channel_layout)
81 return (boost::format("0x%|1$x|") % channel_layout).str();
84 std::string create_sinkfilter_str(const audio_output_pad& output_pad, std::string name)
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); })
91 % create_filter_list(cpplinq::from(output_pad.sample_fmts)
92 .select(&channel_layout_to_string)
94 % create_filter_list(cpplinq::from(output_pad.sample_rates)
95 .select([](int samplerate) { return boost::lexical_cast<std::string>(samplerate); })
102 struct audio_filter::implementation
104 std::string filtergraph_;
106 std::shared_ptr<AVFilterGraph> audio_graph_;
107 std::vector<AVFilterContext*> audio_graph_inputs_;
108 std::vector<AVFilterContext*> audio_graph_outputs_;
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))
116 if (input_pads.empty())
117 CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("input_pads cannot be empty"));
119 if (output_pads.empty())
120 CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("output_pads cannot be empty"));
123 avfilter_graph_alloc(),
126 avfilter_graph_free(&p);
129 std::vector<std::string> complete_filter_graph;
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++)));
137 if (filtergraph_.empty())
138 complete_filter_graph.push_back("[a:0] anull [aout:0]");
140 complete_filter_graph.push_back(filtergraph_);
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++)));
148 configure_filtergraph(
150 boost::join(complete_filter_graph, ";"),
152 audio_graph_outputs_);
154 if (is_logging_quiet_for_thread())
156 << u16(std::string("\n")
157 + avfilter_graph_dump(
162 << u16(std::string("\n")
163 + avfilter_graph_dump(
168 void configure_filtergraph(
169 AVFilterGraph& graph,
170 const std::string& filtergraph,
171 std::vector<AVFilterContext*>& source_contexts,
172 std::vector<AVFilterContext*>& sink_contexts)
174 AVFilterInOut* outputs = nullptr;
175 AVFilterInOut* inputs = nullptr;
177 FF(avfilter_graph_parse2(
183 // Workaround because outputs and inputs are not filled in for some reason
184 for (unsigned i = 0; i < graph.nb_filters; ++i)
186 auto filter = graph.filters[i];
188 if (std::string(filter->filter->name) == "abuffer")
189 source_contexts.push_back(filter);
191 if (std::string(filter->filter->name) == "abuffersink")
192 sink_contexts.push_back(filter);
195 for (AVFilterInOut* iter = inputs; iter; iter = iter->next)
196 source_contexts.push_back(iter->filter_ctx);
198 for (AVFilterInOut* iter = outputs; iter; iter = iter->next)
199 sink_contexts.push_back(iter->filter_ctx);
201 FF(avfilter_graph_config(
206 void push(int input_pad_id, const std::shared_ptr<AVFrame>& src_av_frame)
208 FF(av_buffersrc_add_frame(
209 audio_graph_inputs_.at(input_pad_id),
210 src_av_frame.get()));
213 std::shared_ptr<AVFrame> poll(int output_pad_id)
215 std::shared_ptr<AVFrame> filt_frame(
222 const auto ret = av_buffersink_get_frame(
223 audio_graph_outputs_.at(output_pad_id),
226 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
235 audio_filter::audio_filter(
236 std::vector<audio_input_pad> input_pads,
237 std::vector<audio_output_pad> output_pads,
238 const std::string& filtergraph)
239 : impl_(new implementation(std::move(input_pads), std::move(output_pads), filtergraph))
242 audio_filter::audio_filter(audio_filter&& other) : impl_(std::move(other.impl_)){}
243 audio_filter& audio_filter::operator=(audio_filter&& other){impl_ = std::move(other.impl_); return *this;}
244 void audio_filter::push(int input_pad_id, const std::shared_ptr<AVFrame>& frame){impl_->push(input_pad_id, frame);}
245 std::shared_ptr<AVFrame> audio_filter::poll(int output_pad_id){return impl_->poll(output_pad_id);}
246 std::wstring audio_filter::filter_str() const{return u16(impl_->filtergraph_);}
247 std::vector<spl::shared_ptr<AVFrame>> audio_filter::poll_all(int output_pad_id)
249 std::vector<spl::shared_ptr<AVFrame>> frames;
250 for(auto frame = poll(output_pad_id); frame; frame = poll(output_pad_id))
251 frames.push_back(spl::make_shared_ptr(frame));