Fix fill of timeline periods where there is no video.
authorCarl Hetherington <cth@carlh.net>
Sun, 7 May 2017 20:47:58 +0000 (21:47 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 7 May 2017 23:35:35 +0000 (00:35 +0100)
src/lib/player.cc
src/lib/player.h
src/lib/text_subtitle_decoder.cc

index 858e93c04bc22be540210734e74d848b3c97817e..1110b41b0bea325af567d2e7277d64e7a67d4bb0 100644 (file)
@@ -527,27 +527,26 @@ Player::pass ()
                }
        }
 
-       if (!earliest) {
-               /* No more content; fill up with silent black */
-               DCPTimePeriod remaining_video (DCPTime(), _playlist->length());
-               if (_last_video_time) {
-                       remaining_video.from = _last_video_time.get();
-               }
-               fill_video (remaining_video);
-               DCPTimePeriod remaining_audio (DCPTime(), _playlist->length());
-               if (_last_audio_time) {
-                       remaining_audio.from = _last_audio_time.get();
+       if (earliest) {
+               earliest->done = earliest->decoder->pass ();
+               if (earliest->done && earliest->content->audio) {
+                       /* Flush the Player audio system for this piece */
+                       BOOST_FOREACH (AudioStreamPtr i, earliest->content->audio->streams()) {
+                               audio_flush (earliest, i);
+                       }
                }
-               fill_audio (remaining_audio);
-               return true;
        }
 
-       earliest->done = earliest->decoder->pass ();
-       if (earliest->done && earliest->content->audio) {
-               /* Flush the Player audio system for this piece */
-               BOOST_FOREACH (AudioStreamPtr i, earliest->content->audio->streams()) {
-                       audio_flush (earliest, i);
-               }
+       if (_last_video_time) {
+               fill_video (DCPTimePeriod (_last_video_time.get(), earliest ? earliest_content : _playlist->length()));
+       } else if (_last_seek_time) {
+               fill_video (DCPTimePeriod (_last_seek_time.get(), _last_seek_time.get() + one_video_frame ()));
+       }
+
+       /* XXX: fill audio */
+
+       if (!earliest) {
+               return true;
        }
 
        /* Emit any audio that is ready */
@@ -573,6 +572,7 @@ Player::pass ()
                }
 
                if (_last_audio_time) {
+                       /* XXX: does this remain necessary? */
                        fill_audio (DCPTimePeriod (_last_audio_time.get(), i->second));
                }
 
@@ -583,28 +583,9 @@ Player::pass ()
        return false;
 }
 
-void
-Player::video (weak_ptr<Piece> wp, ContentVideo video)
+optional<PositionImage>
+Player::subtitles_for_frame (DCPTime time) const
 {
-       shared_ptr<Piece> piece = wp.lock ();
-       if (!piece) {
-               return;
-       }
-
-       FrameRateChange frc(piece->content->active_video_frame_rate(), _film->video_frame_rate());
-       if (frc.skip && (video.frame % 2) == 1) {
-               return;
-       }
-
-       /* Time and period of the frame we will emit */
-       DCPTime const time = content_video_to_dcp (piece, video.frame);
-       DCPTimePeriod const period (time, time + one_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)) {
-               return;
-       }
-
        /* Get any subtitles */
 
        optional<PositionImage> subtitles;
@@ -616,35 +597,62 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                        continue;
                }
 
