Hopefully much cleaner handling of PTS changes under resample.
authorCarl Hetherington <cth@carlh.net>
Thu, 25 Jul 2013 21:04:20 +0000 (22:04 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 25 Jul 2013 21:04:20 +0000 (22:04 +0100)
src/lib/audio_decoder.cc
src/lib/audio_decoder.h
src/lib/ffmpeg_decoder.cc
src/lib/player.cc
src/lib/player.h
src/lib/resampler.cc
src/lib/resampler.h
src/lib/sndfile_decoder.cc
test/resampler_test.cc [new file with mode: 0644]
test/wscript

index 846cdf59501a44e36fc37e98df48d884d6674945..1f5868583675314df9158517d6159c2c13bce7ef 100644 (file)
@@ -32,36 +32,16 @@ using std::cout;
 using boost::optional;
 using boost::shared_ptr;
 
-AudioDecoder::AudioDecoder (shared_ptr<const Film> film, shared_ptr<const AudioContent> content)
+AudioDecoder::AudioDecoder (shared_ptr<const Film> 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<const AudioBuffers> data, AudioContent::Frame)
-{
-       if (_resampler) {
-               data = _resampler->run (data);
-       } 
 
-       Audio (data, _audio_position);
-       _audio_position += data->frames ();
 }
 
 void
-AudioDecoder::flush ()
+AudioDecoder::audio (shared_ptr<const AudioBuffers> data, AudioContent::Frame frame)
 {
-       if (_resampler) {
-               _resampler->flush ();
-       }
+       Audio (data, frame);
+       _audio_position = frame + data->frames ();
 }
index e2d28d707908fd4d5b90eeeac6a73fc528c1054f..2ad53da8bf42684e544e753bc6b0ca4708c72d19 100644 (file)
@@ -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<const Film>, boost::shared_ptr<const AudioContent>);
+       AudioDecoder (boost::shared_ptr<const Film>);
 
        /** Emitted when some audio data is ready */
        boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame)> Audio;
 
 protected:
 
-       void flush ();
-
        void audio (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
-       /** Frame index of next emission (post resampling) */
        AudioContent::Frame _audio_position;
-       boost::shared_ptr<Resampler> _resampler;
 };
 
 #endif
index e2e5b9d643cc5d565e00a98307aa6f507ee6906a..58745598a9fae69969409e40d3777424ef18edf1 100644 (file)
@@ -60,7 +60,7 @@ using libdcp::Size;
 FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> 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 ();
index 1ab164d867f216f80c44bb0dd8c4e5c24fd9b0ad..620efbd0dc30cb17bcde648f75dadfbf71fae5c1 100644 (file)
@@ -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<AudioContent> ac = dynamic_pointer_cast<AudioContent> (earliest->content);
+                               assert (ac);
+                               shared_ptr<Resampler> re = resampler (ac, false);
+                               if (re) {
+                                       shared_ptr<const AudioBuffers> b = re->flush ();
+                                       if (b->frames ()) {
+                                               process_audio (earliest, b, ac->audio_length ());
+                                       }
+                               }
+                       }
+       
                }
                break;
        }
@@ -264,6 +278,14 @@ Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers
        shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
        assert (content);
 
+       /* Resample */
+       if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
+               shared_ptr<Resampler> r = resampler (content, true);
+               pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> ro = r->run (audio, frame);
+               audio = ro.first;
+               frame = ro.second;
+       }
+       
        /* Remap channels */
        shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
        dcp_mapped->make_silent ();
@@ -314,6 +336,7 @@ Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers
                shared_ptr<AudioBuffers> 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<Resampler>
+Player::resampler (shared_ptr<AudioContent> c, bool create)
+{
+       map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
+       if (i != _resamplers.end ()) {
+               return i->second;
+       }
+
+       if (!create) {
+               return shared_ptr<Resampler> ();
+       }
+       
+       shared_ptr<Resampler> 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 ()
 {
index 5b1b6936bc95a41ca548fef98f32cc146da9a628..cd480dd1a78c659a6a16479e6d95759d4d0858a3 100644 (file)
@@ -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> resampler (boost::shared_ptr<AudioContent>, bool);
        void film_changed (Film::Property);
        void update_subtitle ();
 
@@ -111,6 +113,7 @@ private:
 
        libdcp::Size _video_container_size;
        boost::shared_ptr<Image> _black_frame;
+       std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
 
        struct {
                boost::weak_ptr<Piece> piece;
index cc50724420922440c7e7ba0cb90a6d1d04086524..7bc933fd01b2536da1b8d7f0e70c6a9264d6a6f0 100644 (file)
@@ -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<const AudioBuffers>
-Resampler::run (shared_ptr<const AudioBuffers> in)
+pair<shared_ptr<const AudioBuffers>, AudioContent::Frame>
+Resampler::run (shared_ptr<const AudioBuffers> 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<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames));
@@ -77,7 +81,7 @@ Resampler::run (shared_ptr<const AudioBuffers> in)
        }
        
        resampled->set_frames (resampled_frames);
-       return resampled;
+       return make_pair (resampled, resamp_time);
 }      
 
 shared_ptr<const AudioBuffers>
index 6e282838a17c5f98e8ae707d0059ab74e1c1bfe4..69ec83ba93047f3493cd23005487b7845580944e 100644 (file)
@@ -22,6 +22,8 @@
 extern "C" {
 #include <libswresample/swresample.h>
 }
+#include "types.h"
+#include "audio_content.h"
 
 class AudioBuffers;
 
@@ -31,7 +33,7 @@ public:
        Resampler (int, int, int);
        ~Resampler ();
 
-       boost::shared_ptr<const AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
+       std::pair<boost::shared_ptr<const AudioBuffers>, AudioContent::Frame> run (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
        boost::shared_ptr<const AudioBuffers> flush ();
 
 private:       
index 09ccf4fbc1fd9ba5d2bcf3eac9d3aa2ae135b45b..1fc1ecaf2cc66c1cbcb650f0af9cad0f0d2a82fc 100644 (file)
@@ -35,7 +35,7 @@ using boost::shared_ptr;
 
 SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> 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 (file)
index 0000000..1ef69b0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+    Copyright (C) 2013 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 <boost/test/unit_test.hpp>
+#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<AudioBuffers> a (new AudioBuffers (1, 1000));
+               a->make_silent ();
+               pair<shared_ptr<const AudioBuffers>, 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);
+}
index f7e7180c8d5b29895b8377806876afa3cdaefb85..fef1584f32c49e01045594bf44c54e66b16e2ab7 100644 (file)
@@ -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