Move some methods into Piece.
[dcpomatic.git] / src / lib / player.cc
index ab05d42ada522c25627cf5c7381bf13a07c649fb..216e68abd8cae901be7d9ff411614a5799d34543 100644 (file)
 
 */
 
+#include "atmos_decoder.h"
 #include "player.h"
 #include "film.h"
 #include "audio_buffers.h"
 #include "content_audio.h"
 #include "dcp_content.h"
+#include "dcpomatic_log.h"
 #include "job.h"
 #include "image.h"
 #include "raw_image_proxy.h"
@@ -83,10 +85,10 @@ int const PlayerProperty::PLAYLIST = 701;
 int const PlayerProperty::FILM_CONTAINER = 702;
 int const PlayerProperty::FILM_VIDEO_FRAME_RATE = 703;
 int const PlayerProperty::DCP_DECODE_REDUCTION = 704;
+int const PlayerProperty::PLAYBACK_LENGTH = 705;
 
-Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist, DCPTime playback_length)
+Player::Player (shared_ptr<const Film> film)
        : _film (film)
-       , _playlist (playlist)
        , _suspended (0)
        , _ignore_video (false)
        , _ignore_audio (false)
@@ -97,14 +99,36 @@ Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist
        , _play_referenced (false)
        , _audio_merger (_film->audio_frame_rate())
        , _shuffler (0)
-       , _playback_length (playback_length)
+{
+       construct ();
+}
+
+Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist_)
+       : _film (film)
+       , _playlist (playlist_)
+       , _suspended (0)
+       , _ignore_video (false)
+       , _ignore_audio (false)
+       , _ignore_text (false)
+       , _always_burn_open_subtitles (false)
+       , _fast (false)
+       , _tolerant (film->tolerant())
+       , _play_referenced (false)
+       , _audio_merger (_film->audio_frame_rate())
+       , _shuffler (0)
+{
+       construct ();
+}
+
+void
+Player::construct ()
 {
        _film_changed_connection = _film->Change.connect (bind (&Player::film_change, this, _1, _2));
        /* The butler must hear about this first, so since we are proxying this through to the butler we must
           be first.
        */
-       _playlist_change_connection = _playlist->Change.connect (bind (&Player::playlist_change, this, _1), boost::signals2::at_front);
-       _playlist_content_change_connection = _playlist->ContentChange.connect (bind(&Player::playlist_content_change, this, _1, _3, _4));
+       _playlist_change_connection = playlist()->Change.connect (bind (&Player::playlist_change, this, _1), boost::signals2::at_front);
+       _playlist_content_change_connection = playlist()->ContentChange.connect (bind(&Player::playlist_content_change, this, _1, _3, _4));
        set_video_container_size (_film->frame_size ());
 
        film_change (CHANGE_TYPE_DONE, Film::AUDIO_PROCESSOR);
@@ -125,10 +149,11 @@ Player::setup_pieces ()
        setup_pieces_unlocked ();
 }
 
+
 bool
 have_video (shared_ptr<const Content> content)
 {
-       return static_cast<bool>(content->video);
+       return static_cast<bool>(content->video) && content->video->use();
 }
 
 bool
@@ -140,6 +165,8 @@ have_audio (shared_ptr<const Content> content)
 void
 Player::setup_pieces_unlocked ()
 {
+       _playback_length = _playlist ? _playlist->length(_film) : _film->length();
+
        list<shared_ptr<Piece> > old_pieces = _pieces;
        _pieces.clear ();
 
@@ -147,7 +174,7 @@ Player::setup_pieces_unlocked ()
        _shuffler = new Shuffler();
        _shuffler->Video.connect(bind(&Player::video, this, _1, _2));
 
-       BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
+       BOOST_FOREACH (shared_ptr<Content> i, playlist()->content()) {
 
                if (!i->paths_valid ()) {
                        continue;
@@ -167,12 +194,9 @@ Player::setup_pieces_unlocked ()
                }
 
                shared_ptr<Decoder> decoder = decoder_factory (_film, i, _fast, _tolerant, old_decoder);
-               FrameRateChange frc (_film, i);
+               DCPOMATIC_ASSERT (decoder);
 
-               if (!decoder) {
-                       /* Not something that we can decode; e.g. Atmos content */
-                       continue;
-               }
+               FrameRateChange frc (_film, i);
 
                if (decoder->video && _ignore_video) {
                        decoder->video->set_ignore (true);
@@ -227,6 +251,10 @@ Player::setup_pieces_unlocked ()
 
                        ++j;
                }
+
+               if (decoder->atmos) {
+                       decoder->atmos->Data.connect (bind(&Player::atmos, this, weak_ptr<Piece>(piece), _1));
+               }
        }
 
        _stream_states.clear ();
@@ -238,8 +266,8 @@ Player::setup_pieces_unlocked ()
                }
        }
 
-       _black = Empty (_film, _playlist, bind(&have_video, _1), _playback_length);
-       _silent = Empty (_film, _playlist, bind(&have_audio, _1), _playback_length);
+       _black = Empty (_film, playlist(), bind(&have_video, _1), _playback_length);
+       _silent = Empty (_film, playlist(), bind(&have_audio, _1), _playback_length);
 
        _last_video_time = DCPTime ();
        _last_video_eyes = EYES_BOTH;
