Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(DASH): Add Label element. #1175

Merged
merged 6 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/source/options/dash_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
reducing overall latency by decoupling latency from segment duration.

--dash_label <label_name>

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.
3 changes: 3 additions & 0 deletions include/packager/packager.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,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 {
Expand Down
5 changes: 5 additions & 0 deletions packager/app/stream_descriptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum FieldType {
kDashRolesField,
kDashOnlyField,
kHlsOnlyField,
kDashLabelField,
};

struct FieldNameToTypeMapping {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -250,6 +252,9 @@ std::optional<StreamDescriptor> 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
<< "\").";
Expand Down
13 changes: 13 additions & 0 deletions packager/app/test/packager_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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',
]
Expand Down Expand Up @@ -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='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),
Expand Down
Binary file not shown.
Binary file not shown.
25 changes: 25 additions & 0 deletions packager/app/test/testdata/dash-label/output.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/shaka-project/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.736067S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
joeyparrish marked this conversation as resolved.
Show resolved Hide resolved
<Label>Main</Label>
<Representation id="0" bandwidth="973483" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-640x360-video.mp4</BaseURL>
<SegmentBase indexRange="870-937" timescale="30000">
<Initialization range="0-869"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Label>English</Label>
<Representation id="1" bandwidth="133334" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>bear-640x360-audio.mp4</BaseURL>
<SegmentBase indexRange="804-871" timescale="44100">
<Initialization range="0-803"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>
3 changes: 3 additions & 0 deletions packager/media/event/mpd_notify_muxer_listener.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,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());
Expand Down
3 changes: 3 additions & 0 deletions packager/media/event/mpd_notify_muxer_listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class MpdNotifyMuxerListener : public MuxerListener {

void set_roles(const std::vector<std::string>& roles) { roles_ = roles; }

void set_dash_label(std::string label) { dash_label_ = label; }

private:
MpdNotifyMuxerListener(const MpdNotifyMuxerListener&) = delete;
MpdNotifyMuxerListener& operator=(const MpdNotifyMuxerListener&) = delete;
Expand All @@ -79,6 +81,7 @@ class MpdNotifyMuxerListener : public MuxerListener {

std::vector<std::string> accessibilities_;
std::vector<std::string> roles_;
std::string dash_label_;

bool is_encrypted_ = false;
// Storage for values passed to OnEncryptionInfoReady().
Expand Down
1 change: 1 addition & 0 deletions packager/media/event/muxer_listener_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ std::unique_ptr<MuxerListener> CreateMpdListenerInternal(
auto listener = std::make_unique<MpdNotifyMuxerListener>(notifier);
listener->set_accessibilities(stream.dash_accessiblities);
listener->set_roles(stream.dash_roles);
listener->set_dash_label(stream.dash_label);
return listener;
}

Expand Down
1 change: 1 addition & 0 deletions packager/media/event/muxer_listener_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class MuxerListenerFactory {
std::vector<std::string> dash_accessiblities;
std::vector<std::string> dash_roles;
bool dash_only = false;
std::string dash_label;
};

/// Create a new muxer listener.
Expand Down
6 changes: 6 additions & 0 deletions packager/mpd/base/adaptation_set.cc
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,9 @@ std::optional<xml::XmlNode> 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)
Expand Down Expand Up @@ -454,6 +457,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()) {
Expand Down
3 changes: 3 additions & 0 deletions packager/mpd/base/adaptation_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ class AdaptationSet {

// Transfer characteristics.
uint32_t transfer_characteristics_ = 0;

// The label of this AdaptationSet.
std::string label_;
};

} // namespace shaka
Expand Down
3 changes: 3 additions & 0 deletions packager/mpd/base/media_info.proto
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,7 @@ 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;

// DASH only. Label element.
optional string dash_label = 26;
}
3 changes: 3 additions & 0 deletions packager/mpd/base/mpd_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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(":");
Expand Down
6 changes: 6 additions & 0 deletions packager/mpd/base/xml/xml_node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand Down
3 changes: 3 additions & 0 deletions packager/mpd/base/xml/xml_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
Expand Down
1 change: 1 addition & 0 deletions packager/packager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,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;
};

Expand Down