7 #include "packager/media/chunking/chunking_handler.h"
9 #include "packager/base/logging.h"
10 #include "packager/base/threading/platform_thread.h"
11 #include "packager/media/base/media_sample.h"
14 int64_t kThreadIdUnset = -1;
15 int64_t kTimeStampToDispatchAllSamples = -1;
21 ChunkingHandler::ChunkingHandler(
const ChunkingOptions& chunking_options)
22 : chunking_options_(chunking_options), thread_id_(kThreadIdUnset) {
23 CHECK_NE(chunking_options.segment_duration_in_seconds, 0u);
26 ChunkingHandler::~ChunkingHandler() {}
29 segment_info_.resize(num_input_streams());
30 subsegment_info_.resize(num_input_streams());
31 time_scales_.resize(num_input_streams());
32 last_sample_end_timestamps_.resize(num_input_streams());
37 switch (stream_data->stream_data_type) {
38 case StreamDataType::kStreamInfo: {
40 const int64_t thread_id =
41 static_cast<int64_t
>(base::PlatformThread::CurrentId());
42 int64_t expected = kThreadIdUnset;
43 if (!thread_id_.compare_exchange_strong(expected, thread_id) &&
44 expected != thread_id) {
45 return Status(error::CHUNKING_ERROR,
46 "Inputs should come from the same thread.");
49 const auto time_scale = stream_data->stream_info->time_scale();
52 const bool is_main_stream =
53 main_stream_index_ == -1 &&
54 (stream_data->stream_info->stream_type() == kStreamVideo ||
55 num_input_streams() == 1);
57 main_stream_index_ = stream_data->stream_index;
60 subsegment_duration_ =
62 }
else if (stream_data->stream_info->stream_type() == kStreamVideo) {
63 return Status(error::CHUNKING_ERROR,
64 "Only one video stream is allowed per chunking handler.");
66 time_scales_[stream_data->stream_index] = time_scale;
69 case StreamDataType::kSegmentInfo:
70 VLOG(3) <<
"Drop existing segment info.";
72 case StreamDataType::kMediaSample: {
73 const int stream_index = stream_data->stream_index;
74 DCHECK_NE(time_scales_[stream_index], 0u)
75 <<
"kStreamInfo should arrive before kMediaSample";
76 if (stream_index != main_stream_index_) {
77 if (!stream_data->media_sample->is_key_frame()) {
78 return Status(error::CHUNKING_ERROR,
79 "All non video samples should be key frames.");
83 non_main_samples_.push_back(std::move(stream_data));
86 const size_t kMaxSamplesPerStreamBeforeVideoSample = 5u;
87 if (non_main_samples_.size() >
88 num_input_streams() * kMaxSamplesPerStreamBeforeVideoSample) {
89 return Status(error::CHUNKING_ERROR,
90 "Too many non video samples before video sample.");
95 const MediaSample* sample = stream_data->media_sample.get();
96 Status status = ProcessMediaSample(sample);
100 if (!segment_info_[stream_index])
102 last_sample_end_timestamps_[stream_index] =
103 sample->dts() + sample->duration();
107 VLOG(3) <<
"Stream data type "
108 <<
static_cast<int>(stream_data->stream_data_type) <<
" ignored.";
111 return Dispatch(std::move(stream_data));
115 if (segment_info_[input_stream_index]) {
117 if (input_stream_index != main_stream_index_) {
118 status = DispatchNonMainSamples(kTimeStampToDispatchAllSamples);
122 auto& segment_info = segment_info_[input_stream_index];
123 if (segment_info->start_timestamp != -1) {
124 segment_info->duration = last_sample_end_timestamps_[input_stream_index] -
125 segment_info->start_timestamp;
135 const bool is_key_frame = sample->is_key_frame();
136 const int64_t timestamp = sample->dts();
138 bool new_segment =
false;
139 bool new_subsegment =
false;
141 const int64_t segment_index = timestamp / segment_duration_;
142 if (segment_index != current_segment_index_) {
143 current_segment_index_ = segment_index;
147 if (!new_segment && subsegment_duration_ > 0 &&
149 const int64_t subsegment_index =
150 (timestamp - segment_info_[main_stream_index_]->start_timestamp) /
151 subsegment_duration_;
152 if (subsegment_index != current_subsegment_index_) {
153 current_subsegment_index_ = subsegment_index;
154 new_subsegment =
true;
159 if (new_segment || new_subsegment) {
162 status.Update(DispatchNonMainSamples(timestamp));
166 status.Update(DispatchSegmentInfoForAllStreams());
167 segment_info_[main_stream_index_]->start_timestamp = timestamp;
169 if (subsegment_duration_ > 0 && (new_segment || new_subsegment)) {
170 status.Update(DispatchSubsegmentInfoForAllStreams());
171 subsegment_info_[main_stream_index_]->start_timestamp = timestamp;
177 return DispatchNonMainSamples(kTimeStampToDispatchAllSamples);
180 Status ChunkingHandler::DispatchNonMainSamples(int64_t timestamp_threshold) {
182 while (status.ok() && !non_main_samples_.empty()) {
183 DCHECK_EQ(non_main_samples_.front()->stream_data_type,
184 StreamDataType::kMediaSample);
185 const int stream_index = non_main_samples_.front()->stream_index;
186 const MediaSample* sample = non_main_samples_.front()->media_sample.get();
189 const int64_t timestamp = sample->dts() + sample->duration() / 2;
191 (timestamp_threshold != kTimeStampToDispatchAllSamples &&
192 (
static_cast<double>(timestamp) / time_scales_[stream_index]) >
193 (
static_cast<double>(timestamp_threshold) /
194 time_scales_[main_stream_index_]));
195 VLOG(3) <<
"Sample ts: " << sample->dts() <<
" "
196 <<
" duration: " << sample->duration()
197 <<
" scale: " << time_scales_[stream_index] <<
"\n"
198 <<
" threshold: " << timestamp_threshold
199 <<
" scale: " << time_scales_[main_stream_index_]
201 : (segment_info_[stream_index] ?
" dispatch "
207 if (segment_info_[stream_index]) {
208 if (segment_info_[stream_index]->start_timestamp == -1)
209 segment_info_[stream_index]->start_timestamp = sample->dts();
210 if (subsegment_info_[stream_index] &&
211 subsegment_info_[stream_index]->start_timestamp == -1) {
212 subsegment_info_[stream_index]->start_timestamp = sample->dts();
214 last_sample_end_timestamps_[stream_index] =
215 sample->dts() + sample->duration();
216 status.Update(
Dispatch(std::move(non_main_samples_.front())));
218 non_main_samples_.pop_front();
223 Status ChunkingHandler::DispatchSegmentInfoForAllStreams() {
225 for (
int i = 0; i < static_cast<int>(segment_info_.size()) && status.ok();
227 if (segment_info_[i] && segment_info_[i]->start_timestamp != -1) {
228 segment_info_[i]->duration =
229 last_sample_end_timestamps_[i] - segment_info_[i]->start_timestamp;
232 segment_info_[i].reset(
new SegmentInfo);
233 subsegment_info_[i].reset();
238 Status ChunkingHandler::DispatchSubsegmentInfoForAllStreams() {
240 for (
int i = 0; i < static_cast<int>(subsegment_info_.size()) && status.ok();
242 if (subsegment_info_[i] && subsegment_info_[i]->start_timestamp != -1) {
243 subsegment_info_[i]->duration =
244 last_sample_end_timestamps_[i] - subsegment_info_[i]->start_timestamp;
247 subsegment_info_[i].reset(
new SegmentInfo);
248 subsegment_info_[i]->is_subsegment =
true;