1// Copyright (c) 2012 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_buffer.h" 6 7#include <algorithm> 8 9#include "base/logging.h" 10#include "media/base/data_buffer.h" 11 12namespace media { 13 14SeekableBuffer::SeekableBuffer(int backward_capacity, int forward_capacity) 15 : current_buffer_offset_(0), 16 backward_capacity_(backward_capacity), 17 backward_bytes_(0), 18 forward_capacity_(forward_capacity), 19 forward_bytes_(0), 20 current_time_(kNoTimestamp()) { 21 current_buffer_ = buffers_.begin(); 22} 23 24SeekableBuffer::~SeekableBuffer() { 25} 26 27void SeekableBuffer::Clear() { 28 buffers_.clear(); 29 current_buffer_ = buffers_.begin(); 30 current_buffer_offset_ = 0; 31 backward_bytes_ = 0; 32 forward_bytes_ = 0; 33 current_time_ = kNoTimestamp(); 34} 35 36int SeekableBuffer::Read(uint8* data, int size) { 37 DCHECK(data); 38 return InternalRead(data, size, true, 0); 39} 40 41int SeekableBuffer::Peek(uint8* data, int size, int forward_offset) { 42 DCHECK(data); 43 return InternalRead(data, size, false, forward_offset); 44} 45 46bool SeekableBuffer::GetCurrentChunk(const uint8** data, int* size) const { 47 BufferQueue::iterator current_buffer = current_buffer_; 48 int current_buffer_offset = current_buffer_offset_; 49 // Advance position if we are in the end of the current buffer. 50 while (current_buffer != buffers_.end() && 51 current_buffer_offset >= (*current_buffer)->data_size()) { 52 ++current_buffer; 53 current_buffer_offset = 0; 54 } 55 if (current_buffer == buffers_.end()) 56 return false; 57 *data = (*current_buffer)->data() + current_buffer_offset; 58 *size = (*current_buffer)->data_size() - current_buffer_offset; 59 return true; 60} 61 62bool SeekableBuffer::Append(const scoped_refptr<DataBuffer>& buffer_in) { 63 if (buffers_.empty() && buffer_in->timestamp() != kNoTimestamp()) { 64 current_time_ = buffer_in->timestamp(); 65 } 66 67 // Since the forward capacity is only used to check the criteria for buffer 68 // full, we always append data to the buffer. 69 buffers_.push_back(buffer_in); 70 71 // After we have written the first buffer, update |current_buffer_| to point 72 // to it. 73 if (current_buffer_ == buffers_.end()) { 74 DCHECK_EQ(0, forward_bytes_); 75 current_buffer_ = buffers_.begin(); 76 } 77 78 // Update the |forward_bytes_| counter since we have more bytes. 79 forward_bytes_ += buffer_in->data_size(); 80 81 // Advise the user to stop append if the amount of forward bytes exceeds 82 // the forward capacity. A false return value means the user should stop 83 // appending more data to this buffer. 84 if (forward_bytes_ >= forward_capacity_) 85 return false; 86 return true; 87} 88 89bool SeekableBuffer::Append(const uint8* data, int size) { 90 if (size > 0) { 91 scoped_refptr<DataBuffer> data_buffer = DataBuffer::CopyFrom(data, size); 92 return Append(data_buffer); 93 } else { 94 // Return true if we have forward capacity. 95 return forward_bytes_ < forward_capacity_; 96 } 97} 98 99bool SeekableBuffer::Seek(int32 offset) { 100 if (offset > 0) 101 return SeekForward(offset); 102 else if (offset < 0) 103 return SeekBackward(-offset); 104 return true; 105} 106 107bool SeekableBuffer::SeekForward(int size) { 108 // Perform seeking forward only if we have enough bytes in the queue. 109 if (size > forward_bytes_) 110 return false; 111 112 // Do a read of |size| bytes. 113 int taken = InternalRead(NULL, size, true, 0); 114 DCHECK_EQ(taken, size); 115 return true; 116} 117 118bool SeekableBuffer::SeekBackward(int size) { 119 if (size > backward_bytes_) 120 return false; 121 // Record the number of bytes taken. 122 int taken = 0; 123 // Loop until we taken enough bytes and rewind by the desired |size|. 124 while (taken < size) { 125 // |current_buffer_| can never be invalid when we are in this loop. It can 126 // only be invalid before any data is appended. The invalid case should be 127 // handled by checks before we enter this loop. 128 DCHECK(current_buffer_ != buffers_.end()); 129 130 // We try to consume at most |size| bytes in the backward direction. We also 131 // have to account for the offset we are in the current buffer, so take the 132 // minimum between the two to determine the amount of bytes to take from the 133 // current buffer. 134 int consumed = std::min(size - taken, current_buffer_offset_); 135 136 // Decreases the offset in the current buffer since we are rewinding. 137 current_buffer_offset_ -= consumed; 138 139 // Increase the amount of bytes taken in the backward direction. This 140 // determines when to stop the loop. 141 taken += consumed; 142 143 // Forward bytes increases and backward bytes decreases by the amount 144 // consumed in the current buffer. 145 forward_bytes_ += consumed; 146 backward_bytes_ -= consumed; 147 DCHECK_GE(backward_bytes_, 0); 148 149 // The current buffer pointed by current iterator has been consumed. Move 150 // the iterator backward so it points to the previous buffer. 151 if (current_buffer_offset_ == 0) { 152 if (current_buffer_ == buffers_.begin()) 153 break; 154 // Move the iterator backward. 155 --current_buffer_; 156 // Set the offset into the current buffer to be the buffer size as we 157 // are preparing for rewind for next iteration. 158 current_buffer_offset_ = (*current_buffer_)->data_size(); 159 } 160 } 161 162 UpdateCurrentTime(current_buffer_, current_buffer_offset_); 163 164 DCHECK_EQ(taken, size); 165 return true; 166} 167 168void SeekableBuffer::EvictBackwardBuffers() { 169 // Advances the iterator until we hit the current pointer. 170 while (backward_bytes_ > backward_capacity_) { 171 BufferQueue::iterator i = buffers_.begin(); 172 if (i == current_buffer_) 173 break; 174 scoped_refptr<DataBuffer> buffer = *i; 175 backward_bytes_ -= buffer->data_size(); 176 DCHECK_GE(backward_bytes_, 0); 177 178 buffers_.erase(i); 179 } 180} 181 182int SeekableBuffer::InternalRead(uint8* data, int size, 183 bool advance_position, 184 int forward_offset) { 185 // Counts how many bytes are actually read from the buffer queue. 186 int taken = 0; 187 188 BufferQueue::iterator current_buffer = current_buffer_; 189 int current_buffer_offset = current_buffer_offset_; 190 191 int bytes_to_skip = forward_offset; 192 while (taken < size) { 193 // |current_buffer| is valid since the first time this buffer is appended 194 // with data. 195 if (current_buffer == buffers_.end()) 196 break; 197 198 scoped_refptr<DataBuffer> buffer = *current_buffer; 199 200 int remaining_bytes_in_buffer = 201 buffer->data_size() - current_buffer_offset; 202 203 if (bytes_to_skip == 0) { 204 // Find the right amount to copy from the current buffer referenced by 205 // |buffer|. We shall copy no more than |size| bytes in total and each 206 // single step copied no more than the current buffer size. 207 int copied = std::min(size - taken, remaining_bytes_in_buffer); 208 209 // |data| is NULL if we are seeking forward, so there's no need to copy. 210 if (data) 211 memcpy(data + taken, buffer->data() + current_buffer_offset, copied); 212 213 // Increase total number of bytes copied, which regulates when to end this 214 // loop. 215 taken += copied; 216 217 // We have read |copied| bytes from the current buffer. Advances the 218 // offset. 219 current_buffer_offset += copied; 220 } else { 221 int skipped = std::min(remaining_bytes_in_buffer, bytes_to_skip); 222 current_buffer_offset += skipped; 223 bytes_to_skip -= skipped; 224 } 225 226 // The buffer has been consumed. 227 if (current_buffer_offset == buffer->data_size()) { 228 if (advance_position) { 229 // Next buffer may not have timestamp, so we need to update current 230 // timestamp before switching to the next buffer. 231 UpdateCurrentTime(current_buffer, current_buffer_offset); 232 } 233 234 BufferQueue::iterator next = current_buffer; 235 ++next; 236 // If we are at the last buffer, don't advance. 237 if (next == buffers_.end()) 238 break; 239 240 // Advances the iterator. 241 current_buffer = next; 242 current_buffer_offset = 0; 243 } 244 } 245 246 if (advance_position) { 247 // We have less forward bytes and more backward bytes. Updates these 248 // counters by |taken|. 249 forward_bytes_ -= taken; 250 backward_bytes_ += taken; 251 DCHECK_GE(forward_bytes_, 0); 252 DCHECK(current_buffer_ != buffers_.end() || forward_bytes_ == 0); 253 254 current_buffer_ = current_buffer; 255 current_buffer_offset_ = current_buffer_offset; 256 257 UpdateCurrentTime(current_buffer_, current_buffer_offset_); 258 EvictBackwardBuffers(); 259 } 260 261 return taken; 262} 263 264void SeekableBuffer::UpdateCurrentTime(BufferQueue::iterator buffer, 265 int offset) { 266 // Garbage values are unavoidable, so this check will remain. 267 if (buffer != buffers_.end() && 268 (*buffer)->timestamp() != kNoTimestamp()) { 269 int64 time_offset = ((*buffer)->duration().InMicroseconds() * offset) / 270 (*buffer)->data_size(); 271 272 current_time_ = (*buffer)->timestamp() + 273 base::TimeDelta::FromMicroseconds(time_offset); 274 } 275} 276 277} // namespace media 278