1 // Copyright 2016 The Chromium Authors. All rights reserved.
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"
9 #include "base/stl_util.h"
10 #include "base/time/time.h"
11 #include "media/base/audio_decoder_config.h"
12 #include "media/base/media_util.h"
13 #include "media/base/mock_media_log.h"
14 #include "media/base/test_helpers.h"
15 #include "testing/gtest/include/gtest/gtest.h"
17 using ::testing::HasSubstr;
21 // Constants to specify the type of audio data used.
22 static const AudioCodec kCodec = kCodecVorbis;
23 static const SampleFormat kSampleFormat = kSampleFormatPlanarF32;
24 static const base::TimeDelta kSeekPreroll;
25 static const int kSamplesPerSecond = 10000;
26 static const base::TimeDelta kBufferDuration =
27 base::TimeDelta::FromMilliseconds(20);
28 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
29 static const int kChannelCount = 2;
30 static const int kChannels = ChannelLayoutToChannelCount(kChannelLayout);
31 static const int kFramesPerBuffer = kBufferDuration.InMicroseconds() *
33 base::Time::kMicrosecondsPerSecond;
36 // 1. Output delay: number of encoded buffers before first decoded output
37 // 2. Codec delay: number of frames of codec delay in decoder config
38 // 3. Front discard: front discard for the first buffer
39 using ValidatorTestParams = testing::tuple<int, int, base::TimeDelta>;
41 class AudioTimestampValidatorTest
42 : public testing::Test,
43 public ::testing::WithParamInterface<ValidatorTestParams> {
45 AudioTimestampValidatorTest() = default;
48 void SetUp() override {
49 output_delay_ = testing::get<0>(GetParam());
50 codec_delay_ = testing::get<1>(GetParam());
51 front_discard_ = testing::get<2>(GetParam());
58 base::TimeDelta front_discard_;
60 testing::StrictMock<MockMediaLog> media_log_;
63 TEST_P(AudioTimestampValidatorTest, WarnForEraticTimes) {
64 AudioDecoderConfig decoder_config;
65 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
66 kSamplesPerSecond, EmptyExtraData(),
67 EncryptionScheme::kUnencrypted, kSeekPreroll,
70 // Validator should fail to stabilize pattern for timestamp expectations.
72 HasSubstr("Failed to reconcile encoded audio times "
73 "with decoded output."));
75 // No gap warnings should be emitted because the timestamps expectations never
77 EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")).Times(0);
79 AudioTimestampValidator validator(decoder_config, &media_log_);
81 const base::TimeDelta kRandomOffsets[] = {
82 base::TimeDelta::FromMilliseconds(100),
83 base::TimeDelta::FromMilliseconds(350)};
85 for (int i = 0; i < 100; ++i) {
86 // Each buffer's timestamp is kBufferDuration from the previous buffer.
87 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
89 // Ping-pong between two random offsets to prevent validator from
90 // stabilizing timestamp pattern.
91 base::TimeDelta randomOffset =
92 kRandomOffsets[i % base::size(kRandomOffsets)];
93 encoded_buffer->set_timestamp(i * kBufferDuration + randomOffset);
96 encoded_buffer->set_discard_padding(
97 std::make_pair(front_discard_, base::TimeDelta()));
100 validator.CheckForTimestampGap(*encoded_buffer);
102 if (i >= output_delay_) {
103 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
104 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
105 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
106 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
107 0.0f, kFramesPerBuffer, i * kBufferDuration);
108 validator.RecordOutputDuration(*decoded_buffer);
113 TEST_P(AudioTimestampValidatorTest, NoWarningForValidTimes) {
114 AudioDecoderConfig decoder_config;
115 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
116 kSamplesPerSecond, EmptyExtraData(),
117 EncryptionScheme::kUnencrypted, kSeekPreroll,
120 // Validator should quickly stabilize pattern for timestamp expectations.
121 EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times "
122 "with decoded output."))
125 // Expect no gap warnings for series of buffers with valid timestamps.
126 EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")).Times(0);
128 AudioTimestampValidator validator(decoder_config, &media_log_);
130 for (int i = 0; i < 100; ++i) {
131 // Each buffer's timestamp is kBufferDuration from the previous buffer.
132 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
133 encoded_buffer->set_timestamp(i * kBufferDuration);
136 encoded_buffer->set_discard_padding(
137 std::make_pair(front_discard_, base::TimeDelta()));
140 validator.CheckForTimestampGap(*encoded_buffer);
142 if (i >= output_delay_) {
143 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
144 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
145 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
146 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
147 0.0f, kFramesPerBuffer, i * kBufferDuration);
148 validator.RecordOutputDuration(*decoded_buffer);
153 TEST_P(AudioTimestampValidatorTest, SingleWarnForSingleLargeGap) {
154 AudioDecoderConfig decoder_config;
155 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
156 kSamplesPerSecond, EmptyExtraData(),
157 EncryptionScheme::kUnencrypted, kSeekPreroll,
160 AudioTimestampValidator validator(decoder_config, &media_log_);
162 // Validator should quickly stabilize pattern for timestamp expectations.
163 EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times "
164 "with decoded output."))
167 for (int i = 0; i < 100; ++i) {
168 // Halfway through the stream, introduce sudden gap of 50 milliseconds.
169 base::TimeDelta offset;
171 offset = base::TimeDelta::FromMilliseconds(100);
173 // This gap never widens, so expect only a single warning when its first
176 EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected"));
178 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
179 encoded_buffer->set_timestamp(i * kBufferDuration + offset);
182 encoded_buffer->set_discard_padding(
183 std::make_pair(front_discard_, base::TimeDelta()));
186 validator.CheckForTimestampGap(*encoded_buffer);
188 if (i >= output_delay_) {
189 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
190 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
191 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
192 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
193 0.0f, kFramesPerBuffer, i * kBufferDuration);
194 validator.RecordOutputDuration(*decoded_buffer);
199 TEST_P(AudioTimestampValidatorTest, RepeatedWarnForSlowAccumulatingDrift) {
200 AudioDecoderConfig decoder_config;
201 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
202 kSamplesPerSecond, EmptyExtraData(),
203 EncryptionScheme::kUnencrypted, kSeekPreroll,
206 AudioTimestampValidator validator(decoder_config, &media_log_);
208 EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times "
209 "with decoded output."))
212 int num_timestamp_gap_warnings = 0;
213 const int kMaxTimestampGapWarnings = 10; // Must be the same as in .cc
215 for (int i = 0; i < 100; ++i) {
216 // Wait for delayed output to begin plus an additional two iterations to
217 // start using drift offset. The the two iterations without offset will
218 // allow the validator to stabilize the pattern of timestamps and begin
219 // checking for gaps. Once stable, increase offset by 1 millisecond for each
221 base::TimeDelta offset;
222 if (i >= output_delay_ + 2)
223 offset = i * base::TimeDelta::FromMilliseconds(1);
225 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
226 encoded_buffer->set_timestamp((i * kBufferDuration) + offset);
228 // Expect gap warnings to start when drift hits 50 milliseconds. Warnings
229 // should continue as the gap widens until log limit is hit.
231 if (offset > base::TimeDelta::FromMilliseconds(50)) {
232 EXPECT_LIMITED_MEDIA_LOG(HasSubstr("timestamp gap detected"),
233 num_timestamp_gap_warnings,
234 kMaxTimestampGapWarnings);
237 validator.CheckForTimestampGap(*encoded_buffer);
239 if (i >= output_delay_) {
240 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
241 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
242 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
243 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
244 0.0f, kFramesPerBuffer, i * kBufferDuration);
245 validator.RecordOutputDuration(*decoded_buffer);
250 // Test with cartesian product of various output delay, codec delay, and front
251 // discard values. These simulate configurations for different containers/codecs
252 // which present different challenges when building timestamp expectations.
253 INSTANTIATE_TEST_SUITE_P(
255 AudioTimestampValidatorTest,
257 ::testing::Values(0, 10), // output delay
258 ::testing::Values(0, 512), // codec delay
259 ::testing::Values(base::TimeDelta(), // front discard
260 base::TimeDelta::FromMilliseconds(65))));