diff options
| -rw-r--r-- | src/cpl.cc | 147 | ||||
| -rw-r--r-- | src/cpl.h | 27 | ||||
| -rw-r--r-- | src/extension_metadata.cc | 96 | ||||
| -rw-r--r-- | src/extension_metadata.h | 75 | ||||
| -rw-r--r-- | src/wscript | 2 |
5 files changed, 286 insertions, 61 deletions
@@ -87,6 +87,9 @@ static string const cpl_metadata_ns = "http://www.smpte-ra.org/schemas/429-16/20 static string const mca_sub_descriptors_ns = "http://isdcf.com/ns/cplmd/mca"; static string const smpte_395_ns = "http://www.smpte-ra.org/reg/395/2014/13/1/aaf"; static string const smpte_335_ns = "http://www.smpte-ra.org/reg/335/2012"; +auto constexpr profile_scope = "http://isdcf.com/ns/cplmd/app"; +auto constexpr profile_name = "Application"; +auto constexpr profile_tag = "DCP Constraints Profile"; CPL::CPL(string annotation_text, ContentKind content_kind, Standard standard, Profile profile) @@ -103,6 +106,10 @@ CPL::CPL(string annotation_text, ContentKind content_kind, Standard standard, Pr ContentVersion cv; cv.label_text = cv.id + LocalTime().as_string(); _content_versions.push_back(cv); + + /* SMPTE Bv2.1 8.6.3 */ + string const profile_value = _profile == Profile::SMPTE_BV20 ? "2.0" : "2.1"; + set_extension_metadata_value(profile_scope, profile_name, profile_tag, fmt::format("SMPTE-RDD-52:2020-Bv{}", profile_value)); } @@ -371,33 +378,12 @@ CPL::read_composition_metadata_asset(cxml::ConstNodePtr node, vector<dcp::Verifi } } - auto eml = node->optional_node_child("ExtensionMetadataList"); - - auto extension_metadata = [eml](string scope, string name, string property) -> boost::optional<std::string> { - if (!eml) { - return {}; - } - - for (auto i: eml->node_children("ExtensionMetadata")) { - auto xml_scope = i->optional_string_attribute("scope"); - auto xml_name = i->optional_string_child("Name"); - if (xml_scope && *xml_scope == scope && xml_name && *xml_name == name) { - auto property_list = i->node_child("PropertyList"); - for (auto j: property_list->node_children("Property")) { - auto property_name = j->optional_string_child("Name"); - auto property_value = j->optional_string_child("Value"); - if (property_name && property_value && *property_name == property) { - return property_value; - } - } - } + _extension_metadata.clear(); + if (auto eml = node->optional_node_child("ExtensionMetadataList")) { + for (auto const& metadata: eml->node_children("ExtensionMetadata")) { + _extension_metadata.push_back(ExtensionMetadata(metadata)); } - - return {}; - }; - - _sign_language_video_language = extension_metadata("http://isdcf.com/2017/10/SignLanguageVideo", "Sign Language Video", "Language Tag"); - _dolby_edr_image_transfer_function = extension_metadata("http://www.dolby.com/schemas/2014/EDR-Metadata", "Dolby EDR", "image transfer function"); + } if (node->optional_node_child("MCASubDescriptors")) { _profile = Profile::SMPTE_BV21; @@ -601,27 +587,11 @@ CPL::maybe_write_composition_metadata_asset(xmlpp::Element* node) const cxml::add_child(meta, "MainSubtitleLanguageList", string("meta"))->add_child_text(lang); } - auto metadata_list = cxml::add_child(meta, "ExtensionMetadataList", string("meta")); - - auto add_extension_metadata = [metadata_list](string scope, string name, string property_name, string property_value) { - auto extension = cxml::add_child(metadata_list, "ExtensionMetadata", string("meta")); - extension->set_attribute("scope", scope); - cxml::add_child(extension, "Name", string("meta"))->add_child_text(name); - auto property = cxml::add_child(cxml::add_child(extension, "PropertyList", string("meta")), "Property", string("meta")); - cxml::add_child(property, "Name", string("meta"))->add_child_text(property_name); - cxml::add_child(property, "Value", string("meta"))->add_child_text(property_value); - }; - - string const profile_name = _profile == Profile::SMPTE_BV20 ? "2.0" : "2.1"; - /* SMPTE Bv2.1 8.6.3 */ - add_extension_metadata("http://isdcf.com/ns/cplmd/app", "Application", "DCP Constraints Profile", fmt::format("SMPTE-RDD-52:2020-Bv{}", profile_name)); - - if (_sign_language_video_language) { - add_extension_metadata("http://isdcf.com/2017/10/SignLanguageVideo", "Sign Language Video", "Language Tag", *_sign_language_video_language); - } - - if (_dolby_edr_image_transfer_function) { - add_extension_metadata("http://www.dolby.com/schemas/2014/EDR-Metadata", "Dolby EDR", "image transfer function", *_dolby_edr_image_transfer_function); + if (!_extension_metadata.empty()) { + auto list = cxml::add_child(meta, "meta:ExtensionMetadataList"); + for (auto metadata: _extension_metadata) { + metadata.as_xml(list); + } } if (_profile == Profile::SMPTE_BV21) { @@ -910,3 +880,86 @@ CPL::picture_encoding() const return encoding; } + +optional<string> +CPL::extension_metadata_value(string const& scope, string const& name, string const& property_name) const +{ + for (auto const& metadata: _extension_metadata) { + if (metadata.scope == scope && metadata.name == name) { + for (auto const& property: metadata.properties) { + if (property.name == property_name) { + return property.value; + } + } + } + } + + return {}; +} + + +void +CPL::set_extension_metadata_value(string const& scope, string const& name, string const& property_name, string const& value) +{ + for (auto& metadata: _extension_metadata) { + if (metadata.scope == scope && metadata.name == name) { + for (auto& property: metadata.properties) { + if (property.name == property_name) { + property.value = value; + return; + } + } + } + } + + _extension_metadata.push_back( + ExtensionMetadata(scope, name, { ExtensionMetadata::Property(property_name, value) }) + ); +} + + +auto constexpr sign_language_scope = "http://isdcf.com/2017/10/SignLanguageVideo"; +auto constexpr sign_language_name = "Sign Language Video"; +auto constexpr sign_language_tag = "Language Tag"; + + +optional<string> +CPL::sign_language_video_language() const +{ + return extension_metadata_value(sign_language_scope, sign_language_name, sign_language_tag); +} + + +void +CPL::set_sign_language_video_language(dcp::LanguageTag language) +{ + set_extension_metadata_value(sign_language_scope, sign_language_name, sign_language_tag, language.as_string()); +} + + +auto constexpr dolby_edr_image_transfer_function_scope = "http://www.dolby.com/schemas/2014/EDR-Metadata"; +auto constexpr dolby_edr_image_transfer_function_name = "Dolby EDR"; +auto constexpr dolby_edr_image_transfer_function_tag = "image transfer function"; + + +optional<string> +CPL::dolby_edr_image_transfer_function() const +{ + return extension_metadata_value( + dolby_edr_image_transfer_function_scope, + dolby_edr_image_transfer_function_name, + dolby_edr_image_transfer_function_tag + ); +} + + +void +CPL::set_dolby_edr_image_transfer_function(string const& function) +{ + set_extension_metadata_value( + dolby_edr_image_transfer_function_scope, + dolby_edr_image_transfer_function_name, + dolby_edr_image_transfer_function_tag, + function + ); +} @@ -44,6 +44,7 @@ #include "asset.h" #include "certificate.h" #include "content_kind.h" +#include "extension_metadata.h" #include "key.h" #include "language_tag.h" #include "main_sound_configuration.h" @@ -323,21 +324,13 @@ public: void set_additional_subtitle_languages(std::vector<dcp::LanguageTag> const& lang); - void set_sign_language_video_language(dcp::LanguageTag lang) { - _sign_language_video_language = lang.as_string(); - } + void set_sign_language_video_language(dcp::LanguageTag lang); - boost::optional<std::string> sign_language_video_language() const { - return _sign_language_video_language; - } + boost::optional<std::string> sign_language_video_language() const; - void set_dolby_edr_image_transfer_function(std::string function) { - _dolby_edr_image_transfer_function = function; - } + void set_dolby_edr_image_transfer_function(std::string const& function); - boost::optional<std::string> dolby_edr_image_transfer_function() const { - return _dolby_edr_image_transfer_function; - } + boost::optional<std::string> dolby_edr_image_transfer_function() const; Standard standard() const { return _standard; @@ -369,6 +362,13 @@ private: void maybe_write_composition_metadata_asset(xmlpp::Element* node) const; void read_composition_metadata_asset(cxml::ConstNodePtr node, std::vector<dcp::VerificationNote>* notes); std::vector<MCASubDescriptor> create_mca_subdescriptors(std::shared_ptr<const SoundAsset> asset) const; + boost::optional<std::string> extension_metadata_value(std::string const& scope, std::string const& name, std::string const& property) const; + void set_extension_metadata_value( + std::string const& scope, + std::string const& name, + std::string const& property, + std::string const& value + ); std::string _issuer; std::string _creator; @@ -404,9 +404,8 @@ private: boost::optional<dcp::Size> _main_picture_active_area; /* See note for _release_territory above */ std::vector<std::string> _additional_subtitle_languages; - boost::optional<std::string> _sign_language_video_language; - boost::optional<std::string> _dolby_edr_image_transfer_function; std::vector<MCASubDescriptor> _mca_sub_descriptors; + std::vector<ExtensionMetadata> _extension_metadata; bool _read_composition_metadata = false; std::vector<std::shared_ptr<Reel>> _reels; diff --git a/src/extension_metadata.cc b/src/extension_metadata.cc new file mode 100644 index 00000000..166dd6a7 --- /dev/null +++ b/src/extension_metadata.cc @@ -0,0 +1,96 @@ +/* + Copyright (C) 2026 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "extension_metadata.h" +#include "warnings.h" +LIBDCP_DISABLE_WARNINGS +#include <libxml++/libxml++.h> +LIBDCP_ENABLE_WARNINGS + + +using std::string; +using std::vector; +using namespace dcp; + + +ExtensionMetadata::ExtensionMetadata(string scope_, string name_, vector<ExtensionMetadata::Property> properties_) + : scope(std::move(scope_)) + , name(std::move(name_)) + , properties(std::move(properties_)) +{ + +} + + +ExtensionMetadata::ExtensionMetadata(cxml::ConstNodePtr node) + : scope(node->string_attribute("scope")) + , name(node->optional_string_child("Name").get_value_or("")) +{ + if (auto list = node->optional_node_child("PropertyList")) { + for (auto property: list->node_children("Property")) { + properties.push_back(Property(property)); + } + } +} + + +void +ExtensionMetadata::as_xml(xmlpp::Element* parent) const +{ + auto extension = cxml::add_child(parent, "ExtensionMetadata", string("meta")); + extension->set_attribute("scope", scope); + cxml::add_child(extension, "Name", string("meta"))->add_child_text(name); + auto property_list = cxml::add_child(extension, "PropertyList", string("meta")); + for (auto const& property: properties) { + property.as_xml(cxml::add_child(property_list, "Property", string("meta"))); + } +} + + +ExtensionMetadata::Property::Property(cxml::ConstNodePtr node) + : name(node->optional_string_child("Name").get_value_or("")) + , value(node->optional_string_child("Value").get_value_or("")) +{ + +} + + +void +ExtensionMetadata::Property::as_xml(xmlpp::Element* parent) const +{ + cxml::add_child(parent, "Name", string("meta"))->add_child_text(name); + cxml::add_child(parent, "Value", string("meta"))->add_child_text(value); +} + + diff --git a/src/extension_metadata.h b/src/extension_metadata.h new file mode 100644 index 00000000..8de687aa --- /dev/null +++ b/src/extension_metadata.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2026 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include <libcxml/cxml.h> +#include <string> + + +namespace dcp { + + +class ExtensionMetadata +{ +public: + class Property + { + public: + Property(std::string name_, std::string value_) + : name(name_) + , value(value_) + {} + + Property(cxml::ConstNodePtr node); + + void as_xml(xmlpp::Element* parent) const; + + std::string name; + std::string value; + }; + + ExtensionMetadata(std::string scope_, std::string name_, std::vector<Property> properties_); + explicit ExtensionMetadata(cxml::ConstNodePtr node); + + void as_xml(xmlpp::Element* parent) const; + + std::string scope; + std::string name; + + std::vector<Property> properties; +}; + + + +} + diff --git a/src/wscript b/src/wscript index 4df80173..92f1363f 100644 --- a/src/wscript +++ b/src/wscript @@ -56,6 +56,7 @@ def build(bld): decrypted_kdm_key.cc encrypted_kdm.cc exceptions.cc + extension_metadata.cc file.cc filesystem.cc font_asset.cc @@ -168,6 +169,7 @@ def build(bld): encrypted_kdm.h equality_options.h exceptions.h + extension_metadata.h file.h filesystem.h font_asset.h |
