diff --git a/packager/media/chunking/chunking_handler.cc b/packager/media/chunking/chunking_handler.cc index 6c1c5578de..02a962dae1 100644 --- a/packager/media/chunking/chunking_handler.cc +++ b/packager/media/chunking/chunking_handler.cc @@ -10,6 +10,7 @@ #include "packager/base/logging.h" #include "packager/media/base/media_sample.h" +#include "packager/status_macros.h" namespace shaka { namespace media { @@ -49,9 +50,7 @@ Status ChunkingHandler::Process(std::unique_ptr stream_data) { } Status ChunkingHandler::OnFlushRequest(size_t input_stream_index) { - Status status = EndSegmentIfStarted(); - if (!status.ok()) - return status; + RETURN_IF_ERROR(EndSegmentIfStarted()); return FlushDownstream(kStreamIndex); } @@ -65,12 +64,16 @@ Status ChunkingHandler::OnStreamInfo(std::shared_ptr info) { } Status ChunkingHandler::OnCueEvent(std::shared_ptr event) { - Status status = EndSegmentIfStarted(); - if (!status.ok()) - return status; + RETURN_IF_ERROR(EndSegmentIfStarted()); + const double event_time_in_seconds = event->time_in_seconds; + RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event))); + // Force start new segment after cue event. segment_start_time_ = base::nullopt; - return DispatchCueEvent(kStreamIndex, std::move(event)); + // |cue_offset_| will be applied to sample timestamp so the segment after cue + // point have duration ~= |segment_duration_|. + cue_offset_ = event_time_in_seconds * time_scale_; + return Status::OK; } Status ChunkingHandler::OnMediaSample( @@ -83,15 +86,15 @@ Status ChunkingHandler::OnMediaSample( const bool can_start_new_segment = sample->is_key_frame() || !chunking_params_.segment_sap_aligned; if (can_start_new_segment) { - const int64_t segment_index = timestamp / segment_duration_; + const int64_t segment_index = + timestamp < cue_offset_ ? 0 + : (timestamp - cue_offset_) / segment_duration_; if (!segment_start_time_ || segment_index != current_segment_index_) { current_segment_index_ = segment_index; // Reset subsegment index. current_subsegment_index_ = 0; - Status status = EndSegmentIfStarted(); - if (!status.ok()) - return status; + RETURN_IF_ERROR(EndSegmentIfStarted()); segment_start_time_ = timestamp; subsegment_start_time_ = timestamp; started_new_segment = true; @@ -106,9 +109,7 @@ Status ChunkingHandler::OnMediaSample( if (subsegment_index != current_subsegment_index_) { current_subsegment_index_ = subsegment_index; - Status status = EndSubsegmentIfStarted(); - if (!status.ok()) - return status; + RETURN_IF_ERROR(EndSubsegmentIfStarted()); subsegment_start_time_ = timestamp; } } diff --git a/packager/media/chunking/chunking_handler.h b/packager/media/chunking/chunking_handler.h index d59e917bfb..55cc533090 100644 --- a/packager/media/chunking/chunking_handler.h +++ b/packager/media/chunking/chunking_handler.h @@ -83,6 +83,10 @@ class ChunkingHandler : public MediaHandler { uint32_t time_scale_ = 0; // The end timestamp of the last dispatched sample. int64_t last_sample_end_timestamp_ = 0; + + // The offset is applied to sample timestamps so a full segment is generated + // after cue points. + int64_t cue_offset_ = 0; }; } // namespace media diff --git a/packager/media/chunking/chunking_handler_unittest.cc b/packager/media/chunking/chunking_handler_unittest.cc index c8085a272a..3b64958a5d 100644 --- a/packager/media/chunking/chunking_handler_unittest.cc +++ b/packager/media/chunking/chunking_handler_unittest.cc @@ -161,6 +161,7 @@ TEST_F(ChunkingHandlerTest, CueEvent) { ASSERT_OK(Process(StreamData::FromStreamInfo( kStreamIndex, GetVideoStreamInfo(kTimeScale1)))); + ClearOutputStreamDataVector(); const int64_t kVideoStartTimestamp = 12345; const double kCueTimeInSeconds = @@ -169,7 +170,7 @@ TEST_F(ChunkingHandlerTest, CueEvent) { auto cue_event = std::make_shared(); cue_event->time_in_seconds = kCueTimeInSeconds; - for (int i = 0; i < 3; ++i) { + for (int i = 0; i < 6; ++i) { const bool is_key_frame = true; ASSERT_OK(Process(StreamData::FromMediaSample( kStreamIndex, GetMediaSample(kVideoStartTimestamp + i * kDuration, @@ -182,7 +183,6 @@ TEST_F(ChunkingHandlerTest, CueEvent) { EXPECT_THAT( GetOutputStreamDataVector(), ElementsAre( - IsStreamInfo(kStreamIndex, kTimeScale1, !kEncrypted), IsMediaSample(kStreamIndex, kVideoStartTimestamp, kDuration, !kEncrypted), // A new segment is created due to the existance of Cue. @@ -192,6 +192,16 @@ TEST_F(ChunkingHandlerTest, CueEvent) { IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 1, kDuration, !kEncrypted), IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 2, + kDuration, !kEncrypted), + IsSegmentInfo(kStreamIndex, kVideoStartTimestamp + kDuration, + kDuration * 2, kIsSubsegment, !kEncrypted), + IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 3, + kDuration, !kEncrypted), + IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 4, + kDuration, !kEncrypted), + IsSegmentInfo(kStreamIndex, kVideoStartTimestamp + kDuration, + kDuration * 4, !kIsSubsegment, !kEncrypted), + IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 5, kDuration, !kEncrypted))); } diff --git a/packager/packager.gyp b/packager/packager.gyp index 47b8cd1fe5..b837e8d410 100644 --- a/packager/packager.gyp +++ b/packager/packager.gyp @@ -156,6 +156,7 @@ 'sources': [ 'status.cc', 'status.h', + 'status_macros.h', ], 'dependencies': [ 'base/base.gyp:base', diff --git a/packager/status_macros.h b/packager/status_macros.h new file mode 100644 index 0000000000..d2a2591d8d --- /dev/null +++ b/packager/status_macros.h @@ -0,0 +1,24 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef PACKAGER_STATUS_MACROS_H_ +#define PACKAGER_STATUS_MACROS_H_ + +#include "packager/status.h" + +// Evaluates an expression that produces a `Status`. If the status is not +// ok, returns it from the current function. +#define RETURN_IF_ERROR(expr) \ + do { \ + /* Using _status below to avoid capture problems if expr is "status". */ \ + shaka::Status _status = (expr); \ + if (!_status.ok()) \ + return _status; \ + } while (0) + +// TODO(kqyang): Support build Status and update Status message through "<<". + +#endif // PACKAGER_STATUS_MACROS_H_