diff --git a/Dockerfile b/Dockerfile index 7a395a5030e..104da3bdf79 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ FROM alpine:3.11 as builder RUN apk add --no-cache \ bash curl \ bsd-compat-headers c-ares-dev linux-headers \ - build-base git ninja python2 python3 + build-base git ninja python2 python3 libexecinfo-dev c-ares-dev # Default to python2 because our build system is ancient. RUN ln -sf python2 /usr/bin/python @@ -35,7 +35,7 @@ RUN ninja -C src/out/Release # Copy only result binaries to our final image. FROM alpine:3.11 -RUN apk add --no-cache libstdc++ python +RUN apk add --no-cache libstdc++ python libexecinfo-dev c-ares-dev COPY --from=builder /shaka_packager/src/out/Release/packager \ /shaka_packager/src/out/Release/mpd_generator \ /shaka_packager/src/out/Release/pssh-box.py \ diff --git a/docs/source/options/dash_options.rst b/docs/source/options/dash_options.rst index e47ae3774f3..00cdaeb0e15 100644 --- a/docs/source/options/dash_options.rst +++ b/docs/source/options/dash_options.rst @@ -99,4 +99,10 @@ DASH options --low_latency_dash_mode If enabled, LL-DASH streaming will be used, - reducing overall latency by decoupling latency from segment duration. \ No newline at end of file + reducing overall latency by decoupling latency from segment duration. + +--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. \ No newline at end of file diff --git a/packager/app/stream_descriptor.cc b/packager/app/stream_descriptor.cc index 715e55d30d7..39fe523965f 100644 --- a/packager/app/stream_descriptor.cc +++ b/packager/app/stream_descriptor.cc @@ -36,6 +36,7 @@ enum FieldType { kDashRolesField, kDashOnlyField, kHlsOnlyField, + kDashLabelField, }; struct FieldNameToTypeMapping { @@ -83,6 +84,7 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = { {"role", kDashRolesField}, {"dash_only", kDashOnlyField}, {"hls_only", kHlsOnlyField}, + {"dash_label", kDashLabelField}, }; FieldType GetFieldType(const std::string& field_name) { @@ -247,6 +249,9 @@ base::Optional ParseStreamDescriptor( } descriptor.hls_only = hls_only_value > 0; break; + case kDashLabelField: + descriptor.dash_label = iter->second; + break; default: LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first << "\")."; diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 113a582482a..e97603288cc 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -304,6 +304,7 @@ def _GetStream(self, 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 @@ def _GetStream(self, 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 @@ def _GetStream(self, 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', ] @@ -781,6 +786,14 @@ def testDashOnlyAndHlsOnly(self): self._GetFlags(output_dash=True, output_hls=True)) self._CheckTestResults('hls-only-dash-only') + def testDashLabel(self): + streams = [ + self._GetStream('video', dash_label='English'), + 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 00000000000..10077f9af8a 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 00000000000..de83807979b 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 00000000000..37330c3a313 --- /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 4e96a5df0bf..db1da9186c9 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -81,6 +81,9 @@ void MpdNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options, media_info->add_dash_roles(role); } + 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 8b62c4402dc..ae76f56964c 100644 --- a/packager/media/event/mpd_notify_muxer_listener.h +++ b/packager/media/event/mpd_notify_muxer_listener.h @@ -65,6 +65,8 @@ class MpdNotifyMuxerListener : public MuxerListener { void set_roles(const std::vector& roles) { roles_ = roles; } + void set_dash_label(std::string label) { dash_label_ = label; } + private: MpdNotifyMuxerListener(const MpdNotifyMuxerListener&) = delete; MpdNotifyMuxerListener& operator=(const MpdNotifyMuxerListener&) = delete; @@ -77,6 +79,7 @@ class MpdNotifyMuxerListener : public MuxerListener { std::vector accessibilities_; std::vector roles_; + std::string dash_label_; bool is_encrypted_ = false; // Storage for values passed to OnEncryptionInfoReady(). diff --git a/packager/media/event/muxer_listener_factory.cc b/packager/media/event/muxer_listener_factory.cc index 287a936f967..ad6c95e0046 100644 --- a/packager/media/event/muxer_listener_factory.cc +++ b/packager/media/event/muxer_listener_factory.cc @@ -42,6 +42,7 @@ std::unique_ptr CreateMpdListenerInternal( auto listener = base::MakeUnique(notifier); listener->set_accessibilities(stream.dash_accessiblities); listener->set_roles(stream.dash_roles); + 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 fd7e384fdcf..16ddedbff5a 100644 --- a/packager/media/event/muxer_listener_factory.h +++ b/packager/media/event/muxer_listener_factory.h @@ -53,6 +53,7 @@ class MuxerListenerFactory { std::vector dash_accessiblities; std::vector dash_roles; bool dash_only = false; + 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 154a9fe5885..523684d921e 100644 --- a/packager/mpd/base/adaptation_set.cc +++ b/packager/mpd/base/adaptation_set.cc @@ -357,6 +357,9 @@ base::Optional AdaptationSet::GetXml() { } } + if (!label_.empty() && !adaptation_set.AddLabelElement(label_)) + return base::nullopt; + for (const auto& representation_pair : representation_map_) { const auto& representation = representation_pair.second; if (suppress_representation_width) @@ -439,6 +442,9 @@ void AdaptationSet::UpdateFromMediaInfo(const MediaInfo& media_info) { AddPictureAspectRatio(video_info, &picture_aspect_ratio_); } + 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 c288ad857a1..e16e098ac12 100644 --- a/packager/mpd/base/adaptation_set.h +++ b/packager/mpd/base/adaptation_set.h @@ -313,6 +313,9 @@ class AdaptationSet { // and HD videos in different AdaptationSets can share the same trick play // stream. std::vector trick_play_references_; + + // 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 b626f6b32a6..3778ad6c621 100644 --- a/packager/mpd/base/media_info.proto +++ b/packager/mpd/base/media_info.proto @@ -212,4 +212,6 @@ message MediaInfo { // with respect to the reference time scale. // Equal to the target segment duration times the reference time scale. optional uint64 segment_duration = 25; + + optional string dash_label = 26; } diff --git a/packager/mpd/base/mpd_utils.cc b/packager/mpd/base/mpd_utils.cc index 018551a80fd..41b1ffc017d 100644 --- a/packager/mpd/base/mpd_utils.cc +++ b/packager/mpd/base/mpd_utils.cc @@ -158,6 +158,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(":"); @@ -327,7 +330,7 @@ const char kMarlinUUID[] = "5e629af5-38da-4063-8977-97ffbd9902d4"; const char kFairPlayUUID[] = "29701fe4-3cc7-4a34-8c5b-ae90c7439a47"; // String representation of media::kPlayReadySystemId. const char kPlayReadyUUID[] = "9a04f079-9840-4286-ab92-e65be0885f95"; -// It is RECOMMENDED to include the @value attribute with name and version "MSPR 2.0". +// It is RECOMMENDED to include the @value attribute with name and version "MSPR 2.0". // See https://docs.microsoft.com/en-us/playready/specifications/mpeg-dash-playready#221-general. const char kContentProtectionValueMSPR20[] = "MSPR 2.0"; diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index a5630fca081..4b08bf84ef4 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -331,6 +331,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 6d8d4b29504..3c72d4e2956 100644 --- a/packager/mpd/base/xml/xml_node.h +++ b/packager/mpd/base/xml/xml_node.h @@ -174,6 +174,9 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode { bool AddRoleElement(const std::string& scheme_id_uri, const std::string& value) WARN_UNUSED_RESULT; + /// @param value is element's content. + bool AddLabelElement(const std::string& value) WARN_UNUSED_RESULT; + private: DISALLOW_COPY_AND_ASSIGN(AdaptationSetXmlNode); }; diff --git a/packager/packager.cc b/packager/packager.cc index 25a3d14f078..2aa5aca1ee7 100644 --- a/packager/packager.cc +++ b/packager/packager.cc @@ -84,6 +84,7 @@ MuxerListenerFactory::StreamData ToMuxerListenerData( data.dash_accessiblities = stream.dash_accessiblities; data.dash_roles = stream.dash_roles; data.dash_only = stream.dash_only; + data.dash_label = stream.dash_label; return data; }; diff --git a/packager/packager.h b/packager/packager.h index 38494853ef7..af7b8395823 100644 --- a/packager/packager.h +++ b/packager/packager.h @@ -140,6 +140,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 {