1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "CCodecBuffers.h"
18
19 #include <gtest/gtest.h>
20
21 #include <codec2/hidl/client.h>
22 #include <media/stagefright/MediaCodecConstants.h>
23
24 #include <C2BlockInternal.h>
25 #include <C2PlatformSupport.h>
26 #include <Codec2Mapper.h>
27
28 namespace android {
29
GetRawGraphicOutputBuffers(int32_t width,int32_t height)30 static std::shared_ptr<RawGraphicOutputBuffers> GetRawGraphicOutputBuffers(
31 int32_t width, int32_t height) {
32 std::shared_ptr<RawGraphicOutputBuffers> buffers =
33 std::make_shared<RawGraphicOutputBuffers>("test");
34 sp<AMessage> format{new AMessage};
35 format->setInt32(KEY_WIDTH, width);
36 format->setInt32(KEY_HEIGHT, height);
37 buffers->setFormat(format);
38 return buffers;
39 }
40
TEST(RawGraphicOutputBuffersTest,ChangeNumSlots)41 TEST(RawGraphicOutputBuffersTest, ChangeNumSlots) {
42 constexpr int32_t kWidth = 3840;
43 constexpr int32_t kHeight = 2160;
44
45 std::shared_ptr<RawGraphicOutputBuffers> buffers =
46 GetRawGraphicOutputBuffers(kWidth, kHeight);
47
48 std::shared_ptr<C2BlockPool> pool;
49 ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool));
50
51 // Register 4 buffers
52 std::vector<sp<MediaCodecBuffer>> clientBuffers;
53 auto registerBuffer = [&buffers, &clientBuffers, &pool] {
54 std::shared_ptr<C2GraphicBlock> block;
55 ASSERT_EQ(OK, pool->fetchGraphicBlock(
56 kWidth, kHeight, HAL_PIXEL_FORMAT_YCbCr_420_888,
57 C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
58 std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateGraphicBuffer(block->share(
59 block->crop(), C2Fence{}));
60 size_t index;
61 sp<MediaCodecBuffer> clientBuffer;
62 ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
63 ASSERT_NE(nullptr, clientBuffer);
64 while (clientBuffers.size() <= index) {
65 clientBuffers.emplace_back();
66 }
67 ASSERT_EQ(nullptr, clientBuffers[index]) << "index = " << index;
68 clientBuffers[index] = clientBuffer;
69 };
70 for (int i = 0; i < 4; ++i) {
71 registerBuffer();
72 }
73
74 // Release 2 buffers
75 auto releaseBuffer = [&buffers, &clientBuffers, kWidth, kHeight](int index) {
76 std::shared_ptr<C2Buffer> c2Buffer;
77 ASSERT_TRUE(buffers->releaseBuffer(clientBuffers[index], &c2Buffer))
78 << "index = " << index;
79 clientBuffers[index] = nullptr;
80 // Sanity checks
81 ASSERT_TRUE(c2Buffer->data().linearBlocks().empty());
82 ASSERT_EQ(1u, c2Buffer->data().graphicBlocks().size());
83 C2ConstGraphicBlock block = c2Buffer->data().graphicBlocks().front();
84 ASSERT_EQ(kWidth, block.width());
85 ASSERT_EQ(kHeight, block.height());
86 };
87 for (int i = 0, index = 0; i < 2 && index < clientBuffers.size(); ++index) {
88 if (clientBuffers[index] == nullptr) {
89 continue;
90 }
91 releaseBuffer(index);
92 ++i;
93 }
94
95 // Simulate # of slots 4->16
96 for (int i = 2; i < 16; ++i) {
97 registerBuffer();
98 }
99
100 // Release everything
101 for (int index = 0; index < clientBuffers.size(); ++index) {
102 if (clientBuffers[index] == nullptr) {
103 continue;
104 }
105 releaseBuffer(index);
106 }
107 }
108
TEST(RawGraphicOutputBuffersTest,WrapNullBuffer)109 TEST(RawGraphicOutputBuffersTest, WrapNullBuffer) {
110 constexpr int32_t kWidth = 320;
111 constexpr int32_t kHeight = 240;
112
113 std::shared_ptr<RawGraphicOutputBuffers> buffers =
114 GetRawGraphicOutputBuffers(kWidth, kHeight);
115
116 sp<Codec2Buffer> buffer = buffers->wrap(nullptr);
117 ASSERT_EQ(nullptr, buffer->base());
118 ASSERT_EQ(0, buffer->size());
119 ASSERT_EQ(0, buffer->offset());
120 }
121
TEST(RawGraphicOutputBuffersTest,FlexYuvColorFormat)122 TEST(RawGraphicOutputBuffersTest, FlexYuvColorFormat) {
123 constexpr int32_t kWidth = 320;
124 constexpr int32_t kHeight = 240;
125
126 std::vector<uint32_t> flexPixelFormats({HAL_PIXEL_FORMAT_YCbCr_420_888});
127 std::shared_ptr<Codec2Client> client = Codec2Client::CreateFromService("default");
128 if (client) {
129 // Query vendor format for Flexible YUV
130 std::vector<std::unique_ptr<C2Param>> heapParams;
131 C2StoreFlexiblePixelFormatDescriptorsInfo *pixelFormatInfo = nullptr;
132 if (client->query(
133 {},
134 {C2StoreFlexiblePixelFormatDescriptorsInfo::PARAM_TYPE},
135 C2_MAY_BLOCK,
136 &heapParams) == C2_OK
137 && heapParams.size() == 1u) {
138 pixelFormatInfo = C2StoreFlexiblePixelFormatDescriptorsInfo::From(
139 heapParams[0].get());
140 } else {
141 pixelFormatInfo = nullptr;
142 }
143 if (pixelFormatInfo && *pixelFormatInfo) {
144 for (size_t i = 0; i < pixelFormatInfo->flexCount(); ++i) {
145 const C2FlexiblePixelFormatDescriptorStruct &desc =
146 pixelFormatInfo->m.values[i];
147 if (desc.bitDepth != 8
148 || desc.subsampling != C2Color::YUV_420
149 // TODO(b/180076105): some devices report wrong layouts
150 // || desc.layout == C2Color::INTERLEAVED_PACKED
151 // || desc.layout == C2Color::INTERLEAVED_ALIGNED
152 || desc.layout == C2Color::UNKNOWN_LAYOUT) {
153 continue;
154 }
155 flexPixelFormats.push_back(desc.pixelFormat);
156 }
157 }
158 }
159
160 for (uint32_t pixelFormat : flexPixelFormats) {
161 std::shared_ptr<RawGraphicOutputBuffers> buffers =
162 std::make_shared<RawGraphicOutputBuffers>(
163 AStringPrintf("test pixel format 0x%x", pixelFormat).c_str());
164
165 sp<AMessage> format{new AMessage};
166 format->setInt32(KEY_WIDTH, kWidth);
167 format->setInt32(KEY_HEIGHT, kHeight);
168 format->setInt32(KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
169 int32_t fwkPixelFormat = 0;
170 if (C2Mapper::mapPixelFormatCodecToFramework(pixelFormat, &fwkPixelFormat)) {
171 format->setInt32("android._color-format", fwkPixelFormat);
172 }
173 buffers->setFormat(format);
174
175 std::shared_ptr<C2BlockPool> pool;
176 ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool));
177
178 std::shared_ptr<C2GraphicBlock> block;
179 ASSERT_EQ(OK, pool->fetchGraphicBlock(
180 kWidth, kHeight, pixelFormat,
181 C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
182
183 {
184 C2GraphicView view = block->map().get();
185 C2PlanarLayout layout = view.layout();
186
187 // Verify the block is in YUV420 format
188 ASSERT_EQ(C2PlanarLayout::TYPE_YUV, layout.type);
189 ASSERT_EQ(3u, layout.numPlanes);
190 const C2PlaneInfo& yPlane = layout.planes[C2PlanarLayout::PLANE_Y];
191 const C2PlaneInfo& uPlane = layout.planes[C2PlanarLayout::PLANE_U];
192 const C2PlaneInfo& vPlane = layout.planes[C2PlanarLayout::PLANE_V];
193
194 // Y plane
195 ASSERT_EQ(1u, yPlane.colSampling);
196 ASSERT_EQ(1u, yPlane.rowSampling);
197 ASSERT_EQ(8u, yPlane.allocatedDepth);
198 ASSERT_EQ(8u, yPlane.bitDepth);
199 ASSERT_EQ(0u, yPlane.rightShift);
200
201 // U plane
202 ASSERT_EQ(2u, uPlane.colSampling);
203 ASSERT_EQ(2u, uPlane.rowSampling);
204 ASSERT_EQ(8u, uPlane.allocatedDepth);
205 ASSERT_EQ(8u, uPlane.bitDepth);
206 ASSERT_EQ(0u, uPlane.rightShift);
207
208 // V plane
209 ASSERT_EQ(2u, vPlane.colSampling);
210 ASSERT_EQ(2u, vPlane.rowSampling);
211 ASSERT_EQ(8u, vPlane.allocatedDepth);
212 ASSERT_EQ(8u, vPlane.bitDepth);
213 ASSERT_EQ(0u, vPlane.rightShift);
214
215 uint8_t *yRowPtr = view.data()[C2PlanarLayout::PLANE_Y];
216 uint8_t *uRowPtr = view.data()[C2PlanarLayout::PLANE_U];
217 uint8_t *vRowPtr = view.data()[C2PlanarLayout::PLANE_V];
218 for (int32_t row = 0; row < kHeight; ++row) {
219 uint8_t *yPtr = yRowPtr;
220 uint8_t *uPtr = uRowPtr;
221 uint8_t *vPtr = vRowPtr;
222 for (int32_t col = 0; col < kWidth; ++col) {
223 *yPtr = ((row + col) & 0xFF);
224 yPtr += yPlane.colInc;
225
226 if (row < kHeight / 2 && col < kWidth / 2) {
227 *uPtr = ((row + col + 1) & 0xFF);
228 *vPtr = ((row + col + 2) & 0xFF);
229 uPtr += uPlane.colInc;
230 vPtr += vPlane.colInc;
231 }
232 }
233 yRowPtr += yPlane.rowInc;
234 if (row < kHeight / 2) {
235 uRowPtr += uPlane.rowInc;
236 vRowPtr += vPlane.rowInc;
237 }
238 }
239 }
240
241 std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateGraphicBuffer(block->share(
242 block->crop(), C2Fence{}));
243 size_t index;
244 sp<MediaCodecBuffer> clientBuffer;
245 ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
246 ASSERT_NE(nullptr, clientBuffer);
247 sp<ABuffer> imageData;
248 ASSERT_TRUE(clientBuffer->format()->findBuffer("image-data", &imageData));
249 MediaImage2 *img = (MediaImage2 *)imageData->data();
250 ASSERT_EQ(MediaImage2::MEDIA_IMAGE_TYPE_YUV, img->mType);
251 ASSERT_EQ(3u, img->mNumPlanes);
252 ASSERT_EQ(kWidth, img->mWidth);
253 ASSERT_EQ(kHeight, img->mHeight);
254 ASSERT_EQ(8u, img->mBitDepth);
255 ASSERT_EQ(8u, img->mBitDepthAllocated);
256 const MediaImage2::PlaneInfo &yPlane = img->mPlane[MediaImage2::Y];
257 const MediaImage2::PlaneInfo &uPlane = img->mPlane[MediaImage2::U];
258 const MediaImage2::PlaneInfo &vPlane = img->mPlane[MediaImage2::V];
259 ASSERT_EQ(1u, yPlane.mHorizSubsampling);
260 ASSERT_EQ(1u, yPlane.mVertSubsampling);
261 ASSERT_EQ(2u, uPlane.mHorizSubsampling);
262 ASSERT_EQ(2u, uPlane.mVertSubsampling);
263 ASSERT_EQ(2u, vPlane.mHorizSubsampling);
264 ASSERT_EQ(2u, vPlane.mVertSubsampling);
265
266 uint8_t *yRowPtr = clientBuffer->data() + yPlane.mOffset;
267 uint8_t *uRowPtr = clientBuffer->data() + uPlane.mOffset;
268 uint8_t *vRowPtr = clientBuffer->data() + vPlane.mOffset;
269 for (int32_t row = 0; row < kHeight; ++row) {
270 uint8_t *yPtr = yRowPtr;
271 uint8_t *uPtr = uRowPtr;
272 uint8_t *vPtr = vRowPtr;
273 for (int32_t col = 0; col < kWidth; ++col) {
274 ASSERT_EQ((row + col) & 0xFF, *yPtr);
275 yPtr += yPlane.mColInc;
276 if (row < kHeight / 2 && col < kWidth / 2) {
277 ASSERT_EQ((row + col + 1) & 0xFF, *uPtr);
278 ASSERT_EQ((row + col + 2) & 0xFF, *vPtr);
279 uPtr += uPlane.mColInc;
280 vPtr += vPlane.mColInc;
281 }
282 }
283 yRowPtr += yPlane.mRowInc;
284 if (row < kHeight / 2) {
285 uRowPtr += uPlane.mRowInc;
286 vRowPtr += vPlane.mRowInc;
287 }
288 }
289 }
290 }
291
TEST(RawGraphicOutputBuffersTest,P010ColorFormat)292 TEST(RawGraphicOutputBuffersTest, P010ColorFormat) {
293 constexpr int32_t kWidth = 320;
294 constexpr int32_t kHeight = 240;
295
296 std::shared_ptr<RawGraphicOutputBuffers> buffers =
297 std::make_shared<RawGraphicOutputBuffers>("test P010");
298
299 sp<AMessage> format{new AMessage};
300 format->setInt32(KEY_WIDTH, kWidth);
301 format->setInt32(KEY_HEIGHT, kHeight);
302 format->setInt32(KEY_COLOR_FORMAT, COLOR_FormatYUVP010);
303 int32_t fwkPixelFormat = 0;
304 if (C2Mapper::mapPixelFormatCodecToFramework(HAL_PIXEL_FORMAT_YCBCR_P010, &fwkPixelFormat)) {
305 format->setInt32("android._color-format", fwkPixelFormat);
306 }
307 buffers->setFormat(format);
308
309 std::shared_ptr<C2BlockPool> pool;
310 ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool));
311
312 std::shared_ptr<C2GraphicBlock> block;
313 c2_status_t err = pool->fetchGraphicBlock(
314 kWidth, kHeight, HAL_PIXEL_FORMAT_YCBCR_P010,
315 C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
316 if (err != C2_OK) {
317 GTEST_SKIP();
318 }
319
320 {
321 C2GraphicView view = block->map().get();
322 C2PlanarLayout layout = view.layout();
323
324 // Verify the block is in YUV420 format
325 ASSERT_EQ(C2PlanarLayout::TYPE_YUV, layout.type);
326 ASSERT_EQ(3u, layout.numPlanes);
327 const C2PlaneInfo& yPlane = layout.planes[C2PlanarLayout::PLANE_Y];
328 const C2PlaneInfo& uPlane = layout.planes[C2PlanarLayout::PLANE_U];
329 const C2PlaneInfo& vPlane = layout.planes[C2PlanarLayout::PLANE_V];
330
331 // Y plane
332 ASSERT_EQ(1u, yPlane.colSampling);
333 ASSERT_EQ(1u, yPlane.rowSampling);
334 ASSERT_EQ(16u, yPlane.allocatedDepth);
335 ASSERT_EQ(10u, yPlane.bitDepth);
336 ASSERT_EQ(6u, yPlane.rightShift);
337
338 // U plane
339 ASSERT_EQ(2u, uPlane.colSampling);
340 ASSERT_EQ(2u, uPlane.rowSampling);
341 ASSERT_EQ(16u, uPlane.allocatedDepth);
342 ASSERT_EQ(10u, uPlane.bitDepth);
343 ASSERT_EQ(6u, uPlane.rightShift);
344
345 // V plane
346 ASSERT_EQ(2u, vPlane.colSampling);
347 ASSERT_EQ(2u, vPlane.rowSampling);
348 ASSERT_EQ(16u, vPlane.allocatedDepth);
349 ASSERT_EQ(10u, vPlane.bitDepth);
350 ASSERT_EQ(6u, vPlane.rightShift);
351
352 uint8_t *yRowPtr = view.data()[C2PlanarLayout::PLANE_Y];
353 uint8_t *uRowPtr = view.data()[C2PlanarLayout::PLANE_U];
354 uint8_t *vRowPtr = view.data()[C2PlanarLayout::PLANE_V];
355 for (int32_t row = 0; row < kHeight; ++row) {
356 uint8_t *yPtr = yRowPtr;
357 uint8_t *uPtr = uRowPtr;
358 uint8_t *vPtr = vRowPtr;
359 for (int32_t col = 0; col < kWidth; ++col) {
360 yPtr[0] = ((row + col) & 0x3) << 6;
361 yPtr[1] = ((row + col) & 0x3FC) >> 2;
362 yPtr += yPlane.colInc;
363
364 if (row < kHeight / 2 && col < kWidth / 2) {
365 uPtr[0] = ((row + col + 1) & 0x3) << 6;
366 uPtr[1] = ((row + col + 1) & 0x3FC) >> 2;
367 vPtr[0] = ((row + col + 2) & 0x3) << 6;
368 vPtr[1] = ((row + col + 2) & 0x3FC) >> 2;
369 uPtr += uPlane.colInc;
370 vPtr += vPlane.colInc;
371 }
372 }
373 yRowPtr += yPlane.rowInc;
374 if (row < kHeight / 2) {
375 uRowPtr += uPlane.rowInc;
376 vRowPtr += vPlane.rowInc;
377 }
378 }
379 }
380
381 std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateGraphicBuffer(block->share(
382 block->crop(), C2Fence{}));
383 size_t index;
384 sp<MediaCodecBuffer> clientBuffer;
385 ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
386 ASSERT_NE(nullptr, clientBuffer);
387 sp<ABuffer> imageData;
388 ASSERT_TRUE(clientBuffer->format()->findBuffer("image-data", &imageData));
389 MediaImage2 *img = (MediaImage2 *)imageData->data();
390 ASSERT_EQ(MediaImage2::MEDIA_IMAGE_TYPE_YUV, img->mType);
391 ASSERT_EQ(3u, img->mNumPlanes);
392 ASSERT_EQ(kWidth, img->mWidth);
393 ASSERT_EQ(kHeight, img->mHeight);
394 ASSERT_EQ(10u, img->mBitDepth);
395 ASSERT_EQ(16u, img->mBitDepthAllocated);
396 const MediaImage2::PlaneInfo &yPlane = img->mPlane[MediaImage2::Y];
397 const MediaImage2::PlaneInfo &uPlane = img->mPlane[MediaImage2::U];
398 const MediaImage2::PlaneInfo &vPlane = img->mPlane[MediaImage2::V];
399 ASSERT_EQ(1u, yPlane.mHorizSubsampling);
400 ASSERT_EQ(1u, yPlane.mVertSubsampling);
401 ASSERT_EQ(2u, uPlane.mHorizSubsampling);
402 ASSERT_EQ(2u, uPlane.mVertSubsampling);
403 ASSERT_EQ(2u, vPlane.mHorizSubsampling);
404 ASSERT_EQ(2u, vPlane.mVertSubsampling);
405
406 uint8_t *yRowPtr = clientBuffer->data() + yPlane.mOffset;
407 uint8_t *uRowPtr = clientBuffer->data() + uPlane.mOffset;
408 uint8_t *vRowPtr = clientBuffer->data() + vPlane.mOffset;
409 for (int32_t row = 0; row < kHeight; ++row) {
410 uint8_t *yPtr = yRowPtr;
411 uint8_t *uPtr = uRowPtr;
412 uint8_t *vPtr = vRowPtr;
413 for (int32_t col = 0; col < kWidth; ++col) {
414 ASSERT_EQ(((row + col) & 0x3) << 6, yPtr[0]);
415 ASSERT_EQ(((row + col) & 0x3FC) >> 2, yPtr[1]);
416 yPtr += yPlane.mColInc;
417 if (row < kHeight / 2 && col < kWidth / 2) {
418 ASSERT_EQ(((row + col + 1) & 0x3) << 6, uPtr[0]);
419 ASSERT_EQ(((row + col + 1) & 0x3FC) >> 2, uPtr[1]);
420 ASSERT_EQ(((row + col + 2) & 0x3) << 6, vPtr[0]);
421 ASSERT_EQ(((row + col + 2) & 0x3FC) >> 2, vPtr[1]);
422 uPtr += uPlane.mColInc;
423 vPtr += vPlane.mColInc;
424 }
425 }
426 yRowPtr += yPlane.mRowInc;
427 if (row < kHeight / 2) {
428 uRowPtr += uPlane.mRowInc;
429 vRowPtr += vPlane.mRowInc;
430 }
431 }
432 }
433
434 class TestGraphicAllocation : public C2GraphicAllocation {
435 public:
TestGraphicAllocation(uint32_t width,uint32_t height,const C2PlanarLayout & layout,size_t capacity,std::vector<size_t> offsets)436 TestGraphicAllocation(
437 uint32_t width,
438 uint32_t height,
439 const C2PlanarLayout &layout,
440 size_t capacity,
441 std::vector<size_t> offsets)
442 : C2GraphicAllocation(width, height),
443 mLayout(layout),
444 mMemory(capacity, 0xAA),
445 mOffsets(offsets) {
446 }
447
map(C2Rect rect,C2MemoryUsage usage,C2Fence * fence,C2PlanarLayout * layout,uint8_t ** addr)448 c2_status_t map(
449 C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
450 C2PlanarLayout *layout, uint8_t **addr) override {
451 (void)rect;
452 (void)usage;
453 (void)fence;
454 *layout = mLayout;
455 for (size_t i = 0; i < mLayout.numPlanes; ++i) {
456 addr[i] = mMemory.data() + mOffsets[i];
457 }
458 return C2_OK;
459 }
460
unmap(uint8_t **,C2Rect,C2Fence *)461 c2_status_t unmap(uint8_t **, C2Rect, C2Fence *) override { return C2_OK; }
462
getAllocatorId() const463 C2Allocator::id_t getAllocatorId() const override { return -1; }
464
handle() const465 const C2Handle *handle() const override { return nullptr; }
466
equals(const std::shared_ptr<const C2GraphicAllocation> & other) const467 bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const override {
468 return other.get() == this;
469 }
470
471 private:
472 C2PlanarLayout mLayout;
473 std::vector<uint8_t> mMemory;
474 std::vector<uint8_t *> mAddr;
475 std::vector<size_t> mOffsets;
476 };
477
478 class LayoutTest : public ::testing::TestWithParam<std::tuple<bool, std::string, bool, int32_t>> {
479 private:
YUVPlanarLayout(int32_t stride)480 static C2PlanarLayout YUVPlanarLayout(int32_t stride) {
481 C2PlanarLayout layout = {
482 C2PlanarLayout::TYPE_YUV,
483 3, /* numPlanes */
484 3, /* rootPlanes */
485 {}, /* planes --- to be filled below */
486 };
487 layout.planes[C2PlanarLayout::PLANE_Y] = {
488 C2PlaneInfo::CHANNEL_Y,
489 1, /* colInc */
490 stride, /* rowInc */
491 1, /* colSampling */
492 1, /* rowSampling */
493 8, /* allocatedDepth */
494 8, /* bitDepth */
495 0, /* rightShift */
496 C2PlaneInfo::NATIVE,
497 C2PlanarLayout::PLANE_Y, /* rootIx */
498 0, /* offset */
499 };
500 layout.planes[C2PlanarLayout::PLANE_U] = {
501 C2PlaneInfo::CHANNEL_CB,
502 1, /* colInc */
503 stride / 2, /* rowInc */
504 2, /* colSampling */
505 2, /* rowSampling */
506 8, /* allocatedDepth */
507 8, /* bitDepth */
508 0, /* rightShift */
509 C2PlaneInfo::NATIVE,
510 C2PlanarLayout::PLANE_U, /* rootIx */
511 0, /* offset */
512 };
513 layout.planes[C2PlanarLayout::PLANE_V] = {
514 C2PlaneInfo::CHANNEL_CR,
515 1, /* colInc */
516 stride / 2, /* rowInc */
517 2, /* colSampling */
518 2, /* rowSampling */
519 8, /* allocatedDepth */
520 8, /* bitDepth */
521 0, /* rightShift */
522 C2PlaneInfo::NATIVE,
523 C2PlanarLayout::PLANE_V, /* rootIx */
524 0, /* offset */
525 };
526 return layout;
527 }
528
YUVSemiPlanarLayout(int32_t stride)529 static C2PlanarLayout YUVSemiPlanarLayout(int32_t stride) {
530 C2PlanarLayout layout = {
531 C2PlanarLayout::TYPE_YUV,
532 3, /* numPlanes */
533 2, /* rootPlanes */
534 {}, /* planes --- to be filled below */
535 };
536 layout.planes[C2PlanarLayout::PLANE_Y] = {
537 C2PlaneInfo::CHANNEL_Y,
538 1, /* colInc */
539 stride, /* rowInc */
540 1, /* colSampling */
541 1, /* rowSampling */
542 8, /* allocatedDepth */
543 8, /* bitDepth */
544 0, /* rightShift */
545 C2PlaneInfo::NATIVE,
546 C2PlanarLayout::PLANE_Y, /* rootIx */
547 0, /* offset */
548 };
549 layout.planes[C2PlanarLayout::PLANE_U] = {
550 C2PlaneInfo::CHANNEL_CB,
551 2, /* colInc */
552 stride, /* rowInc */
553 2, /* colSampling */
554 2, /* rowSampling */
555 8, /* allocatedDepth */
556 8, /* bitDepth */
557 0, /* rightShift */
558 C2PlaneInfo::NATIVE,
559 C2PlanarLayout::PLANE_U, /* rootIx */
560 0, /* offset */
561 };
562 layout.planes[C2PlanarLayout::PLANE_V] = {
563 C2PlaneInfo::CHANNEL_CR,
564 2, /* colInc */
565 stride, /* rowInc */
566 2, /* colSampling */
567 2, /* rowSampling */
568 8, /* allocatedDepth */
569 8, /* bitDepth */
570 0, /* rightShift */
571 C2PlaneInfo::NATIVE,
572 C2PlanarLayout::PLANE_U, /* rootIx */
573 1, /* offset */
574 };
575 return layout;
576 }
577
YVUSemiPlanarLayout(int32_t stride)578 static C2PlanarLayout YVUSemiPlanarLayout(int32_t stride) {
579 C2PlanarLayout layout = {
580 C2PlanarLayout::TYPE_YUV,
581 3, /* numPlanes */
582 2, /* rootPlanes */
583 {}, /* planes --- to be filled below */
584 };
585 layout.planes[C2PlanarLayout::PLANE_Y] = {
586 C2PlaneInfo::CHANNEL_Y,
587 1, /* colInc */
588 stride, /* rowInc */
589 1, /* colSampling */
590 1, /* rowSampling */
591 8, /* allocatedDepth */
592 8, /* bitDepth */
593 0, /* rightShift */
594 C2PlaneInfo::NATIVE,
595 C2PlanarLayout::PLANE_Y, /* rootIx */
596 0, /* offset */
597 };
598 layout.planes[C2PlanarLayout::PLANE_U] = {
599 C2PlaneInfo::CHANNEL_CB,
600 2, /* colInc */
601 stride, /* rowInc */
602 2, /* colSampling */
603 2, /* rowSampling */
604 8, /* allocatedDepth */
605 8, /* bitDepth */
606 0, /* rightShift */
607 C2PlaneInfo::NATIVE,
608 C2PlanarLayout::PLANE_V, /* rootIx */
609 1, /* offset */
610 };
611 layout.planes[C2PlanarLayout::PLANE_V] = {
612 C2PlaneInfo::CHANNEL_CR,
613 2, /* colInc */
614 stride, /* rowInc */
615 2, /* colSampling */
616 2, /* rowSampling */
617 8, /* allocatedDepth */
618 8, /* bitDepth */
619 0, /* rightShift */
620 C2PlaneInfo::NATIVE,
621 C2PlanarLayout::PLANE_V, /* rootIx */
622 0, /* offset */
623 };
624 return layout;
625 }
626
CreateGraphicBlock(uint32_t width,uint32_t height,const C2PlanarLayout & layout,size_t capacity,std::vector<size_t> offsets)627 static std::shared_ptr<C2GraphicBlock> CreateGraphicBlock(
628 uint32_t width,
629 uint32_t height,
630 const C2PlanarLayout &layout,
631 size_t capacity,
632 std::vector<size_t> offsets) {
633 std::shared_ptr<C2GraphicAllocation> alloc = std::make_shared<TestGraphicAllocation>(
634 width,
635 height,
636 layout,
637 capacity,
638 offsets);
639
640 return _C2BlockFactory::CreateGraphicBlock(alloc);
641 }
642
GetPixelValue(uint8_t value,uint32_t row,uint32_t col)643 static constexpr uint8_t GetPixelValue(uint8_t value, uint32_t row, uint32_t col) {
644 return (uint32_t(value) * row + col) & 0xFF;
645 }
646
FillPlane(C2GraphicView & view,size_t index,uint8_t value)647 static void FillPlane(C2GraphicView &view, size_t index, uint8_t value) {
648 C2PlanarLayout layout = view.layout();
649
650 uint8_t *rowPtr = view.data()[index];
651 C2PlaneInfo plane = layout.planes[index];
652 for (uint32_t row = 0; row < view.height() / plane.rowSampling; ++row) {
653 uint8_t *colPtr = rowPtr;
654 for (uint32_t col = 0; col < view.width() / plane.colSampling; ++col) {
655 *colPtr = GetPixelValue(value, row, col);
656 colPtr += plane.colInc;
657 }
658 rowPtr += plane.rowInc;
659 }
660 }
661
FillBlock(const std::shared_ptr<C2GraphicBlock> & block)662 static void FillBlock(const std::shared_ptr<C2GraphicBlock> &block) {
663 C2GraphicView view = block->map().get();
664
665 FillPlane(view, C2PlanarLayout::PLANE_Y, 'Y');
666 FillPlane(view, C2PlanarLayout::PLANE_U, 'U');
667 FillPlane(view, C2PlanarLayout::PLANE_V, 'V');
668 }
669
VerifyPlane(const MediaImage2 * mediaImage,const uint8_t * base,uint32_t index,uint8_t value,std::string * errorMsg)670 static bool VerifyPlane(
671 const MediaImage2 *mediaImage,
672 const uint8_t *base,
673 uint32_t index,
674 uint8_t value,
675 std::string *errorMsg) {
676 *errorMsg = "";
677 MediaImage2::PlaneInfo plane = mediaImage->mPlane[index];
678 const uint8_t *rowPtr = base + plane.mOffset;
679 for (uint32_t row = 0; row < mediaImage->mHeight / plane.mVertSubsampling; ++row) {
680 const uint8_t *colPtr = rowPtr;
681 for (uint32_t col = 0; col < mediaImage->mWidth / plane.mHorizSubsampling; ++col) {
682 if (GetPixelValue(value, row, col) != *colPtr) {
683 *errorMsg = AStringPrintf("row=%u col=%u expected=%02x actual=%02x",
684 row, col, GetPixelValue(value, row, col), *colPtr).c_str();
685 return false;
686 }
687 colPtr += plane.mColInc;
688 }
689 rowPtr += plane.mRowInc;
690 }
691 return true;
692 }
693
694 public:
695 static constexpr int32_t kWidth = 320;
696 static constexpr int32_t kHeight = 240;
697 static constexpr int32_t kGapLength = kWidth * kHeight * 10;
698
CreateAndFillBufferFromParam(const ParamType & param)699 static std::shared_ptr<C2Buffer> CreateAndFillBufferFromParam(const ParamType ¶m) {
700 bool contiguous = std::get<0>(param);
701 std::string planeOrderStr = std::get<1>(param);
702 bool planar = std::get<2>(param);
703 int32_t stride = std::get<3>(param);
704
705 C2PlanarLayout::plane_index_t planeOrder[3];
706 C2PlanarLayout layout;
707
708 if (planeOrderStr.size() != 3) {
709 return nullptr;
710 }
711 for (size_t i = 0; i < 3; ++i) {
712 C2PlanarLayout::plane_index_t planeIndex;
713 switch (planeOrderStr[i]) {
714 case 'Y': planeIndex = C2PlanarLayout::PLANE_Y; break;
715 case 'U': planeIndex = C2PlanarLayout::PLANE_U; break;
716 case 'V': planeIndex = C2PlanarLayout::PLANE_V; break;
717 default: return nullptr;
718 }
719 planeOrder[i] = planeIndex;
720 }
721
722 if (planar) {
723 layout = YUVPlanarLayout(stride);
724 } else { // semi-planar
725 for (size_t i = 0; i < 3; ++i) {
726 if (planeOrder[i] == C2PlanarLayout::PLANE_U) {
727 layout = YUVSemiPlanarLayout(stride);
728 break;
729 }
730 if (planeOrder[i] == C2PlanarLayout::PLANE_V) {
731 layout = YVUSemiPlanarLayout(stride);
732 break;
733 }
734 }
735 }
736 size_t yPlaneSize = stride * kHeight;
737 size_t uvPlaneSize = stride * kHeight / 4;
738 size_t capacity = yPlaneSize + uvPlaneSize * 2;
739 std::vector<size_t> offsets(3);
740
741 if (!contiguous) {
742 if (planar) {
743 capacity += kGapLength * 2;
744 } else { // semi-planar
745 capacity += kGapLength;
746 }
747 }
748
749 offsets[planeOrder[0]] = 0;
750 size_t planeSize = (planeOrder[0] == C2PlanarLayout::PLANE_Y) ? yPlaneSize : uvPlaneSize;
751 for (size_t i = 1; i < 3; ++i) {
752 offsets[planeOrder[i]] = offsets[planeOrder[i - 1]] + planeSize;
753 if (!contiguous) {
754 offsets[planeOrder[i]] += kGapLength;
755 }
756 planeSize = (planeOrder[i] == C2PlanarLayout::PLANE_Y) ? yPlaneSize : uvPlaneSize;
757 if (!planar // semi-planar
758 && planeOrder[i - 1] != C2PlanarLayout::PLANE_Y
759 && planeOrder[i] != C2PlanarLayout::PLANE_Y) {
760 offsets[planeOrder[i]] = offsets[planeOrder[i - 1]] + 1;
761 planeSize = uvPlaneSize * 2 - 1;
762 }
763 }
764
765 std::shared_ptr<C2GraphicBlock> block = CreateGraphicBlock(
766 kWidth,
767 kHeight,
768 layout,
769 capacity,
770 offsets);
771 FillBlock(block);
772 return C2Buffer::CreateGraphicBuffer(
773 block->share(block->crop(), C2Fence()));
774 }
775
VerifyClientBuffer(const sp<MediaCodecBuffer> & buffer,std::string * errorMsg)776 static bool VerifyClientBuffer(
777 const sp<MediaCodecBuffer> &buffer, std::string *errorMsg) {
778 *errorMsg = "";
779 sp<ABuffer> imageData;
780 if (!buffer->format()->findBuffer("image-data", &imageData)) {
781 *errorMsg = "Missing image data";
782 return false;
783 }
784 MediaImage2 *mediaImage = (MediaImage2 *)imageData->data();
785 if (mediaImage->mType != MediaImage2::MEDIA_IMAGE_TYPE_YUV) {
786 *errorMsg = AStringPrintf("Unexpected type: %d", mediaImage->mType).c_str();
787 return false;
788 }
789 std::string planeErrorMsg;
790 if (!VerifyPlane(mediaImage, buffer->base(), MediaImage2::Y, 'Y', &planeErrorMsg)) {
791 *errorMsg = "Y plane does not match: " + planeErrorMsg;
792 return false;
793 }
794 if (!VerifyPlane(mediaImage, buffer->base(), MediaImage2::U, 'U', &planeErrorMsg)) {
795 *errorMsg = "U plane does not match: " + planeErrorMsg;
796 return false;
797 }
798 if (!VerifyPlane(mediaImage, buffer->base(), MediaImage2::V, 'V', &planeErrorMsg)) {
799 *errorMsg = "V plane does not match: " + planeErrorMsg;
800 return false;
801 }
802
803 int32_t width, height, stride;
804 buffer->format()->findInt32(KEY_WIDTH, &width);
805 buffer->format()->findInt32(KEY_HEIGHT, &height);
806 buffer->format()->findInt32(KEY_STRIDE, &stride);
807
808 MediaImage2 legacyYLayout = {
809 MediaImage2::MEDIA_IMAGE_TYPE_Y,
810 1, // mNumPlanes
811 uint32_t(width),
812 uint32_t(height),
813 8,
814 8,
815 {}, // mPlane
816 };
817 legacyYLayout.mPlane[MediaImage2::Y] = {
818 0, // mOffset
819 1, // mColInc
820 stride, // mRowInc
821 1, // mHorizSubsampling
822 1, // mVertSubsampling
823 };
824 if (!VerifyPlane(&legacyYLayout, buffer->data(), MediaImage2::Y, 'Y', &planeErrorMsg)) {
825 *errorMsg = "Y plane by legacy layout does not match: " + planeErrorMsg;
826 return false;
827 }
828 return true;
829 }
830
831 };
832
TEST_P(LayoutTest,VerifyLayout)833 TEST_P(LayoutTest, VerifyLayout) {
834 std::shared_ptr<RawGraphicOutputBuffers> buffers =
835 GetRawGraphicOutputBuffers(kWidth, kHeight);
836
837 std::shared_ptr<C2Buffer> c2Buffer = CreateAndFillBufferFromParam(GetParam());
838 ASSERT_NE(nullptr, c2Buffer);
839 sp<MediaCodecBuffer> clientBuffer;
840 size_t index;
841 ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
842 ASSERT_NE(nullptr, clientBuffer);
843 std::string errorMsg;
844 ASSERT_TRUE(VerifyClientBuffer(clientBuffer, &errorMsg)) << errorMsg;
845 }
846
847 INSTANTIATE_TEST_SUITE_P(
848 RawGraphicOutputBuffersTest,
849 LayoutTest,
850 ::testing::Combine(
851 ::testing::Bool(), /* contiguous */
852 ::testing::Values("YUV", "YVU", "UVY", "VUY"),
853 ::testing::Bool(), /* planar */
854 ::testing::Values(320, 512)),
__anon843eb6da0302(const ::testing::TestParamInfo<LayoutTest::ParamType> &info) 855 [](const ::testing::TestParamInfo<LayoutTest::ParamType> &info) {
856 std::string contiguous = std::get<0>(info.param) ? "Contiguous" : "Noncontiguous";
857 std::string planar = std::get<2>(info.param) ? "Planar" : "SemiPlanar";
858 return contiguous
859 + std::get<1>(info.param)
860 + planar
861 + std::to_string(std::get<3>(info.param));
862 });
863
TEST(LinearOutputBuffersTest,PcmConvertFormat)864 TEST(LinearOutputBuffersTest, PcmConvertFormat) {
865 // Prepare LinearOutputBuffers
866 std::shared_ptr<LinearOutputBuffers> buffers =
867 std::make_shared<LinearOutputBuffers>("test");
868 sp<AMessage> format{new AMessage};
869 format->setInt32(KEY_CHANNEL_COUNT, 1);
870 format->setInt32(KEY_SAMPLE_RATE, 8000);
871 format->setInt32(KEY_PCM_ENCODING, kAudioEncodingPcmFloat);
872 format->setInt32("android._config-pcm-encoding", kAudioEncodingPcm16bit);
873 format->setInt32("android._codec-pcm-encoding", kAudioEncodingPcmFloat);
874 buffers->setFormat(format);
875
876 // Prepare a linear C2Buffer
877 std::shared_ptr<C2BlockPool> pool;
878 ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_LINEAR, nullptr, &pool));
879
880 std::shared_ptr<C2LinearBlock> block;
881 ASSERT_EQ(OK, pool->fetchLinearBlock(
882 1024, C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
883 std::shared_ptr<C2Buffer> c2Buffer =
884 C2Buffer::CreateLinearBuffer(block->share(0, 1024, C2Fence()));
885
886 // Test regular buffer convert
887 size_t index;
888 sp<MediaCodecBuffer> clientBuffer;
889 ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
890 int32_t pcmEncoding = 0;
891 ASSERT_TRUE(clientBuffer->format()->findInt32(KEY_PCM_ENCODING, &pcmEncoding));
892 EXPECT_EQ(kAudioEncodingPcm16bit, pcmEncoding);
893 ASSERT_TRUE(buffers->releaseBuffer(clientBuffer, &c2Buffer));
894
895 // Test null buffer convert
896 ASSERT_EQ(OK, buffers->registerBuffer(nullptr, &index, &clientBuffer));
897 ASSERT_TRUE(clientBuffer->format()->findInt32(KEY_PCM_ENCODING, &pcmEncoding));
898 EXPECT_EQ(kAudioEncodingPcm16bit, pcmEncoding);
899 ASSERT_TRUE(buffers->releaseBuffer(clientBuffer, &c2Buffer));
900
901 // Do the same test in the array mode
902 std::shared_ptr<OutputBuffersArray> array = buffers->toArrayMode(8);
903
904 // Test regular buffer convert
905 ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
906 ASSERT_TRUE(clientBuffer->format()->findInt32(KEY_PCM_ENCODING, &pcmEncoding));
907 EXPECT_EQ(kAudioEncodingPcm16bit, pcmEncoding);
908 ASSERT_TRUE(buffers->releaseBuffer(clientBuffer, &c2Buffer));
909
910 // Test null buffer convert
911 ASSERT_EQ(OK, buffers->registerBuffer(nullptr, &index, &clientBuffer));
912 ASSERT_TRUE(clientBuffer->format()->findInt32(KEY_PCM_ENCODING, &pcmEncoding));
913 EXPECT_EQ(kAudioEncodingPcm16bit, pcmEncoding);
914 ASSERT_TRUE(buffers->releaseBuffer(clientBuffer, &c2Buffer));
915 }
916
917 } // namespace android
918