Add Piece::reference_dcp_audio().
[dcpomatic.git] / src / lib / player.cc
index 0bdf46f4abe33532f2edc7f14e7ad0f29bcb219f..086cfbbbd7b259d447a5e68d01abe364b5b8d8c6 100644 (file)
@@ -187,8 +187,9 @@ Player::setup_pieces_unlocked ()
 
                shared_ptr<Decoder> old_decoder;
                for (auto j: old_pieces) {
-                       if (j->content == i) {
-                               old_decoder = j->decoder;
+                       auto decoder = j->decoder_for(i);
+                       if (decoder) {
+                               old_decoder = decoder;
                                break;
                        }
                }
@@ -257,26 +258,15 @@ Player::setup_pieces_unlocked ()
                }
        }
 
-       _stream_states.clear ();
-       for (auto i: _pieces) {
-               if (i->content->audio) {
-                       for (auto j: i->content->audio->streams()) {
-                               _stream_states[j] = StreamState (i, i->content->position ());
-                       }
-               }
-       }
-
        for (auto i = _pieces.begin(); i != _pieces.end(); ++i) {
-               if (auto video = (*i)->content->video) {
-                       if (video->use() && video->frame_type() != VideoFrameType::THREE_D_LEFT && video->frame_type() != VideoFrameType::THREE_D_RIGHT) {
-                               /* Look for content later in the content list with in-use video that overlaps this */
-                               auto period = DCPTimePeriod((*i)->content->position(), (*i)->content->end(_film));
-                               auto j = i;
-                               ++j;
-                               for (; j != _pieces.end(); ++j) {
-                                       if ((*j)->content->video && (*j)->content->video->use()) {
-                                               (*i)->ignore_video = DCPTimePeriod((*j)->content->position(), (*j)->content->end(_film)).overlap(period);
-                                       }
+               if ((*i)->use_video() && (*i)->video_frame_type() != VideoFrameType::THREE_D_LEFT && (*i)->video_frame_type() != VideoFrameType::THREE_D_RIGHT) {
+                       /* Look for content later in the content list with in-use video that overlaps this */
+                       auto period = DCPTimePeriod((*i)->position(), (*i)->end(_film));
+                       auto j = i;
+                       ++j;
+                       for (; j != _pieces.end(); ++j) {
+                               if ((*j)->use_video()) {
+                                       (*i)->ignore_video = DCPTimePeriod((*j)->position(), (*j)->end(_film)).overlap(period);
                                }
                        }
                }
@@ -408,69 +398,6 @@ Player::black_player_video_frame (Eyes eyes) const
 }
 
 
-Frame
-Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
-{
-       auto s = t - piece->content->position ();
-       s = min (piece->content->length_after_trim(_film), s);
-       s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
-
-       /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
-          then convert that ContentTime to frames at the content's rate.  However this fails for
-          situations like content at 29.9978733fps, DCP at 30fps.  The accuracy of the Time type is not
-          enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
-
-          Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
-       */
-       return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
-}
-
-
-DCPTime
-Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
-{
-       /* See comment in dcp_to_content_video */
-       auto const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime(piece->content->trim_start(), piece->frc);
-       return d + piece->content->position();
-}
-
-
-Frame
-Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
-{
-       auto s = t - piece->content->position ();
-       s = min (piece->content->length_after_trim(_film), s);
-       /* See notes in dcp_to_content_video */
-       return max (DCPTime(), DCPTime(piece->content->trim_start(), piece->frc) + s).frames_floor(_film->audio_frame_rate());
-}
-
-
-DCPTime
-Player::resampled_audio_to_dcp (shared_ptr<const Piece> piece, Frame f) const
-{
-       /* See comment in dcp_to_content_video */
-       return DCPTime::from_frames (f, _film->audio_frame_rate())
-               - DCPTime (piece->content->trim_start(), piece->frc)
-               + piece->content->position();
-}
-
-
-ContentTime
-Player::dcp_to_content_time (shared_ptr<const Piece> piece, DCPTime t) const
-{
-       auto s = t - piece->content->position ();
-       s = min (piece->content->length_after_trim(_film), s);
-       return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
-}
-
-
-DCPTime
-Player::content_time_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
-{
-       return max (DCPTime(), DCPTime(t - piece->content->trim_start(), piece->frc) + piece->content->position());
-}
-
-
 vector<FontData>
 Player::get_subtitle_fonts ()
 {
@@ -658,8 +585,8 @@ Player::pass ()
                        continue;
                }
 
-               auto const t = content_time_to_dcp (i, max(i->decoder->position(), i->content->trim_start()));
-               if (t > i->content->end(_film)) {
+               auto const t = i->decoder_position ();
+               if (t > i->end(_film)) {
                        i->done = true;
                } else {
 
@@ -699,15 +626,13 @@ Player::pass ()
        switch (which) {
        case CONTENT:
        {
-               LOG_DEBUG_PLAYER ("Calling pass() on %1", earliest_content->content->path(0));
-               earliest_content->done = earliest_content->decoder->pass ();
-               auto dcp = dynamic_pointer_cast<DCPContent>(earliest_content->content);
-               if (dcp && !_play_referenced && dcp->reference_audio()) {
+               earliest_content->pass();
+               if (!_play_referenced && earliest_content->reference_dcp_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);
+                       _last_audio_time = earliest_content->end (_film);
                }
                break;
        }
@@ -753,10 +678,8 @@ Player::pass ()
           of our streams, or the position of the _silent.
        */
        auto pull_to = _playback_length;
-       for (auto const& i: _stream_states) {
-               if (!i.second.piece->done && i.second.last_push_end < pull_to) {
-                       pull_to = i.second.last_push_end;
-               }
+       for (auto i: _pieces) {
+               i->update_pull_to (pull_to);
        }
        if (!_silent.done() && _silent.position() < pull_to) {
                pull_to = _silent.position();
@@ -846,24 +769,24 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                return;
        }
 
-       if (!piece->content->video->use()) {
+       if (!piece->use_video()) {
                return;
        }
 
-       FrameRateChange frc (_film, piece->content);
+       auto frc = piece->frame_rate_change();
        if (frc.skip && (video.frame % 2) == 1) {
                return;
        }
 
        /* Time of the first frame we will emit */
-       DCPTime const time = content_video_to_dcp (piece, video.frame);
+       DCPTime const time = piece->content_video_to_dcp (video.frame);
        LOG_DEBUG_PLAYER("Received video frame %1 at %2", video.frame, to_string(time));
 
        /* Discard if it's before the content's period or the last accurate seek.  We can't discard
           if it's after the content's period here as in that case we still need to fill any gap between
           `now' and the end of the content's period.
        */
-       if (time < piece->content->position() || (_last_video_time && time < *_last_video_time)) {
+       if (time < piece->position() || (_last_video_time && time < *_last_video_time)) {
                return;
        }
 
@@ -874,10 +797,10 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
        /* Fill gaps that we discover now that we have some video which needs to be emitted.
           This is where we need to fill to.
        */
-       DCPTime fill_to = min (time, piece->content->end(_film));
+       DCPTime fill_to = min (time, piece->end(_film));
 
        if (_last_video_time) {
-               DCPTime fill_from = max (*_last_video_time, piece->content->position());
+               DCPTime fill_from = max (*_last_video_time, piece->position());
 
                /* Fill if we have more than half a frame to do */
                if ((fill_to - fill_from) > one_video_frame() / 2) {
@@ -887,7 +810,7 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                                if (fill_to_eyes == Eyes::BOTH) {
                                        fill_to_eyes = Eyes::LEFT;
                                }
-                               if (fill_to == piece->content->end(_film)) {
+                               if (fill_to == piece->end(_film)) {
                                        /* Don't fill after the end of the content */
                                        fill_to_eyes = Eyes::LEFT;
                                }
@@ -923,24 +846,11 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                }
        }
 
-       _last_video[wp] = std::make_shared<PlayerVideo>(
-               video.image,
-               piece->content->video->crop (),
-               piece->content->video->fade (_film, video.frame),
-               scale_for_display(piece->content->video->scaled_size(_film->frame_size()), _video_container_size, _film->frame_size()),
-               _video_container_size,
-               video.eyes,
-               video.part,
-               piece->content->video->colour_conversion(),
-               piece->content->video->range(),
-               piece->content,
-               video.frame,
-               false
-               );
+       _last_video[wp] = piece->player_video (video, _film, _video_container_size);
 
        DCPTime t = time;
        for (int i = 0; i < frc.repeat; ++i) {
-               if (t < piece->content->end(_film)) {
+               if (t < piece->end(_film)) {
                        emit_video (_last_video[wp], t);
                }
                t += one_video_frame ();
@@ -958,32 +868,29 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
                return;
        }
 
-       auto content = piece->content->audio;
-       DCPOMATIC_ASSERT (content);
-
-       int const rfr = content->resampled_frame_rate (_film);
+       int const rfr = piece->resampled_audio_frame_rate (_film);
 
        /* Compute time in the DCP */
-       auto time = resampled_audio_to_dcp (piece, content_audio.frame);
+       auto time = piece->resampled_audio_to_dcp (content_audio.frame, _film);
        LOG_DEBUG_PLAYER("Received audio frame %1 at %2", content_audio.frame, to_string(time));
 
        /* And the end of this block in the DCP */
        auto 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()) {
-               auto cut = discard_audio (content_audio.audio, time, piece->content->position());
+       if (time < piece->position()) {
+               auto cut = discard_audio (content_audio.audio, time, piece->position());
                if (!cut.first) {
                        /* This audio is entirely discarded */
                        return;
                }
                content_audio.audio = cut.first;
                time = cut.second;
-       } else if (time > piece->content->end(_film)) {
+       } else if (time > piece->end(_film)) {
                /* Discard it all */
                return;
-       } else if (end > piece->content->end(_film)) {
-               Frame const remaining_frames = DCPTime(piece->content->end(_film) - time).frames_round(rfr);
+       } else if (end > piece->end(_film)) {
+               Frame const remaining_frames = DCPTime(piece->end(_film) - time).frames_round(rfr);
                if (remaining_frames == 0) {
                        return;
                }
@@ -994,9 +901,9 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
 
        /* Gain */
 
-       if (content->gain() != 0) {
+       if (piece->audio_gain() != 0) {
                auto gain = make_shared<AudioBuffers>(content_audio.audio);
-               gain->apply_gain (content->gain());
+               gain->apply_gain (piece->audio_gain());
                content_audio.audio = gain;
        }
 
@@ -1013,8 +920,7 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
        /* Push */
 
        _audio_merger.push (content_audio.audio, time);
-       DCPOMATIC_ASSERT (_stream_states.find (stream) != _stream_states.end ());
-       _stream_states[stream].last_push_end = time + DCPTime::from_frames (content_audio.audio->frames(), _film->audio_frame_rate());
+       piece->set_last_push_end (stream, time + DCPTime::from_frames(content_audio.audio->frames(), _film->audio_frame_rate()));
 }
 
 
@@ -1051,7 +957,7 @@ Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, C
 
        dcp::Size scaled_size (width, height);
        ps.bitmap.push_back (BitmapText(image->scale(scaled_size, dcp::YUVToRGB::REC601, image->pixel_format(), true, _fast), subtitle.sub.rectangle));
-       DCPTime from (content_time_to_dcp (piece, subtitle.from()));
+       DCPTime from (piece->content_time_to_dcp(subtitle.from()));
 
        _active_texts[static_cast<int>(text->type())].add_from (wc, ps, from);
 }
@@ -1067,9 +973,9 @@ Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, Co
        }
 
        PlayerText ps;
-       DCPTime const from (content_time_to_dcp (piece, subtitle.from()));
+       DCPTime const from (piece->content_time_to_dcp(subtitle.from()));
 
-       if (from > piece->content->end(_film)) {
+       if (from > piece->end(_film)) {
                return;
        }
 
@@ -1119,9 +1025,9 @@ Player::subtitle_stop (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, Conte
                return;
        }
 
-       DCPTime const dcp_to = content_time_to_dcp (piece, to);
+       auto const dcp_to = piece->content_time_to_dcp(to);
 
-       if (dcp_to > piece->content->end(_film)) {
+       if (dcp_to > piece->end(_film)) {
                return;
        }
 
@@ -1161,17 +1067,17 @@ Player::seek (DCPTime time, bool accurate)
        }
 
        for (auto i: _pieces) {
-               if (time < i->content->position()) {
+               if (time < i->position()) {
                        /* 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->decoder->seek (i->dcp_to_content_time(i->position(), _film), true);
                        i->done = false;
-               } else if (i->content->position() <= time && time < i->content->end(_film)) {
+               } else if (i->position() <= time && time < i->end(_film)) {
                        /* During; seek to position */
-                       i->decoder->seek (dcp_to_content_time (i, time), accurate);
+                       i->decoder->seek (i->dcp_to_content_time(time, _film), accurate);
                        i->done = false;
                } else {
                        /* After; this piece is done */
@@ -1335,7 +1241,7 @@ Player::content_time_to_dcp (shared_ptr<Content> content, ContentTime t)
 
        for (auto i: _pieces) {
                if (i->content == content) {
-                       return content_time_to_dcp (i, t);
+                       return i->content_time_to_dcp(t);
                }
        }