Generate full segment after cue point

Also added RETURN_IF_ERROR status macro.

Change-Id: I04643b6252ea5623128f9a16fa744a255c91be17
This commit is contained in:
KongQun Yang 2018-03-21 11:03:54 -07:00
parent 8f565bf388
commit db45e0868a
5 changed files with 56 additions and 16 deletions

View File

@ -10,6 +10,7 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/media_sample.h" #include "packager/media/base/media_sample.h"
#include "packager/status_macros.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -49,9 +50,7 @@ Status ChunkingHandler::Process(std::unique_ptr<StreamData> stream_data) {
} }
Status ChunkingHandler::OnFlushRequest(size_t input_stream_index) { Status ChunkingHandler::OnFlushRequest(size_t input_stream_index) {
Status status = EndSegmentIfStarted(); RETURN_IF_ERROR(EndSegmentIfStarted());
if (!status.ok())
return status;
return FlushDownstream(kStreamIndex); return FlushDownstream(kStreamIndex);
} }
@ -65,12 +64,16 @@ Status ChunkingHandler::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
} }
Status ChunkingHandler::OnCueEvent(std::shared_ptr<const CueEvent> event) { Status ChunkingHandler::OnCueEvent(std::shared_ptr<const CueEvent> event) {
Status status = EndSegmentIfStarted(); RETURN_IF_ERROR(EndSegmentIfStarted());
if (!status.ok()) const double event_time_in_seconds = event->time_in_seconds;
return status; RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
// Force start new segment after cue event. // Force start new segment after cue event.
segment_start_time_ = base::nullopt; 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( Status ChunkingHandler::OnMediaSample(
@ -83,15 +86,15 @@ Status ChunkingHandler::OnMediaSample(
const bool can_start_new_segment = const bool can_start_new_segment =
sample->is_key_frame() || !chunking_params_.segment_sap_aligned; sample->is_key_frame() || !chunking_params_.segment_sap_aligned;
if (can_start_new_segment) { 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_) { if (!segment_start_time_ || segment_index != current_segment_index_) {
current_segment_index_ = segment_index; current_segment_index_ = segment_index;
// Reset subsegment index. // Reset subsegment index.
current_subsegment_index_ = 0; current_subsegment_index_ = 0;
Status status = EndSegmentIfStarted(); RETURN_IF_ERROR(EndSegmentIfStarted());
if (!status.ok())
return status;
segment_start_time_ = timestamp; segment_start_time_ = timestamp;
subsegment_start_time_ = timestamp; subsegment_start_time_ = timestamp;
started_new_segment = true; started_new_segment = true;
@ -106,9 +109,7 @@ Status ChunkingHandler::OnMediaSample(
if (subsegment_index != current_subsegment_index_) { if (subsegment_index != current_subsegment_index_) {
current_subsegment_index_ = subsegment_index; current_subsegment_index_ = subsegment_index;
Status status = EndSubsegmentIfStarted(); RETURN_IF_ERROR(EndSubsegmentIfStarted());
if (!status.ok())
return status;
subsegment_start_time_ = timestamp; subsegment_start_time_ = timestamp;
} }
} }

View File

@ -83,6 +83,10 @@ class ChunkingHandler : public MediaHandler {
uint32_t time_scale_ = 0; uint32_t time_scale_ = 0;
// The end timestamp of the last dispatched sample. // The end timestamp of the last dispatched sample.
int64_t last_sample_end_timestamp_ = 0; 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 } // namespace media

View File

@ -161,6 +161,7 @@ TEST_F(ChunkingHandlerTest, CueEvent) {
ASSERT_OK(Process(StreamData::FromStreamInfo( ASSERT_OK(Process(StreamData::FromStreamInfo(
kStreamIndex, GetVideoStreamInfo(kTimeScale1)))); kStreamIndex, GetVideoStreamInfo(kTimeScale1))));
ClearOutputStreamDataVector();
const int64_t kVideoStartTimestamp = 12345; const int64_t kVideoStartTimestamp = 12345;
const double kCueTimeInSeconds = const double kCueTimeInSeconds =
@ -169,7 +170,7 @@ TEST_F(ChunkingHandlerTest, CueEvent) {
auto cue_event = std::make_shared<CueEvent>(); auto cue_event = std::make_shared<CueEvent>();
cue_event->time_in_seconds = kCueTimeInSeconds; 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; const bool is_key_frame = true;
ASSERT_OK(Process(StreamData::FromMediaSample( ASSERT_OK(Process(StreamData::FromMediaSample(
kStreamIndex, GetMediaSample(kVideoStartTimestamp + i * kDuration, kStreamIndex, GetMediaSample(kVideoStartTimestamp + i * kDuration,
@ -182,7 +183,6 @@ TEST_F(ChunkingHandlerTest, CueEvent) {
EXPECT_THAT( EXPECT_THAT(
GetOutputStreamDataVector(), GetOutputStreamDataVector(),
ElementsAre( ElementsAre(
IsStreamInfo(kStreamIndex, kTimeScale1, !kEncrypted),
IsMediaSample(kStreamIndex, kVideoStartTimestamp, kDuration, IsMediaSample(kStreamIndex, kVideoStartTimestamp, kDuration,
!kEncrypted), !kEncrypted),
// A new segment is created due to the existance of Cue. // A new segment is created due to the existance of Cue.
@ -192,6 +192,16 @@ TEST_F(ChunkingHandlerTest, CueEvent) {
IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 1, IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 1,
kDuration, !kEncrypted), kDuration, !kEncrypted),
IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 2, 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))); kDuration, !kEncrypted)));
} }

View File

@ -156,6 +156,7 @@
'sources': [ 'sources': [
'status.cc', 'status.cc',
'status.h', 'status.h',
'status_macros.h',
], ],
'dependencies': [ 'dependencies': [
'base/base.gyp:base', 'base/base.gyp:base',

24
packager/status_macros.h Normal file
View File

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