OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "media/filters/audio_clock.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "media/base/buffers.h" |
| 9 |
| 10 namespace media { |
| 11 |
| 12 AudioClock::AudioClock(int sample_rate) |
| 13 : sample_rate_(sample_rate), last_endpoint_timestamp_(kNoTimestamp()) { |
| 14 } |
| 15 |
| 16 AudioClock::~AudioClock() { |
| 17 } |
| 18 |
| 19 void AudioClock::WroteAudio(int frames, |
| 20 int delay_frames, |
| 21 float playback_rate, |
| 22 base::TimeDelta timestamp) { |
| 23 CHECK_GT(playback_rate, 0); |
| 24 CHECK(timestamp != kNoTimestamp()); |
| 25 DCHECK_GE(frames, 0); |
| 26 DCHECK_GE(delay_frames, 0); |
| 27 |
| 28 if (last_endpoint_timestamp_ == kNoTimestamp()) |
| 29 PushBufferedAudio(delay_frames, 0, kNoTimestamp()); |
| 30 |
| 31 TrimBufferedAudioToMatchDelay(delay_frames); |
| 32 PushBufferedAudio(frames, playback_rate, timestamp); |
| 33 |
| 34 last_endpoint_timestamp_ = timestamp; |
| 35 } |
| 36 |
| 37 void AudioClock::WroteSilence(int frames, int delay_frames) { |
| 38 DCHECK_GE(frames, 0); |
| 39 DCHECK_GE(delay_frames, 0); |
| 40 |
| 41 if (last_endpoint_timestamp_ == kNoTimestamp()) |
| 42 PushBufferedAudio(delay_frames, 0, kNoTimestamp()); |
| 43 |
| 44 TrimBufferedAudioToMatchDelay(delay_frames); |
| 45 PushBufferedAudio(frames, 0, kNoTimestamp()); |
| 46 } |
| 47 |
| 48 base::TimeDelta AudioClock::CurrentMediaTimestamp() const { |
| 49 int silence_frames = 0; |
| 50 for (size_t i = 0; i < buffered_audio_.size(); ++i) { |
| 51 // Account for silence ahead of the buffer closest to being played. |
| 52 if (buffered_audio_[i].playback_rate == 0) { |
| 53 silence_frames += buffered_audio_[i].frames; |
| 54 continue; |
| 55 } |
| 56 |
| 57 // Multiply by playback rate as frames represent time-scaled audio. |
| 58 return buffered_audio_[i].endpoint_timestamp - |
| 59 base::TimeDelta::FromMicroseconds( |
| 60 ((buffered_audio_[i].frames * buffered_audio_[i].playback_rate) + |
| 61 silence_frames) / |
| 62 sample_rate_ * base::Time::kMicrosecondsPerSecond); |
| 63 } |
| 64 |
| 65 // Either: |
| 66 // 1) AudioClock is uninitialziated and we'll return kNoTimestamp() |
| 67 // 2) All previously buffered audio has been replaced by silence, |
| 68 // meaning media time is now at the last endpoint |
| 69 return last_endpoint_timestamp_; |
| 70 } |
| 71 |
| 72 void AudioClock::TrimBufferedAudioToMatchDelay(int delay_frames) { |
| 73 if (buffered_audio_.empty()) |
| 74 return; |
| 75 |
| 76 size_t i = buffered_audio_.size() - 1; |
| 77 while (true) { |
| 78 if (buffered_audio_[i].frames <= delay_frames) { |
| 79 // Reached the end before accounting for all of |delay_frames|. This |
| 80 // means we haven't written enough audio data yet to account for hardware |
| 81 // delay. In this case, do nothing. |
| 82 if (i == 0) |
| 83 return; |
| 84 |
| 85 // Keep accounting for |delay_frames|. |
| 86 delay_frames -= buffered_audio_[i].frames; |
| 87 --i; |
| 88 continue; |
| 89 } |
| 90 |
| 91 // All of |delay_frames| has been accounted for: adjust amount of frames |
| 92 // left in current buffer. All preceeding elements with index < |i| should |
| 93 // be considered played out and hence discarded. |
| 94 buffered_audio_[i].frames = delay_frames; |
| 95 break; |
| 96 } |
| 97 |
| 98 // At this point |i| points at what will be the new head of |buffered_audio_| |
| 99 // however if it contains no audio it should be removed as well. |
| 100 if (buffered_audio_[i].frames == 0) |
| 101 ++i; |
| 102 |
| 103 buffered_audio_.erase(buffered_audio_.begin(), buffered_audio_.begin() + i); |
| 104 } |
| 105 |
| 106 void AudioClock::PushBufferedAudio(int frames, |
| 107 float playback_rate, |
| 108 base::TimeDelta endpoint_timestamp) { |
| 109 if (playback_rate == 0) |
| 110 DCHECK(endpoint_timestamp == kNoTimestamp()); |
| 111 |
| 112 if (frames == 0) |
| 113 return; |
| 114 |
| 115 // Avoid creating extra elements where possible. |
| 116 if (!buffered_audio_.empty() && |
| 117 buffered_audio_.back().playback_rate == playback_rate) { |
| 118 buffered_audio_.back().frames += frames; |
| 119 buffered_audio_.back().endpoint_timestamp = endpoint_timestamp; |
| 120 return; |
| 121 } |
| 122 |
| 123 buffered_audio_.push_back( |
| 124 BufferedAudio(frames, playback_rate, endpoint_timestamp)); |
| 125 } |
| 126 |
| 127 AudioClock::BufferedAudio::BufferedAudio(int frames, |
| 128 float playback_rate, |
| 129 base::TimeDelta endpoint_timestamp) |
| 130 : frames(frames), |
| 131 playback_rate(playback_rate), |
| 132 endpoint_timestamp(endpoint_timestamp) { |
| 133 } |
| 134 |
| 135 } // namespace media |
OLD | NEW |