HLS byte range

- Use byte range format if segment_template is not set in MediaInfo.
- Change HlsNotifier::NotifyNewSegment() to take start byte offset.
- Change HlsNotifyMuxerListener to cache the segment information if
  MuxerOptions::segment_template is set.

Change-Id: Ida1b8b9198acc5c43e3a5d9a641c78c81bc9a6ac
This commit is contained in:
Rintaro Kuroiwa 2017-05-01 13:38:58 -07:00
parent 0dfdace280
commit d096f4b485
12 changed files with 670 additions and 258 deletions

View File

@ -47,16 +47,19 @@ class HlsNotifier {
const std::string& group_id,
uint32_t* stream_id) = 0;
// |stream_id| is the value set by NotifyNewStream().
// |segment_name| is the name of the new segment.
// |start_time| is the start time of the segment in terms of timescale passed
// in |media_info|.
// |duration| is also in terms of timescale.
// |size| is the size in bytes.
/// @param stream_id is the value set by NotifyNewStream().
/// @param segment_name is the name of the new segment.
/// @param start_time is the start time of the segment in timescale units
/// passed in @a media_info.
/// @param duration is also in terms of timescale.
/// @param start_byte_offset is the offset of where the subsegment starts.
/// This should be 0 if the whole segment is a subsegment.
/// @param size is the size in bytes.
virtual bool NotifyNewSegment(uint32_t stream_id,
const std::string& segment_name,
uint64_t start_time,
uint64_t duration,
uint64_t start_byte_offset,
uint64_t size) = 0;
/// @param stream_id is the value set by NotifyNewStream().

View File

