7 #include <packager/media/chunking/chunking_handler.h>
11 #include <absl/log/check.h>
12 #include <absl/log/log.h>
14 #include <packager/macros/logging.h>
15 #include <packager/macros/status.h>
16 #include <packager/media/base/media_sample.h>
21 const size_t kStreamIndex = 0;
23 bool IsNewSegmentIndex(int64_t new_index, int64_t current_index) {
24 return new_index != current_index &&
29 new_index != current_index - 1;
34 ChunkingHandler::ChunkingHandler(
const ChunkingParams& chunking_params)
35 : chunking_params_(chunking_params) {
36 CHECK_NE(chunking_params.segment_duration_in_seconds, 0u);
37 segment_number_ = chunking_params.start_segment_number;
40 Status ChunkingHandler::InitializeInternal() {
41 if (num_input_streams() != 1 || next_output_stream_index() != 1) {
42 return Status(error::INVALID_ARGUMENT,
43 "Expects exactly one input and one output.");
48 Status ChunkingHandler::Process(std::unique_ptr<StreamData> stream_data) {
49 switch (stream_data->stream_data_type) {
50 case StreamDataType::kStreamInfo:
51 return OnStreamInfo(std::move(stream_data->stream_info));
52 case StreamDataType::kCueEvent:
53 return OnCueEvent(std::move(stream_data->cue_event));
54 case StreamDataType::kSegmentInfo:
55 VLOG(3) <<
"Droppping existing segment info.";
57 case StreamDataType::kMediaSample:
58 return OnMediaSample(std::move(stream_data->media_sample));
60 VLOG(3) <<
"Stream data type "
61 <<
static_cast<int>(stream_data->stream_data_type) <<
" ignored.";
62 return Dispatch(std::move(stream_data));
66 Status ChunkingHandler::OnFlushRequest(
size_t ) {
67 RETURN_IF_ERROR(EndSegmentIfStarted());
68 return FlushDownstream(kStreamIndex);
71 Status ChunkingHandler::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
72 time_scale_ = info->time_scale();
74 chunking_params_.segment_duration_in_seconds * time_scale_;
75 subsegment_duration_ =
76 chunking_params_.subsegment_duration_in_seconds * time_scale_;
77 return DispatchStreamInfo(kStreamIndex, std::move(info));
80 Status ChunkingHandler::OnCueEvent(std::shared_ptr<const CueEvent> event) {
81 RETURN_IF_ERROR(EndSegmentIfStarted());
82 const double event_time_in_seconds =
event->time_in_seconds;
83 RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
86 segment_start_time_ = std::nullopt;
89 cue_offset_ = event_time_in_seconds * time_scale_;
93 Status ChunkingHandler::OnMediaSample(
94 std::shared_ptr<const MediaSample> sample) {
95 DCHECK_GT(time_scale_, 0) <<
"kStreamInfo should arrive before kMediaSample";
97 const int64_t timestamp = sample->pts();
99 bool started_new_segment =
false;
100 const bool can_start_new_segment =
101 sample->is_key_frame() || !chunking_params_.segment_sap_aligned;
102 if (can_start_new_segment) {
103 const int64_t segment_index =
104 timestamp < cue_offset_ ? 0
105 : (timestamp - cue_offset_) / segment_duration_;
106 if (!segment_start_time_ ||
107 IsNewSegmentIndex(segment_index, current_segment_index_)) {
108 current_segment_index_ = segment_index;
110 current_subsegment_index_ = 0;
112 RETURN_IF_ERROR(EndSegmentIfStarted());
113 segment_start_time_ = timestamp;
114 subsegment_start_time_ = timestamp;
115 max_segment_time_ = timestamp + sample->duration();
116 started_new_segment =
true;
124 if (!started_new_segment && chunking_params_.low_latency_dash_mode) {
125 current_subsegment_index_++;
127 RETURN_IF_ERROR(EndSubsegmentIfStarted());
128 subsegment_start_time_ = timestamp;
134 if (!started_new_segment && IsSubsegmentEnabled() &&
135 !chunking_params_.low_latency_dash_mode) {
136 const bool can_start_new_subsegment =
137 sample->is_key_frame() || !chunking_params_.subsegment_sap_aligned;
138 if (can_start_new_subsegment) {
139 const int64_t subsegment_index =
140 (timestamp - segment_start_time_.value()) / subsegment_duration_;
141 if (IsNewSegmentIndex(subsegment_index, current_subsegment_index_)) {
142 current_subsegment_index_ = subsegment_index;
144 RETURN_IF_ERROR(EndSubsegmentIfStarted());
145 subsegment_start_time_ = timestamp;
150 VLOG(3) <<
"Sample ts: " << timestamp <<
" "
151 <<
" duration: " << sample->duration() <<
" scale: " << time_scale_
152 << (segment_start_time_ ?
" dispatch " :
" discard ");
153 if (!segment_start_time_) {
154 DCHECK(!subsegment_start_time_);
160 segment_start_time_ = std::min(segment_start_time_.value(), timestamp);
161 subsegment_start_time_ = std::min(subsegment_start_time_.value(), timestamp);
163 std::max(max_segment_time_, timestamp + sample->duration());
164 return DispatchMediaSample(kStreamIndex, std::move(sample));
167 Status ChunkingHandler::EndSegmentIfStarted() {
168 if (!segment_start_time_)
171 auto segment_info = std::make_shared<SegmentInfo>();
172 segment_info->start_timestamp = segment_start_time_.value();
173 segment_info->duration = max_segment_time_ - segment_start_time_.value();
174 segment_info->segment_number = segment_number_++;
176 if (chunking_params_.low_latency_dash_mode) {
177 segment_info->is_chunk =
true;
178 segment_info->is_final_chunk_in_seg =
true;
181 return DispatchSegmentInfo(kStreamIndex, std::move(segment_info));
184 Status ChunkingHandler::EndSubsegmentIfStarted()
const {
185 if (!subsegment_start_time_)
188 auto subsegment_info = std::make_shared<SegmentInfo>();
189 subsegment_info->start_timestamp = subsegment_start_time_.value();
190 subsegment_info->duration =
191 max_segment_time_ - subsegment_start_time_.value();
192 subsegment_info->is_subsegment =
true;
193 if (chunking_params_.low_latency_dash_mode)
194 subsegment_info->is_chunk =
true;
195 return DispatchSegmentInfo(kStreamIndex, std::move(subsegment_info));
All the methods that are virtual are virtual for mocking.