2016-03-25 08:39:07 +00:00
|
|
|
// Copyright 2016 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
|
|
|
|
#include <gmock/gmock.h>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2017-06-15 20:00:28 +00:00
|
|
|
#include "packager/base/files/file_path.h"
|
2017-07-10 18:26:22 +00:00
|
|
|
#include "packager/file/file.h"
|
2016-03-25 08:39:07 +00:00
|
|
|
#include "packager/hls/base/master_playlist.h"
|
|
|
|
#include "packager/hls/base/media_playlist.h"
|
|
|
|
#include "packager/hls/base/mock_media_playlist.h"
|
2016-07-07 19:34:07 +00:00
|
|
|
#include "packager/version/version.h"
|
2016-03-25 08:39:07 +00:00
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2016-03-25 08:39:07 +00:00
|
|
|
namespace hls {
|
|
|
|
|
2018-01-26 23:53:58 +00:00
|
|
|
using base::FilePath;
|
|
|
|
using ::testing::_;
|
2016-03-25 08:39:07 +00:00
|
|
|
using ::testing::AtLeast;
|
2017-06-14 19:25:17 +00:00
|
|
|
using ::testing::NotNull;
|
2016-03-25 08:39:07 +00:00
|
|
|
using ::testing::Return;
|
|
|
|
using ::testing::ReturnRef;
|
2017-06-14 19:25:17 +00:00
|
|
|
using ::testing::SetArgPointee;
|
2017-06-03 00:05:47 +00:00
|
|
|
using ::testing::StrEq;
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
const char kDefaultMasterPlaylistName[] = "playlist.m3u8";
|
2018-01-17 23:43:41 +00:00
|
|
|
const char kDefaultLanguage[] = "en";
|
2017-06-14 19:25:17 +00:00
|
|
|
const uint32_t kWidth = 800;
|
|
|
|
const uint32_t kHeight = 600;
|
2017-06-30 20:42:46 +00:00
|
|
|
const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod;
|
2018-01-26 23:53:58 +00:00
|
|
|
|
|
|
|
std::unique_ptr<MockMediaPlaylist> CreateVideoPlaylist(
|
|
|
|
const std::string& filename,
|
|
|
|
const std::string& codec,
|
|
|
|
uint64_t bitrate) {
|
|
|
|
const char kNoName[] = "";
|
|
|
|
const char kNoGroup[] = "";
|
|
|
|
|
|
|
|
std::unique_ptr<MockMediaPlaylist> playlist(
|
|
|
|
new MockMediaPlaylist(kVodPlaylist, filename, kNoName, kNoGroup));
|
|
|
|
|
|
|
|
playlist->SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kVideo);
|
|
|
|
playlist->SetCodecForTesting(codec);
|
|
|
|
|
|
|
|
EXPECT_CALL(*playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(bitrate));
|
|
|
|
EXPECT_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull()))
|
|
|
|
.WillRepeatedly(DoAll(SetArgPointee<0>(kWidth), SetArgPointee<1>(kHeight),
|
|
|
|
Return(true)));
|
|
|
|
|
|
|
|
return playlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
|
|
|
|
const std::string& filename,
|
|
|
|
const std::string& name,
|
|
|
|
const std::string& group,
|
|
|
|
const std::string& codec,
|
|
|
|
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));
|
|
|
|
|
|
|
|
EXPECT_CALL(*playlist, GetLanguage()).WillRepeatedly(Return(language));
|
|
|
|
EXPECT_CALL(*playlist, GetNumChannels()).WillRepeatedly(Return(channels));
|
|
|
|
|
|
|
|
playlist->SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kAudio);
|
|
|
|
playlist->SetCodecForTesting(codec);
|
|
|
|
|
|
|
|
EXPECT_CALL(*playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(bitrate));
|
|
|
|
EXPECT_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull())).Times(0);
|
|
|
|
|
|
|
|
return playlist;
|
|
|
|
}
|
2016-03-25 08:39:07 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
class MasterPlaylistTest : public ::testing::Test {
|
|
|
|
protected:
|
2017-06-15 20:00:28 +00:00
|
|
|
MasterPlaylistTest()
|
2018-01-17 23:43:41 +00:00
|
|
|
: master_playlist_(kDefaultMasterPlaylistName, kDefaultLanguage),
|
2017-06-15 20:00:28 +00:00
|
|
|
test_output_dir_("memory://test_dir"),
|
|
|
|
master_playlist_path_(
|
|
|
|
FilePath::FromUTF8Unsafe(test_output_dir_)
|
|
|
|
.Append(FilePath::FromUTF8Unsafe(kDefaultMasterPlaylistName))
|
|
|
|
.AsUTF8Unsafe()) {}
|
2016-03-25 08:39:07 +00:00
|
|
|
|
2018-01-26 23:53:58 +00:00
|
|
|
void SetUp() override { SetPackagerVersionForTesting("test"); }
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
MasterPlaylist master_playlist_;
|
|
|
|
std::string test_output_dir_;
|
2017-06-15 20:00:28 +00:00
|
|
|
std::string master_playlist_path_;
|
2016-03-25 08:39:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(MasterPlaylistTest, AddMediaPlaylist) {
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist mock_playlist(kVodPlaylist, "playlist1.m3u8", "somename",
|
|
|
|
"somegroupid");
|
2016-03-25 08:39:07 +00:00
|
|
|
master_playlist_.AddMediaPlaylist(&mock_playlist);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
|
2018-01-26 23:53:58 +00:00
|
|
|
const uint64_t kBitRate = 435889;
|
|
|
|
|
|
|
|
std::unique_ptr<MockMediaPlaylist> mock_playlist =
|
|
|
|
CreateVideoPlaylist("media1.m3u8", "avc1", kBitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(mock_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://myplaylistdomain.com/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
|
|
|
|
|
|
|
|
std::string actual;
|
2017-07-10 18:26:22 +00:00
|
|
|
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
const std::string expected =
|
|
|
|
"#EXTM3U\n"
|
2016-07-07 19:34:07 +00:00
|
|
|
"## Generated with https://github.com/google/shaka-packager version "
|
|
|
|
"test\n"
|
2017-06-14 19:25:17 +00:00
|
|
|
"#EXT-X-STREAM-INF:BANDWIDTH=435889,CODECS=\"avc1\",RESOLUTION=800x600\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"http://myplaylistdomain.com/media1.m3u8\n";
|
|
|
|
|
|
|
|
ASSERT_EQ(expected, actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
2018-01-26 23:53:58 +00:00
|
|
|
const uint64_t kVideo1BitRate = 300000;
|
|
|
|
const uint64_t kVideo2BitRate = 700000;
|
|
|
|
|
|
|
|
const uint64_t kAudio1BitRate = 50000;
|
|
|
|
const uint64_t kAudio2BitRate = 60000;
|
|
|
|
|
|
|
|
const uint64_t kAudio1Channels = 2;
|
|
|
|
const uint64_t kAudio2Channels = 5;
|
|
|
|
|
2016-03-25 08:39:07 +00:00
|
|
|
// First video, sd.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> sd_video_playlist =
|
|
|
|
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", kVideo1BitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(sd_video_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
// Second video, hd.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> hd_video_playlist =
|
|
|
|
CreateVideoPlaylist("hd.m3u8", "hdvideocodec", kVideo2BitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(hd_video_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
// First audio, english.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> english_playlist =
|
|
|
|
CreateAudioPlaylist("eng.m3u8", "english", "audiogroup", "audiocodec",
|
|
|
|
"en", kAudio1Channels, kAudio1BitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(english_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
// Second audio, spanish.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> spanish_playlist =
|
|
|
|
CreateAudioPlaylist("spa.m3u8", "espanol", "audiogroup", "audiocodec",
|
|
|
|
"es", kAudio2Channels, kAudio2BitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(spanish_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://playlists.org/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
|
|
|
|
|
|
|
|
std::string actual;
|
2017-07-10 18:26:22 +00:00
|
|
|
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
const std::string expected =
|
|
|
|
"#EXTM3U\n"
|
2016-07-07 19:34:07 +00:00
|
|
|
"## Generated with https://github.com/google/shaka-packager version "
|
|
|
|
"test\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/eng.m3u8\","
|
|
|
|
"GROUP-ID=\"audiogroup\",LANGUAGE=\"en\",NAME=\"english\","
|
2018-01-17 23:43:41 +00:00
|
|
|
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/spa.m3u8\","
|
|
|
|
"GROUP-ID=\"audiogroup\",LANGUAGE=\"es\",NAME=\"espanol\","
|
2018-01-17 23:43:41 +00:00
|
|
|
"AUTOSELECT=YES,CHANNELS=\"5\"\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-STREAM-INF:BANDWIDTH=360000,CODECS=\"sdvideocodec,audiocodec\","
|
|
|
|
"RESOLUTION=800x600,AUDIO=\"audiogroup\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"http://playlists.org/sd.m3u8\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-STREAM-INF:BANDWIDTH=760000,CODECS=\"hdvideocodec,audiocodec\","
|
|
|
|
"RESOLUTION=800x600,AUDIO=\"audiogroup\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"http://playlists.org/hd.m3u8\n";
|
|
|
|
|
|
|
|
ASSERT_EQ(expected, actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
2018-01-26 23:53:58 +00:00
|
|
|
const uint64_t kVideoBitRate = 300000;
|
|
|
|
|
|
|
|
const uint64_t kAudio1BitRate = 50000;
|
|
|
|
const uint64_t kAudio2BitRate = 100000;
|
|
|
|
|
|
|
|
const uint64_t kAudio1Channels = 1;
|
|
|
|
const uint64_t kAudio2Channels = 8;
|
|
|
|
|
|
|
|
// First video, sd.m3u8.
|
|
|
|
std::unique_ptr<MockMediaPlaylist> video_playlist =
|
|
|
|
CreateVideoPlaylist("video.m3u8", "videocodec", kVideoBitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(video_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
// First audio, eng_lo.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> eng_lo_playlist = CreateAudioPlaylist(
|
|
|
|
"eng_lo.m3u8", "english_lo", "audio_lo", "audiocodec_lo", "en",
|
|
|
|
kAudio1Channels, kAudio1BitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(eng_lo_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
2018-01-17 23:43:41 +00:00
|
|
|
// Second audio, eng_hi.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> eng_hi_playlist = CreateAudioPlaylist(
|
|
|
|
"eng_hi.m3u8", "english_hi", "audio_hi", "audiocodec_hi", "en",
|
|
|
|
kAudio2Channels, kAudio2BitRate);
|
|
|
|
master_playlist_.AddMediaPlaylist(eng_hi_playlist.get());
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://anydomain.com/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
|
|
|
|
|
|
|
|
std::string actual;
|
2017-07-10 18:26:22 +00:00
|
|
|
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
const std::string expected =
|
|
|
|
"#EXTM3U\n"
|
2016-07-07 19:34:07 +00:00
|
|
|
"## Generated with https://github.com/google/shaka-packager version "
|
|
|
|
"test\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\","
|
|
|
|
"GROUP-ID=\"audio_hi\",LANGUAGE=\"en\",NAME=\"english_hi\","
|
2018-01-17 23:43:41 +00:00
|
|
|
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"8\"\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_lo.m3u8\","
|
|
|
|
"GROUP-ID=\"audio_lo\",LANGUAGE=\"en\",NAME=\"english_lo\","
|
2018-01-17 23:43:41 +00:00
|
|
|
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"1\"\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec_hi\","
|
|
|
|
"RESOLUTION=800x600,AUDIO=\"audio_hi\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"http://anydomain.com/video.m3u8\n"
|
2017-12-09 01:53:17 +00:00
|
|
|
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"videocodec,audiocodec_lo\","
|
|
|
|
"RESOLUTION=800x600,AUDIO=\"audio_lo\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"http://anydomain.com/video.m3u8\n";
|
|
|
|
|
|
|
|
ASSERT_EQ(expected, actual);
|
|
|
|
}
|
|
|
|
|
2018-01-17 23:43:41 +00:00
|
|
|
TEST_F(MasterPlaylistTest, WriteMasterPlaylistSameAudioGroupSameLanguage) {
|
|
|
|
// First video, video.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> video_playlist =
|
|
|
|
CreateVideoPlaylist("video.m3u8", "videocodec", 300000);
|
|
|
|
master_playlist_.AddMediaPlaylist(video_playlist.get());
|
2018-01-17 23:43:41 +00:00
|
|
|
|
|
|
|
// First audio, eng_lo.m3u8.
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> eng_lo_playlist = CreateAudioPlaylist(
|
|
|
|
"eng_lo.m3u8", "english", "audio", "audiocodec", "en", 1, 50000);
|
|
|
|
master_playlist_.AddMediaPlaylist(eng_lo_playlist.get());
|
2018-01-17 23:43:41 +00:00
|
|
|
|
2018-01-26 23:53:58 +00:00
|
|
|
std::unique_ptr<MockMediaPlaylist> eng_hi_playlist = CreateAudioPlaylist(
|
|
|
|
"eng_hi.m3u8", "english", "audio", "audiocodec", "en", 8, 100000);
|
|
|
|
master_playlist_.AddMediaPlaylist(eng_hi_playlist.get());
|
2018-01-17 23:43:41 +00:00
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://anydomain.com/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
|
|
|
|
|
|
|
|
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-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_lo.m3u8\","
|
|
|
|
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\","
|
|
|
|
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"1\"\n"
|
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\","
|
|
|
|
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\","
|
|
|
|
"CHANNELS=\"8\"\n"
|
|
|
|
"#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec\","
|
|
|
|
"RESOLUTION=800x600,AUDIO=\"audio\"\n"
|
|
|
|
"http://anydomain.com/video.m3u8\n";
|
|
|
|
|
|
|
|
ASSERT_EQ(expected, actual);
|
|
|
|
}
|
2016-03-25 08:39:07 +00:00
|
|
|
} // namespace hls
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|