@ -6,11 +6,14 @@
#include "packager/hls/base/media_playlist.h"
#include <inttypes.h>
#include <algorithm>
#include <cmath>
#include <memory>
#include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/stringprintf.h"
#include "packager/media/base/language_utils.h"
#include "packager/media/file/file.h"
@ -32,11 +35,36 @@ uint32_t GetTimeScale(const MediaInfo& media_info) {
return 0u;
}
std::string CreatePlaylistHeader(const std::string& init_segment_name,
uint32_t target_duration,
MediaPlaylist::MediaPlaylistType type,
int media_sequence_number,
int discontinuity_sequence_number) {
std::string CreateExtXMap(const MediaInfo& media_info) {
std::string ext_x_map;
if (media_info.has_init_segment_name()) {
base::StringAppendF(&ext_x_map, "#EXT-X-MAP:URI=\"%s\"",
media_info.init_segment_name().data());
} else if (media_info.has_media_file_name() && media_info.has_init_range()) {
// It only makes sense for single segment media to have EXT-X-MAP if
// there is init_range.
base::StringAppendF(&ext_x_map, "#EXT-X-MAP:URI=\"%s\"",
media_info.media_file_name().data());
} else {
return "";
}
if (media_info.has_init_range()) {
const uint64_t begin = media_info.init_range().begin();
const uint64_t end = media_info.init_range().end();
const uint64_t length = end - begin + 1;
base::StringAppendF(&ext_x_map, ",BYTERANGE=%" PRIu64 "@%" PRIu64, length,
begin);
}
ext_x_map += "\n";
return ext_x_map;
}
std::string CreatePlaylistHeader(
const MediaInfo& media_info,
uint32_t target_duration,
MediaPlaylist::MediaPlaylistType type,
int media_sequence_number,
int discontinuity_sequence_number) {
const std::string version = GetPackagerVersion();
std::string version_line;
if (!version.empty()) {
@ -76,18 +104,24 @@ std::string CreatePlaylistHeader(const std::string& init_segment_name,
// Put EXT-X-MAP at the end since the rest of the playlist is about the
// segment and key info.
if (!init_segment_name.empty()) {
header += "#EXT-X-MAP:URI=\"" + init_segment_name + "\"\n";
}
header += CreateExtXMap(media_info);
return header;
}
class SegmentInfoEntry : public HlsEntry {
public:
// If |use_byte_range| true then this will append EXT-X-BYTERANGE
// after EXTINF.
// It uses |previous_segment_end_offset| to determine if it has to also
// specify the start byte offset in the tag.
// |duration| is duration in seconds.
SegmentInfoEntry(const std::string& file_name,
double start_time,
double duration);
double duration,
bool use_byte_range,
uint64_t start_byte_offset,
uint64_t segment_file_size,
uint64_t previous_segment_end_offset);
~SegmentInfoEntry() override;
std::string ToString() override;
@ -98,22 +132,42 @@ class SegmentInfoEntry : public HlsEntry {
const std::string file_name_;
const double start_time_;
const double duration_;
const bool use_byte_range_;
const uint64_t start_byte_offset_;
const uint64_t segment_file_size_;
const uint64_t previous_segment_end_offset_;
DISALLOW_COPY_AND_ASSIGN(SegmentInfoEntry);
};
SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
double start_time,
double duration)
double duration,
bool use_byte_range,
uint64_t start_byte_offset,
uint64_t segment_file_size,
uint64_t previous_segment_end_offset)
: HlsEntry(HlsEntry::EntryType::kExtInf),
file_name_(file_name),
start_time_(start_time),
duration_(duration) {}
duration_(duration),
use_byte_range_(use_byte_range),
start_byte_offset_(start_byte_offset),
segment_file_size_(segment_file_size),
previous_segment_end_offset_(previous_segment_end_offset) {}
SegmentInfoEntry::~SegmentInfoEntry() {}
std::string SegmentInfoEntry::ToString() {
return base::StringPrintf("#EXTINF:%.3f,\n%s\n", duration_,
file_name_.c_str());
std::string result = base::StringPrintf("#EXTINF:%.3f,\n", duration_);
if (use_byte_range_) {
result += "#EXT-X-BYTERANGE:" + base::Uint64ToString(segment_file_size_);
if (previous_segment_end_offset_ + 1 != start_byte_offset_) {
result += "@" + base::Uint64ToString(start_byte_offset_);
}
result += "\n";
}
result += file_name_ + "\n";
return result;
}
class EncryptionInfoEntry : public HlsEntry {
@ -224,12 +278,12 @@ double LatestSegmentStartTime(
HlsEntry::HlsEntry(HlsEntry::EntryType type) : type_(type) {}
HlsEntry::~HlsEntry() {}
MediaPlaylist::MediaPlaylist(MediaPlaylistType type,
MediaPlaylist::MediaPlaylist(MediaPlaylistType playlist_type,
double time_shift_buffer_depth,
const std::string& file_name,
const std::string& name,
const std::string& group_id)
: type_(type),
: playlist_type_(playlist_type),
time_shift_buffer_depth_(time_shift_buffer_depth),
file_name_(file_name),
name_(name),
@ -272,12 +326,15 @@ bool MediaPlaylist::SetMediaInfo(const MediaInfo& media_info) {
void MediaPlaylist::AddSegment(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t start_byte_offset,
uint64_t size) {
if (time_scale_ == 0) {
LOG(WARNING) << "Timescale is not set and the duration for " << duration
<< " cannot be calculated. The output will be wrong.";
entries_.emplace_back(new SegmentInfoEntry(file_name, 0.0, 0.0));
entries_.emplace_back(new SegmentInfoEntry(
file_name, 0.0, 0.0, !media_info_.has_segment_template(),
start_byte_offset, size, previous_segment_end_offset_));
return;
}
@ -291,8 +348,11 @@ void MediaPlaylist::AddSegment(const std::string& file_name,
const int kBitsInByte = 8;
const uint64_t bitrate = kBitsInByte * size / segment_duration_seconds;
max_bitrate_ = std::max(max_bitrate_, bitrate);
entries_.emplace_back(new SegmentInfoEntry(file_name, start_time_seconds,
segment_duration_seconds));
entries_.emplace_back(new SegmentInfoEntry(
file_name, start_time_seconds, segment_duration_seconds,
!media_info_.has_segment_template(), start_byte_offset, size,
previous_segment_end_offset_));
previous_segment_end_offset_ = start_byte_offset + size - 1;
SlideWindow();
}
@ -319,7 +379,7 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) {
}
std::string header = CreatePlaylistHeader(
media_info_.init_segment_name(), target_duration_, type_,
media_info_, target_duration_, playlist_type_,
media_sequence_number_, discontinuity_sequence_number_);
std::string body;
@ -328,7 +388,7 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) {
std::string content = header + body;
if (type_ == MediaPlaylistType::kVod) {
if (playlist_type_ == MediaPlaylistType::kVod) {
content += "#EXT-X-ENDLIST\n";
}
@ -388,8 +448,10 @@ bool MediaPlaylist::GetResolution(uint32_t* width, uint32_t* height) const {
void MediaPlaylist::SlideWindow() {
DCHECK(!entries_.empty());
if (time_shift_buffer_depth_ <= 0.0 || type_ != MediaPlaylistType::kLive)
if (time_shift_buffer_depth_ <= 0.0 ||
playlist_type_ != MediaPlaylistType::kLive) {
return;
}
DCHECK_GT(time_scale_, 0u);
// The start time of the latest segment is considered the current_play_time,

View File

@ -71,7 +71,7 @@ class MediaPlaylist {
/// necessarily the same as @a file_name.
/// @param group_id is the group ID for this playlist. This is the value of
/// GROUP-ID attribute for EXT-X-MEDIA.
MediaPlaylist(MediaPlaylistType type,
MediaPlaylist(MediaPlaylistType playlist_type,
double time_shift_buffer_depth,
const std::string& file_name,
const std::string& name,
@ -100,10 +100,13 @@ class MediaPlaylist {
/// @param file_name is the file name of the segment.
/// @param start_time is in terms of the timescale of the media.
/// @param duration is in terms of the timescale of the media.
/// @param start_byte_offset is the offset of where the subsegment starts.
/// This must be 0 if the whole segment is a subsegment.
/// @param size is size in bytes.
virtual void AddSegment(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t start_byte_offset,
uint64_t size);
/// All segments added after calling this method must be decryptable with
@ -167,7 +170,7 @@ class MediaPlaylist {
// |sequence_number_| by the number of segments removed.
void SlideWindow();
const MediaPlaylistType type_;
const MediaPlaylistType playlist_type_;
const double time_shift_buffer_depth_;
// Mainly for MasterPlaylist to use these values.
const std::string file_name_;
@ -186,6 +189,10 @@ class MediaPlaylist {
uint64_t max_bitrate_ = 0;
// Cache the previous calls AddSegment() end offset. This is used to construct
// SegmentInfoEntry.
uint64_t previous_segment_end_offset_ = 0;
// See SetTargetDuration() comments.
bool target_duration_set_ = false;
uint32_t target_duration_ = 0;

View File

@ -23,6 +23,7 @@ const char kDefaultPlaylistFileName[] = "default_playlist.m3u8";
const double kTimeShiftBufferDepth = 20;
const uint64_t kTimeScale = 90000;
const uint64_t kMBytes = 1000000;
const uint64_t kZeroByteOffset = 0;
MATCHER_P(MatchesString, expected_string, "") {
const std::string arg_string(static_cast<const char*>(arg));
@ -72,14 +73,35 @@ class MediaPlaylistTest : public ::testing::Test {
MediaInfo valid_video_media_info_;
};
class MediaPlaylistMultiSegmentTest : public MediaPlaylistTest {
protected:
MediaPlaylistMultiSegmentTest() : MediaPlaylistTest() {}
// This constructor is for Live and Event playlist tests.
MediaPlaylistMultiSegmentTest(MediaPlaylist::MediaPlaylistType type)
: MediaPlaylistTest(type) {}
void SetUp() override {
MediaPlaylistTest::SetUp();
// This is just set to be consistent with the multisegment format and used
// as a switch in MediaPlaylist.
// The template string doesn't really matter.
valid_video_media_info_.set_segment_template("file$Number$.ts");
}
};
class MediaPlaylistSingleSegmentTest : public MediaPlaylistTest {
protected:
MediaPlaylistSingleSegmentTest() : MediaPlaylistTest() {}
};
// Verify that SetMediaInfo() fails if timescale is not present.
TEST_F(MediaPlaylistTest, NoTimeScale) {
TEST_F(MediaPlaylistMultiSegmentTest, NoTimeScale) {
MediaInfo media_info;
EXPECT_FALSE(media_playlist_.SetMediaInfo(media_info));
}
// The current implementation only handles video and audio.
TEST_F(MediaPlaylistTest, NoAudioOrVideo) {
TEST_F(MediaPlaylistMultiSegmentTest, NoAudioOrVideo) {
MediaInfo media_info;
media_info.set_reference_time_scale(kTimeScale);
MediaInfo::TextInfo* text_info = media_info.mutable_text_info();
@ -87,7 +109,7 @@ TEST_F(MediaPlaylistTest, NoAudioOrVideo) {
EXPECT_FALSE(media_playlist_.SetMediaInfo(media_info));
}
TEST_F(MediaPlaylistTest, SetMediaInfo) {
TEST_F(MediaPlaylistMultiSegmentTest, SetMediaInfo) {
MediaInfo media_info;
media_info.set_reference_time_scale(kTimeScale);
MediaInfo::VideoInfo* video_info = media_info.mutable_video_info();
@ -97,20 +119,93 @@ TEST_F(MediaPlaylistTest, SetMediaInfo) {
}
// Verify that AddSegment works (not crash).
TEST_F(MediaPlaylistTest, AddSegment) {
TEST_F(MediaPlaylistMultiSegmentTest, AddSegment) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 900000, 0, kZeroByteOffset, 1000000);
}
TEST_F(MediaPlaylistSingleSegmentTest, InitRange) {
const std::string kExpectedOutput =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-TARGETDURATION:0\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-MAP:URI=\"file.mp4\",BYTERANGE=501@0\n"
"#EXT-X-ENDLIST\n";
valid_video_media_info_.set_media_file_name("file.mp4");
valid_video_media_info_.mutable_init_range()->set_begin(0);
valid_video_media_info_.mutable_init_range()->set_end(500);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
const char kMemoryFilePath[] = "memory://media.m3u8";
EXPECT_TRUE(media_playlist_.WriteToFile(kMemoryFilePath));
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
TEST_F(MediaPlaylistSingleSegmentTest, InitRangeWithOffset) {
const std::string kExpectedOutput =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-TARGETDURATION:0\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-MAP:URI=\"file.mp4\",BYTERANGE=485@16\n"
"#EXT-X-ENDLIST\n";
valid_video_media_info_.set_media_file_name("file.mp4");
valid_video_media_info_.mutable_init_range()->set_begin(16);
valid_video_media_info_.mutable_init_range()->set_end(500);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
const char kMemoryFilePath[] = "memory://media.m3u8";
EXPECT_TRUE(media_playlist_.WriteToFile(kMemoryFilePath));
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
// Closest to the normal use case where there is an init range and then
// subsegment ranges. There is index range between the subsegment and init range.
TEST_F(MediaPlaylistSingleSegmentTest, AddSegmentByteRange) {
const std::string kExpectedOutput =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-TARGETDURATION:10\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-MAP:URI=\"file.mp4\",BYTERANGE=501@0\n"
"#EXTINF:10.000,\n"
"#EXT-X-BYTERANGE:1000000@1000\n"
"file.mp4\n"
"#EXTINF:10.000,\n"
"#EXT-X-BYTERANGE:2000000\n"
"file.mp4\n"
"#EXT-X-ENDLIST\n";
valid_video_media_info_.set_media_file_name("file.mp4");
valid_video_media_info_.mutable_init_range()->set_begin(0);
valid_video_media_info_.mutable_init_range()->set_end(500);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file.mp4", 0, 10 * kTimeScale, 1000,
1 * kMBytes);
media_playlist_.AddSegment("file.mp4", 10 * kTimeScale, 10 * kTimeScale,
1001000, 2 * kMBytes);
const char kMemoryFilePath[] = "memory://media.m3u8";
EXPECT_TRUE(media_playlist_.WriteToFile(kMemoryFilePath));
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
// Verify that AddEncryptionInfo works (not crash).
TEST_F(MediaPlaylistTest, AddEncryptionInfo) {
TEST_F(MediaPlaylistMultiSegmentTest, AddEncryptionInfo) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "0xabcedf", "",
"");
}
TEST_F(MediaPlaylistTest, WriteToFile) {
TEST_F(MediaPlaylistMultiSegmentTest, WriteToFile) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
const char kExpectedOutput[] =
"#EXTM3U\n"
@ -127,7 +222,7 @@ TEST_F(MediaPlaylistTest, WriteToFile) {
}
// If bitrate (bandwidth) is not set in the MediaInfo, use it.
TEST_F(MediaPlaylistTest, UseBitrateInMediaInfo) {
TEST_F(MediaPlaylistMultiSegmentTest, UseBitrateInMediaInfo) {
valid_video_media_info_.set_bandwidth(8191);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
EXPECT_EQ(8191u, media_playlist_.Bitrate());
@ -135,36 +230,57 @@ TEST_F(MediaPlaylistTest, UseBitrateInMediaInfo) {
// If bitrate (bandwidth) is not set in the MediaInfo, then calculate from the
// segments.
TEST_F(MediaPlaylistTest, GetBitrateFromSegments) {
TEST_F(MediaPlaylistMultiSegmentTest, GetBitrateFromSegments) {
valid_video_media_info_.clear_bandwidth();
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
// Max bitrate is 2000Kb/s.
EXPECT_EQ(2000000u, media_playlist_.Bitrate());
}
TEST_F(MediaPlaylistTest, GetLongestSegmentDuration) {
TEST_F(MediaPlaylistMultiSegmentTest, GetLongestSegmentDuration) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
media_playlist_.AddSegment("file3.ts", 40 * kTimeScale, 14 * kTimeScale,
3 * kMBytes);
kZeroByteOffset, 3 * kMBytes);
EXPECT_NEAR(30.0, media_playlist_.GetLongestSegmentDuration(), 0.01);
}
TEST_F(MediaPlaylistTest, WriteToFileWithSegments) {
TEST_F(MediaPlaylistMultiSegmentTest, SetTargetDuration) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.SetTargetDuration(20);
const std::string kExpectedOutput =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-TARGETDURATION:20\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-ENDLIST\n";
const char kMemoryFilePath[] = "memory://media.m3u8";
EXPECT_TRUE(media_playlist_.WriteToFile(kMemoryFilePath));
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithSegments) {
valid_video_media_info_.set_reference_time_scale(90000);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -183,15 +299,17 @@ TEST_F(MediaPlaylistTest, WriteToFileWithSegments) {
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfo) {
TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithEncryptionInfo) {
valid_video_media_info_.set_reference_time_scale(90000);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "0x12345678",
"com.widevine", "1/2/4");
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -213,15 +331,17 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfo) {
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfoEmptyIv) {
TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithEncryptionInfoEmptyIv) {
valid_video_media_info_.set_reference_time_scale(90000);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "", "com.widevine",
"");
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -243,20 +363,23 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfoEmptyIv) {
}
// Verify that EXT-X-DISCONTINUITY is inserted before EXT-X-KEY.
TEST_F(MediaPlaylistTest, WriteToFileWithClearLead) {
TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithClearLead) {
valid_video_media_info_.set_reference_time_scale(90000);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "0x12345678",
"com.widevine", "1/2/4");
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version test\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXTINF:10.000,\n"
@ -274,7 +397,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithClearLead) {
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
TEST_F(MediaPlaylistTest, GetLanguage) {
TEST_F(MediaPlaylistMultiSegmentTest, GetLanguage) {
MediaInfo media_info;
media_info.set_reference_time_scale(kTimeScale);
@ -292,13 +415,15 @@ TEST_F(MediaPlaylistTest, GetLanguage) {
EXPECT_EQ("apa", media_playlist_.GetLanguage()); // no short form exists
}
TEST_F(MediaPlaylistTest, InitSegment) {
TEST_F(MediaPlaylistMultiSegmentTest, InitSegment) {
valid_video_media_info_.set_reference_time_scale(90000);
valid_video_media_info_.set_init_segment_name("init_segment.mp4");
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.mp4", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.mp4", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.mp4", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
@ -319,16 +444,18 @@ TEST_F(MediaPlaylistTest, InitSegment) {
}
// Verify that kSampleAesCenc is handled correctly.
TEST_F(MediaPlaylistTest, SampleAesCenc) {
TEST_F(MediaPlaylistMultiSegmentTest, SampleAesCenc) {
valid_video_media_info_.set_reference_time_scale(90000);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(
MediaPlaylist::EncryptionMethod::kSampleAesCenc, "http://example.com", "",
"0x12345678", "com.widevine", "1/2/4");
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -350,8 +477,7 @@ TEST_F(MediaPlaylistTest, SampleAesCenc) {
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
// Verify that multiple encryption info can be set.
TEST_F(MediaPlaylistTest, MultipleEncryptionInfo) {
TEST_F(MediaPlaylistMultiSegmentTest, MultipleEncryptionInfo) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
@ -362,9 +488,10 @@ TEST_F(MediaPlaylistTest, MultipleEncryptionInfo) {
MediaPlaylist::EncryptionMethod::kSampleAes, "http://mydomain.com",
"0xfedc", "0x12345678", "com.widevine.someother", "1");
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
5 * kMBytes);
kZeroByteOffset, 5 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -390,18 +517,20 @@ TEST_F(MediaPlaylistTest, MultipleEncryptionInfo) {
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
class LiveMediaPlaylistTest : public MediaPlaylistTest {
class LiveMediaPlaylistTest : public MediaPlaylistMultiSegmentTest {
protected:
LiveMediaPlaylistTest()
: MediaPlaylistTest(MediaPlaylist::MediaPlaylistType::kLive) {}
: MediaPlaylistMultiSegmentTest(MediaPlaylist::MediaPlaylistType::kLive) {
}
};
TEST_F(LiveMediaPlaylistTest, Basic) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -421,11 +550,12 @@ TEST_F(LiveMediaPlaylistTest, Basic) {
TEST_F(LiveMediaPlaylistTest, TimeShifted) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -453,11 +583,12 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfo) {
MediaPlaylist::EncryptionMethod::kSampleAes, "http://mydomain.com",
"0xfedc", "0x12345678", "com.widevine.someother", "1");
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -485,7 +616,8 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfo) {
TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "0x12345678",
@ -495,7 +627,7 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
"0xfedc", "0x12345678", "com.widevine.someother", "1");
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "0x22345678",
@ -505,7 +637,7 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
"0xfedd", "0x22345678", "com.widevine.someother", "1");
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "0x32345678",
@ -515,7 +647,7 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
"0xfede", "0x32345678", "com.widevine.someother", "1");
media_playlist_.AddSegment("file4.ts", 50 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
@ -548,18 +680,20 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
}
class EventMediaPlaylistTest : public MediaPlaylistTest {
class EventMediaPlaylistTest : public MediaPlaylistMultiSegmentTest {
protected:
EventMediaPlaylistTest()
: MediaPlaylistTest(MediaPlaylist::MediaPlaylistType::kEvent) {}
: MediaPlaylistMultiSegmentTest(
MediaPlaylist::MediaPlaylistType::kEvent) {}
};
TEST_F(EventMediaPlaylistTest, Basic) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kMBytes);
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
2 * kMBytes);
kZeroByteOffset, 2 * kMBytes);
const char kExpectedOutput[] =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"

View File

@ -25,10 +25,11 @@ class MockMediaPlaylist : public MediaPlaylist {
~MockMediaPlaylist() override;
MOCK_METHOD1(SetMediaInfo, bool(const MediaInfo& media_info));
MOCK_METHOD4(AddSegment,
MOCK_METHOD5(AddSegment,
void(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t start_byte_offset,
uint64_t size));
MOCK_METHOD0(RemoveOldestSegment, void());
MOCK_METHOD6(AddEncryptionInfo,

View File

@ -299,6 +299,7 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
const std::string& segment_name,
uint64_t start_time,
uint64_t duration,
uint64_t start_byte_offset,
uint64_t size) {
base::AutoLock auto_lock(lock_);
auto stream_iterator = stream_map_.find(stream_id);
@ -311,7 +312,7 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
auto& media_playlist = stream_iterator->second->media_playlist;
media_playlist->AddSegment(prefix_ + relative_segment_name, start_time,
duration, size);
duration, start_byte_offset, size);
// Update target duration.
uint32_t longest_segment_duration =

View File

@ -67,6 +67,7 @@ class SimpleHlsNotifier : public HlsNotifier {
const std::string& segment_name,
uint64_t start_time,
uint64_t duration,
uint64_t start_byte_offset,
uint64_t size) override;
bool NotifyEncryptionUpdate(
uint32_t stream_id,

View File

@ -7,6 +7,8 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include "packager/base/base64.h"
#include "packager/base/files/file_path.h"
#include "packager/hls/base/mock_media_playlist.h"
@ -77,47 +79,42 @@ const char kSampleAesProtectionScheme[] = "cbca";
} // namespace
// : notifier_(profile,
// kTestTimeShiftBufferDepth,
// kTestPrefix,
// kAnyOutputDir,
// kMasterPlaylistName),
class SimpleHlsNotifierTest : public ::testing::Test {
protected:
SimpleHlsNotifierTest()
: SimpleHlsNotifierTest(HlsProfile::kOnDemandProfile) {}
SimpleHlsNotifierTest(HlsProfile profile)
: notifier_(profile,
kTestTimeShiftBufferDepth,
kTestPrefix,
kAnyOutputDir,
kMasterPlaylistName),
widevine_system_id_(
: widevine_system_id_(
media::kWidevineSystemId,
media::kWidevineSystemId + arraysize(media::kWidevineSystemId)),
common_system_id_(
media::kCommonSystemId,
media::kCommonSystemId + arraysize(media::kCommonSystemId)) {}
void InjectMediaPlaylistFactory(
std::unique_ptr<MediaPlaylistFactory> factory) {
notifier_.media_playlist_factory_ = std::move(factory);
}
void InjectMediaPlaylistFactory(std::unique_ptr<MediaPlaylistFactory> factory,
SimpleHlsNotifier* notifier) {
notifier->media_playlist_factory_ = std::move(factory);
}
void InjectMasterPlaylist(std::unique_ptr<MasterPlaylist> playlist) {
notifier_.master_playlist_ = std::move(playlist);
}
void InjectMasterPlaylist(std::unique_ptr<MasterPlaylist> playlist,
SimpleHlsNotifier* notifier) {
notifier->master_playlist_ = std::move(playlist);
}
size_t NumRegisteredMediaPlaylists() { return notifier_.stream_map_.size(); }
size_t NumRegisteredMediaPlaylists(const SimpleHlsNotifier& notifier) {
return notifier.stream_map_.size();
}
uint32_t SetupStream(const std::string& protection_scheme,
MockMediaPlaylist* mock_media_playlist) {
MockMediaPlaylist* mock_media_playlist,
SimpleHlsNotifier* notifier) {
MediaInfo media_info;
media_info.mutable_protected_content()->set_protection_scheme(
protection_scheme);
@ -133,22 +130,24 @@ class SimpleHlsNotifierTest : public ::testing::Test {
EXPECT_CALL(*factory, CreateMock(_, _, _, _, _))
.WillOnce(Return(mock_media_playlist));
InjectMasterPlaylist(std::move(mock_master_playlist));
InjectMediaPlaylistFactory(std::move(factory));
EXPECT_TRUE(notifier_.Init());
InjectMasterPlaylist(std::move(mock_master_playlist), notifier);
InjectMediaPlaylistFactory(std::move(factory), notifier);
EXPECT_TRUE(notifier->Init());
uint32_t stream_id;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
EXPECT_TRUE(notifier->NotifyNewStream(media_info, "playlist.m3u8", "name",
"groupid", &stream_id));
return stream_id;
}
SimpleHlsNotifier notifier_;
const std::vector<uint8_t> widevine_system_id_;
const std::vector<uint8_t> common_system_id_;
};
TEST_F(SimpleHlsNotifierTest, Init) {
EXPECT_TRUE(notifier_.Init());
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
EXPECT_TRUE(notifier.Init());
}
// Verify that relative paths can be handled.
@ -165,31 +164,36 @@ TEST_F(SimpleHlsNotifierTest, RebaseSegmentTemplateRelative) {
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
EXPECT_CALL(*mock_master_playlist, AddMediaPlaylist(mock_media_playlist));
EXPECT_CALL(
*mock_media_playlist,
SetMediaInfo(SegmentTemplateEq("path/to/media$Number$.ts")))
EXPECT_CALL(*mock_media_playlist,
SetMediaInfo(SegmentTemplateEq("path/to/media$Number$.ts")))
.WillOnce(Return(true));
// Verify that the common prefix is stripped for AddSegment().
EXPECT_CALL(*mock_media_playlist,
AddSegment("http://testprefix.com/path/to/media1.ts", _, _, _));
EXPECT_CALL(
*mock_media_playlist,
AddSegment("http://testprefix.com/path/to/media1.ts", _, _, _, _));
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
StrEq("video_playlist.m3u8"), StrEq("name"),
StrEq("groupid")))
.WillOnce(Return(mock_media_playlist));
InjectMasterPlaylist(std::move(mock_master_playlist));
InjectMediaPlaylistFactory(std::move(factory));
EXPECT_TRUE(notifier_.Init());
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
EXPECT_TRUE(notifier.Init());
MediaInfo media_info;
media_info.set_segment_template("anything/path/to/media$Number$.ts");
uint32_t stream_id;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "video_playlist.m3u8",
"name", "groupid", &stream_id));
EXPECT_TRUE(notifier.NotifyNewStream(media_info, "video_playlist.m3u8",
"name", "groupid", &stream_id));
EXPECT_TRUE(
notifier_.NotifyNewSegment(stream_id, "anything/path/to/media1.ts",
kAnyStartTime, kAnyDuration, kAnySize));
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id, "anything/path/to/media1.ts",
kAnyStartTime, kAnyDuration, 0,
kAnySize));
}
// Verify that when segment template's prefix and output dir match, then the
@ -197,7 +201,6 @@ TEST_F(SimpleHlsNotifierTest, RebaseSegmentTemplateRelative) {
TEST_F(SimpleHlsNotifierTest,
RebaseAbsoluteSegmentTemplatePrefixAndOutputDirMatch) {
const char kAbsoluteOutputDir[] = "/tmp/something/";
// Require a separate instance to set kAbsoluteOutputDir.
SimpleHlsNotifier test_notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAbsoluteOutputDir, kMasterPlaylistName);
@ -218,7 +221,7 @@ TEST_F(SimpleHlsNotifierTest,
// Verify that the output_dir is stripped and then kTestPrefix is prepended.
EXPECT_CALL(*mock_media_playlist,
AddSegment("http://testprefix.com/media1.ts", _, _, _));
AddSegment("http://testprefix.com/media1.ts", _, _, _, _));
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
StrEq("video_playlist.m3u8"), StrEq("name"),
StrEq("groupid")))
@ -235,7 +238,7 @@ TEST_F(SimpleHlsNotifierTest,
EXPECT_TRUE(
test_notifier.NotifyNewSegment(stream_id, "/tmp/something/media1.ts",
kAnyStartTime, kAnyDuration, kAnySize));
kAnyStartTime, kAnyDuration, 0, kAnySize));
}
// If the paths don't match at all and they are both absolute and completely
@ -263,7 +266,7 @@ TEST_F(SimpleHlsNotifierTest,
.WillOnce(Return(true));
EXPECT_CALL(*mock_media_playlist,
AddSegment("http://testprefix.com//var/somewhereelse/media1.ts",
_, _, _));
_, _, _, _));
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
StrEq("video_playlist.m3u8"), StrEq("name"),
StrEq("groupid")))
@ -279,18 +282,21 @@ TEST_F(SimpleHlsNotifierTest,
"name", "groupid", &stream_id));
EXPECT_TRUE(
test_notifier.NotifyNewSegment(stream_id, "/var/somewhereelse/media1.ts",
kAnyStartTime, kAnyDuration, kAnySize));
kAnyStartTime, kAnyDuration, 0, kAnySize));
}
TEST_F(SimpleHlsNotifierTest, Flush) {
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
new MockMasterPlaylist());
EXPECT_CALL(*mock_master_playlist,
WriteMasterPlaylist(StrEq(kTestPrefix), StrEq(kAnyOutputDir)))
.WillOnce(Return(true));
InjectMasterPlaylist(std::move(mock_master_playlist));
EXPECT_TRUE(notifier_.Init());
EXPECT_TRUE(notifier_.Flush());
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
EXPECT_TRUE(notifier.Init());
EXPECT_TRUE(notifier.Flush());
}
TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
@ -310,14 +316,18 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
StrEq("groupid")))
.WillOnce(Return(mock_media_playlist));
InjectMasterPlaylist(std::move(mock_master_playlist));
InjectMediaPlaylistFactory(std::move(factory));
EXPECT_TRUE(notifier_.Init());
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
EXPECT_TRUE(notifier.Init());
MediaInfo media_info;
uint32_t stream_id;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "video_playlist.m3u8",
"name", "groupid", &stream_id));
EXPECT_EQ(1u, NumRegisteredMediaPlaylists());
EXPECT_TRUE(notifier.NotifyNewStream(media_info, "video_playlist.m3u8",
"name", "groupid", &stream_id));
EXPECT_EQ(1u, NumRegisteredMediaPlaylists(notifier));
}
TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
@ -343,24 +353,27 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
const std::string segment_name = "segmentname";
EXPECT_CALL(*mock_media_playlist,
AddSegment(StrEq(kTestPrefix + segment_name), kStartTime,
kDuration, kSize));
kDuration, 203, kSize));
const double kLongestSegmentDuration = 11.3;
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
EXPECT_CALL(*mock_media_playlist, GetLongestSegmentDuration())
.WillOnce(Return(kLongestSegmentDuration));
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
InjectMasterPlaylist(std::move(mock_master_playlist));
InjectMediaPlaylistFactory(std::move(factory));
EXPECT_TRUE(notifier_.Init());
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
EXPECT_TRUE(notifier.Init());
MediaInfo media_info;
uint32_t stream_id;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
"groupid", &stream_id));
EXPECT_TRUE(notifier.NotifyNewStream(media_info, "playlist.m3u8", "name",
"groupid", &stream_id));
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id, segment_name, kStartTime,
kDuration, kSize));
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id, segment_name, kStartTime,
kDuration, 203, kSize));
Mock::VerifyAndClearExpectations(mock_master_playlist_ptr);
Mock::VerifyAndClearExpectations(mock_media_playlist);
@ -376,20 +389,26 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
.Append(base::FilePath::FromUTF8Unsafe("playlist.m3u8"))
.AsUTF8Unsafe())))
.WillOnce(Return(true));
EXPECT_TRUE(notifier_.Flush());
EXPECT_TRUE(notifier.Flush());
}
TEST_F(SimpleHlsNotifierTest, NotifyNewSegmentWithoutStreamsRegistered) {
EXPECT_TRUE(notifier_.Init());
EXPECT_FALSE(notifier_.NotifyNewSegment(1u, "anything", 0u, 0u, 0u));
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
EXPECT_TRUE(notifier.Init());
EXPECT_FALSE(notifier.NotifyNewSegment(1u, "anything", 0u, 0u, 0u, 0u));
}
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
const std::vector<uint8_t> iv(16, 0x45);
@ -397,10 +416,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
widevine_pssh_data.set_provider("someprovider");
widevine_pssh_data.set_content_id("contentid");
const uint8_t kAnyKeyId[] = {
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
};
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
@ -430,21 +447,18 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(_,
StrEq("data:text/plain;base64," + expected_json_base64),
StrEq(""),
StrEq("0x45454545454545454545454545454545"),
StrEq("com.widevine"), _));
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_,
StrEq("data:text/plain;base64," + expected_pssh_base64),
_, StrEq("data:text/plain;base64," + expected_json_base64),
StrEq(""), StrEq("0x45454545454545454545454545454545"),
StrEq("com.widevine"), _));
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
StrEq("0x11223344112233441122334411223344"),
StrEq("0x45454545454545454545454545454545"),
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id, any_key_id, widevine_system_id_, iv, pssh_box));
}
@ -453,8 +467,11 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
const std::vector<uint8_t> iv(16, 0x45);
@ -484,28 +501,22 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(_,
StrEq("data:text/plain;base64," + expected_json_base64),
StrEq(""),
StrEq("0x45454545454545454545454545454545"),
StrEq("com.widevine"), _));
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(
_,
StrEq("data:text/plain;base64," + expected_pssh_base64),
StrEq("0x11223344112233441122334411223344"),
StrEq("0x45454545454545454545454545454545"),
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_, StrEq("data:text/plain;base64," + expected_json_base64),
StrEq(""), StrEq("0x45454545454545454545454545454545"),
StrEq("com.widevine"), _));
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
StrEq("0x11223344112233441122334411223344"),
StrEq("0x45454545454545454545454545454545"),
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
const uint8_t kAnyKeyId[] = {
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
};
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id,
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
widevine_system_id_, iv, pssh_box));
@ -515,8 +526,11 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
const std::vector<uint8_t> key_id(16, 0x23);
const std::vector<uint8_t> iv(16, 0x45);
@ -526,14 +540,12 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
base::Base64Encode(std::string(key_id.begin(), key_id.end()),
&expected_key_uri_base64);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(
_,
StrEq("data:text/plain;base64," + expected_key_uri_base64),
StrEq(""),
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_, StrEq("data:text/plain;base64," + expected_key_uri_base64),
StrEq(""), StrEq("0x45454545454545454545454545454545"),
StrEq("identity"), _));
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
}
@ -544,24 +556,23 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
std::vector<uint8_t> iv(16, 0x45);
media::WidevinePsshData widevine_pssh_data;
widevine_pssh_data.set_provider("someprovider");
const uint8_t kFirstKeyId[] = {
0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
};
const uint8_t kSecondKeyId[] = {
0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
};
std::vector<uint8_t> first_keyid(kFirstKeyId,
kFirstKeyId + arraysize(kFirstKeyId));
@ -597,23 +608,20 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(_,
StrEq("data:text/plain;base64," + expected_json_base64),
StrEq(""),
StrEq("0x45454545454545454545454545454545"),
StrEq("com.widevine"), _));
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_, StrEq("data:text/plain;base64," + expected_json_base64),
StrEq(""), StrEq("0x45454545454545454545454545454545"),
StrEq("com.widevine"), _));
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_,
StrEq("data:text/plain;base64," + expected_pssh_base64),
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
StrEq("0x22222222222222222222222222222222"),
StrEq("0x45454545454545454545454545454545"),
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id,
// Use the second key id here so that it will be thre first one in the
// key_ids array in the JSON.
@ -626,8 +634,11 @@ TEST_F(SimpleHlsNotifierTest, EncryptionScheme) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
const uint32_t stream_id =
SetupStream(kCencProtectionScheme, mock_media_playlist);
SetupStream(kCencProtectionScheme, mock_media_playlist, &notifier);
const std::vector<uint8_t> key_id(16, 0x23);
const std::vector<uint8_t> iv(16, 0x45);
@ -641,10 +652,9 @@ TEST_F(SimpleHlsNotifierTest, EncryptionScheme) {
*mock_media_playlist,
AddEncryptionInfo(
MediaPlaylist::EncryptionMethod::kSampleAesCenc,
StrEq("data:text/plain;base64," + expected_key_uri_base64),
StrEq(""),
StrEq("data:text/plain;base64," + expected_key_uri_base64), StrEq(""),
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
}
@ -653,8 +663,11 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
const uint32_t stream_id =
SetupStream(kCencProtectionScheme, mock_media_playlist);
SetupStream(kCencProtectionScheme, mock_media_playlist, &notifier);
const std::vector<uint8_t> iv(16, 0x45);
@ -662,10 +675,8 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
widevine_pssh_data.set_provider("someprovider");
widevine_pssh_data.set_content_id("contentid");
const uint8_t kAnyKeyId[] = {
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
};
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
@ -689,12 +700,11 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo(
_,
StrEq("data:text/plain;base64," + expected_pssh_base64),
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
StrEq("0x11223344112233441122334411223344"),
StrEq("0x45454545454545454545454545454545"),
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id, any_key_id, widevine_system_id_, iv, pssh_box));
}
@ -702,17 +712,18 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
media::WidevinePsshData widevine_pssh_data;
widevine_pssh_data.set_provider("someprovider");
widevine_pssh_data.set_content_id("contentid");
const uint8_t kAnyKeyId[] = {
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
};
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
@ -758,7 +769,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
LOG(INFO) << base_64_encoded_pssh;
std::vector<uint8_t> empty_iv;
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id,
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
widevine_system_id_, empty_iv, pssh_info.CreateBox()));
@ -769,9 +780,12 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
std::vector<uint8_t> iv;
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> key_id;
EXPECT_TRUE(notifier_.Init());
EXPECT_FALSE(notifier_.NotifyEncryptionUpdate(1238u, key_id, system_id, iv,
pssh_data));
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
EXPECT_TRUE(notifier.Init());
EXPECT_FALSE(
notifier.NotifyEncryptionUpdate(1238u, key_id, system_id, iv, pssh_data));
}
class LiveOrEventSimpleHlsNotifierTest
@ -818,7 +832,7 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegment) {
const std::string segment_name = "segmentname";
EXPECT_CALL(*mock_media_playlist,
AddSegment(StrEq(kTestPrefix + segment_name), kStartTime,
kDuration, kSize));
kDuration, _, kSize));
const double kLongestSegmentDuration = 11.3;
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
@ -837,16 +851,19 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegment) {
.AsUTF8Unsafe())))
.WillOnce(Return(true));
InjectMasterPlaylist(std::move(mock_master_playlist));
InjectMediaPlaylistFactory(std::move(factory));
EXPECT_TRUE(notifier_.Init());
SimpleHlsNotifier notifier(GetParam(),
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
EXPECT_TRUE(notifier.Init());
MediaInfo media_info;
uint32_t stream_id;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
"groupid", &stream_id));
EXPECT_TRUE(notifier.NotifyNewStream(media_info, "playlist.m3u8", "name",
"groupid", &stream_id));
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id, segment_name, kStartTime,
kDuration, kSize));
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id, segment_name, kStartTime,
kDuration, 0, kSize));
}
TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
@ -880,20 +897,23 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
*mock_master_playlist,
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist2)));
SimpleHlsNotifier notifier(GetParam(),
kTestTimeShiftBufferDepth, kTestPrefix,
kAnyOutputDir, kMasterPlaylistName);
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
InjectMasterPlaylist(std::move(mock_master_playlist));
InjectMediaPlaylistFactory(std::move(factory));
EXPECT_TRUE(notifier_.Init());
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
EXPECT_TRUE(notifier.Init());
MediaInfo media_info;
uint32_t stream_id1;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist1.m3u8", "name",
EXPECT_TRUE(notifier.NotifyNewStream(media_info, "playlist1.m3u8", "name",
"groupid", &stream_id1));
uint32_t stream_id2;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist2.m3u8", "name",
EXPECT_TRUE(notifier.NotifyNewStream(media_info, "playlist2.m3u8", "name",
"groupid", &stream_id2));
EXPECT_CALL(*mock_media_playlist1, AddSegment(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_media_playlist1, AddSegment(_, _, _, _, _)).Times(1);
const double kLongestSegmentDuration = 11.3;
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
EXPECT_CALL(*mock_media_playlist1, GetLongestSegmentDuration())
@ -918,10 +938,10 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
.AsUTF8Unsafe())))
.WillOnce(Return(true));
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id1, "segment_name", kStartTime,
kDuration, kSize));
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id1, "segment_name", kStartTime,
kDuration, 0, kSize));
EXPECT_CALL(*mock_media_playlist2, AddSegment(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_media_playlist2, AddSegment(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mock_media_playlist2, GetLongestSegmentDuration())
.WillOnce(Return(kLongestSegmentDuration));
EXPECT_CALL(*mock_master_playlist_ptr, WriteMasterPlaylist(_, _))
@ -933,8 +953,8 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
.AsUTF8Unsafe())))
.WillOnce(Return(true));
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id2, "segment_name", kStartTime,
kDuration, kSize));
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id2, "segment_name", kStartTime,
kDuration, 0, kSize));
}
INSTANTIATE_TEST_CASE_P(PlaylistTypes,

View File

@ -94,6 +94,11 @@ void HlsNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options,
next_key_system_infos_, &media_info);
}
media_info_ = media_info;
if (!media_info_.has_segment_template()) {
return;
}
const bool result = hls_notifier_->NotifyNewStream(
media_info, playlist_name_, ext_x_media_name_, ext_x_media_group_id_,
&stream_id_);
@ -117,14 +122,73 @@ void HlsNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
// Don't flush the notifier here. Flushing here would write all the playlists
// before all Media Playlists are read. Which could cause problems
// setting the correct EXT-X-TARGETDURATION.
if (media_info_.has_segment_template()) {
return;
}
if (media_ranges.init_range) {
shaka::Range* init_range = media_info_.mutable_init_range();
init_range->set_begin(media_ranges.init_range.value().start);
init_range->set_end(media_ranges.init_range.value().end);
}
if (media_ranges.index_range) {
shaka::Range* index_range = media_info_.mutable_index_range();
index_range->set_begin(media_ranges.index_range.value().start);
index_range->set_end(media_ranges.index_range.value().end);
}
// TODO(rkuroiwa): Make this a method. This is the same as OnMediaStart().
const bool result = hls_notifier_->NotifyNewStream(
media_info_, playlist_name_, ext_x_media_name_, ext_x_media_group_id_,
&stream_id_);
if (!result) {
LOG(WARNING) << "Failed to notify new stream for VOD.";
return;
}
// TODO(rkuroiwa); Keep track of which (sub)segments are encrypted so that the
// notification is sent right before the enecrypted (sub)segments.
media_started_ = true;
if (must_notify_encryption_start_) {
OnEncryptionStart();
}
if (!media_ranges.subsegment_ranges.empty()) {
const std::vector<Range>& subsegment_ranges =
media_ranges.subsegment_ranges;
size_t num_subsegments = subsegment_ranges.size();
if (segment_infos_.size() != num_subsegments) {
LOG(WARNING) << "Number of subsegment ranges (" << num_subsegments
<< ") does not match the number of subsegments notified to "
"OnNewSegment() ("
<< segment_infos_.size() << ").";
num_subsegments = std::min(segment_infos_.size(), num_subsegments);
}
for (size_t i = 0; i < num_subsegments; ++i) {
const Range& range = subsegment_ranges[i];
const SegmentInfo& subsegment_info = segment_infos_[i];
hls_notifier_->NotifyNewSegment(
stream_id_, media_info_.media_file_name(), subsegment_info.start_time,
subsegment_info.duration, range.start, range.end + 1 - range.start);
}
}
}
void HlsNotifyMuxerListener::OnNewSegment(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t segment_file_size) {
if (!media_info_.has_segment_template()) {
SegmentInfo info;
info.duration = duration;
info.start_time = start_time;
segment_infos_.push_back(info);
return;
}
// For multisegment, it always starts from the beginning of the file.
const size_t kStartingByteOffset = 0u;
const bool result = hls_notifier_->NotifyNewSegment(
stream_id_, file_name, start_time, duration, segment_file_size);
stream_id_, file_name, start_time, duration, kStartingByteOffset,
segment_file_size);
LOG_IF(WARNING, !result) << "Failed to add new segment.";
}

