diff options
| author | Carl Hetherington <cth@carlh.net> | 2012-10-27 01:13:07 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2012-10-27 01:13:07 +0100 |
| commit | 7ffd5bd9b4571c2db06a8c50419fe90b06ef1a07 (patch) | |
| tree | ae1f013240e02da963c6067ab71379caf04162aa /src | |
| parent | 520203da69da7b6638cba569ca1975a7dd036b5e (diff) | |
Try to separate out filter graph code and use a different one for each different pixel format / size of frame.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/decoder.cc | 190 | ||||
| -rw-r--r-- | src/lib/decoder.h | 18 | ||||
| -rw-r--r-- | src/lib/ffmpeg_compatibility.h | 2 | ||||
| -rw-r--r-- | src/lib/filter_graph.cc | 184 | ||||
| -rw-r--r-- | src/lib/filter_graph.h | 25 | ||||
| -rw-r--r-- | src/lib/util.cc | 5 | ||||
| -rw-r--r-- | src/lib/util.h | 2 | ||||
| -rw-r--r-- | src/lib/wscript | 1 |
8 files changed, 249 insertions, 178 deletions
diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index e2e151cb7..358109a7e 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -24,17 +24,6 @@ #include <iostream> #include <stdint.h> #include <boost/lexical_cast.hpp> -extern "C" { -#include <libavfilter/avfiltergraph.h> -#include <libavfilter/buffersrc.h> -#if (LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 53 && LIBAVFILTER_VERSION_MINOR <= 77) || LIBAVFILTER_VERSION_MAJOR == 3 -#include <libavfilter/avcodec.h> -#include <libavfilter/buffersink.h> -#elif LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15 -#include <libavfilter/vsrc_buffer.h> -#endif -#include <libavformat/avio.h> -} #include "film.h" #include "format.h" #include "job.h" @@ -52,6 +41,7 @@ extern "C" { using std::string; using std::stringstream; using std::min; +using std::list; using boost::shared_ptr; /** @param f Film. @@ -68,9 +58,6 @@ Decoder::Decoder (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, , _minimal (minimal) , _ignore_length (ignore_length) , _video_frame (0) - , _buffer_src_context (0) - , _buffer_sink_context (0) - , _have_setup_video_filters (false) , _delay_line (0) , _delay_in_bytes (0) , _audio_frames_processed (0) @@ -170,11 +157,6 @@ Decoder::go () bool Decoder::pass () { - if (!_have_setup_video_filters) { - setup_video_filters (); - _have_setup_video_filters = true; - } - if (!_ignore_length && _video_frame >= _film->dcp_length()) { return true; } @@ -294,167 +276,37 @@ Decoder::process_video (AVFrame* frame) return; } -#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 53 && LIBAVFILTER_VERSION_MINOR <= 61 - - if (av_vsrc_buffer_add_frame (_buffer_src_context, frame, 0) < 0) { - throw DecodeError ("could not push buffer into filter chain."); - } - -#elif LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15 - - AVRational par; - par.num = sample_aspect_ratio_numerator (); - par.den = sample_aspect_ratio_denominator (); + shared_ptr<FilterGraph> graph; - if (av_vsrc_buffer_add_frame (_buffer_src_context, frame, 0, par) < 0) { - throw DecodeError ("could not push buffer into filter chain."); + list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin(); + while (i != _filter_graphs.end() && !(*i)->can_process (Size (frame->width, frame->height), (AVPixelFormat) frame->format)) { + ++i; } -#else - - if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) { - throw DecodeError ("could not push buffer into filter chain."); + if (i == _filter_graphs.end ()) { + graph.reset (new FilterGraph (_film, this, _opt->apply_crop, Size (frame->width, frame->height), (AVPixelFormat) frame->format)); + _filter_graphs.push_back (graph); + std::cout << "NEW GRAPH for " << frame->width << "x" << frame->height << " " << frame->format << "\n"; + } else { + graph = *i; } -#endif - -#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 15 && LIBAVFILTER_VERSION_MINOR <= 61 - while (avfilter_poll_frame (_buffer_sink_context->inputs[0])) { -#else - while (av_buffersink_read (_buffer_sink_context, 0)) { -#endif + list<shared_ptr<Image> > images = graph->process (frame); -#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 15 - - int r = avfilter_request_frame (_buffer_sink_context->inputs[0]); - if (r < 0) { - throw DecodeError ("could not request filtered frame"); + for (list<shared_ptr<Image> >::iterator i = images.begin(); i != images.end(); ++i) { + if (_opt->black_after > 0 && _video_frame > _opt->black_after) { + (*i)->make_black (); } - AVFilterBufferRef* filter_buffer = _buffer_sink_context->inputs[0]->cur_buf; - -#else - - AVFilterBufferRef* filter_buffer; - if (av_buffersink_get_buffer_ref (_buffer_sink_context, &filter_buffer, 0) < 0) { - filter_buffer = 0; + shared_ptr<Subtitle> sub; + if (_timed_subtitle && _timed_subtitle->displayed_at (double (last_video_frame()) / _film->frames_per_second())) { + sub = _timed_subtitle->subtitle (); } - -#endif - if (filter_buffer) { - /* This takes ownership of filter_buffer */ - shared_ptr<Image> image (new FilterBufferImage ((PixelFormat) frame->format, filter_buffer)); - - if (_opt->black_after > 0 && _video_frame > _opt->black_after) { - image->make_black (); - } - - shared_ptr<Subtitle> sub; - if (_timed_subtitle && _timed_subtitle->displayed_at (double (last_video_frame()) / _film->frames_per_second())) { - sub = _timed_subtitle->subtitle (); - } - - TIMING ("Decoder emits %1", _video_frame); - Video (image, _video_frame, sub); - ++_video_frame; - } - } -} - - -/** Set up a video filtering chain to include cropping and any filters that are specified - * by the Film. - */ -void -Decoder::setup_video_filters () -{ - stringstream fs; - Size size_after_crop; - - if (_opt->apply_crop) { - size_after_crop = _film->cropped_size (native_size ()); - fs << crop_string (Position (_film->crop().left, _film->crop().top), size_after_crop); - } else { - size_after_crop = native_size (); - fs << crop_string (Position (0, 0), size_after_crop); - } - - string filters = Filter::ffmpeg_strings (_film->filters()).first; - if (!filters.empty ()) { - filters += ","; - } - - filters += fs.str (); - - avfilter_register_all (); - - AVFilterGraph* graph = avfilter_graph_alloc(); - if (graph == 0) { - throw DecodeError ("Could not create filter graph."); - } - - AVFilter* buffer_src = avfilter_get_by_name("buffer"); - if (buffer_src == 0) { - throw DecodeError ("Could not find buffer src filter"); - } - - AVFilter* buffer_sink = get_sink (); - - stringstream a; - a << native_size().width << ":" - << native_size().height << ":" - << pixel_format() << ":" - << time_base_numerator() << ":" - << time_base_denominator() << ":" - << sample_aspect_ratio_numerator() << ":" - << sample_aspect_ratio_denominator(); - - int r; - - if ((r = avfilter_graph_create_filter (&_buffer_src_context, buffer_src, "in", a.str().c_str(), 0, graph)) < 0) { - throw DecodeError ("could not create buffer source"); - } - - AVBufferSinkParams* sink_params = av_buffersink_params_alloc (); - PixelFormat* pixel_fmts = new PixelFormat[2]; - pixel_fmts[0] = pixel_format (); - pixel_fmts[1] = PIX_FMT_NONE; - sink_params->pixel_fmts = pixel_fmts; - - if (avfilter_graph_create_filter (&_buffer_sink_context, buffer_sink, "out", 0, sink_params, graph) < 0) { - throw DecodeError ("could not create buffer sink."); - } - - AVFilterInOut* outputs = avfilter_inout_alloc (); - outputs->name = av_strdup("in"); - outputs->filter_ctx = _buffer_src_context; - outputs->pad_idx = 0; - outputs->next = 0; - - AVFilterInOut* inputs = avfilter_inout_alloc (); - inputs->name = av_strdup("out"); - inputs->filter_ctx = _buffer_sink_context; - inputs->pad_idx = 0; - inputs->next = 0; - - _film->log()->log ("Using filter chain `" + filters + "'"); - -#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15 - if (avfilter_graph_parse (graph, filters.c_str(), inputs, outputs, 0) < 0) { - throw DecodeError ("could not set up filter graph."); - } -#else - if (avfilter_graph_parse (graph, filters.c_str(), &inputs, &outputs, 0) < 0) { - throw DecodeError ("could not set up filter graph."); - } -#endif - - if (avfilter_graph_config (graph, 0) < 0) { - throw DecodeError ("could not configure filter graph."); + TIMING ("Decoder emits %1", _video_frame); + Video ((*i), _video_frame, sub); + ++_video_frame; } - - /* XXX: leaking `inputs' / `outputs' ? */ } void diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 7940aed6c..b20adb511 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -31,6 +31,7 @@ #include <boost/signals2.hpp> #include "util.h" #include "stream.h" +#include "filter_graph.h" class Job; class Options; @@ -69,6 +70,11 @@ public: virtual int64_t audio_channel_layout () const = 0; virtual bool has_subtitles () const = 0; + virtual int time_base_numerator () const = 0; + virtual int time_base_denominator () const = 0; + virtual int sample_aspect_ratio_numerator () const = 0; + virtual int sample_aspect_ratio_denominator () const = 0; + void process_begin (); bool pass (); void process_end (); @@ -96,15 +102,12 @@ public: /** Emitted when some audio data is ready */ boost::signals2::signal<void (boost::shared_ptr<AudioBuffers>)> Audio; - + protected: + /** perform a single pass at our content */ virtual bool do_pass () = 0; virtual PixelFormat pixel_format () const = 0; - virtual int time_base_numerator () const = 0; - virtual int time_base_denominator () const = 0; - virtual int sample_aspect_ratio_numerator () const = 0; - virtual int sample_aspect_ratio_denominator () const = 0; void process_video (AVFrame *); void process_audio (uint8_t *, int); @@ -128,16 +131,13 @@ protected: bool _ignore_length; private: - void setup_video_filters (); void emit_audio (uint8_t* data, int size); /** last video frame to be processed */ int _video_frame; - AVFilterContext* _buffer_src_context; - AVFilterContext* _buffer_sink_context; + std::list<boost::shared_ptr<FilterGraph> > _filter_graphs; - bool _have_setup_video_filters; DelayLine* _delay_line; int _delay_in_bytes; diff --git a/src/lib/ffmpeg_compatibility.h b/src/lib/ffmpeg_compatibility.h index 7aaae92e2..80cc79ffb 100644 --- a/src/lib/ffmpeg_compatibility.h +++ b/src/lib/ffmpeg_compatibility.h @@ -17,6 +17,8 @@ */ +struct AVFilterInOut; + extern AVFilter* get_sink (); extern AVFilterInOut* avfilter_inout_alloc (); diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc new file mode 100644 index 000000000..7d627b1b0 --- /dev/null +++ b/src/lib/filter_graph.cc @@ -0,0 +1,184 @@ +extern "C" { +#include <libavfilter/avfiltergraph.h> +#include <libavfilter/buffersrc.h> +#if (LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 53 && LIBAVFILTER_VERSION_MINOR <= 77) || LIBAVFILTER_VERSION_MAJOR == 3 +#include <libavfilter/avcodec.h> +#include <libavfilter/buffersink.h> +#elif LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15 +#include <libavfilter/vsrc_buffer.h> +#endif +#include <libavformat/avio.h> +} +#include "film.h" +#include "decoder.h" +#include "filter_graph.h" +#include "ffmpeg_compatibility.h" +#include "filter.h" +#include "exceptions.h" +#include "image.h" + +using std::stringstream; +using std::string; +using std::list; +using boost::shared_ptr; + +FilterGraph::FilterGraph (shared_ptr<Film> film, Decoder* decoder, bool crop, Size s, AVPixelFormat p) + : _buffer_src_context (0) + , _buffer_sink_context (0) + , _size (s) + , _pixel_format (p) +{ + stringstream fs; + Size size_after_crop; + + if (crop) { + size_after_crop = film->cropped_size (decoder->native_size ()); + fs << crop_string (Position (film->crop().left, film->crop().top), size_after_crop); + } else { + size_after_crop = decoder->native_size (); + fs << crop_string (Position (0, 0), size_after_crop); + } + + string filters = Filter::ffmpeg_strings (film->filters()).first; + if (!filters.empty ()) { + filters += ","; + } + + filters += fs.str (); + + avfilter_register_all (); + + AVFilterGraph* graph = avfilter_graph_alloc(); + if (graph == 0) { + throw DecodeError ("Could not create filter graph."); + } + + AVFilter* buffer_src = avfilter_get_by_name("buffer"); + if (buffer_src == 0) { + throw DecodeError ("Could not find buffer src filter"); + } + + AVFilter* buffer_sink = get_sink (); + + stringstream a; + a << _size.width << ":" + << _size.height << ":" + << _pixel_format << ":" + << decoder->time_base_numerator() << ":" + << decoder->time_base_denominator() << ":" + << decoder->sample_aspect_ratio_numerator() << ":" + << decoder->sample_aspect_ratio_denominator(); + + int r; + + if ((r = avfilter_graph_create_filter (&_buffer_src_context, buffer_src, "in", a.str().c_str(), 0, graph)) < 0) { + throw DecodeError ("could not create buffer source"); + } + + AVBufferSinkParams* sink_params = av_buffersink_params_alloc (); + PixelFormat* pixel_fmts = new PixelFormat[2]; + pixel_fmts[0] = _pixel_format; + pixel_fmts[1] = PIX_FMT_NONE; + sink_params->pixel_fmts = pixel_fmts; + + if (avfilter_graph_create_filter (&_buffer_sink_context, buffer_sink, "out", 0, sink_params, graph) < 0) { + throw DecodeError ("could not create buffer sink."); + } + + AVFilterInOut* outputs = avfilter_inout_alloc (); + outputs->name = av_strdup("in"); + outputs->filter_ctx = _buffer_src_context; + outputs->pad_idx = 0; + outputs->next = 0; + + AVFilterInOut* inputs = avfilter_inout_alloc (); + inputs->name = av_strdup("out"); + inputs->filter_ctx = _buffer_sink_context; + inputs->pad_idx = 0; + inputs->next = 0; + +#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15 + if (avfilter_graph_parse (graph, filters.c_str(), inputs, outputs, 0) < 0) { + throw DecodeError ("could not set up filter graph."); + } +#else + if (avfilter_graph_parse (graph, filters.c_str(), &inputs, &outputs, 0) < 0) { + throw DecodeError ("could not set up filter graph."); + } +#endif + + if (avfilter_graph_config (graph, 0) < 0) { + throw DecodeError ("could not configure filter graph."); + } + + /* XXX: leaking `inputs' / `outputs' ? */ +} + +list<shared_ptr<Image> > +FilterGraph::process (AVFrame* frame) +{ + list<shared_ptr<Image> > images; + +#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 53 && LIBAVFILTER_VERSION_MINOR <= 61 + + if (av_vsrc_buffer_add_frame (_buffer_src_context, frame, 0) < 0) { + throw DecodeError ("could not push buffer into filter chain."); + } + +#elif LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15 + + AVRational par; + par.num = sample_aspect_ratio_numerator (); + par.den = sample_aspect_ratio_denominator (); + + if (av_vsrc_buffer_add_frame (_buffer_src_context, frame, 0, par) < 0) { + throw DecodeError ("could not push buffer into filter chain."); + } + +#else + + if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) { + throw DecodeError ("could not push buffer into filter chain."); + } + +#endif + +#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 15 && LIBAVFILTER_VERSION_MINOR <= 61 + while (avfilter_poll_frame (_buffer_sink_context->inputs[0])) { +#else + while (av_buffersink_read (_buffer_sink_context, 0)) { +#endif + +#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 15 + + int r = avfilter_request_frame (_buffer_sink_context->inputs[0]); + if (r < 0) { + throw DecodeError ("could not request filtered frame"); + } + + AVFilterBufferRef* filter_buffer = _buffer_sink_context->inputs[0]->cur_buf; + +#else + + AVFilterBufferRef* filter_buffer; + if (av_buffersink_get_buffer_ref (_buffer_sink_context, &filter_buffer, 0) < 0) { + filter_buffer = 0; + } + +#endif + + if (filter_buffer) { + /* This takes ownership of filter_buffer */ + images.push_back (shared_ptr<Image> (new FilterBufferImage ((PixelFormat) frame->format, filter_buffer))); + } + } + + return images; +} + +bool +FilterGraph::can_process (Size s, AVPixelFormat p) const +{ + return (_size == s && _pixel_format == p); +} + diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h new file mode 100644 index 000000000..c6ae15e48 --- /dev/null +++ b/src/lib/filter_graph.h @@ -0,0 +1,25 @@ +#ifndef DVDOMATIC_FILTER_GRAPH_H +#define DVDOMATIC_FILTER_GRAPH_H + +#include "util.h" + +class Decoder; +class Image; +class Film; + +class FilterGraph +{ +public: + FilterGraph (boost::shared_ptr<Film> film, Decoder* decoder, bool crop, Size s, AVPixelFormat p); + + bool can_process (Size s, AVPixelFormat p) const; + std::list<boost::shared_ptr<Image> > process (AVFrame* frame); + +private: + AVFilterContext* _buffer_src_context; + AVFilterContext* _buffer_sink_context; + Size _size; + AVPixelFormat _pixel_format; +}; + +#endif diff --git a/src/lib/util.cc b/src/lib/util.cc index 90b27f39a..b68f7e392 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -387,6 +387,11 @@ dcp_audio_sample_rate (int fs) return 96000; } +bool operator== (Size const & a, Size const & b) +{ + return (a.width == b.width && a.height == b.height); +} + bool operator== (Crop const & a, Crop const & b) { return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom); diff --git a/src/lib/util.h b/src/lib/util.h index eb2af8381..1af650085 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -83,6 +83,8 @@ struct Size int height; }; +extern bool operator== (Size const & a, Size const & b); + /** A description of the crop of an image or video. */ struct Crop { diff --git a/src/lib/wscript b/src/lib/wscript index 497af6e5c..969a69606 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -22,6 +22,7 @@ def build(bld): encoder.cc encoder_factory.cc examine_content_job.cc + filter_graph.cc ffmpeg_compatibility.cc ffmpeg_decoder.cc film.cc |
