From e98a150d6237ae755f05d6e24c08fe15ab8ce928 Mon Sep 17 00:00:00 2001 From: Aaron Vaage Date: Fri, 2 Feb 2018 09:52:31 -0800 Subject: [PATCH] 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 --- packager/hls/base/master_playlist.cc | 161 ++++++++++-------- packager/hls/base/master_playlist_unittest.cc | 23 ++- 2 files changed, 102 insertions(+), 82 deletions(-) diff --git a/packager/hls/base/master_playlist.cc b/packager/hls/base/master_playlist.cc index df3c4f154c..419620fe53 100644 --- a/packager/hls/base/master_playlist.cc +++ b/packager/hls/base/master_playlist.cc @@ -137,35 +137,6 @@ std::list 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, - const std::string& group_id, - std::string* out) { +// 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); - tag.AddString("TYPE", "SUBTITLES"); + // 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(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>& 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 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 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, - &audio_output); - } - } + 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 variants = BuildVariants(audio_playlist_groups_, subtitle_playlist_groups_); diff --git a/packager/hls/base/master_playlist_unittest.cc b/packager/hls/base/master_playlist_unittest.cc index 499be5d53c..7b8bb41158 100644 --- a/packager/hls/base/master_playlist_unittest.cc +++ b/packager/hls/base/master_playlist_unittest.cc @@ -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"