diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-02-24 22:15:50 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-02-24 22:15:50 +0000 |
| commit | cf1e212c30ec7419b96388e4f78b44cb55bf34c5 (patch) | |
| tree | bef5e7ceb56f8df18af94bd5987b7119b4c40c53 /src/lib | |
| parent | bbd352c3a0acf312e66b8be0d8bfb475275102d6 (diff) | |
Basic stuff to analyse audio (job).
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/analyse_audio_job.cc | 108 | ||||
| -rw-r--r-- | src/lib/analyse_audio_job.h | 45 | ||||
| -rw-r--r-- | src/lib/audio_analysis.cc | 69 | ||||
| -rw-r--r-- | src/lib/audio_analysis.h | 59 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 12 | ||||
| -rw-r--r-- | src/lib/film.cc | 29 | ||||
| -rw-r--r-- | src/lib/film.h | 6 | ||||
| -rw-r--r-- | src/lib/options.h | 6 | ||||
| -rw-r--r-- | src/lib/wscript | 2 |
9 files changed, 329 insertions, 7 deletions
diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc new file mode 100644 index 000000000..5623fdfcc --- /dev/null +++ b/src/lib/analyse_audio_job.cc @@ -0,0 +1,108 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "audio_analysis.h" +#include "analyse_audio_job.h" +#include "compose.hpp" +#include "film.h" +#include "options.h" +#include "decoder_factory.h" +#include "audio_decoder.h" + +using std::string; +using std::max; +using boost::shared_ptr; + +int const AnalyseAudioJob::_num_points = 1024; + +AnalyseAudioJob::AnalyseAudioJob (shared_ptr<Film> f) + : Job (f) + , _done_for_this_point (0) + , _done (0) + , _samples_per_point (1) +{ + +} + +string +AnalyseAudioJob::name () const +{ + return String::compose ("Analyse audio of %1", _film->name()); +} + +void +AnalyseAudioJob::run () +{ + if (!_film->audio_stream () || !_film->length()) { + set_progress (1); + set_state (FINISHED_ERROR); + return; + } + + DecodeOptions options; + options.decode_video = false; + + Decoders decoders = decoder_factory (_film, options); + assert (decoders.audio); + + decoders.audio->set_audio_stream (_film->audio_stream ()); + decoders.audio->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1)); + + int64_t total_audio_frames = video_frames_to_audio_frames (_film->length().get(), _film->audio_stream()->sample_rate(), _film->frames_per_second()); + _samples_per_point = total_audio_frames / _num_points; + + _current.resize (_film->audio_stream()->channels ()); + _analysis.reset (new AudioAnalysis (_film->audio_stream()->channels())); + + while (!decoders.audio->pass()) { + set_progress (float (_done) / total_audio_frames); + } + + _analysis->write (_film->audio_analysis_path ()); + + set_progress (1); + set_state (FINISHED_OK); +} + +void +AnalyseAudioJob::audio (shared_ptr<AudioBuffers> b) +{ + for (int i = 0; i < b->frames(); ++i) { + for (int j = 0; j < b->channels(); ++j) { + float const s = b->data(j)[i]; + _current[j][AudioPoint::RMS] += pow (s, 2); + _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s)); + + if (_done_for_this_point == _samples_per_point) { + _current[j][AudioPoint::RMS] = 20 * log10 (sqrt (_current[j][AudioPoint::RMS] / _samples_per_point)); + _current[j][AudioPoint::PEAK] = 20 * log10 (_current[j][AudioPoint::PEAK]); + + _analysis->add_point (j, _current[j]); + + _done_for_this_point = 0; + _current[j] = AudioPoint (); + } + } + + ++_done_for_this_point; + } + + _done += b->frames (); +} + diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h new file mode 100644 index 000000000..1e7229ce6 --- /dev/null +++ b/src/lib/analyse_audio_job.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "job.h" +#include "audio_analysis.h" + +class AudioBuffers; + +class AnalyseAudioJob : public Job +{ +public: + AnalyseAudioJob (boost::shared_ptr<Film> f); + + std::string name () const; + void run (); + +private: + void audio (boost::shared_ptr<AudioBuffers>); + + int64_t _done_for_this_point; + int64_t _done; + int64_t _samples_per_point; + std::vector<AudioPoint> _current; + + boost::shared_ptr<AudioAnalysis> _analysis; + + static const int _num_points; +}; + diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc new file mode 100644 index 000000000..4a710f4c1 --- /dev/null +++ b/src/lib/audio_analysis.cc @@ -0,0 +1,69 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <stdint.h> +#include <cassert> +#include <fstream> +#include "audio_analysis.h" + +using std::ostream; +using std::string; +using std::ofstream; +using std::vector; + +AudioPoint::AudioPoint () +{ + for (int i = 0; i < COUNT; ++i) { + _data[i] = 0; + } +} + +void +AudioPoint::write (ostream& s) const +{ + for (int i = 0; i < COUNT; ++i) { + s << _data[i] << "\n"; + } +} + + +AudioAnalysis::AudioAnalysis (int channels) +{ + _data.resize (channels); +} + +void +AudioAnalysis::add_point (int c, AudioPoint const & p) +{ + assert (c < int (_data.size ())); + _data[c].push_back (p); +} + +void +AudioAnalysis::write (string filename) +{ + ofstream f (filename.c_str ()); + f << _data.size() << "\n"; + for (vector<vector<AudioPoint> >::iterator i = _data.begin(); i != _data.end(); ++i) { + f << i->size () << "\n"; + for (vector<AudioPoint>::iterator j = i->begin(); j != i->end(); ++j) { + j->write (f); + } + } +} diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h new file mode 100644 index 000000000..1c668b9c2 --- /dev/null +++ b/src/lib/audio_analysis.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef DVDOMATIC_AUDIO_ANALYSIS_H +#define DVDOMATIC_AUDIO_ANALYSIS_H + +#include <iostream> +#include <vector> + +class AudioPoint +{ +public: + enum Type { + PEAK, + RMS, + COUNT + }; + + AudioPoint (); + + void write (std::ostream &) const; + + float& operator[] (Type t) { + return _data[t]; + } + +private: + float _data[COUNT]; +}; + +class AudioAnalysis +{ +public: + AudioAnalysis (int c); + + void add_point (int c, AudioPoint const & p); + void write (std::string); + +private: + std::vector<std::vector<AudioPoint> > _data; +}; + +#endif diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index d4ed76e37..58c7317ac 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -236,8 +236,10 @@ FFmpegDecoder::pass () int frame_finished; - while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - filter_and_emit_video (_frame); + if (_opt.decode_video) { + while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { + filter_and_emit_video (_frame); + } } if (_audio_stream && _opt.decode_audio) { @@ -258,7 +260,7 @@ FFmpegDecoder::pass () shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream); - if (_packet.stream_index == _video_stream) { + if (_packet.stream_index == _video_stream && _opt.decode_video) { int frame_finished; int const r = avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet); @@ -288,9 +290,9 @@ FFmpegDecoder::pass () was before this packet. Until then audio is thrown away. */ - if (_first_video && _first_video.get() <= source_pts_seconds) { + if ((_first_video && _first_video.get() <= source_pts_seconds) || !_opt.decode_video) { - if (!_first_audio) { + if (!_first_audio && _opt.decode_video) { _first_audio = source_pts_seconds; /* This is our first audio frame, and if we've arrived here we must have had our diff --git a/src/lib/film.cc b/src/lib/film.cc index 1cf161259..fb3fb4cde 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -51,6 +51,7 @@ #include "video_decoder.h" #include "audio_decoder.h" #include "external_audio_decoder.h" +#include "analyse_audio_job.h" using std::string; using std::stringstream; @@ -237,6 +238,15 @@ Film::video_mxf_filename () const return video_state_identifier() + ".mxf"; } +string +Film::audio_analysis_path () const +{ + boost::filesystem::path p; + p /= "analysis"; + p /= content_digest(); + return file (p.string ()); +} + /** Add suitable Jobs to the JobManager to create a DCP for this Film */ void Film::make_dcp () @@ -303,6 +313,19 @@ Film::make_dcp () } } +/** Start a job to analyse the audio of our content file */ +void +Film::analyse_audio () +{ + if (_analyse_audio_job) { + return; + } + + _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this())); + _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this)); + JobManager::instance()->add (_analyse_audio_job); +} + /** Start a job to examine our content file */ void Film::examine_content () @@ -317,6 +340,12 @@ Film::examine_content () } void +Film::analyse_audio_finished () +{ + _analyse_audio_job.reset (); +} + +void Film::examine_content_finished () { _examine_content_job.reset (); diff --git a/src/lib/film.h b/src/lib/film.h index 04a483998..c268d3eac 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -45,6 +45,7 @@ class Job; class Filter; class Log; class ExamineContentJob; +class AnalyseAudioJob; class ExternalAudioStream; /** @class Film @@ -65,8 +66,10 @@ public: std::string info_path (int f) const; std::string video_mxf_dir () const; std::string video_mxf_filename () const; + std::string audio_analysis_path () const; void examine_content (); + void analyse_audio (); void send_dcp_to_tms (); void make_dcp (); @@ -374,9 +377,12 @@ private: /** Any running ExamineContentJob, or 0 */ boost::shared_ptr<ExamineContentJob> _examine_content_job; + /** Any running AnalyseAudioJob, or 0 */ + boost::shared_ptr<AnalyseAudioJob> _analyse_audio_job; void signal_changed (Property); void examine_content_finished (); + void analyse_audio_finished (); std::string video_state_identifier () const; /** Complete path to directory containing the film metadata; diff --git a/src/lib/options.h b/src/lib/options.h index 2cd7dffde..0d2c07fd5 100644 --- a/src/lib/options.h +++ b/src/lib/options.h @@ -28,11 +28,13 @@ class DecodeOptions { public: DecodeOptions () - : decode_audio (true) + : decode_video (true) + , decode_audio (true) , decode_subtitles (false) , video_sync (true) {} - + + bool decode_video; bool decode_audio; bool decode_subtitles; bool video_sync; diff --git a/src/lib/wscript b/src/lib/wscript index eee04190c..c2b46112c 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -16,6 +16,8 @@ def build(bld): obj.source = """ ab_transcode_job.cc ab_transcoder.cc + analyse_audio_job.cc + audio_analysis.cc audio_decoder.cc audio_source.cc config.cc |
