7 #include "packager/media/chunking/cue_alignment_handler.h" 9 #include "packager/status_macros.h" 17 const size_t kMaxBufferSize = 1000;
19 int64_t GetScaledTime(
const StreamInfo& info,
const StreamData& data) {
20 DCHECK(data.text_sample || data.media_sample);
22 if (data.text_sample) {
23 return data.text_sample->start_time();
26 if (info.stream_type() == kStreamText) {
31 <<
"A text streams should use text samples, not media samples.";
34 if (info.stream_type() == kStreamAudio) {
38 return data.media_sample->pts() + data.media_sample->duration() / 2;
41 DCHECK_EQ(info.stream_type(), kStreamVideo);
42 return data.media_sample->pts();
45 double TimeInSeconds(
const StreamInfo& info,
const StreamData& data) {
46 const int64_t scaled_time = GetScaledTime(info, data);
47 const uint32_t time_scale = info.time_scale();
49 return static_cast<double>(scaled_time) / time_scale;
52 Status GetNextCue(
double hint,
53 SyncPointQueue* sync_points,
54 std::shared_ptr<const CueEvent>* out_cue) {
58 *out_cue = sync_points->GetNext(hint);
61 return *out_cue ? Status::OK
62 : Status(error::CANCELLED,
"SyncPointQueue is cancelled.");
66 CueAlignmentHandler::CueAlignmentHandler(SyncPointQueue* sync_points)
67 : sync_points_(sync_points) {}
69 Status CueAlignmentHandler::InitializeInternal() {
70 sync_points_->AddThread();
71 stream_states_.resize(num_input_streams());
75 hint_ = sync_points_->GetHint(-1);
80 Status CueAlignmentHandler::Process(std::unique_ptr<StreamData> data) {
81 switch (data->stream_data_type) {
82 case StreamDataType::kStreamInfo:
83 return OnStreamInfo(std::move(data));
84 case StreamDataType::kTextSample:
85 case StreamDataType::kMediaSample:
86 return OnSample(std::move(data));
88 VLOG(3) <<
"Dropping unsupported data type " 89 <<
static_cast<int>(data->stream_data_type);
94 Status CueAlignmentHandler::OnFlushRequest(
size_t stream_index) {
95 stream_states_[stream_index].to_be_flushed =
true;
100 for (
const StreamState& stream_state : stream_states_) {
101 if (!stream_state.to_be_flushed) {
110 for (StreamState& stream : stream_states_) {
111 DCHECK(stream.to_be_flushed);
113 if (stream.info->stream_type() == kStreamVideo) {
114 DCHECK_EQ(stream.samples.size(), 0u)
115 <<
"Video streams should not store samples";
116 DCHECK_EQ(stream.cues.size(), 0u)
117 <<
"Video streams should not store cues";
123 while (sync_points_->HasMore(hint_)) {
124 std::shared_ptr<const CueEvent> next_cue;
125 RETURN_IF_ERROR(GetNextCue(hint_, sync_points_, &next_cue));
126 RETURN_IF_ERROR(UseNewSyncPoint(std::move(next_cue)));
131 for (StreamState& stream : stream_states_) {
132 RETURN_IF_ERROR(RunThroughSamples(&stream));
133 DCHECK_EQ(stream.samples.size(), 0u);
137 while (stream.cues.size()) {
138 RETURN_IF_ERROR(
Dispatch(std::move(stream.cues.front())));
139 stream.cues.pop_front();
146 Status CueAlignmentHandler::OnStreamInfo(std::unique_ptr<StreamData> data) {
147 StreamState& stream_state = stream_states_[data->stream_index];
150 stream_state.info = data->stream_info;
155 Status CueAlignmentHandler::OnVideoSample(std::unique_ptr<StreamData> sample) {
157 DCHECK(sample->media_sample);
159 const size_t stream_index = sample->stream_index;
160 StreamState& stream = stream_states_[stream_index];
162 const double sample_time = TimeInSeconds(*stream.info, *sample);
163 const bool is_key_frame = sample->media_sample->is_key_frame();
165 if (is_key_frame && sample_time >= hint_) {
166 auto next_sync = sync_points_->PromoteAt(sample_time);
169 LOG(ERROR) <<
"Failed to promote sync point at " << sample_time
170 <<
". This happens only if video streams are not GOP-aligned.";
171 return Status(error::INVALID_ARGUMENT,
172 "Streams are not properly GOP-aligned.");
175 RETURN_IF_ERROR(UseNewSyncPoint(std::move(next_sync)));
176 DCHECK_EQ(stream.cues.size(), 1u);
177 RETURN_IF_ERROR(
Dispatch(std::move(stream.cues.front())));
178 stream.cues.pop_front();
184 Status CueAlignmentHandler::OnNonVideoSample(
185 std::unique_ptr<StreamData> sample) {
187 DCHECK(sample->media_sample || sample->text_sample);
189 const size_t stream_index = sample->stream_index;
190 StreamState& stream_state = stream_states_[stream_index];
194 RETURN_IF_ERROR(AcceptSample(std::move(sample), &stream_state));
200 if (EveryoneWaitingAtHint()) {
201 std::shared_ptr<const CueEvent> next_sync;
202 RETURN_IF_ERROR(GetNextCue(hint_, sync_points_, &next_sync));
203 RETURN_IF_ERROR(UseNewSyncPoint(next_sync));
209 Status CueAlignmentHandler::OnSample(std::unique_ptr<StreamData> sample) {
220 const size_t stream_index = sample->stream_index;
221 const StreamType stream_type =
222 stream_states_[stream_index].info->stream_type();
224 const bool is_video = stream_type == kStreamVideo;
226 return is_video ? OnVideoSample(std::move(sample))
227 : OnNonVideoSample(
std::move(sample));
230 Status CueAlignmentHandler::UseNewSyncPoint(
231 std::shared_ptr<const CueEvent> new_sync) {
232 hint_ = sync_points_->GetHint(new_sync->time_in_seconds);
233 DCHECK_GT(hint_, new_sync->time_in_seconds);
235 for (
size_t stream_index = 0; stream_index < stream_states_.size();
237 StreamState& stream = stream_states_[stream_index];
238 stream.cues.push_back(StreamData::FromCueEvent(stream_index, new_sync));
240 RETURN_IF_ERROR(RunThroughSamples(&stream));
246 bool CueAlignmentHandler::EveryoneWaitingAtHint()
const {
247 for (
const StreamState& stream_state : stream_states_) {
248 if (stream_state.samples.empty()) {
255 Status CueAlignmentHandler::AcceptSample(std::unique_ptr<StreamData> sample,
256 StreamState* stream) {
258 DCHECK(sample->media_sample || sample->text_sample);
263 const size_t stream_index = sample->stream_index;
265 stream->samples.push_back(std::move(sample));
267 if (stream->samples.size() > kMaxBufferSize) {
268 LOG(ERROR) <<
"Stream " << stream_index <<
" has buffered " 269 << stream->samples.size() <<
" when the max is " 271 return Status(error::INVALID_ARGUMENT,
272 "Streams are not properly multiplexed.");
275 return RunThroughSamples(stream);
278 Status CueAlignmentHandler::RunThroughSamples(StreamState* stream) {
281 while (stream->cues.size() && stream->samples.size()) {
282 const double cue_time = stream->cues.front()->cue_event->time_in_seconds;
283 const double sample_time =
284 TimeInSeconds(*stream->info, *stream->samples.front());
286 if (sample_time < cue_time) {
287 RETURN_IF_ERROR(
Dispatch(std::move(stream->samples.front())));
288 stream->samples.pop_front();
290 RETURN_IF_ERROR(
Dispatch(std::move(stream->cues.front())));
291 stream->cues.pop_front();
298 while (stream->samples.size() &&
299 TimeInSeconds(*stream->info, *stream->samples.front()) < hint_) {
300 RETURN_IF_ERROR(
Dispatch(std::move(stream->samples.front())));
301 stream->samples.pop_front();
All the methods that are virtual are virtual for mocking.