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_