blob: 9b57c0a5808887e46d3fa2c3adcc5fa17383b6a3 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/filters/source_buffer_stream.h"
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/test/scoped_feature_list.h"
#include "media/base/data_buffer.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/base/media_util.h"
#include "media/base/mock_media_log.h"
#include "media/base/test_helpers.h"
#include "media/base/text_track_config.h"
#include "media/base/timestamp_constants.h"
#include "media/base/webvtt_util.h"
#include "media/filters/source_buffer_range.h"
#include "media/filters/source_buffer_range_by_dts.h"
#include "media/filters/source_buffer_range_by_pts.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::HasSubstr;
using ::testing::InSequence;
using ::testing::StrictMock;
using ::testing::Values;
namespace {
enum class TimeGranularity { kMicrosecond, kMillisecond };
// See https://crbug.com/718641 and kMseBufferByPts. This controls which kind of
// buffering implementation is constructed and tested.
enum class BufferingApi { kLegacyByDts, kNewByPts };
} // namespace
namespace media {
typedef StreamParser::BufferQueue BufferQueue;
static const int kDefaultFramesPerSecond = 30;
static const int kDefaultKeyframesPerSecond = 6;
static const uint8_t kDataA = 0x11;
static const uint8_t kDataB = 0x33;
static const int kDataSize = 1;
// Matchers for verifying common media log entry strings.
MATCHER(ContainsSameTimestampAt30MillisecondsLog, "") {
return CONTAINS_STRING(arg,
"Detected an append sequence with keyframe following "
"a non-keyframe, both with the same decode timestamp "
"of 0.03");
}
MATCHER_P(ContainsTrackBufferExhaustionSkipLog, skip_milliseconds, "") {
return CONTAINS_STRING(arg,
"Media append that overlapped current playback "
"position caused time gap in playing VIDEO stream "
"because the next keyframe is " +
base::IntToString(skip_milliseconds) +
"ms beyond last overlapped frame. Media may "
"appear temporarily frozen.");
}
// Based on runtime SourceBufferStreamTest parameter, picks the associated
// stream member pointer and performs |operation| on it through the pointer.
#define STREAM_OP(operation) \
(buffering_api_ == BufferingApi::kLegacyByDts ? stream_dts_->operation \
: stream_pts_->operation)
#define STREAM_RESET(config) \
{ \
if (buffering_api_ == BufferingApi::kLegacyByDts) { \
stream_dts_.reset(new SourceBufferStream<SourceBufferRangeByDts>( \
config, &media_log_)); \
} else { \
stream_pts_.reset(new SourceBufferStream<SourceBufferRangeByPts>( \
config, &media_log_)); \
} \
}
#define EXPECT_STATUS_FOR_STREAM_OP(status_suffix, operation) \
{ \
if (buffering_api_ == BufferingApi::kLegacyByDts) { \
EXPECT_EQ(SourceBufferStreamStatus::status_suffix, \
stream_dts_->operation); \
} else { \
EXPECT_EQ(SourceBufferStreamStatus::status_suffix, \
stream_pts_->operation); \
} \
}
// Test parameter determines which kind of SourceBufferRange we use in the test
// instance's SourceBufferStream<>. An attempt to used TYPED_TEST instead of
// TEST_P led to most lines in test cases needing prefix "this->", so we instead
// use TestWithParam, conditional fixture logic and helper macros.
class SourceBufferStreamTest : public testing::TestWithParam<BufferingApi> {
protected:
SourceBufferStreamTest() {
buffering_api_ = GetParam();
video_config_ = TestVideoConfig::Normal();
SetStreamInfo(kDefaultFramesPerSecond, kDefaultKeyframesPerSecond);
STREAM_RESET(video_config_);
}
void SetMemoryLimit(size_t buffers_of_data) {
STREAM_OP(set_memory_limit(buffers_of_data * kDataSize));
}
void SetStreamInfo(int frames_per_second, int keyframes_per_second) {
frames_per_second_ = frames_per_second;
keyframes_per_second_ = keyframes_per_second;
frame_duration_ = ConvertToFrameDuration(frames_per_second);
}
void SetTextStream() {
video_config_ = TestVideoConfig::Invalid();
TextTrackConfig config(kTextSubtitles, "", "", "");
STREAM_RESET(config);
SetStreamInfo(2, 2);
}
void SetAudioStream() {
video_config_ = TestVideoConfig::Invalid();
audio_config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32,
CHANNEL_LAYOUT_STEREO, 1000, EmptyExtraData(),
Unencrypted(), base::TimeDelta(), 0);
STREAM_RESET(audio_config_);
// Equivalent to 2ms per frame.
SetStreamInfo(500, 500);
}
void NewCodedFrameGroupAppend(int starting_position, int number_of_buffers) {
AppendBuffers(starting_position, number_of_buffers, true,
base::TimeDelta(), true, &kDataA, kDataSize);
}
void NewCodedFrameGroupAppend(int starting_position,
int number_of_buffers,
const uint8_t* data) {
AppendBuffers(starting_position, number_of_buffers, true,
base::TimeDelta(), true, data, kDataSize);
}
void NewCodedFrameGroupAppend_OffsetFirstBuffer(
int starting_position,
int number_of_buffers,
base::TimeDelta first_buffer_offset) {
AppendBuffers(starting_position, number_of_buffers, true,
first_buffer_offset, true, &kDataA, kDataSize);
}
void NewCodedFrameGroupAppend_ExpectFailure(int starting_position,
int number_of_buffers) {
AppendBuffers(starting_position, number_of_buffers, true,
base::TimeDelta(), false, &kDataA, kDataSize);
}
void AppendBuffers(int starting_position, int number_of_buffers) {
AppendBuffers(starting_position, number_of_buffers, false,
base::TimeDelta(), true, &kDataA, kDataSize);
}
void AppendBuffers(int starting_position,
int number_of_buffers,
const uint8_t* data) {
AppendBuffers(starting_position, number_of_buffers, false,
base::TimeDelta(), true, data, kDataSize);
}
void NewCodedFrameGroupAppend(const std::string& buffers_to_append) {
AppendBuffers(buffers_to_append, true, kNoTimestamp, false, true);
}
void NewCodedFrameGroupAppend(base::TimeDelta start_timestamp,
const std::string& buffers_to_append) {
AppendBuffers(buffers_to_append, true, start_timestamp, false, true);
}
void AppendBuffers(const std::string& buffers_to_append) {
AppendBuffers(buffers_to_append, false, kNoTimestamp, false, true);
}
void NewCodedFrameGroupAppendOneByOne(const std::string& buffers_to_append) {
AppendBuffers(buffers_to_append, true, kNoTimestamp, true, true);
}
void AppendBuffersOneByOne(const std::string& buffers_to_append) {
AppendBuffers(buffers_to_append, false, kNoTimestamp, true, true);
}
void NewCodedFrameGroupAppend_ExpectFailure(
const std::string& buffers_to_append) {
AppendBuffers(buffers_to_append, true, kNoTimestamp, false, false);
}
void AppendBuffers_ExpectFailure(const std::string& buffers_to_append) {
AppendBuffers(buffers_to_append, false, kNoTimestamp, false, false);
}
void Seek(int position) { STREAM_OP(Seek(position * frame_duration_)); }
void SeekToTimestampMs(int64_t timestamp_ms) {
STREAM_OP(Seek(base::TimeDelta::FromMilliseconds(timestamp_ms)));
}
bool GarbageCollect(base::TimeDelta media_time, int new_data_size) {
return STREAM_OP(GarbageCollectIfNeeded(
DecodeTimestamp::FromPresentationTime(media_time), new_data_size));
}
bool GarbageCollectWithPlaybackAtBuffer(int position, int newDataBuffers) {
return STREAM_OP(GarbageCollectIfNeeded(
DecodeTimestamp::FromPresentationTime(position * frame_duration_),
newDataBuffers * kDataSize));
}
void RemoveInMs(int start, int end, int duration) {
Remove(base::TimeDelta::FromMilliseconds(start),
base::TimeDelta::FromMilliseconds(end),
base::TimeDelta::FromMilliseconds(duration));
}
void Remove(base::TimeDelta start, base::TimeDelta end,
base::TimeDelta duration) {
STREAM_OP(Remove(start, end, duration));
}
void SignalStartOfCodedFrameGroup(base::TimeDelta start_timestamp) {
STREAM_OP(OnStartOfCodedFrameGroup(
DecodeTimestamp::FromPresentationTime(start_timestamp),
start_timestamp));
}
int GetRemovalRangeInMs(int start, int end, int bytes_to_free,
int* removal_end) {
DecodeTimestamp removal_end_timestamp =
DecodeTimestamp::FromMilliseconds(*removal_end);
int bytes_removed =
STREAM_OP(GetRemovalRange(DecodeTimestamp::FromMilliseconds(start),
DecodeTimestamp::FromMilliseconds(end),
bytes_to_free, &removal_end_timestamp));
*removal_end = removal_end_timestamp.InMilliseconds();
return bytes_removed;
}
void CheckExpectedRanges(const std::string& expected) {
Ranges<base::TimeDelta> r = STREAM_OP(GetBufferedTime());
std::stringstream ss;
ss << "{ ";
for (size_t i = 0; i < r.size(); ++i) {
int64_t start = (r.start(i) / frame_duration_);
int64_t end = (r.end(i) / frame_duration_) - 1;
ss << "[" << start << "," << end << ") ";
}
ss << "}";
EXPECT_EQ(expected, ss.str());
}
void CheckExpectedRangesByTimestamp(
const std::string& expected,
TimeGranularity granularity = TimeGranularity::kMillisecond) {
Ranges<base::TimeDelta> r = STREAM_OP(GetBufferedTime());
std::stringstream ss;
ss << "{ ";
for (size_t i = 0; i < r.size(); ++i) {
int64_t start = r.start(i).InMicroseconds();
int64_t end = r.end(i).InMicroseconds();
if (granularity == TimeGranularity::kMillisecond) {
start /= base::Time::kMicrosecondsPerMillisecond;
end /= base::Time::kMicrosecondsPerMillisecond;
}
ss << "[" << start << "," << end << ") ";
}
ss << "}";
EXPECT_EQ(expected, ss.str());
}
void CheckExpectedRangeEndTimes(const std::string& expected) {
std::stringstream ss;
ss << "{ ";
switch (buffering_api_) {
case BufferingApi::kLegacyByDts:
for (const auto& r : stream_dts_->ranges_) {
base::TimeDelta highest_pts;
base::TimeDelta end_time;
r->GetRangeEndTimesForTesting(&highest_pts, &end_time);
ss << "<" << highest_pts.InMilliseconds() << ","
<< end_time.InMilliseconds() << "> ";
}
break;
case BufferingApi::kNewByPts:
for (const auto& r : stream_pts_->ranges_) {
base::TimeDelta highest_pts;
base::TimeDelta end_time;
r->GetRangeEndTimesForTesting(&highest_pts, &end_time);
ss << "<" << highest_pts.InMilliseconds() << ","
<< end_time.InMilliseconds() << "> ";
}
break;
}
ss << "}";
EXPECT_EQ(expected, ss.str());
}
void CheckIsNextInPTSSequenceWithFirstRange(int64_t pts_in_ms,
bool expectation) {
ASSERT_GE(STREAM_OP(ranges_.size()), 1u);
switch (buffering_api_) {
case BufferingApi::kLegacyByDts: {
const auto& range_ptr = *(stream_dts_->ranges_.begin());
EXPECT_EQ(expectation,
range_ptr->IsNextInPresentationSequence(
base::TimeDelta::FromMilliseconds(pts_in_ms)));
break;
}
case BufferingApi::kNewByPts: {
const auto& range_ptr = *(stream_pts_->ranges_.begin());
EXPECT_EQ(expectation,
range_ptr->IsNextInPresentationSequence(
base::TimeDelta::FromMilliseconds(pts_in_ms)));
break;
}
}
}
void CheckExpectedBuffers(
int starting_position, int ending_position) {
CheckExpectedBuffers(starting_position, ending_position, false, NULL, 0);
}
void CheckExpectedBuffers(
int starting_position, int ending_position, bool expect_keyframe) {
CheckExpectedBuffers(starting_position, ending_position, expect_keyframe,
NULL, 0);
}
void CheckExpectedBuffers(int starting_position,
int ending_position,
const uint8_t* data) {
CheckExpectedBuffers(starting_position, ending_position, false, data,
kDataSize);
}
void CheckExpectedBuffers(int starting_position,
int ending_position,
const uint8_t* data,
bool expect_keyframe) {
CheckExpectedBuffers(starting_position, ending_position, expect_keyframe,
data, kDataSize);
}
void CheckExpectedBuffers(int starting_position,
int ending_position,
bool expect_keyframe,
const uint8_t* expected_data,
int expected_size) {
int current_position = starting_position;
for (; current_position <= ending_position; current_position++) {
scoped_refptr<StreamParserBuffer> buffer;
SourceBufferStreamStatus status = STREAM_OP(GetNextBuffer(&buffer));
EXPECT_NE(status, SourceBufferStreamStatus::kConfigChange);
if (status != SourceBufferStreamStatus::kSuccess)
break;
if (expect_keyframe && current_position == starting_position)
EXPECT_TRUE(buffer->is_key_frame());
if (expected_data) {
const uint8_t* actual_data = buffer->data();
const int actual_size = buffer->data_size();
EXPECT_EQ(expected_size, actual_size);
for (int i = 0; i < std::min(actual_size, expected_size); i++) {
EXPECT_EQ(expected_data[i], actual_data[i]);
}
}
EXPECT_EQ(buffer->GetDecodeTimestamp() / frame_duration_,
current_position);
}
EXPECT_EQ(ending_position + 1, current_position);
}
void CheckExpectedBuffers(
const std::string& expected,
TimeGranularity granularity = TimeGranularity::kMillisecond) {
std::vector<std::string> timestamps = base::SplitString(
expected, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
std::stringstream ss;
const SourceBufferStreamType type = STREAM_OP(GetType());
for (size_t i = 0; i < timestamps.size(); i++) {
scoped_refptr<StreamParserBuffer> buffer;
SourceBufferStreamStatus status = STREAM_OP(GetNextBuffer(&buffer));
if (i > 0)
ss << " ";
if (status == SourceBufferStreamStatus::kConfigChange) {
switch (type) {
case SourceBufferStreamType::kVideo:
STREAM_OP(GetCurrentVideoDecoderConfig());
break;
case SourceBufferStreamType::kAudio:
STREAM_OP(GetCurrentAudioDecoderConfig());
break;
case SourceBufferStreamType::kText:
STREAM_OP(GetCurrentTextTrackConfig());
break;
}
EXPECT_EQ("C", timestamps[i]);
ss << "C";
continue;
}
EXPECT_EQ(SourceBufferStreamStatus::kSuccess, status);
if (status != SourceBufferStreamStatus::kSuccess)
break;
if (granularity == TimeGranularity::kMillisecond)
ss << buffer->timestamp().InMilliseconds();
else
ss << buffer->timestamp().InMicroseconds();
if (buffer->GetDecodeTimestamp() !=
DecodeTimestamp::FromPresentationTime(buffer->timestamp())) {
if (granularity == TimeGranularity::kMillisecond)
ss << "|" << buffer->GetDecodeTimestamp().InMilliseconds();
else
ss << "|" << buffer->GetDecodeTimestamp().InMicroseconds();
}
// Check duration if expected timestamp contains it.
if (timestamps[i].find('D') != std::string::npos) {
ss << "D" << buffer->duration().InMilliseconds();
}
// Check duration estimation if expected timestamp contains it.
if (timestamps[i].find('E') != std::string::npos &&
buffer->is_duration_estimated()) {
ss << "E";
}
// Handle preroll buffers.
if (base::EndsWith(timestamps[i], "P", base::CompareCase::SENSITIVE)) {
ASSERT_TRUE(buffer->is_key_frame());
scoped_refptr<StreamParserBuffer> preroll_buffer;
preroll_buffer.swap(buffer);
// When a preroll buffer is encountered we should be able to request one
// more buffer. The first buffer should match the timestamp and config
// of the second buffer, except that its discard_padding() should be its
// duration.
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&buffer));
ASSERT_EQ(buffer->GetConfigId(), preroll_buffer->GetConfigId());
ASSERT_EQ(buffer->track_id(), preroll_buffer->track_id());
ASSERT_EQ(buffer->timestamp(), preroll_buffer->timestamp());
ASSERT_EQ(buffer->GetDecodeTimestamp(),
preroll_buffer->GetDecodeTimestamp());
ASSERT_EQ(kInfiniteDuration, preroll_buffer->discard_padding().first);
ASSERT_EQ(base::TimeDelta(), preroll_buffer->discard_padding().second);
ASSERT_TRUE(buffer->is_key_frame());
ss << "P";
} else if (buffer->is_key_frame()) {
ss << "K";
}
}
EXPECT_EQ(expected, ss.str());
}
void CheckNoNextBuffer() {
scoped_refptr<StreamParserBuffer> buffer;
EXPECT_STATUS_FOR_STREAM_OP(kNeedBuffer, GetNextBuffer(&buffer));
}
void CheckEOSReached() {
scoped_refptr<StreamParserBuffer> buffer;
EXPECT_STATUS_FOR_STREAM_OP(kEndOfStream, GetNextBuffer(&buffer));
}
void CheckVideoConfig(const VideoDecoderConfig& config) {
const VideoDecoderConfig& actual =
STREAM_OP(GetCurrentVideoDecoderConfig());
EXPECT_TRUE(actual.Matches(config))
<< "Expected: " << config.AsHumanReadableString()
<< "\nActual: " << actual.AsHumanReadableString();
}
void CheckAudioConfig(const AudioDecoderConfig& config) {
const AudioDecoderConfig& actual =
STREAM_OP(GetCurrentAudioDecoderConfig());
EXPECT_TRUE(actual.Matches(config))
<< "Expected: " << config.AsHumanReadableString()
<< "\nActual: " << actual.AsHumanReadableString();
}
base::TimeDelta frame_duration() const { return frame_duration_; }
StrictMock<MockMediaLog> media_log_;
std::unique_ptr<SourceBufferStream<SourceBufferRangeByDts>> stream_dts_;
std::unique_ptr<SourceBufferStream<SourceBufferRangeByPts>> stream_pts_;
VideoDecoderConfig video_config_;
AudioDecoderConfig audio_config_;
BufferingApi buffering_api_;
private:
DemuxerStream::Type GetStreamType() {
switch (STREAM_OP(GetType())) {
case SourceBufferStreamType::kAudio:
return DemuxerStream::AUDIO;
case SourceBufferStreamType::kVideo:
return DemuxerStream::VIDEO;
case SourceBufferStreamType::kText:
return DemuxerStream::TEXT;
default:
NOTREACHED();
return DemuxerStream::UNKNOWN;
}
}
base::TimeDelta ConvertToFrameDuration(int frames_per_second) {
return base::TimeDelta::FromMicroseconds(
base::Time::kMicrosecondsPerSecond / frames_per_second);
}
void AppendBuffers(int starting_position,
int number_of_buffers,
bool begin_coded_frame_group,
base::TimeDelta first_buffer_offset,
bool expect_success,
const uint8_t* data,
int size) {
if (begin_coded_frame_group) {
STREAM_OP(OnStartOfCodedFrameGroup(
DecodeTimestamp::FromPresentationTime(starting_position *
frame_duration_),
starting_position * frame_duration_));
}
int keyframe_interval = frames_per_second_ / keyframes_per_second_;
BufferQueue queue;
for (int i = 0; i < number_of_buffers; i++) {
int position = starting_position + i;
bool is_keyframe = position % keyframe_interval == 0;
// Track ID is meaningless to these tests.
scoped_refptr<StreamParserBuffer> buffer = StreamParserBuffer::CopyFrom(
data, size, is_keyframe, GetStreamType(), 0);
base::TimeDelta timestamp = frame_duration_ * position;
if (i == 0)
timestamp += first_buffer_offset;
buffer->SetDecodeTimestamp(
DecodeTimestamp::FromPresentationTime(timestamp));
// Simulate an IBB...BBP pattern in which all B-frames reference both
// the I- and P-frames. For a GOP with playback order 12345, this would
// result in a decode timestamp order of 15234.
base::TimeDelta presentation_timestamp;
if (is_keyframe) {
presentation_timestamp = timestamp;
} else if ((position - 1) % keyframe_interval == 0) {
// This is the P-frame (first frame following the I-frame)
presentation_timestamp =
(timestamp + frame_duration_ * (keyframe_interval - 2));
} else {
presentation_timestamp = timestamp - frame_duration_;
}
buffer->set_timestamp(presentation_timestamp);
buffer->set_duration(frame_duration_);
queue.push_back(buffer);
}
if (!queue.empty())
EXPECT_EQ(expect_success, STREAM_OP(Append(queue)));
}
void UpdateLastBufferDuration(DecodeTimestamp current_dts,
BufferQueue* buffers) {
if (buffers->empty() || buffers->back()->duration() > base::TimeDelta())
return;
DecodeTimestamp last_dts = buffers->back()->GetDecodeTimestamp();
DCHECK(current_dts >= last_dts);
buffers->back()->set_duration(current_dts - last_dts);
}
// StringToBufferQueue() allows for the generation of StreamParserBuffers from
// coded strings of timestamps separated by spaces.
//
// Supported syntax (options must be in this order):
// pp[u][|dd[u]][Dzz][E][P][K]
//
// pp:
// Generates a StreamParserBuffer with decode and presentation timestamp xx.
// E.g., "0 1 2 3".
// pp is interpreted as milliseconds, unless suffixed with "u", in which case
// pp is interpreted as microseconds.
//
// pp|dd:
// Generates a StreamParserBuffer with presentation timestamp pp and decode
// timestamp dd. E.g., "0|0 3|1 1|2 2|3". dd is interpreted as milliseconds,
// unless suffixed with "u", in which case dd is interpreted as microseconds.
//
// Dzz[u]
// Explicitly describe the duration of the buffer. zz specifies the duration
// in milliseconds (or in microseconds if suffixed with "u"). If the duration
// isn't specified with this syntax, the duration is derived using the
// timestamp delta between this buffer and the next buffer. If not specified,
// the final buffer will simply copy the duration of the previous buffer. If
// the queue only contains 1 buffer then the duration must be explicitly
// specified with this format.
// E.g. "0D10 10D20"
//
// E:
// Indicates that the buffer should be marked as containing an *estimated*
// duration. E.g., "0D20E 20 25E 30"
//
// P:
// Indicates the buffer with will also have a preroll buffer
// associated with it. The preroll buffer will just be dummy data.
// E.g. "0P 5 10"
//
// K:
// Indicates the buffer is a keyframe. E.g., "0K 1|2K 2|4D2K 6 8".
BufferQueue StringToBufferQueue(const std::string& buffers_to_append) {
std::vector<std::string> timestamps = base::SplitString(
buffers_to_append, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
CHECK_GT(timestamps.size(), 0u);
BufferQueue buffers;
for (size_t i = 0; i < timestamps.size(); i++) {
bool is_keyframe = false;
bool has_preroll = false;
bool is_duration_estimated = false;
if (base::EndsWith(timestamps[i], "K", base::CompareCase::SENSITIVE)) {
is_keyframe = true;
// Remove the "K" off of the token.
timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
}
// Handle preroll buffers.
if (base::EndsWith(timestamps[i], "P", base::CompareCase::SENSITIVE)) {
is_keyframe = true;
has_preroll = true;
// Remove the "P" off of the token.
timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
}
if (base::EndsWith(timestamps[i], "E", base::CompareCase::SENSITIVE)) {
is_duration_estimated = true;
// Remove the "E" off of the token.
timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
}
int duration_in_us = -1;
size_t duration_pos = timestamps[i].find('D');
if (duration_pos != std::string::npos) {
bool is_duration_us = false; // Default to millisecond interpretation.
if (base::EndsWith(timestamps[i], "u", base::CompareCase::SENSITIVE)) {
is_duration_us = true;
timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
}
CHECK(base::StringToInt(timestamps[i].substr(duration_pos + 1),
&duration_in_us));
if (!is_duration_us)
duration_in_us *= base::Time::kMicrosecondsPerMillisecond;
timestamps[i] = timestamps[i].substr(0, duration_pos);
}
std::vector<std::string> buffer_timestamp_strings = base::SplitString(
timestamps[i], "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (buffer_timestamp_strings.size() == 1)
buffer_timestamp_strings.push_back(buffer_timestamp_strings[0]);
CHECK_EQ(2u, buffer_timestamp_strings.size());
std::vector<base::TimeDelta> buffer_timestamps;
// Parse PTS, then DTS into TimeDeltas.
for (size_t j = 0; j < 2; ++j) {
int us = 0;
bool is_us = false; // Default to millisecond interpretation.
if (base::EndsWith(buffer_timestamp_strings[j], "u",
base::CompareCase::SENSITIVE)) {
is_us = true;
buffer_timestamp_strings[j] = buffer_timestamp_strings[j].substr(
0, buffer_timestamp_strings[j].length() - 1);
}
CHECK(base::StringToInt(buffer_timestamp_strings[j], &us));
if (!is_us)
us *= base::Time::kMicrosecondsPerMillisecond;
buffer_timestamps.push_back(base::TimeDelta::FromMicroseconds(us));
}
// Create buffer. Track ID is meaningless to these tests
scoped_refptr<StreamParserBuffer> buffer = StreamParserBuffer::CopyFrom(
&kDataA, kDataSize, is_keyframe, GetStreamType(), 0);
buffer->set_timestamp(buffer_timestamps[0]);
if (is_duration_estimated)
buffer->set_is_duration_estimated(true);
if (buffer_timestamps[1] != buffer_timestamps[0]) {
buffer->SetDecodeTimestamp(
DecodeTimestamp::FromPresentationTime(buffer_timestamps[1]));
}
if (duration_in_us >= 0)
buffer->set_duration(base::TimeDelta::FromMicroseconds(duration_in_us));
// Simulate preroll buffers by just generating another buffer and sticking
// it as the preroll.
if (has_preroll) {
scoped_refptr<StreamParserBuffer> preroll_buffer =
StreamParserBuffer::CopyFrom(&kDataA, kDataSize, is_keyframe,
GetStreamType(), 0);
preroll_buffer->set_duration(frame_duration_);
buffer->SetPrerollBuffer(preroll_buffer);
}
UpdateLastBufferDuration(buffer->GetDecodeTimestamp(), &buffers);
buffers.push_back(buffer);
}
// If the last buffer doesn't have a duration, assume it is the
// same as the second to last buffer.
if (buffers.size() >= 2 && buffers.back()->duration() == kNoTimestamp) {
buffers.back()->set_duration(
buffers[buffers.size() - 2]->duration());
}
return buffers;
}
void AppendBuffers(const std::string& buffers_to_append,
bool start_new_coded_frame_group,
base::TimeDelta coded_frame_group_start_timestamp,
bool one_by_one,
bool expect_success) {
BufferQueue buffers = StringToBufferQueue(buffers_to_append);
if (start_new_coded_frame_group) {
DecodeTimestamp start_timestamp = DecodeTimestamp::FromPresentationTime(
coded_frame_group_start_timestamp);
DecodeTimestamp buffers_start_timestamp =
buffering_api_ == BufferingApi::kLegacyByDts
? buffers[0]->GetDecodeTimestamp()
: DecodeTimestamp::FromPresentationTime(buffers[0]->timestamp());
if (start_timestamp == kNoDecodeTimestamp())
start_timestamp = buffers_start_timestamp;
else
ASSERT_TRUE(start_timestamp <= buffers_start_timestamp);
STREAM_OP(OnStartOfCodedFrameGroup(start_timestamp,
start_timestamp.ToPresentationTime()));
}
if (!one_by_one) {
EXPECT_EQ(expect_success, STREAM_OP(Append(buffers)));
return;
}
// Append each buffer one by one.
for (size_t i = 0; i < buffers.size(); i++) {
BufferQueue wrapper;
wrapper.push_back(buffers[i]);
EXPECT_TRUE(STREAM_OP(Append(wrapper)));
}
}
int frames_per_second_;
int keyframes_per_second_;
base::TimeDelta frame_duration_;
DISALLOW_COPY_AND_ASSIGN(SourceBufferStreamTest);
};
TEST_P(SourceBufferStreamTest, Append_SingleRange) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
}
TEST_P(SourceBufferStreamTest, Append_SingleRange_OneBufferAtATime) {
// Append 15 buffers starting at position 0, one buffer at a time.
NewCodedFrameGroupAppend(0, 1);
for (int i = 1; i < 15; i++)
AppendBuffers(i, 1);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
}
TEST_P(SourceBufferStreamTest, Append_DisjointRanges) {
// Append 5 buffers at positions 0 through 4.
NewCodedFrameGroupAppend(0, 5);
// Append 10 buffers at positions 15 through 24.
NewCodedFrameGroupAppend(15, 10);
// Check expected ranges.
CheckExpectedRanges("{ [0,4) [15,24) }");
// Check buffers in ranges.
Seek(0);
CheckExpectedBuffers(0, 4);
Seek(15);
CheckExpectedBuffers(15, 24);
}
TEST_P(SourceBufferStreamTest, Append_AdjacentRanges) {
// Append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10);
// Append 11 buffers at positions 15 through 25.
NewCodedFrameGroupAppend(15, 11);
// Append 5 buffers at positions 10 through 14 to bridge the gap.
NewCodedFrameGroupAppend(10, 5);
// Check expected range.
CheckExpectedRanges("{ [0,25) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 25);
}
TEST_P(SourceBufferStreamTest, Complete_Overlap) {
// Append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5);
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
}
TEST_P(SourceBufferStreamTest,
Complete_Overlap_AfterGroupTimestampAndBeforeFirstBufferTimestamp) {
// Append a coded frame group with a start timestamp of 0, but the first
// buffer starts at 30ms. This can happen in muxed content where the
// audio starts before the first frame.
NewCodedFrameGroupAppend(base::TimeDelta::FromMilliseconds(0),
"30K 60K 90K 120K");
CheckExpectedRangesByTimestamp("{ [0,150) }");
// Completely overlap the old buffers, with a coded frame group that starts
// after the old coded frame group start timestamp, but before the timestamp
// of the first buffer in the coded frame group.
NewCodedFrameGroupAppend("20K 50K 80K 110D10K");
// Verify that the buffered ranges are updated properly and we don't crash.
CheckExpectedRangesByTimestamp("{ [0,150) }");
SeekToTimestampMs(0);
CheckExpectedBuffers("20K 50K 80K 110K 120K");
}
TEST_P(SourceBufferStreamTest, Complete_Overlap_EdgeCase) {
// Make each frame a keyframe so that it's okay to overlap frames at any point
// (instead of needing to respect keyframe boundaries).
SetStreamInfo(30, 30);
// Append 6 buffers at positions 6 through 11.
NewCodedFrameGroupAppend(6, 6);
// Append 8 buffers at positions 5 through 12.
NewCodedFrameGroupAppend(5, 8);
// Check expected range.
CheckExpectedRanges("{ [5,12) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 12);
}
TEST_P(SourceBufferStreamTest, Start_Overlap) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 5);
// Append 6 buffers at positions 10 through 15.
NewCodedFrameGroupAppend(10, 6);
// Check expected range.
CheckExpectedRanges("{ [5,15) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 15);
}
TEST_P(SourceBufferStreamTest, End_Overlap) {
// Append 10 buffers at positions 10 through 19.
NewCodedFrameGroupAppend(10, 10);
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10);
// Check expected range.
CheckExpectedRanges("{ [5,19) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 19);
}
// Using position based test API:
// DTS : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// PTS : 0 4 1 2 3 5 9 6 7 8 10 14 11 12 13 15 19 16 17 18 20
// old  : A a a a a A a a a a
// new : B b b b b B b b
// after: B b b b b B b b A a a a a
TEST_P(SourceBufferStreamTest, End_Overlap_Several) {
// Append 10 buffers at positions 10 through 19 (DTS and PTS).
NewCodedFrameGroupAppend(10, 10, &kDataA);
// Append 8 buffers at positions 5 through 12 (DTS); 5 through 14 (PTS) with
// partial second GOP.
NewCodedFrameGroupAppend(5, 8, &kDataB);
// Check expected ranges: stream should not have kept buffers at DTS 13,14;
// PTS 12,13 because the keyframe on which they depended (10, PTS=DTS) was
// overwritten. Note that partial second GOP of B includes PTS [10,14), DTS
// [10,12). In both ByDts and ByPts, these are continuous with the overlapped
// original range's next GOP at (15, PTS=DTS).
// Unlike the rest of the position based test API used in this case, these
// range expectation strings are the actual timestamps (divided by
// frame_duration_), in DTS if ByDts, in PTS if ByPts.
CheckExpectedRanges("{ [5,19) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 12, &kDataB);
// No seek is necessary (1 continuous range).
CheckExpectedBuffers(15, 19, &kDataA);
CheckNoNextBuffer();
}
// Test an end overlap edge case where a single buffer overlaps the
// beginning of a range.
// old : 0K 30 60 90 120K 150
// new : 0K
// after: 0K 120K 150
// track:
TEST_P(SourceBufferStreamTest, End_Overlap_SingleBuffer) {
// Seek to start of stream.
SeekToTimestampMs(0);
NewCodedFrameGroupAppend("0K 30 60 90 120K 150");
CheckExpectedRangesByTimestamp("{ [0,180) }");
NewCodedFrameGroupAppend("0D30K");
CheckExpectedRangesByTimestamp("{ [0,180) }");
CheckExpectedBuffers("0K 120K 150");
CheckNoNextBuffer();
}
// Using position based test API:
// DTS : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// PTS : 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0
// old  : A a A a A a
// new : B b b b b B b b b b B b b b b B b b b b B b b b b B b b b b
// after == new
TEST_P(SourceBufferStreamTest, Complete_Overlap_Several) {
// Append 2 buffers at positions 5 through 6 (DTS); 5 through 9 (PTS) partial
// GOP.
NewCodedFrameGroupAppend(5, 2, &kDataA);
// Append 2 buffers at positions 15 through 16 (DTS); 15 through 19 (PTS)
// partial GOP.
NewCodedFrameGroupAppend(15, 2, &kDataA);
// Append 2 buffers at positions 25 through 26 (DTS); 25 through 29 (PTS)
// partial GOP.
NewCodedFrameGroupAppend(25, 2, &kDataA);
// Check expected ranges. Unlike the rest of the position based test API used
// in this case, these range expectation strings are the actual timestamps
// (divided by frame_duration_), in DTS if ByDts, in PTS if ByPts.
if (buffering_api_ == BufferingApi::kLegacyByDts)
CheckExpectedRanges("{ [5,6) [15,16) [25,26) }");
else
CheckExpectedRanges("{ [5,9) [15,19) [25,29) }");
// Append buffers at positions 0 through 29 (DTS and PTS).
NewCodedFrameGroupAppend(0, 30, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,29) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 29, &kDataB);
}
// Using position based test API:
// DTS:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
// PTS:0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9
// old: A a A a A a A a
// new:B b b b b B b b b b B b b b b B b b b b B b b b b B b b b b B b b b b
TEST_P(SourceBufferStreamTest, Complete_Overlap_Several_Then_Merge) {
// Append 2 buffers at positions 5 through 6 (DTS); 5 through 9 (PTS) partial
// GOP.
NewCodedFrameGroupAppend(5, 2, &kDataA);
// Append 2 buffers at positions 10 through 11 (DTS); 15 through 19 (PTS)
// partial GOP.
NewCodedFrameGroupAppend(15, 2, &kDataA);
// Append 2 buffers at positions 25 through 26 (DTS); 25 through 29 (PTS)
// partial GOP.
NewCodedFrameGroupAppend(25, 2, &kDataA);
// Append 2 buffers at positions 35 through 36 (DTS); 35 through 39 (PTS)
// partial GOP.
NewCodedFrameGroupAppend(35, 2, &kDataA);
// Append buffers at positions 0 through 34 (DTS and PTS).
NewCodedFrameGroupAppend(0, 35, &kDataB);
// Check expected ranges. Unlike the rest of the position based test API used
// in this case, these range expectation strings are the actual timestamps
// (divided by frame_duration_), in DTS if ByDts, in PTS if ByPts.
if (buffering_api_ == BufferingApi::kLegacyByDts)
CheckExpectedRanges("{ [0,36) }");
else
CheckExpectedRanges("{ [0,39) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 34, &kDataB);
CheckExpectedBuffers(35, 36, &kDataA);
}
TEST_P(SourceBufferStreamTest, Complete_Overlap_Selected) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to buffer at position 5.
Seek(5);
// Replace old data with new data.
NewCodedFrameGroupAppend(5, 10, &kDataB);
// Check ranges are correct.
CheckExpectedRanges("{ [5,14) }");
// Check that data has been replaced with new data.
CheckExpectedBuffers(5, 14, &kDataB);
}
// This test is testing that a client can append data to SourceBufferStream that
// overlaps the range from which the client is currently grabbing buffers. We
// would expect that the SourceBufferStream would return old data until it hits
// the keyframe of the new data, after which it will return the new data.
TEST_P(SourceBufferStreamTest, Complete_Overlap_Selected_TrackBuffer) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to buffer at position 5 and get next buffer.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Do a complete overlap by appending 20 buffers at positions 0 through 19.
NewCodedFrameGroupAppend(0, 20, &kDataB);
// Check range is correct.
CheckExpectedRanges("{ [0,19) }");
// Expect old data up until next keyframe in new data.
CheckExpectedBuffers(6, 9, &kDataA);
CheckExpectedBuffers(10, 10, &kDataB, true);
// Expect rest of data to be new.
CheckExpectedBuffers(11, 19, &kDataB);
// Seek back to beginning; all data should be new.
Seek(0);
CheckExpectedBuffers(0, 19, &kDataB);
// Check range continues to be correct.
CheckExpectedRanges("{ [0,19) }");
}
TEST_P(SourceBufferStreamTest, Complete_Overlap_Selected_EdgeCase) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to buffer at position 5 and get next buffer.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Replace existing data with new data.
NewCodedFrameGroupAppend(5, 10, &kDataB);
// Check ranges are correct.
CheckExpectedRanges("{ [5,14) }");
// Expect old data up until next keyframe in new data.
CheckExpectedBuffers(6, 9, &kDataA);
CheckExpectedBuffers(10, 10, &kDataB, true);
// Expect rest of data to be new.
CheckExpectedBuffers(11, 14, &kDataB);
// Seek back to beginning; all data should be new.
Seek(5);
CheckExpectedBuffers(5, 14, &kDataB);
// Check range continues to be correct.
CheckExpectedRanges("{ [5,14) }");
}
TEST_P(SourceBufferStreamTest, Complete_Overlap_Selected_Multiple) {
static const uint8_t kDataC = 0x55;
static const uint8_t kDataD = 0x77;
// Append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5, &kDataA);
// Seek to buffer at position 5 and get next buffer.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Replace existing data with new data.
NewCodedFrameGroupAppend(5, 5, &kDataB);
// Then replace it again with different data.
NewCodedFrameGroupAppend(5, 5, &kDataC);
// Now append 5 new buffers at positions 10 through 14.
NewCodedFrameGroupAppend(10, 5, &kDataC);
// Now replace all the data entirely.
NewCodedFrameGroupAppend(5, 10, &kDataD);
// Expect buffers 6 through 9 to be DataA, and the remaining
// buffers to be kDataD.
CheckExpectedBuffers(6, 9, &kDataA);
CheckExpectedBuffers(10, 14, &kDataD);
// At this point we cannot fulfill request.
CheckNoNextBuffer();
// Seek back to beginning; all data should be new.
Seek(5);
CheckExpectedBuffers(5, 14, &kDataD);
}
TEST_P(SourceBufferStreamTest, Start_Overlap_Selected) {
// Append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10, &kDataA);
// Seek to position 5, then add buffers to overlap data at that position.
Seek(5);
NewCodedFrameGroupAppend(5, 10, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Because we seeked to a keyframe, the next buffers should all be new data.
CheckExpectedBuffers(5, 14, &kDataB);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 4, &kDataA);
CheckExpectedBuffers(5, 14, &kDataB);
}
TEST_P(SourceBufferStreamTest, Start_Overlap_Selected_TrackBuffer) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15, &kDataA);
// Seek to 10 and get buffer.
Seek(10);
CheckExpectedBuffers(10, 10, &kDataA);
// Now append 10 buffers of new data at positions 10 through 19.
NewCodedFrameGroupAppend(10, 10, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,19) }");
// The next 4 buffers should be a from the old buffer, followed by a keyframe
// from the new data.
CheckExpectedBuffers(11, 14, &kDataA);
CheckExpectedBuffers(15, 15, &kDataB, true);
// The rest of the buffers should be new data.
CheckExpectedBuffers(16, 19, &kDataB);
// Now seek to the beginning; positions 0 through 9 should be the original
// data, positions 10 through 19 should be the new data.
Seek(0);
CheckExpectedBuffers(0, 9, &kDataA);
CheckExpectedBuffers(10, 19, &kDataB);
// Make sure range is still correct.
CheckExpectedRanges("{ [0,19) }");
}
TEST_P(SourceBufferStreamTest, Start_Overlap_Selected_EdgeCase) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
Seek(10);
CheckExpectedBuffers(10, 10, &kDataA);
// Now replace the last 5 buffers with new data.
NewCodedFrameGroupAppend(10, 5, &kDataB);
// The next 4 buffers should be the origial data, held in the track buffer.
CheckExpectedBuffers(11, 14, &kDataA);
// The next buffer is at position 15, so we should fail to fulfill the
// request.
CheckNoNextBuffer();
// Now append data at 15 through 19 and check to make sure it's correct.
NewCodedFrameGroupAppend(15, 5, &kDataB);
CheckExpectedBuffers(15, 19, &kDataB);
// Seek to beginning of buffered range and check buffers.
Seek(5);
CheckExpectedBuffers(5, 9, &kDataA);
CheckExpectedBuffers(10, 19, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [5,19) }");
}
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next buffer is a keyframe that's being overlapped by new
// buffers.
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : *A*a a a a A a a a a
// new  : B b b b b B b b b b
// after: B b b b b*B*b b b b A a a a a
TEST_P(SourceBufferStreamTest, End_Overlap_Selected) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to position 5.
Seek(5);
// Now append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Because we seeked to a keyframe, the next buffers should be new.
CheckExpectedBuffers(5, 9, &kDataB);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 9, &kDataB);
CheckExpectedBuffers(10, 14, &kDataA);
}
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next buffer in the range is after the newly appended buffers.
//
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : |A a a a a A a a*a*a|
// new  : B b b b b B b b b b
// after: |B b b b b B b b b b A a a*a*a|
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_AfterEndOfNew_1) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to position 10, then move to position 13.
Seek(10);
CheckExpectedBuffers(10, 12, &kDataA);
// Now append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Make sure rest of data is as expected.
CheckExpectedBuffers(13, 14, &kDataA);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 9, &kDataB);
CheckExpectedBuffers(10, 14, &kDataA);
}
// Using position based test API:
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next buffer in the range is after the newly appended buffers.
//
// DTS : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// PTS : 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0
// old  : A a a a a A a a*a*a
// new  : B b b b b B b b
// after: B b b b b B b b A a a*a*a
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_AfterEndOfNew_2) {
// Append 10 buffers at positions 5 through 14 (DTS and PTS, 2 full GOPs)
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to position 10, then move to position 13.
Seek(10);
CheckExpectedBuffers(10, 12, &kDataA);
// Now append 8 buffers at positions 0 through 7 (DTS); 0 through 9 (PTS) with
// partial second GOP.
NewCodedFrameGroupAppend(0, 8, &kDataB);
// Check expected ranges: stream should not have kept buffers at DTS 8,9;
// PTS 7,8 because the keyframe on which they depended (5, PTS=DTS) was
// overwritten. Note that partial second GOP of B includes PTS [5,9), DTS
// [5,7). In both ByDts and ByPts, these are continuous with the overlapped
// original range's next GOP at (10, PTS=DTS).
// Unlike the rest of the position based test API used in this case, these
// range expectation strings are the actual timestamps (divided by
// frame_duration_), in DTS if ByDts, in PTS if ByPts.
CheckExpectedRanges("{ [0,14) }");
// Make sure rest of data is as expected.
CheckExpectedBuffers(13, 14, &kDataA);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 7, &kDataB);
// No seek should be necessary (1 continuous range).
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
}
// Using position based test API:
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next buffer in the range is after the newly appended buffers.
//
// DTS : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// PTS : 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0
// old  : A a a*a*a A a a a a
// new  : B b b b b B b b
// after: B b b b b B b b A a a a a
// track: a a
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_AfterEndOfNew_3) {
// Append 10 buffers at positions 5 through 14 (DTS and PTS, 2 full GOPs)
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to position 5, then move to position 8.
Seek(5);
CheckExpectedBuffers(5, 7, &kDataA);
// Now append 8 buffers at positions 0 through 7 (DTS); 0 through 9 (PTS) with
// partial second GOP.
NewCodedFrameGroupAppend(0, 8, &kDataB);
// Check expected ranges: stream should not have kept buffers at DTS 8,9;
// PTS 7,8 because the keyframe on which they depended (5, PTS=DTS) was
// overwritten. However, they were in the GOP being read from, so were put
// into the track buffer. Note that partial second GOP of B includes PTS
// [5,9), DTS [5,7). In both ByDts and ByPts, these are continuous with the
// overlapped original range's next GOP at (10, PTS=DTS).
// Unlike the rest of the position based test API used in this case, these
// range expectation strings are the actual timestamps (divided by
// frame_duration_), in DTS if ByDts, in PTS if ByPts.
CheckExpectedRanges("{ [0,14) }");
// Check for data in the track buffer.
CheckExpectedBuffers(8, 9, &kDataA);
// The buffer immediately after the track buffer should be a keyframe.
CheckExpectedBuffers(10, 10, &kDataA, true);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 7, &kDataB);
// No seek should be necessary (1 continuous range).
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
}
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next buffer in the range is overlapped by the new buffers.
//
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : |A a a*a*a A a a a a|
// new  : B b b b b B b b b b
// after: |B b b b b B b b b b A a a a a|
// track: |a a|
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_OverlappedByNew_1) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to position 5, then move to position 8.
Seek(5);
CheckExpectedBuffers(5, 7, &kDataA);
// Now append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Check for data in the track buffer.
CheckExpectedBuffers(8, 9, &kDataA);
// The buffer immediately after the track buffer should be a keyframe.
CheckExpectedBuffers(10, 10, &kDataA, true);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 9, &kDataB);
CheckExpectedBuffers(10, 14, &kDataA);
}
// Using position based test API:
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next buffer in the range is overlapped by the new buffers.
//
// DTS : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// PTS : 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0
// old  : A*a*a a a A a a a a
// new  : B b b b b B b
// after: B b b b b B b A a a a a
// track: a a a a
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_OverlappedByNew_2) {
// Append 10 buffers at positions 5 through 14 (PTS and DTS, 2 full GOPs).
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to position 5, then move to position 6.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Now append 7 buffers at positions 0 through 6 (DTS); 0 through 9 (PTS) with
// partial second GOP.
NewCodedFrameGroupAppend(0, 7, &kDataB);
// Check expected ranges: stream should not have kept buffers at DTS 7,8,9;
// PTS 6,7,8 because the keyframe on which they depended (5, PTS=DTS) was
// overwritten. However, they were in the GOP being read from, so were put
// into the track buffer. Note that partial second GOP of B includes PTS
// [5,9), DTS [5,6). In both ByDts and ByPts, these are continuous with the
// overlapped original range's next GOP at (10, PTS=DTS).
// Unlike the rest of the position based test API used in this case, these
// range expectation strings are the actual timestamps (divided by
// frame_duration_), in DTS if ByDts, in PTS if ByPts.
CheckExpectedRanges("{ [0,14) }");
// Check for data in the track buffer.
CheckExpectedBuffers(6, 9, &kDataA);
// The buffer immediately after the track buffer should be a keyframe.
CheckExpectedBuffers(10, 10, &kDataA, true);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 6, &kDataB);
// No seek should be necessary (1 continuous range).
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
}
// Using position based test API:
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next buffer in the range is overlapped by the new buffers.
// In this particular case, the next keyframe after the track buffer is in the
// range with the new buffers.
//
// DTS : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// PTS : 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0
// old  : A*a*a a a A a a a a A a a a a
// new  : B b b b b B b b b b B
// after: B b b b b B b b b b B A a a a a
// track: a a a a
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_OverlappedByNew_3) {
// Append 15 buffers at positions 5 through 19 (PTS and DTS, 3 full GOPs).
NewCodedFrameGroupAppend(5, 15, &kDataA);
// Seek to position 5, then move to position 6.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Now append 11 buffers at positions 0 through 10 (PTS and DTS, 2 full GOPs
// and just the keyframe of a third GOP).
NewCodedFrameGroupAppend(0, 11, &kDataB);
// Check expected ranges: stream should not have kept buffers at 11-14 (DTS
// and PTS) because the keyframe on which they depended (10, PTS=DTS) was
// overwritten. The GOP being read from was overwritten, so track buffer
// should contain DTS 6-9 (PTS 9,6,7,8). Note that the partial third GOP of B
// includes (10, PTS=DTS). In both ByDts and ByPts, this partial GOP is
// continuous with the overlapped original range's next GOP at (15, PTS=DTS).
// Unlike the rest of the position based test API used in this case, these
// range expectation strings are the actual timestamps (divided by
// frame_duration_), in DTS if ByDts, in PTS if ByPts.
CheckExpectedRanges("{ [0,19) }");
// Check for data in the track buffer.
CheckExpectedBuffers(6, 9, &kDataA);
// The buffer immediately after the track buffer should be a keyframe
// from the new data.
CheckExpectedBuffers(10, 10, &kDataB, true);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 10, &kDataB);
// No seek should be necessary (1 continuous range).
CheckExpectedBuffers(15, 19, &kDataA);
}
// This test covers the case where new buffers end-overlap an existing, selected
// range, and there is no keyframe after the end of the new buffers.
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : |A*a*a a a|
// new  : B b b b b B
// after: |B b b b b B|
// track: |a a a a|
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_NoKeyframeAfterNew) {
// Append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5, &kDataA);
// Seek to position 5, then move to position 6.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Now append 6 buffers at positions 0 through 5.
NewCodedFrameGroupAppend(0, 6, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,5) }");
// Check for data in the track buffer.
CheckExpectedBuffers(6, 9, &kDataA);
// Now there's no data to fulfill the request.
CheckNoNextBuffer();
// Let's fill in the gap, buffers 6 through 10.
AppendBuffers(6, 5, &kDataB);
// We should be able to get the next buffer.
CheckExpectedBuffers(10, 10, &kDataB);
}
// Using position based test API:
// This test covers the case where new buffers end-overlap an existing, selected
// range, and there is no keyframe after the end of the new buffers, then more
// buffers end-overlap the beginning.
// DTS : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// PTS : 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0
// old  : A a a a a A*a*
// new  : B b b b b B b b b b B
// after: B b b b b B b b b b B
// new  : A a a a a A
// after: A a a a a A B b b b b B
// track: a
// new : B b b b b B
// after: A a a a a A B b b b b B b b b b B
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_NoKeyframeAfterNew2) {
// Append 7 buffers at positions 10 through 16 (DTS); 10 through 19 (PTS) with
// a partial second GOP.
NewCodedFrameGroupAppend(10, 7, &kDataA);
// Seek to position 15, then move to position 16.
Seek(15);
CheckExpectedBuffers(15, 15, &kDataA);
// Now append 11 buffers at positions 5 through 15 (PTS and DTS), 2 full GOPs
// and just the keyframe of a third GOP.
NewCodedFrameGroupAppend(5, 11, &kDataB);
// Check expected ranges: stream should not have kept buffer at DTS 16, PTS 19
// because the keyframe it depended on (15, PTS=DTS) was overwritten.
// The GOP being read from was overwritten, so track buffer
// should contain DTS 16, PTS 19.
// Unlike the rest of the position based test API used in this case,
// CheckExpectedRanges() uses expectation strings containing actual timestamps
// (divided by frame_duration_), in DTS if ByDts, in PTS if ByPts.
CheckExpectedRanges("{ [5,15) }");
// Now do another end-overlap. Append one full GOP plus keyframe of 2nd.
// Note that this new keyframe at (5, PTS=DTS) is continuous in both ByPts and
// ByDts with the overlapped range's next GOP (B) at (10, PTS=DTS).
NewCodedFrameGroupAppend(0, 6, &kDataA);
CheckExpectedRanges("{ [0,15) }");
// Check for data in the track buffer.
CheckExpectedBuffers(16, 16, &kDataA);
// Now there's no data to fulfill the request.
CheckNoNextBuffer();
// Add data to the end of the range in the position just read from the track
// buffer. The stream should not be able to fulfill the next read
// until we've added a keyframe continuous beyond this point.
NewCodedFrameGroupAppend(15, 1, &kDataB);
CheckNoNextBuffer();
for (int i = 16; i <= 19; i++) {
AppendBuffers(i, 1, &kDataB);
CheckNoNextBuffer();
}
// Now append a keyframe at PTS=DTS=20.
AppendBuffers(20, 1, &kDataB);
// If ByPts, the buffer at position 16 (PTS 19) in track buffer is adjacent to
// the next keyframe, so no warning should be emitted on that track buffer
// exhaustion.
if (buffering_api_ == BufferingApi::kLegacyByDts)
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(133));
// We should be able to get the next buffer (no longer from the track buffer).
CheckExpectedBuffers(20, 20, &kDataB, true);
CheckNoNextBuffer();
// Make sure all data is correct.
CheckExpectedRanges("{ [0,20) }");
Seek(0);
CheckExpectedBuffers(0, 5, &kDataA);
// No seek should be necessary (1 continuous range).
CheckExpectedBuffers(10, 20, &kDataB);
CheckNoNextBuffer();
}
// This test covers the case where new buffers end-overlap an existing, selected
// range, and the next keyframe in a separate range.
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : |A*a*a a a| |A a a a a|
// new  : B b b b b B
// after: |B b b b b B| |A a a a a|
// track: |a a a a|
TEST_P(SourceBufferStreamTest, End_Overlap_Selected_NoKeyframeAfterNew3) {
// Append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5, &kDataA);
// Append 5 buffers at positions 15 through 19.
NewCodedFrameGroupAppend(15, 5, &kDataA);
// Check expected range.
CheckExpectedRanges("{ [5,9) [15,19) }");
// Seek to position 5, then move to position 6.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Now append 6 buffers at positions 0 through 5.
NewCodedFrameGroupAppend(0, 6, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,5) [15,19) }");
// Check for data in the track buffer.
CheckExpectedBuffers(6, 9, &kDataA);
// Now there's no data to fulfill the request.
CheckNoNextBuffer();
// Let's fill in the gap, buffers 6 through 14.
AppendBuffers(6, 9, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,19) }");
// We should be able to get the next buffer.
CheckExpectedBuffers(10, 14, &kDataB);
// We should be able to get the next buffer.
CheckExpectedBuffers(15, 19, &kDataA);
}
// This test covers the case when new buffers overlap the middle of a selected
// range. This tests the case when there is precise overlap of an existing GOP,
// and the next buffer is a keyframe.
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : A a a a a*A*a a a a A a a a a
// new  : B b b b b
// after: A a a a a*B*b b b b A a a a a
TEST_P(SourceBufferStreamTest, Middle_Overlap_Selected_1) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15, &kDataA);
// Seek to position 5.
Seek(5);
// Now append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Check for next data; should be new data.
CheckExpectedBuffers(5, 9, &kDataB);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 4, &kDataA);
CheckExpectedBuffers(5, 9, &kDataB);
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
}
// This test covers the case when new buffers overlap the middle of a selected
// range. This tests the case when there is precise overlap of an existing GOP,
// and the next buffer is a non-keyframe in a GOP after the new buffers.
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : A a a a a A a a a a A*a*a a a
// new  : B b b b b
// after: A a a a a B b b b b A*a*a a a
TEST_P(SourceBufferStreamTest, Middle_Overlap_Selected_2) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15, &kDataA);
// Seek to 10 then move to position 11.
Seek(10);
CheckExpectedBuffers(10, 10, &kDataA);
// Now append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Make sure data is correct.
CheckExpectedBuffers(11, 14, &kDataA);
CheckNoNextBuffer();
Seek(0);
CheckExpectedBuffers(0, 4, &kDataA);
CheckExpectedBuffers(5, 9, &kDataB);
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
}
// This test covers the case when new buffers overlap the middle of a selected
// range. This tests the case when only a partial GOP is appended, that append
// is merged into the overlapped range, and the next buffer is before the new
// buffers.
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : A a*a*a a A a a a a A a a a a
// new  : B
// after: A a*a*a a B A a a a a
TEST_P(SourceBufferStreamTest, Middle_Overlap_Selected_3) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15, &kDataA);
// Seek to beginning then move to position 2.
Seek(0);
CheckExpectedBuffers(0, 1, &kDataA);
// Now append 1 buffer at position 5 (just the keyframe of a GOP).
NewCodedFrameGroupAppend(5, 1, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Make sure data is correct.
CheckExpectedBuffers(2, 4, &kDataA);
CheckExpectedBuffers(5, 5, &kDataB);
// No seek should be necessary (1 continuous range).
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
// Seek to the beginning and recheck data in case track buffer erroneously
// became involved.
Seek(0);
CheckExpectedBuffers(0, 4, &kDataA);
CheckExpectedBuffers(5, 5, &kDataB);
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
}
// This test covers the case when new buffers overlap the middle of a selected
// range. This tests the case when only a partial GOP is appended, and the next
// buffer is after the new buffers, and comes from the track buffer until the
// next GOP in the original buffers.
// index: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// old  : A a a a a A a a*a*a A a a a a
// new  : B
// after: A a a a a B A a a a a
// track: a a
TEST_P(SourceBufferStreamTest, Middle_Overlap_Selected_4) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15, &kDataA);
// Seek to 5 then move to position 8.
Seek(5);
CheckExpectedBuffers(5, 7, &kDataA);
// Now append 1 buffer at position 5.
NewCodedFrameGroupAppend(5, 1, &kDataB);
// Check expected range.
CheckExpectedRanges("{ [0,14) }");
// Buffers 8 and 9 should be in the track buffer.
CheckExpectedBuffers(8, 9, &kDataA);
// The buffer immediately after the track buffer should be a keyframe.
CheckExpectedBuffers(10, 10, &kDataA, true);
// Make sure all data is correct.
Seek(0);
CheckExpectedBuffers(0, 4, &kDataA);
CheckExpectedBuffers(5, 5, &kDataB);
// No seek should be necessary (1 continuous range).
CheckExpectedBuffers(10, 14, &kDataA);
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Overlap_OneByOne) {
// Append 5 buffers starting at 10ms, 30ms apart.
NewCodedFrameGroupAppendOneByOne("10K 40 70 100 130");
// The range ends at 160, accounting for the last buffer's duration.
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Overlap with 10 buffers starting at the beginning, appended one at a
// time.
NewCodedFrameGroupAppend(0, 1, &kDataB);
for (int i = 1; i < 10; i++)
AppendBuffers(i, 1, &kDataB);
// All data should be replaced.
Seek(0);
CheckExpectedRanges("{ [0,9) }");
CheckExpectedBuffers(0, 9, &kDataB);
}
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_DeleteGroup) {
NewCodedFrameGroupAppendOneByOne("10K 40 70 100 130K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 130ms.
SeekToTimestampMs(130);
// Overlap with a new coded frame group from 0 to 130ms.
NewCodedFrameGroupAppendOneByOne("0K 120D10");
// Next buffer should still be 130ms.
CheckExpectedBuffers("130K");
// Check the final buffers is correct.
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 120 130K");
}
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_BetweenCodedFrameGroups) {
// Append 5 buffers starting at 110ms, 30ms apart.
NewCodedFrameGroupAppendOneByOne("110K 140 170 200 230");
CheckExpectedRangesByTimestamp("{ [110,260) }");
// Now append 2 coded frame groups from 0ms to 210ms, 30ms apart. Note that
// the
// old keyframe 110ms falls in between these two groups.
NewCodedFrameGroupAppendOneByOne("0K 30 60 90");
NewCodedFrameGroupAppendOneByOne("120K 150 180 210");
CheckExpectedRangesByTimestamp("{ [0,240) }");
// Check the final buffers is correct; the keyframe at 110ms should be
// deleted.
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 30 60 90 120K 150 180 210");
}
// old : 10K 40 *70* 100K 125 130K
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer) {
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(50));
NewCodedFrameGroupAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
SeekToTimestampMs(70);
CheckExpectedBuffers("10K 40");
// Overlap with a new coded frame group from 0 to 130ms.
NewCodedFrameGroupAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
// Should return frame 70ms from the track buffer, then switch
// to the new data at 120K, then switch back to the old data at 130K. The
// frame at 125ms that depended on keyframe 100ms should have been deleted.
CheckExpectedBuffers("70 120K 130K");
// Check the final result: should not include data from the track buffer.
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 30 60 90 120K 130K");
}
// Overlap the next keyframe after the end of the track buffer with a new
// keyframe.
// old : 10K 40 *70* 100K 125 130K
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
// track: 70
// new : 110K 130
// after: 0K 30 60 90 *110K* 130
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer2) {
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(40));
NewCodedFrameGroupAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
SeekToTimestampMs(70);
CheckExpectedBuffers("10K 40");
// Overlap with a new coded frame group from 0 to 120ms; 70ms and 100ms go in
// track
// buffer.
NewCodedFrameGroupAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
// Now overlap the keyframe at 120ms.
NewCodedFrameGroupAppendOneByOne("110K 130");
// Should return frame 70ms from the track buffer. Then it should
// return the keyframe after the track buffer, which is at 110ms.
CheckExpectedBuffers("70 110K 130");
}
// Overlap the next keyframe after the end of the track buffer without a
// new keyframe.
// old : 10K 40 *70* 100K 125 130K
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
// track: 70
// new : 50K 80 110 140
// after: 0K 30 50K 80 110 140 * (waiting for keyframe)
// track: 70
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer3) {
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(80));
NewCodedFrameGroupAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
SeekToTimestampMs(70);
CheckExpectedBuffers("10K 40");
// Overlap with a new coded frame group from 0 to 120ms; 70ms goes in track
// buffer.
NewCodedFrameGroupAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
// Now overlap the keyframe at 120ms and 130ms.
NewCodedFrameGroupAppendOneByOne("50K 80 110 140");
CheckExpectedRangesByTimestamp("{ [0,170) }");
// Should have all the buffers from the track buffer, then stall.
CheckExpectedBuffers("70");
CheckNoNextBuffer();
// Appending a keyframe should fulfill the read.
AppendBuffersOneByOne("150D30K");
CheckExpectedBuffers("150K");
CheckNoNextBuffer();
}
// Overlap the next keyframe after the end of the track buffer with a keyframe
// that comes before the end of the track buffer.
// old : 10K 40 *70* 100K 125 130K
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
// track: 70
// new : 80K 110 140
// after: 0K 30 60 *80K* 110 140
// track: 70
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer4) {
NewCodedFrameGroupAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
SeekToTimestampMs(70);
CheckExpectedBuffers("10K 40");
// Overlap with a new coded frame group from 0 to 120ms; 70ms and 100ms go in
// track
// buffer.
NewCodedFrameGroupAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
// Now append a keyframe at 80ms.
NewCodedFrameGroupAppendOneByOne("80K 110 140");
CheckExpectedBuffers("70 80K 110 140");
CheckNoNextBuffer();
}
// Overlap the next keyframe after the end of the track buffer with a keyframe
// that comes before the end of the track buffer, when the selected stream was
// waiting for the next keyframe.
// old : 10K 40 *70* 100K
// new : 0K 30 60 90 120
// after: 0K 30 60 90 120 * (waiting for keyframe)
// track: 70
// new : 80K 110 140
// after: 0K 30 60 *80K* 110 140
// track: 70
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer5) {
NewCodedFrameGroupAppendOneByOne("10K 40 70 100K");
CheckExpectedRangesByTimestamp("{ [10,130) }");
// Seek to 70ms.
SeekToTimestampMs(70);
CheckExpectedBuffers("10K 40");
// Overlap with a new coded frame group from 0 to 120ms; 70ms goes in track
// buffer.
NewCodedFrameGroupAppendOneByOne("0K 30 60 90 120");
CheckExpectedRangesByTimestamp("{ [0,150) }");
// Now append a keyframe at 80ms.
NewCodedFrameGroupAppendOneByOne("80K 110 140");
CheckExpectedBuffers("70 80K 110 140");
CheckNoNextBuffer();
}
// Test that appending to a different range while there is data in
// the track buffer doesn't affect the selected range or track buffer state.
// old : 10K 40 *70* 100K 125 130K ... 200K 230
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K ... 200K 230
// track: 70
// old : 0K 30 60 90 *120K* 130K ... 200K 230
// new : 260K 290
// after: 0K 30 60 90 *120K* 130K ... 200K 230 260K 290
// track: 70
TEST_P(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer6) {
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(50));
NewCodedFrameGroupAppendOneByOne("10K 40 70 100K 125 130D30K");
NewCodedFrameGroupAppendOneByOne("200K 230");
CheckExpectedRangesByTimestamp("{ [10,160) [200,260) }");
// Seek to 70ms.
SeekToTimestampMs(70);
CheckExpectedBuffers("10K 40");
// Overlap with a new coded frame group from 0 to 120ms.
NewCodedFrameGroupAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) [200,260) }");
// Verify that 70 gets read out of the track buffer.
CheckExpectedBuffers("70");
// Append more data to the unselected range.
NewCodedFrameGroupAppendOneByOne("260K 290");
CheckExpectedRangesByTimestamp("{ [0,160) [200,320) }");
CheckExpectedBuffers("120K 130K");
CheckNoNextBuffer();
// Check the final result: should not include data from the track buffer.
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 30 60 90 120K 130K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Seek_Keyframe) {
// Append 6 buffers at positions 0 through 5.
NewCodedFrameGroupAppend(0, 6);
// Seek to beginning.
Seek(0);
CheckExpectedBuffers(0, 5, true);
}
TEST_P(SourceBufferStreamTest, Seek_NonKeyframe) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15);
// Seek to buffer at position 13.
Seek(13);
// Expect seeking back to the nearest keyframe.
CheckExpectedBuffers(10, 14, true);
// Seek to buffer at position 3.
Seek(3);
// Expect seeking back to the nearest keyframe.
CheckExpectedBuffers(0, 3, true);
}
TEST_P(SourceBufferStreamTest, Seek_NotBuffered) {
// Seek to beginning.
SeekToTimestampMs(0);
// Try to get buffer; nothing's appended.
CheckNoNextBuffer();
// Append 1 buffer at time 0, duration 10ms.
NewCodedFrameGroupAppend("0D10K");
// Confirm we can read it back.
SeekToTimestampMs(0);
CheckExpectedBuffers("0K");
// Try to get buffer out of range.
SeekToTimestampMs(10);
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Seek_InBetweenTimestamps) {
// Append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10);
base::TimeDelta bump = frame_duration() / 4;
CHECK(bump > base::TimeDelta());
// Seek to buffer a little after position 5.
STREAM_OP(Seek(5 * frame_duration() + bump));
CheckExpectedBuffers(5, 5, true);
// Seek to buffer a little before position 5.
STREAM_OP(Seek(5 * frame_duration() - bump));
CheckExpectedBuffers(0, 0, true);
}
// This test will do a complete overlap of an existing range in order to add
// buffers to the track buffers. Then the test does a seek to another part of
// the stream. The SourceBufferStream should clear its internal track buffer in
// response to the Seek().
TEST_P(SourceBufferStreamTest, Seek_After_TrackBuffer_Filled) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10, &kDataA);
// Seek to buffer at position 5 and get next buffer.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Do a complete overlap by appending 20 buffers at positions 0 through 19.
NewCodedFrameGroupAppend(0, 20, &kDataB);
// Check range is correct.
CheckExpectedRanges("{ [0,19) }");
// Seek to beginning; all data should be new.
Seek(0);
CheckExpectedBuffers(0, 19, &kDataB);
// Check range continues to be correct.
CheckExpectedRanges("{ [0,19) }");
}
TEST_P(SourceBufferStreamTest, Seek_StartOfGroup) {
base::TimeDelta bump = frame_duration() / 4;
CHECK(bump > base::TimeDelta());
// Append 5 buffers at position (5 + |bump|) through 9, where the coded frame
// group begins at position 5.
Seek(5);
NewCodedFrameGroupAppend_OffsetFirstBuffer(5, 5, bump);
scoped_refptr<StreamParserBuffer> buffer;
// GetNextBuffer() should return the next buffer at position (5 + |bump|).
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&buffer));
EXPECT_EQ(buffer->GetDecodeTimestamp(),
DecodeTimestamp::FromPresentationTime(5 * frame_duration() + bump));
// Check rest of buffers.
CheckExpectedBuffers(6, 9);
// Seek to position 15.
Seek(15);
// Append 5 buffers at positions (15 + |bump|) through 19, where the coded
// frame group begins at 15.
NewCodedFrameGroupAppend_OffsetFirstBuffer(15, 5, bump);
// GetNextBuffer() should return the next buffer at position (15 + |bump|).
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&buffer));
EXPECT_EQ(buffer->GetDecodeTimestamp(), DecodeTimestamp::FromPresentationTime(
15 * frame_duration() + bump));
// Check rest of buffers.
CheckExpectedBuffers(16, 19);
}
TEST_P(SourceBufferStreamTest, Seek_BeforeStartOfGroup) {
// Append 10 buffers at positions 5 through 14.
NewCodedFrameGroupAppend(5, 10);
// Seek to a time before the first buffer in the range.
Seek(0);
// Should return buffers from the beginning of the range.
CheckExpectedBuffers(5, 14);
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_CompleteOverlap) {
// Append 5 buffers at positions 0 through 4.
NewCodedFrameGroupAppend(0, 4);
// Append 5 buffers at positions 10 through 14, and seek to the beginning of
// this range.
NewCodedFrameGroupAppend(10, 5);
Seek(10);
// Now seek to the beginning of the first range.
Seek(0);
// Completely overlap the old seek point.
NewCodedFrameGroupAppend(5, 15);
// The GetNextBuffer() call should respect the 2nd seek point.
CheckExpectedBuffers(0, 0);
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_CompleteOverlap_Pending) {
// Append 2 buffers at positions 0 through 1.
NewCodedFrameGroupAppend(0, 2);
// Append 5 buffers at positions 15 through 19 and seek to beginning of the
// range.
NewCodedFrameGroupAppend(15, 5);
Seek(15);
// Now seek position 5.
Seek(5);
// Completely overlap the old seek point.
NewCodedFrameGroupAppend(10, 15);
// The seek at position 5 should still be pending.
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_MiddleOverlap) {
// Append 1 buffer at position 0, duration 10ms.
NewCodedFrameGroupAppend("0D10K");
// Append 3 IPBBB GOPs starting at 50ms.
NewCodedFrameGroupAppend(
"50K 90|60 60|70 70|80 80|90 "
"100K 140|110 110|120 120|130 130|140 "
"150K 190|160 160|170 170|180 180|190");
SeekToTimestampMs(150);
// Now seek to the beginning of the stream.
SeekToTimestampMs(0);
// Overlap the middle of the last range with a partial GOP, just a keyframe.
NewCodedFrameGroupAppend("100D10K");
CheckExpectedRangesByTimestamp("{ [0,10) [50,200) }");
// The GetNextBuffer() call should respect the 2nd seek point.
CheckExpectedBuffers("0K");
CheckNoNextBuffer();
// Check the data in the second range.
SeekToTimestampMs(50);
CheckExpectedBuffers(
"50K 90|60 60|70 70|80 80|90 100K 150K 190|160 160|170 170|180 180|190");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_MiddleOverlap_Pending) {
// Append 1 buffer at position 0, duration 10ms.
NewCodedFrameGroupAppend("0D10K");
// Append 3 IPBBB GOPs starting at 50ms. Then seek to 150ms.
NewCodedFrameGroupAppend(
"50K 90|60 60|70 70|80 80|90 "
"100K 140|110 110|120 120|130 130|140 "
"150K 190|160 160|170 170|180 180|190");
SeekToTimestampMs(150);
// Now seek to unbuffered time 20ms.
SeekToTimestampMs(20);
// Overlap the middle of the last range with a partial GOP, just a keyframe.
NewCodedFrameGroupAppend("100D10K");
CheckExpectedRangesByTimestamp("{ [0,10) [50,200) }");
// The seek to 20ms should still be pending.
CheckNoNextBuffer();
// Check the data in both ranges.
SeekToTimestampMs(0);
CheckExpectedBuffers("0K");
CheckNoNextBuffer();
SeekToTimestampMs(50);
CheckExpectedBuffers(
"50K 90|60 60|70 70|80 80|90 100K 150K 190|160 160|170 170|180 180|190");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_StartOverlap) {
// Append 2 buffers at positions 0 through 1.
NewCodedFrameGroupAppend(0, 2);
// Append 15 buffers at positions 5 through 19 and seek to position 15.
NewCodedFrameGroupAppend(5, 15);
Seek(15);
// Now seek to the beginning of the stream.
Seek(0);
// Start overlap the old seek point.
NewCodedFrameGroupAppend(10, 10);
// The GetNextBuffer() call should respect the 2nd seek point.
CheckExpectedBuffers(0, 0);
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_StartOverlap_Pending) {
// Append 2 buffers at positions 0 through 1.
NewCodedFrameGroupAppend(0, 2);
// Append 15 buffers at positions 10 through 24 and seek to position 20.
NewCodedFrameGroupAppend(10, 15);
Seek(20);
// Now seek to position 5.
Seek(5);
// Start overlap the old seek point.
NewCodedFrameGroupAppend(15, 10);
// The seek at time 0 should still be pending.
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_EndOverlap) {
// Append 5 buffers at positions 0 through 4.
NewCodedFrameGroupAppend(0, 4);
// Append 15 buffers at positions 10 through 24 and seek to start of range.
NewCodedFrameGroupAppend(10, 15);
Seek(10);
// Now seek to the beginning of the stream.
Seek(0);
// End overlap the old seek point.
NewCodedFrameGroupAppend(5, 10);
// The GetNextBuffer() call should respect the 2nd seek point.
CheckExpectedBuffers(0, 0);
}
TEST_P(SourceBufferStreamTest, OldSeekPoint_EndOverlap_Pending) {
// Append 2 buffers at positions 0 through 1.
NewCodedFrameGroupAppend(0, 2);
// Append 15 buffers at positions 15 through 29 and seek to start of range.
NewCodedFrameGroupAppend(15, 15);
Seek(15);
// Now seek to position 5
Seek(5);
// End overlap the old seek point.
NewCodedFrameGroupAppend(10, 10);
// The seek at time 5 should still be pending.
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, GetNextBuffer_AfterMerges) {
// Append 5 buffers at positions 10 through 14.
NewCodedFrameGroupAppend(10, 5);
// Seek to buffer at position 12.
Seek(12);
// Append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5);
// Make sure ranges are merged.
CheckExpectedRanges("{ [5,14) }");
// Make sure the next buffer is correct.
CheckExpectedBuffers(10, 10);
// Append 5 buffers at positions 15 through 19.
NewCodedFrameGroupAppend(15, 5);
CheckExpectedRanges("{ [5,19) }");
// Make sure the remaining next buffers are correct.
CheckExpectedBuffers(11, 14);
}
TEST_P(SourceBufferStreamTest, GetNextBuffer_ExhaustThenAppend) {
// Append 4 buffers at positions 0 through 3.
NewCodedFrameGroupAppend(0, 4);
// Seek to buffer at position 0 and get all buffers.
Seek(0);
CheckExpectedBuffers(0, 3);
// Next buffer is at position 4, so should not be able to fulfill request.
CheckNoNextBuffer();
// Append 2 buffers at positions 4 through 5.
AppendBuffers(4, 2);
CheckExpectedBuffers(4, 5);
}
// This test covers the case where new buffers start-overlap a range whose next
// buffer is not buffered.
TEST_P(SourceBufferStreamTest, GetNextBuffer_ExhaustThenStartOverlap) {
// Append 10 buffers at positions 0 through 9 and exhaust the buffers.
NewCodedFrameGroupAppend(0, 10, &kDataA);
Seek(0);
CheckExpectedBuffers(0, 9, &kDataA);
// Next buffer is at position 10, so should not be able to fulfill request.
CheckNoNextBuffer();
// Append 6 buffers at positons 5 through 10. This is to test that doing a
// start-overlap successfully fulfills the read at position 10, even though
// position 10 was unbuffered.
NewCodedFrameGroupAppend(5, 6, &kDataB);
CheckExpectedBuffers(10, 10, &kDataB);
// Then add 5 buffers from positions 11 though 15.
AppendBuffers(11, 5, &kDataB);
// Check the next 4 buffers are correct, which also effectively seeks to
// position 15.
CheckExpectedBuffers(11, 14, &kDataB);
// Replace the next buffer at position 15 with another start overlap.
NewCodedFrameGroupAppend(15, 2, &kDataA);
CheckExpectedBuffers(15, 16, &kDataA);
}
// Tests a start overlap that occurs right at the timestamp of the last output
// buffer that was returned by GetNextBuffer(). This test verifies that
// GetNextBuffer() skips to second GOP in the newly appended data instead
// of returning two buffers with the same timestamp.
TEST_P(SourceBufferStreamTest, GetNextBuffer_ExhaustThenStartOverlap2) {
NewCodedFrameGroupAppend("0K 30 60 90 120");
Seek(0);
CheckExpectedBuffers("0K 30 60 90 120");
CheckNoNextBuffer();
// Append a keyframe with the same timestamp as the last buffer output.
NewCodedFrameGroupAppend("120D30K");
CheckNoNextBuffer();
// Append the rest of the coded frame group and make sure that buffers are
// returned from the first GOP after 120.
AppendBuffers("150 180 210K 240");
CheckExpectedBuffers("210K 240");
// Seek to the beginning and verify the contents of the source buffer.
Seek(0);
CheckExpectedBuffers("0K 30 60 90 120K 150 180 210K 240");
CheckNoNextBuffer();
}
// This test covers the case where new buffers completely overlap a range
// whose next buffer is not buffered.
TEST_P(SourceBufferStreamTest, GetNextBuffer_ExhaustThenCompleteOverlap) {
// Append 5 buffers at positions 10 through 14 and exhaust the buffers.
NewCodedFrameGroupAppend(10, 5, &kDataA);
Seek(10);
CheckExpectedBuffers(10, 14, &kDataA);
// Next buffer is at position 15, so should not be able to fulfill request.
CheckNoNextBuffer();
// Do a complete overlap and test that this successfully fulfills the read
// at position 15.
NewCodedFrameGroupAppend(5, 11, &kDataB);
CheckExpectedBuffers(15, 15, &kDataB);
// Then add 5 buffers from positions 16 though 20.
AppendBuffers(16, 5, &kDataB);
// Check the next 4 buffers are correct, which also effectively seeks to
// position 20.
CheckExpectedBuffers(16, 19, &kDataB);
// Do a complete overlap and replace the buffer at position 20.
NewCodedFrameGroupAppend(0, 21, &kDataA);
CheckExpectedBuffers(20, 20, &kDataA);
}
// This test covers the case where a range is stalled waiting for its next
// buffer, then an end-overlap causes the end of the range to be deleted.
TEST_P(SourceBufferStreamTest, GetNextBuffer_ExhaustThenEndOverlap) {
// Append 5 buffers at positions 10 through 14 and exhaust the buffers.
NewCodedFrameGroupAppend(10, 5, &kDataA);
Seek(10);
CheckExpectedBuffers(10, 14, &kDataA);
CheckExpectedRanges("{ [10,14) }");
// Next buffer is at position 15, so should not be able to fulfill request.
CheckNoNextBuffer();
// Do an end overlap that causes the latter half of the range to be deleted.
NewCodedFrameGroupAppend(5, 6, &kDataB);
CheckNoNextBuffer();
CheckExpectedRanges("{ [5,10) }");
// Fill in the gap. Getting the next buffer should still stall at position 15.
for (int i = 11; i <= 14; i++) {
AppendBuffers(i, 1, &kDataB);
CheckNoNextBuffer();
}
// Append the buffer at position 15 and check to make sure all is correct.
AppendBuffers(15, 1);
CheckExpectedBuffers(15, 15);
CheckExpectedRanges("{ [5,15) }");
}
// This test is testing the "next buffer" logic after a complete overlap. In
// this scenario, when the track buffer is exhausted, there is no buffered data
// to fulfill the request. The SourceBufferStream should be able to fulfill the
// request when the data is later appended, and should not lose track of the
// "next buffer" position.
TEST_P(SourceBufferStreamTest, GetNextBuffer_Overlap_Selected_Complete) {
// Append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5, &kDataA);
// Seek to buffer at position 5 and get next buffer.
Seek(5);
CheckExpectedBuffers(5, 5, &kDataA);
// Replace existing data with new data.
NewCodedFrameGroupAppend(5, 5, &kDataB);
// Expect old data up until next keyframe in new data.
CheckExpectedBuffers(6, 9, &kDataA);
// Next buffer is at position 10, so should not be able to fulfill the
// request.
CheckNoNextBuffer();
// Now add 5 new buffers at positions 10 through 14.
AppendBuffers(10, 5, &kDataB);
CheckExpectedBuffers(10, 14, &kDataB);
}
TEST_P(SourceBufferStreamTest, PresentationTimestampIndependence) {
// Append 20 buffers at position 0.
NewCodedFrameGroupAppend(0, 20);
Seek(0);
int last_keyframe_idx = -1;
base::TimeDelta last_keyframe_presentation_timestamp;
base::TimeDelta last_p_frame_presentation_timestamp;
// Check for IBB...BBP pattern.
for (int i = 0; i < 20; i++) {
scoped_refptr<StreamParserBuffer> buffer;
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&buffer));
if (buffer->is_key_frame()) {
EXPECT_EQ(DecodeTimestamp::FromPresentationTime(buffer->timestamp()),
buffer->GetDecodeTimestamp());
last_keyframe_idx = i;
last_keyframe_presentation_timestamp = buffer->timestamp();
} else if (i == last_keyframe_idx + 1) {
ASSERT_NE(last_keyframe_idx, -1);
last_p_frame_presentation_timestamp = buffer->timestamp();
EXPECT_LT(last_keyframe_presentation_timestamp,
last_p_frame_presentation_timestamp);
} else {
EXPECT_GT(buffer->timestamp(), last_keyframe_presentation_timestamp);
EXPECT_LT(buffer->timestamp(), last_p_frame_presentation_timestamp);
EXPECT_LT(DecodeTimestamp::FromPresentationTime(buffer->timestamp()),
buffer->GetDecodeTimestamp());
}
}
}
TEST_P(SourceBufferStreamTest, GarbageCollection_DeleteFront) {
// Set memory limit to 20 buffers.
SetMemoryLimit(20);
// Append 20 buffers at positions 0 through 19.
NewCodedFrameGroupAppend(0, 1, &kDataA);
for (int i = 1; i < 20; i++)
AppendBuffers(i, 1, &kDataA);
// GC should be a no-op, since we are just under memory limit.
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(0, 0));
CheckExpectedRanges("{ [0,19) }");
Seek(0);
CheckExpectedBuffers(0, 19, &kDataA);
// Seek to the middle of the stream.
Seek(10);
// We are about to append 5 new buffers and current playback position is 10,
// so the GC algorithm should be able to delete some old data from the front.
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(10, 5));
CheckExpectedRanges("{ [5,19) }");
// Append 5 buffers to the end of the stream.
AppendBuffers(20, 5, &kDataA);
CheckExpectedRanges("{ [5,24) }");
CheckExpectedBuffers(10, 24, &kDataA);
Seek(5);
CheckExpectedBuffers(5, 9, &kDataA);
}
TEST_P(SourceBufferStreamTest,
GarbageCollection_DeleteFront_PreserveSeekedGOP) {
// Set memory limit to 15 buffers.
SetMemoryLimit(15);
NewCodedFrameGroupAppend("0K 10 20 30 40 50K 60 70 80 90");
NewCodedFrameGroupAppend("1000K 1010 1020 1030 1040");
// GC should be a no-op, since we are just under memory limit.
EXPECT_TRUE(STREAM_OP(GarbageCollectIfNeeded(DecodeTimestamp(), 0)));
CheckExpectedRangesByTimestamp("{ [0,100) [1000,1050) }");
// Seek to the near the end of the first range
SeekToTimestampMs(95);
// We are about to append 7 new buffers and current playback position is at
// the end of the last GOP in the first range, so the GC algorithm should be
// able to delete some old data from the front, but must not collect the last
// GOP in that first range. Neither can it collect the last appended GOP
// (which is the entire second range), so GC should return false since it
// couldn't collect enough.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(95), 7)));
CheckExpectedRangesByTimestamp("{ [50,100) [1000,1050) }");
}
TEST_P(SourceBufferStreamTest, GarbageCollection_DeleteFrontGOPsAtATime) {
// Set memory limit to 20 buffers.
SetMemoryLimit(20);
// Append 20 buffers at positions 0 through 19.
NewCodedFrameGroupAppend(0, 20, &kDataA);
// Seek to position 10.
Seek(10);
CheckExpectedRanges("{ [0,19) }");
// Add one buffer to put the memory over the cap.
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(10, 1));
AppendBuffers(20, 1, &kDataA);
// GC should have deleted the first 5 buffers so that the range still begins
// with a keyframe.
CheckExpectedRanges("{ [5,20) }");
CheckExpectedBuffers(10, 20, &kDataA);
Seek(5);
CheckExpectedBuffers(5, 9, &kDataA);
}
TEST_P(SourceBufferStreamTest, GarbageCollection_DeleteBack) {
// Set memory limit to 5 buffers.
SetMemoryLimit(5);
// Append 5 buffers at positions 15 through 19.
NewCodedFrameGroupAppend(15, 5, &kDataA);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(0, 0));
// Append 5 buffers at positions 0 through 4.
NewCodedFrameGroupAppend(0, 5, &kDataA);
CheckExpectedRanges("{ [0,4) [15,19) }");
// Seek to position 0.
Seek(0);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(0, 0));
// Should leave the first 5 buffers from 0 to 4.
CheckExpectedRanges("{ [0,4) }");
CheckExpectedBuffers(0, 4, &kDataA);
}
TEST_P(SourceBufferStreamTest, GarbageCollection_DeleteFrontAndBack) {
// Set memory limit to 3 buffers.
SetMemoryLimit(3);
// Seek to position 15.
Seek(15);
// Append 40 buffers at positions 0 through 39.
NewCodedFrameGroupAppend(0, 40, &kDataA);
// GC will try to keep data between current playback position and last append
// position. This will ensure that the last append position is 19 and will
// allow GC algorithm to collect data outside of the range [15,19)
NewCodedFrameGroupAppend(15, 5, &kDataA);
CheckExpectedRanges("{ [0,39) }");
// Should leave the GOP containing the current playback position 15 and the
// last append position 19. GC returns false, since we are still above limit.
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(15, 0));
CheckExpectedRanges("{ [15,19) }");
CheckExpectedBuffers(15, 19, &kDataA);
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, GarbageCollection_DeleteSeveralRanges) {
// Append 5 buffers at positions 0 through 4.
NewCodedFrameGroupAppend(0, 5);
// Append 5 buffers at positions 10 through 14.
NewCodedFrameGroupAppend(10, 5);
// Append 5 buffers at positions 20 through 24.
NewCodedFrameGroupAppend(20, 5);
// Append 5 buffers at positions 40 through 44.
NewCodedFrameGroupAppend(40, 5);
CheckExpectedRanges("{ [0,4) [10,14) [20,24) [40,44) }");
// Seek to position 20.
Seek(20);
CheckExpectedBuffers(20, 20);
// Set memory limit to 1 buffer.
SetMemoryLimit(1);
// Append 5 buffers at positions 30 through 34.
NewCodedFrameGroupAppend(30, 5);
// We will have more than 1 buffer left, GC will fail
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(20, 0));
// Should have deleted all buffer ranges before the current buffer and after
// last GOP
CheckExpectedRanges("{ [20,24) [30,34) }");
CheckExpectedBuffers(21, 24);
CheckNoNextBuffer();
// Continue appending into the last range to make sure it didn't break.
AppendBuffers(35, 10);
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(20, 0));
// Should save everything between read head and last appended
CheckExpectedRanges("{ [20,24) [30,44) }");
// Make sure appending before and after the ranges didn't somehow break.
SetMemoryLimit(100);
NewCodedFrameGroupAppend(0, 10);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(20, 0));
CheckExpectedRanges("{ [0,9) [20,24) [30,44) }");
Seek(0);
CheckExpectedBuffers(0, 9);
NewCodedFrameGroupAppend(90, 10);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(0, 0));
CheckExpectedRanges("{ [0,9) [20,24) [30,44) [90,99) }");
Seek(30);
CheckExpectedBuffers(30, 44);
CheckNoNextBuffer();
Seek(90);
CheckExpectedBuffers(90, 99);
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, GarbageCollection_DeleteAfterLastAppend) {
// Set memory limit to 10 buffers.
SetMemoryLimit(10);
// Append 1 GOP starting at 310ms, 30ms apart.
NewCodedFrameGroupAppend("310K 340 370");
// Append 2 GOPs starting at 490ms, 30ms apart.
NewCodedFrameGroupAppend("490K 520 550 580K 610 640");
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(0, 0));
CheckExpectedRangesByTimestamp("{ [310,400) [490,670) }");
// Seek to the GOP at 580ms.
SeekToTimestampMs(580);
// Append 2 GOPs before the existing ranges.
// So the ranges before GC are "{ [100,280) [310,400) [490,670) }".
NewCodedFrameGroupAppend("100K 130 160 190K 220 250K");
EXPECT_TRUE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(580), 0)));
// Should save the newly appended GOPs.
CheckExpectedRangesByTimestamp("{ [100,280) [580,670) }");
}
TEST_P(SourceBufferStreamTest, GarbageCollection_DeleteAfterLastAppendMerged) {
// Set memory limit to 10 buffers.
SetMemoryLimit(10);
// Append 3 GOPs starting at 400ms, 30ms apart.
NewCodedFrameGroupAppend("400K 430 460 490K 520 550 580K 610 640");
// Seek to the GOP at 580ms.
SeekToTimestampMs(580);
// Append 2 GOPs starting at 220ms, and they will be merged with the existing
// range. So the range before GC is "{ [220,670) }".
NewCodedFrameGroupAppend("220K 250 280 310K 340 370");
EXPECT_TRUE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(580), 0)));
// Should save the newly appended GOPs.
CheckExpectedRangesByTimestamp("{ [220,400) [580,670) }");
}
TEST_P(SourceBufferStreamTest, GarbageCollection_NoSeek) {
// Set memory limit to 20 buffers.
SetMemoryLimit(20);
// Append 25 buffers at positions 0 through 24.
NewCodedFrameGroupAppend(0, 25, &kDataA);
// If playback is still in the first GOP (starting at 0), GC should fail.
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(2, 0));
CheckExpectedRanges("{ [0,24) }");
// As soon as playback position moves past the first GOP, it should be removed
// and after removing the first GOP we are under memory limit.
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(5, 0));
CheckExpectedRanges("{ [5,24) }");
CheckNoNextBuffer();
Seek(5);
CheckExpectedBuffers(5, 24, &kDataA);
}
TEST_P(SourceBufferStreamTest, GarbageCollection_PendingSeek) {
// Append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10, &kDataA);
// Append 5 buffers at positions 25 through 29.
NewCodedFrameGroupAppend(25, 5, &kDataA);
// Seek to position 15.
Seek(15);
CheckNoNextBuffer();
CheckExpectedRanges("{ [0,9) [25,29) }");
// Set memory limit to 5 buffers.
SetMemoryLimit(5);
// Append 5 buffers as positions 30 to 34 to trigger GC.
AppendBuffers(30, 5, &kDataA);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(30, 0));
// The current algorithm will delete from the beginning until the memory is
// under cap.
CheckExpectedRanges("{ [30,34) }");
// Expand memory limit again so that GC won't be triggered.
SetMemoryLimit(100);
// Append data to fulfill seek.
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(30, 5));
NewCodedFrameGroupAppend(15, 5, &kDataA);
// Check to make sure all is well.
CheckExpectedRanges("{ [15,19) [30,34) }");
CheckExpectedBuffers(15, 19, &kDataA);
Seek(30);
CheckExpectedBuffers(30, 34, &kDataA);
}
TEST_P(SourceBufferStreamTest, GarbageCollection_NeedsMoreData) {
// Set memory limit to 15 buffers.
SetMemoryLimit(15);
// Append 10 buffers at positions 0 through 9.
NewCodedFrameGroupAppend(0, 10, &kDataA);
// Advance next buffer position to 10.
Seek(0);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(0, 0));
CheckExpectedBuffers(0, 9, &kDataA);
CheckNoNextBuffer();
// Append 20 buffers at positions 15 through 34.
NewCodedFrameGroupAppend(15, 20, &kDataA);
CheckExpectedRanges("{ [0,9) [15,34) }");
// GC should save the keyframe before the next buffer position and the data
// closest to the next buffer position. It will also save all buffers from
// next buffer to the last GOP appended, which overflows limit and leads to
// failure.
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(5, 0));
CheckExpectedRanges("{ [5,9) [15,34) }");
// Now fulfill the seek at position 10. This will make GC delete the data
// before position 10 to keep it within cap.
NewCodedFrameGroupAppend(10, 5, &kDataA);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(10, 0));
CheckExpectedRanges("{ [10,24) }");
CheckExpectedBuffers(10, 24, &kDataA);
}
// Using position based test API:
// DTS : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
// PTS : 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3 5 9 6 7 8 0 4 1 2 3
// old  : A a a a a A a a a a A a a a a*A*a a
// -- Garbage Collect --
// after: A a a
// -- Read one buffer --
// after: A*a*a
// new : B b b b b B b b b b B b b b b B b b b b
// track: *a*a
// -- Garbage Collect --
// after: B b b b b
// track: *a*a
// -- Read 2 buffers -> exhausts track buffer
// after: B b b b b
// (awaiting next keyframe after GOP at position 15)
// new : *B*b b b b
// after: B b b b b*B*b b b b
// -- Garbage Collect --
// after: *B*b b b b
TEST_P(SourceBufferStreamTest, GarbageCollection_TrackBuffer) {
// Set memory limit to 3 buffers.
SetMemoryLimit(3);
// Seek to position 15.
Seek(15);
// Append 18 buffers at positions 0 through 17 (DTS), 0 through 19 (PTS) with
// partial 4th GOP.
NewCodedFrameGroupAppend(0, 18, &kDataA);
EXPECT_TRUE(GarbageCollectWithPlaybackAtBuffer(15, 0));
// GC should leave GOP containing seek position (15,16,17 DTS; 15,19,16 PTS).
// Unlike the rest of the position based test API used in this case,
// CheckExpectedRanges() uses expectation strings containing actual timestamps
// (divided by frame_duration_), in DTS if ByDts, in PTS if ByPts.
if (buffering_api_ == BufferingApi::kLegacyByDts)
CheckExpectedRanges("{ [15,17) }");
else
CheckExpectedRanges("{ [15,19) }");
// Move next buffer position to 16.
CheckExpectedBuffers(15, 15, &kDataA);
// Completely overlap the existing buffers with 4 full GOPs (0-19, PTS and
// DTS).
NewCodedFrameGroupAppend(0, 20, &kDataB);
// Final GOP [15,19) contains 5 buffers, which is more than memory limit of
// 3 buffers set at the beginning of the test, so GC will fail.
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(15, 0));
// Because buffers 16,17 (DTS), 19,16 (PTS) are not keyframes, they are moved
// to the track buffer upon overlap. The source buffer (i.e. not the track
// buffer) is now waiting for the next keyframe beyond GOP that survived GC.
CheckExpectedRanges("{ [15,19) }"); // Source buffer
CheckExpectedBuffers(16, 17, &kDataA); // Exhaust the track buffer
CheckNoNextBuffer(); // Confirms the source buffer is awaiting next keyframe.
// Now add a 5-frame GOP at position 20-24 (PTS and DTS).
AppendBuffers(20, 5, &kDataB);
// 5 buffers in final GOP, GC will fail
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(20, 0));
// Should garbage collect such that there are 5 frames remaining, starting at
// the keyframe.
CheckExpectedRanges("{ [20,24) }");
// If ByPts, the buffer at position 16 (PTS 19) in track buffer was adjacent
// to the next keyframe (PTS=DTS=20), so no warning should be emitted on that
// track buffer exhaustion even though the last frame read out of track buffer
// before exhaustion was position 17 (PTS 16).
// If ByDts, exhaustion jumps from highest DTS in track buffer (17) to next
// keyframe (20). The test infra uses 33ms durations for these position based
// tests, resulting in a 99ms warning:
if (buffering_api_ == BufferingApi::kLegacyByDts)
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(99));
CheckExpectedBuffers(20, 24, &kDataB);
CheckNoNextBuffer();
}
// Test GC preserves data starting at first GOP containing playback position.
TEST_P(SourceBufferStreamTest, GarbageCollection_SaveDataAtPlaybackPosition) {
// Set memory limit to 30 buffers = 1 second of data.
SetMemoryLimit(30);
// And append 300 buffers = 10 seconds of data.
NewCodedFrameGroupAppend(0, 300, &kDataA);
CheckExpectedRanges("{ [0,299) }");
// Playback position at 0, all data must be preserved.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(0), 0)));
CheckExpectedRanges("{ [0,299) }");
// Playback position at 1 sec, the first second of data [0,29) should be
// collected, since we are way over memory limit.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(1000), 0)));
CheckExpectedRanges("{ [30,299) }");
// Playback position at 1.1 sec, no new data can be collected, since the
// playback position is still in the first GOP of buffered data.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(1100), 0)));
CheckExpectedRanges("{ [30,299) }");
// Playback position at 5.166 sec, just at the very end of GOP corresponding
// to buffer range 150-155, which should be preserved.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(5166), 0)));
CheckExpectedRanges("{ [150,299) }");
// Playback position at 5.167 sec, just past the end of GOP corresponding to
// buffer range 150-155, it should be garbage collected now.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(5167), 0)));
CheckExpectedRanges("{ [155,299) }");
// Playback at 9.0 sec, we can now successfully collect all data except the
// last second and we are back under memory limit of 30 buffers, so GCIfNeeded
// should return true.
EXPECT_TRUE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(9000), 0)));
CheckExpectedRanges("{ [270,299) }");
// Playback at 9.999 sec, GC succeeds, since we are under memory limit even
// without removing any data.
EXPECT_TRUE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(9999), 0)));
CheckExpectedRanges("{ [270,299) }");
// Playback at 15 sec, this should never happen during regular playback in
// browser, since this position has no data buffered, but it should still
// cause no problems to GC algorithm, so test it just in case.
EXPECT_TRUE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(15000), 0)));
CheckExpectedRanges("{ [270,299) }");
}
// Test saving the last GOP appended when this GOP is the only GOP in its range.
TEST_P(SourceBufferStreamTest, GarbageCollection_SaveAppendGOP) {
// Set memory limit to 3 and make sure the 4-byte GOP is not garbage
// collected.
SetMemoryLimit(3);
NewCodedFrameGroupAppend("0K 30 60 90");
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(0, 0));
CheckExpectedRangesByTimestamp("{ [0,120) }");
// Make sure you can continue appending data to this GOP; again, GC should not
// wipe out anything.
AppendBuffers("120D30");
EXPECT_FALSE(GarbageCollectWithPlaybackAtBuffer(0, 0));
CheckExpectedRangesByTimestamp("{ [0,150) }");
// Append a 2nd range after this without triggering GC.
NewCodedFrameGroupAppend("200K 230 260 290K 320 350");
CheckExpectedRangesByTimestamp("{ [0,150) [200,380) }");
// Seek to 290ms.
SeekToTimestampMs(290);
// Now append a GOP in a separate range after the selected range and trigger
// GC. Because it is after 290ms, this tests that the GOP is saved when
// deleting from the back.
NewCodedFrameGroupAppend("500K 530 560 590");
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(290), 0)));
// Should save GOPs between 290ms and the last GOP appended.
CheckExpectedRangesByTimestamp("{ [290,380) [500,620) }");
// Continue appending to this GOP after GC.
AppendBuffers("620D30");
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(290), 0)));
CheckExpectedRangesByTimestamp("{ [290,380) [500,650) }");
}
// Test saving the last GOP appended when this GOP is in the middle of a
// non-selected range.
TEST_P(SourceBufferStreamTest, GarbageCollection_SaveAppendGOP_Middle) {
// Append 3 GOPs starting at 0ms, 30ms apart.
NewCodedFrameGroupAppend("0K 30 60 90K 120 150 180K 210 240");
CheckExpectedRangesByTimestamp("{ [0,270) }");
// Now set the memory limit to 1 and overlap the middle of the range with a
// new GOP.
SetMemoryLimit(1);
NewCodedFrameGroupAppend("80K 110 140");
// This whole GOP should be saved after GC, which will fail due to GOP being
// larger than 1 buffer
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(80), 0)));
CheckExpectedRangesByTimestamp("{ [80,170) }");
// We should still be able to continue appending data to GOP
AppendBuffers("170D30");
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(80), 0)));
CheckExpectedRangesByTimestamp("{ [80,200) }");
// Append a 2nd range after this range, without triggering GC.
NewCodedFrameGroupAppend("400K 430 460 490K 520 550 580K 610 640");
CheckExpectedRangesByTimestamp("{ [80,200) [400,670) }");
// Seek to 80ms to make the first range the selected range.
SeekToTimestampMs(80);
// Now append a GOP in the middle of the second range and trigger GC. Because
// it is after the selected range, this tests that the GOP is saved when
// deleting from the back.
NewCodedFrameGroupAppend("500K 530 560 590");
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(80), 0)));
// Should save the GOPs between the seek point and GOP that was last appended
CheckExpectedRangesByTimestamp("{ [80,200) [400,620) }");
// Continue appending to this GOP after GC.
AppendBuffers("620D30");
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(80), 0)));
CheckExpectedRangesByTimestamp("{ [80,200) [400,650) }");
}
// Test saving the last GOP appended when the GOP containing the next buffer is
// adjacent to the last GOP appended.
TEST_P(SourceBufferStreamTest, GarbageCollection_SaveAppendGOP_Selected1) {
// Append 3 GOPs at 0ms, 90ms, and 180ms.
NewCodedFrameGroupAppend("0K 30 60 90K 120 150 180K 210 240");
CheckExpectedRangesByTimestamp("{ [0,270) }");
// Seek to the GOP at 90ms.
SeekToTimestampMs(90);
// Set the memory limit to 1, then overlap the GOP at 0.
SetMemoryLimit(1);
NewCodedFrameGroupAppend("0K 30 60");
// GC should save the GOP at 0ms and 90ms, and will fail since GOP larger
// than 1 buffer
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(90), 0)));
CheckExpectedRangesByTimestamp("{ [0,180) }");
// Seek to 0 and check all buffers.
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 30 60 90K 120 150");
CheckNoNextBuffer();
// Now seek back to 90ms and append a GOP at 180ms.
SeekToTimestampMs(90);
NewCodedFrameGroupAppend("180K 210 240");
// Should save the GOP at 90ms and the GOP at 180ms.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(90), 0)));
CheckExpectedRangesByTimestamp("{ [90,270) }");
CheckExpectedBuffers("90K 120 150 180K 210 240");
CheckNoNextBuffer();
}
// Test saving the last GOP appended when it is at the beginning or end of the
// selected range. This tests when the last GOP appended is before or after the
// GOP containing the next buffer, but not directly adjacent to this GOP.
TEST_P(SourceBufferStreamTest, GarbageCollection_SaveAppendGOP_Selected2) {
// Append 4 GOPs starting at positions 0ms, 90ms, 180ms, 270ms.
NewCodedFrameGroupAppend("0K 30 60 90K 120 150 180K 210 240 270K 300 330");
CheckExpectedRangesByTimestamp("{ [0,360) }");
// Seek to the last GOP at 270ms.
SeekToTimestampMs(270);
// Set the memory limit to 1, then overlap the GOP at 90ms.
SetMemoryLimit(1);
NewCodedFrameGroupAppend("90K 120 150");
// GC will save data in the range where the most recent append has happened
// [0; 180) and the range where the next read position is [270;360)
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(270), 0)));
CheckExpectedRangesByTimestamp("{ [0,180) [270,360) }");
// Add 3 GOPs to the end of the selected range at 360ms, 450ms, and 540ms.
NewCodedFrameGroupAppend("360K 390 420 450K 480 510 540K 570 600");
CheckExpectedRangesByTimestamp("{ [0,180) [270,630) }");
// Overlap the GOP at 450ms and garbage collect to test deleting from the
// back.
NewCodedFrameGroupAppend("450K 480 510");
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(270), 0)));
// Should save GOPs from GOP at 270ms to GOP at 450ms.
CheckExpectedRangesByTimestamp("{ [270,540) }");
}
// Test saving the last GOP appended when it is the same as the GOP containing
// the next buffer.
TEST_P(SourceBufferStreamTest, GarbageCollection_SaveAppendGOP_Selected3) {
// Seek to start of stream.
SeekToTimestampMs(0);
// Append 3 GOPs starting at 0ms, 90ms, 180ms.
NewCodedFrameGroupAppend("0K 30 60 90K 120 150 180K 210 240");
CheckExpectedRangesByTimestamp("{ [0,270) }");
// Set the memory limit to 1 then begin appending the start of a GOP starting
// at 0ms.
SetMemoryLimit(1);
NewCodedFrameGroupAppend("0K 30");
// GC should save the newly appended GOP, which is also the next GOP that
// will be returned from the seek request.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(0), 0)));
CheckExpectedRangesByTimestamp("{ [0,60) }");
// Check the buffers in the range.
CheckExpectedBuffers("0K 30");
CheckNoNextBuffer();
// Continue appending to this buffer.
AppendBuffers("60 90");
// GC should still save the rest of this GOP and should be able to fulfill
// the read.
EXPECT_FALSE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(0), 0)));
CheckExpectedRangesByTimestamp("{ [0,120) }");
CheckExpectedBuffers("60 90");
CheckNoNextBuffer();
}
// Test the performance of garbage collection.
TEST_P(SourceBufferStreamTest, GarbageCollection_Performance) {
// Force |keyframes_per_second_| to be equal to kDefaultFramesPerSecond.
SetStreamInfo(kDefaultFramesPerSecond, kDefaultFramesPerSecond);
const int kBuffersToKeep = 1000;
SetMemoryLimit(kBuffersToKeep);
int buffers_appended = 0;
NewCodedFrameGroupAppend(0, kBuffersToKeep);
buffers_appended += kBuffersToKeep;
const int kBuffersToAppend = 1000;
const int kGarbageCollections = 3;
for (int i = 0; i < kGarbageCollections; ++i) {
AppendBuffers(buffers_appended, kBuffersToAppend);
buffers_appended += kBuffersToAppend;
}
}
TEST_P(SourceBufferStreamTest, GarbageCollection_MediaTimeAfterLastAppendTime) {
// Set memory limit to 10 buffers.
SetMemoryLimit(10);
// Append 12 buffers. The duration of the last buffer is 30
NewCodedFrameGroupAppend("0K 30 60 90 120K 150 180 210K 240 270 300K 330D30");
CheckExpectedRangesByTimestamp("{ [0,360) }");
// Do a garbage collection with the media time higher than the timestamp of
// the last appended buffer (330), but still within buffered ranges, taking
// into account the duration of the last frame (timestamp of the last frame is
// 330, duration is 30, so the latest valid buffered position is 330+30=360).
EXPECT_TRUE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(360), 0)));
// GC should collect one GOP from the front to bring us back under memory
// limit of 10 buffers.
CheckExpectedRangesByTimestamp("{ [120,360) }");
}
TEST_P(SourceBufferStreamTest,
GarbageCollection_MediaTimeOutsideOfStreamBufferedRange) {
// Set memory limit to 10 buffers.
SetMemoryLimit(10);
// Append 12 buffers.
NewCodedFrameGroupAppend("0K 30 60 90 120K 150 180 210K 240 270 300K 330");
CheckExpectedRangesByTimestamp("{ [0,360) }");
// Seek in order to set the stream read position to 330 an ensure that the
// stream selects the buffered range.
SeekToTimestampMs(330);
// Do a garbage collection with the media time outside the buffered ranges
// (this might happen when there's both audio and video streams, audio stream
// buffered range is longer than the video stream buffered range, since
// media::Pipeline uses audio stream as a time source in that case, it might
// return a media_time that is slightly outside of video buffered range). In
// those cases the GC algorithm should clamp the media_time value to the
// buffered ranges to work correctly (see crbug.com/563292).
EXPECT_TRUE(STREAM_OP(
GarbageCollectIfNeeded(DecodeTimestamp::FromMilliseconds(361), 0)));
// GC should collect one GOP from the front to bring us back under memory
// limit of 10 buffers.
CheckExpectedRangesByTimestamp("{ [120,360) }");
}
TEST_P(SourceBufferStreamTest, GetRemovalRange_BytesToFree) {
// Append 2 GOPs starting at 300ms, 30ms apart.
NewCodedFrameGroupAppend("300K 330 360 390K 420 450");
// Append 2 GOPs starting at 600ms, 30ms apart.
NewCodedFrameGroupAppend("600K 630 660 690K 720 750");
// Append 2 GOPs starting at 900ms, 30ms apart.
NewCodedFrameGroupAppend("900K 930 960 990K 1020 1050");
CheckExpectedRangesByTimestamp("{ [300,480) [600,780) [900,1080) }");
int remove_range_end = -1;
int bytes_removed = -1;
// Size 0.
bytes_removed = GetRemovalRangeInMs(300, 1080, 0, &remove_range_end);
EXPECT_EQ(-1, remove_range_end);
EXPECT_EQ(0, bytes_removed);
// Smaller than the size of GOP.
bytes_removed = GetRemovalRangeInMs(300, 1080, 1, &remove_range_end);
EXPECT_EQ(390, remove_range_end);
// Remove as the size of GOP.
EXPECT_EQ(3, bytes_removed);
// The same size with a GOP.
bytes_removed = GetRemovalRangeInMs(300, 1080, 3, &remove_range_end);
EXPECT_EQ(390, remove_range_end);
EXPECT_EQ(3, bytes_removed);
// The same size with a range.
bytes_removed = GetRemovalRangeInMs(300, 1080, 6, &remove_range_end);
EXPECT_EQ(480, remove_range_end);
EXPECT_EQ(6, bytes_removed);
// A frame larger than a range.
bytes_removed = GetRemovalRangeInMs(300, 1080, 7, &remove_range_end);
EXPECT_EQ(690, remove_range_end);
EXPECT_EQ(9, bytes_removed);
// The same size with two ranges.
bytes_removed = GetRemovalRangeInMs(300, 1080, 12, &remove_range_end);
EXPECT_EQ(780, remove_range_end);
EXPECT_EQ(12, bytes_removed);
// Larger than two ranges.
bytes_removed = GetRemovalRangeInMs(300, 1080, 14, &remove_range_end);
EXPECT_EQ(990, remove_range_end);
EXPECT_EQ(15, bytes_removed);
// The same size with the whole ranges.
bytes_removed = GetRemovalRangeInMs(300, 1080, 18, &remove_range_end);
EXPECT_EQ(1080, remove_range_end);
EXPECT_EQ(18, bytes_removed);
// Larger than the whole ranges.
bytes_removed = GetRemovalRangeInMs(300, 1080, 20, &remove_range_end);
EXPECT_EQ(1080, remove_range_end);
EXPECT_EQ(18, bytes_removed);
}
TEST_P(SourceBufferStreamTest, GetRemovalRange_Range) {
// Append 2 GOPs starting at 300ms, 30ms apart.
NewCodedFrameGroupAppend("300K 330 360 390K 420 450");
// Append 2 GOPs starting at 600ms, 30ms apart.
NewCodedFrameGroupAppend("600K 630 660 690K 720 750");
// Append 2 GOPs starting at 900ms, 30ms apart.
NewCodedFrameGroupAppend("900K 930 960 990K 1020 1050");
CheckExpectedRangesByTimestamp("{ [300,480) [600,780) [900,1080) }");
int remove_range_end = -1;
int bytes_removed = -1;
// Within a GOP and no keyframe.
bytes_removed = GetRemovalRangeInMs(630, 660, 20, &remove_range_end);
EXPECT_EQ(-1, remove_range_end);
EXPECT_EQ(0, bytes_removed);
// Across a GOP and no keyframe.
bytes_removed = GetRemovalRangeInMs(630, 750, 20, &remove_range_end);
EXPECT_EQ(-1, remove_range_end);
EXPECT_EQ(0, bytes_removed);
// The same size with a range.
bytes_removed = GetRemovalRangeInMs(600, 780, 20, &remove_range_end);
EXPECT_EQ(780, remove_range_end);
EXPECT_EQ(6, bytes_removed);
// One frame larger than a range.
bytes_removed = GetRemovalRangeInMs(570, 810, 20, &remove_range_end);
EXPECT_EQ(780, remove_range_end);
EXPECT_EQ(6, bytes_removed);
// Facing the other ranges.
bytes_removed = GetRemovalRangeInMs(480, 900, 20, &remove_range_end);
EXPECT_EQ(780, remove_range_end);
EXPECT_EQ(6, bytes_removed);
// In the midle of the other ranges, but not including any GOP.
bytes_removed = GetRemovalRangeInMs(420, 960, 20, &remove_range_end);
EXPECT_EQ(780, remove_range_end);
EXPECT_EQ(6, bytes_removed);
// In the middle of the other ranges.
bytes_removed = GetRemovalRangeInMs(390, 990, 20, &remove_range_end);
EXPECT_EQ(990, remove_range_end);
EXPECT_EQ(12, bytes_removed);
// A frame smaller than the whole ranges.
bytes_removed = GetRemovalRangeInMs(330, 1050, 20, &remove_range_end);
EXPECT_EQ(990, remove_range_end);
EXPECT_EQ(12, bytes_removed);
// The same with the whole ranges.
bytes_removed = GetRemovalRangeInMs(300, 1080, 20, &remove_range_end);
EXPECT_EQ(1080, remove_range_end);
EXPECT_EQ(18, bytes_removed);
// Larger than the whole ranges.
bytes_removed = GetRemovalRangeInMs(270, 1110, 20, &remove_range_end);
EXPECT_EQ(1080, remove_range_end);
EXPECT_EQ(18, bytes_removed);
}
TEST_P(SourceBufferStreamTest, ConfigChange_Basic) {
VideoDecoderConfig new_config = TestVideoConfig::Large();
ASSERT_FALSE(new_config.Matches(video_config_));
Seek(0);
CheckVideoConfig(video_config_);
// Append 5 buffers at positions 0 through 4
NewCodedFrameGroupAppend(0, 5, &kDataA);
CheckVideoConfig(video_config_);
// Signal a config change.
STREAM_OP(UpdateVideoConfig(new_config, false));
// Make sure updating the config doesn't change anything since new_config
// should not be associated with the buffer GetNextBuffer() will return.
CheckVideoConfig(video_config_);
// Append 5 buffers at positions 5 through 9.
NewCodedFrameGroupAppend(5, 5, &kDataB);
// Consume the buffers associated with the initial config.
scoped_refptr<StreamParserBuffer> buffer;
for (int i = 0; i < 5; i++) {
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&buffer));
CheckVideoConfig(video_config_);
}
// Verify the next attempt to get a buffer will signal that a config change
// has happened.
EXPECT_STATUS_FOR_STREAM_OP(kConfigChange, GetNextBuffer(&buffer));
// Verify that the new config is now returned.
CheckVideoConfig(new_config);
// Consume the remaining buffers associated with the new config.
for (int i = 0; i < 5; i++) {
CheckVideoConfig(new_config);
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&buffer));
}
}
TEST_P(SourceBufferStreamTest, ConfigChange_Seek) {
scoped_refptr<StreamParserBuffer> buffer;
VideoDecoderConfig new_config = TestVideoConfig::Large();
Seek(0);
NewCodedFrameGroupAppend(0, 5, &kDataA);
STREAM_OP(UpdateVideoConfig(new_config, false));
NewCodedFrameGroupAppend(5, 5, &kDataB);
// Seek to the start of the buffers with the new config and make sure a
// config change is signalled.
CheckVideoConfig(video_config_);
Seek(5);
CheckVideoConfig(video_config_);
EXPECT_STATUS_FOR_STREAM_OP(kConfigChange, GetNextBuffer(&buffer));
CheckVideoConfig(new_config);
CheckExpectedBuffers(5, 9, &kDataB);
// Seek to the start which has a different config. Don't fetch any buffers and
// seek back to buffers with the current config. Make sure a config change
// isn't signalled in this case.
CheckVideoConfig(new_config);
Seek(0);
Seek(7);
CheckExpectedBuffers(5, 9, &kDataB);
// Seek to the start and make sure a config change is signalled.
CheckVideoConfig(new_config);
Seek(0);
CheckVideoConfig(new_config);
EXPECT_STATUS_FOR_STREAM_OP(kConfigChange, GetNextBuffer(&buffer));
CheckVideoConfig(video_config_);
CheckExpectedBuffers(0, 4, &kDataA);
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration) {
// Append 3 discontinuous partial GOPs.
NewCodedFrameGroupAppend("50K 90|60");
NewCodedFrameGroupAppend("150K 190|160");
NewCodedFrameGroupAppend("250K 290|260");
// Check expected ranges.
if (buffering_api_ == BufferingApi::kLegacyByDts)
CheckExpectedRangesByTimestamp("{ [50,70) [150,170) [250,270) }");
else
CheckExpectedRangesByTimestamp("{ [50,100) [150,200) [250,300) }");
// Set duration to be 80ms.
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(80)));
// Truncate the buffered data after 80ms (DTS if ByDts, PTS if ByPts).
if (buffering_api_ == BufferingApi::kLegacyByDts) {
CheckExpectedRangesByTimestamp("{ [50,70) }");
} else {
// In ByPts buffering, the simulated P-frame at PTS 90ms should have been
// removed by the duration truncation. Only the frame at PTS 50ms should
// remain.
CheckExpectedRangesByTimestamp("{ [50,60) }");
}
// Adding data past the previous duration should still work.
NewCodedFrameGroupAppend("0D50K 50 100K");
CheckExpectedRangesByTimestamp("{ [0,150) }");
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration_EdgeCase) {
// Append 10 buffers at positions 10 through 19.
NewCodedFrameGroupAppend(10, 10);
// Append 5 buffers at positions 25 through 29.
NewCodedFrameGroupAppend(25, 5);
// Check expected ranges.
CheckExpectedRanges("{ [10,19) [25,29) }");
// Set duration to be right before buffer 25.
STREAM_OP(OnSetDuration(frame_duration() * 25));
// Should truncate the last range.
CheckExpectedRanges("{ [10,19) }");
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration_EdgeCase2) {
// This test requires specific relative proportions for fudge room, append
// size, and duration truncation amounts. See details at:
// https://codereview.chromium.org/2385423002
// Append buffers with first buffer establishing max_inter_buffer_distance
// of 5 ms. This translates to a fudge room (2 x max_interbuffer_distance) of
// 10 ms.
NewCodedFrameGroupAppend("0K 5K 9D4K");
CheckExpectedRangesByTimestamp("{ [0,13) }");
// Trim off last 2 buffers, totaling 8 ms. Notably less than the current fudge
// room of 10 ms.
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(5)));
// Verify truncation.
CheckExpectedRangesByTimestamp("{ [0,5) }");
// Append new buffers just beyond the fudge-room allowance of 10ms.
AppendBuffers("11K 15K");
// Verify new append creates a gap.
CheckExpectedRangesByTimestamp("{ [0,5) [11,19) }");
}
TEST_P(SourceBufferStreamTest, RemoveWithinFudgeRoom) {
// This test requires specific relative proportions for fudge room, append
// size, and removal amounts. See details at:
// https://codereview.chromium.org/2385423002
// Append buffers with first buffer establishing max_inter_buffer_distance
// of 5 ms. This translates to a fudge room (2 x max_interbuffer_distance) of
// 10 ms.
NewCodedFrameGroupAppend("0K 5K 9D4K");
CheckExpectedRangesByTimestamp("{ [0,13) }");
// Trim off last 2 buffers, totaling 8 ms. Notably less than the current fudge
// room of 10 ms.
RemoveInMs(5, 13, 13);
// Verify removal.
CheckExpectedRangesByTimestamp("{ [0,5) }");
// Append new buffers just beyond the fudge-room allowance of 10ms.
AppendBuffers("11K 15K");
// Verify new append creates a gap.
CheckExpectedRangesByTimestamp("{ [0,5) [11,19) }");
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration_DeletePartialRange) {
// Append IPBBB GOPs into 3 discontinuous ranges.
NewCodedFrameGroupAppend("0K 40|10 10|20 20|30 30|40");
NewCodedFrameGroupAppend(
"100K 140|110 110|120 120|130 130|140 "
"150K 190|160 160|170 170|180 180|190");
NewCodedFrameGroupAppend("250K 290|260 260|270 270|280 280|290");
// Check expected ranges.
CheckExpectedRangesByTimestamp("{ [0,50) [100,200) [250,300) }");
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(140)));
if (buffering_api_ == BufferingApi::kLegacyByDts) {
// Should truncate the data after 140ms.
CheckExpectedRangesByTimestamp("{ [0,50) [100,140) }");
} else {
// The B-frames at PTS 110-130 were in the GOP in decode order after
// the simulated P-frame at PTS 140 which was truncated, so those B-frames
// are also removed.
CheckExpectedRangesByTimestamp("{ [0,50) [100,110) }");
}
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration_DeleteSelectedRange) {
// Append 3 discontinuous partial GOPs.
NewCodedFrameGroupAppend("50K 90|60");
NewCodedFrameGroupAppend("150K 190|160");
NewCodedFrameGroupAppend("250K 290|260");
// Check expected ranges.
if (buffering_api_ == BufferingApi::kLegacyByDts)
CheckExpectedRangesByTimestamp("{ [50,70) [150,170) [250,270) }");
else
CheckExpectedRangesByTimestamp("{ [50,100) [150,200) [250,300) }");
SeekToTimestampMs(150);
// Set duration to 50ms.
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(50)));
// Expect everything to be deleted, and should not have next buffer anymore.
CheckNoNextBuffer();
CheckExpectedRangesByTimestamp("{ }");
// Appending data 0ms through 250ms should not fulfill the seek.
// (If the duration is set to be something smaller than the current seek
// point, which had been 150ms, then the seek point is reset and the
// SourceBufferStream waits for a new seek request. Therefore even if the data
// is re-appended, it should not fulfill the old seek.)
NewCodedFrameGroupAppend("0K 50K 100K 150K 200K");
CheckNoNextBuffer();
CheckExpectedRangesByTimestamp("{ [0,250) }");
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration_DeletePartialSelectedRange) {
// Append 5 buffers at positions 0 through 4.
NewCodedFrameGroupAppend(0, 5);
// Append 20 buffers at positions 10 through 29.
NewCodedFrameGroupAppend(10, 20);
// Check expected ranges.
CheckExpectedRanges("{ [0,4) [10,29) }");
// Seek to position 10.
Seek(10);
// Set duration to be between buffers 24 and 25.
STREAM_OP(OnSetDuration(frame_duration() * 25));
// Should truncate the data after 24.
CheckExpectedRanges("{ [0,4) [10,24) }");
// The seek position should not be lost.
CheckExpectedBuffers(10, 10);
// Now set the duration immediately after buffer 10.
STREAM_OP(OnSetDuration(frame_duration() * 11));
// Seek position should be reset.
CheckNoNextBuffer();
CheckExpectedRanges("{ [0,4) [10,10) }");
}
// Test the case where duration is set while the stream parser buffers
// already start passing the data to decoding pipeline. Selected range,
// when invalidated by getting truncated, should be updated to NULL
// accordingly so that successive append operations keep working.
TEST_P(SourceBufferStreamTest, SetExplicitDuration_UpdateSelectedRange) {
// Seek to start of stream.
SeekToTimestampMs(0);
NewCodedFrameGroupAppend("0K 30 60 90");
// Read out the first few buffers.
CheckExpectedBuffers("0K 30");
// Set duration to be right before buffer 1.
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(60)));
// Verify that there is no next buffer.
CheckNoNextBuffer();
// We should be able to append new buffers at this point.
NewCodedFrameGroupAppend("120K 150");
CheckExpectedRangesByTimestamp("{ [0,60) [120,180) }");
}
TEST_P(SourceBufferStreamTest,
SetExplicitDuration_AfterGroupTimestampAndBeforeFirstBufferTimestamp) {
NewCodedFrameGroupAppend("0K 30K 60K");
// Append a coded frame group with a start timestamp of 200, but the first
// buffer starts at 230ms. This can happen in muxed content where the
// audio starts before the first frame.
NewCodedFrameGroupAppend(base::TimeDelta::FromMilliseconds(200),
"230K 260K 290K 320K");
NewCodedFrameGroupAppend("400K 430K 460K");
CheckExpectedRangesByTimestamp("{ [0,90) [200,350) [400,490) }");
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(120)));
// Verify that the buffered ranges are updated properly and we don't crash.
CheckExpectedRangesByTimestamp("{ [0,90) }");
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration_MarkEOS) {
// Append 1 full and 1 partial GOP: IPBBBIPBB
NewCodedFrameGroupAppend(
"0K 40|10 10|20 20|30 30|40 "
"50K 90|60 60|70 70|80");
// Check expected ranges.
if (buffering_api_ == BufferingApi::kLegacyByDts)
CheckExpectedRangesByTimestamp("{ [0,90) }");
else
CheckExpectedRangesByTimestamp("{ [0,100) }");
SeekToTimestampMs(50);
// Set duration to be before the seeked to position.
// This will result in truncation of the selected range and a reset
// of NextBufferPosition.
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(40)));
// Check the expected ranges.
if (buffering_api_ == BufferingApi::kLegacyByDts) {
CheckExpectedRangesByTimestamp("{ [0,40) }");
} else {
// The P-frame at PTS 40ms was removed, so its dependent
// B-frames at PTS 10-30 were also removed.
CheckExpectedRangesByTimestamp("{ [0,10) }");
}
// Mark EOS reached.
STREAM_OP(MarkEndOfStream());
// Expect EOS to be reached.
CheckEOSReached();
}
TEST_P(SourceBufferStreamTest, SetExplicitDuration_MarkEOS_IsSeekPending) {
// Append 1 full and 1 partial GOP: IPBBBIPBB
NewCodedFrameGroupAppend(
"0K 40|10 10|20 20|30 30|40 "
"50K 90|60 60|70 70|80");
// Check expected ranges.
if (buffering_api_ == BufferingApi::kLegacyByDts)
CheckExpectedRangesByTimestamp("{ [0,90) }");
else
CheckExpectedRangesByTimestamp("{ [0,100) }");
// Seek to 100ms will result in a pending seek.
SeekToTimestampMs(100);
// Set duration to be before the seeked to position.
// This will result in truncation of the selected range and a reset
// of NextBufferPosition.
STREAM_OP(OnSetDuration(base::TimeDelta::FromMilliseconds(40)));
// Check the expected ranges.
if (buffering_api_ == BufferingApi::kLegacyByDts) {
CheckExpectedRangesByTimestamp("{ [0,40) }");
} else {
// The P-frame at PTS 40ms was removed, so its dependent
// B-frames at PTS 10-30 were also removed.
CheckExpectedRangesByTimestamp("{ [0,10) }");
}
EXPECT_TRUE(STREAM_OP(IsSeekPending()));
// Mark EOS reached.
STREAM_OP(MarkEndOfStream());
EXPECT_FALSE(STREAM_OP(IsSeekPending()));
}
// Test the case were the current playback position is at the end of the
// buffered data and several overlaps occur.
TEST_P(SourceBufferStreamTest, OverlapWhileWaitingForMoreData) {
// Seek to start of stream.
SeekToTimestampMs(0);
NewCodedFrameGroupAppend("0K 30 60 90 120K 150");
CheckExpectedRangesByTimestamp("{ [0,180) }");
// Read all the buffered data.
CheckExpectedBuffers("0K 30 60 90 120K 150");
CheckNoNextBuffer();
// Append data over the current GOP so that a keyframe is needed before
// playback can continue from the current position.
NewCodedFrameGroupAppend("120K 150");
CheckExpectedRangesByTimestamp("{ [0,180) }");
// Append buffers that replace the first GOP with a partial GOP.
NewCodedFrameGroupAppend("0K 30");
CheckExpectedRangesByTimestamp("{ [0,180) }");
// Append buffers that complete that partial GOP.
AppendBuffers("60 90");
CheckExpectedRangesByTimestamp("{ [0,180) }");
// Verify that we still don't have a next buffer.
CheckNoNextBuffer();
// Add more data to the end and verify that this new data is read correctly.
NewCodedFrameGroupAppend("180K 210");
CheckExpectedRangesByTimestamp("{ [0,240) }");
CheckExpectedBuffers("180K 210");
CheckNoNextBuffer();
}
// Verify that a single coded frame at the current read position unblocks the
// read even if the frame is buffered after the previously read position is
// removed.
TEST_P(SourceBufferStreamTest, AfterRemove_SingleFrameRange_Unblocks_Read) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90D30");
CheckExpectedRangesByTimestamp("{ [0,120) }");
CheckExpectedBuffers("0K 30 60 90");
CheckNoNextBuffer();
RemoveInMs(0, 120, 120);
CheckExpectedRangesByTimestamp("{ }");
NewCodedFrameGroupAppend("120D30K");
CheckExpectedRangesByTimestamp("{ [120,150) }");
CheckExpectedBuffers("120K");
CheckNoNextBuffer();
}
// Verify that multiple short (relative to max-inter-buffer-distance * 2) coded
// frames at the current read position unblock the read even if the frames are
// buffered after the previously read position is removed.
TEST_P(SourceBufferStreamTest, AfterRemove_TinyFrames_Unblock_Read_1) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90D30");
CheckExpectedRangesByTimestamp("{ [0,120) }");
CheckExpectedBuffers("0K 30 60 90");
CheckNoNextBuffer();
RemoveInMs(0, 120, 120);
CheckExpectedRangesByTimestamp("{ }");
NewCodedFrameGroupAppend("120D1K 121D1");
CheckExpectedRangesByTimestamp("{ [120,122) }");
CheckExpectedBuffers("120K 121");
CheckNoNextBuffer();
}
// Verify that multiple short (relative to max-inter-buffer-distance * 2) coded
// frames starting at the fudge room boundary unblock the read even if the
// frames are buffered after the previously read position is removed.
TEST_P(SourceBufferStreamTest, AfterRemove_TinyFrames_Unblock_Read_2) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90D30");
CheckExpectedRangesByTimestamp("{ [0,120) }");
CheckExpectedBuffers("0K 30 60 90");
CheckNoNextBuffer();
RemoveInMs(0, 120, 120);
CheckExpectedRangesByTimestamp("{ }");
NewCodedFrameGroupAppend("150D1K 151D1");
CheckExpectedRangesByTimestamp("{ [150,152) }");
CheckExpectedBuffers("150K 151");
CheckNoNextBuffer();
}
// Verify that coded frames starting after the fudge room boundary do not
// unblock the read when buffered after the previously read position is removed.
TEST_P(SourceBufferStreamTest, AfterRemove_BeyondFudge_Stalled) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90D30");
CheckExpectedRangesByTimestamp("{ [0,120) }");
CheckExpectedBuffers("0K 30 60 90");
CheckNoNextBuffer();
RemoveInMs(0, 120, 120);
CheckExpectedRangesByTimestamp("{ }");
NewCodedFrameGroupAppend("151D1K 152D1");
CheckExpectedRangesByTimestamp("{ [151,153) }");
CheckNoNextBuffer();
}
// Verify that non-keyframes with the same timestamp in the same
// append are handled correctly.
TEST_P(SourceBufferStreamTest, SameTimestamp_Video_SingleAppend) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 30 60 90 120K 150");
CheckExpectedBuffers("0K 30 30 60 90 120K 150");
}
// Verify that non-keyframes with the same timestamp can occur
// in different appends.
TEST_P(SourceBufferStreamTest, SameTimestamp_Video_TwoAppends) {
Seek(0);
NewCodedFrameGroupAppend("0K 30D0");
AppendBuffers("30 60 90 120K 150");
CheckExpectedBuffers("0K 30 30 60 90 120K 150");
}
// Verify that a non-keyframe followed by a keyframe with the same timestamp
// is allowed, but also results in a MediaLog.
TEST_P(SourceBufferStreamTest, SameTimestamp_Video_SingleAppend_Warning) {
EXPECT_MEDIA_LOG(ContainsSameTimestampAt30MillisecondsLog());
Seek(0);
NewCodedFrameGroupAppend("0K 30 30K 60");
CheckExpectedBuffers("0K 30 30K 60");
}
TEST_P(SourceBufferStreamTest, SameTimestamp_Video_TwoAppends_Warning) {
EXPECT_MEDIA_LOG(ContainsSameTimestampAt30MillisecondsLog());
Seek(0);
NewCodedFrameGroupAppend("0K 30D0");
AppendBuffers("30K 60");
CheckExpectedBuffers("0K 30 30K 60");
}
// Verify that a keyframe followed by a non-keyframe with the same timestamp
// is allowed.
TEST_P(SourceBufferStreamTest, SameTimestamp_VideoKeyFrame_TwoAppends) {
Seek(0);
NewCodedFrameGroupAppend("0K 30D0K");
AppendBuffers("30 60");
CheckExpectedBuffers("0K 30K 30 60");
}
TEST_P(SourceBufferStreamTest, SameTimestamp_VideoKeyFrame_SingleAppend) {
Seek(0);
NewCodedFrameGroupAppend("0K 30K 30 60");
CheckExpectedBuffers("0K 30K 30 60");
}
TEST_P(SourceBufferStreamTest, SameTimestamp_Video_Overlap_1) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 60 90 120K 150");
NewCodedFrameGroupAppend("60K 91 121K 151");
CheckExpectedBuffers("0K 30 60K 91 121K 151");
}
TEST_P(SourceBufferStreamTest, SameTimestamp_Video_Overlap_2) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 60 90 120K 150");
NewCodedFrameGroupAppend("0K 30 61");
CheckExpectedBuffers("0K 30 61 120K 150");
}
TEST_P(SourceBufferStreamTest, SameTimestamp_Video_Overlap_3) {
Seek(0);
NewCodedFrameGroupAppend("0K 20 40 60 80 100K 101 102 103K");
NewCodedFrameGroupAppend("0K 20 40 60 80 90D0");
CheckExpectedBuffers("0K 20 40 60 80 90 100K 101 102 103K");
AppendBuffers("90 110K 150");
Seek(0);
CheckExpectedBuffers("0K 20 40 60 80 90 90 110K 150");
CheckNoNextBuffer();
CheckExpectedRangesByTimestamp("{ [0,190) }");
}
// Test all the valid same timestamp cases for audio.
TEST_P(SourceBufferStreamTest, SameTimestamp_Audio) {
AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO,
44100, EmptyExtraData(), Unencrypted());
STREAM_RESET(config);
Seek(0);
NewCodedFrameGroupAppend("0K 0K 30K 30 60 60");
CheckExpectedBuffers("0K 0K 30K 30 60 60");
}
TEST_P(SourceBufferStreamTest, SameTimestamp_Audio_SingleAppend_Warning) {
EXPECT_MEDIA_LOG(ContainsSameTimestampAt30MillisecondsLog());
AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO,
44100, EmptyExtraData(), Unencrypted());
STREAM_RESET(config);
Seek(0);
// Note, in reality, a non-keyframe audio frame is rare or perhaps not
// possible.
NewCodedFrameGroupAppend("0K 30 30K 60");
CheckExpectedBuffers("0K 30 30K 60");
}
// If seeking past any existing range and the seek is pending
// because no data has been provided for that position,
// the stream position can be considered as the end of stream.
TEST_P(SourceBufferStreamTest, EndSelected_During_PendingSeek) {
// Append 15 buffers at positions 0 through 14.
NewCodedFrameGroupAppend(0, 15);
Seek(20);
EXPECT_TRUE(STREAM_OP(IsSeekPending()));
STREAM_OP(MarkEndOfStream());
EXPECT_FALSE(STREAM_OP(IsSeekPending()));
}
// If there is a pending seek between 2 existing ranges,
// the end of the stream has not been reached.
TEST_P(SourceBufferStreamTest, EndNotSelected_During_PendingSeek) {
// Append:
// - 10 buffers at positions 0 through 9.
// - 10 buffers at positions 30 through 39
NewCodedFrameGroupAppend(0, 10);
NewCodedFrameGroupAppend(30, 10);
Seek(20);
EXPECT_TRUE(STREAM_OP(IsSeekPending()));
STREAM_OP(MarkEndOfStream());
EXPECT_TRUE(STREAM_OP(IsSeekPending()));
}
// Removing exact start & end of a range.
TEST_P(SourceBufferStreamTest, Remove_WholeRange1) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
RemoveInMs(10, 160, 160);
CheckExpectedRangesByTimestamp("{ }");
}
// Removal range starts before range and ends exactly at end.
TEST_P(SourceBufferStreamTest, Remove_WholeRange2) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
RemoveInMs(0, 160, 160);
CheckExpectedRangesByTimestamp("{ }");
}
// Removal range starts at the start of a range and ends beyond the
// range end.
TEST_P(SourceBufferStreamTest, Remove_WholeRange3) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
RemoveInMs(10, 200, 200);
CheckExpectedRangesByTimestamp("{ }");
}
// Removal range starts before range start and ends after the range end.
TEST_P(SourceBufferStreamTest, Remove_WholeRange4) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
RemoveInMs(0, 200, 200);
CheckExpectedRangesByTimestamp("{ }");
}
// Removes multiple ranges.
TEST_P(SourceBufferStreamTest, Remove_WholeRange5) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
NewCodedFrameGroupAppend("1000K 1030 1060K 1090 1120K");
NewCodedFrameGroupAppend("2000K 2030 2060K 2090 2120K");
CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) [2000,2150) }");
RemoveInMs(10, 3000, 3000);
CheckExpectedRangesByTimestamp("{ }");
}
// Verifies a [0-infinity) range removes everything.
TEST_P(SourceBufferStreamTest, Remove_ZeroToInfinity) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
NewCodedFrameGroupAppend("1000K 1030 1060K 1090 1120K");
NewCodedFrameGroupAppend("2000K 2030 2060K 2090 2120K");
CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) [2000,2150) }");
Remove(base::TimeDelta(), kInfiniteDuration, kInfiniteDuration);
CheckExpectedRangesByTimestamp("{ }");
}
// Removal range starts at the beginning of the range and ends in the
// middle of the range. This test verifies that full GOPs are removed.
TEST_P(SourceBufferStreamTest, Remove_Partial1) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
NewCodedFrameGroupAppend("1000K 1030 1060K 1090 1120K");
CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) }");
RemoveInMs(0, 80, 2200);
CheckExpectedRangesByTimestamp("{ [130,160) [1000,1150) }");
}
// Removal range starts in the middle of a range and ends at the exact
// end of the range.
TEST_P(SourceBufferStreamTest, Remove_Partial2) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
NewCodedFrameGroupAppend("1000K 1030 1060K 1090 1120K");
CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) }");
RemoveInMs(40, 160, 2200);
CheckExpectedRangesByTimestamp("{ [10,40) [1000,1150) }");
}
// Removal range starts and ends within a range.
TEST_P(SourceBufferStreamTest, Remove_Partial3) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
NewCodedFrameGroupAppend("1000K 1030 1060K 1090 1120K");
CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) }");
RemoveInMs(40, 120, 2200);
CheckExpectedRangesByTimestamp("{ [10,40) [130,160) [1000,1150) }");
}
// Removal range starts in the middle of one range and ends in the
// middle of another range.
TEST_P(SourceBufferStreamTest, Remove_Partial4) {
Seek(0);
NewCodedFrameGroupAppend("10K 40 70K 100 130K");
NewCodedFrameGroupAppend("1000K 1030 1060K 1090 1120K");
NewCodedFrameGroupAppend("2000K 2030 2060K 2090 2120K");
CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) [2000,2150) }");
RemoveInMs(40, 2030, 2200);
CheckExpectedRangesByTimestamp("{ [10,40) [2060,2150) }");
}
// Test behavior when the current position is removed and new buffers
// are appended over the removal range.
TEST_P(SourceBufferStreamTest, Remove_CurrentPosition) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90K 120 150 180K 210 240 270K 300 330");
CheckExpectedRangesByTimestamp("{ [0,360) }");
CheckExpectedBuffers("0K 30 60 90K 120");
// Remove a range that includes the next buffer (i.e., 150).
RemoveInMs(150, 210, 360);
CheckExpectedRangesByTimestamp("{ [0,150) [270,360) }");
// Verify that no next buffer is returned.
CheckNoNextBuffer();
// Append some buffers to fill the gap that was created.
NewCodedFrameGroupAppend("120K 150 180 210K 240");
CheckExpectedRangesByTimestamp("{ [0,360) }");
// Verify that buffers resume at the next keyframe after the
// current position.
CheckExpectedBuffers("210K 240 270K 300 330");
}
// Test behavior when buffers in the selected range before the current position
// are removed.
TEST_P(SourceBufferStreamTest, Remove_BeforeCurrentPosition) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90K 120 150 180K 210 240 270K 300 330");
CheckExpectedRangesByTimestamp("{ [0,360) }");
CheckExpectedBuffers("0K 30 60 90K 120");
// Remove a range that is before the current playback position.
RemoveInMs(0, 90, 360);
CheckExpectedRangesByTimestamp("{ [90,360) }");
CheckExpectedBuffers("150 180K 210 240 270K 300 330");
}
// Test removing the preliminary portion for the current coded frame group being
// appended.
TEST_P(SourceBufferStreamTest, Remove_MidGroup) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90 120K 150 180 210");
CheckExpectedRangesByTimestamp("{ [0,240) }");
// Partially replace the first GOP, then read its keyframe.
NewCodedFrameGroupAppend("0K 30");
CheckExpectedBuffers("0K");
CheckExpectedRangesByTimestamp("{ [0,240) }");
// Remove the partial GOP that we're in the middle of reading.
RemoveInMs(0, 60, 240);
// Verify that there is no next buffer since it was removed and the remaining
// buffered range is beyond the current position.
CheckNoNextBuffer();
CheckExpectedRangesByTimestamp("{ [120,240) }");
// Continue appending frames for the current GOP.
AppendBuffers("60 90");
// Verify that the non-keyframes are not added.
CheckExpectedRangesByTimestamp("{ [120,240) }");
// Finish the previous GOP and start the next one.
AppendBuffers("120 150K 180");
// Verify that new GOP replaces the existing GOP.
CheckExpectedRangesByTimestamp("{ [150,210) }");
SeekToTimestampMs(150);
CheckExpectedBuffers("150K 180");
CheckNoNextBuffer();
}
// Test removing the current GOP being appended, while not removing
// the entire range the GOP belongs to.
TEST_P(SourceBufferStreamTest, Remove_GOPBeingAppended) {
Seek(0);
NewCodedFrameGroupAppend("0K 30 60 90 120K 150 180");
CheckExpectedRangesByTimestamp("{ [0,210) }");
// Remove the current GOP being appended.
RemoveInMs(120, 150, 240);
CheckExpectedRangesByTimestamp("{ [0,120) }");
// Continue appending the current GOP and the next one.
AppendBuffers("210 240K 270 300");
// Verify that the non-keyframe in the previous GOP does
// not effect any existing ranges and a new range is started at the
// beginning of the next GOP.
CheckExpectedRangesByTimestamp("{ [0,120) [240,330) }");
// Verify the buffers in the ranges.
CheckExpectedBuffers("0K 30 60 90");
CheckNoNextBuffer();
SeekToTimestampMs(240);
CheckExpectedBuffers("240K 270 300");
}
TEST_P(SourceBufferStreamTest, Remove_WholeGOPBeingAppended) {
SeekToTimestampMs(1000);
NewCodedFrameGroupAppend("1000K 1030 1060 1090");
CheckExpectedRangesByTimestamp("{ [1000,1120) }");
// Remove the keyframe of the current GOP being appended.
RemoveInMs(1000, 1030, 1120);
CheckExpectedRangesByTimestamp("{ }");
// Continue appending the current GOP.
AppendBuffers("1210 1240");
CheckExpectedRangesByTimestamp("{ }");
// Append the beginning of the next GOP.
AppendBuffers("1270K 1300");
// Verify that the new range is started at the
// beginning of the next GOP.
CheckExpectedRangesByTimestamp("{ [1270,1330) }");
// Verify the buffers in the ranges.
CheckNoNextBuffer();
SeekToTimestampMs(1270);
CheckExpectedBuffers("1270K 1300");
}
TEST_P(SourceBufferStreamTest,
Remove_PreviousAppendDestroyedAndOverwriteExistingRange) {
SeekToTimestampMs(90);
NewCodedFrameGroupAppend("90K 120 150");
CheckExpectedRangesByTimestamp("{ [90,180) }");
// Append a coded frame group before the previously appended data.
NewCodedFrameGroupAppend("0K 30 60");
// Verify that the ranges get merged.
CheckExpectedRangesByTimestamp("{ [0,180) }");
// Remove the data from the last append.
RemoveInMs(0, 90, 360);
CheckExpectedRangesByTimestamp("{ [90,180) }");
// Append a new coded frame group that follows the removed group and
// starts at the beginning of the range left over from the
// remove.
NewCodedFrameGroupAppend("90K 121 151");
CheckExpectedBuffers("90K 121 151");
}
TEST_P(SourceBufferStreamTest, Remove_GapAtBeginningOfGroup) {
Seek(0);
// Append a coded frame group that has a gap at the beginning of it.
NewCodedFrameGroupAppend(base::TimeDelta::FromMilliseconds(0),
"30K 60 90 120K 150");
CheckExpectedRangesByTimestamp("{ [0,180) }");
// Remove the gap that doesn't contain any buffers.
RemoveInMs(0, 10, 180);
CheckExpectedRangesByTimestamp("{ [10,180) }");
// Verify we still get the first buffer still since only part of
// the gap was removed.
// TODO(acolwell/wolenetz): Consider not returning a buffer at this
// point since the current seek position has been explicitly
// removed but didn't happen to remove any buffers.
// http://crbug.com/384016
CheckExpectedBuffers("30K");
// Remove a range that includes the first GOP.
RemoveInMs(0, 60, 180);
// Verify that no buffer is returned because the current buffer
// position has been removed.
CheckNoNextBuffer();
CheckExpectedRangesByTimestamp("{ [120,180) }");
}
TEST_P(SourceBufferStreamTest,
OverlappingAppendRangeMembership_OneMicrosecond_Video) {
NewCodedFrameGroupAppend("10D20K");
CheckExpectedRangesByTimestamp("{ [10000,30000) }",
TimeGranularity::kMicrosecond);
// Append a buffer 1 microsecond earlier, with estimated duration.
NewCodedFrameGroupAppend("9999uD20EK");
CheckExpectedRangesByTimestamp("{ [9999,30000) }",
TimeGranularity::kMicrosecond);
// Append that same buffer again, but without any discontinuity signalled / no
// new coded frame group.
AppendBuffers("9999uD20EK");
CheckExpectedRangesByTimestamp("{ [9999,30000) }",
TimeGranularity::kMicrosecond);
Seek(0);
CheckExpectedBuffers("9999K 9999K 10000K", TimeGranularity::kMicrosecond);
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
OverlappingAppendRangeMembership_TwoMicroseconds_Video) {
NewCodedFrameGroupAppend("10D20K");
CheckExpectedRangesByTimestamp("{ [10000,30000) }",
TimeGranularity::kMicrosecond);
// Append an exactly abutting buffer 2us earlier.
NewCodedFrameGroupAppend("9998uD20EK");
CheckExpectedRangesByTimestamp("{ [9998,30000) }",
TimeGranularity::kMicrosecond);
// Append that same buffer again, but without any discontinuity signalled / no
// new coded frame group.
AppendBuffers("9998uD20EK");
CheckExpectedRangesByTimestamp("{ [9998,30000) }",
TimeGranularity::kMicrosecond);
Seek(0);
CheckExpectedBuffers("9998K 9998K 10000K", TimeGranularity::kMicrosecond);
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Text_Append_SingleRange) {
SetTextStream();
NewCodedFrameGroupAppend("0K 500K 1000K");
CheckExpectedRangesByTimestamp("{ [0,1500) }");
Seek(0);
CheckExpectedBuffers("0K 500K 1000K");
}
TEST_P(SourceBufferStreamTest, Text_Append_DisjointAfter) {
SetTextStream();
NewCodedFrameGroupAppend("0K 500K 1000K");
CheckExpectedRangesByTimestamp("{ [0,1500) }");
NewCodedFrameGroupAppend("3000K 3500K 4000K");
CheckExpectedRangesByTimestamp("{ [0,4500) }");
Seek(0);
CheckExpectedBuffers("0K 500K 1000K 3000K 3500K 4000K");
}
TEST_P(SourceBufferStreamTest, Text_Append_DisjointBefore) {
SetTextStream();
NewCodedFrameGroupAppend("3000K 3500K 4000K");
CheckExpectedRangesByTimestamp("{ [3000,4500) }");
NewCodedFrameGroupAppend("0K 500K 1000K");
CheckExpectedRangesByTimestamp("{ [0,4500) }");
Seek(0);
CheckExpectedBuffers("0K 500K 1000K 3000K 3500K 4000K");
}
TEST_P(SourceBufferStreamTest, Text_CompleteOverlap) {
SetTextStream();
NewCodedFrameGroupAppend("3000K 3500K 4000K");
CheckExpectedRangesByTimestamp("{ [3000,4500) }");
NewCodedFrameGroupAppend(
"0K 501K 1001K 1501K 2001K 2501K "
"3001K 3501K 4001K 4501K 5001K");
CheckExpectedRangesByTimestamp("{ [0,5501) }");
Seek(0);
CheckExpectedBuffers("0K 501K 1001K 1501K 2001K 2501K "
"3001K 3501K 4001K 4501K 5001K");
}
TEST_P(SourceBufferStreamTest, Text_OverlapAfter) {
SetTextStream();
NewCodedFrameGroupAppend("0K 500K 1000K 1500K 2000K");
CheckExpectedRangesByTimestamp("{ [0,2500) }");
NewCodedFrameGroupAppend("1499K 2001K 2501K 3001K");
CheckExpectedRangesByTimestamp("{ [0,3501) }");
Seek(0);
CheckExpectedBuffers("0K 500K 1000K 1499K 2001K 2501K 3001K");
}
TEST_P(SourceBufferStreamTest, Text_OverlapBefore) {
SetTextStream();
NewCodedFrameGroupAppend("1500K 2000K 2500K 3000K 3500K");
CheckExpectedRangesByTimestamp("{ [1500,4000) }");
NewCodedFrameGroupAppend("0K 501K 1001K 1501K 2001K");
CheckExpectedRangesByTimestamp("{ [0,4000) }");
Seek(0);
CheckExpectedBuffers("0K 501K 1001K 1501K 2001K 3000K 3500K");
}
TEST_P(SourceBufferStreamTest, Audio_SpliceTrimmingForOverlap) {
SetAudioStream();
Seek(0);
NewCodedFrameGroupAppend("0K 2K 4K 6K 8K 10K 12K");
CheckExpectedRangesByTimestamp("{ [0,14) }");
// Note that duration of frame at time 10 is verified to be 2 ms.
CheckExpectedBuffers("0K 2K 4K 6K 8K 10D2K 12K");
CheckNoNextBuffer();
// Append new group with front slightly overlapping existing buffer at 10ms.
EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(11000, 10000, 1000));
NewCodedFrameGroupAppend("11K 13K 15K 17K");
// Cross-fade splicing is no longer implemented. Instead we should expect
// wholly overlapped buffers to be removed (12K). If a buffer is partially
// overlapped (e.g. last millisecond of 10K), the existing buffer should be
// trimmed to perfectly abut the newly appended buffers.
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,19) }");
CheckExpectedBuffers("0K 2K 4K 6K 8K 10D1K 11D2K 13K 15K 17K");
CheckNoNextBuffer();
}
// Test that a splice is not created if an end timestamp and start timestamp
// perfectly overlap.
TEST_P(SourceBufferStreamTest, Audio_SpliceFrame_NoSplice) {
SetAudioStream();
Seek(0);
// Add 10 frames across 2 *non-overlapping* appends.
NewCodedFrameGroupAppend("0K 2K 4K 6K 8K 10K");
NewCodedFrameGroupAppend("12K 14K 16K 18K");
// Manually inspect the buffers at the no-splice boundary to verify duration
// and lack of discard padding (set when splicing).
scoped_refptr<StreamParserBuffer> buffer;
const DecoderBuffer::DiscardPadding kEmptyDiscardPadding;
for (int i = 0; i < 10; i++) {
// Verify buffer timestamps and durations are preserved and no buffers have
// discard padding (indicating no splice trimming).
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&buffer));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(i * 2), buffer->timestamp());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(2), buffer->duration());
EXPECT_EQ(kEmptyDiscardPadding, buffer->discard_padding());
}
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Audio_NoSpliceForBadOverlap) {
SetAudioStream();
Seek(0);
// Add 2 frames with matching PTS and ov, where the duration of the first
// frame suggests that it overlaps the second frame. The overlap is within a
// coded frame group (bad content), so no splicing is expected.
NewCodedFrameGroupAppend("0D10K 0D10K");
CheckExpectedRangesByTimestamp("{ [0,10) }");
CheckExpectedBuffers("0D10K 0D10K");
CheckNoNextBuffer();
Seek(0);
// Add a new frame in a separate coded frame group that falls into the
// overlap of the two existing frames. Splicing should not be performed since
// the content is poorly muxed. We can't know which frame to splice when the
// content is already messed up.
EXPECT_MEDIA_LOG(NoSpliceForBadMux(2, 2000));
NewCodedFrameGroupAppend("2D10K");
CheckExpectedRangesByTimestamp("{ [0,12) }");
CheckExpectedBuffers("0D10K 0D10K 2D10K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Audio_NoSpliceForEstimatedDuration) {
SetAudioStream();
Seek(0);
// Append two buffers, the latter having estimated duration.
NewCodedFrameGroupAppend("0D10K 10D10EK");
CheckExpectedRangesByTimestamp("{ [0,20) }");
CheckExpectedBuffers("0D10K 10D10EK");
CheckNoNextBuffer();
Seek(0);
// Add a new frame in a separate coded frame group that falls in the middle of
// the second buffer. In spite of the overlap, no splice should be performed
// due to the overlapped buffer having estimated duration.
NewCodedFrameGroupAppend("15D10K");
CheckExpectedRangesByTimestamp("{ [0,25) }");
CheckExpectedBuffers("0D10K 10D10EK 15D10K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Audio_SpliceTrimming_ExistingTrimming) {
const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(4);
const base::TimeDelta kNoDiscard = base::TimeDelta();
const bool is_keyframe = true;
SetAudioStream();
Seek(0);
// Make two BufferQueues with a mix of buffers containing start/end discard.
// Buffer PTS and duration have been adjusted to reflect discard. A_buffers
// will be appended first, then B_buffers. The start of B will overlap A
// to generate a splice.
BufferQueue A_buffers;
BufferQueue B_buffers;
// Buffer A1: PTS = 0, front discard = 2ms, duration = 2ms.
scoped_refptr<StreamParserBuffer> bufferA1 = StreamParserBuffer::CopyFrom(
&kDataA, kDataSize, is_keyframe, DemuxerStream::AUDIO, 0);
bufferA1->set_timestamp(base::TimeDelta::FromMilliseconds(0));
bufferA1->set_duration(kDuration / 2);
const DecoderBuffer::DiscardPadding discardA1 =
std::make_pair(kDuration / 2, kNoDiscard);
bufferA1->set_discard_padding(discardA1);
A_buffers.push_back(bufferA1);
// Buffer A2: PTS = 2, end discard = 2ms, duration = 2ms.
scoped_refptr<StreamParserBuffer> bufferA2 = StreamParserBuffer::CopyFrom(
&kDataA, kDataSize, is_keyframe, DemuxerStream::AUDIO, 0);
bufferA2->set_timestamp(base::TimeDelta::FromMilliseconds(2));
bufferA2->set_duration(kDuration / 2);
const DecoderBuffer::DiscardPadding discardA2 =
std::make_pair(kNoDiscard, kDuration / 2);
bufferA2->set_discard_padding(discardA2);
A_buffers.push_back(bufferA2);
// Buffer B1: PTS = 3, front discard = 2ms, duration = 2ms.
scoped_refptr<StreamParserBuffer> bufferB1 = StreamParserBuffer::CopyFrom(
&kDataA, kDataSize, is_keyframe, DemuxerStream::AUDIO, 0);
bufferB1->set_timestamp(base::TimeDelta::FromMilliseconds(3));
bufferB1->set_duration(kDuration / 2);
const DecoderBuffer::DiscardPadding discardB1 =
std::make_pair(kDuration / 2, kNoDiscard);
bufferB1->set_discard_padding(discardB1);
B_buffers.push_back(bufferB1);
// Buffer B2: PTS = 5, no discard padding, duration = 4ms.
scoped_refptr<StreamParserBuffer> bufferB2 = StreamParserBuffer::CopyFrom(
&kDataA, kDataSize, is_keyframe, DemuxerStream::AUDIO, 0);
bufferB2->set_timestamp(base::TimeDelta::FromMilliseconds(5));
bufferB2->set_duration(kDuration);
B_buffers.push_back(bufferB2);
// Append buffers, trigger splice trimming.
STREAM_OP(OnStartOfCodedFrameGroup(bufferA1->GetDecodeTimestamp(),
bufferA1->timestamp()));
STREAM_OP(Append(A_buffers));
EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(3000, 2000, 1000));
STREAM_OP(Append(B_buffers));
// Verify buffers.
scoped_refptr<StreamParserBuffer> read_buffer;
// Buffer A1 was not spliced, should be unchanged.
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&read_buffer));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(0), read_buffer->timestamp());
EXPECT_EQ(kDuration / 2, read_buffer->duration());
EXPECT_EQ(discardA1, read_buffer->discard_padding());
// Buffer A2 was overlapped by buffer B1 1ms. Splice trimming should trim A2's
// duration and increase its discard padding by 1ms.
const base::TimeDelta overlap = base::TimeDelta::FromMilliseconds(1);
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&read_buffer));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(2), read_buffer->timestamp());
EXPECT_EQ((kDuration / 2) - overlap, read_buffer->duration());
const DecoderBuffer::DiscardPadding overlap_discard =
std::make_pair(discardA2.first, discardA2.second + overlap);
EXPECT_EQ(overlap_discard, read_buffer->discard_padding());
// Buffer B1 is overlapping A2, but B1 should be unchanged - splice trimming
// only modifies the earlier buffer (A1).
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&read_buffer));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(3), read_buffer->timestamp());
EXPECT_EQ(kDuration / 2, read_buffer->duration());
EXPECT_EQ(discardB1, read_buffer->discard_padding());
// Buffer B2 is not spliced, should be unchanged.
EXPECT_STATUS_FOR_STREAM_OP(kSuccess, GetNextBuffer(&read_buffer));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(5), read_buffer->timestamp());
EXPECT_EQ(kDuration, read_buffer->duration());
EXPECT_EQ(std::make_pair(kNoDiscard, kNoDiscard),
read_buffer->discard_padding());
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Audio_SpliceFrame_NoMillisecondSplices) {
EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(1250, 250));
video_config_ = TestVideoConfig::Invalid();
audio_config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32,
CHANNEL_LAYOUT_STEREO, 4000, EmptyExtraData(),
Unencrypted(), base::TimeDelta(), 0);
STREAM_RESET(audio_config_);
// Equivalent to 0.5ms per frame.
SetStreamInfo(2000, 2000);
Seek(0);
// Append four buffers with a 0.5ms duration each.
NewCodedFrameGroupAppend(0, 4);
CheckExpectedRangesByTimestamp("{ [0,2) }");
// Overlap the range [0, 2) with [1.25, 2); this results in an overlap of
// 0.25ms between the original buffer at time 1.0 and the new buffer at time
// 1.25.
NewCodedFrameGroupAppend_OffsetFirstBuffer(
2, 2, base::TimeDelta::FromMillisecondsD(0.25));
CheckExpectedRangesByTimestamp("{ [0,2) }");
// A splice frame should not be generated since it requires at least 1ms of
// data to crossfade.
CheckExpectedBuffers("0K 0K 1K 1K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Audio_PrerollFrame) {
Seek(0);
NewCodedFrameGroupAppend("0K 3P 6K");
CheckExpectedBuffers("0K 3P 6K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Audio_ConfigChangeWithPreroll) {
AudioDecoderConfig new_config(kCodecVorbis, kSampleFormatPlanarF32,
CHANNEL_LAYOUT_MONO, 2000, EmptyExtraData(),
Unencrypted());
SetAudioStream();
Seek(0);
// Append some audio using the default configuration.
CheckAudioConfig(audio_config_);
NewCodedFrameGroupAppend("0K 3K 6K");
// Update the configuration.
STREAM_OP(UpdateAudioConfig(new_config, false));
// We haven't read any buffers at this point, so the config for the next
// buffer at time 0 should still be the original config.
CheckAudioConfig(audio_config_);
// Append new audio containing preroll and using the new config.
EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(7000, 6000, 2000));
NewCodedFrameGroupAppend("7P 8K");
// Check buffers from the first append.
CheckExpectedBuffers("0K 3K 6K");
// Verify the next attempt to get a buffer will signal that a config change
// has happened.
scoped_refptr<StreamParserBuffer> buffer;
EXPECT_STATUS_FOR_STREAM_OP(kConfigChange, GetNextBuffer(&buffer));
// Verify upcoming buffers will use the new config.
CheckAudioConfig(new_config);
// Check buffers from the second append, including preroll.
// CheckExpectedBuffers("6P 7K 8K");
CheckExpectedBuffers("7P 8K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, Audio_Opus_SeekToJustBeforeRangeStart) {
// Seek to a time within the fudge room of seekability to a buffered Opus
// audio frame's range, but before the range's start. Use small seek_preroll
// in case the associated logic to check same config in the preroll time
// interval requires a nonzero seek_preroll value.
video_config_ = TestVideoConfig::Invalid();
audio_config_.Initialize(kCodecOpus, kSampleFormatPlanarF32,
CHANNEL_LAYOUT_STEREO, 1000, EmptyExtraData(),
Unencrypted(), base::TimeDelta::FromMilliseconds(10),
0);
STREAM_RESET(audio_config_);
// Equivalent to 1s per frame.
SetStreamInfo(1, 1);
Seek(0);
// Append a buffer at 1.5 seconds, with duration 1 second, increasing the
// fudge room to 2 * 1 seconds. The pending seek to time 0 should be satisfied
// with this buffer's range, because that seek time is within the fudge room
// of 2.
NewCodedFrameGroupAppend("1500D1000K");
CheckExpectedRangesByTimestamp("{ [1500,2500) }");
CheckExpectedBuffers("1500K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, BFrames) {
Seek(0);
NewCodedFrameGroupAppend("0K 120|30 30|60 60|90 90|120");
CheckExpectedRangesByTimestamp("{ [0,150) }");
CheckExpectedBuffers("0K 120|30 30|60 60|90 90|120");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, RemoveShouldAlwaysExcludeEnd) {
NewCodedFrameGroupAppend("10D2K 12D2 14D2");
CheckExpectedRangesByTimestamp("{ [10,16) }");
// Start new coded frame group, appending KF to abut the start of previous
// group.
NewCodedFrameGroupAppend("0D10K");
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,16) }");
CheckExpectedBuffers("0K 10K 12 14");
CheckNoNextBuffer();
// Append another buffer with the same timestamp as the last KF. This triggers
// special logic that allows two buffers to have the same timestamp. When
// preparing for this new append, there is no reason to remove the later GOP
// starting at timestamp 10. This verifies the fix for http://crbug.com/469325
// where the decision *not* to remove the start of the overlapped range was
// erroneously triggering buffers with a timestamp matching the end
// of the append (and any later dependent frames) to be removed.
AppendBuffers("0D10");
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,16) }");
CheckExpectedBuffers("0K 0 10K 12 14");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, RefinedDurationEstimates_BackOverlap) {
// Append a few buffers, the last one having estimated duration.
NewCodedFrameGroupAppend("0K 5 10 20D10E");
CheckExpectedRangesByTimestamp("{ [0,30) }");
Seek(0);
CheckExpectedBuffers("0K 5 10 20D10E");
CheckNoNextBuffer();
// Append a buffer to the end that overlaps the *back* of the existing range.
// This should trigger the estimated duration to be recomputed as a timestamp
// delta.
AppendBuffers("25D10");
CheckExpectedRangesByTimestamp("{ [0,35) }");
Seek(0);
// The duration of the buffer at time 20 has changed from 10ms to 5ms.
CheckExpectedBuffers("0K 5 10 20D5E 25");
CheckNoNextBuffer();
// If the last buffer is removed, the adjusted duration should remain at 5ms.
RemoveInMs(25, 35, 35);
CheckExpectedRangesByTimestamp("{ [0,25) }");
Seek(0);
CheckExpectedBuffers("0K 5 10 20D5E");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, RefinedDurationEstimates_FrontOverlap) {
// Append a few buffers.
NewCodedFrameGroupAppend("10K 15 20D5");
CheckExpectedRangesByTimestamp("{ [10,25) }");
SeekToTimestampMs(10);
CheckExpectedBuffers("10K 15 20");
CheckNoNextBuffer();
// Append new buffers, where the last has estimated duration that overlaps the
// *front* of the existing range. The overlap should trigger refinement of the
// estimated duration from 7ms to 5ms.
NewCodedFrameGroupAppend("0K 5D7E");
CheckExpectedRangesByTimestamp("{ [0,25) }");
Seek(0);
CheckExpectedBuffers("0K 5D5E 10K 15 20");
CheckNoNextBuffer();
// If the overlapped buffer at timestamp 10 is removed, the adjusted duration
// should remain adjusted.
RemoveInMs(10, 20, 25);
CheckExpectedRangesByTimestamp("{ [0,10) }");
Seek(0);
CheckExpectedBuffers("0K 5D5E");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, SeekToStartSatisfiedUpToThreshold) {
NewCodedFrameGroupAppend("999K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [999,1030) }");
SeekToTimestampMs(0);
CheckExpectedBuffers("999K 1010 1020D10");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, SeekToStartUnsatisfiedBeyondThreshold) {
NewCodedFrameGroupAppend("1000K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [1000,1030) }");
SeekToTimestampMs(0);
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
ReSeekToStartSatisfiedUpToThreshold_SameTimestamps) {
// Append a few buffers.
NewCodedFrameGroupAppend("999K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [999,1030) }");
// Don't read any buffers between Seek and Remove.
SeekToTimestampMs(0);
RemoveInMs(999, 1030, 1030);
CheckExpectedRangesByTimestamp("{ }");
CheckNoNextBuffer();
// Append buffers at the original timestamps and verify no stall.
NewCodedFrameGroupAppend("999K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [999,1030) }");
CheckExpectedBuffers("999K 1010 1020D10");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
ReSeekToStartSatisfiedUpToThreshold_EarlierTimestamps) {
// Append a few buffers.
NewCodedFrameGroupAppend("999K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [999,1030) }");
// Don't read any buffers between Seek and Remove.
SeekToTimestampMs(0);
RemoveInMs(999, 1030, 1030);
CheckExpectedRangesByTimestamp("{ }");
CheckNoNextBuffer();
// Append buffers before the original timestamps and verify no stall (the
// re-seek to time 0 should still be satisfied with the new buffers).
NewCodedFrameGroupAppend("500K 510 520D10");
CheckExpectedRangesByTimestamp("{ [500,530) }");
CheckExpectedBuffers("500K 510 520D10");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
ReSeekToStartSatisfiedUpToThreshold_LaterTimestamps) {
// Append a few buffers.
NewCodedFrameGroupAppend("500K 510 520D10");
CheckExpectedRangesByTimestamp("{ [500,530) }");
// Don't read any buffers between Seek and Remove.
SeekToTimestampMs(0);
RemoveInMs(500, 530, 530);
CheckExpectedRangesByTimestamp("{ }");
CheckNoNextBuffer();
// Append buffers beginning after original timestamps, but still below the
// start threshold, and verify no stall (the re-seek to time 0 should still be
// satisfied with the new buffers).
NewCodedFrameGroupAppend("999K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [999,1030) }");
CheckExpectedBuffers("999K 1010 1020D10");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, ReSeekBeyondStartThreshold_SameTimestamps) {
// Append a few buffers.
NewCodedFrameGroupAppend("1000K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [1000,1030) }");
// Don't read any buffers between Seek and Remove.
SeekToTimestampMs(1000);
RemoveInMs(1000, 1030, 1030);
CheckExpectedRangesByTimestamp("{ }");
CheckNoNextBuffer();
// Append buffers at the original timestamps and verify no stall.
NewCodedFrameGroupAppend("1000K 1010 1020D10");
CheckExpectedRangesByTimestamp("{ [1000,1030) }");
CheckExpectedBuffers("1000K 1010 1020D10");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, ReSeekBeyondThreshold_EarlierTimestamps) {
// Append a few buffers.
NewCodedFrameGroupAppend("2000K 2010 2020D10");
CheckExpectedRangesByTimestamp("{ [2000,2030) }");
// Don't read any buffers between Seek and Remove.
SeekToTimestampMs(2000);
RemoveInMs(2000, 2030, 2030);
CheckExpectedRangesByTimestamp("{ }");
CheckNoNextBuffer();
// Append buffers before the original timestamps and verify no stall (the
// re-seek to time 2 seconds should still be satisfied with the new buffers
// and should emit preroll from last keyframe).
NewCodedFrameGroupAppend("1080K 1090 2000D10");
CheckExpectedRangesByTimestamp("{ [1080,2010) }");
CheckExpectedBuffers("1080K 1090 2000D10");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, ConfigChange_ReSeek) {
// Append a few buffers, with a config change in the middle.
VideoDecoderConfig new_config = TestVideoConfig::Large();
NewCodedFrameGroupAppend("2000K 2010 2020D10");
STREAM_OP(UpdateVideoConfig(new_config, false));
NewCodedFrameGroupAppend("2030K 2040 2050D10");
CheckExpectedRangesByTimestamp("{ [2000,2060) }");
// Read the config change, but don't read any non-config-change buffer between
// Seek and Remove.
scoped_refptr<StreamParserBuffer> buffer;
CheckVideoConfig(video_config_);
SeekToTimestampMs(2030);
CheckVideoConfig(video_config_);
EXPECT_STATUS_FOR_STREAM_OP(kConfigChange, GetNextBuffer(&buffer));
CheckVideoConfig(new_config);
// Trigger the re-seek.
RemoveInMs(2030, 2060, 2060);
CheckExpectedRangesByTimestamp("{ [2000,2030) }");
CheckNoNextBuffer();
// Append buffers at the original timestamps and verify no stall or redundant
// signalling of config change.
NewCodedFrameGroupAppend("2030K 2040 2050D10");
CheckVideoConfig(new_config);
CheckExpectedRangesByTimestamp("{ [2000,2060) }");
CheckExpectedBuffers("2030K 2040 2050D10");
CheckNoNextBuffer();
CheckVideoConfig(new_config);
// Seek to the start of buffered and verify config changes and buffers.
SeekToTimestampMs(2000);
CheckVideoConfig(new_config);
ASSERT_FALSE(new_config.Matches(video_config_));
EXPECT_STATUS_FOR_STREAM_OP(kConfigChange, GetNextBuffer(&buffer));
CheckVideoConfig(video_config_);
CheckExpectedBuffers("2000K 2010 2020D10");
CheckVideoConfig(video_config_);
EXPECT_STATUS_FOR_STREAM_OP(kConfigChange, GetNextBuffer(&buffer));
CheckVideoConfig(new_config);
CheckExpectedBuffers("2030K 2040 2050D10");
CheckNoNextBuffer();
CheckVideoConfig(new_config);
}
TEST_P(SourceBufferStreamTest, TrackBuffer_ExhaustionWithSkipForward) {
NewCodedFrameGroupAppend("0K 10 20 30 40");
// Read the first 4 buffers, so next buffer is at time 40.
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,50) }");
CheckExpectedBuffers("0K 10 20 30");
// Overlap-append, populating track buffer with timestamp 40 from original
// append. Confirm there could be a large jump in time until the next key
// frame after exhausting the track buffer.
NewCodedFrameGroupAppend(
"31K 41 51 61 71 81 91 101 111 121 "
"131K 141");
CheckExpectedRangesByTimestamp("{ [0,151) }");
// Confirm the large jump occurs and warning log is generated.
// If this test is changed, update
// TrackBufferExhaustion_ImmediateNewTrackBuffer accordingly.
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(91));
CheckExpectedBuffers("40 131K 141");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
TrackBuffer_ExhaustionAndImmediateNewTrackBuffer) {
NewCodedFrameGroupAppend("0K 10 20 30 40");
// Read the first 4 buffers, so next buffer is at time 40.
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,50) }");
CheckExpectedBuffers("0K 10 20 30");
// Overlap-append
NewCodedFrameGroupAppend(
"31K 41 51 61 71 81 91 101 111 121 "
"131K 141");
CheckExpectedRangesByTimestamp("{ [0,151) }");
// Exhaust the track buffer, but don't read any of the overlapping append yet.
CheckExpectedBuffers("40");
// Selected range's next buffer is now the 131K buffer from the overlapping
// append. (See TrackBuffer_ExhaustionWithSkipForward for that verification.)
// Do another overlap-append to immediately create another track buffer and
// verify both track buffer exhaustions skip forward and emit log warnings.
NewCodedFrameGroupAppend(
"22K 32 42 52 62 72 82 92 102 112 122K 132 142 152K 162");
CheckExpectedRangesByTimestamp("{ [0,172) }");
InSequence s;
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(91));
EXPECT_MEDIA_LOG(ContainsTrackBufferExhaustionSkipLog(11));
CheckExpectedBuffers("131K 141 152K 162");
CheckNoNextBuffer();
}
TEST_P(
SourceBufferStreamTest,
AdjacentCodedFrameGroupContinuation_NoGapCreatedByTinyGapInGroupContinuation) {
NewCodedFrameGroupAppend("0K 10 20K 30 40K 50D10");
CheckExpectedRangesByTimestamp("{ [0,60) }");
// Continue appending to the previously started coded frame group, albeit with
// a tiny (1ms) gap. This gap should *NOT* produce a buffered range gap.
AppendBuffers("61K 71D10");
CheckExpectedRangesByTimestamp("{ [0,81) }");
}
TEST_P(SourceBufferStreamTest,
AdjacentCodedFrameGroupContinuation_NoGapCreatedPrefixRemoved) {
NewCodedFrameGroupAppend("0K 10 20K 30 40K 50D10");
CheckExpectedRangesByTimestamp("{ [0,60) }");
RemoveInMs(0, 35, 60);
CheckExpectedRangesByTimestamp("{ [40,60) }");
// Continue appending to the previously started coded frame group, albeit with
// a tiny (1ms) gap. This gap should *NOT* produce a buffered range gap.
AppendBuffers("61K 71D10");
CheckExpectedRangesByTimestamp("{ [40,81) }");
}
TEST_P(SourceBufferStreamTest,
AdjacentNewCodedFrameGroupContinuation_NoGapCreatedPrefixRemoved) {
NewCodedFrameGroupAppend("0K 10 20K 30 40K 50D10");
CheckExpectedRangesByTimestamp("{ [0,60) }");
RemoveInMs(0, 35, 60);
CheckExpectedRangesByTimestamp("{ [40,60) }");
// Continue appending, with a new coded frame group, albeit with
// a tiny (1ms) gap. This gap should *NOT* produce a buffered range gap.
// This test demonstrates the "pre-relaxation" behavior, where a new "media
// segment" (now a new "coded frame group") was signaled at every media
// segment boundary.
NewCodedFrameGroupAppend("61K 71D10");
CheckExpectedRangesByTimestamp("{ [40,81) }");
}
TEST_P(SourceBufferStreamTest,
StartCodedFrameGroup_RemoveThenAppendMoreMuchLater) {
NewCodedFrameGroupAppend("1000K 1010 1020 1030K 1040 1050 1060K 1070 1080");
NewCodedFrameGroupAppend("0K 10 20");
CheckExpectedRangesByTimestamp("{ [0,30) [1000,1090) }");
SignalStartOfCodedFrameGroup(base::TimeDelta::FromMilliseconds(1070));
CheckExpectedRangesByTimestamp("{ [0,30) [1000,1090) }");
RemoveInMs(1030, 1050, 1090);
CheckExpectedRangesByTimestamp("{ [0,30) [1000,1030) [1060,1090) }");
// We've signalled that we're about to do some appends to a coded frame group
// which starts at time 1070ms. Note that the first frame, if any ever,
// appended to this SourceBufferStream for that coded frame group must have a
// decode timestamp >= 1070ms (it can be significantly in the future).
// Regardless, that appended frame must be buffered into the same existing
// range as current [1060,1090), since the new coded frame group's start of
// 1070ms is within that range.
AppendBuffers("2000K 2010");
CheckExpectedRangesByTimestamp("{ [0,30) [1000,1030) [1060,2020) }");
SeekToTimestampMs(1060);
CheckExpectedBuffers("1060K 2000K 2010");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
StartCodedFrameGroup_InExisting_AppendMuchLater) {
NewCodedFrameGroupAppend("0K 10 20 30K 40 50");
SignalStartOfCodedFrameGroup(base::TimeDelta::FromMilliseconds(45));
CheckExpectedRangesByTimestamp("{ [0,60) }");
AppendBuffers("2000K 2010");
CheckExpectedRangesByTimestamp("{ [0,2020) }");
Seek(0);
CheckExpectedBuffers("0K 10 20 30K 40 2000K 2010");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
StartCodedFrameGroup_InExisting_RemoveGOP_ThenAppend_1) {
NewCodedFrameGroupAppend("0K 10 20 30K 40 50");
SignalStartOfCodedFrameGroup(base::TimeDelta::FromMilliseconds(30));
RemoveInMs(30, 60, 60);
CheckExpectedRangesByTimestamp("{ [0,30) }");
AppendBuffers("2000K 2010");
CheckExpectedRangesByTimestamp("{ [0,2020) }");
Seek(0);
CheckExpectedBuffers("0K 10 20 2000K 2010");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
StartCodedFrameGroup_InExisting_RemoveGOP_ThenAppend_2) {
NewCodedFrameGroupAppend("0K 10 20 30K 40 50");
// Though we signal 45ms, it's adjusted internally (due to detected overlap)
// to be 40.001ms (which is just beyond the highest buffered timestamp at or
// before 45ms) to help prevent potential discontinuity across the front of
// the overlapping append.
SignalStartOfCodedFrameGroup(base::TimeDelta::FromMilliseconds(45));
RemoveInMs(30, 60, 60);
CheckExpectedRangesByTimestamp("{ [0,30) }");
AppendBuffers("2000K 2010");
CheckExpectedRangesByTimestamp("{ [0,30) [40,2020) }");
Seek(0);
CheckExpectedBuffers("0K 10 20");
CheckNoNextBuffer();
SeekToTimestampMs(40);
CheckExpectedBuffers("2000K 2010");
CheckNoNextBuffer();
SeekToTimestampMs(1000);
CheckExpectedBuffers("2000K 2010");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
StartCodedFrameGroup_InExisting_RemoveMostRecentAppend_ThenAppend_1) {
NewCodedFrameGroupAppend("0K 10 20 30K 40 50");
SignalStartOfCodedFrameGroup(base::TimeDelta::FromMilliseconds(45));
RemoveInMs(50, 60, 60);
CheckExpectedRangesByTimestamp("{ [0,50) }");
AppendBuffers("2000K 2010");
CheckExpectedRangesByTimestamp("{ [0,2020) }");
Seek(0);
CheckExpectedBuffers("0K 10 20 30K 40 2000K 2010");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
StartCodedFrameGroup_InExisting_RemoveMostRecentAppend_ThenAppend_2) {
NewCodedFrameGroupAppend("0K 10 20 30K 40 50");
SignalStartOfCodedFrameGroup(base::TimeDelta::FromMilliseconds(50));
RemoveInMs(50, 60, 60);
CheckExpectedRangesByTimestamp("{ [0,50) }");
AppendBuffers("2000K 2010");
CheckExpectedRangesByTimestamp("{ [0,2020) }");
Seek(0);
CheckExpectedBuffers("0K 10 20 30K 40 2000K 2010");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, GetHighestPresentationTimestamp) {
EXPECT_EQ(base::TimeDelta(), STREAM_OP(GetHighestPresentationTimestamp()));
NewCodedFrameGroupAppend("0K 10K");
EXPECT_EQ(base::TimeDelta::FromMilliseconds(10),
STREAM_OP(GetHighestPresentationTimestamp()));
RemoveInMs(0, 10, 20);
EXPECT_EQ(base::TimeDelta::FromMilliseconds(10),
STREAM_OP(GetHighestPresentationTimestamp()));
RemoveInMs(10, 20, 20);
EXPECT_EQ(base::TimeDelta(), STREAM_OP(GetHighestPresentationTimestamp()));
NewCodedFrameGroupAppend("0K 10K");
EXPECT_EQ(base::TimeDelta::FromMilliseconds(10),
STREAM_OP(GetHighestPresentationTimestamp()));
RemoveInMs(10, 20, 20);
EXPECT_EQ(base::TimeDelta(), STREAM_OP(GetHighestPresentationTimestamp()));
}
TEST_P(SourceBufferStreamTest, GarbageCollectionUnderMemoryPressure) {
SetMemoryLimit(16);
NewCodedFrameGroupAppend("0K 1 2 3K 4 5 6K 7 8 9K 10 11 12K 13 14 15K");
CheckExpectedRangesByTimestamp("{ [0,16) }");
// This feature is disabled by default, so by default memory pressure
// notification takes no effect and the memory limits and won't remove
// anything from buffered ranges, since we are under the limit of 20 bytes.
STREAM_OP(OnMemoryPressure(
DecodeTimestamp::FromMilliseconds(0),
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, false));
EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(8), 0));
CheckExpectedRangesByTimestamp("{ [0,16) }");
// Now enable the feature (on top of any overrides already in
// |scoped_feature_list_|.)
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kMemoryPressureBasedSourceBufferGC);
// Verify that effective MSE memory limit is reduced under memory pressure.
STREAM_OP(OnMemoryPressure(
DecodeTimestamp::FromMilliseconds(0),
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, false));
// Effective memory limit is now 8 buffers, but we still will not collect any
// data between the current playback position 3 and last append position 15.
EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(4), 0));
CheckExpectedRangesByTimestamp("{ [3,16) }");
// As playback proceeds further to time 9 we should be able to collect
// enough data to bring us back under memory limit of 8 buffers.
EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(9), 0));
CheckExpectedRangesByTimestamp("{ [9,16) }");
// If memory pressure becomes critical, the garbage collection algorithm
// becomes even more aggressive and collects everything up to the current
// playback position.
STREAM_OP(OnMemoryPressure(
DecodeTimestamp::FromMilliseconds(0),
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, false));
EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(13), 0));
CheckExpectedRangesByTimestamp("{ [12,16) }");
// But even under critical memory pressure the MSE memory limit imposed by the
// memory pressure is soft, i.e. we should be able to append more data
// successfully up to the hard limit of 16 bytes.
NewCodedFrameGroupAppend("16K 17 18 19 20 21 22 23 24 25 26 27");
CheckExpectedRangesByTimestamp("{ [12,28) }");
EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(13), 0));
CheckExpectedRangesByTimestamp("{ [12,28) }");
}
TEST_P(SourceBufferStreamTest, InstantGarbageCollectionUnderMemoryPressure) {
SetMemoryLimit(16);
NewCodedFrameGroupAppend("0K 1 2 3K 4 5 6K 7 8 9K 10 11 12K 13 14 15K");
CheckExpectedRangesByTimestamp("{ [0,16) }");
// Verify that garbage collection happens immediately on critical memory
// pressure notification, even without explicit GarbageCollect invocation,
// when the immediate GC is allowed.
// First, enable the feature (on top of any overrides already in
// |scoped_feature_list_|.)
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kMemoryPressureBasedSourceBufferGC);
STREAM_OP(OnMemoryPressure(
DecodeTimestamp::FromMilliseconds(7),
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, true));
CheckExpectedRangesByTimestamp("{ [6,16) }");
STREAM_OP(OnMemoryPressure(
DecodeTimestamp::FromMilliseconds(9),
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, true));
CheckExpectedRangesByTimestamp("{ [9,16) }");
}
TEST_P(SourceBufferStreamTest, GCFromFrontThenExplicitRemoveFromMiddleToEnd) {
// Attempts to exercise SBRByPts::GetBufferIndexAt() after its
// |keyframe_map_index_base_| has been increased, and when there is a GOP
// following the search timestamp. GC followed by an explicit remove may
// trigger that code path.
SetMemoryLimit(10);
// Append 3 IBPPP GOPs in one continuous range.
NewCodedFrameGroupAppend(
"0K 40|10 10|20 20|30 30|40 "
"50K 90|60 60|70 70|80 80|90 "
"100K 140|110 110|120 120|130 130|140");
CheckExpectedRangesByTimestamp("{ [0,150) }");
// Seek to the second GOP's keyframe to allow GC to collect all of the first
// GOP (ostensibly increasing SBR's |keyframe_map_index_base_|).
SeekToTimestampMs(50);
GarbageCollect(base::TimeDelta::FromMilliseconds(50), 0);
CheckExpectedRangesByTimestamp("{ [50,150) }");
// Remove from the middle of the first remaining GOP to the end of the range.
RemoveInMs(60, 150, 150);
CheckExpectedRangesByTimestamp("{ [50,60) }");
}
TEST_P(SourceBufferStreamTest, BFrames_WithoutEditList) {
// Simulates B-frame content where MP4 edit lists are not used to shift PTS so
// it matches DTS. From acolwell@chromium.org in https://crbug.com/398130
Seek(0);
if (buffering_api_ == BufferingApi::kLegacyByDts) {
NewCodedFrameGroupAppend(base::TimeDelta(),
"60|0K 180|30 90|60 120|90 150|120");
CheckExpectedRangesByTimestamp("{ [0,150) }");
} else {
NewCodedFrameGroupAppend(base::TimeDelta::FromMilliseconds(60),
"60|0K 180|30 90|60 120|90 150|120");
CheckExpectedRangesByTimestamp("{ [60,210) }");
}
CheckExpectedBuffers("60|0K 180|30 90|60 120|90 150|120");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, OverlapSameTimestampWithinSameGOP) {
// We use distinct appends here to make sure the intended frame durations
// are respected by the test helpers (which the OneByOne helper doesn't
// respect always). We need granular appends of this GOP for at least the
// append of PTS=DTS=30, below.
NewCodedFrameGroupAppend("0|0D10K");
AppendBuffers("30|10D0");
AppendBuffers("20|20D10");
// The following should *not* remove the PTS frame 30, above.
AppendBuffers("30|30 40|40");
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,50) }");
CheckExpectedBuffers("0K 30|10 20 30 40");
}
struct VideoEndTimeCase {
// Times in Milliseconds
int64_t new_frame_pts;
int64_t new_frame_duration;
int64_t expected_highest_pts;
int64_t expected_end_time;
};
TEST_P(SourceBufferStreamTest, VideoRangeEndTimeCases) {
// With a basic range containing just a single keyframe [10,20), verify
// various keyframe overlap append cases' results on the range end time.
const VideoEndTimeCase kCases[] = {
{0, 10, 10, 20},
{20, 1, 20, 21},
{15, 3, 15, 18},
{15, 5, 15, 20},
{15, 8, 15, 23},
// Cases where the new frame removes the previous frame:
{10, 3, 10, 13},
{10, 10, 10, 20},
{10, 13, 10, 23},
{5, 8, 5, 13},
{5, 15, 5, 20},
{5, 20, 5, 25}};
for (const auto& c : kCases) {
RemoveInMs(0, 100, 100);
NewCodedFrameGroupAppend("10D10K");
CheckExpectedRangesByTimestamp("{ [10,20) }");
CheckExpectedRangeEndTimes("{ <10,20> }");
std::stringstream ss;
ss << c.new_frame_pts << "D" << c.new_frame_duration << "K";
DVLOG(1) << "Appending " << ss.str();
NewCodedFrameGroupAppend(ss.str());
std::stringstream expected;
expected << "{ <" << c.expected_highest_pts << "," << c.expected_end_time
<< "> }";
CheckExpectedRangeEndTimes(expected.str());
}
}
struct AudioEndTimeCase {
// Times in Milliseconds
int64_t new_frame_pts;
int64_t new_frame_duration;
int64_t expected_highest_pts;
int64_t expected_end_time;
bool expect_splice;
};
TEST_P(SourceBufferStreamTest, AudioRangeEndTimeCases) {
// With a basic range containing just a single keyframe [10,20), verify
// various keyframe overlap append cases' results on the range end time.
const AudioEndTimeCase kCases[] = {
{0, 10, 10, 20, false},
{20, 1, 20, 21, false},
{15, 3, 15, 18, true},
{15, 5, 15, 20, true},
{15, 8, 15, 23, true},
// Cases where the new frame removes the previous frame:
{10, 3, 10, 13, false},
{10, 10, 10, 20, false},
{10, 13, 10, 23, false},
{5, 8, 5, 13, false},
{5, 15, 5, 20, false},
{5, 20, 5, 25, false}};
SetAudioStream();
for (const auto& c : kCases) {
InSequence s;
RemoveInMs(0, 100, 100);
NewCodedFrameGroupAppend("10D10K");
CheckExpectedRangesByTimestamp("{ [10,20) }");
CheckExpectedRangeEndTimes("{ <10,20> }");
std::stringstream ss;
ss << c.new_frame_pts << "D" << c.new_frame_duration << "K";
if (c.expect_splice) {
EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(c.new_frame_pts * 1000, 10000,
(20 - c.new_frame_pts) * 1000));
}
DVLOG(1) << "Appending " << ss.str();
NewCodedFrameGroupAppend(ss.str());
std::stringstream expected;
expected << "{ <" << c.expected_highest_pts << "," << c.expected_end_time
<< "> }";
CheckExpectedRangeEndTimes(expected.str());
}
}
TEST_P(SourceBufferStreamTest, SameTimestampEstimatedDurations_Video) {
// Start a coded frame group with a frame having a non-estimated duration.
NewCodedFrameGroupAppend("10D10K");
// In the same coded frame group, append a same-timestamp frame with estimated
// duration smaller than the first frame. (This can happen at least if there
// was an intervening init segment resetting the estimation logic.) This
// second frame need not be a keyframe. We use a non-keyframe here to
// differentiate the buffers in CheckExpectedBuffers(), below.
AppendBuffers("10D9E");
// The next append, which triggered https://crbug.com/761567, didn't need to
// be with same timestamp as the earlier ones; it just needs to be in the same
// buffered range. Also, it doesn't need to be a keyframe, have an estimated
// duration, nor be in the same coded frame group to trigger that issue.
NewCodedFrameGroupAppend("11D10K");
Seek(0);
CheckExpectedRangesByTimestamp("{ [10,21) }");
CheckExpectedRangeEndTimes("{ <11,21> }");
CheckExpectedBuffers("10K 10 11K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, RangeIsNextInPTS_Simple) {
// Append a simple GOP where DTS==PTS, perform basic PTS continuity checks.
NewCodedFrameGroupAppend("10D10K");
CheckIsNextInPTSSequenceWithFirstRange(9, false);
CheckIsNextInPTSSequenceWithFirstRange(10, true);
CheckIsNextInPTSSequenceWithFirstRange(20, true);
CheckIsNextInPTSSequenceWithFirstRange(30, true);
CheckIsNextInPTSSequenceWithFirstRange(31, false);
}
TEST_P(SourceBufferStreamTest, RangeIsNextInPTS_OutOfOrder) {
// Append a GOP where DTS != PTS such that a timestamp used as DTS would not
// be continuous, but used as PTS is, and verify PTS continuity.
NewCodedFrameGroupAppend("1000|0K 1120|30 1030|60 1060|90 1090|120");
CheckIsNextInPTSSequenceWithFirstRange(0, false);
CheckIsNextInPTSSequenceWithFirstRange(30, false);
CheckIsNextInPTSSequenceWithFirstRange(60, false);
CheckIsNextInPTSSequenceWithFirstRange(90, false);
CheckIsNextInPTSSequenceWithFirstRange(120, false);
CheckIsNextInPTSSequenceWithFirstRange(150, false);
CheckIsNextInPTSSequenceWithFirstRange(1000, false);
CheckIsNextInPTSSequenceWithFirstRange(1030, false);
CheckIsNextInPTSSequenceWithFirstRange(1060, false);
CheckIsNextInPTSSequenceWithFirstRange(1090, false);
CheckIsNextInPTSSequenceWithFirstRange(1119, false);
CheckIsNextInPTSSequenceWithFirstRange(1120, true);
CheckIsNextInPTSSequenceWithFirstRange(1150, true);
CheckIsNextInPTSSequenceWithFirstRange(1180, true);
CheckIsNextInPTSSequenceWithFirstRange(1181, false);
}
TEST_P(SourceBufferStreamTest, RangeCoalescenceOnFudgeRoomIncrease_1) {
// Change the fudge room (by increasing frame duration) and verify coalescence
// behavior.
NewCodedFrameGroupAppend("0K 10K");
NewCodedFrameGroupAppend("100K 110K");
NewCodedFrameGroupAppend("500K 510K");
CheckExpectedRangesByTimestamp("{ [0,20) [100,120) [500,520) }");
// Increase the fudge room almost enough to merge the first two buffered
// ranges.
NewCodedFrameGroupAppend("1000D44K");
CheckExpectedRangesByTimestamp("{ [0,20) [100,120) [500,520) [1000,1044) }");
// Increase the fudge room again to merge the first two buffered ranges.
NewCodedFrameGroupAppend("2000D45K");
CheckExpectedRangesByTimestamp(
"{ [0,120) [500,520) [1000,1044) [2000,2045) }");
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 10K 100K 110K");
CheckNoNextBuffer();
SeekToTimestampMs(500);
CheckExpectedBuffers("500K 510K");
CheckNoNextBuffer();
SeekToTimestampMs(1000);
CheckExpectedBuffers("1000K");
CheckNoNextBuffer();
SeekToTimestampMs(2000);
CheckExpectedBuffers("2000K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, RangeCoalescenceOnFudgeRoomIncrease_2) {
// Change the fudge room (by increasing frame duration) and verify coalescence
// behavior.
NewCodedFrameGroupAppend("0K 10K");
NewCodedFrameGroupAppend("40K 50K 60K");
CheckExpectedRangesByTimestamp("{ [0,20) [40,70) }");
// Increase the fudge room to merge the first two buffered ranges.
NewCodedFrameGroupAppend("1000D20K");
CheckExpectedRangesByTimestamp("{ [0,70) [1000,1020) }");
// Try to trigger unsorted ranges, as might occur if the first two buffered
// ranges were not correctly coalesced.
NewCodedFrameGroupAppend("45D10K");
CheckExpectedRangesByTimestamp("{ [0,70) [1000,1020) }");
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 10K 40K 45K 60K");
CheckNoNextBuffer();
SeekToTimestampMs(1000);
CheckExpectedBuffers("1000K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, NoRangeGapWhenIncrementallyOverlapped) {
// Append 2 SAP-Type-1 GOPs continuous in DTS and PTS interval and with frame
// durations and number of frames per GOP such that the first keyframe by
// itself would not be considered "adjacent" to the second GOP by our fudge
// room logic alone, but we now adjust the range start times occurring during
// an overlap to enable overlap appends to remain continuous with the
// remainder of the overlapped range, if any. Then incrementally reappend
// each frame of the first GOP.
NewCodedFrameGroupAppend("0K 10 20 30 40 50K 60 70 80 90");
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,100) }");
CheckExpectedRangeEndTimes("{ <90,100> }");
CheckExpectedBuffers("0K 10 20 30 40 50K 60 70 80 90");
CheckNoNextBuffer();
NewCodedFrameGroupAppend("0D10K"); // Replaces first GOP with 1 frame.
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,100) }");
CheckExpectedRangeEndTimes("{ <90,100> }");
CheckExpectedBuffers("0K 50K 60 70 80 90");
CheckNoNextBuffer();
// Add more of the replacement GOP.
AppendBuffers("10 20");
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,100) }");
CheckExpectedRangeEndTimes("{ <90,100> }");
CheckExpectedBuffers("0K 10 20 50K 60 70 80 90");
CheckNoNextBuffer();
// Add more of the replacement GOP.
AppendBuffers("30D10");
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,100) }");
CheckExpectedRangeEndTimes("{ <90,100> }");
CheckExpectedBuffers("0K 10 20 30 50K 60 70 80 90");
CheckNoNextBuffer();
// Complete the replacement GOP.
AppendBuffers("40D10");
Seek(0);
CheckExpectedRangesByTimestamp("{ [0,100) }");
CheckExpectedRangeEndTimes("{ <90,100> }");
CheckExpectedBuffers("0K 10 20 30 40 50K 60 70 80 90");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, AllowIncrementalAppendsToCoalesceRangeGap) {
// Append a SAP-Type-1 GOP with a coded frame group start time far before the
// timestamp of the first GOP (beyond any fudge room possible in this test).
// This simulates one of multiple muxed tracks with jagged start times
// following a discontinuity.
// Then incrementally append a preceding SAP-Type-1 GOP with frames that
// eventually are adjacent within fudge room of the first appended GOP's group
// start time and observe the buffered range and demux gap coalesces. Finally,
// incrementally append more frames of that preceding GOP to fill in the
// timeline to abut the first appended GOP's keyframe timestamp and observe no
// further buffered range change or discontinuity.
NewCodedFrameGroupAppend(base::TimeDelta::FromMilliseconds(100), "150K 160");
SeekToTimestampMs(100);
CheckExpectedRangesByTimestamp("{ [100,170) }");
CheckExpectedRangeEndTimes("{ <160,170> }");
CheckExpectedBuffers("150K 160");
CheckNoNextBuffer();
NewCodedFrameGroupAppend("70D10K");
SeekToTimestampMs(70);
CheckExpectedRangesByTimestamp("{ [70,80) [100,170) }");
CheckExpectedRangeEndTimes("{ <70,80> <160,170> }");
CheckExpectedBuffers("70K");
CheckNoNextBuffer();
SeekToTimestampMs(100);
CheckExpectedBuffers("150K 160");
CheckNoNextBuffer();
AppendBuffers("80D10"); // 80ms is just close enough to 100ms to coalesce.
SeekToTimestampMs(70);
CheckExpectedRangesByTimestamp("{ [70,170) }");
CheckExpectedRangeEndTimes("{ <160,170> }");
CheckExpectedBuffers("70K 80 150K 160");
CheckNoNextBuffer();
AppendBuffers("90D10");
SeekToTimestampMs(70);
CheckExpectedRangesByTimestamp("{ [70,170) }");
CheckExpectedRangeEndTimes("{ <160,170> }");
CheckExpectedBuffers("70K 80 90 150K 160");
CheckNoNextBuffer();
AppendBuffers("100 110 120");
SeekToTimestampMs(70);
CheckExpectedRangesByTimestamp("{ [70,170) }");
CheckExpectedRangeEndTimes("{ <160,170> }");
CheckExpectedBuffers("70K 80 90 100 110 120 150K 160");
CheckNoNextBuffer();
AppendBuffers("130D10");
SeekToTimestampMs(70);
CheckExpectedRangesByTimestamp("{ [70,170) }");
CheckExpectedRangeEndTimes("{ <160,170> }");
CheckExpectedBuffers("70K 80 90 100 110 120 130 150K 160");
CheckNoNextBuffer();
AppendBuffers("140D10");
SeekToTimestampMs(70);
CheckExpectedRangesByTimestamp("{ [70,170) }");
CheckExpectedRangeEndTimes("{ <160,170> }");
CheckExpectedBuffers("70K 80 90 100 110 120 130 140 150K 160");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, PreciselyOverlapLastAudioFrameAppended_1) {
// Appends an audio frame, A, which is then immediately followed by a
// subsequent frame, B. Then appends a new frame, C, which precisely overlaps
// frame B, and verifies that there is exactly 1 buffered range resulting.
SetAudioStream();
// Frame A
NewCodedFrameGroupAppend("0D10K");
SeekToTimestampMs(0);
CheckExpectedRangesByTimestamp("{ [0,10) }");
CheckExpectedRangeEndTimes("{ <0,10> }");
CheckExpectedBuffers("0K");
CheckNoNextBuffer();
// Frame B
NewCodedFrameGroupAppend("10D10K");
SeekToTimestampMs(0);
CheckExpectedRangesByTimestamp("{ [0,20) }");
CheckExpectedRangeEndTimes("{ <10,20> }");
CheckExpectedBuffers("0K 10K");
CheckNoNextBuffer();
// Frame C. FrameProcessor won't signal a new CFG here when buffering by DTS,
// because the DTS remains continuous per MSE spec. When buffering by PTS,
// though, FrameProcessor signals new CFG more granularly, including in this
// case.
if (buffering_api_ == BufferingApi::kLegacyByDts) {
AppendBuffers("10D10K");
} else {
NewCodedFrameGroupAppend("10D10K");
}
SeekToTimestampMs(0);
CheckExpectedRangesByTimestamp("{ [0,20) }");
CheckExpectedRangeEndTimes("{ <10,20> }");
CheckExpectedBuffers("0K 10K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, PreciselyOverlapLastAudioFrameAppended_2) {
// Appends an audio frame, A, which is then splice-trim-truncated by a
// subsequent frame, B. Then appends a new frame, C, which precisely overlaps
// frame B, and verifies that there is exactly 1 buffered range resulting.
SetAudioStream();
// Frame A
NewCodedFrameGroupAppend("0D100K");
SeekToTimestampMs(0);
CheckExpectedRangesByTimestamp("{ [0,100) }");
CheckExpectedRangeEndTimes("{ <0,100> }");
CheckExpectedBuffers("0K");
CheckNoNextBuffer();
// Frame B
EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(60000, 0, 40000));
NewCodedFrameGroupAppend("60D10K");
SeekToTimestampMs(0);
CheckExpectedRangesByTimestamp("{ [0,70) }");
CheckExpectedRangeEndTimes("{ <60,70> }");
CheckExpectedBuffers("0K 60K");
CheckNoNextBuffer();
// Frame C. FrameProcessor won't signal a new CFG here when buffering by DTS,
// because the DTS remains continuous per MSE spec. When buffering by PTS,
// though, FrameProcessor signals new CFG more granularly, including in this
// case.
if (buffering_api_ == BufferingApi::kLegacyByDts) {
AppendBuffers("60D10K");
} else {
NewCodedFrameGroupAppend("60D10K");
}
SeekToTimestampMs(0);
CheckExpectedRangesByTimestamp("{ [0,70) }");
CheckExpectedRangeEndTimes("{ <60,70> }");
CheckExpectedBuffers("0K 60K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, ZeroDurationBuffersThenIncreasingFudgeRoom) {
// Appends some zero duration buffers to result in disjoint buffered ranges.
// Verifies that increasing the fudge room allows those that become within
// adjacency threshold to merge, including those for which the new fudge room
// is well more than sufficient to let them be adjacent.
SetAudioStream();
NewCodedFrameGroupAppend("0uD0K");
CheckExpectedRangesByTimestamp("{ [0,1) }", TimeGranularity::kMicrosecond);
NewCodedFrameGroupAppend("1uD0K");
CheckExpectedRangesByTimestamp("{ [0,2) }", TimeGranularity::kMicrosecond);
// Initial fudge room allows for up to 2ms gap to coalesce.
NewCodedFrameGroupAppend("5000uD0K");
CheckExpectedRangesByTimestamp("{ [0,2) [5000,5001) }",
TimeGranularity::kMicrosecond);
NewCodedFrameGroupAppend("2002uD0K");
CheckExpectedRangesByTimestamp("{ [0,2) [2002,2003) [5000,5001) }",
TimeGranularity::kMicrosecond);
// Grow the fudge room enough to coalesce the first two ranges.
NewCodedFrameGroupAppend("8000uD1001uK");
CheckExpectedRangesByTimestamp("{ [0,2003) [5000,5001) [8000,9001) }",
TimeGranularity::kMicrosecond);
// Append a buffer with duration 4ms, much larger than previous buffers. This
// grows the fudge room to 8ms (2 * 4ms). Expect that the first three ranges
// are retroactively merged due to being adjacent per the new, larger fudge
// room.
NewCodedFrameGroupAppend("100D4K");
CheckExpectedRangesByTimestamp("{ [0,9001) [100000,104000) }",
TimeGranularity::kMicrosecond);
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 1K 2002K 5000K 8000K",
TimeGranularity::kMicrosecond);
CheckNoNextBuffer();
SeekToTimestampMs(100);
CheckExpectedBuffers("100K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, NonZeroDurationBuffersThenIncreasingFudgeRoom) {
// Verifies that a single fudge room increase which merges more than 2
// previously disjoint ranges in a row performs the merging correctly.
NewCodedFrameGroupAppend("0D10K");
NewCodedFrameGroupAppend("50D10K");
NewCodedFrameGroupAppend("100D10K");
NewCodedFrameGroupAppend("150D10K");
NewCodedFrameGroupAppend("500D10K");
CheckExpectedRangesByTimestamp(
"{ [0,10) [50,60) [100,110) [150,160) [500,510) }");
NewCodedFrameGroupAppend("600D30K");
CheckExpectedRangesByTimestamp("{ [0,160) [500,510) [600,630) }");
SeekToTimestampMs(0);
CheckExpectedBuffers("0K 50K 100K 150K");
CheckNoNextBuffer();
SeekToTimestampMs(500);
CheckExpectedBuffers("500K");
CheckNoNextBuffer();
SeekToTimestampMs(600);
CheckExpectedBuffers("600K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest, SapType2WithNonkeyframePtsInEarlierRange) {
// Buffer a standalone GOP [0,10).
NewCodedFrameGroupAppend("0D10K");
CheckExpectedRangesByTimestamp("{ [0,10) }");
// Following discontinuity (simulated by DTS gap, signalled by new coded frame
// group with time beyond fudge room of [0,10)), buffer 2 new GOPs in a later
// range: a SAP-2 GOP with a nonkeyframe with PTS belonging to the first
// range, and a subsequent minimal GOP.
NewCodedFrameGroupAppend("30D10K 1|40D10");
if (buffering_api_ == BufferingApi::kLegacyByDts) {
CheckExpectedRangesByTimestamp("{ [0,10) [30,50) }");
} else {
CheckExpectedRangesByTimestamp("{ [0,10) [30,40) }");
}
NewCodedFrameGroupAppend("40|50D10K");
// Verify that there are two distinct ranges, and that the SAP-2 nonkeyframe
// is buffered as part of the second range's first GOP.
if (buffering_api_ == BufferingApi::kLegacyByDts) {
CheckExpectedRangesByTimestamp("{ [0,10) [30,60) }");
} else {
CheckExpectedRangesByTimestamp("{ [0,10) [30,50) }");
}
SeekToTimestampMs(0);
CheckExpectedBuffers("0K");
CheckNoNextBuffer();
SeekToTimestampMs(30);
CheckExpectedBuffers("30K 1|40 40|50K");
CheckNoNextBuffer();
}
TEST_P(SourceBufferStreamTest,
MergeAllowedIfRangeEndTimeWithEstimatedDurationMatchesNextRangeStart) {
// Tests the edge case where fudge room is not increased when an estimated
// duration is increased due to overlap appends, causing two ranges to not be
// within fudge room of each other (nor merged), yet abutting each other.
// Append a GOP that has fudge room as its interval (e.g. 2 frames of same
// duration >= minimum 1ms).
NewCodedFrameGroupAppend("0D10K 10D10");
CheckExpectedRangesByTimestamp("{ [0,20) }");
// Trigger a DTS discontinuity so later 21ms append also is discontinuous and
// retains 10ms*2 fudge room.
NewCodedFrameGroupAppend("100D10K");
CheckExpectedRangesByTimestamp("{ [0,20) [100,110) }");
// Append another keyframe that starts within fudge room distance of the
// non-keyframe in the GOP appended, above.
NewCodedFrameGroupAppend("21D10K");
CheckExpectedRangesByTimestamp("{ [0,31) [100,110) }");
// Overlap-append the original GOP with a single estimated-duration keyframe.
// Though its timestamp is not within fudge room of the next keyframe, that
// next keyframe at time 21ms was in the overlapped range and is retained in
// the result of the overlap append's range.
NewCodedFrameGroupAppend("0D10EK");
CheckExpectedRangesByTimestamp("{ [0,31) [100,110) }");
// That new keyframe at time 0 now has derived estimated duration 21ms. That
// increased estimated duration did *not* increase the fudge room (which is
// still 2 * 10ms = 20ms.) So the next line, which splices in a new frame at
// time 21 causes the estimated keyframe at time 0 to not have a timestamp
// within fudge room of the new range that starts right at 21ms, the same time
// that ends the first buffered range, requiring CanAppendBuffersToEnd to
// handle this scenario specifically.
NewCodedFrameGroupAppend("21D10K");
CheckExpectedRangesByTimestamp("{ [0,31) [100,110) }");
SeekToTimestampMs(0);
CheckExpectedBuffers("0D21EK 21D10K");
CheckNoNextBuffer();
SeekToTimestampMs(100);
CheckExpectedBuffers("100D10K");
CheckNoNextBuffer();
}
INSTANTIATE_TEST_CASE_P(LegacyByDts,
SourceBufferStreamTest,
Values(BufferingApi::kLegacyByDts));
INSTANTIATE_TEST_CASE_P(NewByPts,
SourceBufferStreamTest,
Values(BufferingApi::kNewByPts));
} // namespace media