summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cpl.cc147
-rw-r--r--src/cpl.h27
-rw-r--r--src/extension_metadata.cc96
-rw-r--r--src/extension_metadata.h75
-rw-r--r--src/wscript2
5 files changed, 286 insertions, 61 deletions
diff --git a/src/cpl.cc b/src/cpl.cc
index 3e7ca7a3..27481939 100644
--- a/src/cpl.cc
+++ b/src/cpl.cc
@@ -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
+ );
+}
diff --git a/src/cpl.h b/src/cpl.h
index 7bbb2d53..4f1bc834 100644
--- a/src/cpl.h
+++ b/src/cpl.h
@@ -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