Try again to sort out the audio padding / alignment.
authorCarl Hetherington <cth@carlh.net>
Mon, 22 Oct 2012 20:04:50 +0000 (21:04 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 22 Oct 2012 20:04:50 +0000 (21:04 +0100)
src/lib/decoder.cc
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/film_state.cc
src/lib/film_state.h
src/lib/j2k_wav_encoder.cc
src/lib/j2k_wav_encoder.h
src/lib/util.cc
src/lib/util.h
test/test.cc

index 65e5ff7225ccb1e913ed54c6d2366183121d600a..c0ad85da3523ca3effa15a599a8024ae55e17e38 100644 (file)
@@ -112,7 +112,7 @@ Decoder::process_end ()
           in to get it to the right length.
        */
 
-       int64_t const video_length_in_audio_frames = ((int64_t) _fs->dcp_length() * _fs->target_sample_rate() / _fs->frames_per_second());
+       int64_t const video_length_in_audio_frames = ((int64_t) _fs->dcp_length() * _fs->audio_sample_rate() / _fs->frames_per_second());
        int64_t const audio_short_by_frames = video_length_in_audio_frames - _audio_frames_processed;
 
        _log->log (
@@ -266,7 +266,7 @@ Decoder::emit_audio (uint8_t* data, int size)
        }
 
        /* Update the number of audio frames we've pushed to the encoder */
-       _audio_frames_processed += frames;
+       _audio_frames_processed += audio->frames ();
 
        Audio (audio);
 }
index c74fee008458a53914daaf38ea4244f4166d5d31..5eb32a0147f14e2d277474f6fe46c8fbbaf650ee 100644 (file)
@@ -66,8 +66,6 @@ FFmpegDecoder::FFmpegDecoder (boost::shared_ptr<const FilmState> s, boost::share
        , _audio_codec (0)
        , _subtitle_codec_context (0)
        , _subtitle_codec (0)
-       , _first_video_pts (-1)
-       , _first_audio_pts (-1)
 {
        setup_general ();
        setup_video ();
@@ -213,6 +211,7 @@ bool
 FFmpegDecoder::do_pass ()
 {
        int r = av_read_frame (_format_context, &_packet);
+       
        if (r < 0) {
                if (r != AVERROR_EOF) {
                        throw DecodeError ("error on av_read_frame");
@@ -245,10 +244,12 @@ FFmpegDecoder::do_pass ()
                return true;
        }
 
+       double const pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base) * _packet.pts;
+       
        if (_packet.stream_index == _video_stream) {
 
-               if (_first_video_pts == -1) {
-                       _first_video_pts = _packet.pts;
+               if (!_first_video) {
+                       _first_video = pts_seconds;
                }
                
                int frame_finished;
@@ -256,39 +257,33 @@ FFmpegDecoder::do_pass ()
                        process_video (_frame);
                }
 
-       } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio && (_first_video_pts != -1 && _packet.pts > _first_video_pts)) {
+       } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio && _first_video && _first_video.get() <= pts_seconds) {
 
-               /* Note: We only decode audio if we've had our first video packet through, and if this
-                  packet comes after it.  Until then it is thrown away.
+               /* Note: We only decode audio if we've had our first video packet through, and if it
+                  was before this packet.  Until then audio is thrown away.
                */
-
-               if (_first_audio_pts == -1) {
-                       _first_audio_pts = _packet.pts;
-
+               
+               if (!_first_audio) {
+                       _first_audio = pts_seconds;
+                       
                        /* This is our first audio packet, and if we've arrived here we must have had our
                           first video packet.  Push some silence to make up the gap between our first
                           video packet and our first audio.
                        */
                        
-                       AVStream* v = _format_context->streams[_video_stream];
-                       AVStream* a = _format_context->streams[_audio_stream];
+                       /* frames of silence that we must push */
+                       int const s = rint ((_first_audio.get() - _first_video.get()) * audio_sample_rate ());
                        
-                       assert (v->time_base.num == a->time_base.num);
-                       assert (v->time_base.den == a->time_base.den);
-
-                       /* samples of silence that we must push */
-                       int const s = rint (av_q2d (v->time_base) * (_first_audio_pts - _first_video_pts) * audio_sample_rate ());
-
                        _log->log (
                                String::compose (
-                                       "First video at %1, first audio at %2, pushing %3 samples of silence",
-                                       _first_video_pts, _first_audio_pts, s
+                                       "First video at %1, first audio at %2, pushing %3 frames of silence for %4 channels (%5 bytes per sample)",
+                                       _first_video.get(), _first_audio.get(), s, audio_channels(), bytes_per_audio_sample()
                                        )
                                );
-
+                       
                        /* hence bytes */
                        int const b = s * audio_channels() * bytes_per_audio_sample();
-
+                       
                        /* XXX: this assumes that it won't be too much, and there are shaky assumptions
                           that all sound representations are silent with memset()ed zero data.
                        */
@@ -296,7 +291,7 @@ FFmpegDecoder::do_pass ()
                        memset (silence, 0, b);
                        process_audio (silence, b);
                }
-
+               
                avcodec_get_frame_defaults (_frame);
                
                int frame_finished;
@@ -304,11 +299,11 @@ FFmpegDecoder::do_pass ()
                        int const data_size = av_samples_get_buffer_size (
                                0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
                                );
-
+                       
                        assert (_audio_codec_context->channels == _fs->audio_channels());
                        process_audio (_frame->data[0], data_size);
                }
-
+                       
        } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles) {
 
                int got_subtitle;
index ac4cd6fcfa1dae4ad18258bad2e349c5186abc91..29e06c7d41244ba4a8e7d5a6bdd5385813a94e3a 100644 (file)
@@ -25,6 +25,7 @@
 #include <string>
 #include <stdint.h>
 #include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
 extern "C" {
 #include <libavcodec/avcodec.h>
 #include <libpostproc/postprocess.h>
@@ -105,6 +106,6 @@ private:
 
        AVPacket _packet;
 
-       int64_t _first_video_pts;
-       int64_t _first_audio_pts;
+       boost::optional<double> _first_video;
+       boost::optional<double> _first_audio;
 };
index 283830f59703b39b287feb843f1f4c12ad53040b..0d92a16eec38c8421d948e5273dc05a6c45840cd 100644 (file)
@@ -348,8 +348,9 @@ FilmState::content_type () const
        return VIDEO;
 }
 
