1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/audio_timestamp_validator.h"
11 // Defines how many milliseconds of DecoderBuffer timestamp gap will be allowed
12 // before warning the user. See CheckForTimestampGap(). Value of 50 chosen, as
13 // this is low enough to catch issues early, but high enough to avoid noise for
14 // containers like WebM that default to low granularity timestamp precision.
15 const int kGapWarningThresholdMsec = 50;
17 // Limits the number of adjustments to |audio_ts_offset_| in order to reach a
18 // stable state where gaps between encoded timestamps match decoded output
19 // intervals. See CheckForTimestampGap().
20 const int kLimitTriesForStableTiming = 5;
22 // Limits the milliseconds of difference between expected and actual timestamps
23 // gaps to consider timestamp expectations "stable". 1 chosen because some
24 // containers (WebM) default to millisecond timestamp precision. See
25 // CheckForTimestampGap().
26 const int kStableTimeGapThrsholdMsec = 1;
28 // Maximum number of timestamp gap warnings sent to MediaLog.
29 const int kMaxTimestampGapWarnings = 10;
31 AudioTimestampValidator::AudioTimestampValidator(
32 const AudioDecoderConfig& decoder_config,
34 : has_codec_delay_(decoder_config.codec_delay() > 0),
35 media_log_(media_log),
36 audio_base_ts_(kNoTimestamp),
37 reached_stable_state_(false),
38 num_unstable_audio_tries_(0),
39 limit_unstable_audio_tries_(kLimitTriesForStableTiming),
40 drift_warning_threshold_msec_(kGapWarningThresholdMsec) {
41 DCHECK(decoder_config.IsValidConfig());
44 AudioTimestampValidator::~AudioTimestampValidator() = default;
46 void AudioTimestampValidator::CheckForTimestampGap(
47 const DecoderBuffer& buffer) {
48 if (buffer.end_of_stream())
50 DCHECK_NE(kNoTimestamp, buffer.timestamp());
52 // If audio_base_ts_ == kNoTimestamp, we are processing our first buffer.
53 // If stream has neither codec delay nor discard padding, we should expect
54 // timestamps and output durations to line up from the start (i.e. be stable).
55 if (audio_base_ts_ == kNoTimestamp && !has_codec_delay_ &&
56 buffer.discard_padding().first == base::TimeDelta() &&
57 buffer.discard_padding().second == base::TimeDelta()) {
58 DVLOG(3) << __func__ << " Expecting stable timestamps - stream has neither "
59 << "codec delay nor discard padding.";
60 limit_unstable_audio_tries_ = 0;
63 // Don't continue checking timestamps if we've exhausted tries to reach stable
64 // state. This suggests the media's encoded timestamps are way off.
65 if (num_unstable_audio_tries_ > limit_unstable_audio_tries_)
68 // Keep resetting encode base ts until we start getting decode output. Some
69 // codecs/containers (e.g. chained Ogg) will take several encoded buffers
70 // before producing the first decoded output.
71 if (!audio_output_ts_helper_) {
72 audio_base_ts_ = buffer.timestamp();
74 << " setting audio_base:" << audio_base_ts_.InMicroseconds();
78 base::TimeDelta expected_ts = audio_output_ts_helper_->GetTimestamp();
79 base::TimeDelta ts_delta = buffer.timestamp() - expected_ts;
81 // Reconciling encoded buffer timestamps with decoded output often requires
82 // adjusting expectations by some offset. This accounts for varied (and at
83 // this point unknown) handling of front trimming and codec delay. Codec delay
84 // and skip trimming may or may not be accounted for in the encoded timestamps
85 // depending on the codec (e.g. MP3 vs Opus) and demuxers used (e.g. FFmpeg
86 // vs MSE stream parsers).
87 if (!reached_stable_state_) {
88 if (std::abs(ts_delta.InMilliseconds()) < kStableTimeGapThrsholdMsec) {
89 reached_stable_state_ = true;
90 DVLOG(3) << __func__ << " stabilized! tries:" << num_unstable_audio_tries_
92 << audio_output_ts_helper_->base_timestamp().InMicroseconds();
94 base::TimeDelta orig_offset = audio_output_ts_helper_->base_timestamp();
96 // Save since this gets reset when we set new base time.
97 int64_t decoded_frame_count = audio_output_ts_helper_->frame_count();
98 audio_output_ts_helper_->SetBaseTimestamp(orig_offset + ts_delta);
99 audio_output_ts_helper_->AddFrames(decoded_frame_count);
102 << " NOT stabilized. tries:" << num_unstable_audio_tries_
103 << " offset was:" << orig_offset.InMicroseconds() << " now:"
104 << audio_output_ts_helper_->base_timestamp().InMicroseconds();
105 num_unstable_audio_tries_++;
107 // Let developers know if their files timestamps are way off from
108 if (num_unstable_audio_tries_ > limit_unstable_audio_tries_) {
109 MEDIA_LOG(WARNING, media_log_)
110 << "Failed to reconcile encoded audio times with decoded output.";
114 // Don't bother with further checking until we reach stable state.
118 if (std::abs(ts_delta.InMilliseconds()) > drift_warning_threshold_msec_) {
119 LIMITED_MEDIA_LOG(WARNING, media_log_, num_timestamp_gap_warnings_,
120 kMaxTimestampGapWarnings)
121 << " Large timestamp gap detected; may cause AV sync to drift."
122 << " time:" << buffer.timestamp().InMicroseconds() << "us"
123 << " expected:" << expected_ts.InMicroseconds() << "us"
124 << " delta:" << ts_delta.InMicroseconds() << "us";
125 // Increase threshold to avoid log spam but, let us know if gap widens.
126 drift_warning_threshold_msec_ = std::abs(ts_delta.InMilliseconds());
128 DVLOG(3) << __func__ << " delta:" << ts_delta.InMicroseconds()
129 << " expected_ts:" << expected_ts.InMicroseconds()
130 << " actual_ts:" << buffer.timestamp().InMicroseconds()
131 << " audio_ts_offset:"
132 << audio_output_ts_helper_->base_timestamp().InMicroseconds();
135 void AudioTimestampValidator::RecordOutputDuration(
136 const AudioBuffer& audio_buffer) {
137 if (!audio_output_ts_helper_) {
138 DCHECK_NE(audio_base_ts_, kNoTimestamp);
139 // SUBTLE: deliberately creating this with output buffer sample rate because
140 // demuxer stream config is potentially stale for implicit AAC.
141 audio_output_ts_helper_ =
142 std::make_unique<AudioTimestampHelper>(audio_buffer.sample_rate());
143 audio_output_ts_helper_->SetBaseTimestamp(audio_base_ts_);
146 DVLOG(3) << __func__ << " " << audio_buffer.frame_count() << " frames";
147 audio_output_ts_helper_->AddFrames(audio_buffer.frame_count());