Insert EXT-X-PLACEMENT-OPPORTUNITY on CueEvent
See https://support.google.com/dfp_premium/answer/7295798?hl=en Change-Id: I931427512cb695dc68d5c8c2ec214fe743161bcb
This commit is contained in:
parent
feabf770bf
commit
435d69c3a6
|
@ -121,13 +121,15 @@ class SegmentInfoEntry : public HlsEntry {
|
||||||
uint64_t start_byte_offset,
|
uint64_t start_byte_offset,
|
||||||
uint64_t segment_file_size,
|
uint64_t segment_file_size,
|
||||||
uint64_t previous_segment_end_offset);
|
uint64_t previous_segment_end_offset);
|
||||||
~SegmentInfoEntry() override;
|
|
||||||
|
|
||||||
std::string ToString() override;
|
std::string ToString() override;
|
||||||
double start_time() const { return start_time_; }
|
double start_time() const { return start_time_; }
|
||||||
double duration() const { return duration_; }
|
double duration() const { return duration_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SegmentInfoEntry(const SegmentInfoEntry&) = delete;
|
||||||
|
SegmentInfoEntry& operator=(const SegmentInfoEntry&) = delete;
|
||||||
|
|
||||||
const std::string file_name_;
|
const std::string file_name_;
|
||||||
const double start_time_;
|
const double start_time_;
|
||||||
const double duration_;
|
const double duration_;
|
||||||
|
@ -135,8 +137,6 @@ class SegmentInfoEntry : public HlsEntry {
|
||||||
const uint64_t start_byte_offset_;
|
const uint64_t start_byte_offset_;
|
||||||
const uint64_t segment_file_size_;
|
const uint64_t segment_file_size_;
|
||||||
const uint64_t previous_segment_end_offset_;
|
const uint64_t previous_segment_end_offset_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(SegmentInfoEntry);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
||||||
|
@ -154,7 +154,6 @@ SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
||||||
start_byte_offset_(start_byte_offset),
|
start_byte_offset_(start_byte_offset),
|
||||||
segment_file_size_(segment_file_size),
|
segment_file_size_(segment_file_size),
|
||||||
previous_segment_end_offset_(previous_segment_end_offset) {}
|
previous_segment_end_offset_(previous_segment_end_offset) {}
|
||||||
SegmentInfoEntry::~SegmentInfoEntry() {}
|
|
||||||
|
|
||||||
std::string SegmentInfoEntry::ToString() {
|
std::string SegmentInfoEntry::ToString() {
|
||||||
std::string result = base::StringPrintf("#EXTINF:%.3f,\n", duration_);
|
std::string result = base::StringPrintf("#EXTINF:%.3f,\n", duration_);
|
||||||
|
@ -178,19 +177,18 @@ class EncryptionInfoEntry : public HlsEntry {
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions);
|
const std::string& key_format_versions);
|
||||||
|
|
||||||
~EncryptionInfoEntry() override;
|
|
||||||
|
|
||||||
std::string ToString() override;
|
std::string ToString() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
EncryptionInfoEntry(const EncryptionInfoEntry&) = delete;
|
||||||
|
EncryptionInfoEntry& operator=(const EncryptionInfoEntry&) = delete;
|
||||||
|
|
||||||
const MediaPlaylist::EncryptionMethod method_;
|
const MediaPlaylist::EncryptionMethod method_;
|
||||||
const std::string url_;
|
const std::string url_;
|
||||||
const std::string key_id_;
|
const std::string key_id_;
|
||||||
const std::string iv_;
|
const std::string iv_;
|
||||||
const std::string key_format_;
|
const std::string key_format_;
|
||||||
const std::string key_format_versions_;
|
const std::string key_format_versions_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(EncryptionInfoEntry);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
||||||
|
@ -207,8 +205,6 @@ EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
||||||
key_format_(key_format),
|
key_format_(key_format),
|
||||||
key_format_versions_(key_format_versions) {}
|
key_format_versions_(key_format_versions) {}
|
||||||
|
|
||||||
EncryptionInfoEntry::~EncryptionInfoEntry() {}
|
|
||||||
|
|
||||||
std::string EncryptionInfoEntry::ToString() {
|
std::string EncryptionInfoEntry::ToString() {
|
||||||
std::string method_attribute;
|
std::string method_attribute;
|
||||||
if (method_ == MediaPlaylist::EncryptionMethod::kSampleAes) {
|
if (method_ == MediaPlaylist::EncryptionMethod::kSampleAes) {
|
||||||
|
@ -242,23 +238,39 @@ class DiscontinuityEntry : public HlsEntry {
|
||||||
public:
|
public:
|
||||||
DiscontinuityEntry();
|
DiscontinuityEntry();
|
||||||
|
|
||||||
~DiscontinuityEntry() override;
|
|
||||||
|
|
||||||
std::string ToString() override;
|
std::string ToString() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(DiscontinuityEntry);
|
DiscontinuityEntry(const DiscontinuityEntry&) = delete;
|
||||||
|
DiscontinuityEntry& operator=(const DiscontinuityEntry&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
DiscontinuityEntry::DiscontinuityEntry()
|
DiscontinuityEntry::DiscontinuityEntry()
|
||||||
: HlsEntry(HlsEntry::EntryType::kExtDiscontinuity) {}
|
: HlsEntry(HlsEntry::EntryType::kExtDiscontinuity) {}
|
||||||
|
|
||||||
DiscontinuityEntry::~DiscontinuityEntry() {}
|
|
||||||
|
|
||||||
std::string DiscontinuityEntry::ToString() {
|
std::string DiscontinuityEntry::ToString() {
|
||||||
return "#EXT-X-DISCONTINUITY\n";
|
return "#EXT-X-DISCONTINUITY\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PlacementOpportunityEntry : public HlsEntry {
|
||||||
|
public:
|
||||||
|
PlacementOpportunityEntry();
|
||||||
|
|
||||||
|
std::string ToString() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PlacementOpportunityEntry(const PlacementOpportunityEntry&) = delete;
|
||||||
|
PlacementOpportunityEntry& operator=(const PlacementOpportunityEntry&) =
|
||||||
|
delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
PlacementOpportunityEntry::PlacementOpportunityEntry()
|
||||||
|
: HlsEntry(HlsEntry::EntryType::kExtPlacementOpportunity) {}
|
||||||
|
|
||||||
|
std::string PlacementOpportunityEntry::ToString() {
|
||||||
|
return "#EXT-X-PLACEMENT-OPPORTUNITY\n";
|
||||||
|
}
|
||||||
|
|
||||||
double LatestSegmentStartTime(
|
double LatestSegmentStartTime(
|
||||||
const std::list<std::unique_ptr<HlsEntry>>& entries) {
|
const std::list<std::unique_ptr<HlsEntry>>& entries) {
|
||||||
DCHECK(!entries.empty());
|
DCHECK(!entries.empty());
|
||||||
|
@ -372,6 +384,10 @@ void MediaPlaylist::AddEncryptionInfo(MediaPlaylist::EncryptionMethod method,
|
||||||
method, url, key_id, iv, key_format, key_format_versions));
|
method, url, key_id, iv, key_format, key_format_versions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaPlaylist::AddPlacementOpportunity() {
|
||||||
|
entries_.emplace_back(new PlacementOpportunityEntry());
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
||||||
if (!target_duration_set_) {
|
if (!target_duration_set_) {
|
||||||
SetTargetDuration(ceil(GetLongestSegmentDuration()));
|
SetTargetDuration(ceil(GetLongestSegmentDuration()));
|
||||||
|
|
|
@ -27,6 +27,7 @@ class HlsEntry {
|
||||||
kExtInf,
|
kExtInf,
|
||||||
kExtKey,
|
kExtKey,
|
||||||
kExtDiscontinuity,
|
kExtDiscontinuity,
|
||||||
|
kExtPlacementOpportunity,
|
||||||
};
|
};
|
||||||
virtual ~HlsEntry();
|
virtual ~HlsEntry();
|
||||||
|
|
||||||
|
@ -121,6 +122,10 @@ class MediaPlaylist {
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions);
|
const std::string& key_format_versions);
|
||||||
|
|
||||||
|
/// Add #EXT-X-PLACEMENT-OPPORTUNITY for mid-roll ads. See
|
||||||
|
/// https://support.google.com/dfp_premium/answer/7295798?hl=en.
|
||||||
|
virtual void AddPlacementOpportunity();
|
||||||
|
|
||||||
/// Write the playlist to |file_path|.
|
/// Write the playlist to |file_path|.
|
||||||
/// This does not close the file.
|
/// This does not close the file.
|
||||||
/// If target duration is not set expliticly, this will try to find the target
|
/// If target duration is not set expliticly, this will try to find the target
|
||||||
|
|
|
@ -316,6 +316,35 @@ TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithSegments) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MediaPlaylistMultiSegmentTest,
|
||||||
|
WriteToFileWithSegmentsAndPlacementOpportunity) {
|
||||||
|
valid_video_media_info_.set_reference_time_scale(90000);
|
||||||
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
|
media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
|
||||||
|
kMBytes);
|
||||||
|
media_playlist_.AddPlacementOpportunity();
|
||||||
|
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
|
const char kExpectedOutput[] =
|
||||||
|
"#EXTM3U\n"
|
||||||
|
"#EXT-X-VERSION:6\n"
|
||||||
|
"## Generated with https://github.com/google/shaka-packager version "
|
||||||
|
"test\n"
|
||||||
|
"#EXT-X-TARGETDURATION:30\n"
|
||||||
|
"#EXT-X-PLAYLIST-TYPE:VOD\n"
|
||||||
|
"#EXTINF:10.000,\n"
|
||||||
|
"file1.ts\n"
|
||||||
|
"#EXT-X-PLACEMENT-OPPORTUNITY\n"
|
||||||
|
"#EXTINF:30.000,\n"
|
||||||
|
"file2.ts\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, WriteToFileWithEncryptionInfo) {
|
TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithEncryptionInfo) {
|
||||||
valid_video_media_info_.set_reference_time_scale(90000);
|
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_));
|
||||||
|
|
|
@ -38,6 +38,7 @@ class MockMediaPlaylist : public MediaPlaylist {
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions));
|
const std::string& key_format_versions));
|
||||||
|
MOCK_METHOD0(AddPlacementOpportunity, void());
|
||||||
MOCK_METHOD1(WriteToFile, bool(const std::string& file_path));
|
MOCK_METHOD1(WriteToFile, bool(const std::string& file_path));
|
||||||
MOCK_CONST_METHOD0(Bitrate, uint64_t());
|
MOCK_CONST_METHOD0(Bitrate, uint64_t());
|
||||||
MOCK_CONST_METHOD0(GetLongestSegmentDuration, double());
|
MOCK_CONST_METHOD0(GetLongestSegmentDuration, double());
|
||||||
|
|
|
@ -367,10 +367,16 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleHlsNotifier::NotifyCueEvent(uint32_t container_id,
|
bool SimpleHlsNotifier::NotifyCueEvent(uint32_t stream_id, uint64_t timestamp) {
|
||||||
uint64_t timestamp) {
|
base::AutoLock auto_lock(lock_);
|
||||||
NOTIMPLEMENTED();
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
return false;
|
if (stream_iterator == stream_map_.end()) {
|
||||||
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto& media_playlist = stream_iterator->second->media_playlist;
|
||||||
|
media_playlist->AddPlacementOpportunity();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
||||||
|
|
|
@ -877,6 +877,21 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
|
||||||
notifier.NotifyEncryptionUpdate(1238u, key_id, system_id, iv, pssh_data));
|
notifier.NotifyEncryptionUpdate(1238u, key_id, system_id, iv, pssh_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SimpleHlsNotifierTest, NotifyCueEvent) {
|
||||||
|
// Pointer released by SimpleHlsNotifier.
|
||||||
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
|
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
|
||||||
|
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
|
||||||
|
kTestPrefix, kIdentityKeyUri, kAnyOutputDir,
|
||||||
|
kMasterPlaylistName);
|
||||||
|
const uint32_t stream_id =
|
||||||
|
SetupStream(kCencProtectionScheme, mock_media_playlist, ¬ifier);
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock_media_playlist, AddPlacementOpportunity());
|
||||||
|
const uint64_t kCueEventTimestamp = 12345;
|
||||||
|
EXPECT_TRUE(notifier.NotifyCueEvent(stream_id, kCueEventTimestamp));
|
||||||
|
}
|
||||||
|
|
||||||
class LiveOrEventSimpleHlsNotifierTest
|
class LiveOrEventSimpleHlsNotifierTest
|
||||||
: public SimpleHlsNotifierTest,
|
: public SimpleHlsNotifierTest,
|
||||||
public ::testing::WithParamInterface<HlsPlaylistType> {
|
public ::testing::WithParamInterface<HlsPlaylistType> {
|
||||||
|
|
Loading…
Reference in New Issue