-               if (!sub_piece->content->subtitle->use() || (!_always_burn_subtitles && !piece->content->subtitle->burn())) {
+               if (!sub_piece->content->subtitle->use() || (!_always_burn_subtitles && !sub_piece->content->subtitle->burn())) {
                        continue;
                }
 
-               ActiveSubtitles sub = i->second;
+               BOOST_FOREACH (ActiveSubtitles j, i->second) {
 
-               if (sub.from > time || (sub.to && sub.to.get() <= time)) {
-                       continue;
-               }
+                       if (j.from > time || (j.to && j.to.get() <= time)) {
+                               continue;
+                       }
 
-               list<PositionImage> sub_images;
+                       list<PositionImage> sub_images;
 
-               /* Image subtitles */
-               list<PositionImage> c = transform_image_subtitles (sub.subs.image);
-               copy (c.begin(), c.end(), back_inserter (sub_images));
+                       /* Image subtitles */
+                       list<PositionImage> c = transform_image_subtitles (j.subs.image);
+                       copy (c.begin(), c.end(), back_inserter (sub_images));
 
-               /* Text subtitles (rendered to an image) */
-               if (!sub.subs.text.empty ()) {
-                       list<PositionImage> s = render_subtitles (sub.subs.text, sub.subs.fonts, _video_container_size, time);
-                       copy (s.begin (), s.end (), back_inserter (sub_images));
-               }
+                       /* Text subtitles (rendered to an image) */
+                       if (!j.subs.text.empty ()) {
+                               list<PositionImage> s = render_subtitles (j.subs.text, j.subs.fonts, _video_container_size, time);
+                               copy (s.begin (), s.end (), back_inserter (sub_images));
+                       }
 
-               if (!sub_images.empty ()) {
-                       subtitles = merge (sub_images);
+                       if (!sub_images.empty ()) {
+                               subtitles = merge (sub_images);
+                       }
                }
        }
 
+       return subtitles;
+}
+
+void
+Player::video (weak_ptr<Piece> wp, ContentVideo video)
+{
+       shared_ptr<Piece> piece = wp.lock ();
+       if (!piece) {
+               return;
+       }
+
+       FrameRateChange frc(piece->content->active_video_frame_rate(), _film->video_frame_rate());
+       if (frc.skip && (video.frame % 2) == 1) {
+               return;
+       }
+
+       /* Time and period of the frame we will emit */
+       DCPTime const time = content_video_to_dcp (piece, video.frame);
+       DCPTimePeriod const period (time, time + one_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)) {
+               return;
+       }
+
        /* Fill gaps caused by (the hopefully rare event of) a decoder not emitting contiguous video */
 
+       /* XXX: is this necessary? can it be done by the fill in pass? */
        if (_last_video_time) {
                fill_video (DCPTimePeriod (_last_video_time.get(), time));
        }
@@ -664,6 +672,7 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                        )
                );
 
+       optional<PositionImage> subtitles = subtitles_for_frame (time);
        if (subtitles) {
                _last_video->set_subtitle (subtitles.get ());
        }
@@ -675,8 +684,14 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
        /* Clear any finished _active_subtitles */
        ActiveSubtitlesMap updated;
        for (ActiveSubtitlesMap::const_iterator i = _active_subtitles.begin(); i != _active_subtitles.end(); ++i) {
-               if (!i->second.to || i->second.to.get() >= time) {
-                       updated[i->first] = i->second;
+               list<ActiveSubtitles> as;
+               BOOST_FOREACH (ActiveSubtitles j, i->second) {
+                       if (!j.to || j.to.get() >= time) {
+                               as.push_back (j);
+                       }
+               }
+               if (!as.empty ()) {
+                       updated[i->first] = as;
                }
        }
        _active_subtitles = updated;
@@ -832,7 +847,10 @@ Player::image_subtitle_start (weak_ptr<Piece> wp, ContentImageSubtitle subtitle)
        ps.image.push_back (subtitle.sub);
        DCPTime from (content_time_to_dcp (piece, subtitle.from()));
 
-       _active_subtitles[wp] = ActiveSubtitles (ps, from);
+       if (_active_subtitles.find(wp) == _active_subtitles.end()) {
+               _active_subtitles[wp] = list<ActiveSubtitles>();
+       }
+       _active_subtitles[wp].push_back (ActiveSubtitles (ps, from));
 }
 
 void
@@ -871,7 +889,10 @@ Player::text_subtitle_start (weak_ptr<Piece> wp, ContentTextSubtitle subtitle)
                ps.add_fonts (piece->content->subtitle->fonts ());
        }
 
