1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147

media / base / audio_shifter.h [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_BASE_AUDIO_SHIFTER_H_
#define MEDIA_BASE_AUDIO_SHIFTER_H_

#include <stddef.h>

#include <memory>

#include "base/containers/circular_deque.h"
#include "base/time/time.h"
#include "media/base/media_export.h"
#include "media/base/multi_channel_resampler.h"

namespace media {

class AudioBus;
class ClockSmoother;

// This class works like a buffer between a push based audio source
// and a pull-based audio sink. The source and sink should operate
// at nominally the same rate, but since they may run on different
// hardware clocks, the rate may differ a little. If left unchecked,
// this difference will first cause lip sync issues between audio
// and video and eventually it will cause buffer overruns/underruns.
// This class solves all that by dynamically resampling the audio
// so that both input and output sources are happy.
//
// A note about TimeTicks. The playout_time specified in Push and
// Pull calls must come from the same timeline. That timeline can
// be anything you choose as it is never compared to any real-world
// clocks, but they must come from the same clock. Specifically,
// specifying samples / rate as the playout time in Push() or Pull()
// will NOT work.
//
class MEDIA_EXPORT AudioShifter {
 public:
  // |max_buffer_size| is how much audio we are allowed to buffer.
  // Often, this can be set fairly large as Push() will limit the
  // size when it specifies when to play the audio.
  // |clock_accuracy| is used to determine if a skip has occurred
  // in the audio (as opposed to an inaccuracy in the timestamp.)
  // It also limits the smallest amount of buffering allowed.
  // |adjustement_time| specifies how long time should be used
  // to adjust the audio. This should normally at least a few
  // seconds. The larger the value, the smoother and less audible
  // the transitions will be. (But it means that perfect audio
  // sync will take longer to achieve.)
  // |rate| is audio frames per second, eg 48000.
  // |channels| is number of channels in input and output audio.
  // TODO(hubbe): Allow input rate and output rate to be different
  // since we're going to be resampling anyways.
  AudioShifter(base::TimeDelta max_buffer_size,
               base::TimeDelta clock_accuracy,
               base::TimeDelta adjustment_time,
               int rate,
               int channels);
  ~AudioShifter();

  int sample_rate() const { return rate_; }
  int channels() const { return channels_; }

  // Push Audio into the shifter. All inputs must have the same number of
  // channels, but bus size can vary. The playout time can be noisy and
  // does not have to line up perfectly with the number of samples pushed
  // so far. However, the playout_time in Push calls and Pull calls must
  // not diverge over time.
  // Given audio from an a microphone, a reasonable way to calculate
  // playout_time would be now + 30ms.
  // Ideally playout_time is some time in the future, in which case
  // the samples will be buffered until the appropriate time. If
  // playout_time is in the past, everything will still work, and we'll
  // try to keep the buffering to a minimum.
  void Push(std::unique_ptr<AudioBus> input, base::TimeTicks playout_time);

  // Fills out |output| with samples. Tries to stretch/shrink the audio
  // to compensate for drift between input and output.
  // If called from an output device data pull, a reasonable way to
  // calculate playout_time would be now + audio pipeline delay.
  void Pull(AudioBus* output, base::TimeTicks playout_time);

  int frames_pushed_for_testing() { return frames_pushed_for_testing_; }

 private:
  struct AudioQueueEntry {
    AudioQueueEntry(base::TimeTicks target_playout_time,
                    std::unique_ptr<AudioBus> audio);
    AudioQueueEntry(AudioQueueEntry&& other);
    ~AudioQueueEntry();
    base::TimeTicks target_playout_time;
    std::unique_ptr<AudioBus> audio;
  };

  void Zero(AudioBus* output);
  void ResamplerCallback(int frame_delay, AudioBus* destination);

  // Set from constructor.
  const base::TimeDelta max_buffer_size_;
  const base::TimeDelta clock_accuracy_;
  const base::TimeDelta adjustment_time_;
  // Kept as a double to make it easier to preserve precision in frame count ->
  // total time conversions .
  const double rate_;
  const int channels_;

  // The clock smoothers are used to smooth out timestamps
  // and adjust for drift and inaccurate clocks.
  std::unique_ptr<ClockSmoother> input_clock_smoother_;
  std::unique_ptr<ClockSmoother> output_clock_smoother_;

  // Are we currently outputting data?
  bool running_;

  // Number of frames already consumed from |queue_|.
  size_t position_ = 0;

  // Queue of data provided to us.
  base::circular_deque<AudioQueueEntry> queue_;

  // Timestamp from last Pull() call.
  base::TimeTicks previous_playout_time_;

  // Number of frames requested in last Pull call.
  int previous_requested_samples_ = 0;

  // Timestamp at the end of last audio bus
  // consumed by resampler.
  base::TimeTicks end_of_last_consumed_audiobus_;

  // If Push() timestamps are in the past, we have to decide the playout delay
  // ourselves. The delay is then stored here.
  base::TimeDelta bias_;

  // Resampler.
  MultiChannelResampler resampler_;

  // Current resampler ratio.
  double current_ratio_ = 1.0;

  int frames_pushed_for_testing_ = 0;
};

}  // namespace media

#endif  // MEDIA_BASE_AUDIO_SHIFTER_H_