Whitespace.
[dcpomatic.git] / src / lib / player.cc
index 499aa2a389c0c1545d82b915f17daef218d8df4e..b011fffdc351702c6b6c0dbe14ec2bf0bdca29ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -48,6 +48,7 @@
 #include "dcp_decoder.h"
 #include "image_decoder.h"
 #include "compose.hpp"
+#include "shuffler.h"
 #include <dcp/reel.h>
 #include <dcp/reel_sound_asset.h>
 #include <dcp/reel_subtitle_asset.h>
@@ -87,6 +88,7 @@ Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist
        , _fast (false)
        , _play_referenced (false)
        , _audio_merger (_film->audio_frame_rate())
+       , _shuffler (0)
 {
        _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
        _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
@@ -98,11 +100,20 @@ Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist
        seek (DCPTime (), true);
 }
 
+Player::~Player ()
+{
+       delete _shuffler;
+}
+
 void
 Player::setup_pieces ()
 {
        _pieces.clear ();
 
+       delete _shuffler;
+       _shuffler = new Shuffler();
+       _shuffler->Video.connect(bind(&Player::video, this, _1, _2));
+
        BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
 
                if (!i->paths_valid ()) {
@@ -137,7 +148,11 @@ Player::setup_pieces ()
                _pieces.push_back (piece);
 
                if (decoder->video) {
-                       decoder->video->Data.connect (bind (&Player::video, this, weak_ptr<Piece> (piece), _1));
+                       if (i->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT || i->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) {
+                               decoder->video->Data.connect (bind (&Shuffler::video, _shuffler, weak_ptr<Piece>(piece), _1));
+                       } else {
+                               decoder->video->Data.connect (bind (&Player::video, this, weak_ptr<Piece>(piece), _1));
+                       }
                }
 
                if (decoder->audio) {
@@ -164,6 +179,7 @@ Player::setup_pieces ()
        _silent = Empty (_film->content(), _film->length(), bind(&Content::audio, _1));
 
        _last_video_time = DCPTime ();
+       _last_video_eyes = EYES_BOTH;
        _last_audio_time = DCPTime ();
        _have_valid_pieces = true;
 }
@@ -186,8 +202,7 @@ Player::playlist_content_changed (weak_ptr<Content> w, int property, bool freque
                property == DCPContentProperty::NEEDS_ASSETS ||
                property == DCPContentProperty::NEEDS_KDM ||
                property == SubtitleContentProperty::COLOUR ||
-               property == SubtitleContentProperty::OUTLINE ||
-               property == SubtitleContentProperty::SHADOW ||
+               property == SubtitleContentProperty::EFFECT ||
                property == SubtitleContentProperty::EFFECT_COLOUR ||
                property == FFmpegContentProperty::SUBTITLE_STREAM ||
                property == FFmpegContentProperty::FILTERS ||
@@ -310,7 +325,7 @@ Player::transform_image_subtitles (list<ImageSubtitle> subs) const
 }
 
 shared_ptr<PlayerVideo>
-Player::black_player_video_frame () const
+Player::black_player_video_frame (Eyes eyes) const
 {
        return shared_ptr<PlayerVideo> (
                new PlayerVideo (
@@ -319,7 +334,7 @@ Player::black_player_video_frame () const
                        optional<double> (),
                        _video_container_size,
                        _video_container_size,
-                       EYES_BOTH,
+                       eyes,
                        PART_WHOLE,
                        PresetColourConversion::all().front().conversion
                )
@@ -517,7 +532,7 @@ Player::pass ()
 
        if (_playlist->length() == DCPTime()) {
                /* Special case of an empty Film; just give one black frame */
-               emit_video (black_player_video_frame(), DCPTime());
+               emit_video (black_player_video_frame(EYES_BOTH), DCPTime());
                return true;
        }
 
@@ -535,6 +550,7 @@ Player::pass ()
                if (t > i->content->end()) {
                        i->done = true;
                } else {
+
                        /* Given two choices at the same time, pick the one with a subtitle so we see it before
                           the video.
                        */
@@ -573,12 +589,20 @@ Player::pass ()
                earliest_content->done = earliest_content->decoder->pass ();
                break;
        case BLACK:
-               emit_video (black_player_video_frame(), _black.position());
+               emit_video (black_player_video_frame(EYES_BOTH), _black.position());
                _black.set_position (_black.position() + one_video_frame());
                break;
        case SILENT:
        {
                DCPTimePeriod period (_silent.period_at_position());
+               if (_last_audio_time) {
+                       /* Sometimes the thing that happened last finishes fractionally before
+                          this silence.  Bodge the start time of the silence to fix it.  I'm
+                          not sure if this is the right solution --- maybe the last thing should
+                          be padded `forward' rather than this thing padding `back'.
+                       */
+                       period.from = min(period.from, *_last_audio_time);
+               }
                if (period.duration() > one_video_frame()) {
                        period.to = period.from + one_video_frame();
                }
@@ -593,12 +617,18 @@ Player::pass ()
 
        /* Emit any audio that is ready */
 
+       /* Work out the time before which the audio is definitely all here.  This is the earliest last_push_end of one
+          of our streams, or the position of the _silent.
+       */
        DCPTime pull_to = _film->length ();
        for (map<AudioStreamPtr, StreamState>::const_iterator i = _stream_states.begin(); i != _stream_states.end(); ++i) {
                if (!i->second.piece->done && i->second.last_push_end < pull_to) {
                        pull_to = i->second.last_push_end;
                }
        }
+       if (!_silent.done() && _silent.position() < pull_to) {
+               pull_to = _silent.position();
+       }
 
        list<pair<shared_ptr<AudioBuffers>, DCPTime> > audio = _audio_merger.pull (pull_to);
        for (list<pair<shared_ptr<AudioBuffers>, DCPTime> >::iterator i = audio.begin(); i != audio.end(); ++i) {
@@ -617,6 +647,9 @@ Player::pass ()
                emit_audio (i->first, i->second);
        }
 
+       if (done) {
+               _shuffler->flush ();
+       }
        return done;
 }
 
@@ -625,7 +658,9 @@ Player::subtitles_for_frame (DCPTime time) const
 {
        list<PositionImage> subtitles;
 
-       BOOST_FOREACH (PlayerSubtitles i, _active_subtitles.get_burnt (time, _always_burn_subtitles)) {
+       int const vfr = _film->video_frame_rate();
+
+       BOOST_FOREACH (PlayerSubtitles i, _active_subtitles.get_burnt (DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_subtitles)) {
 
                /* Image subtitles */
                list<PositionImage> c = transform_image_subtitles (i.image);
@@ -633,7 +668,7 @@ Player::subtitles_for_frame (DCPTime time) const
 
                /* Text subtitles (rendered to an image) */
                if (!i.text.empty ()) {
-                       list<PositionImage> s = render_subtitles (i.text, i.fonts, _video_container_size, time);
+                       list<PositionImage> s = render_subtitles (i.text, i.fonts, _video_container_size, time, vfr);
                        copy (s.begin(), s.end(), back_inserter (subtitles));
                }
        }
@@ -661,25 +696,48 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
        /* Time of the first frame we will emit */
        DCPTime const time = content_video_to_dcp (piece, video.frame);
 
-       /* Discard if it's outside the content's period or if it's before the last accurate seek */
-       if (
-               time < piece->content->position() ||
-               time >= piece->content->end() ||
-               (_last_video_time && time < *_last_video_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)) {
                return;
        }
 
-       /* Fill gaps that we discover now that we have some video which needs to be emitted */
+       /* 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());
 
        if (_last_video_time) {
-               /* XXX: this may not work for 3D */
                DCPTime fill_from = max (*_last_video_time, piece->content->position());
-               for (DCPTime j = fill_from; j < time; j += one_video_frame()) {
-                       LastVideoMap::const_iterator k = _last_video.find (wp);
-                       if (k != _last_video.end ()) {
-                               emit_video (k->second, j);
-                       } else {
-                               emit_video (black_player_video_frame(), j);
+               LastVideoMap::const_iterator last = _last_video.find (wp);
+               if (_film->three_d()) {
+                       DCPTime j = fill_from;
+                       Eyes eyes = _last_video_eyes.get_value_or(EYES_LEFT);
+                       if (eyes == EYES_BOTH) {
+                               eyes = EYES_LEFT;
+                       }
+                       while (j < fill_to || eyes != video.eyes) {
+                               if (last != _last_video.end()) {
+                                       shared_ptr<PlayerVideo> copy = last->second->shallow_copy();
+                                       copy->set_eyes (eyes);
+                                       emit_video (copy, j);
+                               } else {
+                                       emit_video (black_player_video_frame(eyes), j);
+                               }
+                               if (eyes == EYES_RIGHT) {
+                                       j += one_video_frame();
+                               }
+                               eyes = increment_eyes (eyes);
+                       }
+               } else {
+                       for (DCPTime j = fill_from; j < fill_to; j += one_video_frame()) {
+                               if (last != _last_video.end()) {
+                                       emit_video (last->second, j);
+                               } else {
+                                       emit_video (black_player_video_frame(EYES_BOTH), j);
+                               }
                        }
                }
        }
@@ -701,7 +759,9 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
 
        DCPTime t = time;
        for (int i = 0; i < frc.repeat; ++i) {
-               emit_video (_last_video[wp], t);
+               if (t < piece->content->end()) {
+                       emit_video (_last_video[wp], t);
+               }
                t += one_video_frame ();
        }
 }
@@ -867,6 +927,10 @@ Player::seek (DCPTime time, bool accurate)
                setup_pieces ();
        }
 
+       if (_shuffler) {
+               _shuffler->clear ();
+       }
+
        if (_audio_processor) {
                _audio_processor->flush ();
        }
@@ -891,9 +955,11 @@ Player::seek (DCPTime time, bool accurate)
 
        if (accurate) {
                _last_video_time = time;
+               _last_video_eyes = EYES_LEFT;
                _last_audio_time = time;
        } else {
                _last_video_time = optional<DCPTime>();
+               _last_video_eyes = optional<Eyes>();
                _last_audio_time = optional<DCPTime>();
        }
 
@@ -917,11 +983,14 @@ Player::emit_video (shared_ptr<PlayerVideo> pv, DCPTime time)
                _last_video_time = time + one_video_frame();
                _active_subtitles.clear_before (time);
        }
+       _last_video_eyes = increment_eyes (pv->eyes());
 }
 
 void
 Player::emit_audio (shared_ptr<AudioBuffers> data, DCPTime time)
 {
+       /* This audio must follow on from the previous */
+       DCPOMATIC_ASSERT (!_last_audio_time || time == *_last_audio_time);
        Audio (data, time);
        _last_audio_time = time + DCPTime::from_frames (data->frames(), _film->audio_frame_rate());
 }