Move some methods out of util.{cc,h}
[dcpomatic.git] / src / lib / player.cc
index e9ca38c81d551c3ca9caee170f0d6f20932f1cca..c7dd83d6daa152a47ad294ffcf80bf077059589d 100644 (file)
@@ -140,18 +140,102 @@ Player::construct ()
        auto film = _film.lock();
        DCPOMATIC_ASSERT(film);
 
+       connect();
+       set_video_container_size(film->frame_size());
+
+       film_change (ChangeType::DONE, Film::Property::AUDIO_PROCESSOR);
+
+       setup_pieces ();
+       seek (DCPTime (), true);
+}
+
+
+void
+Player::connect()
+{
+       auto film = _film.lock();
+       DCPOMATIC_ASSERT(film);
+
        _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));
-       set_video_container_size(film->frame_size());
+}
 
-       film_change (ChangeType::DONE, Film::Property::AUDIO_PROCESSOR);
 
-       setup_pieces ();
-       seek (DCPTime (), true);
+Player::Player(Player&& other)
+       : _film(other._film)
+       , _playlist(std::move(other._playlist))
+       , _suspended(other._suspended.load())
+       , _pieces(std::move(other._pieces))
+       , _video_container_size(other._video_container_size.load())
+       , _black_image(std::move(other._black_image))
+       , _ignore_video(other._ignore_video.load())
+       , _ignore_audio(other._ignore_audio.load())
+       , _ignore_text(other._ignore_text.load())
+       , _always_burn_open_subtitles(other._always_burn_open_subtitles.load())
+       , _fast(other._fast.load())
+       , _tolerant(other._tolerant)
+       , _play_referenced(other._play_referenced.load())
+       , _next_video_time(other._next_video_time)
+       , _next_audio_time(other._next_audio_time)
+       , _dcp_decode_reduction(other._dcp_decode_reduction.load())
+       , _last_video(std::move(other._last_video))
+       , _audio_merger(std::move(other._audio_merger))
+       , _shuffler(std::move(other._shuffler))
+       , _delay(std::move(other._delay))
+       , _stream_states(std::move(other._stream_states))
+       , _black(std::move(other._black))
+       , _silent(std::move(other._silent))
+       , _active_texts(std::move(other._active_texts))
+       , _audio_processor(std::move(other._audio_processor))
+       , _playback_length(other._playback_length.load())
+       , _subtitle_alignment(other._subtitle_alignment)
+{
+       connect();
+}
+
+
+Player&
+Player::operator=(Player&& other)
+{
+       if (this == &other) {
+               return *this;
+       }
+
+       _film = std::move(other._film);
+       _playlist = std::move(other._playlist);
+       _suspended = other._suspended.load();
+       _pieces = std::move(other._pieces);
+       _video_container_size = other._video_container_size.load();
+       _black_image = std::move(other._black_image);
+       _ignore_video = other._ignore_video.load();
+       _ignore_audio = other._ignore_audio.load();
+       _ignore_text = other._ignore_text.load();
+       _always_burn_open_subtitles = other._always_burn_open_subtitles.load();
+       _fast = other._fast.load();
+       _tolerant = other._tolerant;
+       _play_referenced = other._play_referenced.load();
+       _next_video_time = other._next_video_time;
+       _next_audio_time = other._next_audio_time;
+       _dcp_decode_reduction = other._dcp_decode_reduction.load();
+       _last_video = std::move(other._last_video);
+       _audio_merger = std::move(other._audio_merger);
+       _shuffler = std::move(other._shuffler);
+       _delay = std::move(other._delay);
+       _stream_states = std::move(other._stream_states);
+       _black = std::move(other._black);
+       _silent = std::move(other._silent);
+       _active_texts = std::move(other._active_texts);
+       _audio_processor = std::move(other._audio_processor);
+       _playback_length = other._playback_length.load();
+       _subtitle_alignment = other._subtitle_alignment;
+
+       connect();
+
+       return *this;
 }
 
 
@@ -198,29 +282,29 @@ Player::setup_pieces ()
                _shuffler->Video.connect(bind(&Player::video, this, _1, _2));
        }
 
