Chromium Code Reviews| Index: media/gpu/video_encode_accelerator_unittest.cc |
| diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc |
| index 4e0dd6ad8ec14bcdf064266d6eb131e9d8a9b3d5..5d9e52bee69af436fea4a9dad9578cd56ed22aae 100644 |
| --- a/media/gpu/video_encode_accelerator_unittest.cc |
| +++ b/media/gpu/video_encode_accelerator_unittest.cc |
| @@ -1762,9 +1762,161 @@ void VEAClient::WriteIvfFrameHeader(int frame_index, size_t frame_size) { |
| // This client is only used to make sure the encoder does not return an encoded |
| // frame before getting any input. |
| class VEANoInputClient : public VideoEncodeAccelerator::Client { |
|
wuchengli
2016/11/23 14:43:33
There are some boilerplate code in every VEAXxxCli
hywu1
2016/11/24 06:01:29
Done.
|
| + public: |
|
Owen Lin
2016/11/24 03:00:52
Did you change the code here on purpose?
I think
hywu1
2016/11/24 06:01:29
Done.
|
| + explicit VEANoInputClient(ClientStateNotification<ClientState>* note); |
| + ~VEANoInputClient() override; |
| + void CreateEncoder(); |
| + void DestroyEncoder(); |
| + |
| + // VideoDecodeAccelerator::Client implementation. |
| + void RequireBitstreamBuffers(unsigned int input_count, |
| + const gfx::Size& input_coded_size, |
| + size_t output_buffer_size) override; |
| + void BitstreamBufferReady(int32_t bitstream_buffer_id, |
| + size_t payload_size, |
| + bool key_frame, |
| + base::TimeDelta timestamp) override; |
| + void NotifyError(VideoEncodeAccelerator::Error error) override; |
| + |
| + private: |
| + bool has_encoder() { return encoder_.get(); } |
| + |
| + void SetState(ClientState new_state); |
| + |
| + // Provide the encoder with a new output buffer. |
| + void FeedEncoderWithOutput(base::SharedMemory* shm, size_t output_size); |
| + |
| + std::unique_ptr<VideoEncodeAccelerator> encoder_; |
| + |
| + // Used to notify another thread about the state. VEAClient does not own this. |
|
wuchengli
2016/11/23 14:43:33
Please run git cl format to check the style.
hywu1
2016/11/24 06:01:29
Done.
|
| + ClientStateNotification<ClientState>* note_; |
| + |
| + // All methods of this class should be run on the same thread. |
| + base::ThreadChecker thread_checker_; |
| + |
| + // Ids for output BitstreamBuffers. |
| + ScopedVector<base::SharedMemory> output_shms_; |
| + int32_t next_output_buffer_id_; |
| + |
| + // The timer used to monitor the encoder doesn't return an output buffer in |
| + // a period of time. |
| + std::unique_ptr<base::Timer> timer_; |
| +}; |
| + |
| +VEANoInputClient::VEANoInputClient(ClientStateNotification<ClientState>* note) |
| + : note_(note), next_output_buffer_id_(0) { |
| + thread_checker_.DetachFromThread(); |
| + } |
| + |
| +VEANoInputClient::~VEANoInputClient() { |
| + LOG_ASSERT(!has_encoder()); |
| +} |
| + |
| +void VEANoInputClient::CreateEncoder() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + LOG_ASSERT(!has_encoder()); |
| + LOG_ASSERT(g_env->test_streams_.size()); |
| + |
| + const int kDefaultWidth = 320; |
| + const int kDefaultHeight = 240; |
| + const int kDefaultBitrate = 200000; |
| + const int kDefaultFps = 30; |
| + |
| + std::unique_ptr<VideoEncodeAccelerator> encoders[] = { |
| + CreateFakeVEA(), CreateV4L2VEA(), CreateVaapiVEA(), CreateVTVEA(), |
| + CreateMFVEA()}; |
| + |
| + // The test case is no input. Use default visible size, bitrate, and fps. |
| + gfx::Size visible_size(kDefaultWidth, kDefaultHeight); |
| + for (auto& encoder : encoders) { |
| + if (!encoder) |
| + continue; |
| + encoder_ = std::move(encoder); |
| + if (encoder_->Initialize(kInputFormat, visible_size, |
| + g_env->test_streams_[0]->requested_profile, |
| + kDefaultBitrate, this)) { |
| + encoder_->RequestEncodingParametersChange(kDefaultBitrate, kDefaultFps); |
| + SetState(CS_INITIALIZED); |
| + return; |
| + } |
| + } |
| + encoder_.reset(); |
| + LOG(ERROR) << "VideoEncodeAccelerator::Initialize() failed"; |
| + SetState(CS_ERROR); |
| +} |
| + |
| +void VEANoInputClient::DestroyEncoder() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + if (!has_encoder()) |
| + return; |
| + // Clear the objects that should be destroyed on the same thread as creation. |
| + encoder_.reset(); |
| + timer_.reset(); |
| +} |
| + |
| +void VEANoInputClient::RequireBitstreamBuffers( |
| + unsigned int input_count, |
| + const gfx::Size& input_coded_size, |
| + size_t output_size) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + SetState(CS_ENCODING); |
| + ASSERT_GT(output_size, 0UL); |
| + |
| + for (unsigned int i = 0; i < kNumOutputBuffers; ++i) { |
| + base::SharedMemory* shm = new base::SharedMemory(); |
| + LOG_ASSERT(shm->CreateAndMapAnonymous(output_size)); |
| + output_shms_.push_back(shm); |
| + FeedEncoderWithOutput(shm, output_size); |
| + } |
| + // Timer is used to make sure there is no output frame in 100ms. |
| + timer_.reset(new base::Timer(FROM_HERE, |
| + base::TimeDelta::FromMilliseconds(100), |
| + base::Bind(&VEANoInputClient::SetState, |
| + base::Unretained(this), CS_FINISHED), |
| + false)); |
| + timer_->Reset(); |
| +} |
| + |
| +void VEANoInputClient::BitstreamBufferReady(int32_t bitstream_buffer_id, |
| + size_t payload_size, |
| + bool key_frame, |
| + base::TimeDelta timestamp) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + SetState(CS_ERROR); |
| +} |
| + |
| +void VEANoInputClient::NotifyError(VideoEncodeAccelerator::Error error) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + SetState(CS_ERROR); |
| +} |
| + |
| +void VEANoInputClient::SetState(ClientState new_state) { |
| + DVLOG(4) << "Changing state to " << new_state; |
| + note_->Notify(new_state); |
| +} |
| + |
| +void VEANoInputClient::FeedEncoderWithOutput(base::SharedMemory* shm, |
| + size_t output_size) { |
| + if (!has_encoder()) |
| + return; |
| + |
| + base::SharedMemoryHandle dup_handle; |
| + LOG_ASSERT(shm->ShareToProcess(base::GetCurrentProcessHandle(), &dup_handle)); |
| + |
| + BitstreamBuffer bitstream_buffer(next_output_buffer_id_++, dup_handle, |
| + output_size); |
| + encoder_->UseOutputBitstreamBuffer(bitstream_buffer); |
| +} |
| + |
| +/* This client is only used to test input frame with the size of U and V planes |
| + unaligned to cache line. |
| + To have both width and height divisible by 16 but not 32 will make the size |
| + of U/V plane (width * height / 4) unaligned to 128-byte cache line. */ |
| +class VEACacheLineUnalignedInputClient : public VideoEncodeAccelerator::Client { |
| public: |
| - explicit VEANoInputClient(ClientStateNotification<ClientState>* note); |
| - ~VEANoInputClient() override; |
| + explicit VEACacheLineUnalignedInputClient( |
| + ClientStateNotification<ClientState>* note); |
| + ~VEACacheLineUnalignedInputClient() override; |
| void CreateEncoder(); |
| void DestroyEncoder(); |
| @@ -1786,6 +1938,9 @@ class VEANoInputClient : public VideoEncodeAccelerator::Client { |
| // Provide the encoder with a new output buffer. |
| void FeedEncoderWithOutput(base::SharedMemory* shm, size_t output_size); |
| + // Feed the encoder with one input frame. |
| + void FeedEncoderWithOneInput(const gfx::Size& input_coded_size); |
| + |
| std::unique_ptr<VideoEncodeAccelerator> encoder_; |
| // Used to notify another thread about the state. VEAClient does not own this. |
| @@ -1798,44 +1953,44 @@ class VEANoInputClient : public VideoEncodeAccelerator::Client { |
| ScopedVector<base::SharedMemory> output_shms_; |
| int32_t next_output_buffer_id_; |
| - // The timer used to monitor the encoder doesn't return an output buffer in |
| - // a period of time. |
| - std::unique_ptr<base::Timer> timer_; |
| + const int default_width_; |
| + const int default_height_; |
| + const int default_bitrate_; |
| + const int default_fps_; |
| }; |
| -VEANoInputClient::VEANoInputClient(ClientStateNotification<ClientState>* note) |
| - : note_(note), next_output_buffer_id_(0) { |
| +VEACacheLineUnalignedInputClient::VEACacheLineUnalignedInputClient( |
| + ClientStateNotification<ClientState>* note) |
| + : note_(note), |
| + next_output_buffer_id_(0), |
| + default_width_(368), // divisible by 16 but not 32 |
| + default_height_(368), // divisible by 16 but not 32 |
| + default_bitrate_(200000), |
| + default_fps_(30) { |
| thread_checker_.DetachFromThread(); |
| } |
| -VEANoInputClient::~VEANoInputClient() { |
| +VEACacheLineUnalignedInputClient::~VEACacheLineUnalignedInputClient() { |
| LOG_ASSERT(!has_encoder()); |
| } |
| -void VEANoInputClient::CreateEncoder() { |
| +void VEACacheLineUnalignedInputClient::CreateEncoder() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| LOG_ASSERT(!has_encoder()); |
| - LOG_ASSERT(g_env->test_streams_.size()); |
| - |
| - const int kDefaultWidth = 320; |
| - const int kDefaultHeight = 240; |
| - const int kDefaultBitrate = 200000; |
| - const int kDefaultFps = 30; |
| std::unique_ptr<VideoEncodeAccelerator> encoders[] = { |
| CreateFakeVEA(), CreateV4L2VEA(), CreateVaapiVEA(), CreateVTVEA(), |
| CreateMFVEA()}; |
| - // The test case is no input. Use default visible size, bitrate, and fps. |
| - gfx::Size visible_size(kDefaultWidth, kDefaultHeight); |
| + gfx::Size visible_size(default_width_, default_height_); |
| for (auto& encoder : encoders) { |
| if (!encoder) |
| continue; |
| encoder_ = std::move(encoder); |
| if (encoder_->Initialize(kInputFormat, visible_size, |
| g_env->test_streams_[0]->requested_profile, |
| - kDefaultBitrate, this)) { |
| - encoder_->RequestEncodingParametersChange(kDefaultBitrate, kDefaultFps); |
| + default_bitrate_, this)) { |
| + encoder_->RequestEncodingParametersChange(default_bitrate_, default_fps_); |
| SetState(CS_INITIALIZED); |
| return; |
| } |
| @@ -1845,16 +2000,15 @@ void VEANoInputClient::CreateEncoder() { |
| SetState(CS_ERROR); |
| } |
| -void VEANoInputClient::DestroyEncoder() { |
| +void VEACacheLineUnalignedInputClient::DestroyEncoder() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!has_encoder()) |
| return; |
| // Clear the objects that should be destroyed on the same thread as creation. |
| encoder_.reset(); |
| - timer_.reset(); |
| } |
| -void VEANoInputClient::RequireBitstreamBuffers( |
| +void VEACacheLineUnalignedInputClient::RequireBitstreamBuffers( |
| unsigned int input_count, |
| const gfx::Size& input_coded_size, |
| size_t output_size) { |
| @@ -1868,35 +2022,33 @@ void VEANoInputClient::RequireBitstreamBuffers( |
| output_shms_.push_back(shm); |
| FeedEncoderWithOutput(shm, output_size); |
| } |
| - // Timer is used to make sure there is no output frame in 100ms. |
| - timer_.reset(new base::Timer(FROM_HERE, |
| - base::TimeDelta::FromMilliseconds(100), |
| - base::Bind(&VEANoInputClient::SetState, |
| - base::Unretained(this), CS_FINISHED), |
| - false)); |
| - timer_->Reset(); |
| + FeedEncoderWithOneInput(input_coded_size); |
| } |
| -void VEANoInputClient::BitstreamBufferReady(int32_t bitstream_buffer_id, |
| - size_t payload_size, |
| - bool key_frame, |
| - base::TimeDelta timestamp) { |
| +void VEACacheLineUnalignedInputClient::BitstreamBufferReady( |
| + int32_t bitstream_buffer_id, |
| + size_t payload_size, |
| + bool key_frame, |
| + base::TimeDelta timestamp) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| - SetState(CS_ERROR); |
| + // It's enough to encode just one frame |
| + SetState(CS_FINISHED); |
| } |
| -void VEANoInputClient::NotifyError(VideoEncodeAccelerator::Error error) { |
| +void VEACacheLineUnalignedInputClient::NotifyError( |
| + VideoEncodeAccelerator::Error error) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| SetState(CS_ERROR); |
| } |
| -void VEANoInputClient::SetState(ClientState new_state) { |
| +void VEACacheLineUnalignedInputClient::SetState(ClientState new_state) { |
| DVLOG(4) << "Changing state to " << new_state; |
| note_->Notify(new_state); |
| } |
| -void VEANoInputClient::FeedEncoderWithOutput(base::SharedMemory* shm, |
| - size_t output_size) { |
| +void VEACacheLineUnalignedInputClient::FeedEncoderWithOutput( |
| + base::SharedMemory* shm, |
| + size_t output_size) { |
| if (!has_encoder()) |
| return; |
| @@ -1908,6 +2060,32 @@ void VEANoInputClient::FeedEncoderWithOutput(base::SharedMemory* shm, |
| encoder_->UseOutputBitstreamBuffer(bitstream_buffer); |
| } |
| +void VEACacheLineUnalignedInputClient::FeedEncoderWithOneInput( |
| + const gfx::Size& input_coded_size) { |
| + if (!has_encoder()) |
| + return; |
| + |
| + std::vector<char, AlignedAllocator<char, kPlatformBufferAlignment>> |
| + aligned_in_file_data(VideoFrame::AllocationSize(PIXEL_FORMAT_I420, |
| + input_coded_size)); |
| + |
| + uint8_t* frame_data_y = reinterpret_cast<uint8_t*>(&aligned_in_file_data[0]); |
| + uint8_t* frame_data_u = frame_data_y + |
| + VideoFrame::PlaneSize(PIXEL_FORMAT_I420, 0, input_coded_size).GetArea(); |
| + uint8_t* frame_data_v = frame_data_u + |
| + VideoFrame::PlaneSize(PIXEL_FORMAT_I420, 1, input_coded_size).GetArea(); |
| + |
| + scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData( |
| + PIXEL_FORMAT_I420, input_coded_size, gfx::Rect(input_coded_size), |
| + input_coded_size, input_coded_size.width(), |
| + input_coded_size.width() / 2, input_coded_size.width() / 2, |
| + frame_data_y, frame_data_u, frame_data_v, |
| + base::TimeDelta().FromMilliseconds( |
| + base::Time::kMillisecondsPerSecond / default_fps_)); |
| + |
| + encoder_->Encode(video_frame, false); |
| +} |
| + |
| // Test parameters: |
| // - Number of concurrent encoders. The value takes effect when there is only |
| // one input stream; otherwise, one encoder per input stream will be |
| @@ -2076,6 +2254,41 @@ TEST(VEANoInputTest, CheckOutput) { |
| encoder_thread.Stop(); |
| } |
| +TEST(VEACacheLineUnalignedInputTest, CheckOutput) { |
| + std::unique_ptr<ClientStateNotification<ClientState>> note( |
| + new ClientStateNotification<ClientState>()); |
| + std::unique_ptr<VEACacheLineUnalignedInputClient> client( |
| + new VEACacheLineUnalignedInputClient(note.get())); |
| + base::Thread encoder_thread("EncoderThread"); |
| + ASSERT_TRUE(encoder_thread.Start()); |
| + |
| + encoder_thread.task_runner()->PostTask( |
| + FROM_HERE, base::Bind(&VEACacheLineUnalignedInputClient::CreateEncoder, |
| + base::Unretained(client.get()))); |
| + |
| + // Encoder must pass through states in this order. |
| + enum ClientState state_transitions[] = {CS_INITIALIZED, CS_ENCODING, |
| + CS_FINISHED}; |
| + |
| + ClientState expected_state, wait_state; |
| + for (const auto& state : state_transitions) { |
| + expected_state = state; |
| + wait_state = note->Wait(); |
| + if (expected_state != wait_state) { |
| + break; |
| + } |
| + } |
| + |
| + encoder_thread.task_runner()->PostTask( |
| + FROM_HERE, base::Bind(&VEACacheLineUnalignedInputClient::DestroyEncoder, |
| + base::Unretained(client.get()))); |
| + |
| + // This ensures all tasks have finished. |
| + encoder_thread.Stop(); |
| + |
| + ASSERT_EQ(expected_state, wait_state); |
| +} |
| + |
| #elif defined(OS_MACOSX) || defined(OS_WIN) |
| INSTANTIATE_TEST_CASE_P( |
| SimpleEncode, |