fixup! WIP: allow Piece to take multiple content/decoder.
authorCarl Hetherington <cth@carlh.net>
Wed, 5 May 2021 20:27:01 +0000 (22:27 +0200)
committerCarl Hetherington <cth@carlh.net>
Fri, 7 May 2021 07:29:59 +0000 (09:29 +0200)
23 files changed:
src/lib/atmos_mxf_decoder.cc
src/lib/atmos_mxf_decoder.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/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/image_decoder.cc
src/lib/image_decoder.h
src/lib/piece.cc
src/lib/piece.h
src/lib/player.cc
src/lib/string_text_file_decoder.cc
src/lib/string_text_file_decoder.h
src/lib/video_mxf_decoder.cc
src/lib/video_mxf_decoder.h
src/wx/text_view.cc
test/dcp_subtitle_test.cc
test/ffmpeg_decoder_seek_test.cc
test/video_level_test.cc

index 9fcd9d2a6db6f06a4f6b50f1f13e6e7890e89b9e..2c31157e06a5d72c975432f56305b56a0462a9b9 100644 (file)
@@ -45,7 +45,7 @@ AtmosMXFDecoder::AtmosMXFDecoder (std::shared_ptr<const Film> film, std::shared_
 
 
 bool
-AtmosMXFDecoder::pass ()
+AtmosMXFDecoder::do_pass ()
 {
        auto const vfr = _content->active_video_frame_rate (film());
        auto const frame = _next.frames_round (vfr);
index b8e7fc53d81b19a08a2a6c80f5e67f97f4fe10ff..e5b3066906dde5a45a2f148c197a4cd1615ae7e7 100644 (file)
@@ -33,10 +33,11 @@ class AtmosMXFDecoder : public Decoder
 public:
        AtmosMXFDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const AtmosMXFContent>);
 
-       bool pass () override;
        void seek (dcpomatic::ContentTime t, bool accurate) override;
 
 private:
+       bool do_pass () override;
+
        std::shared_ptr<const AtmosMXFContent> _content;
        dcpomatic::ContentTime _next;
        std::shared_ptr<dcp::AtmosAssetReader> _reader;
index a0b983e4987d8dc02d7c85d4bb98da10f1643a4b..23815bef336674867819d49e5ad3b4878dc6e93e 100644 (file)
@@ -129,7 +129,7 @@ DCPDecoder::DCPDecoder (shared_ptr<const Film> film, shared_ptr<const DCPContent
 
 
 bool
-DCPDecoder::pass ()
+DCPDecoder::do_pass ()
 {
        if (!_dcp_content->can_be_played()) {
                return true;
index e4944e3bfe76c1cdd0f344aa2eb8eabd649e712a..469e7549f25e0b039006e3b53f6c13da42185d90 100644 (file)
@@ -59,7 +59,6 @@ public:
        void set_decode_referenced (bool r);
        void set_forced_reduction (boost::optional<int> reduction);
 
-       bool pass () override;
        void seek (dcpomatic::ContentTime t, bool accurate) override;
 
        std::vector<dcpomatic::FontData> fonts () const override;
@@ -73,6 +72,7 @@ public:
 private:
        friend struct dcp_subtitle_within_dcp_test;
 
+       bool do_pass () override;
        void next_reel ();
        void get_readers ();
        void pass_texts (dcpomatic::ContentTime next, dcp::Size size);
index 024d62f3424f3138f2f84f190e5f99119d4a8984..23938f30476f10fc279b0e108a5bcc1ca881b528 100644 (file)
@@ -80,7 +80,7 @@ DCPSubtitleDecoder::seek (ContentTime time, bool accurate)
 
 
 bool
-DCPSubtitleDecoder::pass ()
+DCPSubtitleDecoder::do_pass ()
 {
        if (_next == _subtitles.end ()) {
                return true;
index 95e783d0612a9876ec5f40050240bbd1e3bb03af..a5b2140b322d6918c3c27ba94e6ce910de9aa86f 100644 (file)
@@ -32,12 +32,13 @@ class DCPSubtitleDecoder : public DCPSubtitle, public Decoder
 public:
        DCPSubtitleDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const DCPSubtitleContent>);
 
-       bool pass ();
        void seek (dcpomatic::ContentTime time, bool accurate);
 
        std::vector<dcpomatic::FontData> fonts () const;
 
 private:
+       bool do_pass ();
+
        dcpomatic::ContentTimePeriod content_time_period (std::shared_ptr<const dcp::Subtitle> s) const;
 
        std::vector<std::shared_ptr<const dcp::Subtitle>> _subtitles;
index 5d191512874916c74a82eed7ce049b9e2e33803d..1e7bcbdea584e79354a5d9de6c01a43f1231adbd 100644 (file)
@@ -87,6 +87,8 @@ Decoder::seek (ContentTime, bool)
        for (auto i: text) {
                i->seek ();
        }
+
+       _done = false;
 }
 
 
@@ -99,3 +101,11 @@ Decoder::only_text () const
        }
        return text.front();
 }
+
+
+void
+Decoder::pass ()
+{
+       _done = do_pass ();
+}
+
index 00d629f815f1a6ca41a095f7e020578658303c17..ed35662b3547243c7957225b43e667c32f024468 100644 (file)
@@ -63,10 +63,13 @@ public:
 
        std::shared_ptr<TextDecoder> only_text () const;
 
-       /** Do some decoding and perhaps emit video, audio or subtitle data.
-        *  @return true if this decoder will emit no more data unless a seek() happens.
-        */
-       virtual bool pass () = 0;
+       bool done () const {
+               return _done;
+       }
+
+       /** Do some decoding and perhaps emit video, audio or subtitle data */
+       void pass ();
+
        virtual void seek (dcpomatic::ContentTime time, bool accurate);
 
        virtual dcpomatic::ContentTime position () const;
@@ -76,6 +79,10 @@ public:
        }
 
        boost::signals2::signal<void ()> Flush;
+
+private:
+       virtual bool do_pass () = 0;
+       bool _done = false;
 };
 
 
