1 #include "../../stdafx.h"
\r
5 #include "../../ffmpeg_error.h"
\r
7 #include <common/exception/exceptions.h>
\r
8 #include <core/producer/frame/basic_frame.h>
\r
9 #include <core/producer/frame/frame_factory.h>
\r
10 #include <core/mixer/write_frame.h>
\r
12 #include <boost/circular_buffer.hpp>
\r
19 #define __STDC_CONSTANT_MACROS
\r
20 #define __STDC_LIMIT_MACROS
\r
21 #include <libavutil/avutil.h>
\r
22 #include <libavutil/imgutils.h>
\r
23 #include <libavfilter/avfilter.h>
\r
24 #include <libavfilter/avcodec.h>
\r
25 #include <libavfilter/vsrc_buffer.h>
\r
26 #include <libavfilter/avfiltergraph.h>
\r
31 struct filter::implementation
\r
33 std::string filters_;
\r
34 std::shared_ptr<AVFilterGraph> graph_;
\r
35 AVFilterContext* video_in_filter_;
\r
36 AVFilterContext* video_out_filter_;
\r
38 boost::circular_buffer<std::shared_ptr<AVFilterBufferRef>> buffers_;
\r
40 implementation(const std::string& filters)
\r
43 std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);
\r
45 buffers_.set_capacity(3);
\r
48 void push(const safe_ptr<AVFrame>& frame)
\r
54 graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});
\r
57 std::stringstream buffer_ss;
\r
58 buffer_ss << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio
\r
59 errn = avfilter_graph_create_filter(&video_in_filter_, avfilter_get_by_name("buffer"), "src", buffer_ss.str().c_str(), NULL, graph_.get());
\r
60 if(errn < 0 || !video_in_filter_)
\r
62 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<
\r
63 boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));
\r
67 errn = avfilter_graph_create_filter(&video_out_filter_, avfilter_get_by_name("nullsink"), "out", NULL, NULL, graph_.get());
\r
68 if(errn < 0 || !video_out_filter_)
\r
70 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<
\r
71 boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));
\r
74 AVFilterInOut* outputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));
\r
75 AVFilterInOut* inputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));
\r
77 outputs->name = av_strdup("in");
\r
78 outputs->filter_ctx = video_in_filter_;
\r
79 outputs->pad_idx = 0;
\r
80 outputs->next = NULL;
\r
82 inputs->name = av_strdup("out");
\r
83 inputs->filter_ctx = video_out_filter_;
\r
84 inputs->pad_idx = 0;
\r
85 inputs->next = NULL;
\r
87 errn = avfilter_graph_parse(graph_.get(), filters_.c_str(), inputs, outputs, NULL);
\r
90 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<
\r
91 boost::errinfo_api_function("avfilter_graph_parse") << boost::errinfo_errno(AVUNERROR(errn)));
\r
94 // av_free(outputs);
\r
97 errn = avfilter_graph_config(graph_.get(), NULL);
\r
100 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn))
\r
101 << boost::errinfo_api_function("avfilter_graph_config") << boost::errinfo_errno(AVUNERROR(errn)));
\r
105 errn = av_vsrc_buffer_add_frame(video_in_filter_, frame.get(), AV_VSRC_BUF_FLAG_OVERWRITE);
\r
108 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<
\r
109 boost::errinfo_api_function("av_vsrc_buffer_add_frame") << boost::errinfo_errno(AVUNERROR(errn)));
\r
113 std::vector<safe_ptr<AVFrame>> poll()
\r
115 int errn = avfilter_poll_frame(video_out_filter_->inputs[0]);
\r
118 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<
\r
119 boost::errinfo_api_function("avfilter_poll_frame") << boost::errinfo_errno(AVUNERROR(errn)));
\r
122 std::vector<safe_ptr<AVFrame>> result;
\r
124 std::generate_n(std::back_inserter(result), errn, [&]{return request_frame();});
\r
129 safe_ptr<AVFrame> request_frame()
\r
131 auto link = video_out_filter_->inputs[0];
\r
133 int errn = avfilter_request_frame(link);
\r
136 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<
\r
137 boost::errinfo_api_function("avfilter_request_frame") << boost::errinfo_errno(AVUNERROR(errn)));
\r
140 auto pic = reinterpret_cast<AVPicture*>(link->cur_buf->buf);
\r
142 safe_ptr<AVFrame> frame(avcodec_alloc_frame(), av_free);
\r
143 avcodec_get_frame_defaults(frame.get());
\r
145 for(size_t n = 0; n < 4; ++n)
\r
147 frame->data[n] = pic->data[n];
\r
148 frame->linesize[n] = pic->linesize[n];
\r
152 frame->width = link->cur_buf->video->w;
\r
153 frame->height = link->cur_buf->video->h;
\r
154 frame->format = link->cur_buf->format;
\r
155 frame->interlaced_frame = link->cur_buf->video->interlaced;
\r
156 frame->top_field_first = link->cur_buf->video->top_field_first;
\r
157 frame->key_frame = link->cur_buf->video->key_frame;
\r
159 buffers_.push_back(std::shared_ptr<AVFilterBufferRef>(link->cur_buf, avfilter_unref_buffer));
\r
166 int errn = avfilter_poll_frame(video_out_filter_->inputs[0]);
\r
169 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<
\r
170 boost::errinfo_api_function("avfilter_poll_frame") << boost::errinfo_errno(AVUNERROR(errn)));
\r
175 filter::filter(const std::string& filters) : impl_(new implementation(filters)){}
\r
176 void filter::push(const safe_ptr<AVFrame>& frame) {impl_->push(frame);}
\r
177 std::vector<safe_ptr<AVFrame>> filter::poll() {return impl_->poll();}
\r
178 bool filter::is_ready() const{return impl_->graph_ != nullptr;}
\r