]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
aa83cf36662584a583fd758a55b3850c03d0a53b
[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/avcodec.h>
54         #include <libavfilter/buffersink.h>
55         #include <libavfilter/buffersrc.h>
56 }
57 #if defined(_MSC_VER)
58 #pragma warning (pop)
59 #endif
60
61 namespace caspar { namespace ffmpeg {
62 struct filter::implementation
63 {
64         std::string                                                             filtergraph_;
65
66         std::shared_ptr<AVFilterGraph>                  video_graph_;
67     AVFilterContext*                                            video_graph_in_;
68     AVFilterContext*                                            video_graph_out_;
69
70         std::queue<std::shared_ptr<AVFrame>>    fast_path_;
71
72         implementation(
73                         int in_width,
74                         int in_height,
75                         boost::rational<int> in_time_base,
76                         boost::rational<int> in_frame_rate,
77                         boost::rational<int> in_sample_aspect_ratio,
78                         AVPixelFormat in_pix_fmt,
79                         std::vector<AVPixelFormat> out_pix_fmts,
80                         const std::string& filtergraph,
81                         bool multithreaded)
82                 : filtergraph_(boost::to_lower_copy(filtergraph))
83         {
84                 if(out_pix_fmts.empty())
85                 {
86                         out_pix_fmts = {
87                                 AV_PIX_FMT_YUVA420P,
88                                 AV_PIX_FMT_YUV444P,
89                                 AV_PIX_FMT_YUV422P,
90                                 AV_PIX_FMT_YUV420P,
91                                 AV_PIX_FMT_YUV411P,
92                                 AV_PIX_FMT_BGRA,
93                                 AV_PIX_FMT_ARGB,
94                                 AV_PIX_FMT_RGBA,
95                                 AV_PIX_FMT_ABGR,
96                                 AV_PIX_FMT_GRAY8
97                         };
98                 }
99
100                 out_pix_fmts.push_back(AV_PIX_FMT_NONE);
101
102                 video_graph_.reset(
103                         avfilter_graph_alloc(),
104                         [](AVFilterGraph* p)
105                         {
106                                 avfilter_graph_free(&p);
107                         });
108
109                 if (multithreaded)
110                 {
111                         video_graph_->nb_threads        = 0;
112                         video_graph_->thread_type       = AVFILTER_THREAD_SLICE;
113                 }
114                 else
115                 {
116                         video_graph_->nb_threads        = 1;
117                 }
118
119                 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%")
120                         % in_width % in_height
121                         % in_pix_fmt
122                         % in_time_base.numerator() % in_time_base.denominator()
123                         % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator()
124                         % in_frame_rate.numerator() % in_frame_rate.denominator()).str();
125
126                 AVFilterContext* filt_vsrc = nullptr;
127                 FF(avfilter_graph_create_filter(
128                         &filt_vsrc,
129                         avfilter_get_by_name("buffer"),
130                         "filter_buffer",
131                         vsrc_options.c_str(),
132                         nullptr,
133                         video_graph_.get()));
134
135                 AVFilterContext* filt_vsink = nullptr;
136                 FF(avfilter_graph_create_filter(
137                         &filt_vsink,
138                         avfilter_get_by_name("buffersink"),
139                         "filter_buffersink",
140                         nullptr,
141                         nullptr,
142                         video_graph_.get()));
143
144 #pragma warning (push)
145 #pragma warning (disable : 4245)
146
147                 FF(av_opt_set_int_list(
148                         filt_vsink,
149                         "pix_fmts",
150                         out_pix_fmts.data(),
151                         -1,
152                         AV_OPT_SEARCH_CHILDREN));
153
154 #pragma warning (pop)
155
156                 configure_filtergraph(
157                         *video_graph_,
158                         filtergraph_,
159                         *filt_vsrc,
160                         *filt_vsink);
161
162                 video_graph_in_  = filt_vsrc;
163                 video_graph_out_ = filt_vsink;
164
165                 if (is_logging_quiet_for_thread())
166                         CASPAR_LOG(trace)
167                                 <<      u16(std::string("\n")
168                                         + avfilter_graph_dump(
169                                                         video_graph_.get(),
170                                                         nullptr));
171                 else
172                         CASPAR_LOG(debug)
173                                 << u16(std::string("\n")
174                                         + avfilter_graph_dump(
175                                                         video_graph_.get(),
176                                                         nullptr));
177         }
178
179         void configure_filtergraph(
180                 AVFilterGraph& graph,
181                 const std::string& filtergraph,
182                 AVFilterContext& source_ctx,
183                 AVFilterContext& sink_ctx)
184         {
185                 if (!filtergraph.empty())
186                 {
187                         auto outputs = avfilter_inout_alloc();
188                         auto inputs  = avfilter_inout_alloc();
189
190                         CASPAR_VERIFY(outputs && inputs);
191
192                         outputs->name       = av_strdup("in");
193                         outputs->filter_ctx = &source_ctx;
194                         outputs->pad_idx    = 0;
195                         outputs->next       = nullptr;
196
197                         inputs->name        = av_strdup("out");
198                         inputs->filter_ctx  = &sink_ctx;
199                         inputs->pad_idx     = 0;
200                         inputs->next        = nullptr;
201
202                         FF(avfilter_graph_parse(
203                                         &graph,
204                                         filtergraph.c_str(),
205                                         inputs,
206                                         outputs,
207                                         nullptr));
208                 }
209                 else
210                 {
211                         FF(avfilter_link(
212                                         &source_ctx,
213                                         0,
214                                         &sink_ctx,
215                                         0));
216                 }
217
218                 FF(avfilter_graph_config(&graph, nullptr));
219         }
220
221         bool fast_path() const
222         {
223                 return filtergraph_.empty();
224         }
225
226         void push(const std::shared_ptr<AVFrame>& src_av_frame)
227         {
228                 if (fast_path())
229                         fast_path_.push(src_av_frame);
230                 else
231                         FF(av_buffersrc_add_frame(
232                                 video_graph_in_,
233                                 src_av_frame.get()));
234         }
235
236         std::shared_ptr<AVFrame> poll()
237         {
238                 if (fast_path())
239                 {
240                         if (fast_path_.empty())
241                                 return nullptr;
242
243                         auto result = fast_path_.front();
244                         fast_path_.pop();
245                         return result;
246                 }
247
248                 auto filt_frame = create_frame();
249
250                 const auto ret = av_buffersink_get_frame(
251                         video_graph_out_,
252                         filt_frame.get());
253
254                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
255                         return nullptr;
256
257                 FF_RET(ret, "poll");
258
259                 return filt_frame;
260         }
261 };
262
263 filter::filter(
264                 int in_width,
265                 int in_height,
266                 boost::rational<int> in_time_base,
267                 boost::rational<int> in_frame_rate,
268                 boost::rational<int> in_sample_aspect_ratio,
269                 AVPixelFormat in_pix_fmt,
270                 std::vector<AVPixelFormat> out_pix_fmts,
271                 const std::string& filtergraph,
272                 bool multithreaded)
273         : impl_(new implementation(
274                         in_width,
275                         in_height,
276                         in_time_base,
277                         in_frame_rate,
278                         in_sample_aspect_ratio,
279                         in_pix_fmt,
280                         out_pix_fmts,
281                         filtergraph,
282                         multithreaded)){}
283 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}
284 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}
285 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
286 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}
287 std::wstring filter::filter_str() const{return u16(impl_->filtergraph_);}
288 std::vector<spl::shared_ptr<AVFrame>> filter::poll_all()
289 {
290         std::vector<spl::shared_ptr<AVFrame>> frames;
291         for(auto frame = poll(); frame; frame = poll())
292                 frames.push_back(spl::make_shared_ptr(frame));
293         return frames;
294 }
295 }}