Conversion of frame index to FFmpeg time must always use the original or detected...
[dcpomatic.git] / src / lib / ffmpeg_decoder.cc
index 7a5bf8ba832f51a538ea57775147ba20296a3c06..f2d434e3bab1415ce38b170b7ae762a82062fc6f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 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
@@ -46,6 +46,10 @@ extern "C" {
 
 #include "i18n.h"
 
+#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_WARNING(...) film->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
+
 using std::cout;
 using std::string;
 using std::vector;
@@ -158,7 +162,7 @@ FFmpegDecoder::pass ()
                        av_strerror (r, buf, sizeof(buf));
                        shared_ptr<const Film> film = _film.lock ();
                        assert (film);
-                       film->log()->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r));
+                       LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), buf, r);
                }
 
                flush ();
@@ -330,7 +334,7 @@ FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
        }
 
        /* Initial seek time in the stream's timebase */
-       int64_t const initial_vt = ((initial / _ffmpeg_content->video_frame_rate()) - _pts_offset) / time_base;
+       int64_t const initial_vt = ((initial / _ffmpeg_content->original_video_frame_rate()) - _pts_offset) / time_base;
 
        av_seek_frame (_format_context, _video_stream, initial_vt, AVSEEK_FLAG_BACKWARD);
 
@@ -356,7 +360,7 @@ FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
                return;
        }
 
-       while (1) {
+       while (true) {
                int r = av_read_frame (_format_context, &_packet);
                if (r < 0) {
                        return;
@@ -371,7 +375,7 @@ FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
                r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
                if (r >= 0 && finished) {
                        _video_position = rint (
-                               (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * _ffmpeg_content->video_frame_rate()
+                               (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * _ffmpeg_content->original_video_frame_rate()
                                );
 
                        if (_video_position >= (frame - 1)) {
@@ -382,6 +386,11 @@ FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
                
                av_free_packet (&_packet);
        }
+
+       /* _video_position should be the next thing to be emitted, which will the one after the thing
+          we just saw.
+       */
+       _video_position++;
 }
 
 void
@@ -400,7 +409,7 @@ FFmpegDecoder::decode_audio_packet ()
                if (decode_result < 0) {
                        shared_ptr<const Film> film = _film.lock ();
                        assert (film);
-                       film->log()->log (String::compose ("avcodec_decode_audio4 failed (%1)", decode_result));
+                       LOG_ERROR ("avcodec_decode_audio4 failed (%1)", decode_result);
                        return;
                }
 
@@ -413,15 +422,18 @@ FFmpegDecoder::decode_audio_packet ()
 
                                if (pts > 0) {
                                        /* Emit some silence */
-                                       shared_ptr<AudioBuffers> silence (
-                                               new AudioBuffers (
-                                                       _ffmpeg_content->audio_channels(),
-                                                       pts * _ffmpeg_content->content_audio_frame_rate()
-                                                       )
-                                               );
+                                       int64_t frames = pts * _ffmpeg_content->content_audio_frame_rate ();
+                                       while (frames > 0) {
+                                               int64_t const this_time = min (frames, (int64_t) _ffmpeg_content->content_audio_frame_rate() / 2);
+                                               
+                                               shared_ptr<AudioBuffers> silence (
+                                                       new AudioBuffers (_ffmpeg_content->audio_channels(), this_time)
+                                                       );
                                        
-                                       silence->make_silent ();
-                                       audio (silence, _audio_position);
+                                               silence->make_silent ();
+                                               audio (silence, _audio_position);
+                                               frames -= this_time;
+                                       }
                                }
                        }
                        
@@ -461,13 +473,16 @@ FFmpegDecoder::decode_video_packet ()
                graph.reset (new FilterGraph (_ffmpeg_content, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
                _filter_graphs.push_back (graph);
 
-               film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
+               LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
        } else {
                graph = *i;
        }
 
        list<pair<shared_ptr<Image>, int64_t> > images = graph->process (_frame);
 
+       shared_ptr<const Film> film = _film.lock ();
+       assert (film);
+
        for (list<pair<shared_ptr<Image>, int64_t> >::iterator i = images.begin(); i != images.end(); ++i) {
 
                shared_ptr<Image> image = i->first;
@@ -480,12 +495,12 @@ FFmpegDecoder::decode_video_packet ()
                                /* We just did a seek, so disable any attempts to correct for where we
                                   are / should be.
                                */
-                               _video_position = rint (pts * _ffmpeg_content->video_frame_rate ());
+                               _video_position = rint (pts * _ffmpeg_content->original_video_frame_rate ());
                                _just_sought = false;
                        }
 
-                       double const next = _video_position / _ffmpeg_content->video_frame_rate();
-                       double const one_frame = 1 / _ffmpeg_content->video_frame_rate ();
+                       double const next = _video_position / _ffmpeg_content->original_video_frame_rate();
+                       double const one_frame = 1 / _ffmpeg_content->original_video_frame_rate ();
                        double delta = pts - next;
 
                        while (delta > one_frame) {
@@ -502,20 +517,21 @@ FFmpegDecoder::decode_video_packet ()
                                                )
                                        );
                                
+                               shared_ptr<const Film> film = _film.lock ();
+                               assert (film);
+
                                black->make_black ();
-                               video (shared_ptr<ImageProxy> (new RawImageProxy (image)), false, _video_position);
+                               video (shared_ptr<ImageProxy> (new RawImageProxy (image, film->log())), false, _video_position);
                                delta -= one_frame;
                        }
 
                        if (delta > -one_frame) {
                                /* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */
-                               video (shared_ptr<ImageProxy> (new RawImageProxy (image)), false, _video_position);
+                               video (shared_ptr<ImageProxy> (new RawImageProxy (image, film->log())), false, _video_position);
                        }
                                
                } else {
-                       shared_ptr<const Film> film = _film.lock ();
-                       assert (film);
-                       film->log()->log ("Dropping frame without PTS");
+                       LOG_WARNING ("Dropping frame without PTS");
                }
        }