blob: 1cba04d902fbc0146d366b9082326c79a0e3e604 [file] [log] [blame]
agsantosc9181b42020-11-26 12:03:04 -05001/*
Sébastien Blincb783e32021-02-12 11:34:10 -05002 * Copyright (C) 2020-2021 Savoir-faire Linux Inc.
agsantosc9181b42020-11-26 12:03:04 -05003 *
4 * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
5 *
6 * This program 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 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "frameFilter.h"
22
23extern "C" {
24#include <libavfilter/buffersink.h>
25#include <libavfilter/buffersrc.h>
26}
27
28#include <algorithm>
29#include <functional>
30#include <memory>
31#include <sstream>
32#include <thread>
33
34#include "pluglog.h"
35
36namespace jami {
37
38FrameFilter::FrameFilter() {}
39
40FrameFilter::~FrameFilter()
41{
42 clean();
43}
44
45int
46FrameFilter::initialize(const std::string& filterDesc, std::vector<MediaStream> msps)
47{
48 int ret = 0;
49 desc_ = filterDesc;
50 graph_ = avfilter_graph_alloc();
51
52 if (!graph_)
53 return fail("Failed to allocate filter graph", AVERROR(ENOMEM));
54
55 graph_->nb_threads = std::max(1u, std::min(8u, std::thread::hardware_concurrency() / 2));
56
57 AVFilterInOut* in;
58 AVFilterInOut* out;
59 if ((ret = avfilter_graph_parse2(graph_, desc_.c_str(), &in, &out)) < 0)
60 return fail("Failed to parse filter graph", ret);
61
62 using AVFilterInOutPtr = std::unique_ptr<AVFilterInOut, std::function<void(AVFilterInOut*)>>;
63 AVFilterInOutPtr outputs(out, [](AVFilterInOut* f) { avfilter_inout_free(&f); });
64 AVFilterInOutPtr inputs(in, [](AVFilterInOut* f) { avfilter_inout_free(&f); });
65
66 if (outputs && outputs->next)
67 return fail("Filters with multiple outputs are not supported", AVERROR(ENOTSUP));
68
69 if ((ret = initOutputFilter(outputs.get())) < 0)
70 return fail("Failed to create output for filter graph", ret);
71 // make sure inputs linked list is the same size as msps
72 size_t count = 0;
73 AVFilterInOut* dummyInput = inputs.get();
74 while (dummyInput && ++count) // increment count before evaluating its value
75 dummyInput = dummyInput->next;
76 if (count != msps.size())
77 return fail(
78 "Size mismatch between number of inputs in filter graph and input parameter array",
79 AVERROR(EINVAL));
80 for (AVFilterInOut* current = inputs.get(); current; current = current->next) {
81 if (!current->name)
82 return fail("Filters require non empty names", AVERROR(EINVAL));
83 std::string name = current->name;
84 const auto& it = std::find_if(msps.begin(), msps.end(), [name](const MediaStream& msp) {
85 return msp.name == name;
86 });
87 if (it != msps.end()) {
88 if ((ret = initInputFilter(current, *it)) < 0) {
89 std::string msg = "Failed to initialize input: " + name;
90 return fail(msg, ret);
91 }
92 } else {
93 std::string msg = "Failed to find matching parameters for: " + name;
94 return fail(msg, ret);
95 }
96 }
97
98 if ((ret = avfilter_graph_config(graph_, nullptr)) < 0)
99 return fail("Failed to configure filter graph", ret);
100
101 initialized_ = true;
102 return 0;
103}
104
105MediaStream
106FrameFilter::getInputParams(const std::string& inputName) const
107{
108 for (auto ms : inputParams_)
109 if (ms.name == inputName)
110 return ms;
111 return {};
112}
113
114MediaStream
115FrameFilter::getOutputParams() const
116{
117 MediaStream output;
118 if (!output_ || !initialized_) {
119 fail("Filter not initialized", -1);
120 return output;
121 }
122
123 switch (av_buffersink_get_type(output_)) {
124 case AVMEDIA_TYPE_VIDEO:
125 output.name = "videoOutput";
126 output.format = av_buffersink_get_format(output_);
127 output.isVideo = true;
128 output.timeBase = av_buffersink_get_time_base(output_);
129 output.width = av_buffersink_get_w(output_);
130 output.height = av_buffersink_get_h(output_);
131 output.bitrate = 0;
132 output.frameRate = av_buffersink_get_frame_rate(output_);
133 break;
134 case AVMEDIA_TYPE_AUDIO:
135 output.name = "audioOutput";
136 output.format = av_buffersink_get_format(output_);
137 output.isVideo = false;
138 output.timeBase = av_buffersink_get_time_base(output_);
139 output.sampleRate = av_buffersink_get_sample_rate(output_);
140 output.nbChannels = av_buffersink_get_channels(output_);
141 break;
142 default:
143 output.format = -1;
144 break;
145 }
146 return output;
147}
148
149int
150FrameFilter::feedEOF(const std::string& inputName)
151{
152 int ret = 0;
153 if (!initialized_)
154 return fail("Filter not initialized", -1);
155
156 for (size_t i = 0; i < inputs_.size(); ++i) {
157 auto& ms = inputParams_[i];
158 if (ms.name != inputName)
159 continue;
160
161 if ((ret = av_buffersrc_add_frame_flags(inputs_[i], nullptr, AV_BUFFERSRC_FLAG_PUSH)) < 0)
162 return fail("Could not pass frame to filters", ret);
163 else
164 return 0;
165 }
166
167 std::stringstream ss;
168 ss << "Specified filter (" << inputName << ") not found";
169 return fail(ss.str(), AVERROR(EINVAL));
170}
171
172int
173FrameFilter::feedInput(AVFrame* frame, const std::string& inputName)
174{
175 int ret = 0;
176 if (!initialized_)
177 return fail("Filter not initialized", -1);
178
179 if (!frame)
180 return 0;
181
182 for (size_t i = 0; i < inputs_.size(); ++i) {
183 auto& ms = inputParams_[i];
184 if (ms.name != inputName)
185 continue;
186
187 if (ms.format != frame->format
188 || (ms.isVideo && (ms.width != frame->width || ms.height != frame->height))
189 || (!ms.isVideo
190 && (ms.sampleRate != frame->sample_rate || ms.nbChannels != frame->channels))) {
191 ms.update(frame);
192 if ((ret = reinitialize()) < 0)
193 return fail("Failed to reinitialize filter with new input parameters", ret);
194 }
195
196 if ((ret = av_buffersrc_add_frame_flags(inputs_[i], frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
197 return fail("Could not pass frame to filters", ret);
198 else
199 return 0;
200 }
201
202 std::stringstream ss;
203 ss << "Specified filter (" << inputName << ") not found";
204 return fail(ss.str(), AVERROR(EINVAL));
205}
206
207AVFrame*
208FrameFilter::readOutput()
209{
210 if (!initialized_) {
211 fail("Not properly initialized", -1);
212 return nullptr;
213 }
214
215 AVFrame* frame = av_frame_alloc();
216
217 auto type = av_buffersink_get_type(output_);
218 if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
219 return nullptr;
220 }
221 auto err = av_buffersink_get_frame(output_, frame);
222 if (err >= 0) {
223 return frame;
224 } else if (err == AVERROR(EAGAIN)) {
225 fail("no data. ", err);
226 } else if (err == AVERROR_EOF) {
227 fail("eof. ", err);
228 } else {
229 fail("Error occurred while pulling from filter graph", err);
230 }
231 return nullptr;
232}
233
234int
235FrameFilter::initOutputFilter(AVFilterInOut* out)
236{
237 int ret = 0;
238 const AVFilter* buffersink;
agsantosb3c90842020-12-10 14:19:41 -0500239 AVFilterContext* buffersinkCtx{};
agsantosc9181b42020-11-26 12:03:04 -0500240 AVMediaType mediaType = avfilter_pad_get_type(out->filter_ctx->input_pads, out->pad_idx);
241
242 if (mediaType == AVMEDIA_TYPE_VIDEO)
243 buffersink = avfilter_get_by_name("buffersink");
244 else
245 buffersink = avfilter_get_by_name("abuffersink");
246
247 if ((ret
248 = avfilter_graph_create_filter(&buffersinkCtx, buffersink, "out", nullptr, nullptr, graph_))
249 < 0) {
250 avfilter_free(buffersinkCtx);
251 return fail("Failed to create buffer sink", ret);
252 }
253
254 if ((ret = avfilter_link(out->filter_ctx, out->pad_idx, buffersinkCtx, 0)) < 0) {
255 avfilter_free(buffersinkCtx);
256 return fail("Could not link buffer sink to graph", ret);
257 }
258
259 output_ = buffersinkCtx;
260 return ret;
261}
262
263int
264FrameFilter::initInputFilter(AVFilterInOut* in, MediaStream msp)
265{
266 int ret = 0;
267 AVBufferSrcParameters* params = av_buffersrc_parameters_alloc();
268 if (!params)
269 return -1;
270
271 const AVFilter* buffersrc;
272 AVMediaType mediaType = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
273 params->format = msp.format;
274 params->time_base = msp.timeBase;
275 if (mediaType == AVMEDIA_TYPE_VIDEO) {
276 params->width = msp.width;
277 params->height = msp.height;
278 params->frame_rate = msp.frameRate;
279 buffersrc = avfilter_get_by_name("buffer");
280 } else {
281 params->sample_rate = msp.sampleRate;
282 params->channel_layout = av_get_default_channel_layout(msp.nbChannels);
283 buffersrc = avfilter_get_by_name("abuffer");
284 }
285
agsantosb3c90842020-12-10 14:19:41 -0500286 AVFilterContext* buffersrcCtx{};
agsantosc9181b42020-11-26 12:03:04 -0500287 if (buffersrc) {
288 char name[128];
289 snprintf(name, sizeof(name), "buffersrc_%s_%d", in->name, in->pad_idx);
290 buffersrcCtx = avfilter_graph_alloc_filter(graph_, buffersrc, name);
291 }
292 if (!buffersrcCtx) {
293 av_free(params);
294 return fail("Failed to allocate filter graph input", AVERROR(ENOMEM));
295 }
296 ret = av_buffersrc_parameters_set(buffersrcCtx, params);
297 av_free(params);
298 if (ret < 0)
299 return fail("Failed to set filter graph input parameters", ret);
300
301 if ((ret = avfilter_init_str(buffersrcCtx, nullptr)) < 0)
302 return fail("Failed to initialize buffer source", ret);
303
304 if ((ret = avfilter_link(buffersrcCtx, 0, in->filter_ctx, in->pad_idx)) < 0)
305 return fail("Failed to link buffer source to graph", ret);
306
307 inputs_.push_back(buffersrcCtx);
308 msp.name = in->name;
309 inputParams_.push_back(msp);
310 return ret;
311}
312
313int
314FrameFilter::reinitialize()
315{
316 // keep parameters needed for initialization before clearing filter
317 auto params = std::move(inputParams_);
318 auto desc = std::move(desc_);
319 clean();
320 auto ret = initialize(desc, params);
321 return ret;
322}
323
324int
325FrameFilter::fail(std::string msg, int err) const
326{
327 if (!msg.empty())
328 Plog::log(Plog::LogPriority::INFO, "Frame Filter: ", "("+std::to_string(err)+") "+msg);
329 return err;
330}
331
332void
333FrameFilter::clean()
334{
335 initialized_ = false;
336 avfilter_graph_free(&graph_); // frees inputs_ and output_
337 desc_.clear();
338 inputs_.clear(); // don't point to freed memory
339 output_ = nullptr; // don't point to freed memory
340 inputParams_.clear();
341}
342} // namespace jami