Propage CueEvent to MpdNotifier/HlsNotifier
Change-Id: I9828af11a28300d20cc8742251bbe1b4ebfdbad1
This commit is contained in:
parent
b0edec8c40
commit
db74d6756e
|
@ -57,6 +57,11 @@ class HlsNotifier {
|
||||||
uint64_t start_byte_offset,
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) = 0;
|
uint64_t size) = 0;
|
||||||
|
|
||||||
|
/// @param stream_id is the value set by NotifyNewStream().
|
||||||
|
/// @param timestamp is the timestamp of the CueEvent.
|
||||||
|
/// @return true on success, false otherwise.
|
||||||
|
virtual bool NotifyCueEvent(uint32_t stream_id, uint64_t timestamp) = 0;
|
||||||
|
|
||||||
/// @param stream_id is the value set by NotifyNewStream().
|
/// @param stream_id is the value set by NotifyNewStream().
|
||||||
/// @param key_id is the key ID for the stream.
|
/// @param key_id is the key ID for the stream.
|
||||||
/// @param system_id is the DRM system ID in e.g. PSSH boxes. For example this
|
/// @param system_id is the DRM system ID in e.g. PSSH boxes. For example this
|
||||||
|
|
|
@ -367,6 +367,12 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SimpleHlsNotifier::NotifyCueEvent(uint32_t container_id,
|
||||||
|
uint64_t timestamp) {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
||||||
uint32_t stream_id,
|
uint32_t stream_id,
|
||||||
const std::vector<uint8_t>& key_id,
|
const std::vector<uint8_t>& key_id,
|
||||||
|
|
|
@ -73,6 +73,7 @@ class SimpleHlsNotifier : public HlsNotifier {
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t start_byte_offset,
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) override;
|
uint64_t size) override;
|
||||||
|
bool NotifyCueEvent(uint32_t container_id, uint64_t timestamp) override;
|
||||||
bool NotifyEncryptionUpdate(
|
bool NotifyEncryptionUpdate(
|
||||||
uint32_t stream_id,
|
uint32_t stream_id,
|
||||||
const std::vector<uint8_t>& key_id,
|
const std::vector<uint8_t>& key_id,
|
||||||
|
|
|
@ -60,6 +60,30 @@ const uint8_t kData[]{
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
std::string StreamDataTypeToString(StreamDataType stream_data_type) {
|
||||||
|
switch (stream_data_type) {
|
||||||
|
case StreamDataType::kStreamInfo:
|
||||||
|
return "stream info";
|
||||||
|
case StreamDataType::kMediaSample:
|
||||||
|
return "media sample";
|
||||||
|
case StreamDataType::kTextSample:
|
||||||
|
return "text sample";
|
||||||
|
case StreamDataType::kSegmentInfo:
|
||||||
|
return "segment info";
|
||||||
|
case StreamDataType::kScte35Event:
|
||||||
|
return "scte35 event";
|
||||||
|
case StreamDataType::kCueEvent:
|
||||||
|
return "cue event";
|
||||||
|
case StreamDataType::kUnknown:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BoolToString(bool value) {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
bool FakeInputMediaHandler::ValidateOutputStreamIndex(size_t index) const {
|
bool FakeInputMediaHandler::ValidateOutputStreamIndex(size_t index) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -170,6 +194,7 @@ std::shared_ptr<MediaSample> MediaHandlerTestBase::GetMediaSample(
|
||||||
std::shared_ptr<MediaSample> sample =
|
std::shared_ptr<MediaSample> sample =
|
||||||
MediaSample::CopyFrom(data, data_length, nullptr, 0, is_keyframe);
|
MediaSample::CopyFrom(data, data_length, nullptr, 0, is_keyframe);
|
||||||
sample->set_dts(timestamp);
|
sample->set_dts(timestamp);
|
||||||
|
sample->set_pts(timestamp);
|
||||||
sample->set_duration(duration);
|
sample->set_duration(duration);
|
||||||
|
|
||||||
return sample;
|
return sample;
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#ifndef PACKAGER_MEDIA_BASE_MEDIA_HANDLER_TEST_BASE_H_
|
||||||
|
#define PACKAGER_MEDIA_BASE_MEDIA_HANDLER_TEST_BASE_H_
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
@ -13,16 +16,25 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
std::string StreamDataTypeToString(StreamDataType stream_data_type);
|
||||||
|
std::string BoolToString(bool value);
|
||||||
|
|
||||||
MATCHER_P(IsStreamInfo, stream_index, "") {
|
MATCHER_P(IsStreamInfo, stream_index, "") {
|
||||||
return arg->stream_index == stream_index &&
|
return arg->stream_index == stream_index &&
|
||||||
arg->stream_data_type == StreamDataType::kStreamInfo;
|
arg->stream_data_type == StreamDataType::kStreamInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
MATCHER_P3(IsStreamInfo, stream_index, time_scale, encrypted, "") {
|
MATCHER_P3(IsStreamInfo, stream_index, time_scale, encrypted, "") {
|
||||||
*result_listener << "which is (" << stream_index << "," << time_scale << ","
|
if (arg->stream_data_type != StreamDataType::kStreamInfo) {
|
||||||
<< (encrypted ? "encrypted" : "not encrypted") << ")";
|
*result_listener << "which is "
|
||||||
|
<< StreamDataTypeToString(arg->stream_data_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*result_listener << "which is (" << arg->stream_index << ","
|
||||||
|
<< arg->stream_info->time_scale() << ","
|
||||||
|
<< BoolToString(arg->stream_info->is_encrypted()) << ")";
|
||||||
return arg->stream_index == stream_index &&
|
return arg->stream_index == stream_index &&
|
||||||
arg->stream_data_type == StreamDataType::kStreamInfo &&
|
|
||||||
arg->stream_info->time_scale() == time_scale &&
|
arg->stream_info->time_scale() == time_scale &&
|
||||||
arg->stream_info->is_encrypted() == encrypted;
|
arg->stream_info->is_encrypted() == encrypted;
|
||||||
}
|
}
|
||||||
|
@ -34,12 +46,18 @@ MATCHER_P5(IsSegmentInfo,
|
||||||
subsegment,
|
subsegment,
|
||||||
encrypted,
|
encrypted,
|
||||||
"") {
|
"") {
|
||||||
*result_listener << "which is (" << stream_index << "," << start_timestamp
|
if (arg->stream_data_type != StreamDataType::kSegmentInfo) {
|
||||||
<< "," << duration << ","
|
*result_listener << "which is "
|
||||||
<< (subsegment ? "subsegment" : "not subsegment") << ","
|
<< StreamDataTypeToString(arg->stream_data_type);
|
||||||
<< (encrypted ? "encrypted" : "not encrypted") << ")";
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*result_listener << "which is (" << arg->stream_index << ","
|
||||||
|
<< arg->segment_info->start_timestamp << ","
|
||||||
|
<< arg->segment_info->duration << ","
|
||||||
|
<< BoolToString(arg->segment_info->is_subsegment) << ","
|
||||||
|
<< BoolToString(arg->segment_info->is_encrypted) << ")";
|
||||||
return arg->stream_index == stream_index &&
|
return arg->stream_index == stream_index &&
|
||||||
arg->stream_data_type == StreamDataType::kSegmentInfo &&
|
|
||||||
arg->segment_info->start_timestamp == start_timestamp &&
|
arg->segment_info->start_timestamp == start_timestamp &&
|
||||||
arg->segment_info->duration == duration &&
|
arg->segment_info->duration == duration &&
|
||||||
arg->segment_info->is_subsegment == subsegment &&
|
arg->segment_info->is_subsegment == subsegment &&
|
||||||
|
@ -54,12 +72,14 @@ MATCHER_P6(MatchEncryptionConfig,
|
||||||
constant_iv,
|
constant_iv,
|
||||||
key_id,
|
key_id,
|
||||||
"") {
|
"") {
|
||||||
*result_listener << "which is (" << FourCCToString(protection_scheme) << ","
|
*result_listener << "which is (" << FourCCToString(arg.protection_scheme)
|
||||||
<< static_cast<int>(crypt_byte_block) << ","
|
<< "," << static_cast<int>(arg.crypt_byte_block) << ","
|
||||||
<< static_cast<int>(skip_byte_block) << ","
|
<< static_cast<int>(arg.skip_byte_block) << ","
|
||||||
<< static_cast<int>(per_sample_iv_size) << ","
|
<< static_cast<int>(arg.per_sample_iv_size) << ","
|
||||||
<< base::HexEncode(constant_iv.data(), constant_iv.size())
|
<< base::HexEncode(arg.constant_iv.data(),
|
||||||
<< "," << base::HexEncode(key_id.data(), key_id.size())
|
arg.constant_iv.size())
|
||||||
|
<< ","
|
||||||
|
<< base::HexEncode(arg.key_id.data(), arg.key_id.size())
|
||||||
<< ")";
|
<< ")";
|
||||||
return arg.protection_scheme == protection_scheme &&
|
return arg.protection_scheme == protection_scheme &&
|
||||||
arg.crypt_byte_block == crypt_byte_block &&
|
arg.crypt_byte_block == crypt_byte_block &&
|
||||||
|
@ -69,25 +89,51 @@ MATCHER_P6(MatchEncryptionConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
MATCHER_P4(IsMediaSample, stream_index, timestamp, duration, encrypted, "") {
|
MATCHER_P4(IsMediaSample, stream_index, timestamp, duration, encrypted, "") {
|
||||||
*result_listener << "which is (" << stream_index << "," << timestamp << ","
|
if (arg->stream_data_type != StreamDataType::kMediaSample) {
|
||||||
<< duration << ","
|
*result_listener << "which is "
|
||||||
<< (encrypted ? "encrypted" : "not encrypted") << ")";
|
<< StreamDataTypeToString(arg->stream_data_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*result_listener << "which is (" << stream_index << ","
|
||||||
|
<< arg->media_sample->dts() << ","
|
||||||
|
<< arg->media_sample->duration() << ","
|
||||||
|
<< BoolToString(arg->media_sample->is_encrypted()) << ")";
|
||||||
return arg->stream_index == stream_index &&
|
return arg->stream_index == stream_index &&
|
||||||
arg->stream_data_type == StreamDataType::kMediaSample &&
|
|
||||||
arg->media_sample->dts() == timestamp &&
|
arg->media_sample->dts() == timestamp &&
|
||||||
arg->media_sample->duration() == duration &&
|
arg->media_sample->duration() == duration &&
|
||||||
arg->media_sample->is_encrypted() == encrypted;
|
arg->media_sample->is_encrypted() == encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
MATCHER_P5(IsTextSample, id, start_time, end_time, settings, payload, "") {
|
MATCHER_P5(IsTextSample, id, start_time, end_time, settings, payload, "") {
|
||||||
return arg->stream_data_type == StreamDataType::kTextSample &&
|
if (arg->stream_data_type != StreamDataType::kTextSample) {
|
||||||
arg->text_sample->id() == id &&
|
*result_listener << "which is "
|
||||||
|
<< StreamDataTypeToString(arg->stream_data_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*result_listener << "which is (" << arg->text_sample->id() << ","
|
||||||
|
<< arg->text_sample->start_time() << ","
|
||||||
|
<< arg->text_sample->EndTime() << ","
|
||||||
|
<< arg->text_sample->settings() << ","
|
||||||
|
<< arg->text_sample->payload() << ")";
|
||||||
|
return arg->text_sample->id() == id &&
|
||||||
arg->text_sample->start_time() == start_time &&
|
arg->text_sample->start_time() == start_time &&
|
||||||
arg->text_sample->EndTime() == end_time &&
|
arg->text_sample->EndTime() == end_time &&
|
||||||
arg->text_sample->settings() == settings &&
|
arg->text_sample->settings() == settings &&
|
||||||
arg->text_sample->payload() == payload;
|
arg->text_sample->payload() == payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MATCHER_P2(IsCueEvent, stream_index, timestamp, "") {
|
||||||
|
if (arg->stream_data_type != StreamDataType::kCueEvent) {
|
||||||
|
*result_listener << "which is "
|
||||||
|
<< StreamDataTypeToString(arg->stream_data_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*result_listener << "which is (" << arg->stream_index << ","
|
||||||
|
<< arg->cue_event->timestamp << ")";
|
||||||
|
return arg->stream_index == stream_index &&
|
||||||
|
arg->cue_event->timestamp == timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
class FakeInputMediaHandler : public MediaHandler {
|
class FakeInputMediaHandler : public MediaHandler {
|
||||||
public:
|
public:
|
||||||
using MediaHandler::Dispatch;
|
using MediaHandler::Dispatch;
|
||||||
|
@ -234,3 +280,5 @@ class MediaHandlerGraphTestBase : public MediaHandlerTestBase {
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
||||||
|
#endif // PACKAGER_MEDIA_BASE_MEDIA_HANDLER_TEST_BASE_H_
|
||||||
|
|
|
@ -72,6 +72,12 @@ Status Muxer::Process(std::unique_ptr<StreamData> stream_data) {
|
||||||
case StreamDataType::kMediaSample:
|
case StreamDataType::kMediaSample:
|
||||||
return AddSample(stream_data->stream_index,
|
return AddSample(stream_data->stream_index,
|
||||||
*stream_data->media_sample);
|
*stream_data->media_sample);
|
||||||
|
case StreamDataType::kCueEvent:
|
||||||
|
if (muxer_listener_) {
|
||||||
|
muxer_listener_->OnCueEvent(stream_data->cue_event->timestamp,
|
||||||
|
stream_data->cue_event->cue_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
VLOG(3) << "Stream data type "
|
VLOG(3) << "Stream data type "
|
||||||
<< static_cast<int>(stream_data->stream_data_type) << " ignored.";
|
<< static_cast<int>(stream_data->stream_data_type) << " ignored.";
|
||||||
|
|
|
@ -149,6 +149,7 @@ Status ChunkingHandler::ProcessMediaSample(const MediaSample* sample) {
|
||||||
// Check if we need to terminate the current (sub)segment.
|
// Check if we need to terminate the current (sub)segment.
|
||||||
bool new_segment = false;
|
bool new_segment = false;
|
||||||
bool new_subsegment = false;
|
bool new_subsegment = false;
|
||||||
|
std::shared_ptr<CueEvent> cue_event;
|
||||||
if (is_key_frame || !chunking_params_.segment_sap_aligned) {
|
if (is_key_frame || !chunking_params_.segment_sap_aligned) {
|
||||||
const int64_t segment_index = timestamp / segment_duration_;
|
const int64_t segment_index = timestamp / segment_duration_;
|
||||||
if (segment_index != current_segment_index_) {
|
if (segment_index != current_segment_index_) {
|
||||||
|
@ -161,11 +162,16 @@ Status ChunkingHandler::ProcessMediaSample(const MediaSample* sample) {
|
||||||
// events that may be very close to each other.
|
// events that may be very close to each other.
|
||||||
while (!scte35_events_.empty() &&
|
while (!scte35_events_.empty() &&
|
||||||
(scte35_events_.top()->scte35_event->start_time <= timestamp)) {
|
(scte35_events_.top()->scte35_event->start_time <= timestamp)) {
|
||||||
if (!new_segment) {
|
// For simplicity, don't change |current_segment_index_|.
|
||||||
// Reset subsegment index but don't change current_segment_index_.
|
|
||||||
current_subsegment_index_ = 0;
|
current_subsegment_index_ = 0;
|
||||||
new_segment = true;
|
new_segment = true;
|
||||||
}
|
|
||||||
|
cue_event = std::make_shared<CueEvent>();
|
||||||
|
// Use PTS instead of DTS for cue event timestamp.
|
||||||
|
cue_event->timestamp = sample->pts();
|
||||||
|
cue_event->cue_data = scte35_events_.top()->scte35_event->cue_data;
|
||||||
|
VLOG(1) << "Chunked at " << timestamp << " for Ad Cue.";
|
||||||
|
|
||||||
scte35_events_.pop();
|
scte35_events_.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,6 +196,9 @@ Status ChunkingHandler::ProcessMediaSample(const MediaSample* sample) {
|
||||||
if (new_segment) {
|
if (new_segment) {
|
||||||
status.Update(DispatchSegmentInfoForAllStreams());
|
status.Update(DispatchSegmentInfoForAllStreams());
|
||||||
segment_info_[main_stream_index_]->start_timestamp = timestamp;
|
segment_info_[main_stream_index_]->start_timestamp = timestamp;
|
||||||
|
|
||||||
|
if (cue_event)
|
||||||
|
status.Update(DispatchCueEventForAllStreams(std::move(cue_event)));
|
||||||
}
|
}
|
||||||
if (subsegment_duration_ > 0 && (new_segment || new_subsegment)) {
|
if (subsegment_duration_ > 0 && (new_segment || new_subsegment)) {
|
||||||
status.Update(DispatchSubsegmentInfoForAllStreams());
|
status.Update(DispatchSubsegmentInfoForAllStreams());
|
||||||
|
@ -273,5 +282,17 @@ Status ChunkingHandler::DispatchSubsegmentInfoForAllStreams() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status ChunkingHandler::DispatchCueEventForAllStreams(
|
||||||
|
std::shared_ptr<CueEvent> cue_event) {
|
||||||
|
Status status;
|
||||||
|
for (size_t i = 0; i < segment_info_.size() && status.ok(); ++i) {
|
||||||
|
std::shared_ptr<CueEvent> new_cue_event(new CueEvent(*cue_event));
|
||||||
|
new_cue_event->timestamp = cue_event->timestamp * time_scales_[i] /
|
||||||
|
time_scales_[main_stream_index_];
|
||||||
|
status.Update(DispatchCueEvent(i, std::move(new_cue_event)));
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -71,6 +71,7 @@ class ChunkingHandler : public MediaHandler {
|
||||||
// The (sub)segments are aligned and dispatched together.
|
// The (sub)segments are aligned and dispatched together.
|
||||||
Status DispatchSegmentInfoForAllStreams();
|
Status DispatchSegmentInfoForAllStreams();
|
||||||
Status DispatchSubsegmentInfoForAllStreams();
|
Status DispatchSubsegmentInfoForAllStreams();
|
||||||
|
Status DispatchCueEventForAllStreams(std::shared_ptr<CueEvent> cue_event);
|
||||||
|
|
||||||
const ChunkingParams chunking_params_;
|
const ChunkingParams chunking_params_;
|
||||||
|
|
||||||
|
|
|
@ -286,5 +286,43 @@ TEST_F(ChunkingHandlerTest, AudioAndVideo) {
|
||||||
EXPECT_THAT(GetOutputStreamDataVector(), IsEmpty());
|
EXPECT_THAT(GetOutputStreamDataVector(), IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ChunkingHandlerTest, Scte35Event) {
|
||||||
|
ChunkingParams chunking_params;
|
||||||
|
chunking_params.segment_duration_in_seconds = 1;
|
||||||
|
chunking_params.subsegment_duration_in_seconds = 0.5;
|
||||||
|
SetUpChunkingHandler(1, chunking_params);
|
||||||
|
|
||||||
|
ASSERT_OK(Process(StreamData::FromStreamInfo(
|
||||||
|
kStreamIndex0, GetVideoStreamInfo(kTimeScale1))));
|
||||||
|
|
||||||
|
const int64_t kVideoStartTimestamp = 12345;
|
||||||
|
|
||||||
|
auto scte35_event = std::make_shared<Scte35Event>();
|
||||||
|
scte35_event->start_time = kVideoStartTimestamp + kDuration1;
|
||||||
|
ASSERT_OK(Process(StreamData::FromScte35Event(kStreamIndex0, scte35_event)));
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
const bool is_key_frame = true;
|
||||||
|
ASSERT_OK(Process(StreamData::FromMediaSample(
|
||||||
|
kStreamIndex0, GetMediaSample(kVideoStartTimestamp + i * kDuration1,
|
||||||
|
kDuration1, is_key_frame))));
|
||||||
|
}
|
||||||
|
EXPECT_THAT(
|
||||||
|
GetOutputStreamDataVector(),
|
||||||
|
ElementsAre(
|
||||||
|
IsStreamInfo(kStreamIndex0, kTimeScale1, !kEncrypted),
|
||||||
|
IsMediaSample(kStreamIndex0, kVideoStartTimestamp, kDuration1,
|
||||||
|
!kEncrypted),
|
||||||
|
// A new segment is created due to the existance of Cue.
|
||||||
|
IsSegmentInfo(kStreamIndex0, kVideoStartTimestamp, kDuration1,
|
||||||
|
!kIsSubsegment, !kEncrypted),
|
||||||
|
IsCueEvent(kStreamIndex0,
|
||||||
|
static_cast<double>(kVideoStartTimestamp + kDuration1)),
|
||||||
|
IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 1,
|
||||||
|
kDuration1, !kEncrypted),
|
||||||
|
IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 2,
|
||||||
|
kDuration1, !kEncrypted)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -65,5 +65,12 @@ void CombinedMuxerListener::OnNewSegment(const std::string& file_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CombinedMuxerListener::OnCueEvent(uint64_t timestamp,
|
||||||
|
const std::string& cue_data) {
|
||||||
|
for (auto& listener : muxer_listeners_) {
|
||||||
|
listener->OnCueEvent(timestamp, cue_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -39,6 +39,7 @@ class CombinedMuxerListener : public MuxerListener {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) override;
|
uint64_t segment_file_size) override;
|
||||||
|
void OnCueEvent(uint64_t timestamp, const std::string& cue_data) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<std::unique_ptr<MuxerListener>> muxer_listeners_;
|
std::list<std::unique_ptr<MuxerListener>> muxer_listeners_;
|
||||||
|
|
|
@ -155,16 +155,19 @@ void HlsNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
|
||||||
const std::vector<Range>& subsegment_ranges =
|
const std::vector<Range>& subsegment_ranges =
|
||||||
media_ranges.subsegment_ranges;
|
media_ranges.subsegment_ranges;
|
||||||
size_t num_subsegments = subsegment_ranges.size();
|
size_t num_subsegments = subsegment_ranges.size();
|
||||||
if (segment_infos_.size() != num_subsegments) {
|
if (subsegments_.size() != num_subsegments) {
|
||||||
LOG(WARNING) << "Number of subsegment ranges (" << num_subsegments
|
LOG(WARNING) << "Number of subsegment ranges (" << num_subsegments
|
||||||
<< ") does not match the number of subsegments notified to "
|
<< ") does not match the number of subsegments notified to "
|
||||||
"OnNewSegment() ("
|
"OnNewSegment() ("
|
||||||
<< segment_infos_.size() << ").";
|
<< subsegments_.size() << ").";
|
||||||
num_subsegments = std::min(segment_infos_.size(), num_subsegments);
|
num_subsegments = std::min(subsegments_.size(), num_subsegments);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < num_subsegments; ++i) {
|
for (size_t i = 0; i < num_subsegments; ++i) {
|
||||||
const Range& range = subsegment_ranges[i];
|
const Range& range = subsegment_ranges[i];
|
||||||
const SegmentInfo& subsegment_info = segment_infos_[i];
|
const SubsegmentInfo& subsegment_info = subsegments_[i];
|
||||||
|
if (subsegment_info.cue_break) {
|
||||||
|
hls_notifier_->NotifyCueEvent(stream_id_, subsegment_info.start_time);
|
||||||
|
}
|
||||||
hls_notifier_->NotifyNewSegment(
|
hls_notifier_->NotifyNewSegment(
|
||||||
stream_id_, media_info_.media_file_name(), subsegment_info.start_time,
|
stream_id_, media_info_.media_file_name(), subsegment_info.start_time,
|
||||||
subsegment_info.duration, range.start, range.end + 1 - range.start);
|
subsegment_info.duration, range.start, range.end + 1 - range.start);
|
||||||
|
@ -177,10 +180,10 @@ void HlsNotifyMuxerListener::OnNewSegment(const std::string& file_name,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) {
|
uint64_t segment_file_size) {
|
||||||
if (!media_info_.has_segment_template()) {
|
if (!media_info_.has_segment_template()) {
|
||||||
SegmentInfo info;
|
SubsegmentInfo subsegment = {start_time, duration, segment_file_size,
|
||||||
info.duration = duration;
|
next_subsegment_contains_cue_break_};
|
||||||
info.start_time = start_time;
|
subsegments_.push_back(subsegment);
|
||||||
segment_infos_.push_back(info);
|
next_subsegment_contains_cue_break_ = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// For multisegment, it always starts from the beginning of the file.
|
// For multisegment, it always starts from the beginning of the file.
|
||||||
|
@ -191,5 +194,14 @@ void HlsNotifyMuxerListener::OnNewSegment(const std::string& file_name,
|
||||||
LOG_IF(WARNING, !result) << "Failed to add new segment.";
|
LOG_IF(WARNING, !result) << "Failed to add new segment.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HlsNotifyMuxerListener::OnCueEvent(uint64_t timestamp,
|
||||||
|
const std::string& cue_data) {
|
||||||
|
if (!media_info_.has_segment_template()) {
|
||||||
|
next_subsegment_contains_cue_break_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hls_notifier_->NotifyCueEvent(stream_id_, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#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/media_info.pb.h"
|
||||||
#include "packager/mpd/base/segment_info.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
|
@ -59,9 +58,18 @@ class HlsNotifyMuxerListener : public MuxerListener {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) override;
|
uint64_t segment_file_size) override;
|
||||||
|
void OnCueEvent(uint64_t timestamp, const std::string& cue_data) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// This stores data passed into OnNewSegment() for VOD.
|
||||||
|
struct SubsegmentInfo {
|
||||||
|
uint64_t start_time;
|
||||||
|
uint64_t duration;
|
||||||
|
uint64_t segment_file_size;
|
||||||
|
bool cue_break;
|
||||||
|
};
|
||||||
|
|
||||||
const std::string playlist_name_;
|
const std::string playlist_name_;
|
||||||
const std::string ext_x_media_name_;
|
const std::string ext_x_media_name_;
|
||||||
const std::string ext_x_media_group_id_;
|
const std::string ext_x_media_group_id_;
|
||||||
|
@ -79,7 +87,9 @@ class HlsNotifyMuxerListener : public MuxerListener {
|
||||||
// MediaInfo passed to Notifier::OnNewStream(). Mainly for single segment
|
// MediaInfo passed to Notifier::OnNewStream(). Mainly for single segment
|
||||||
// playlists.
|
// playlists.
|
||||||
MediaInfo media_info_;
|
MediaInfo media_info_;
|
||||||
std::vector<SegmentInfo> segment_infos_;
|
std::vector<SubsegmentInfo> subsegments_;
|
||||||
|
// Whether the next subsegment contains AdCue break.
|
||||||
|
bool next_subsegment_contains_cue_break_ = false;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(HlsNotifyMuxerListener);
|
DISALLOW_COPY_AND_ASSIGN(HlsNotifyMuxerListener);
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,7 @@ class MockHlsNotifier : public hls::HlsNotifier {
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t start_byte_offset,
|
uint64_t start_byte_offset,
|
||||||
uint64_t size));
|
uint64_t size));
|
||||||
|
MOCK_METHOD2(NotifyCueEvent, bool(uint32_t stream_id, uint64_t timestamp));
|
||||||
MOCK_METHOD5(
|
MOCK_METHOD5(
|
||||||
NotifyEncryptionUpdate,
|
NotifyEncryptionUpdate,
|
||||||
bool(uint32_t stream_id,
|
bool(uint32_t stream_id,
|
||||||
|
@ -322,7 +323,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnMediaEnd) {
|
||||||
listener_.OnMediaEnd(MuxerListener::MediaRanges(), 0);
|
listener_.OnMediaEnd(MuxerListener::MediaRanges(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlsNotifyMuxerListenerTest, OnNewSegment) {
|
TEST_F(HlsNotifyMuxerListenerTest, OnNewSegmentAndCueEvent) {
|
||||||
ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
|
ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
|
||||||
.WillByDefault(Return(true));
|
.WillByDefault(Return(true));
|
||||||
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
@ -336,9 +337,11 @@ TEST_F(HlsNotifyMuxerListenerTest, OnNewSegment) {
|
||||||
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_, NotifyCueEvent(_, kStartTime));
|
||||||
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_.OnCueEvent(kStartTime, "dummy cue data");
|
||||||
listener_.OnNewSegment("new_segment_name10.ts", kStartTime, kDuration,
|
listener_.OnNewSegment("new_segment_name10.ts", kStartTime, kDuration,
|
||||||
kFileSize);
|
kFileSize);
|
||||||
}
|
}
|
||||||
|
@ -361,6 +364,7 @@ TEST_F(HlsNotifyMuxerListenerTest, NoSegmentTemplateOnMediaEnd) {
|
||||||
const uint64_t kDuration = 98028;
|
const uint64_t kDuration = 98028;
|
||||||
const uint64_t kFileSize = 756739;
|
const uint64_t kFileSize = 756739;
|
||||||
|
|
||||||
|
listener_.OnCueEvent(kStartTime, "dummy cue data");
|
||||||
listener_.OnNewSegment("filename.mp4", kStartTime, kDuration,
|
listener_.OnNewSegment("filename.mp4", kStartTime, kDuration,
|
||||||
kFileSize);
|
kFileSize);
|
||||||
MuxerListener::MediaRanges ranges;
|
MuxerListener::MediaRanges ranges;
|
||||||
|
@ -380,6 +384,7 @@ TEST_F(HlsNotifyMuxerListenerTest, NoSegmentTemplateOnMediaEnd) {
|
||||||
ranges.index_range = index_range;
|
ranges.index_range = index_range;
|
||||||
ranges.subsegment_ranges = segment_ranges;
|
ranges.subsegment_ranges = segment_ranges;
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_notifier_, NotifyCueEvent(_, kStartTime));
|
||||||
EXPECT_CALL(mock_notifier_,
|
EXPECT_CALL(mock_notifier_,
|
||||||
NotifyNewSegment(_, StrEq("filename.mp4"), kStartTime,
|
NotifyNewSegment(_, StrEq("filename.mp4"), kStartTime,
|
||||||
kDuration, kSegmentStartOffset, kFileSize));
|
kDuration, kSegmentStartOffset, kFileSize));
|
||||||
|
|
|
@ -61,6 +61,9 @@ class MockMuxerListener : public MuxerListener {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size));
|
uint64_t segment_file_size));
|
||||||
|
|
||||||
|
MOCK_METHOD2(OnCueEvent,
|
||||||
|
void(uint64_t timestamp, const std::string& cue_data));
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -132,10 +132,13 @@ void MpdNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
|
||||||
mpd_notifier_->NotifyNewContainer(*media_info_, &id);
|
mpd_notifier_->NotifyNewContainer(*media_info_, &id);
|
||||||
// TODO(rkuroiwa): Use media_ranges.subsegment_ranges instead of caching the
|
// TODO(rkuroiwa): Use media_ranges.subsegment_ranges instead of caching the
|
||||||
// subsegments.
|
// subsegments.
|
||||||
for (std::list<SubsegmentInfo>::const_iterator it = subsegments_.begin();
|
for (const SubsegmentInfo& subsegment : subsegments_) {
|
||||||
it != subsegments_.end(); ++it) {
|
if (subsegment.cue_break) {
|
||||||
mpd_notifier_->NotifyNewSegment(id, it->start_time, it->duration,
|
mpd_notifier_->NotifyCueEvent(id, subsegment.start_time);
|
||||||
it->segment_file_size);
|
}
|
||||||
|
mpd_notifier_->NotifyNewSegment(id, subsegment.start_time,
|
||||||
|
subsegment.duration,
|
||||||
|
subsegment.segment_file_size);
|
||||||
}
|
}
|
||||||
subsegments_.clear();
|
subsegments_.clear();
|
||||||
mpd_notifier_->Flush();
|
mpd_notifier_->Flush();
|
||||||
|
@ -152,10 +155,22 @@ void MpdNotifyMuxerListener::OnNewSegment(const std::string& file_name,
|
||||||
if (mpd_notifier_->mpd_type() == MpdType::kDynamic)
|
if (mpd_notifier_->mpd_type() == MpdType::kDynamic)
|
||||||
mpd_notifier_->Flush();
|
mpd_notifier_->Flush();
|
||||||
} else {
|
} else {
|
||||||
SubsegmentInfo subsegment = {start_time, duration, segment_file_size};
|
SubsegmentInfo subsegment = {start_time, duration, segment_file_size,
|
||||||
|
next_subsegment_contains_cue_break_};
|
||||||
|
next_subsegment_contains_cue_break_ = false;
|
||||||
subsegments_.push_back(subsegment);
|
subsegments_.push_back(subsegment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MpdNotifyMuxerListener::OnCueEvent(uint64_t timestamp,
|
||||||
|
const std::string& cue_data) {
|
||||||
|
// Not using |cue_data| at this moment.
|
||||||
|
if (mpd_notifier_->dash_profile() == DashProfile::kLive) {
|
||||||
|
mpd_notifier_->NotifyCueEvent(notification_id_, timestamp);
|
||||||
|
} else {
|
||||||
|
next_subsegment_contains_cue_break_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -51,6 +51,7 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) override;
|
uint64_t segment_file_size) override;
|
||||||
|
void OnCueEvent(uint64_t timestamp, const std::string& cue_data) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -59,15 +60,16 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
||||||
uint64_t start_time;
|
uint64_t start_time;
|
||||||
uint64_t duration;
|
uint64_t duration;
|
||||||
uint64_t segment_file_size;
|
uint64_t segment_file_size;
|
||||||
|
bool cue_break;
|
||||||
};
|
};
|
||||||
|
|
||||||
MpdNotifier* const mpd_notifier_;
|
MpdNotifier* const mpd_notifier_ = nullptr;
|
||||||
uint32_t notification_id_;
|
uint32_t notification_id_ = 0;
|
||||||
std::unique_ptr<MediaInfo> media_info_;
|
std::unique_ptr<MediaInfo> media_info_;
|
||||||
|
|
||||||
bool is_encrypted_;
|
bool is_encrypted_ = false;
|
||||||
// Storage for values passed to OnEncryptionInfoReady().
|
// Storage for values passed to OnEncryptionInfoReady().
|
||||||
FourCC protection_scheme_;
|
FourCC protection_scheme_ = FOURCC_NULL;
|
||||||
std::vector<uint8_t> default_key_id_;
|
std::vector<uint8_t> default_key_id_;
|
||||||
std::vector<ProtectionSystemSpecificInfo> key_system_info_;
|
std::vector<ProtectionSystemSpecificInfo> key_system_info_;
|
||||||
|
|
||||||
|
@ -76,6 +78,8 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
||||||
// (in OnMediaEnd). This is not used for live because NotifyNewSegment() is
|
// (in OnMediaEnd). This is not used for live because NotifyNewSegment() is
|
||||||
// called immediately in OnNewSegment().
|
// called immediately in OnNewSegment().
|
||||||
std::list<SubsegmentInfo> subsegments_;
|
std::list<SubsegmentInfo> subsegments_;
|
||||||
|
// Whether the next subsegment contains AdCue break.
|
||||||
|
bool next_subsegment_contains_cue_break_ = false;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(MpdNotifyMuxerListener);
|
DISALLOW_COPY_AND_ASSIGN(MpdNotifyMuxerListener);
|
||||||
};
|
};
|
||||||
|
|
|
@ -251,6 +251,7 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegment) {
|
||||||
kDefaultReferenceTimeScale,
|
kDefaultReferenceTimeScale,
|
||||||
MuxerListener::kContainerMp4);
|
MuxerListener::kContainerMp4);
|
||||||
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
|
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
|
||||||
|
listener_->OnCueEvent(kStartTime2, "dummy cue data");
|
||||||
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
|
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
|
||||||
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
@ -259,6 +260,7 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegment) {
|
||||||
ExpectMediaInfoEq(kExpectedDefaultMediaInfo), _));
|
ExpectMediaInfoEq(kExpectedDefaultMediaInfo), _));
|
||||||
EXPECT_CALL(*notifier_,
|
EXPECT_CALL(*notifier_,
|
||||||
NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1));
|
NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1));
|
||||||
|
EXPECT_CALL(*notifier_, NotifyCueEvent(_, kStartTime2));
|
||||||
EXPECT_CALL(*notifier_,
|
EXPECT_CALL(*notifier_,
|
||||||
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
||||||
EXPECT_CALL(*notifier_, Flush());
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
@ -317,6 +319,7 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
|
||||||
// Flush should only be called once in OnMediaEnd.
|
// Flush should only be called once in OnMediaEnd.
|
||||||
if (GetParam() == MpdType::kDynamic)
|
if (GetParam() == MpdType::kDynamic)
|
||||||
EXPECT_CALL(*notifier_, Flush());
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
EXPECT_CALL(*notifier_, NotifyCueEvent(_, kStartTime2));
|
||||||
EXPECT_CALL(*notifier_,
|
EXPECT_CALL(*notifier_,
|
||||||
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
||||||
if (GetParam() == MpdType::kDynamic)
|
if (GetParam() == MpdType::kDynamic)
|
||||||
|
@ -330,6 +333,7 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
|
||||||
kDefaultReferenceTimeScale,
|
kDefaultReferenceTimeScale,
|
||||||
MuxerListener::kContainerMp4);
|
MuxerListener::kContainerMp4);
|
||||||
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
|
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
|
||||||
|
listener_->OnCueEvent(kStartTime2, "dummy cue data");
|
||||||
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
|
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
|
||||||
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,11 @@ class MuxerListener {
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) = 0;
|
uint64_t segment_file_size) = 0;
|
||||||
|
|
||||||
|
/// Called when there is a new Ad Cue, which should align with (sub)segments.
|
||||||
|
/// @param timestamp indicate the cue timestamp.
|
||||||
|
/// @param cue_data is the data of the cue.
|
||||||
|
virtual void OnCueEvent(uint64_t timestamp, const std::string& cue_data) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MuxerListener() {};
|
MuxerListener() {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -88,6 +88,11 @@ void VodMediaInfoDumpMuxerListener::OnNewSegment(const std::string& file_name,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) {}
|
uint64_t segment_file_size) {}
|
||||||
|
|
||||||
|
void VodMediaInfoDumpMuxerListener::OnCueEvent(uint64_t timestamp,
|
||||||
|
const std::string& cue_data) {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool VodMediaInfoDumpMuxerListener::WriteMediaInfoToFile(
|
bool VodMediaInfoDumpMuxerListener::WriteMediaInfoToFile(
|
||||||
const MediaInfo& media_info,
|
const MediaInfo& media_info,
|
||||||
|
|
|
@ -50,6 +50,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t segment_file_size) override;
|
uint64_t segment_file_size) override;
|
||||||
|
void OnCueEvent(uint64_t timestamp, const std::string& cue_data) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// Write @a media_info to @a output_file_path in human readable format.
|
/// Write @a media_info to @a output_file_path in human readable format.
|
||||||
|
|
|
@ -31,6 +31,7 @@ class MockMpdNotifier : public MpdNotifier {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t size));
|
uint64_t size));
|
||||||
|
MOCK_METHOD2(NotifyCueEvent, bool(uint32_t container_id, uint64_t timestamp));
|
||||||
MOCK_METHOD4(NotifyEncryptionUpdate,
|
MOCK_METHOD4(NotifyEncryptionUpdate,
|
||||||
bool(uint32_t container_id,
|
bool(uint32_t container_id,
|
||||||
const std::string& drm_uuid,
|
const std::string& drm_uuid,
|
||||||
|
|
|
@ -71,6 +71,13 @@ class MpdNotifier {
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t size) = 0;
|
uint64_t size) = 0;
|
||||||
|
|
||||||
|
/// Notifies MpdBuilder that there is a new CueEvent.
|
||||||
|
/// @param container_id Container ID obtained from calling
|
||||||
|
/// NotifyNewContainer().
|
||||||
|
/// @param timestamp is the timestamp of the CueEvent.
|
||||||
|
/// @return true on success, false otherwise.
|
||||||
|
virtual bool NotifyCueEvent(uint32_t container_id, uint64_t timestamp) = 0;
|
||||||
|
|
||||||
/// Notifiers MpdBuilder that there is a new PSSH for the container.
|
/// Notifiers MpdBuilder that there is a new PSSH for the container.
|
||||||
/// This may be called whenever the key has to change, e.g. key rotation.
|
/// This may be called whenever the key has to change, e.g. key rotation.
|
||||||
/// @param container_id Container ID obtained from calling
|
/// @param container_id Container ID obtained from calling
|
||||||
|
|
|
@ -96,6 +96,12 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SimpleMpdNotifier::NotifyCueEvent(uint32_t container_id,
|
||||||
|
uint64_t timestamp) {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
||||||
uint32_t container_id,
|
uint32_t container_id,
|
||||||
const std::string& drm_uuid,
|
const std::string& drm_uuid,
|
||||||
|
|
|
@ -43,6 +43,7 @@ class SimpleMpdNotifier : public MpdNotifier {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t size) override;
|
uint64_t size) override;
|
||||||
|
bool NotifyCueEvent(uint32_t container_id, uint64_t timestamp) override;
|
||||||
bool NotifyEncryptionUpdate(uint32_t container_id,
|
bool NotifyEncryptionUpdate(uint32_t container_id,
|
||||||
const std::string& drm_uuid,
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
|
|
Loading…
Reference in New Issue