Shaka Packager SDK
chunking_handler.cc
1 // Copyright 2017 Google Inc. 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/chunking_handler.h"
8 
9 #include <algorithm>
10 
11 #include "packager/base/logging.h"
12 #include "packager/media/base/media_sample.h"
13 #include "packager/status_macros.h"
14 
15 namespace shaka {
16 namespace media {
17 namespace {
18 const size_t kStreamIndex = 0;
19 } // namespace
20 
21 ChunkingHandler::ChunkingHandler(const ChunkingParams& chunking_params)
22  : chunking_params_(chunking_params) {
23  CHECK_NE(chunking_params.segment_duration_in_seconds, 0u);
24 }
25 
27  if (num_input_streams() != 1 || next_output_stream_index() != 1) {
28  return Status(error::INVALID_ARGUMENT,
29  "Expects exactly one input and one output.");
30  }
31  return Status::OK;
32 }
33 
34 Status ChunkingHandler::Process(std::unique_ptr<StreamData> stream_data) {
35  switch (stream_data->stream_data_type) {
36  case StreamDataType::kStreamInfo:
37  return OnStreamInfo(std::move(stream_data->stream_info));
38  case StreamDataType::kCueEvent:
39  return OnCueEvent(std::move(stream_data->cue_event));
40  case StreamDataType::kSegmentInfo:
41  VLOG(3) << "Droppping existing segment info.";
42  return Status::OK;
43  case StreamDataType::kMediaSample:
44  return OnMediaSample(std::move(stream_data->media_sample));
45  default:
46  VLOG(3) << "Stream data type "
47  << static_cast<int>(stream_data->stream_data_type) << " ignored.";
48  return Dispatch(std::move(stream_data));
49  }
50 }
51 
52 Status ChunkingHandler::OnFlushRequest(size_t input_stream_index) {
53  RETURN_IF_ERROR(EndSegmentIfStarted());
54  return FlushDownstream(kStreamIndex);
55 }
56 
57 Status ChunkingHandler::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
58  time_scale_ = info->time_scale();
59  segment_duration_ =
60  chunking_params_.segment_duration_in_seconds * time_scale_;
61  subsegment_duration_ =
62  chunking_params_.subsegment_duration_in_seconds * time_scale_;
63  return DispatchStreamInfo(kStreamIndex, std::move(info));
64 }
65 
66 Status ChunkingHandler::OnCueEvent(std::shared_ptr<const CueEvent> event) {
67  RETURN_IF_ERROR(EndSegmentIfStarted());
68  const double event_time_in_seconds = event->time_in_seconds;
69  RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
70 
71  // Force start new segment after cue event.
72  segment_start_time_ = base::nullopt;
73  // |cue_offset_| will be applied to sample timestamp so the segment after cue
74  // point have duration ~= |segment_duration_|.
75  cue_offset_ = event_time_in_seconds * time_scale_;
76  return Status::OK;
77 }
78 
79 Status ChunkingHandler::OnMediaSample(
80  std::shared_ptr<const MediaSample> sample) {
81  DCHECK_NE(time_scale_, 0u) << "kStreamInfo should arrive before kMediaSample";
82 
83  const int64_t timestamp = sample->dts();
84 
85  bool started_new_segment = false;
86  const bool can_start_new_segment =
87  sample->is_key_frame() || !chunking_params_.segment_sap_aligned;
88  if (can_start_new_segment) {
89  const int64_t segment_index =
90  timestamp < cue_offset_ ? 0
91  : (timestamp - cue_offset_) / segment_duration_;
92  if (!segment_start_time_ || segment_index != current_segment_index_) {
93  current_segment_index_ = segment_index;
94  // Reset subsegment index.
95  current_subsegment_index_ = 0;
96 
97  RETURN_IF_ERROR(EndSegmentIfStarted());
98  segment_start_time_ = timestamp;
99  subsegment_start_time_ = timestamp;
100  started_new_segment = true;
101  }
102  }
103  if (!started_new_segment && IsSubsegmentEnabled()) {
104  const bool can_start_new_subsegment =
105  sample->is_key_frame() || !chunking_params_.subsegment_sap_aligned;
106  if (can_start_new_subsegment) {
107  const int64_t subsegment_index =
108  (timestamp - segment_start_time_.value()) / subsegment_duration_;
109  if (subsegment_index != current_subsegment_index_) {
110  current_subsegment_index_ = subsegment_index;
111 
112  RETURN_IF_ERROR(EndSubsegmentIfStarted());
113  subsegment_start_time_ = timestamp;
114  }
115  }
116  }
117 
118  VLOG(3) << "Sample ts: " << timestamp << " "
119  << " duration: " << sample->duration() << " scale: " << time_scale_
120  << (segment_start_time_ ? " dispatch " : " discard ");
121  // Discard samples before segment start. If the segment has started,
122  // |segment_start_time_| won't be null.
123  if (!segment_start_time_)
124  return Status::OK;
125  last_sample_end_timestamp_ = timestamp + sample->duration();
126  return DispatchMediaSample(kStreamIndex, std::move(sample));
127 }
128 
129 Status ChunkingHandler::EndSegmentIfStarted() const {
130  if (!segment_start_time_)
131  return Status::OK;
132 
133  auto segment_info = std::make_shared<SegmentInfo>();
134  segment_info->start_timestamp = segment_start_time_.value();
135  segment_info->duration =
136  last_sample_end_timestamp_ - segment_start_time_.value();
137  return DispatchSegmentInfo(kStreamIndex, std::move(segment_info));
138 }
139 
140 Status ChunkingHandler::EndSubsegmentIfStarted() const {
141  if (!subsegment_start_time_)
142  return Status::OK;
143 
144  auto subsegment_info = std::make_shared<SegmentInfo>();
145  subsegment_info->start_timestamp = subsegment_start_time_.value();
146  subsegment_info->duration =
147  last_sample_end_timestamp_ - subsegment_start_time_.value();
148  subsegment_info->is_subsegment = true;
149  return DispatchSegmentInfo(kStreamIndex, std::move(subsegment_info));
150 }
151 
152 } // namespace media
153 } // namespace shaka
Status Process(std::unique_ptr< StreamData > stream_data) override
Status InitializeInternal() override
All the methods that are virtual are virtual for mocking.
Status OnFlushRequest(size_t input_stream_index) override
Event handler for flush request at the specific input stream index.