diff --git a/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.m3u8 b/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.m3u8 index 070810b3cc..bd66ab30ce 100644 --- a/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.m3u8 +++ b/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.m3u8 @@ -2,3 +2,6 @@ ## Generated with https://github.com/google/shaka-packager version -- #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 diff --git a/packager/app/test/testdata/flac-with-encryption/output.m3u8 b/packager/app/test/testdata/flac-with-encryption/output.m3u8 index 553420832b..08fc9157eb 100644 --- a/packager/app/test/testdata/flac-with-encryption/output.m3u8 +++ b/packager/app/test/testdata/flac-with-encryption/output.m3u8 @@ -2,3 +2,6 @@ ## Generated with https://github.com/google/shaka-packager version -- #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 diff --git a/packager/hls/base/master_playlist.cc b/packager/hls/base/master_playlist.cc index a5f479cad1..b885066617 100644 --- a/packager/hls/base/master_playlist.cc +++ b/packager/hls/base/master_playlist.cc @@ -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 diff --git a/packager/hls/base/master_playlist_unittest.cc b/packager/hls/base/master_playlist_unittest.cc index 2d3e043e12..4e46d6d755 100644 --- a/packager/hls/base/master_playlist_unittest.cc +++ b/packager/hls/base/master_playlist_unittest.cc @@ -98,7 +98,8 @@ std::unique_ptr 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 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 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