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 &param) {
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