Basic grunt-work, untested and unfinished, but it compiles.
authorCarl Hetherington <cth@carlh.net>
Mon, 21 Nov 2016 16:57:15 +0000 (16:57 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 19 Apr 2017 22:04:32 +0000 (23:04 +0100)
41 files changed:
doc/design/decoder_structures.tex
src/lib/analyse_audio_job.cc
src/lib/analyse_audio_job.h
src/lib/audio_decoder.cc
src/lib/audio_decoder.h
src/lib/audio_decoder_stream.cc
src/lib/audio_decoder_stream.h
src/lib/dcp_decoder.cc
src/lib/dcp_decoder.h
src/lib/dcp_subtitle_decoder.cc
src/lib/dcp_subtitle_decoder.h
src/lib/decoder.cc
src/lib/decoder.h
src/lib/decoder_part.cc
src/lib/decoder_part.h
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_content.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/ffmpeg_subtitle_stream.cc
src/lib/ffmpeg_subtitle_stream.h
src/lib/image_decoder.cc
src/lib/image_decoder.h
src/lib/player.cc
src/lib/player.h
src/lib/subtitle_decoder.cc
src/lib/subtitle_decoder.h
src/lib/text_subtitle_decoder.cc
src/lib/text_subtitle_decoder.h
src/lib/transcoder.cc
src/lib/transcoder.h
src/lib/video_decoder.cc
src/lib/video_decoder.h
src/lib/video_mxf_decoder.cc
src/lib/video_mxf_decoder.h
src/tools/server_test.cc
src/wx/film_viewer.cc
src/wx/film_viewer.h
src/wx/subtitle_view.cc
test/audio_decoder_test.cc
test/wscript

index 3aa85b1..ebd25bf 100644 (file)
@@ -154,6 +154,7 @@ or perhaps
 Questions:
 \begin{itemize}
 \item Video / audio frame or \texttt{ContentTime}?
+\item Can all the subtitle period notation code go?
 \end{itemize}
 
 \end{document}
index 4274cc3..1378b66 100644 (file)
@@ -102,6 +102,7 @@ AnalyseAudioJob::run ()
        player->set_ignore_video ();
        player->set_fast ();
        player->set_play_referenced ();
+       player->Audio.connect (bind (&AnalyseAudioJob::analyse, this, _1, _2));
 
        DCPTime const start = _playlist->start().get_value_or (DCPTime ());
        DCPTime const length = _playlist->length ();
@@ -122,17 +123,7 @@ AnalyseAudioJob::run ()
 
        if (has_any_audio) {
                _done = 0;
-               DCPTime const block = DCPTime::from_seconds (1.0 / 8);
-               for (DCPTime t = start; t < length; t += block) {
-                       shared_ptr<const AudioBuffers> audio = player->get_audio (t, block, false);
-#ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
-                       if (Config::instance()->analyse_ebur128 ()) {
-                               _ebur128->process (audio);
-                       }
-#endif
-                       analyse (audio);
-                       set_progress ((t.seconds() - start.seconds()) / (length.seconds() - start.seconds()));
-               }
+               while (!player->pass ()) {}
        }
 
        vector<AudioAnalysis::PeakTime> sample_peak;
@@ -172,8 +163,14 @@ AnalyseAudioJob::run ()
 }
 
 void
-AnalyseAudioJob::analyse (shared_ptr<const AudioBuffers> b)
+AnalyseAudioJob::analyse (shared_ptr<const AudioBuffers> b, DCPTime time)
 {
+#ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
+       if (Config::instance()->analyse_ebur128 ()) {
+               _ebur128->process (b);
+       }
+#endif
+
        int const frames = b->frames ();
        int const channels = b->channels ();
 
@@ -204,4 +201,8 @@ AnalyseAudioJob::analyse (shared_ptr<const AudioBuffers> b)
        }
 
        _done += frames;
+
+       DCPTime const start = _playlist->start().get_value_or (DCPTime ());
+       DCPTime const length = _playlist->length ();
+       set_progress ((time.seconds() - start.seconds()) / (length.seconds() - start.seconds()));
 }
index ee20bed..7e5ea47 100644 (file)
@@ -25,6 +25,7 @@
 #include "job.h"
 #include "audio_point.h"
 #include "types.h"
+#include "dcpomatic_time.h"
 
 class AudioBuffers;
 class AudioAnalysis;
@@ -55,7 +56,7 @@ public:
        }
 
 private:
-       void analyse (boost::shared_ptr<const AudioBuffers>);
+       void analyse (boost::shared_ptr<const AudioBuffers>, DCPTime time);
 
        boost::shared_ptr<const Playlist> _playlist;
 
index 1b1ae70..b866d3e 100644 (file)
@@ -42,14 +42,8 @@ AudioDecoder::AudioDecoder (Decoder* parent, shared_ptr<const AudioContent> cont
        }
 }
 
-ContentAudio
-AudioDecoder::get (AudioStreamPtr stream, Frame frame, Frame length, bool accurate)
-{
-       return _streams[stream]->get (frame, length, accurate);
-}
-
 void
-AudioDecoder::give (AudioStreamPtr stream, shared_ptr<const AudioBuffers> data, ContentTime time)
+AudioDecoder::emit (AudioStreamPtr stream, shared_ptr<const AudioBuffers> data, ContentTime time)
 {
        if (ignore ()) {
                return;
@@ -89,15 +83,6 @@ AudioDecoder::flush ()
        }
 }
 
-void
-AudioDecoder::seek (ContentTime t, bool accurate)
-{
-       _log->log (String::compose ("AD seek to %1", to_string(t)), LogEntry::TYPE_DEBUG_DECODE);
-       for (StreamMap::const_iterator i = _streams.begin(); i != _streams.end(); ++i) {
-               i->second->seek (t, accurate);
-       }
-}
-
 void
 AudioDecoder::set_fast ()
 {
index cdb643c..a777592 100644 (file)
@@ -30,6 +30,7 @@
 #include "audio_stream.h"
 #include "decoder_part.h"
 #include <boost/enable_shared_from_this.hpp>
+#include <boost/signals2.hpp>
 
 class AudioBuffers;
 class AudioContent;
@@ -44,19 +45,12 @@ class AudioDecoder : public boost::enable_shared_from_this<AudioDecoder>, public
 public:
        AudioDecoder (Decoder* parent, boost::shared_ptr<const AudioContent>, boost::shared_ptr<Log> log);
 
-       /** Try to fetch some audio from a specific place in this content.
-        *  @param frame Frame to start from (after resampling, if applicable)
-        *  @param length Frames to get (after resampling, if applicable)
-        *  @param accurate true to try hard to return frames from exactly `frame', false if we don't mind nearby frames.
-        *  @return Time-stamped audio data which may or may not be from the location (and of the length) requested.
-        */
-       ContentAudio get (AudioStreamPtr stream, Frame time, Frame length, bool accurate);
-
        void set_fast ();
-
-       void give (AudioStreamPtr stream, boost::shared_ptr<const AudioBuffers>, ContentTime);
        void flush ();
-       void seek (ContentTime t, bool accurate);
+
+       void emit (AudioStreamPtr stream, boost::shared_ptr<const AudioBuffers>, ContentTime);
+
+       boost::signals2::signal<void (ContentAudio)> Data;
 
        boost::optional<ContentTime> position () const;
 
index a82ebc4..8f0905e 100644 (file)
@@ -66,102 +66,6 @@ AudioDecoderStream::reset_decoded ()
        _decoded = ContentAudio (shared_ptr<AudioBuffers> (new AudioBuffers (_stream->channels(), 0)), 0);
 }
 
-ContentAudio
-AudioDecoderStream::get (Frame frame, Frame length, bool accurate)
-{
-       shared_ptr<ContentAudio> dec;
-
-       _log->log (
-               String::compose (
-                       "ADS has request for %1 %2; has %3 %4",
-                       frame, length, _decoded.frame, _decoded.audio->frames()
-                       ), LogEntry::TYPE_DEBUG_DECODE
-               );
-
-       Frame const from = frame;
-       Frame const to = from + length;
-       Frame const have_from = _decoded.frame;
-       Frame const have_to = _decoded.frame + _decoded.audio->frames();
-
-       optional<Frame> missing;
-       if (have_from > from || have_to < to) {
-               /* We need something */
-               if (have_from <= from && from < have_to) {
-                       missing = have_to;
-               } else {
-                       missing = from;
-               }
-       }
-
-       if (missing) {
-               optional<ContentTime> pos = _audio_decoder->position ();
-               _log->log (
-                       String::compose ("ADS suggests seek to %1 (now at %2)", *missing, pos ? to_string(pos.get()) : "none"),
-                       LogEntry::TYPE_DEBUG_DECODE
-                       );
-               _audio_decoder->maybe_seek (ContentTime::from_frames (*missing, _content->resampled_frame_rate()), accurate);
-       }
-
-       /* Offset of the data that we want from the start of _decoded.audio
-          (to be set up shortly)
-       */
-       Frame decoded_offset = 0;
-
-       /* Now enough pass() calls will either:
-        *  (a) give us what we want, or
-        *  (b) hit the end of the decoder.
-        *
-        * If we are being accurate, we want the right frames,
-        * otherwise any frames will do.
-        */
-       if (accurate) {
-               /* Keep stuffing data into _decoded until we have enough data, or the subclass does not want to give us any more */
-               while (
-                       (_decoded.frame > frame || (_decoded.frame + _decoded.audio->frames()) <= to) &&
-                       !_decoder->pass (Decoder::PASS_REASON_AUDIO, accurate)
-                       )
-               {}
-
-               decoded_offset = frame - _decoded.frame;
-
-               _log->log (
-                       String::compose ("Accurate ADS::get has offset %1 from request %2 and available %3", decoded_offset, frame, have_from),
-                       LogEntry::TYPE_DEBUG_DECODE
-                       );
-       } else {
-               while (
-                       _decoded.audio->frames() < length &&
-                       !_decoder->pass (Decoder::PASS_REASON_AUDIO, accurate)
-                       )
-               {}
-
-               /* Use decoded_offset of 0, as we don't really care what frames we return */
-       }
-
-       /* The amount of data available in _decoded.audio starting from `frame'.  This could be -ve
-          if pass() returned true before we got enough data.
-       */
-       Frame const available = _decoded.audio->frames() - decoded_offset;
-
-       /* We will return either that, or the requested amount, whichever is smaller */
-       Frame const to_return = max ((Frame) 0, min (available, length));
-
-       /* Copy our data to the output */
-       shared_ptr<AudioBuffers> out (new AudioBuffers (_decoded.audio->channels(), to_return));
-       out->copy_from (_decoded.audio.get(), to_return, decoded_offset, 0);
-
-       Frame const remaining = max ((Frame) 0, available - to_return);
-
-       /* Clean up decoded; first, move the data after what we just returned to the start of the buffer */
-       _decoded.audio->move (decoded_offset + to_return, 0, remaining);
-       /* And set up the number of frames we have left */
-       _decoded.audio->set_frames (remaining);
-       /* Also bump where those frames are in terms of the content */
-       _decoded.frame += decoded_offset + to_return;
-
-       return ContentAudio (out, frame);
-}
-
 /** Audio timestamping is made hard by many factors, but perhaps the most entertaining is resampling.
  *  We have to assume that we are feeding continuous data into the resampler, and so we get continuous
  *  data out.  Hence we do the timestamping here, post-resampler, just by counting samples.
@@ -255,16 +159,6 @@ AudioDecoderStream::flush ()
        }
 }
 
-void
-AudioDecoderStream::seek (ContentTime t, bool accurate)
-{
-       _position.reset ();
-       reset_decoded ();
-       if (accurate) {
-               _seek_reference = t;
-       }
-}
-
 void
 AudioDecoderStream::set_fast ()
 {
index 9ec5c5a..b2ab65a 100644 (file)
@@ -37,10 +37,8 @@ class AudioDecoderStream
 public:
        AudioDecoderStream (boost::shared_ptr<const AudioContent>, AudioStreamPtr, Decoder* decoder, AudioDecoder* audio_decoder, boost::shared_ptr<Log> log);
 
-       ContentAudio get (Frame time, Frame length, bool accurate);
        void audio (boost::shared_ptr<const AudioBuffers>, ContentTime);
        void flush ();
-       void seek (ContentTime time, bool accurate);
        void set_fast ();
 
        boost::optional<ContentTime> position () const;
index 6d0d1de..25c805d 100644 (file)
@@ -56,15 +56,7 @@ DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log)
        video.reset (new VideoDecoder (this, c, log));
        audio.reset (new AudioDecoder (this, c->audio, log));
 
-       subtitle.reset (
-               new SubtitleDecoder (
-                       this,
-                       c->subtitle,
-                       log,
-                       bind (&DCPDecoder::image_subtitles_during, this, _1, _2),
-                       bind (&DCPDecoder::text_subtitles_during, this, _1, _2)
-                       )
-               );
+       subtitle.reset (new SubtitleDecoder (this, c->subtitle, log));
 
        shared_ptr<dcp::CPL> cpl;
        BOOST_FOREACH (shared_ptr<dcp::CPL> i, cpls ()) {
@@ -87,11 +79,11 @@ DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log)
        get_readers ();
 }
 
-bool
-DCPDecoder::pass (PassReason reason, bool)
+void
+DCPDecoder::pass ()
 {
        if (_reel == _reels.end () || !_dcp_content->can_be_played ()) {
-               return true;
+               return;
        }
 
        double const vfr = _dcp_content->active_video_frame_rate ();
@@ -99,24 +91,24 @@ DCPDecoder::pass (PassReason reason, bool)
        /* Frame within the (played part of the) reel that is coming up next */
        int64_t const frame = _next.frames_round (vfr);
 
