1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192

media / base / seekable_buffer.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// SeekableBuffer to support backward and forward seeking in a buffer for
// reading a media data source.
//
// In order to support backward and forward seeking, this class buffers data in
// both backward and forward directions, the current read position can be reset
// to anywhere in the buffered data.
//
// The amount of data buffered is regulated by two variables at construction,
// `backward_capacity` and `forward_capacity`.
//
// In the case of reading and seeking forward, the current read position
// advances and there will be more data in the backward direction. If backward
// bytes exceeds `backward_capacity`, the exceeding bytes are evicted and thus
// backward_bytes() will always be less than or equal to `backward_capacity`.
// The eviction will be caused by Read() and Seek() in the forward direction and
// is done internally when the mentioned criteria is fulfilled.
//
// In the case of appending data to the buffer, there is an advisory limit of
// how many bytes can be kept in the forward direction, regulated by
// `forward_capacity`. The append operation (by calling Append()) that caused
// forward bytes to exceed `forward_capacity` will have a return value that
// advises a halt of append operation, further append operations are allowed but
// are not advised. Since this class is used as a backend buffer for caching
// media files downloaded from network we cannot afford losing data, we can
// only advise a halt of further writing to this buffer.
// This class is not inherently thread-safe. Concurrent access must be
// externally serialized.

#ifndef MEDIA_BASE_SEEKABLE_BUFFER_H_
#define MEDIA_BASE_SEEKABLE_BUFFER_H_

#include <stdint.h>

#include <list>
#include <optional>

#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/checked_math.h"
#include "base/time/time.h"
#include "media/base/media_export.h"

namespace media {

class DataBuffer;

class MEDIA_EXPORT SeekableBuffer {
 public:
  // Constructs an instance with `forward_capacity` and `backward_capacity`.
  // The buffer capacity is measured in bytes.
  SeekableBuffer(size_t backward_capacity, size_t forward_capacity);

  SeekableBuffer(const SeekableBuffer&) = delete;
  SeekableBuffer& operator=(const SeekableBuffer&) = delete;

  ~SeekableBuffer();

  // Clears the buffer queue.
  void Clear();

  // Reads from the current read position into `data`, up to the size of `data`.
  // Returns the number of bytes read. The current read position will advance by
  // the amount of bytes read. If reading caused backward_bytes() to exceed
  // backward_capacity(), an eviction of the backward buffer will be done
  // internally.
  size_t Read(base::span<uint8_t> data);

  // Copies bytes from current position to `data` up to the size of `data`.
  // Returns number of bytes copied. Doesn't advance current position.
  // Optionally starts at a `forward_offset` from current position.
  size_t Peek(base::span<uint8_t> data, size_t forward_offset = 0);

  // Returns a view to the current subsection of data that is ready for
  // consumption in the current data buffer, or an empty span if there is no
  // data left at all in the buffer queue. The returned `data` value becomes
  // invalid when Read(), Append() or Seek() are called.
  base::span<const uint8_t> GetCurrentChunk() const;

  // Appends `buffer_in` to this buffer up to the capacity of this buffer. A
  // false return value means the user should stop appending more data to this
  // buffer. The data is added to the buffer in any case.
  bool Append(const scoped_refptr<DataBuffer>& buffer_in);

  // Appends `data` to the buffer. Result is the same as for Append(const
  // scoped_refptr<DataBuffer>&).
  bool Append(base::span<const uint8_t> data);

  // Moves the read position by `offset` bytes. If `offset` is positive, the
  // current read position is moved forward. If negative, the current read
  // position is moved backward. A zero `offset` value will keep the current
  // read position stationary.
  //
  // If `offset` exceeds bytes buffered in either direction, reported by
  // forward_bytes() when seeking forward and backward_bytes() when seeking
  // backward, the seek operation will fail and return value will be false.
  // If the seek operation fails, the current read position will not be updated.
  // If a forward seeking caused backward_bytes() to exceed backward_capacity(),
  // this method call will cause an eviction of the backward buffer.
  bool Seek(ptrdiff_t offset);

  // Returns the number of bytes buffered beyond the current read position.
  size_t forward_bytes() const { return forward_bytes_.ValueOrDie(); }

  // Returns the number of bytes buffered that precedes the current read
  // position.
  size_t backward_bytes() const { return backward_bytes_.ValueOrDie(); }

  // Sets the forward_capacity to `new_forward_capacity` bytes.
  void set_forward_capacity(size_t new_forward_capacity) {
    forward_capacity_ = new_forward_capacity;
  }

  // Sets the backward_capacity to `new_backward_capacity` bytes.
  void set_backward_capacity(size_t new_backward_capacity) {
    backward_capacity_ = new_backward_capacity;
  }

  // Returns the maximum number of bytes that should be kept in the forward
  // direction.
  size_t forward_capacity() const { return forward_capacity_; }

  // Returns the maximum number of bytes that should be kept in the backward
  // direction.
  size_t backward_capacity() const { return backward_capacity_; }

  // Returns the timestamp associated with the current read position. The value
  // is defined as the timestamp of the current buffer plus the fraction of
  // the buffer's duration corresponding to the current buffer offset.
  //
  // If timestamp for the current buffer is set to 0 or the data was added with
  // a call to `Append()`, then returns value that corresponds to the
  // last position in a buffer that had timestamp set.
  // kNoTimestamp is returned if no buffers we read from had timestamp set.
  base::TimeDelta current_time() const { return current_time_; }

 private:
  using BufferQueue = std::list<scoped_refptr<DataBuffer>>;

  // A helper method to evict buffers in the backward direction until backward
  // bytes is within the backward capacity.
  void EvictBackwardBuffers();

  // An internal method shared by Read() and SeekForward() that actually does
  // reading. It reads a maximum of `num_bytes` bytes into `data`. Returns the
  // number of bytes read. The current read position will be moved forward by
  // the number of bytes read. If `data` is NULL, only the current read position
  // will advance but no data will be copied.
  size_t InternalRead(std::optional<base::span<uint8_t>> data,
                      size_t num_bytes,
                      bool advance_position,
                      size_t forward_offset);

  // A helper method that moves the current read position forward by `num_bytes`
  // bytes.
  // If the return value is true, the operation completed successfully.
  // If the return value is false, `num_bytes` is greater than forward_bytes()
  // and the seek operation failed. The current read position is not updated.
  bool SeekForward(size_t num_bytes);

  // A helper method that moves the current read position backward by
  // `num_bytes` bytes. If the return value is true, the operation completed
  // successfully. If the return value is false, `num_bytes` is greater than
  // backward_bytes() and the seek operation failed. The current read position
  // is not updated.
  bool SeekBackward(size_t num_bytes);

  // Updates `current_time_` with the time that corresponds to the
  // specified position in the buffer.
  void UpdateCurrentTime(BufferQueue::iterator buffer, size_t offset);

  BufferQueue buffers_;
  BufferQueue::iterator current_buffer_;
  size_t current_buffer_offset_ = 0;

  size_t backward_capacity_;
  base::CheckedNumeric<size_t> backward_bytes_ = 0;

  size_t forward_capacity_;
  base::CheckedNumeric<size_t> forward_bytes_ = 0;

  // Keeps track of the most recent time we've seen in case the `buffers_` is
  // empty when our owner asks what time it is.
  base::TimeDelta current_time_;
};

}  // namespace media

#endif  // MEDIA_BASE_SEEKABLE_BUFFER_H_