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>
|
|
|
|
|
|
|
|
#include "packager/base/files/file_util.h"
|
|
|
|
#include "packager/base/files/scoped_temp_dir.h"
|
|
|
|
#include "packager/hls/base/master_playlist.h"
|
|
|
|
#include "packager/hls/base/media_playlist.h"
|
|
|
|
#include "packager/hls/base/mock_media_playlist.h"
|
|
|
|
#include "packager/media/file/file.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 {
|
|
|
|
|
|
|
|
using ::testing::AtLeast;
|
|
|
|
using ::testing::Return;
|
|
|
|
using ::testing::ReturnRef;
|
|
|
|
using ::testing::_;
|
2016-08-14 22:28:21 +00:00
|
|
|
using base::FilePath;
|
2016-03-25 08:39:07 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
const char kDefaultMasterPlaylistName[] = "playlist.m3u8";
|
2016-04-20 22:23:19 +00:00
|
|
|
const MediaPlaylist::MediaPlaylistType kVodPlaylist =
|
|
|
|
MediaPlaylist::MediaPlaylistType::kVod;
|
2016-03-25 08:39:07 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
class MasterPlaylistTest : public ::testing::Test {
|
|
|
|
protected:
|
|
|
|
MasterPlaylistTest() : master_playlist_(kDefaultMasterPlaylistName) {}
|
|
|
|
|
|
|
|
void SetUp() override {
|
2016-07-07 19:34:07 +00:00
|
|
|
SetPackagerVersionForTesting("test");
|
2016-03-25 08:39:07 +00:00
|
|
|
GetOutputDir(&test_output_dir_path_, &test_output_dir_);
|
|
|
|
}
|
|
|
|
|
|
|
|
MasterPlaylist master_playlist_;
|
2016-08-14 22:28:21 +00:00
|
|
|
FilePath test_output_dir_path_;
|
2016-03-25 08:39:07 +00:00
|
|
|
std::string test_output_dir_;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Creates a path to the output directory for writing out playlists.
|
|
|
|
// |temp_dir_path| is set to the temporary directory so that it can be opened
|
|
|
|
// using base::File* related API.
|
|
|
|
// |output_dir| is set to an equivalent value to |temp_dir_path| but formatted
|
|
|
|
// so that media::File interface can Open it.
|
2016-08-14 22:28:21 +00:00
|
|
|
void GetOutputDir(FilePath* temp_dir_path, std::string* output_dir) {
|
2016-03-25 08:39:07 +00:00
|
|
|
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
|
|
|
|
ASSERT_TRUE(temp_dir_.IsValid());
|
|
|
|
*temp_dir_path = temp_dir_.path();
|
|
|
|
// TODO(rkuroiwa): Use memory file sys once prefix is exposed.
|
2016-08-14 22:28:21 +00:00
|
|
|
*output_dir = media::kLocalFilePrefix + temp_dir_.path().AsUTF8Unsafe()
|
|
|
|
+ "/";
|
2016-03-25 08:39:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
base::ScopedTempDir temp_dir_;
|
|
|
|
};
|
|
|
|
|
|
|
|
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) {
|
|
|
|
std::string codec = "avc1";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist mock_playlist(kVodPlaylist, "media1.m3u8", "somename",
|
|
|
|
"somegroupid");
|
|
|
|
mock_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
|
2016-03-25 08:39:07 +00:00
|
|
|
mock_playlist.SetCodecForTesting(codec);
|
|
|
|
EXPECT_CALL(mock_playlist, Bitrate()).WillOnce(Return(435889));
|
|
|
|
master_playlist_.AddMediaPlaylist(&mock_playlist);
|
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://myplaylistdomain.com/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
|
|
|
|
|
2016-08-14 22:28:21 +00:00
|
|
|
FilePath master_playlist_path =
|
|
|
|
test_output_dir_path_.Append(FilePath::FromUTF8Unsafe(
|
|
|
|
kDefaultMasterPlaylistName));
|
2016-03-25 08:39:07 +00:00
|
|
|
ASSERT_TRUE(base::PathExists(master_playlist_path))
|
|
|
|
<< "Cannot find " << master_playlist_path.value();
|
|
|
|
|
|
|
|
std::string actual;
|
|
|
|
ASSERT_TRUE(base::ReadFileToString(master_playlist_path, &actual));
|
|
|
|
|
|
|
|
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"
|
2016-04-16 22:58:47 +00:00
|
|
|
"#EXT-X-STREAM-INF:CODECS=\"avc1\",BANDWIDTH=435889\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"http://myplaylistdomain.com/media1.m3u8\n";
|
|
|
|
|
|
|
|
ASSERT_EQ(expected, actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
|
|
|
// First video, sd.m3u8.
|
|
|
|
std::string sd_video_codec = "sdvideocodec";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist sd_video_playlist(kVodPlaylist, "sd.m3u8", "somename",
|
|
|
|
"somegroupid");
|
|
|
|
sd_video_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
|
2016-03-25 08:39:07 +00:00
|
|
|
sd_video_playlist.SetCodecForTesting(sd_video_codec);
|
|
|
|
EXPECT_CALL(sd_video_playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(300000));
|
|
|
|
master_playlist_.AddMediaPlaylist(&sd_video_playlist);
|
|
|
|
|
|
|
|
// Second video, hd.m3u8.
|
|
|
|
std::string hd_video_codec = "hdvideocodec";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist hd_video_playlist(kVodPlaylist, "hd.m3u8", "somename",
|
|
|
|
"somegroupid");
|
|
|
|
hd_video_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
|
2016-03-25 08:39:07 +00:00
|
|
|
hd_video_playlist.SetCodecForTesting(hd_video_codec);
|
|
|
|
EXPECT_CALL(hd_video_playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(700000));
|
|
|
|
master_playlist_.AddMediaPlaylist(&hd_video_playlist);
|
|
|
|
|
|
|
|
// First audio, english.m3u8.
|
|
|
|
// Note that audiocodecs should match for different audio tracks with same
|
|
|
|
// group ID.
|
|
|
|
std::string audio_codec = "audiocodec";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist english_playlist(kVodPlaylist, "eng.m3u8", "english",
|
|
|
|
"audiogroup");
|
2017-03-10 23:18:42 +00:00
|
|
|
EXPECT_CALL(english_playlist, GetLanguage()).WillRepeatedly(Return("en"));
|
2016-04-20 22:23:19 +00:00
|
|
|
english_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
2016-03-25 08:39:07 +00:00
|
|
|
english_playlist.SetCodecForTesting(audio_codec);
|
|
|
|
EXPECT_CALL(english_playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(50000));
|
|
|
|
master_playlist_.AddMediaPlaylist(&english_playlist);
|
|
|
|
|
|
|
|
// Second audio, spanish.m3u8.
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist spanish_playlist(kVodPlaylist, "spa.m3u8", "espanol",
|
|
|
|
"audiogroup");
|
2017-03-10 23:18:42 +00:00
|
|
|
EXPECT_CALL(spanish_playlist, GetLanguage()).WillRepeatedly(Return("es"));
|
2016-04-20 22:23:19 +00:00
|
|
|
spanish_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
2016-03-25 08:39:07 +00:00
|
|
|
spanish_playlist.SetCodecForTesting(audio_codec);
|
|
|
|
EXPECT_CALL(spanish_playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(60000));
|
|
|
|
master_playlist_.AddMediaPlaylist(&spanish_playlist);
|
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://playlists.org/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
|
|
|
|
|
2016-08-14 22:28:21 +00:00
|
|
|
FilePath master_playlist_path =
|
|
|
|
test_output_dir_path_.Append(FilePath::FromUTF8Unsafe(
|
|
|
|
kDefaultMasterPlaylistName));
|
2016-03-25 08:39:07 +00:00
|
|
|
ASSERT_TRUE(base::PathExists(master_playlist_path))
|
|
|
|
<< "Cannot find " << master_playlist_path.value();
|
|
|
|
|
|
|
|
std::string actual;
|
|
|
|
ASSERT_TRUE(base::ReadFileToString(master_playlist_path, &actual));
|
|
|
|
|
|
|
|
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"
|
2016-03-25 08:39:07 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audiogroup\",NAME=\"english\","
|
2017-03-10 23:18:42 +00:00
|
|
|
"LANGUAGE=\"en\",URI=\"http://playlists.org/eng.m3u8\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audiogroup\",NAME=\"espanol\","
|
2017-03-10 23:18:42 +00:00
|
|
|
"LANGUAGE=\"es\",URI=\"http://playlists.org/spa.m3u8\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"#EXT-X-STREAM-INF:AUDIO=\"audiogroup\","
|
2016-04-16 22:58:47 +00:00
|
|
|
"CODECS=\"sdvideocodec,audiocodec\","
|
2016-03-25 08:39:07 +00:00
|
|
|
"BANDWIDTH=360000\n"
|
|
|
|
"http://playlists.org/sd.m3u8\n"
|
|
|
|
"#EXT-X-STREAM-INF:AUDIO=\"audiogroup\","
|
2016-04-16 22:58:47 +00:00
|
|
|
"CODECS=\"hdvideocodec,audiocodec\","
|
2016-03-25 08:39:07 +00:00
|
|
|
"BANDWIDTH=760000\n"
|
|
|
|
"http://playlists.org/hd.m3u8\n";
|
|
|
|
|
|
|
|
ASSERT_EQ(expected, actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
|
|
|
// First video, sd.m3u8.
|
|
|
|
std::string video_codec = "videocodec";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist video_playlist(kVodPlaylist, "video.m3u8", "somename",
|
|
|
|
"somegroupid");
|
|
|
|
video_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
|
2016-03-25 08:39:07 +00:00
|
|
|
video_playlist.SetCodecForTesting(video_codec);
|
|
|
|
EXPECT_CALL(video_playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(300000));
|
|
|
|
master_playlist_.AddMediaPlaylist(&video_playlist);
|
|
|
|
|
|
|
|
// First audio, eng_lo.m3u8.
|
|
|
|
std::string audio_codec_lo = "audiocodec_lo";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist eng_lo_playlist(kVodPlaylist, "eng_lo.m3u8", "english_lo",
|
|
|
|
"audio_lo");
|
2017-03-10 23:18:42 +00:00
|
|
|
EXPECT_CALL(eng_lo_playlist, GetLanguage()).WillRepeatedly(Return("en"));
|
2016-04-20 22:23:19 +00:00
|
|
|
eng_lo_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
2016-03-25 08:39:07 +00:00
|
|
|
eng_lo_playlist.SetCodecForTesting(audio_codec_lo);
|
|
|
|
EXPECT_CALL(eng_lo_playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(50000));
|
|
|
|
master_playlist_.AddMediaPlaylist(&eng_lo_playlist);
|
|
|
|
|
|
|
|
std::string audio_codec_hi = "audiocodec_hi";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist eng_hi_playlist(kVodPlaylist, "eng_hi.m3u8", "english_hi",
|
|
|
|
"audio_hi");
|
2017-03-10 23:18:42 +00:00
|
|
|
EXPECT_CALL(eng_hi_playlist, GetLanguage()).WillRepeatedly(Return("en"));
|
2016-04-20 22:23:19 +00:00
|
|
|
eng_hi_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
2016-03-25 08:39:07 +00:00
|
|
|
eng_hi_playlist.SetCodecForTesting(audio_codec_hi);
|
|
|
|
EXPECT_CALL(eng_hi_playlist, Bitrate())
|
|
|
|
.Times(AtLeast(1))
|
|
|
|
.WillRepeatedly(Return(100000));
|
|
|
|
master_playlist_.AddMediaPlaylist(&eng_hi_playlist);
|
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://anydomain.com/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
|
|
|
|
|
2016-08-14 22:28:21 +00:00
|
|
|
FilePath master_playlist_path = test_output_dir_path_.Append(
|
|
|
|
FilePath::FromUTF8Unsafe(kDefaultMasterPlaylistName));
|
2016-03-25 08:39:07 +00:00
|
|
|
ASSERT_TRUE(base::PathExists(master_playlist_path))
|
|
|
|
<< "Cannot find " << master_playlist_path.value();
|
|
|
|
|
|
|
|
std::string actual;
|
|
|
|
ASSERT_TRUE(base::ReadFileToString(master_playlist_path, &actual));
|
|
|
|
|
|
|
|
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"
|
2016-03-25 08:39:07 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio_hi\",NAME=\"english_hi\","
|
2017-03-10 23:18:42 +00:00
|
|
|
"LANGUAGE=\"en\",URI=\"http://anydomain.com/eng_hi.m3u8\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio_lo\",NAME=\"english_lo\","
|
2017-03-10 23:18:42 +00:00
|
|
|
"LANGUAGE=\"en\",URI=\"http://anydomain.com/eng_lo.m3u8\"\n"
|
2016-03-25 08:39:07 +00:00
|
|
|
"#EXT-X-STREAM-INF:AUDIO=\"audio_hi\","
|
2016-04-16 22:58:47 +00:00
|
|
|
"CODECS=\"videocodec,audiocodec_hi\","
|
2016-03-25 08:39:07 +00:00
|
|
|
"BANDWIDTH=400000\n"
|
|
|
|
"http://anydomain.com/video.m3u8\n"
|
|
|
|
"#EXT-X-STREAM-INF:AUDIO=\"audio_lo\","
|
2016-04-16 22:58:47 +00:00
|
|
|
"CODECS=\"videocodec,audiocodec_lo\","
|
2016-03-25 08:39:07 +00:00
|
|
|
"BANDWIDTH=350000\n"
|
|
|
|
"http://anydomain.com/video.m3u8\n";
|
|
|
|
|
|
|
|
ASSERT_EQ(expected, actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
MATCHER_P(FileNameMatches, expected_file_name, "") {
|
|
|
|
const std::string& actual_filename = arg->file_name();
|
|
|
|
*result_listener << "which is " << actual_filename;
|
|
|
|
return expected_file_name == actual_filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test basically is WriteMasterPlaylist() and also make sure that
|
|
|
|
// the target duration is set for MediaPlaylist and
|
|
|
|
// MediaPlaylist::WriteToFile() is called.
|
|
|
|
TEST_F(MasterPlaylistTest, WriteAllPlaylists) {
|
|
|
|
std::string codec = "avc1";
|
2016-04-20 22:23:19 +00:00
|
|
|
MockMediaPlaylist mock_playlist(kVodPlaylist, "media1.m3u8", "somename",
|
|
|
|
"somegroupid");
|
|
|
|
mock_playlist.SetStreamTypeForTesting(
|
|
|
|
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
|
2016-03-25 08:39:07 +00:00
|
|
|
mock_playlist.SetCodecForTesting(codec);
|
|
|
|
ON_CALL(mock_playlist, Bitrate()).WillByDefault(Return(435889));
|
|
|
|
|
|
|
|
EXPECT_CALL(mock_playlist, GetLongestSegmentDuration()).WillOnce(Return(10));
|
|
|
|
EXPECT_CALL(mock_playlist, SetTargetDuration(10)).WillOnce(Return(true));
|
|
|
|
master_playlist_.AddMediaPlaylist(&mock_playlist);
|
|
|
|
|
2016-08-14 22:28:21 +00:00
|
|
|
EXPECT_CALL(
|
|
|
|
mock_playlist,
|
|
|
|
WriteToFile(FileNameMatches(
|
|
|
|
test_output_dir_path_.Append(FilePath::FromUTF8Unsafe("media1.m3u8"))
|
|
|
|
.AsUTF8Unsafe())))
|
2016-03-25 08:39:07 +00:00
|
|
|
.WillOnce(Return(true));
|
|
|
|
|
|
|
|
const char kBaseUrl[] = "http://domain.com/";
|
|
|
|
EXPECT_TRUE(master_playlist_.WriteAllPlaylists(kBaseUrl, test_output_dir_));
|
2016-08-14 22:28:21 +00:00
|
|
|
FilePath master_playlist_path = test_output_dir_path_.Append(
|
|
|
|
FilePath::FromUTF8Unsafe(kDefaultMasterPlaylistName));
|
2016-03-25 08:39:07 +00:00
|
|
|
ASSERT_TRUE(base::PathExists(master_playlist_path))
|
|
|
|
<< "Cannot find master playlist at " << master_playlist_path.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace hls
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|