Added DEFAULT and AUTOSELECT for Text

Took the same logic for DEFAULT and AUTOSELECT used by audio and
applied it to text. Combined the build tag logic for audio and
text as they were the same expect for a couple fields.

Bug: #205
Change-Id: I75ecbf4b25cd559b826982d12a5b132e70b83b69
This commit is contained in:
Aaron Vaage 2018-02-02 09:52:31 -08:00
parent e2dae2d960
commit e98a150d62
2 changed files with 102 additions and 82 deletions

View File

@ -137,35 +137,6 @@ std::list<Variant> BuildVariants(
return merged;
}
void BuildAudioTag(const std::string& base_url,
const std::string& group_id,
const MediaPlaylist& audio_playlist,
bool is_default,
bool is_autoselect,
std::string* out) {
DCHECK(out);
Tag tag("#EXT-X-MEDIA", out);
tag.AddString("TYPE", "AUDIO");
tag.AddQuotedString("URI", base_url + audio_playlist.file_name());
tag.AddQuotedString("GROUP-ID", group_id);
const std::string& language = audio_playlist.GetLanguage();
if (!language.empty()) {
tag.AddQuotedString("LANGUAGE", language);
}
tag.AddQuotedString("NAME", audio_playlist.name());
if (is_default) {
tag.AddString("DEFAULT", "YES");
}
if (is_autoselect) {
tag.AddString("AUTOSELECT", "YES");
}
tag.AddQuotedString("CHANNELS",
std::to_string(audio_playlist.GetNumChannels()));
out->append("\n");
}
void BuildVideoTag(const MediaPlaylist& playlist,
uint64_t max_audio_bitrate,
const std::string& audio_codec,
@ -204,23 +175,102 @@ void BuildVideoTag(const MediaPlaylist& playlist,
playlist.file_name().c_str());
}
void BuildSubtitleTag(const MediaPlaylist& playlist,
const std::string& base_url,
// Need to pass in |group_id| as it may have changed to a new default when
// grouped with other playlists.
void BuildMediaTag(const MediaPlaylist& playlist,
const std::string& group_id,
bool is_default,
bool is_autoselect,
const std::string& base_url,
std::string* out) {
// Tag attribures should follow the order as defined in
// https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-3.5
Tag tag("#EXT-X-MEDIA", out);
// We should only be making media tags for audio and text.
switch (playlist.stream_type()) {
case MediaPlaylist::MediaPlaylistStreamType::kAudio:
tag.AddString("TYPE", "AUDIO");
break;
case MediaPlaylist::MediaPlaylistStreamType::kSubtitle:
tag.AddString("TYPE", "SUBTITLES");
break;
default:
NOTREACHED() << "Cannot build media tag for type "
<< static_cast<int>(playlist.stream_type());
break;
}
tag.AddQuotedString("URI", base_url + playlist.file_name());
tag.AddQuotedString("GROUP-ID", group_id);
const std::string& language = playlist.GetLanguage();
if (!language.empty()) {
tag.AddQuotedString("LANGUAGE", language);
}
tag.AddQuotedString("NAME", playlist.name());
if (is_default) {
tag.AddString("DEFAULT", "YES");
}
if (is_autoselect) {
tag.AddString("AUTOSELECT", "YES");
}
const MediaPlaylist::MediaPlaylistStreamType kAudio =
MediaPlaylist::MediaPlaylistStreamType::kAudio;
if (playlist.stream_type() == kAudio) {
std::string channel_string = std::to_string(playlist.GetNumChannels());
tag.AddQuotedString("CHANNELS", channel_string);
}
out->append("\n");
}
void BuildMediaTags(
const std::map<std::string, std::list<const MediaPlaylist*>>& groups,
const std::string& default_language,
const std::string& base_url,
std::string* out) {
for (const auto& group : groups) {
const std::string& group_id = group.first;
const auto& playlists = group.second;
// Tracks the language of the playlist in this group.
// According to HLS spec: https://goo.gl/MiqjNd 4.3.4.1.1. Rendition Groups
// - A Group MUST NOT have more than one member with a DEFAULT attribute of
// YES.
// - Each EXT-X-MEDIA tag with an AUTOSELECT=YES attribute SHOULD have a
// combination of LANGUAGE[RFC5646], ASSOC-LANGUAGE, FORCED, and
// CHARACTERISTICS attributes that is distinct from those of other
// AUTOSELECT=YES members of its Group.
// We tag the first rendition encountered with a particular language with
// 'AUTOSELECT'; it is tagged with 'DEFAULT' too if the language matches
// |default_language_|.
std::set<std::string> languages;
for (const auto& playlist : playlists) {
bool is_default = false;
bool is_autoselect = false;
const std::string language = playlist->GetLanguage();
if (languages.find(language) == languages.end()) {
is_default = !language.empty() && language == default_language;
is_autoselect = true;
languages.insert(language);
}
BuildMediaTag(*playlist, group_id, is_default, is_autoselect, base_url,
out);
}
}
}
} // namespace
MasterPlaylist::MasterPlaylist(const std::string& file_name,
@ -264,47 +314,12 @@ bool MasterPlaylist::WriteMasterPlaylist(const std::string& base_url,
std::string subtitle_output;
// Write out all the audio tags.
for (const auto& group : audio_playlist_groups_) {
const auto& group_id = group.first;
const auto& playlists = group.second;
// Tracks the language of the playlist in this group.
// According to HLS spec: https://goo.gl/MiqjNd 4.3.4.1.1. Rendition Groups
// - A Group MUST NOT have more than one member with a DEFAULT attribute of
// YES.
// - Each EXT-X-MEDIA tag with an AUTOSELECT=YES attribute SHOULD have a
// combination of LANGUAGE[RFC5646], ASSOC-LANGUAGE, FORCED, and
// CHARACTERISTICS attributes that is distinct from those of other
// AUTOSELECT=YES members of its Group.
// We tag the first rendition encountered with a particular language with
// 'AUTOSELECT'; it is tagged with 'DEFAULT' too if the language matches
// |default_language_|.
std::set<std::string> languages;
for (const auto& playlist : playlists) {
bool is_default = false;
bool is_autoselect = false;
const std::string language = playlist->GetLanguage();
if (languages.find(language) == languages.end()) {
is_default = !language.empty() && language == default_language_;
is_autoselect = true;
languages.insert(language);
}
BuildAudioTag(base_url, group_id, *playlist, is_default, is_autoselect,
BuildMediaTags(audio_playlist_groups_, default_language_, base_url,
&audio_output);
}
}
// Write out all the text tags.
for (const auto& group : subtitle_playlist_groups_) {
const auto& group_id = group.first;
const auto& playlists = group.second;
for (const auto& playlist : playlists) {
BuildSubtitleTag(*playlist, base_url, group_id, &subtitle_output);
}
}
BuildMediaTags(subtitle_playlist_groups_, default_language_, base_url,
&subtitle_output);
std::list<Variant> variants =
BuildVariants(audio_playlist_groups_, subtitle_playlist_groups_);

View File

@ -285,8 +285,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistSameAudioGroupSameLanguage) {
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\","
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"1\"\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\","
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\","
"CHANNELS=\"8\"\n"
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\",CHANNELS=\"8\"\n"
"#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec\","
"RESOLUTION=800x600,AUDIO=\"audio\"\n"
"http://anydomain.com/video.m3u8\n";
@ -326,9 +325,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/eng.m3u8\","
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\"\n"
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
"AUTOSELECT=YES\n"
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/fr.m3u8\","
"GROUP-ID=\"textgroup\",LANGUAGE=\"fr\",NAME=\"french\"\n"
"GROUP-ID=\"textgroup\",LANGUAGE=\"fr\",NAME=\"french\",AUTOSELECT=YES\n"
"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec\","
"RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n"
"http://playlists.org/sd.m3u8\n"
@ -366,9 +366,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/eng.m3u8\","
"GROUP-ID=\"en-text-group\",LANGUAGE=\"en\",NAME=\"english\"\n"
"GROUP-ID=\"en-text-group\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
"AUTOSELECT=YES\n"
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/fr.m3u8\","
"GROUP-ID=\"fr-text-group\",LANGUAGE=\"fr\",NAME=\"french\"\n"
"GROUP-ID=\"fr-text-group\",LANGUAGE=\"fr\",NAME=\"french\",AUTOSELECT="
"YES\n"
"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec\","
"RESOLUTION=800x600,SUBTITLES=\"en-text-group\"\n"
"http://playlists.org/sd.m3u8\n"
@ -409,7 +411,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
"GROUP-ID=\"audiogroup\",LANGUAGE=\"en\",NAME=\"english\","
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/eng.m3u8\","
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\"\n"
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
"AUTOSELECT=YES\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\","
"RESOLUTION=800x600,AUDIO=\"audiogroup\",SUBTITLES=\"textgroup\"\n"
"http://playlists.org/sd.m3u8\n";
@ -463,10 +466,12 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVidesAudiosTextsDifferentGroups) {
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/text-1.m3u8\","
"GROUP-ID=\"text-group-1\",LANGUAGE=\"en\",NAME=\"text 1\"\n"
"GROUP-ID=\"text-group-1\",LANGUAGE=\"en\",NAME=\"text "
"1\",DEFAULT=YES,AUTOSELECT=YES\n"
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/text-2.m3u8\","
"GROUP-ID=\"text-group-2\",LANGUAGE=\"en\",NAME=\"text 2\"\n"
"GROUP-ID=\"text-group-2\",LANGUAGE=\"en\",NAME=\"text "
"2\",DEFAULT=YES,AUTOSELECT=YES\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\","
"RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-1\"\n"