Index: content/common/gpu/media/video_decode_accelerator_unittest.cc |
diff --git a/content/common/gpu/media/video_decode_accelerator_unittest.cc b/content/common/gpu/media/video_decode_accelerator_unittest.cc |
index 94c0b3664a5f693730c56aafa15500881644ba68..942d7709ae91261de13c91d4bf982100771701bf 100644 |
--- a/content/common/gpu/media/video_decode_accelerator_unittest.cc |
+++ b/content/common/gpu/media/video_decode_accelerator_unittest.cc |
@@ -325,6 +325,8 @@ class GLRenderingVDAClient |
// will start delaying the call to ReusePictureBuffer() for kReuseDelay. |
// |decode_calls_per_second| is the number of VDA::Decode calls per second. |
// If |decode_calls_per_second| > 0, |num_in_flight_decodes| must be 1. |
+ // When |test_full_flush| is true, request the VDA to return all buffers it |
+ // owns on Flush(), and verify that. |
GLRenderingVDAClient(size_t window_id, |
RenderingHelper* rendering_helper, |
ClientStateNotification<ClientState>* note, |
@@ -340,7 +342,8 @@ class GLRenderingVDAClient |
bool suppress_rendering, |
int delay_reuse_after_frame_num, |
int decode_calls_per_second, |
- bool render_as_thumbnails); |
+ bool render_as_thumbnails, |
+ bool test_full_flush); |
~GLRenderingVDAClient() override; |
void CreateAndStartDecoder(); |
@@ -375,7 +378,10 @@ class GLRenderingVDAClient |
void SetState(ClientState new_state); |
void FinishInitialization(); |
+ void ReturnTexture(int32_t picture_buffer_id); |
void ReturnPicture(int32_t picture_buffer_id); |
+ void CallReusePicture(int32_t picture_buffer_id); |
+ void FinishFlush(); |
// Delete the associated decoder helper. |
void DeleteDecoder(); |
@@ -417,6 +423,7 @@ class GLRenderingVDAClient |
int num_queued_fragments_; |
int num_decoded_frames_; |
int num_done_bitstream_buffers_; |
+ int num_pictures_at_decoder_; |
base::TimeTicks initialize_done_ticks_; |
media::VideoCodecProfile profile_; |
int fake_decoder_; |
@@ -431,6 +438,7 @@ class GLRenderingVDAClient |
// The number of VDA::Decode calls per second. This is to simulate webrtc. |
int decode_calls_per_second_; |
bool render_as_thumbnails_; |
+ bool test_full_flush_; |
// A map of the textures that are currently active for the decoder, i.e., |
// have been created via AssignPictureBuffers() and not dismissed via |
@@ -479,7 +487,8 @@ GLRenderingVDAClient::GLRenderingVDAClient( |
bool suppress_rendering, |
int delay_reuse_after_frame_num, |
int decode_calls_per_second, |
- bool render_as_thumbnails) |
+ bool render_as_thumbnails, |
+ bool test_full_flush) |
: window_id_(window_id), |
rendering_helper_(rendering_helper), |
frame_size_(frame_width, frame_height), |
@@ -497,12 +506,14 @@ GLRenderingVDAClient::GLRenderingVDAClient( |
num_queued_fragments_(0), |
num_decoded_frames_(0), |
num_done_bitstream_buffers_(0), |
+ num_pictures_at_decoder_(0), |
fake_decoder_(fake_decoder), |
texture_target_(0), |
suppress_rendering_(suppress_rendering), |
delay_reuse_after_frame_num_(delay_reuse_after_frame_num), |
decode_calls_per_second_(decode_calls_per_second), |
render_as_thumbnails_(render_as_thumbnails), |
+ test_full_flush_(test_full_flush), |
next_picture_buffer_id_(1), |
weak_this_factory_(this) { |
LOG_ASSERT(num_in_flight_decodes > 0); |
@@ -529,10 +540,20 @@ void GLRenderingVDAClient::CreateAndStartDecoder() { |
LOG_ASSERT(decoder_deleted()); |
LOG_ASSERT(!decoder_.get()); |
+ VideoDecodeAccelerator::Config config(profile_); |
+ config.output_mode = |
+ (g_test_import |
+ ? media::VideoDecodeAccelerator::Config::OutputMode::IMPORT |
+ : media::VideoDecodeAccelerator::Config::OutputMode::ALLOCATE); |
+ config.flush_mode = |
+ (test_full_flush_ ? media::VideoDecodeAccelerator::Config::FlushMode:: |
+ RETURN_OUTPUT_BUFFERS |
+ : media::VideoDecodeAccelerator::Config::FlushMode:: |
+ KEEP_OUTPUT_BUFFERS); |
if (fake_decoder_) { |
decoder_.reset(new FakeVideoDecodeAccelerator( |
frame_size_, base::Bind(&DoNothingReturnTrue))); |
- LOG_ASSERT(decoder_->Initialize(profile_, this)); |
+ LOG_ASSERT(decoder_->Initialize(config, this)); |
} else { |
if (!vda_factory_) { |
vda_factory_ = GpuVideoDecodeAcceleratorFactoryImpl::Create( |
@@ -542,16 +563,14 @@ void GLRenderingVDAClient::CreateAndStartDecoder() { |
LOG_ASSERT(vda_factory_); |
} |
- VideoDecodeAccelerator::Config config(profile_); |
- if (g_test_import) { |
- config.output_mode = |
- media::VideoDecodeAccelerator::Config::OutputMode::IMPORT; |
- } |
gpu::GpuPreferences gpu_preferences; |
decoder_ = vda_factory_->CreateVDA(this, config, gpu_preferences); |
} |
LOG_ASSERT(decoder_) << "Failed creating a VDA"; |
+ weak_vda_ptr_factory_.reset( |
+ new base::WeakPtrFactory<VideoDecodeAccelerator>(decoder_.get())); |
+ weak_vda_ = weak_vda_ptr_factory_->GetWeakPtr(); |
decoder_->TryToSetupDecodeOnSeparateThread( |
weak_this_, base::ThreadTaskRunnerHandle::Get()); |
@@ -645,10 +664,14 @@ void GLRenderingVDAClient::ProvidePictureBuffers( |
decoder_->ImportBufferForPicture(buffer.id(), handles); |
} |
} |
+ |
+ num_pictures_at_decoder_ = buffers.size(); |
} |
void GLRenderingVDAClient::DismissPictureBuffer(int32_t picture_buffer_id) { |
LOG_ASSERT(1U == active_textures_.erase(picture_buffer_id)); |
+ EXPECT_GT(num_pictures_at_decoder_, 0); |
+ --num_pictures_at_decoder_; |
} |
void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { |
@@ -658,6 +681,15 @@ void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { |
if (decoder_deleted()) |
return; |
+ EXPECT_GT(num_pictures_at_decoder_, 0); |
+ --num_pictures_at_decoder_; |
+ |
+ if (picture.bitstream_buffer_id() == -1) { |
+ // The picture does not contain any decoded data, reuse it immediately. |
+ ReturnPicture(picture.picture_buffer_id()); |
+ return; |
+ } |
+ |
base::TimeTicks now = base::TimeTicks::Now(); |
frame_delivery_times_.push_back(now); |
@@ -689,7 +721,7 @@ void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { |
scoped_refptr<VideoFrameTexture> video_frame = new VideoFrameTexture( |
texture_target_, texture_it->second->texture_id(), |
- base::Bind(&GLRenderingVDAClient::ReturnPicture, AsWeakPtr(), |
+ base::Bind(&GLRenderingVDAClient::ReturnTexture, AsWeakPtr(), |
picture.picture_buffer_id())); |
ASSERT_TRUE(pending_textures_.insert(*texture_it).second); |
@@ -701,9 +733,10 @@ void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { |
} |
} |
-void GLRenderingVDAClient::ReturnPicture(int32_t picture_buffer_id) { |
+void GLRenderingVDAClient::ReturnTexture(int32_t picture_buffer_id) { |
if (decoder_deleted()) |
return; |
+ |
LOG_ASSERT(1U == pending_textures_.erase(picture_buffer_id)); |
if (pending_textures_.empty() && state_ == CS_RESETTING) { |
@@ -712,13 +745,36 @@ void GLRenderingVDAClient::ReturnPicture(int32_t picture_buffer_id) { |
return; |
} |
+ ReturnPicture(picture_buffer_id); |
+} |
+ |
+void GLRenderingVDAClient::CallReusePicture(int32_t picture_buffer_id) { |
+ if (state_ == CS_FLUSHED && test_full_flush_) { |
+ // If we just got a notification from the VDA that it's flushed and are |
+ // testing full flush, we want to have a chance to test that VDA really |
+ // returned all buffers before NotifyFlushDone(). Delay returning the buffer |
+ // so that we have a chance in FinishFlush() to test we got all the buffers. |
+ base::MessageLoop::current()->PostTask( |
+ FROM_HERE, base::Bind(&GLRenderingVDAClient::CallReusePicture, |
+ weak_this_, picture_buffer_id)); |
+ return; |
+ } |
+ |
+ if (weak_vda_) |
+ weak_vda_->ReusePictureBuffer(picture_buffer_id); |
+ ++num_pictures_at_decoder_; |
+} |
+ |
+void GLRenderingVDAClient::ReturnPicture(int32_t picture_buffer_id) { |
if (num_decoded_frames_ > delay_reuse_after_frame_num_) { |
base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, base::Bind(&VideoDecodeAccelerator::ReusePictureBuffer, |
- weak_vda_, picture_buffer_id), |
+ FROM_HERE, base::Bind(&GLRenderingVDAClient::CallReusePicture, |
+ weak_this_, picture_buffer_id), |
kReuseDelay); |
} else { |
- decoder_->ReusePictureBuffer(picture_buffer_id); |
+ base::MessageLoop::current()->PostTask( |
+ FROM_HERE, base::Bind(&GLRenderingVDAClient::CallReusePicture, |
+ weak_this_, picture_buffer_id)); |
} |
} |
@@ -745,11 +801,11 @@ void GLRenderingVDAClient::NotifyEndOfBitstreamBuffer( |
} |
} |
-void GLRenderingVDAClient::NotifyFlushDone() { |
- if (decoder_deleted()) |
- return; |
+void GLRenderingVDAClient::FinishFlush() { |
+ ASSERT_EQ(CS_FLUSHED, state_); |
+ if (test_full_flush_) |
+ EXPECT_EQ(0, num_pictures_at_decoder_); |
- SetState(CS_FLUSHED); |
--remaining_play_throughs_; |
DCHECK_GE(remaining_play_throughs_, 0); |
if (decoder_deleted()) |
@@ -758,6 +814,27 @@ void GLRenderingVDAClient::NotifyFlushDone() { |
SetState(CS_RESETTING); |
} |
+void GLRenderingVDAClient::NotifyFlushDone() { |
+ if (decoder_deleted()) |
+ return; |
+ |
+ ASSERT_EQ(CS_FLUSHING, state_); |
+ SetState(CS_FLUSHED); |
+ |
+ // We may have already posted some ReusePictureBuffer() calls before we got |
+ // here. If we are testing RETURN_OUTPUT_BUFFERS flush mode, make sure we |
+ // don't execute them returning the buffers to VDA until we have a chance |
+ // to verify the VDA really returned all the buffers before finishing flush. |
+ // Otherwise, we don't care and can continue without holding off any pending |
+ // ReusePictureBuffer() calls |
+ if (test_full_flush_) { |
+ base::MessageLoop::current()->PostTask( |
+ FROM_HERE, base::Bind(&GLRenderingVDAClient::FinishFlush, weak_this_)); |
+ } else { |
+ FinishFlush(); |
+ } |
+} |
+ |
void GLRenderingVDAClient::NotifyResetDone() { |
if (decoder_deleted()) |
return; |
@@ -1192,10 +1269,12 @@ void VideoDecodeAcceleratorTest::OutputLogFile( |
// - delete_decoder_phase: see GLRenderingVDAClient ctor. |
// - whether to test slow rendering by delaying ReusePictureBuffer(). |
// - whether the video frames are rendered as thumbnails. |
+// - whether to test VDA::Flush(true) |
class VideoDecodeAcceleratorParamTest |
: public VideoDecodeAcceleratorTest, |
public ::testing::WithParamInterface< |
- base::Tuple<int, int, int, ResetPoint, ClientState, bool, bool> > { |
+ base:: |
+ Tuple<int, int, int, ResetPoint, ClientState, bool, bool, bool>> { |
}; |
// Wait for |note| to report a state and if it's not |expected_state| then |
@@ -1226,6 +1305,7 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) { |
const int delete_decoder_state = base::get<4>(GetParam()); |
bool test_reuse_delay = base::get<5>(GetParam()); |
const bool render_as_thumbnails = base::get<6>(GetParam()); |
+ const bool test_full_flush = base::get<7>(GetParam()); |
if (test_video_files_.size() > 1) |
num_concurrent_decoders = test_video_files_.size(); |
@@ -1284,7 +1364,8 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) { |
suppress_rendering, |
delay_after_frame_num, |
0, |
- render_as_thumbnails); |
+ render_as_thumbnails, |
+ test_full_flush); |
clients[index] = client; |
helper_params.window_sizes.push_back( |
@@ -1439,59 +1520,74 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) { |
INSTANTIATE_TEST_CASE_P( |
ReplayAfterEOS, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple(1, 1, 4, END_OF_STREAM_RESET, CS_RESET, false, false))); |
+ MakeTuple(1, 1, 4, END_OF_STREAM_RESET, CS_RESET, false, false, |
+ false))); |
// Test that Reset() before the first Decode() works fine. |
INSTANTIATE_TEST_CASE_P( |
ResetBeforeDecode, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple(1, 1, 1, START_OF_STREAM_RESET, CS_RESET, false, false))); |
+ MakeTuple(1, 1, 1, START_OF_STREAM_RESET, CS_RESET, false, false, |
+ false))); |
// Test Reset() immediately after Decode() containing config info. |
INSTANTIATE_TEST_CASE_P( |
ResetAfterFirstConfigInfo, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple( |
- 1, 1, 1, RESET_AFTER_FIRST_CONFIG_INFO, CS_RESET, false, false))); |
+ MakeTuple(1, 1, 1, RESET_AFTER_FIRST_CONFIG_INFO, CS_RESET, false, |
+ false, false))); |
// Test that Reset() mid-stream works fine and doesn't affect decoding even when |
// Decode() calls are made during the reset. |
INSTANTIATE_TEST_CASE_P( |
MidStreamReset, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple(1, 1, 1, MID_STREAM_RESET, CS_RESET, false, false))); |
+ MakeTuple(1, 1, 1, MID_STREAM_RESET, CS_RESET, false, false, false))); |
+ |
+// Test that the VDA returns all buffers to us on Flush(true). |
+INSTANTIATE_TEST_CASE_P( |
+ FullFlush, VideoDecodeAcceleratorParamTest, |
+ ::testing::Values( |
+ MakeTuple(1, 1, 1, MID_STREAM_RESET, CS_RESET, false, false, true))); |
+ |
INSTANTIATE_TEST_CASE_P( |
SlowRendering, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, true, false))); |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, true, false, false))); |
// Test that Destroy() mid-stream works fine (primarily this is testing that no |
// crashes occur). |
INSTANTIATE_TEST_CASE_P( |
TearDownTiming, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_DECODER_SET, false, false), |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_INITIALIZED, false, false), |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHING, false, false), |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHED, false, false), |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESETTING, false, false), |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_DECODER_SET, false, false, |
+ false), |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_INITIALIZED, false, false, |
+ false), |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHING, false, false, |
+ false), |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHED, false, false, |
+ false), |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESETTING, false, false, |
+ false), |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false, false), |
MakeTuple(1, 1, 1, END_OF_STREAM_RESET, |
- static_cast<ClientState>(-1), false, false), |
+ static_cast<ClientState>(-1), false, false, false), |
MakeTuple(1, 1, 1, END_OF_STREAM_RESET, |
- static_cast<ClientState>(-10), false, false), |
+ static_cast<ClientState>(-10), false, false, false), |
MakeTuple(1, 1, 1, END_OF_STREAM_RESET, |
- static_cast<ClientState>(-100), false, false))); |
+ static_cast<ClientState>(-100), false, false, false))); |
// Test that decoding various variation works with multiple in-flight decodes. |
INSTANTIATE_TEST_CASE_P( |
DecodeVariations, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
- MakeTuple(1, 10, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false, false), |
+ MakeTuple(1, 10, 1, END_OF_STREAM_RESET, CS_RESET, false, false, false), |
// Tests queuing. |
- MakeTuple(1, 15, 1, END_OF_STREAM_RESET, CS_RESET, false, false))); |
+ MakeTuple(1, 15, 1, END_OF_STREAM_RESET, CS_RESET, false, false, |
+ false))); |
// Find out how many concurrent decoders can go before we exhaust system |
// resources. |
@@ -1500,15 +1596,15 @@ INSTANTIATE_TEST_CASE_P( |
::testing::Values( |
// +0 hack below to promote enum to int. |
MakeTuple(kMinSupportedNumConcurrentDecoders + 0, 1, 1, |
- END_OF_STREAM_RESET, CS_RESET, false, false), |
+ END_OF_STREAM_RESET, CS_RESET, false, false, false), |
MakeTuple(kMinSupportedNumConcurrentDecoders + 1, 1, 1, |
- END_OF_STREAM_RESET, CS_RESET, false, false))); |
+ END_OF_STREAM_RESET, CS_RESET, false, false, false))); |
// Thumbnailing test |
INSTANTIATE_TEST_CASE_P( |
Thumbnail, VideoDecodeAcceleratorParamTest, |
::testing::Values( |
- MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, true))); |
+ MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, true, false))); |
// Measure the median of the decode time when VDA::Decode is called 30 times per |
// second. |
@@ -1538,7 +1634,8 @@ TEST_F(VideoDecodeAcceleratorTest, TestDecodeTimeMedian) { |
true, |
std::numeric_limits<int>::max(), |
kWebRtcDecodeCallsPerSecond, |
- false /* render_as_thumbnail */); |
+ false /* render_as_thumbnail */, |
+ false /* test_full_flush */); |
helper_params.window_sizes.push_back( |
gfx::Size(test_video_files_[0]->width, test_video_files_[0]->height)); |
InitializeRenderingHelper(helper_params); |