Shaka Packager SDK
text_chunker.cc
1 // Copyright 2017 Google LLC. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include <packager/media/chunking/text_chunker.h>
8 
9 #include <absl/log/check.h>
10 
11 #include <packager/macros/status.h>
12 
13 namespace shaka {
14 namespace media {
15 namespace {
16 const size_t kStreamIndex = 0;
17 } // namespace
18 
19 TextChunker::TextChunker(double segment_duration_in_seconds,
20  int64_t start_segment_number)
21  : segment_duration_in_seconds_(segment_duration_in_seconds),
22  segment_number_(start_segment_number){};
23 
24 Status TextChunker::Process(std::unique_ptr<StreamData> data) {
25  switch (data->stream_data_type) {
26  case StreamDataType::kStreamInfo:
27  return OnStreamInfo(std::move(data->stream_info));
28  case StreamDataType::kTextSample:
29  return OnTextSample(data->text_sample);
30  case StreamDataType::kCueEvent:
31  return OnCueEvent(data->cue_event);
32  default:
33  return Status(error::INTERNAL_ERROR,
34  "Invalid stream data type for this handler");
35  }
36 }
37 
38 Status TextChunker::OnFlushRequest(size_t /*input_stream_index*/) {
39  // Keep outputting segments until all the samples leave the system. Calling
40  // |DispatchSegment| will remove samples over time.
41  while (samples_in_current_segment_.size()) {
42  RETURN_IF_ERROR(DispatchSegment(segment_duration_));
43  }
44 
45  return FlushAllDownstreams();
46 }
47 
48 Status TextChunker::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
49  time_scale_ = info->time_scale();
50  segment_duration_ = ScaleTime(segment_duration_in_seconds_);
51 
52  return DispatchStreamInfo(kStreamIndex, std::move(info));
53 }
54 
55 Status TextChunker::OnCueEvent(std::shared_ptr<const CueEvent> event) {
56  // We are going to end the current segment prematurely using the cue event's
57  // time as the new segment end.
58 
59  // Because the cue should have been inserted into the stream such that no
60  // later sample could start before it does, we know that there should
61  // be no later samples starting before the cue event.
62 
63  // Convert the event's time to be scaled to the time of each sample.
64  const int64_t event_time = ScaleTime(event->time_in_seconds);
65  // Output all full segments before the segment that the cue event interupts.
66  while (segment_start_ + segment_duration_ < event_time) {
67  RETURN_IF_ERROR(DispatchSegment(segment_duration_));
68  }
69 
70  const int64_t shorten_duration = event_time - segment_start_;
71  RETURN_IF_ERROR(DispatchSegment(shorten_duration));
72  return DispatchCueEvent(kStreamIndex, std::move(event));
73 }
74 
75 Status TextChunker::OnTextSample(std::shared_ptr<const TextSample> sample) {
76  // Output all segments that come before our new sample.
77  const int64_t sample_start = sample->start_time();
78 
79  // If we have not seen a sample yet, base all segments off the first sample's
80  // start time.
81  if (segment_start_ < 0) {
82  // Force the first segment to start at the segment that would have started
83  // before the sample. This should allow segments from different streams to
84  // align.
85  segment_start_ = (sample_start / segment_duration_) * segment_duration_;
86  }
87 
88  // We need to write all the segments that would have ended before the new
89  // sample started.
90  while (sample_start >= segment_start_ + segment_duration_) {
91  // |DispatchSegment| will advance |segment_start_|.
92  RETURN_IF_ERROR(DispatchSegment(segment_duration_));
93  }
94 
95  samples_in_current_segment_.push_back(std::move(sample));
96 
97  return Status::OK;
98 }
99 
100 Status TextChunker::DispatchSegment(int64_t duration) {
101  DCHECK_GT(duration, 0) << "Segment duration should always be positive";
102 
103  // Output all the samples that are part of the segment.
104  for (const auto& sample : samples_in_current_segment_) {
105  RETURN_IF_ERROR(DispatchTextSample(kStreamIndex, sample));
106  }
107 
108  // Output the segment info.
109  std::shared_ptr<SegmentInfo> info = std::make_shared<SegmentInfo>();
110  info->start_timestamp = segment_start_;
111  info->duration = duration;
112  info->segment_number = segment_number_++;
113 
114  RETURN_IF_ERROR(DispatchSegmentInfo(kStreamIndex, std::move(info)));
115 
116  // Move onto the next segment.
117  const int64_t new_segment_start = segment_start_ + duration;
118  segment_start_ = new_segment_start;
119 
120  // Remove all samples that end before the (new) current segment started.
121  samples_in_current_segment_.remove_if(
122  [new_segment_start](const std::shared_ptr<const TextSample>& sample) {
123  // For the sample to even be in this list, it should have started
124  // before the (new) current segment.
125  DCHECK_LT(sample->start_time(), new_segment_start);
126  return sample->EndTime() <= new_segment_start;
127  });
128 
129  return Status::OK;
130 }
131 
132 int64_t TextChunker::ScaleTime(double seconds) const {
133  DCHECK_GT(time_scale_, 0) << "Need positive time scale to scale time.";
134  return static_cast<int64_t>(seconds * time_scale_);
135 }
136 } // namespace media
137 } // namespace shaka
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66