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/time/time.h"
10 #include "media/base/audio_decoder_config.h"
11 #include "media/base/media_util.h"
12 #include "media/base/mock_media_log.h"
13 #include "media/base/test_helpers.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 using ::testing::HasSubstr;
20 // Constants to specify the type of audio data used.
21 static const AudioCodec kCodec = kCodecVorbis;
22 static const SampleFormat kSampleFormat = kSampleFormatPlanarF32;
23 static const base::TimeDelta kSeekPreroll;
24 static const int kSamplesPerSecond = 10000;
25 static const base::TimeDelta kBufferDuration =
26 base::TimeDelta::FromMilliseconds(20);
27 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
28 static const int kChannelCount = 2;
29 static const int kChannels = ChannelLayoutToChannelCount(kChannelLayout);
30 static const int kFramesPerBuffer = kBufferDuration.InMicroseconds() *
32 base::Time::kMicrosecondsPerSecond;
35 // 1. Output delay: number of encoded buffers before first decoded output
36 // 2. Codec delay: number of frames of codec delay in decoder config
37 // 3. Front discard: front discard for the first buffer
38 using ValidatorTestParams = testing::tuple<int, int, base::TimeDelta>;
40 class AudioTimestampValidatorTest
41 : public testing::Test,
42 public ::testing::WithParamInterface<ValidatorTestParams> {
44 AudioTimestampValidatorTest() = default;
47 void SetUp() override {
48 output_delay_ = testing::get<0>(GetParam());
49 codec_delay_ = testing::get<1>(GetParam());
50 front_discard_ = testing::get<2>(GetParam());
57 base::TimeDelta front_discard_;
59 testing::StrictMock<MockMediaLog> media_log_;
62 TEST_P(AudioTimestampValidatorTest, WarnForEraticTimes) {
63 AudioDecoderConfig decoder_config;
64 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
65 kSamplesPerSecond, EmptyExtraData(), Unencrypted(),
66 kSeekPreroll, codec_delay_);
68 // Validator should fail to stabilize pattern for timestamp expectations.
70 HasSubstr("Failed to reconcile encoded audio times "
71 "with decoded output."));
73 // No gap warnings should be emitted because the timestamps expectations never
75 EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")).Times(0);
77 AudioTimestampValidator validator(decoder_config, &media_log_);
79 const base::TimeDelta kRandomOffsets[] = {
80 base::TimeDelta::FromMilliseconds(100),
81 base::TimeDelta::FromMilliseconds(350)};
83 for (int i = 0; i < 100; ++i) {
84 // Each buffer's timestamp is kBufferDuration from the previous buffer.
85 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
87 // Ping-pong between two random offsets to prevent validator from
88 // stabilizing timestamp pattern.
89 base::TimeDelta randomOffset =
90 kRandomOffsets[i % arraysize(kRandomOffsets)];
91 encoded_buffer->set_timestamp(i * kBufferDuration + randomOffset);
94 encoded_buffer->set_discard_padding(
95 std::make_pair(front_discard_, base::TimeDelta()));
98 validator.CheckForTimestampGap(*encoded_buffer);
100 if (i >= output_delay_) {
101 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
102 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
103 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
104 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
105 0.0f, kFramesPerBuffer, i * kBufferDuration);
106 validator.RecordOutputDuration(decoded_buffer.get());
111 TEST_P(AudioTimestampValidatorTest, NoWarningForValidTimes) {
112 AudioDecoderConfig decoder_config;
113 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
114 kSamplesPerSecond, EmptyExtraData(), Unencrypted(),
115 kSeekPreroll, codec_delay_);
117 // Validator should quickly stabilize pattern for timestamp expectations.
118 EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times "
119 "with decoded output."))
122 // Expect no gap warnings for series of buffers with valid timestamps.
123 EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")).Times(0);
125 AudioTimestampValidator validator(decoder_config, &media_log_);
127 for (int i = 0; i < 100; ++i) {
128 // Each buffer's timestamp is kBufferDuration from the previous buffer.
129 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
130 encoded_buffer->set_timestamp(i * kBufferDuration);
133 encoded_buffer->set_discard_padding(
134 std::make_pair(front_discard_, base::TimeDelta()));
137 validator.CheckForTimestampGap(*encoded_buffer);
139 if (i >= output_delay_) {
140 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
141 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
142 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
143 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
144 0.0f, kFramesPerBuffer, i * kBufferDuration);
145 validator.RecordOutputDuration(decoded_buffer.get());
150 TEST_P(AudioTimestampValidatorTest, SingleWarnForSingleLargeGap) {
151 AudioDecoderConfig decoder_config;
152 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
153 kSamplesPerSecond, EmptyExtraData(), Unencrypted(),
154 kSeekPreroll, codec_delay_);
156 AudioTimestampValidator validator(decoder_config, &media_log_);
158 // Validator should quickly stabilize pattern for timestamp expectations.
159 EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times "
160 "with decoded output."))
163 for (int i = 0; i < 100; ++i) {
164 // Halfway through the stream, introduce sudden gap of 50 milliseconds.
165 base::TimeDelta offset;
167 offset = base::TimeDelta::FromMilliseconds(100);
169 // This gap never widens, so expect only a single warning when its first
172 EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected"));
174 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
175 encoded_buffer->set_timestamp(i * kBufferDuration + offset);
178 encoded_buffer->set_discard_padding(
179 std::make_pair(front_discard_, base::TimeDelta()));
182 validator.CheckForTimestampGap(*encoded_buffer);
184 if (i >= output_delay_) {
185 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
186 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
187 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
188 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
189 0.0f, kFramesPerBuffer, i * kBufferDuration);
190 validator.RecordOutputDuration(decoded_buffer.get());
195 TEST_P(AudioTimestampValidatorTest, RepeatedWarnForSlowAccumulatingDrift) {
196 AudioDecoderConfig decoder_config;
197 decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout,
198 kSamplesPerSecond, EmptyExtraData(), Unencrypted(),
199 kSeekPreroll, codec_delay_);
201 AudioTimestampValidator validator(decoder_config, &media_log_);
203 EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times "
204 "with decoded output."))
207 int num_timestamp_gap_warnings = 0;
208 const int kMaxTimestampGapWarnings = 10; // Must be the same as in .cc
210 for (int i = 0; i < 100; ++i) {
211 // Wait for delayed output to begin plus an additional two iterations to
212 // start using drift offset. The the two iterations without offset will
213 // allow the validator to stabilize the pattern of timestamps and begin
214 // checking for gaps. Once stable, increase offset by 1 millisecond for each
216 base::TimeDelta offset;
217 if (i >= output_delay_ + 2)
218 offset = i * base::TimeDelta::FromMilliseconds(1);
220 scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0);
221 encoded_buffer->set_timestamp((i * kBufferDuration) + offset);
223 // Expect gap warnings to start when drift hits 50 milliseconds. Warnings
224 // should continue as the gap widens until log limit is hit.
226 if (offset > base::TimeDelta::FromMilliseconds(50)) {
227 EXPECT_LIMITED_MEDIA_LOG(HasSubstr("timestamp gap detected"),
228 num_timestamp_gap_warnings,
229 kMaxTimestampGapWarnings);
232 validator.CheckForTimestampGap(*encoded_buffer);
234 if (i >= output_delay_) {
235 // kFramesPerBuffer is derived to perfectly match kBufferDuration, so
236 // no gaps exists as long as timestamps are exactly kBufferDuration apart.
237 scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>(
238 kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f,
239 0.0f, kFramesPerBuffer, i * kBufferDuration);
240 validator.RecordOutputDuration(decoded_buffer.get());
245 // Test with cartesian product of various output delay, codec delay, and front
246 // discard values. These simulate configurations for different containers/codecs
247 // which present different challenges when building timestamp expectations.
248 INSTANTIATE_TEST_CASE_P(
250 AudioTimestampValidatorTest,
252 ::testing::Values(0, 10), // output delay
253 ::testing::Values(0, 512), // codec delay
254 ::testing::Values(base::TimeDelta(), // front discard
255 base::TimeDelta::FromMilliseconds(65))));