[M120 Migration][MM] Framerate calculation
[platform/framework/web/chromium-efl.git] / media / mojo / mojom / video_frame_mojom_traits_unittest.cc
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/mojo/mojom/video_frame_mojom_traits.h"
6
7 #include "base/functional/callback_helpers.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/test/task_environment.h"
10 #include "build/build_config.h"
11 #include "gpu/command_buffer/common/mailbox.h"
12 #include "gpu/command_buffer/common/mailbox_holder.h"
13 #include "gpu/command_buffer/common/sync_token.h"
14 #include "media/base/color_plane_layout.h"
15 #include "media/base/video_frame.h"
16 #include "media/mojo/mojom/traits_test_service.mojom.h"
17 #include "media/video/fake_gpu_memory_buffer.h"
18 #include "mojo/public/cpp/bindings/message.h"
19 #include "mojo/public/cpp/bindings/receiver_set.h"
20 #include "mojo/public/cpp/bindings/remote.h"
21 #include "mojo/public/cpp/system/buffer.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/gfx/geometry/rect.h"
24 #include "ui/gfx/geometry/size.h"
25
26 namespace media {
27
28 namespace {
29
30 class VideoFrameStructTraitsTest : public testing::Test,
31                                    public media::mojom::TraitsTestService {
32  public:
33   VideoFrameStructTraitsTest() = default;
34
35   VideoFrameStructTraitsTest(const VideoFrameStructTraitsTest&) = delete;
36   VideoFrameStructTraitsTest& operator=(const VideoFrameStructTraitsTest&) =
37       delete;
38
39  protected:
40   mojo::Remote<mojom::TraitsTestService> GetTraitsTestRemote() {
41     mojo::Remote<mojom::TraitsTestService> remote;
42     traits_test_receivers_.Add(this, remote.BindNewPipeAndPassReceiver());
43     return remote;
44   }
45
46   bool RoundTrip(scoped_refptr<VideoFrame>* frame) {
47     scoped_refptr<VideoFrame> input = std::move(*frame);
48     mojo::Remote<mojom::TraitsTestService> remote = GetTraitsTestRemote();
49     return remote->EchoVideoFrame(std::move(input), frame);
50   }
51
52  private:
53   void EchoVideoFrame(const scoped_refptr<VideoFrame>& f,
54                       EchoVideoFrameCallback callback) override {
55     // Touch all data in the received frame to ensure that it is valid.
56     if (f && f->IsMappable()) {
57       base::MD5Context md5_context;
58       base::MD5Init(&md5_context);
59       VideoFrame::HashFrameForTesting(&md5_context, *f);
60     }
61
62     std::move(callback).Run(f);
63   }
64
65   base::test::TaskEnvironment task_environment_;
66   mojo::ReceiverSet<TraitsTestService> traits_test_receivers_;
67 };
68
69 }  // namespace
70
71 TEST_F(VideoFrameStructTraitsTest, Null) {
72   scoped_refptr<VideoFrame> frame;
73
74   ASSERT_TRUE(RoundTrip(&frame));
75   EXPECT_FALSE(frame);
76 }
77
78 TEST_F(VideoFrameStructTraitsTest, EOS) {
79   scoped_refptr<VideoFrame> frame = VideoFrame::CreateEOSFrame();
80
81   ASSERT_TRUE(RoundTrip(&frame));
82   ASSERT_TRUE(frame);
83   EXPECT_TRUE(frame->metadata().end_of_stream);
84 }
85
86 TEST_F(VideoFrameStructTraitsTest, MappableVideoFrame) {
87   constexpr VideoFrame::StorageType storage_types[] = {
88       VideoFrame::STORAGE_SHMEM,
89       VideoFrame::STORAGE_OWNED_MEMORY,
90       VideoFrame::STORAGE_UNOWNED_MEMORY,
91   };
92   constexpr VideoPixelFormat formats[] = {PIXEL_FORMAT_I420, PIXEL_FORMAT_NV12};
93   constexpr gfx::Size kCodedSize(100, 100);
94   constexpr gfx::Rect kVisibleRect(kCodedSize);
95   constexpr gfx::Size kNaturalSize = kCodedSize;
96   constexpr double kFrameRate = 42.0;
97   constexpr base::TimeDelta kTimestamp = base::Seconds(100);
98   for (auto format : formats) {
99     for (auto storage_type : storage_types) {
100       scoped_refptr<media::VideoFrame> frame;
101       base::MappedReadOnlyRegion region;
102       if (storage_type == VideoFrame::STORAGE_OWNED_MEMORY) {
103         frame = media::VideoFrame::CreateFrame(format, kCodedSize, kVisibleRect,
104                                                kNaturalSize, kTimestamp);
105       } else {
106         std::vector<int32_t> strides =
107             VideoFrame::ComputeStrides(format, kCodedSize);
108         size_t aggregate_size = 0;
109         size_t sizes[3] = {};
110         for (size_t i = 0; i < strides.size(); ++i) {
111           sizes[i] = media::VideoFrame::Rows(i, format, kCodedSize.height()) *
112                      strides[i];
113           aggregate_size += sizes[i];
114         }
115         region = base::ReadOnlySharedMemoryRegion::Create(aggregate_size);
116         ASSERT_TRUE(region.IsValid());
117
118         uint8_t* data[3] = {};
119         data[0] = const_cast<uint8_t*>(region.mapping.GetMemoryAs<uint8_t>());
120         for (size_t i = 1; i < strides.size(); ++i)
121           data[i] = data[i - 1] + sizes[i];
122
123         strides.resize(3, 0);
124         frame = media::VideoFrame::WrapExternalYuvData(
125             format, kCodedSize, kVisibleRect, kNaturalSize, strides[0],
126             strides[1], strides[2], data[0], data[1], data[2], kTimestamp);
127         if (storage_type == VideoFrame::STORAGE_SHMEM)
128           frame->BackWithSharedMemory(&region.region);
129       }
130
131       ASSERT_TRUE(frame);
132       frame->metadata().frame_rate = kFrameRate;
133       ASSERT_EQ(frame->storage_type(), storage_type);
134       ASSERT_TRUE(RoundTrip(&frame));
135       ASSERT_TRUE(frame);
136       EXPECT_FALSE(frame->metadata().end_of_stream);
137       EXPECT_EQ(frame->format(), format);
138       EXPECT_EQ(*frame->metadata().frame_rate, kFrameRate);
139       EXPECT_EQ(frame->coded_size(), kCodedSize);
140       EXPECT_EQ(frame->visible_rect(), kVisibleRect);
141       EXPECT_EQ(frame->natural_size(), kNaturalSize);
142       EXPECT_EQ(frame->timestamp(), kTimestamp);
143       ASSERT_EQ(frame->storage_type(), VideoFrame::STORAGE_SHMEM);
144       EXPECT_TRUE(frame->shm_region()->IsValid());
145     }
146   }
147 }
148
149 TEST_F(VideoFrameStructTraitsTest, InvalidOffsets) {
150   constexpr auto kFormat = PIXEL_FORMAT_I420;
151
152   // This test works by patching the outgoing mojo message, so choose a size
153   // that's two primes to try and maximize the uniqueness of the values we're
154   // scanning for in the message.
155   constexpr gfx::Size kSize(127, 149);
156
157   auto strides = VideoFrame::ComputeStrides(kFormat, kSize);
158   size_t aggregate_size = 0;
159   size_t sizes[3] = {};
160   for (size_t i = 0; i < strides.size(); ++i) {
161     sizes[i] = VideoFrame::Rows(i, kFormat, kSize.height()) * strides[i];
162     aggregate_size += sizes[i];
163   }
164
165   auto region = base::ReadOnlySharedMemoryRegion::Create(aggregate_size);
166   ASSERT_TRUE(region.IsValid());
167
168   uint8_t* data[3] = {};
169   data[0] = const_cast<uint8_t*>(region.mapping.GetMemoryAs<uint8_t>());
170   for (size_t i = 1; i < strides.size(); ++i) {
171     data[i] = data[i - 1] + sizes[i];
172   }
173
174   auto frame = VideoFrame::WrapExternalYuvData(
175       kFormat, kSize, gfx::Rect(kSize), kSize, strides[0], strides[1],
176       strides[2], data[0], data[1], data[2], base::Seconds(1));
177   ASSERT_TRUE(frame);
178
179   frame->BackWithSharedMemory(&region.region);
180
181   auto message = mojom::VideoFrame::SerializeAsMessage(&frame);
182
183   // Scan for the offsets array in the message body. It will start with an
184   // array header and then have the three offsets matching our frame.
185   base::span<uint32_t> body(
186       reinterpret_cast<uint32_t*>(message.mutable_payload()),
187       message.payload_num_bytes() / sizeof(uint32_t));
188   std::vector<uint32_t> offsets = {
189       static_cast<uint32_t>(data[0] - data[0]),  // offsets[0]
190       static_cast<uint32_t>(data[1] - data[0]),  // offsets[1]
191       static_cast<uint32_t>(data[2] - data[0]),  // offsets[2]
192   };
193
194   bool patched_offsets = false;
195   for (size_t i = 0; i + 3 < body.size(); ++i) {
196     if (body[i] == offsets[0] && body[i + 1] == offsets[1] &&
197         body[i + 2] == offsets[2]) {
198       body[i + 1] = 0xc01db33f;
199       patched_offsets = true;
200       break;
201     }
202   }
203   ASSERT_TRUE(patched_offsets);
204
205   // Required to pass base deserialize checks.
206   mojo::ScopedMessageHandle handle = message.TakeMojoMessage();
207   message = mojo::Message::CreateFromMessageHandle(&handle);
208
209   // Ensure deserialization fails instead of crashing.
210   scoped_refptr<VideoFrame> new_frame;
211   EXPECT_FALSE(mojom::VideoFrame::DeserializeFromMessage(std::move(message),
212                                                          &new_frame));
213 }
214
215 TEST_F(VideoFrameStructTraitsTest, MailboxVideoFrame) {
216   gpu::Mailbox mailbox = gpu::Mailbox::GenerateForSharedImage();
217   gpu::MailboxHolder mailbox_holder[VideoFrame::kMaxPlanes];
218   mailbox_holder[0] = gpu::MailboxHolder(mailbox, gpu::SyncToken(), 0);
219   scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTextures(
220       PIXEL_FORMAT_ARGB, mailbox_holder, VideoFrame::ReleaseMailboxCB(),
221       gfx::Size(100, 100), gfx::Rect(10, 10, 80, 80), gfx::Size(200, 100),
222       base::Seconds(100));
223
224   ASSERT_TRUE(RoundTrip(&frame));
225   ASSERT_TRUE(frame);
226   EXPECT_FALSE(frame->metadata().end_of_stream);
227   EXPECT_EQ(frame->format(), PIXEL_FORMAT_ARGB);
228   EXPECT_EQ(frame->coded_size(), gfx::Size(100, 100));
229   EXPECT_EQ(frame->visible_rect(), gfx::Rect(10, 10, 80, 80));
230   EXPECT_EQ(frame->natural_size(), gfx::Size(200, 100));
231   EXPECT_EQ(frame->timestamp(), base::Seconds(100));
232   ASSERT_TRUE(frame->HasTextures());
233   ASSERT_EQ(frame->mailbox_holder(0).mailbox, mailbox);
234 }
235
236 // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) because
237 // media::FakeGpuMemoryBuffer supports NativePixmapHandle backed
238 // GpuMemoryBufferHandle only. !BUILDFLAG(IS_OZONE) so as to force
239 // GpuMemoryBufferSupport to select gfx::ClientNativePixmapFactoryDmabuf for
240 // gfx::ClientNativePixmapFactory.
241 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_OZONE)
242 TEST_F(VideoFrameStructTraitsTest, GpuMemoryBufferVideoFrame) {
243   gfx::Size coded_size = gfx::Size(256, 256);
244   gfx::Rect visible_rect(coded_size);
245   auto timestamp = base::Milliseconds(1);
246   std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
247       std::make_unique<FakeGpuMemoryBuffer>(
248           coded_size, gfx::BufferFormat::YUV_420_BIPLANAR);
249   gfx::BufferFormat expected_gmb_format = gmb->GetFormat();
250   gfx::Size expected_gmb_size = gmb->GetSize();
251   gpu::MailboxHolder mailbox_holders[media::VideoFrame::kMaxPlanes] = {
252       gpu::MailboxHolder(gpu::Mailbox::GenerateForSharedImage(),
253                          gpu::SyncToken(), 5),
254       gpu::MailboxHolder(gpu::Mailbox::GenerateForSharedImage(),
255                          gpu::SyncToken(), 10)};
256   auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
257       visible_rect, visible_rect.size(), std::move(gmb), mailbox_holders,
258       base::NullCallback(), timestamp);
259   ASSERT_TRUE(RoundTrip(&frame));
260   ASSERT_TRUE(frame);
261   ASSERT_EQ(frame->storage_type(), VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
262   EXPECT_TRUE(frame->HasGpuMemoryBuffer());
263   EXPECT_FALSE(frame->metadata().end_of_stream);
264   EXPECT_EQ(frame->format(), PIXEL_FORMAT_NV12);
265   EXPECT_EQ(frame->coded_size(), coded_size);
266   EXPECT_EQ(frame->visible_rect(), visible_rect);
267   EXPECT_EQ(frame->natural_size(), visible_rect.size());
268   EXPECT_EQ(frame->timestamp(), timestamp);
269   ASSERT_TRUE(frame->HasTextures());
270   EXPECT_EQ(frame->mailbox_holder(0).mailbox, mailbox_holders[0].mailbox);
271   EXPECT_EQ(frame->mailbox_holder(1).mailbox, mailbox_holders[1].mailbox);
272   EXPECT_EQ(frame->GetGpuMemoryBuffer()->GetFormat(), expected_gmb_format);
273   EXPECT_EQ(frame->GetGpuMemoryBuffer()->GetSize(), expected_gmb_size);
274 }
275 #endif  // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) &&
276         // !BUILDFLAG(IS_OZONE)
277 }  // namespace media