Pass playlist type to MediaPlaylist

- The current implementation only handles VOD.
- Add #EXT-X-PLATLIST-TYPE.
- For VOD Add #EXT-X-ENDLIST at the end.
- Append comma at the end of EXTINF.

Change-Id: I16f01da66f8bbf0229395cb380fa125ffd9328a8
This commit is contained in:
Rintaro Kuroiwa 2016-04-20 15:23:19 -07:00
parent 25305b6aa3
commit 67e0f9f31b
10 changed files with 175 additions and 88 deletions

View File

@ -87,14 +87,16 @@ bool MasterPlaylist::WriteMasterPlaylist(const std::string& base_url,
std::map<std::string, std::list<const MediaPlaylist*>> audio_group_map; std::map<std::string, std::list<const MediaPlaylist*>> audio_group_map;
std::list<const MediaPlaylist*> video_playlists; std::list<const MediaPlaylist*> video_playlists;
for (const MediaPlaylist* media_playlist : media_playlists_) { for (const MediaPlaylist* media_playlist : media_playlists_) {
MediaPlaylist::MediaPlaylistType type = media_playlist->type(); MediaPlaylist::MediaPlaylistStreamType stream_type =
if (type == MediaPlaylist::MediaPlaylistType::kPlayListAudio) { media_playlist->stream_type();
if (stream_type == MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio) {
auto& audio_playlists = audio_group_map[media_playlist->group_id()]; auto& audio_playlists = audio_group_map[media_playlist->group_id()];
audio_playlists.push_back(media_playlist); audio_playlists.push_back(media_playlist);
} else if (type == MediaPlaylist::MediaPlaylistType::kPlayListVideo) { } else if (stream_type ==
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo) {
video_playlists.push_back(media_playlist); video_playlists.push_back(media_playlist);
} else { } else {
NOTIMPLEMENTED() << static_cast<int>(type) << " not handled."; NOTIMPLEMENTED() << static_cast<int>(stream_type) << " not handled.";
} }
} }

View File

@ -24,6 +24,8 @@ using ::testing::_;
namespace { namespace {
const char kDefaultMasterPlaylistName[] = "playlist.m3u8"; const char kDefaultMasterPlaylistName[] = "playlist.m3u8";
const MediaPlaylist::MediaPlaylistType kVodPlaylist =
MediaPlaylist::MediaPlaylistType::kVod;
} // namespace } // namespace
class MasterPlaylistTest : public ::testing::Test { class MasterPlaylistTest : public ::testing::Test {
@ -56,15 +58,17 @@ class MasterPlaylistTest : public ::testing::Test {
}; };
TEST_F(MasterPlaylistTest, AddMediaPlaylist) { TEST_F(MasterPlaylistTest, AddMediaPlaylist) {
MockMediaPlaylist mock_playlist("playlist1.m3u8", "somename", "somegroupid"); MockMediaPlaylist mock_playlist(kVodPlaylist, "playlist1.m3u8", "somename",
"somegroupid");
master_playlist_.AddMediaPlaylist(&mock_playlist); master_playlist_.AddMediaPlaylist(&mock_playlist);
} }
TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) { TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
std::string codec = "avc1"; std::string codec = "avc1";
MockMediaPlaylist mock_playlist("media1.m3u8", "somename", "somegroupid"); MockMediaPlaylist mock_playlist(kVodPlaylist, "media1.m3u8", "somename",
mock_playlist.SetTypeForTesting( "somegroupid");
MediaPlaylist::MediaPlaylistType::kPlayListVideo); mock_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
mock_playlist.SetCodecForTesting(codec); mock_playlist.SetCodecForTesting(codec);
EXPECT_CALL(mock_playlist, Bitrate()).WillOnce(Return(435889)); EXPECT_CALL(mock_playlist, Bitrate()).WillOnce(Return(435889));
master_playlist_.AddMediaPlaylist(&mock_playlist); master_playlist_.AddMediaPlaylist(&mock_playlist);
@ -91,9 +95,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) { TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
// First video, sd.m3u8. // First video, sd.m3u8.
std::string sd_video_codec = "sdvideocodec"; std::string sd_video_codec = "sdvideocodec";
MockMediaPlaylist sd_video_playlist("sd.m3u8", "somename", "somegroupid"); MockMediaPlaylist sd_video_playlist(kVodPlaylist, "sd.m3u8", "somename",
sd_video_playlist.SetTypeForTesting( "somegroupid");
MediaPlaylist::MediaPlaylistType::kPlayListVideo); sd_video_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
sd_video_playlist.SetCodecForTesting(sd_video_codec); sd_video_playlist.SetCodecForTesting(sd_video_codec);
EXPECT_CALL(sd_video_playlist, Bitrate()) EXPECT_CALL(sd_video_playlist, Bitrate())
.Times(AtLeast(1)) .Times(AtLeast(1))
@ -102,9 +107,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
// Second video, hd.m3u8. // Second video, hd.m3u8.
std::string hd_video_codec = "hdvideocodec"; std::string hd_video_codec = "hdvideocodec";
MockMediaPlaylist hd_video_playlist("hd.m3u8", "somename", "somegroupid"); MockMediaPlaylist hd_video_playlist(kVodPlaylist, "hd.m3u8", "somename",
hd_video_playlist.SetTypeForTesting( "somegroupid");
MediaPlaylist::MediaPlaylistType::kPlayListVideo); hd_video_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
hd_video_playlist.SetCodecForTesting(hd_video_codec); hd_video_playlist.SetCodecForTesting(hd_video_codec);
EXPECT_CALL(hd_video_playlist, Bitrate()) EXPECT_CALL(hd_video_playlist, Bitrate())
.Times(AtLeast(1)) .Times(AtLeast(1))
@ -115,9 +121,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
// Note that audiocodecs should match for different audio tracks with same // Note that audiocodecs should match for different audio tracks with same
// group ID. // group ID.
std::string audio_codec = "audiocodec"; std::string audio_codec = "audiocodec";
MockMediaPlaylist english_playlist("eng.m3u8", "english", "audiogroup"); MockMediaPlaylist english_playlist(kVodPlaylist, "eng.m3u8", "english",
english_playlist.SetTypeForTesting( "audiogroup");
MediaPlaylist::MediaPlaylistType::kPlayListAudio); english_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
english_playlist.SetCodecForTesting(audio_codec); english_playlist.SetCodecForTesting(audio_codec);
EXPECT_CALL(english_playlist, Bitrate()) EXPECT_CALL(english_playlist, Bitrate())
.Times(AtLeast(1)) .Times(AtLeast(1))
@ -125,9 +132,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
master_playlist_.AddMediaPlaylist(&english_playlist); master_playlist_.AddMediaPlaylist(&english_playlist);
// Second audio, spanish.m3u8. // Second audio, spanish.m3u8.
MockMediaPlaylist spanish_playlist("spa.m3u8", "espanol", "audiogroup"); MockMediaPlaylist spanish_playlist(kVodPlaylist, "spa.m3u8", "espanol",
spanish_playlist.SetTypeForTesting( "audiogroup");
MediaPlaylist::MediaPlaylistType::kPlayListAudio); spanish_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
spanish_playlist.SetCodecForTesting(audio_codec); spanish_playlist.SetCodecForTesting(audio_codec);
EXPECT_CALL(spanish_playlist, Bitrate()) EXPECT_CALL(spanish_playlist, Bitrate())
.Times(AtLeast(1)) .Times(AtLeast(1))
@ -166,9 +174,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) { TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
// First video, sd.m3u8. // First video, sd.m3u8.
std::string video_codec = "videocodec"; std::string video_codec = "videocodec";
MockMediaPlaylist video_playlist("video.m3u8", "somename", "somegroupid"); MockMediaPlaylist video_playlist(kVodPlaylist, "video.m3u8", "somename",
video_playlist.SetTypeForTesting( "somegroupid");
MediaPlaylist::MediaPlaylistType::kPlayListVideo); video_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
video_playlist.SetCodecForTesting(video_codec); video_playlist.SetCodecForTesting(video_codec);
EXPECT_CALL(video_playlist, Bitrate()) EXPECT_CALL(video_playlist, Bitrate())
.Times(AtLeast(1)) .Times(AtLeast(1))
@ -177,9 +186,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
// First audio, eng_lo.m3u8. // First audio, eng_lo.m3u8.
std::string audio_codec_lo = "audiocodec_lo"; std::string audio_codec_lo = "audiocodec_lo";
MockMediaPlaylist eng_lo_playlist("eng_lo.m3u8", "english_lo", "audio_lo"); MockMediaPlaylist eng_lo_playlist(kVodPlaylist, "eng_lo.m3u8", "english_lo",
eng_lo_playlist.SetTypeForTesting( "audio_lo");
MediaPlaylist::MediaPlaylistType::kPlayListAudio); eng_lo_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
eng_lo_playlist.SetCodecForTesting(audio_codec_lo); eng_lo_playlist.SetCodecForTesting(audio_codec_lo);
EXPECT_CALL(eng_lo_playlist, Bitrate()) EXPECT_CALL(eng_lo_playlist, Bitrate())
.Times(AtLeast(1)) .Times(AtLeast(1))
@ -187,9 +197,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
master_playlist_.AddMediaPlaylist(&eng_lo_playlist); master_playlist_.AddMediaPlaylist(&eng_lo_playlist);
std::string audio_codec_hi = "audiocodec_hi"; std::string audio_codec_hi = "audiocodec_hi";
MockMediaPlaylist eng_hi_playlist("eng_hi.m3u8", "english_hi", "audio_hi"); MockMediaPlaylist eng_hi_playlist(kVodPlaylist, "eng_hi.m3u8", "english_hi",
eng_hi_playlist.SetTypeForTesting( "audio_hi");
MediaPlaylist::MediaPlaylistType::kPlayListAudio); eng_hi_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
eng_hi_playlist.SetCodecForTesting(audio_codec_hi); eng_hi_playlist.SetCodecForTesting(audio_codec_hi);
EXPECT_CALL(eng_hi_playlist, Bitrate()) EXPECT_CALL(eng_hi_playlist, Bitrate())
.Times(AtLeast(1)) .Times(AtLeast(1))
@ -236,9 +247,10 @@ MATCHER_P(FileNameMatches, expected_file_name, "") {
// MediaPlaylist::WriteToFile() is called. // MediaPlaylist::WriteToFile() is called.
TEST_F(MasterPlaylistTest, WriteAllPlaylists) { TEST_F(MasterPlaylistTest, WriteAllPlaylists) {
std::string codec = "avc1"; std::string codec = "avc1";
MockMediaPlaylist mock_playlist("media1.m3u8", "somename", "somegroupid"); MockMediaPlaylist mock_playlist(kVodPlaylist, "media1.m3u8", "somename",
mock_playlist.SetTypeForTesting( "somegroupid");
MediaPlaylist::MediaPlaylistType::kPlayListVideo); mock_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
mock_playlist.SetCodecForTesting(codec); mock_playlist.SetCodecForTesting(codec);
ON_CALL(mock_playlist, Bitrate()).WillByDefault(Return(435889)); ON_CALL(mock_playlist, Bitrate()).WillByDefault(Return(435889));

View File

@ -51,7 +51,7 @@ SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
SegmentInfoEntry::~SegmentInfoEntry() {} SegmentInfoEntry::~SegmentInfoEntry() {}
std::string SegmentInfoEntry::ToString() { std::string SegmentInfoEntry::ToString() {
return base::StringPrintf("#EXTINF:%.3f\n%s\n", duration_, return base::StringPrintf("#EXTINF:%.3f,\n%s\n", duration_,
file_name_.c_str()); file_name_.c_str());
} }
@ -120,15 +120,23 @@ std::string EncryptionInfoEntry::ToString() {
HlsEntry::HlsEntry(HlsEntry::EntryType type) : type_(type) {} HlsEntry::HlsEntry(HlsEntry::EntryType type) : type_(type) {}
HlsEntry::~HlsEntry() {} HlsEntry::~HlsEntry() {}
MediaPlaylist::MediaPlaylist(const std::string& file_name, MediaPlaylist::MediaPlaylist(MediaPlaylistType type,
const std::string& file_name,
const std::string& name, const std::string& name,
const std::string& group_id) const std::string& group_id)
: file_name_(file_name), name_(name), group_id_(group_id), : file_name_(file_name),
entries_deleter_(&entries_) {} name_(name),
group_id_(group_id),
type_(type),
entries_deleter_(&entries_) {
LOG_IF(WARNING, type != MediaPlaylistType::kVod)
<< "Non VOD Media Playlist is not supported.";
}
MediaPlaylist::~MediaPlaylist() {} MediaPlaylist::~MediaPlaylist() {}
void MediaPlaylist::SetTypeForTesting(MediaPlaylistType type) { void MediaPlaylist::SetStreamTypeForTesting(
type_ = type; MediaPlaylistStreamType stream_type) {
stream_type_ = stream_type;
} }
void MediaPlaylist::SetCodecForTesting(const std::string& codec) { void MediaPlaylist::SetCodecForTesting(const std::string& codec) {
@ -143,10 +151,10 @@ bool MediaPlaylist::SetMediaInfo(const MediaInfo& media_info) {
} }
if (media_info.has_video_info()) { if (media_info.has_video_info()) {
type_ = MediaPlaylistType::kPlayListVideo; stream_type_ = MediaPlaylistStreamType::kPlayListVideo;
codec_ = media_info.video_info().codec(); codec_ = media_info.video_info().codec();
} else if (media_info.has_audio_info()) { } else if (media_info.has_audio_info()) {
type_ = MediaPlaylistType::kPlayListAudio; stream_type_ = MediaPlaylistStreamType::kPlayListAudio;
codec_ = media_info.audio_info().codec(); codec_ = media_info.audio_info().codec();
} else { } else {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
@ -270,12 +278,20 @@ bool MediaPlaylist::WriteToFile(media::File* file) {
"#EXT-X-VERSION:4\n" "#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:%d\n", "#EXT-X-TARGETDURATION:%d\n",
target_duration_); target_duration_);
if (type_ == MediaPlaylistType::kVod) {
header += "#EXT-X-PLAYLIST-TYPE:VOD\n";
}
std::string body; std::string body;
for (const auto& entry : entries_) { for (const auto& entry : entries_) {
body.append(entry->ToString()); body.append(entry->ToString());
} }
std::string content = header + body; std::string content = header + body;
if (type_ == MediaPlaylistType::kVod) {
content += "#EXT-X-ENDLIST\n";
}
int64_t bytes_written = file->Write(content.data(), content.size()); int64_t bytes_written = file->Write(content.data(), content.size());
if (bytes_written < 0) { if (bytes_written < 0) {
LOG(ERROR) << "Error while writing playlist to file."; LOG(ERROR) << "Error while writing playlist to file.";

View File

@ -45,6 +45,11 @@ class HlsEntry {
class MediaPlaylist { class MediaPlaylist {
public: public:
enum class MediaPlaylistType { enum class MediaPlaylistType {
kVod,
kEvent,
kLive,
};
enum class MediaPlaylistStreamType {
kPlaylistUnknown, kPlaylistUnknown,
kPlayListAudio, kPlayListAudio,
kPlayListVideo, kPlayListVideo,
@ -56,26 +61,27 @@ class MediaPlaylist {
kSampleAes, // Encrypted using Sample AES method. kSampleAes, // Encrypted using Sample AES method.
}; };
/// @param type is the type of this media playlist.
/// @param file_name is the file name of this media playlist. /// @param file_name is the file name of this media playlist.
/// @param name is the name of this playlist. In other words this is the /// @param name is the name of this playlist. In other words this is the
/// value of the NAME attribute for EXT-X-MEDIA. This is not /// value of the NAME attribute for EXT-X-MEDIA. This is not
/// necessarily the same as @a file_name. /// necessarily the same as @a file_name.
/// @param group_id is the group ID for this playlist. This is the value of /// @param group_id is the group ID for this playlist. This is the value of
/// GROUP-ID attribute for EXT-X-MEDIA. /// GROUP-ID attribute for EXT-X-MEDIA.
MediaPlaylist( MediaPlaylist(MediaPlaylistType type,
const std::string& file_name, const std::string& file_name,
const std::string& name, const std::string& name,
const std::string& group_id); const std::string& group_id);
virtual ~MediaPlaylist(); virtual ~MediaPlaylist();
const std::string& file_name() const { return file_name_; } const std::string& file_name() const { return file_name_; }
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
const std::string& group_id() const { return group_id_; } const std::string& group_id() const { return group_id_; }
MediaPlaylistType type() const { return type_; } MediaPlaylistStreamType stream_type() const { return stream_type_; }
const std::string& codec() const { return codec_; } const std::string& codec() const { return codec_; }
/// For testing only. /// For testing only.
void SetTypeForTesting(MediaPlaylistType type); void SetStreamTypeForTesting(MediaPlaylistStreamType stream_type);
/// For testing only. /// For testing only.
void SetCodecForTesting(const std::string& codec); void SetCodecForTesting(const std::string& codec);
@ -151,7 +157,9 @@ class MediaPlaylist {
const std::string name_; const std::string name_;
const std::string group_id_; const std::string group_id_;
MediaInfo media_info_; MediaInfo media_info_;
MediaPlaylistType type_ = MediaPlaylistType::kPlaylistUnknown; const MediaPlaylistType type_;
MediaPlaylistStreamType stream_type_ =
MediaPlaylistStreamType::kPlaylistUnknown;
std::string codec_; std::string codec_;
double longest_segment_duration_ = 0.0; double longest_segment_duration_ = 0.0;

View File

@ -50,7 +50,10 @@ class MediaPlaylistTest : public ::testing::Test {
: default_file_name_(kDefaultPlaylistFileName), : default_file_name_(kDefaultPlaylistFileName),
default_name_("default_name"), default_name_("default_name"),
default_group_id_("default_group_id"), default_group_id_("default_group_id"),
media_playlist_(default_file_name_, default_name_, default_group_id_) {} media_playlist_(MediaPlaylist::MediaPlaylistType::kVod,
default_file_name_,
default_name_,
default_group_id_) {}
void SetUp() override { void SetUp() override {
MediaInfo::VideoInfo* video_info = MediaInfo::VideoInfo* video_info =
@ -114,7 +117,9 @@ TEST_F(MediaPlaylistTest, WriteToFile) {
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:4\n" "#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:0\n"; "#EXT-X-TARGETDURATION:0\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-ENDLIST\n";
MockFile file; MockFile file;
EXPECT_CALL(file, EXPECT_CALL(file,
@ -167,7 +172,9 @@ TEST_F(MediaPlaylistTest, SetTargetDuration) {
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:4\n" "#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:20\n"; "#EXT-X-TARGETDURATION:20\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-ENDLIST\n";
MockFile file; MockFile file;
EXPECT_CALL(file, EXPECT_CALL(file,
@ -192,10 +199,12 @@ TEST_F(MediaPlaylistTest, WriteToFileWithSegments) {
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:4\n" "#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
"#EXTINF:10.000\n" "#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXTINF:10.000,\n"
"file1.ts\n" "file1.ts\n"
"#EXTINF:30.000\n" "#EXTINF:30.000,\n"
"file2.ts\n"; "file2.ts\n"
"#EXT-X-ENDLIST\n";
MockFile file; MockFile file;
EXPECT_CALL(file, EXPECT_CALL(file,
@ -219,13 +228,15 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfo) {
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:4\n" "#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-KEY:METHOD=SAMPLE-AES," "#EXT-X-KEY:METHOD=SAMPLE-AES,"
"URI=\"http://example.com\",IV=0x12345678,KEYFORMATVERSIONS=\"1/2/4\"," "URI=\"http://example.com\",IV=0x12345678,KEYFORMATVERSIONS=\"1/2/4\","
"KEYFORMAT=\"com.widevine\"\n" "KEYFORMAT=\"com.widevine\"\n"
"#EXTINF:10.000\n" "#EXTINF:10.000,\n"
"file1.ts\n" "file1.ts\n"
"#EXTINF:30.000\n" "#EXTINF:30.000,\n"
"file2.ts\n"; "file2.ts\n"
"#EXT-X-ENDLIST\n";
MockFile file; MockFile file;
EXPECT_CALL(file, EXPECT_CALL(file,
@ -249,12 +260,14 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfoEmptyIv) {
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:4\n" "#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-KEY:METHOD=SAMPLE-AES," "#EXT-X-KEY:METHOD=SAMPLE-AES,"
"URI=\"http://example.com\",KEYFORMAT=\"com.widevine\"\n" "URI=\"http://example.com\",KEYFORMAT=\"com.widevine\"\n"
"#EXTINF:10.000\n" "#EXTINF:10.000,\n"
"file1.ts\n" "file1.ts\n"
"#EXTINF:30.000\n" "#EXTINF:30.000,\n"
"file2.ts\n"; "file2.ts\n"
"#EXT-X-ENDLIST\n";
MockFile file; MockFile file;
EXPECT_CALL(file, EXPECT_CALL(file,
@ -277,8 +290,10 @@ TEST_F(MediaPlaylistTest, RemoveOldestSegment) {
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:4\n" "#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
"#EXTINF:30.000\n" "#EXT-X-PLAYLIST-TYPE:VOD\n"
"file2.ts\n"; "#EXTINF:30.000,\n"
"file2.ts\n"
"#EXT-X-ENDLIST\n";
MockFile file; MockFile file;
EXPECT_CALL(file, EXPECT_CALL(file,

View File

@ -9,10 +9,11 @@
namespace edash_packager { namespace edash_packager {
namespace hls { namespace hls {
MockMediaPlaylist::MockMediaPlaylist(const std::string& file_name, MockMediaPlaylist::MockMediaPlaylist(MediaPlaylistType type,
const std::string& file_name,
const std::string& name, const std::string& name,
const std::string& group_id) const std::string& group_id)
: MediaPlaylist(file_name, name, group_id) {} : MediaPlaylist(type, file_name, name, group_id) {}
MockMediaPlaylist::~MockMediaPlaylist() {} MockMediaPlaylist::~MockMediaPlaylist() {}
} // namespace hls } // namespace hls

View File

@ -18,7 +18,8 @@ class MockMediaPlaylist : public MediaPlaylist {
public: public:
// The actual parameters to MediaPlaylist() (parent) constructor doesn't // The actual parameters to MediaPlaylist() (parent) constructor doesn't
// matter because the return value can be mocked. // matter because the return value can be mocked.
MockMediaPlaylist(const std::string& file_name, MockMediaPlaylist(MediaPlaylistType type,
const std::string& file_name,
const std::string& name, const std::string& name,
const std::string& group_id); const std::string& group_id);
~MockMediaPlaylist() override; ~MockMediaPlaylist() override;

View File

@ -29,17 +29,19 @@ bool IsWidevineSystemId(const std::vector<uint8_t>& system_id) {
MediaPlaylistFactory::~MediaPlaylistFactory() {} MediaPlaylistFactory::~MediaPlaylistFactory() {}
scoped_ptr<MediaPlaylist> MediaPlaylistFactory::Create( scoped_ptr<MediaPlaylist> MediaPlaylistFactory::Create(
MediaPlaylist::MediaPlaylistType type,
const std::string& file_name, const std::string& file_name,
const std::string& name, const std::string& name,
const std::string& group_id) { const std::string& group_id) {
return scoped_ptr<MediaPlaylist>( return scoped_ptr<MediaPlaylist>(
new MediaPlaylist(file_name, name, group_id)); new MediaPlaylist(type, file_name, name, group_id));
} }
SimpleHlsNotifier::SimpleHlsNotifier(const std::string& prefix, SimpleHlsNotifier::SimpleHlsNotifier(HlsProfile profile,
const std::string& prefix,
const std::string& output_dir, const std::string& output_dir,
const std::string& master_playlist_name) const std::string& master_playlist_name)
: HlsNotifier(HlsNotifier::HlsProfile::kOnDemandProfile), : HlsNotifier(profile),
prefix_(prefix), prefix_(prefix),
output_dir_(output_dir), output_dir_(output_dir),
media_playlist_factory_(new MediaPlaylistFactory()), media_playlist_factory_(new MediaPlaylistFactory()),
@ -60,8 +62,20 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
DCHECK(stream_id); DCHECK(stream_id);
*stream_id = sequence_number_.GetNext(); *stream_id = sequence_number_.GetNext();
MediaPlaylist::MediaPlaylistType type;
switch (profile()) {
case HlsProfile::kLiveProfile:
type = MediaPlaylist::MediaPlaylistType::kLive;
break;
case HlsProfile::kOnDemandProfile:
type = MediaPlaylist::MediaPlaylistType::kVod;
break;
default:
NOTREACHED();
return false;
}
scoped_ptr<MediaPlaylist> media_playlist = scoped_ptr<MediaPlaylist> media_playlist =
media_playlist_factory_->Create(playlist_name, name, group_id); media_playlist_factory_->Create(type, playlist_name, name, group_id);
if (!media_playlist->SetMediaInfo(media_info)) { if (!media_playlist->SetMediaInfo(media_info)) {
LOG(ERROR) << "Failed to set media info for playlist " << playlist_name; LOG(ERROR) << "Failed to set media info for playlist " << playlist_name;
return false; return false;

View File

@ -14,6 +14,7 @@
#include "packager/base/synchronization/lock.h" #include "packager/base/synchronization/lock.h"
#include "packager/hls/base/hls_notifier.h" #include "packager/hls/base/hls_notifier.h"
#include "packager/hls/base/master_playlist.h" #include "packager/hls/base/master_playlist.h"
#include "packager/hls/base/media_playlist.h"
namespace edash_packager { namespace edash_packager {
namespace hls { namespace hls {
@ -23,9 +24,11 @@ namespace hls {
class MediaPlaylistFactory { class MediaPlaylistFactory {
public: public:
virtual ~MediaPlaylistFactory(); virtual ~MediaPlaylistFactory();
virtual scoped_ptr<MediaPlaylist> Create(const std::string& file_name, virtual scoped_ptr<MediaPlaylist> Create(
const std::string& name, MediaPlaylist::MediaPlaylistType type,
const std::string& group_id); const std::string& file_name,
const std::string& name,
const std::string& group_id);
}; };
/// This is thread safe. /// This is thread safe.
@ -33,12 +36,14 @@ class SimpleHlsNotifier : public HlsNotifier {
public: public:
/// @a prefix is used as hte prefix for all the URIs for Media Playlist. This /// @a prefix is used as hte prefix for all the URIs for Media Playlist. This
/// includes the segment URIs in the Media Playlists. /// includes the segment URIs in the Media Playlists.
/// @param profile is the profile of the playlists.
/// @param prefix is the used as the prefix for MediaPlaylist URIs. May be /// @param prefix is the used as the prefix for MediaPlaylist URIs. May be
/// empty for relative URI from the playlist. /// empty for relative URI from the playlist.
/// @param output_dir is the output directory of the playlists. May be empty /// @param output_dir is the output directory of the playlists. May be empty
/// to write to current directory. /// to write to current directory.
/// @param master_playlist_name is the name of the master playlist. /// @param master_playlist_name is the name of the master playlist.
SimpleHlsNotifier(const std::string& prefix, SimpleHlsNotifier(HlsProfile profile,
const std::string& prefix,
const std::string& output_dir, const std::string& output_dir,
const std::string& master_playlist_name); const std::string& master_playlist_name);
~SimpleHlsNotifier() override; ~SimpleHlsNotifier() override;

View File

@ -21,6 +21,8 @@ using ::testing::_;
namespace { namespace {
const char kMasterPlaylistName[] = "master.m3u8"; const char kMasterPlaylistName[] = "master.m3u8";
const MediaPlaylist::MediaPlaylistType kVodPlaylist =
MediaPlaylist::MediaPlaylistType::kVod;
class MockMasterPlaylist : public MasterPlaylist { class MockMasterPlaylist : public MasterPlaylist {
public: public:
@ -35,15 +37,18 @@ class MockMasterPlaylist : public MasterPlaylist {
class MockMediaPlaylistFactory : public MediaPlaylistFactory { class MockMediaPlaylistFactory : public MediaPlaylistFactory {
public: public:
MOCK_METHOD3(CreateMock, MOCK_METHOD4(CreateMock,
MediaPlaylist*(const std::string& file_name, MediaPlaylist*(MediaPlaylist::MediaPlaylistType type,
const std::string& file_name,
const std::string& name, const std::string& name,
const std::string& group_id)); const std::string& group_id));
scoped_ptr<MediaPlaylist> Create(const std::string& file_name, scoped_ptr<MediaPlaylist> Create(MediaPlaylist::MediaPlaylistType type,
const std::string& file_name,
const std::string& name, const std::string& name,
const std::string& group_id) override { const std::string& group_id) override {
return scoped_ptr<MediaPlaylist>(CreateMock(file_name, name, group_id)); return scoped_ptr<MediaPlaylist>(
CreateMock(type, file_name, name, group_id));
} }
}; };
@ -55,7 +60,10 @@ const char kAnyOutputDir[] = "anything/";
class SimpleHlsNotifierTest : public ::testing::Test { class SimpleHlsNotifierTest : public ::testing::Test {
protected: protected:
SimpleHlsNotifierTest() SimpleHlsNotifierTest()
: notifier_(kTestPrefix, kAnyOutputDir, kMasterPlaylistName) {} : notifier_(HlsNotifier::HlsProfile::kOnDemandProfile,
kTestPrefix,
kAnyOutputDir,
kMasterPlaylistName) {}
void InjectMediaPlaylistFactory(scoped_ptr<MediaPlaylistFactory> factory) { void InjectMediaPlaylistFactory(scoped_ptr<MediaPlaylistFactory> factory) {
notifier_.media_playlist_factory_ = factory.Pass(); notifier_.media_playlist_factory_ = factory.Pass();
@ -81,12 +89,13 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory()); scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
// Pointer released by SimpleHlsNotifier. // Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist = new MockMediaPlaylist("", "", ""); MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
EXPECT_CALL(*mock_master_playlist, AddMediaPlaylist(mock_media_playlist)); EXPECT_CALL(*mock_master_playlist, AddMediaPlaylist(mock_media_playlist));
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true)); EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
EXPECT_CALL(*factory, CreateMock(StrEq("video_playlist.m3u8"), StrEq("name"), EXPECT_CALL(*factory, CreateMock(kVodPlaylist, StrEq("video_playlist.m3u8"),
StrEq("groupid"))) StrEq("name"), StrEq("groupid")))
.WillOnce(Return(mock_media_playlist)); .WillOnce(Return(mock_media_playlist));
InjectMasterPlaylist(mock_master_playlist.Pass()); InjectMasterPlaylist(mock_master_playlist.Pass());
@ -104,13 +113,14 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory()); scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
// Pointer released by SimpleHlsNotifier. // Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist = new MockMediaPlaylist("", "", ""); MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
EXPECT_CALL( EXPECT_CALL(
*mock_master_playlist, *mock_master_playlist,
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist))); AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true)); EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
EXPECT_CALL(*factory, CreateMock(_, _, _)) EXPECT_CALL(*factory, CreateMock(_, _, _, _))
.WillOnce(Return(mock_media_playlist)); .WillOnce(Return(mock_media_playlist));
const uint64_t kStartTime = 1328; const uint64_t kStartTime = 1328;
@ -142,13 +152,14 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdate) {
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory()); scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
// Pointer released by SimpleHlsNotifier. // Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist = new MockMediaPlaylist("", "", ""); MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
EXPECT_CALL( EXPECT_CALL(
*mock_master_playlist, *mock_master_playlist,
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist))); AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true)); EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
EXPECT_CALL(*factory, CreateMock(_, _, _)) EXPECT_CALL(*factory, CreateMock(_, _, _, _))
.WillOnce(Return(mock_media_playlist)); .WillOnce(Return(mock_media_playlist));
InjectMasterPlaylist(mock_master_playlist.Pass()); InjectMasterPlaylist(mock_master_playlist.Pass());
@ -206,13 +217,14 @@ TEST_F(SimpleHlsNotifierTest, MultipleKeyIdsInPssh) {
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory()); scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
// Pointer released by SimpleHlsNotifier. // Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist = new MockMediaPlaylist("", "", ""); MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
EXPECT_CALL( EXPECT_CALL(
*mock_master_playlist, *mock_master_playlist,
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist))); AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true)); EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
EXPECT_CALL(*factory, CreateMock(_, _, _)) EXPECT_CALL(*factory, CreateMock(_, _, _, _))
.WillOnce(Return(mock_media_playlist)); .WillOnce(Return(mock_media_playlist));
InjectMasterPlaylist(mock_master_playlist.Pass()); InjectMasterPlaylist(mock_master_playlist.Pass());
@ -276,13 +288,14 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) {
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory()); scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
// Pointer released by SimpleHlsNotifier. // Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist = new MockMediaPlaylist("", "", ""); MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
EXPECT_CALL( EXPECT_CALL(
*mock_master_playlist, *mock_master_playlist,
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist))); AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true)); EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
EXPECT_CALL(*factory, CreateMock(_, _, _)) EXPECT_CALL(*factory, CreateMock(_, _, _, _))
.WillOnce(Return(mock_media_playlist)); .WillOnce(Return(mock_media_playlist));
InjectMasterPlaylist(mock_master_playlist.Pass()); InjectMasterPlaylist(mock_master_playlist.Pass());