@@ -350,63 +378,24 @@ Player::black_player_video_frame (Eyes eyes) const
        );
 }
 
-Frame
-Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
-{
-       DCPTime 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 */
-       DCPTime 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
-{
-       DCPTime 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 */
+       /* See notes in content_video_to_dcp */
        return DCPTime::from_frames (f, _film->audio_frame_rate())
                - DCPTime (piece->content->trim_start(), piece->frc)
-               + piece->content->position();
+               + piece->position();
 }
 
 ContentTime
 Player::dcp_to_content_time (shared_ptr<const Piece> piece, DCPTime t) const
 {
-       DCPTime s = t - piece->content->position ();
+       DCPTime s = t - piece->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());
-}
-
 list<shared_ptr<Font> >
 Player::get_subtitle_fonts ()
 {
@@ -496,7 +485,7 @@ Player::get_reel_assets ()
 
        list<ReferencedReelAsset> a;
 
-       BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
+       BOOST_FOREACH (shared_ptr<Content> i, playlist()->content()) {
                shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
                if (!j) {
                        continue;
@@ -586,8 +575,8 @@ Player::pass ()
                        continue;
                }
 
-               DCPTime const t = content_time_to_dcp (i, max(i->decoder->position(), i->content->trim_start()));
-               if (t > i->content->end(_film)) {
+               DCPTime const t = i->content_time_to_dcp (max(i->decoder->position(), i->content->trim_start()));
+               if (t > i->end(_film)) {
                        i->done = true;
                } else {
 
@@ -639,6 +628,7 @@ Player::pass ()
                break;
        }
        case BLACK:
+               LOG_DEBUG_PLAYER ("Emit black for gap at %1", to_string(_black.position()));
                emit_video (black_player_video_frame(EYES_BOTH), _black.position());
                _black.set_position (_black.position() + one_video_frame());
                break;
@@ -768,29 +758,33 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                return;
        }
 
+       if (!piece->content->video->use()) {
+               return;
+       }
+
        FrameRateChange frc (_film, piece->content);
        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);
 
        /* 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;
        }
 
        /* 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) {
@@ -800,7 +794,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;
                                }
@@ -811,10 +805,12 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                                }
                                while (j < fill_to || eyes != fill_to_eyes) {
                                        if (last != _last_video.end()) {
+                                               LOG_DEBUG_PLAYER("Fill using last video at %1 in 3D mode", to_string(j));
                                                shared_ptr<PlayerVideo> copy = last->second->shallow_copy();
                                                copy->set_eyes (eyes);
                                                emit_video (copy, j);
                                        } else {
+                                               LOG_DEBUG_PLAYER("Fill using black at %1 in 3D mode", to_string(j));
                                                emit_video (black_player_video_frame(eyes), j);
                                        }
                                        if (eyes == EYES_RIGHT) {
@@ -839,9 +835,7 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                        video.image,
                        piece->content->video->crop (),
                        piece->content->video->fade (_film, video.frame),
-                       piece->content->video->scale().size (
-                               piece->content->video, _video_container_size, _film->frame_size ()
-                               ),
+                       scale_for_display(piece->content->video->scaled_size(_film->frame_size()), _video_container_size, _film->frame_size()),
                        _video_container_size,
                        video.eyes,
                        video.part,
@@ -855,7 +849,7 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
 
        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 ();
@@ -883,19 +877,19 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
        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()) {
-               pair<shared_ptr<AudioBuffers>, DCPTime> cut = discard_audio (content_audio.audio, time, piece->content->position());
+       if (time < piece->position()) {
+               pair<shared_ptr<AudioBuffers>, DCPTime> 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;
                }
@@ -962,7 +956,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::YUV_TO_RGB_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[text->type()].add_from (wc, ps, from);
 }
@@ -977,9 +971,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;
        }
 
@@ -1028,9 +1022,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);
+       DCPTime const dcp_to = piece->content_time_to_dcp(to);
 
-       if (dcp_to > piece->content->end(_film)) {
+       if (dcp_to > piece->end(_film)) {
                return;
        }
 
@@ -1076,7 +1070,7 @@ Player::seek (DCPTime time, bool accurate)
                        */
                        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)) {
+               } else if (i->content->position() <= time && time < i->end(_film)) {
                        /* During; seek to position */
                        i->decoder->seek (dcp_to_content_time (i, time), accurate);
                        i->done = false;
@@ -1224,10 +1218,25 @@ Player::content_time_to_dcp (shared_ptr<Content> content, ContentTime t)
 
        BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
                if (i->content == content) {
-                       return content_time_to_dcp (i, t);
+                       return i->content_time_to_dcp (t);
                }
        }
 
        /* We couldn't find this content; perhaps things are being changed over */
        return optional<DCPTime>();
 }
+
+
+shared_ptr<const Playlist>
+Player::playlist () const
+{
+       return _playlist ? _playlist : _film->playlist();
+}
+
+
+void
+Player::atmos (weak_ptr<Piece>, ContentAtmos data)
+{
+       Atmos (data.data, DCPTime::from_frames(data.frame, _film->video_frame_rate()), data.metadata);
+}
+