diff options
| author | Carl Hetherington <cth@carlh.net> | 2015-11-18 17:07:07 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2015-11-18 20:52:20 +0000 |
| commit | 419afb743ad2aaf1ea301356ac09f9a26ee15567 (patch) | |
| tree | a90a3776b0919871750c173c2bb753321ddf7282 /src/lib | |
| parent | e07926b8b0d3d93c2b670aea4b49230170a3532b (diff) | |
Basic (untested) ebur128 (#368).
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/analyse_audio_job.cc | 47 | ||||
| -rw-r--r-- | src/lib/analyse_audio_job.h | 9 | ||||
| -rw-r--r-- | src/lib/audio_analysis.cc | 36 | ||||
| -rw-r--r-- | src/lib/audio_analysis.h | 46 | ||||
| -rw-r--r-- | src/lib/filter_graph.cc | 35 | ||||
| -rw-r--r-- | src/lib/filter_graph.h | 6 | ||||
| -rw-r--r-- | src/lib/video_filter_graph.cc | 16 | ||||
| -rw-r--r-- | src/lib/video_filter_graph.h | 4 | ||||
| -rw-r--r-- | src/lib/wscript | 1 |
9 files changed, 161 insertions, 39 deletions
diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 525ac6c91..5dba56598 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -25,6 +25,11 @@ #include "film.h" #include "player.h" #include "playlist.h" +#include "filter.h" +#include "audio_filter_graph.h" +extern "C" { +#include <libavutil/channel_layout.h> +} #include <boost/foreach.hpp> #include <iostream> @@ -39,20 +44,35 @@ using boost::dynamic_pointer_cast; int const AnalyseAudioJob::_num_points = 1024; +extern "C" { +/* I added these functions to the FFmpeg that DCP-o-matic is built with, but there isn't an + existing header file to put the prototype in. Cheat by putting it in here. +*/ +double* av_ebur128_get_true_peaks (void* context); +double* av_ebur128_get_sample_peaks (void* context); +double av_ebur128_get_integrated_loudness(void* context); +double av_ebur128_get_loudness_range (void* context); +} + AnalyseAudioJob::AnalyseAudioJob (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist) : Job (film) , _playlist (playlist) , _done (0) , _samples_per_point (1) , _current (0) - , _overall_peak (0) - , _overall_peak_frame (0) + , _sample_peak (0) + , _sample_peak_frame (0) + , _ebur128 (new AudioFilterGraph (film->audio_frame_rate(), av_get_default_channel_layout(film->audio_channels()))) { - + _filters.push_back (new Filter ("ebur128", "ebur128", "audio", "ebur128=peak=true")); + _ebur128->setup (_filters); } AnalyseAudioJob::~AnalyseAudioJob () { + BOOST_FOREACH (Filter const * i, _filters) { + delete const_cast<Filter*> (i); + } delete[] _current; } @@ -97,12 +117,23 @@ AnalyseAudioJob::run () _done = 0; DCPTime const block = DCPTime::from_seconds (1.0 / 8); for (DCPTime t = start; t < length; t += block) { - analyse (player->get_audio (t, block, false)); + shared_ptr<const AudioBuffers> audio = player->get_audio (t, block, false); + _ebur128->process (audio); + analyse (audio); set_progress ((t.seconds() - start.seconds()) / (length.seconds() - start.seconds())); } } - _analysis->set_peak (_overall_peak, DCPTime::from_frames (_overall_peak_frame, _film->audio_frame_rate ())); + _analysis->set_sample_peak (_sample_peak, DCPTime::from_frames (_sample_peak_frame, _film->audio_frame_rate ())); + + void* eb = _ebur128->get("Parsed_ebur128_0")->priv; + double true_peak = 0; + for (int i = 0; i < _film->audio_channels(); ++i) { + true_peak = max (true_peak, av_ebur128_get_true_peaks(eb)[i]); + } + _analysis->set_true_peak (true_peak); + _analysis->set_integrated_loudness (av_ebur128_get_integrated_loudness(eb)); + _analysis->set_loudness_range (av_ebur128_get_loudness_range(eb)); if (_playlist->content().size() == 1) { /* If there was only one piece of content in this analysis we may later need to know what its @@ -138,9 +169,9 @@ AnalyseAudioJob::analyse (shared_ptr<const AudioBuffers> b) _current[j][AudioPoint::RMS] += pow (s, 2); _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], as); - if (as > _overall_peak) { - _overall_peak = as; - _overall_peak_frame = _done + i; + if (as > _sample_peak) { + _sample_peak = as; + _sample_peak_frame = _done + i; } if (((_done + i) % _samples_per_point) == 0) { diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h index d9b90ec66..65282b192 100644 --- a/src/lib/analyse_audio_job.h +++ b/src/lib/analyse_audio_job.h @@ -29,6 +29,8 @@ class AudioBuffers; class AudioAnalysis; class Playlist; class AudioPoint; +class AudioFilterGraph; +class Filter; /** @class AnalyseAudioJob * @brief A job to analyse the audio of a film and make a note of its @@ -60,10 +62,13 @@ private: int64_t _samples_per_point; AudioPoint* _current; - float _overall_peak; - Frame _overall_peak_frame; + float _sample_peak; + Frame _sample_peak_frame; boost::shared_ptr<AudioAnalysis> _analysis; + boost::shared_ptr<AudioFilterGraph> _ebur128; + std::vector<Filter const *> _filters; + static const int _num_points; }; diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index 10e022322..03f35d84e 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -62,8 +62,22 @@ AudioAnalysis::AudioAnalysis (boost::filesystem::path filename) _data.push_back (channel); } - _peak = f.number_child<float> ("Peak"); - _peak_time = DCPTime (f.number_child<DCPTime::Type> ("PeakTime")); + _sample_peak = f.optional_number_child<float> ("Peak"); + if (!_sample_peak) { + /* New key */ + _sample_peak = f.optional_number_child<float> ("SamplePeak"); + } + + if (f.optional_number_child<DCPTime::Type> ("PeakTime")) { + _sample_peak_time = DCPTime (f.number_child<DCPTime::Type> ("PeakTime")); + } else if (f.optional_number_child<DCPTime::Type> ("SamplePeakTime")) { + _sample_peak_time = DCPTime (f.number_child<DCPTime::Type> ("SamplePeakTime")); + } + + _true_peak = f.optional_number_child<float> ("TruePeak"); + _integrated_loudness = f.optional_number_child<float> ("IntegratedLoudness"); + _loudness_range = f.optional_number_child<float> ("LoudnessRange"); + _analysis_gain = f.optional_number_child<double> ("AnalysisGain"); } @@ -107,9 +121,21 @@ AudioAnalysis::write (boost::filesystem::path filename) } } - if (_peak) { - root->add_child("Peak")->add_child_text (raw_convert<string> (_peak.get ())); - root->add_child("PeakTime")->add_child_text (raw_convert<string> (_peak_time.get().get ())); + if (_sample_peak) { + root->add_child("SamplePeak")->add_child_text (raw_convert<string> (_sample_peak.get ())); + root->add_child("SamplePeakTime")->add_child_text (raw_convert<string> (_sample_peak_time.get().get ())); + } + + if (_true_peak) { + root->add_child("TruePeak")->add_child_text (raw_convert<string> (_true_peak.get ())); + } + + if (_integrated_loudness) { + root->add_child("IntegratedLoudness")->add_child_text (raw_convert<string> (_integrated_loudness.get ())); + } + + if (_loudness_range) { + root->add_child("LoudnessRange")->add_child_text (raw_convert<string> (_loudness_range.get ())); } if (_analysis_gain) { diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index 0d06e5973..959453496 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -40,21 +40,46 @@ public: AudioAnalysis (boost::filesystem::path); void add_point (int c, AudioPoint const & p); - void set_peak (float peak, DCPTime time) { - _peak = peak; - _peak_time = time; + + void set_sample_peak (float peak, DCPTime time) { + _sample_peak = peak; + _sample_peak_time = time; + } + + void set_true_peak (float peak) { + _true_peak = peak; + } + + void set_integrated_loudness (float l) { + _integrated_loudness = l; + } + + void set_loudness_range (float r) { + _loudness_range = r; } AudioPoint get_point (int c, int p) const; int points (int c) const; int channels () const; - boost::optional<float> peak () const { - return _peak; + boost::optional<float> sample_peak () const { + return _sample_peak; + } + + boost::optional<DCPTime> sample_peak_time () const { + return _sample_peak_time; + } + + boost::optional<float> true_peak () const { + return _true_peak; + } + + boost::optional<float> integrated_loudness () const { + return _integrated_loudness; } - boost::optional<DCPTime> peak_time () const { - return _peak_time; + boost::optional<float> loudness_range () const { + return _loudness_range; } boost::optional<double> analysis_gain () const { @@ -71,8 +96,11 @@ public: private: std::vector<std::vector<AudioPoint> > _data; - boost::optional<float> _peak; - boost::optional<DCPTime> _peak_time; + boost::optional<float> _sample_peak; + boost::optional<DCPTime> _sample_peak_time; + boost::optional<float> _true_peak; + boost::optional<float> _integrated_loudness; + boost::optional<float> _loudness_range; /** If this analysis was run on a single piece of * content we store its gain in dB when the analysis * happened. diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 3efb6970c..249fa7966 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -54,7 +54,8 @@ using dcp::Size; * @param p Pixel format of the images to process. */ FilterGraph::FilterGraph () - : _copy (false) + : _graph (0) + , _copy (false) , _buffer_src_context (0) , _buffer_sink_context (0) , _frame (0) @@ -73,28 +74,28 @@ FilterGraph::setup (vector<Filter const *> filters) _frame = av_frame_alloc (); - AVFilterGraph* graph = avfilter_graph_alloc(); - if (graph == 0) { + _graph = avfilter_graph_alloc(); + if (!_graph) { throw DecodeError (N_("could not create filter graph.")); } - AVFilter* buffer_src = avfilter_get_by_name(N_("buffer")); - if (buffer_src == 0) { + AVFilter* buffer_src = avfilter_get_by_name (src_name().c_str()); + if (!buffer_src) { throw DecodeError (N_("could not find buffer src filter")); } - AVFilter* buffer_sink = avfilter_get_by_name(N_("buffersink")); - if (buffer_sink == 0) { + AVFilter* buffer_sink = avfilter_get_by_name (sink_name().c_str()); + if (!buffer_sink) { throw DecodeError (N_("Could not create buffer sink filter")); } - if (avfilter_graph_create_filter (&_buffer_src_context, buffer_src, "in", src_parameters().c_str(), 0, graph) < 0) { + if (avfilter_graph_create_filter (&_buffer_src_context, buffer_src, N_("in"), src_parameters().c_str(), 0, _graph) < 0) { throw DecodeError (N_("could not create buffer source")); } - AVBufferSinkParams* sink_params = sink_parameters (); + void* sink_params = sink_parameters (); - if (avfilter_graph_create_filter (&_buffer_sink_context, buffer_sink, N_("out"), 0, sink_params, graph) < 0) { + if (avfilter_graph_create_filter (&_buffer_sink_context, buffer_sink, N_("out"), 0, sink_params, _graph) < 0) { throw DecodeError (N_("could not create buffer sink.")); } @@ -112,11 +113,11 @@ FilterGraph::setup (vector<Filter const *> filters) inputs->pad_idx = 0; inputs->next = 0; - if (avfilter_graph_parse (graph, filters_string.c_str(), inputs, outputs, 0) < 0) { + if (avfilter_graph_parse (_graph, filters_string.c_str(), inputs, outputs, 0) < 0) { throw DecodeError (N_("could not set up filter graph.")); } - if (avfilter_graph_config (graph, 0) < 0) { + if (avfilter_graph_config (_graph, 0) < 0) { throw DecodeError (N_("could not configure filter graph.")); } } @@ -126,4 +127,14 @@ FilterGraph::~FilterGraph () if (_frame) { av_frame_free (&_frame); } + + if (_graph) { + avfilter_graph_free (&_graph); + } +} + +AVFilterContext * +FilterGraph::get (string name) +{ + return avfilter_graph_get_filter (_graph, name.c_str ()); } diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index 073340637..00b89d494 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -44,11 +44,15 @@ public: virtual ~FilterGraph (); void setup (std::vector<Filter const *>); + AVFilterContext* get (std::string name); protected: virtual std::string src_parameters () const = 0; - virtual AVBufferSinkParams* sink_parameters () const = 0; + virtual std::string src_name () const = 0; + virtual void* sink_parameters () const = 0; + virtual std::string sink_name () const = 0; + AVFilterGraph* _graph; /** true if this graph has no filters in, so it just copies stuff straight through */ bool _copy; AVFilterContext* _buffer_src_context; diff --git a/src/lib/video_filter_graph.cc b/src/lib/video_filter_graph.cc index 306a17a5d..8aef4fb00 100644 --- a/src/lib/video_filter_graph.cc +++ b/src/lib/video_filter_graph.cc @@ -93,13 +93,27 @@ VideoFilterGraph::src_parameters () const return a.str (); } -AVBufferSinkParams * +void * VideoFilterGraph::sink_parameters () const { AVBufferSinkParams* sink_params = av_buffersink_params_alloc (); AVPixelFormat* pixel_fmts = new AVPixelFormat[2]; + pixel_fmts = new AVPixelFormat[2]; pixel_fmts[0] = _pixel_format; pixel_fmts[1] = AV_PIX_FMT_NONE; sink_params->pixel_fmts = pixel_fmts; return sink_params; } + +string +VideoFilterGraph::src_name () const +{ + return "buffer"; +} + +string +VideoFilterGraph::sink_name () const +{ + return "buffersink"; +} + diff --git a/src/lib/video_filter_graph.h b/src/lib/video_filter_graph.h index 08de557ca..8607e02fc 100644 --- a/src/lib/video_filter_graph.h +++ b/src/lib/video_filter_graph.h @@ -29,7 +29,9 @@ public: protected: std::string src_parameters () const; - AVBufferSinkParams* sink_parameters () const; + std::string src_name () const; + void* sink_parameters () const; + std::string sink_name () const; private: dcp::Size _size; ///< size of the images that this chain can process diff --git a/src/lib/wscript b/src/lib/wscript index 315190307..a76ca9b74 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -28,6 +28,7 @@ sources = """ audio_decoder_stream.cc audio_delay.cc audio_filter.cc + audio_filter_graph.cc audio_mapping.cc audio_point.cc audio_processor.cc |
