7 #include "packager/media/chunking/cue_alignment_handler.h" 9 #include "packager/status_macros.h" 17 const size_t kMaxBufferSize = 1000;
19 double TimeInSeconds(
const StreamInfo& info,
const StreamData& data) {
22 switch (data.stream_data_type) {
23 case StreamDataType::kMediaSample:
24 time_scale = info.time_scale();
25 if (info.stream_type() == kStreamAudio) {
33 data.media_sample->pts() + data.media_sample->duration() / 2;
35 scaled_time = data.media_sample->pts();
38 case StreamDataType::kTextSample:
41 scaled_time = data.text_sample->start_time();
46 NOTREACHED() <<
"TimeInSeconds should only be called on media samples " 51 return static_cast<double>(scaled_time) / time_scale;
54 Status GetNextCue(
double hint,
55 SyncPointQueue* sync_points,
56 std::shared_ptr<const CueEvent>* out_cue) {
60 *out_cue = sync_points->GetNext(hint);
63 return *out_cue ? Status::OK
64 : Status(error::CANCELLED,
"SyncPointQueue is cancelled.");
68 CueAlignmentHandler::CueAlignmentHandler(SyncPointQueue* sync_points)
69 : sync_points_(sync_points) {}
71 Status CueAlignmentHandler::InitializeInternal() {
72 sync_points_->AddThread();
73 stream_states_.resize(num_input_streams());
77 hint_ = sync_points_->GetHint(-1);
82 Status CueAlignmentHandler::Process(std::unique_ptr<StreamData> data) {
83 switch (data->stream_data_type) {
84 case StreamDataType::kStreamInfo:
85 return OnStreamInfo(std::move(data));
86 case StreamDataType::kTextSample:
87 case StreamDataType::kMediaSample:
88 return OnSample(std::move(data));
90 VLOG(3) <<
"Dropping unsupported data type " 91 <<
static_cast<int>(data->stream_data_type);
96 Status CueAlignmentHandler::OnFlushRequest(
size_t stream_index) {
97 stream_states_[stream_index].to_be_flushed =
true;
102 for (
const StreamState& stream_state : stream_states_) {
103 if (!stream_state.to_be_flushed) {
112 for (StreamState& stream : stream_states_) {
113 DCHECK(stream.to_be_flushed);
115 if (stream.info->stream_type() == kStreamVideo) {
116 DCHECK_EQ(stream.samples.size(), 0u)
117 <<
"Video streams should not store samples";
118 DCHECK_EQ(stream.cues.size(), 0u)
119 <<
"Video streams should not store cues";
125 while (sync_points_->HasMore(hint_)) {
126 std::shared_ptr<const CueEvent> next_cue;
127 RETURN_IF_ERROR(GetNextCue(hint_, sync_points_, &next_cue));
128 RETURN_IF_ERROR(UseNewSyncPoint(std::move(next_cue)));
133 for (StreamState& stream : stream_states_) {
134 RETURN_IF_ERROR(RunThroughSamples(&stream));
135 DCHECK_EQ(stream.samples.size(), 0u);
139 while (stream.cues.size()) {
140 RETURN_IF_ERROR(
Dispatch(std::move(stream.cues.front())));
141 stream.cues.pop_front();
148 Status CueAlignmentHandler::OnStreamInfo(std::unique_ptr<StreamData> data) {
149 StreamState& stream_state = stream_states_[data->stream_index];
152 stream_state.info = data->stream_info;
157 Status CueAlignmentHandler::OnVideoSample(std::unique_ptr<StreamData> sample) {
159 DCHECK(sample->media_sample);
161 const size_t stream_index = sample->stream_index;
162 StreamState& stream = stream_states_[stream_index];
164 const double sample_time = TimeInSeconds(*stream.info, *sample);
165 const bool is_key_frame = sample->media_sample->is_key_frame();
167 if (is_key_frame && sample_time >= hint_) {
168 auto next_sync = sync_points_->PromoteAt(sample_time);
171 LOG(ERROR) <<
"Failed to promote sync point at " << sample_time
172 <<
". This happens only if video streams are not GOP-aligned.";
173 return Status(error::INVALID_ARGUMENT,
174 "Streams are not properly GOP-aligned.");
177 RETURN_IF_ERROR(UseNewSyncPoint(std::move(next_sync)));
178 DCHECK_EQ(stream.cues.size(), 1u);
179 RETURN_IF_ERROR(
Dispatch(std::move(stream.cues.front())));
180 stream.cues.pop_front();
186 Status CueAlignmentHandler::OnNonVideoSample(
187 std::unique_ptr<StreamData> sample) {
189 DCHECK(sample->media_sample || sample->text_sample);
191 const size_t stream_index = sample->stream_index;
192 StreamState& stream_state = stream_states_[stream_index];
196 RETURN_IF_ERROR(AcceptSample(std::move(sample), &stream_state));
202 if (EveryoneWaitingAtHint()) {
203 std::shared_ptr<const CueEvent> next_sync;
204 RETURN_IF_ERROR(GetNextCue(hint_, sync_points_, &next_sync));
205 RETURN_IF_ERROR(UseNewSyncPoint(next_sync));
211 Status CueAlignmentHandler::OnSample(std::unique_ptr<StreamData> sample) {
222 const size_t stream_index = sample->stream_index;
223 const StreamType stream_type =
224 stream_states_[stream_index].info->stream_type();
226 const bool is_video = stream_type == kStreamVideo;
228 return is_video ? OnVideoSample(std::move(sample))
229 : OnNonVideoSample(
std::move(sample));
232 Status CueAlignmentHandler::UseNewSyncPoint(
233 std::shared_ptr<const CueEvent> new_sync) {
234 hint_ = sync_points_->GetHint(new_sync->time_in_seconds);
235 DCHECK_GT(hint_, new_sync->time_in_seconds);
237 for (
size_t stream_index = 0; stream_index < stream_states_.size();
239 StreamState& stream = stream_states_[stream_index];
240 stream.cues.push_back(StreamData::FromCueEvent(stream_index, new_sync));
242 RETURN_IF_ERROR(RunThroughSamples(&stream));
248 bool CueAlignmentHandler::EveryoneWaitingAtHint()
const {
249 for (
const StreamState& stream_state : stream_states_) {
250 if (stream_state.samples.empty()) {
257 Status CueAlignmentHandler::AcceptSample(std::unique_ptr<StreamData> sample,
258 StreamState* stream) {
260 DCHECK(sample->media_sample || sample->text_sample);
265 const size_t stream_index = sample->stream_index;
267 stream->samples.push_back(std::move(sample));
269 if (stream->samples.size() > kMaxBufferSize) {
270 LOG(ERROR) <<
"Stream " << stream_index <<
" has buffered " 271 << stream->samples.size() <<
" when the max is " 273 return Status(error::INVALID_ARGUMENT,
274 "Streams are not properly multiplexed.");
277 return RunThroughSamples(stream);
280 Status CueAlignmentHandler::RunThroughSamples(StreamState* stream) {
283 while (stream->cues.size() && stream->samples.size()) {
284 const double cue_time = stream->cues.front()->cue_event->time_in_seconds;
285 const double sample_time =
286 TimeInSeconds(*stream->info, *stream->samples.front());
288 if (sample_time < cue_time) {
289 RETURN_IF_ERROR(
Dispatch(std::move(stream->samples.front())));
290 stream->samples.pop_front();
292 RETURN_IF_ERROR(
Dispatch(std::move(stream->cues.front())));
293 stream->cues.pop_front();
300 while (stream->samples.size() &&
301 TimeInSeconds(*stream->info, *stream->samples.front()) < hint_) {
302 RETURN_IF_ERROR(
Dispatch(std::move(stream->samples.front())));
303 stream->samples.pop_front();
All the methods that are virtual are virtual for mocking.