audio_buffer_queue.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright 2013 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/base/audio_buffer_queue.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "media/base/audio_bus.h"
11#include "media/base/buffers.h"
12
13namespace media {
14
15AudioBufferQueue::AudioBufferQueue() { Clear(); }
16AudioBufferQueue::~AudioBufferQueue() {}
17
18void AudioBufferQueue::Clear() {
19  buffers_.clear();
20  current_buffer_ = buffers_.begin();
21  current_buffer_offset_ = 0;
22  frames_ = 0;
23  current_time_ = kNoTimestamp();
24}
25
26void AudioBufferQueue::Append(const scoped_refptr<AudioBuffer>& buffer_in) {
27  // If we have just written the first buffer, update |current_time_| to be the
28  // start time.
29  if (buffers_.empty()) {
30    DCHECK_EQ(frames_, 0);
31    current_time_ = buffer_in->timestamp();
32  }
33
34  // Add the buffer to the queue. Inserting into deque invalidates all
35  // iterators, so point to the first buffer.
36  buffers_.push_back(buffer_in);
37  current_buffer_ = buffers_.begin();
38
39  // Update the |frames_| counter since we have added frames.
40  frames_ += buffer_in->frame_count();
41  CHECK_GT(frames_, 0);  // make sure it doesn't overflow.
42}
43
44int AudioBufferQueue::ReadFrames(int frames, AudioBus* dest) {
45  DCHECK_GE(dest->frames(), frames);
46  return InternalRead(frames, true, 0, dest);
47}
48
49int AudioBufferQueue::PeekFrames(int frames,
50                                 int forward_offset,
51                                 AudioBus* dest) {
52  DCHECK_GE(dest->frames(), frames);
53  return InternalRead(frames, false, forward_offset, dest);
54}
55
56void AudioBufferQueue::SeekFrames(int frames) {
57  // Perform seek only if we have enough bytes in the queue.
58  CHECK_LE(frames, frames_);
59  int taken = InternalRead(frames, true, 0, NULL);
60  DCHECK_EQ(taken, frames);
61}
62
63int AudioBufferQueue::InternalRead(int frames,
64                                   bool advance_position,
65                                   int forward_offset,
66                                   AudioBus* dest) {
67  // Counts how many frames are actually read from the buffer queue.
68  int taken = 0;
69  BufferQueue::iterator current_buffer = current_buffer_;
70  int current_buffer_offset = current_buffer_offset_;
71
72  int frames_to_skip = forward_offset;
73  while (taken < frames) {
74    // |current_buffer| is valid since the first time this buffer is appended
75    // with data. Make sure there is data to be processed.
76    if (current_buffer == buffers_.end())
77      break;
78
79    scoped_refptr<AudioBuffer> buffer = *current_buffer;
80
81    int remaining_frames_in_buffer =
82        buffer->frame_count() - current_buffer_offset;
83
84    if (frames_to_skip > 0) {
85      // If there are frames to skip, do it first. May need to skip into
86      // subsequent buffers.
87      int skipped = std::min(remaining_frames_in_buffer, frames_to_skip);
88      current_buffer_offset += skipped;
89      frames_to_skip -= skipped;
90    } else {
91      // Find the right amount to copy from the current buffer. We shall copy no
92      // more than |frames| frames in total and each single step copies no more
93      // than the current buffer size.
94      int copied = std::min(frames - taken, remaining_frames_in_buffer);
95
96      // if |dest| is NULL, there's no need to copy.
97      if (dest)
98        buffer->ReadFrames(copied, current_buffer_offset, taken, dest);
99
100      // Increase total number of frames copied, which regulates when to end
101      // this loop.
102      taken += copied;
103
104      // We have read |copied| frames from the current buffer. Advance the
105      // offset.
106      current_buffer_offset += copied;
107    }
108
109    // Has the buffer has been consumed?
110    if (current_buffer_offset == buffer->frame_count()) {
111      if (advance_position) {
112        // Next buffer may not have timestamp, so we need to update current
113        // timestamp before switching to the next buffer.
114        UpdateCurrentTime(current_buffer, current_buffer_offset);
115      }
116
117      // If we are at the last buffer, no more data to be copied, so stop.
118      BufferQueue::iterator next = current_buffer + 1;
119      if (next == buffers_.end())
120        break;
121
122      // Advances the iterator.
123      current_buffer = next;
124      current_buffer_offset = 0;
125    }
126  }
127
128  if (advance_position) {
129    // Update the appropriate values since |taken| frames have been copied out.
130    frames_ -= taken;
131    DCHECK_GE(frames_, 0);
132    DCHECK(current_buffer_ != buffers_.end() || frames_ == 0);
133
134    current_buffer_ = current_buffer;
135    current_buffer_offset_ = current_buffer_offset;
136
137    UpdateCurrentTime(current_buffer_, current_buffer_offset_);
138
139    // Remove any buffers before the current buffer as there is no going
140    // backwards.
141    buffers_.erase(buffers_.begin(), current_buffer_);
142  }
143
144  return taken;
145}
146
147void AudioBufferQueue::UpdateCurrentTime(BufferQueue::iterator buffer,
148                                         int offset) {
149  if (buffer != buffers_.end() && (*buffer)->timestamp() != kNoTimestamp()) {
150    double time_offset = ((*buffer)->duration().InMicroseconds() * offset) /
151                         static_cast<double>((*buffer)->frame_count());
152    current_time_ =
153        (*buffer)->timestamp() + base::TimeDelta::FromMicroseconds(
154                                     static_cast<int64>(time_offset + 0.5));
155  }
156}
157
158}  // namespace media
159