diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp index 8f4626c56d..d9610c2d28 100644 --- a/camera/device/3.4/default/ExternalCameraUtils.cpp +++ b/camera/device/3.4/default/ExternalCameraUtils.cpp @@ -108,9 +108,38 @@ int AllocatedFrame::allocate(YCbCrLayout* out) { return -EINVAL; } - uint32_t dataSize = mWidth * mHeight * 3 / 2; // YUV420 - if (mData.size() != dataSize) { - mData.resize(dataSize); + // This frame might be sent to jpeglib to be encoded. Since AllocatedFrame only contains YUV420, + // jpeglib expects height and width of Y component to be an integral multiple of 2*DCTSIZE, + // and heights and widths of Cb and Cr components to be an integral multiple of DCTSIZE. If the + // image size does not meet this requirement, libjpeg expects its input to be padded to meet the + // constraints. This padding is removed from the final encoded image so the content in the + // padding doesn't matter. What matters is that the memory is accessible to jpeglib at the time + // of encoding. + // For example, if the image size is 1500x844 and DCTSIZE is 8, jpeglib expects a YUV 420 + // frame with components of following sizes: + // Y: 1504x848 because 1504 and 848 are the next smallest multiples of 2*8 + // Cb/Cr: 752x424 which are the next smallest multiples of 8 + + // jpeglib takes an array of row pointers which makes vertical padding trivial when setting up + // the pointers. Padding horizontally is a bit more complicated. AllocatedFrame holds the data + // in a flattened buffer, which means memory accesses past a row will flow into the next logical + // row. For any row of a component, we can consider the first few bytes of the next row as + // padding for the current one. This is true for Y and Cb components and all but last row of the + // Cr component. Reading past the last row of Cr component will lead to undefined behavior as + // libjpeg attempts to read memory past the allocated buffer. To prevent undefined behavior, + // the buffer allocated here is padded such that libjpeg never accesses unallocated memory when + // reading the last row. Effectively, we only need to ensure that the last row of Cr component + // has width that is an integral multiple of DCTSIZE. + + size_t dataSize = mWidth * mHeight * 3 / 2; // YUV420 + + size_t cbWidth = mWidth / 2; + size_t requiredCbWidth = DCTSIZE * ((cbWidth + DCTSIZE - 1) / DCTSIZE); + size_t padding = requiredCbWidth - cbWidth; + size_t finalSize = dataSize + padding; + + if (mData.size() != finalSize) { + mData.resize(finalSize); } if (out != nullptr) {