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