]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
[ffmpeg] Remove usage of deprecated API usage to easier support a newer version of...
[casparcg] / modules / ffmpeg / producer / filter / 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 "filter.h"
25
26 #include "../../ffmpeg_error.h"
27 #include "../../ffmpeg.h"
28 #include "../util/util.h"
29
30 #include <common/assert.h>
31 #include <common/except.h>
32
33 #include <boost/algorithm/string.hpp>
34 #include <boost/thread.hpp>
35 #include <boost/format.hpp>
36 #include <boost/rational.hpp>
37
38 #include <cstdio>
39 #include <sstream>
40 #include <string>
41 #include <queue>
42
43 #if defined(_MSC_VER)
44 #pragma warning (push)
45 #pragma warning (disable : 4244)
46 #endif
47 extern "C"
48 {
49         #include <libavutil/avutil.h>
50         #include <libavutil/imgutils.h>
51         #include <libavutil/opt.h>
52         #include <libavfilter/avfilter.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 struct filter::implementation
62 {
63         std::string                                                             filtergraph_;
64
65         std::shared_ptr<AVFilterGraph>                  video_graph_;
66     AVFilterContext*                                            video_graph_in_;
67     AVFilterContext*                                            video_graph_out_;
68
69         std::queue<std::shared_ptr<AVFrame>>    fast_path_;
70
71         implementation(
72                         int in_width,
73                         int in_height,
74                         boost::rational<int> in_time_base,
75                         boost::rational<int> in_frame_rate,
76                         boost::rational<int> in_sample_aspect_ratio,
77                         AVPixelFormat in_pix_fmt,
78                         std::vector<AVPixelFormat> out_pix_fmts,
79                         const std::string& filtergraph,
80                         bool multithreaded)
81                 : filtergraph_(boost::to_lower_copy(filtergraph))
82         {
83                 if(out_pix_fmts.empty())
84                 {
85                         out_pix_fmts = {
86                                 AV_PIX_FMT_YUVA420P,
87                                 AV_PIX_FMT_YUV444P,
88                                 AV_PIX_FMT_YUV422P,
89                                 AV_PIX_FMT_YUV420P,
90                                 AV_PIX_FMT_YUV411P,
91                                 AV_PIX_FMT_BGRA,
92                                 AV_PIX_FMT_ARGB,
93                                 AV_PIX_FMT_RGBA,
94                                 AV_PIX_FMT_ABGR,
95                                 AV_PIX_FMT_GRAY8
96                         };
97                 }
98
99                 out_pix_fmts.push_back(AV_PIX_FMT_NONE);
100
101                 video_graph_.reset(
102                         avfilter_graph_alloc(),
103                         [](AVFilterGraph* p)
104                         {
105                                 avfilter_graph_free(&p);
106                         });
107
108                 if (multithreaded)
109                 {
110                         video_graph_->nb_threads        = 0;
111                         video_graph_->thread_type       = AVFILTER_THREAD_SLICE;
112                 }
113                 else
114                 {
115                         video_graph_->nb_threads        = 1;
116                 }
117
118                 const auto vsrc_options = (boost::format("video_size=%1%x%2%:pix_fmt=%3%:time_base=%4%/%5%:pixel_aspect=%6%/%7%:frame_rate=%8%/%9%")
119                         % in_width % in_height
120                         % in_pix_fmt
121                         % in_time_base.numerator() % in_time_base.denominator()
122                         % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator()
123                         % in_frame_rate.numerator() % in_frame_rate.denominator()).str();
124
125                 AVFilterContext* filt_vsrc = nullptr;
126                 FF(avfilter_graph_create_filter(
127                         &filt_vsrc,
128                         avfilter_get_by_name("buffer"),
129                         "filter_buffer",
130                         vsrc_options.c_str(),
131                         nullptr,
132                         video_graph_.get()));
133
134                 AVFilterContext* filt_vsink = nullptr;
135                 FF(avfilter_graph_create_filter(
136                         &filt_vsink,
137                         avfilter_get_by_name("buffersink"),
138                         "filter_buffersink",
139                         nullptr,
140                         nullptr,
141                         video_graph_.get()));
142
143 #pragma warning (push)
144 #pragma warning (disable : 4245)
145
146                 FF(av_opt_set_int_list(
147                         filt_vsink,
148                         "pix_fmts",
149                         out_pix_fmts.data(),
150                         -1,
151                         AV_OPT_SEARCH_CHILDREN));
152
153 #pragma warning (pop)
154
155                 configure_filtergraph(
156                         *video_graph_,
157                         filtergraph_,
158                         *filt_vsrc,
159                         *filt_vsink);
160
161                 video_graph_in_  = filt_vsrc;
162                 video_graph_out_ = filt_vsink;
163
164                 if (is_logging_quiet_for_thread())
165                         CASPAR_LOG(trace)
166                                 <<      u16(std::string("\n")
167                                         + avfilter_graph_dump(
168                                                         video_graph_.get(),
169                                                         nullptr));
170                 else
171                         CASPAR_LOG(debug)
172                                 << u16(std::string("\n")
173                                         + avfilter_graph_dump(
174                                                         video_graph_.get(),
175                                                         nullptr));
176         }
177
178         void configure_filtergraph(
179                 AVFilterGraph& graph,
180                 const std::string& filtergraph,
181                 AVFilterContext& source_ctx,
182                 AVFilterContext& sink_ctx)
183         {
184                 if (!filtergraph.empty())
185                 {
186                         auto outputs = avfilter_inout_alloc();
187                         auto 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(&graph, nullptr));
218         }
219
220         bool fast_path() const
221         {
222                 return filtergraph_.empty();
223         }
224
225         void push(const std::shared_ptr<AVFrame>& src_av_frame)
226         {
227                 if (fast_path())
228                         fast_path_.push(src_av_frame);
229                 else
230                         FF(av_buffersrc_add_frame(
231                                 video_graph_in_,
232                                 src_av_frame.get()));
233         }
234
235         std::shared_ptr<AVFrame> poll()
236         {
237                 if (fast_path())
238                 {
239                         if (fast_path_.empty())
240                                 return nullptr;
241
242                         auto result = fast_path_.front();
243                         fast_path_.pop();
244                         return result;
245                 }
246
247                 auto filt_frame = create_frame();
248
249                 const auto ret = av_buffersink_get_frame(
250                         video_graph_out_,
251                         filt_frame.get());
252
253                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
254                         return nullptr;
255
256                 FF_RET(ret, "poll");
257
258                 return filt_frame;
259         }
260 };
261
262 filter::filter(
263                 int in_width,
264                 int in_height,
265                 boost::rational<int> in_time_base,
266                 boost::rational<int> in_frame_rate,
267                 boost::rational<int> in_sample_aspect_ratio,
268                 AVPixelFormat in_pix_fmt,
269                 std::vector<AVPixelFormat> out_pix_fmts,
270                 const std::string& filtergraph,
271                 bool multithreaded)
272         : impl_(new implementation(
273                         in_width,
274                         in_height,
275                         in_time_base,
276                         in_frame_rate,
277                         in_sample_aspect_ratio,
278                         in_pix_fmt,
279                         out_pix_fmts,
280                         filtergraph,
281                         multithreaded)){}
282 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}
283 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}
284 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
285 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}
286 std::wstring filter::filter_str() const{return u16(impl_->filtergraph_);}
287 std::vector<spl::shared_ptr<AVFrame>> filter::poll_all()
288 {
289         std::vector<spl::shared_ptr<AVFrame>> frames;
290         for(auto frame = poll(); frame; frame = poll())
291                 frames.push_back(spl::make_shared_ptr(frame));
292         return frames;
293 }
294 }}