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