Generate full segment after cue point
Also added RETURN_IF_ERROR status macro. Change-Id: I04643b6252ea5623128f9a16fa744a255c91be17
This commit is contained in:
parent
8f565bf388
commit
db45e0868a
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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_
|
Loading…
Reference in New Issue