-       if ((_mono_reader || _stereo_reader) && reason != PASS_REASON_SUBTITLE && (_decode_referenced || !_dcp_content->reference_video())) {
+       if ((_mono_reader || _stereo_reader) && (_decode_referenced || !_dcp_content->reference_video())) {
                shared_ptr<dcp::PictureAsset> asset = (*_reel)->main_picture()->asset ();
                int64_t const entry_point = (*_reel)->main_picture()->entry_point ();
                if (_mono_reader) {
-                       video->give (
+                       video->emit (
                                shared_ptr<ImageProxy> (
                                        new J2KImageProxy (_mono_reader->get_frame (entry_point + frame), asset->size(), AV_PIX_FMT_XYZ12LE)
                                        ),
                                _offset + frame
                                );
                } else {
-                       video->give (
+                       video->emit (
                                shared_ptr<ImageProxy> (
                                        new J2KImageProxy (_stereo_reader->get_frame (entry_point + frame), asset->size(), dcp::EYE_LEFT, AV_PIX_FMT_XYZ12LE)),
                                _offset + frame
                                );
 
-                       video->give (
+                       video->emit (
                                shared_ptr<ImageProxy> (
                                        new J2KImageProxy (_stereo_reader->get_frame (entry_point + frame), asset->size(), dcp::EYE_RIGHT, AV_PIX_FMT_XYZ12LE)),
                                _offset + frame
@@ -124,7 +116,7 @@ DCPDecoder::pass (PassReason reason, bool)
                }
        }
 
-       if (_sound_reader && reason != PASS_REASON_SUBTITLE && (_decode_referenced || !_dcp_content->reference_audio())) {
+       if (_sound_reader && (_decode_referenced || !_dcp_content->reference_audio())) {
                int64_t const entry_point = (*_reel)->main_sound()->entry_point ();
                shared_ptr<const dcp::SoundFrame> sf = _sound_reader->get_frame (entry_point + frame);
                uint8_t const * from = sf->data ();
@@ -140,7 +132,7 @@ DCPDecoder::pass (PassReason reason, bool)
                        }
                }
 
-               audio->give (_dcp_content->audio->stream(), data, ContentTime::from_frames (_offset, vfr) + _next);
+               audio->emit (_dcp_content->audio->stream(), data, ContentTime::from_frames (_offset, vfr) + _next);
        }
 
        if ((*_reel)->main_subtitle() && (_decode_referenced || !_dcp_content->reference_subtitle())) {
@@ -153,7 +145,7 @@ DCPDecoder::pass (PassReason reason, bool)
 
                if (!subs.empty ()) {
                        /* XXX: assuming that all `subs' are at the same time; maybe this is ok */
-                       subtitle->give_text (
+                       subtitle->emit_text (
                                ContentTimePeriod (
                                        ContentTime::from_frames (_offset - entry_point, vfr) + ContentTime::from_seconds (subs.front().in().as_seconds ()),
                                        ContentTime::from_frames (_offset - entry_point, vfr) + ContentTime::from_seconds (subs.front().out().as_seconds ())
@@ -171,8 +163,6 @@ DCPDecoder::pass (PassReason reason, bool)
                        _next = ContentTime ();
                }
        }
-
-       return false;
 }
 
 void
@@ -218,12 +208,8 @@ DCPDecoder::get_readers ()
 }
 
 void
-DCPDecoder::seek (ContentTime t, bool accurate)
+DCPDecoder::seek (ContentTime t, bool)
 {
-       video->seek (t, accurate);
-       audio->seek (t, accurate);
-       subtitle->seek (t, accurate);
-
        _reel = _reels.begin ();
        _offset = 0;
        get_readers ();
@@ -236,52 +222,6 @@ DCPDecoder::seek (ContentTime t, bool accurate)
        _next = t;
 }
 
-
-list<ContentTimePeriod>
-DCPDecoder::image_subtitles_during (ContentTimePeriod, bool) const
-{
-       return list<ContentTimePeriod> ();
-}
-
-list<ContentTimePeriod>
-DCPDecoder::text_subtitles_during (ContentTimePeriod period, bool starting) const
-{
-       /* XXX: inefficient */
-
-       list<ContentTimePeriod> ctp;
-       double const vfr = _dcp_content->active_video_frame_rate ();
-
-       int offset = 0;
-
-       BOOST_FOREACH (shared_ptr<dcp::Reel> r, _reels) {
-               if (!r->main_subtitle ()) {
-                       offset += r->main_picture()->duration();
-                       continue;
-               }
-
-               int64_t const entry_point = r->main_subtitle()->entry_point ();
-
-               list<dcp::SubtitleString> subs = r->main_subtitle()->asset()->subtitles_during (
-                       dcp::Time (period.from.seconds(), 1000) - dcp::Time (offset - entry_point, vfr, vfr),
-                       dcp::Time (period.to.seconds(), 1000) - dcp::Time (offset - entry_point, vfr, vfr),
-                       starting
-                       );
-
-               BOOST_FOREACH (dcp::SubtitleString const & s, subs) {
-                       ctp.push_back (
-                               ContentTimePeriod (
-                                       ContentTime::from_seconds (s.in().as_seconds ()) + ContentTime::from_frames (offset - entry_point, vfr),
-                                       ContentTime::from_seconds (s.out().as_seconds ()) + ContentTime::from_frames (offset - entry_point, vfr)
-                                       )
-                               );
-               }
-
-               offset += r->main_subtitle()->duration();
-       }
-
-       return ctp;
-}
-
 void
 DCPDecoder::set_decode_referenced ()
 {
index b1b2605..84deab1 100644 (file)
@@ -47,17 +47,15 @@ public:
 
        void set_decode_referenced ();
 
+       void pass ();
+       void seek (ContentTime t, bool accurate);
+
 private:
        friend struct dcp_subtitle_within_dcp_test;
 
-       bool pass (PassReason, bool accurate);
-       void seek (ContentTime t, bool accurate);
        void next_reel ();
        void get_readers ();
 
-       std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod, bool starting) const;
-       std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod, bool starting) const;
-
        /** Time of next thing to return from pass relative to the start of _reel */
        ContentTime _next;
        std::list<boost::shared_ptr<dcp::Reel> > _reels;
index 824ddba..9db3254 100644 (file)
@@ -30,15 +30,7 @@ using boost::bind;
 
 DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr<const DCPSubtitleContent> content, shared_ptr<Log> log)
 {
-       subtitle.reset (
-               new SubtitleDecoder (
-                       this,
-                       content->subtitle,
-                       log,
-                       bind (&DCPSubtitleDecoder::image_subtitles_during, this, _1, _2),
-                       bind (&DCPSubtitleDecoder::text_subtitles_during, this, _1, _2)
-                       )
-               );
+       subtitle.reset (new SubtitleDecoder (this, content->subtitle, log));
 
        shared_ptr<dcp::SubtitleAsset> c (load (content->path (0)));
        _subtitles = c->subtitles ();
@@ -46,10 +38,8 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr<const DCPSubtitleContent> con
 }
 
 void
-DCPSubtitleDecoder::seek (ContentTime time, bool accurate)
+DCPSubtitleDecoder::seek (ContentTime time, bool)
 {
-       subtitle->seek (time, accurate);
-
        _next = _subtitles.begin ();
        list<dcp::SubtitleString>::const_iterator i = _subtitles.begin ();
        while (i != _subtitles.end() && ContentTime::from_seconds (_next->in().as_seconds()) < time) {
@@ -57,11 +47,11 @@ DCPSubtitleDecoder::seek (ContentTime time, bool accurate)
        }
 }
 
-bool
-DCPSubtitleDecoder::pass (PassReason, bool)
+void
+DCPSubtitleDecoder::pass ()
 {
        if (_next == _subtitles.end ()) {
-               return true;
+               return;
        }
 
        /* Gather all subtitles with the same time period that are next
@@ -79,35 +69,8 @@ DCPSubtitleDecoder::pass (PassReason, bool)
                ++_next;
        }
 
-       subtitle->give_text (p, s);
-
-       return false;
-}
-
-list<ContentTimePeriod>
-DCPSubtitleDecoder::image_subtitles_during (ContentTimePeriod, bool) const
-{
-       return list<ContentTimePeriod> ();
-}
-
-list<ContentTimePeriod>
-DCPSubtitleDecoder::text_subtitles_during (ContentTimePeriod p, bool starting) const
-{
-       /* XXX: inefficient */
-
-       list<ContentTimePeriod> d;
-
-       for (list<dcp::SubtitleString>::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
-               ContentTimePeriod period = content_time_period (*i);
-               if ((starting && p.contains(period.from)) || (!starting && p.overlap(period))) {
-                       d.push_back (period);
-               }
-       }
-
-       d.sort ();
-       d.unique ();
-
-       return d;
+       subtitle->emit_text (p, s);
+       subtitle->set_position (p.from);
 }
 
 ContentTimePeriod
index b6e9aa6..076dc3f 100644 (file)
@@ -28,13 +28,10 @@ class DCPSubtitleDecoder : public DCPSubtitle, public Decoder
 public:
        DCPSubtitleDecoder (boost::shared_ptr<const DCPSubtitleContent>, boost::shared_ptr<Log> log);
 
-protected:
-       bool pass (PassReason, bool accurate);
+       void pass ();
        void seek (ContentTime time, bool accurate);
 
 private:
-       std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod, bool starting) const;
-       std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod, bool starting) const;
        ContentTimePeriod content_time_period (dcp::SubtitleString s) const;
 
        std::list<dcp::SubtitleString> _subtitles;
index 114e2eb..785fb96 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 */
 
 #include "decoder.h"
-#include "decoder_part.h"
-#include <iostream>
+#include "video_decoder.h"
+#include "audio_decoder.h"
+#include "subtitle_decoder.h"
 
-using std::cout;
-using boost::optional;
-
-void
-Decoder::maybe_seek (optional<ContentTime> position, ContentTime time, bool accurate)
+ContentTime
+Decoder::position () const
 {
-       if (position && (time >= position.get() && time < (position.get() + ContentTime::from_seconds(1)))) {
-               /* No need to seek: caller should just pass() */
-               return;
+       ContentTime pos;
+
+       if (video && video->position()) {
+               pos = min (pos, video->position().get());
+       }
+
+       if (audio && audio->position()) {
+               pos = min (pos, audio->position().get());
+       }
+
+       if (subtitle && subtitle->position()) {
+               pos = min (pos, subtitle->position().get());
        }
 
-       seek (time, accurate);
+       return pos;
 }
index 3717d93..f70eca8 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -47,32 +47,10 @@ public:
        boost::shared_ptr<AudioDecoder> audio;
        boost::shared_ptr<SubtitleDecoder> subtitle;
 
-       enum PassReason {
-               PASS_REASON_VIDEO,
-               PASS_REASON_AUDIO,
-               PASS_REASON_SUBTITLE
-       };
-
-       /** @return true if this decoder has already returned all its data and will give no more */
-       virtual bool pass (PassReason, bool accurate) = 0;
-
-       /** Ensure that any future get() calls return data that reflect
-        *  changes in our content's settings.
-        */
-       virtual void reset () {}
-
-       void maybe_seek (boost::optional<ContentTime> position, ContentTime time, bool accurate);
-
-private:
-       /** Seek so that the next pass() will yield the next thing
-        *  (video/sound frame, subtitle etc.) at or after the requested
-        *  time.  Pass accurate = true to try harder to ensure that, at worst,
-        *  the next thing we yield comes before `time'.  This may entail
-        *  seeking some way before `time' to be on the safe side.
-        *  Alternatively, if seeking is 100% accurate for this decoder,
-        *  it may seek to just the right spot.
-        */
+       virtual void pass () = 0;
        virtual void seek (ContentTime time, bool accurate) = 0;
+
+       ContentTime position () const;
 };
 
 #endif
index 6d53e4a..d8f9883 100644 (file)
@@ -30,9 +30,3 @@ DecoderPart::DecoderPart (Decoder* parent, shared_ptr<Log> log)
 {
 
 }
-
-void
-DecoderPart::maybe_seek (ContentTime time, bool accurate)
-{
-       _parent->maybe_seek (position(), time, accurate);
-}
index f2ab96d..3659477 100644 (file)
@@ -43,8 +43,6 @@ public:
 
        virtual boost::optional<ContentTime> position () const = 0;
 
-       void maybe_seek (ContentTime time, bool accurate);
-
 protected:
        Decoder* _parent;
        boost::shared_ptr<Log> _log;
index 0e782c9..44e1b2a 100644 (file)
@@ -411,28 +411,6 @@ FFmpegContent::identifier () const
        return s;
 }
 
