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 segment_file_size,
|
||||
uint64_t previous_segment_end_offset);
|
||||
~SegmentInfoEntry() override;
|
||||
|
||||
std::string ToString() override;
|
||||
double start_time() const { return start_time_; }
|
||||
double duration() const { return duration_; }
|
||||
|
||||
private:
|
||||
SegmentInfoEntry(const SegmentInfoEntry&) = delete;
|
||||
SegmentInfoEntry& operator=(const SegmentInfoEntry&) = delete;
|
||||
|
||||
const std::string file_name_;
|
||||
const double start_time_;
|
||||
const double duration_;
|
||||
|
@ -135,8 +137,6 @@ class SegmentInfoEntry : public HlsEntry {
|
|||
const uint64_t start_byte_offset_;
|
||||
const uint64_t segment_file_size_;
|
||||
const uint64_t previous_segment_end_offset_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SegmentInfoEntry);
|
||||
};
|
||||
|
||||
SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
||||
|
@ -154,7 +154,6 @@ SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
|||
start_byte_offset_(start_byte_offset),
|
||||
segment_file_size_(segment_file_size),
|
||||
previous_segment_end_offset_(previous_segment_end_offset) {}
|
||||
SegmentInfoEntry::~SegmentInfoEntry() {}
|
||||
|
||||
std::string SegmentInfoEntry::ToString() {
|
||||
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_versions);
|
||||
|
||||
~EncryptionInfoEntry() override;
|
||||
|
||||
std::string ToString() override;
|
||||
|
||||
private:
|
||||
EncryptionInfoEntry(const EncryptionInfoEntry&) = delete;
|
||||
EncryptionInfoEntry& operator=(const EncryptionInfoEntry&) = delete;
|
||||
|
||||
const MediaPlaylist::EncryptionMethod method_;
|
||||
const std::string url_;
|
||||
const std::string key_id_;
|
||||
const std::string iv_;
|
||||
const std::string key_format_;
|
||||
const std::string key_format_versions_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(EncryptionInfoEntry);
|
||||
};
|
||||
|
||||
EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
||||
|
@ -207,8 +205,6 @@ EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
|||
key_format_(key_format),
|
||||
key_format_versions_(key_format_versions) {}
|
||||
|
||||
EncryptionInfoEntry::~EncryptionInfoEntry() {}
|
||||
|
||||
std::string EncryptionInfoEntry::ToString() {
|
||||
std::string method_attribute;
|
||||
if (method_ == MediaPlaylist::EncryptionMethod::kSampleAes) {
|
||||
|
@ -242,23 +238,39 @@ class DiscontinuityEntry : public HlsEntry {
|
|||
public:
|
||||
DiscontinuityEntry();
|
||||
|
||||
~DiscontinuityEntry() override;
|
||||
|
||||
std::string ToString() override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(DiscontinuityEntry);
|
||||
DiscontinuityEntry(const DiscontinuityEntry&) = delete;
|
||||
DiscontinuityEntry& operator=(const DiscontinuityEntry&) = delete;
|
||||
};
|
||||
|
||||
DiscontinuityEntry::DiscontinuityEntry()
|
||||
: HlsEntry(HlsEntry::EntryType::kExtDiscontinuity) {}
|
||||
|
||||
DiscontinuityEntry::~DiscontinuityEntry() {}
|
||||
|
||||
std::string DiscontinuityEntry::ToString() {
|
||||
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(
|
||||
const std::list<std::unique_ptr<HlsEntry>>& entries) {
|
||||
DCHECK(!entries.empty());
|
||||
|
@ -372,6 +384,10 @@ void MediaPlaylist::AddEncryptionInfo(MediaPlaylist::EncryptionMethod method,
|
|||
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) {
|
||||
if (!target_duration_set_) {
|
||||
SetTargetDuration(ceil(GetLongestSegmentDuration()));
|
||||
|
|
|
@ -27,6 +27,7 @@ class HlsEntry {
|
|||
kExtInf,
|
||||
kExtKey,
|
||||
kExtDiscontinuity,
|
||||
kExtPlacementOpportunity,
|
||||
};
|
||||
virtual ~HlsEntry();
|
||||
|
||||
|
@ -121,6 +122,10 @@ class MediaPlaylist {
|
|||
const std::string& key_format,
|
||||
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|.
|
||||
/// This does not close the file.
|
||||
/// 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
valid_video_media_info_.set_reference_time_scale(90000);
|
||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||
|
|
|
@ -38,6 +38,7 @@ class MockMediaPlaylist : public MediaPlaylist {
|
|||
const std::string& iv,
|
||||
const std::string& key_format,
|
||||
const std::string& key_format_versions));
|
||||
MOCK_METHOD0(AddPlacementOpportunity, void());
|
||||
MOCK_METHOD1(WriteToFile, bool(const std::string& file_path));
|
||||
MOCK_CONST_METHOD0(Bitrate, uint64_t());
|
||||
MOCK_CONST_METHOD0(GetLongestSegmentDuration, double());
|
||||
|
|
|
@ -367,11 +367,17 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SimpleHlsNotifier::NotifyCueEvent(uint32_t container_id,
|
||||
uint64_t timestamp) {
|
||||
NOTIMPLEMENTED();
|
||||
bool SimpleHlsNotifier::NotifyCueEvent(uint32_t stream_id, uint64_t timestamp) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
auto stream_iterator = stream_map_.find(stream_id);
|
||||
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(
|
||||
uint32_t stream_id,
|
||||
|
|
|
@ -877,6 +877,21 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
|
|||
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
|
||||
: public SimpleHlsNotifierTest,
|
||||
public ::testing::WithParamInterface<HlsPlaylistType> {
|
||||
|
|
Loading…
Reference in New Issue