-       for (auto i: playlist()->content()) {
+       for (auto content: playlist()->content()) {
 
-               if (!i->paths_valid ()) {
+               if (!content->paths_valid()) {
                        continue;
                }
 
-               if (_ignore_video && _ignore_audio && i->text.empty()) {
+               if (_ignore_video && _ignore_audio && content->text.empty()) {
                        /* We're only interested in text and this content has none */
                        continue;
                }
 
                shared_ptr<Decoder> old_decoder;
                for (auto j: old_pieces) {
-                       if (j->content == i) {
+                       if (j->content == content) {
                                old_decoder = j->decoder;
                                break;
                        }
                }
 
-               auto decoder = decoder_factory(film, i, _fast, _tolerant, old_decoder);
+               auto decoder = decoder_factory(film, content, _fast, _tolerant, old_decoder);
                DCPOMATIC_ASSERT (decoder);
 
-               FrameRateChange frc(film, i);
+               FrameRateChange frc(film, content);
 
                if (decoder->video && _ignore_video) {
                        decoder->video->set_ignore (true);
@@ -244,7 +328,7 @@ Player::setup_pieces ()
                        }
                }
 
-               auto piece = make_shared<Piece>(i, decoder, frc);
+               auto piece = make_shared<Piece>(content, decoder, frc);
                _pieces.push_back (piece);
 
                if (decoder->video) {
@@ -285,7 +369,7 @@ Player::setup_pieces ()
        for (auto i: _pieces) {
                if (i->content->audio) {
                        for (auto j: i->content->audio->streams()) {
-                               _stream_states[j] = StreamState (i, i->content->position ());
+                               _stream_states[j] = StreamState(i);
                        }
                }
        }
@@ -716,21 +800,25 @@ Player::pass ()
 
        using state_pair = std::pair<AudioStreamPtr, StreamState>;
 
+       /* Find streams that have pushed */
+       std::vector<state_pair> have_pushed;
+       std::copy_if(_stream_states.begin(), _stream_states.end(), std::back_inserter(have_pushed), [](state_pair const& a) { return static_cast<bool>(a.second.last_push_end); });
+
        /* Find the 'leading' stream (i.e. the one that pushed data most recently) */
        auto latest_last_push_end = std::max_element(
-               _stream_states.begin(),
-               _stream_states.end(),
-               [](state_pair const& a, state_pair const& b) { return a.second.last_push_end < b.second.last_push_end; }
+               have_pushed.begin(),
+               have_pushed.end(),
+               [](state_pair const& a, state_pair const& b) { return a.second.last_push_end.get() < b.second.last_push_end.get(); }
                );
 
-       if (latest_last_push_end != _stream_states.end()) {
-               LOG_DEBUG_PLAYER("Leading audio stream is in %1 at %2", latest_last_push_end->second.piece->content->path(0), to_string(latest_last_push_end->second.last_push_end));
+       if (latest_last_push_end != have_pushed.end()) {
+               LOG_DEBUG_PLAYER("Leading audio stream is in %1 at %2", latest_last_push_end->second.piece->content->path(0), to_string(latest_last_push_end->second.last_push_end.get()));
        }
 
        /* Now make a list of those streams that are less than ignore_streams_behind behind the leader */
        std::map<AudioStreamPtr, StreamState> alive_stream_states;
        for (auto const& i: _stream_states) {
-               if ((latest_last_push_end->second.last_push_end - i.second.last_push_end) < dcpomatic::DCPTime::from_seconds(ignore_streams_behind)) {
+               if (!i.second.last_push_end || (latest_last_push_end->second.last_push_end.get() - i.second.last_push_end.get()) < dcpomatic::DCPTime::from_seconds(ignore_streams_behind)) {
                        alive_stream_states.insert(i);
                } else {
                        LOG_DEBUG_PLAYER("Ignoring stream %1 because it is too far behind", i.second.piece->content->path(0));
@@ -739,8 +827,9 @@ Player::pass ()
 
        auto pull_to = _playback_length.load();
        for (auto const& i: alive_stream_states) {
-               if (!i.second.piece->done && i.second.last_push_end < pull_to) {
-                       pull_to = i.second.last_push_end;
+               auto position = i.second.last_push_end.get_value_or(i.second.piece->content->position());
+               if (!i.second.piece->done && position < pull_to) {
+                       pull_to = position;
                }
        }
        if (!_silent.done() && _silent.position() < pull_to) {
@@ -843,6 +932,18 @@ Player::open_subtitles_for_frame (DCPTime time) const
 }
 
 
+static
+Eyes
+increment_eyes (Eyes e)
+{
+       if (e == Eyes::LEFT) {
+               return Eyes::RIGHT;
+       }
+
+       return Eyes::LEFT;
+}
+
+
 void
 Player::video (weak_ptr<Piece> weak_piece, ContentVideo video)
 {
@@ -995,10 +1096,10 @@ Player::audio (weak_ptr<Piece> weak_piece, AudioStreamPtr stream, ContentAudio c
 
        /* Compute time in the DCP */
        auto time = resampled_audio_to_dcp (piece, content_audio.frame);
-       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);
+       LOG_DEBUG_PLAYER("Received audio frame %1 covering %2 to %3 (%4)", content_audio.frame, to_string(time), to_string(end), piece->content->path(0).filename());
 
        /* Remove anything that comes before the start or after the end of the content */
        if (time < piece->content->position()) {
@@ -1258,6 +1359,10 @@ Player::seek (DCPTime time, bool accurate)
        _silent.set_position (time);
 
        _last_video.clear ();
+
+       for (auto& state: _stream_states) {
+               state.second.last_push_end = {};
+       }
 }