2017-09-18 15:43:21 +00:00
|
|
|
// Copyright 2017 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
|
|
|
|
|
2018-03-23 22:28:30 +00:00
|
|
|
#include "packager/media/chunking/text_chunker.h"
|
2017-09-18 15:43:21 +00:00
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
#include "packager/status_macros.h"
|
|
|
|
|
2017-09-18 15:43:21 +00:00
|
|
|
namespace shaka {
|
|
|
|
namespace media {
|
|
|
|
namespace {
|
|
|
|
const size_t kStreamIndex = 0;
|
2018-03-23 01:33:01 +00:00
|
|
|
|
|
|
|
std::shared_ptr<const SegmentInfo> MakeSegmentInfo(int64_t start_ms,
|
|
|
|
int64_t end_ms) {
|
|
|
|
DCHECK_LT(start_ms, end_ms);
|
|
|
|
|
|
|
|
std::shared_ptr<SegmentInfo> info = std::make_shared<SegmentInfo>();
|
|
|
|
info->start_timestamp = start_ms;
|
|
|
|
info->duration = end_ms - start_ms;
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
2018-02-05 19:16:00 +00:00
|
|
|
} // namespace
|
2017-09-18 15:43:21 +00:00
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
TextChunker::TextChunker(int64_t segment_duration_ms)
|
|
|
|
: segment_duration_ms_(segment_duration_ms),
|
|
|
|
segment_start_ms_(0),
|
|
|
|
segment_expected_end_ms_(segment_duration_ms) {}
|
2017-09-18 15:43:21 +00:00
|
|
|
|
2018-03-23 22:28:30 +00:00
|
|
|
Status TextChunker::InitializeInternal() {
|
2017-09-18 15:43:21 +00:00
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
Status TextChunker::Process(std::unique_ptr<StreamData> data) {
|
|
|
|
switch (data->stream_data_type) {
|
2017-09-18 15:43:21 +00:00
|
|
|
case StreamDataType::kStreamInfo:
|
2018-03-23 01:33:01 +00:00
|
|
|
return OnStreamInfo(std::move(data->stream_info));
|
2017-09-18 15:43:21 +00:00
|
|
|
case StreamDataType::kTextSample:
|
2018-03-23 01:33:01 +00:00
|
|
|
return OnTextSample(data->text_sample);
|
|
|
|
case StreamDataType::kCueEvent:
|
|
|
|
return OnCueEvent(data->cue_event);
|
2017-09-18 15:43:21 +00:00
|
|
|
default:
|
|
|
|
return Status(error::INTERNAL_ERROR,
|
|
|
|
"Invalid stream data type for this handler");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-23 22:28:30 +00:00
|
|
|
Status TextChunker::OnFlushRequest(size_t input_stream_index) {
|
2018-03-23 01:33:01 +00:00
|
|
|
// Keep outputting segments until all the samples leave the system.
|
|
|
|
while (segment_samples_.size()) {
|
|
|
|
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
2017-09-18 15:43:21 +00:00
|
|
|
}
|
2018-02-05 17:55:58 +00:00
|
|
|
|
|
|
|
return FlushAllDownstreams();
|
2017-09-18 15:43:21 +00:00
|
|
|
}
|
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
Status TextChunker::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
|
|
|
|
// There is no information we need from the stream info, so just pass it
|
|
|
|
// downstream.
|
|
|
|
return DispatchStreamInfo(kStreamIndex, std::move(info));
|
|
|
|
}
|
2017-09-18 15:43:21 +00:00
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
Status TextChunker::OnCueEvent(std::shared_ptr<const CueEvent> event) {
|
|
|
|
// We are going to cut the current segment into two using the event's time as
|
|
|
|
// the division.
|
|
|
|
const int64_t cue_time_in_ms = event->time_in_seconds * 1000;
|
2017-09-18 15:43:21 +00:00
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
// In the case that there is a gap with no samples between the last sample
|
|
|
|
// and the cue event, output all the segments until we get to the segment that
|
|
|
|
// the cue event interrupts.
|
|
|
|
while (segment_expected_end_ms_ < cue_time_in_ms) {
|
|
|
|
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
2017-09-18 15:43:21 +00:00
|
|
|
}
|
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
RETURN_IF_ERROR(EndSegment(cue_time_in_ms));
|
|
|
|
RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
|
2018-02-05 19:16:00 +00:00
|
|
|
|
2018-01-31 19:10:13 +00:00
|
|
|
return Status::OK;
|
2017-09-18 15:43:21 +00:00
|
|
|
}
|
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
Status TextChunker::OnTextSample(std::shared_ptr<const TextSample> sample) {
|
|
|
|
// Output all segments that come before our new sample.
|
|
|
|
while (segment_expected_end_ms_ <= sample->start_time()) {
|
|
|
|
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
2017-09-18 15:43:21 +00:00
|
|
|
}
|
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
segment_samples_.push_back(std::move(sample));
|
|
|
|
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status TextChunker::EndSegment(int64_t segment_actual_end_ms) {
|
|
|
|
// Output all the samples that are part of the segment.
|
|
|
|
for (const auto& sample : segment_samples_) {
|
|
|
|
RETURN_IF_ERROR(DispatchTextSample(kStreamIndex, sample));
|
2018-01-02 21:58:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
RETURN_IF_ERROR(DispatchSegmentInfo(
|
|
|
|
kStreamIndex, MakeSegmentInfo(segment_start_ms_, segment_actual_end_ms)));
|
|
|
|
|
|
|
|
// Create a new segment that comes right after the old segment and remove all
|
|
|
|
// samples that don't cross over into the new segment.
|
|
|
|
StartNewSegment(segment_actual_end_ms);
|
2018-02-05 19:16:00 +00:00
|
|
|
|
2018-03-23 01:33:01 +00:00
|
|
|
return Status::OK;
|
2017-09-18 15:43:21 +00:00
|
|
|
}
|
2018-03-23 01:33:01 +00:00
|
|
|
|
|
|
|
void TextChunker::StartNewSegment(int64_t start_ms) {
|
|
|
|
segment_start_ms_ = start_ms;
|
|
|
|
segment_expected_end_ms_ = start_ms + segment_duration_ms_;
|
|
|
|
|
|
|
|
// Remove all samples that no longer overlap with the new segment.
|
|
|
|
segment_samples_.remove_if(
|
|
|
|
[start_ms](const std::shared_ptr<const TextSample>& sample) {
|
|
|
|
return sample->EndTime() <= start_ms;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-09-18 15:43:21 +00:00
|
|
|
} // namespace media
|
|
|
|
} // namespace shaka
|