Merge FilmState / Film.
[dcpomatic.git] / src / lib / ffmpeg_decoder.cc
index c74fee008458a53914daaf38ea4244f4166d5d31..30701797affc9f3f74837378b79735d19b66e08a 100644 (file)
@@ -41,7 +41,6 @@ extern "C" {
 #include "transcoder.h"
 #include "job.h"
 #include "filter.h"
-#include "film_state.h"
 #include "options.h"
 #include "exceptions.h"
 #include "image.h"
@@ -53,8 +52,8 @@ extern "C" {
 using namespace std;
 using namespace boost;
 
-FFmpegDecoder::FFmpegDecoder (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Job* j, Log* l, bool minimal, bool ignore_length)
-       : Decoder (s, o, j, l, minimal, ignore_length)
+FFmpegDecoder::FFmpegDecoder (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, Job* j, bool minimal, bool ignore_length)
+       : Decoder (f, o, j, minimal, ignore_length)
        , _format_context (0)
        , _video_stream (-1)
        , _audio_stream (-1)
@@ -66,8 +65,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 ();
@@ -100,8 +97,8 @@ FFmpegDecoder::setup_general ()
        
        av_register_all ();
 
-       if ((r = avformat_open_input (&_format_context, _fs->content_path().c_str(), 0, 0)) != 0) {
-               throw OpenFileError (_fs->content_path ());
+       if ((r = avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0)) != 0) {
+               throw OpenFileError (_film->content_path ());
        }
 
        if (avformat_find_stream_info (_format_context, 0) < 0) {
@@ -129,12 +126,12 @@ FFmpegDecoder::setup_general ()
 
        /* Now override audio and subtitle streams with those from the Film, if it has any */
 
-       if (_fs->audio_stream_index() != -1) {
-               _audio_stream = _fs->audio_stream().id();
+       if (_film->audio_stream_index() != -1) {
+               _audio_stream = _film->audio_stream().id();
        }
 
-       if (_fs->subtitle_stream_index() != -1) {
-               _subtitle_stream = _fs->subtitle_stream().id ();
+       if (_film->subtitle_stream_index() != -1) {
+               _subtitle_stream = _film->subtitle_stream().id ();
        }
 
        if (_video_stream < 0) {
@@ -213,6 +210,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");
@@ -237,7 +235,7 @@ FFmpegDecoder::do_pass ()
                                        0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
                                        );
 
-                               assert (_audio_codec_context->channels == _fs->audio_channels());
+                               assert (_audio_codec_context->channels == _film->audio_channels());
                                process_audio (_frame->data[0], data_size);
                        }
                }
@@ -245,10 +243,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 +256,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 +290,7 @@ FFmpegDecoder::do_pass ()
                        memset (silence, 0, b);
                        process_audio (silence, b);
                }
-
+               
                avcodec_get_frame_defaults (_frame);
                
                int frame_finished;
@@ -304,21 +298,23 @@ 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());
+                       
+                       assert (_audio_codec_context->channels == _film->audio_channels());
                        process_audio (_frame->data[0], data_size);
                }
-
-       } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles) {
+                       
+       } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles && _first_video) {
 
                int got_subtitle;
                AVSubtitle sub;
                if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) {
-                       /* I'm not entirely sure why, but sometimes we get an AVSubtitle with
-                          no AVSubtitleRects.
+                       /* Sometimes we get an empty AVSubtitle, which is used by some codecs to
+                          indicate that the previous subtitle should stop.
                        */
                        if (sub.num_rects > 0) {
-                               process_subtitle (shared_ptr<TimedSubtitle> (new TimedSubtitle (sub)));
+                               process_subtitle (shared_ptr<TimedSubtitle> (new TimedSubtitle (sub, _first_video.get())));
+                       } else {
+                               process_subtitle (shared_ptr<TimedSubtitle> ());
                        }
                        avsubtitle_free (&sub);
                }
@@ -328,12 +324,6 @@ FFmpegDecoder::do_pass ()
        return false;
 }
 
-int
-FFmpegDecoder::length_in_frames () const
-{
-       return (_format_context->duration / AV_TIME_BASE) * frames_per_second ();
-}
-
 float
 FFmpegDecoder::frames_per_second () const
 {