diff options
| author | Carl Hetherington <cth@carlh.net> | 2016-08-23 00:27:12 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2016-08-24 10:01:52 +0100 |
| commit | df28b0e939bd0f12ae31e6f7ba94fa954496b3b8 (patch) | |
| tree | 9dcdf8708fe678fc1419e90dc6fdc3f8da8fafb2 /src/lib | |
| parent | 2f1d460898f40844d47d6a3b858449ac09d8b342 (diff) | |
Allow import of OV/VF DCPs (#906).
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/dcp.cc | 59 | ||||
| -rw-r--r-- | src/lib/dcp.h | 41 | ||||
| -rw-r--r-- | src/lib/dcp_content.cc | 45 | ||||
| -rw-r--r-- | src/lib/dcp_content.h | 16 | ||||
| -rw-r--r-- | src/lib/dcp_decoder.cc | 18 | ||||
| -rw-r--r-- | src/lib/dcp_decoder.h | 4 | ||||
| -rw-r--r-- | src/lib/dcp_examiner.cc | 101 | ||||
| -rw-r--r-- | src/lib/dcp_examiner.h | 13 | ||||
| -rw-r--r-- | src/lib/wscript | 1 |
9 files changed, 247 insertions, 51 deletions
diff --git a/src/lib/dcp.cc b/src/lib/dcp.cc new file mode 100644 index 000000000..6d5c0b291 --- /dev/null +++ b/src/lib/dcp.cc @@ -0,0 +1,59 @@ +/* + Copyright (C) 2014-2016 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "dcp.h" +#include "config.h" +#include "dcp_content.h" +#include <dcp/dcp.h> +#include <dcp/decrypted_kdm.h> +#include <boost/foreach.hpp> + +using std::list; +using boost::shared_ptr; + +/** Find all the CPLs in our directories, cross-add assets and return the CPLs */ +list<shared_ptr<dcp::CPL> > +DCP::cpls () const +{ + list<shared_ptr<dcp::DCP> > dcps; + list<shared_ptr<dcp::CPL> > cpls; + + BOOST_FOREACH (boost::filesystem::path i, _dcp_content->directories()) { + shared_ptr<dcp::DCP> dcp (new dcp::DCP (i)); + dcp->read (false, 0, true); + if (_dcp_content->kdm ()) { + dcp->add (dcp::DecryptedKDM (_dcp_content->kdm().get(), Config::instance()->decryption_chain()->key().get ())); + } + dcps.push_back (dcp); + BOOST_FOREACH (shared_ptr<dcp::CPL> i, dcp->cpls()) { + cpls.push_back (i); + } + } + + BOOST_FOREACH (shared_ptr<dcp::DCP> i, dcps) { + BOOST_FOREACH (shared_ptr<dcp::DCP> j, dcps) { + if (i != j) { + i->resolve_refs (j->assets ()); + } + } + } + + return cpls; +} diff --git a/src/lib/dcp.h b/src/lib/dcp.h new file mode 100644 index 000000000..d83f95f37 --- /dev/null +++ b/src/lib/dcp.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2014-2016 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef DCPOMATIC_DCP_H +#define DCPOMATIC_DCP_H + +#include <dcp/cpl.h> +#include <boost/shared_ptr.hpp> +#include <list> + +class DCPContent; + +class DCP +{ +protected: + DCP (boost::shared_ptr<const DCPContent> content) + : _dcp_content (content) + {} + + std::list<boost::shared_ptr<dcp::CPL> > cpls () const; + boost::shared_ptr<const DCPContent> _dcp_content; +}; + +#endif diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc index a14dbaf5f..03e6f1aaa 100644 --- a/src/lib/dcp_content.cc +++ b/src/lib/dcp_content.cc @@ -61,6 +61,7 @@ int const DCPContentProperty::REFERENCE_SUBTITLE = 603; DCPContent::DCPContent (shared_ptr<const Film> film, boost::filesystem::path p) : Content (film) , _encrypted (false) + , _needs_assets (false) , _kdm_valid (false) , _reference_video (false) , _reference_audio (false) @@ -95,8 +96,8 @@ DCPContent::DCPContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, in ); _name = node->string_child ("Name"); - _encrypted = node->bool_child ("Encrypted"); + _needs_assets = node->bool_child ("NeedsAssets"); if (node->optional_node_child ("KDM")) { _kdm = dcp::EncryptedKDM (node->string_child ("KDM")); } @@ -115,6 +116,7 @@ DCPContent::DCPContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, in } } _three_d = node->optional_bool_child("ThreeD").get_value_or (false); + _cpl = node->optional_string_child("CPL"); } void @@ -160,9 +162,11 @@ DCPContent::examine (shared_ptr<Job> job) subtitle.reset (new SubtitleContent (this)); } _encrypted = examiner->encrypted (); + _needs_assets = examiner->needs_assets (); _kdm_valid = examiner->kdm_valid (); _standard = examiner->standard (); _three_d = examiner->three_d (); + _cpl = examiner->cpl (); } if (could_be_played != can_be_played ()) { @@ -212,6 +216,7 @@ DCPContent::as_xml (xmlpp::Node* node) const boost::mutex::scoped_lock lm (_mutex); node->add_child("Name")->add_child_text (_name); node->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0"); + node->add_child("NeedsAssets")->add_child_text (_needs_assets ? "1" : "0"); if (_kdm) { node->add_child("KDM")->add_child_text (_kdm->as_xml ()); } @@ -232,6 +237,9 @@ DCPContent::as_xml (xmlpp::Node* node) const } } node->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0"); + if (_cpl) { + node->add_child("CPL")->add_child_text (_cpl.get ()); + } } DCPTime @@ -259,27 +267,36 @@ DCPContent::add_kdm (dcp::EncryptedKDM k) _kdm = k; } +void +DCPContent::add_ov (boost::filesystem::path ov) +{ + read_directory (ov); +} + bool DCPContent::can_be_played () const { + return !needs_kdm() && !needs_assets(); +} + +bool +DCPContent::needs_kdm () const +{ boost::mutex::scoped_lock lm (_mutex); - return !_encrypted || _kdm_valid; + return _encrypted && !_kdm_valid; } -boost::filesystem::path -DCPContent::directory () const +bool +DCPContent::needs_assets () const { - optional<size_t> smallest; - boost::filesystem::path dir; - for (size_t i = 0; i < number_of_paths(); ++i) { - boost::filesystem::path const p = path (i).parent_path (); - size_t const d = distance (p.begin(), p.end()); - if (!smallest || d < smallest.get ()) { - dir = p; - } - } + boost::mutex::scoped_lock lm (_mutex); + return _needs_assets; +} - return dir; +vector<boost::filesystem::path> +DCPContent::directories () const +{ + return dcp::DCP::directories_from_files (paths ()); } void diff --git a/src/lib/dcp_content.h b/src/lib/dcp_content.h index cb9279706..f3a8236a2 100644 --- a/src/lib/dcp_content.h +++ b/src/lib/dcp_content.h @@ -68,7 +68,7 @@ public: void set_default_colour_conversion (); std::list<DCPTime> reel_split_points () const; - boost::filesystem::path directory () const; + std::vector<boost::filesystem::path> directories () const; bool encrypted () const { boost::mutex::scoped_lock lm (_mutex); @@ -76,12 +76,15 @@ public: } void add_kdm (dcp::EncryptedKDM); + void add_ov (boost::filesystem::path ov); boost::optional<dcp::EncryptedKDM> kdm () const { return _kdm; } bool can_be_played () const; + bool needs_kdm () const; + bool needs_assets () const; void set_reference_video (bool r); @@ -110,6 +113,11 @@ public: bool can_reference_subtitle (std::list<std::string> &) const; + boost::optional<std::string> cpl () const { + boost::mutex::scoped_lock lm (_mutex); + return _cpl; + } + private: void add_properties (std::list<UserProperty>& p) const; @@ -124,6 +132,8 @@ private: std::string _name; /** true if our DCP is encrypted */ bool _encrypted; + /** true if this DCP needs more assets before it can be played */ + bool _needs_assets; boost::optional<dcp::EncryptedKDM> _kdm; /** true if _kdm successfully decrypts the first frame of our DCP */ bool _kdm_valid; @@ -142,6 +152,10 @@ private: boost::optional<dcp::Standard> _standard; bool _three_d; + /** ID of the CPL to use; older metadata might not specify this: in that case + * just use the only CPL. + */ + boost::optional<std::string> _cpl; }; #endif diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index a4207f144..2e3ed374a 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -51,7 +51,7 @@ using boost::shared_ptr; using boost::dynamic_pointer_cast; DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log) - : _dcp_content (c) + : DCP (c) , _decode_referenced (false) { video.reset (new VideoDecoder (this, c, log)); @@ -66,13 +66,15 @@ DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log) ) ); - dcp::DCP dcp (c->directory ()); - dcp.read (false, 0, true); - if (c->kdm ()) { - dcp.add (dcp::DecryptedKDM (c->kdm().get (), Config::instance()->decryption_chain()->key().get ())); + shared_ptr<dcp::CPL> cpl; + BOOST_FOREACH (shared_ptr<dcp::CPL> i, cpls ()) { + if (_dcp_content->cpl() && i->id() == _dcp_content->cpl().get()) { + cpl = i; + } } - DCPOMATIC_ASSERT (dcp.cpls().size() == 1); - _reels = dcp.cpls().front()->reels (); + + DCPOMATIC_ASSERT (cpl); + _reels = cpl->reels (); _reel = _reels.begin (); _offset = 0; @@ -178,7 +180,7 @@ DCPDecoder::next_reel () void DCPDecoder::get_readers () { - if (_reel == _reels.end()) { + if (_reel == _reels.end() || !_dcp_content->can_be_played ()) { _mono_reader.reset (); _stereo_reader.reset (); _sound_reader.reset (); diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h index 12e37104e..6e1f95356 100644 --- a/src/lib/dcp_decoder.h +++ b/src/lib/dcp_decoder.h @@ -23,6 +23,7 @@ */ #include "decoder.h" +#include "dcp.h" namespace dcp { class Reel; @@ -35,7 +36,7 @@ class DCPContent; class Log; struct dcp_subtitle_within_dcp_test; -class DCPDecoder : public Decoder +class DCPDecoder : public DCP, public Decoder { public: DCPDecoder (boost::shared_ptr<const DCPContent>, boost::shared_ptr<Log> log); @@ -57,7 +58,6 @@ private: std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod, bool starting) const; std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod, bool starting) const; - boost::shared_ptr<const DCPContent> _dcp_content; /** Time of next thing to return from pass relative to the start of _reel */ ContentTime _next; std::list<boost::shared_ptr<dcp::Reel> > _reels; diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc index 06bef4c3a..b2034890b 100644 --- a/src/lib/dcp_examiner.cc +++ b/src/lib/dcp_examiner.cc @@ -35,7 +35,9 @@ #include <dcp/stereo_picture_asset.h> #include <dcp/stereo_picture_asset_reader.h> #include <dcp/stereo_picture_frame.h> +#include <dcp/reel_subtitle_asset.h> #include <dcp/sound_asset.h> +#include <boost/foreach.hpp> #include <iostream> #include "i18n.h" @@ -47,33 +49,67 @@ using boost::shared_ptr; using boost::dynamic_pointer_cast; DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content) - : _video_length (0) + : DCP (content) + , _video_length (0) , _audio_length (0) , _has_subtitles (false) , _encrypted (false) + , _needs_assets (false) , _kdm_valid (false) , _three_d (false) { - dcp::DCP dcp (content->directory ()); - dcp.read (false, 0, true); + shared_ptr<dcp::CPL> cpl; - if (content->kdm ()) { - dcp.add (dcp::DecryptedKDM (content->kdm().get(), Config::instance()->decryption_chain()->key().get ())); + if (content->cpl ()) { + /* Use the CPL that the content was using before */ + BOOST_FOREACH (shared_ptr<dcp::CPL> i, cpls()) { + if (i->id() == content->cpl().get()) { + cpl = i; + } + } + } else { + /* Choose the CPL with the fewest unsatisfied references */ + + int least_unsatisfied = INT_MAX; + + BOOST_FOREACH (shared_ptr<dcp::CPL> i, cpls()) { + int unsatisfied = 0; + BOOST_FOREACH (shared_ptr<dcp::Reel> j, i->reels()) { + if (j->main_picture() && !j->main_picture()->asset_ref().resolved()) { + ++unsatisfied; + } + if (j->main_sound() && !j->main_sound()->asset_ref().resolved()) { + ++unsatisfied; + } + if (j->main_subtitle() && !j->main_subtitle()->asset_ref().resolved()) { + ++unsatisfied; + } + } + + if (unsatisfied < least_unsatisfied) { + least_unsatisfied = unsatisfied; + cpl = i; + } + } } - if (dcp.cpls().size() == 0) { + if (!cpl) { throw DCPError ("No CPLs found in DCP"); - } else if (dcp.cpls().size() > 1) { - throw DCPError ("Multiple CPLs found in DCP"); } - _name = dcp.cpls().front()->content_title_text (); + _cpl = cpl->id (); + _name = cpl->content_title_text (); - list<shared_ptr<dcp::Reel> > reels = dcp.cpls().front()->reels (); - for (list<shared_ptr<dcp::Reel> >::const_iterator i = reels.begin(); i != reels.end(); ++i) { + BOOST_FOREACH (shared_ptr<dcp::Reel> i, cpl->reels()) { - if ((*i)->main_picture ()) { - dcp::Fraction const frac = (*i)->main_picture()->edit_rate (); + if (i->main_picture ()) { + if (!i->main_picture()->asset_ref().resolved()) { + /* We are missing this asset so we can't continue; examination will be repeated later */ + _needs_assets = true; + return; + } + + dcp::Fraction const frac = i->main_picture()->edit_rate (); float const fr = float(frac.numerator) / frac.denominator; if (!_video_frame_rate) { _video_frame_rate = fr; @@ -81,18 +117,24 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content) throw DCPError (_("Mismatched frame rates in DCP")); } - shared_ptr<dcp::PictureAsset> asset = (*i)->main_picture()->asset (); + shared_ptr<dcp::PictureAsset> asset = i->main_picture()->asset (); if (!_video_size) { _video_size = asset->size (); } else if (_video_size.get() != asset->size ()) { throw DCPError (_("Mismatched video sizes in DCP")); } - _video_length += (*i)->main_picture()->duration(); + _video_length += i->main_picture()->duration(); } - if ((*i)->main_sound ()) { - shared_ptr<dcp::SoundAsset> asset = (*i)->main_sound()->asset (); + if (i->main_sound ()) { + if (!i->main_sound()->asset_ref().resolved()) { + /* We are missing this asset so we can't continue; examination will be repeated later */ + _needs_assets = true; + return; + } + + shared_ptr<dcp::SoundAsset> asset = i->main_sound()->asset (); if (!_audio_channels) { _audio_channels = asset->channels (); @@ -106,21 +148,27 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content) throw DCPError (_("Mismatched audio sample rates in DCP")); } - _audio_length += (*i)->main_sound()->duration(); + _audio_length += i->main_sound()->duration(); } - if ((*i)->main_subtitle ()) { + if (i->main_subtitle ()) { + if (!i->main_subtitle()->asset_ref().resolved()) { + /* We are missing this asset so we can't continue; examination will be repeated later */ + _needs_assets = true; + return; + } + _has_subtitles = true; } } - _encrypted = dcp.encrypted (); + _encrypted = cpl->encrypted (); _kdm_valid = true; /* Check that we can read the first picture frame */ try { - if (!dcp.cpls().empty () && !dcp.cpls().front()->reels().empty ()) { - shared_ptr<dcp::PictureAsset> asset = dcp.cpls().front()->reels().front()->main_picture()->asset (); + if (!cpl->reels().empty ()) { + shared_ptr<dcp::PictureAsset> asset = cpl->reels().front()->main_picture()->asset (); shared_ptr<dcp::MonoPictureAsset> mono = dynamic_pointer_cast<dcp::MonoPictureAsset> (asset); shared_ptr<dcp::StereoPictureAsset> stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (asset); @@ -139,7 +187,10 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content) } } - _standard = dcp.standard (); - _three_d = !reels.empty() && reels.front()->main_picture() && - dynamic_pointer_cast<dcp::StereoPictureAsset> (reels.front()->main_picture()->asset()); + DCPOMATIC_ASSERT (cpl->standard ()); + _standard = cpl->standard().get(); + _three_d = !cpl->reels().empty() && cpl->reels().front()->main_picture() && + dynamic_pointer_cast<dcp::StereoPictureAsset> (cpl->reels().front()->main_picture()->asset()); + + _cpl = cpl->id (); } diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h index 6fc041cc6..0558bfcfc 100644 --- a/src/lib/dcp_examiner.h +++ b/src/lib/dcp_examiner.h @@ -24,10 +24,11 @@ #include "video_examiner.h" #include "audio_examiner.h" +#include "dcp.h" class DCPContent; -class DCPExaminer : public VideoExaminer, public AudioExaminer +class DCPExaminer : public DCP, public VideoExaminer, public AudioExaminer { public: DCPExaminer (boost::shared_ptr<const DCPContent>); @@ -60,6 +61,10 @@ public: return _encrypted; } + bool needs_assets () const { + return _needs_assets; + } + int audio_channels () const { return _audio_channels.get_value_or (0); } @@ -84,6 +89,10 @@ public: return _three_d; } + std::string cpl () const { + return _cpl; + } + private: boost::optional<double> _video_frame_rate; boost::optional<dcp::Size> _video_size; @@ -94,7 +103,9 @@ private: std::string _name; bool _has_subtitles; bool _encrypted; + bool _needs_assets; bool _kdm_valid; boost::optional<dcp::Standard> _standard; bool _three_d; + std::string _cpl; }; diff --git a/src/lib/wscript b/src/lib/wscript index 911ee0af4..b43443bd1 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -45,6 +45,7 @@ sources = """ content_factory.cc cross.cc curl_uploader.cc + dcp.cc dcp_content.cc dcp_content_type.cc dcp_decoder.cc |
