7#include <packager/media/chunking/chunking_handler.h>
16#include <absl/log/check.h>
17#include <absl/log/log.h>
19#include <packager/chunking_params.h>
20#include <packager/macros/status.h>
21#include <packager/media/base/media_handler.h>
22#include <packager/media/base/media_sample.h>
23#include <packager/media/base/stream_info.h>
24#include <packager/status.h>
29const size_t kStreamIndex = 0;
31bool IsNewSegmentIndex(int64_t new_index, int64_t current_index) {
32 return new_index != current_index &&
37 new_index != current_index - 1;
42ChunkingHandler::ChunkingHandler(
const ChunkingParams& chunking_params)
43 : chunking_params_(chunking_params) {
44 CHECK_NE(chunking_params.segment_duration_in_seconds, 0u);
45 segment_number_ = chunking_params.start_segment_number;
48Status ChunkingHandler::InitializeInternal() {
49 if (num_input_streams() != 1 || next_output_stream_index() != 1) {
50 return Status(error::INVALID_ARGUMENT,
51 "Expects exactly one input and one output.");
56Status ChunkingHandler::Process(std::unique_ptr<StreamData> stream_data) {
57 switch (stream_data->stream_data_type) {
58 case StreamDataType::kStreamInfo:
59 return OnStreamInfo(std::move(stream_data->stream_info));
60 case StreamDataType::kCueEvent:
61 return OnCueEvent(std::move(stream_data->cue_event));
62 case StreamDataType::kSegmentInfo:
63 VLOG(3) <<
"Droppping existing segment info.";
65 case StreamDataType::kMediaSample:
66 return OnMediaSample(std::move(stream_data->media_sample));
68 VLOG(3) <<
"Stream data type "
69 <<
static_cast<int>(stream_data->stream_data_type) <<
" ignored.";
70 return Dispatch(std::move(stream_data));
74Status ChunkingHandler::OnFlushRequest(
size_t ) {
75 RETURN_IF_ERROR(EndSegmentIfStarted());
76 return FlushDownstream(kStreamIndex);
79Status ChunkingHandler::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
80 time_scale_ = info->time_scale();
82 chunking_params_.segment_duration_in_seconds * time_scale_;
83 subsegment_duration_ =
84 chunking_params_.subsegment_duration_in_seconds * time_scale_;
85 return DispatchStreamInfo(kStreamIndex, std::move(info));
88Status ChunkingHandler::OnCueEvent(std::shared_ptr<const CueEvent> event) {
89 RETURN_IF_ERROR(EndSegmentIfStarted());
90 const double event_time_in_seconds =
event->time_in_seconds;
91 RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
94 segment_start_time_ = std::nullopt;
97 cue_offset_ = event_time_in_seconds * time_scale_;
101Status ChunkingHandler::OnMediaSample(
102 std::shared_ptr<const MediaSample> sample) {
103 DCHECK_GT(time_scale_, 0) <<
"kStreamInfo should arrive before kMediaSample";
105 const int64_t timestamp = sample->pts();
107 bool started_new_segment =
false;
108 const bool can_start_new_segment =
109 sample->is_key_frame() || !chunking_params_.segment_sap_aligned;
110 if (can_start_new_segment) {
111 const int64_t segment_index =
112 timestamp < cue_offset_ ? 0
113 : (timestamp - cue_offset_) / segment_duration_;
114 if (!segment_start_time_ ||
115 IsNewSegmentIndex(segment_index, current_segment_index_)) {
116 current_segment_index_ = segment_index;
118 current_subsegment_index_ = 0;
120 RETURN_IF_ERROR(EndSegmentIfStarted());
121 segment_start_time_ = timestamp;
122 subsegment_start_time_ = timestamp;
123 max_segment_time_ = timestamp + sample->duration();
124 started_new_segment =
true;
132 if (!started_new_segment && chunking_params_.low_latency_dash_mode) {
133 current_subsegment_index_++;
135 RETURN_IF_ERROR(EndSubsegmentIfStarted());
136 subsegment_start_time_ = timestamp;
142 if (!started_new_segment && IsSubsegmentEnabled() &&
143 !chunking_params_.low_latency_dash_mode) {
144 const bool can_start_new_subsegment =
145 sample->is_key_frame() || !chunking_params_.subsegment_sap_aligned;
146 if (can_start_new_subsegment) {
147 const int64_t subsegment_index =
148 (timestamp - segment_start_time_.value()) / subsegment_duration_;
149 if (IsNewSegmentIndex(subsegment_index, current_subsegment_index_)) {
150 current_subsegment_index_ = subsegment_index;
152 RETURN_IF_ERROR(EndSubsegmentIfStarted());
153 subsegment_start_time_ = timestamp;
158 VLOG(3) <<
"Sample ts: " << timestamp <<
" "
159 <<
" duration: " << sample->duration() <<
" scale: " << time_scale_
160 << (segment_start_time_ ?
" dispatch " :
" discard ");
161 if (!segment_start_time_) {
162 DCHECK(!subsegment_start_time_);
168 segment_start_time_ = std::min(segment_start_time_.value(), timestamp);
169 subsegment_start_time_ = std::min(subsegment_start_time_.value(), timestamp);
171 std::max(max_segment_time_, timestamp + sample->duration());
172 return DispatchMediaSample(kStreamIndex, std::move(sample));
175Status ChunkingHandler::EndSegmentIfStarted() {
176 if (!segment_start_time_)
181 int64_t unwrapped_start = pts_unwrapper_.Unwrap(segment_start_time_.value());
182 int64_t unwrapped_max = pts_unwrapper_.Unwrap(max_segment_time_);
184 auto segment_info = std::make_shared<SegmentInfo>();
185 segment_info->start_timestamp = unwrapped_start;
186 segment_info->duration = unwrapped_max - unwrapped_start;
187 segment_info->segment_number = segment_number_++;
189 DVLOG(2) <<
"ChunkingHandler: Segment " << segment_info->segment_number
190 <<
" start=" << unwrapped_start
191 <<
" duration=" << segment_info->duration
192 <<
" (wrapped: start=" << segment_start_time_.value()
193 <<
" max=" << max_segment_time_ <<
")";
195 if (chunking_params_.low_latency_dash_mode) {
196 segment_info->is_chunk =
true;
197 segment_info->is_final_chunk_in_seg =
true;
200 return DispatchSegmentInfo(kStreamIndex, std::move(segment_info));
203Status ChunkingHandler::EndSubsegmentIfStarted()
const {
204 if (!subsegment_start_time_)
207 auto subsegment_info = std::make_shared<SegmentInfo>();
208 subsegment_info->start_timestamp = subsegment_start_time_.value();
209 subsegment_info->duration =
210 max_segment_time_ - subsegment_start_time_.value();
211 subsegment_info->is_subsegment =
true;
212 if (chunking_params_.low_latency_dash_mode)
213 subsegment_info->is_chunk =
true;
214 return DispatchSegmentInfo(kStreamIndex, std::move(subsegment_info));
All the methods that are virtual are virtual for mocking.