OLD | NEW |
---|---|
(Empty) | |
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/seekable_audio_buffer.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 | |
13 namespace media { | |
14 | |
15 SeekableAudioBuffer::SeekableAudioBuffer(int forward_capacity) | |
16 : current_buffer_offset_(0), | |
17 forward_capacity_(forward_capacity), | |
18 forward_frames_(0), | |
19 current_time_(kNoTimestamp()) { | |
20 current_buffer_ = buffers_.begin(); | |
21 } | |
22 | |
23 SeekableAudioBuffer::~SeekableAudioBuffer() {} | |
24 | |
25 void SeekableAudioBuffer::Clear() { | |
scherkus (not reviewing)
2013/06/19 23:38:06
do we ever call this method in production?
can we
jrummell
2013/06/20 21:47:01
Clear() is called by alsa_output.cc, pulse_input.c
| |
26 buffers_.clear(); | |
27 current_buffer_ = buffers_.begin(); | |
28 current_buffer_offset_ = 0; | |
29 forward_frames_ = 0; | |
30 current_time_ = kNoTimestamp(); | |
31 } | |
32 | |
33 bool SeekableAudioBuffer::Append(const scoped_refptr<AudioBuffer>& buffer_in) { | |
34 // Since the forward capacity is only used to check the criteria for buffer | |
35 // full, we always append data to the buffer. | |
36 buffers_.push_back(buffer_in); | |
DaleCurtis
2013/06/20 00:49:49
Does this not invalidate the current_buffer_ itera
jrummell
2013/06/20 21:47:01
For list, no. But for deque it does, so fixed.
| |
37 | |
38 // After we have written the first buffer, update |current_buffer_| to point | |
39 // to it. | |
40 if (current_buffer_ == buffers_.end()) { | |
41 DCHECK_EQ(0, forward_frames_); | |
DaleCurtis
2013/06/20 00:49:49
no yoda conditions.
scherkus (not reviewing)
2013/06/20 00:57:42
I feel I should elaborate on this.
The gtest EXPE
jrummell
2013/06/20 21:47:01
Done. However, test code updated to have lots of y
| |
42 current_buffer_ = buffers_.begin(); | |
43 current_buffer_offset_ = 0; | |
44 current_time_ = buffer_in->timestamp(); | |
45 } | |
46 | |
47 // Update the |forward_frames_| counter since we have added frames. | |
48 forward_frames_ += buffer_in->frame_count(); | |
49 | |
50 // Advise the user to stop append if the amount of forward frames exceeds the | |
51 // forward capacity. A false return value means the user should stop appending | |
52 // more data to this buffer. | |
53 return forward_frames_ < forward_capacity_; | |
54 } | |
55 | |
56 int SeekableAudioBuffer::ReadFrames(int frames, AudioBus* dest) { | |
57 DCHECK(dest); | |
scherkus (not reviewing)
2013/06/19 23:38:06
don't need to DCHECK pointers we immediately deref
jrummell
2013/06/20 21:47:01
Done.
| |
58 DCHECK_GE(dest->frames(), frames); | |
59 return InternalRead(frames, true, 0, dest); | |
60 } | |
61 | |
62 int SeekableAudioBuffer::PeekFrames(int frames, | |
63 int forward_offset, | |
64 AudioBus* dest) { | |
65 DCHECK(dest); | |
66 DCHECK_GE(dest->frames(), frames); | |
67 return InternalRead(frames, false, forward_offset, dest); | |
68 } | |
69 | |
70 bool SeekableAudioBuffer::SeekFrames(int frames) { | |
71 // Perform seeking forward only if we have enough bytes in the queue. | |
72 if (frames > forward_frames_) | |
scherkus (not reviewing)
2013/06/19 23:38:06
think this can be a CHECK()?
I like APIs that fai
jrummell
2013/06/20 21:47:01
Done.
| |
73 return false; | |
74 | |
75 // Do a read of |frames| frames. | |
76 int taken = InternalRead(frames, true, 0, NULL); | |
77 DCHECK_EQ(taken, frames); | |
78 return true; | |
79 } | |
80 | |
81 int SeekableAudioBuffer::InternalRead(int frames, | |
82 bool advance_position, | |
83 int forward_offset, | |
84 AudioBus* dest) { | |
85 // Counts how many frames are actually read from the buffer queue. | |
86 int taken = 0; | |
87 BufferQueue::iterator current_buffer = current_buffer_; | |
88 int current_buffer_offset = current_buffer_offset_; | |
89 | |
90 int frames_to_skip = forward_offset; | |
91 while (taken < frames) { | |
92 // |current_buffer| is valid since the first time this buffer is appended | |
93 // with data. Make sure there is data to be processed. | |
94 if (current_buffer == buffers_.end()) | |
95 break; | |
96 | |
97 scoped_refptr<AudioBuffer> buffer = *current_buffer; | |
98 | |
99 int remaining_frames_in_buffer = | |
100 buffer->frame_count() - current_buffer_offset; | |
101 | |
102 if (frames_to_skip > 0) { | |
103 // If there are frames to skip, do it first. May need to skip into | |
104 // subsequent buffers. | |
105 int skipped = std::min(remaining_frames_in_buffer, frames_to_skip); | |
106 current_buffer_offset += skipped; | |
107 frames_to_skip -= skipped; | |
108 } else { | |
109 // Find the right amount to copy from the current buffer. We shall copy no | |
110 // more than |frames| frames in total and each single step copies no more | |
111 // than the current buffer size. | |
112 int copied = std::min(frames - taken, remaining_frames_in_buffer); | |
113 | |
114 // if |dest| is NULL, there's no need to copy. | |
115 if (dest) | |
116 buffer->ReadFrames(copied, current_buffer_offset, taken, dest); | |
117 | |
118 // Increase total number of frames copied, which regulates when to end | |
119 // this loop. | |
120 taken += copied; | |
121 | |
122 // We have read |copied| frames from the current buffer. Advance the | |
123 // offset. | |
124 current_buffer_offset += copied; | |
125 } | |
126 | |
127 // Has the buffer has been consumed? | |
128 if (current_buffer_offset == buffer->frame_count()) { | |
129 if (advance_position) { | |
130 // Next buffer may not have timestamp, so we need to update current | |
131 // timestamp before switching to the next buffer. | |
132 UpdateCurrentTime(current_buffer, current_buffer_offset); | |
133 } | |
134 | |
135 BufferQueue::iterator next = current_buffer; | |
136 ++next; | |
137 // If we are at the last buffer, no more data to be copied, so stop. | |
138 if (next == buffers_.end()) | |
139 break; | |
140 | |
141 // Advances the iterator. | |
142 current_buffer = next; | |
143 current_buffer_offset = 0; | |
144 } | |
145 } | |
146 | |
147 if (advance_position) { | |
148 // Update the appropriate values since |taken| frames have been copied out. | |
149 forward_frames_ -= taken; | |
150 DCHECK_GE(forward_frames_, 0); | |
151 DCHECK(current_buffer_ != buffers_.end() || forward_frames_ == 0); | |
152 | |
153 current_buffer_ = current_buffer; | |
154 current_buffer_offset_ = current_buffer_offset; | |
155 | |
156 UpdateCurrentTime(current_buffer_, current_buffer_offset_); | |
157 | |
158 // Remove any buffers before the current pointer as there is no going | |
159 // backwards. | |
160 while (true) { | |
DaleCurtis
2013/06/20 00:49:49
buffers_.erase(buffers_.begin(), current_buffer_)?
jrummell
2013/06/20 21:47:01
Done.
| |
161 BufferQueue::iterator i = buffers_.begin(); | |
162 if (i == current_buffer_) | |
163 break; | |
164 buffers_.erase(i); | |
165 } | |
166 } | |
167 | |
168 return taken; | |
169 } | |
170 | |
171 void SeekableAudioBuffer::UpdateCurrentTime(BufferQueue::iterator buffer, | |
172 int offset) { | |
173 if (buffer != buffers_.end() && (*buffer)->timestamp() != kNoTimestamp()) { | |
174 int64 time_offset = ((*buffer)->duration().InMicroseconds() * offset) / | |
DaleCurtis
2013/06/20 00:49:49
Use InMicrosecondsF() and round up by adding 0.5?
jrummell
2013/06/20 21:47:01
There is no InMicrosecondsF(), since TimeDelta is
| |
175 (*buffer)->frame_count(); | |
176 current_time_ = (*buffer)->timestamp() + | |
177 base::TimeDelta::FromMicroseconds(time_offset); | |
178 } | |
179 } | |
180 | |
181 } // namespace media | |
OLD | NEW |