Avoid random black gaps between bits of content A and B when
[dcpomatic.git] / src / lib / player.cc
index 6e6cd75d7d774b1ef3f3d1c3388ed507fd2b2bcf..bdfa51528deb305c4b72e09b457490012e7f50bb 100644 (file)
@@ -48,6 +48,7 @@
 #include "image_decoder.h"
 #include "compose.hpp"
 #include "shuffler.h"
+#include "timer.h"
 #include <dcp/reel.h>
 #include <dcp/reel_sound_asset.h>
 #include <dcp/reel_subtitle_asset.h>
@@ -75,6 +76,7 @@ using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
 using boost::optional;
 using boost::scoped_ptr;
+using namespace dcpomatic;
 
 int const PlayerProperty::VIDEO_CONTAINER_SIZE = 700;
 int const PlayerProperty::PLAYLIST = 701;
@@ -136,6 +138,7 @@ have_audio (shared_ptr<Piece> piece)
 void
 Player::setup_pieces_unlocked ()
 {
+       list<shared_ptr<Piece> > old_pieces = _pieces;
        _pieces.clear ();
 
        delete _shuffler;
@@ -153,7 +156,15 @@ Player::setup_pieces_unlocked ()
                        continue;
                }
 
-               shared_ptr<Decoder> decoder = decoder_factory (_film, i, _fast);
+               shared_ptr<Decoder> old_decoder;
+               BOOST_FOREACH (shared_ptr<Piece> j, old_pieces) {
+                       if (j->content == i) {
+                               old_decoder = j->decoder;
+                               break;
+                       }
+               }
+
+               shared_ptr<Decoder> decoder = decoder_factory (_film, i, _fast, old_decoder);
                FrameRateChange frc (_film, i);
 
                if (!decoder) {
@@ -331,6 +342,7 @@ Player::black_player_video_frame (Eyes eyes) const
                        eyes,
                        PART_WHOLE,
                        PresetColourConversion::all().front().conversion,
+                       VIDEO_RANGE_FULL,
                        boost::weak_ptr<Content>(),
                        boost::optional<Frame>()
                )
@@ -467,11 +479,11 @@ static void
 maybe_add_asset (list<ReferencedReelAsset>& a, shared_ptr<dcp::ReelAsset> r, Frame reel_trim_start, Frame reel_trim_end, DCPTime from, int const ffr)
 {
        DCPOMATIC_ASSERT (r);
-       r->set_entry_point (r->entry_point() + reel_trim_start);
-       r->set_duration (r->duration() - reel_trim_start - reel_trim_end);
-       if (r->duration() > 0) {
+       r->set_entry_point (r->entry_point().get_value_or(0) + reel_trim_start);
+       r->set_duration (r->actual_duration() - reel_trim_start - reel_trim_end);
+       if (r->actual_duration() > 0) {
                a.push_back (
-                       ReferencedReelAsset(r, DCPTimePeriod(from, from + DCPTime::from_frames(r->duration(), ffr)))
+                       ReferencedReelAsset(r, DCPTimePeriod(from, from + DCPTime::from_frames(r->actual_duration(), ffr)))
                        );
        }
 }
@@ -508,13 +520,13 @@ Player::get_reel_assets ()
                int64_t offset_from_end = 0;
                BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
                        /* Assume that main picture duration is the length of the reel */
-                       offset_from_end += k->main_picture()->duration();
+                       offset_from_end += k->main_picture()->actual_duration();
                }
 
                BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
 
                        /* Assume that main picture duration is the length of the reel */
-                       int64_t const reel_duration = k->main_picture()->duration();
+                       int64_t const reel_duration = k->main_picture()->actual_duration();
 
                        /* See doc/design/trim_reels.svg */
                        Frame const reel_trim_start = min(reel_duration, max(int64_t(0), trim_start - offset_from_start));
@@ -613,8 +625,18 @@ Player::pass ()
 
        switch (which) {
        case CONTENT:
+       {
                earliest_content->done = earliest_content->decoder->pass ();
+               shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent>(earliest_content->content);
+               if (dcp && !_play_referenced && dcp->reference_audio()) {
+                       /* We are skipping some referenced DCP audio content, so we need to update _last_audio_time
+                          to `hide' the fact that no audio was emitted during the referenced DCP (though
+                          we need to behave as though it was).
+                       */
+                       _last_audio_time = dcp->end (_film);
+               }
                break;
+       }
        case BLACK:
                emit_video (black_player_video_frame(EYES_BOTH), _black.position());
                _black.set_position (_black.position() + one_video_frame());
@@ -815,6 +837,7 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                        video.eyes,
                        video.part,
                        piece->content->video->colour_conversion(),
+                       piece->content->video->range(),
                        piece->content,
                        video.frame
                        )
@@ -842,10 +865,12 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
        shared_ptr<AudioContent> content = piece->content->audio;
        DCPOMATIC_ASSERT (content);
 
+       int const rfr = content->resampled_frame_rate (_film);
+
        /* Compute time in the DCP */
        DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame);
        /* And the end of this block in the DCP */
-       DCPTime end = time + DCPTime::from_frames(content_audio.audio->frames(), content->resampled_frame_rate(_film));
+       DCPTime end = time + DCPTime::from_frames(content_audio.audio->frames(), rfr);
 
        /* Remove anything that comes before the start or after the end of the content */
        if (time < piece->content->position()) {
@@ -860,7 +885,7 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
                /* Discard it all */
                return;
        } else if (end > piece->content->end(_film)) {
-               Frame const remaining_frames = DCPTime(piece->content->end(_film) - time).frames_round(_film->audio_frame_rate());
+               Frame const remaining_frames = DCPTime(piece->content->end(_film) - time).frames_round(rfr);
                if (remaining_frames == 0) {
                        return;
                }
@@ -1029,8 +1054,12 @@ Player::seek (DCPTime time, bool accurate)
 
        BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
                if (time < i->content->position()) {
-                       /* Before; seek to the start of the content */
-                       i->decoder->seek (dcp_to_content_time (i, i->content->position()), accurate);
+                       /* Before; seek to the start of the content.  Even if this request is for an inaccurate seek
+                          we must seek this (following) content accurately, otherwise when we come to the end of the current
+                          content we may not start right at the beginning of the next, causing a gap (if the next content has
+                          been trimmed to a point between keyframes, or something).
+                       */
+                       i->decoder->seek (dcp_to_content_time (i, i->content->position()), true);
                        i->done = false;
                } else if (i->content->position() <= time && time < i->content->end(_film)) {
                        /* During; seek to position */