Home | History | Annotate | Download | only in base
      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 
     12 namespace media {
     13 
     14 SeekableBuffer::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 
     24 SeekableBuffer::~SeekableBuffer() {
     25 }
     26 
     27 void 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 
     36 int SeekableBuffer::Read(uint8* data, int size) {
     37   DCHECK(data);
     38   return InternalRead(data, size, true, 0);
     39 }
     40 
     41 int SeekableBuffer::Peek(uint8* data, int size, int forward_offset) {
     42   DCHECK(data);
     43   return InternalRead(data, size, false, forward_offset);
     44 }
     45 
     46 bool 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 
     62 bool 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 
     89 bool 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 
     99 bool 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 
    107 bool 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 
    118 bool 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 
    168 void 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 
    182 int 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 
    264 void 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