[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:
parent
cd7640a5bd
commit
89611a526b
|
@ -2,3 +2,6 @@
|
||||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
## 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-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
|
||||||
|
|
|
@ -2,3 +2,6 @@
|
||||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
## 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-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
|
||||||
|
|
|
@ -192,6 +192,7 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
|
||||||
|
|
||||||
std::string tag_name;
|
std::string tag_name;
|
||||||
switch (playlist.stream_type()) {
|
switch (playlist.stream_type()) {
|
||||||
|
case MediaPlaylist::MediaPlaylistStreamType::kAudio:
|
||||||
case MediaPlaylist::MediaPlaylistStreamType::kVideo:
|
case MediaPlaylist::MediaPlaylistStreamType::kVideo:
|
||||||
tag_name = "#EXT-X-STREAM-INF";
|
tag_name = "#EXT-X-STREAM-INF";
|
||||||
break;
|
break;
|
||||||
|
@ -219,8 +220,9 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
|
||||||
|
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
CHECK(playlist.GetDisplayResolution(&width, &height));
|
if (playlist.GetDisplayResolution(&width, &height)) {
|
||||||
tag.AddNumberPair("RESOLUTION", width, 'x', height);
|
tag.AddNumberPair("RESOLUTION", width, 'x', height);
|
||||||
|
}
|
||||||
|
|
||||||
if (variant.audio_group_id) {
|
if (variant.audio_group_id) {
|
||||||
tag.AddQuotedString("AUDIO", *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);
|
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
|
} // namespace
|
||||||
|
|
|
@ -98,7 +98,8 @@ std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
|
||||||
EXPECT_CALL(*playlist, AvgBitrate())
|
EXPECT_CALL(*playlist, AvgBitrate())
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(Return(avg_bitrate));
|
.WillRepeatedly(Return(avg_bitrate));
|
||||||
EXPECT_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull())).Times(0);
|
ON_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull()))
|
||||||
|
.WillByDefault(Return(false));
|
||||||
|
|
||||||
return playlist;
|
return playlist;
|
||||||
}
|
}
|
||||||
|
@ -640,5 +641,56 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
||||||
|
|
||||||
ASSERT_EQ(expected, actual);
|
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 hls
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
Loading…
Reference in New Issue