Do not write into EglSurfaceTexture after creation
... beacuse it prevents other clients from connecting.
Instead, render/compress black image into output buffers
directly if the input surface texture doesn't have any
buffer yet.
Bug: 301023410
Test: OpenCamera
Test: atest virtual_camera_tests
Change-Id: I289ff00b590c9bc18052ae0cc15a9d7320b8f033
diff --git a/services/camera/virtualcamera/util/JpegUtil.cc b/services/camera/virtualcamera/util/JpegUtil.cc
index 6f10376..2b19c13 100644
--- a/services/camera/virtualcamera/util/JpegUtil.cc
+++ b/services/camera/virtualcamera/util/JpegUtil.cc
@@ -20,6 +20,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
+#include <vector>
#include "android/hardware_buffer.h"
#include "jpeglib.h"
@@ -37,10 +38,9 @@
class LibJpegContext {
public:
- LibJpegContext(int width, int height, const android_ycbcr& ycbcr,
- const size_t outBufferSize, void* outBuffer)
- : mYCbCr(ycbcr),
- mWidth(width),
+ LibJpegContext(int width, int height, const size_t outBufferSize,
+ void* outBuffer)
+ : mWidth(width),
mHeight(height),
mDstBufferSize(outBufferSize),
mDstBuffer(outBuffer) {
@@ -94,15 +94,15 @@
mCompressStruct.comp_info[2].v_samp_factor = 1;
}
- bool compress() {
+ bool compress(const android_ycbcr& ycbr) {
// Prepare arrays of pointers to scanlines of each plane.
std::vector<JSAMPROW> yLines(mHeight);
std::vector<JSAMPROW> cbLines(mHeight / 2);
std::vector<JSAMPROW> crLines(mHeight / 2);
- uint8_t* y = static_cast<uint8_t*>(mYCbCr.y);
- uint8_t* cb = static_cast<uint8_t*>(mYCbCr.cb);
- uint8_t* cr = static_cast<uint8_t*>(mYCbCr.cr);
+ uint8_t* y = static_cast<uint8_t*>(ycbr.y);
+ uint8_t* cb = static_cast<uint8_t*>(ycbr.cb);
+ uint8_t* cr = static_cast<uint8_t*>(ycbr.cr);
// Since UV samples might be interleaved (semiplanar) we need to copy
// them to separate planes, since libjpeg doesn't directly
@@ -115,20 +115,59 @@
for (int i = 0; i < c_samples; ++i) {
cb_plane[i] = *cb;
cr_plane[i] = *cr;
- cb += mYCbCr.chroma_step;
- cr += mYCbCr.chroma_step;
+ cb += ycbr.chroma_step;
+ cr += ycbr.chroma_step;
}
// Collect pointers to individual scanline of each plane.
for (int i = 0; i < mHeight; ++i) {
- yLines[i] = y + i * mYCbCr.ystride;
+ yLines[i] = y + i * ycbr.ystride;
}
for (int i = 0; i < (mHeight / 2); ++i) {
cbLines[i] = cb_plane.data() + i * (mWidth / 2);
crLines[i] = cr_plane.data() + i * (mWidth / 2);
}
- // Perform actual compression.
+ return compress(yLines, cbLines, crLines);
+ }
+
+ bool compressBlackImage() {
+ // We only really need to prepare one scanline for Y and one shared scanline
+ // for Cb & Cr.
+ std::vector<uint8_t> yLine(mWidth, 0);
+ std::vector<uint8_t> chromaLine(mWidth / 2, 0xff / 2);
+
+ std::vector<JSAMPROW> yLines(mHeight, yLine.data());
+ std::vector<JSAMPROW> cLines(mHeight / 2, chromaLine.data());
+
+ return compress(yLines, cLines, cLines);
+ }
+
+ private:
+ void setSuccess(const boolean success) {
+ mSuccess = success;
+ }
+
+ void initDestination() {
+ mDestinationMgr.next_output_byte = reinterpret_cast<JOCTET*>(mDstBuffer);
+ mDestinationMgr.free_in_buffer = mDstBufferSize;
+ ALOGV("%s:%d jpeg start: %p [%zu]", __FUNCTION__, __LINE__, mDstBuffer,
+ mDstBufferSize);
+ }
+
+ void termDestination() {
+ mEncodedSize = mDstBufferSize - mDestinationMgr.free_in_buffer;
+ ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, mEncodedSize);
+ }
+
+ // Perform actual compression.
+ //
+ // Takes vector of pointers to Y / Cb / Cr scanlines as an input. Length of
+ // each vector needs to correspond to height of corresponding plane.
+ //
+ // Returns true if compression is successful, false otherwise.
+ bool compress(std::vector<JSAMPROW>& yLines, std::vector<JSAMPROW>& cbLines,
+ std::vector<JSAMPROW>& crLines) {
jpeg_start_compress(&mCompressStruct, TRUE);
while (mCompressStruct.next_scanline < mCompressStruct.image_height) {
@@ -149,23 +188,6 @@
return mSuccess;
}
- private:
- void setSuccess(const boolean success) {
- mSuccess = success;
- }
-
- void initDestination() {
- mDestinationMgr.next_output_byte = reinterpret_cast<JOCTET*>(mDstBuffer);
- mDestinationMgr.free_in_buffer = mDstBufferSize;
- ALOGV("%s:%d jpeg start: %p [%zu]", __FUNCTION__, __LINE__, mDstBuffer,
- mDstBufferSize);
- }
-
- void termDestination() {
- mEncodedSize = mDstBufferSize - mDestinationMgr.free_in_buffer;
- ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, mEncodedSize);
- }
-
// === libjpeg callbacks below ===
static void onOutputError(j_common_ptr cinfo) {
@@ -195,9 +217,6 @@
jpeg_error_mgr mErrorMgr;
jpeg_destination_mgr mDestinationMgr;
- // Layout of the input image.
- android_ycbcr mYCbCr;
-
// Dimensions of the input image.
int mWidth;
int mHeight;
@@ -216,11 +235,15 @@
} // namespace
-// Returns true if the EGL is in an error state and logs the error.
bool compressJpeg(int width, int height, const android_ycbcr& ycbcr,
size_t outBufferSize, void* outBuffer) {
- return LibJpegContext(width, height, ycbcr, outBufferSize, outBuffer)
- .compress();
+ return LibJpegContext(width, height, outBufferSize, outBuffer).compress(ycbcr);
+}
+
+bool compressBlackJpeg(int width, int height, size_t outBufferSize,
+ void* outBuffer) {
+ return LibJpegContext(width, height, outBufferSize, outBuffer)
+ .compressBlackImage();
}
} // namespace virtualcamera