2 * Copyright 2013 Sveriges Television AB http://casparcg.com/
\r
4 * This file is part of CasparCG (www.casparcg.com).
\r
6 * CasparCG is free software: you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation, either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * CasparCG is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
\r
19 * Author: Robert Nagy, ronag89@gmail.com
\r
22 #include "../../stdafx.h"
\r
26 #include "parallel_yadif.h"
\r
28 #include "../../ffmpeg_error.h"
\r
30 #include <common/exception/exceptions.h>
\r
32 #include <boost/assign.hpp>
\r
33 #include <boost/range/iterator_range.hpp>
\r
34 #include <boost/range/adaptors.hpp>
\r
35 #include <boost/assign.hpp>
\r
36 #include <boost/algorithm/string.hpp>
\r
37 #include <boost/foreach.hpp>
\r
38 #include <boost/thread.hpp>
\r
39 #include <boost/format.hpp>
\r
40 #include <boost/rational.hpp>
\r
46 #if defined(_MSC_VER)
\r
47 #pragma warning (push)
\r
48 #pragma warning (disable : 4244)
\r
52 #include <libavutil/avutil.h>
\r
53 #include <libavutil/imgutils.h>
\r
54 #include <libavutil/opt.h>
\r
55 #include <libavfilter/avfilter.h>
\r
56 #include <libavfilter/avcodec.h>
\r
57 #include <libavfilter/avfilter.h>
\r
58 #include <libavfilter/buffersink.h>
\r
59 #include <libavfilter/buffersrc.h>
\r
61 #if defined(_MSC_VER)
\r
62 #pragma warning (pop)
\r
65 namespace caspar { namespace ffmpeg {
\r
67 //static int query_formats_444(AVFilterContext *ctx)
\r
69 // static const int pix_fmts[] = {PIX_FMT_YUV444P, PIX_FMT_NONE};
\r
70 // avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
\r
74 //static int query_formats_422(AVFilterContext *ctx)
\r
76 // static const int pix_fmts[] = {PIX_FMT_YUV422P, PIX_FMT_NONE};
\r
77 // avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
\r
81 //static int query_formats_420(AVFilterContext *ctx)
\r
83 // static const int pix_fmts[] = {PIX_FMT_YUV420P, PIX_FMT_NONE};
\r
84 // avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
\r
88 //static int query_formats_420a(AVFilterContext *ctx)
\r
90 // static const int pix_fmts[] = {PIX_FMT_YUVA420P, PIX_FMT_NONE};
\r
91 // avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
\r
95 //static int query_formats_411(AVFilterContext *ctx)
\r
97 // static const int pix_fmts[] = {PIX_FMT_YUV411P, PIX_FMT_NONE};
\r
98 // avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
\r
102 //static int query_formats_410(AVFilterContext *ctx)
\r
104 // static const int pix_fmts[] = {PIX_FMT_YUV410P, PIX_FMT_NONE};
\r
105 // avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
\r
109 struct filter::implementation
\r
111 std::wstring filters_;
\r
112 //std::shared_ptr<AVFilterGraph> graph_;
\r
113 //AVFilterContext* buffersink_ctx_;
\r
114 //AVFilterContext* buffersrc_ctx_;
\r
115 //std::shared_ptr<void> parallel_yadif_ctx_;
\r
116 std::vector<PixelFormat> pix_fmts_;
\r
117 //std::queue<safe_ptr<AVFrame>> bypass_;
\r
119 std::shared_ptr<AVFilterGraph> video_graph_;
\r
120 AVFilterContext* video_graph_in_;
\r
121 AVFilterContext* video_graph_out_;
\r
126 boost::rational<int> in_time_base,
\r
127 boost::rational<int> in_frame_rate,
\r
128 boost::rational<int> in_sample_aspect_ratio,
\r
129 AVPixelFormat in_pix_fmt,
\r
130 std::vector<AVPixelFormat> out_pix_fmts,
\r
131 const std::string& filtergraph)
\r
133 if(out_pix_fmts.empty())
\r
135 pix_fmts_ = boost::assign::list_of
\r
136 (AV_PIX_FMT_YUVA420P)
\r
137 (AV_PIX_FMT_YUV444P)
\r
138 (AV_PIX_FMT_YUV422P)
\r
139 (AV_PIX_FMT_YUV420P)
\r
140 (AV_PIX_FMT_YUV411P)
\r
145 (AV_PIX_FMT_GRAY8);
\r
148 out_pix_fmts.push_back(AV_PIX_FMT_NONE);
\r
150 video_graph_.reset(
\r
151 avfilter_graph_alloc(),
\r
152 [](AVFilterGraph* p)
\r
154 avfilter_graph_free(&p);
\r
157 video_graph_->nb_threads = boost::thread::hardware_concurrency();
\r
158 video_graph_->thread_type = AVFILTER_THREAD_SLICE;
\r
160 //const auto sample_aspect_ratio =
\r
161 // boost::rational<int>(
\r
162 // in_video_format_.square_width,
\r
163 // in_video_format_.square_height) /
\r
164 // boost::rational<int>(
\r
165 // in_video_format_.width,
\r
166 // in_video_format_.height);
\r
168 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%")
\r
169 % in_width % in_height
\r
171 % in_time_base.numerator() % in_time_base.denominator()
\r
172 % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator()
\r
173 % in_frame_rate.numerator() % in_frame_rate.denominator()).str();
\r
175 AVFilterContext* filt_vsrc = nullptr;
\r
176 FF(avfilter_graph_create_filter(
\r
178 avfilter_get_by_name("buffer"),
\r
179 "ffmpeg_consumer_buffer",
\r
180 vsrc_options.c_str(),
\r
182 video_graph_.get()));
\r
184 AVFilterContext* filt_vsink = nullptr;
\r
185 FF(avfilter_graph_create_filter(
\r
187 avfilter_get_by_name("buffersink"),
\r
188 "ffmpeg_consumer_buffersink",
\r
191 video_graph_.get()));
\r
193 #pragma warning (push)
\r
194 #pragma warning (disable : 4245)
\r
196 FF(av_opt_set_int_list(
\r
199 out_pix_fmts.data(),
\r
201 AV_OPT_SEARCH_CHILDREN));
\r
203 #pragma warning (pop)
\r
205 configure_filtergraph(
\r
211 video_graph_in_ = filt_vsrc;
\r
212 video_graph_out_ = filt_vsink;
\r
215 << widen(std::string("\n")
\r
216 + avfilter_graph_dump(
\r
217 video_graph_.get(),
\r
221 void configure_filtergraph(
\r
222 AVFilterGraph& graph,
\r
223 const std::string& filtergraph,
\r
224 AVFilterContext& source_ctx,
\r
225 AVFilterContext& sink_ctx)
\r
227 AVFilterInOut* outputs = nullptr;
\r
228 AVFilterInOut* inputs = nullptr;
\r
232 if(!filtergraph.empty())
\r
234 outputs = avfilter_inout_alloc();
\r
235 inputs = avfilter_inout_alloc();
\r
237 CASPAR_VERIFY(outputs && inputs);
\r
239 outputs->name = av_strdup("in");
\r
240 outputs->filter_ctx = &source_ctx;
\r
241 outputs->pad_idx = 0;
\r
242 outputs->next = nullptr;
\r
244 inputs->name = av_strdup("out");
\r
245 inputs->filter_ctx = &sink_ctx;
\r
246 inputs->pad_idx = 0;
\r
247 inputs->next = nullptr;
\r
249 FF(avfilter_graph_parse(
\r
251 filtergraph.c_str(),
\r
265 FF(avfilter_graph_config(
\r
271 avfilter_inout_free(&outputs);
\r
272 avfilter_inout_free(&inputs);
\r
277 void push(const std::shared_ptr<AVFrame>& src_av_frame)
\r
279 FF(av_buffersrc_add_frame(
\r
281 src_av_frame.get()));
\r
284 std::shared_ptr<AVFrame> poll()
\r
286 std::shared_ptr<AVFrame> filt_frame(
\r
293 const auto ret = av_buffersink_get_frame(
\r
297 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
\r
300 FF_RET(ret, "poll");
\r
305 // void push(const std::shared_ptr<AVFrame>& frame)
\r
310 // if(frame->data[0] == nullptr || frame->width < 1)
\r
311 // BOOST_THROW_EXCEPTION(invalid_argument());
\r
313 // if(filters_.empty())
\r
315 // bypass_.push(make_safe_ptr(frame));
\r
325 // graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});
\r
328 // std::stringstream args;
\r
329 // args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio
\r
330 // THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get()), "[filter]");
\r
332 //#if FF_API_OLD_VSINK_API
\r
333 // THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts_.data(), graph_.get()), "[filter]");
\r
335 // safe_ptr<AVBufferSinkParams> buffersink_params(av_buffersink_params_alloc(), av_free);
\r
336 // buffersink_params->pixel_fmts = pix_fmts_.data();
\r
337 // THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, buffersink_params.get(), graph_.get()), "[filter]");
\r
339 // AVFilterInOut* inputs = avfilter_inout_alloc();
\r
340 // AVFilterInOut* outputs = avfilter_inout_alloc();
\r
342 // outputs->name = av_strdup("in");
\r
343 // outputs->filter_ctx = buffersrc_ctx_;
\r
344 // outputs->pad_idx = 0;
\r
345 // outputs->next = nullptr;
\r
347 // inputs->name = av_strdup("out");
\r
348 // inputs->filter_ctx = buffersink_ctx_;
\r
349 // inputs->pad_idx = 0;
\r
350 // inputs->next = nullptr;
\r
352 // std::string filters = boost::to_lower_copy(narrow(filters_));
\r
353 // THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters.c_str(), &inputs, &outputs, NULL), "[filter]");
\r
355 // auto yadif_filter = boost::adaptors::filtered([&](AVFilterContext* p){return strstr(p->name, "yadif") != 0;});
\r
357 // BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)
\r
359 // // Don't trust that libavfilter chooses optimal format.
\r
360 // filter_ctx->filter->query_formats = [&]() -> int (*)(AVFilterContext*)
\r
362 // switch(frame->format)
\r
364 // case PIX_FMT_YUV444P16:
\r
365 // case PIX_FMT_YUV444P10:
\r
366 // case PIX_FMT_YUV444P9:
\r
367 // case PIX_FMT_YUV444P:
\r
368 // case PIX_FMT_BGR24:
\r
369 // case PIX_FMT_RGB24:
\r
370 // return query_formats_444;
\r
371 // case PIX_FMT_YUV422P16:
\r
372 // case PIX_FMT_YUV422P10:
\r
373 // case PIX_FMT_YUV422P9:
\r
374 // case PIX_FMT_YUV422P:
\r
375 // case PIX_FMT_UYVY422:
\r
376 // case PIX_FMT_YUYV422:
\r
377 // return query_formats_422;
\r
378 // case PIX_FMT_YUV420P16:
\r
379 // case PIX_FMT_YUV420P10:
\r
380 // case PIX_FMT_YUV420P9:
\r
381 // case PIX_FMT_YUV420P:
\r
382 // return query_formats_420;
\r
383 // case PIX_FMT_YUVA420P:
\r
384 // case PIX_FMT_BGRA:
\r
385 // case PIX_FMT_RGBA:
\r
386 // case PIX_FMT_ABGR:
\r
387 // case PIX_FMT_ARGB:
\r
388 // return query_formats_420a;
\r
389 // case PIX_FMT_UYYVYY411:
\r
390 // case PIX_FMT_YUV411P:
\r
391 // return query_formats_411;
\r
392 // case PIX_FMT_YUV410P:
\r
393 // return query_formats_410;
\r
395 // return filter_ctx->filter->query_formats;
\r
400 // THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]");
\r
402 // BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)
\r
403 // parallel_yadif_ctx_ = make_parallel_yadif(filter_ctx);
\r
407 // graph_ = nullptr;
\r
412 // THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]");
\r
414 // catch(ffmpeg_error&)
\r
420 // BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));
\r
424 // std::shared_ptr<AVFrame> poll()
\r
426 // if(filters_.empty())
\r
428 // if(bypass_.empty())
\r
430 // auto frame = bypass_.front();
\r
440 // if(avfilter_poll_frame(buffersink_ctx_->inputs[0]))
\r
442 // AVFilterBufferRef *picref;
\r
443 // THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]");
\r
448 // safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)
\r
451 // avfilter_unref_buffer(picref);
\r
454 // avcodec_get_frame_defaults(frame.get());
\r
456 // memcpy(frame->data, picref->data, sizeof(frame->data));
\r
457 // memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize));
\r
458 // frame->format = picref->format;
\r
459 // frame->width = picref->video->w;
\r
460 // frame->height = picref->video->h;
\r
461 // frame->pkt_pos = picref->pos;
\r
462 // frame->interlaced_frame = picref->video->interlaced;
\r
463 // frame->top_field_first = picref->video->top_field_first;
\r
464 // frame->key_frame = picref->video->key_frame;
\r
465 // frame->pict_type = picref->video->pict_type;
\r
466 // frame->sample_aspect_ratio = picref->video->sample_aspect_ratio;
\r
473 // catch(ffmpeg_error&)
\r
479 // BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));
\r
487 boost::rational<int> in_time_base,
\r
488 boost::rational<int> in_frame_rate,
\r
489 boost::rational<int> in_sample_aspect_ratio,
\r
490 AVPixelFormat in_pix_fmt,
\r
491 std::vector<AVPixelFormat> out_pix_fmts,
\r
492 const std::string& filtergraph)
\r
493 : impl_(new implementation(
\r
498 in_sample_aspect_ratio,
\r
502 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}
\r
503 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}
\r
504 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
\r
505 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}
\r
506 std::wstring filter::filter_str() const{return impl_->filters_;}
\r
507 std::vector<safe_ptr<AVFrame>> filter::poll_all()
\r
509 std::vector<safe_ptr<AVFrame>> frames;
\r
510 for(auto frame = poll(); frame; frame = poll())
\r
511 frames.push_back(make_safe_ptr(frame));
\r