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/check_op.h"
10 #include "media/base/audio_bus.h"
11 
12 namespace media {
13 
AudioBufferQueue()14 AudioBufferQueue::AudioBufferQueue() { Clear(); }
15 AudioBufferQueue::~AudioBufferQueue() = default;
16 
Clear()17 void AudioBufferQueue::Clear() {
18   buffers_.clear();
19   front_buffer_offset_ = 0;
20   frames_ = 0;
21 }
22 
Append(scoped_refptr<AudioBuffer> buffer_in)23 void AudioBufferQueue::Append(scoped_refptr<AudioBuffer> buffer_in) {
24   // Update the |frames_| counter since we have added frames.
25   frames_ += buffer_in->frame_count();
26   CHECK_GT(frames_, 0);  // make sure it doesn't overflow.
27 
28   // Add the buffer to the queue. Inserting into deque invalidates all
29   // iterators, so point to the first buffer.
30   buffers_.push_back(std::move(buffer_in));
31 }
32 
ReadFrames(int frames,int dest_frame_offset,AudioBus * dest)33 int AudioBufferQueue::ReadFrames(int frames,
34                                  int dest_frame_offset,
35                                  AudioBus* dest) {
36   DCHECK_GE(dest->frames(), frames + dest_frame_offset);
37   return InternalRead(frames, true, 0, dest_frame_offset, dest);
38 }
39 
PeekFrames(int frames,int source_frame_offset,int dest_frame_offset,AudioBus * dest)40 int AudioBufferQueue::PeekFrames(int frames,
41                                  int source_frame_offset,
42                                  int dest_frame_offset,
43                                  AudioBus* dest) {
44   DCHECK_GE(dest->frames(), frames);
45   return InternalRead(
46       frames, false, source_frame_offset, dest_frame_offset, dest);
47 }
48 
SeekFrames(int frames)49 void AudioBufferQueue::SeekFrames(int frames) {
50   // Perform seek only if we have enough bytes in the queue.
51   CHECK_LE(frames, frames_);
52   int taken = InternalRead(frames, true, 0, 0, NULL);
53   DCHECK_EQ(taken, frames);
54 }
55 
InternalRead(int frames,bool advance_position,int source_frame_offset,int dest_frame_offset,AudioBus * dest)56 int AudioBufferQueue::InternalRead(int frames,
57                                    bool advance_position,
58                                    int source_frame_offset,
59                                    int dest_frame_offset,
60                                    AudioBus* dest) {
61   if (buffers_.empty())
62     return 0;
63 
64   if (buffers_.front()->IsBitstreamFormat()) {
65     // For compressed bitstream formats, a partial compressed audio frame is
66     // less useful, since it can't generate any PCM frame out of it. Also, we
67     // want to keep the granularity as fine as possible so that discarding a
68     // small chunk of PCM frames is still possible. Thus, we only transfer a
69     // complete AudioBuffer at a time.
70     DCHECK(!dest_frame_offset ||
71            dest_frame_offset == dest->GetBitstreamFrames());
72     DCHECK(!source_frame_offset);
73 
74     const auto& buffer = buffers_.front();
75     int taken = buffer->frame_count();
76 
77     // if |dest| is NULL, there's no need to copy.
78     if (dest) {
79       // We always copy a whole bitstream buffer. Make sure we have space.
80       CHECK_GE(frames, buffer->frame_count());
81       buffer->ReadFrames(buffer->frame_count(), 0, dest_frame_offset, dest);
82     }
83 
84     if (advance_position) {
85       // Update the appropriate values since |taken| frames have been copied
86       // out.
87       frames_ -= taken;
88       DCHECK_GE(frames_, 0);
89 
90       // Remove any buffers before the current buffer as there is no going
91       // backwards.
92       buffers_.pop_front();
93     }
94 
95     return taken;
96   }
97 
98   // Counts how many frames are actually read from the buffer queue.
99   int taken = 0;
100   BufferQueue::iterator current_buffer = buffers_.begin();
101   int current_buffer_offset = front_buffer_offset_;
102 
103   int frames_to_skip = source_frame_offset;
104   while (taken < frames) {
105     // |current_buffer| is valid since the first time this buffer is appended
106     // with data. Make sure there is data to be processed.
107     if (current_buffer == buffers_.end())
108       break;
109 
110     scoped_refptr<AudioBuffer> buffer = *current_buffer;
111 
112     int remaining_frames_in_buffer =
113         buffer->frame_count() - current_buffer_offset;
114 
115     if (frames_to_skip > 0) {
116       // If there are frames to skip, do it first. May need to skip into
117       // subsequent buffers.
118       int skipped = std::min(remaining_frames_in_buffer, frames_to_skip);
119       current_buffer_offset += skipped;
120       frames_to_skip -= skipped;
121     } else {
122       // Find the right amount to copy from the current buffer. We shall copy no
123       // more than |frames| frames in total and each single step copies no more
124       // than the current buffer size.
125       int copied = std::min(frames - taken, remaining_frames_in_buffer);
126 
127       // if |dest| is NULL, there's no need to copy.
128       if (dest) {
129         buffer->ReadFrames(
130             copied, current_buffer_offset, dest_frame_offset + taken, dest);
131       }
132 
133       // Increase total number of frames copied, which regulates when to end
134       // this loop.
135       taken += copied;
136 
137       // We have read |copied| frames from the current buffer. Advance the
138       // offset.
139       current_buffer_offset += copied;
140     }
141 
142     // Has the buffer has been consumed?
143     if (current_buffer_offset == buffer->frame_count()) {
144       // If we are at the last buffer, no more data to be copied, so stop.
145       BufferQueue::iterator next = current_buffer + 1;
146       if (next == buffers_.end())
147         break;
148 
149       // Advances the iterator.
150       current_buffer = next;
151       current_buffer_offset = 0;
152     }
153   }
154 
155   if (advance_position) {
156     // Update the appropriate values since |taken| frames have been copied out.
157     frames_ -= taken;
158     DCHECK_GE(frames_, 0);
159 
160     // Remove any buffers before the current buffer as there is no going
161     // backwards.
162     buffers_.erase(buffers_.begin(), current_buffer);
163     front_buffer_offset_ = current_buffer_offset;
164   }
165 
166   return taken;
167 }
168 
169 }  // namespace media
170