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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
media / filters / source_buffer_range.h [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_FILTERS_SOURCE_BUFFER_RANGE_H_
#define MEDIA_FILTERS_SOURCE_BUFFER_RANGE_H_
#include <stddef.h>
#include <map>
#include <memory>
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/time/time.h"
#include "media/base/media_export.h"
#include "media/base/stream_parser_buffer.h"
namespace media {
// Base class for representing a continuous range of buffered data in the
// presentation timeline. All buffers in a SourceBufferRange are ordered
// sequentially by GOP presentation interval, and within each GOP by decode
// order. Unless constructed with |ALLOW_GAPS|, the range contains no internal
// presentation gaps.
class MEDIA_EXPORT SourceBufferRange {
public:
// Returns the maximum distance in time between any buffer seen in the stream
// of which this range is a part. Used to estimate the duration of a buffer if
// its duration is not known, and in GetFudgeRoom() for determining whether a
// time or coded frame is close enough to be considered part of this range.
using InterbufferDistanceCB = base::RepeatingCallback<base::TimeDelta()>;
using BufferQueue = StreamParser::BufferQueue;
// Policy for handling large gaps between buffers. Continuous media like
// audio & video should use NO_GAPS_ALLOWED. Discontinuous media like
// timed text should use ALLOW_GAPS because large differences in timestamps
// are common and acceptable.
enum GapPolicy {
NO_GAPS_ALLOWED,
ALLOW_GAPS
};
// Creates a range with |new_buffers|. |new_buffers| cannot be empty and the
// front of |new_buffers| must be a keyframe.
// |range_start_pts| refers to the starting timestamp for the coded
// frame group to which these buffers belong.
SourceBufferRange(GapPolicy gap_policy,
const BufferQueue& new_buffers,
base::TimeDelta range_start_pts,
InterbufferDistanceCB interbuffer_distance_cb);
SourceBufferRange(const SourceBufferRange&) = delete;
SourceBufferRange& operator=(const SourceBufferRange&) = delete;
~SourceBufferRange();
// Deletes all buffers in range.
void DeleteAll(BufferQueue* deleted_buffers);
// Seeks to the beginning of the range.
void SeekToStart();
// Updates |out_buffer| with the next buffer in presentation order by GOP and
// by decode order within each GOP (in general, in sequence to feed a
// decoder). Seek() must be called before calls to GetNextBuffer(), and
// buffers are returned in order from the last call to Seek(). Returns true if
// |out_buffer| is filled with a valid buffer, false if there is not enough
// data to fulfill the request.
bool GetNextBuffer(scoped_refptr<StreamParserBuffer>* out_buffer);
bool HasNextBuffer() const;
// Returns the config ID for the buffer that will be returned by
// GetNextBuffer().
int GetNextConfigId() const;
// Returns true if the range knows the position of the next buffer it should
// return, i.e. it has been Seek()ed. This does not necessarily mean that it
// has the next buffer yet.
bool HasNextBufferPosition() const;
// Resets this range to an "unseeked" state.
void ResetNextBufferPosition();
// Appends the buffers from |range| into this range.
// The first buffer in |range| must come directly after the last buffer
// in this range.
// If |transfer_current_position| is true, |range|'s |next_buffer_index_|
// is transferred to this SourceBufferRange.
// Note: Use these only to merge existing ranges. |range|'s first buffer
// timestamp must be adjacent to this range. No group start timestamp
// adjacency is involved in these methods.
// During append, |highest_frame_| is updated, if necessary.
void AppendRangeToEnd(const SourceBufferRange& range,
bool transfer_current_position);
bool CanAppendRangeToEnd(const SourceBufferRange& range) const;
// Appends |buffers| to the end of the range and updates |keyframe_map_| as
// it encounters new keyframes.
// If |new_buffers_group_start_pts| is kNoTimestamp, then the
// first buffer in |buffers| must come directly after the last buffer in this
// range (within the fudge room) - specifically, if the first buffer in
// |buffers| is not a keyframe, then it must be next in DTS order w.r.t. last
// buffer in |buffers|. Otherwise, it's a keyframe that must be next in PTS
// order w.r.t. |highest_frame_| or be immediately adjacent to the last buffer
// in this range if that buffer has estimated duration (only allowed in WebM
// streams).
// If |new_buffers_group_start_pts| is set otherwise, then that time must come
// directly after |highest_frame_| (within the fudge room), or directly after
// the last buffered frame if it has estimated duration (only allowed in WebM
// streams), and the first buffer in |buffers| must be a keyframe.
// The latter scenario is required when a muxed coded frame group has such a
// large jagged start across tracks that its first buffer is not within the
// fudge room, yet its group start was.
// The conditions around estimated duration are handled by
// AllowableAppendAfterEstimatedDuration, and are intended to solve the edge
// case in the SourceBufferStreamTest
// MergeAllowedIfRangeEndTimeWithEstimatedDurationMatchesNextRangeStart.
// During append, |highest_frame_| is updated, if necessary.
void AppendBuffersToEnd(const BufferQueue& buffers,
base::TimeDelta new_buffers_group_start_timestamp);
bool AllowableAppendAfterEstimatedDuration(
const BufferQueue& buffers,
base::TimeDelta new_buffers_group_start_pts) const;
bool CanAppendBuffersToEnd(const BufferQueue& buffers,
base::TimeDelta new_buffers_group_start_pts) const;
// Updates |next_buffer_index_| to point to the keyframe with presentation
// timestamp at or before |timestamp|. Assumes |timestamp| is valid and in
// this range.
void Seek(base::TimeDelta timestamp);
// Returns true if the range has enough data to seek to the specified
// |timestamp|, false otherwise.
bool CanSeekTo(base::TimeDelta timestamp) const;
// Return the config ID for the buffer at |timestamp|. Precondition: callers
// must first verify CanSeekTo(timestamp) == true.
int GetConfigIdAtTime(base::TimeDelta timestamp) const;
// Return true if all buffers in range of [start, end] have the same config
// ID. Precondition: callers must first verify that
// CanSeekTo(start) == CanSeekTo(end) == true.
bool SameConfigThruRange(base::TimeDelta start, base::TimeDelta end) const;
// Finds the next keyframe from |buffers_| starting at or after |timestamp|
// and creates and returns a new SourceBufferRange with the buffers from
// that keyframe onward. The buffers in the new SourceBufferRange are
// moved out of this range. The start time of the new SourceBufferRange
// is set to the later of |timestamp| and this range's GetStartTimestamp().
// Note that this may result in temporary overlap of the new range and this
// range until the caller truncates any nonkeyframes out of this range with
// time > |timestamp|. If there is no keyframe at or after |timestamp|,
// SplitRange() returns null and this range is unmodified. This range can
// become empty if |timestamp| <= the PTS of the first buffer in this range.
// |highest_frame_| is updated, if necessary.
std::unique_ptr<SourceBufferRange> SplitRange(base::TimeDelta timestamp);
// Deletes the buffers from this range starting at |timestamp|, exclusive if
// |is_exclusive| is true, inclusive otherwise.
// Resets |next_buffer_index_| if the buffer at |next_buffer_index_| was
// deleted, and deletes the |keyframe_map_| entries for the buffers that
// were removed.
// |highest_frame_| is updated, if necessary.
// |deleted_buffers| contains the buffers that were deleted from this range,
// starting at the buffer that had been at |next_buffer_index_|.
// Returns true if everything in the range was deleted. Otherwise
// returns false.
bool TruncateAt(base::TimeDelta timestamp,
BufferQueue* deleted_buffers,
bool is_exclusive);
// Deletes a GOP from the front or back of the range and moves these
// buffers into |deleted_buffers|. Returns the number of bytes deleted from
// the range (i.e. the size in bytes of |deleted_buffers|).
// |highest_frame_| is updated, if necessary.
// This range must NOT be empty when these methods are called.
// The GOP being deleted must NOT contain the next buffer position.
size_t DeleteGOPFromFront(BufferQueue* deleted_buffers);
size_t DeleteGOPFromBack(BufferQueue* deleted_buffers);
// Gets the range of GOP to secure at least |bytes_to_free| from
// [|start_timestamp|, |end_timestamp|).
// Returns the size of the buffers to secure if the buffers of
// [|start_timestamp|, |end_removal_timestamp|) is removed.
// Will not update |end_removal_timestamp| if the returned size is 0.
size_t GetRemovalGOP(base::TimeDelta start_timestamp,
base::TimeDelta end_timestamp,
size_t bytes_to_free,
base::TimeDelta* end_removal_timestamp) const;
// Returns true iff the buffered end time of the first GOP in this range is
// at or before |media_time|.
bool FirstGOPEarlierThanMediaTime(base::TimeDelta media_time) const;
// Indicates whether the GOP at the beginning or end of the range contains the
// next buffer position.
bool FirstGOPContainsNextBufferPosition() const;
bool LastGOPContainsNextBufferPosition() const;
// Returns the timestamp of the next buffer that will be returned from
// GetNextBuffer(), or kNoTimestamp if the timestamp is unknown.
base::TimeDelta GetNextTimestamp() const;
// Returns the start timestamp of the range.
base::TimeDelta GetStartTimestamp() const;
// Returns the highest presentation timestamp of frames in the last GOP in the
// range.
base::TimeDelta GetEndTimestamp() const;
// Returns the timestamp for the end of the buffered region in this range.
// This is an approximation if the duration for the buffer with highest PTS in
// the last GOP in the range is unset.
base::TimeDelta GetBufferedEndTimestamp() const;
// Returns whether a buffer with a starting timestamp of |timestamp| would
// belong in this range. This includes a buffer that would be appended to
// the end of the range.
bool BelongsToRange(base::TimeDelta timestamp) const;
// Returns the highest time from among GetStartTimestamp() and frame timestamp
// (in order in |buffers_| beginning at the first keyframe at or before
// |timestamp|) for buffers in this range up to and including |timestamp|.
// Note that |timestamp| must belong to this range.
base::TimeDelta FindHighestBufferedTimestampAtOrBefore(
base::TimeDelta timestamp) const;
// Gets the timestamp for the keyframe that is at or after |timestamp|. If
// there isn't such a keyframe in the range then kNoTimestamp is returned.
// If |timestamp| is in the "gap" between the value returned by
// GetStartTimestamp() and the timestamp on the first buffer in |buffers_|,
// then |timestamp| is returned.
base::TimeDelta NextKeyframeTimestamp(base::TimeDelta timestamp) const;
// Gets the timestamp for the closest keyframe that is <= |timestamp|. If
// there isn't a keyframe before |timestamp| or |timestamp| is outside
// this range, then kNoTimestamp is returned.
base::TimeDelta KeyframeBeforeTimestamp(base::TimeDelta timestamp) const;
// Adds all buffers which overlap [start, end) to the end of |buffers|. If no
// buffers exist in the range returns false, true otherwise. This method is
// only used for finding audio splice overlap buffers, so all buffers are
// expected to be keyframes here, or if not keyframes, to at least be in PTS
// order since the previous keyframe.
bool GetBuffersInRange(base::TimeDelta start,
base::TimeDelta end,
BufferQueue* buffers) const;
// Returns the memory usage of the buffered data in bytes.
size_t GetMemoryUsage() const { return memory_usage_in_bytes_; }
private:
// Friend of private is only for IsNextInPresentationSequence testing.
friend class SourceBufferStreamTest;
using KeyframeMap = std::map<base::TimeDelta, int>;
// Called during AppendBuffersToEnd to adjust estimated duration at the
// end of the last append to match the delta in timestamps between
// the last append and the upcoming append. This is a workaround for
// WebM media where a duration is not always specified. Caller should take
// care of updating |highest_frame_|.
void AdjustEstimatedDurationForNewAppend(const BufferQueue& new_buffers);
// Frees the buffers in |buffers_| from [|start_point|,|ending_point|) and
// updates the |memory_usage_in_bytes_| accordingly. Note, this does not
// update |keyframe_map_|.
void FreeBufferRange(const BufferQueue::const_iterator& starting_point,
const BufferQueue::const_iterator& ending_point);
// Returns the distance in time estimating how far from the beginning or end
// of this range a buffer can be to be considered in the range.
base::TimeDelta GetFudgeRoom() const;
// Returns the approximate duration of a buffer in this range.
base::TimeDelta GetApproximateDuration() const;
// Updates |highest_frame_| if |new_buffer| has a higher PTS than
// |highest_frame_|, |new_buffer| has the same PTS as |highest_frame_| and
// duration at least as long as |highest_frame_|, or if the range was
// previously empty.
void UpdateEndTime(scoped_refptr<StreamParserBuffer> new_buffer);
// Returns true if |timestamp| is allowed in this range as the timestamp of
// the next buffer in presentation sequence at or after |highest_frame_|.
// |buffers_| must not be empty, and |highest_frame_| must not be nullptr.
// Uses |gap_policy_| to potentially allow gaps.
//
// Due to potential for out-of-order decode vs presentation time, this method
// should only be used to determine adjacency of keyframes with the end of
// |buffers_|.
bool IsNextInPresentationSequence(base::TimeDelta timestamp) const;
// Returns true if |decode_timestamp| is allowed in this range as the decode
// timestamp of the next buffer in decode sequence at or after the last buffer
// in |buffers_|'s decode timestamp. |buffers_| must not be empty. Uses
// |gap_policy_| to potentially allow gaps.
//
// Due to potential for out-of-order decode vs presentation time, this method
// should only be used to determine adjacency of non-keyframes with the end of
// |buffers_|, when determining if a non-keyframe with |decode_timestamp|
// continues the decode sequence of the coded frame group at the end of
// |buffers_|.
bool IsNextInDecodeSequence(DecodeTimestamp decode_timestamp) const;
// Helper method for Appending |range| to the end of this range. If |range|'s
// first buffer time is before the time of the last buffer in this range,
// returns kNoTimestamp. Otherwise, returns the closest time within
// [|range|'s start time, |range|'s first buffer time] that is at or after the
// this range's GetEndTimestamp(). This allows |range| to potentially be
// determined to be adjacent within fudge room for appending to the end of
// this range, especially if |range| has a start time that is before its first
// buffer's time.
base::TimeDelta NextRangeStartTimeForAppendRangeToEnd(
const SourceBufferRange& range) const;
// Returns an index (or iterator) into |buffers_| pointing to the first buffer
// at or after |timestamp|. If |skip_given_timestamp| is true, this returns
// the first buffer with timestamp strictly greater than |timestamp|. If
// |buffers_| has no such buffer, returns |buffers_.size()| (or
// |buffers_.end()|).
size_t GetBufferIndexAt(base::TimeDelta timestamp,
bool skip_given_timestamp) const;
BufferQueue::const_iterator GetBufferItrAt(base::TimeDelta timestamp,
bool skip_given_timestamp) const;
// Returns an iterator in |keyframe_map_| pointing to the next keyframe after
// |timestamp|. If |skip_given_timestamp| is true, this returns the first
// keyframe with a timestamp strictly greater than |timestamp|.
KeyframeMap::const_iterator GetFirstKeyframeAt(
base::TimeDelta timestamp,
bool skip_given_timestamp) const;
// Returns an iterator in |keyframe_map_| pointing to the first keyframe
// before or at |timestamp|.
KeyframeMap::const_iterator GetFirstKeyframeAtOrBefore(
base::TimeDelta timestamp) const;
// Helper method to delete buffers in |buffers_| starting at
// |starting_point|, an index in |buffers_|.
// Returns true if everything in the range was removed. Returns
// false if the range still contains buffers.
bool TruncateAt(const size_t starting_point, BufferQueue* deleted_buffers);
// Updates |highest_frame_| to be the frame with highest PTS in the last GOP
// in this range. If there are no buffers in this range, resets
// |highest_frame_|.
// Normally, incremental additions to this range should just use
// UpdateEndTime(). When removing buffers from this range (which could be out
// of order presentation vs decode order), inspecting the last buffer in
// decode order of this range can be insufficient to determine the correct
// presentation end time of this range. Hence this helper method.
void UpdateEndTimeUsingLastGOP();
// Helper for debugging state.
std::string ToStringForDebugging() const;
// Keeps track of whether gaps are allowed.
const GapPolicy gap_policy_;
// The ordered list of buffers in this range.
BufferQueue buffers_;
// Index into |buffers_| for the next buffer to be returned by
// GetNextBuffer(), set to -1 by ResetNextBufferPosition().
int next_buffer_index_;
// Caches the buffer, if any, with the highest PTS currently in |buffers_|.
// This is nullptr if this range is empty. This is useful in determining
// range membership and adjacency.
scoped_refptr<StreamParserBuffer> highest_frame_;
// Called to get the largest interbuffer distance seen so far in the stream.
InterbufferDistanceCB interbuffer_distance_cb_;
// Stores the amount of memory taken up to store |buffers_|.
size_t memory_usage_in_bytes_ = 0;
// If the first buffer in this range is the beginning of a coded frame group,
// |range_start_pts_| is the presentation time when the coded frame group
// begins. This is especially important in muxed media where the first coded
// frames for each track do not necessarily begin at the same time.
// |range_start_pts_| may be <= the timestamp of the first buffer in
// |buffers_|. |range_start_pts_| is kNoTimestamp if this range does not start
// at the beginning of a coded frame group, which can happen by range removal
// or split when we don't have a way of knowing, across potentially multiple
// muxed streams, the coded frame group start timestamp for the new range.
base::TimeDelta range_start_pts_;
// Index base of all positions in |keyframe_map_|. In other words, the
// real position of entry |k| of |keyframe_map_| in the range is:
// keyframe_map_[k] - keyframe_map_index_base_
int keyframe_map_index_base_;
// Maps keyframe presentation timestamps to GOP start index of |buffers_|
// (with index adjusted by |keyframe_map_index_base_|);
KeyframeMap keyframe_map_;
};
} // namespace media
#endif // MEDIA_FILTERS_SOURCE_BUFFER_RANGE_H_