+/** @return The sampling rate that we will resample the audio to */
 int
-FilmState::target_sample_rate () const
+FilmState::target_audio_sample_rate () const
 {
        /* Resample to a DCI-approved sample rate */
        double t = dcp_audio_sample_rate (_audio_sample_rate);
index 8940d0e5df19206b829ae1e15e476e9ce9fa4b9d..14a8f7eb5e19b763786d60bf9a9fede0223dfcbe 100644 (file)
@@ -130,7 +130,7 @@ public:
        std::string thumb_base (int) const;
        int thumb_frame (int) const;
 
-       int target_sample_rate () const;
+       int target_audio_sample_rate () const;
        
        void write_metadata () const;
        void read_metadata ();
index 8747bb7ad8fcad2a0d7e2d40130bd34259473e11..c0c785d118d8254be4eb3489bca2806ca8f9a230 100644 (file)
@@ -49,6 +49,7 @@ J2KWAVEncoder::J2KWAVEncoder (shared_ptr<const FilmState> s, shared_ptr<const Op
 #ifdef HAVE_SWRESAMPLE   
        , _swr_context (0)
 #endif   
+       , _audio_frames_written (0)
        , _process_end (false)
 {
        /* Create sound output files with .tmp suffixes; we will rename
@@ -218,11 +219,11 @@ J2KWAVEncoder::encoder_thread (ServerDescription* server)
 void
 J2KWAVEncoder::process_begin (int64_t audio_channel_layout)
 {
-       if (_fs->audio_sample_rate() != _fs->target_sample_rate()) {
+       if (_fs->audio_sample_rate() != _fs->target_audio_sample_rate()) {
 #ifdef HAVE_SWRESAMPLE
 
                stringstream s;
-               s << "Will resample audio from " << _fs->audio_sample_rate() << " to " << _fs->target_sample_rate();
+               s << "Will resample audio from " << _fs->audio_sample_rate() << " to " << _fs->target_audio_sample_rate();
                _log->log (s.str ());
 
                /* We will be using planar float data when we call the resampler */
@@ -230,7 +231,7 @@ J2KWAVEncoder::process_begin (int64_t audio_channel_layout)
                        0,
                        audio_channel_layout,
                        AV_SAMPLE_FMT_FLTP,
-                       _fs->target_sample_rate(),
+                       _fs->target_audio_sample_rate(),
                        audio_channel_layout,
                        AV_SAMPLE_FMT_FLTP,
                        _fs->audio_sample_rate(),
@@ -321,7 +322,13 @@ J2KWAVEncoder::process_end ()
 
                swr_free (&_swr_context);
        }
-#endif 
+#endif
+
+       int const dcp_sr = dcp_audio_sample_rate (_fs->audio_sample_rate ());
+       int64_t const extra_audio_frames = dcp_sr - (_audio_frames_written % dcp_sr);
+       shared_ptr<AudioBuffers> silence (new AudioBuffers (_fs->audio_channels(), extra_audio_frames));
+       silence->make_silent ();
+       write_audio (silence);
        
        close_sound_files ();
 
@@ -344,7 +351,7 @@ J2KWAVEncoder::process_audio (shared_ptr<const AudioBuffers> audio)
        if (_swr_context) {
 
                /* Compute the resampled frames count and add 32 for luck */
-               int const max_resampled_frames = ceil (audio->frames() * _fs->target_sample_rate() / _fs->audio_sample_rate()) + 32;
+               int const max_resampled_frames = ceil (audio->frames() * _fs->target_audio_sample_rate() / _fs->audio_sample_rate()) + 32;
 
                resampled.reset (new AudioBuffers (_fs->audio_channels(), max_resampled_frames));
 
@@ -368,10 +375,12 @@ J2KWAVEncoder::process_audio (shared_ptr<const AudioBuffers> audio)
 }
 
 void
-J2KWAVEncoder::write_audio (shared_ptr<const AudioBuffers> audio) const
+J2KWAVEncoder::write_audio (shared_ptr<const AudioBuffers> audio)
 {
        for (int i = 0; i < _fs->audio_channels(); ++i) {
                sf_write_float (_sound_files[i], audio->data(i), audio->frames());
        }
+
+       _audio_frames_written += audio->frames ();
 }
 
index 6733221deca3b47a9f785ccc312ba3f5cab6f194..69d445b09c62cb73d85f47c6e32a1672363a686c 100644 (file)
@@ -57,7 +57,7 @@ public:
 
 private:
 
-       void write_audio (boost::shared_ptr<const AudioBuffers> audio) const;
+       void write_audio (boost::shared_ptr<const AudioBuffers> audio);
        void encoder_thread (ServerDescription *);
        void close_sound_files ();
        void terminate_worker_threads ();
@@ -67,6 +67,7 @@ private:
 #endif 
 
        std::vector<SNDFILE*> _sound_files;
+       int64_t _audio_frames_written;
 
        bool _process_end;
        std::list<boost::shared_ptr<DCPVideoFrame> > _queue;
index 6221b8b62c6aacb0a2ee164595678a284ffdc8bf..81dc72b3d49c82cb5af9a75d5ab7832551884d05 100644 (file)
@@ -734,6 +734,16 @@ AudioBuffers::set_frames (int f)
        _frames = f;
 }
 
+void
+AudioBuffers::make_silent ()
+{
+       for (int i = 0; i < _channels; ++i) {
+               for (int j = 0; j < _frames; ++j) {
+                       _data[i][j] = 0;
+               }
+       }
+}
+
 void
 ensure_ui_thread ()
 {
index 2265dfe707520e2c3d43813c2bab6f9ea47e801a..1d9984d9d74f5513db5089013a72f2a111786222 100644 (file)
@@ -222,6 +222,8 @@ public:
 
        void set_frames (int f);
 
+       void make_silent ();
+
 private:
        /* no copy construction */
        AudioBuffers (AudioBuffers const &);
index 5aed18c523043512729a14533c85fbb9ccc56350..569ea33e222b4848b45c732afd12a37dc527b166 100644 (file)
@@ -406,21 +406,21 @@ BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
        fs.set_frames_per_second (24);
 
        fs.set_audio_sample_rate (48000);
-       BOOST_CHECK_EQUAL (fs.target_sample_rate(), 48000);
+       BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 48000);
 
        fs.set_audio_sample_rate (44100);
-       BOOST_CHECK_EQUAL (fs.target_sample_rate(), 48000);
+       BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 48000);
 
        fs.set_audio_sample_rate (80000);
-       BOOST_CHECK_EQUAL (fs.target_sample_rate(), 96000);
+       BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 96000);
 
        fs.set_frames_per_second (23.976);
        fs.set_audio_sample_rate (48000);
-       BOOST_CHECK_EQUAL (fs.target_sample_rate(), 47952);
+       BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 47952);
 
        fs.set_frames_per_second (29.97);
        fs.set_audio_sample_rate (48000);
-       BOOST_CHECK_EQUAL (fs.target_sample_rate(), 47952);
+       BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 47952);
 }
 
 class TestJob : public Job