[HLS] Support audio only master playlist

Generate an audio only master playlist if there are no videos and
subtitles.

We do not support mixing audio only EXT-X-STREAM-INF with video
EXT-X-STREAM-INF right now.

Fixes #461.

Change-Id: I999b335ad7abbe183ffcb0f5d471948977c2772f
This commit is contained in:
KongQun Yang 2018-11-27 16:10:07 -08:00
parent cd7640a5bd
commit 89611a526b
4 changed files with 80 additions and 3 deletions

View File

@ -2,3 +2,6 @@
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
#EXT-X-STREAM-INF:BANDWIDTH=133850,AVERAGE-BANDWIDTH=126393,CODECS="mp4a.40.2",AUDIO="default-audio-group"
bear-640x360-audio.m3u8

View File

@ -2,3 +2,6 @@
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-MEDIA:TYPE=AUDIO,URI="stream_0.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
#EXT-X-STREAM-INF:BANDWIDTH=672924,AVERAGE-BANDWIDTH=631380,CODECS="flac",AUDIO="default-audio-group"
stream_0.m3u8

View File

@ -192,6 +192,7 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
std::string tag_name;
switch (playlist.stream_type()) {
case MediaPlaylist::MediaPlaylistStreamType::kAudio:
case MediaPlaylist::MediaPlaylistStreamType::kVideo:
tag_name = "#EXT-X-STREAM-INF";
break;
@ -219,8 +220,9 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
uint32_t width;
uint32_t height;
CHECK(playlist.GetDisplayResolution(&width, &height));
tag.AddNumberPair("RESOLUTION", width, 'x', height);
if (playlist.GetDisplayResolution(&width, &height)) {
tag.AddNumberPair("RESOLUTION", width, 'x', height);
}
if (variant.audio_group_id) {
tag.AddQuotedString("AUDIO", *variant.audio_group_id);
@ -403,6 +405,23 @@ void AppendPlaylists(const std::string& default_audio_language,
BuildStreamInfTag(*playlist, Variant(), base_url, content);
}
}
// Generate audio-only master playlist when there are no videos and subtitles.
if (!audio_playlist_groups.empty() && video_playlists.empty() &&
subtitle_playlist_groups.empty()) {
content->append("\n");
for (const auto& playlist_group : audio_playlist_groups) {
Variant variant;
// Populate |audio_group_id|, which will be propagated to "AUDIO" field.
// Leaving other fields, e.g. xxx_audio_bitrate in |Variant|, as
// null/empty/zero intentionally as the information is already available
// in audio |playlist|.
variant.audio_group_id = &playlist_group.first;
for (const auto& playlist : playlist_group.second) {
BuildStreamInfTag(*playlist, variant, base_url, content);
}
}
}
}
} // namespace

View File

@ -98,7 +98,8 @@ std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
EXPECT_CALL(*playlist, AvgBitrate())
.Times(AtLeast(1))
.WillRepeatedly(Return(avg_bitrate));
EXPECT_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull())).Times(0);
ON_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull()))
.WillByDefault(Return(false));
return playlist;
}
@ -640,5 +641,56 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
ASSERT_EQ(expected, actual);
}
TEST_F(MasterPlaylistTest, WriteMasterPlaylistAudioOnly) {
const uint64_t kAudioChannels = 2;
const uint64_t kAudioMaxBitrate = 50000;
const uint64_t kAudioAvgBitrate = 30000;
std::unique_ptr<MockMediaPlaylist> media_playlists[] = {
// AUDIO
CreateAudioPlaylist("audio-1.m3u8", "audio 1", "audio-group-1",
"audiocodec", "en", kAudioChannels, kAudioMaxBitrate,
kAudioAvgBitrate),
CreateAudioPlaylist("audio-2.m3u8", "audio 2", "audio-group-2",
"audiocodec", "fr", kAudioChannels, kAudioMaxBitrate,
kAudioAvgBitrate),
};
// Add all the media playlists to the master playlist.
std::list<MediaPlaylist*> media_playlist_list;
for (const auto& media_playlist : media_playlists) {
media_playlist_list.push_back(media_playlist.get());
}
const char kBaseUrl[] = "http://playlists.org/";
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_,
media_playlist_list));
std::string actual;
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
const std::string expected =
"#EXTM3U\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/audio-1.m3u8\","
"GROUP-ID=\"audio-group-1\",LANGUAGE=\"en\",NAME=\"audio 1\","
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/audio-2.m3u8\","
"GROUP-ID=\"audio-group-2\",LANGUAGE=\"fr\",NAME=\"audio 2\","
"AUTOSELECT=YES,CHANNELS=\"2\"\n"
"\n"
"#EXT-X-STREAM-INF:BANDWIDTH=50000,AVERAGE-BANDWIDTH=30000,"
"CODECS=\"audiocodec\",AUDIO=\"audio-group-1\"\n"
"http://playlists.org/audio-1.m3u8\n"
"#EXT-X-STREAM-INF:BANDWIDTH=50000,AVERAGE-BANDWIDTH=30000,"
"CODECS=\"audiocodec\",AUDIO=\"audio-group-2\"\n"
"http://playlists.org/audio-2.m3u8\n";
ASSERT_EQ(expected, actual);
}
} // namespace hls
} // namespace shaka