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,
|
const std::string& group_id,
|
||||||
uint32_t* stream_id) = 0;
|
uint32_t* stream_id) = 0;
|
||||||
|
|
||||||
// |stream_id| is the value set by NotifyNewStream().
|
/// @param stream_id is the value set by NotifyNewStream().
|
||||||
// |segment_name| is the name of the new segment.
|
/// @param segment_name is the name of the new segment.
|
||||||
// |start_time| is the start time of the segment in terms of timescale passed
|
/// @param start_time is the start time of the segment in timescale units
|
||||||
// in |media_info|.
|
/// passed in @a media_info.
|
||||||
// |duration| is also in terms of timescale.
|
/// @param duration is also in terms of timescale.
|
||||||
// |size| is the size in bytes.
|
/// @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,
|
virtual bool NotifyNewSegment(uint32_t stream_id,
|
||||||
const std::string& segment_name,
|
const std::string& segment_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) = 0;
|
uint64_t size) = 0;
|
||||||
|
|
||||||
/// @param stream_id is the value set by NotifyNewStream().
|
/// @param stream_id is the value set by NotifyNewStream().
|
||||||
|
|
|
@ -6,11 +6,14 @@
|
||||||
|
|
||||||
#include "packager/hls/base/media_playlist.h"
|
#include "packager/hls/base/media_playlist.h"
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include "packager/base/strings/stringprintf.h"
|
||||||
#include "packager/media/base/language_utils.h"
|
#include "packager/media/base/language_utils.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
|
@ -32,7 +35,32 @@ uint32_t GetTimeScale(const MediaInfo& media_info) {
|
||||||
return 0u;
|
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,
|
uint32_t target_duration,
|
||||||
MediaPlaylist::MediaPlaylistType type,
|
MediaPlaylist::MediaPlaylistType type,
|
||||||
int media_sequence_number,
|
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
|
// Put EXT-X-MAP at the end since the rest of the playlist is about the
|
||||||
// segment and key info.
|
// segment and key info.
|
||||||
if (!init_segment_name.empty()) {
|
header += CreateExtXMap(media_info);
|
||||||
header += "#EXT-X-MAP:URI=\"" + init_segment_name + "\"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SegmentInfoEntry : public HlsEntry {
|
class SegmentInfoEntry : public HlsEntry {
|
||||||
public:
|
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,
|
SegmentInfoEntry(const std::string& file_name,
|
||||||
double start_time,
|
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;
|
~SegmentInfoEntry() override;
|
||||||
|
|
||||||
std::string ToString() override;
|
std::string ToString() override;
|
||||||
|
@ -98,22 +132,42 @@ class SegmentInfoEntry : public HlsEntry {
|
||||||
const std::string file_name_;
|
const std::string file_name_;
|
||||||
const double start_time_;
|
const double start_time_;
|
||||||
const double duration_;
|
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);
|
DISALLOW_COPY_AND_ASSIGN(SegmentInfoEntry);
|
||||||
};
|
};
|
||||||
|
|
||||||
SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
||||||
double start_time,
|
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),
|
: HlsEntry(HlsEntry::EntryType::kExtInf),
|
||||||
file_name_(file_name),
|
file_name_(file_name),
|
||||||
start_time_(start_time),
|
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() {}
|
SegmentInfoEntry::~SegmentInfoEntry() {}
|
||||||
|
|
||||||
std::string SegmentInfoEntry::ToString() {
|
std::string SegmentInfoEntry::ToString() {
|
||||||
return base::StringPrintf("#EXTINF:%.3f,\n%s\n", duration_,
|
std::string result = base::StringPrintf("#EXTINF:%.3f,\n", duration_);
|
||||||
file_name_.c_str());
|
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 {
|
class EncryptionInfoEntry : public HlsEntry {
|
||||||
|
@ -224,12 +278,12 @@ double LatestSegmentStartTime(
|
||||||
HlsEntry::HlsEntry(HlsEntry::EntryType type) : type_(type) {}
|
HlsEntry::HlsEntry(HlsEntry::EntryType type) : type_(type) {}
|
||||||
HlsEntry::~HlsEntry() {}
|
HlsEntry::~HlsEntry() {}
|
||||||
|
|
||||||
MediaPlaylist::MediaPlaylist(MediaPlaylistType type,
|
MediaPlaylist::MediaPlaylist(MediaPlaylistType playlist_type,
|
||||||
double time_shift_buffer_depth,
|
double time_shift_buffer_depth,
|
||||||
const std::string& file_name,
|
const std::string& file_name,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::string& group_id)
|
const std::string& group_id)
|
||||||
: type_(type),
|
: playlist_type_(playlist_type),
|
||||||
time_shift_buffer_depth_(time_shift_buffer_depth),
|
time_shift_buffer_depth_(time_shift_buffer_depth),
|
||||||
file_name_(file_name),
|
file_name_(file_name),
|
||||||
name_(name),
|
name_(name),
|
||||||
|
@ -272,12 +326,15 @@ bool MediaPlaylist::SetMediaInfo(const MediaInfo& media_info) {
|
||||||
void MediaPlaylist::AddSegment(const std::string& file_name,
|
void MediaPlaylist::AddSegment(const std::string& file_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
if (time_scale_ == 0) {
|
if (time_scale_ == 0) {
|
||||||
LOG(WARNING) << "Timescale is not set and the duration for " << duration
|
LOG(WARNING) << "Timescale is not set and the duration for " << duration
|
||||||
<< " cannot be calculated. The output will be wrong.";
|
<< " 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,8 +348,11 @@ void MediaPlaylist::AddSegment(const std::string& file_name,
|
||||||
const int kBitsInByte = 8;
|
const int kBitsInByte = 8;
|
||||||
const uint64_t bitrate = kBitsInByte * size / segment_duration_seconds;
|
const uint64_t bitrate = kBitsInByte * size / segment_duration_seconds;
|
||||||
max_bitrate_ = std::max(max_bitrate_, bitrate);
|
max_bitrate_ = std::max(max_bitrate_, bitrate);
|
||||||
entries_.emplace_back(new SegmentInfoEntry(file_name, start_time_seconds,
|
entries_.emplace_back(new SegmentInfoEntry(
|
||||||
segment_duration_seconds));
|
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();
|
SlideWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,7 +379,7 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string header = CreatePlaylistHeader(
|
std::string header = CreatePlaylistHeader(
|
||||||
media_info_.init_segment_name(), target_duration_, type_,
|
media_info_, target_duration_, playlist_type_,
|
||||||
media_sequence_number_, discontinuity_sequence_number_);
|
media_sequence_number_, discontinuity_sequence_number_);
|
||||||
|
|
||||||
std::string body;
|
std::string body;
|
||||||
|
@ -328,7 +388,7 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
||||||
|
|
||||||
std::string content = header + body;
|
std::string content = header + body;
|
||||||
|
|
||||||
if (type_ == MediaPlaylistType::kVod) {
|
if (playlist_type_ == MediaPlaylistType::kVod) {
|
||||||
content += "#EXT-X-ENDLIST\n";
|
content += "#EXT-X-ENDLIST\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,8 +448,10 @@ bool MediaPlaylist::GetResolution(uint32_t* width, uint32_t* height) const {
|
||||||
|
|
||||||
void MediaPlaylist::SlideWindow() {
|
void MediaPlaylist::SlideWindow() {
|
||||||
DCHECK(!entries_.empty());
|
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;
|
return;
|
||||||
|
}
|
||||||
DCHECK_GT(time_scale_, 0u);
|
DCHECK_GT(time_scale_, 0u);
|
||||||
|
|
||||||
// The start time of the latest segment is considered the current_play_time,
|
// 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.
|
/// necessarily the same as @a file_name.
|
||||||
/// @param group_id is the group ID for this playlist. This is the value of
|
/// @param group_id is the group ID for this playlist. This is the value of
|
||||||
/// GROUP-ID attribute for EXT-X-MEDIA.
|
/// GROUP-ID attribute for EXT-X-MEDIA.
|
||||||
MediaPlaylist(MediaPlaylistType type,
|
MediaPlaylist(MediaPlaylistType playlist_type,
|
||||||
double time_shift_buffer_depth,
|
double time_shift_buffer_depth,
|
||||||
const std::string& file_name,
|
const std::string& file_name,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
|
@ -100,10 +100,13 @@ class MediaPlaylist {
|
||||||
/// @param file_name is the file name of the segment.
|
/// @param file_name is the file name of the segment.
|
||||||
/// @param start_time is in terms of the timescale of the media.
|
/// @param start_time is in terms of the timescale of the media.
|
||||||
/// @param duration 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.
|
/// @param size is size in bytes.
|
||||||
virtual void AddSegment(const std::string& file_name,
|
virtual void AddSegment(const std::string& file_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
uint64_t start_byte_offset,
|
||||||
uint64_t size);
|
uint64_t size);
|
||||||
|
|
||||||
/// All segments added after calling this method must be decryptable with
|
/// 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.
|
// |sequence_number_| by the number of segments removed.
|
||||||
void SlideWindow();
|
void SlideWindow();
|
||||||
|
|
||||||
const MediaPlaylistType type_;
|
const MediaPlaylistType playlist_type_;
|
||||||
const double time_shift_buffer_depth_;
|
const double time_shift_buffer_depth_;
|
||||||
// Mainly for MasterPlaylist to use these values.
|
// Mainly for MasterPlaylist to use these values.
|
||||||
const std::string file_name_;
|
const std::string file_name_;
|
||||||
|
@ -186,6 +189,10 @@ class MediaPlaylist {
|
||||||
|
|
||||||
uint64_t max_bitrate_ = 0;
|
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.
|
// See SetTargetDuration() comments.
|
||||||
bool target_duration_set_ = false;
|
bool target_duration_set_ = false;
|
||||||
uint32_t target_duration_ = 0;
|
uint32_t target_duration_ = 0;
|
||||||
|
|
|
@ -23,6 +23,7 @@ const char kDefaultPlaylistFileName[] = "default_playlist.m3u8";
|
||||||
const double kTimeShiftBufferDepth = 20;
|
const double kTimeShiftBufferDepth = 20;
|
||||||
const uint64_t kTimeScale = 90000;
|
const uint64_t kTimeScale = 90000;
|
||||||
const uint64_t kMBytes = 1000000;
|
const uint64_t kMBytes = 1000000;
|
||||||
|
const uint64_t kZeroByteOffset = 0;
|
||||||
|
|
||||||
MATCHER_P(MatchesString, expected_string, "") {
|
MATCHER_P(MatchesString, expected_string, "") {
|
||||||
const std::string arg_string(static_cast<const char*>(arg));
|
const std::string arg_string(static_cast<const char*>(arg));
|
||||||
|
@ -72,14 +73,35 @@ class MediaPlaylistTest : public ::testing::Test {
|
||||||
MediaInfo valid_video_media_info_;
|
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.
|
// Verify that SetMediaInfo() fails if timescale is not present.
|
||||||
TEST_F(MediaPlaylistTest, NoTimeScale) {
|
TEST_F(MediaPlaylistMultiSegmentTest, NoTimeScale) {
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
EXPECT_FALSE(media_playlist_.SetMediaInfo(media_info));
|
EXPECT_FALSE(media_playlist_.SetMediaInfo(media_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The current implementation only handles video and audio.
|
// The current implementation only handles video and audio.
|
||||||
TEST_F(MediaPlaylistTest, NoAudioOrVideo) {
|
TEST_F(MediaPlaylistMultiSegmentTest, NoAudioOrVideo) {
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
media_info.set_reference_time_scale(kTimeScale);
|
media_info.set_reference_time_scale(kTimeScale);
|
||||||
MediaInfo::TextInfo* text_info = media_info.mutable_text_info();
|
MediaInfo::TextInfo* text_info = media_info.mutable_text_info();
|
||||||
|
@ -87,7 +109,7 @@ TEST_F(MediaPlaylistTest, NoAudioOrVideo) {
|
||||||
EXPECT_FALSE(media_playlist_.SetMediaInfo(media_info));
|
EXPECT_FALSE(media_playlist_.SetMediaInfo(media_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MediaPlaylistTest, SetMediaInfo) {
|
TEST_F(MediaPlaylistMultiSegmentTest, SetMediaInfo) {
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
media_info.set_reference_time_scale(kTimeScale);
|
media_info.set_reference_time_scale(kTimeScale);
|
||||||
MediaInfo::VideoInfo* video_info = media_info.mutable_video_info();
|
MediaInfo::VideoInfo* video_info = media_info.mutable_video_info();
|
||||||
|
@ -97,20 +119,93 @@ TEST_F(MediaPlaylistTest, SetMediaInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that AddSegment works (not crash).
|
// Verify that AddSegment works (not crash).
|
||||||
TEST_F(MediaPlaylistTest, AddSegment) {
|
TEST_F(MediaPlaylistMultiSegmentTest, AddSegment) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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).
|
// Verify that AddEncryptionInfo works (not crash).
|
||||||
TEST_F(MediaPlaylistTest, AddEncryptionInfo) {
|
TEST_F(MediaPlaylistMultiSegmentTest, AddEncryptionInfo) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "0xabcedf", "",
|
"http://example.com", "", "0xabcedf", "",
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MediaPlaylistTest, WriteToFile) {
|
TEST_F(MediaPlaylistMultiSegmentTest, WriteToFile) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -127,7 +222,7 @@ TEST_F(MediaPlaylistTest, WriteToFile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If bitrate (bandwidth) is not set in the MediaInfo, use it.
|
// 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);
|
valid_video_media_info_.set_bandwidth(8191);
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
EXPECT_EQ(8191u, media_playlist_.Bitrate());
|
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
|
// If bitrate (bandwidth) is not set in the MediaInfo, then calculate from the
|
||||||
// segments.
|
// segments.
|
||||||
TEST_F(MediaPlaylistTest, GetBitrateFromSegments) {
|
TEST_F(MediaPlaylistMultiSegmentTest, GetBitrateFromSegments) {
|
||||||
valid_video_media_info_.clear_bandwidth();
|
valid_video_media_info_.clear_bandwidth();
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
|
|
||||||
// Max bitrate is 2000Kb/s.
|
// Max bitrate is 2000Kb/s.
|
||||||
EXPECT_EQ(2000000u, media_playlist_.Bitrate());
|
EXPECT_EQ(2000000u, media_playlist_.Bitrate());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MediaPlaylistTest, GetLongestSegmentDuration) {
|
TEST_F(MediaPlaylistMultiSegmentTest, GetLongestSegmentDuration) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
media_playlist_.AddSegment("file3.ts", 40 * kTimeScale, 14 * kTimeScale,
|
media_playlist_.AddSegment("file3.ts", 40 * kTimeScale, 14 * kTimeScale,
|
||||||
3 * kMBytes);
|
kZeroByteOffset, 3 * kMBytes);
|
||||||
|
|
||||||
EXPECT_NEAR(30.0, media_playlist_.GetLongestSegmentDuration(), 0.01);
|
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_));
|
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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -183,15 +299,17 @@ TEST_F(MediaPlaylistTest, WriteToFileWithSegments) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
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_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "0x12345678",
|
"http://example.com", "", "0x12345678",
|
||||||
"com.widevine", "1/2/4");
|
"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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -213,15 +331,17 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfo) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
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_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "", "com.widevine",
|
"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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\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.
|
// 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_));
|
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,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "0x12345678",
|
"http://example.com", "", "0x12345678",
|
||||||
"com.widevine", "1/2/4");
|
"com.widevine", "1/2/4");
|
||||||
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\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-TARGETDURATION:30\n"
|
||||||
"#EXT-X-PLAYLIST-TYPE:VOD\n"
|
"#EXT-X-PLAYLIST-TYPE:VOD\n"
|
||||||
"#EXTINF:10.000,\n"
|
"#EXTINF:10.000,\n"
|
||||||
|
@ -274,7 +397,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithClearLead) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MediaPlaylistTest, GetLanguage) {
|
TEST_F(MediaPlaylistMultiSegmentTest, GetLanguage) {
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
media_info.set_reference_time_scale(kTimeScale);
|
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
|
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");
|
valid_video_media_info_.set_init_segment_name("init_segment.mp4");
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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,
|
media_playlist_.AddSegment("file2.mp4", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
|
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -319,16 +444,18 @@ TEST_F(MediaPlaylistTest, InitSegment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that kSampleAesCenc is handled correctly.
|
// 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_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(
|
media_playlist_.AddEncryptionInfo(
|
||||||
MediaPlaylist::EncryptionMethod::kSampleAesCenc, "http://example.com", "",
|
MediaPlaylist::EncryptionMethod::kSampleAesCenc, "http://example.com", "",
|
||||||
"0x12345678", "com.widevine", "1/2/4");
|
"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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -350,8 +477,7 @@ TEST_F(MediaPlaylistTest, SampleAesCenc) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that multiple encryption info can be set.
|
TEST_F(MediaPlaylistMultiSegmentTest, MultipleEncryptionInfo) {
|
||||||
TEST_F(MediaPlaylistTest, MultipleEncryptionInfo) {
|
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
|
@ -362,9 +488,10 @@ TEST_F(MediaPlaylistTest, MultipleEncryptionInfo) {
|
||||||
MediaPlaylist::EncryptionMethod::kSampleAes, "http://mydomain.com",
|
MediaPlaylist::EncryptionMethod::kSampleAes, "http://mydomain.com",
|
||||||
"0xfedc", "0x12345678", "com.widevine.someother", "1");
|
"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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
5 * kMBytes);
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -390,18 +517,20 @@ TEST_F(MediaPlaylistTest, MultipleEncryptionInfo) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LiveMediaPlaylistTest : public MediaPlaylistTest {
|
class LiveMediaPlaylistTest : public MediaPlaylistMultiSegmentTest {
|
||||||
protected:
|
protected:
|
||||||
LiveMediaPlaylistTest()
|
LiveMediaPlaylistTest()
|
||||||
: MediaPlaylistTest(MediaPlaylist::MediaPlaylistType::kLive) {}
|
: MediaPlaylistMultiSegmentTest(MediaPlaylist::MediaPlaylistType::kLive) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(LiveMediaPlaylistTest, Basic) {
|
TEST_F(LiveMediaPlaylistTest, Basic) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -421,11 +550,12 @@ TEST_F(LiveMediaPlaylistTest, Basic) {
|
||||||
TEST_F(LiveMediaPlaylistTest, TimeShifted) {
|
TEST_F(LiveMediaPlaylistTest, TimeShifted) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
|
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -453,11 +583,12 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfo) {
|
||||||
MediaPlaylist::EncryptionMethod::kSampleAes, "http://mydomain.com",
|
MediaPlaylist::EncryptionMethod::kSampleAes, "http://mydomain.com",
|
||||||
"0xfedc", "0x12345678", "com.widevine.someother", "1");
|
"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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
|
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -485,7 +616,8 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfo) {
|
||||||
TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
|
TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "0x12345678",
|
"http://example.com", "", "0x12345678",
|
||||||
|
@ -495,7 +627,7 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
|
||||||
"0xfedc", "0x12345678", "com.widevine.someother", "1");
|
"0xfedc", "0x12345678", "com.widevine.someother", "1");
|
||||||
|
|
||||||
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "0x22345678",
|
"http://example.com", "", "0x22345678",
|
||||||
|
@ -505,7 +637,7 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
|
||||||
"0xfedd", "0x22345678", "com.widevine.someother", "1");
|
"0xfedd", "0x22345678", "com.widevine.someother", "1");
|
||||||
|
|
||||||
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
|
media_playlist_.AddSegment("file3.ts", 30 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "0x32345678",
|
"http://example.com", "", "0x32345678",
|
||||||
|
@ -515,7 +647,7 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
|
||||||
"0xfede", "0x32345678", "com.widevine.someother", "1");
|
"0xfede", "0x32345678", "com.widevine.someother", "1");
|
||||||
|
|
||||||
media_playlist_.AddSegment("file4.ts", 50 * kTimeScale, 20 * kTimeScale,
|
media_playlist_.AddSegment("file4.ts", 50 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
@ -548,18 +680,20 @@ TEST_F(LiveMediaPlaylistTest, TimeShiftedWithEncryptionInfoShifted) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EventMediaPlaylistTest : public MediaPlaylistTest {
|
class EventMediaPlaylistTest : public MediaPlaylistMultiSegmentTest {
|
||||||
protected:
|
protected:
|
||||||
EventMediaPlaylistTest()
|
EventMediaPlaylistTest()
|
||||||
: MediaPlaylistTest(MediaPlaylist::MediaPlaylistType::kEvent) {}
|
: MediaPlaylistMultiSegmentTest(
|
||||||
|
MediaPlaylist::MediaPlaylistType::kEvent) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(EventMediaPlaylistTest, Basic) {
|
TEST_F(EventMediaPlaylistTest, Basic) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
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,
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 20 * kTimeScale,
|
||||||
2 * kMBytes);
|
kZeroByteOffset, 2 * kMBytes);
|
||||||
const char kExpectedOutput[] =
|
const char kExpectedOutput[] =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
|
|
|
@ -25,10 +25,11 @@ class MockMediaPlaylist : public MediaPlaylist {
|
||||||
~MockMediaPlaylist() override;
|
~MockMediaPlaylist() override;
|
||||||
|
|
||||||
MOCK_METHOD1(SetMediaInfo, bool(const MediaInfo& media_info));
|
MOCK_METHOD1(SetMediaInfo, bool(const MediaInfo& media_info));
|
||||||
MOCK_METHOD4(AddSegment,
|
MOCK_METHOD5(AddSegment,
|
||||||
void(const std::string& file_name,
|
void(const std::string& file_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
uint64_t start_byte_offset,
|
||||||
uint64_t size));
|
uint64_t size));
|
||||||
MOCK_METHOD0(RemoveOldestSegment, void());
|
MOCK_METHOD0(RemoveOldestSegment, void());
|
||||||
MOCK_METHOD6(AddEncryptionInfo,
|
MOCK_METHOD6(AddEncryptionInfo,
|
||||||
|
|
|
@ -299,6 +299,7 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
|
||||||
const std::string& segment_name,
|
const std::string& segment_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
auto stream_iterator = stream_map_.find(stream_id);
|
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;
|
auto& media_playlist = stream_iterator->second->media_playlist;
|
||||||
media_playlist->AddSegment(prefix_ + relative_segment_name, start_time,
|
media_playlist->AddSegment(prefix_ + relative_segment_name, start_time,
|
||||||
duration, size);
|
duration, start_byte_offset, size);
|
||||||
|
|
||||||
// Update target duration.
|
// Update target duration.
|
||||||
uint32_t longest_segment_duration =
|
uint32_t longest_segment_duration =
|
||||||
|
|
|
@ -67,6 +67,7 @@ class SimpleHlsNotifier : public HlsNotifier {
|
||||||
const std::string& segment_name,
|
const std::string& segment_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) override;
|
uint64_t size) override;
|
||||||
bool NotifyEncryptionUpdate(
|
bool NotifyEncryptionUpdate(
|
||||||
uint32_t stream_id,
|
uint32_t stream_id,
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "packager/base/base64.h"
|
#include "packager/base/base64.h"
|
||||||
#include "packager/base/files/file_path.h"
|
#include "packager/base/files/file_path.h"
|
||||||
#include "packager/hls/base/mock_media_playlist.h"
|
#include "packager/hls/base/mock_media_playlist.h"
|
||||||
|
@ -77,47 +79,42 @@ const char kSampleAesProtectionScheme[] = "cbca";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// : notifier_(profile,
|
||||||
|
// kTestTimeShiftBufferDepth,
|
||||||
|
// kTestPrefix,
|
||||||
|
// kAnyOutputDir,
|
||||||
|
// kMasterPlaylistName),
|
||||||
|
|
||||||
class SimpleHlsNotifierTest : public ::testing::Test {
|
class SimpleHlsNotifierTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
SimpleHlsNotifierTest()
|
SimpleHlsNotifierTest()
|
||||||
: SimpleHlsNotifierTest(HlsProfile::kOnDemandProfile) {}
|
: SimpleHlsNotifierTest(HlsProfile::kOnDemandProfile) {}
|
||||||
|
|
||||||
SimpleHlsNotifierTest(HlsProfile profile)
|
SimpleHlsNotifierTest(HlsProfile profile)
|
||||||
: notifier_(profile,
|
: widevine_system_id_(
|
||||||
kTestTimeShiftBufferDepth,
|
|
||||||
kTestPrefix,
|
|
||||||
kAnyOutputDir,
|
|
||||||
kMasterPlaylistName),
|
|
||||||
widevine_system_id_(
|
|
||||||
media::kWidevineSystemId,
|
media::kWidevineSystemId,
|
||||||
media::kWidevineSystemId + arraysize(media::kWidevineSystemId)),
|
media::kWidevineSystemId + arraysize(media::kWidevineSystemId)),
|
||||||
common_system_id_(
|
common_system_id_(
|
||||||
media::kCommonSystemId,
|
media::kCommonSystemId,
|
||||||
media::kCommonSystemId + arraysize(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,
|
void InjectMediaPlaylistFactory(std::unique_ptr<MediaPlaylistFactory> factory,
|
||||||
SimpleHlsNotifier* notifier) {
|
SimpleHlsNotifier* notifier) {
|
||||||
notifier->media_playlist_factory_ = std::move(factory);
|
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,
|
void InjectMasterPlaylist(std::unique_ptr<MasterPlaylist> playlist,
|
||||||
SimpleHlsNotifier* notifier) {
|
SimpleHlsNotifier* notifier) {
|
||||||
notifier->master_playlist_ = std::move(playlist);
|
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,
|
uint32_t SetupStream(const std::string& protection_scheme,
|
||||||
MockMediaPlaylist* mock_media_playlist) {
|
MockMediaPlaylist* mock_media_playlist,
|
||||||
|
SimpleHlsNotifier* notifier) {
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
media_info.mutable_protected_content()->set_protection_scheme(
|
media_info.mutable_protected_content()->set_protection_scheme(
|
||||||
protection_scheme);
|
protection_scheme);
|
||||||
|
@ -133,22 +130,24 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
||||||
EXPECT_CALL(*factory, CreateMock(_, _, _, _, _))
|
EXPECT_CALL(*factory, CreateMock(_, _, _, _, _))
|
||||||
.WillOnce(Return(mock_media_playlist));
|
.WillOnce(Return(mock_media_playlist));
|
||||||
|
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
InjectMasterPlaylist(std::move(mock_master_playlist), notifier);
|
||||||
InjectMediaPlaylistFactory(std::move(factory));
|
InjectMediaPlaylistFactory(std::move(factory), notifier);
|
||||||
EXPECT_TRUE(notifier_.Init());
|
EXPECT_TRUE(notifier->Init());
|
||||||
uint32_t stream_id;
|
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));
|
"groupid", &stream_id));
|
||||||
return stream_id;
|
return stream_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleHlsNotifier notifier_;
|
|
||||||
const std::vector<uint8_t> widevine_system_id_;
|
const std::vector<uint8_t> widevine_system_id_;
|
||||||
const std::vector<uint8_t> common_system_id_;
|
const std::vector<uint8_t> common_system_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, Init) {
|
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.
|
// Verify that relative paths can be handled.
|
||||||
|
@ -165,31 +164,36 @@ TEST_F(SimpleHlsNotifierTest, RebaseSegmentTemplateRelative) {
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
EXPECT_CALL(*mock_master_playlist, AddMediaPlaylist(mock_media_playlist));
|
EXPECT_CALL(*mock_master_playlist, AddMediaPlaylist(mock_media_playlist));
|
||||||
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
*mock_media_playlist,
|
|
||||||
SetMediaInfo(SegmentTemplateEq("path/to/media$Number$.ts")))
|
SetMediaInfo(SegmentTemplateEq("path/to/media$Number$.ts")))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
// Verify that the common prefix is stripped for AddSegment().
|
// Verify that the common prefix is stripped for AddSegment().
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(
|
||||||
AddSegment("http://testprefix.com/path/to/media1.ts", _, _, _));
|
*mock_media_playlist,
|
||||||
|
AddSegment("http://testprefix.com/path/to/media1.ts", _, _, _, _));
|
||||||
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
|
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
|
||||||
StrEq("video_playlist.m3u8"), StrEq("name"),
|
StrEq("video_playlist.m3u8"), StrEq("name"),
|
||||||
StrEq("groupid")))
|
StrEq("groupid")))
|
||||||
.WillOnce(Return(mock_media_playlist));
|
.WillOnce(Return(mock_media_playlist));
|
||||||
|
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
InjectMediaPlaylistFactory(std::move(factory));
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
EXPECT_TRUE(notifier_.Init());
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
|
|
||||||
|
InjectMasterPlaylist(std::move(mock_master_playlist), ¬ifier);
|
||||||
|
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||||
|
|
||||||
|
EXPECT_TRUE(notifier.Init());
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
media_info.set_segment_template("anything/path/to/media$Number$.ts");
|
media_info.set_segment_template("anything/path/to/media$Number$.ts");
|
||||||
uint32_t stream_id;
|
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));
|
"name", "groupid", &stream_id));
|
||||||
|
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id, "anything/path/to/media1.ts",
|
||||||
notifier_.NotifyNewSegment(stream_id, "anything/path/to/media1.ts",
|
kAnyStartTime, kAnyDuration, 0,
|
||||||
kAnyStartTime, kAnyDuration, kAnySize));
|
kAnySize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that when segment template's prefix and output dir match, then the
|
// Verify that when segment template's prefix and output dir match, then the
|
||||||
|
@ -197,7 +201,6 @@ TEST_F(SimpleHlsNotifierTest, RebaseSegmentTemplateRelative) {
|
||||||
TEST_F(SimpleHlsNotifierTest,
|
TEST_F(SimpleHlsNotifierTest,
|
||||||
RebaseAbsoluteSegmentTemplatePrefixAndOutputDirMatch) {
|
RebaseAbsoluteSegmentTemplatePrefixAndOutputDirMatch) {
|
||||||
const char kAbsoluteOutputDir[] = "/tmp/something/";
|
const char kAbsoluteOutputDir[] = "/tmp/something/";
|
||||||
// Require a separate instance to set kAbsoluteOutputDir.
|
|
||||||
SimpleHlsNotifier test_notifier(HlsProfile::kOnDemandProfile,
|
SimpleHlsNotifier test_notifier(HlsProfile::kOnDemandProfile,
|
||||||
kTestTimeShiftBufferDepth, kTestPrefix,
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
kAbsoluteOutputDir, kMasterPlaylistName);
|
kAbsoluteOutputDir, kMasterPlaylistName);
|
||||||
|
@ -218,7 +221,7 @@ TEST_F(SimpleHlsNotifierTest,
|
||||||
|
|
||||||
// Verify that the output_dir is stripped and then kTestPrefix is prepended.
|
// Verify that the output_dir is stripped and then kTestPrefix is prepended.
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
AddSegment("http://testprefix.com/media1.ts", _, _, _));
|
AddSegment("http://testprefix.com/media1.ts", _, _, _, _));
|
||||||
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
|
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
|
||||||
StrEq("video_playlist.m3u8"), StrEq("name"),
|
StrEq("video_playlist.m3u8"), StrEq("name"),
|
||||||
StrEq("groupid")))
|
StrEq("groupid")))
|
||||||
|
@ -235,7 +238,7 @@ TEST_F(SimpleHlsNotifierTest,
|
||||||
|
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
test_notifier.NotifyNewSegment(stream_id, "/tmp/something/media1.ts",
|
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
|
// 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));
|
.WillOnce(Return(true));
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
AddSegment("http://testprefix.com//var/somewhereelse/media1.ts",
|
AddSegment("http://testprefix.com//var/somewhereelse/media1.ts",
|
||||||
_, _, _));
|
_, _, _, _));
|
||||||
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
|
EXPECT_CALL(*factory, CreateMock(kVodPlaylist, Eq(kTestTimeShiftBufferDepth),
|
||||||
StrEq("video_playlist.m3u8"), StrEq("name"),
|
StrEq("video_playlist.m3u8"), StrEq("name"),
|
||||||
StrEq("groupid")))
|
StrEq("groupid")))
|
||||||
|
@ -279,18 +282,21 @@ TEST_F(SimpleHlsNotifierTest,
|
||||||
"name", "groupid", &stream_id));
|
"name", "groupid", &stream_id));
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
test_notifier.NotifyNewSegment(stream_id, "/var/somewhereelse/media1.ts",
|
test_notifier.NotifyNewSegment(stream_id, "/var/somewhereelse/media1.ts",
|
||||||
kAnyStartTime, kAnyDuration, kAnySize));
|
kAnyStartTime, kAnyDuration, 0, kAnySize));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, Flush) {
|
TEST_F(SimpleHlsNotifierTest, Flush) {
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
|
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
|
||||||
new MockMasterPlaylist());
|
new MockMasterPlaylist());
|
||||||
EXPECT_CALL(*mock_master_playlist,
|
EXPECT_CALL(*mock_master_playlist,
|
||||||
WriteMasterPlaylist(StrEq(kTestPrefix), StrEq(kAnyOutputDir)))
|
WriteMasterPlaylist(StrEq(kTestPrefix), StrEq(kAnyOutputDir)))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
InjectMasterPlaylist(std::move(mock_master_playlist), ¬ifier);
|
||||||
EXPECT_TRUE(notifier_.Init());
|
EXPECT_TRUE(notifier.Init());
|
||||||
EXPECT_TRUE(notifier_.Flush());
|
EXPECT_TRUE(notifier.Flush());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
|
TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
|
||||||
|
@ -310,14 +316,18 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
|
||||||
StrEq("groupid")))
|
StrEq("groupid")))
|
||||||
.WillOnce(Return(mock_media_playlist));
|
.WillOnce(Return(mock_media_playlist));
|
||||||
|
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
InjectMediaPlaylistFactory(std::move(factory));
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
EXPECT_TRUE(notifier_.Init());
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
|
|
||||||
|
InjectMasterPlaylist(std::move(mock_master_playlist), ¬ifier);
|
||||||
|
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||||
|
EXPECT_TRUE(notifier.Init());
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
uint32_t stream_id;
|
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));
|
"name", "groupid", &stream_id));
|
||||||
EXPECT_EQ(1u, NumRegisteredMediaPlaylists());
|
EXPECT_EQ(1u, NumRegisteredMediaPlaylists(notifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
|
TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
|
@ -343,24 +353,27 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
const std::string segment_name = "segmentname";
|
const std::string segment_name = "segmentname";
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
AddSegment(StrEq(kTestPrefix + segment_name), kStartTime,
|
AddSegment(StrEq(kTestPrefix + segment_name), kStartTime,
|
||||||
kDuration, kSize));
|
kDuration, 203, kSize));
|
||||||
|
|
||||||
const double kLongestSegmentDuration = 11.3;
|
const double kLongestSegmentDuration = 11.3;
|
||||||
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
|
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
|
||||||
EXPECT_CALL(*mock_media_playlist, GetLongestSegmentDuration())
|
EXPECT_CALL(*mock_media_playlist, GetLongestSegmentDuration())
|
||||||
.WillOnce(Return(kLongestSegmentDuration));
|
.WillOnce(Return(kLongestSegmentDuration));
|
||||||
|
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
|
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
InjectMasterPlaylist(std::move(mock_master_playlist), ¬ifier);
|
||||||
InjectMediaPlaylistFactory(std::move(factory));
|
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||||
EXPECT_TRUE(notifier_.Init());
|
EXPECT_TRUE(notifier.Init());
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
uint32_t stream_id;
|
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));
|
"groupid", &stream_id));
|
||||||
|
|
||||||
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id, segment_name, kStartTime,
|
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id, segment_name, kStartTime,
|
||||||
kDuration, kSize));
|
kDuration, 203, kSize));
|
||||||
|
|
||||||
Mock::VerifyAndClearExpectations(mock_master_playlist_ptr);
|
Mock::VerifyAndClearExpectations(mock_master_playlist_ptr);
|
||||||
Mock::VerifyAndClearExpectations(mock_media_playlist);
|
Mock::VerifyAndClearExpectations(mock_media_playlist);
|
||||||
|
@ -376,20 +389,26 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist.m3u8"))
|
.Append(base::FilePath::FromUTF8Unsafe("playlist.m3u8"))
|
||||||
.AsUTF8Unsafe())))
|
.AsUTF8Unsafe())))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_TRUE(notifier_.Flush());
|
EXPECT_TRUE(notifier.Flush());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyNewSegmentWithoutStreamsRegistered) {
|
TEST_F(SimpleHlsNotifierTest, NotifyNewSegmentWithoutStreamsRegistered) {
|
||||||
EXPECT_TRUE(notifier_.Init());
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
EXPECT_FALSE(notifier_.NotifyNewSegment(1u, "anything", 0u, 0u, 0u));
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
|
EXPECT_TRUE(notifier.Init());
|
||||||
|
EXPECT_FALSE(notifier.NotifyNewSegment(1u, "anything", 0u, 0u, 0u, 0u));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
|
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
const uint32_t stream_id =
|
const uint32_t stream_id =
|
||||||
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
|
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier);
|
||||||
|
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
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_provider("someprovider");
|
||||||
widevine_pssh_data.set_content_id("contentid");
|
widevine_pssh_data.set_content_id("contentid");
|
||||||
const uint8_t kAnyKeyId[] = {
|
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));
|
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(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()),
|
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
||||||
&expected_pssh_base64);
|
&expected_pssh_base64);
|
||||||
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
*mock_media_playlist,
|
AddEncryptionInfo(
|
||||||
AddEncryptionInfo(_,
|
_, StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
StrEq("data:text/plain;base64," + expected_json_base64),
|
StrEq(""), StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq(""),
|
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
|
||||||
StrEq("com.widevine"), _));
|
StrEq("com.widevine"), _));
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
AddEncryptionInfo(
|
AddEncryptionInfo(
|
||||||
_,
|
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
|
||||||
StrEq("data:text/plain;base64," + expected_pssh_base64),
|
|
||||||
StrEq("0x11223344112233441122334411223344"),
|
StrEq("0x11223344112233441122334411223344"),
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
|
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));
|
stream_id, any_key_id, widevine_system_id_, iv, pssh_box));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,8 +467,11 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
const uint32_t stream_id =
|
const uint32_t stream_id =
|
||||||
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
|
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier);
|
||||||
|
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
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()),
|
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
||||||
&expected_pssh_base64);
|
&expected_pssh_base64);
|
||||||
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
*mock_media_playlist,
|
|
||||||
AddEncryptionInfo(_,
|
|
||||||
StrEq("data:text/plain;base64," + expected_json_base64),
|
|
||||||
StrEq(""),
|
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
|
||||||
StrEq("com.widevine"), _));
|
|
||||||
EXPECT_CALL(
|
|
||||||
*mock_media_playlist,
|
|
||||||
AddEncryptionInfo(
|
AddEncryptionInfo(
|
||||||
_,
|
_, StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
StrEq("data:text/plain;base64," + expected_pssh_base64),
|
StrEq(""), StrEq("0x45454545454545454545454545454545"),
|
||||||
|
StrEq("com.widevine"), _));
|
||||||
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
|
AddEncryptionInfo(
|
||||||
|
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
|
||||||
StrEq("0x11223344112233441122334411223344"),
|
StrEq("0x11223344112233441122334411223344"),
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
|
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
|
||||||
const uint8_t kAnyKeyId[] = {
|
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,
|
stream_id,
|
||||||
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
||||||
widevine_system_id_, iv, pssh_box));
|
widevine_system_id_, iv, pssh_box));
|
||||||
|
@ -515,8 +526,11 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
const uint32_t stream_id =
|
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> key_id(16, 0x23);
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
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()),
|
base::Base64Encode(std::string(key_id.begin(), key_id.end()),
|
||||||
&expected_key_uri_base64);
|
&expected_key_uri_base64);
|
||||||
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
*mock_media_playlist,
|
|
||||||
AddEncryptionInfo(
|
AddEncryptionInfo(
|
||||||
_,
|
_, StrEq("data:text/plain;base64," + expected_key_uri_base64),
|
||||||
StrEq("data:text/plain;base64," + expected_key_uri_base64),
|
StrEq(""), StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq(""),
|
StrEq("identity"), _));
|
||||||
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
|
||||||
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
|
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,24 +556,23 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
uint32_t stream_id =
|
uint32_t stream_id =
|
||||||
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
|
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier);
|
||||||
|
|
||||||
std::vector<uint8_t> iv(16, 0x45);
|
std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
|
||||||
media::WidevinePsshData widevine_pssh_data;
|
media::WidevinePsshData widevine_pssh_data;
|
||||||
widevine_pssh_data.set_provider("someprovider");
|
widevine_pssh_data.set_provider("someprovider");
|
||||||
const uint8_t kFirstKeyId[] = {
|
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[] = {
|
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,
|
std::vector<uint8_t> first_keyid(kFirstKeyId,
|
||||||
kFirstKeyId + arraysize(kFirstKeyId));
|
kFirstKeyId + arraysize(kFirstKeyId));
|
||||||
|
@ -597,23 +608,20 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
|
||||||
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
||||||
&expected_pssh_base64);
|
&expected_pssh_base64);
|
||||||
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
*mock_media_playlist,
|
AddEncryptionInfo(
|
||||||
AddEncryptionInfo(_,
|
_, StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
StrEq("data:text/plain;base64," + expected_json_base64),
|
StrEq(""), StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq(""),
|
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
|
||||||
StrEq("com.widevine"), _));
|
StrEq("com.widevine"), _));
|
||||||
|
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
AddEncryptionInfo(
|
AddEncryptionInfo(
|
||||||
_,
|
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
|
||||||
StrEq("data:text/plain;base64," + expected_pssh_base64),
|
|
||||||
StrEq("0x22222222222222222222222222222222"),
|
StrEq("0x22222222222222222222222222222222"),
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
|
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
|
||||||
|
|
||||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id,
|
||||||
// Use the second key id here so that it will be thre first one in the
|
// Use the second key id here so that it will be thre first one in the
|
||||||
// key_ids array in the JSON.
|
// key_ids array in the JSON.
|
||||||
|
@ -626,8 +634,11 @@ TEST_F(SimpleHlsNotifierTest, EncryptionScheme) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
const uint32_t stream_id =
|
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> key_id(16, 0x23);
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
const std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
@ -641,10 +652,9 @@ TEST_F(SimpleHlsNotifierTest, EncryptionScheme) {
|
||||||
*mock_media_playlist,
|
*mock_media_playlist,
|
||||||
AddEncryptionInfo(
|
AddEncryptionInfo(
|
||||||
MediaPlaylist::EncryptionMethod::kSampleAesCenc,
|
MediaPlaylist::EncryptionMethod::kSampleAesCenc,
|
||||||
StrEq("data:text/plain;base64," + expected_key_uri_base64),
|
StrEq("data:text/plain;base64," + expected_key_uri_base64), StrEq(""),
|
||||||
StrEq(""),
|
|
||||||
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
|
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
|
||||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
|
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,8 +663,11 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
const uint32_t stream_id =
|
const uint32_t stream_id =
|
||||||
SetupStream(kCencProtectionScheme, mock_media_playlist);
|
SetupStream(kCencProtectionScheme, mock_media_playlist, ¬ifier);
|
||||||
|
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
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_provider("someprovider");
|
||||||
widevine_pssh_data.set_content_id("contentid");
|
widevine_pssh_data.set_content_id("contentid");
|
||||||
const uint8_t kAnyKeyId[] = {
|
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));
|
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
||||||
|
@ -689,12 +700,11 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
|
||||||
|
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
AddEncryptionInfo(
|
AddEncryptionInfo(
|
||||||
_,
|
_, StrEq("data:text/plain;base64," + expected_pssh_base64),
|
||||||
StrEq("data:text/plain;base64," + expected_pssh_base64),
|
|
||||||
StrEq("0x11223344112233441122334411223344"),
|
StrEq("0x11223344112233441122334411223344"),
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _));
|
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));
|
stream_id, any_key_id, widevine_system_id_, iv, pssh_box));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -702,17 +712,18 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
const uint32_t stream_id =
|
const uint32_t stream_id =
|
||||||
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
|
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier);
|
||||||
|
|
||||||
media::WidevinePsshData widevine_pssh_data;
|
media::WidevinePsshData widevine_pssh_data;
|
||||||
widevine_pssh_data.set_provider("someprovider");
|
widevine_pssh_data.set_provider("someprovider");
|
||||||
widevine_pssh_data.set_content_id("contentid");
|
widevine_pssh_data.set_content_id("contentid");
|
||||||
const uint8_t kAnyKeyId[] = {
|
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));
|
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
||||||
|
@ -758,7 +769,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
|
||||||
LOG(INFO) << base_64_encoded_pssh;
|
LOG(INFO) << base_64_encoded_pssh;
|
||||||
|
|
||||||
std::vector<uint8_t> empty_iv;
|
std::vector<uint8_t> empty_iv;
|
||||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id,
|
||||||
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
||||||
widevine_system_id_, empty_iv, pssh_info.CreateBox()));
|
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> iv;
|
||||||
std::vector<uint8_t> pssh_data;
|
std::vector<uint8_t> pssh_data;
|
||||||
std::vector<uint8_t> key_id;
|
std::vector<uint8_t> key_id;
|
||||||
EXPECT_TRUE(notifier_.Init());
|
SimpleHlsNotifier notifier(HlsProfile::kOnDemandProfile,
|
||||||
EXPECT_FALSE(notifier_.NotifyEncryptionUpdate(1238u, key_id, system_id, iv,
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
pssh_data));
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
|
EXPECT_TRUE(notifier.Init());
|
||||||
|
EXPECT_FALSE(
|
||||||
|
notifier.NotifyEncryptionUpdate(1238u, key_id, system_id, iv, pssh_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
class LiveOrEventSimpleHlsNotifierTest
|
class LiveOrEventSimpleHlsNotifierTest
|
||||||
|
@ -818,7 +832,7 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
const std::string segment_name = "segmentname";
|
const std::string segment_name = "segmentname";
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
AddSegment(StrEq(kTestPrefix + segment_name), kStartTime,
|
AddSegment(StrEq(kTestPrefix + segment_name), kStartTime,
|
||||||
kDuration, kSize));
|
kDuration, _, kSize));
|
||||||
|
|
||||||
const double kLongestSegmentDuration = 11.3;
|
const double kLongestSegmentDuration = 11.3;
|
||||||
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
|
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
|
||||||
|
@ -837,16 +851,19 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
.AsUTF8Unsafe())))
|
.AsUTF8Unsafe())))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
SimpleHlsNotifier notifier(GetParam(),
|
||||||
InjectMediaPlaylistFactory(std::move(factory));
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
EXPECT_TRUE(notifier_.Init());
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
|
InjectMasterPlaylist(std::move(mock_master_playlist), ¬ifier);
|
||||||
|
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||||
|
EXPECT_TRUE(notifier.Init());
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
uint32_t stream_id;
|
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));
|
"groupid", &stream_id));
|
||||||
|
|
||||||
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id, segment_name, kStartTime,
|
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id, segment_name, kStartTime,
|
||||||
kDuration, kSize));
|
kDuration, 0, kSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
|
TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
|
||||||
|
@ -880,20 +897,23 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
|
||||||
*mock_master_playlist,
|
*mock_master_playlist,
|
||||||
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist2)));
|
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist2)));
|
||||||
|
|
||||||
|
SimpleHlsNotifier notifier(GetParam(),
|
||||||
|
kTestTimeShiftBufferDepth, kTestPrefix,
|
||||||
|
kAnyOutputDir, kMasterPlaylistName);
|
||||||
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
|
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
InjectMasterPlaylist(std::move(mock_master_playlist), ¬ifier);
|
||||||
InjectMediaPlaylistFactory(std::move(factory));
|
InjectMediaPlaylistFactory(std::move(factory), ¬ifier);
|
||||||
EXPECT_TRUE(notifier_.Init());
|
EXPECT_TRUE(notifier.Init());
|
||||||
|
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
uint32_t stream_id1;
|
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));
|
"groupid", &stream_id1));
|
||||||
uint32_t stream_id2;
|
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));
|
"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 double kLongestSegmentDuration = 11.3;
|
||||||
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
|
const uint32_t kTargetDuration = 12; // ceil(kLongestSegmentDuration).
|
||||||
EXPECT_CALL(*mock_media_playlist1, GetLongestSegmentDuration())
|
EXPECT_CALL(*mock_media_playlist1, GetLongestSegmentDuration())
|
||||||
|
@ -918,10 +938,10 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
|
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
|
||||||
.AsUTF8Unsafe())))
|
.AsUTF8Unsafe())))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id1, "segment_name", kStartTime,
|
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id1, "segment_name", kStartTime,
|
||||||
kDuration, kSize));
|
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())
|
EXPECT_CALL(*mock_media_playlist2, GetLongestSegmentDuration())
|
||||||
.WillOnce(Return(kLongestSegmentDuration));
|
.WillOnce(Return(kLongestSegmentDuration));
|
||||||
EXPECT_CALL(*mock_master_playlist_ptr, WriteMasterPlaylist(_, _))
|
EXPECT_CALL(*mock_master_playlist_ptr, WriteMasterPlaylist(_, _))
|
||||||
|
@ -933,8 +953,8 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
|
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
|
||||||
.AsUTF8Unsafe())))
|
.AsUTF8Unsafe())))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_TRUE(notifier_.NotifyNewSegment(stream_id2, "segment_name", kStartTime,
|
EXPECT_TRUE(notifier.NotifyNewSegment(stream_id2, "segment_name", kStartTime,
|
||||||
kDuration, kSize));
|
kDuration, 0, kSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(PlaylistTypes,
|
INSTANTIATE_TEST_CASE_P(PlaylistTypes,
|
||||||
|
|
|
@ -94,6 +94,11 @@ void HlsNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options,
|
||||||
next_key_system_infos_, &media_info);
|
next_key_system_infos_, &media_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
media_info_ = media_info;
|
||||||
|
if (!media_info_.has_segment_template()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const bool result = hls_notifier_->NotifyNewStream(
|
const bool result = hls_notifier_->NotifyNewStream(
|
||||||
media_info, playlist_name_, ext_x_media_name_, ext_x_media_group_id_,
|
media_info, playlist_name_, ext_x_media_name_, ext_x_media_group_id_,
|
||||||
&stream_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
|
// Don't flush the notifier here. Flushing here would write all the playlists
|
||||||
// before all Media Playlists are read. Which could cause problems
|
// before all Media Playlists are read. Which could cause problems
|
||||||
// setting the correct EXT-X-TARGETDURATION.
|
// 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,
|
void HlsNotifyMuxerListener::OnNewSegment(const std::string& file_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) {
|
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(
|
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.";
|
LOG_IF(WARNING, !result) << "Failed to add new segment.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
#include "packager/base/macros.h"
|
||||||
#include "packager/media/event/muxer_listener.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 {
|
namespace shaka {
|
||||||
|
|
||||||
|
@ -75,6 +77,11 @@ class HlsNotifyMuxerListener : public MuxerListener {
|
||||||
std::vector<ProtectionSystemSpecificInfo> next_key_system_infos_;
|
std::vector<ProtectionSystemSpecificInfo> next_key_system_infos_;
|
||||||
FourCC protection_scheme_ = FOURCC_NULL;
|
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);
|
DISALLOW_COPY_AND_ASSIGN(HlsNotifyMuxerListener);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,12 @@ class MockHlsNotifier : public hls::HlsNotifier {
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::string& group_id,
|
const std::string& group_id,
|
||||||
uint32_t* stream_id));
|
uint32_t* stream_id));
|
||||||
MOCK_METHOD5(NotifyNewSegment,
|
MOCK_METHOD6(NotifyNewSegment,
|
||||||
bool(uint32_t stream_id,
|
bool(uint32_t stream_id,
|
||||||
const std::string& segment_name,
|
const std::string& segment_name,
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
uint64_t start_byte_offset,
|
||||||
uint64_t size));
|
uint64_t size));
|
||||||
MOCK_METHOD5(
|
MOCK_METHOD5(
|
||||||
NotifyEncryptionUpdate,
|
NotifyEncryptionUpdate,
|
||||||
|
@ -125,6 +126,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) {
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
MuxerOptions muxer_options;
|
MuxerOptions muxer_options;
|
||||||
|
muxer_options.segment_template = "$Number$.mp4";
|
||||||
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
||||||
MuxerListener::kContainerMpeg2ts);
|
MuxerListener::kContainerMpeg2ts);
|
||||||
}
|
}
|
||||||
|
@ -156,6 +158,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) {
|
||||||
std::shared_ptr<StreamInfo> video_stream_info =
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
CreateVideoStreamInfo(video_params);
|
CreateVideoStreamInfo(video_params);
|
||||||
MuxerOptions muxer_options;
|
MuxerOptions muxer_options;
|
||||||
|
muxer_options.segment_template = "$Number$.mp4";
|
||||||
|
|
||||||
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
|
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
|
||||||
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
||||||
|
@ -196,6 +199,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStartBeforeMediaStart) {
|
||||||
std::shared_ptr<StreamInfo> video_stream_info =
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
CreateVideoStreamInfo(video_params);
|
CreateVideoStreamInfo(video_params);
|
||||||
MuxerOptions muxer_options;
|
MuxerOptions muxer_options;
|
||||||
|
muxer_options.segment_template = "$Number$.mp4";
|
||||||
|
|
||||||
// It doesn't really matter when this is called, could be called right away in
|
// 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
|
// 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 =
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
CreateVideoStreamInfo(video_params);
|
CreateVideoStreamInfo(video_params);
|
||||||
MuxerOptions muxer_options;
|
MuxerOptions muxer_options;
|
||||||
|
muxer_options.segment_template = "$Number$.mp4";
|
||||||
|
|
||||||
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
|
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
|
||||||
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
||||||
|
@ -250,6 +255,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReady) {
|
||||||
std::shared_ptr<StreamInfo> video_stream_info =
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
CreateVideoStreamInfo(video_params);
|
CreateVideoStreamInfo(video_params);
|
||||||
MuxerOptions muxer_options;
|
MuxerOptions muxer_options;
|
||||||
|
muxer_options.segment_template = "$Number$.mp4";
|
||||||
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
|
||||||
MuxerListener::kContainerMpeg2ts);
|
MuxerListener::kContainerMpeg2ts);
|
||||||
|
|
||||||
|
@ -317,15 +323,120 @@ TEST_F(HlsNotifyMuxerListenerTest, OnMediaEnd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlsNotifyMuxerListenerTest, OnNewSegment) {
|
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 kStartTime = 19283;
|
||||||
const uint64_t kDuration = 98028;
|
const uint64_t kDuration = 98028;
|
||||||
const uint64_t kFileSize = 756739;
|
const uint64_t kFileSize = 756739;
|
||||||
EXPECT_CALL(mock_notifier_,
|
EXPECT_CALL(mock_notifier_,
|
||||||
NotifyNewSegment(_, StrEq("new_segment_name10.ts"), kStartTime,
|
NotifyNewSegment(_, StrEq("new_segment_name10.ts"), kStartTime,
|
||||||
kDuration, kFileSize));
|
kDuration, _, kFileSize));
|
||||||
listener_.OnNewSegment("new_segment_name10.ts", kStartTime, kDuration,
|
listener_.OnNewSegment("new_segment_name10.ts", kStartTime, kDuration,
|
||||||
kFileSize);
|
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 media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -165,6 +165,7 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
|
||||||
if (muxer_options.segment_template.empty()) {
|
if (muxer_options.segment_template.empty()) {
|
||||||
media_info->set_media_file_name(muxer_options.output_file_name);
|
media_info->set_media_file_name(muxer_options.output_file_name);
|
||||||
} else {
|
} else {
|
||||||
|
if (!muxer_options.output_file_name.empty())
|
||||||
media_info->set_init_segment_name(muxer_options.output_file_name);
|
media_info->set_init_segment_name(muxer_options.output_file_name);
|
||||||
media_info->set_segment_template(muxer_options.segment_template);
|
media_info->set_segment_template(muxer_options.segment_template);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue