diff --git a/packager/media/base/media_handler.h b/packager/media/base/media_handler.h index 0664c145f7..73a3fe91a4 100644 --- a/packager/media/base/media_handler.h +++ b/packager/media/base/media_handler.h @@ -45,8 +45,8 @@ enum class CueEventType { kCueIn, kCueOut, kCuePoint }; // In server-based model, Chunking Handler consolidates SCTE-35 events and // generates CueEvent before an ad is about to be inserted. struct CueEvent { - int64_t timestamp = 0; CueEventType type = CueEventType::kCuePoint; + double time_in_seconds; std::string cue_data; }; diff --git a/packager/media/base/media_handler_test_base.h b/packager/media/base/media_handler_test_base.h index 0c9755e928..db89cb9378 100644 --- a/packager/media/base/media_handler_test_base.h +++ b/packager/media/base/media_handler_test_base.h @@ -139,16 +139,16 @@ MATCHER_P5(IsTextSample, id, start_time, end_time, settings, payload, "") { arg->text_sample->payload() == payload; } -MATCHER_P2(IsCueEvent, stream_index, timestamp, "") { +MATCHER_P2(IsCueEvent, stream_index, time_in_seconds, "") { 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 << ")"; + << arg->cue_event->time_in_seconds << ")"; return arg->stream_index == stream_index && - arg->cue_event->timestamp == timestamp; + arg->cue_event->time_in_seconds == time_in_seconds; } class FakeInputMediaHandler : public MediaHandler { diff --git a/packager/media/base/muxer.cc b/packager/media/base/muxer.cc index ef31861473..dad91c97e1 100644 --- a/packager/media/base/muxer.cc +++ b/packager/media/base/muxer.cc @@ -70,11 +70,15 @@ Status Muxer::Process(std::unique_ptr stream_data) { return FinalizeSegment(stream_data->stream_index, segment_info); } case StreamDataType::kMediaSample: - return AddSample(stream_data->stream_index, - *stream_data->media_sample); + return AddSample(stream_data->stream_index, *stream_data->media_sample); case StreamDataType::kCueEvent: if (muxer_listener_) { - muxer_listener_->OnCueEvent(stream_data->cue_event->timestamp, + const int64_t time_scale = + streams_[stream_data->stream_index]->time_scale(); + const double time_in_seconds = stream_data->cue_event->time_in_seconds; + const int64_t scaled_time = + static_cast(time_in_seconds * time_scale); + muxer_listener_->OnCueEvent(scaled_time, stream_data->cue_event->cue_data); } break; diff --git a/packager/media/chunking/chunking_handler.cc b/packager/media/chunking/chunking_handler.cc index ba9f19f03f..4facd451ab 100644 --- a/packager/media/chunking/chunking_handler.cc +++ b/packager/media/chunking/chunking_handler.cc @@ -12,12 +12,15 @@ #include "packager/base/threading/platform_thread.h" #include "packager/media/base/media_sample.h" -namespace { -int64_t kThreadIdUnset = -1; -} // namespace - namespace shaka { namespace media { +namespace { +int64_t kThreadIdUnset = -1; + +double TimeInSeconds(const Scte35Event& event, int64_t timescale) { + return static_cast(event.start_time) / timescale; +} +} // namespace ChunkingHandler::ChunkingHandler(const ChunkingParams& chunking_params) : chunking_params_(chunking_params), @@ -179,6 +182,9 @@ Status ChunkingHandler::OnMediaSample(std::unique_ptr stream_data) { Status ChunkingHandler::ProcessMainMediaSample(const MediaSample* sample) { const bool is_key_frame = sample->is_key_frame(); const int64_t timestamp = sample->dts(); + const int64_t time_scale = time_scales_[main_stream_index_]; + const double dts_in_seconds = static_cast(sample->dts()) / time_scale; + // Check if we need to terminate the current (sub)segment. bool new_segment = false; bool new_subsegment = false; @@ -194,16 +200,16 @@ Status ChunkingHandler::ProcessMainMediaSample(const MediaSample* sample) { // We use 'while' instead of 'if' to make sure to pop off multiple SCTE35 // events that may be very close to each other. while (!scte35_events_.empty() && - (scte35_events_.top()->start_time <= timestamp)) { + TimeInSeconds(*scte35_events_.top(), time_scale) <= dts_in_seconds) { // For simplicity, don't change |current_segment_index_|. current_subsegment_index_ = 0; new_segment = true; cue_event = std::make_shared(); - // Use PTS instead of DTS for cue event timestamp. - cue_event->timestamp = sample->pts(); + cue_event->time_in_seconds = + static_cast(sample->pts()) / time_scale; cue_event->cue_data = scte35_events_.top()->cue_data; - LOG(INFO) << "Chunked at " << timestamp << " for Ad Cue."; + LOG(INFO) << "Chunked at " << dts_in_seconds << " seconds for Ad Cue."; scte35_events_.pop(); } @@ -298,10 +304,7 @@ Status ChunkingHandler::DispatchCueEventForAllStreams( std::shared_ptr cue_event) { Status status; for (size_t i = 0; i < segment_info_.size() && status.ok(); ++i) { - std::shared_ptr 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))); + status.Update(DispatchCueEvent(i, cue_event)); } return status; } diff --git a/packager/media/chunking/chunking_handler_unittest.cc b/packager/media/chunking/chunking_handler_unittest.cc index f1a2cc6750..073f397b5d 100644 --- a/packager/media/chunking/chunking_handler_unittest.cc +++ b/packager/media/chunking/chunking_handler_unittest.cc @@ -65,8 +65,7 @@ TEST_F(ChunkingHandlerTest, AudioNoSubsegmentsThenFlush) { for (int i = 0; i < 5; ++i) { ClearOutputStreamDataVector(); ASSERT_OK(Process(StreamData::FromMediaSample( - kStreamIndex0, - GetMediaSample(i * kDuration1, kDuration1, kKeyFrame)))); + kStreamIndex0, GetMediaSample(i * kDuration1, kDuration1, kKeyFrame)))); // One output stream_data except when i == 3, which also has SegmentInfo. if (i == 3) { EXPECT_THAT(GetOutputStreamDataVector(), @@ -99,8 +98,7 @@ TEST_F(ChunkingHandlerTest, AudioWithSubsegments) { kStreamIndex0, GetAudioStreamInfo(kTimeScale0)))); for (int i = 0; i < 5; ++i) { ASSERT_OK(Process(StreamData::FromMediaSample( - kStreamIndex0, - GetMediaSample(i * kDuration1, kDuration1, kKeyFrame)))); + kStreamIndex0, GetMediaSample(i * kDuration1, kDuration1, kKeyFrame)))); } EXPECT_THAT( GetOutputStreamDataVector(), @@ -131,11 +129,8 @@ TEST_F(ChunkingHandlerTest, VideoAndSubsegmentAndNonzeroStart) { // Alternate key frame. const bool is_key_frame = (i % 2) == 1; ASSERT_OK(Process(StreamData::FromMediaSample( - kStreamIndex0, - GetMediaSample( - kVideoStartTimestamp + i * kDuration1, - kDuration1, - is_key_frame)))); + kStreamIndex0, GetMediaSample(kVideoStartTimestamp + i * kDuration1, + kDuration1, is_key_frame)))); } EXPECT_THAT( GetOutputStreamDataVector(), @@ -224,22 +219,16 @@ TEST_F(ChunkingHandlerTest, AudioAndVideo) { // The audio and video are made ~aligned. ASSERT_OK(Process(StreamData::FromMediaSample( kStreamIndex0, - GetMediaSample( - kAudioStartTimestamp + kDuration0 * 5, - kDuration0, - true)))); // 13595 + GetMediaSample(kAudioStartTimestamp + kDuration0 * 5, kDuration0, + true)))); // 13595 ASSERT_OK(Process(StreamData::FromMediaSample( kStreamIndex1, - GetMediaSample( - kVideoStartTimestamp + kDuration1 * 5, - kDuration1, - true)))); // 13845 + GetMediaSample(kVideoStartTimestamp + kDuration1 * 5, kDuration1, + true)))); // 13845 ASSERT_OK(Process(StreamData::FromMediaSample( kStreamIndex0, - GetMediaSample( - kAudioStartTimestamp + kDuration0 * 6, - kDuration0, - true)))); // 13845 + GetMediaSample(kAudioStartTimestamp + kDuration0 * 6, kDuration0, + true)))); // 13845 // This expectation are separated from the expectation above because // ElementsAre supports at most 10 elements. EXPECT_THAT( @@ -304,6 +293,7 @@ TEST_F(ChunkingHandlerTest, Scte35Event) { kStreamIndex0, GetMediaSample(kVideoStartTimestamp + i * kDuration1, kDuration1, is_key_frame)))); } + EXPECT_THAT( GetOutputStreamDataVector(), ElementsAre( @@ -313,8 +303,9 @@ TEST_F(ChunkingHandlerTest, Scte35Event) { // A new segment is created due to the existance of Cue. IsSegmentInfo(kStreamIndex0, kVideoStartTimestamp, kDuration1, !kIsSubsegment, !kEncrypted), - IsCueEvent(kStreamIndex0, - static_cast(kVideoStartTimestamp + kDuration1)), + IsCueEvent( + kStreamIndex0, + static_cast(kVideoStartTimestamp + kDuration1) / 1000), IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 1, kDuration1, !kEncrypted), IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 2,