Fix _audio_position after seek.
[dcpomatic.git] / src / lib / player.cc
index 9f9e35709c00493aeed109b2d40b8c7ef1ad5965..eb7b177ecd21aacc86bc5c96d89e404a8333f7e1 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include <stdint.h>
+#include <algorithm>
 #include "player.h"
 #include "film.h"
 #include "ffmpeg_decoder.h"
@@ -107,13 +108,37 @@ Player::pass ()
 
        for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
 
-               shared_ptr<Decoded> dec = (*i)->decoder->peek ();
+               DCPTime const offset = (*i)->content->position() - (*i)->content->trim_start();
+               
+               bool done = false;
+               shared_ptr<Decoded> dec;
+               while (!done) {
+                       dec = (*i)->decoder->peek ();
+                       if (!dec) {
+                               /* Decoder has nothing else to give us */
+                               break;
+                       }
+
+                       dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset);
+                       DCPTime const t = dec->dcp_time - offset;
+                       if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) {
+                               /* In the end-trimmed part; decoder has nothing else to give us */
+                               dec.reset ();
+                               done = true;
+                       } else if (t >= (*i)->content->trim_start ()) {
+                               /* Within the un-trimmed part; everything's ok */
+                               done = true;
+                       } else {
+                               /* Within the start-trimmed part; get something else */
+                               (*i)->decoder->consume ();
+                       }
+               }
 
-               if (dec) {
-                       dec->set_dcp_times ((*i)->frc.speed_up, (*i)->content->position());
+               if (!dec) {
+                       continue;
                }
 
-               if (dec && dec->dcp_time < earliest_time) {
+               if (dec->dcp_time < earliest_time) {
                        earliest_piece = *i;
                        earliest_decoded = dec;
                        earliest_time = dec->dcp_time;
@@ -130,7 +155,7 @@ Player::pass ()
        }
 
        if (earliest_audio != TIME_MAX) {
-               TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (earliest_audio);
+               TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (max (int64_t (0), earliest_audio));
                Audio (tb.audio, tb.time);
                /* This assumes that the audio_frames_to_time conversion is exact
                   so that there are no accumulated errors caused by rounding.
@@ -172,11 +197,13 @@ Player::pass ()
                        if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
                                /* We're outside all video content */
                                emit_black ();
+                               _statistics.video.black++;
                        } else {
                                /* We're inside some video; repeat the frame */
                                _last_incoming_video.video->dcp_time = _video_position;
                                emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
                                step_video_position (_last_incoming_video.video);
+                               _statistics.video.repeat++;
                        }
 
                        consume = false;
@@ -185,24 +212,28 @@ Player::pass ()
                        /* We're ok */
                        emit_video (earliest_piece, dv);
                        step_video_position (dv);
+                       _statistics.video.good++;
                } else {
                        /* Too far behind: skip */
+                       _statistics.video.skip++;
                }
 
+               _just_did_inaccurate_seek = false;
+
        } else if (da && _audio) {
 
-               if (_just_did_inaccurate_seek) {
-                       /* Just emit; no subtlety */
-                       emit_audio (earliest_piece, da);
-               } else if (da->dcp_time - _audio_position > margin) {
+               if (da->dcp_time - _audio_position > margin) {
                        /* Too far ahead */
                        emit_silence (da->dcp_time - _audio_position);
                        consume = false;
+                       _statistics.audio.silence += (da->dcp_time - _audio_position);
                } else if (abs (da->dcp_time - _audio_position) < margin) {
                        /* We're ok */
                        emit_audio (earliest_piece, da);
+                       _statistics.audio.good += da->data->frames();
                } else {
                        /* Too far behind: skip */
+                       _statistics.audio.skip += da->data->frames();
                }
                
        } else if (ds && _video) {
@@ -215,8 +246,6 @@ Player::pass ()
                earliest_piece->decoder->consume ();
        }                       
        
-       _just_did_inaccurate_seek = false;
-
        return false;
 }
 
@@ -262,7 +291,7 @@ Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
 
                Position<int> const container_offset (
                        (_video_container_size.width - image_size.width) / 2,
-                       (_video_container_size.height - image_size.width) / 2
+                       (_video_container_size.height - image_size.height) / 2
                        );
 
                pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
@@ -307,10 +336,6 @@ Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
                audio->data = gain;
        }
 
-       if (content->trimmed (audio->dcp_time - content->position ())) {
-               return;
-       }
-
        /* Remap channels */
        shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
        dcp_mapped->make_silent ();
@@ -345,16 +370,16 @@ void
 Player::flush ()
 {
        TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
-       if (tb.audio) {
+       if (_audio && tb.audio) {
                Audio (tb.audio, tb.time);
                _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
        }
 
-       while (_video_position < _audio_position) {
+       while (_video && _video_position < _audio_position) {
                emit_black ();
        }
 
-       while (_audio_position < _video_position) {
+       while (_audio && _audio_position < _video_position) {
                emit_silence (_video_position - _audio_position);
        }
        
@@ -382,7 +407,7 @@ Player::seek (DCPTime t, bool accurate)
                s = min ((*i)->content->length_after_trim(), s);
 
                /* Convert this to the content time */
-               ContentTime ct = (s * (*i)->frc.speed_up) + (*i)->content->trim_start ();
+               ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up;
 
                /* And seek the decoder */
                (*i)->decoder->seek (ct, accurate);
@@ -472,7 +497,8 @@ Player::setup_pieces ()
                        }
                }
 
-               decoder->seek ((*i)->trim_start (), true);
+               ContentTime st = (*i)->trim_start() * frc->speed_up;
+               decoder->seek (st, true);
                
                _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
        }
@@ -696,3 +722,15 @@ PlayerImage::image (AVPixelFormat format, bool aligned)
        return out;
 }
 
+void
+PlayerStatistics::dump (shared_ptr<Log> log) const
+{
+       log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
+       log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
+}
+
+PlayerStatistics const &
+Player::statistics () const
+{
+       return _statistics;
+}