Home | History | Annotate | Download | only in filters
      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
    136