From ee10f5c7297fe16b5b38035c3b4384b23cf7be8e Mon Sep 17 00:00:00 2001 From: davemar-bbc Date: Thu, 3 Apr 2025 09:38:24 +0100 Subject: [PATCH 1/6] Added tagList support. Extended profile support. --- include/adm/document.hpp | 37 +++ include/adm/elements.hpp | 1 + include/adm/elements/audio_channel_format.hpp | 2 +- include/adm/elements/profile_list.hpp | 4 +- include/adm/elements/tag_list.hpp | 256 ++++++++++++++++++ include/adm/private/document_parser.hpp | 6 + include/adm/private/rapidxml_formatter.hpp | 3 + include/adm/private/rapidxml_wrapper.hpp | 17 ++ src/CMakeLists.txt | 1 + src/document.cpp | 42 +++ src/elements/tag_list.cpp | 92 +++++++ src/private/document_parser.cpp | 44 +++ src/private/rapidxml_formatter.cpp | 17 ++ src/private/xml_writer.cpp | 2 + 14 files changed, 521 insertions(+), 3 deletions(-) create mode 100644 include/adm/elements/tag_list.hpp create mode 100644 src/elements/tag_list.cpp diff --git a/include/adm/document.hpp b/include/adm/document.hpp index 3e605782..f10ff24b 100644 --- a/include/adm/document.hpp +++ b/include/adm/document.hpp @@ -80,6 +80,10 @@ namespace adm { ADM_EXPORT bool add(std::shared_ptr trackFormat); /// @brief Add an AudioTrackUid ADM_EXPORT bool add(std::shared_ptr trackUid); + /// @brief Add a profileList + ADM_EXPORT bool add(std::shared_ptr profileList); + /// @brief Add a tagList + ADM_EXPORT bool add(std::shared_ptr tagList); ///@} /** @name Remove ADM elements @@ -104,6 +108,10 @@ namespace adm { ADM_EXPORT bool remove(std::shared_ptr trackFormat); /// @brief Remove an AudioTrackUid ADM_EXPORT bool remove(std::shared_ptr trackUid); + /// @brief Remove a profileList + ADM_EXPORT bool remove(std::shared_ptr profileList); + /// @brief Remove a tagList + ADM_EXPORT bool remove(std::shared_ptr tagList); ///@} /** @@ -126,6 +134,13 @@ namespace adm { template ElementRange getElements(); + template + std::shared_ptr getElement() const; + + template + std::shared_ptr getElement(); + + /** @name Lookup ADM elements by its Id * * Lookup the first ADM element with the given Id. @@ -257,6 +272,10 @@ namespace adm { detail::ParameterTraits::tag) const; ADM_EXPORT ElementRange getElements( detail::ParameterTraits::tag) const; + ADM_EXPORT std::shared_ptr getElement( + detail::ParameterTraits::tag) const; + ADM_EXPORT std::shared_ptr getElement( + detail::ParameterTraits::tag) const; ADM_EXPORT ElementRange getElements( detail::ParameterTraits::tag); ADM_EXPORT ElementRange getElements( @@ -273,6 +292,10 @@ namespace adm { detail::ParameterTraits::tag); ADM_EXPORT ElementRange getElements( detail::ParameterTraits::tag); + ADM_EXPORT std::shared_ptr getElement( + detail::ParameterTraits::tag); + ADM_EXPORT std::shared_ptr getElement( + detail::ParameterTraits::tag); /// check the parent of an element /// @@ -300,6 +323,8 @@ namespace adm { std::vector> audioStreamFormats_; std::vector> audioTrackFormats_; std::vector> audioTrackUids_; + std::shared_ptr profileList_; + std::shared_ptr tagList_; detail::IdAssigner idAssigner_; }; @@ -317,4 +342,16 @@ namespace adm { return getElements(Tag()); } + template + std::shared_ptr Document::getElement() const { + typedef typename detail::ParameterTraits::tag Tag; + return getElement(Tag()); + } + + template + std::shared_ptr Document::getElement() { + typedef typename detail::ParameterTraits::tag Tag; + return getElement(Tag()); + } + } // namespace adm diff --git a/include/adm/elements.hpp b/include/adm/elements.hpp index 9fdf9366..70bdd16d 100644 --- a/include/adm/elements.hpp +++ b/include/adm/elements.hpp @@ -27,6 +27,7 @@ #include "adm/elements/audio_stream_format.hpp" #include "adm/elements/audio_track_uid.hpp" #include "adm/elements/profile_list.hpp" +#include "adm/elements/tag_list.hpp" #include "adm/elements/audio_block_format_direct_speakers.hpp" #include "adm/elements/audio_block_format_matrix.hpp" diff --git a/include/adm/elements/audio_channel_format.hpp b/include/adm/elements/audio_channel_format.hpp index 45df467e..aa474157 100644 --- a/include/adm/elements/audio_channel_format.hpp +++ b/include/adm/elements/audio_channel_format.hpp @@ -381,7 +381,7 @@ namespace adm { return previous == 0u || current == previous.get() + 1u; } } - + template void AudioChannelFormat::assignId(BlockFormat &blockFormat, BlockFormat *previousBlock) { diff --git a/include/adm/elements/profile_list.hpp b/include/adm/elements/profile_list.hpp index a49faf0d..ec19a79e 100644 --- a/include/adm/elements/profile_list.hpp +++ b/include/adm/elements/profile_list.hpp @@ -85,12 +85,12 @@ namespace adm { using ProfileListBase = HasParameters>; } // namespace detail - struct ProfileceListTag {}; + struct ProfileListTag {}; class ProfileList : private detail::ProfileListBase, private detail::AddWrapperMethods { public: - using tag = ProfileceListTag; + using tag = ProfileListTag; template explicit ProfileList(Parameters... namedArgs) { diff --git a/include/adm/elements/tag_list.hpp b/include/adm/elements/tag_list.hpp new file mode 100644 index 00000000..8ee4ee84 --- /dev/null +++ b/include/adm/elements/tag_list.hpp @@ -0,0 +1,256 @@ +#pragma once +#include +#include "adm/detail/auto_base.hpp" +#include "adm/elements/audio_programme.hpp" +#include "adm/elements_fwd.hpp" +#include "adm/detail/named_option_helper.hpp" +#include "adm/detail/optional_comparison.hpp" +#include "adm/errors.hpp" + +namespace adm { + struct TTagValueTag {}; + using TTagValue = detail::NamedType; + + struct TTagClassTag {}; + using TTagClass = detail::NamedType; + + struct TTagTag {}; + + namespace detail { + extern template class ADM_EXPORT_TEMPLATE_METHODS RequiredParameter; + extern template class ADM_EXPORT_TEMPLATE_METHODS OptionalParameter; + + using TTagBase = HasParameters, + OptionalParameter>; + } // namespace detail + + class TTag : private detail::TTagBase, + private detail::AddWrapperMethods { + public: + using tag = TTagTag; + + template + explicit TTag(Parameters... namedArgs) { + detail::setNamedOptionHelper(this, std::move(namedArgs)...); + } + + ADM_EXPORT explicit TTag(std::string str) + : TTag(TTagValue(std::move(str))) {} + ADM_EXPORT explicit TTag(const char* s); + + ADM_EXPORT void print(std::ostream& os) const; + + using detail::TTagBase::set; + using detail::TTagBase::unset; + using detail::AddWrapperMethods::get; + using detail::AddWrapperMethods::has; + using detail::AddWrapperMethods::isDefault; + using detail::AddWrapperMethods::unset; + + private: + using detail::TTagBase::get; + using detail::TTagBase::has; + + friend class detail::AddWrapperMethods; + }; + + struct TTagsTag {}; + + using TTags = std::vector; + ADD_TRAIT(TTags, TTagsTag); + + inline bool operator==(const TTag &a, const TTag &b) { + return detail::optionalsEqual(a, b); + } + + inline bool operator!=(const TTag &a, const TTag &b) { + return !(a==b); + } + + struct TagGroupTag {}; + + namespace detail { + extern template class ADM_EXPORT_TEMPLATE_METHODS VectorParameter; + + using TagGroupBase = HasParameters>; + } // namespace detail + + class TagGroup : private detail::TagGroupBase, + private detail::AddWrapperMethods { + public: + using tag = TagGroupTag; + + // DEBUG FUNCTIONS + int getTempId() { return temp_id_; }; + void setTempId(int n) { temp_id_ = n; }; + + template + explicit TagGroup(Parameters... namedArgs) { + detail::setNamedOptionHelper(this, std::move(namedArgs)...); + } + + /// @brief Add reference to an AudioProgramme + ADM_EXPORT bool addReference(std::shared_ptr programme); + + /// @brief Add reference to an AudioContent + ADM_EXPORT bool addReference(std::shared_ptr content); + + /// @brief Add reference to an AudioObject + ADM_EXPORT bool addReference(std::shared_ptr object); + + template + ElementRange getReferences(); + + template + ElementRange getReferences() const; + + /// @brief Remove reference to an AudioProgramme + ADM_EXPORT void removeReference(std::shared_ptr programme); + + /// @brief Remove reference to an AudioContent + ADM_EXPORT void removeReference(std::shared_ptr content); + + /// @brief Remove reference to an AudioObject + ADM_EXPORT void removeReference(std::shared_ptr object); + + template + void clearReferences(); + + using detail::TagGroupBase::set; + using detail::AddWrapperMethods::get; + using detail::AddWrapperMethods::has; + using detail::AddWrapperMethods::isDefault; + using detail::AddWrapperMethods::unset; + using detail::TagGroupBase::add; + using detail::TagGroupBase::remove; + + private: + int temp_id_; + + using detail::TagGroupBase::get; + using detail::TagGroupBase::has; + using detail::TagGroupBase::isDefault; + using detail::TagGroupBase::unset; + + friend class detail::AddWrapperMethods; + + ADM_EXPORT ElementRange getReferences( + detail::ParameterTraits::tag) const; + ADM_EXPORT ElementRange getReferences( + detail::ParameterTraits::tag); + ADM_EXPORT ElementRange getReferences( + detail::ParameterTraits::tag) const; + ADM_EXPORT ElementRange getReferences( + detail::ParameterTraits::tag); + ADM_EXPORT ElementRange getReferences( + detail::ParameterTraits::tag) const; + ADM_EXPORT ElementRange getReferences( + detail::ParameterTraits::tag); + + ADM_EXPORT void clearReferences(detail::ParameterTraits::tag); + ADM_EXPORT void clearReferences(detail::ParameterTraits::tag); + ADM_EXPORT void clearReferences(detail::ParameterTraits::tag); + + ADM_EXPORT void disconnectReferences(); + + std::vector> audioProgrammes_; + std::vector> audioContents_; + std::vector> audioObjects_; + }; + + inline bool operator==(const TagGroup &a, const TagGroup &b) { + return detail::optionalsEqual(a, b); + } + + inline bool operator!=(const TagGroup &a, const TagGroup &b) { + return !(a == b); + } + + template + ElementRange TagGroup::getReferences() const { + typedef typename detail::ParameterTraits::tag Tag; + return getReferences(Tag()); + } + + template + ElementRange TagGroup::getReferences() { + typedef typename detail::ParameterTraits::tag Tag; + return getReferences(Tag()); + } + + inline ElementRange TagGroup::getReferences( + detail::ParameterTraits::tag) const { + return detail::makeElementRange(audioProgrammes_); + } + + inline ElementRange TagGroup::getReferences( + detail::ParameterTraits::tag) { + return detail::makeElementRange(audioProgrammes_); + } + + inline ElementRange TagGroup::getReferences( + detail::ParameterTraits::tag) const { + return detail::makeElementRange(audioContents_); + } + + inline ElementRange TagGroup::getReferences( + detail::ParameterTraits::tag) { + return detail::makeElementRange(audioContents_); + } + + inline ElementRange TagGroup::getReferences( + detail::ParameterTraits::tag) const { + return detail::makeElementRange(audioObjects_); + } + + inline ElementRange TagGroup::getReferences( + detail::ParameterTraits::tag) { + return detail::makeElementRange(audioObjects_); + } + + template + void TagGroup::clearReferences() { + typedef typename detail::ParameterTraits::tag Tag; + clearReferences(Tag()); + } + + struct TagGroupsTag {}; + + using TagGroups = std::vector; + ADD_TRAIT(TagGroups, TagGroupsTag); + + namespace detail { + extern template class ADM_EXPORT_TEMPLATE_METHODS VectorParameter; + + using TagListBase = HasParameters>; + } // namespace detail + + struct TagListTag {}; + + class TagList : private detail::TagListBase, + private detail::AddWrapperMethods { + public: + using tag = TagListTag; + + template + explicit TagList(Parameters... namedArgs) { + detail::setNamedOptionHelper(this, std::move(namedArgs)...); + } + + using detail::TagListBase::set; + using detail::AddWrapperMethods::get; + using detail::AddWrapperMethods::has; + using detail::AddWrapperMethods::isDefault; + using detail::AddWrapperMethods::unset; + using detail::TagListBase::add; + using detail::TagListBase::remove; + + private: + using detail::TagListBase::get; + using detail::TagListBase::has; + using detail::TagListBase::isDefault; + using detail::TagListBase::unset; + + friend class detail::AddWrapperMethods; + }; +} // namespace adm diff --git a/include/adm/private/document_parser.hpp b/include/adm/private/document_parser.hpp index ee2ac5c2..bcaa7118 100644 --- a/include/adm/private/document_parser.hpp +++ b/include/adm/private/document_parser.hpp @@ -70,6 +70,7 @@ namespace adm { NodePtr node, boost::optional timeReference); Profile parseProfile(NodePtr node); ProfileList parseProfileList(NodePtr node); + TTag parseTTag(NodePtr node); NodePtr findAudioFormatExtendedNodeEbuCore(NodePtr root); NodePtr findAudioFormatExtendedNodeFullRecursive(NodePtr root); @@ -105,6 +106,8 @@ namespace adm { std::shared_ptr parseAudioPackFormat(NodePtr node); std::shared_ptr parseAudioTrackUid(NodePtr node); std::shared_ptr parseAudioChannelFormat(NodePtr node); + std::shared_ptr parseTagGroup(NodePtr node); + TagList parseTagList(NodePtr node); rapidxml::file<> xmlFile_; ParserOptions options_; @@ -127,6 +130,9 @@ namespace adm { std::map, AudioChannelFormatId> streamFormatChannelFormatRef_; std::map, AudioPackFormatId> streamFormatPackFormatRef_; std::map, std::vector> streamFormatTrackFormatRefs_; + std::map, std::vector> tagGroupProgrammeRefs_; + std::map, std::vector> tagGroupContentRefs_; + std::map, std::vector> tagGroupObjectRefs_; // clang-format on /// used to keep track of element IDs ourselves to avoid having it diff --git a/include/adm/private/rapidxml_formatter.hpp b/include/adm/private/rapidxml_formatter.hpp index df06671e..97ea6c38 100644 --- a/include/adm/private/rapidxml_formatter.hpp +++ b/include/adm/private/rapidxml_formatter.hpp @@ -55,6 +55,9 @@ namespace adm { XmlNode &node, const std::shared_ptr trackUid); void formatProfileList(XmlNode &node, const ProfileList &profileList); void formatProfile(XmlNode &node, const Profile &profile); + void formatTagList(XmlNode &node, const TagList &tagList); + void formatTagGroup(XmlNode &node, const TagGroup &tagGroup); + void formatTag(XmlNode &node, const TTag &tag); void formatBlockFormatDirectSpeakers( XmlNode &node, const AudioBlockFormatDirectSpeakers &audioBlock, diff --git a/include/adm/private/rapidxml_wrapper.hpp b/include/adm/private/rapidxml_wrapper.hpp index 5d4ce89a..8843b648 100644 --- a/include/adm/private/rapidxml_wrapper.hpp +++ b/include/adm/private/rapidxml_wrapper.hpp @@ -102,6 +102,11 @@ namespace adm { void addBaseElements(const Source &src, const std::string &name, Callable formatter); + template + void addBaseElement(const Source &src, const std::string &name, + Callable formatter); + template void addReference(const Source &src, const std::string &name); @@ -262,6 +267,18 @@ namespace adm { } } + template + void XmlNode::addBaseElement(const Source &src, const std::string &name, + Callable formatter) { + auto admElement = src->template getElement(); + if (admElement) { + auto node = addNode(name); + formatter(node, *admElement); + } + } + + template void XmlNode::addReference(const Source &src, const std::string &name) { addElement(src->template getReference(), name); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b11f800f..7e4d98d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ add_library(adm elements/type_descriptor.cpp elements/format_descriptor.cpp elements/headphone_virtualise.cpp + elements/tag_list.cpp utilities/block_duration_assignment.cpp utilities/copy.cpp utilities/id_assignment.cpp diff --git a/src/document.cpp b/src/document.cpp index 85759cdd..6e5490d7 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -215,6 +215,18 @@ namespace adm { } } + bool Document::add(std::shared_ptr profileList) { + // Attorney here? + profileList_ = profileList; + return true; + } + + bool Document::add(std::shared_ptr tagList) { + // Attorney here? + tagList_ = tagList; + return true; + } + // ---- remove elements --- // bool Document::remove(std::shared_ptr programme) { auto it = @@ -361,6 +373,16 @@ namespace adm { return false; } + bool Document::remove(std::shared_ptr profileList) { + profileList_ = nullptr; // Probably need something nicer + return true; + } + + bool Document::remove(std::shared_ptr tagList) { + tagList_ = nullptr; // Probably need something nicer + return true; + } + // ---- get elements ---- // ElementRange Document::getElements( detail::ParameterTraits::tag) const { @@ -402,6 +424,16 @@ namespace adm { return detail::makeElementRange(audioTrackUids_); } + std::shared_ptr Document::getElement( + detail::ParameterTraits::tag) const { + return std::shared_ptr(profileList_); + } + + std::shared_ptr Document::getElement( + detail::ParameterTraits::tag) const { + return std::shared_ptr(tagList_); + } + ElementRange Document::getElements( detail::ParameterTraits::tag) { return detail::makeElementRange(audioProgrammes_); @@ -442,6 +474,16 @@ namespace adm { return detail::makeElementRange(audioTrackUids_); } + std::shared_ptr Document::getElement( + detail::ParameterTraits::tag) { + return std::shared_ptr(profileList_); + } + + std::shared_ptr Document::getElement( + detail::ParameterTraits::tag) { + return std::shared_ptr(tagList_); + } + // ---- lookup elements ---- // std::shared_ptr Document::lookup(const AudioProgrammeId& id) { return lookupById(getElements(), id); diff --git a/src/elements/tag_list.cpp b/src/elements/tag_list.cpp new file mode 100644 index 00000000..2f4c5f75 --- /dev/null +++ b/src/elements/tag_list.cpp @@ -0,0 +1,92 @@ +#include "adm/elements/tag_list.hpp" + +namespace adm { + TTag::TTag(const char* s) { + // to avoid UB from std::string + if (!s) { + throw error::AdmGenericRuntimeError{ + "Cannot construct Tag from null const char*"}; + } + set(TTagValue{std::string{s}}); + } + + // ---- References ---- // + bool TagGroup::addReference(std::shared_ptr programme) { + auto it = std::find(audioProgrammes_.begin(), audioProgrammes_.end(), programme); + if (it == audioProgrammes_.end()) { + audioProgrammes_.push_back(std::move(programme)); + return true; + } else { + return false; + } + } + + bool TagGroup::addReference(std::shared_ptr content) { + auto it = std::find(audioContents_.begin(), audioContents_.end(), content); + if (it == audioContents_.end()) { + audioContents_.push_back(std::move(content)); + return true; + } else { + return false; + } + } + + bool TagGroup::addReference(std::shared_ptr object) { + auto it = std::find(audioObjects_.begin(), audioObjects_.end(), object); + if (it == audioObjects_.end()) { + audioObjects_.push_back(std::move(object)); + return true; + } else { + return false; + } + } + + void TagGroup::removeReference(std::shared_ptr programme) { + auto it = std::find(audioProgrammes_.begin(), audioProgrammes_.end(), programme); + if (it != audioProgrammes_.end()) { + audioProgrammes_.erase(it); + } + } + + void TagGroup::removeReference(std::shared_ptr content) { + auto it = std::find(audioContents_.begin(), audioContents_.end(), content); + if (it != audioContents_.end()) { + audioContents_.erase(it); + } + } + + void TagGroup::removeReference(std::shared_ptr object) { + auto it = std::find(audioObjects_.begin(), audioObjects_.end(), object); + if (it != audioObjects_.end()) { + audioObjects_.erase(it); + } + } + + void TagGroup::disconnectReferences() { + clearReferences(); + clearReferences(); + clearReferences(); + } + + void TagGroup::clearReferences( + detail::ParameterTraits::tag) { + audioProgrammes_.clear(); + } + + void TagGroup::clearReferences( + detail::ParameterTraits::tag) { + audioContents_.clear(); + } + + void TagGroup::clearReferences( + detail::ParameterTraits::tag) { + audioObjects_.clear(); + } + + namespace detail { + template class RequiredParameter; + template class OptionalParameter; + template class VectorParameter; + template class VectorParameter; + } // namespace detail +} // namespace adm diff --git a/src/private/document_parser.cpp b/src/private/document_parser.cpp index cb2635b8..75c94bdf 100644 --- a/src/private/document_parser.cpp +++ b/src/private/document_parser.cpp @@ -3,6 +3,7 @@ #include "adm/private/xml_parser_helper.hpp" #include "adm/detail/named_type_validators.hpp" #include "adm/errors.hpp" + namespace adm { namespace xml { @@ -111,6 +112,18 @@ namespace adm { resolveReference(streamFormatPackFormatRef_); resolveReferences(streamFormatTrackFormatRefs_); + // add other ADM elements to ADM document + for (NodePtr node = root->first_node(); node; + node = node->next_sibling()) { + std::string nodeName(node->name(), node->name_size()); + if (nodeName == "profileList") { + // Can't use the local add function as that contains an ID setting + document_->add(std::make_shared(parseProfileList(node))); + } else if (nodeName == "tagList") { + // Can't use the local add function as that contains an ID setting + document_->add(std::make_shared(parseTagList(node))); + } + } } else { throw error::XmlParsingError("audioFormatExtended node not found"); } @@ -540,6 +553,37 @@ namespace adm { return profileList; } + TTag parseTTag(NodePtr node) { + TTag ttag; + setValue(node, ttag); + setOptionalAttribute(node, "class", ttag); + return ttag; + } + + std::shared_ptr DocumentParser::parseTagGroup(NodePtr node) { + auto tagGroup = std::make_shared(); + tagGroup->setTempId(rand() % 1000); + addOptionalElements(node, "tag", tagGroup, &parseTTag); + addOptionalReferences(node, "audioProgrammeIDRef", tagGroup, tagGroupProgrammeRefs_, &parseAudioProgrammeId); + addOptionalReferences(node, "audioContentIDRef", tagGroup, tagGroupContentRefs_, &parseAudioContentId); + addOptionalReferences(node, "audioObjectIDRef", tagGroup, tagGroupObjectRefs_, &parseAudioObjectId); + + resolveReferences(tagGroupProgrammeRefs_); + resolveReferences(tagGroupContentRefs_); + resolveReferences(tagGroupObjectRefs_); + + return tagGroup; + } + + TagList DocumentParser::parseTagList(NodePtr node) { + TagList tagList; + auto elements = detail::findElements(node, "tagGroup"); + for (auto& element : elements) { + detail::invokeAdd(tagList, TagGroup(*parseTagGroup(element))); + } + return tagList; + } + namespace { template void addTimeParametersToBlock( diff --git a/src/private/rapidxml_formatter.cpp b/src/private/rapidxml_formatter.cpp index eed2d7bf..3861132a 100644 --- a/src/private/rapidxml_formatter.cpp +++ b/src/private/rapidxml_formatter.cpp @@ -789,5 +789,22 @@ namespace adm { node.addAttribute(&profile, "profileVersion"); node.addAttribute(&profile, "profileLevel"); } + + void formatTagList(XmlNode &node, const TagList &tagList) { + node.addVectorElements(&tagList, "tagGroup", &formatTagGroup); + } + + void formatTagGroup(XmlNode &node, const TagGroup &tagGroup) { + node.addVectorElements(&tagGroup, "tag", &formatTag); + node.addReferences(&tagGroup, "audioProgrammeIDRef"); + node.addReferences(&tagGroup, "audioContentIDRef"); + node.addReferences(&tagGroup, "audioObjectIDRef"); + } + + void formatTag(XmlNode &node, const TTag &tag) { + node.setValue(tag.get()); + node.addAttribute(&tag, "class"); + } + } // namespace xml } // namespace adm diff --git a/src/private/xml_writer.cpp b/src/private/xml_writer.cpp index 6ebc608a..956b5a01 100644 --- a/src/private/xml_writer.cpp +++ b/src/private/xml_writer.cpp @@ -30,6 +30,8 @@ namespace adm { TimeReference timeReference = TimeReference::TOTAL) { // clang-format off audioFormatExtended.addOptionalAttribute(document, "version"); + audioFormatExtended.addBaseElement(document, "profileList", &formatProfileList); + audioFormatExtended.addBaseElement(document, "tagList", &formatTagList); audioFormatExtended.addBaseElements(document, "audioProgramme", &formatAudioProgramme); audioFormatExtended.addBaseElements(document, "audioContent", &formatAudioContent); audioFormatExtended.addBaseElements(document, "audioObject", &formatAudioObject); From 814f02e19a37a02e3a897ecce06ed9399f8a386f Mon Sep 17 00:00:00 2001 From: davemar-bbc Date: Thu, 3 Apr 2025 10:03:25 +0100 Subject: [PATCH 2/6] Added parseFrame to parse.hpp/cpp --- include/adm/parse.hpp | 17 +++++++++++++++++ src/parse.cpp | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/include/adm/parse.hpp b/include/adm/parse.hpp index 0ccfae24..cbfaa961 100644 --- a/include/adm/parse.hpp +++ b/include/adm/parse.hpp @@ -88,6 +88,23 @@ namespace adm { std::istream& stream, FrameHeader const& header, xml::ParserOptions options = xml::ParserOptions::none); + /** + * @brief Parse an xml frame containing an audioFormatExtended + * node into an adm::Document, using a SADM FrameHeader to check for consistency. + * Primarily intended for parsing sadm frames. + * + * Parse adm data from an `std::istream`. + * @param stream input stream to parse XML data + * @param header S-ADM Frame header + * @param options Options to influence the XML parser behaviour + * @param commonDefinitions Common Defintions document + */ + ADM_EXPORT std::shared_ptr parseFrame( + std::istream& stream, + const FrameHeader& header, + xml::ParserOptions options, + std::shared_ptr commonDefinitions); + /** * @brief Parse an XML representation of a serial ADM frame and return * the frameHeader element as an adm::FrameHeader object diff --git a/src/parse.cpp b/src/parse.cpp index e7d7bba4..b98165b7 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -37,6 +37,15 @@ namespace adm { return parser.parse(); } + std::shared_ptr parseFrame(std::istream& stream, + const FrameHeader& header, + xml::ParserOptions options, + std::shared_ptr commonDefinitions) { + xml::DocumentParser parser(stream, options, commonDefinitions); + parser.setHeader(header); + return parser.parse(); + } + FrameHeader parseFrameHeader(std::istream& stream, xml::ParserOptions options) { xml::FrameHeaderParser parser(stream, options); From 582c17a1dbb38d1ffff655d3af95b23d26898d2f Mon Sep 17 00:00:00 2001 From: davemar-bbc Date: Thu, 3 Apr 2025 10:24:50 +0100 Subject: [PATCH 3/6] Added some tests for tagList. --- tests/CMakeLists.txt | 1 + tests/tag_list_tests.cpp | 64 +++++++++++++++++++++++++++ tests/test_data/tag_list.accepted.xml | 32 ++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 tests/tag_list_tests.cpp create mode 100644 tests/test_data/tag_list.accepted.xml diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0fef76a9..0315572e 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -72,6 +72,7 @@ add_adm_test("screen_edge_lock_tests") add_adm_test("speaker_position_tests") add_adm_test("type_descriptor_tests") add_adm_test("version_tests") +add_adm_test("tag_list_tests") add_adm_test("xml_audio_block_format_objects_tests") add_adm_test("xml_loudness_metadata_tests") add_adm_test("xml_parser_audio_block_format_direct_speakers_tests") diff --git a/tests/tag_list_tests.cpp b/tests/tag_list_tests.cpp new file mode 100644 index 00000000..2812ba98 --- /dev/null +++ b/tests/tag_list_tests.cpp @@ -0,0 +1,64 @@ +#include +#include "helper/parameter_checks.hpp" +#include "adm/document.hpp" +#include "adm/parse.hpp" +#include "adm/write.hpp" +#include "helper/file_comparator.hpp" +#include "adm/elements/tag_list.hpp" + +#include + +using namespace adm; +using namespace adm_test; + +TEST_CASE("Tag parameters") { + TTag tag{ + TTagValue("value") + }; + + check_optional_param(tag, + canBeSetTo(TTagClass{"class2"})); + check_required_param(tag, + hasDefaultOf(TTagValue{"value"}), + canBeSetTo(TTagValue{"value2"})); +} + +TEST_CASE("TagGroup parameters") { + TagGroup tagGroup; + TTag tag{ + TTagClass("class"), + TTagValue("value") + }; + + check_vector_param(tagGroup, canBeSetTo(TTags{tag})); +} + +TEST_CASE("TagList parameters") { + TTag tag{ + TTagClass("class"), + TTagValue("value") + }; + + TagGroup tagGroup; + tagGroup.add(tag); + TagList tagList; + + check_vector_param(tagList, canBeSetTo(TagGroups{tagGroup})); +} + +TEST_CASE("adm xml/taglist") { + auto doc = parseXml("tag_list.accepted.xml"); + auto tag_list = doc->getElement(); + auto tag_groups = tag_list->get(); + REQUIRE(tag_groups.size() == 1); + + auto tag_group = tag_groups.at(0); + auto tags = tag_group.get(); + auto tag = tags.at(0); + CHECK(tag.get() == "class1"); + CHECK(tag.get() == "value1"); + + std::stringstream xml; + writeXml(xml, doc); + CHECK_THAT(xml.str(), EqualsXmlFile("tag_list")); +} diff --git a/tests/test_data/tag_list.accepted.xml b/tests/test_data/tag_list.accepted.xml new file mode 100644 index 00000000..4c349e5d --- /dev/null +++ b/tests/test_data/tag_list.accepted.xml @@ -0,0 +1,32 @@ + + + + + + + + value1 + APR_1001 + ACO_1001 + AO_1001 + + + + ACO_1001 + + + AO_1001 + + + AP_00010001 + ATU_00000001 + + + AT_00010003_01 + AP_00010001 + + + + + + From bddab940e49858b2bf0767673b879018a6370ed1 Mon Sep 17 00:00:00 2001 From: Richard Bailey Date: Fri, 11 Apr 2025 10:54:26 +0100 Subject: [PATCH 4/6] Fix formatting --- include/adm/document.hpp | 1 - include/adm/elements/tag_list.hpp | 22 ++++++++++++---------- include/adm/parse.hpp | 6 ++---- include/adm/private/rapidxml_wrapper.hpp | 11 ++++------- src/elements/tag_list.cpp | 15 +++++++-------- src/parse.cpp | 7 +++---- src/private/document_parser.cpp | 15 +++++++++++---- src/private/rapidxml_formatter.cpp | 9 ++++++--- tests/tag_list_tests.cpp | 20 +++++--------------- 9 files changed, 50 insertions(+), 56 deletions(-) diff --git a/include/adm/document.hpp b/include/adm/document.hpp index f10ff24b..8dffa716 100644 --- a/include/adm/document.hpp +++ b/include/adm/document.hpp @@ -140,7 +140,6 @@ namespace adm { template std::shared_ptr getElement(); - /** @name Lookup ADM elements by its Id * * Lookup the first ADM element with the given Id. diff --git a/include/adm/elements/tag_list.hpp b/include/adm/elements/tag_list.hpp index 8ee4ee84..d3703c52 100644 --- a/include/adm/elements/tag_list.hpp +++ b/include/adm/elements/tag_list.hpp @@ -17,8 +17,10 @@ namespace adm { struct TTagTag {}; namespace detail { - extern template class ADM_EXPORT_TEMPLATE_METHODS RequiredParameter; - extern template class ADM_EXPORT_TEMPLATE_METHODS OptionalParameter; + extern template class ADM_EXPORT_TEMPLATE_METHODS + RequiredParameter; + extern template class ADM_EXPORT_TEMPLATE_METHODS + OptionalParameter; using TTagBase = HasParameters, OptionalParameter>; @@ -36,9 +38,9 @@ namespace adm { ADM_EXPORT explicit TTag(std::string str) : TTag(TTagValue(std::move(str))) {} - ADM_EXPORT explicit TTag(const char* s); + ADM_EXPORT explicit TTag(const char *s); - ADM_EXPORT void print(std::ostream& os) const; + ADM_EXPORT void print(std::ostream &os) const; using detail::TTagBase::set; using detail::TTagBase::unset; @@ -63,9 +65,7 @@ namespace adm { return detail::optionalsEqual(a, b); } - inline bool operator!=(const TTag &a, const TTag &b) { - return !(a==b); - } + inline bool operator!=(const TTag &a, const TTag &b) { return !(a == b); } struct TagGroupTag {}; @@ -76,7 +76,7 @@ namespace adm { } // namespace detail class TagGroup : private detail::TagGroupBase, - private detail::AddWrapperMethods { + private detail::AddWrapperMethods { public: using tag = TagGroupTag; @@ -147,7 +147,8 @@ namespace adm { ADM_EXPORT ElementRange getReferences( detail::ParameterTraits::tag); - ADM_EXPORT void clearReferences(detail::ParameterTraits::tag); + ADM_EXPORT void clearReferences( + detail::ParameterTraits::tag); ADM_EXPORT void clearReferences(detail::ParameterTraits::tag); ADM_EXPORT void clearReferences(detail::ParameterTraits::tag); @@ -220,7 +221,8 @@ namespace adm { ADD_TRAIT(TagGroups, TagGroupsTag); namespace detail { - extern template class ADM_EXPORT_TEMPLATE_METHODS VectorParameter; + extern template class ADM_EXPORT_TEMPLATE_METHODS + VectorParameter; using TagListBase = HasParameters>; } // namespace detail diff --git a/include/adm/parse.hpp b/include/adm/parse.hpp index cbfaa961..11f8c3cc 100644 --- a/include/adm/parse.hpp +++ b/include/adm/parse.hpp @@ -100,10 +100,8 @@ namespace adm { * @param commonDefinitions Common Defintions document */ ADM_EXPORT std::shared_ptr parseFrame( - std::istream& stream, - const FrameHeader& header, - xml::ParserOptions options, - std::shared_ptr commonDefinitions); + std::istream& stream, const FrameHeader& header, + xml::ParserOptions options, std::shared_ptr commonDefinitions); /** * @brief Parse an XML representation of a serial ADM frame and return diff --git a/include/adm/private/rapidxml_wrapper.hpp b/include/adm/private/rapidxml_wrapper.hpp index 8843b648..65a9af99 100644 --- a/include/adm/private/rapidxml_wrapper.hpp +++ b/include/adm/private/rapidxml_wrapper.hpp @@ -102,10 +102,9 @@ namespace adm { void addBaseElements(const Source &src, const std::string &name, Callable formatter); - template + template void addBaseElement(const Source &src, const std::string &name, - Callable formatter); + Callable formatter); template void addReference(const Source &src, const std::string &name); @@ -267,10 +266,9 @@ namespace adm { } } - template + template void XmlNode::addBaseElement(const Source &src, const std::string &name, - Callable formatter) { + Callable formatter) { auto admElement = src->template getElement(); if (admElement) { auto node = addNode(name); @@ -278,7 +276,6 @@ namespace adm { } } - template void XmlNode::addReference(const Source &src, const std::string &name) { addElement(src->template getReference(), name); diff --git a/src/elements/tag_list.cpp b/src/elements/tag_list.cpp index 2f4c5f75..b333eabd 100644 --- a/src/elements/tag_list.cpp +++ b/src/elements/tag_list.cpp @@ -12,7 +12,8 @@ namespace adm { // ---- References ---- // bool TagGroup::addReference(std::shared_ptr programme) { - auto it = std::find(audioProgrammes_.begin(), audioProgrammes_.end(), programme); + auto it = + std::find(audioProgrammes_.begin(), audioProgrammes_.end(), programme); if (it == audioProgrammes_.end()) { audioProgrammes_.push_back(std::move(programme)); return true; @@ -42,7 +43,8 @@ namespace adm { } void TagGroup::removeReference(std::shared_ptr programme) { - auto it = std::find(audioProgrammes_.begin(), audioProgrammes_.end(), programme); + auto it = + std::find(audioProgrammes_.begin(), audioProgrammes_.end(), programme); if (it != audioProgrammes_.end()) { audioProgrammes_.erase(it); } @@ -68,18 +70,15 @@ namespace adm { clearReferences(); } - void TagGroup::clearReferences( - detail::ParameterTraits::tag) { + void TagGroup::clearReferences(detail::ParameterTraits::tag) { audioProgrammes_.clear(); } - void TagGroup::clearReferences( - detail::ParameterTraits::tag) { + void TagGroup::clearReferences(detail::ParameterTraits::tag) { audioContents_.clear(); } - void TagGroup::clearReferences( - detail::ParameterTraits::tag) { + void TagGroup::clearReferences(detail::ParameterTraits::tag) { audioObjects_.clear(); } diff --git a/src/parse.cpp b/src/parse.cpp index b98165b7..36911408 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -37,10 +37,9 @@ namespace adm { return parser.parse(); } - std::shared_ptr parseFrame(std::istream& stream, - const FrameHeader& header, - xml::ParserOptions options, - std::shared_ptr commonDefinitions) { + std::shared_ptr parseFrame( + std::istream& stream, const FrameHeader& header, + xml::ParserOptions options, std::shared_ptr commonDefinitions) { xml::DocumentParser parser(stream, options, commonDefinitions); parser.setHeader(header); return parser.parse(); diff --git a/src/private/document_parser.cpp b/src/private/document_parser.cpp index 75c94bdf..b25cd9f7 100644 --- a/src/private/document_parser.cpp +++ b/src/private/document_parser.cpp @@ -118,7 +118,8 @@ namespace adm { std::string nodeName(node->name(), node->name_size()); if (nodeName == "profileList") { // Can't use the local add function as that contains an ID setting - document_->add(std::make_shared(parseProfileList(node))); + document_->add( + std::make_shared(parseProfileList(node))); } else if (nodeName == "tagList") { // Can't use the local add function as that contains an ID setting document_->add(std::make_shared(parseTagList(node))); @@ -564,9 +565,15 @@ namespace adm { auto tagGroup = std::make_shared(); tagGroup->setTempId(rand() % 1000); addOptionalElements(node, "tag", tagGroup, &parseTTag); - addOptionalReferences(node, "audioProgrammeIDRef", tagGroup, tagGroupProgrammeRefs_, &parseAudioProgrammeId); - addOptionalReferences(node, "audioContentIDRef", tagGroup, tagGroupContentRefs_, &parseAudioContentId); - addOptionalReferences(node, "audioObjectIDRef", tagGroup, tagGroupObjectRefs_, &parseAudioObjectId); + addOptionalReferences(node, "audioProgrammeIDRef", + tagGroup, tagGroupProgrammeRefs_, + &parseAudioProgrammeId); + addOptionalReferences(node, "audioContentIDRef", tagGroup, + tagGroupContentRefs_, + &parseAudioContentId); + addOptionalReferences(node, "audioObjectIDRef", tagGroup, + tagGroupObjectRefs_, + &parseAudioObjectId); resolveReferences(tagGroupProgrammeRefs_); resolveReferences(tagGroupContentRefs_); diff --git a/src/private/rapidxml_formatter.cpp b/src/private/rapidxml_formatter.cpp index 3861132a..0f245890 100644 --- a/src/private/rapidxml_formatter.cpp +++ b/src/private/rapidxml_formatter.cpp @@ -796,9 +796,12 @@ namespace adm { void formatTagGroup(XmlNode &node, const TagGroup &tagGroup) { node.addVectorElements(&tagGroup, "tag", &formatTag); - node.addReferences(&tagGroup, "audioProgrammeIDRef"); - node.addReferences(&tagGroup, "audioContentIDRef"); - node.addReferences(&tagGroup, "audioObjectIDRef"); + node.addReferences( + &tagGroup, "audioProgrammeIDRef"); + node.addReferences(&tagGroup, + "audioContentIDRef"); + node.addReferences(&tagGroup, + "audioObjectIDRef"); } void formatTag(XmlNode &node, const TTag &tag) { diff --git a/tests/tag_list_tests.cpp b/tests/tag_list_tests.cpp index 2812ba98..5dd69daa 100644 --- a/tests/tag_list_tests.cpp +++ b/tests/tag_list_tests.cpp @@ -12,32 +12,22 @@ using namespace adm; using namespace adm_test; TEST_CASE("Tag parameters") { - TTag tag{ - TTagValue("value") - }; + TTag tag{TTagValue("value")}; - check_optional_param(tag, - canBeSetTo(TTagClass{"class2"})); - check_required_param(tag, - hasDefaultOf(TTagValue{"value"}), + check_optional_param(tag, canBeSetTo(TTagClass{"class2"})); + check_required_param(tag, hasDefaultOf(TTagValue{"value"}), canBeSetTo(TTagValue{"value2"})); } TEST_CASE("TagGroup parameters") { TagGroup tagGroup; - TTag tag{ - TTagClass("class"), - TTagValue("value") - }; + TTag tag{TTagClass("class"), TTagValue("value")}; check_vector_param(tagGroup, canBeSetTo(TTags{tag})); } TEST_CASE("TagList parameters") { - TTag tag{ - TTagClass("class"), - TTagValue("value") - }; + TTag tag{TTagClass("class"), TTagValue("value")}; TagGroup tagGroup; tagGroup.add(tag); From 353aa56a5dd889fb7786a8fb20c667050d79ad02 Mon Sep 17 00:00:00 2001 From: Richard Bailey Date: Wed, 16 Apr 2025 11:49:01 +0100 Subject: [PATCH 5/6] Add test for tag group validity after document copy --- tests/tag_list_tests.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/tag_list_tests.cpp b/tests/tag_list_tests.cpp index 5dd69daa..0152ee77 100644 --- a/tests/tag_list_tests.cpp +++ b/tests/tag_list_tests.cpp @@ -5,6 +5,7 @@ #include "adm/write.hpp" #include "helper/file_comparator.hpp" #include "adm/elements/tag_list.hpp" +#include "adm/utilities/object_creation.hpp" #include @@ -28,7 +29,6 @@ TEST_CASE("TagGroup parameters") { TEST_CASE("TagList parameters") { TTag tag{TTagClass("class"), TTagValue("value")}; - TagGroup tagGroup; tagGroup.add(tag); TagList tagList; @@ -52,3 +52,22 @@ TEST_CASE("adm xml/taglist") { writeXml(xml, doc); CHECK_THAT(xml.str(), EqualsXmlFile("tag_list")); } + +TEST_CASE("document copy updates tagList references") { + auto doc = Document::create(); + auto holder = addSimpleObjectTo(doc, "Test"); + TTag tag{TTagClass("class"), TTagValue("value")}; + TagGroup tagGroup; + tagGroup.add(tag); + tagGroup.addReference(holder.audioObject); + auto tagList = std::make_shared(); + tagList->add(tagGroup); + doc->add(tagList); + + auto doc_copy = doc->deepCopy(); + auto copied_object = doc_copy->getElements().front(); + auto copied_tag_group = doc->getElement()->get().front(); + auto tagged_object_ref = + copied_tag_group.getReferences().front(); + REQUIRE(copied_object == tagged_object_ref); +} From 5332c4d0d6d18d96cb15c4520242443d78a4800a Mon Sep 17 00:00:00 2001 From: davemar-bbc Date: Wed, 29 Oct 2025 17:12:12 +0000 Subject: [PATCH 6/6] Added segmenter code for turning an ADM document into S-ADM frames. --- examples/CMakeLists.txt | 3 + examples/adm_segmenter.cpp | 86 +++++ include/adm/segmenter.hpp | 338 ++++++++++++++++++ .../utilities/block_duration_assignment.hpp | 14 + src/CMakeLists.txt | 1 + src/segmenter.cpp | 309 ++++++++++++++++ tests/CMakeLists.txt | 2 + tests/segmenter_tests.cpp | 61 ++++ tests/test_data/segmenter_input.xml | 129 +++++++ .../segmenter_output_0000.accepted.xml | 51 +++ .../segmenter_output_0001.accepted.xml | 51 +++ .../segmenter_output_0002.accepted.xml | 51 +++ .../segmenter_output_0003.accepted.xml | 51 +++ .../segmenter_output_0004.accepted.xml | 51 +++ .../segmenter_output_0005.accepted.xml | 73 ++++ .../segmenter_output_0006.accepted.xml | 73 ++++ .../segmenter_output_0007.accepted.xml | 73 ++++ .../segmenter_output_0008.accepted.xml | 42 +++ .../segmenter_output_0009.accepted.xml | 20 ++ 19 files changed, 1479 insertions(+) create mode 100644 examples/adm_segmenter.cpp create mode 100644 include/adm/segmenter.hpp create mode 100644 src/segmenter.cpp create mode 100644 tests/segmenter_tests.cpp create mode 100644 tests/test_data/segmenter_input.xml create mode 100644 tests/test_data/segmenter_output_0000.accepted.xml create mode 100644 tests/test_data/segmenter_output_0001.accepted.xml create mode 100644 tests/test_data/segmenter_output_0002.accepted.xml create mode 100644 tests/test_data/segmenter_output_0003.accepted.xml create mode 100644 tests/test_data/segmenter_output_0004.accepted.xml create mode 100644 tests/test_data/segmenter_output_0005.accepted.xml create mode 100644 tests/test_data/segmenter_output_0006.accepted.xml create mode 100644 tests/test_data/segmenter_output_0007.accepted.xml create mode 100644 tests/test_data/segmenter_output_0008.accepted.xml create mode 100644 tests/test_data/segmenter_output_0009.accepted.xml diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8791d085..ff8fda1a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -26,3 +26,6 @@ target_link_libraries(create_sadm_from_scratch PRIVATE adm) add_executable(parse_sadm_xml parse_sadm_xml.cpp) target_link_libraries(parse_sadm_xml PRIVATE adm) + +add_executable(adm_segmenter adm_segmenter.cpp) +target_link_libraries(adm_segmenter PRIVATE adm) diff --git a/examples/adm_segmenter.cpp b/examples/adm_segmenter.cpp new file mode 100644 index 00000000..d6b548a4 --- /dev/null +++ b/examples/adm_segmenter.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include "adm/common_definitions.hpp" +#include "adm/parse.hpp" +#include "adm/write.hpp" +#include "adm/segmenter.hpp" +#include "adm/utilities/id_assignment.hpp" +#include "adm/utilities/object_creation.hpp" + +using namespace adm; + +std::string genSadmFilename(std::string pref, uint32_t ind); +TrackUidList buildTrackList(std::shared_ptr document); + +int main(int argc, char const *argv[]) { + std::string fout_pre = "/tmp/segmenter_output"; + int size = 1000; + int max_fr = 10; + + auto document = parseXml("../tests/test_data/segmenter_input.xml"); + + // Get file length + std::chrono::nanoseconds filelength((uint64_t)(max_fr * size) * 1000000L); + for (auto programme : document->getElements()) { + if (programme->has() && programme->has()) { + filelength = programme->get().get().asNanoseconds() - programme->get().get().asNanoseconds(); + break; + } else if (programme->has()) { + filelength = programme->get().get().asNanoseconds(); + break; + } + } + + auto trackUidList = buildTrackList(document); + + Segmenter segmenter(document, Time(filelength)); + SegmentStart segment_start(std::chrono::milliseconds(0)); + SegmentDuration segment_size(std::chrono::milliseconds{size}); + int fr = 0; + while (segment_start < filelength && fr < max_fr) { + segmenter.buildFrame(segment_start, segment_size, fr); + auto transportTrackFormat = segmenter.generateTransportTrackFormat(trackUidList, + segment_start, segment_size); + auto frame = segmenter.getFrame(); + auto frameHeader = segmenter.getFrameHeader(); + + std::stringstream xmlStream; + writeXml(xmlStream, frame, *frameHeader); + + std::string fname = genSadmFilename(fout_pre, fr); + std::ofstream sfile(fname); + + sfile << xmlStream.str(); + + // Get ready for next frame + xmlStream.str(""); + segment_start = SegmentStart(segment_start.get() + segment_size.get()); + fr++; + } + + return 0; +} + + +std::string genSadmFilename(std::string pref, uint32_t ind) { + char c[256]; + sprintf(c, "%s_%04d.xml", pref.c_str(), ind); + std::string s(c); + return s; +} + +TrackUidList buildTrackList(std::shared_ptr document) { + TrackUidList trackUidList; + + auto atus = document->getElements(); + uint16_t track_idx = 1; + for (auto atu : atus) { + TrackUid track_uid; + track_uid.uid = formatId(atu->get()); + track_uid.trackIndex = track_idx; + trackUidList.trackUid.push_back(track_uid); + track_idx++; + } + return trackUidList; +} \ No newline at end of file diff --git a/include/adm/segmenter.hpp b/include/adm/segmenter.hpp new file mode 100644 index 00000000..bec493df --- /dev/null +++ b/include/adm/segmenter.hpp @@ -0,0 +1,338 @@ +#pragma once + +#include "adm/document.hpp" +#include "adm/serial/frame_header.hpp" +#include "adm/route.hpp" +#include "adm/utilities/comparator.hpp" +#include +#include "adm/route.hpp" +#include "adm/route_tracer.hpp" + +#include +#include +#include + +namespace adm { + + namespace detail { + struct SegmenterItem { + SegmenterItem(std::shared_ptr programme, + std::shared_ptr content, + std::vector> objects, + std::vector> packFormats, + std::shared_ptr channelFormat, + std::shared_ptr streamFormat, + std::shared_ptr trackFormat, + std::shared_ptr trackUid, + std::chrono::nanoseconds start, + boost::optional end) + : programme(programme), + content(content), + objects(objects), + packFormats(packFormats), + channelFormat(channelFormat), + streamFormat(streamFormat), + trackFormat(trackFormat), + trackUid(trackUid), + start(start), end(end){}; + std::shared_ptr programme; + std::shared_ptr content; + std::vector> objects; + std::vector> packFormats; + std::shared_ptr channelFormat; + std::shared_ptr streamFormat; + std::shared_ptr trackFormat; + std::shared_ptr trackUid; + std::chrono::nanoseconds start; + boost::optional end; + bool use; + }; + } // namespace detail + + /// @brief Tag for NamedType ::SegmentStart + struct SegmentStartTag {}; + /// @brief NamedType for the audioContentName attribute + using SegmentStart = + detail::NamedType; + /// @brief Tag for NamedType ::SegmentDuration + struct SegmentDurationTag {}; + /// @brief NamedType for the audioContentName attribute + using SegmentDuration = + detail::NamedType; + + struct TrackUid { + uint16_t trackIndex; + std::string uid; + }; + + /// @brief This could be filled from the chna chunk. + struct TrackUidList { + std::vector trackUid; + }; + + + // This routing strategy ensures audioTrackUid is included. + struct CustomStrategy { + template + bool shouldRecurse(std::shared_ptr, std::shared_ptr) { + return true; + } + bool shouldRecurse(std::shared_ptr, + std::shared_ptr) { + return false; + } + + template + bool shouldAdd(std::shared_ptr) { + return true; + } + + template + bool isEndOfRoute(std::shared_ptr) { + return false; + } + + bool isEndOfRoute(std::shared_ptr) { + return true; + } + + bool isEndOfRoute( + const std::shared_ptr &track_uid) { + return track_uid->isSilent(); + } + }; + + /** + * @brief Segment Document into Frames + * + * For now this class can only create full frames. + * + * The segmenter adds all AudioBlockFormats which *could* be relevant to the + * requested segment. It does take into account the duration of the + * AudioObject and AudioProgramme, but ignores the duration of the + * AudioBlockFormat. Consider the following example: + * + * @code + 0s 1s 2s 3s 4s 5s 6s + |-----|-----|-----|-----|-----|-----| + + +-----+-----+-----+-----+ + | AO1 | + +-----+-----+-----+-----+ + | AB1 | AB2 | AB3 | AB4 | + +-----+-----+-----+-----+ + @endcode + * + * For the following segment ranges the behaviour will be as follows: + * + * - [0s; 0.5s]: no AudioBlockFormat + * - [1s; 2s]: AB1, AB2 + * - AB2 to calc the duration of AB1 + * - [2s; 2.5s]: AB1, AB2, AB3 + * - AB1 to calc the start values of an interpolation + * - AB2 to calc the end values of an interpolation + * - AB3 to calc the duration of AB2 + * - [2.25s; 2.75s]: AB1, AB2, AB3 + * - AB1 to calc the start values of an interpolation + * - AB2 to calc the end values of an interpolation + * - AB3 to calc the duration of AB2 + * - [2s; 3s]: AB1, AB2, AB3 + * - AB1 to calc the start values of an interpolation + * - AB2 to calc the end values of an interpolation + * - AB3 to calc the duration of AB2 + * - AB4 is not relevant yet! + * - [4.5s; 5s]: AB3, AB4 + * - AB3 to calc the start values of an interpolation + * - AB4 to calc the end values of an interpolation + */ + class Segmenter { + public: + /** + * @brief Segmenter Ctor + * + * @warning All the audioBlockFormats in the adm::document, which is passed + * to the ctor will be sorted by time! + */ + ADM_EXPORT Segmenter(std::shared_ptr document, boost::optional