summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-02-24 22:15:50 +0000
committerCarl Hetherington <cth@carlh.net>2013-02-24 22:15:50 +0000
commitcf1e212c30ec7419b96388e4f78b44cb55bf34c5 (patch)
treebef5e7ceb56f8df18af94bd5987b7119b4c40c53 /src/lib
parentbbd352c3a0acf312e66b8be0d8bfb475275102d6 (diff)
Basic stuff to analyse audio (job).
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/analyse_audio_job.cc108
-rw-r--r--src/lib/analyse_audio_job.h45
-rw-r--r--src/lib/audio_analysis.cc69
-rw-r--r--src/lib/audio_analysis.h59
-rw-r--r--src/lib/ffmpeg_decoder.cc12
-rw-r--r--src/lib/film.cc29
-rw-r--r--src/lib/film.h6
-rw-r--r--src/lib/options.h6
-rw-r--r--src/lib/wscript2
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