[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>
|
||||
|
||||
#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>
|
||||
|
||||
#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;
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue