diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cpl.cc | 90 | ||||
| -rw-r--r-- | src/cpl.h | 1 | ||||
| -rw-r--r-- | src/exceptions.cc | 8 | ||||
| -rw-r--r-- | src/exceptions.h | 7 | ||||
| -rw-r--r-- | src/mca_sub_descriptor.cc | 106 | ||||
| -rw-r--r-- | src/mca_sub_descriptor.h | 11 | ||||
| -rw-r--r-- | src/sound_asset_writer.cc | 84 | ||||
| -rw-r--r-- | src/util.cc | 11 | ||||
| -rw-r--r-- | src/util.h | 1 |
9 files changed, 168 insertions, 151 deletions
@@ -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); } } @@ -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 (); @@ -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. |
