summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cpl.cc90
-rw-r--r--src/cpl.h1
-rw-r--r--src/exceptions.cc8
-rw-r--r--src/exceptions.h7
-rw-r--r--src/mca_sub_descriptor.cc106
-rw-r--r--src/mca_sub_descriptor.h11
-rw-r--r--src/sound_asset_writer.cc84
-rw-r--r--src/util.cc11
-rw-r--r--src/util.h1
9 files changed, 168 insertions, 151 deletions
diff --git a/src/cpl.cc b/src/cpl.cc
index 27481939..dc092d3d 100644
--- a/src/cpl.cc
+++ b/src/cpl.cc
@@ -398,82 +398,6 @@ CPL::read_composition_metadata_asset(cxml::ConstNodePtr node, vector<dcp::Verifi
}
-vector<MCASubDescriptor>
-CPL::create_mca_subdescriptors(shared_ptr<const SoundAsset> asset) const
-{
- vector<MCASubDescriptor> descriptors;
-
- auto reader = asset->start_read();
-
- ASDCP::MXF::SoundfieldGroupLabelSubDescriptor* soundfield;
- ASDCP::Result_t r = reader->reader()->OP1aHeader().GetMDObjectByType(
- asdcp_smpte_dict->ul(ASDCP::MDD_SoundfieldGroupLabelSubDescriptor),
- reinterpret_cast<ASDCP::MXF::InterchangeObject**>(&soundfield)
- );
-
- if (KM_SUCCESS(r)) {
- MCASubDescriptor descriptor("SoundfieldGroupLabelSubDescriptor");
- char buffer[64];
- soundfield->InstanceUID.EncodeString(buffer, sizeof(buffer));
- descriptor.instance_id = buffer;
- soundfield->MCALabelDictionaryID.EncodeString(buffer, sizeof(buffer));
- descriptor.mca_label_dictionary_id = "urn:smpte:ul:" + string(buffer);
- soundfield->MCALinkID.EncodeString(buffer, sizeof(buffer));
- descriptor.mca_link_id = buffer;
- soundfield->MCATagSymbol.EncodeString(buffer, sizeof(buffer));
- descriptor.mca_tag_symbol = buffer;
- if (!soundfield->MCATagName.empty()) {
- soundfield->MCATagName.get().EncodeString(buffer, sizeof(buffer));
- descriptor.mca_tag_name = buffer;
- }
- if (!soundfield->RFC5646SpokenLanguage.empty()) {
- soundfield->RFC5646SpokenLanguage.get().EncodeString(buffer, sizeof(buffer));
- descriptor.rfc5646_spoken_language = buffer;
- }
-
- descriptors.push_back(descriptor);
-
- /* Find the MCA subdescriptors in the MXF so that we can also write them here */
- list<ASDCP::MXF::InterchangeObject*> channels;
- auto r = reader->reader()->OP1aHeader().GetMDObjectsByType(
- asdcp_smpte_dict->ul(ASDCP::MDD_AudioChannelLabelSubDescriptor),
- channels
- );
-
- for (auto i: channels) {
- MCASubDescriptor descriptor("AudioChannelLabelSubDescriptor");
- auto channel = reinterpret_cast<ASDCP::MXF::AudioChannelLabelSubDescriptor*>(i);
- channel->InstanceUID.EncodeString(buffer, sizeof(buffer));
- descriptor.instance_id = buffer;
- channel->MCALabelDictionaryID.EncodeString(buffer, sizeof(buffer));
- descriptor.mca_label_dictionary_id = "urn:smpte:ul:" + string(buffer);
- channel->MCALinkID.EncodeString(buffer, sizeof(buffer));
- descriptor.mca_link_id = string(buffer);
- channel->MCATagSymbol.EncodeString(buffer, sizeof(buffer));
- descriptor.mca_tag_symbol = buffer;
- if (!channel->MCATagName.empty()) {
- channel->MCATagName.get().EncodeString(buffer, sizeof(buffer));
- descriptor.mca_tag_name = buffer;
- }
- if (!channel->MCAChannelID.empty()) {
- descriptor.mca_channel_id = fmt::to_string(channel->MCAChannelID.get());
- }
- if (!channel->RFC5646SpokenLanguage.empty()) {
- channel->RFC5646SpokenLanguage.get().EncodeString(buffer, sizeof(buffer));
- descriptor.rfc5646_spoken_language = buffer;
- }
- if (!channel->SoundfieldGroupLinkID.empty()) {
- channel->SoundfieldGroupLinkID.get().EncodeString(buffer, sizeof(buffer));
- descriptor.soundfield_group_link_id = buffer;
- }
- descriptors.push_back(descriptor);
- }
- }
-
- return descriptors;
-}
-
-
/** Write a CompositionMetadataAsset node as a child of @param node provided
* the required metadata is stored in the object. If any required metadata
* is missing this method will do nothing.
@@ -595,22 +519,26 @@ CPL::maybe_write_composition_metadata_asset(xmlpp::Element* node) const
}
if (_profile == Profile::SMPTE_BV21) {
- vector<MCASubDescriptor> descriptors;
+ pair<MCASubDescriptor, vector<MCASubDescriptor>> descriptors;
if (!_mca_sub_descriptors.empty()) {
/* We read these in, so reproduce them */
- descriptors = _mca_sub_descriptors;
+ descriptors.first = _mca_sub_descriptors[0];
+ for (size_t i = 1; i < _mca_sub_descriptors.size(); ++i) {
+ descriptors.second.push_back(_mca_sub_descriptors[i]);
+ }
} else if (_reels.front()->main_sound()) {
if (auto asset = _reels.front()->main_sound()->asset()) {
- descriptors = create_mca_subdescriptors(asset);
+ descriptors = create_mca_subdescriptors({}, 14, {});
}
}
- if (!descriptors.empty()) {
+ if (!descriptors.second.empty()) {
auto mca_subs = cxml::add_child(meta, "mca:MCASubDescriptors");
mca_subs->set_namespace_declaration(mca_sub_descriptors_ns, "mca");
mca_subs->set_namespace_declaration(smpte_395_ns, "r0");
mca_subs->set_namespace_declaration(smpte_335_ns, "r1");
- for (auto const& descriptor: descriptors) {
+ descriptors.first.as_xml(mca_subs);
+ for (auto const& descriptor: descriptors.second) {
descriptor.as_xml(mca_subs);
}
}
diff --git a/src/cpl.h b/src/cpl.h
index 4f1bc834..d9c70bf7 100644
--- a/src/cpl.h
+++ b/src/cpl.h
@@ -361,7 +361,6 @@ 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,
diff --git a/src/exceptions.cc b/src/exceptions.cc
index ebcd70da..9c36de5c 100644
--- a/src/exceptions.cc
+++ b/src/exceptions.cc
@@ -206,6 +206,14 @@ BadURNUUIDError::BadURNUUIDError(string bad_id)
}
+
+BadURNSMPTEULError::BadURNSMPTEULError(string bad_id)
+ : runtime_error(String::compose("Badly-formed URN SMPTE UL %1", bad_id))
+{
+
+}
+
+
LoadVariableZError::LoadVariableZError(string variable_z)
: runtime_error(fmt::format("Badly-formed LoadVariableZ string {}", variable_z))
{
diff --git a/src/exceptions.h b/src/exceptions.h
index 5ccd616c..e43b748c 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -351,6 +351,13 @@ public:
};
+class BadURNSMPTEULError : public std::runtime_error
+{
+public:
+ BadURNSMPTEULError(std::string bad_id);
+};
+
+
class LoadVariableZError : public std::runtime_error
{
public:
diff --git a/src/mca_sub_descriptor.cc b/src/mca_sub_descriptor.cc
index 34bde9f8..a2341f9a 100644
--- a/src/mca_sub_descriptor.cc
+++ b/src/mca_sub_descriptor.cc
@@ -33,14 +33,19 @@
#include "dcp_assert.h"
+#include "main_sound_configuration.h"
#include "mca_sub_descriptor.h"
#include "util.h"
+#include <fmt/format.h>
LIBDCP_DISABLE_WARNINGS
#include <libxml++/libxml++.h>
LIBDCP_ENABLE_WARNINGS
+using std::pair;
using std::string;
+using std::vector;
+using boost::optional;
using namespace dcp;
@@ -48,7 +53,7 @@ MCASubDescriptor::MCASubDescriptor(cxml::ConstNodePtr node)
{
tag = node->name();
instance_id = remove_urn_uuid(node->string_child("InstanceID"));
- mca_label_dictionary_id = node->string_child("MCALabelDictionaryID");
+ mca_label_dictionary_id = remove_urn_smpte_ul(node->string_child("MCALabelDictionaryID"));
mca_link_id = remove_urn_uuid(node->string_child("MCALinkID"));
mca_tag_symbol = node->string_child("MCATagSymbol");
mca_tag_name = node->optional_string_child("MCATagName");
@@ -60,12 +65,20 @@ MCASubDescriptor::MCASubDescriptor(cxml::ConstNodePtr node)
}
+MCASubDescriptor::MCASubDescriptor(std::string tag_)
+ : tag(std::move(tag_))
+ , instance_id(make_uuid())
+{
+
+}
+
+
void
MCASubDescriptor::as_xml(xmlpp::Element* parent) const
{
auto node = cxml::add_child(parent, tag, string("r0"));
cxml::add_child(node, "InstanceID", string("r1"))->add_child_text("urn:uuid:" + instance_id);
- cxml::add_child(node, "MCALabelDictionaryID", string("r1"))->add_child_text(mca_label_dictionary_id);
+ cxml::add_child(node, "MCALabelDictionaryID", string("r1"))->add_child_text("urn:smpte:ul:" + mca_label_dictionary_id);
cxml::add_child(node, "MCALinkID", string("r1"))->add_child_text("urn:uuid:" + mca_link_id);
cxml::add_child(node, "MCATagSymbol", string("r1"))->add_child_text(mca_tag_symbol);
if (mca_tag_name) {
@@ -82,3 +95,92 @@ MCASubDescriptor::as_xml(xmlpp::Element* parent) const
}
}
+
+pair<MCASubDescriptor, vector<MCASubDescriptor>>
+dcp::create_mca_subdescriptors(vector<dcp::Channel> const& extra_active_channels, int total_channels, optional<string> language)
+{
+ /* We always make a descriptor for these channels if they are present in the asset;
+ * there's no way for the caller to tell us whether they are active or not.
+ */
+ std::vector<dcp::Channel> dcp_channels = {
+ Channel::LEFT,
+ Channel::RIGHT,
+ Channel::CENTRE,
+ Channel::LFE,
+ Channel::LS,
+ Channel::RS
+ };
+
+ /* We add descriptors for some extra channels that the caller gave us (we made sure earlier
+ * that nothing "bad" is in this list).
+ */
+ std::copy(extra_active_channels.begin(), extra_active_channels.end(), back_inserter(dcp_channels));
+
+ /* Remove duplicates */
+ std::sort(dcp_channels.begin(), dcp_channels.end());
+ dcp_channels.erase(std::unique(dcp_channels.begin(), dcp_channels.end()), dcp_channels.end());
+
+ /* Remove channels that aren't actually in this MXF at all */
+ dcp_channels.erase(
+ std::remove_if(dcp_channels.begin(), dcp_channels.end(), [total_channels](dcp::Channel channel) {
+ return static_cast<int>(channel) >= total_channels;
+ }),
+ dcp_channels.end()
+ );
+
+ MCASoundField const field =
+ (
+ find(extra_active_channels.begin(), extra_active_channels.end(), dcp::Channel::BSL) != extra_active_channels.end() ||
+ find(extra_active_channels.begin(), extra_active_channels.end(), dcp::Channel::BSR) != extra_active_channels.end()
+ ) ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
+
+ auto group = MCASubDescriptor("SoundfieldGroupLabelSubDescriptor");
+ byte_t const* id = nullptr;
+ group.mca_link_id = make_uuid();
+ switch (field) {
+ case MCASoundField::SEVEN_POINT_ONE:
+ id = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
+ group.mca_tag_symbol = "sg71";
+ group.mca_tag_name = "7.1DS";
+ break;
+ case MCASoundField::FIVE_POINT_ONE:
+ id = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
+ group.mca_tag_symbol = "sg51";
+ group.mca_tag_name = "5.1";
+ break;
+ case MCASoundField::OTHER:
+ break;
+ }
+
+ if (id) {
+ group.mca_label_dictionary_id = fmt::format(
+ "{:02x}{:02x}{:02x}{:02x}.{:02x}{:02x}{:02x}{:02x}.{:02x}{:02x}{:02x}{:02x}.{:02x}{:02x}{:02x}{:02x}",
+ id[0], id[1], id[2], id[3],
+ id[4], id[5], id[6], id[7],
+ id[8], id[9], id[10], id[11],
+ id[12], id[13], id[14], id[15]
+ );
+ }
+
+ vector<MCASubDescriptor> channels;
+ for (auto dcp_channel: dcp_channels) {
+ auto channel = MCASubDescriptor("AudioChannelLabelSubDescriptor");
+ channel.soundfield_group_link_id = group.mca_link_id;
+ channel.mca_channel_id = fmt::to_string(static_cast<int>(dcp_channel) + 1);
+ channel.mca_tag_symbol = "ch" + channel_to_mca_id(dcp_channel, field);
+ channel.mca_tag_name = channel_to_mca_name(dcp_channel, field);
+ channel.rfc5646_spoken_language = language;
+ auto label = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict).Value();
+ channel.mca_label_dictionary_id = fmt::format(
+ "{:02x}{:02x}{:02x}{:02x}.{:02x}{:02x}{:02x}{:02x}.{:02x}{:02x}{:02x}{:02x}.{:02x}{:02x}{:02x}{:02x}",
+ label[0], label[1], label[2], label[3],
+ label[4], label[5], label[6], label[7],
+ label[8], label[9], label[10], label[11],
+ label[12], label[13], label[14], label[15]
+ );
+ channels.push_back(channel);
+ }
+
+ return { group, channels };
+}
+
diff --git a/src/mca_sub_descriptor.h b/src/mca_sub_descriptor.h
index 4a2e8c09..9cbd7d4f 100644
--- a/src/mca_sub_descriptor.h
+++ b/src/mca_sub_descriptor.h
@@ -32,6 +32,7 @@
*/
+#include "types.h"
#include <libcxml/cxml.h>
#include <boost/optional.hpp>
#include <string>
@@ -43,9 +44,8 @@ namespace dcp {
class MCASubDescriptor
{
public:
- explicit MCASubDescriptor(std::string tag_)
- : tag(std::move(tag_))
- {}
+ MCASubDescriptor() = default;
+ explicit MCASubDescriptor(std::string tag_);
explicit MCASubDescriptor(cxml::ConstNodePtr node);
void as_xml(xmlpp::Element* node) const;
@@ -62,4 +62,9 @@ public:
};
+std::pair<MCASubDescriptor, std::vector<MCASubDescriptor>> create_mca_subdescriptors(
+ std::vector<dcp::Channel> const& extra_active_channels, int total_channels, boost::optional<std::string> language
+);
+
+
}
diff --git a/src/sound_asset_writer.cc b/src/sound_asset_writer.cc
index 0c04e771..bad4842c 100644
--- a/src/sound_asset_writer.cc
+++ b/src/sound_asset_writer.cc
@@ -44,6 +44,7 @@
#include "exceptions.h"
#include "filesystem.h"
#include "main_sound_configuration.h"
+#include "mca_sub_descriptor.h"
#include "sound_asset.h"
#include "sound_asset_writer.h"
#include "warnings.h"
@@ -154,6 +155,7 @@ SoundAssetWriter::start ()
throw_from_asdcplib(r, _file, FileError("could not open audio MXF for writing", _file.string(), r));
}
+#if 0
if (_asset->standard() == Standard::SMPTE && _include_mca_subdescriptors) {
ASDCP::MXF::WaveAudioDescriptor* essence_descriptor = nullptr;
@@ -163,81 +165,35 @@ SoundAssetWriter::start ()
DCP_ASSERT (essence_descriptor);
essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_4_WTF);
- auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
- GenRandomValue (soundfield->MCALinkID);
- if (auto lang = _asset->language()) {
- soundfield->RFC5646SpokenLanguage = *lang;
- }
-
- MCASoundField const field =
- (
- find(_extra_active_channels.begin(), _extra_active_channels.end(), dcp::Channel::BSL) != _extra_active_channels.end() ||
- find(_extra_active_channels.begin(), _extra_active_channels.end(), dcp::Channel::BSR) != _extra_active_channels.end()
- ) ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
+ auto descriptors = create_mca_subdescriptors(_extra_active_channels, _asset->channels(), _asset->language());
- if (field == MCASoundField::SEVEN_POINT_ONE) {
- soundfield->MCATagSymbol = "sg71";
- soundfield->MCATagName = "7.1DS";
-LIBDCP_DISABLE_WARNINGS
- soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
-LIBDCP_ENABLE_WARNINGS
- } else {
- soundfield->MCATagSymbol = "sg51";
- soundfield->MCATagName = "5.1";
-LIBDCP_DISABLE_WARNINGS
- soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
-LIBDCP_ENABLE_WARNINGS
+ auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
+ soundfield->MCALinkID = descriptors.first.mca_link_id;
+ if (descriptors.first.rfc5646_spoken_language) {
+ soundfield->RFC5646SpokenLanguage = *descriptors.first.rfc5646_spoken_language);
}
-
+ soundfield->MCATagSymbol = descriptors.first.mca_tag_symbol;
+ soundfield->MCATagName = *descriptors.first.mca_tag_name;
+ soundfield->MCALabelDictionaryID = descriptors.first.mca_label_dictionary_id;
_state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
- /* We always make a descriptor for these channels if they are present in the asset;
- * there's no way for the caller to tell us whether they are active or not.
- */
- std::vector<dcp::Channel> dcp_channels = {
- Channel::LEFT,
- Channel::RIGHT,
- Channel::CENTRE,
- Channel::LFE,
- Channel::LS,
- Channel::RS
- };
-
- /* We add descriptors for some extra channels that the caller gave us (we made sure earlier
- * that nothing "bad" is in this list).
- */
- std::copy(_extra_active_channels.begin(), _extra_active_channels.end(), back_inserter(dcp_channels));
-
- /* Remove duplicates */
- std::sort(dcp_channels.begin(), dcp_channels.end());
- dcp_channels.erase(std::unique(dcp_channels.begin(), dcp_channels.end()), dcp_channels.end());
-
- /* Remove channels that aren't actually in this MXF at all */
- dcp_channels.erase(
- std::remove_if(dcp_channels.begin(), dcp_channels.end(), [this](dcp::Channel channel) {
- return static_cast<int>(channel) >= _asset->channels();
- }),
- dcp_channels.end()
- );
-
- for (auto dcp_channel: dcp_channels) {
- auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
- GenRandomValue (channel->MCALinkID);
- channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
- channel->MCAChannelID = static_cast<int>(dcp_channel) + 1;
- channel->MCATagSymbol = "ch" + channel_to_mca_id(dcp_channel, field);
- channel->MCATagName = channel_to_mca_name(dcp_channel, field);
+ for (auto const& channel: descriptors.second) {
+ auto mxf_descriptor = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
+ mxf_descriptor->MCALinkID = channel.mca_link_id;
+ channel->SoundfieldGroupLinkID = *channel.soundfield_group_link_id;
+ channel->MCAChannelID = channel.mca_channel_id;
+ channel->MCATagSymbol = channel.mca_tag_symbol;
+ channel->MCATagName = channel.mca_tag_name;
if (auto lang = _asset->language()) {
- channel->RFC5646SpokenLanguage = *lang;
+ channel->RFC5646SpokenLanguage = *channel.rfc5646_spoken_language;
}
-LIBDCP_DISABLE_WARNINGS
- channel->MCALabelDictionaryID = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict);
-LIBDCP_ENABLE_WARNINGS
+ channel->MCALabelDictionaryID = *channel.mca_label_dictionary_id;
_state->mxf_writer.OP1aHeader().AddChildObject(channel);
essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
}
}
+#endif
_asset->set_file (_file);
_started = true;
diff --git a/src/util.cc b/src/util.cc
index 77493ffa..17649c51 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -330,6 +330,17 @@ dcp::remove_urn_uuid(optional<string> raw)
string
+dcp::remove_urn_smpte_ul(string raw)
+{
+ if (raw.substr(0, 13) != "urn:smpte:ul:") {
+ throw BadURNSMPTEULError(raw);
+ }
+
+ return raw.substr(13);
+}
+
+
+string
dcp::openjpeg_version ()
{
return opj_version ();
diff --git a/src/util.h b/src/util.h
index cfed9fcb..f7943463 100644
--- a/src/util.h
+++ b/src/util.h
@@ -97,6 +97,7 @@ extern std::string make_digest (ArrayData data);
extern bool ids_equal (std::string a, std::string b);
extern std::string remove_urn_uuid (std::string raw);
extern boost::optional<std::string> remove_urn_uuid(boost::optional<std::string> raw);
+extern std::string remove_urn_smpte_ul(std::string raw);
/** Set up various bits that the library needs. Should be called once
* by client applications.