using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgumentPointee;
+using ::testing::Values;
using ::testing::_;
namespace media {
// The data pointed by |buffer| should be at least 8 bytes long.
// |number| should be in the range 0 <= number < 0x00FFFFFFFFFFFFFF.
static void WriteInt64(uint8* buffer, int64 number) {
- DCHECK(number >= 0 && number < GG_LONGLONG(0x00FFFFFFFFFFFFFF));
+ DCHECK(number >= 0 && number < 0x00FFFFFFFFFFFFFFLL);
buffer[0] = 0x01;
int64 tmp = number;
for (int i = 7; i > 0; i--) {
static void LogFunc(const std::string& str) { DVLOG(1) << str; }
-class ChunkDemuxerTest : public testing::Test {
+// Test parameter determines which coded frame processor is used to process
+// appended data. If true, LegacyFrameProcessor is used. Otherwise, the new
+// FrameProcessor is used.
+class ChunkDemuxerTest : public ::testing::TestWithParam<bool> {
protected:
enum CodecsIndex {
AUDIO,
return GenerateCluster(46, 66, 5);
}
- ChunkDemuxerTest() {
+ ChunkDemuxerTest()
+ : append_window_end_for_next_append_(kInfiniteDuration()) {
+ use_legacy_frame_processor_ = GetParam();
CreateNewDemuxer();
}
base::Bind(&ChunkDemuxerTest::DemuxerOpened, base::Unretained(this));
Demuxer::NeedKeyCB need_key_cb =
base::Bind(&ChunkDemuxerTest::DemuxerNeedKey, base::Unretained(this));
- demuxer_.reset(new ChunkDemuxer(open_cb, need_key_cb,
- base::Bind(&LogFunc)));
+ demuxer_.reset(
+ new ChunkDemuxer(open_cb, need_key_cb, base::Bind(&LogFunc), true));
}
virtual ~ChunkDemuxerTest() {
return AddId(kSourceId, HAS_AUDIO | HAS_VIDEO);
}
- return demuxer_->AddId(source_id, type, codecs);
+ return demuxer_->AddId(source_id, type, codecs,
+ use_legacy_frame_processor_);
+ }
+
+ ChunkDemuxer::Status AddIdForMp2tSource(const std::string& source_id) {
+ std::vector<std::string> codecs;
+ std::string type = "video/mp2t";
+ codecs.push_back("mp4a.40.2");
+ codecs.push_back("avc1.640028");
+ return demuxer_->AddId(source_id, type, codecs,
+ use_legacy_frame_processor_);
}
void AppendData(const uint8* data, size_t length) {
void AppendData(const std::string& source_id,
const uint8* data, size_t length) {
EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber());
- demuxer_->AppendData(source_id, data, length);
+
+ // TODO(wolenetz): Test timestamp offset updating once "sequence" append
+ // mode processing is implemented. See http://crbug.com/249422.
+ demuxer_->AppendData(source_id, data, length,
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[source_id]);
}
void AppendDataInPieces(const uint8* data, size_t length) {
// bear-640x360.webm VideoDecoderConfig returns 640x360 for its natural_size()
// The resulting video stream returns data from each file for the following
// time ranges.
- // bear-320x240.webm : [0-501) [801-2737)
+ // bear-320x240.webm : [0-501) [801-2736)
// bear-640x360.webm : [527-793)
//
// bear-320x240.webm AudioDecoderConfig returns 3863 for its extra_data_size()
// bear-640x360.webm AudioDecoderConfig returns 3935 for its extra_data_size()
// The resulting audio stream returns data from each file for the following
// time ranges.
- // bear-320x240.webm : [0-524) [779-2737)
+ // bear-320x240.webm : [0-524) [779-2736)
// bear-640x360.webm : [527-759)
bool InitDemuxerWithConfigChangeData() {
scoped_refptr<DecoderBuffer> bear1 = ReadTestDataFile("bear-320x240.webm");
scoped_refptr<DecoderBuffer> bear2 = ReadTestDataFile("bear-640x360.webm");
EXPECT_CALL(*this, DemuxerOpened());
+
demuxer_->Initialize(
&host_, CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744),
PIPELINE_OK), true);
return false;
// Append the whole bear1 file.
+ // TODO(wolenetz/acolwell): Remove this extra SetDuration expectation once
+ // the files are fixed to have the correct duration in their init segments,
+ // and the CreateInitDoneCB() call, above, is fixed to used that duration.
+ // See http://crbug.com/354284.
+ EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2746)));
AppendData(bear1->data(), bear1->data_size());
- CheckExpectedRanges(kSourceId, "{ [0,2737) }");
+ // Last audio frame has timestamp 2721 and duration 24 (estimated from max
+ // seen so far for audio track).
+ // Last video frame has timestamp 2703 and duration 33 (from TrackEntry
+ // DefaultDuration for video track).
+ CheckExpectedRanges(kSourceId, "{ [0,2736) }");
// Append initialization segment for bear2.
// Note: Offsets here and below are derived from
// Append a media segment that goes from [0.527000, 1.014000).
AppendData(bear2->data() + 55290, 18785);
- CheckExpectedRanges(kSourceId, "{ [0,1028) [1201,2737) }");
+ CheckExpectedRanges(kSourceId, "{ [0,1027) [1201,2736) }");
// Append initialization segment for bear1 & fill gap with [779-1197)
// segment.
AppendData(bear1->data(), 4370);
AppendData(bear1->data() + 72737, 28183);
- CheckExpectedRanges(kSourceId, "{ [0,2737) }");
+ CheckExpectedRanges(kSourceId, "{ [0,2736) }");
MarkEndOfStream(PIPELINE_OK);
return true;
cb.SetClusterTimecode(timecode);
// Create simple blocks for everything except the last block.
- for (int i = 0; timecode < (end_timecode - block_duration); i++) {
+ while (timecode < (end_timecode - block_duration)) {
cb.AddSimpleBlock(track_number, timecode, kWebMFlagKeyframe,
&data[0], data.size());
timecode += block_duration;
}
- // Make the last block a BlockGroup so that it doesn't get delayed by the
- // block duration calculation logic.
if (track_number == kVideoTrackNum) {
AddVideoBlockGroup(&cb, track_number, timecode, block_duration,
kWebMFlagKeyframe);
cb.AddBlockGroup(track_number, timecode, block_duration,
kWebMFlagKeyframe, &data[0], data.size());
}
+
return cb.Finish();
}
message_loop_.RunUntilIdle();
}
+ bool SetTimestampOffset(const std::string& id,
+ base::TimeDelta timestamp_offset) {
+ if (demuxer_->IsParsingMediaSegment(id))
+ return false;
+
+ timestamp_offset_map_[id] = timestamp_offset;
+ return true;
+ }
+
base::MessageLoop message_loop_;
MockDemuxerHost host_;
scoped_ptr<ChunkDemuxer> demuxer_;
+ bool use_legacy_frame_processor_;
+
+ base::TimeDelta append_window_start_for_next_append_;
+ base::TimeDelta append_window_end_for_next_append_;
+
+ // Map of source id to timestamp offset to use for the next AppendData()
+ // operation for that source id.
+ std::map<std::string, base::TimeDelta> timestamp_offset_map_;
private:
DISALLOW_COPY_AND_ASSIGN(ChunkDemuxerTest);
};
-TEST_F(ChunkDemuxerTest, Init) {
+TEST_P(ChunkDemuxerTest, Init) {
// Test no streams, audio-only, video-only, and audio & video scenarios.
// Audio and video streams can be encrypted or not encrypted.
for (int i = 0; i < 16; i++) {
EXPECT_EQ(kSampleFormatPlanarF32, config.sample_format());
EXPECT_EQ(is_audio_encrypted,
audio_stream->audio_decoder_config().is_encrypted());
+ EXPECT_TRUE(static_cast<ChunkDemuxerStream*>(audio_stream)
+ ->supports_partial_append_window_trimming());
} else {
EXPECT_FALSE(audio_stream);
}
EXPECT_TRUE(video_stream);
EXPECT_EQ(is_video_encrypted,
video_stream->video_decoder_config().is_encrypted());
+ EXPECT_FALSE(static_cast<ChunkDemuxerStream*>(video_stream)
+ ->supports_partial_append_window_trimming());
} else {
EXPECT_FALSE(video_stream);
}
// TODO(acolwell): Fold this test into Init tests since the tests are
// almost identical.
-TEST_F(ChunkDemuxerTest, InitText) {
+TEST_P(ChunkDemuxerTest, InitText) {
// Test with 1 video stream and 1 text streams, and 0 or 1 audio streams.
// No encryption cases handled here.
bool has_video = true;
ASSERT_TRUE(text_stream);
EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
EXPECT_EQ(kTextSubtitles, text_config.kind());
+ EXPECT_FALSE(static_cast<ChunkDemuxerStream*>(text_stream)
+ ->supports_partial_append_window_trimming());
DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
if (has_audio) {
EXPECT_EQ(kSampleFormatPlanarF32, config.sample_format());
EXPECT_EQ(is_audio_encrypted,
audio_stream->audio_decoder_config().is_encrypted());
+ EXPECT_TRUE(static_cast<ChunkDemuxerStream*>(audio_stream)
+ ->supports_partial_append_window_trimming());
} else {
EXPECT_FALSE(audio_stream);
}
EXPECT_TRUE(video_stream);
EXPECT_EQ(is_video_encrypted,
video_stream->video_decoder_config().is_encrypted());
+ EXPECT_FALSE(static_cast<ChunkDemuxerStream*>(video_stream)
+ ->supports_partial_append_window_trimming());
} else {
EXPECT_FALSE(video_stream);
}
// Make sure that the demuxer reports an error if Shutdown()
// is called before all the initialization segments are appended.
-TEST_F(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppended) {
+TEST_P(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppended) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(
ShutdownDemuxer();
}
-TEST_F(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppendedText) {
+TEST_P(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppendedText) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(
// Verifies that all streams waiting for data receive an end of stream
// buffer when Shutdown() is called.
-TEST_F(ChunkDemuxerTest, Shutdown_EndOfStreamWhileWaitingForData) {
+TEST_P(ChunkDemuxerTest, Shutdown_EndOfStreamWhileWaitingForData) {
DemuxerStream* text_stream = NULL;
EXPECT_CALL(host_, AddTextStream(_, _))
.WillOnce(SaveArg<0>(&text_stream));
// Test that Seek() completes successfully when the first cluster
// arrives.
-TEST_F(ChunkDemuxerTest, AppendDataAfterSeek) {
+TEST_P(ChunkDemuxerTest, AppendDataAfterSeek) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
}
// Test that parsing errors are handled for clusters appended after init.
-TEST_F(ChunkDemuxerTest, ErrorWhileParsingClusterAfterInit) {
+TEST_P(ChunkDemuxerTest, ErrorWhileParsingClusterAfterInit) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
// Test the case where a Seek() is requested while the parser
// is in the middle of cluster. This is to verify that the parser
// does not reset itself on a seek.
-TEST_F(ChunkDemuxerTest, SeekWhileParsingCluster) {
+TEST_P(ChunkDemuxerTest, SeekWhileParsingCluster) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
InSequence s;
ExpectRead(DemuxerStream::AUDIO, 0);
ExpectRead(DemuxerStream::VIDEO, 0);
ExpectRead(DemuxerStream::AUDIO, kAudioBlockDuration);
- // Note: We skip trying to read a video buffer here because computing
- // the duration for this block relies on successfully parsing the last block
- // in the cluster the cluster.
- ExpectRead(DemuxerStream::AUDIO, 2 * kAudioBlockDuration);
Seek(base::TimeDelta::FromSeconds(5));
}
// Test the case where AppendData() is called before Init().
-TEST_F(ChunkDemuxerTest, AppendDataBeforeInit) {
+TEST_P(ChunkDemuxerTest, AppendDataBeforeInit) {
scoped_ptr<uint8[]> info_tracks;
int info_tracks_size = 0;
CreateInitSegment(HAS_AUDIO | HAS_VIDEO,
false, false, &info_tracks, &info_tracks_size);
-
- demuxer_->AppendData(kSourceId, info_tracks.get(), info_tracks_size);
+ demuxer_->AppendData(kSourceId, info_tracks.get(), info_tracks_size,
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[kSourceId]);
}
// Make sure Read() callbacks are dispatched with the proper data.
-TEST_F(ChunkDemuxerTest, Read) {
+TEST_P(ChunkDemuxerTest, Read) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
EXPECT_TRUE(video_read_done);
}
-TEST_F(ChunkDemuxerTest, OutOfOrderClusters) {
+TEST_P(ChunkDemuxerTest, OutOfOrderClusters) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
AppendCluster(GenerateCluster(10, 4));
// Verify that AppendData() can still accept more data.
scoped_ptr<Cluster> cluster_c(GenerateCluster(45, 2));
- demuxer_->AppendData(kSourceId, cluster_c->data(), cluster_c->size());
+ demuxer_->AppendData(kSourceId, cluster_c->data(), cluster_c->size(),
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[kSourceId]);
}
-TEST_F(ChunkDemuxerTest, NonMonotonicButAboveClusterTimecode) {
+TEST_P(ChunkDemuxerTest, NonMonotonicButAboveClusterTimecode) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
// Verify that AppendData() ignores data after the error.
scoped_ptr<Cluster> cluster_b(GenerateCluster(20, 2));
- demuxer_->AppendData(kSourceId, cluster_b->data(), cluster_b->size());
+ demuxer_->AppendData(kSourceId, cluster_b->data(), cluster_b->size(),
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[kSourceId]);
}
-TEST_F(ChunkDemuxerTest, BackwardsAndBeforeClusterTimecode) {
+TEST_P(ChunkDemuxerTest, BackwardsAndBeforeClusterTimecode) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
// Verify that AppendData() ignores data after the error.
scoped_ptr<Cluster> cluster_b(GenerateCluster(6, 2));
- demuxer_->AppendData(kSourceId, cluster_b->data(), cluster_b->size());
+ demuxer_->AppendData(kSourceId, cluster_b->data(), cluster_b->size(),
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[kSourceId]);
}
-TEST_F(ChunkDemuxerTest, PerStreamMonotonicallyIncreasingTimestamps) {
+TEST_P(ChunkDemuxerTest, PerStreamMonotonicallyIncreasingTimestamps) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
// Test the case where a cluster is passed to AppendCluster() before
// INFO & TRACKS data.
-TEST_F(ChunkDemuxerTest, ClusterBeforeInitSegment) {
+TEST_P(ChunkDemuxerTest, ClusterBeforeInitSegment) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN), true);
}
// Test cases where we get an MarkEndOfStream() call during initialization.
-TEST_F(ChunkDemuxerTest, EOSDuringInit) {
+TEST_P(ChunkDemuxerTest, EOSDuringInit) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN), true);
MarkEndOfStream(PIPELINE_OK);
}
-TEST_F(ChunkDemuxerTest, EndOfStreamWithNoAppend) {
+TEST_P(ChunkDemuxerTest, EndOfStreamWithNoAppend) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN), true);
demuxer_.reset();
}
-TEST_F(ChunkDemuxerTest, EndOfStreamWithNoMediaAppend) {
+TEST_P(ChunkDemuxerTest, EndOfStreamWithNoMediaAppend) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
CheckExpectedRanges("{ }");
CheckExpectedRanges("{ }");
}
-TEST_F(ChunkDemuxerTest, DecodeErrorEndOfStream) {
+TEST_P(ChunkDemuxerTest, DecodeErrorEndOfStream) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
CheckExpectedRanges(kDefaultFirstClusterRange);
}
-TEST_F(ChunkDemuxerTest, NetworkErrorEndOfStream) {
+TEST_P(ChunkDemuxerTest, NetworkErrorEndOfStream) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
// Make sure that all pending reads that we don't have media data for get an
// "end of stream" buffer when MarkEndOfStream() is called.
-TEST_F(ChunkDemuxerTest, EndOfStreamWithPendingReads) {
+TEST_P(ChunkDemuxerTest, EndOfStreamWithPendingReads) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(GenerateCluster(0, 2));
// Make sure that all Read() calls after we get an MarkEndOfStream()
// call return an "end of stream" buffer.
-TEST_F(ChunkDemuxerTest, ReadsAfterEndOfStream) {
+TEST_P(ChunkDemuxerTest, ReadsAfterEndOfStream) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(GenerateCluster(0, 2));
end_of_stream_helper_3.CheckIfReadDonesWereCalled(true);
}
-TEST_F(ChunkDemuxerTest, EndOfStreamDuringCanceledSeek) {
+TEST_P(ChunkDemuxerTest, EndOfStreamDuringCanceledSeek) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(0, 10);
}
// Verify buffered range change behavior for audio/video/text tracks.
-TEST_F(ChunkDemuxerTest, EndOfStreamRangeChanges) {
+TEST_P(ChunkDemuxerTest, EndOfStreamRangeChanges) {
DemuxerStream* text_stream = NULL;
EXPECT_CALL(host_, AddTextStream(_, _))
.WillOnce(SaveArg<0>(&text_stream));
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT));
- AppendSingleStreamCluster(kSourceId, kVideoTrackNum, "0K 30");
+ AppendSingleStreamCluster(kSourceId, kVideoTrackNum, "0K 33");
AppendSingleStreamCluster(kSourceId, kAudioTrackNum, "0K 23K");
// Check expected ranges and verify that an empty text track does not
// affect the expected ranges.
CheckExpectedRanges(kSourceId, "{ [0,46) }");
- EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(60)));
+ EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(66)));
MarkEndOfStream(PIPELINE_OK);
// Check expected ranges and verify that an empty text track does not
// affect the expected ranges.
- CheckExpectedRanges(kSourceId, "{ [0,60) }");
+ CheckExpectedRanges(kSourceId, "{ [0,66) }");
// Unmark end of stream state and verify that the ranges return to
// their pre-"end of stream" values.
}
// Make sure AppendData() will accept elements that span multiple calls.
-TEST_F(ChunkDemuxerTest, AppendingInPieces) {
+TEST_P(ChunkDemuxerTest, AppendingInPieces) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true);
GenerateExpectedReads(0, 9);
}
-TEST_F(ChunkDemuxerTest, WebMFile_AudioAndVideo) {
+TEST_P(ChunkDemuxerTest, WebMFile_AudioAndVideo) {
struct BufferTimestamps buffer_timestamps[] = {
{0, 0},
{33, 3},
{kSkip, kSkip},
};
+ // TODO(wolenetz/acolwell): Remove this SetDuration expectation and update the
+ // ParseWebMFile() call's expected duration, below, once the file is fixed to
+ // have the correct duration in the init segment. See http://crbug.com/354284.
+ EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2746)));
+
ASSERT_TRUE(ParseWebMFile("bear-320x240.webm", buffer_timestamps,
base::TimeDelta::FromMilliseconds(2744)));
}
-TEST_F(ChunkDemuxerTest, WebMFile_LiveAudioAndVideo) {
+TEST_P(ChunkDemuxerTest, WebMFile_LiveAudioAndVideo) {
struct BufferTimestamps buffer_timestamps[] = {
{0, 0},
{33, 3},
kInfiniteDuration()));
}
-TEST_F(ChunkDemuxerTest, WebMFile_AudioOnly) {
+TEST_P(ChunkDemuxerTest, WebMFile_AudioOnly) {
struct BufferTimestamps buffer_timestamps[] = {
{kSkip, 0},
{kSkip, 3},
{kSkip, kSkip},
};
+ // TODO(wolenetz/acolwell): Remove this SetDuration expectation and update the
+ // ParseWebMFile() call's expected duration, below, once the file is fixed to
+ // have the correct duration in the init segment. See http://crbug.com/354284.
+ EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2746)));
+
ASSERT_TRUE(ParseWebMFile("bear-320x240-audio-only.webm", buffer_timestamps,
base::TimeDelta::FromMilliseconds(2744),
HAS_AUDIO));
}
-TEST_F(ChunkDemuxerTest, WebMFile_VideoOnly) {
+TEST_P(ChunkDemuxerTest, WebMFile_VideoOnly) {
struct BufferTimestamps buffer_timestamps[] = {
{0, kSkip},
{33, kSkip},
{kSkip, kSkip},
};
+ // TODO(wolenetz/acolwell): Remove this SetDuration expectation and update the
+ // ParseWebMFile() call's expected duration, below, once the file is fixed to
+ // have the correct duration in the init segment. See http://crbug.com/354284.
+ EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2736)));
+
ASSERT_TRUE(ParseWebMFile("bear-320x240-video-only.webm", buffer_timestamps,
base::TimeDelta::FromMilliseconds(2703),
HAS_VIDEO));
}
-TEST_F(ChunkDemuxerTest, WebMFile_AltRefFrames) {
+TEST_P(ChunkDemuxerTest, WebMFile_AltRefFrames) {
struct BufferTimestamps buffer_timestamps[] = {
{0, 0},
{33, 3},
}
// Verify that we output buffers before the entire cluster has been parsed.
-TEST_F(ChunkDemuxerTest, IncrementalClusterParsing) {
+TEST_P(ChunkDemuxerTest, IncrementalClusterParsing) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendEmptyCluster(0);
EXPECT_FALSE(audio_read_done);
EXPECT_FALSE(video_read_done);
- // Append data one byte at a time until the audio read completes.
+ // Append data one byte at a time until one or both reads complete.
int i = 0;
- for (; i < cluster->size() && !audio_read_done; ++i) {
+ for (; i < cluster->size() && !(audio_read_done || video_read_done); ++i) {
AppendData(cluster->data() + i, 1);
message_loop_.RunUntilIdle();
}
- EXPECT_TRUE(audio_read_done);
- EXPECT_FALSE(video_read_done);
+ EXPECT_TRUE(audio_read_done || video_read_done);
EXPECT_GT(i, 0);
EXPECT_LT(i, cluster->size());
- // Append data one byte at a time until the video read completes.
- for (; i < cluster->size() && !video_read_done; ++i) {
- AppendData(cluster->data() + i, 1);
- message_loop_.RunUntilIdle();
- }
-
- EXPECT_TRUE(video_read_done);
- EXPECT_LT(i, cluster->size());
-
audio_read_done = false;
video_read_done = false;
ReadAudio(base::Bind(&OnReadDone,
EXPECT_TRUE(video_read_done);
}
-TEST_F(ChunkDemuxerTest, ParseErrorDuringInit) {
+TEST_P(ChunkDemuxerTest, ParseErrorDuringInit) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(
ASSERT_EQ(AddId(), ChunkDemuxer::kOk);
uint8 tmp = 0;
- demuxer_->AppendData(kSourceId, &tmp, 1);
+ demuxer_->AppendData(kSourceId, &tmp, 1,
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[kSourceId]);
}
-TEST_F(ChunkDemuxerTest, AVHeadersWithAudioOnlyType) {
+TEST_P(ChunkDemuxerTest, AVHeadersWithAudioOnlyType) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(kNoTimestamp(),
std::vector<std::string> codecs(1);
codecs[0] = "vorbis";
- ASSERT_EQ(demuxer_->AddId(kSourceId, "audio/webm", codecs),
+ ASSERT_EQ(demuxer_->AddId(kSourceId, "audio/webm", codecs,
+ use_legacy_frame_processor_),
ChunkDemuxer::kOk);
AppendInitSegment(HAS_AUDIO | HAS_VIDEO);
}
-TEST_F(ChunkDemuxerTest, AVHeadersWithVideoOnlyType) {
+TEST_P(ChunkDemuxerTest, AVHeadersWithVideoOnlyType) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(kNoTimestamp(),
std::vector<std::string> codecs(1);
codecs[0] = "vp8";
- ASSERT_EQ(demuxer_->AddId(kSourceId, "video/webm", codecs),
+ ASSERT_EQ(demuxer_->AddId(kSourceId, "video/webm", codecs,
+ use_legacy_frame_processor_),
ChunkDemuxer::kOk);
AppendInitSegment(HAS_AUDIO | HAS_VIDEO);
}
-TEST_F(ChunkDemuxerTest, MultipleHeaders) {
+TEST_P(ChunkDemuxerTest, MultipleHeaders) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
GenerateExpectedReads(0, 9);
}
-TEST_F(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideo) {
+TEST_P(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideo) {
std::string audio_id = "audio1";
std::string video_id = "video1";
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
GenerateVideoStreamExpectedReads(0, 4);
}
-TEST_F(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideoText) {
+TEST_P(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideoText) {
// TODO(matthewjheaney): Here and elsewhere, we need more tests
// for inband text tracks (http://crbug/321455).
GenerateVideoStreamExpectedReads(0, 4);
}
-TEST_F(ChunkDemuxerTest, AddIdFailures) {
+TEST_P(ChunkDemuxerTest, AddIdFailures) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true);
}
// Test that Read() calls after a RemoveId() return "end of stream" buffers.
-TEST_F(ChunkDemuxerTest, RemoveId) {
+TEST_P(ChunkDemuxerTest, RemoveId) {
std::string audio_id = "audio1";
std::string video_id = "video1";
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
// Test that removing an ID immediately after adding it does not interfere with
// quota for new IDs in the future.
-TEST_F(ChunkDemuxerTest, RemoveAndAddId) {
+TEST_P(ChunkDemuxerTest, RemoveAndAddId) {
std::string audio_id_1 = "audio1";
ASSERT_TRUE(AddId(audio_id_1, HAS_AUDIO) == ChunkDemuxer::kOk);
demuxer_->RemoveId(audio_id_1);
ASSERT_TRUE(AddId(audio_id_2, HAS_AUDIO) == ChunkDemuxer::kOk);
}
-TEST_F(ChunkDemuxerTest, SeekCanceled) {
+TEST_P(ChunkDemuxerTest, SeekCanceled) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
// Append cluster at the beginning of the stream.
GenerateExpectedReads(0, 4);
}
-TEST_F(ChunkDemuxerTest, SeekCanceledWhileWaitingForSeek) {
+TEST_P(ChunkDemuxerTest, SeekCanceledWhileWaitingForSeek) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
// Append cluster at the beginning of the stream.
}
// Test that Seek() successfully seeks to all source IDs.
-TEST_F(ChunkDemuxerTest, SeekAudioAndVideoSources) {
+TEST_P(ChunkDemuxerTest, SeekAudioAndVideoSources) {
std::string audio_id = "audio1";
std::string video_id = "video1";
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
// is called before data is available for that seek point.
// This scenario might be useful if seeking past the end of stream
// of either audio or video (or both).
-TEST_F(ChunkDemuxerTest, EndOfStreamAfterPastEosSeek) {
+TEST_P(ChunkDemuxerTest, EndOfStreamAfterPastEosSeek) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(GenerateSingleStreamCluster(0, 120, kAudioTrackNum, 10));
// Test that EndOfStream is ignored if coming during a pending seek
// whose seek time is before some existing ranges.
-TEST_F(ChunkDemuxerTest, EndOfStreamDuringPendingSeek) {
+TEST_P(ChunkDemuxerTest, EndOfStreamDuringPendingSeek) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(GenerateSingleStreamCluster(0, 120, kAudioTrackNum, 10));
}
// Test ranges in an audio-only stream.
-TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioIdOnly) {
+TEST_P(ChunkDemuxerTest, GetBufferedRanges_AudioIdOnly) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true);
}
// Test ranges in a video-only stream.
-TEST_F(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) {
+TEST_P(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true);
CheckExpectedRanges("{ [0,132) [200,299) }");
}
-TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) {
+TEST_P(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
// Audio: 0 -> 23
CheckExpectedRanges("{ [0,23) [320,400) [520,570) [720,750) [920,950) }");
}
-TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideoText) {
+TEST_P(ChunkDemuxerTest, GetBufferedRanges_AudioVideoText) {
EXPECT_CALL(host_, AddTextStream(_, _));
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT));
// Once MarkEndOfStream() is called, GetBufferedRanges should not cut off any
// over-hanging tails at the end of the ranges as this is likely due to block
// duration differences.
-TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) {
+TEST_P(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendSingleStreamCluster(kSourceId, kAudioTrackNum, "0K 23K");
// Append and remove data so that the 2 streams' end ranges do not overlap.
EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(246)));
- EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(366)));
+ EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(398)));
AppendSingleStreamCluster(kSourceId, kAudioTrackNum, "200K 223K");
AppendSingleStreamCluster(kSourceId, kVideoTrackNum,
- "200K 233 266 299 300K 333");
+ "200K 233 266 299 332K 365");
// At this point, the per-stream ranges are as follows:
// Audio: [0,46) [200,246)
- // Video: [0,66) [200,366)
+ // Video: [0,66) [200,398)
CheckExpectedRanges("{ [0,46) [200,246) }");
demuxer_->Remove(kSourceId, base::TimeDelta::FromMilliseconds(200),
// At this point, the per-stream ranges are as follows:
// Audio: [0,46)
- // Video: [0,66) [300,366)
+ // Video: [0,66) [332,398)
CheckExpectedRanges("{ [0,46) }");
AppendSingleStreamCluster(kSourceId, kAudioTrackNum, "200K 223K");
// At this point, the per-stream ranges are as follows:
// Audio: [0,46) [200,246)
- // Video: [0,66) [200,266) [300,366)
+ // Video: [0,66) [200,266) [332,398)
// NOTE: The last range on each stream do not overlap in time.
CheckExpectedRanges("{ [0,46) [200,246) }");
// NOTE: The last range on each stream gets extended to the highest
// end timestamp according to the spec. The last audio range gets extended
- // from [200,246) to [200,366) which is why the intersection results in the
+ // from [200,246) to [200,398) which is why the intersection results in the
// middle range getting larger AND the new range appearing.
- CheckExpectedRanges("{ [0,46) [200,266) [300,366) }");
+ CheckExpectedRanges("{ [0,46) [200,266) [332,398) }");
}
-TEST_F(ChunkDemuxerTest, DifferentStreamTimecodes) {
+TEST_P(ChunkDemuxerTest, DifferentStreamTimecodes) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
// Create a cluster where the video timecode begins 25ms after the audio.
GenerateExpectedReads(5025, 5000, 8);
}
-TEST_F(ChunkDemuxerTest, DifferentStreamTimecodesSeparateSources) {
+TEST_P(ChunkDemuxerTest, DifferentStreamTimecodesSeparateSources) {
std::string audio_id = "audio1";
std::string video_id = "video1";
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
GenerateVideoStreamExpectedReads(30, 4);
}
-TEST_F(ChunkDemuxerTest, DifferentStreamTimecodesOutOfRange) {
+TEST_P(ChunkDemuxerTest, DifferentStreamTimecodesOutOfRange) {
std::string audio_id = "audio1";
std::string video_id = "video1";
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
ExpectEndOfStream(DemuxerStream::VIDEO);
}
-TEST_F(ChunkDemuxerTest, ClusterWithNoBuffers) {
+TEST_P(ChunkDemuxerTest, ClusterWithNoBuffers) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
// Generate and append an empty cluster beginning at 0.
ExpectRead(DemuxerStream::VIDEO, 0);
}
-TEST_F(ChunkDemuxerTest, CodecPrefixMatching) {
+TEST_P(ChunkDemuxerTest, CodecPrefixMatching) {
ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported;
#if defined(USE_PROPRIETARY_CODECS)
std::vector<std::string> codecs;
codecs.push_back("avc1.4D4041");
- EXPECT_EQ(demuxer_->AddId("source_id", "video/mp4", codecs), expected);
+ EXPECT_EQ(demuxer_->AddId("source_id", "video/mp4", codecs,
+ use_legacy_frame_processor_),
+ expected);
}
// Test codec ID's that are not compliant with RFC6381, but have been
// seen in the wild.
-TEST_F(ChunkDemuxerTest, CodecIDsThatAreNotRFC6381Compliant) {
+TEST_P(ChunkDemuxerTest, CodecIDsThatAreNotRFC6381Compliant) {
ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported;
#if defined(USE_PROPRIETARY_CODECS)
codecs.push_back(codec_ids[i]);
ChunkDemuxer::Status result =
- demuxer_->AddId("source_id", "audio/mp4", codecs);
+ demuxer_->AddId("source_id", "audio/mp4", codecs,
+ use_legacy_frame_processor_);
EXPECT_EQ(result, expected)
<< "Fail to add codec_id '" << codec_ids[i] << "'";
}
}
-TEST_F(ChunkDemuxerTest, EndOfStreamStillSetAfterSeek) {
+TEST_P(ChunkDemuxerTest, EndOfStreamStillSetAfterSeek) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
EXPECT_CALL(host_, SetDuration(_))
EXPECT_EQ(kLastVideoTimestamp, last_timestamp);
}
-TEST_F(ChunkDemuxerTest, GetBufferedRangesBeforeInitSegment) {
+TEST_P(ChunkDemuxerTest, GetBufferedRangesBeforeInitSegment) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(&host_, CreateInitDoneCB(PIPELINE_OK), true);
ASSERT_EQ(AddId("audio", HAS_AUDIO), ChunkDemuxer::kOk);
// Test that Seek() completes successfully when the first cluster
// arrives.
-TEST_F(ChunkDemuxerTest, EndOfStreamDuringSeek) {
+TEST_P(ChunkDemuxerTest, EndOfStreamDuringSeek) {
InSequence s;
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
end_of_stream_helper.CheckIfReadDonesWereCalled(true);
}
-TEST_F(ChunkDemuxerTest, ConfigChange_Video) {
+TEST_P(ChunkDemuxerTest, ConfigChange_Video) {
InSequence s;
ASSERT_TRUE(InitDemuxerWithConfigChangeData());
ASSERT_EQ(status, DemuxerStream::kOk);
}
-TEST_F(ChunkDemuxerTest, ConfigChange_Audio) {
+TEST_P(ChunkDemuxerTest, ConfigChange_Audio) {
InSequence s;
ASSERT_TRUE(InitDemuxerWithConfigChangeData());
ExpectRead(DemuxerStream::AUDIO, 0);
+ // The first config change seen is from a splice frame representing an overlap
+ // of buffer from config 1 by buffers from config 2.
ReadUntilNotOkOrEndOfStream(DemuxerStream::AUDIO, &status, &last_timestamp);
-
ASSERT_EQ(status, DemuxerStream::kConfigChanged);
EXPECT_EQ(last_timestamp.InMilliseconds(), 524);
EXPECT_EQ(audio_config_2.samples_per_second(), 44100);
EXPECT_EQ(audio_config_2.extra_data_size(), 3935u);
- ExpectRead(DemuxerStream::AUDIO, 527);
-
- // Read until the next config change.
+ // The next config change is from a splice frame representing an overlap of
+ // buffers from config 2 by buffers from config 1.
ReadUntilNotOkOrEndOfStream(DemuxerStream::AUDIO, &status, &last_timestamp);
ASSERT_EQ(status, DemuxerStream::kConfigChanged);
- EXPECT_EQ(last_timestamp.InMilliseconds(), 759);
-
- // Get the new config and verify that it matches the first one.
+ EXPECT_EQ(last_timestamp.InMilliseconds(), 782);
ASSERT_TRUE(audio_config_1.Matches(audio->audio_decoder_config()));
- ExpectRead(DemuxerStream::AUDIO, 779);
-
// Read until the end of the stream just to make sure there aren't any other
// config changes.
ReadUntilNotOkOrEndOfStream(DemuxerStream::AUDIO, &status, &last_timestamp);
ASSERT_EQ(status, DemuxerStream::kOk);
+ EXPECT_EQ(last_timestamp.InMilliseconds(), 2744);
}
-TEST_F(ChunkDemuxerTest, ConfigChange_Seek) {
+TEST_P(ChunkDemuxerTest, ConfigChange_Seek) {
InSequence s;
ASSERT_TRUE(InitDemuxerWithConfigChangeData());
ASSERT_TRUE(video_config_1.Matches(video->video_decoder_config()));
}
-TEST_F(ChunkDemuxerTest, TimestampPositiveOffset) {
+TEST_P(ChunkDemuxerTest, TimestampPositiveOffset) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
- ASSERT_TRUE(demuxer_->SetTimestampOffset(
- kSourceId, base::TimeDelta::FromSeconds(30)));
+ ASSERT_TRUE(SetTimestampOffset(kSourceId, base::TimeDelta::FromSeconds(30)));
AppendCluster(GenerateCluster(0, 2));
Seek(base::TimeDelta::FromMilliseconds(30000));
GenerateExpectedReads(30000, 2);
}
-TEST_F(ChunkDemuxerTest, TimestampNegativeOffset) {
+TEST_P(ChunkDemuxerTest, TimestampNegativeOffset) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
- ASSERT_TRUE(demuxer_->SetTimestampOffset(
- kSourceId, base::TimeDelta::FromSeconds(-1)));
+ ASSERT_TRUE(SetTimestampOffset(kSourceId, base::TimeDelta::FromSeconds(-1)));
AppendCluster(GenerateCluster(1000, 2));
GenerateExpectedReads(0, 2);
}
-TEST_F(ChunkDemuxerTest, TimestampOffsetSeparateStreams) {
+TEST_P(ChunkDemuxerTest, TimestampOffsetSeparateStreams) {
std::string audio_id = "audio1";
std::string video_id = "video1";
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
- ASSERT_TRUE(demuxer_->SetTimestampOffset(
+ ASSERT_TRUE(SetTimestampOffset(
audio_id, base::TimeDelta::FromMilliseconds(-2500)));
- ASSERT_TRUE(demuxer_->SetTimestampOffset(
+ ASSERT_TRUE(SetTimestampOffset(
video_id, base::TimeDelta::FromMilliseconds(-2500)));
AppendCluster(audio_id, GenerateSingleStreamCluster(2500,
2500 + kAudioBlockDuration * 4, kAudioTrackNum, kAudioBlockDuration));
Seek(base::TimeDelta::FromMilliseconds(27300));
- ASSERT_TRUE(demuxer_->SetTimestampOffset(
+ ASSERT_TRUE(SetTimestampOffset(
audio_id, base::TimeDelta::FromMilliseconds(27300)));
- ASSERT_TRUE(demuxer_->SetTimestampOffset(
+ ASSERT_TRUE(SetTimestampOffset(
video_id, base::TimeDelta::FromMilliseconds(27300)));
AppendCluster(audio_id, GenerateSingleStreamCluster(
0, kAudioBlockDuration * 4, kAudioTrackNum, kAudioBlockDuration));
GenerateAudioStreamExpectedReads(27300, 4);
}
-TEST_F(ChunkDemuxerTest, TimestampOffsetMidMediaSegment) {
+TEST_P(ChunkDemuxerTest, IsParsingMediaSegmentMidMediaSegment) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
scoped_ptr<Cluster> cluster = GenerateCluster(0, 2);
// Append only part of the cluster data.
AppendData(cluster->data(), cluster->size() - 13);
- // Setting a timestamp should fail because we're in the middle of a cluster.
- ASSERT_FALSE(demuxer_->SetTimestampOffset(
- kSourceId, base::TimeDelta::FromSeconds(25)));
+ // Confirm we're in the middle of parsing a media segment.
+ ASSERT_TRUE(demuxer_->IsParsingMediaSegment(kSourceId));
- demuxer_->Abort(kSourceId);
- // After Abort(), setting a timestamp should succeed since we're no longer
- // in the middle of a cluster
- ASSERT_TRUE(demuxer_->SetTimestampOffset(
- kSourceId, base::TimeDelta::FromSeconds(25)));
+ demuxer_->Abort(kSourceId,
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[kSourceId]);
+
+ // After Abort(), parsing should no longer be in the middle of a media
+ // segment.
+ ASSERT_FALSE(demuxer_->IsParsingMediaSegment(kSourceId));
+}
+
+#if defined(USE_PROPRIETARY_CODECS)
+#if defined(ENABLE_MPEG2TS_STREAM_PARSER)
+TEST_P(ChunkDemuxerTest, EmitBuffersDuringAbort) {
+ EXPECT_CALL(*this, DemuxerOpened());
+ demuxer_->Initialize(
+ &host_, CreateInitDoneCB(kInfiniteDuration(), PIPELINE_OK), true);
+ EXPECT_EQ(ChunkDemuxer::kOk, AddIdForMp2tSource(kSourceId));
+
+ // For info:
+ // DTS/PTS derived using dvbsnoop -s ts -if bear-1280x720.ts -tssubdecode
+ // Video: first PES:
+ // PTS: 126912 (0x0001efc0) [= 90 kHz-Timestamp: 0:00:01.4101]
+ // DTS: 123909 (0x0001e405) [= 90 kHz-Timestamp: 0:00:01.3767]
+ // Audio: first PES:
+ // PTS: 126000 (0x0001ec30) [= 90 kHz-Timestamp: 0:00:01.4000]
+ // DTS: 123910 (0x0001e406) [= 90 kHz-Timestamp: 0:00:01.3767]
+ // Video: last PES:
+ // PTS: 370155 (0x0005a5eb) [= 90 kHz-Timestamp: 0:00:04.1128]
+ // DTS: 367152 (0x00059a30) [= 90 kHz-Timestamp: 0:00:04.0794]
+ // Audio: last PES:
+ // PTS: 353788 (0x000565fc) [= 90 kHz-Timestamp: 0:00:03.9309]
+
+ scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("bear-1280x720.ts");
+ AppendData(kSourceId, buffer->data(), buffer->data_size());
+
+ // Confirm we're in the middle of parsing a media segment.
+ ASSERT_TRUE(demuxer_->IsParsingMediaSegment(kSourceId));
+
+ // Abort on the Mpeg2 TS parser triggers the emission of the last video
+ // buffer which is pending in the stream parser.
+ Ranges<base::TimeDelta> range_before_abort =
+ demuxer_->GetBufferedRanges(kSourceId);
+ demuxer_->Abort(kSourceId,
+ append_window_start_for_next_append_,
+ append_window_end_for_next_append_,
+ ×tamp_offset_map_[kSourceId]);
+ Ranges<base::TimeDelta> range_after_abort =
+ demuxer_->GetBufferedRanges(kSourceId);
+
+ ASSERT_EQ(range_before_abort.size(), 1u);
+ ASSERT_EQ(range_after_abort.size(), 1u);
+ EXPECT_EQ(range_after_abort.start(0), range_before_abort.start(0));
+ EXPECT_GT(range_after_abort.end(0), range_before_abort.end(0));
}
+#endif
+#endif
-TEST_F(ChunkDemuxerTest, WebMParsingMediaSegmentDetection) {
+TEST_P(ChunkDemuxerTest, WebMIsParsingMediaSegmentDetection) {
// TODO(wolenetz): Also test 'unknown' sized clusters.
// See http://crbug.com/335676.
const uint8 kBuffer[] = {
0xE7, 0x81, 0x01, // Cluster TIMECODE (value = 1)
};
- // Setting timestamp offset or append mode is allowed only while not
- // parsing a media segment. This array indicates whether or not these
- // operations are allowed following each incrementally appended byte in
- // |kBuffer|.
+ // This array indicates expected return value of IsParsingMediaSegment()
+ // following each incrementally appended byte in |kBuffer|.
const bool kExpectedReturnValues[] = {
- true, true, true, true, false,
- false, false, true,
+ false, false, false, false, true,
+ true, true, false,
};
COMPILE_ASSERT(arraysize(kBuffer) == arraysize(kExpectedReturnValues),
DVLOG(3) << "Appending and testing index " << i;
AppendData(kBuffer + i, 1);
bool expected_return_value = kExpectedReturnValues[i];
- EXPECT_EQ(expected_return_value, demuxer_->SetTimestampOffset(
- kSourceId, base::TimeDelta::FromSeconds(25)));
- EXPECT_EQ(expected_return_value, demuxer_->SetSequenceMode(
- kSourceId, true));
- EXPECT_EQ(expected_return_value, demuxer_->SetSequenceMode(
- kSourceId, false));
+ EXPECT_EQ(expected_return_value,
+ demuxer_->IsParsingMediaSegment(kSourceId));
}
}
-TEST_F(ChunkDemuxerTest, SetSequenceModeMidMediaSegment) {
- ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
-
- scoped_ptr<Cluster> cluster = GenerateCluster(0, 2);
- // Append only part of the cluster data.
- AppendData(cluster->data(), cluster->size() - 13);
-
- // Setting append mode should fail because we're in the middle of a cluster.
- ASSERT_FALSE(demuxer_->SetSequenceMode(kSourceId, true));
- ASSERT_FALSE(demuxer_->SetSequenceMode(kSourceId, false));
-
- demuxer_->Abort(kSourceId);
- // After Abort(), setting append mode should succeed since we're no longer
- // in the middle of a cluster.
- ASSERT_TRUE(demuxer_->SetSequenceMode(kSourceId, true));
- ASSERT_TRUE(demuxer_->SetSequenceMode(kSourceId, false));
-}
-
-TEST_F(ChunkDemuxerTest, DurationChange) {
+TEST_P(ChunkDemuxerTest, DurationChange) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
const int kStreamDuration = kDefaultDuration().InMilliseconds();
CheckExpectedRanges(kSourceId, "{ [201191,201224) }");
- // Add data at the currently set duration. The duration should not increase.
+ // Add data beginning at the currently set duration and expect a new duration
+ // to be signaled. Note that the last video block will have a higher end
+ // timestamp than the last audio block.
+ if (use_legacy_frame_processor_) {
+ const int kNewStreamDurationAudio = kStreamDuration + kAudioBlockDuration;
+ EXPECT_CALL(host_, SetDuration(
+ base::TimeDelta::FromMilliseconds(kNewStreamDurationAudio)));
+ }
+ const int kNewStreamDurationVideo = kStreamDuration + kVideoBlockDuration;
+ EXPECT_CALL(host_, SetDuration(
+ base::TimeDelta::FromMilliseconds(kNewStreamDurationVideo)));
AppendCluster(GenerateCluster(kDefaultDuration().InMilliseconds(), 2));
- // Range should not be affected.
- CheckExpectedRanges(kSourceId, "{ [201191,201224) }");
+ CheckExpectedRanges(kSourceId, "{ [201191,201247) }");
- // Now add data past the duration and expect a new duration to be signalled.
- const int kNewStreamDuration = kStreamDuration + kAudioBlockDuration * 2;
+ // Add more data to the end of each media type. Note that the last audio block
+ // will have a higher end timestamp than the last video block.
+ const int kFinalStreamDuration = kStreamDuration + kAudioBlockDuration * 3;
EXPECT_CALL(host_, SetDuration(
- base::TimeDelta::FromMilliseconds(kNewStreamDuration)));
+ base::TimeDelta::FromMilliseconds(kFinalStreamDuration)));
AppendCluster(GenerateCluster(kStreamDuration + kAudioBlockDuration,
kStreamDuration + kVideoBlockDuration,
- 2));
+ 3));
- // See that the range has increased appropriately.
- CheckExpectedRanges(kSourceId, "{ [201191,201270) }");
+ // See that the range has increased appropriately (but not to the full
+ // duration of 201293, since there is not enough video appended for that).
+ CheckExpectedRanges(kSourceId, "{ [201191,201290) }");
}
-TEST_F(ChunkDemuxerTest, DurationChangeTimestampOffset) {
+TEST_P(ChunkDemuxerTest, DurationChangeTimestampOffset) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
- ASSERT_TRUE(demuxer_->SetTimestampOffset(kSourceId, kDefaultDuration()));
+ ASSERT_TRUE(SetTimestampOffset(kSourceId, kDefaultDuration()));
+ if (use_legacy_frame_processor_) {
+ EXPECT_CALL(host_, SetDuration(
+ kDefaultDuration() + base::TimeDelta::FromMilliseconds(
+ kAudioBlockDuration * 2)));
+ }
EXPECT_CALL(host_, SetDuration(
kDefaultDuration() + base::TimeDelta::FromMilliseconds(
- kAudioBlockDuration * 2)));
+ kVideoBlockDuration * 2)));
AppendCluster(GenerateCluster(0, 4));
}
-TEST_F(ChunkDemuxerTest, EndOfStreamTruncateDuration) {
+TEST_P(ChunkDemuxerTest, EndOfStreamTruncateDuration) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(kDefaultFirstCluster());
}
-TEST_F(ChunkDemuxerTest, ZeroLengthAppend) {
+TEST_P(ChunkDemuxerTest, ZeroLengthAppend) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendData(NULL, 0);
}
-TEST_F(ChunkDemuxerTest, AppendAfterEndOfStream) {
+TEST_P(ChunkDemuxerTest, AppendAfterEndOfStream) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
EXPECT_CALL(host_, SetDuration(_))
// Test receiving a Shutdown() call before we get an Initialize()
// call. This can happen if video element gets destroyed before
// the pipeline has a chance to initialize the demuxer.
-TEST_F(ChunkDemuxerTest, Shutdown_BeforeInitialize) {
+TEST_P(ChunkDemuxerTest, Shutdown_BeforeInitialize) {
demuxer_->Shutdown();
demuxer_->Initialize(
&host_, CreateInitDoneCB(DEMUXER_ERROR_COULD_NOT_OPEN), true);
message_loop_.RunUntilIdle();
}
-TEST_F(ChunkDemuxerTest, ReadAfterAudioDisabled) {
- ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
- AppendCluster(kDefaultFirstCluster());
-
- DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::AUDIO);
- ASSERT_TRUE(stream);
-
- // The stream should no longer be present.
- demuxer_->OnAudioRendererDisabled();
- ASSERT_FALSE(demuxer_->GetStream(DemuxerStream::AUDIO));
-
- // Normally this would return an audio buffer at timestamp zero, but
- // all reads should return EOS buffers when disabled.
- bool audio_read_done = false;
- stream->Read(base::Bind(&OnReadDone_EOSExpected, &audio_read_done));
- message_loop_.RunUntilIdle();
-
- EXPECT_TRUE(audio_read_done);
-}
-
-// Verifies that signalling end of stream while stalled at a gap
+// Verifies that signaling end of stream while stalled at a gap
// boundary does not trigger end of stream buffers to be returned.
-TEST_F(ChunkDemuxerTest, EndOfStreamWhileWaitingForGapToBeFilled) {
+TEST_P(ChunkDemuxerTest, EndOfStreamWhileWaitingForGapToBeFilled) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
AppendCluster(0, 10);
AppendCluster(300, 10);
CheckExpectedRanges(kSourceId, "{ [0,132) [300,432) }");
-
GenerateExpectedReads(0, 10);
bool audio_read_done = false;
demuxer_->UnmarkEndOfStream();
- AppendCluster(138, 24);
+ AppendCluster(138, 22);
message_loop_.RunUntilIdle();
- CheckExpectedRanges(kSourceId, "{ [0,438) }");
+ CheckExpectedRanges(kSourceId, "{ [0,435) }");
// Verify that the reads have completed.
EXPECT_TRUE(audio_read_done);
EXPECT_TRUE(video_read_done);
// Read the rest of the buffers.
- GenerateExpectedReads(161, 171, 22);
+ GenerateExpectedReads(161, 171, 20);
// Verify that reads block because the append cleared the end of stream state.
audio_read_done = false;
EXPECT_FALSE(audio_read_done);
EXPECT_FALSE(video_read_done);
+ EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(437)));
MarkEndOfStream(PIPELINE_OK);
EXPECT_TRUE(audio_read_done);
EXPECT_TRUE(video_read_done);
}
-TEST_F(ChunkDemuxerTest, CanceledSeekDuringInitialPreroll) {
+TEST_P(ChunkDemuxerTest, CanceledSeekDuringInitialPreroll) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
// Cancel preroll.
AppendCluster(seek_time.InMilliseconds(), 10);
}
-TEST_F(ChunkDemuxerTest, GCDuringSeek) {
+TEST_P(ChunkDemuxerTest, GCDuringSeek) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO));
demuxer_->SetMemoryLimitsForTesting(5 * kBlockSize);
CheckExpectedRanges(kSourceId, "{ [500,592) [792,815) }");
}
-TEST_F(ChunkDemuxerTest, RemoveBeforeInitSegment) {
+TEST_P(ChunkDemuxerTest, RemoveBeforeInitSegment) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(kNoTimestamp(), PIPELINE_OK), true);
base::TimeDelta::FromMilliseconds(1));
}
-TEST_F(ChunkDemuxerTest, AppendWindow_Video) {
+TEST_P(ChunkDemuxerTest, AppendWindow_Video) {
ASSERT_TRUE(InitDemuxer(HAS_VIDEO));
DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
// Set the append window to [20,280).
- demuxer_->SetAppendWindowStart(kSourceId,
- base::TimeDelta::FromMilliseconds(20));
- demuxer_->SetAppendWindowEnd(kSourceId,
- base::TimeDelta::FromMilliseconds(280));
+ append_window_start_for_next_append_ = base::TimeDelta::FromMilliseconds(20);
+ append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(280);
// Append a cluster that starts before and ends after the append window.
AppendSingleStreamCluster(kSourceId, kVideoTrackNum,
// Verify that GOPs that start outside the window are not included
// in the buffer. Also verify that buffers that start inside the
- // window and extend beyond the end of the window are included.
- CheckExpectedRanges(kSourceId, "{ [120,300) }");
- CheckExpectedBuffers(stream, "120 150 180 210 240 270");
+ // window and extend beyond the end of the window are not included.
+ CheckExpectedRanges(kSourceId, "{ [120,270) }");
+ CheckExpectedBuffers(stream, "120 150 180 210 240");
// Extend the append window to [20,650).
- demuxer_->SetAppendWindowEnd(kSourceId,
- base::TimeDelta::FromMilliseconds(650));
+ append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(650);
// Append more data and verify that adding buffers start at the next
// keyframe.
AppendSingleStreamCluster(kSourceId, kVideoTrackNum,
"360 390 420K 450 480 510 540K 570 600 630K");
- CheckExpectedRanges(kSourceId, "{ [120,300) [420,660) }");
+ CheckExpectedRanges(kSourceId, "{ [120,270) [420,630) }");
}
-TEST_F(ChunkDemuxerTest, AppendWindow_Audio) {
+TEST_P(ChunkDemuxerTest, AppendWindow_Audio) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO));
DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::AUDIO);
// Set the append window to [20,280).
- demuxer_->SetAppendWindowStart(kSourceId,
- base::TimeDelta::FromMilliseconds(20));
- demuxer_->SetAppendWindowEnd(kSourceId,
- base::TimeDelta::FromMilliseconds(280));
+ append_window_start_for_next_append_ = base::TimeDelta::FromMilliseconds(20);
+ append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(280);
// Append a cluster that starts before and ends after the append window.
AppendSingleStreamCluster(
kSourceId, kAudioTrackNum,
"0K 30K 60K 90K 120K 150K 180K 210K 240K 270K 300K 330K");
- // Verify that frames that start outside the window are not included
+ // Verify that frames that end outside the window are not included
// in the buffer. Also verify that buffers that start inside the
- // window and extend beyond the end of the window are included.
- CheckExpectedRanges(kSourceId, "{ [30,300) }");
- CheckExpectedBuffers(stream, "30 60 90 120 150 180 210 240 270");
+ // window and extend beyond the end of the window are not included.
+ //
+ // The first 20ms of the first buffer should be trimmed off since it
+ // overlaps the start of the append window.
+ CheckExpectedRanges(kSourceId, "{ [20,270) }");
+ CheckExpectedBuffers(stream, "20 30 60 90 120 150 180 210 240");
// Extend the append window to [20,650).
- demuxer_->SetAppendWindowEnd(kSourceId,
- base::TimeDelta::FromMilliseconds(650));
+ append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(650);
// Append more data and verify that a new range is created.
AppendSingleStreamCluster(
kSourceId, kAudioTrackNum,
"360K 390K 420K 450K 480K 510K 540K 570K 600K 630K");
- CheckExpectedRanges(kSourceId, "{ [30,300) [360,660) }");
+ CheckExpectedRanges(kSourceId, "{ [20,270) [360,630) }");
+}
+
+TEST_P(ChunkDemuxerTest, AppendWindow_AudioOverlapStartAndEnd) {
+ ASSERT_TRUE(InitDemuxer(HAS_AUDIO));
+
+ // Set the append window to [10,20).
+ append_window_start_for_next_append_ = base::TimeDelta::FromMilliseconds(10);
+ append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(20);
+
+ // Append a cluster that starts before and ends after the append window.
+ AppendSingleStreamCluster(kSourceId, kAudioTrackNum, "0K");
+
+ // Verify that everything is dropped in this case. No partial append should
+ // be generated.
+ CheckExpectedRanges(kSourceId, "{ }");
}
-TEST_F(ChunkDemuxerTest, AppendWindow_Text) {
+TEST_P(ChunkDemuxerTest, AppendWindow_Text) {
DemuxerStream* text_stream = NULL;
EXPECT_CALL(host_, AddTextStream(_, _))
.WillOnce(SaveArg<0>(&text_stream));
DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
// Set the append window to [20,280).
- demuxer_->SetAppendWindowStart(kSourceId,
- base::TimeDelta::FromMilliseconds(20));
- demuxer_->SetAppendWindowEnd(kSourceId,
- base::TimeDelta::FromMilliseconds(280));
+ append_window_start_for_next_append_ = base::TimeDelta::FromMilliseconds(20);
+ append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(280);
// Append a cluster that starts before and ends after the append
// window.
// Verify that text cues that start outside the window are not included
// in the buffer. Also verify that cues that extend beyond the
- // window are included.
- CheckExpectedRanges(kSourceId, "{ [120,300) }");
- CheckExpectedBuffers(video_stream, "120 150 180 210 240 270");
- CheckExpectedBuffers(text_stream, "100 200");
+ // window are not included.
+ CheckExpectedRanges(kSourceId, "{ [120,270) }");
+ CheckExpectedBuffers(video_stream, "120 150 180 210 240");
+ CheckExpectedBuffers(text_stream, "100");
// Extend the append window to [20,650).
- demuxer_->SetAppendWindowEnd(kSourceId,
- base::TimeDelta::FromMilliseconds(650));
+ append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(650);
// Append more data and verify that a new range is created.
AppendSingleStreamCluster(kSourceId, kVideoTrackNum,
"360 390 420K 450 480 510 540K 570 600 630K");
AppendSingleStreamCluster(kSourceId, kTextTrackNum, "400K 500K 600K 700K");
- CheckExpectedRanges(kSourceId, "{ [120,300) [420,660) }");
+ CheckExpectedRanges(kSourceId, "{ [120,270) [420,630) }");
// Seek to the new range and verify that the expected buffers are returned.
Seek(base::TimeDelta::FromMilliseconds(420));
- CheckExpectedBuffers(video_stream, "420 450 480 510 540 570 600 630");
- CheckExpectedBuffers(text_stream, "400 500 600");
+ CheckExpectedBuffers(video_stream, "420 450 480 510 540 570 600");
+ CheckExpectedBuffers(text_stream, "400 500");
}
-TEST_F(ChunkDemuxerTest, StartWaitingForSeekAfterParseError) {
+TEST_P(ChunkDemuxerTest, StartWaitingForSeekAfterParseError) {
ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
EXPECT_CALL(host_, OnDemuxerError(PIPELINE_ERROR_DECODE));
AppendGarbage();
demuxer_->StartWaitingForSeek(seek_time);
}
-TEST_F(ChunkDemuxerTest, Remove_AudioVideoText) {
+TEST_P(ChunkDemuxerTest, Remove_AudioVideoText) {
DemuxerStream* text_stream = NULL;
EXPECT_CALL(host_, AddTextStream(_, _))
.WillOnce(SaveArg<0>(&text_stream));
// Verifies that a Seek() will complete without text cues for
// the seek point and will return cues after the seek position
// when they are eventually appended.
-TEST_F(ChunkDemuxerTest, SeekCompletesWithoutTextCues) {
+TEST_P(ChunkDemuxerTest, SeekCompletesWithoutTextCues) {
DemuxerStream* text_stream = NULL;
EXPECT_CALL(host_, AddTextStream(_, _))
.WillOnce(SaveArg<0>(&text_stream));
// to the pending read initiated above.
CheckExpectedBuffers(text_stream, "175 225");
- // Verify that audio & video streams contiue to return expected values.
+ // Verify that audio & video streams continue to return expected values.
CheckExpectedBuffers(audio_stream, "160 180");
CheckExpectedBuffers(video_stream, "180 210");
}
+// Generate two sets of tests: one using FrameProcessor, and one using
+// LegacyFrameProcessor.
+INSTANTIATE_TEST_CASE_P(NewFrameProcessor, ChunkDemuxerTest, Values(false));
+INSTANTIATE_TEST_CASE_P(LegacyFrameProcessor, ChunkDemuxerTest, Values(true));
+
} // namespace media