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:
KongQun Yang 2018-01-11 17:33:37 -08:00
parent feabf770bf
commit 435d69c3a6
6 changed files with 91 additions and 19 deletions

View File

@ -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()));

View File

@ -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

View File

@ -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_));

View File

@ -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());

View File

@ -367,10 +367,16 @@ 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(

View File

@ -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, &notifier);
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> {