View File

@ -11,6 +11,8 @@
#include "packager/base/macros.h"
#include "packager/media/event/muxer_listener.h"
#include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/segment_info.h"
namespace shaka {
@ -75,6 +77,11 @@ class HlsNotifyMuxerListener : public MuxerListener {
std::vector<ProtectionSystemSpecificInfo> next_key_system_infos_;
FourCC protection_scheme_ = FOURCC_NULL;
// MediaInfo passed to Notifier::OnNewStream(). Mainly for single segment
// playlists.
MediaInfo media_info_;
std::vector<SegmentInfo> segment_infos_;
DISALLOW_COPY_AND_ASSIGN(HlsNotifyMuxerListener);
};

View File

@ -33,11 +33,12 @@ class MockHlsNotifier : public hls::HlsNotifier {
const std::string& name,
const std::string& group_id,
uint32_t* stream_id));
MOCK_METHOD5(NotifyNewSegment,
MOCK_METHOD6(NotifyNewSegment,
bool(uint32_t stream_id,
const std::string& segment_name,
uint64_t start_time,
uint64_t duration,
uint64_t start_byte_offset,
uint64_t size));
MOCK_METHOD5(
NotifyEncryptionUpdate,
@ -125,6 +126,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) {
.WillOnce(Return(true));
MuxerOptions muxer_options;
muxer_options.segment_template = "$Number$.mp4";
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
MuxerListener::kContainerMpeg2ts);
}
@ -156,6 +158,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) {
std::shared_ptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
MuxerOptions muxer_options;
muxer_options.segment_template = "$Number$.mp4";
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
@ -196,6 +199,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStartBeforeMediaStart) {
std::shared_ptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
MuxerOptions muxer_options;
muxer_options.segment_template = "$Number$.mp4";
// It doesn't really matter when this is called, could be called right away in
// OnEncryptionStart() if that is possible. Just matters that it is called by
@ -235,6 +239,7 @@ TEST_F(HlsNotifyMuxerListenerTest, NoEncryptionUpdateIfNotifyNewStreamFails) {
std::shared_ptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
MuxerOptions muxer_options;
muxer_options.segment_template = "$Number$.mp4";
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
@ -250,6 +255,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReady) {
std::shared_ptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
MuxerOptions muxer_options;
muxer_options.segment_template = "$Number$.mp4";
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
MuxerListener::kContainerMpeg2ts);
@ -317,15 +323,120 @@ TEST_F(HlsNotifyMuxerListenerTest, OnMediaEnd) {
}
TEST_F(HlsNotifyMuxerListenerTest, OnNewSegment) {
ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
.WillByDefault(Return(true));
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
std::shared_ptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
MuxerOptions muxer_options;
muxer_options.segment_template = "$Number$.mp4";
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
MuxerListener::kContainerMpeg2ts);
const uint64_t kStartTime = 19283;
const uint64_t kDuration = 98028;
const uint64_t kFileSize = 756739;
EXPECT_CALL(mock_notifier_,
NotifyNewSegment(_, StrEq("new_segment_name10.ts"), kStartTime,
kDuration, kFileSize));
kDuration, _, kFileSize));
listener_.OnNewSegment("new_segment_name10.ts", kStartTime, kDuration,
kFileSize);
}
// Verify that the notifier is called for every segment in OnMediaEnd if
// segment_template is not set.
TEST_F(HlsNotifyMuxerListenerTest, NoSegmentTemplateOnMediaEnd) {
ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
.WillByDefault(Return(true));
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
std::shared_ptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
MuxerOptions muxer_options;
muxer_options.output_file_name = "filename.mp4";
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
MuxerListener::kContainerMpeg2ts);
const uint64_t kSegmentStartOffset = 10000;
const uint64_t kStartTime = 19283;
const uint64_t kDuration = 98028;
const uint64_t kFileSize = 756739;
listener_.OnNewSegment("filename.mp4", kStartTime, kDuration,
kFileSize);
MuxerListener::MediaRanges ranges;
Range init_range;
init_range.start = 0;
init_range.end = 100;
Range index_range;
index_range.start = 101;
index_range.end = 200;
// Only one segment range for this test.
std::vector<Range> segment_ranges;
Range segment_range;
segment_range.start = kSegmentStartOffset;
segment_range.end = kSegmentStartOffset + kFileSize - 1;
segment_ranges.push_back(segment_range);
ranges.init_range = init_range;
ranges.index_range = index_range;
ranges.subsegment_ranges = segment_ranges;
EXPECT_CALL(mock_notifier_,
NotifyNewSegment(_, StrEq("filename.mp4"), kStartTime,
kDuration, kSegmentStartOffset, kFileSize));
listener_.OnMediaEnd(ranges, 200000, 98234328);
}
// Verify that when there is a mismatch in the number of calls to
// NotifyNewSegment and the number of segment ranges, it uses the min of the
// two.
TEST_F(HlsNotifyMuxerListenerTest,
NoSegmentTemplateOnMediaEndSubsegmentSizeMismatch) {
ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
.WillByDefault(Return(true));
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
std::shared_ptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
MuxerOptions muxer_options;
muxer_options.output_file_name = "filename.mp4";
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
MuxerListener::kContainerMpeg2ts);
const uint64_t kSegmentStartOffset = 10000;
const uint64_t kStartTime = 19283;
const uint64_t kDuration = 98028;
const uint64_t kFileSize = 756739;
listener_.OnNewSegment("filename.mp4", kStartTime, kDuration,
kFileSize);
MuxerListener::MediaRanges ranges;
Range init_range;
init_range.start = 0;
init_range.end = 100;
Range index_range;
index_range.start = 101;
index_range.end = 200;
// Only one segment range for this test.
std::vector<Range> segment_ranges;
Range segment_range1;
segment_range1.start = kSegmentStartOffset;
segment_range1.end = kSegmentStartOffset + kFileSize - 1;
segment_ranges.push_back(segment_range1);
Range segment_range2;
segment_range2.start = segment_range1.end + 1;
segment_range2.end = segment_range2.start + 109823;
segment_ranges.push_back(segment_range2);
ranges.init_range = init_range;
ranges.index_range = index_range;
ranges.subsegment_ranges = segment_ranges;
EXPECT_CALL(mock_notifier_,
NotifyNewSegment(_, StrEq("filename.mp4"), kStartTime,
kDuration, kSegmentStartOffset, kFileSize));
listener_.OnMediaEnd(ranges, 200000, 98234328);
}
} // namespace media
} // namespace shaka

View File

@ -165,7 +165,8 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
if (muxer_options.segment_template.empty()) {
media_info->set_media_file_name(muxer_options.output_file_name);
} else {
media_info->set_init_segment_name(muxer_options.output_file_name);
if (!muxer_options.output_file_name.empty())
media_info->set_init_segment_name(muxer_options.output_file_name);
media_info->set_segment_template(muxer_options.segment_template);
}
}