index 432749c29d6aa5e41e182f406c4bd42aa7b5f305..c661240c3961e1f4f3c928a039fdcab04528fde5 100644 (file)
@@ -181,7 +181,7 @@ FFmpegDecoder::flush ()
 
 
 bool
-FFmpegDecoder::pass ()
+FFmpegDecoder::do_pass ()
 {
        auto packet = av_packet_alloc();
        DCPOMATIC_ASSERT (packet);
@@ -479,6 +479,7 @@ FFmpegDecoder::process_audio_frame (shared_ptr<FFmpegAudioStream> stream)
                data->move (data->frames() - remove, remove, 0);
                data->set_frames (data->frames() - remove);
                ct += ContentTime::from_frames (remove, stream->frame_rate());
+               std::cout << "FF discarded " << remove << "\n";
        }
 
        if (ct < ContentTime()) {
index f15257f98346ad9b790c531735b5d19332637614..f39aa707359a39ff7c2a736753bdee59dcb24c84 100644 (file)
@@ -46,12 +46,12 @@ class FFmpegDecoder : public FFmpeg, public Decoder
 public:
        FFmpegDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const FFmpegContent>);
 
-       bool pass () override;
        void seek (dcpomatic::ContentTime time, bool) override;
 
 private:
        friend struct ::ffmpeg_pts_offset_test;
 
+       bool do_pass () override;
        bool flush ();
 
        static std::shared_ptr<AudioBuffers> deinterleave_audio (AVFrame* frame);