-list<ContentTimePeriod>
-FFmpegContent::image_subtitles_during (ContentTimePeriod period, bool starting) const
-{
-       shared_ptr<FFmpegSubtitleStream> stream = subtitle_stream ();
-       if (!stream) {
-               return list<ContentTimePeriod> ();
-       }
-
-       return stream->image_subtitles_during (period, starting);
-}
-
-list<ContentTimePeriod>
-FFmpegContent::text_subtitles_during (ContentTimePeriod period, bool starting) const
-{
-       shared_ptr<FFmpegSubtitleStream> stream = subtitle_stream ();
-       if (!stream) {
-               return list<ContentTimePeriod> ();
-       }
-
-       return stream->text_subtitles_during (period, starting);
-}
-
 void
 FFmpegContent::set_default_colour_conversion ()
 {
index 91caac2..f6553df 100644 (file)
@@ -91,9 +91,6 @@ public:
                return _first_video;
        }
 
-       std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod, bool starting) const;
-       std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod, bool starting) const;
-
        void signal_subtitle_stream_changed ();
 
 private:
index b6b6e59..32903a2 100644 (file)
@@ -94,15 +94,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log>
        }
 
        if (c->subtitle) {
-               subtitle.reset (
-                       new SubtitleDecoder (
-                               this,
-                               c->subtitle,
-                               log,
-                               bind (&FFmpegDecoder::image_subtitles_during, this, _1, _2),
-                               bind (&FFmpegDecoder::text_subtitles_during, this, _1, _2)
-                               )
-                       );
+               subtitle.reset (new SubtitleDecoder (this, c->subtitle, log));
        }
 }
 
@@ -124,8 +116,8 @@ FFmpegDecoder::flush ()
        }
 }
 
-bool
-FFmpegDecoder::pass (PassReason reason, bool accurate)
+void
+FFmpegDecoder::pass ()
 {
        int r = av_read_frame (_format_context, &_packet);
 
@@ -142,22 +134,21 @@ FFmpegDecoder::pass (PassReason reason, bool accurate)
                }
 
                flush ();
-               return true;
+               return;
        }
 
        int const si = _packet.stream_index;
        shared_ptr<const FFmpegContent> fc = _ffmpeg_content;
 
-       if (_video_stream && si == _video_stream.get() && !video->ignore() && (accurate || reason != PASS_REASON_SUBTITLE)) {
+       if (_video_stream && si == _video_stream.get() && !video->ignore()) {
                decode_video_packet ();
        } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index (_format_context, si)) {
                decode_subtitle_packet ();
-       } else if (accurate || reason != PASS_REASON_SUBTITLE) {
+       } else {
                decode_audio_packet ();
        }
 
        av_packet_unref (&_packet);
-       return false;
 }
 
 /** @param data pointer to array of pointers to buffers.
@@ -307,18 +298,6 @@ FFmpegDecoder::bytes_per_audio_sample (shared_ptr<FFmpegAudioStream> stream) con
 void
 FFmpegDecoder::seek (ContentTime time, bool accurate)
 {
-       if (video) {
-               video->seek (time, accurate);
-       }
-
-       if (audio) {
-               audio->seek (time, accurate);
-       }
-
-       if (subtitle) {
-               subtitle->seek (time, accurate);
-       }
-
        /* If we are doing an `accurate' seek, we need to use pre-roll, as
           we don't really know what the seek will give us.
        */
@@ -428,7 +407,7 @@ FFmpegDecoder::decode_audio_packet ()
 
                        /* Give this data provided there is some, and its time is sane */
                        if (ct >= ContentTime() && data->frames() > 0) {
-                               audio->give (*stream, data, ct);
+                               audio->emit (*stream, data, ct);
                        }
                }
 
@@ -473,7 +452,7 @@ FFmpegDecoder::decode_video_packet ()
 
                if (i->second != AV_NOPTS_VALUE) {
                        double const pts = i->second * av_q2d (_format_context->streams[_video_stream.get()]->time_base) + _pts_offset.seconds ();
-                       video->give (
+                       video->emit (
                                shared_ptr<ImageProxy> (new RawImageProxy (image)),
                                llrint (pts * _ffmpeg_content->active_video_frame_rate ())
                                );
@@ -534,18 +513,6 @@ FFmpegDecoder::decode_subtitle_packet ()
        avsubtitle_free (&sub);
 }
 
-list<ContentTimePeriod>
-FFmpegDecoder::image_subtitles_during (ContentTimePeriod p, bool starting) const
-{
-       return _ffmpeg_content->image_subtitles_during (p, starting);
-}
-
-list<ContentTimePeriod>
-FFmpegDecoder::text_subtitles_during (ContentTimePeriod p, bool starting) const
-{
-       return _ffmpeg_content->text_subtitles_during (p, starting);
-}
-
 void
 FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimePeriod period)
 {
@@ -616,7 +583,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimeP
                static_cast<double> (rect->h) / target_height
                );
 
-       subtitle->give_image (period, image, scaled_rect);
+       subtitle->emit_image (period, image, scaled_rect);
 }
 
 void
@@ -636,6 +603,6 @@ FFmpegDecoder::decode_ass_subtitle (string ass, ContentTimePeriod period)
        list<sub::RawSubtitle> raw = sub::SSAReader::parse_line (base, bits[9]);
 
        BOOST_FOREACH (sub::Subtitle const & i, sub::collect<list<sub::Subtitle> > (raw)) {
-               subtitle->give_text (period, i);
+               subtitle->emit_text (period, i);
        }
 }
index 76755c1..82472c1 100644 (file)
@@ -46,11 +46,12 @@ class FFmpegDecoder : public FFmpeg, public Decoder
 public:
        FFmpegDecoder (boost::shared_ptr<const FFmpegContent>, boost::shared_ptr<Log>);
 
+       void pass ();
+       void seek (ContentTime time, bool);
+
 private:
        friend struct ::ffmpeg_pts_offset_test;
 
-       bool pass (PassReason, bool accurate);
-       void seek (ContentTime time, bool);
        void flush ();
 
        AVSampleFormat audio_sample_format (boost::shared_ptr<FFmpegAudioStream> stream) const;
@@ -66,9 +67,6 @@ private:
        void maybe_add_subtitle ();
        boost::shared_ptr<AudioBuffers> deinterleave_audio (boost::shared_ptr<FFmpegAudioStream> stream) const;
 
-       std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod, bool starting) const;
-       std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod, bool starting) const;
-
        boost::shared_ptr<Log> _log;
 
        std::list<boost::shared_ptr<VideoFilterGraph> > _filter_graphs;
index d389714..62accfa 100644 (file)
@@ -132,42 +132,6 @@ FFmpegSubtitleStream::add_text_subtitle (string id, ContentTimePeriod period)
        _text_subtitles[id] = period;
 }
 
-list<ContentTimePeriod>
-FFmpegSubtitleStream::image_subtitles_during (ContentTimePeriod period, bool starting) const
-{
-       return subtitles_during (period, starting, _image_subtitles);
-}
-
-list<ContentTimePeriod>
-FFmpegSubtitleStream::text_subtitles_during (ContentTimePeriod period, bool starting) const
-{
-       return subtitles_during (period, starting, _text_subtitles);
-}
-
-struct PeriodSorter
-{
-       bool operator() (ContentTimePeriod const & a, ContentTimePeriod const & b) {
-               return a.from < b.from;
-       }
-};
-
-list<ContentTimePeriod>
-FFmpegSubtitleStream::subtitles_during (ContentTimePeriod period, bool starting, PeriodMap const & subs) const
-{
-       list<ContentTimePeriod> d;
-
-       /* XXX: inefficient */
-       for (map<string, ContentTimePeriod>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
-               if ((starting && period.contains(i->second.from)) || (!starting && period.overlap(i->second))) {
-                       d.push_back (i->second);
-               }
-       }
-
-       d.sort (PeriodSorter ());
-
-       return d;
-}
-
 ContentTime
 FFmpegSubtitleStream::find_subtitle_to (string id) const
 {
index 7c4f8cd..6133440 100644 (file)
@@ -37,9 +37,6 @@ public:
        void add_image_subtitle (std::string id, ContentTimePeriod period);
        void add_text_subtitle (std::string id, ContentTimePeriod period);
        void set_subtitle_to (std::string id, ContentTime to);
-       bool unknown_to (std::string id) const;
-       std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod period, bool starting) const;
-       std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod period, bool starting) const;
        ContentTime find_subtitle_to (std::string id) const;
        void add_offset (ContentTime offset);
        void set_colour (RGBA from, RGBA to);
@@ -53,7 +50,6 @@ private:
        typedef std::map<std::string, ContentTimePeriod> PeriodMap;
 
        void as_xml (xmlpp::Node *, PeriodMap const & subs, std::string node) const;
-       std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod period, bool starting, PeriodMap const & subs) const;
 
        PeriodMap _image_subtitles;
        PeriodMap _text_subtitles;
index dae7366..b084168 100644 (file)
@@ -44,11 +44,11 @@ ImageDecoder::ImageDecoder (shared_ptr<const ImageContent> c, shared_ptr<Log> lo
        video.reset (new VideoDecoder (this, c, log));
 }
 
-bool
-ImageDecoder::pass (PassReason, bool)
+void
+ImageDecoder::pass ()
 {
        if (_frame_video_position >= _image_content->video->length()) {
-               return true;
+               return;
        }
 
        if (!_image_content->still() || !_image) {
@@ -72,14 +72,14 @@ ImageDecoder::pass (PassReason, bool)
                }
        }
 
-       video->give (_image, _frame_video_position);
+       video->set_position (ContentTime::from_frames (_frame_video_position, _image_content->active_video_frame_rate ()));
+       video->emit (_image, _frame_video_position);
        ++_frame_video_position;
-       return false;
+       return;
 }
 
 void
-ImageDecoder::seek (ContentTime time, bool accurate)
+ImageDecoder::seek (ContentTime time, bool)
 {
-       video->seek (time, accurate);
        _frame_video_position = time.frames_round (_image_content->active_video_frame_rate ());
 }
index d023636..7978f34 100644 (file)
@@ -33,10 +33,11 @@ public:
                return _image_content;
        }
 
-private:
-       bool pass (Decoder::PassReason, bool);
+       void pass ();
        void seek (ContentTime, bool);
 
+private:
+
        boost::shared_ptr<const ImageContent> _image_content;
        boost::shared_ptr<ImageProxy> _image;
        Frame _frame_video_position;
index 57bb0c3..3451b89 100644 (file)
@@ -148,7 +148,21 @@ Player::setup_pieces ()
                        dcp->set_decode_referenced ();
                }
 
-               _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc)));
+               shared_ptr<Piece> piece (new Piece (i, decoder, frc));
+               _pieces.push_back (piece);
+
+               if (decoder->video) {
+                       decoder->video->Data.connect (bind (&Player::video, this, weak_ptr<Piece> (piece), _1));
+               }
+
+               if (decoder->audio) {
+                       decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1));
+               }
+
+               if (decoder->subtitle) {
+                       decoder->subtitle->ImageData.connect (bind (&Player::image_subtitle, this, weak_ptr<Piece> (piece), _1));
+                       decoder->subtitle->TextData.connect (bind (&Player::text_subtitle, this, weak_ptr<Piece> (piece), _1));
+               }
        }
 
        _have_valid_pieces = true;
@@ -187,19 +201,7 @@ Player::playlist_content_changed (weak_ptr<Content> w, int property, bool freque
                property == SubtitleContentProperty::OUTLINE_WIDTH ||
                property == SubtitleContentProperty::Y_SCALE ||
                property == SubtitleContentProperty::FADE_IN ||
-               property == SubtitleContentProperty::FADE_OUT
-               ) {
-
-               /* These changes just need the pieces' decoders to be reset.
-                  It's quite possible that other changes could be handled by
-                  this branch rather than the _have_valid_pieces = false branch
-                  above.  This would make things a lot faster.
-               */
-
-               reset_pieces ();
-               Changed (frequent);
-
-       } else if (
+               property == SubtitleContentProperty::FADE_OUT ||
                property == ContentProperty::VIDEO_FRAME_RATE ||
                property == SubtitleContentProperty::USE ||
                property == SubtitleContentProperty::X_OFFSET ||
@@ -318,225 +320,6 @@ Player::black_player_video_frame (DCPTime time) const
        );
 }
 
