]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
Manually merged #223
[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
28 #include <common/assert.h>
29 #include <common/except.h>
30
31 #include <boost/assign.hpp>
32 #include <boost/range/iterator_range.hpp>
33 #include <boost/range/adaptors.hpp>
34 #include <boost/assign.hpp>
35 #include <boost/algorithm/string.hpp>
36 #include <boost/foreach.hpp>
37 #include <boost/thread.hpp>
38 #include <boost/format.hpp>
39 #include <boost/rational.hpp>
40
41 #include <cstdio>
42 #include <sstream>
43 #include <string>
44
45 #if defined(_MSC_VER)
46 #pragma warning (push)
47 #pragma warning (disable : 4244)
48 #endif
49 extern "C" 
50 {
51         #include <libavutil/avutil.h>
52         #include <libavutil/imgutils.h>
53         #include <libavutil/opt.h>
54         #include <libavfilter/avfilter.h>
55         #include <libavfilter/avcodec.h>
56         #include <libavfilter/avfilter.h>
57         #include <libavfilter/buffersink.h>
58         #include <libavfilter/buffersrc.h>
59 }
60 #if defined(_MSC_VER)
61 #pragma warning (pop)
62 #endif
63
64 namespace caspar { namespace ffmpeg {
65         
66 struct filter::implementation
67 {
68         std::string                                             filtergraph_;
69
70         std::shared_ptr<AVFilterGraph>  video_graph_;   
71     AVFilterContext*                            video_graph_in_;  
72     AVFilterContext*                            video_graph_out_; 
73                 
74         implementation(
75                 int in_width,
76                 int in_height,
77                 boost::rational<int> in_time_base,
78                 boost::rational<int> in_frame_rate,
79                 boost::rational<int> in_sample_aspect_ratio,
80                 AVPixelFormat in_pix_fmt,
81                 std::vector<AVPixelFormat> out_pix_fmts,
82                 const std::string& filtergraph) 
83                 : filtergraph_(boost::to_lower_copy(filtergraph))
84         {
85                 if(out_pix_fmts.empty())
86                 {
87                         out_pix_fmts = boost::assign::list_of
88                                 (AV_PIX_FMT_YUVA420P)
89                                 (AV_PIX_FMT_YUV444P)
90                                 (AV_PIX_FMT_YUV422P)
91                                 (AV_PIX_FMT_YUV420P)
92                                 (AV_PIX_FMT_YUV411P)
93                                 (AV_PIX_FMT_BGRA)
94                                 (AV_PIX_FMT_ARGB)
95                                 (AV_PIX_FMT_RGBA)
96                                 (AV_PIX_FMT_ABGR)
97                                 (AV_PIX_FMT_GRAY8);
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                 video_graph_->nb_threads  = boost::thread::hardware_concurrency();
110                 video_graph_->thread_type = AVFILTER_THREAD_SLICE;
111                                 
112                 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%")
113                         % in_width % in_height
114                         % in_pix_fmt
115                         % in_time_base.numerator() % in_time_base.denominator()
116                         % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator()
117                         % in_frame_rate.numerator() % in_frame_rate.denominator()).str();
118                                         
119                 AVFilterContext* filt_vsrc = nullptr;                   
120                 FF(avfilter_graph_create_filter(
121                         &filt_vsrc,
122                         avfilter_get_by_name("buffer"), 
123                         "filter_buffer",
124                         vsrc_options.c_str(), 
125                         nullptr, 
126                         video_graph_.get()));
127                                 
128                 AVFilterContext* filt_vsink = nullptr;
129                 FF(avfilter_graph_create_filter(
130                         &filt_vsink,
131                         avfilter_get_by_name("buffersink"), 
132                         "filter_buffersink",
133                         nullptr, 
134                         nullptr, 
135                         video_graph_.get()));
136                 
137 #pragma warning (push)
138 #pragma warning (disable : 4245)
139
140                 FF(av_opt_set_int_list(
141                         filt_vsink, 
142                         "pix_fmts", 
143                         out_pix_fmts.data(), 
144                         -1,
145                         AV_OPT_SEARCH_CHILDREN));
146
147 #pragma warning (pop)
148                         
149                 configure_filtergraph(
150                         *video_graph_, 
151                         filtergraph_,
152                         *filt_vsrc,
153                         *filt_vsink);
154
155                 video_graph_in_  = filt_vsrc;
156                 video_graph_out_ = filt_vsink;
157                 
158                 CASPAR_LOG(info)
159                         <<      u16(std::string("\n") 
160                                 + avfilter_graph_dump(
161                                                 video_graph_.get(), 
162                                                 nullptr));
163         }
164         
165         void configure_filtergraph(
166                 AVFilterGraph& graph, 
167                 const std::string& filtergraph, 
168                 AVFilterContext& source_ctx, 
169                 AVFilterContext& sink_ctx)
170         {
171                 AVFilterInOut* outputs = nullptr;
172                 AVFilterInOut* inputs = nullptr;
173
174                 try
175                 {
176                         if(!filtergraph.empty()) 
177                         {
178                                 outputs = avfilter_inout_alloc();
179                                 inputs  = avfilter_inout_alloc();
180
181                                 CASPAR_VERIFY(outputs && inputs);
182
183                                 outputs->name       = av_strdup("in");
184                                 outputs->filter_ctx = &source_ctx;
185                                 outputs->pad_idx    = 0;
186                                 outputs->next       = nullptr;
187
188                                 inputs->name        = av_strdup("out");
189                                 inputs->filter_ctx  = &sink_ctx;
190                                 inputs->pad_idx     = 0;
191                                 inputs->next        = nullptr;
192
193                                 FF(avfilter_graph_parse(
194                                         &graph, 
195                                         filtergraph.c_str(), 
196                                         &inputs, 
197                                         &outputs, 
198                                         nullptr));
199                         } 
200                         else 
201                         {
202                                 FF(avfilter_link(
203                                         &source_ctx, 
204                                         0, 
205                                         &sink_ctx, 
206                                         0));
207                         }
208
209                         FF(avfilter_graph_config(
210                                 &graph, 
211                                 nullptr));
212                 }
213                 catch(...)
214                 {
215                         avfilter_inout_free(&outputs);
216                         avfilter_inout_free(&inputs);
217                         throw;
218                 }
219         }
220
221         void push(const std::shared_ptr<AVFrame>& src_av_frame)
222         {               
223                 FF(av_buffersrc_add_frame(
224                         video_graph_in_, 
225                         src_av_frame.get()));
226         }
227
228         std::shared_ptr<AVFrame> poll()
229         {
230                 std::shared_ptr<AVFrame> filt_frame(
231                         av_frame_alloc(), 
232                         [](AVFrame* p)
233                         {
234                                 av_frame_free(&p);
235                         });
236                 
237                 const auto ret = av_buffersink_get_frame(
238                         video_graph_out_, 
239                         filt_frame.get());
240                                 
241                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
242                         return nullptr;
243                                         
244                 FF_RET(ret, "poll");
245
246                 return filt_frame;
247         }
248 };
249
250 filter::filter(
251                 int in_width,
252                 int in_height,
253                 boost::rational<int> in_time_base,
254                 boost::rational<int> in_frame_rate,
255                 boost::rational<int> in_sample_aspect_ratio,
256                 AVPixelFormat in_pix_fmt,
257                 std::vector<AVPixelFormat> out_pix_fmts,
258                 const std::string& filtergraph) 
259                 : impl_(new implementation(
260                         in_width,
261                         in_height,
262                         in_time_base,
263                         in_frame_rate,
264                         in_sample_aspect_ratio,
265                         in_pix_fmt,
266                         out_pix_fmts,
267                         filtergraph)){}
268 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}
269 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}
270 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
271 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}
272 std::wstring filter::filter_str() const{return u16(impl_->filtergraph_);}
273 std::vector<spl::shared_ptr<AVFrame>> filter::poll_all()
274 {       
275         std::vector<spl::shared_ptr<AVFrame>> frames;
276         for(auto frame = poll(); frame; frame = poll())
277                 frames.push_back(spl::make_shared_ptr(frame));
278         return frames;
279 }
280
281 }}