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 e1bb27f130
commit a42200cfaf
5 changed files with 56 additions and 16 deletions

View File

@ -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<StreamData> 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<const StreamInfo> info) {
}
Status ChunkingHandler::OnCueEvent(std::shared_ptr<const CueEvent> 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;
}
}

View File

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

View File

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

View File

@ -154,6 +154,7 @@
'sources': [
'status.cc',
'status.h',
'status_macros.h',
],
'dependencies': [
'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_