| Index: media/filters/ffmpeg_demuxer_unittest.cc
|
| diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
|
| index 7ef340b668fe986e2b59eb606dd0f1d2c96a3274..ae43b504034896ee3708655ef8977c8ef6eb73ae 100644
|
| --- a/media/filters/ffmpeg_demuxer_unittest.cc
|
| +++ b/media/filters/ffmpeg_demuxer_unittest.cc
|
| @@ -278,6 +278,10 @@ class FFmpegDemuxerTest : public testing::Test {
|
| demuxer_->duration_ = kInfiniteDuration;
|
| }
|
|
|
| + void SetStreamCapacity(FFmpegDemuxerStream* s, base::TimeDelta capacity) {
|
| + s->stream_capacity_ = capacity;
|
| + }
|
| +
|
| // Fixture members.
|
|
|
| base::test::ScopedTaskScheduler task_scheduler_;
|
| @@ -1660,4 +1664,112 @@ TEST_F(FFmpegDemuxerTest, StreamStatusNotifications) {
|
| CheckStreamStatusNotifications(demuxer_.get(), video_stream);
|
| }
|
|
|
| +// This test verifies that when a video stream is restarted, we do an internal
|
| +// seek in the ffmpeg demuxer (because we need to read video frames starting
|
| +// with the previous video key frame before the current playback position, so
|
| +// that the video renderer is able to resume playback from the current media
|
| +// time), but then we'd drop all audio packets up to the last_audio_packet_pos_,
|
| +// because those have already been processed and are sitting in the audio stream
|
| +// buffer queue, we don't want to duplicate those.
|
| +TEST_F(FFmpegDemuxerTest, SeekAndDropSeenAudioPacketsAfterVideoRestart) {
|
| + CreateDemuxer("bear-320x240.webm");
|
| + InitializeDemuxer();
|
| + FFmpegDemuxerStream* audio =
|
| + static_cast<FFmpegDemuxerStream*>(GetStream(DemuxerStream::AUDIO));
|
| + FFmpegDemuxerStream* video =
|
| + static_cast<FFmpegDemuxerStream*>(GetStream(DemuxerStream::VIDEO));
|
| + EXPECT_NE(nullptr, audio);
|
| + EXPECT_NE(nullptr, video);
|
| +
|
| + // The audio stream in the test file has audio frames with timestamps about 3
|
| + // milliseconds apart (0, 3, 6, 9, 12, 15, 17, 20, 23etc). So setting stream
|
| + // capacity to 5 millisec allows the demuxer to buffer 3 frames. We'll read
|
| + // two of them immediately, so the demuxer will have read 5 frames from FFmpeg
|
| + // by the time the video track is re-enabled.
|
| + SetStreamCapacity(audio, base::TimeDelta::FromMilliseconds(5));
|
| +
|
| + // Disable the video stream and read a few frames from the audio stream.
|
| + demuxer_->OnSelectedVideoTrackChanged(base::nullopt, base::TimeDelta());
|
| +
|
| + audio->Read(NewReadCB(FROM_HERE, 29, 0, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| + audio->Read(NewReadCB(FROM_HERE, 27, 3000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| +
|
| + // At this point the demuxer have read frames with timestamps 0-15 millisec.
|
| + // Now re-enable the video stream, which will cause an FFmpeg read position to
|
| + // be seeked back to 0 (since the previous video key frame is at 0).
|
| + demuxer_->OnSelectedVideoTrackChanged(MediaTrack::Id("1"), base::TimeDelta());
|
| +
|
| + // Now verify that frames read from the audio stream are continuous, as
|
| + // expected. There should be no duplicates or missing frames.
|
| +
|
| + // The first three frames were read and buffered before the video track was
|
| + // re-enabled.
|
| + audio->Read(NewReadCB(FROM_HERE, 31, 6000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| + audio->Read(NewReadCB(FROM_HERE, 30, 9000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| + audio->Read(NewReadCB(FROM_HERE, 32, 12000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| +
|
| + // And the following three frames were read when we freed up capacity by
|
| + // reading the previous 3 frames above (i.e. after the video track was
|
| + // restarted). These should continue the audio stream reading sequence.
|
| + audio->Read(NewReadCB(FROM_HERE, 29, 15000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| + audio->Read(NewReadCB(FROM_HERE, 31, 17000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| + audio->Read(NewReadCB(FROM_HERE, 36, 20000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| +}
|
| +
|
| +TEST_F(FFmpegDemuxerTest, SeekDuringVideoStreamRestart) {
|
| + CreateDemuxer("bear-320x240.webm");
|
| + InitializeDemuxer();
|
| + FFmpegDemuxerStream* video =
|
| + static_cast<FFmpegDemuxerStream*>(GetStream(DemuxerStream::VIDEO));
|
| + EXPECT_NE(nullptr, video);
|
| +
|
| + // Disable and then re-enable the video stream, this will initiate an
|
| + // internal seek for the video stream.
|
| + demuxer_->OnSelectedVideoTrackChanged(base::nullopt, base::TimeDelta());
|
| + demuxer_->OnSelectedVideoTrackChanged(MediaTrack::Id("1"), base::TimeDelta());
|
| +
|
| + // Now immediately request a seek to a different position. This should cancel
|
| + // the previous seek and kick off a new seek.
|
| + Seek(base::TimeDelta::FromMilliseconds(500));
|
| +
|
| + // Verify that the next read returns the video buffer from the expected
|
| + // position, matching the latest seek.
|
| + video->Read(NewReadCB(FROM_HERE, 5636, 400000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| +}
|
| +
|
| +TEST_F(FFmpegDemuxerTest, VideoStreamRestartDuringSeek) {
|
| + CreateDemuxer("bear-320x240.webm");
|
| + InitializeDemuxer();
|
| + FFmpegDemuxerStream* video =
|
| + static_cast<FFmpegDemuxerStream*>(GetStream(DemuxerStream::VIDEO));
|
| + EXPECT_NE(nullptr, video);
|
| +
|
| + demuxer_->OnSelectedVideoTrackChanged(base::nullopt, base::TimeDelta());
|
| +
|
| + // Initiate a seek and re-enable the video track without waiting for the seek
|
| + // to be completed.
|
| + base::TimeDelta seek_target = base::TimeDelta::FromMilliseconds(500);
|
| + WaitableMessageLoopEvent seek_event;
|
| + demuxer_->Seek(seek_target, seek_event.GetPipelineStatusCB());
|
| +
|
| + demuxer_->OnSelectedVideoTrackChanged(MediaTrack::Id("1"), base::TimeDelta());
|
| +
|
| + // Wait for the seek to finish.
|
| + seek_event.RunAndWaitForStatus(PIPELINE_OK);
|
| +
|
| + // Verify that the next read returns the video buffer from the expected
|
| + // position, matching the latest seek.
|
| + video->Read(NewReadCB(FROM_HERE, 5636, 400000, true, DemuxerStream::kOk));
|
| + base::RunLoop().Run();
|
| +}
|
| +
|
| } // namespace media
|
|
|