-/** @return All PlayerVideos at the given time.  There may be none if the content
- *  at `time' is a DCP which we are passing through (i.e. referring to by reference)
- *  or 2 if we have 3D.
- */
-list<shared_ptr<PlayerVideo> >
-Player::get_video (DCPTime time, bool accurate)
-{
-       if (!_have_valid_pieces) {
-               setup_pieces ();
-       }
-
-       /* Find subtitles for possible burn-in */
-
-       PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false, true, accurate);
-
-       list<PositionImage> sub_images;
-
-       /* Image subtitles */
-       list<PositionImage> c = transform_image_subtitles (ps.image);
-       copy (c.begin(), c.end(), back_inserter (sub_images));
-
-       /* Text subtitles (rendered to an image) */
-       if (!ps.text.empty ()) {
-               list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size, time);
-               copy (s.begin (), s.end (), back_inserter (sub_images));
-       }
-
-       optional<PositionImage> subtitles;
-       if (!sub_images.empty ()) {
-               subtitles = merge (sub_images);
-       }
-
-       /* Find pieces containing video which is happening now */
-
-       list<shared_ptr<Piece> > ov = overlaps (
-               time,
-               time + DCPTime::from_frames (1, _film->video_frame_rate ()),
-               &has_video
-               );
-
-       list<shared_ptr<PlayerVideo> > pvf;
-
-       if (ov.empty ()) {
-               /* No video content at this time */
-               pvf.push_back (black_player_video_frame (time));
-       } else {
-               /* Some video content at this time */
-               shared_ptr<Piece> last = *(ov.rbegin ());
-               VideoFrameType const last_type = last->content->video->frame_type ();
-
-               /* Get video from appropriate piece(s) */
-               BOOST_FOREACH (shared_ptr<Piece> piece, ov) {
-
-                       shared_ptr<VideoDecoder> decoder = piece->decoder->video;
-                       DCPOMATIC_ASSERT (decoder);
-
-                       shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (piece->content);
-                       if (dcp_content && dcp_content->reference_video () && !_play_referenced) {
-                               continue;
-                       }
-
-                       bool const use =
-                               /* always use the last video */
-                               piece == last ||
-                               /* with a corresponding L/R eye if appropriate */
-                               (last_type == VIDEO_FRAME_TYPE_3D_LEFT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) ||
-                               (last_type == VIDEO_FRAME_TYPE_3D_RIGHT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT);
-
-                       if (use) {
-                               /* We want to use this piece */
-                               list<ContentVideo> content_video = decoder->get (dcp_to_content_video (piece, time), accurate);
-                               if (content_video.empty ()) {
-                                       pvf.push_back (black_player_video_frame (time));
-                               } else {
-                                       dcp::Size image_size = piece->content->video->scale().size (
-                                               piece->content->video, _video_container_size, _film->frame_size ()
-                                               );
-
-                                       for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
-                                               pvf.push_back (
-                                                       shared_ptr<PlayerVideo> (
-                                                               new PlayerVideo (
-                                                                       i->image,
-                                                                       time,
-                                                                       piece->content->video->crop (),
-                                                                       piece->content->video->fade (i->frame.index()),
-                                                                       image_size,
-                                                                       _video_container_size,
-                                                                       i->frame.eyes(),
-                                                                       i->part,
-                                                                       piece->content->video->colour_conversion ()
-                                                                       )
-                                                               )
-                                                       );
-                                       }
-                               }
-                       } else {
-                               /* Discard unused video */
-                               decoder->get (dcp_to_content_video (piece, time), accurate);
-                       }
-               }
-       }
-
-       if (subtitles) {
-               BOOST_FOREACH (shared_ptr<PlayerVideo> p, pvf) {
-                       p->set_subtitle (subtitles.get ());
-               }
-       }
-
-       return pvf;
-}
-
-/** @return Audio data or 0 if the only audio data here is referenced DCP data */
-shared_ptr<AudioBuffers>
-Player::get_audio (DCPTime time, DCPTime length, bool accurate)
-{
-       if (!_have_valid_pieces) {
-               setup_pieces ();
-       }
-
-       Frame const length_frames = length.frames_round (_film->audio_frame_rate ());
-
-       shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
-       audio->make_silent ();
-
-       list<shared_ptr<Piece> > ov = overlaps (time, time + length, has_audio);
-       if (ov.empty ()) {
-               return audio;
-       }
-
-       bool all_referenced = true;
-       BOOST_FOREACH (shared_ptr<Piece> i, ov) {
-               shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (i->content);
-               if (i->content->audio && (!dcp_content || !dcp_content->reference_audio ())) {
-                       /* There is audio content which is not from a DCP or not set to be referenced */
-                       all_referenced = false;
-               }
-       }
-
-       if (all_referenced && !_play_referenced) {
-               return shared_ptr<AudioBuffers> ();
-       }
-
-       BOOST_FOREACH (shared_ptr<Piece> i, ov) {
-
-               DCPOMATIC_ASSERT (i->content->audio);
-               shared_ptr<AudioDecoder> decoder = i->decoder->audio;
-               DCPOMATIC_ASSERT (decoder);
-
-               /* The time that we should request from the content */
-               DCPTime request = time - DCPTime::from_seconds (i->content->audio->delay() / 1000.0);
-               Frame request_frames = length_frames;
-               DCPTime offset;
-               if (request < DCPTime ()) {
-                       /* We went off the start of the content, so we will need to offset
-                          the stuff we get back.
-                       */
-                       offset = -request;
-                       request_frames += request.frames_round (_film->audio_frame_rate ());
-                       if (request_frames < 0) {
-                               request_frames = 0;
-                       }
-                       request = DCPTime ();
-               }
-
-               Frame const content_frame = dcp_to_resampled_audio (i, request);
-
-               BOOST_FOREACH (AudioStreamPtr j, i->content->audio->streams ()) {
-
-                       if (j->channels() == 0) {
-                               /* Some content (e.g. DCPs) can have streams with no channels */
-                               continue;
-                       }
-
-                       /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
-                       ContentAudio all = decoder->get (j, content_frame, request_frames, accurate);
-
-                       /* Gain */
-                       if (i->content->audio->gain() != 0) {
-                               shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
-                               gain->apply_gain (i->content->audio->gain ());
-                               all.audio = gain;
-                       }
-
-                       /* Remap channels */
-                       shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
-                       dcp_mapped->make_silent ();
-                       AudioMapping map = j->mapping ();
-                       for (int i = 0; i < map.input_channels(); ++i) {
-                               for (int j = 0; j < _film->audio_channels(); ++j) {
-                                       if (map.get (i, j) > 0) {
-                                               dcp_mapped->accumulate_channel (
-                                                       all.audio.get(),
-                                                       i,
-                                                       j,
-                                                       map.get (i, j)
-                                                       );
-                                       }
-                               }
-                       }
-
-                       if (_audio_processor) {
-                               dcp_mapped = _audio_processor->run (dcp_mapped, _film->audio_channels ());
-                       }
-
-                       all.audio = dcp_mapped;
-
-                       audio->accumulate_frames (
-                               all.audio.get(),
-                               content_frame - all.frame,
-                               offset.frames_round (_film->audio_frame_rate()),
-                               min (Frame (all.audio->frames()), request_frames)
-                               );
-               }
-       }
-
-       return audio;
-}
-
 Frame
 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
 {
@@ -585,82 +368,6 @@ Player::content_subtitle_to_dcp (shared_ptr<const Piece> piece, ContentTime t) c
        return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
 }
 
-/** @param burnt true to return only subtitles to be burnt, false to return only
- *  subtitles that should not be burnt.  This parameter will be ignored if
- *  _always_burn_subtitles is true; in this case, all subtitles will be returned.
- */
-PlayerSubtitles
-Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt, bool accurate)
-{
-       list<shared_ptr<Piece> > subs = overlaps (time, time + length, has_subtitle);
-
-       PlayerSubtitles ps (time);
-
-       for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
-               if (!(*j)->content->subtitle->use () || (!_always_burn_subtitles && (burnt != (*j)->content->subtitle->burn ()))) {
-                       continue;
-               }
-
-               shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> ((*j)->content);
-               if (dcp_content && dcp_content->reference_subtitle () && !_play_referenced) {
-                       continue;
-               }
-
-               shared_ptr<SubtitleDecoder> subtitle_decoder = (*j)->decoder->subtitle;
-               ContentTime const from = dcp_to_content_subtitle (*j, time);
-               /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
-               ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
-
-               list<ContentImageSubtitle> image = subtitle_decoder->get_image (ContentTimePeriod (from, to), starting, accurate);
-               for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
-
-                       /* Apply content's subtitle offsets */
-                       i->sub.rectangle.x += (*j)->content->subtitle->x_offset ();
-                       i->sub.rectangle.y += (*j)->content->subtitle->y_offset ();
-
-                       /* Apply content's subtitle scale */
-                       i->sub.rectangle.width *= (*j)->content->subtitle->x_scale ();
-                       i->sub.rectangle.height *= (*j)->content->subtitle->y_scale ();
-
-                       /* Apply a corrective translation to keep the subtitle centred after that scale */
-                       i->sub.rectangle.x -= i->sub.rectangle.width * ((*j)->content->subtitle->x_scale() - 1);
-                       i->sub.rectangle.y -= i->sub.rectangle.height * ((*j)->content->subtitle->y_scale() - 1);
-
-                       ps.image.push_back (i->sub);
-               }
-
-               list<ContentTextSubtitle> text = subtitle_decoder->get_text (ContentTimePeriod (from, to), starting, accurate);
-               BOOST_FOREACH (ContentTextSubtitle& ts, text) {
-                       BOOST_FOREACH (dcp::SubtitleString s, ts.subs) {
-                               s.set_h_position (s.h_position() + (*j)->content->subtitle->x_offset ());
-                               s.set_v_position (s.v_position() + (*j)->content->subtitle->y_offset ());
-                               float const xs = (*j)->content->subtitle->x_scale();
-                               float const ys = (*j)->content->subtitle->y_scale();
-                               float size = s.size();
-
-                               /* Adjust size to express the common part of the scaling;
-                                  e.g. if xs = ys = 0.5 we scale size by 2.
-                               */
-                               if (xs > 1e-5 && ys > 1e-5) {
-                                       size *= 1 / min (1 / xs, 1 / ys);
-                               }
-                               s.set_size (size);
-
-                               /* Then express aspect ratio changes */
-                               if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
-                                       s.set_aspect_adjust (xs / ys);
-                               }
-                               s.set_in (dcp::Time(content_subtitle_to_dcp (*j, ts.period().from).seconds(), 1000));
-                               s.set_out (dcp::Time(content_subtitle_to_dcp (*j, ts.period().to).seconds(), 1000));
-                               ps.text.push_back (SubtitleString (s, (*j)->content->subtitle->outline_width()));
-                               ps.add_fonts ((*j)->content->subtitle->fonts ());
-                       }
-               }
-       }
-
-       return ps;
-}
-
 list<shared_ptr<Font> >
 Player::get_subtitle_fonts ()
 {
@@ -803,10 +510,83 @@ Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> va
        return overlaps;
 }
 
-void
-Player::reset_pieces ()
+bool
+Player::pass ()
 {
+       if (!_have_valid_pieces) {
+               setup_pieces ();
+       }
+
+       shared_ptr<Piece> earliest;
+       DCPTime earliest_position;
        BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
-               i->decoder->reset ();
+               /* Convert i->decoder->position() to DCPTime and work out the earliest */
+       }
+
+       earliest->decoder->pass ();
+
+       /* XXX: collect audio and maybe emit some */
+}
+
+void
+Player::video (weak_ptr<Piece> wp, ContentVideo video)
+{
+       shared_ptr<Piece> piece = wp.lock ();
+       if (!piece) {
+               return;
+       }
+
+       /* Get subs to burn in and burn them in */
+
+
+       /* Fill gaps */
+
+       DCPTime time = content_video_to_dcp (piece, video.frame.index());
+
+       dcp::Size image_size = piece->content->video->scale().size (
+               piece->content->video, _video_container_size, _film->frame_size ()
+               );
+
+       Video (
+               shared_ptr<PlayerVideo> (
+                       new PlayerVideo (
+                               video.image,
+                               time,
+                               piece->content->video->crop (),
+                               piece->content->video->fade (video.frame.index()),
+                               image_size,
+                               _video_container_size,
+                               video.frame.eyes(),
+                               video.part,
+                               piece->content->video->colour_conversion ()
+                               )
+                       )
+               );
+
+}
+
+void
+Player::audio (weak_ptr<Piece> piece, ContentAudio video)
+{
+       /* Put into merge buffer */
+}
+
+void
+Player::image_subtitle (weak_ptr<Piece> piece, ContentImageSubtitle subtitle)
+{
+       /* Store for video to see */
+}
+
+void
+Player::text_subtitle (weak_ptr<Piece> piece, ContentTextSubtitle subtitle)
+{
+       /* Store for video to see, or emit */
+}
+
+void
+Player::seek (DCPTime time, bool accurate)
+{
+       if (accurate) {
+               _last_video = time - DCPTime::from_frames (1, _film->video_frame_rate ());
        }
 }