index 2e0a980928ea99213e29d94255aa6a520e14de40..36291276c4fb24d3c40ca5365ffdd7c07e6d7018 100644 (file)
@@ -51,7 +51,7 @@ ImageDecoder::ImageDecoder (shared_ptr<const Film> film, shared_ptr<const ImageC
 
 
 bool
-ImageDecoder::pass ()
+ImageDecoder::do_pass ()
 {
        if (_frame_video_position >= _image_content->video->length()) {
                return true;
index 067a247208f31b05f7c7cc4f1f6722b1deed09ba..65279284fbe7b13f9cdf7a591e81deaaade195c2 100644 (file)
@@ -36,10 +36,10 @@ public:
                return _image_content;
        }
 
-       bool pass () override;
        void seek (dcpomatic::ContentTime, bool) override;
 
 private:
+       bool do_pass () override;
 
        std::shared_ptr<const ImageContent> _image_content;
        std::shared_ptr<ImageProxy> _image;
index 5d70512d4d5e707993c4cb594f98d26070ac1bdc..b8d587cbcfe678b6b0e9d7ef7d5ed24ab68bace9 100644 (file)
@@ -108,12 +108,28 @@ Piece::video (shared_ptr<const Content> content, shared_ptr<const ImageProxy> im
 }
 
 
+/** @param frame Frame position in the content, as if there were no trim */
 void
 Piece::audio (shared_ptr<const Content> content, AudioStreamPtr stream_ptr, shared_ptr<const AudioBuffers> audio, Frame frame)
 {
        auto film = _film.lock ();
        DCPOMATIC_ASSERT (film);
 
+       /* Here we have the frame index into the content, and shortly we're going to lose that information and
+        * start thinking about frame indices into the piece.  Here's the last chance for us to apply this content's
+        * trim, so we'll take it.
+        */
+       auto const start_trim = content->trim_start().frames_round(stream_ptr->frame_rate());
+       auto const remove_from_start = std::max(Frame(0), start_trim - frame);
+       if (remove_from_start > 0) {
+               auto trimmed = make_shared<AudioBuffers>(audio);
+               trimmed->trim_start (remove_from_start);
+               frame += remove_from_start;
+       }
+
+       auto const end_trim = content->trim_send().frames_round(stream_ptr->frame_rate());
+
+
        auto content_streams = content->audio->streams();
        auto content_stream_iter = std::find(content_streams.begin(), content_streams.end(), stream_ptr);
        DCPOMATIC_ASSERT (content_stream_iter != content_streams.end());
@@ -151,6 +167,7 @@ Piece::audio (shared_ptr<const Content> content, AudioStreamPtr stream_ptr, shar
                stream.position = frame;
        }
 
+       std::cout << "piece sends frame " << stream.position << " " << to_string(resampled_audio_to_dcp(stream.position) << "\n";
        Audio (PieceAudio(index, audio, resampled_audio_to_dcp(stream.position), stream_ptr->mapping()));
        stream.position += audio->frames();
 }
@@ -357,14 +374,13 @@ Piece::pass ()
        auto film = _film.lock ();
        DCPOMATIC_ASSERT (film);
 
-       for (auto const& i: _content) {
-               auto t = content_time_to_dcp(i.content, std::max(i.decoder->position(), i.content->trim_start()));
-               DCPOMATIC_ASSERT (t);
-               if (*t < i.content->end(film)) {
-                       LOG_DEBUG_PLAYER ("Calling pass() on %1", i.content->path(0));
-                       i.decoder->pass();
-                       return;
+       for (auto& i: _content) {
+               if (i.decoder->done()) {
+                       continue;
                }
+               LOG_DEBUG_PLAYER ("Calling pass() on %1", i.content->path(0));
+               i.decoder->pass();
+               return;
        }
 }
 
@@ -388,7 +404,7 @@ Piece::seek (DCPTime time, bool accurate)
                i.position = 0;
        }
 
-       for (auto const& i: _content) {
+       for (auto& i: _content) {
                if (time < i.content->position()) {
                        /* Before; seek to the start of the content.  Even if this request is for an inaccurate seek
                           we must seek this (following) content accurately, otherwise when we come to the end of the current
@@ -411,9 +427,9 @@ Piece::decoder_before(optional<dcpomatic::DCPTime> time)
        DCPOMATIC_ASSERT (film);
 
        for (auto const& i: _content) {
-               auto t = content_time_to_dcp(i.content, std::max(i.decoder->position(), i.content->trim_start()));
-               DCPOMATIC_ASSERT (t);
-               if (*t < i.content->end(film)) {
+               if (!i.decoder->done()) {
+                       auto t = content_time_to_dcp(i.content, std::max(i.decoder->position(), i.content->trim_start()));
+                       DCPOMATIC_ASSERT (t);
                        /* This is the first unfinished decoder we have, so we'll make a decision with it.
                           Given two choices at the same time, pick the one with texts so we see it before
                           the video.
@@ -454,10 +470,12 @@ Piece::flush ()
 {
        int index = 0;
        for (auto& i: _audio_streams) {
-               auto ro = i.resampler->flush ();
-               if (ro->frames() > 0) {
-                       Audio (PieceAudio(index, ro, resampled_audio_to_dcp(i.position), i.mapping));
-                       i.position += ro->frames();
+               if (i.resampler) {
+                       auto ro = i.resampler->flush ();
+                       if (ro->frames() > 0) {
+                               Audio (PieceAudio(index, ro, resampled_audio_to_dcp(i.position), i.mapping));
+                               i.position += ro->frames();
+                       }
                }
                ++index;
        }
@@ -467,9 +485,12 @@ Piece::flush ()
 bool
 Piece::done () const
 {
-       auto film = _film.lock();
-       DCPOMATIC_ASSERT (film);
-       auto const& last = _content.back();
-       return content_time_to_dcp(last.content, std::max(last.decoder->position(), last.content->trim_start())) > last.content->end(film);
+       for (auto const& i: _content) {
+               if (!i.decoder->done()) {
+                       return false;
+               }
+       }
+
+       return true;
 }
 
index 48d7ba0731d79481395a78915715e1b8a6dbb5bb..a2718d4283ca187031fa72d6f64059f0bc7a6425 100644 (file)
@@ -49,8 +49,6 @@ class Piece
 {
 public:
        struct Pair {
-               Pair () {}
-
                Pair (std::shared_ptr<Content> c, std::shared_ptr<Decoder> d)
                        : content(c)
                        , decoder(d)
index 16bb499451501b17da80dfe2cec6081ec6f64614..8b785f0fe7d7c040dba0a6598dbe53c83a0e7cd9 100644 (file)
@@ -906,6 +906,7 @@ Player::audio (weak_ptr<Piece> wp, PieceAudio audio)
        /* The end of this block in the DCP */
        int const rfr = piece->resampled_audio_frame_rate ();
        auto end = audio.time + DCPTime::from_frames(audio.audio->frames(), rfr);
+       std::cout << "Player gets " << to_string(audio.time) << "\n";
 
        /* Remove anything that comes before the start or after the end of the content */
        if (audio.time < piece->position()) {
index 86f7321330b9f2ef5dfd059f9fb2617d604fcb9c..4a737dac0239ece0727b13ece7745554ca47a771 100644 (file)
@@ -71,7 +71,7 @@ StringTextFileDecoder::seek (ContentTime time, bool accurate)
 }
 
 bool
-StringTextFileDecoder::pass ()
+StringTextFileDecoder::do_pass ()
 {
        if (_next >= _subtitles.size ()) {
                return true;
index aa8c80e6e9152341d2f114a0118e4934dcf3fc4e..86c81147a5dbf55ffd2bc8eb75b6c68425bcb68d 100644 (file)
@@ -32,11 +32,12 @@ public:
        StringTextFileDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const StringTextFileContent>);
 
        void seek (dcpomatic::ContentTime time, bool accurate);
-       bool pass ();
 
        std::vector<dcpomatic::FontData> fonts () const;
 
 private:
+       bool do_pass ();
+
        dcpomatic::ContentTimePeriod content_time_period (sub::Subtitle s) const;
 
        size_t _next;
index 39c4a37fb865be99d11b0769bdaedec453050b37..6fb2828af62e35ba167c1645a4f119a784338271 100644 (file)
@@ -76,7 +76,7 @@ VideoMXFDecoder::VideoMXFDecoder (shared_ptr<const Film> film, shared_ptr<const
 
 
 bool
-VideoMXFDecoder::pass ()
+VideoMXFDecoder::do_pass ()
 {
        auto const vfr = _content->active_video_frame_rate (film());
        auto const frame = _next.frames_round (vfr);
index 774e269c6d5da15275e1ccf61259f4ad1f510fa3..3492a54d41484b3e42a75f967577425fbbb47653 100644 (file)
@@ -33,10 +33,10 @@ class VideoMXFDecoder : public Decoder
 public:
        VideoMXFDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const VideoMXFContent>);
 
-       bool pass () override;
        void seek (dcpomatic::ContentTime t, bool accurate) override;
 
 private:
+       bool do_pass () override;
 
        std::shared_ptr<const VideoMXFContent> _content;
        /** Time of next thing to return from pass */
index abe73a5a9fea212eeb10ce08947cee0e4fb2bd78..555207ae2211943caef062c26870ee599f5c42de 100644 (file)
@@ -103,7 +103,9 @@ TextView::TextView (
                        i->Stop.connect (bind(&TextView::data_stop, this, _1));
                }
        }
-       while (!decoder->pass ()) {}
+       while (!decoder->done()) {
+               decoder->pass();
+       }
        SetSizerAndFit (sizer);
 }
 
index 2b05d0bc9fd1d6abd39f63937d2694722a4bd430..cd2d71de6221940c29bee5dc1abc311e2743c104 100644 (file)
@@ -110,7 +110,9 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_within_dcp_test)
        decoder->only_text()->StringStart.connect (bind(store, _1, _2));
 
        stored.clear();
-       while (!decoder->pass() && stored.empty()) {}
+       while (!decoder->done() && stored.empty()) {
+               decoder->pass();
+       }
 
        BOOST_REQUIRE_EQUAL (stored.size(), 2U);
        BOOST_CHECK_EQUAL (stored.front().text(), "Noch mal.");
@@ -132,7 +134,8 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test2)
        decoder->only_text()->StringStart.connect (bind(store, _1, _2));
 
        stored.clear();
-       while (!decoder->pass()) {
+       while (!decoder->done()) {
+               decoder->pass();
                if (!stored.empty() && stored_time == ContentTime(0)) {
                        BOOST_CHECK_EQUAL (stored.front().text(), "&lt;b&gt;Hello world!&lt;/b&gt;");
                }
@@ -156,7 +159,8 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test3)
 
        auto decoder = make_shared<DCPSubtitleDecoder>(film, content);
        stored.clear();
-       while (!decoder->pass ()) {
+       while (!decoder->done()) {
+               decoder->pass();
                decoder->only_text()->StringStart.connect (bind (store, _1, _2));
                if (!stored.empty() && stored_time == ContentTime::from_seconds(0.08)) {
                        auto i = stored.begin ();
index 4d796d1165364970b18c79bc7cf3ddc3c56d1609..2299fb1c59fdacf6e69981ba6091d91edd857776 100644 (file)
@@ -63,48 +63,38 @@ static void
 check (shared_ptr<FFmpegDecoder> decoder, int frame)
 {
        BOOST_REQUIRE (decoder->ffmpeg_content()->video_frame_rate ());
-       decoder->seek (ContentTime::from_frames (frame, decoder->ffmpeg_content()->video_frame_rate().get()), true);
+       decoder->seek (ContentTime::from_frames(frame, decoder->ffmpeg_content()->video_frame_rate().get()), true);
        stored_frame = {};
-       while (!decoder->pass() && !stored_frame) {}
+       while (!decoder->done() && !stored_frame) {
+               decoder->pass();
+       }
        BOOST_CHECK (*stored_frame <= frame);
 }
 
 static void
 test (boost::filesystem::path file, vector<int> frames)
 {
-       boost::filesystem::path path = TestPaths::private_data() / file;
+       auto path = TestPaths::private_data() / file;
        BOOST_REQUIRE (boost::filesystem::exists (path));
 
-       shared_ptr<Film> film = new_test_film ("ffmpeg_decoder_seek_test_" + file.string());
+       auto film = new_test_film ("ffmpeg_decoder_seek_test_" + file.string());
        auto content = make_shared<FFmpegContent>(path);
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
        auto decoder = make_shared<FFmpegDecoder>(film, content);
        decoder->video->Data.connect (bind (&store, _2));
 
-       for (vector<int>::const_iterator i = frames.begin(); i != frames.end(); ++i) {
-               check (decoder, *i);
+       for (auto i: frames) {
+               check (decoder, i);
        }
 }
 
 BOOST_AUTO_TEST_CASE (ffmpeg_decoder_seek_test)
 {
-       vector<int> frames;
-
-       frames.clear ();
-       frames.push_back (0);
-       frames.push_back (42);
-       frames.push_back (999);
-       frames.push_back (0);
-
+       vector<int> frames = { 0, 42, 999, 0 };
        test ("boon_telly.mkv", frames);
        test ("Sintel_Trailer1.480p.DivX_Plus_HD.mkv", frames);
 
-       frames.clear ();
-       frames.push_back (15);
-       frames.push_back (42);
-       frames.push_back (999);
-       frames.push_back (15);
-
+       frames = { 15, 42, 999, 15 };
        test ("prophet_long_clip.mkv", frames);
 }
index 0022a2595ab0f90f548fc5296e1448ac4c9d6b41..6c2576e4339a8b9091540116196be8bd656cebb1 100644 (file)
@@ -203,7 +203,8 @@ pixel_range (shared_ptr<Film> film, shared_ptr<const FFmpegContent> content)
        decoder->video->Data.connect (bind(&video_handler, _1));
        decoded_image = {};
        while (!decoded_image) {
-               BOOST_REQUIRE (!decoder->pass());
+               decoder->pass();
+               BOOST_REQUIRE (!decoder->done());
        }
 
        return pixel_range (decoded_image->image().image);
@@ -218,7 +219,8 @@ pixel_range (shared_ptr<Film> film, shared_ptr<const ImageContent> content)
        decoder->video->Data.connect (bind(&video_handler, _1));
        decoded_image = {};
        while (!decoded_image) {
-               BOOST_REQUIRE (!decoder->pass());
+               decoder->pass();
+               BOOST_REQUIRE (!decoder->done());
        }
 
        return pixel_range (decoded_image->image().image);