1 // Copyright 2014 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_clock.h"
13 #include "base/check_op.h"
14 #include "base/numerics/safe_conversions.h"
18 AudioClock::AudioClock(base::TimeDelta start_timestamp, int sample_rate)
19 : start_timestamp_(start_timestamp),
20 microseconds_per_frame_(
21 static_cast<double>(base::Time::kMicrosecondsPerSecond) /
23 total_buffered_frames_(0),
24 front_timestamp_micros_(start_timestamp.InMicrosecondsF()),
25 back_timestamp_micros_(start_timestamp.InMicrosecondsF()) {}
27 AudioClock::~AudioClock() = default;
29 void AudioClock::WroteAudio(int frames_written,
32 double playback_rate) {
33 DCHECK_GE(frames_written, 0);
34 DCHECK_LE(frames_written, frames_requested);
35 DCHECK_GE(delay_frames, 0);
36 DCHECK_GE(playback_rate, 0);
38 // First write: initialize buffer with silence.
39 if (start_timestamp_.InMicrosecondsF() == front_timestamp_micros_ &&
41 PushBufferedAudioData(delay_frames, 0.0);
44 // Move frames from |buffered_| into the computed timestamp based on
47 // The ordering of compute -> push -> pop eliminates unnecessary memory
48 // reallocations in cases where |buffered_| gets emptied.
49 int64_t frames_played =
50 std::max(INT64_C(0), total_buffered_frames_ - delay_frames);
51 PushBufferedAudioData(frames_written, playback_rate);
52 PushBufferedAudioData(frames_requested - frames_written, 0.0);
53 PopBufferedAudioData(frames_played);
55 // Update our front and back timestamps. The back timestamp is considered the
56 // authoritative source of truth, so base the front timestamp on range of data
57 // buffered. Doing so avoids accumulation errors on the front timestamp.
58 back_timestamp_micros_ +=
59 frames_written * playback_rate * microseconds_per_frame_;
61 // Don't let front timestamp move earlier in time, as could occur due to delay
62 // frames pushed in the first write, above.
63 front_timestamp_micros_ =
64 std::max(front_timestamp_micros_,
65 back_timestamp_micros_ - ComputeBufferedMediaDurationMicros());
66 DCHECK_GE(front_timestamp_micros_, start_timestamp_.InMicrosecondsF());
67 DCHECK_LE(front_timestamp_micros_, back_timestamp_micros_);
70 void AudioClock::CompensateForSuspendedWrites(base::TimeDelta elapsed,
72 const int64_t frames_elapsed = base::ClampRound<int64_t>(
73 elapsed.InMicrosecondsF() / microseconds_per_frame_);
75 // No need to do anything if we're within the limits of our played out audio
76 // or there are no delay frames, the next WroteAudio() call will expire
77 // everything correctly.
78 if (frames_elapsed < total_buffered_frames_ || !delay_frames)
81 // Otherwise, flush everything and prime with the delay frames.
82 WroteAudio(0, 0, 0, 0);
83 DCHECK(buffered_.empty());
84 PushBufferedAudioData(delay_frames, 0.0);
87 base::TimeDelta AudioClock::TimeUntilPlayback(base::TimeDelta timestamp) const {
88 // Use front/back_timestamp() methods rather than internal members. The public
89 // methods round to the nearest microsecond for conversion to TimeDelta and
90 // the rounded value will likely be used by the caller.
91 DCHECK_GE(timestamp, front_timestamp());
92 DCHECK_LE(timestamp, back_timestamp());
94 int64_t frames_until_timestamp = 0;
95 double timestamp_us = timestamp.InMicrosecondsF();
96 double media_time_us = front_timestamp().InMicrosecondsF();
98 for (size_t i = 0; i < buffered_.size(); ++i) {
99 // Leading silence is always accounted prior to anything else.
100 if (buffered_[i].playback_rate == 0) {
101 frames_until_timestamp += buffered_[i].frames;
105 // Calculate upper bound on media time for current block of buffered frames.
106 double delta_us = buffered_[i].frames * buffered_[i].playback_rate *
107 microseconds_per_frame_;
108 double max_media_time_us = media_time_us + delta_us;
110 // Determine amount of media time to convert to frames for current block. If
111 // target timestamp falls within current block, scale the amount of frames
112 // based on remaining amount of media time.
113 if (timestamp_us <= max_media_time_us) {
114 frames_until_timestamp +=
115 buffered_[i].frames * (timestamp_us - media_time_us) / delta_us;
119 media_time_us = max_media_time_us;
120 frames_until_timestamp += buffered_[i].frames;
123 return base::Microseconds(
124 std::round(frames_until_timestamp * microseconds_per_frame_));
127 void AudioClock::ContiguousAudioDataBufferedForTesting(
128 base::TimeDelta* total,
129 base::TimeDelta* same_rate_total) const {
130 double scaled_frames = 0;
131 double scaled_frames_at_same_rate = 0;
132 bool found_silence = false;
133 for (size_t i = 0; i < buffered_.size(); ++i) {
134 if (buffered_[i].playback_rate == 0) {
135 found_silence = true;
139 // Any buffered silence breaks our contiguous stretch of audio data.
143 scaled_frames += (buffered_[i].frames * buffered_[i].playback_rate);
146 scaled_frames_at_same_rate = scaled_frames;
149 *total = base::Microseconds(scaled_frames * microseconds_per_frame_);
151 base::Microseconds(scaled_frames_at_same_rate * microseconds_per_frame_);
154 AudioClock::AudioData::AudioData(int64_t frames, double playback_rate)
155 : frames(frames), playback_rate(playback_rate) {
158 void AudioClock::PushBufferedAudioData(int64_t frames, double playback_rate) {
162 total_buffered_frames_ += frames;
164 // Avoid creating extra elements where possible.
165 if (!buffered_.empty() && buffered_.back().playback_rate == playback_rate) {
166 buffered_.back().frames += frames;
170 buffered_.push_back(AudioData(frames, playback_rate));
173 void AudioClock::PopBufferedAudioData(int64_t frames) {
174 DCHECK_LE(frames, total_buffered_frames_);
176 total_buffered_frames_ -= frames;
179 int64_t frames_to_pop = std::min(buffered_.front().frames, frames);
180 buffered_.front().frames -= frames_to_pop;
181 if (buffered_.front().frames == 0)
182 buffered_.pop_front();
184 frames -= frames_to_pop;
188 double AudioClock::ComputeBufferedMediaDurationMicros() const {
189 double scaled_frames = 0;
190 for (const auto& buffer : buffered_)
191 scaled_frames += buffer.frames * buffer.playback_rate;
192 return scaled_frames * microseconds_per_frame_;