index cb3403c..c0e0f9f 100644 (file)
@@ -26,6 +26,9 @@
 #include "content.h"
 #include "position_image.h"
 #include "piece.h"
+#include "content_video.h"
+#include "content_audio.h"
+#include "content_subtitle.h"
 #include <boost/shared_ptr.hpp>
 #include <boost/enable_shared_from_this.hpp>
 #include <list>
@@ -48,9 +51,9 @@ class Player : public boost::enable_shared_from_this<Player>, public boost::nonc
 public:
        Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist> playlist);
 
-       std::list<boost::shared_ptr<PlayerVideo> > get_video (DCPTime time, bool accurate);
-       boost::shared_ptr<AudioBuffers> get_audio (DCPTime time, DCPTime length, bool accurate);
-       PlayerSubtitles get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt, bool accurate);
+       bool pass ();
+       void seek (DCPTime time, bool accurate);
+
        std::list<boost::shared_ptr<Font> > get_subtitle_fonts ();
        std::list<ReferencedReelAsset> get_reel_assets ();
 
@@ -70,6 +73,10 @@ public:
         */
        boost::signals2::signal<void (bool)> Changed;
 
+       boost::signals2::signal<void (boost::shared_ptr<PlayerVideo>)> Video;
+       boost::signals2::signal<void (boost::shared_ptr<AudioBuffers>, DCPTime)> Audio;
+       boost::signals2::signal<void (PlayerSubtitles)> Subtitle;
+
 private:
        friend class PlayerWrapper;
        friend class Piece;
@@ -79,7 +86,6 @@ private:
        friend struct player_time_calculation_test3;
 
        void setup_pieces ();
-       void reset_pieces ();
        void flush ();
        void film_changed (Film::Property);
        void playlist_changed ();
@@ -93,6 +99,10 @@ private:
        DCPTime content_subtitle_to_dcp (boost::shared_ptr<const Piece> piece, ContentTime t) const;
        boost::shared_ptr<PlayerVideo> black_player_video_frame (DCPTime) const;
        std::list<boost::shared_ptr<Piece> > overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid);
+       void video (boost::weak_ptr<Piece>, ContentVideo);
+       void audio (boost::weak_ptr<Piece>, ContentAudio);
+       void image_subtitle (boost::weak_ptr<Piece>, ContentImageSubtitle);
+       void text_subtitle (boost::weak_ptr<Piece>, ContentTextSubtitle);
 
        boost::shared_ptr<const Film> _film;
        boost::shared_ptr<const Playlist> _playlist;
@@ -118,6 +128,8 @@ private:
        /** true if we should `play' (i.e output) referenced DCP data (e.g. for preview) */
        bool _play_referenced;
 
+       DCPTime _last_video;
+
        boost::shared_ptr<AudioProcessor> _audio_processor;
 
        boost::signals2::scoped_connection _film_changed_connection;
index 3e6ee92..c437ec9 100644 (file)
@@ -40,14 +40,10 @@ using boost::function;
 SubtitleDecoder::SubtitleDecoder (
        Decoder* parent,
        shared_ptr<const SubtitleContent> c,
-       shared_ptr<Log> log,
-       function<list<ContentTimePeriod> (ContentTimePeriod, bool)> image_during,
-       function<list<ContentTimePeriod> (ContentTimePeriod, bool)> text_during
+       shared_ptr<Log> log
        )
        : DecoderPart (parent, log)
        , _content (c)
-       , _image_during (image_during)
-       , _text_during (text_during)
 {
 
 }
@@ -60,14 +56,13 @@ SubtitleDecoder::SubtitleDecoder (
  *  of the video frame)
  */
 void
-SubtitleDecoder::give_image (ContentTimePeriod period, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
+SubtitleDecoder::emit_image (ContentTimePeriod period, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
 {
-       _decoded_image.push_back (ContentImageSubtitle (period, image, rect));
-       _position = period.from;
+       ImageData (ContentImageSubtitle (period, image, rect));
 }
 
 void
-SubtitleDecoder::give_text (ContentTimePeriod period, list<dcp::SubtitleString> s)
+SubtitleDecoder::emit_text (ContentTimePeriod period, list<dcp::SubtitleString> s)
 {
        /* We must escape < and > in strings, otherwise they might confuse our subtitle
           renderer (which uses some HTML-esque markup to do bold/italic etc.)
@@ -79,115 +74,11 @@ SubtitleDecoder::give_text (ContentTimePeriod period, list<dcp::SubtitleString>
                i.set_text (t);
        }
 
-       _decoded_text.push_back (ContentTextSubtitle (period, s));
-       _position = period.to;
-}
-
-/** Get the subtitles that correspond to a given list of periods.
- *  @param subs Subtitles.
- *  @param sp Periods for which to extract subtitles from subs.
- */
-template <class T>
-list<T>
-SubtitleDecoder::get (list<T> const & subs, list<ContentTimePeriod> const & sp, ContentTimePeriod period, bool accurate)
-{
-       if (sp.empty ()) {
-               return list<T> ();
-       }
-
-       /* Find the time of the first subtitle we don't have in subs */
-       optional<ContentTime> missing;
-       BOOST_FOREACH (ContentTimePeriod i, sp) {
-               typename list<T>::const_iterator j = subs.begin();
-               while (j != subs.end() && j->period() != i) {
-                       ++j;
-               }
-               if (j == subs.end ()) {
-                       missing = i.from;
-                       break;
-               }
-       }
-
-       /* Suggest to our parent decoder that it might want to seek if we haven't got what we're being asked for */
-       if (missing) {
-               _log->log (
-                       String::compose (
-                               "SD suggests seek to %1 from %2",
-                               to_string (*missing),
-                               position() ? to_string(*position()) : "nowhere"),
-                       LogEntry::TYPE_DEBUG_DECODE);
-               maybe_seek (*missing, true);
-       }
-
-       /* Now enough pass() calls will either:
-        *  (a) give us what we want, or
-        *  (b) hit the end of the decoder.
-        */
-       while (!_parent->pass(Decoder::PASS_REASON_SUBTITLE, accurate) && (subs.empty() || (subs.back().period().to < sp.back().to))) {}
-
-       /* Now look for what we wanted in the data we have collected */
-       /* XXX: inefficient */
-
-       list<T> out;
-       BOOST_FOREACH (ContentTimePeriod i, sp) {
-               typename list<T>::const_iterator j = subs.begin();
-               while (j != subs.end() && j->period() != i) {
-                       ++j;
-               }
-               if (j != subs.end()) {
-                       out.push_back (*j);
-               }
-       }
-
-       /* Discard anything in _decoded_image_subtitles that is outside 5 seconds either side of period */
-
-       list<ContentImageSubtitle>::iterator i = _decoded_image.begin();
-       while (i != _decoded_image.end()) {
-               list<ContentImageSubtitle>::iterator tmp = i;
-               ++tmp;
-
-               if (
-                       i->period().to < (period.from - ContentTime::from_seconds (5)) ||
-                       i->period().from > (period.to + ContentTime::from_seconds (5))
-                       ) {
-                       _decoded_image.erase (i);
-               }
-
-               i = tmp;
-       }
-
-       return out;
-}
-
-list<ContentTextSubtitle>
-SubtitleDecoder::get_text (ContentTimePeriod period, bool starting, bool accurate)
-{
-       return get<ContentTextSubtitle> (_decoded_text, _text_during (period, starting), period, accurate);
-}
-
-list<ContentImageSubtitle>
-SubtitleDecoder::get_image (ContentTimePeriod period, bool starting, bool accurate)
-{
-       return get<ContentImageSubtitle> (_decoded_image, _image_during (period, starting), period, accurate);
-}
-
-void
-SubtitleDecoder::seek (ContentTime t, bool)
-{
-       _log->log (String::compose ("SD seek to %1", to_string(t)), LogEntry::TYPE_DEBUG_DECODE);
-       reset ();
-       _position.reset ();
-}
-
-void
-SubtitleDecoder::reset ()
-{
-       _decoded_text.clear ();
-       _decoded_image.clear ();
+       TextData (ContentTextSubtitle (period, s));
 }
 
 void
-SubtitleDecoder::give_text (ContentTimePeriod period, sub::Subtitle const & subtitle)
+SubtitleDecoder::emit_text (ContentTimePeriod period, sub::Subtitle const & subtitle)
 {
        /* See if our next subtitle needs to be placed on screen by us */
        bool needs_placement = false;
@@ -295,5 +186,5 @@ SubtitleDecoder::give_text (ContentTimePeriod period, sub::Subtitle const & subt
                }
        }
 
-       give_text (period, out);
+       emit_text (period, out);
 }
index e5e9316..904aaed 100644 (file)
@@ -27,6 +27,7 @@
 #include "content_subtitle.h"
 #include "decoder_part.h"
 #include <dcp/subtitle_string.h>
+#include <boost/signals2.hpp>
 
 namespace sub {
        class Subtitle;
@@ -44,46 +45,22 @@ public:
        SubtitleDecoder (
                Decoder* parent,
                boost::shared_ptr<const SubtitleContent>,
-               boost::shared_ptr<Log> log,
-               boost::function<std::list<ContentTimePeriod> (ContentTimePeriod, bool)> image_during,
-               boost::function<std::list<ContentTimePeriod> (ContentTimePeriod, bool)> text_during
+               boost::shared_ptr<Log> log
                );
 
-       std::list<ContentImageSubtitle> get_image (ContentTimePeriod period, bool starting, bool accurate);
-       std::list<ContentTextSubtitle> get_text (ContentTimePeriod period, bool starting, bool accurate);
-
-       void seek (ContentTime, bool);
-       void reset ();
-
-       void give_image (ContentTimePeriod period, boost::shared_ptr<Image>, dcpomatic::Rect<double>);
-       void give_text (ContentTimePeriod period, std::list<dcp::SubtitleString>);
-       void give_text (ContentTimePeriod period, sub::Subtitle const & subtitle);
+       void emit_image (ContentTimePeriod period, boost::shared_ptr<Image>, dcpomatic::Rect<double>);
+       void emit_text (ContentTimePeriod period, std::list<dcp::SubtitleString>);
+       void emit_text (ContentTimePeriod period, sub::Subtitle const & subtitle);
 
        boost::shared_ptr<const SubtitleContent> content () const {
                return _content;
        }
 
-       boost::optional<ContentTime> position () const {
-               return _position;
-       }
-
-       void reset_position () {
-               _position.reset ();
-       }
+       boost::signals2::signal<void (ContentImageSubtitle)> ImageData;
+       boost::signals2::signal<void (ContentTextSubtitle)> TextData;
 
 private:
-
-       std::list<ContentImageSubtitle> _decoded_image;
-       std::list<ContentTextSubtitle> _decoded_text;
        boost::shared_ptr<const SubtitleContent> _content;
-
-       template <class T>
-       std::list<T> get (std::list<T> const & subs, std::list<ContentTimePeriod> const & sp, ContentTimePeriod period, bool accurate);
-
-       boost::function<std::list<ContentTimePeriod> (ContentTimePeriod, bool)> _image_during;
-       boost::function<std::list<ContentTimePeriod> (ContentTimePeriod, bool)> _text_during;
-
-       boost::optional<ContentTime> _position;
 };
 
 #endif
index dd64cb5..b598087 100644 (file)
@@ -38,73 +38,31 @@ TextSubtitleDecoder::TextSubtitleDecoder (shared_ptr<const TextSubtitleContent>
        : TextSubtitle (content)
        , _next (0)
 {
-       subtitle.reset (
-               new SubtitleDecoder (
-                       this,
-                       content->subtitle,
-                       log,
-                       bind (&TextSubtitleDecoder::image_subtitles_during, this, _1, _2),
-                       bind (&TextSubtitleDecoder::text_subtitles_during, this, _1, _2)
-                       )
-               );
+       subtitle.reset (new SubtitleDecoder (this, content->subtitle, log));
 }
 
 void
-TextSubtitleDecoder::seek (ContentTime time, bool accurate)
+TextSubtitleDecoder::seek (ContentTime time, bool)
 {
-       subtitle->seek (time, accurate);
-
        _next = 0;
        while (_next < _subtitles.size() && ContentTime::from_seconds (_subtitles[_next].from.all_as_seconds ()) < time) {
                ++_next;
        }
 }
 
-bool
-TextSubtitleDecoder::pass (PassReason, bool)
+void
+TextSubtitleDecoder::pass ()
 {
        if (_next >= _subtitles.size ()) {
-               return true;
+               return;
        }
 
        ContentTimePeriod const p = content_time_period (_subtitles[_next]);
-       subtitle->give_text (p, _subtitles[_next]);
+       subtitle->emit_text (p, _subtitles[_next]);
+       subtitle->set_position (p.from);
 
        ++_next;
-       return false;
-}
-
-list<ContentTimePeriod>
-TextSubtitleDecoder::image_subtitles_during (ContentTimePeriod, bool) const
-{
-       return list<ContentTimePeriod> ();
-}
-
-list<ContentTimePeriod>
-TextSubtitleDecoder::text_subtitles_during (ContentTimePeriod p, bool starting) const
-{
-       /* XXX: inefficient */
-
-       list<ContentTimePeriod> d;
-
-       /* Only take `during' (not starting) subs if they overlap more than half the requested period;
-          here's the threshold for being significant.
-       */
-       ContentTime const significant (p.duration().get() / 2);
-
-       for (vector<sub::Subtitle>::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
-               ContentTimePeriod t = content_time_period (*i);
-               if (starting && p.contains(t.from)) {
-                       d.push_back (t);
-               } else if (!starting) {
-                       optional<ContentTimePeriod> const o = p.overlap (t);
-                       if (o && o->duration() > significant) {
-                               d.push_back (t);
-                       }
-               }
-       }
-
-       return d;
+       return;
 }
 
 ContentTimePeriod
@@ -115,9 +73,3 @@ TextSubtitleDecoder::content_time_period (sub::Subtitle s) const
                ContentTime::from_seconds (s.to.all_as_seconds())
                );
 }
-
-void
-TextSubtitleDecoder::reset ()
-{
-       subtitle->reset ();
-}
index 5477e6f..0133868 100644 (file)
@@ -31,14 +31,10 @@ class TextSubtitleDecoder : public Decoder, public TextSubtitle
 public:
        TextSubtitleDecoder (boost::shared_ptr<const TextSubtitleContent>, boost::shared_ptr<Log> log);
 
-protected:
        void seek (ContentTime time, bool accurate);
-       bool pass (PassReason, bool accurate);
-       void reset ();
+       void pass ();
 
 private:
-       std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod, bool starting) const;
-       std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod, bool starting) const;
        ContentTimePeriod content_time_period (sub::Subtitle s) const;
 
        size_t _next;
