Support EXT-X-I-FRAME-STREAM-INF in master playlist
Issue: #287 Change-Id: I07fdfa095fe1797b3aa091d48798a2b5fbb4dfbe
This commit is contained in:
parent
c991490e82
commit
1750357024
|
@ -145,43 +145,59 @@ std::list<Variant> BuildVariants(
|
|||
return merged;
|
||||
}
|
||||
|
||||
void BuildVideoTag(const MediaPlaylist& playlist,
|
||||
uint64_t max_audio_bitrate,
|
||||
const std::string& audio_codec,
|
||||
const std::string* audio_group_id,
|
||||
const std::string* text_group_id,
|
||||
void BuildStreamInfTag(const MediaPlaylist& playlist,
|
||||
const Variant& variant,
|
||||
const std::string& base_url,
|
||||
std::string* out) {
|
||||
DCHECK(out);
|
||||
|
||||
const uint64_t bitrate = playlist.Bitrate() + max_audio_bitrate;
|
||||
const uint64_t bitrate = playlist.Bitrate() + variant.audio_bitrate;
|
||||
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
CHECK(playlist.GetDisplayResolution(&width, &height));
|
||||
|
||||
std::string codecs = playlist.codec();
|
||||
if (!audio_codec.empty()) {
|
||||
base::StringAppendF(&codecs, ",%s", audio_codec.c_str());
|
||||
if (!variant.audio_codec.empty()) {
|
||||
base::StringAppendF(&codecs, ",%s", variant.audio_codec.c_str());
|
||||
}
|
||||
|
||||
Tag tag("#EXT-X-STREAM-INF", out);
|
||||
std::string tag_name;
|
||||
switch (playlist.stream_type()) {
|
||||
case MediaPlaylist::MediaPlaylistStreamType::kVideo:
|
||||
tag_name = "#EXT-X-STREAM-INF";
|
||||
break;
|
||||
case MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly:
|
||||
tag_name = "#EXT-X-I-FRAME-STREAM-INF";
|
||||
break;
|
||||
default:
|
||||
NOTREACHED() << "Cannot build STREAM-INFO tag for type "
|
||||
<< static_cast<int>(playlist.stream_type());
|
||||
break;
|
||||
}
|
||||
Tag tag(tag_name, out);
|
||||
|
||||
tag.AddNumber("BANDWIDTH", bitrate);
|
||||
tag.AddQuotedString("CODECS", codecs);
|
||||
tag.AddNumberPair("RESOLUTION", width, 'x', height);
|
||||
|
||||
if (audio_group_id) {
|
||||
tag.AddQuotedString("AUDIO", *audio_group_id);
|
||||
if (variant.audio_group_id) {
|
||||
tag.AddQuotedString("AUDIO", *variant.audio_group_id);
|
||||
}
|
||||
|
||||
if (text_group_id) {
|
||||
tag.AddQuotedString("SUBTITLES", *text_group_id);
|
||||
if (variant.text_group_id) {
|
||||
tag.AddQuotedString("SUBTITLES", *variant.text_group_id);
|
||||
}
|
||||
|
||||
if (playlist.stream_type() ==
|
||||
MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly) {
|
||||
tag.AddQuotedString("URI", base_url + playlist.file_name());
|
||||
out->append("\n");
|
||||
} else {
|
||||
base::StringAppendF(out, "\n%s%s\n", base_url.c_str(),
|
||||
playlist.file_name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Need to pass in |group_id| as it may have changed to a new default when
|
||||
// grouped with other playlists.
|
||||
|
@ -297,6 +313,9 @@ void AppendPlaylists(const std::string& default_language,
|
|||
case MediaPlaylist::MediaPlaylistStreamType::kVideo:
|
||||
video_playlists.push_back(playlist);
|
||||
break;
|
||||
case MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly:
|
||||
iframe_playlists.push_back(playlist);
|
||||
break;
|
||||
case MediaPlaylist::MediaPlaylistStreamType::kSubtitle:
|
||||
subtitle_playlist_groups[GetGroupId(*playlist)].push_back(playlist);
|
||||
break;
|
||||
|
@ -313,11 +332,14 @@ void AppendPlaylists(const std::string& default_language,
|
|||
BuildVariants(audio_playlist_groups, subtitle_playlist_groups);
|
||||
for (const auto& playlist : video_playlists) {
|
||||
for (const auto& variant : variants) {
|
||||
BuildVideoTag(*playlist, variant.audio_bitrate, variant.audio_codec,
|
||||
variant.audio_group_id, variant.text_group_id, base_url,
|
||||
content);
|
||||
BuildStreamInfTag(*playlist, variant, base_url, content);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& playlist : iframe_playlists) {
|
||||
// I-Frame playlists do not have variant. Just use the default.
|
||||
BuildStreamInfTag(*playlist, Variant(), base_url, content);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -57,6 +57,16 @@ std::unique_ptr<MockMediaPlaylist> CreateVideoPlaylist(
|
|||
return playlist;
|
||||
}
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> CreateIframePlaylist(
|
||||
const std::string& filename,
|
||||
const std::string& codec,
|
||||
uint64_t bitrate) {
|
||||
auto playlist = CreateVideoPlaylist(filename, codec, bitrate);
|
||||
playlist->SetStreamTypeForTesting(
|
||||
MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly);
|
||||
return playlist;
|
||||
}
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
|
@ -65,8 +75,6 @@ std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
|
|||
const std::string& language,
|
||||
uint64_t channels,
|
||||
uint64_t bitrate) {
|
||||
// Note that audiocodecs should match for different audio tracks with same
|
||||
// group ID.
|
||||
std::unique_ptr<MockMediaPlaylist> playlist(
|
||||
new MockMediaPlaylist(kVodPlaylist, filename, name, group));
|
||||
|
||||
|
@ -85,7 +93,8 @@ std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
|
|||
return playlist;
|
||||
}
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> MakeText(const std::string& filename,
|
||||
std::unique_ptr<MockMediaPlaylist> CreateTextPlaylist(
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::string& group,
|
||||
const std::string& language) {
|
||||
|
@ -140,6 +149,29 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
|
|||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneIframePlaylist) {
|
||||
const uint64_t kBitRate = 435889;
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> mock_playlist =
|
||||
CreateIframePlaylist("media1.m3u8", "avc1", kBitRate);
|
||||
|
||||
const char kBaseUrl[] = "http://myplaylistdomain.com/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_,
|
||||
{mock_playlist.get()}));
|
||||
|
||||
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"
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=435889,CODECS=\"avc1\",RESOLUTION="
|
||||
"800x600,URI=\"http://myplaylistdomain.com/media1.m3u8\"\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
||||
const uint64_t kVideo1BitRate = 300000;
|
||||
const uint64_t kVideo2BitRate = 700000;
|
||||
|
@ -295,11 +327,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
|
|||
|
||||
// Text, eng.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text_eng =
|
||||
MakeText("eng.m3u8", "english", "textgroup", "en");
|
||||
CreateTextPlaylist("eng.m3u8", "english", "textgroup", "en");
|
||||
|
||||
// Text, fr.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text_fr =
|
||||
MakeText("fr.m3u8", "french", "textgroup", "fr");
|
||||
CreateTextPlaylist("fr.m3u8", "french", "textgroup", "fr");
|
||||
|
||||
const char kBaseUrl[] = "http://playlists.org/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
|
||||
|
@ -335,11 +367,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
|
|||
|
||||
// Text, eng.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text_eng =
|
||||
MakeText("eng.m3u8", "english", "en-text-group", "en");
|
||||
CreateTextPlaylist("eng.m3u8", "english", "en-text-group", "en");
|
||||
|
||||
// Text, fr.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text_fr =
|
||||
MakeText("fr.m3u8", "french", "fr-text-group", "fr");
|
||||
CreateTextPlaylist("fr.m3u8", "french", "fr-text-group", "fr");
|
||||
|
||||
const char kBaseUrl[] = "http://playlists.org/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
|
||||
|
@ -380,7 +412,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
|
|||
|
||||
// Text, english.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text =
|
||||
MakeText("eng.m3u8", "english", "textgroup", "en");
|
||||
CreateTextPlaylist("eng.m3u8", "english", "textgroup", "en");
|
||||
|
||||
const char kBaseUrl[] = "http://playlists.org/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
|
||||
|
@ -406,10 +438,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
|
|||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVidesAudiosTextsDifferentGroups) {
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
||||
const uint64_t kAudioChannels = 2;
|
||||
const uint64_t kAudioBitRate = 50000;
|
||||
const uint64_t kVideoBitRate = 300000;
|
||||
const uint64_t kIframeBitRate = 100000;
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> media_playlists[] = {
|
||||
// AUDIO
|
||||
|
@ -419,12 +452,16 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVidesAudiosTextsDifferentGroups) {
|
|||
"audiocodec", "en", kAudioChannels, kAudioBitRate),
|
||||
|
||||
// SUBTITLES
|
||||
MakeText("text-1.m3u8", "text 1", "text-group-1", "en"),
|
||||
MakeText("text-2.m3u8", "text 2", "text-group-2", "en"),
|
||||
CreateTextPlaylist("text-1.m3u8", "text 1", "text-group-1", "en"),
|
||||
CreateTextPlaylist("text-2.m3u8", "text 2", "text-group-2", "en"),
|
||||
|
||||
// VIDEO
|
||||
CreateVideoPlaylist("video-1.m3u8", "sdvideocodec", kVideoBitRate),
|
||||
CreateVideoPlaylist("video-2.m3u8", "sdvideocodec", kVideoBitRate),
|
||||
|
||||
// I-Frame
|
||||
CreateIframePlaylist("iframe-1.m3u8", "sdvideocodec", kIframeBitRate),
|
||||
CreateIframePlaylist("iframe-2.m3u8", "sdvideocodec", kIframeBitRate),
|
||||
};
|
||||
|
||||
// Add all the media playlists to the master playlist.
|
||||
|
@ -491,7 +528,13 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVidesAudiosTextsDifferentGroups) {
|
|||
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\","
|
||||
"RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-2\"\n"
|
||||
"http://playlists.org/video-2.m3u8\n";
|
||||
"http://playlists.org/video-2.m3u8\n"
|
||||
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,CODECS=\"sdvideocodec\","
|
||||
"RESOLUTION=800x600,URI=\"http://playlists.org/iframe-1.m3u8\"\n"
|
||||
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,CODECS=\"sdvideocodec\","
|
||||
"RESOLUTION=800x600,URI=\"http://playlists.org/iframe-2.m3u8\"\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue