| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdint.h> |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "components/viz/test/test_context_provider.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/video_frame.h" |
| #include "media/video/gpu_memory_buffer_video_frame_pool.h" |
| #include "media/video/mock_gpu_video_accelerator_factories.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "ui/gfx/buffer_format_util.h" |
| |
| using ::testing::_; |
| using ::testing::AtLeast; |
| |
| namespace media { |
| |
| class GpuMemoryBufferVideoFramePoolTest : public ::testing::Test { |
| public: |
| GpuMemoryBufferVideoFramePoolTest() = default; |
| void SetUp() override { |
| // Seed test clock with some dummy non-zero value to avoid confusion with |
| // empty base::TimeTicks values. |
| test_clock_.Advance(base::Seconds(1234)); |
| |
| sii_ = std::make_unique<viz::TestSharedImageInterface>(); |
| media_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>(); |
| copy_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>(); |
| media_task_runner_handle_ = |
| std::make_unique<base::ThreadTaskRunnerHandle>(media_task_runner_); |
| mock_gpu_factories_ = |
| std::make_unique<MockGpuVideoAcceleratorFactories>(sii_.get()); |
| gpu_memory_buffer_pool_ = std::make_unique<GpuMemoryBufferVideoFramePool>( |
| media_task_runner_, copy_task_runner_.get(), mock_gpu_factories_.get()); |
| gpu_memory_buffer_pool_->SetTickClockForTesting(&test_clock_); |
| } |
| |
| void TearDown() override { |
| gpu_memory_buffer_pool_.reset(); |
| RunUntilIdle(); |
| mock_gpu_factories_.reset(); |
| |
| if (y_data_) |
| delete[] y_data_; |
| if (u_data_) |
| delete[] u_data_; |
| if (v_data_) |
| delete[] v_data_; |
| if (uv_data_) |
| delete[] uv_data_; |
| } |
| |
| void RunUntilIdle() { |
| media_task_runner_->RunUntilIdle(); |
| copy_task_runner_->RunUntilIdle(); |
| media_task_runner_->RunUntilIdle(); |
| } |
| |
| static scoped_refptr<VideoFrame> CreateTestYUVVideoFrame( |
| int dimension, |
| size_t bit_depth = 8, |
| int visible_rect_crop = 0) { |
| const int kDimension = 10; |
| // Data buffers are overdimensioned to acommodate up to 16bpc samples. |
| static uint8_t y_data[2 * kDimension * kDimension] = {0}; |
| static uint8_t u_data[2 * kDimension * kDimension / 4] = {0}; |
| static uint8_t v_data[2 * kDimension * kDimension / 4] = {0}; |
| |
| const VideoPixelFormat format = |
| (bit_depth > 8) ? PIXEL_FORMAT_YUV420P10 : PIXEL_FORMAT_I420; |
| const int multiplier = format == PIXEL_FORMAT_YUV420P10 ? 2 : 1; |
| DCHECK_LE(dimension, kDimension); |
| const gfx::Size size(dimension, dimension); |
| |
| scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData( |
| format, // format |
| size, // coded_size |
| gfx::Rect(visible_rect_crop, visible_rect_crop, |
| size.width() - visible_rect_crop, |
| size.height() - visible_rect_crop), // visible_rect |
| size, // natural_size |
| size.width() * multiplier, // y_stride |
| size.width() * multiplier / 2, // u_stride |
| size.width() * multiplier / 2, // v_stride |
| y_data, // y_data |
| u_data, // u_data |
| v_data, // v_data |
| base::TimeDelta()); // timestamp |
| EXPECT_TRUE(video_frame); |
| return video_frame; |
| } |
| |
| scoped_refptr<VideoFrame> CreateTestYUVVideoFrameWithOddSize( |
| int dimension, |
| size_t bit_depth = 8, |
| int visible_rect_crop = 0) { |
| const VideoPixelFormat format = |
| (bit_depth > 8) ? PIXEL_FORMAT_YUV420P10 : PIXEL_FORMAT_I420; |
| const int multiplier = format == PIXEL_FORMAT_YUV420P10 ? 2 : 1; |
| |
| int dimension_aligned = (dimension + 1) & ~1; |
| y_data_ = new uint8_t[multiplier * dimension * dimension](); |
| u_data_ = |
| new uint8_t[multiplier * dimension_aligned * dimension_aligned / 4](); |
| v_data_ = |
| new uint8_t[multiplier * dimension_aligned * dimension_aligned / 4](); |
| |
| // Initialize the last pixel of each plane |
| int y_size = multiplier * dimension * dimension; |
| y_data_[y_size - multiplier] = kYValue; |
| int u_v_size = multiplier * dimension_aligned * dimension_aligned / 4; |
| u_data_[u_v_size - multiplier] = kUValue; |
| v_data_[u_v_size - multiplier] = kVValue; |
| |
| const gfx::Size size(dimension, dimension); |
| scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData( |
| format, // format |
| size, // coded_size |
| gfx::Rect(visible_rect_crop, visible_rect_crop, |
| size.width() - visible_rect_crop * 2, |
| size.height() - visible_rect_crop * 2), // visible_rect |
| size, // natural_size |
| size.width() * multiplier, // y_stride |
| dimension_aligned * multiplier / 2, // u_stride |
| dimension_aligned * multiplier / 2, // v_stride |
| y_data_, // y_data |
| u_data_, // u_data |
| v_data_, // v_data |
| base::TimeDelta()); // timestamp |
| EXPECT_TRUE(video_frame); |
| return video_frame; |
| } |
| |
| static scoped_refptr<VideoFrame> CreateTestYUVAVideoFrame(int dimension) { |
| const int kDimension = 10; |
| static uint8_t y_data[kDimension * kDimension] = {0}; |
| static uint8_t u_data[kDimension * kDimension / 4] = {0}; |
| static uint8_t v_data[kDimension * kDimension / 4] = {0}; |
| static uint8_t a_data[kDimension * kDimension] = {0}; |
| |
| constexpr VideoPixelFormat format = PIXEL_FORMAT_I420A; |
| DCHECK_LE(dimension, kDimension); |
| const gfx::Size size(dimension, dimension); |
| |
| scoped_refptr<VideoFrame> video_frame = |
| VideoFrame::WrapExternalYuvaData(format, // format |
| size, // coded_size |
| gfx::Rect(size), // visible_rect |
| size, // natural_size |
| size.width(), // y_stride |
| size.width() / 2, // u_stride |
| size.width() / 2, // v_stride |
| size.width(), // a_stride |
| y_data, // y_data |
| u_data, // u_data |
| v_data, // v_data |
| a_data, // a_data |
| base::TimeDelta()); // timestamp |
| EXPECT_TRUE(video_frame); |
| return video_frame; |
| } |
| |
| static scoped_refptr<VideoFrame> CreateTestNV12VideoFrame(int dimension) { |
| // Set the video buffer memory dimension default to 10. |
| const int kDimension = 10; |
| static uint8_t y_data[kDimension * kDimension] = {0}; |
| // Subsampled by 2x2, two components. |
| static uint8_t uv_data[kDimension * kDimension / 2] = {0}; |
| |
| const VideoPixelFormat format = PIXEL_FORMAT_NV12; |
| DCHECK_LE(dimension, kDimension); |
| const gfx::Size size(dimension, dimension); |
| |
| scoped_refptr<VideoFrame> video_frame = |
| VideoFrame::WrapExternalYuvData(format, // format |
| size, // coded_size |
| gfx::Rect(size), // visible_rect |
| size, // natural_size |
| size.width(), // y_stride |
| size.width(), // uv_stride |
| y_data, // y_data |
| uv_data, // uv_data |
| base::TimeDelta()); // timestamp |
| EXPECT_TRUE(video_frame); |
| return video_frame; |
| } |
| |
| scoped_refptr<VideoFrame> CreateTestNV12VideoFrameWithOddSize(int dimension) { |
| // Set the video buffer memory dimension to the same size of the requested |
| // dimension. |
| int dimension_aligned = (dimension + 1) & ~1; |
| y_data_ = new uint8_t[dimension * dimension](); |
| // Subsampled by 2x2, two components. |
| uv_data_ = new uint8_t[dimension_aligned * dimension_aligned / 2](); |
| |
| // Initialize the last pixel of each plane |
| y_data_[dimension * dimension - 1] = kYValue; |
| uv_data_[(dimension_aligned * dimension_aligned / 2) - 2] = kUValue; |
| uv_data_[(dimension_aligned * dimension_aligned / 2) - 1] = kVValue; |
| |
| const VideoPixelFormat format = PIXEL_FORMAT_NV12; |
| const gfx::Size size(dimension, dimension); |
| |
| scoped_refptr<VideoFrame> video_frame = |
| VideoFrame::WrapExternalYuvData(format, // format |
| size, // coded_size |
| gfx::Rect(size), // visible_rect |
| size, // natural_size |
| size.width(), // y_stride |
| dimension_aligned, // uv_stride |
| y_data_, // y_data |
| uv_data_, // uv_data |
| base::TimeDelta()); // timestamp |
| EXPECT_TRUE(video_frame); |
| return video_frame; |
| } |
| |
| // Note, the X portion is set to 1 since it may use ARGB instead of |
| // XRGB on some platforms. |
| uint32_t as_xr30(uint32_t r, uint32_t g, uint32_t b) { |
| return (3 << 30) | (r << 20) | (g << 10) | b; |
| } |
| uint32_t as_xb30(uint32_t r, uint32_t g, uint32_t b) { |
| return (3 << 30) | (b << 20) | (g << 10) | r; |
| } |
| |
| protected: |
| static constexpr uint8_t kYValue = 210; |
| static constexpr uint8_t kUValue = 50; |
| static constexpr uint8_t kVValue = 150; |
| |
| uint8_t* y_data_ = nullptr; |
| uint8_t* u_data_ = nullptr; |
| uint8_t* v_data_ = nullptr; |
| uint8_t* uv_data_ = nullptr; |
| |
| base::SimpleTestTickClock test_clock_; |
| std::unique_ptr<MockGpuVideoAcceleratorFactories> mock_gpu_factories_; |
| std::unique_ptr<GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool_; |
| scoped_refptr<base::TestSimpleTaskRunner> media_task_runner_; |
| scoped_refptr<base::TestSimpleTaskRunner> copy_task_runner_; |
| // GpuMemoryBufferVideoFramePool uses BindToCurrentLoop(), which requires |
| // ThreadTaskRunnerHandle initialization. |
| std::unique_ptr<base::ThreadTaskRunnerHandle> media_task_runner_handle_; |
| std::unique_ptr<viz::TestSharedImageInterface> sii_; |
| }; |
| |
| void MaybeCreateHardwareFrameCallback( |
| scoped_refptr<VideoFrame>* video_frame_output, |
| scoped_refptr<VideoFrame> video_frame) { |
| *video_frame_output = std::move(video_frame); |
| } |
| |
| void MaybeCreateHardwareFrameCallbackAndTrackTime( |
| scoped_refptr<VideoFrame>* video_frame_output, |
| base::TimeTicks* output_time, |
| scoped_refptr<VideoFrame> video_frame) { |
| *video_frame_output = std::move(video_frame); |
| *output_time = base::TimeTicks::Now(); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, VideoFrameOutputFormatUnknown) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareFrame) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_I420, frame->format()); |
| EXPECT_EQ(3u, frame->NumTextures()); |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareFrameWithOddSize) { |
| scoped_refptr<VideoFrame> software_frame = |
| CreateTestYUVVideoFrameWithOddSize(9); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| if (gfx::IsOddWidthMultiPlanarBuffersAllowed() && |
| gfx::IsOddHeightMultiPlanarBuffersAllowed()) { |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_I420, frame->format()); |
| EXPECT_EQ(3u, frame->NumTextures()); |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| |
| EXPECT_EQ(3u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Map(); |
| mock_gpu_factories_->created_memory_buffers()[2]->Map(); |
| |
| const auto* y_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(0)); |
| const auto* u_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[1]->memory(0)); |
| const auto* v_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[2]->memory(0)); |
| |
| // Y plane = 9x9, U and V plan = 5x5. |
| EXPECT_EQ(kYValue, software_frame->visible_data(VideoFrame::kYPlane)[80]); |
| EXPECT_EQ(kUValue, software_frame->visible_data(VideoFrame::kUPlane)[24]); |
| EXPECT_EQ(kVValue, software_frame->visible_data(VideoFrame::kVPlane)[24]); |
| |
| // Compare the last pixel of each plane in |software_frame| and |frame|. |
| auto y_stride = mock_gpu_factories_->created_memory_buffers()[0]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kYPlane)[80], |
| y_memory[y_stride * 8 + 8]); |
| auto u_stride = mock_gpu_factories_->created_memory_buffers()[1]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kUPlane)[24], |
| u_memory[u_stride * 4 + 4]); |
| auto v_stride = mock_gpu_factories_->created_memory_buffers()[2]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kVPlane)[24], |
| v_memory[v_stride * 4 + 4]); |
| |
| mock_gpu_factories_->created_memory_buffers()[0]->Unmap(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Unmap(); |
| mock_gpu_factories_->created_memory_buffers()[2]->Unmap(); |
| |
| } else { |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| } |
| |
| // Tests the current workaround for odd positioned video frame input. Once |
| // https://crbug.com/638906 is fixed, output should be different. |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareFrameWithOddOrigin) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(9, 8, 1); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, |
| CreateOneHardwareFrameWithOddOriginOddSize) { |
| scoped_refptr<VideoFrame> software_frame = |
| CreateTestYUVVideoFrameWithOddSize(11, 8, 1); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOne10BppHardwareFrame) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10, 10); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_I420, frame->format()); |
| EXPECT_EQ(3u, frame->NumTextures()); |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, |
| CreateOne10BppHardwareFrameWithOddSize) { |
| scoped_refptr<VideoFrame> software_frame = |
| CreateTestYUVVideoFrameWithOddSize(17, 10); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| if (gfx::IsOddWidthMultiPlanarBuffersAllowed() && |
| gfx::IsOddHeightMultiPlanarBuffersAllowed()) { |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_I420, frame->format()); |
| EXPECT_EQ(3u, frame->NumTextures()); |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| |
| EXPECT_EQ(3u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Map(); |
| mock_gpu_factories_->created_memory_buffers()[2]->Map(); |
| |
| // Copy 10 bpp to I420. |
| const auto* y_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(0)); |
| const auto* u_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[1]->memory(0)); |
| const auto* v_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[2]->memory(0)); |
| |
| const uint16_t* y_plane_data = reinterpret_cast<uint16_t*>( |
| software_frame->visible_data(VideoFrame::kYPlane)); |
| const uint16_t* u_plane_data = reinterpret_cast<uint16_t*>( |
| software_frame->visible_data(VideoFrame::kUPlane)); |
| const uint16_t* v_plane_data = reinterpret_cast<uint16_t*>( |
| software_frame->visible_data(VideoFrame::kVPlane)); |
| |
| // Y plane = 17x17 = 289, U and V plan = 9x9. |
| EXPECT_EQ(kYValue, y_plane_data[288]); |
| EXPECT_EQ(kUValue, u_plane_data[80]); |
| EXPECT_EQ(kVValue, v_plane_data[80]); |
| |
| // Compare the last pixel of each plane in |software_frame| and |frame|. |
| // y_memory = 17x17, u_memory/v_memory = 9x9. scale = 10 bits - 8 bits = 2 |
| auto y_stride = mock_gpu_factories_->created_memory_buffers()[0]->stride(0); |
| EXPECT_EQ(y_plane_data[288] >> 2, y_memory[y_stride * 16 + 16]); |
| auto u_stride = mock_gpu_factories_->created_memory_buffers()[1]->stride(0); |
| EXPECT_EQ(u_plane_data[80] >> 2, u_memory[u_stride * 8 + 8]); |
| auto v_stride = mock_gpu_factories_->created_memory_buffers()[2]->stride(0); |
| EXPECT_EQ(v_plane_data[80] >> 2, v_memory[v_stride * 8 + 8]); |
| |
| mock_gpu_factories_->created_memory_buffers()[0]->Unmap(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Unmap(); |
| mock_gpu_factories_->created_memory_buffers()[2]->Unmap(); |
| |
| } else { |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, ReuseFirstResource) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| gpu::Mailbox mailbox = frame->mailbox_holder(0).mailbox; |
| const gpu::SyncToken sync_token = frame->mailbox_holder(0).sync_token; |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| |
| scoped_refptr<VideoFrame> frame2; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame2)); |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame2.get()); |
| EXPECT_NE(mailbox, frame2->mailbox_holder(0).mailbox); |
| EXPECT_EQ(6u, sii_->shared_image_count()); |
| |
| frame = nullptr; |
| frame2 = nullptr; |
| RunUntilIdle(); |
| |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(6u, sii_->shared_image_count()); |
| EXPECT_EQ(frame->mailbox_holder(0).mailbox, mailbox); |
| EXPECT_NE(frame->mailbox_holder(0).sync_token, sync_token); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, DropResourceWhenSizeIsDifferent) { |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| CreateTestYUVVideoFrame(10), |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| // Check that the mailboxes in the VideoFrame were properly created. |
| gpu::Mailbox old_mailboxes[3]; |
| for (size_t i = 0; i < 3; ++i) { |
| old_mailboxes[i] = frame->mailbox_holder(i).mailbox; |
| EXPECT_TRUE(sii_->CheckSharedImageExists(old_mailboxes[i])); |
| } |
| |
| frame = nullptr; |
| RunUntilIdle(); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| CreateTestYUVVideoFrame(4), |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| RunUntilIdle(); |
| // Check that the mailboxes in the old VideoFrame were properly destroyed. |
| for (const auto& mailbox : old_mailboxes) |
| EXPECT_FALSE(sii_->CheckSharedImageExists(mailbox)); |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| // Check that the mailboxes in the new VideoFrame were properly created. |
| for (size_t i = 0; i < 3; ++i) |
| EXPECT_TRUE(sii_->CheckSharedImageExists(frame->mailbox_holder(i).mailbox)); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareNV12Frame) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::NV12_SINGLE_GMB); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_NV12, frame->format()); |
| if (GpuMemoryBufferVideoFramePool::MultiPlaneVideoSharedImagesEnabled()) { |
| EXPECT_EQ(2u, frame->NumTextures()); |
| EXPECT_EQ(2u, sii_->shared_image_count()); |
| } else { |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| } |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, |
| CreateOneHardwareNV12FrameWithOddSize) { |
| scoped_refptr<VideoFrame> software_frame = |
| CreateTestYUVVideoFrameWithOddSize(13); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::NV12_SINGLE_GMB); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| if (gfx::IsOddWidthMultiPlanarBuffersAllowed() && |
| gfx::IsOddHeightMultiPlanarBuffersAllowed()) { |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_NV12, frame->format()); |
| if (GpuMemoryBufferVideoFramePool::MultiPlaneVideoSharedImagesEnabled()) { |
| EXPECT_EQ(2u, frame->NumTextures()); |
| EXPECT_EQ(2u, sii_->shared_image_count()); |
| |
| EXPECT_EQ(1u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| |
| const auto* y_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(0)); |
| const auto* uv_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(1)); |
| |
| // Y plane = 13x13 = 169, U and V plan = 7x7 = 49. |
| EXPECT_EQ(kYValue, |
| software_frame->visible_data(VideoFrame::kYPlane)[168]); |
| EXPECT_EQ(kUValue, software_frame->visible_data(VideoFrame::kUPlane)[48]); |
| EXPECT_EQ(kVValue, software_frame->visible_data(VideoFrame::kVPlane)[48]); |
| |
| // Compare the last pixel of each plane in |software_frame| and |frame|. |
| // y_memory = 13x13, uv_memory = 14x 7. |
| auto y_stride = |
| mock_gpu_factories_->created_memory_buffers()[0]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kYPlane)[168], |
| y_memory[y_stride * 12 + 12]); |
| auto uv_stride = |
| mock_gpu_factories_->created_memory_buffers()[0]->stride(1); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kUPlane)[48], |
| uv_memory[uv_stride * 6 + 12]); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kVPlane)[48], |
| uv_memory[uv_stride * 6 + 13]); |
| |
| mock_gpu_factories_->created_memory_buffers()[0]->Unmap(); |
| } else { |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| } |
| |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| |
| } else { |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareNV12Frame2) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::NV12_DUAL_GMB); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_NV12, frame->format()); |
| EXPECT_EQ(2u, frame->NumTextures()); |
| EXPECT_EQ(2u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, |
| CreateOneHardwareNV12Frame2WithOddSize) { |
| scoped_refptr<VideoFrame> software_frame = |
| CreateTestYUVVideoFrameWithOddSize(5); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::NV12_DUAL_GMB); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| if (gfx::IsOddWidthMultiPlanarBuffersAllowed() && |
| gfx::IsOddHeightMultiPlanarBuffersAllowed()) { |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_NV12, frame->format()); |
| EXPECT_EQ(2u, frame->NumTextures()); |
| EXPECT_EQ(2u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| |
| EXPECT_EQ(2u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Map(); |
| |
| const auto* y_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(0)); |
| const auto* uv_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[1]->memory(0)); |
| |
| // Y plane = 5x5, U and V plan = 3x3. |
| EXPECT_EQ(kYValue, software_frame->visible_data(VideoFrame::kYPlane)[24]); |
| EXPECT_EQ(kUValue, software_frame->visible_data(VideoFrame::kUPlane)[8]); |
| EXPECT_EQ(kVValue, software_frame->visible_data(VideoFrame::kVPlane)[8]); |
| |
| // Compare the last pixel of each plane in |software_frame| and |frame|. |
| // y_memory = 5x5, uv_memory = 6x3. |
| auto y_stride = mock_gpu_factories_->created_memory_buffers()[0]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kYPlane)[24], |
| y_memory[y_stride * 4 + 4]); |
| auto uv_stride = |
| mock_gpu_factories_->created_memory_buffers()[1]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kUPlane)[8], |
| uv_memory[uv_stride * 2 + 4]); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kVPlane)[8], |
| uv_memory[uv_stride * 2 + 5]); |
| |
| mock_gpu_factories_->created_memory_buffers()[0]->Unmap(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Unmap(); |
| } else { |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareFrameForNV12Input) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestNV12VideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::NV12_DUAL_GMB); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_NV12, frame->format()); |
| EXPECT_EQ(2u, frame->NumTextures()); |
| EXPECT_EQ(2u, sii_->shared_image_count()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, |
| CreateOneHardwareFrameForNV12InputWithOddSize) { |
| scoped_refptr<VideoFrame> software_frame = |
| CreateTestNV12VideoFrameWithOddSize(135); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::NV12_DUAL_GMB); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| if (gfx::IsOddWidthMultiPlanarBuffersAllowed() && |
| gfx::IsOddHeightMultiPlanarBuffersAllowed()) { |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_NV12, frame->format()); |
| EXPECT_EQ(2u, frame->NumTextures()); |
| EXPECT_EQ(2u, sii_->shared_image_count()); |
| |
| EXPECT_EQ(2u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Map(); |
| |
| const auto* y_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(0)); |
| const auto* uv_memory = reinterpret_cast<uint8_t*>( |
| mock_gpu_factories_->created_memory_buffers()[1]->memory(0)); |
| |
| // Y plane = 135x135 = 18225, UV plan = 136x68 = 9248. |
| EXPECT_EQ(kYValue, |
| software_frame->visible_data(VideoFrame::kYPlane)[18224]); |
| EXPECT_EQ(kUValue, |
| software_frame->visible_data(VideoFrame::kUVPlane)[9246]); |
| EXPECT_EQ(kVValue, |
| software_frame->visible_data(VideoFrame::kUVPlane)[9247]); |
| |
| // Compare the last pixel of each plane in |software_frame| and |frame|. |
| // y_memory = 135x135, uv_memory = 136x68. |
| auto y_stride = mock_gpu_factories_->created_memory_buffers()[0]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kYPlane)[18224], |
| y_memory[y_stride * 134 + 134]); |
| auto uv_stride = |
| mock_gpu_factories_->created_memory_buffers()[1]->stride(0); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kUVPlane)[9246], |
| uv_memory[uv_stride * 67 + 134]); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kUVPlane)[9247], |
| uv_memory[uv_stride * 67 + 135]); |
| |
| mock_gpu_factories_->created_memory_buffers()[0]->Unmap(); |
| mock_gpu_factories_->created_memory_buffers()[1]->Unmap(); |
| |
| } else { |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareXR30Frame) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10, 10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::XR30); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_XR30, frame->format()); |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| |
| EXPECT_EQ(1u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| |
| void* memory = mock_gpu_factories_->created_memory_buffers()[0]->memory(0); |
| EXPECT_EQ(as_xr30(0, 311, 0), *static_cast<uint32_t*>(memory)); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareP010Frame) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10, 10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::P010); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_P016LE, frame->format()); |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| |
| EXPECT_EQ(1u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| |
| const uint16_t* y_memory = reinterpret_cast<uint16_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(0)); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kYPlane)[0] << 6, |
| y_memory[0]); |
| const uint16_t* uv_memory = reinterpret_cast<uint16_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(1)); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kUPlane)[0] << 6, |
| uv_memory[0]); |
| EXPECT_EQ(software_frame->visible_data(VideoFrame::kVPlane)[0] << 6, |
| uv_memory[1]); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, |
| CreateOneHardwareP010FrameWithOddSize) { |
| scoped_refptr<VideoFrame> software_frame = |
| CreateTestYUVVideoFrameWithOddSize(7, 10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::P010); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| if (gfx::IsOddWidthMultiPlanarBuffersAllowed() && |
| gfx::IsOddHeightMultiPlanarBuffersAllowed()) { |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_P016LE, frame->format()); |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| |
| EXPECT_EQ(1u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| |
| // Copy I010 To P010. |
| const uint16_t* y_memory = reinterpret_cast<uint16_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(0)); |
| const uint16_t* uv_memory = reinterpret_cast<uint16_t*>( |
| mock_gpu_factories_->created_memory_buffers()[0]->memory(1)); |
| |
| const uint16_t* y_plane_data = reinterpret_cast<uint16_t*>( |
| software_frame->visible_data(VideoFrame::kYPlane)); |
| const uint16_t* u_plane_data = reinterpret_cast<uint16_t*>( |
| software_frame->visible_data(VideoFrame::kUPlane)); |
| const uint16_t* v_plane_data = reinterpret_cast<uint16_t*>( |
| software_frame->visible_data(VideoFrame::kVPlane)); |
| |
| // Y plane = 7x7 = 49, U and V plan = 4x4 = 16. |
| EXPECT_EQ(kYValue, y_plane_data[48]); |
| EXPECT_EQ(kUValue, u_plane_data[15]); |
| EXPECT_EQ(kVValue, v_plane_data[15]); |
| // Compare the last pixel of each plane in |software_frame| and |frame|. |
| // y_memory = 7x7, uv_memory = 8x4, scale = 16-10 = 6. |
| auto y_stride = mock_gpu_factories_->created_memory_buffers()[0]->stride(0); |
| EXPECT_EQ(y_plane_data[48], y_memory[y_stride / 2 * 6 + 6] >> 6); |
| auto uv_stride = |
| mock_gpu_factories_->created_memory_buffers()[0]->stride(1); |
| EXPECT_EQ(u_plane_data[15], uv_memory[uv_stride / 2 * 3 + 6] >> 6); |
| EXPECT_EQ(v_plane_data[15], uv_memory[uv_stride / 2 * 3 + 7] >> 6); |
| |
| mock_gpu_factories_->created_memory_buffers()[0]->Unmap(); |
| } else { |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| } |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareXR30FrameBT709) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10, 10); |
| software_frame->set_color_space(gfx::ColorSpace::CreateREC709()); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::XR30); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_XR30, frame->format()); |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| |
| EXPECT_EQ(1u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| |
| void* memory = mock_gpu_factories_->created_memory_buffers()[0]->memory(0); |
| EXPECT_EQ(as_xr30(0, 311, 0), *static_cast<uint32_t*>(memory)); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareXR30FrameBT601) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10, 10); |
| software_frame->set_color_space(gfx::ColorSpace::CreateREC601()); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::XR30); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_XR30, frame->format()); |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| |
| EXPECT_EQ(1u, mock_gpu_factories_->created_memory_buffers().size()); |
| mock_gpu_factories_->created_memory_buffers()[0]->Map(); |
| |
| void* memory = mock_gpu_factories_->created_memory_buffers()[0]->memory(0); |
| EXPECT_EQ(as_xr30(0, 543, 0), *static_cast<uint32_t*>(memory)); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareXB30Frame) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10, 10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::XB30); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_XB30, frame->format()); |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareRGBAFrame) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVAVideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::RGBA); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_EQ(PIXEL_FORMAT_ABGR, frame->format()); |
| EXPECT_EQ(1u, frame->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame->metadata().read_lock_fences_enabled); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, PreservesMetadata) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| software_frame->metadata().end_of_stream = true; |
| base::TimeTicks kTestReferenceTime = |
| base::Milliseconds(12345) + base::TimeTicks(); |
| software_frame->metadata().reference_time = kTestReferenceTime; |
| scoped_refptr<VideoFrame> frame; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame.get(), frame.get()); |
| EXPECT_TRUE(frame->metadata().end_of_stream); |
| EXPECT_EQ(kTestReferenceTime, *frame->metadata().reference_time); |
| } |
| |
| // CreateGpuMemoryBuffer can return null (e.g: when the GPU process is down). |
| // This test checks that in that case we don't crash and don't create the |
| // textures. |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateGpuMemoryBufferFail) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetFailToAllocateGpuMemoryBufferForTesting(true); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| |
| RunUntilIdle(); |
| |
| // Software frame should be returned if mapping fails. |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| EXPECT_EQ(0u, sii_->shared_image_count()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, |
| CreateGpuMemoryBufferFailAfterShutdown) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame; |
| mock_gpu_factories_->SetFailToMapGpuMemoryBufferForTesting(true); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame)); |
| gpu_memory_buffer_pool_.reset(); |
| RunUntilIdle(); |
| |
| // Software frame should be returned if mapping fails. |
| EXPECT_EQ(software_frame.get(), frame.get()); |
| EXPECT_EQ(0u, sii_->shared_image_count()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, ShutdownReleasesUnusedResources) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_1; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_1)); |
| |
| RunUntilIdle(); |
| EXPECT_NE(software_frame.get(), frame_1.get()); |
| |
| scoped_refptr<VideoFrame> frame_2; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_2)); |
| RunUntilIdle(); |
| EXPECT_NE(software_frame.get(), frame_2.get()); |
| EXPECT_NE(frame_1.get(), frame_2.get()); |
| |
| EXPECT_EQ(6u, sii_->shared_image_count()); |
| |
| // Drop frame and verify that resources are still available for reuse. |
| frame_1 = nullptr; |
| RunUntilIdle(); |
| EXPECT_EQ(6u, sii_->shared_image_count()); |
| |
| // While still holding onto the second frame, destruct the frame pool and |
| // verify that the inner pool releases the resources for the first frame. |
| gpu_memory_buffer_pool_.reset(); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| } |
| |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, StaleFramesAreExpired) { |
| scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_1; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_1)); |
| |
| RunUntilIdle(); |
| EXPECT_NE(software_frame.get(), frame_1.get()); |
| |
| scoped_refptr<VideoFrame> frame_2; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_2)); |
| RunUntilIdle(); |
| EXPECT_NE(software_frame.get(), frame_2.get()); |
| EXPECT_NE(frame_1.get(), frame_2.get()); |
| |
| EXPECT_EQ(6u, sii_->shared_image_count()); |
| |
| // Drop frame and verify that resources are still available for reuse. |
| frame_1 = nullptr; |
| RunUntilIdle(); |
| EXPECT_EQ(6u, sii_->shared_image_count()); |
| |
| // Advance clock far enough to hit stale timer; ensure only frame_1 has its |
| // resources released. |
| test_clock_.Advance(base::Minutes(1)); |
| frame_2 = nullptr; |
| RunUntilIdle(); |
| EXPECT_EQ(3u, sii_->shared_image_count()); |
| } |
| |
| // Test when we request two copies in a row, there should be at most one frame |
| // copy in flight at any time. |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, AtMostOneCopyInFlight) { |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::NV12_SINGLE_GMB); |
| |
| scoped_refptr<VideoFrame> software_frame_1 = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_1; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_1, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_1)); |
| |
| scoped_refptr<VideoFrame> software_frame_2 = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_2; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_2, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_2)); |
| |
| media_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(1u, copy_task_runner_->NumPendingTasks()); |
| copy_task_runner_->RunUntilIdle(); |
| media_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(1u, copy_task_runner_->NumPendingTasks()); |
| RunUntilIdle(); |
| } |
| |
| // Tests that adding a frame that the pool doesn't handle does not break the |
| // FIFO order in tasks. |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, PreservesOrder) { |
| std::vector<scoped_refptr<VideoFrame>> frame_outputs; |
| |
| scoped_refptr<VideoFrame> software_frame_1 = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_1; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_1, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_1)); |
| |
| scoped_refptr<VideoFrame> software_frame_2 = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_2; |
| base::TimeTicks time_2; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_2, |
| base::BindOnce(MaybeCreateHardwareFrameCallbackAndTrackTime, &frame_2, |
| &time_2)); |
| |
| scoped_refptr<VideoFrame> software_frame_3 = VideoFrame::CreateEOSFrame(); |
| scoped_refptr<VideoFrame> frame_3; |
| base::TimeTicks time_3; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_3, |
| base::BindOnce(MaybeCreateHardwareFrameCallbackAndTrackTime, &frame_3, |
| &time_3)); |
| |
| // Queue all the tasks |media_task_runner_|. Make sure that none is early |
| // returned. |
| media_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(frame_1.get()); |
| EXPECT_FALSE(frame_2.get()); |
| EXPECT_FALSE(frame_3.get()); |
| EXPECT_EQ(1u, copy_task_runner_->NumPendingTasks()); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(frame_1.get()); |
| EXPECT_NE(software_frame_1.get(), frame_1.get()); |
| EXPECT_FALSE(frame_2.get()); |
| EXPECT_FALSE(frame_3.get()); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(frame_2.get()); |
| EXPECT_TRUE(frame_3.get()); |
| EXPECT_NE(software_frame_2.get(), frame_2.get()); |
| EXPECT_EQ(software_frame_3.get(), frame_3.get()); |
| EXPECT_LE(time_2, time_3); |
| } |
| |
| // Test that Abort() stops any pending copies. |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, AbortCopies) { |
| scoped_refptr<VideoFrame> software_frame_1 = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_1; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_1, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_1)); |
| |
| scoped_refptr<VideoFrame> software_frame_2 = CreateTestYUVVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_2; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_2, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_2)); |
| |
| media_task_runner_->RunUntilIdle(); |
| EXPECT_GE(1u, copy_task_runner_->NumPendingTasks()); |
| copy_task_runner_->RunUntilIdle(); |
| |
| gpu_memory_buffer_pool_->Abort(); |
| media_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(0u, copy_task_runner_->NumPendingTasks()); |
| RunUntilIdle(); |
| ASSERT_FALSE(frame_2); |
| } |
| |
| // Tests that an I420 VideoFrame after an I420A is ignored, i.e. passed through. |
| // See e.g. https://crbug.com/875158. |
| TEST_F(GpuMemoryBufferVideoFramePoolTest, VideoFrameChangesPixelFormat) { |
| scoped_refptr<VideoFrame> software_frame_1 = CreateTestYUVAVideoFrame(10); |
| scoped_refptr<VideoFrame> frame_1; |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::RGBA); |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_1, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_1)); |
| RunUntilIdle(); |
| |
| EXPECT_NE(software_frame_1.get(), frame_1.get()); |
| EXPECT_EQ(PIXEL_FORMAT_ABGR, frame_1->format()); |
| EXPECT_EQ(1u, frame_1->NumTextures()); |
| EXPECT_EQ(1u, sii_->shared_image_count()); |
| EXPECT_TRUE(frame_1->metadata().read_lock_fences_enabled); |
| |
| scoped_refptr<VideoFrame> software_frame_2 = CreateTestYUVVideoFrame(10); |
| mock_gpu_factories_->SetVideoFrameOutputFormat( |
| media::GpuVideoAcceleratorFactories::OutputFormat::I420); |
| scoped_refptr<VideoFrame> frame_2; |
| gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| software_frame_2, |
| base::BindOnce(MaybeCreateHardwareFrameCallback, &frame_2)); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(software_frame_2.get(), frame_2.get()); |
| } |
| |
| } // namespace media |