diff options
| -rw-r--r-- | src/content_version.cc | 35 | ||||
| -rw-r--r-- | src/content_version.h | 38 | ||||
| -rw-r--r-- | src/cpl.cc | 294 | ||||
| -rw-r--r-- | src/cpl.h | 60 | ||||
| -rw-r--r-- | src/parse/cpl.cc | 4 | ||||
| -rw-r--r-- | src/reel.h | 2 | ||||
| -rw-r--r-- | src/wscript | 1 |
7 files changed, 177 insertions, 257 deletions
diff --git a/src/content_version.cc b/src/content_version.cc new file mode 100644 index 00000000..c036e5f0 --- /dev/null +++ b/src/content_version.cc @@ -0,0 +1,35 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <libcxml/cxml.h> +#include "content_version.h" + +using boost::shared_ptr; +using namespace libdcp; + +ContentVersion::ContentVersion (shared_ptr<const cxml::Node> node) +{ + id = node->optional_string_child ("Id").get_value_or (""); + if (!id.empty ()) { + /* Trim urn:uri: */ + id = id.substr (8); + } + label_text = node->string_child ("LabelText"); + node->done (); +} diff --git a/src/content_version.h b/src/content_version.h new file mode 100644 index 00000000..de8f0939 --- /dev/null +++ b/src/content_version.h @@ -0,0 +1,38 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <boost/shared_ptr.hpp> + +namespace cxml { + class Node; +} + +namespace libdcp { + +class ContentVersion +{ +public: + ContentVersion () {} + ContentVersion (boost::shared_ptr<const cxml::Node> node); + + std::string id; + std::string label_text; +}; + +} @@ -30,6 +30,8 @@ #include "metadata.h" #include "encryption.h" #include "exceptions.h" +#include "xml.h" +#include "content_version.h" #include "compose.hpp" using std::string; @@ -44,169 +46,48 @@ using boost::lexical_cast; using boost::optional; using namespace libdcp; -CPL::CPL (string directory, string name, ContentKind content_kind) - : _directory (directory) - , _name (name) - , _content_kind (content_kind) +CPL::CPL () + : content_kind (FEATURE) , _length (0) { - _id = make_uuid (); + id = make_uuid (); } -/** Construct a CPL object from a XML file. - * @param directory The directory containing this CPL's DCP. - * @param file The CPL XML filename. - * @param asset_maps AssetMaps to look for assets in. - * @param require_mxfs true to throw an exception if a required MXF file does not exist. - */ -CPL::CPL (string directory, string file, list<PathAssetMap> asset_maps, bool require_mxfs) - : _directory (directory) - , _content_kind (FEATURE) +CPL::CPL (boost::filesystem::path file) + : content_kind (FEATURE) , _length (0) { - /* Read the XML */ - shared_ptr<parse::CPL> cpl; - try { - cpl.reset (new parse::CPL (file)); - } catch (FileError& e) { - boost::throw_exception (FileError ("could not load CPL file", file)); - } - - /* Now cherry-pick the required bits into our own data structure */ - - _name = cpl->annotation_text; - _content_kind = cpl->content_kind; + cxml::Document f ("CompositionPlaylist"); + f.read_file (file); /* Trim urn:uuid: off the front */ - _id = cpl->id.substr (9); - - for (list<shared_ptr<parse::Reel> >::iterator i = cpl->reels.begin(); i != cpl->reels.end(); ++i) { - - shared_ptr<parse::Picture> p; - - if ((*i)->asset_list->main_picture) { - p = (*i)->asset_list->main_picture; - } else { - p = (*i)->asset_list->main_stereoscopic_picture; - } - - shared_ptr<PictureAsset> picture; - shared_ptr<SoundAsset> sound; - shared_ptr<SubtitleAsset> subtitle; - - /* Some rather twisted logic to decide if we are 3D or not; - some DCPs give a MainStereoscopicPicture to indicate 3D, others - just have a FrameRate twice the EditRate and apparently - expect you to divine the fact that they are hence 3D. - */ - - if (!(*i)->asset_list->main_stereoscopic_picture && p->edit_rate == p->frame_rate) { - - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, p->id); - - try { - picture.reset (new MonoPictureAsset ( - asset.first, - asset.second->chunks.front()->path - ) - ); - - picture->set_entry_point (p->entry_point); - picture->set_duration (p->duration); - if (p->key_id.length() > 9) { - /* Trim urn:uuid: */ - picture->set_key_id (p->key_id.substr (9)); - } - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - - } else { - try { - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, p->id); - - picture.reset (new StereoPictureAsset ( - asset.first, - asset.second->chunks.front()->path, - p->edit_rate.numerator, - p->duration - ) - ); - - picture->set_entry_point (p->entry_point); - picture->set_duration (p->duration); - if (p->key_id.length() > 9) { - /* Trim urn:uuid: */ - picture->set_key_id (p->key_id.substr (9)); - } - - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - - } - - if ((*i)->asset_list->main_sound) { - - try { - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, (*i)->asset_list->main_sound->id); - - sound.reset (new SoundAsset ( - asset.first, - asset.second->chunks.front()->path - ) - ); - - shared_ptr<parse::MainSound> s = (*i)->asset_list->main_sound; - - sound->set_entry_point (s->entry_point); - sound->set_duration (s->duration); - if (s->key_id.length() > 9) { - /* Trim urn:uuid: */ - sound->set_key_id (s->key_id.substr (9)); - } - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - } - - if ((*i)->asset_list->main_subtitle) { - - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, (*i)->asset_list->main_subtitle->id); - - subtitle.reset (new SubtitleAsset ( - asset.first, - asset.second->chunks.front()->path - ) - ); - - subtitle->set_entry_point ((*i)->asset_list->main_subtitle->entry_point); - subtitle->set_duration ((*i)->asset_list->main_subtitle->duration); - } - - _reels.push_back (shared_ptr<Reel> (new Reel (picture, sound, subtitle))); + id = f.string_child ("Id"); + annotation_text = f.optional_string_child ("AnnotationText").get_value_or (""); + issue_date = f.string_child ("IssueDate"); + creator = f.optional_string_child ("Creator").get_value_or (""); + content_title_text = f.string_child ("ContentTitleText"); + content_kind = content_kind_from_string (f.string_child ("ContentKind")); + content_version = optional_type_child<ContentVersion> (f, "ContentVersion"); + if (!content_version) { + content_version.reset (new ContentVersion); } -} + f.ignore_child ("RatingList"); + reels = type_grand_children<Reel> (f, "ReelList", "Reel"); -void -CPL::add_reel (shared_ptr<Reel> reel) -{ - _reels.push_back (reel); + f.ignore_child ("Issuer"); + f.ignore_child ("Signer"); + f.ignore_child ("Signature"); + + f.done (); } void -CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<Encryption> crypt) const +CPL::write_xml (boost::filesystem::path directory, bool interop, shared_ptr<Encryption> crypt) const { boost::filesystem::path p; - p /= _directory; + p /= directory; stringstream s; - s << _id << "_cpl.xml"; + s << id << "_cpl.xml"; p /= s.str(); xmlpp::Document doc; @@ -220,24 +101,35 @@ CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<Encryptio if (crypt) { root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig"); } + + /* XXX + Old maps: + _name -> AnnotationText + metadata.issue_date -> IssueDate + metadata.issuer -> Issuer + metadata.creator -> Creator + _name -> ContentTitleText + _id + metadata.issue_date -> ContentVersion/Id + _id + metadata.issue_date -> ContentVersion/LabelText + */ - root->add_child("Id")->add_child_text ("urn:uuid:" + _id); - root->add_child("AnnotationText")->add_child_text (_name); - root->add_child("IssueDate")->add_child_text (metadata.issue_date); - root->add_child("Issuer")->add_child_text (metadata.issuer); - root->add_child("Creator")->add_child_text (metadata.creator); - root->add_child("ContentTitleText")->add_child_text (_name); - root->add_child("ContentKind")->add_child_text (content_kind_to_string (_content_kind)); + root->add_child("Id")->add_child_text ("urn:uuid:" + id); + root->add_child("AnnotationText")->add_child_text (annotation_text); + root->add_child("IssueDate")->add_child_text (issue_date); + root->add_child("Issuer")->add_child_text (issuer); + root->add_child("Creator")->add_child_text (creator); + root->add_child("ContentTitleText")->add_child_text (content_title_text); + root->add_child("ContentKind")->add_child_text (content_kind_to_string (content_kind)); { xmlpp::Node* cv = root->add_child ("ContentVersion"); - cv->add_child ("Id")->add_child_text ("urn:uri:" + _id + "_" + metadata.issue_date); - cv->add_child ("LabelText")->add_child_text (_id + "_" + metadata.issue_date); + cv->add_child ("Id")->add_child_text ("urn:uri:" + content_version->id); + cv->add_child ("LabelText")->add_child_text (content_version->label_text); } root->add_child("RatingList"); xmlpp::Element* reel_list = root->add_child ("ReelList"); - for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { + for (list<shared_ptr<Reel> >::const_iterator i = reels.begin(); i != reels.end(); ++i) { (*i)->write_to_cpl (reel_list, interop); } @@ -255,70 +147,48 @@ void CPL::write_to_pkl (xmlpp::Node* node) const { xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); + asset->add_child("Id")->add_child_text ("urn:uuid:" + id); asset->add_child("Hash")->add_child_text (_digest); asset->add_child("Size")->add_child_text (lexical_cast<string> (_length)); asset->add_child("Type")->add_child_text ("text/xml"); } -list<shared_ptr<const Asset> > -CPL::assets () const -{ - list<shared_ptr<const Asset> > a; - for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { - if ((*i)->main_picture ()) { - a.push_back ((*i)->main_picture ()); - } - if ((*i)->main_sound ()) { - a.push_back ((*i)->main_sound ()); - } - if ((*i)->main_subtitle ()) { - a.push_back ((*i)->main_subtitle ()); - } - } - - return a; -} - void CPL::write_to_assetmap (xmlpp::Node* node) const { xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); + asset->add_child("Id")->add_child_text ("urn:uuid:" + id); xmlpp::Node* chunk_list = asset->add_child ("ChunkList"); xmlpp::Node* chunk = chunk_list->add_child ("Chunk"); - chunk->add_child("Path")->add_child_text (_id + "_cpl.xml"); + chunk->add_child("Path")->add_child_text (id + "_cpl.xml"); chunk->add_child("VolumeIndex")->add_child_text ("1"); chunk->add_child("Offset")->add_child_text("0"); chunk->add_child("Length")->add_child_text(lexical_cast<string> (_length)); } - - + +#define EQUALS_COMPARE(property, message) if (property != other->property) { note (ERROR, message " differ"); return false; } bool -CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const +CPL::equals (shared_ptr<const CPL> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const { - if (_name != other._name && !opt.cpl_names_can_differ) { - stringstream s; - s << "names differ: " << _name << " vs " << other._name << "\n"; - note (ERROR, s.str ()); - return false; - } - - if (_content_kind != other._content_kind) { - note (ERROR, "content kinds differ"); - return false; - } - - if (_reels.size() != other._reels.size()) { - note (ERROR, String::compose ("reel counts differ (%1 vs %2)", _reels.size(), other._reels.size())); + EQUALS_COMPARE (id, "CPL ids"); + EQUALS_COMPARE (annotation_text, "CPL annotation texts"); + EQUALS_COMPARE (issue_date, "CPL issue dates"); + EQUALS_COMPARE (creator, "CPL creators"); + EQUALS_COMPARE (content_title_text, "CPL content title texts"); + EQUALS_COMPARE (content_kind, "CPL content kinds"); + + /* XXX: content_version */ + + if (reels.size() != other->reels.size()) { + note (ERROR, String::compose ("reel counts differ (%1 vs %2)", reels.size(), other->reels.size())); return false; } - list<shared_ptr<Reel> >::const_iterator a = _reels.begin (); - list<shared_ptr<Reel> >::const_iterator b = other._reels.begin (); + list<shared_ptr<Reel> >::const_iterator a = reels.begin (); + list<shared_ptr<Reel> >::const_iterator b = other->reels.begin (); - while (a != _reels.end ()) { + while (a != reels.end ()) { if (!(*a)->equals (*b, opt, note)) { return false; } @@ -329,6 +199,8 @@ CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteT return true; } +#undef EQUALS_COMPARE + shared_ptr<xmlpp::Document> CPL::make_kdm ( CertificateChain const & certificates, @@ -382,8 +254,8 @@ CPL::make_kdm ( recipient->add_child("X509SubjectName")->add_child_text (recipient_cert->subject()); } - kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text("urn:uuid:" + _id); - kdm_required_extensions->add_child("ContentTitleText")->add_child_text(_name); + kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text("urn:uuid:" + id); + kdm_required_extensions->add_child("ContentTitleText")->add_child_text(content_title_text); kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text(certificates.leaf()->thumbprint()); kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text("XXX"); kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text("XXX"); @@ -398,6 +270,8 @@ CPL::make_kdm ( } } + #if 0 + XXX { xmlpp::Element* key_id_list = kdm_required_extensions->add_child("KeyIdList"); list<shared_ptr<const Asset> > a = assets(); @@ -409,6 +283,7 @@ CPL::make_kdm ( } } } + #endif { xmlpp::Element* forensic_mark_flag_list = kdm_required_extensions->add_child("ForensicMarkFlagList"); @@ -488,7 +363,7 @@ CPL::make_kdm ( bool CPL::encrypted () const { - for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { + for (list<shared_ptr<Reel> >::const_iterator i = reels.begin(); i != reels.end(); ++i) { if ((*i)->encrypted ()) { return true; } @@ -500,20 +375,7 @@ CPL::encrypted () const void CPL::add_kdm (KDM const & kdm) { - for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { + for (list<shared_ptr<Reel> >::const_iterator i = reels.begin(); i != reels.end(); ++i) { (*i)->add_kdm (kdm); } } - -pair<string, shared_ptr<const parse::AssetMapAsset> > -CPL::asset_from_id (list<PathAssetMap> asset_maps, string id) const -{ - for (list<PathAssetMap>::const_iterator i = asset_maps.begin(); i != asset_maps.end(); ++i) { - shared_ptr<parse::AssetMapAsset> a = i->second->asset_from_id (id); - if (a) { - return make_pair (i->first, a); - } - } - - return make_pair ("", shared_ptr<const parse::AssetMapAsset> ()); -} @@ -25,6 +25,7 @@ #include <boost/function.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/optional.hpp> +#include <boost/filesystem.hpp> #include <libxml++/libxml++.h> #include "types.h" #include "certificates.h" @@ -42,45 +43,25 @@ class XMLMetadata; class MXFMetadata; class Encryption; class KDM; +class ContentVersion; /** @brief A CPL within a DCP */ class CPL { public: - CPL (std::string directory, std::string name, ContentKind content_kind); - CPL (std::string directory, std::string file, std::list<PathAssetMap> asset_maps, bool require_mxfs = true); + /** Create a new CPL */ + CPL (); - void add_reel (boost::shared_ptr<Reel> reel); - - /** @return the type of the content, used by media servers - * to categorise things (e.g. feature, trailer, etc.) + /** Read a CPL from an XML file. + * @param file File to read. */ - ContentKind content_kind () const { - return _content_kind; - } - - std::list<boost::shared_ptr<Reel> > reels () const { - return _reels; - } - - /** @return the CPL's name, as will be presented on projector - * media servers and theatre management systems. - */ - std::string name () const { - return _name; - } - - std::list<boost::shared_ptr<const Asset> > assets () const; + CPL (boost::filesystem::path file); bool encrypted () const; - std::string id () const { - return _id; - } + bool equals (boost::shared_ptr<const CPL> other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const; - bool equals (CPL const & other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const; - - void write_xml (bool, XMLMetadata const &, boost::shared_ptr<Encryption>) const; + void write_xml (boost::filesystem::path directory, bool interop, boost::shared_ptr<Encryption>) const; void write_to_assetmap (xmlpp::Node *) const; void write_to_pkl (xmlpp::Node *) const; @@ -96,21 +77,18 @@ public: ) const; void add_kdm (KDM const &); + + std::string id; + std::string annotation_text; + std::string issue_date; + std::string issuer; + std::string creator; + std::string content_title_text; + ContentKind content_kind; + boost::shared_ptr<ContentVersion> content_version; + std::list<boost::shared_ptr<Reel> > reels; private: - std::pair<std::string, boost::shared_ptr<const parse::AssetMapAsset> > asset_from_id (std::list<PathAssetMap>, std::string id) const; - - std::string _directory; - /** the name of the DCP */ - std::string _name; - /** the content kind of the CPL */ - ContentKind _content_kind; - /** reels */ - std::list<boost::shared_ptr<Reel> > _reels; - - /** our UUID */ - std::string _id; - /* XXX: nasty */ /** a SHA1 digest of our XML */ mutable std::string _digest; diff --git a/src/parse/cpl.cc b/src/parse/cpl.cc index f6ce434c..8e62adab 100644 --- a/src/parse/cpl.cc +++ b/src/parse/cpl.cc @@ -56,6 +56,10 @@ CPL::CPL (string file) ContentVersion::ContentVersion (shared_ptr<const cxml::Node> node) { id = node->optional_string_child ("Id").get_value_or (""); + if (!id.empty ()) { + /* Trim urn:uri: */ + id = id.substr (8); + } label_text = node->string_child ("LabelText"); node->done (); } @@ -50,6 +50,8 @@ public: , _main_sound (sound) , _main_subtitle (subtitle) {} + + Reel (boost::shared_ptr<const cxml::Node>); boost::shared_ptr<const MainPicture> main_picture () const { return _main_picture; diff --git a/src/wscript b/src/wscript index cded0ce4..9281aed2 100644 --- a/src/wscript +++ b/src/wscript @@ -15,6 +15,7 @@ def build(bld): asset_instance.cc certificates.cc colour_matrix.cc + content_version.cc crypt_chain.cc cpl.cc dcp.cc |
