1 // Copyright 2014 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_clock.h"
7 #include "base/logging.h"
8 #include "media/base/buffers.h"
12 AudioClock::AudioClock(int sample_rate
)
13 : sample_rate_(sample_rate
), last_endpoint_timestamp_(kNoTimestamp()) {
16 AudioClock::~AudioClock() {
19 void AudioClock::WroteAudio(int frames
,
22 base::TimeDelta timestamp
) {
23 CHECK_GT(playback_rate
, 0);
24 CHECK(timestamp
!= kNoTimestamp());
26 DCHECK_GE(delay_frames
, 0);
28 if (last_endpoint_timestamp_
== kNoTimestamp())
29 PushBufferedAudio(delay_frames
, 0, kNoTimestamp());
31 TrimBufferedAudioToMatchDelay(delay_frames
);
32 PushBufferedAudio(frames
, playback_rate
, timestamp
);
34 last_endpoint_timestamp_
= timestamp
;
37 void AudioClock::WroteSilence(int frames
, int delay_frames
) {
39 DCHECK_GE(delay_frames
, 0);
41 if (last_endpoint_timestamp_
== kNoTimestamp())
42 PushBufferedAudio(delay_frames
, 0, kNoTimestamp());
44 TrimBufferedAudioToMatchDelay(delay_frames
);
45 PushBufferedAudio(frames
, 0, kNoTimestamp());
48 base::TimeDelta
AudioClock::CurrentMediaTimestamp(
49 base::TimeDelta time_since_writing
) const {
51 static_cast<int>(time_since_writing
.InSecondsF() * sample_rate_
);
52 int silence_frames
= 0;
53 for (size_t i
= 0; i
< buffered_audio_
.size(); ++i
) {
54 int frames
= buffered_audio_
[i
].frames
;
55 if (frames_to_skip
> 0) {
56 if (frames
<= frames_to_skip
) {
57 frames_to_skip
-= frames
;
60 frames
-= frames_to_skip
;
64 // Account for silence ahead of the buffer closest to being played.
65 if (buffered_audio_
[i
].playback_rate
== 0) {
66 silence_frames
+= frames
;
70 // Multiply by playback rate as frames represent time-scaled audio.
71 return buffered_audio_
[i
].endpoint_timestamp
-
72 base::TimeDelta::FromMicroseconds(
73 ((frames
* buffered_audio_
[i
].playback_rate
) + silence_frames
) /
74 sample_rate_
* base::Time::kMicrosecondsPerSecond
);
78 // 1) AudioClock is uninitialziated and we'll return kNoTimestamp()
79 // 2) All previously buffered audio has been replaced by silence,
80 // meaning media time is now at the last endpoint
81 return last_endpoint_timestamp_
;
84 void AudioClock::TrimBufferedAudioToMatchDelay(int delay_frames
) {
85 if (buffered_audio_
.empty())
88 size_t i
= buffered_audio_
.size() - 1;
90 if (buffered_audio_
[i
].frames
<= delay_frames
) {
91 // Reached the end before accounting for all of |delay_frames|. This
92 // means we haven't written enough audio data yet to account for hardware
93 // delay. In this case, do nothing.
97 // Keep accounting for |delay_frames|.
98 delay_frames
-= buffered_audio_
[i
].frames
;
103 // All of |delay_frames| has been accounted for: adjust amount of frames
104 // left in current buffer. All preceeding elements with index < |i| should
105 // be considered played out and hence discarded.
106 buffered_audio_
[i
].frames
= delay_frames
;
110 // At this point |i| points at what will be the new head of |buffered_audio_|
111 // however if it contains no audio it should be removed as well.
112 if (buffered_audio_
[i
].frames
== 0)
115 buffered_audio_
.erase(buffered_audio_
.begin(), buffered_audio_
.begin() + i
);
118 void AudioClock::PushBufferedAudio(int frames
,
120 base::TimeDelta endpoint_timestamp
) {
121 if (playback_rate
== 0)
122 DCHECK(endpoint_timestamp
== kNoTimestamp());
127 // Avoid creating extra elements where possible.
128 if (!buffered_audio_
.empty() &&
129 buffered_audio_
.back().playback_rate
== playback_rate
) {
130 buffered_audio_
.back().frames
+= frames
;
131 buffered_audio_
.back().endpoint_timestamp
= endpoint_timestamp
;
135 buffered_audio_
.push_back(
136 BufferedAudio(frames
, playback_rate
, endpoint_timestamp
));
139 AudioClock::BufferedAudio::BufferedAudio(int frames
,
141 base::TimeDelta endpoint_timestamp
)
143 playback_rate(playback_rate
),
144 endpoint_timestamp(endpoint_timestamp
) {