Make player more tolerant of some DCP errors.
authorCarl Hetherington <cth@carlh.net>
Fri, 30 Aug 2019 22:16:21 +0000 (23:16 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 30 Aug 2019 22:16:21 +0000 (23:16 +0100)
17 files changed:
src/lib/dcp.cc
src/lib/dcp.h
src/lib/dcp_content.cc
src/lib/dcp_decoder.cc
src/lib/dcp_decoder.h
src/lib/dcp_examiner.cc
src/lib/dcp_examiner.h
src/lib/decoder_factory.cc
src/lib/decoder_factory.h
src/lib/film.cc
src/lib/film.h
src/lib/player.cc
src/lib/player.h
src/tools/dcpomatic_player.cc
src/wx/content_menu.cc
src/wx/text_panel.cc
test/dcp_subtitle_test.cc

index f506d5c..0b80a37 100644 (file)
@@ -46,7 +46,7 @@ DCP::cpls () const
        LOG_GENERAL ("Reading %1 DCP directories", _dcp_content->directories().size());
        BOOST_FOREACH (boost::filesystem::path i, _dcp_content->directories()) {
                shared_ptr<dcp::DCP> dcp (new dcp::DCP (i));
-               dcp->read (false, 0, true);
+               dcp->read (_tolerant, 0, true);
                dcps.push_back (dcp);
                LOG_GENERAL ("Reading DCP %1: %2 CPLs", i.string(), dcp->cpls().size());
                BOOST_FOREACH (shared_ptr<dcp::CPL> i, dcp->cpls()) {
index 575bcc8..d449fdb 100644 (file)
@@ -24,6 +24,7 @@
 #include <dcp/cpl.h>
 #include <boost/shared_ptr.hpp>
 #include <list>
+#include <iostream>
 
 class DCPContent;
 
@@ -33,11 +34,15 @@ public:
        std::list<boost::shared_ptr<dcp::CPL> > cpls () const;
 
 protected:
-       explicit DCP (boost::shared_ptr<const DCPContent> content)
+       explicit DCP (boost::shared_ptr<const DCPContent> content, bool tolerant)
                : _dcp_content (content)
+               , _tolerant (tolerant)
        {}
 
        boost::shared_ptr<const DCPContent> _dcp_content;
+
+private:
+       bool _tolerant;
 };
 
 #endif
index dc1a463..95f293b 100644 (file)
@@ -198,7 +198,7 @@ DCPContent::examine (shared_ptr<const Film> film, shared_ptr<Job> job)
        }
        Content::examine (film, job);
 
-       shared_ptr<DCPExaminer> examiner (new DCPExaminer (shared_from_this ()));
+       shared_ptr<DCPExaminer> examiner (new DCPExaminer(shared_from_this(), film->tolerant()));
 
        if (examiner->has_video()) {
                {
@@ -478,7 +478,7 @@ DCPContent::reels (shared_ptr<const Film> film) const
        if (reel_lengths.empty ()) {
                /* Old metadata with no reel lengths; get them here instead */
                try {
-                       scoped_ptr<DCPExaminer> examiner (new DCPExaminer (shared_from_this()));
+                       scoped_ptr<DCPExaminer> examiner (new DCPExaminer(shared_from_this(), film->tolerant()));
                        reel_lengths = examiner->reel_lengths ();
                } catch (...) {
                        /* Could not examine the DCP; guess reels */
@@ -625,7 +625,7 @@ DCPContent::can_reference_audio (shared_ptr<const Film> film, string& why_not) c
 {
        shared_ptr<DCPDecoder> decoder;
        try {
-               decoder.reset (new DCPDecoder (film, shared_from_this(), false));
+               decoder.reset (new DCPDecoder (film, shared_from_this(), false, film->tolerant(), shared_ptr<DCPDecoder>()));
        } catch (dcp::DCPReadError &) {
                /* We couldn't read the DCP, so it's probably missing */
                return false;
@@ -660,7 +660,7 @@ DCPContent::can_reference_text (shared_ptr<const Film> film, TextType type, stri
 {
        shared_ptr<DCPDecoder> decoder;
        try {
-               decoder.reset (new DCPDecoder (film, shared_from_this(), false));
+               decoder.reset (new DCPDecoder (film, shared_from_this(), false, film->tolerant(), shared_ptr<DCPDecoder>()));
        } catch (dcp::DCPReadError &) {
                /* We couldn't read the DCP, so it's probably missing */
                return false;
index b694721..90b730f 100644 (file)
@@ -56,8 +56,8 @@ using boost::dynamic_pointer_cast;
 using boost::optional;
 using namespace dcpomatic;
 
-DCPDecoder::DCPDecoder (shared_ptr<const Film> film, shared_ptr<const DCPContent> c, bool fast, shared_ptr<DCPDecoder> old)
-       : DCP (c)
+DCPDecoder::DCPDecoder (shared_ptr<const Film> film, shared_ptr<const DCPContent> c, bool fast, bool tolerant, shared_ptr<DCPDecoder> old)
+       : DCP (c, tolerant)
        , Decoder (film)
        , _decode_referenced (false)
 {
index f31d280..2e63b24 100644 (file)
@@ -44,7 +44,8 @@ public:
                boost::shared_ptr<const Film> film,
                boost::shared_ptr<const DCPContent>,
                bool fast,
-               boost::shared_ptr<DCPDecoder> old = boost::shared_ptr<DCPDecoder>()
+               bool tolerant,
+               boost::shared_ptr<DCPDecoder> old
                );
 
        std::list<boost::shared_ptr<dcp::Reel> > reels () const {
index f44db84..8866ba7 100644 (file)
@@ -52,8 +52,8 @@ using std::runtime_error;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
 
-DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content)
-       : DCP (content)
+DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content, bool tolerant)
+       : DCP (content, tolerant)
        , _video_length (0)
        , _audio_length (0)
        , _has_video (false)
index da90924..e52234e 100644 (file)
@@ -31,7 +31,7 @@ class DCPContent;
 class DCPExaminer : public DCP, public VideoExaminer, public AudioExaminer
 {
 public:
-       explicit DCPExaminer (boost::shared_ptr<const DCPContent>);
+       explicit DCPExaminer (boost::shared_ptr<const DCPContent>, bool tolerant);
 
        bool has_video () const {
                return _has_video;
index 5d75895..2ded95f 100644 (file)
@@ -47,9 +47,12 @@ maybe_cast (shared_ptr<Decoder> d)
        return dynamic_pointer_cast<T> (d);
 }
 
-/** @param old_decoder A `used' decoder that has been previously made for this piece of content, or 0 */
+/**
+   @param tolerant true to proceed in the face of `survivable' errors, otherwise false.
+   @param old_decoder A `used' decoder that has been previously made for this piece of content, or 0
+*/
 shared_ptr<Decoder>
-decoder_factory (shared_ptr<const Film> film, shared_ptr<const Content> content, bool fast, shared_ptr<Decoder> old_decoder)
+decoder_factory (shared_ptr<const Film> film, shared_ptr<const Content> content, bool fast, bool tolerant, shared_ptr<Decoder> old_decoder)
 {
        shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (content);
        if (fc) {
@@ -59,7 +62,7 @@ decoder_factory (shared_ptr<const Film> film, shared_ptr<const Content> content,
        shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (content);
        if (dc) {
                try {
-                       return shared_ptr<Decoder> (new DCPDecoder(film, dc, fast, maybe_cast<DCPDecoder>(old_decoder)));
+                       return shared_ptr<Decoder> (new DCPDecoder(film, dc, fast, tolerant, maybe_cast<DCPDecoder>(old_decoder)));
                } catch (KDMError& e) {
                        /* This will be found and reported to the user when the content is examined */
                        return shared_ptr<Decoder>();
index cb145c8..0cd90a5 100644 (file)
@@ -24,5 +24,6 @@ extern boost::shared_ptr<Decoder> decoder_factory (
        boost::shared_ptr<const Film> film,
        boost::shared_ptr<const Content> content,
        bool fast,
+       bool tolerant,
        boost::shared_ptr<Decoder> old_decoder
        );
index 4eed5e3..dcaa737 100644 (file)
@@ -161,6 +161,7 @@ Film::Film (optional<boost::filesystem::path> dir)
        , _user_explicit_video_frame_rate (false)
        , _state_version (current_state_version)
        , _dirty (false)
+       , _tolerant (false)
 {
        set_isdcf_date_today ();
 
index a28d81b..6f1294b 100644 (file)
@@ -174,6 +174,14 @@ public:
        bool references_dcp_video () const;
        bool references_dcp_audio () const;
 
+       void set_tolerant (bool t) {
+               _tolerant = t;
+       }
+
+       bool tolerant () const {
+               return _tolerant;
+       }
+
        /** Identifiers for the parts of our state;
            used for signalling changes.
        */
@@ -432,6 +440,11 @@ private:
        /** film being used as a template, or 0 */
        boost::shared_ptr<Film> _template_film;
 
+       /** Be tolerant of errors in content (currently applies to DCP only).
+           Not saved as state.
+       */
+       bool _tolerant;
+
        boost::signals2::scoped_connection _playlist_change_connection;
        boost::signals2::scoped_connection _playlist_order_changed_connection;
        boost::signals2::scoped_connection _playlist_content_change_connection;
index bdfa515..1a88505 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -93,6 +93,7 @@ Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist
        , _ignore_text (false)
        , _always_burn_open_subtitles (false)
        , _fast (false)
+       , _tolerant (film->tolerant())
        , _play_referenced (false)
        , _audio_merger (_film->audio_frame_rate())
        , _shuffler (0)
@@ -145,13 +146,17 @@ Player::setup_pieces_unlocked ()
        _shuffler = new Shuffler();
        _shuffler->Video.connect(bind(&Player::video, this, _1, _2));
 
+       cout << "SPU " << _playlist->content().size() << ".\n";
+
        BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
 
                if (!i->paths_valid ()) {
+                       cout << "not valid.\n";
                        continue;
                }
 
                if (_ignore_video && _ignore_audio && i->text.empty()) {
+                       cout << "text only.\n";
                        /* We're only interested in text and this content has none */
                        continue;
                }
@@ -164,7 +169,8 @@ Player::setup_pieces_unlocked ()
                        }
                }
 
-               shared_ptr<Decoder> decoder = decoder_factory (_film, i, _fast, old_decoder);
+               cout << " DF " << _tolerant << "\n";
+               shared_ptr<Decoder> decoder = decoder_factory (_film, i, _fast, _tolerant, old_decoder);
                FrameRateChange frc (_film, i);
 
                if (!decoder) {
@@ -503,7 +509,7 @@ Player::get_reel_assets ()
 
                scoped_ptr<DCPDecoder> decoder;
                try {
-                       decoder.reset (new DCPDecoder (_film, j, false));
+                       decoder.reset (new DCPDecoder (_film, j, false, false, shared_ptr<DCPDecoder>()));
                } catch (...) {
                        return a;
                }
index 9f3dc7e..7558f6d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -164,6 +164,8 @@ private:
        bool _always_burn_open_subtitles;
        /** true if we should try to be fast rather than high quality */
        bool _fast;
+       /** true if we should keep going in the face of `survivable' errors */
+       bool _tolerant;
        /** true if we should `play' (i.e output) referenced DCP data (e.g. for preview) */
        bool _play_referenced;
 
index cdacee7..8648188 100644 (file)
@@ -312,7 +312,7 @@ public:
 
                shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent>(_film->content().front());
                if (dcp) {
-                       DCPExaminer ex (dcp);
+                       DCPExaminer ex (dcp, true);
                        shared_ptr<dcp::CPL> playing_cpl;
                        BOOST_FOREACH (shared_ptr<dcp::CPL> i, ex.cpls()) {
                                if (!dcp->cpl() || i->id() == *dcp->cpl()) {
@@ -394,6 +394,7 @@ public:
        void reset_film (shared_ptr<Film> film = shared_ptr<Film>(new Film(optional<boost::filesystem::path>())))
        {
                _film = film;
+               _film->set_tolerant (true);
                _viewer->set_film (_film);
                _controls->set_film (_film);
                _film->Change.connect (bind(&DOMFrame::film_changed, this, _1, _2));
@@ -448,7 +449,7 @@ public:
                        /* Offer a CPL menu */
                        shared_ptr<DCPContent> first = dynamic_pointer_cast<DCPContent>(_film->content().front());
                        if (first) {
-                               DCPExaminer ex (first);
+                               DCPExaminer ex (first, true);
                                int id = ID_view_cpl;
                                BOOST_FOREACH (shared_ptr<dcp::CPL> i, ex.cpls()) {
                                        wxMenuItem* j = _cpl_menu->AppendRadioItem(
@@ -701,7 +702,7 @@ private:
        {
                shared_ptr<DCPContent> dcp = boost::dynamic_pointer_cast<DCPContent>(_film->content().front());
                DCPOMATIC_ASSERT (dcp);
-               DCPExaminer ex (dcp);
+               DCPExaminer ex (dcp, true);
                int id = ev.GetId() - ID_view_cpl;
                DCPOMATIC_ASSERT (id >= 0);
                DCPOMATIC_ASSERT (id < int(ex.cpls().size()));
index e5cadd7..c5dc160 100644 (file)
@@ -126,7 +126,7 @@ ContentMenu::popup (weak_ptr<Film> film, ContentList c, TimelineContentViewList
                        _kdm->Enable (dcp->encrypted ());
                        _ov->Enable (dcp->needs_assets ());
                        try {
-                               DCPExaminer ex (dcp);
+                               DCPExaminer ex (dcp, true);
                                list<shared_ptr<dcp::CPL> > cpls = ex.cpls ();
                                _choose_cpl->Enable (cpls.size() > 1);
                                /* We can't have 0 as a menu item ID on OS X */
@@ -433,7 +433,7 @@ ContentMenu::cpl_selected (wxCommandEvent& ev)
        shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
        DCPOMATIC_ASSERT (dcp);
 
-       DCPExaminer ex (dcp);
+       DCPExaminer ex (dcp, true);
        list<shared_ptr<dcp::CPL> > cpls = ex.cpls ();
        DCPOMATIC_ASSERT (ev.GetId() > 0);
        DCPOMATIC_ASSERT (ev.GetId() <= int (cpls.size()));
index 98398aa..27465ad 100644 (file)
@@ -667,7 +667,7 @@ TextPanel::text_view_clicked ()
        ContentList c = _parent->selected_text ();
        DCPOMATIC_ASSERT (c.size() == 1);
 
-       shared_ptr<Decoder> decoder = decoder_factory (_parent->film(), c.front(), false, shared_ptr<Decoder>());
+       shared_ptr<Decoder> decoder = decoder_factory (_parent->film(), c.front(), false, false, shared_ptr<Decoder>());
 
        if (decoder) {
                _text_view = new TextView (this, _parent->film(), c.front(), c.front()->text_of_original_type(_original_type), decoder, _parent->film_viewer());
index 150cd85..4b3d8fc 100644 (file)
@@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_within_dcp_test)
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs ());
 
-       shared_ptr<DCPDecoder> decoder (new DCPDecoder (film, content, false));
+       shared_ptr<DCPDecoder> decoder (new DCPDecoder (film, content, false, false, shared_ptr<DCPDecoder>()));
        decoder->only_text()->PlainStart.connect (bind (store, _1));
 
        stored = optional<ContentStringText> ();