index ee099c7..da6b08c 100644 (file)
@@ -61,8 +61,17 @@ Transcoder::Transcoder (shared_ptr<const Film> film, weak_ptr<Job> j)
        , _writer (new Writer (film, j))
        , _encoder (new Encoder (film, _writer))
        , _finishing (false)
+       , _non_burnt_subtitles (false)
 {
+       _player->Video.connect (bind (&Transcoder::video, this, _1));
+       _player->Audio.connect (bind (&Transcoder::audio, this, _1, _2));
+       _player->Subtitle.connect (bind (&Transcoder::subtitle, this, _1));
 
+       BOOST_FOREACH (shared_ptr<const Content> c, _film->content ()) {
+               if (c->subtitle && c->subtitle->use() && !c->subtitle->burn()) {
+                       _non_burnt_subtitles = true;
+               }
+       }
 }
 
 void
@@ -77,54 +86,11 @@ Transcoder::go ()
                job->sub (_("Encoding"));
        }
 
-       DCPTime const frame = DCPTime::from_frames (1, _film->video_frame_rate ());
-       DCPTime const length = _film->length ();
-
-       int burnt_subtitles = 0;
-       int non_burnt_subtitles = 0;
-       BOOST_FOREACH (shared_ptr<const Content> c, _film->content ()) {
-               if (c->subtitle && c->subtitle->use()) {
-                       if (c->subtitle->burn()) {
-                               ++burnt_subtitles;
-                       } else {
-                               ++non_burnt_subtitles;
-                       }
-               }
-       }
-
-       if (non_burnt_subtitles) {
+       if (_non_burnt_subtitles) {
                _writer->write (_player->get_subtitle_fonts ());
        }
 
-       for (DCPTime t; t < length; t += frame) {
-
-               BOOST_FOREACH (shared_ptr<PlayerVideo> i, _player->get_video (t, true)) {
-                       if (!_film->three_d()) {
-                               /* 2D DCP */
-                               if (i->eyes() == EYES_RIGHT) {
-                                       /* Discard right-eye images */
-                                       continue;
-                               } else if (i->eyes() == EYES_LEFT) {
-                                       /* Use left-eye images for both eyes */
-                                       i->set_eyes (EYES_BOTH);
-                               }
-                       }
-
-                       _encoder->encode (i);
-               }
-
-               _writer->write (_player->get_audio (t, frame, true));
-
-               if (non_burnt_subtitles) {
-                       _writer->write (_player->get_subtitles (t, frame, true, false, true));
-               }
-
-               {
-                       shared_ptr<Job> job = _job.lock ();
-                       DCPOMATIC_ASSERT (job);
-                       job->set_progress (float(t.get()) / length.get());
-               }
-       }
+       while (!_player->pass ()) {}
 
        BOOST_FOREACH (ReferencedReelAsset i, _player->get_reel_assets ()) {
                _writer->write (i);
@@ -135,6 +101,35 @@ Transcoder::go ()
        _writer->finish ();
 }
 
+void
+Transcoder::video (shared_ptr<PlayerVideo> data)
+{
+       if (!_film->three_d() && data->eyes() == EYES_LEFT) {
+               /* Use left-eye images for both eyes */
+               data->set_eyes (EYES_BOTH);
+       }
+
+       _encoder->encode (data);
+}
+
+void
+Transcoder::audio (shared_ptr<AudioBuffers> data, DCPTime time)
+{
+       _writer->write (data);
+
+       shared_ptr<Job> job = _job.lock ();
+       DCPOMATIC_ASSERT (job);
+       job->set_progress (float(time.get()) / _film->length().get());
+}
+
+void
+Transcoder::subtitle (PlayerSubtitles data)
+{
+       if (_non_burnt_subtitles) {
+               _writer->write (data);
+       }
+}
+
 float
 Transcoder::current_encoding_rate () const
 {
index 14f3636..1b20bbf 100644 (file)
@@ -19,6 +19,7 @@
 */
 
 #include "types.h"
+#include "player_subtitles.h"
 #include <boost/weak_ptr.hpp>
 
 class Film;
@@ -26,6 +27,8 @@ class Encoder;
 class Player;
 class Writer;
 class Job;
+class PlayerVideo;
+class AudioBuffers;
 
 /** @class Transcoder */
 class Transcoder : public boost::noncopyable
@@ -44,10 +47,16 @@ public:
        }
 
 private:
+
+       void video (boost::shared_ptr<PlayerVideo>);
+       void audio (boost::shared_ptr<AudioBuffers>, DCPTime);
+       void subtitle (PlayerSubtitles);
+
        boost::shared_ptr<const Film> _film;
        boost::weak_ptr<Job> _job;
        boost::shared_ptr<Player> _player;
        boost::shared_ptr<Writer> _writer;
        boost::shared_ptr<Encoder> _encoder;
        bool _finishing;
+       bool _non_burnt_subtitles;
 };
index 0e9ee0c..6ec479a 100644 (file)
@@ -38,223 +38,9 @@ using boost::optional;
 
 VideoDecoder::VideoDecoder (Decoder* parent, shared_ptr<const Content> c, shared_ptr<Log> log)
        : DecoderPart (parent, log)
-#ifdef DCPOMATIC_DEBUG
-       , test_gaps (0)
-#endif
        , _content (c)
-       , _last_seek_accurate (true)
 {
-       _black_image.reset (new Image (AV_PIX_FMT_RGB24, _content->video->size(), true));
-       _black_image->make_black ();
-}
-
-list<ContentVideo>
-VideoDecoder::decoded (Frame frame)
-{
-       list<ContentVideo> output;
-
-       BOOST_FOREACH (ContentVideo const & i, _decoded) {
-               if (i.frame.index() == frame) {
-                       output.push_back (i);
-               }
-       }
-
-       return output;
-}
-
-/** Get all frames which exist in the content at a given frame index.
- *  @param frame Frame index.
- *  @param accurate true to try hard to return frames at the precise time that was requested, otherwise frames nearby may be returned.
- *  @return Frames; there may be none (if there is no video there), 1 for 2D or 2 for 3D.
- */
-list<ContentVideo>
-VideoDecoder::get (Frame frame, bool accurate)
-{
-       if (_no_data_frame && frame >= _no_data_frame.get()) {
-               return list<ContentVideo> ();
-       }
-
-       _log->log (String::compose ("VD has request for %1", frame), LogEntry::TYPE_DEBUG_DECODE);
-
-       /* See if we have frame, and suggest a seek if not */
-
-       list<ContentVideo>::const_iterator i = _decoded.begin ();
-       while (i != _decoded.end() && i->frame.index() != frame) {
-               _log->log (String::compose ("VD has stored %1 which is no good", i->frame.index()), LogEntry::TYPE_DEBUG_DECODE);
-               ++i;
-       }
-
-       if (i == _decoded.end()) {
-               Frame seek_frame = frame;
-               if (_content->video->frame_type() == VIDEO_FRAME_TYPE_3D_ALTERNATE) {
-                       /* 3D alternate is a special case as the frame index in the content is not the same
-                          as the frame index we are talking about here.
-                       */
-                       seek_frame *= 2;
-               }
-               _log->log (String::compose ("VD suggests seek to %1", seek_frame), LogEntry::TYPE_DEBUG_DECODE);
-               maybe_seek (ContentTime::from_frames (seek_frame, _content->active_video_frame_rate()), accurate);
-       }
-
-       /* Work out the number of frames that we should return; we
-          must return all frames in our content at the requested `time'
-          (i.e. frame)
-       */
-       unsigned int frames_wanted = 0;
-       switch (_content->video->frame_type()) {
-       case VIDEO_FRAME_TYPE_2D:
-       case VIDEO_FRAME_TYPE_3D_LEFT:
-       case VIDEO_FRAME_TYPE_3D_RIGHT:
-               frames_wanted = 1;
-               break;
-       case VIDEO_FRAME_TYPE_3D:
-       case VIDEO_FRAME_TYPE_3D_ALTERNATE:
-       case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
-       case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
-               frames_wanted = 2;
-               break;
-       default:
-               DCPOMATIC_ASSERT (false);
-       }
-
-       list<ContentVideo> dec;
-
-       /* Now enough pass() calls should either:
-        *  (a) give us what we want, or
-        *  (b) give us something after what we want, indicating that we will never get what we want, or
-        *  (c) hit the end of the decoder.
-        */
-       if (accurate) {
-               /* We are being accurate, so we want the right frame.
-                * This could all be one statement but it's split up for clarity.
-                */
-               bool no_data = false;
-
-               while (true) {
-                       if (decoded(frame).size() == frames_wanted) {
-                               /* We got what we want */
-                               break;
-                       }
-
-                       if (_parent->pass (Decoder::PASS_REASON_VIDEO, accurate)) {
-                               /* The decoder has nothing more for us */
-                               no_data = true;
-                               break;
-                       }
-
-                       if (!_decoded.empty() && _decoded.front().frame.index() > frame) {
-                               /* We're never going to get the frame we want.  Perhaps the caller is asking
-                                * for a video frame before the content's video starts (if its audio
-                                * begins before its video, for example).
-                                */
-                               break;
-                       }
-               }
-
-               dec = decoded (frame);
-
-               if (no_data && dec.empty()) {
-                       _no_data_frame = frame;
-               }
-
-       } else {
-               /* Any frame(s) will do: use the first one(s) that comes out of pass() */
-               while (_decoded.size() < frames_wanted && !_parent->pass (Decoder::PASS_REASON_VIDEO, accurate)) {}
-               list<ContentVideo>::const_iterator i = _decoded.begin();
-               unsigned int j = 0;
-               while (i != _decoded.end() && j < frames_wanted) {
-                       dec.push_back (*i);
-                       ++i;
-                       ++j;
-               }
-       }
-
-       /* Clean up _decoded; keep the frame we are returning, if any (which may have two images
-          for 3D), but nothing before that
-       */
-       while (!_decoded.empty() && !dec.empty() && _decoded.front().frame.index() < dec.front().frame.index()) {
-               _log->log (String::compose ("VD discards %1", _decoded.front().frame.index()), LogEntry::TYPE_DEBUG_DECODE);
-               _decoded.pop_front ();
-       }
-
-       return dec;
-}
-
-/** Fill _decoded from `from' up to, but not including, `to' with
- *  a frame for one particular Eyes value (which could be EYES_BOTH,
- *  EYES_LEFT or EYES_RIGHT)
- */
-void
-VideoDecoder::fill_one_eye (Frame from, Frame to, Eyes eye)
-{
-       if (to == 0) {
-               /* Already OK */
-               return;
-       }
-
-       /* Fill with black... */
-       shared_ptr<const ImageProxy> filler_image (new RawImageProxy (_black_image));
-       Part filler_part = PART_WHOLE;
-
-       /* ...unless there's some video we can fill with */
-       if (!_decoded.empty ()) {
-               filler_image = _decoded.back().image;
-               filler_part = _decoded.back().part;
-       }
-
-       for (Frame i = from; i < to; ++i) {
-#ifdef DCPOMATIC_DEBUG
-               test_gaps++;
-#endif
-               _decoded.push_back (
-                       ContentVideo (filler_image, VideoFrame (i, eye), filler_part)
-                       );
-       }
-}
-
-/** Fill _decoded from `from' up to, but not including, `to'
- *  adding both left and right eye frames.
- */
-void
-VideoDecoder::fill_both_eyes (VideoFrame from, VideoFrame to)
-{
-       /* Fill with black... */
-       shared_ptr<const ImageProxy> filler_left_image (new RawImageProxy (_black_image));
-       shared_ptr<const ImageProxy> filler_right_image (new RawImageProxy (_black_image));
-       Part filler_left_part = PART_WHOLE;
-       Part filler_right_part = PART_WHOLE;
-
-       /* ...unless there's some video we can fill with */
-       for (list<ContentVideo>::const_reverse_iterator i = _decoded.rbegin(); i != _decoded.rend(); ++i) {
-               if (i->frame.eyes() == EYES_LEFT && !filler_left_image) {
-                       filler_left_image = i->image;
-                       filler_left_part = i->part;
-               } else if (i->frame.eyes() == EYES_RIGHT && !filler_right_image) {
-                       filler_right_image = i->image;
-                       filler_right_part = i->part;
-               }
-
-               if (filler_left_image && filler_right_image) {
-                       break;
-               }
-       }
-
-       while (from != to) {
 
-#ifdef DCPOMATIC_DEBUG
-               test_gaps++;
-#endif
-
-               _decoded.push_back (
-                       ContentVideo (
-                               from.eyes() == EYES_LEFT ? filler_left_image : filler_right_image,
-                               from,
-                               from.eyes() == EYES_LEFT ? filler_left_part : filler_right_part
-                               )
-                       );
-
-               ++from;
-       }
 }
 
 /** Called by decoder classes when they have a video frame ready.
@@ -267,128 +53,45 @@ VideoDecoder::fill_both_eyes (VideoFrame from, VideoFrame to)
  *  and so on.
  */
 void
