diff --git a/docs/source/options/dash_options.rst b/docs/source/options/dash_options.rst index d1d6d6782a..e0424e0dcc 100644 --- a/docs/source/options/dash_options.rst +++ b/docs/source/options/dash_options.rst @@ -104,4 +104,10 @@ DASH options --force_cl_index True forces the muxer to order streams in the order given - on the command-line. False uses the previous unordered behavior. \ No newline at end of file + on the command-line. False uses the previous unordered behavior. + +--dash_label + + Optional. Will add Label tag to adapation set and will be taken into + consideration along with codecs, language, media type (audio, video etc) + and container type to create different adaptation sets. diff --git a/include/packager/packager.h b/include/packager/packager.h index 34625381e8..3c32abe9c7 100644 --- a/include/packager/packager.h +++ b/include/packager/packager.h @@ -151,6 +151,9 @@ struct StreamDescriptor { bool dash_only = false; /// Set to true to indicate that the stream is for hls only. bool hls_only = false; + + /// Optional for DASH output. It defines the Label element in Adaptation Set. + std::string dash_label; }; class SHAKA_EXPORT Packager { diff --git a/packager/app/stream_descriptor.cc b/packager/app/stream_descriptor.cc index c65c7e1256..9326d2face 100644 --- a/packager/app/stream_descriptor.cc +++ b/packager/app/stream_descriptor.cc @@ -39,6 +39,7 @@ enum FieldType { kDashRolesField, kDashOnlyField, kHlsOnlyField, + kDashLabelField, }; struct FieldNameToTypeMapping { @@ -86,6 +87,7 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = { {"role", kDashRolesField}, {"dash_only", kDashOnlyField}, {"hls_only", kHlsOnlyField}, + {"dash_label", kDashLabelField}, }; FieldType GetFieldType(const std::string& field_name) { @@ -250,6 +252,9 @@ std::optional ParseStreamDescriptor( } descriptor.hls_only = hls_only_value > 0; break; + case kDashLabelField: + descriptor.dash_label = pair.second; + break; default: LOG(ERROR) << "Unknown field in stream descriptor (\"" << pair.first << "\")."; diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 8e2b805203..da9f5e24e6 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -304,6 +304,7 @@ class PackagerAppTest(unittest.TestCase): dash_accessibilities=None, dash_roles=None, dash_only=None, + dash_label=None, trick_play_factor=None, drm_label=None, skip_encryption=None, @@ -334,6 +335,7 @@ class PackagerAppTest(unittest.TestCase): dash_accessibilities: Accessibility element for the DASH stream. dash_roles: Role element for the DASH stream. dash_only: If set to true, will indicate that the stream is for DASH only. + dash_label: Label element for the DASH stream. trick_play_factor: Signals the stream is to be used for a trick play stream and which key frames to use. A trick play factor of 0 is the same as not specifying a trick play factor. @@ -400,6 +402,9 @@ class PackagerAppTest(unittest.TestCase): if dash_only: stream.Append('dash_only', 1) + if dash_label: + stream.Append('dash_label', dash_label) + requires_init_segment = segmented and base_ext not in [ 'aac', 'ac3', 'ec3', 'ts', 'vtt', 'ttml', ] @@ -786,6 +791,14 @@ class PackagerFunctionalTest(PackagerAppTest): self._GetFlags(output_dash=True, output_hls=True)) self._CheckTestResults('hls-only-dash-only') + def testDashLabel(self): + streams = [ + self._GetStream('video', dash_label='Main'), + self._GetStream('audio', dash_label='English'), + ] + self.assertPackageSuccess(streams, self._GetFlags(output_dash=True)) + self._CheckTestResults('dash-label') + def testAudioVideoWithLanguageOverride(self): self.assertPackageSuccess( self._GetStreams(['audio', 'video'], language='por', hls=True), diff --git a/packager/app/test/testdata/dash-label/bear-640x360-audio.mp4 b/packager/app/test/testdata/dash-label/bear-640x360-audio.mp4 new file mode 100644 index 0000000000..10077f9af8 Binary files /dev/null and b/packager/app/test/testdata/dash-label/bear-640x360-audio.mp4 differ diff --git a/packager/app/test/testdata/dash-label/bear-640x360-video.mp4 b/packager/app/test/testdata/dash-label/bear-640x360-video.mp4 new file mode 100644 index 0000000000..de83807979 Binary files /dev/null and b/packager/app/test/testdata/dash-label/bear-640x360-video.mp4 differ diff --git a/packager/app/test/testdata/dash-label/output.mpd b/packager/app/test/testdata/dash-label/output.mpd new file mode 100644 index 0000000000..537849bb38 --- /dev/null +++ b/packager/app/test/testdata/dash-label/output.mpd @@ -0,0 +1,25 @@ + + + + + + + + bear-640x360-video.mp4 + + + + + + + + + + bear-640x360-audio.mp4 + + + + + + + diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index ee44adf126..530c843421 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -88,6 +88,9 @@ void MpdNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options, if (index_.has_value()) media_info->set_index(index_.value()); + if (!dash_label_.empty()) + media_info->set_dash_label(dash_label_); + if (is_encrypted_) { internal::SetContentProtectionFields(protection_scheme_, default_key_id_, key_system_info_, media_info.get()); diff --git a/packager/media/event/mpd_notify_muxer_listener.h b/packager/media/event/mpd_notify_muxer_listener.h index a371ca8772..94375fc41b 100644 --- a/packager/media/event/mpd_notify_muxer_listener.h +++ b/packager/media/event/mpd_notify_muxer_listener.h @@ -69,6 +69,8 @@ class MpdNotifyMuxerListener : public MuxerListener { void set_index(std::optional idx) { index_ = idx; } + void set_dash_label(std::string label) { dash_label_ = label; } + private: MpdNotifyMuxerListener(const MpdNotifyMuxerListener&) = delete; MpdNotifyMuxerListener& operator=(const MpdNotifyMuxerListener&) = delete; @@ -81,6 +83,7 @@ class MpdNotifyMuxerListener : public MuxerListener { std::vector accessibilities_; std::vector roles_; + std::string dash_label_; std::optional index_ = 0; diff --git a/packager/media/event/muxer_listener_factory.cc b/packager/media/event/muxer_listener_factory.cc index e91be91993..3578a08b23 100644 --- a/packager/media/event/muxer_listener_factory.cc +++ b/packager/media/event/muxer_listener_factory.cc @@ -45,6 +45,7 @@ std::unique_ptr CreateMpdListenerInternal( listener->set_accessibilities(stream.dash_accessiblities); listener->set_roles(stream.dash_roles); listener->set_index(stream.index); + listener->set_dash_label(stream.dash_label); return listener; } diff --git a/packager/media/event/muxer_listener_factory.h b/packager/media/event/muxer_listener_factory.h index e5a3e7c096..2eec362e32 100644 --- a/packager/media/event/muxer_listener_factory.h +++ b/packager/media/event/muxer_listener_factory.h @@ -55,6 +55,7 @@ class MuxerListenerFactory { std::vector dash_roles; bool dash_only = false; std::optional index; + std::string dash_label; }; /// Create a new muxer listener. diff --git a/packager/mpd/base/adaptation_set.cc b/packager/mpd/base/adaptation_set.cc index 2a16bfe85e..c033710d05 100644 --- a/packager/mpd/base/adaptation_set.cc +++ b/packager/mpd/base/adaptation_set.cc @@ -382,6 +382,9 @@ std::optional AdaptationSet::GetXml() { } } + if (!label_.empty() && !adaptation_set.AddLabelElement(label_)) + return std::nullopt; + for (const auto& representation_pair : representation_map_) { const auto& representation = representation_pair.second; if (suppress_representation_width) @@ -474,6 +477,9 @@ void AdaptationSet::UpdateFromMediaInfo(const MediaInfo& media_info) { } } + if (media_info.has_dash_label()) + label_ = media_info.dash_label(); + if (media_info.has_video_info()) { content_type_ = "video"; } else if (media_info.has_audio_info()) { diff --git a/packager/mpd/base/adaptation_set.h b/packager/mpd/base/adaptation_set.h index 18046cfa4b..3ed4d12f0f 100644 --- a/packager/mpd/base/adaptation_set.h +++ b/packager/mpd/base/adaptation_set.h @@ -330,6 +330,9 @@ class AdaptationSet { // the command-line index for this AdaptationSet std::optional index_; + + // The label of this AdaptationSet. + std::string label_; }; } // namespace shaka diff --git a/packager/mpd/base/media_info.proto b/packager/mpd/base/media_info.proto index f03ecfd7dc..e0377da23d 100644 --- a/packager/mpd/base/media_info.proto +++ b/packager/mpd/base/media_info.proto @@ -215,4 +215,7 @@ message MediaInfo { // stream index for consistent ordering of streams optional uint32 index = 28; + + // DASH only. Label element. + optional string dash_label = 29; } diff --git a/packager/mpd/base/mpd_utils.cc b/packager/mpd/base/mpd_utils.cc index 09ffe4c7ed..856aab40d3 100644 --- a/packager/mpd/base/mpd_utils.cc +++ b/packager/mpd/base/mpd_utils.cc @@ -161,6 +161,9 @@ std::string GetAdaptationSetKey(const MediaInfo& media_info, key.append("unknown:"); } + if (media_info.has_dash_label()) + key.append(media_info.dash_label() + ":"); + key.append(MediaInfo_ContainerType_Name(media_info.container_type())); if (!ignore_codec) { key.append(":"); diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index 7d4d3523d4..0042b54242 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -337,6 +337,12 @@ bool AdaptationSetXmlNode::AddRoleElement(const std::string& scheme_id_uri, return AddDescriptor("Role", scheme_id_uri, value); } +bool AdaptationSetXmlNode::AddLabelElement(const std::string& value) { + XmlNode descriptor("Label"); + descriptor.SetContent(value); + return AddChild(std::move(descriptor)); +} + RepresentationXmlNode::RepresentationXmlNode() : RepresentationBaseXmlNode("Representation") {} RepresentationXmlNode::~RepresentationXmlNode() {} diff --git a/packager/mpd/base/xml/xml_node.h b/packager/mpd/base/xml/xml_node.h index 541132a6a5..766c475583 100644 --- a/packager/mpd/base/xml/xml_node.h +++ b/packager/mpd/base/xml/xml_node.h @@ -171,6 +171,9 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode { [[nodiscard]] bool AddRoleElement(const std::string& scheme_id_uri, const std::string& value); + /// @param value is element's content. + [[nodiscard]] bool AddLabelElement(const std::string& value); + private: DISALLOW_COPY_AND_ASSIGN(AdaptationSetXmlNode); }; diff --git a/packager/packager.cc b/packager/packager.cc index a6b759cf7c..636bada632 100644 --- a/packager/packager.cc +++ b/packager/packager.cc @@ -75,6 +75,7 @@ MuxerListenerFactory::StreamData ToMuxerListenerData( data.dash_roles = stream.dash_roles; data.dash_only = stream.dash_only; data.index = stream.index; + data.dash_label = stream.dash_label; return data; };