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:
parent
0dfdace280
commit
d096f4b485
|
@ -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().
|
||||
|
|
|
@ -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,7 +35,32 @@ uint32_t GetTimeScale(const MediaInfo& media_info) {
|
|||
return 0u;
|
||||
}
|
||||
|
||||
std::string CreatePlaylistHeader(const std::string& init_segment_name,
|
||||
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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
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), ¬ifier);
|
||||
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||
|
||||
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",
|
||||
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), ¬ifier);
|
||||
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), ¬ifier);
|
||||
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||
EXPECT_TRUE(notifier.Init());
|
||||
MediaInfo media_info;
|
||||
uint32_t stream_id;
|
||||
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "video_playlist.m3u8",
|
||||
EXPECT_TRUE(notifier.NotifyNewStream(media_info, "video_playlist.m3u8",
|
||||
"name", "groupid", &stream_id));
|
||||
EXPECT_EQ(1u, NumRegisteredMediaPlaylists());
|
||||
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), ¬ifier);
|
||||
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||
EXPECT_TRUE(notifier.Init());
|
||||
MediaInfo media_info;
|
||||
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));
|
||||
|
||||
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, ¬ifier);
|
||||
|
||||
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"),
|
||||
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("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, ¬ifier);
|
||||
|
||||
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,
|
||||
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"), _));
|
||||
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, ¬ifier);
|
||||
|
||||
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,
|
||||
EXPECT_CALL(*mock_media_playlist,
|
||||
AddEncryptionInfo(
|
||||
_,
|
||||
StrEq("data:text/plain;base64," + expected_key_uri_base64),
|
||||
StrEq(""),
|
||||
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
|
||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||
_, 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, ¬ifier);
|
||||
|
||||
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"),
|
||||
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, ¬ifier);
|
||||
|
||||
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, ¬ifier);
|
||||
|
||||
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, ¬ifier);
|
||||
|
||||
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), ¬ifier);
|
||||
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||
EXPECT_TRUE(notifier.Init());
|
||||
MediaInfo media_info;
|
||||
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));
|
||||
|
||||
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), ¬ifier);
|
||||
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||
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,
|
||||
|
|
|
@ -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.";
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -165,6 +165,7 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
|
|||
if (muxer_options.segment_template.empty()) {
|
||||
media_info->set_media_file_name(muxer_options.output_file_name);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue