From: Carl Hetherington Date: Thu, 25 Jul 2013 21:04:20 +0000 (+0100) Subject: Hopefully much cleaner handling of PTS changes under resample. X-Git-Tag: v2.0.48~1337^2~125 X-Git-Url: https://git.carlh.net/gitweb/?a=commitdiff_plain;h=78e5a331074a456097a162d47501daf1df1ab1a3;p=dcpomatic.git Hopefully much cleaner handling of PTS changes under resample. --- diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index 846cdf595..1f5868583 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -32,36 +32,16 @@ using std::cout; using boost::optional; using boost::shared_ptr; -AudioDecoder::AudioDecoder (shared_ptr film, shared_ptr content) +AudioDecoder::AudioDecoder (shared_ptr film) : Decoder (film) , _audio_position (0) { - if (content->content_audio_frame_rate() != content->output_audio_frame_rate() && content->audio_channels ()) { - _resampler.reset ( - new Resampler ( - content->content_audio_frame_rate(), - content->output_audio_frame_rate(), - content->audio_channels() - ) - ); - } -} - -void -AudioDecoder::audio (shared_ptr data, AudioContent::Frame) -{ - if (_resampler) { - data = _resampler->run (data); - } - Audio (data, _audio_position); - _audio_position += data->frames (); } void -AudioDecoder::flush () +AudioDecoder::audio (shared_ptr data, AudioContent::Frame frame) { - if (_resampler) { - _resampler->flush (); - } + Audio (data, frame); + _audio_position = frame + data->frames (); } diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index e2d28d707..2ad53da8b 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -29,7 +29,6 @@ #include "audio_content.h" class AudioBuffers; -class Resampler; /** @class AudioDecoder. * @brief Parent class for audio decoders. @@ -37,19 +36,15 @@ class Resampler; class AudioDecoder : public virtual Decoder { public: - AudioDecoder (boost::shared_ptr, boost::shared_ptr); + AudioDecoder (boost::shared_ptr); /** Emitted when some audio data is ready */ boost::signals2::signal, AudioContent::Frame)> Audio; protected: - void flush (); - void audio (boost::shared_ptr, AudioContent::Frame); - /** Frame index of next emission (post resampling) */ AudioContent::Frame _audio_position; - boost::shared_ptr _resampler; }; #endif diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index e2e5b9d64..58745598a 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -60,7 +60,7 @@ using libdcp::Size; FFmpegDecoder::FFmpegDecoder (shared_ptr f, shared_ptr c, bool video, bool audio) : Decoder (f) , VideoDecoder (f, c) - , AudioDecoder (f, c) + , AudioDecoder (f) , SubtitleDecoder (f) , FFmpeg (c) , _subtitle_codec_context (0) @@ -140,8 +140,6 @@ FFmpegDecoder::flush () decode_audio_packet (); } - AudioDecoder::flush (); - /* Stop us being asked for any more data */ _video_position = _ffmpeg_content->video_length (); _audio_position = _ffmpeg_content->audio_length (); diff --git a/src/lib/player.cc b/src/lib/player.cc index 1ab164d86..620efbd0d 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -31,6 +31,7 @@ #include "job.h" #include "image.h" #include "ratio.h" +#include "resampler.h" #include "log.h" #include "scaler.h" @@ -190,6 +191,19 @@ Player::pass () cout << "Pass " << *earliest << "\n"; #endif earliest->decoder->pass (); + + if (earliest->decoder->done()) { + shared_ptr ac = dynamic_pointer_cast (earliest->content); + assert (ac); + shared_ptr re = resampler (ac, false); + if (re) { + shared_ptr b = re->flush (); + if (b->frames ()) { + process_audio (earliest, b, ac->audio_length ()); + } + } + } + } break; } @@ -264,6 +278,14 @@ Player::process_audio (weak_ptr weak_piece, shared_ptr content = dynamic_pointer_cast (piece->content); assert (content); + /* Resample */ + if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) { + shared_ptr r = resampler (content, true); + pair, AudioContent::Frame> ro = r->run (audio, frame); + audio = ro.first; + frame = ro.second; + } + /* Remap channels */ shared_ptr dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames())); dcp_mapped->make_silent (); @@ -314,6 +336,7 @@ Player::process_audio (weak_ptr weak_piece, shared_ptr emit (new AudioBuffers (_audio_buffers.channels(), N)); emit->copy_from (&_audio_buffers, N, 0, 0); Audio (emit, _audio_position); + _audio_position = piece->audio_position = _audio_position + _film->audio_frames_to_time (N); /* And remove it from our buffers */ @@ -497,6 +520,23 @@ Player::set_video_container_size (libdcp::Size s) _black_frame->make_black (); } +shared_ptr +Player::resampler (shared_ptr c, bool create) +{ + map, shared_ptr >::iterator i = _resamplers.find (c); + if (i != _resamplers.end ()) { + return i->second; + } + + if (!create) { + return shared_ptr (); + } + + shared_ptr r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels())); + _resamplers[c] = r; + return r; +} + void Player::emit_black () { diff --git a/src/lib/player.h b/src/lib/player.h index 5b1b6936b..cd480dd1a 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -35,6 +35,7 @@ class Playlist; class AudioContent; class Piece; class Image; +class Resampler; /** @class Player * @brief A class which can `play' a Playlist; emitting its audio and video. @@ -89,6 +90,7 @@ private: void flush (); void emit_black (); void emit_silence (OutputAudioFrame); + boost::shared_ptr resampler (boost::shared_ptr, bool); void film_changed (Film::Property); void update_subtitle (); @@ -111,6 +113,7 @@ private: libdcp::Size _video_container_size; boost::shared_ptr _black_frame; + std::map, boost::shared_ptr > _resamplers; struct { boost::weak_ptr piece; diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc index cc5072442..7bc933fd0 100644 --- a/src/lib/resampler.cc +++ b/src/lib/resampler.cc @@ -27,6 +27,8 @@ extern "C" { #include "i18n.h" using std::cout; +using std::pair; +using std::make_pair; using boost::shared_ptr; Resampler::Resampler (int in, int out, int channels) @@ -61,9 +63,11 @@ Resampler::~Resampler () swr_free (&_swr_context); } -shared_ptr -Resampler::run (shared_ptr in) +pair, AudioContent::Frame> +Resampler::run (shared_ptr in, AudioContent::Frame frame) { + AudioContent::Frame const resamp_time = swr_next_pts (_swr_context, frame * _out_rate) / _in_rate; + /* Compute the resampled frames count and add 32 for luck */ int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32; shared_ptr resampled (new AudioBuffers (_channels, max_resampled_frames)); @@ -77,7 +81,7 @@ Resampler::run (shared_ptr in) } resampled->set_frames (resampled_frames); - return resampled; + return make_pair (resampled, resamp_time); } shared_ptr diff --git a/src/lib/resampler.h b/src/lib/resampler.h index 6e282838a..69ec83ba9 100644 --- a/src/lib/resampler.h +++ b/src/lib/resampler.h @@ -22,6 +22,8 @@ extern "C" { #include } +#include "types.h" +#include "audio_content.h" class AudioBuffers; @@ -31,7 +33,7 @@ public: Resampler (int, int, int); ~Resampler (); - boost::shared_ptr run (boost::shared_ptr); + std::pair, AudioContent::Frame> run (boost::shared_ptr, AudioContent::Frame); boost::shared_ptr flush (); private: diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index 09ccf4fbc..1fc1ecaf2 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -35,7 +35,7 @@ using boost::shared_ptr; SndfileDecoder::SndfileDecoder (shared_ptr f, shared_ptr c) : Decoder (f) - , AudioDecoder (f, c) + , AudioDecoder (f) , _sndfile_content (c) , _deinterleave_buffer (0) { diff --git a/test/resampler_test.cc b/test/resampler_test.cc new file mode 100644 index 000000000..1ef69b0c2 --- /dev/null +++ b/test/resampler_test.cc @@ -0,0 +1,55 @@ +/* + Copyright (C) 2013 Carl Hetherington + + 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 +#include "lib/audio_buffers.h" +#include "lib/resampler.h" + +using std::pair; +using std::cout; +using boost::shared_ptr; + +static void +resampler_test_one (int from, int to) +{ + Resampler resamp (from, to, 1); + + int total_out = 0; + + /* 3 hours */ + int64_t const N = from * 60 * 60 * 3; + + for (int64_t i = 0; i < N; i += 1000) { + shared_ptr a (new AudioBuffers (1, 1000)); + a->make_silent (); + pair, AudioContent::Frame> r = resamp.run (a, i); + BOOST_CHECK_EQUAL (r.second, total_out); + total_out += r.first->frames (); + } +} + +/** Check that the timings that come back from the resampler correspond + to the number of samples it generates. +*/ +BOOST_AUTO_TEST_CASE (resampler_test) +{ + resampler_test_one (44100, 48000); + resampler_test_one (44100, 46080); + resampler_test_one (44100, 50000); +} diff --git a/test/wscript b/test/wscript index f7e7180c8..fef1584f3 100644 --- a/test/wscript +++ b/test/wscript @@ -16,6 +16,7 @@ def build(bld): obj.use = 'libdcpomatic' obj.source = """ test.cc + resampler_test.cc ffmpeg_audio_test.cc threed_test.cc play_test.cc