1 /*
2  * Copyright 2018, 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "Codec2BufferUtils"
19 #include <utils/Log.h>
20 
21 #include <list>
22 #include <mutex>
23 
24 #include <media/hardware/HardwareAPI.h>
25 #include <media/stagefright/foundation/AUtils.h>
26 
27 #include <C2Debug.h>
28 
29 #include "Codec2BufferUtils.h"
30 
31 namespace android {
32 
33 namespace {
34 
35 /**
36  * A flippable, optimizable memcpy. Constructs such as (from ? src : dst) do not work as the results are
37  * always const.
38  */
39 template<bool ToA, size_t S>
40 struct MemCopier {
41     template<typename A, typename B>
copyandroid::__anon8074fd200111::MemCopier42     inline static void copy(A *a, const B *b, size_t size) {
43         __builtin_memcpy(a, b, size);
44     }
45 };
46 
47 template<size_t S>
48 struct MemCopier<false, S> {
49     template<typename A, typename B>
copyandroid::__anon8074fd200111::MemCopier50     inline static void copy(const A *a, B *b, size_t size) {
51         MemCopier<true, S>::copy(b, a, size);
52     }
53 };
54 
55 /**
56  * Copies between a MediaImage and a graphic view.
57  *
58  * \param ToMediaImage whether to copy to (or from) the MediaImage
59  * \param view graphic view (could be ConstGraphicView or GraphicView depending on direction)
60  * \param img MediaImage data
61  * \param imgBase base of MediaImage (could be const uint8_t* or uint8_t* depending on direction)
62  */
63 template<bool ToMediaImage, typename View, typename ImagePixel>
_ImageCopy(View & view,const MediaImage2 * img,ImagePixel * imgBase)64 static status_t _ImageCopy(View &view, const MediaImage2 *img, ImagePixel *imgBase) {
65     // TODO: more efficient copying --- e.g. one row at a time, copying
66     //       interleaved planes together, etc.
67     const C2PlanarLayout &layout = view.layout();
68     const size_t bpp = divUp(img->mBitDepthAllocated, 8u);
69     if (view.width() != img->mWidth
70             || view.height() != img->mHeight) {
71         return BAD_VALUE;
72     }
73     for (uint32_t i = 0; i < layout.numPlanes; ++i) {
74         typename std::conditional<ToMediaImage, uint8_t, const uint8_t>::type *imgRow =
75             imgBase + img->mPlane[i].mOffset;
76         typename std::conditional<ToMediaImage, const uint8_t, uint8_t>::type *viewRow =
77             viewRow = view.data()[i];
78         const C2PlaneInfo &plane = layout.planes[i];
79         if (plane.colSampling != img->mPlane[i].mHorizSubsampling
80                 || plane.rowSampling != img->mPlane[i].mVertSubsampling
81                 || plane.allocatedDepth != img->mBitDepthAllocated
82                 || plane.allocatedDepth < plane.bitDepth
83                 // MediaImage only supports MSB values
84                 || plane.rightShift != plane.allocatedDepth - plane.bitDepth
85                 || (bpp > 1 && plane.endianness != plane.NATIVE)) {
86             return BAD_VALUE;
87         }
88 
89         uint32_t planeW = img->mWidth / plane.colSampling;
90         uint32_t planeH = img->mHeight / plane.rowSampling;
91         for (uint32_t row = 0; row < planeH; ++row) {
92             decltype(imgRow) imgPtr = imgRow;
93             decltype(viewRow) viewPtr = viewRow;
94             for (uint32_t col = 0; col < planeW; ++col) {
95                 MemCopier<ToMediaImage, 0>::copy(imgPtr, viewPtr, bpp);
96                 imgPtr += img->mPlane[i].mColInc;
97                 viewPtr += plane.colInc;
98             }
99             imgRow += img->mPlane[i].mRowInc;
100             viewRow += plane.rowInc;
101         }
102     }
103     return OK;
104 }
105 
106 }  // namespace
107 
ImageCopy(uint8_t * imgBase,const MediaImage2 * img,const C2GraphicView & view)108 status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView &view) {
109     return _ImageCopy<true>(view, img, imgBase);
110 }
111 
ImageCopy(C2GraphicView & view,const uint8_t * imgBase,const MediaImage2 * img)112 status_t ImageCopy(C2GraphicView &view, const uint8_t *imgBase, const MediaImage2 *img) {
113     return _ImageCopy<false>(view, img, imgBase);
114 }
115 
IsYUV420(const C2GraphicView & view)116 bool IsYUV420(const C2GraphicView &view) {
117     const C2PlanarLayout &layout = view.layout();
118     return (layout.numPlanes == 3
119             && layout.type == C2PlanarLayout::TYPE_YUV
120             && layout.planes[layout.PLANE_Y].channel == C2PlaneInfo::CHANNEL_Y
121             && layout.planes[layout.PLANE_Y].allocatedDepth == 8
122             && layout.planes[layout.PLANE_Y].bitDepth == 8
123             && layout.planes[layout.PLANE_Y].rightShift == 0
124             && layout.planes[layout.PLANE_Y].colSampling == 1
125             && layout.planes[layout.PLANE_Y].rowSampling == 1
126             && layout.planes[layout.PLANE_U].channel == C2PlaneInfo::CHANNEL_CB
127             && layout.planes[layout.PLANE_U].allocatedDepth == 8
128             && layout.planes[layout.PLANE_U].bitDepth == 8
129             && layout.planes[layout.PLANE_U].rightShift == 0
130             && layout.planes[layout.PLANE_U].colSampling == 2
131             && layout.planes[layout.PLANE_U].rowSampling == 2
132             && layout.planes[layout.PLANE_V].channel == C2PlaneInfo::CHANNEL_CR
133             && layout.planes[layout.PLANE_V].allocatedDepth == 8
134             && layout.planes[layout.PLANE_V].bitDepth == 8
135             && layout.planes[layout.PLANE_V].rightShift == 0
136             && layout.planes[layout.PLANE_V].colSampling == 2
137             && layout.planes[layout.PLANE_V].rowSampling == 2);
138 }
139 
CreateYUV420PlanarMediaImage2(uint32_t width,uint32_t height,uint32_t stride,uint32_t vstride)140 MediaImage2 CreateYUV420PlanarMediaImage2(
141         uint32_t width, uint32_t height, uint32_t stride, uint32_t vstride) {
142     return MediaImage2 {
143         .mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV,
144         .mNumPlanes = 3,
145         .mWidth = width,
146         .mHeight = height,
147         .mBitDepth = 8,
148         .mPlane = {
149             {
150                 .mOffset = 0,
151                 .mColInc = 1,
152                 .mRowInc = (int32_t)stride,
153                 .mHorizSubsampling = 1,
154                 .mVertSubsampling = 1,
155             },
156             {
157                 .mOffset = stride * vstride,
158                 .mColInc = 1,
159                 .mRowInc = (int32_t)stride / 2,
160                 .mHorizSubsampling = 2,
161                 .mVertSubsampling = 2,
162             },
163             {
164                 .mOffset = stride * vstride * 5 / 4,
165                 .mColInc = 1,
166                 .mRowInc = (int32_t)stride / 2,
167                 .mHorizSubsampling = 2,
168                 .mVertSubsampling = 2,
169             }
170         },
171     };
172 }
173 
CreateYUV420SemiPlanarMediaImage2(uint32_t width,uint32_t height,uint32_t stride,uint32_t vstride)174 MediaImage2 CreateYUV420SemiPlanarMediaImage2(
175         uint32_t width, uint32_t height, uint32_t stride, uint32_t vstride) {
176     return MediaImage2 {
177         .mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV,
178         .mNumPlanes = 3,
179         .mWidth = width,
180         .mHeight = height,
181         .mBitDepth = 8,
182         .mPlane = {
183             {
184                 .mOffset = 0,
185                 .mColInc = 1,
186                 .mRowInc = (int32_t)stride,
187                 .mHorizSubsampling = 1,
188                 .mVertSubsampling = 1,
189             },
190             {
191                 .mOffset = stride * vstride,
192                 .mColInc = 2,
193                 .mRowInc = (int32_t)stride,
194                 .mHorizSubsampling = 2,
195                 .mVertSubsampling = 2,
196             },
197             {
198                 .mOffset = stride * vstride + 1,
199                 .mColInc = 2,
200                 .mRowInc = (int32_t)stride,
201                 .mHorizSubsampling = 2,
202                 .mVertSubsampling = 2,
203             }
204         },
205     };
206 }
207 
ConvertRGBToPlanarYUV(uint8_t * dstY,size_t dstStride,size_t dstVStride,size_t bufferSize,const C2GraphicView & src)208 status_t ConvertRGBToPlanarYUV(
209         uint8_t *dstY, size_t dstStride, size_t dstVStride, size_t bufferSize,
210         const C2GraphicView &src) {
211     CHECK(dstY != nullptr);
212     CHECK((src.width() & 1) == 0);
213     CHECK((src.height() & 1) == 0);
214 
215     if (dstStride * dstVStride * 3 / 2 > bufferSize) {
216         ALOGD("conversion buffer is too small for converting from RGB to YUV");
217         return NO_MEMORY;
218     }
219 
220     uint8_t *dstU = dstY + dstStride * dstVStride;
221     uint8_t *dstV = dstU + (dstStride >> 1) * (dstVStride >> 1);
222 
223     const C2PlanarLayout &layout = src.layout();
224     const uint8_t *pRed   = src.data()[C2PlanarLayout::PLANE_R];
225     const uint8_t *pGreen = src.data()[C2PlanarLayout::PLANE_G];
226     const uint8_t *pBlue  = src.data()[C2PlanarLayout::PLANE_B];
227 
228 #define CLIP3(x,y,z) (((z) < (x)) ? (x) : (((z) > (y)) ? (y) : (z)))
229     for (size_t y = 0; y < src.height(); ++y) {
230         for (size_t x = 0; x < src.width(); ++x) {
231             uint8_t red = *pRed;
232             uint8_t green = *pGreen;
233             uint8_t blue = *pBlue;
234 
235             // using ITU-R BT.601 conversion matrix
236             unsigned luma =
237                 CLIP3(0, (((red * 66 + green * 129 + blue * 25) >> 8) + 16), 255);
238 
239             dstY[x] = luma;
240 
241             if ((x & 1) == 0 && (y & 1) == 0) {
242                 unsigned U =
243                     CLIP3(0, (((-red * 38 - green * 74 + blue * 112) >> 8) + 128), 255);
244 
245                 unsigned V =
246                     CLIP3(0, (((red * 112 - green * 94 - blue * 18) >> 8) + 128), 255);
247 
248                 dstU[x >> 1] = U;
249                 dstV[x >> 1] = V;
250             }
251             pRed   += layout.planes[C2PlanarLayout::PLANE_R].colInc;
252             pGreen += layout.planes[C2PlanarLayout::PLANE_G].colInc;
253             pBlue  += layout.planes[C2PlanarLayout::PLANE_B].colInc;
254         }
255 
256         if ((y & 1) == 0) {
257             dstU += dstStride >> 1;
258             dstV += dstStride >> 1;
259         }
260 
261         pRed   -= layout.planes[C2PlanarLayout::PLANE_R].colInc * src.width();
262         pGreen -= layout.planes[C2PlanarLayout::PLANE_G].colInc * src.width();
263         pBlue  -= layout.planes[C2PlanarLayout::PLANE_B].colInc * src.width();
264         pRed   += layout.planes[C2PlanarLayout::PLANE_R].rowInc;
265         pGreen += layout.planes[C2PlanarLayout::PLANE_G].rowInc;
266         pBlue  += layout.planes[C2PlanarLayout::PLANE_B].rowInc;
267 
268         dstY += dstStride;
269     }
270     return OK;
271 }
272 
273 namespace {
274 
275 /**
276  * A block of raw allocated memory.
277  */
278 struct MemoryBlockPoolBlock {
MemoryBlockPoolBlockandroid::__anon8074fd200211::MemoryBlockPoolBlock279     MemoryBlockPoolBlock(size_t size)
280         : mData(new uint8_t[size]), mSize(mData ? size : 0) { }
281 
~MemoryBlockPoolBlockandroid::__anon8074fd200211::MemoryBlockPoolBlock282     ~MemoryBlockPoolBlock() {
283         delete[] mData;
284     }
285 
dataandroid::__anon8074fd200211::MemoryBlockPoolBlock286     const uint8_t *data() const {
287         return mData;
288     }
289 
sizeandroid::__anon8074fd200211::MemoryBlockPoolBlock290     size_t size() const {
291         return mSize;
292     }
293 
294     C2_DO_NOT_COPY(MemoryBlockPoolBlock);
295 
296 private:
297     uint8_t *mData;
298     size_t mSize;
299 };
300 
301 /**
302  * A simple raw memory block pool implementation.
303  */
304 struct MemoryBlockPoolImpl {
releaseandroid::__anon8074fd200211::MemoryBlockPoolImpl305     void release(std::list<MemoryBlockPoolBlock>::const_iterator block) {
306         std::lock_guard<std::mutex> lock(mMutex);
307         // return block to free blocks if it is the current size; otherwise, discard
308         if (block->size() == mCurrentSize) {
309             mFreeBlocks.splice(mFreeBlocks.begin(), mBlocksInUse, block);
310         } else {
311             mBlocksInUse.erase(block);
312         }
313     }
314 
fetchandroid::__anon8074fd200211::MemoryBlockPoolImpl315     std::list<MemoryBlockPoolBlock>::const_iterator fetch(size_t size) {
316         std::lock_guard<std::mutex> lock(mMutex);
317         mFreeBlocks.remove_if([size](const MemoryBlockPoolBlock &block) -> bool {
318             return block.size() != size;
319         });
320         mCurrentSize = size;
321         if (mFreeBlocks.empty()) {
322             mBlocksInUse.emplace_front(size);
323         } else {
324             mBlocksInUse.splice(mBlocksInUse.begin(), mFreeBlocks, mFreeBlocks.begin());
325         }
326         return mBlocksInUse.begin();
327     }
328 
329     MemoryBlockPoolImpl() = default;
330 
331     C2_DO_NOT_COPY(MemoryBlockPoolImpl);
332 
333 private:
334     std::mutex mMutex;
335     std::list<MemoryBlockPoolBlock> mFreeBlocks;
336     std::list<MemoryBlockPoolBlock> mBlocksInUse;
337     size_t mCurrentSize;
338 };
339 
340 } // namespace
341 
342 struct MemoryBlockPool::Impl : MemoryBlockPoolImpl {
343 };
344 
345 struct MemoryBlock::Impl {
Implandroid::MemoryBlock::Impl346     Impl(std::list<MemoryBlockPoolBlock>::const_iterator block,
347          std::shared_ptr<MemoryBlockPoolImpl> pool)
348         : mBlock(block), mPool(pool) {
349     }
350 
~Implandroid::MemoryBlock::Impl351     ~Impl() {
352         mPool->release(mBlock);
353     }
354 
dataandroid::MemoryBlock::Impl355     const uint8_t *data() const {
356         return mBlock->data();
357     }
358 
sizeandroid::MemoryBlock::Impl359     size_t size() const {
360         return mBlock->size();
361     }
362 
363 private:
364     std::list<MemoryBlockPoolBlock>::const_iterator mBlock;
365     std::shared_ptr<MemoryBlockPoolImpl> mPool;
366 };
367 
fetch(size_t size)368 MemoryBlock MemoryBlockPool::fetch(size_t size) {
369     std::list<MemoryBlockPoolBlock>::const_iterator poolBlock = mImpl->fetch(size);
370     return MemoryBlock(std::make_shared<MemoryBlock::Impl>(
371             poolBlock, std::static_pointer_cast<MemoryBlockPoolImpl>(mImpl)));
372 }
373 
MemoryBlockPool()374 MemoryBlockPool::MemoryBlockPool()
375     : mImpl(std::make_shared<MemoryBlockPool::Impl>()) {
376 }
377 
MemoryBlock(std::shared_ptr<MemoryBlock::Impl> impl)378 MemoryBlock::MemoryBlock(std::shared_ptr<MemoryBlock::Impl> impl)
379     : mImpl(impl) {
380 }
381 
382 MemoryBlock::MemoryBlock() = default;
383 
384 MemoryBlock::~MemoryBlock() = default;
385 
data() const386 const uint8_t* MemoryBlock::data() const {
387     return mImpl ? mImpl->data() : nullptr;
388 }
389 
size() const390 size_t MemoryBlock::size() const {
391     return mImpl ? mImpl->size() : 0;
392 }
393 
Allocate(size_t size)394 MemoryBlock MemoryBlock::Allocate(size_t size) {
395     return MemoryBlockPool().fetch(size);
396 }
397 
398 }  // namespace android
399