[DASH] Support custom Accessibility and Role elements
Add dash_accessibilities stream descriptor, which is a semi-colon separated list of accessibility_scheme_id_uri=value. It is optional. Add dash_roles stream descriptor, which is a semi-colon separated list of strings. It is optional. Closes #565. Change-Id: Idb1c20bb410fdd016db07e11fe507c102a3dd8ea
This commit is contained in:
parent
0a2b43939c
commit
796974d2a1
|
@ -0,0 +1,16 @@
|
||||||
|
DASH specific stream descriptor fields
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
:dash_accessibilities (accessibilities):
|
||||||
|
|
||||||
|
Optional semicolon separated list of values for DASH Accessibility element.
|
||||||
|
The value should be in the format: scheme_id_uri=value, which propagates
|
||||||
|
to the Accessibility element in the result DASH manifest. See DASH
|
||||||
|
(ISO/IEC 23009-1) specification for details.
|
||||||
|
|
||||||
|
:dash_roles (roles):
|
||||||
|
|
||||||
|
Optional semicolon separated list of values for DASH Role element. The
|
||||||
|
value should be one of: **caption**, **subtitle**, **main**, **alternate**,
|
||||||
|
**supplementary**, **commentary** and **dub**. See DASH (ISO/IEC 23009-1)
|
||||||
|
specification for details.
|
|
@ -82,5 +82,6 @@ which describes the streams.
|
||||||
Configuration options
|
Configuration options
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
.. include:: /options/dash_stream_descriptors.rst
|
||||||
.. include:: /options/dash_options.rst
|
.. include:: /options/dash_options.rst
|
||||||
.. include:: /options/segment_template_formatting.rst
|
.. include:: /options/segment_template_formatting.rst
|
||||||
|
|
|
@ -104,7 +104,14 @@ const char kUsage[] =
|
||||||
" unspecified, no I-Frames only playlist is created.\n"
|
" unspecified, no I-Frames only playlist is created.\n"
|
||||||
" - hls_characteristics (charcs): Optional colon/semicolon separated\n"
|
" - hls_characteristics (charcs): Optional colon/semicolon separated\n"
|
||||||
" list of values for the CHARACTERISTICS attribute for EXT-X-MEDIA.\n"
|
" list of values for the CHARACTERISTICS attribute for EXT-X-MEDIA.\n"
|
||||||
" See CHARACTERISTICS attribute in http://bit.ly/2OOUkdB for details.\n";
|
" See CHARACTERISTICS attribute in http://bit.ly/2OOUkdB for details.\n"
|
||||||
|
" - dash_accessibilities (accessibilities): Optional semicolon separated\n"
|
||||||
|
" list of values for DASH Accessibility elements. The value should be\n"
|
||||||
|
" in the format: scheme_id_uri=value.\n"
|
||||||
|
" - dash_roles (roles): Optional semicolon separated list of values for\n"
|
||||||
|
" DASH Role elements. The value should be one of: caption, subtitle,\n"
|
||||||
|
" main, alternate, supplementary, commentary and dub. See DASH\n"
|
||||||
|
" (ISO/IEC 23009-1) specification for details.\n";
|
||||||
|
|
||||||
// Labels for parameters in RawKey key info.
|
// Labels for parameters in RawKey key info.
|
||||||
const char kDrmLabelLabel[] = "label";
|
const char kDrmLabelLabel[] = "label";
|
||||||
|
|
|
@ -31,6 +31,8 @@ enum FieldType {
|
||||||
kSkipEncryptionField,
|
kSkipEncryptionField,
|
||||||
kDrmStreamLabelField,
|
kDrmStreamLabelField,
|
||||||
kHlsCharacteristicsField,
|
kHlsCharacteristicsField,
|
||||||
|
kDashAccessiblitiesField,
|
||||||
|
kDashRolesField,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FieldNameToTypeMapping {
|
struct FieldNameToTypeMapping {
|
||||||
|
@ -67,6 +69,14 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
|
||||||
{"hls_characteristics", kHlsCharacteristicsField},
|
{"hls_characteristics", kHlsCharacteristicsField},
|
||||||
{"characteristics", kHlsCharacteristicsField},
|
{"characteristics", kHlsCharacteristicsField},
|
||||||
{"charcs", kHlsCharacteristicsField},
|
{"charcs", kHlsCharacteristicsField},
|
||||||
|
{"dash_accessibilities", kDashAccessiblitiesField},
|
||||||
|
{"dash_accessibility", kDashAccessiblitiesField},
|
||||||
|
{"accessibilities", kDashAccessiblitiesField},
|
||||||
|
{"accessibility", kDashAccessiblitiesField},
|
||||||
|
{"dash_roles", kDashRolesField},
|
||||||
|
{"dash_role", kDashRolesField},
|
||||||
|
{"roles", kDashRolesField},
|
||||||
|
{"role", kDashRolesField},
|
||||||
};
|
};
|
||||||
|
|
||||||
FieldType GetFieldType(const std::string& field_name) {
|
FieldType GetFieldType(const std::string& field_name) {
|
||||||
|
@ -176,6 +186,26 @@ base::Optional<StreamDescriptor> ParseStreamDescriptor(
|
||||||
base::SplitString(iter->second, ";:", base::TRIM_WHITESPACE,
|
base::SplitString(iter->second, ";:", base::TRIM_WHITESPACE,
|
||||||
base::SPLIT_WANT_NONEMPTY);
|
base::SPLIT_WANT_NONEMPTY);
|
||||||
break;
|
break;
|
||||||
|
case kDashAccessiblitiesField:
|
||||||
|
descriptor.dash_accessiblities =
|
||||||
|
base::SplitString(iter->second, ";", base::TRIM_WHITESPACE,
|
||||||
|
base::SPLIT_WANT_NONEMPTY);
|
||||||
|
for (const std::string& accessibility :
|
||||||
|
descriptor.dash_accessiblities) {
|
||||||
|
size_t pos = accessibility.find('=');
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
LOG(ERROR)
|
||||||
|
<< "Accessibility should be in scheme=value format, but seeing "
|
||||||
|
<< accessibility;
|
||||||
|
return base::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kDashRolesField:
|
||||||
|
descriptor.dash_roles =
|
||||||
|
base::SplitString(iter->second, ";", base::TRIM_WHITESPACE,
|
||||||
|
base::SPLIT_WANT_NONEMPTY);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
|
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
|
||||||
<< "\").";
|
<< "\").";
|
||||||
|
|
|
@ -275,6 +275,8 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
using_time_specifier=False,
|
using_time_specifier=False,
|
||||||
hls=False,
|
hls=False,
|
||||||
hls_characteristics=None,
|
hls_characteristics=None,
|
||||||
|
dash_accessibilities=None,
|
||||||
|
dash_roles=None,
|
||||||
trick_play_factor=None,
|
trick_play_factor=None,
|
||||||
drm_label=None,
|
drm_label=None,
|
||||||
skip_encryption=None,
|
skip_encryption=None,
|
||||||
|
@ -301,6 +303,8 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
$Number$. This flag is only relevant if segmented is True.
|
$Number$. This flag is only relevant if segmented is True.
|
||||||
hls: Should the output be for an HLS manifest.
|
hls: Should the output be for an HLS manifest.
|
||||||
hls_characteristics: CHARACTERISTICS attribute for the HLS stream.
|
hls_characteristics: CHARACTERISTICS attribute for the HLS stream.
|
||||||
|
dash_accessibilities: Accessibility element for the DASH stream.
|
||||||
|
dash_roles: Role element for the DASH stream.
|
||||||
trick_play_factor: Signals the stream is to be used for a trick play
|
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
|
stream and which key frames to use. A trick play factor of 0 is the
|
||||||
same as not specifying a trick play factor.
|
same as not specifying a trick play factor.
|
||||||
|
@ -356,6 +360,11 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
if hls_characteristics:
|
if hls_characteristics:
|
||||||
stream.Append('hls_characteristics', hls_characteristics)
|
stream.Append('hls_characteristics', hls_characteristics)
|
||||||
|
|
||||||
|
if dash_accessibilities:
|
||||||
|
stream.Append('dash_accessibilities', dash_accessibilities)
|
||||||
|
if dash_roles:
|
||||||
|
stream.Append('dash_roles', dash_roles)
|
||||||
|
|
||||||
requires_init_segment = segmented and base_ext not in [
|
requires_init_segment = segmented and base_ext not in [
|
||||||
'aac', 'ac3', 'ec3', 'ts', 'vtt'
|
'aac', 'ac3', 'ec3', 'ts', 'vtt'
|
||||||
]
|
]
|
||||||
|
@ -638,6 +647,18 @@ class PackagerFunctionalTest(PackagerAppTest):
|
||||||
self._GetStreams(['audio', 'video']), self._GetFlags(output_dash=True))
|
self._GetStreams(['audio', 'video']), self._GetFlags(output_dash=True))
|
||||||
self._CheckTestResults('audio-video')
|
self._CheckTestResults('audio-video')
|
||||||
|
|
||||||
|
def testAudioVideoWithAccessibilitiesAndRoles(self):
|
||||||
|
streams = [
|
||||||
|
self._GetStream(
|
||||||
|
'audio',
|
||||||
|
dash_accessibilities='urn:tva:metadata:cs:AudioPurposeCS:2007=1',
|
||||||
|
dash_roles='alternate'),
|
||||||
|
self._GetStream('video'),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertPackageSuccess(streams, self._GetFlags(output_dash=True))
|
||||||
|
self._CheckTestResults('audio-video-with-accessibilities-and-roles')
|
||||||
|
|
||||||
def testAudioVideoWithTrickPlay(self):
|
def testAudioVideoWithTrickPlay(self):
|
||||||
streams = [
|
streams = [
|
||||||
self._GetStream('audio'),
|
self._GetStream('audio'),
|
||||||
|
|
BIN
packager/app/test/testdata/audio-video-with-accessibilities-and-roles/bear-640x360-audio.mp4
vendored
Normal file
BIN
packager/app/test/testdata/audio-video-with-accessibilities-and-roles/bear-640x360-audio.mp4
vendored
Normal file
Binary file not shown.
BIN
packager/app/test/testdata/audio-video-with-accessibilities-and-roles/bear-640x360-video.mp4
vendored
Normal file
BIN
packager/app/test/testdata/audio-video-with-accessibilities-and-roles/bear-640x360-video.mp4
vendored
Normal file
Binary file not shown.
25
packager/app/test/testdata/audio-video-with-accessibilities-and-roles/output.mpd
vendored
Normal file
25
packager/app/test/testdata/audio-video-with-accessibilities-and-roles/output.mpd
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generated with https://github.com/google/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.7360665798187256S">
|
||||||
|
<Period id="0">
|
||||||
|
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
|
||||||
|
<Representation id="0" bandwidth="973483" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||||
|
<BaseURL>bear-640x360-video.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="859-926" timescale="30000">
|
||||||
|
<Initialization range="0-858"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
|
||||||
|
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"/>
|
||||||
|
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="alternate"/>
|
||||||
|
<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="793-860" timescale="44100">
|
||||||
|
<Initialization range="0-792"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
|
@ -71,6 +71,10 @@ void MpdNotifyMuxerListener::OnMediaStart(
|
||||||
LOG(ERROR) << "Failed to generate MediaInfo from input.";
|
LOG(ERROR) << "Failed to generate MediaInfo from input.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
for (const std::string& accessibility : accessibilities_)
|
||||||
|
media_info->add_dash_accessibilities(accessibility);
|
||||||
|
for (const std::string& role : roles_)
|
||||||
|
media_info->add_dash_roles(role);
|
||||||
|
|
||||||
if (is_encrypted_) {
|
if (is_encrypted_) {
|
||||||
internal::SetContentProtectionFields(protection_scheme_, default_key_id_,
|
internal::SetContentProtectionFields(protection_scheme_, default_key_id_,
|
||||||
|
|
|
@ -55,6 +55,12 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
||||||
void OnCueEvent(int64_t timestamp, const std::string& cue_data) override;
|
void OnCueEvent(int64_t timestamp, const std::string& cue_data) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
void set_accessibilities(const std::vector<std::string>& accessiblities) {
|
||||||
|
accessibilities_ = accessiblities;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_roles(const std::vector<std::string>& roles) { roles_ = roles; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MpdNotifyMuxerListener(const MpdNotifyMuxerListener&) = delete;
|
MpdNotifyMuxerListener(const MpdNotifyMuxerListener&) = delete;
|
||||||
MpdNotifyMuxerListener& operator=(const MpdNotifyMuxerListener&) = delete;
|
MpdNotifyMuxerListener& operator=(const MpdNotifyMuxerListener&) = delete;
|
||||||
|
@ -65,6 +71,9 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
||||||
base::Optional<uint32_t> notification_id_;
|
base::Optional<uint32_t> notification_id_;
|
||||||
std::unique_ptr<MediaInfo> media_info_;
|
std::unique_ptr<MediaInfo> media_info_;
|
||||||
|
|
||||||
|
std::vector<std::string> accessibilities_;
|
||||||
|
std::vector<std::string> roles_;
|
||||||
|
|
||||||
bool is_encrypted_ = false;
|
bool is_encrypted_ = false;
|
||||||
// Storage for values passed to OnEncryptionInfoReady().
|
// Storage for values passed to OnEncryptionInfoReady().
|
||||||
FourCC protection_scheme_ = FOURCC_NULL;
|
FourCC protection_scheme_ = FOURCC_NULL;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "packager/media/event/muxer_listener_factory.h"
|
#include "packager/media/event/muxer_listener_factory.h"
|
||||||
|
|
||||||
|
#include "packager/base/memory/ptr_util.h"
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include "packager/base/strings/stringprintf.h"
|
||||||
#include "packager/hls/base/hls_notifier.h"
|
#include "packager/hls/base/hls_notifier.h"
|
||||||
#include "packager/media/event/combined_muxer_listener.h"
|
#include "packager/media/event/combined_muxer_listener.h"
|
||||||
|
@ -30,10 +31,13 @@ std::unique_ptr<MuxerListener> CreateMediaInfoDumpListenerInternal(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<MuxerListener> CreateMpdListenerInternal(
|
std::unique_ptr<MuxerListener> CreateMpdListenerInternal(
|
||||||
|
const MuxerListenerFactory::StreamData& stream,
|
||||||
MpdNotifier* notifier) {
|
MpdNotifier* notifier) {
|
||||||
DCHECK(notifier);
|
DCHECK(notifier);
|
||||||
|
|
||||||
std::unique_ptr<MuxerListener> listener(new MpdNotifyMuxerListener(notifier));
|
auto listener = base::MakeUnique<MpdNotifyMuxerListener>(notifier);
|
||||||
|
listener->set_accessibilities(stream.dash_accessiblities);
|
||||||
|
listener->set_roles(stream.dash_roles);
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +95,8 @@ std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
|
||||||
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
|
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
|
||||||
}
|
}
|
||||||
if (mpd_notifier_) {
|
if (mpd_notifier_) {
|
||||||
combined_listener->AddListener(CreateMpdListenerInternal(mpd_notifier_));
|
combined_listener->AddListener(
|
||||||
|
CreateMpdListenerInternal(stream, mpd_notifier_));
|
||||||
}
|
}
|
||||||
if (hls_notifier_) {
|
if (hls_notifier_) {
|
||||||
for (auto& listener :
|
for (auto& listener :
|
||||||
|
|
|
@ -46,6 +46,11 @@ class MuxerListenerFactory {
|
||||||
std::string hls_playlist_name;
|
std::string hls_playlist_name;
|
||||||
std::string hls_iframe_playlist_name;
|
std::string hls_iframe_playlist_name;
|
||||||
std::vector<std::string> hls_characteristics;
|
std::vector<std::string> hls_characteristics;
|
||||||
|
|
||||||
|
// DASH specific values needed to write DASH mpd. Will only be used if an
|
||||||
|
// MpdNotifier is given to the factory.
|
||||||
|
std::vector<std::string> dash_accessiblities;
|
||||||
|
std::vector<std::string> dash_roles;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a new muxer listener.
|
/// Create a new muxer listener.
|
||||||
|
|
|
@ -55,11 +55,8 @@ std::string RoleToText(AdaptationSet::Role role) {
|
||||||
case AdaptationSet::kRoleDub:
|
case AdaptationSet::kRoleDub:
|
||||||
return "dub";
|
return "dub";
|
||||||
default:
|
default:
|
||||||
break;
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
NOTREACHED();
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the picture aspect ratio string e.g. "16:9", "4:3".
|
// Returns the picture aspect ratio string e.g. "16:9", "4:3".
|
||||||
|
@ -225,6 +222,11 @@ void AdaptationSet::UpdateContentProtectionPssh(const std::string& drm_uuid,
|
||||||
&content_protection_elements_);
|
&content_protection_elements_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdaptationSet::AddAccessibility(const std::string& scheme,
|
||||||
|
const std::string& value) {
|
||||||
|
accessibilities_.push_back(Accessibility{scheme, value});
|
||||||
|
}
|
||||||
|
|
||||||
void AdaptationSet::AddRole(Role role) {
|
void AdaptationSet::AddRole(Role role) {
|
||||||
roles_.insert(role);
|
roles_.insert(role);
|
||||||
}
|
}
|
||||||
|
@ -318,6 +320,11 @@ xml::scoped_xml_ptr<xmlNode> AdaptationSet::GetXml() {
|
||||||
"urn:mpeg:dash:adaptation-set-switching:2016", switching_ids);
|
"urn:mpeg:dash:adaptation-set-switching:2016", switching_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const AdaptationSet::Accessibility& accessibility : accessibilities_) {
|
||||||
|
adaptation_set.AddAccessibilityElement(accessibility.scheme,
|
||||||
|
accessibility.value);
|
||||||
|
}
|
||||||
|
|
||||||
for (AdaptationSet::Role role : roles_)
|
for (AdaptationSet::Role role : roles_)
|
||||||
adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011", RoleToText(role));
|
adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011", RoleToText(role));
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ class AdaptationSet {
|
||||||
// element to the AdaptationSet with schemeIdUri=urn:mpeg:dash:role:2011.
|
// element to the AdaptationSet with schemeIdUri=urn:mpeg:dash:role:2011.
|
||||||
// See ISO/IEC 23009-1:2012 section 5.8.5.5.
|
// See ISO/IEC 23009-1:2012 section 5.8.5.5.
|
||||||
enum Role {
|
enum Role {
|
||||||
|
kRoleUnknown,
|
||||||
kRoleCaption,
|
kRoleCaption,
|
||||||
kRoleSubtitle,
|
kRoleSubtitle,
|
||||||
kRoleMain,
|
kRoleMain,
|
||||||
|
@ -94,6 +95,13 @@ class AdaptationSet {
|
||||||
virtual void UpdateContentProtectionPssh(const std::string& drm_uuid,
|
virtual void UpdateContentProtectionPssh(const std::string& drm_uuid,
|
||||||
const std::string& pssh);
|
const std::string& pssh);
|
||||||
|
|
||||||
|
/// Set the Accessibility element for this AdaptationSet.
|
||||||
|
/// See ISO/IEC 23009-1:2012 section 5.8.4.3.
|
||||||
|
/// @param scheme is the schemeIdUri of the accessibility element.
|
||||||
|
/// @param value is the value of the accessibility element.
|
||||||
|
virtual void AddAccessibility(const std::string& scheme,
|
||||||
|
const std::string& value);
|
||||||
|
|
||||||
/// Set the Role element for this AdaptationSet.
|
/// Set the Role element for this AdaptationSet.
|
||||||
/// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'.
|
/// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'.
|
||||||
/// See ISO/IEC 23009-1:2012 section 5.8.5.5.
|
/// See ISO/IEC 23009-1:2012 section 5.8.5.5.
|
||||||
|
@ -271,6 +279,13 @@ class AdaptationSet {
|
||||||
// in this set.
|
// in this set.
|
||||||
std::set<std::string> picture_aspect_ratio_;
|
std::set<std::string> picture_aspect_ratio_;
|
||||||
|
|
||||||
|
// accessibilities of this AdaptationSet.
|
||||||
|
struct Accessibility {
|
||||||
|
std::string scheme;
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
std::vector<Accessibility> accessibilities_;
|
||||||
|
|
||||||
// The roles of this AdaptationSet.
|
// The roles of this AdaptationSet.
|
||||||
std::set<Role> roles_;
|
std::set<Role> roles_;
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,22 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetId) {
|
||||||
AttributeEqual("id", std::to_string(kAdaptationSetId)));
|
AttributeEqual("id", std::to_string(kAdaptationSetId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify AdaptationSet::AddAccessibilityElement() works.
|
||||||
|
TEST_F(AdaptationSetTest, AddAccessibilityElement) {
|
||||||
|
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||||
|
adaptation_set->AddAccessibility("urn:tva:metadata:cs:AudioPurposeCS:2007",
|
||||||
|
"2");
|
||||||
|
|
||||||
|
// The empty contentType is sort of a side effect of being able to generate an
|
||||||
|
// MPD without adding any Representations.
|
||||||
|
const char kExpectedOutput[] =
|
||||||
|
"<AdaptationSet contentType=\"\">\n"
|
||||||
|
" <Accessibility schemeIdUri=\"urn:tva:metadata:cs:AudioPurposeCS:2007\""
|
||||||
|
" value=\"2\"/>\n"
|
||||||
|
"</AdaptationSet>";
|
||||||
|
EXPECT_THAT(adaptation_set->GetXml().get(), XmlNodeEqual(kExpectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
// Verify AdaptationSet::AddRole() works for "main" role.
|
// Verify AdaptationSet::AddRole() works for "main" role.
|
||||||
TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) {
|
TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) {
|
||||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||||
|
|
|
@ -164,4 +164,12 @@ message MediaInfo {
|
||||||
|
|
||||||
// HLS only. Defines CHARACTERISTICS attribute of the stream.
|
// HLS only. Defines CHARACTERISTICS attribute of the stream.
|
||||||
repeated string hls_characteristics = 20;
|
repeated string hls_characteristics = 20;
|
||||||
|
|
||||||
|
// DASH only. Defines Accessibility elements of the stream. It should be in
|
||||||
|
// the format: scheme_id_uri=value.
|
||||||
|
repeated string dash_accessibilities = 21;
|
||||||
|
// DASH only. Defines Role elements of the stream. The value can be a valid
|
||||||
|
// Role value defined in "urn:mpeg:dash:role:2011" scheme or in the format:
|
||||||
|
// scheme_id_uri=value (to be implemented).
|
||||||
|
repeated string dash_roles = 22;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,18 @@ std::string GetAdaptationSetKey(const MediaInfo& media_info) {
|
||||||
key.append(":trick_play");
|
key.append(":trick_play");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!media_info.dash_accessibilities().empty()) {
|
||||||
|
key.append(":accessibility_");
|
||||||
|
for (const std::string& accessibility : media_info.dash_accessibilities())
|
||||||
|
key.append(accessibility);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!media_info.dash_roles().empty()) {
|
||||||
|
key.append(":roles_");
|
||||||
|
for (const std::string& role : media_info.dash_roles())
|
||||||
|
key.append(role);
|
||||||
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,24 @@ const std::string& GetDefaultTextLanguage(const MpdOptions& mpd_options) {
|
||||||
: mpd_options.mpd_params.default_text_language;
|
: mpd_options.mpd_params.default_text_language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdaptationSet::Role RoleFromString(const std::string& role_str) {
|
||||||
|
if (role_str == "caption")
|
||||||
|
return AdaptationSet::Role::kRoleCaption;
|
||||||
|
if (role_str == "subtitle")
|
||||||
|
return AdaptationSet::Role::kRoleSubtitle;
|
||||||
|
if (role_str == "main")
|
||||||
|
return AdaptationSet::Role::kRoleMain;
|
||||||
|
if (role_str == "alternate")
|
||||||
|
return AdaptationSet::Role::kRoleAlternate;
|
||||||
|
if (role_str == "supplementary")
|
||||||
|
return AdaptationSet::Role::kRoleSupplementary;
|
||||||
|
if (role_str == "commentary")
|
||||||
|
return AdaptationSet::Role::kRoleCommentary;
|
||||||
|
if (role_str == "dub")
|
||||||
|
return AdaptationSet::Role::kRoleDub;
|
||||||
|
return AdaptationSet::Role::kRoleUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Period::Period(uint32_t period_id,
|
Period::Period(uint32_t period_id,
|
||||||
|
@ -159,7 +177,16 @@ bool Period::SetNewAdaptationSetAttributes(
|
||||||
const MediaInfo& media_info,
|
const MediaInfo& media_info,
|
||||||
const std::list<AdaptationSet*>& adaptation_sets,
|
const std::list<AdaptationSet*>& adaptation_sets,
|
||||||
AdaptationSet* new_adaptation_set) {
|
AdaptationSet* new_adaptation_set) {
|
||||||
if (!language.empty()) {
|
if (!media_info.dash_roles().empty()) {
|
||||||
|
for (const std::string& role_str : media_info.dash_roles()) {
|
||||||
|
AdaptationSet::Role role = RoleFromString(role_str);
|
||||||
|
if (role == AdaptationSet::kRoleUnknown) {
|
||||||
|
LOG(ERROR) << "Unrecognized role '" << role_str << "'.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
new_adaptation_set->AddRole(role);
|
||||||
|
}
|
||||||
|
} else if (!language.empty()) {
|
||||||
const bool is_main_role =
|
const bool is_main_role =
|
||||||
language == (media_info.has_audio_info()
|
language == (media_info.has_audio_info()
|
||||||
? GetDefaultAudioLanguage(mpd_options_)
|
? GetDefaultAudioLanguage(mpd_options_)
|
||||||
|
@ -167,6 +194,17 @@ bool Period::SetNewAdaptationSetAttributes(
|
||||||
if (is_main_role)
|
if (is_main_role)
|
||||||
new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
|
new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
|
||||||
}
|
}
|
||||||
|
for (const std::string& accessibility : media_info.dash_accessibilities()) {
|
||||||
|
size_t pos = accessibility.find('=');
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
LOG(ERROR)
|
||||||
|
<< "Accessibility should be in scheme=value format, but seeing "
|
||||||
|
<< accessibility;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
new_adaptation_set->AddAccessibility(accessibility.substr(0, pos),
|
||||||
|
accessibility.substr(pos + 1));
|
||||||
|
}
|
||||||
|
|
||||||
if (media_info.has_video_info()) {
|
if (media_info.has_video_info()) {
|
||||||
// Because 'language' is ignored for videos, |adaptation_sets| must have
|
// Because 'language' is ignored for videos, |adaptation_sets| must have
|
||||||
|
|
|
@ -286,6 +286,16 @@ AdaptationSetXmlNode::AdaptationSetXmlNode()
|
||||||
: RepresentationBaseXmlNode("AdaptationSet") {}
|
: RepresentationBaseXmlNode("AdaptationSet") {}
|
||||||
AdaptationSetXmlNode::~AdaptationSetXmlNode() {}
|
AdaptationSetXmlNode::~AdaptationSetXmlNode() {}
|
||||||
|
|
||||||
|
void AdaptationSetXmlNode::AddAccessibilityElement(
|
||||||
|
const std::string& scheme_id_uri,
|
||||||
|
const std::string& value) {
|
||||||
|
XmlNode accessibility("Accessibility");
|
||||||
|
accessibility.SetStringAttribute("schemeIdUri", scheme_id_uri);
|
||||||
|
if (!value.empty())
|
||||||
|
accessibility.SetStringAttribute("value", value);
|
||||||
|
AddChild(accessibility.PassScopedPtr());
|
||||||
|
}
|
||||||
|
|
||||||
void AdaptationSetXmlNode::AddRoleElement(const std::string& scheme_id_uri,
|
void AdaptationSetXmlNode::AddRoleElement(const std::string& scheme_id_uri,
|
||||||
const std::string& value) {
|
const std::string& value) {
|
||||||
XmlNode role("Role");
|
XmlNode role("Role");
|
||||||
|
|
|
@ -130,6 +130,11 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode {
|
||||||
AdaptationSetXmlNode();
|
AdaptationSetXmlNode();
|
||||||
~AdaptationSetXmlNode() override;
|
~AdaptationSetXmlNode() override;
|
||||||
|
|
||||||
|
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
||||||
|
/// @param value is the content of value attribute.
|
||||||
|
void AddAccessibilityElement(const std::string& scheme_id_uri,
|
||||||
|
const std::string& value);
|
||||||
|
|
||||||
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
||||||
/// @param value is the content of value attribute.
|
/// @param value is the content of value attribute.
|
||||||
void AddRoleElement(const std::string& scheme_id_uri,
|
void AddRoleElement(const std::string& scheme_id_uri,
|
||||||
|
|
|
@ -87,11 +87,15 @@ MuxerListenerFactory::StreamData ToMuxerListenerData(
|
||||||
const StreamDescriptor& stream) {
|
const StreamDescriptor& stream) {
|
||||||
MuxerListenerFactory::StreamData data;
|
MuxerListenerFactory::StreamData data;
|
||||||
data.media_info_output = stream.output;
|
data.media_info_output = stream.output;
|
||||||
|
|
||||||
data.hls_group_id = stream.hls_group_id;
|
data.hls_group_id = stream.hls_group_id;
|
||||||
data.hls_name = stream.hls_name;
|
data.hls_name = stream.hls_name;
|
||||||
data.hls_playlist_name = stream.hls_playlist_name;
|
data.hls_playlist_name = stream.hls_playlist_name;
|
||||||
data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
|
data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
|
||||||
data.hls_characteristics = stream.hls_characteristics;
|
data.hls_characteristics = stream.hls_characteristics;
|
||||||
|
|
||||||
|
data.dash_accessiblities = stream.dash_accessiblities;
|
||||||
|
data.dash_roles = stream.dash_roles;
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,11 @@ struct StreamDescriptor {
|
||||||
/// Optional for HLS output. It defines the CHARACTERISTICS attribute of the
|
/// Optional for HLS output. It defines the CHARACTERISTICS attribute of the
|
||||||
/// stream.
|
/// stream.
|
||||||
std::vector<std::string> hls_characteristics;
|
std::vector<std::string> hls_characteristics;
|
||||||
|
|
||||||
|
/// Optional for DASH output. It defines Accessibility elements of the stream.
|
||||||
|
std::vector<std::string> dash_accessiblities;
|
||||||
|
/// Optional for DASH output. It defines Role elements of the stream.
|
||||||
|
std::vector<std::string> dash_roles;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SHAKA_EXPORT Packager {
|
class SHAKA_EXPORT Packager {
|
||||||
|
|
Loading…
Reference in New Issue