-       _active_subtitles[wp] = ActiveSubtitles (ps, from);
+       if (_active_subtitles.find(wp) == _active_subtitles.end()) {
+               _active_subtitles[wp] = list<ActiveSubtitles> ();
+       }
+       _active_subtitles[wp].push_back (ActiveSubtitles (ps, from));
 }
 
 void
@@ -889,10 +910,14 @@ Player::subtitle_stop (weak_ptr<Piece> wp, ContentTime to)
        DCPTime const dcp_to = content_time_to_dcp (piece, to);
 
        if (piece->content->subtitle->use() && !_always_burn_subtitles && !piece->content->subtitle->burn()) {
-               Subtitle (_active_subtitles[wp].subs, DCPTimePeriod (_active_subtitles[wp].from, dcp_to));
+               Subtitle (_active_subtitles[wp].back().subs, DCPTimePeriod (_active_subtitles[wp].back().from, dcp_to));
        }
 
-       _active_subtitles[wp].to = dcp_to;
+       _active_subtitles[wp].back().to = dcp_to;
+
+       BOOST_FOREACH (SubtitleString& i, _active_subtitles[wp].back().subs.text) {
+               i.set_out (dcp::Time(dcp_to.seconds(), 1000));
+       }
 }
 
 void
@@ -932,6 +957,8 @@ Player::seek (DCPTime time, bool accurate)
                _last_video_time = optional<DCPTime> ();
                _last_audio_time = optional<DCPTime> ();
        }
+
+       _last_seek_time = time;
 }
 
 shared_ptr<Resampler>
@@ -970,8 +997,14 @@ Player::fill_video (DCPTimePeriod period)
                        if (_playlist->video_content_at(j) && _last_video) {
                                Video (shared_ptr<PlayerVideo> (new PlayerVideo (*_last_video)), j);
                        } else {
-                               Video (black_player_video_frame(), j);
+                               shared_ptr<PlayerVideo> black = black_player_video_frame ();
+                               optional<PositionImage> subtitles = subtitles_for_frame (j);
+                               if (subtitles) {
+                                       black->set_subtitle (subtitles.get ());
+                               }
+                               Video (black, j);
                        }
+                       _last_video_time = j;
                }
        }
 }
index 6d4f6836f6db28f75360afcf47fec172b48f67c5..48dcd8892c6891761b96979551477bb272107102 100644 (file)
@@ -116,6 +116,7 @@ private:
        std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio (
                boost::shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTime discard_to
                ) const;
+       boost::optional<PositionImage> subtitles_for_frame (DCPTime time) const;
 
        boost::shared_ptr<const Film> _film;
        boost::shared_ptr<const Playlist> _playlist;
@@ -147,6 +148,7 @@ private:
        boost::optional<DCPTime> _last_video_time;
        /** Time just after the last audio frame we emitted, or the last seek time */
        boost::optional<DCPTime> _last_audio_time;
+       boost::optional<DCPTime> _last_seek_time;
 
        AudioMerger _audio_merger;
 
@@ -182,7 +184,7 @@ private:
                DCPTime from;
                boost::optional<DCPTime> to;
        };
-       typedef std::map<boost::weak_ptr<Piece>, ActiveSubtitles> ActiveSubtitlesMap;
+       typedef std::map<boost::weak_ptr<Piece>, std::list<ActiveSubtitles> > ActiveSubtitlesMap;
        ActiveSubtitlesMap _active_subtitles;
 
        boost::shared_ptr<AudioProcessor> _audio_processor;
index bdec17a8dba0a7ad386ba9f3b2e676ca881d7885..631e0cc0cfbf1b9a018447b709c221fd71b043d0 100644 (file)
@@ -44,6 +44,11 @@ TextSubtitleDecoder::TextSubtitleDecoder (shared_ptr<const TextSubtitleContent>
 void
 TextSubtitleDecoder::seek (ContentTime time, bool accurate)
 {
+       time -= ContentTime::from_seconds (5);
+       if (time < ContentTime()) {
+               time = ContentTime();
+       }
+
        Decoder::seek (time, accurate);
 
        _next = 0;