summaryrefslogtreecommitdiff
path: root/chromium/media/filters/audio_clock.h
blob: 286f355c63b1df989a92cd68f0f7c01f856b4d61 (plain)
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
148
149
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_FILTERS_AUDIO_CLOCK_H_
#define MEDIA_FILTERS_AUDIO_CLOCK_H_

#include <stdint.h>

#include <cmath>

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

namespace media {

// Models a queue of buffered audio in a playback pipeline for use with
// estimating the amount of delay in wall clock time. Takes changes in playback
// rate into account to handle scenarios where multiple rates may be present in
// a playback pipeline with large delay.
//
//
// USAGE
//
// Prior to starting audio playback, construct an AudioClock with an initial
// media timestamp and a sample rate matching the sample rate the audio device
// was opened at.
//
// Each time the audio rendering callback is executed, call WroteAudio() once
// (and only once!) containing information on what was written:
//   1) How many frames of audio data requested
//   2) How many frames of audio data provided
//   3) The playback rate of the audio data provided
//   4) The current amount of delay
//
// After a call to WroteAudio(), clients can inspect the resulting media
// timestamp. This can be used for UI purposes, synchronizing video, etc...
//
//
// DETAILS
//
// Silence (whether caused by the initial audio delay or failing to write the
// amount of requested frames due to underflow) is also modeled and will cause
// the media timestamp to stop increasing until all known silence has been
// played. AudioClock's model is initialized with silence during the first call
// to WroteAudio() using the delay value.
//
// Playback rates are tracked for translating frame durations into media
// durations. Since silence doesn't affect media timestamps, it also isn't
// affected by playback rates.
class MEDIA_EXPORT AudioClock {
 public:
  AudioClock(base::TimeDelta start_timestamp, int sample_rate);

  AudioClock(const AudioClock&) = delete;
  AudioClock& operator=(const AudioClock&) = delete;

  ~AudioClock();

  // |frames_written| amount of audio data scaled to |playback_rate| written.
  // |frames_requested| amount of audio data requested by hardware.
  // |delay_frames| is the current amount of hardware delay.
  void WroteAudio(int frames_written,
                  int frames_requested,
                  int delay_frames,
                  double playback_rate);

  // If WroteAudio() calls are suspended (i.e. due to playback being paused) the
  // AudioClock will not properly advance time (even though all data up until
  // back_timestamp() will playout on the physical device).
  //
  // To compensate for this, when calls resume, before the next WroteAudio(),
  // callers should call CompensateForSuspendedWrites() to advance the clock for
  // audio which continued playing out while WroteAudio() calls were suspended.
  //
  // |delay_frames| must be provided to properly prime the clock to compensate
  // for a new initial delay.
  void CompensateForSuspendedWrites(base::TimeDelta elapsed, int delay_frames);

  // Returns the bounds of media data currently buffered by the audio hardware,
  // taking silence and changes in playback rate into account. Buffered audio
  // structure and timestamps are updated with every call to WroteAudio().
  //
  //  start_timestamp = 1000 ms                           sample_rate = 40 Hz
  // +-----------------------+-----------------------+-----------------------+
  // |   10 frames silence   |   20 frames @ 1.0x    |   20 frames @ 0.5x    |
  // |      = 250 ms (wall)  |      = 500 ms (wall)  |      = 500 ms (wall)  |
  // |      =   0 ms (media) |      = 500 ms (media) |      = 250 ms (media) |
  // +-----------------------+-----------------------+-----------------------+
  // ^                                                                       ^
  // front_timestamp() is equal to               back_timestamp() is equal to
  // |start_timestamp| since no                  amount of media frames tracked
  // media data has been played yet.             by AudioClock, which would be
  //                                             1000 + 500 + 250 = 1750 ms.
  base::TimeDelta front_timestamp() const {
    return base::Microseconds(std::round(front_timestamp_micros_));
  }
  base::TimeDelta back_timestamp() const {
    return base::Microseconds(std::round(back_timestamp_micros_));
  }

  // Returns the amount of wall time until |timestamp| will be played by the
  // audio hardware.
  //
  // |timestamp| must be within front_timestamp() and back_timestamp().
  base::TimeDelta TimeUntilPlayback(base::TimeDelta timestamp) const;

  void ContiguousAudioDataBufferedForTesting(
      base::TimeDelta* total,
      base::TimeDelta* same_rate_total) const;

 private:
  // Even with a ridiculously high sample rate of 256kHz, using 64 bits will
  // permit tracking up to 416999965 days worth of time (that's 1141 millenia).
  //
  // 32 bits on the other hand would top out at measly 2 hours and 20 minutes.
  struct AudioData {
    AudioData(int64_t frames, double playback_rate);

    int64_t frames;
    double playback_rate;
  };

  // Helpers for operating on |buffered_|.
  void PushBufferedAudioData(int64_t frames, double playback_rate);
  void PopBufferedAudioData(int64_t frames);
  double ComputeBufferedMediaDurationMicros() const;

  const base::TimeDelta start_timestamp_;
  const double microseconds_per_frame_;

  base::circular_deque<AudioData> buffered_;
  int64_t total_buffered_frames_;

  // Use double rather than TimeDelta to avoid loss of partial microseconds when
  // converting between frames-written/delayed and time-passed (see conversion
  // in WroteAudio()). Particularly for |back_timestamp|, which accumulates more
  // time with each call to WroteAudio(), the loss of precision can accumulate
  // to create noticeable audio/video sync drift for longer (2-3 hr) videos.
  // See http://crbug.com/564604.
  double front_timestamp_micros_;
  double back_timestamp_micros_;
};

}  // namespace media

#endif  // MEDIA_FILTERS_AUDIO_CLOCK_H_