Chromium Code Reviews| 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 |