-VideoDecoder::give (shared_ptr<const ImageProxy> image, Frame frame)
+VideoDecoder::emit (shared_ptr<const ImageProxy> image, Frame frame)
 {
        if (ignore ()) {
                return;
        }
 
-       _log->log (String::compose ("VD receives %1", frame), LogEntry::TYPE_DEBUG_DECODE);
-       _position = ContentTime::from_frames (frame, _content->active_video_frame_rate());
-
        /* Work out what we are going to push into _decoded next */
-       list<ContentVideo> to_push;
        switch (_content->video->frame_type ()) {
        case VIDEO_FRAME_TYPE_2D:
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, EYES_BOTH), PART_WHOLE));
+               Data (ContentVideo (image, VideoFrame (frame, EYES_BOTH), PART_WHOLE));
                break;
        case VIDEO_FRAME_TYPE_3D:
        {
                /* We receive the same frame index twice for 3D; hence we know which
                   frame this one is.
                */
-               bool const same = (!_decoded.empty() && frame == _decoded.back().frame.index());
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, same ? EYES_RIGHT : EYES_LEFT), PART_WHOLE));
+               bool const same = (_last_emitted && _last_emitted.get() == frame);
+               Data (ContentVideo (image, VideoFrame (frame, same ? EYES_RIGHT : EYES_LEFT), PART_WHOLE));
+               _last_emitted = frame;
                break;
        }
        case VIDEO_FRAME_TYPE_3D_ALTERNATE:
-               to_push.push_back (ContentVideo (image, VideoFrame (frame / 2, (frame % 2) ? EYES_RIGHT : EYES_LEFT), PART_WHOLE));
+               Data (ContentVideo (image, VideoFrame (frame / 2, (frame % 2) ? EYES_RIGHT : EYES_LEFT), PART_WHOLE));
                break;
        case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, EYES_LEFT), PART_LEFT_HALF));
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, EYES_RIGHT), PART_RIGHT_HALF));
+               Data (ContentVideo (image, VideoFrame (frame, EYES_LEFT), PART_LEFT_HALF));
+               Data (ContentVideo (image, VideoFrame (frame, EYES_RIGHT), PART_RIGHT_HALF));
                break;
        case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, EYES_LEFT), PART_TOP_HALF));
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, EYES_RIGHT), PART_BOTTOM_HALF));
+               Data (ContentVideo (image, VideoFrame (frame, EYES_LEFT), PART_TOP_HALF));
+               Data (ContentVideo (image, VideoFrame (frame, EYES_RIGHT), PART_BOTTOM_HALF));
                break;
        case VIDEO_FRAME_TYPE_3D_LEFT:
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, EYES_LEFT), PART_WHOLE));
+               Data (ContentVideo (image, VideoFrame (frame, EYES_LEFT), PART_WHOLE));
                break;
        case VIDEO_FRAME_TYPE_3D_RIGHT:
-               to_push.push_back (ContentVideo (image, VideoFrame (frame, EYES_RIGHT), PART_WHOLE));
+               Data (ContentVideo (image, VideoFrame (frame, EYES_RIGHT), PART_WHOLE));
                break;
        default:
                DCPOMATIC_ASSERT (false);
        }
-
-       /* Now VideoDecoder is required never to have gaps in the frames that it presents
-          via get_video().  Hence we need to fill in any gap between the last thing in _decoded
-          and the things we are about to push.
-       */
-
-       optional<VideoFrame> from;
-
-       if (_decoded.empty() && _last_seek_time && _last_seek_accurate) {
-               from = VideoFrame (
-                       _last_seek_time->frames_round (_content->active_video_frame_rate ()),
-                       _content->video->frame_type() == VIDEO_FRAME_TYPE_2D ? EYES_BOTH : EYES_LEFT
-                       );
-       } else if (!_decoded.empty ()) {
-               /* Get the last frame we have */
-               from = _decoded.back().frame;
-               /* And move onto the first frame we need */
-               ++(*from);
-               if (_content->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT || _content->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) {
-                       /* The previous ++ will increment a 3D-left-eye to the same index right-eye.  If we are dealing with
-                          a single-eye source we need an extra ++ to move back to the same eye.
-                       */
-                       ++(*from);
-               }
-       }
-
-       /* If we've pre-rolled on a seek we may now receive out-of-order frames
-          (frames before the last seek time) which we can just ignore.
-       */
-       if (from && (*from) > to_push.front().frame) {
-               return;
-       }
-
-       unsigned int const max_decoded_size = 96;
-
-       /* If _decoded is already `full' there is no point in adding anything more to it,
-          as the new stuff will just be removed again.
-       */
-       if (_decoded.size() < max_decoded_size) {
-               if (from) {
-                       switch (_content->video->frame_type ()) {
-                       case VIDEO_FRAME_TYPE_2D:
-                               fill_one_eye (from->index(), to_push.front().frame.index(), EYES_BOTH);
-                               break;
-                       case VIDEO_FRAME_TYPE_3D:
-                       case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
-                       case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
-                       case VIDEO_FRAME_TYPE_3D_ALTERNATE:
-                               fill_both_eyes (from.get(), to_push.front().frame);
-                               break;
-                       case VIDEO_FRAME_TYPE_3D_LEFT:
-                               fill_one_eye (from->index(), to_push.front().frame.index(), EYES_LEFT);
-                               break;
-                       case VIDEO_FRAME_TYPE_3D_RIGHT:
-                               fill_one_eye (from->index(), to_push.front().frame.index(), EYES_RIGHT);
-                               break;
-                       }
-               }
-
-               copy (to_push.begin(), to_push.end(), back_inserter (_decoded));
-       }
-
-       /* We can't let this build up too much or we will run out of memory.  There is a
-          `best' value for the allowed size of _decoded which balances memory use
-          with decoding efficiency (lack of seeks).  Throwing away video frames here
-          is not a problem for correctness, so do it.
-       */
-       while (_decoded.size() > max_decoded_size) {
-               _decoded.pop_back ();
-       }
-}
-
-void
-VideoDecoder::seek (ContentTime s, bool accurate)
-{
-       _log->log (String::compose ("VD seek to %1", to_string(s)), LogEntry::TYPE_DEBUG_DECODE);
-       _decoded.clear ();
-       _last_seek_time = s;
-       _last_seek_accurate = accurate;
-       _position.reset ();
 }
index 156ee42..08173d3 100644 (file)
@@ -46,44 +46,18 @@ class VideoDecoder : public DecoderPart
 public:
        VideoDecoder (Decoder* parent, boost::shared_ptr<const Content> c, boost::shared_ptr<Log> log);
 
-       std::list<ContentVideo> get (Frame frame, bool accurate);
-
-#ifdef DCPOMATIC_DEBUG
-       int test_gaps;
-#endif
-
        friend struct video_decoder_fill_test1;
        friend struct video_decoder_fill_test2;
        friend struct ffmpeg_pts_offset_test;
        friend void ffmpeg_decoder_sequential_test_one (boost::filesystem::path file, float fps, int gaps, int video_length);
 
-       void seek (ContentTime time, bool accurate);
-       void give (boost::shared_ptr<const ImageProxy>, Frame frame);
+       void emit (boost::shared_ptr<const ImageProxy>, Frame frame);
 
-       boost::optional<ContentTime> position () const {
-               return _position;
-       }
-
-       void reset_position () {
-               _position.reset ();
-       }
+       boost::signals2::signal<void (ContentVideo)> Data;
 
 private:
-
-       std::list<ContentVideo> decoded (Frame frame);
-       void fill_one_eye (Frame from, Frame to, Eyes);
-       void fill_both_eyes (VideoFrame from, VideoFrame to);
-
        boost::shared_ptr<const Content> _content;
-       std::list<ContentVideo> _decoded;
-       boost::shared_ptr<Image> _black_image;
-       boost::optional<ContentTime> _last_seek_time;
-       bool _last_seek_accurate;
-       /** if set, this is a frame for which we got no data because the Decoder said
-        *  it has no more to give.
-        */
-       boost::optional<Frame> _no_data_frame;
-       boost::optional<ContentTime> _position;
+       boost::optional<Frame> _last_emitted;
 };
 
 #endif
index dc4f8d6..95dd668 100644 (file)
@@ -66,36 +66,34 @@ VideoMXFDecoder::VideoMXFDecoder (shared_ptr<const VideoMXFContent> content, sha
        }
 }
 
