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_ == kInvalidStreamIndex &&
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 size_t 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;
131 const int output_stream_index = input_stream_index;
136 const bool is_key_frame = sample->is_key_frame();
137 const int64_t timestamp = sample->dts();
139 bool new_segment =
false;
140 bool new_subsegment =
false;
142 const int64_t segment_index = timestamp / segment_duration_;
143 if (segment_index != current_segment_index_) {
144 current_segment_index_ = segment_index;
146 current_subsegment_index_ = 0;
150 if (!new_segment && subsegment_duration_ > 0 &&
152 const int64_t subsegment_index =
153 (timestamp - segment_info_[main_stream_index_]->start_timestamp) /
154 subsegment_duration_;
155 if (subsegment_index != current_subsegment_index_) {
156 current_subsegment_index_ = subsegment_index;
157 new_subsegment =
true;
162 if (new_segment || new_subsegment) {
165 status.Update(DispatchNonMainSamples(timestamp));
169 status.Update(DispatchSegmentInfoForAllStreams());
170 segment_info_[main_stream_index_]->start_timestamp = timestamp;
172 if (subsegment_duration_ > 0 && (new_segment || new_subsegment)) {
173 status.Update(DispatchSubsegmentInfoForAllStreams());
174 subsegment_info_[main_stream_index_]->start_timestamp = timestamp;
180 return DispatchNonMainSamples(kTimeStampToDispatchAllSamples);
183 Status ChunkingHandler::DispatchNonMainSamples(int64_t timestamp_threshold) {
185 while (status.ok() && !non_main_samples_.empty()) {
186 DCHECK_EQ(non_main_samples_.front()->stream_data_type,
187 StreamDataType::kMediaSample);
188 const size_t stream_index = non_main_samples_.front()->stream_index;
189 const MediaSample* sample = non_main_samples_.front()->media_sample.get();
192 const int64_t timestamp = sample->dts() + sample->duration() / 2;
194 (timestamp_threshold != kTimeStampToDispatchAllSamples &&
195 (
static_cast<double>(timestamp) / time_scales_[stream_index]) >
196 (
static_cast<double>(timestamp_threshold) /
197 time_scales_[main_stream_index_]));
198 VLOG(3) <<
"Sample ts: " << sample->dts() <<
" "
199 <<
" duration: " << sample->duration()
200 <<
" scale: " << time_scales_[stream_index] <<
"\n"
201 <<
" threshold: " << timestamp_threshold
202 <<
" scale: " << time_scales_[main_stream_index_]
204 : (segment_info_[stream_index] ?
" dispatch "
210 if (segment_info_[stream_index]) {
211 if (segment_info_[stream_index]->start_timestamp == -1)
212 segment_info_[stream_index]->start_timestamp = sample->dts();
213 if (subsegment_info_[stream_index] &&
214 subsegment_info_[stream_index]->start_timestamp == -1) {
215 subsegment_info_[stream_index]->start_timestamp = sample->dts();
217 last_sample_end_timestamps_[stream_index] =
218 sample->dts() + sample->duration();
219 status.Update(
Dispatch(std::move(non_main_samples_.front())));
221 non_main_samples_.pop_front();
226 Status ChunkingHandler::DispatchSegmentInfoForAllStreams() {
228 for (
size_t i = 0; i < segment_info_.size() && status.ok(); ++i) {
229 if (segment_info_[i] && segment_info_[i]->start_timestamp != -1) {
230 segment_info_[i]->duration =
231 last_sample_end_timestamps_[i] - segment_info_[i]->start_timestamp;
234 segment_info_[i].reset(
new SegmentInfo);
235 subsegment_info_[i].reset();
240 Status ChunkingHandler::DispatchSubsegmentInfoForAllStreams() {
242 for (
size_t i = 0; i < subsegment_info_.size() && status.ok(); ++i) {
243 if (subsegment_info_[i] && subsegment_info_[i]->start_timestamp != -1) {
244 subsegment_info_[i]->duration =
245 last_sample_end_timestamps_[i] - subsegment_info_[i]->start_timestamp;
248 subsegment_info_[i].reset(
new SegmentInfo);
249 subsegment_info_[i]->is_subsegment =
true;