-bool
-VideoMXFDecoder::pass (PassReason, bool)
+void
+VideoMXFDecoder::pass ()
 {
        double const vfr = _content->active_video_frame_rate ();
        int64_t const frame = _next.frames_round (vfr);
 
        if (frame >= _content->video->length()) {
-               return true;
+               return;
        }
 
        if (_mono_reader) {
-               video->give (
+               video->emit (
                        shared_ptr<ImageProxy> (new J2KImageProxy (_mono_reader->get_frame(frame), _size, AV_PIX_FMT_XYZ12LE)), frame
                        );
        } else {
-               video->give (
+               video->emit (
                        shared_ptr<ImageProxy> (new J2KImageProxy (_stereo_reader->get_frame(frame), _size, dcp::EYE_LEFT, AV_PIX_FMT_XYZ12LE)), frame
                        );
-               video->give (
+               video->emit (
                        shared_ptr<ImageProxy> (new J2KImageProxy (_stereo_reader->get_frame(frame), _size, dcp::EYE_RIGHT, AV_PIX_FMT_XYZ12LE)), frame
                        );
        }
 
        _next += ContentTime::from_frames (1, vfr);
-       return false;
 }
 
 void
-VideoMXFDecoder::seek (ContentTime t, bool accurate)
+VideoMXFDecoder::seek (ContentTime t, bool)
 {
-       video->seek (t, accurate);
        _next = t;
 }
index 330d59e..3a93bbb 100644 (file)
@@ -30,10 +30,11 @@ class VideoMXFDecoder : public Decoder
 public:
        VideoMXFDecoder (boost::shared_ptr<const VideoMXFContent>, boost::shared_ptr<Log> log);
 
-private:
-       bool pass (PassReason, bool accurate);
+       void pass ();
        void seek (ContentTime t, bool accurate);
 
+private:
+
        boost::shared_ptr<const VideoMXFContent> _content;
        /** Time of next thing to return from pass */
        ContentTime _next;
index cb3d49f..3fa7ebb 100644 (file)
@@ -42,6 +42,7 @@ using std::string;
 using std::pair;
 using boost::shared_ptr;
 using boost::optional;
+using boost::bind;
 using dcp::Data;
 
 static shared_ptr<Film> film;
@@ -146,11 +147,8 @@ main (int argc, char* argv[])
                film->read_metadata ();
 
                shared_ptr<Player> player (new Player (film, film->playlist ()));
-
-               DCPTime const frame = DCPTime::from_frames (1, film->video_frame_rate ());
-               for (DCPTime t; t < film->length(); t += frame) {
-                       process_video (player->get_video(t, true).front ());
-               }
+               player->Video.connect (bind (&process_video, _1));
+               while (!player->pass ()) {}
        } catch (std::exception& e) {
                cerr << "Error: " << e.what() << "\n";
        }
index 3c6138d..beab8f3 100644 (file)
@@ -79,7 +79,7 @@ FilmViewer::FilmViewer (wxWindow* p)
        , _play_button (new wxToggleButton (this, wxID_ANY, _("Play")))
        , _coalesce_player_changes (false)
        , _pending_player_change (false)
-       , _last_get_accurate (true)
+       , _last_seek_accurate (true)
 {
 #ifndef __WXOSX__
        _panel->SetDoubleBuffered (true);
@@ -196,93 +196,56 @@ FilmViewer::refresh_panel ()
 }
 
 void
-FilmViewer::get (DCPTime p, bool accurate)
+FilmViewer::video (shared_ptr<PlayerVideo> pv)
 {
        if (!_player) {
                return;
        }
 
-       list<shared_ptr<PlayerVideo> > all_pv;
-       try {
-               all_pv = _player->get_video (p, accurate);
-       } catch (exception& e) {
-               error_dialog (this, wxString::Format (_("Could not get video for view (%s)"), std_to_wx(e.what()).data()));
-       }
-
-       if (!all_pv.empty ()) {
-               try {
-                       shared_ptr<PlayerVideo> pv;
-                       if (all_pv.size() == 2) {
-                               /* We have 3D; choose the correct eye */
-                               if (_left_eye->GetValue()) {
-                                       if (all_pv.front()->eyes() == EYES_LEFT) {
-                                               pv = all_pv.front();
-                                       } else {
-                                               pv = all_pv.back();
-                                       }
-                               } else {
-                                       if (all_pv.front()->eyes() == EYES_RIGHT) {
-                                               pv = all_pv.front();
-                                       } else {
-                                               pv = all_pv.back();
-                                       }
-                               }
-                       } else {
-                               /* 2D; no choice to make */
-                               pv = all_pv.front ();
-                       }
-
-                       /* In an ideal world, what we would do here is:
-                        *
-                        * 1. convert to XYZ exactly as we do in the DCP creation path.
-                        * 2. convert back to RGB for the preview display, compensating
-                        *    for the monitor etc. etc.
-                        *
-                        * but this is inefficient if the source is RGB.  Since we don't
-                        * (currently) care too much about the precise accuracy of the preview's
-                        * colour mapping (and we care more about its speed) we try to short-
-                        * circuit this "ideal" situation in some cases.
-                        *
-                        * The content's specified colour conversion indicates the colourspace
-                        * which the content is in (according to the user).
-                        *
-                        * PlayerVideo::image (bound to PlayerVideo::always_rgb) will take the source
-                        * image and convert it (from whatever the user has said it is) to RGB.
-                        */
-
-                       _frame = pv->image (
-                               bind (&Log::dcp_log, _film->log().get(), _1, _2),
-                               bind (&PlayerVideo::always_rgb, _1),
-                               false, true
-                               );
-
-                       ImageChanged (pv);
-
-                       _position = pv->time ();
-                       _inter_position = pv->inter_position ();
-                       _inter_size = pv->inter_size ();
-               } catch (dcp::DCPReadError& e) {
-                       /* This can happen on the following sequence of events:
-                        * - load encrypted DCP
-                        * - add KDM
-                        * - DCP is examined again, which sets its "playable" flag to 1
-                        * - as a side effect of the exam, the viewer is updated using the old pieces
-                        * - the DCPDecoder in the old piece gives us an encrypted frame
-                        * - then, the pieces are re-made (but too late).
-                        *
-                        * I hope there's a better way to handle this ...
-                        */
-                       _frame.reset ();
-                       _position = p;
+       if (_film->three_d ()) {
+               if ((_left_eye->GetValue() && pv->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && pv->eyes() == EYES_LEFT)) {
+                       return;
                }
-       } else {
-               _frame.reset ();
-               _position = p;
        }
 
+       /* In an ideal world, what we would do here is:
+        *
+        * 1. convert to XYZ exactly as we do in the DCP creation path.
+        * 2. convert back to RGB for the preview display, compensating
+        *    for the monitor etc. etc.
+        *
+        * but this is inefficient if the source is RGB.  Since we don't
+        * (currently) care too much about the precise accuracy of the preview's
+        * colour mapping (and we care more about its speed) we try to short-
+        * circuit this "ideal" situation in some cases.
+        *
+        * The content's specified colour conversion indicates the colourspace
+        * which the content is in (according to the user).
+        *
+        * PlayerVideo::image (bound to PlayerVideo::always_rgb) will take the source
+        * image and convert it (from whatever the user has said it is) to RGB.
+        */
+
+       _frame = pv->image (
+               bind (&Log::dcp_log, _film->log().get(), _1, _2),
+               bind (&PlayerVideo::always_rgb, _1),
+               false, true
+               );
+
+       ImageChanged (pv);
+
+       _position = pv->time ();
+       _inter_position = pv->inter_position ();
+       _inter_size = pv->inter_size ();
+
        refresh_panel ();
+}
 
-       _last_get_accurate = accurate;
+void
+FilmViewer::get ()
+{
+       Image const * current = _frame.get ();
+       while (!_player->pass() && _frame.get() == current) {}
 }
 
 void
@@ -294,7 +257,7 @@ FilmViewer::timer ()
                _play_button->SetValue (false);
                check_play_state ();
        } else {
-               get (_position + frame, true);
+               get ();
        }
 
        update_position_label ();
@@ -351,7 +314,7 @@ FilmViewer::slider_moved ()
        if (t >= _film->length ()) {
                t = _film->length() - DCPTime::from_frames (1, _film->video_frame_rate ());
        }
-       get (t, false);
+       seek (t, false);
        update_position_label ();
 }
 
@@ -485,7 +448,7 @@ FilmViewer::go_to (DCPTime t)
                t = _film->length ();
        }
 
-       get (t, true);
+       seek (t, true);
        update_position_label ();
        update_position_slider ();
 }
@@ -551,14 +514,14 @@ FilmViewer::film_changed (Film::Property p)
 void
 FilmViewer::refresh ()
 {
-       get (_position, _last_get_accurate);
+       seek (_position, _last_seek_accurate);
 }
 
 void
 FilmViewer::set_position (DCPTime p)
 {
        _position = p;
-       get (_position, true);
+       seek (p, true);
        update_position_label ();
        update_position_slider ();
 }
@@ -602,3 +565,11 @@ FilmViewer::jump_to_selected_clicked ()
 {
        Config::instance()->set_jump_to_selected (_jump_to_selected->GetValue ());
 }
+
+void
+FilmViewer::seek (DCPTime t, bool accurate)
+{
+       _player->seek (t, accurate);
+       _last_seek_accurate = accurate;
+       get ();
+}
index 0db4bcc..563178a 100644 (file)
@@ -67,7 +67,9 @@ private:
        void player_changed (bool);
        void update_position_label ();
        void update_position_slider ();
-       void get (DCPTime, bool);
+       void video (boost::shared_ptr<PlayerVideo>);
+       void get ();
+       void seek (DCPTime t, bool accurate);
        void refresh_panel ();
        void setup_sensitivity ();
        void film_changed (Film::Property);
@@ -105,12 +107,11 @@ private:
        dcp::Size _out_size;
        /** Size of the panel that we have available */
        dcp::Size _panel_size;
-       /** true if the last call to ::get() was specified to be accurate;
+       /** true if the last call to Player::seek() was specified to be accurate;
         *  this is used so that when re-fetching the current frame we
         *  can get the same one that we got last time.
         */
-       bool _last_get_accurate;
-
+       bool _last_seek_accurate;
        boost::signals2::scoped_connection _film_connection;
        boost::signals2::scoped_connection _player_connection;
 };
index e3aaffd..6ec71fc 100644 (file)
@@ -66,6 +66,9 @@ SubtitleView::SubtitleView (wxWindow* parent, shared_ptr<Film> film, shared_ptr<
                sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
        }
 
+#if 0
+       XXX
+
        list<ContentTextSubtitle> subs = decoder->subtitle->get_text (ContentTimePeriod (ContentTime(), ContentTime::max ()), true, true);
        FrameRateChange const frc = film->active_frame_rate_change (position);
        int n = 0;
@@ -81,6 +84,7 @@ SubtitleView::SubtitleView (wxWindow* parent, shared_ptr<Film> film, shared_ptr<
                        ++n;
                }
        }
+#endif
 
        SetSizerAndFit (sizer);
 }
index 57852dc..6e92c48 100644 (file)
@@ -72,7 +72,7 @@ public:
                audio.reset (new AudioDecoder (this, content->audio, log));
        }
 
-       bool pass (PassReason, bool)
+       bool pass ()
        {
                Frame const N = min (
                        Frame (2000),
@@ -94,7 +94,6 @@ public:
 
        void seek (ContentTime t, bool accurate)
        {
-               audio->seek (t, accurate);
                _position = t.frames_round (_test_audio_content->audio->resampled_frame_rate ());
        }
 
index 9b18867..80f2c1c 100644 (file)
@@ -43,7 +43,6 @@ def build(bld):
                  audio_analysis_test.cc
                  audio_buffers_test.cc
                  audio_delay_test.cc
-                 audio_decoder_test.cc
                  audio_filter_test.cc
                  audio_mapping_test.cc
                  audio_processor_test.cc
@@ -51,14 +50,10 @@ def build(bld):
                  black_fill_test.cc
                  client_server_test.cc
                  colour_conversion_test.cc
-                 dcp_subtitle_test.cc
                  dcpomatic_time_test.cc
                  digest_test.cc
                  ffmpeg_audio_test.cc
-                 ffmpeg_audio_only_test.cc
                  ffmpeg_dcp_test.cc
-                 ffmpeg_decoder_seek_test.cc
-                 ffmpeg_decoder_sequential_test.cc
                  ffmpeg_examiner_test.cc
                  ffmpeg_pts_offset_test.cc
                  file_group_test.cc
@@ -74,7 +69,6 @@ def build(bld):
                  j2k_bandwidth_test.cc
                  job_test.cc
                  make_black_test.cc
-                 player_test.cc
                  pixel_formats_test.cc
                  ratio_test.cc
                  repeat_frame_test.cc
@@ -85,7 +79,6 @@ def build(bld):
                  render_subtitles_test.cc
                  resampler_test.cc
                  scaling_test.cc
-                 seek_zero_test.cc
                  silence_padding_test.cc
                  skip_frame_test.cc
                  srt_subtitle_test.cc
@@ -95,11 +88,9 @@ def build(bld):
                  threed_test.cc
                  time_calculation_test.cc
                  update_checker_test.cc
-                 upmixer_a_test.cc
                  util_test.cc
                  vf_test.cc
                  video_content_scale_test.cc
-                 video_decoder_fill_test.cc
                  video_frame_test.cc
                  video_mxf_content_test.cc
                  vf_kdm_test.cc
@@ -109,5 +100,17 @@ def build(bld):
     # and others...
     # burnt_subtitle_test.cc
 
+    # XXX
+    # audio_decoder_test.cc
+    # dcp_subtitle_test.cc
+    # ffmpeg_audio_only_test.cc
+    # ffmpeg_decoder_seek_test.cc
+    # ffmpeg_decoder_sequential_test.cc
+    # silence_padding_test.cc
+    # player_test.cc
+    # seek_zero_test.cc
+    # upmixer_a_test.cc
+    # video_decoder_fill_test.cc
+
     obj.target = 'unit-tests'
     obj.install_path = ''