Camera: add webcam offline processing support
Test: CTS/VTS in follow up changes
Bug: 135142453
Change-Id: Ie95f22795f4807ed384443de0fffc65c5dde16d1
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 9ff0d74..5f86742 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -81,8 +81,6 @@
return locked;
}
-buffer_handle_t sEmptyBuffer = nullptr;
-
} // Anonymous namespace
// Static instances
@@ -119,8 +117,8 @@
std::string make, model;
if (ret < 0) {
ALOGW("%s v4l2 QUERYCAP failed", __FUNCTION__);
- make = "Generic UVC webcam";
- model = "Generic UVC webcam";
+ mExifMake = "Generic UVC webcam";
+ mExifModel = "Generic UVC webcam";
} else {
// capability.card is UTF-8 encoded
char card[32];
@@ -134,11 +132,11 @@
}
}
if (j == 0 || card[j - 1] != '\0') {
- make = "Generic UVC webcam";
- model = "Generic UVC webcam";
+ mExifMake = "Generic UVC webcam";
+ mExifModel = "Generic UVC webcam";
} else {
- make = card;
- model = card;
+ mExifMake = card;
+ mExifModel = card;
}
}
@@ -147,7 +145,7 @@
ALOGE("%s: init OutputThread failed!", __FUNCTION__);
return true;
}
- mOutputThread->setExifMakeModel(make, model);
+ mOutputThread->setExifMakeModel(mExifMake, mExifModel);
status_t status = initDefaultRequests();
if (status != OK) {
@@ -161,7 +159,7 @@
ALOGE("%s: invalid request fmq", __FUNCTION__);
return true;
}
- mResultMetadataQueue = std::make_shared<RequestMetadataQueue>(
+ mResultMetadataQueue = std::make_shared<ResultMetadataQueue>(
kMetadataMsgQueueSize, false /* non blocking */);
if (!mResultMetadataQueue->isValid()) {
ALOGE("%s: invalid result fmq", __FUNCTION__);
@@ -183,7 +181,7 @@
}
void ExternalCameraDeviceSession::initOutputThread() {
- mOutputThread = new OutputThread(this, mCroppingType);
+ mOutputThread = new OutputThread(this, mCroppingType, mCameraCharacteristics);
}
void ExternalCameraDeviceSession::closeOutputThread() {
@@ -518,35 +516,9 @@
uint64_t bufId, buffer_handle_t buf,
/*out*/buffer_handle_t** outBufPtr,
bool allowEmptyBuf) {
-
- if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
- if (allowEmptyBuf) {
- *outBufPtr = &sEmptyBuffer;
- return Status::OK;
- } else {
- ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
- return Status::ILLEGAL_ARGUMENT;
- }
- }
-
- CirculatingBuffers& cbs = mCirculatingBuffers[streamId];
- if (cbs.count(bufId) == 0) {
- if (buf == nullptr) {
- ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
- return Status::ILLEGAL_ARGUMENT;
- }
- // Register a newly seen buffer
- buffer_handle_t importedBuf = buf;
- sHandleImporter.importBuffer(importedBuf);
- if (importedBuf == nullptr) {
- ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId);
- return Status::INTERNAL_ERROR;
- } else {
- cbs[bufId] = importedBuf;
- }
- }
- *outBufPtr = &cbs[bufId];
- return Status::OK;
+ return importBufferImpl(
+ mCirculatingBuffers, sHandleImporter, streamId,
+ bufId, buf, outBufPtr, allowEmptyBuf);
}
Status ExternalCameraDeviceSession::importRequestLockedImpl(
@@ -791,15 +763,32 @@
//TODO: refactor with processCaptureResult
Status ExternalCameraDeviceSession::processCaptureRequestError(
- const std::shared_ptr<HalRequest>& req) {
+ const std::shared_ptr<HalRequest>& req,
+ /*out*/std::vector<NotifyMsg>* outMsgs,
+ /*out*/std::vector<CaptureResult>* outResults) {
ATRACE_CALL();
// Return V4L2 buffer to V4L2 buffer queue
- enqueueV4l2Frame(req->frameIn);
+ sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+ static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get());
+ enqueueV4l2Frame(v4l2Frame);
- // NotifyShutter
- notifyShutter(req->frameNumber, req->shutterTs);
+ if (outMsgs == nullptr) {
+ notifyShutter(req->frameNumber, req->shutterTs);
+ notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+ } else {
+ NotifyMsg shutter;
+ shutter.type = MsgType::SHUTTER;
+ shutter.msg.shutter.frameNumber = req->frameNumber;
+ shutter.msg.shutter.timestamp = req->shutterTs;
- notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+ NotifyMsg error;
+ error.type = MsgType::ERROR;
+ error.msg.error.frameNumber = req->frameNumber;
+ error.msg.error.errorStreamId = -1;
+ error.msg.error.errorCode = ErrorCode::ERROR_REQUEST;
+ outMsgs->push_back(shutter);
+ outMsgs->push_back(error);
+ }
// Fill output buffers
hidl_vec<CaptureResult> results;
@@ -826,16 +815,22 @@
mInflightFrames.erase(req->frameNumber);
}
- // Callback into framework
- invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
- freeReleaseFences(results);
+ if (outResults == nullptr) {
+ // Callback into framework
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+ freeReleaseFences(results);
+ } else {
+ outResults->push_back(result);
+ }
return Status::OK;
}
Status ExternalCameraDeviceSession::processCaptureResult(std::shared_ptr<HalRequest>& req) {
ATRACE_CALL();
// Return V4L2 buffer to V4L2 buffer queue
- enqueueV4l2Frame(req->frameIn);
+ sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+ static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get());
+ enqueueV4l2Frame(v4l2Frame);
// NotifyShutter
notifyShutter(req->frameNumber, req->shutterTs);
@@ -923,29 +918,10 @@
mProcessCaptureResultLock.unlock();
}
-void ExternalCameraDeviceSession::freeReleaseFences(hidl_vec<CaptureResult>& results) {
- for (auto& result : results) {
- if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
- native_handle_t* handle = const_cast<native_handle_t*>(
- result.inputBuffer.releaseFence.getNativeHandle());
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- for (auto& buf : result.outputBuffers) {
- if (buf.releaseFence.getNativeHandle() != nullptr) {
- native_handle_t* handle = const_cast<native_handle_t*>(
- buf.releaseFence.getNativeHandle());
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- }
- }
- return;
-}
-
ExternalCameraDeviceSession::OutputThread::OutputThread(
- wp<ExternalCameraDeviceSession> parent,
- CroppingType ct) : mParent(parent), mCroppingType(ct) {}
+ wp<OutputThreadInterface> parent, CroppingType ct,
+ const common::V1_0::helper::CameraMetadata& chars) :
+ mParent(parent), mCroppingType(ct), mCameraCharacteristics(chars) {}
ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
@@ -955,88 +931,6 @@
mExifModel = model;
}
-uint32_t ExternalCameraDeviceSession::OutputThread::getFourCcFromLayout(
- const YCbCrLayout& layout) {
- intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
- intptr_t cr = reinterpret_cast<intptr_t>(layout.cr);
- if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) {
- // Interleaved format
- if (layout.cb > layout.cr) {
- return V4L2_PIX_FMT_NV21;
- } else {
- return V4L2_PIX_FMT_NV12;
- }
- } else if (layout.chromaStep == 1) {
- // Planar format
- if (layout.cb > layout.cr) {
- return V4L2_PIX_FMT_YVU420; // YV12
- } else {
- return V4L2_PIX_FMT_YUV420; // YU12
- }
- } else {
- return FLEX_YUV_GENERIC;
- }
-}
-
-int ExternalCameraDeviceSession::OutputThread::getCropRect(
- CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) {
- if (out == nullptr) {
- ALOGE("%s: out is null", __FUNCTION__);
- return -1;
- }
-
- uint32_t inW = inSize.width;
- uint32_t inH = inSize.height;
- uint32_t outW = outSize.width;
- uint32_t outH = outSize.height;
-
- // Handle special case where aspect ratio is close to input but scaled
- // dimension is slightly larger than input
- float arIn = ASPECT_RATIO(inSize);
- float arOut = ASPECT_RATIO(outSize);
- if (isAspectRatioClose(arIn, arOut)) {
- out->left = 0;
- out->top = 0;
- out->width = inW;
- out->height = inH;
- return 0;
- }
-
- if (ct == VERTICAL) {
- uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
- if (scaledOutH > inH) {
- ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d",
- __FUNCTION__, outW, outH, inW, inH);
- return -1;
- }
- scaledOutH = scaledOutH & ~0x1; // make it multiple of 2
-
- out->left = 0;
- out->top = ((inH - scaledOutH) / 2) & ~0x1;
- out->width = inW;
- out->height = static_cast<int32_t>(scaledOutH);
- ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d",
- __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH));
- } else {
- uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH;
- if (scaledOutW > inW) {
- ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d",
- __FUNCTION__, outW, outH, inW, inH);
- return -1;
- }
- scaledOutW = scaledOutW & ~0x1; // make it multiple of 2
-
- out->left = ((inW - scaledOutW) / 2) & ~0x1;
- out->top = 0;
- out->width = static_cast<int32_t>(scaledOutW);
- out->height = inH;
- ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d",
- __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW));
- }
-
- return 0;
-}
-
int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked(
sp<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) {
Size inSz = {in->mWidth, in->mHeight};
@@ -1274,265 +1168,6 @@
return 0;
}
-int ExternalCameraDeviceSession::OutputThread::formatConvertLocked(
- const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
- int ret = 0;
- switch (format) {
- case V4L2_PIX_FMT_NV21:
- ret = libyuv::I420ToNV21(
- static_cast<uint8_t*>(in.y),
- in.yStride,
- static_cast<uint8_t*>(in.cb),
- in.cStride,
- static_cast<uint8_t*>(in.cr),
- in.cStride,
- static_cast<uint8_t*>(out.y),
- out.yStride,
- static_cast<uint8_t*>(out.cr),
- out.cStride,
- sz.width,
- sz.height);
- if (ret != 0) {
- ALOGE("%s: convert to NV21 buffer failed! ret %d",
- __FUNCTION__, ret);
- return ret;
- }
- break;
- case V4L2_PIX_FMT_NV12:
- ret = libyuv::I420ToNV12(
- static_cast<uint8_t*>(in.y),
- in.yStride,
- static_cast<uint8_t*>(in.cb),
- in.cStride,
- static_cast<uint8_t*>(in.cr),
- in.cStride,
- static_cast<uint8_t*>(out.y),
- out.yStride,
- static_cast<uint8_t*>(out.cb),
- out.cStride,
- sz.width,
- sz.height);
- if (ret != 0) {
- ALOGE("%s: convert to NV12 buffer failed! ret %d",
- __FUNCTION__, ret);
- return ret;
- }
- break;
- case V4L2_PIX_FMT_YVU420: // YV12
- case V4L2_PIX_FMT_YUV420: // YU12
- // TODO: maybe we can speed up here by somehow save this copy?
- ret = libyuv::I420Copy(
- static_cast<uint8_t*>(in.y),
- in.yStride,
- static_cast<uint8_t*>(in.cb),
- in.cStride,
- static_cast<uint8_t*>(in.cr),
- in.cStride,
- static_cast<uint8_t*>(out.y),
- out.yStride,
- static_cast<uint8_t*>(out.cb),
- out.cStride,
- static_cast<uint8_t*>(out.cr),
- out.cStride,
- sz.width,
- sz.height);
- if (ret != 0) {
- ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d",
- __FUNCTION__, ret);
- return ret;
- }
- break;
- case FLEX_YUV_GENERIC:
- // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow.
- ALOGE("%s: unsupported flexible yuv layout"
- " y %p cb %p cr %p y_str %d c_str %d c_step %d",
- __FUNCTION__, out.y, out.cb, out.cr,
- out.yStride, out.cStride, out.chromaStep);
- return -1;
- default:
- ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format);
- return -1;
- }
- return 0;
-}
-
-int ExternalCameraDeviceSession::OutputThread::encodeJpegYU12(
- const Size & inSz, const YCbCrLayout& inLayout,
- int jpegQuality, const void *app1Buffer, size_t app1Size,
- void *out, const size_t maxOutSize, size_t &actualCodeSize)
-{
- /* libjpeg is a C library so we use C-style "inheritance" by
- * putting libjpeg's jpeg_destination_mgr first in our custom
- * struct. This allows us to cast jpeg_destination_mgr* to
- * CustomJpegDestMgr* when we get it passed to us in a callback */
- struct CustomJpegDestMgr {
- struct jpeg_destination_mgr mgr;
- JOCTET *mBuffer;
- size_t mBufferSize;
- size_t mEncodedSize;
- bool mSuccess;
- } dmgr;
-
- jpeg_compress_struct cinfo = {};
- jpeg_error_mgr jerr;
-
- /* Initialize error handling with standard callbacks, but
- * then override output_message (to print to ALOG) and
- * error_exit to set a flag and print a message instead
- * of killing the whole process */
- cinfo.err = jpeg_std_error(&jerr);
-
- cinfo.err->output_message = [](j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
-
- /* Create the message */
- (*cinfo->err->format_message)(cinfo, buffer);
- ALOGE("libjpeg error: %s", buffer);
- };
- cinfo.err->error_exit = [](j_common_ptr cinfo) {
- (*cinfo->err->output_message)(cinfo);
- if(cinfo->client_data) {
- auto & dmgr =
- *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
- dmgr.mSuccess = false;
- }
- };
- /* Now that we initialized some callbacks, let's create our compressor */
- jpeg_create_compress(&cinfo);
-
- /* Initialize our destination manager */
- dmgr.mBuffer = static_cast<JOCTET*>(out);
- dmgr.mBufferSize = maxOutSize;
- dmgr.mEncodedSize = 0;
- dmgr.mSuccess = true;
- cinfo.client_data = static_cast<void*>(&dmgr);
-
- /* These lambdas become C-style function pointers and as per C++11 spec
- * may not capture anything */
- dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
- auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
- dmgr.mgr.next_output_byte = dmgr.mBuffer;
- dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
- ALOGV("%s:%d jpeg start: %p [%zu]",
- __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
- };
-
- dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
- ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
- return 0;
- };
-
- dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
- auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
- dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
- ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
- };
- cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
-
- /* We are going to be using JPEG in raw data mode, so we are passing
- * straight subsampled planar YCbCr and it will not touch our pixel
- * data or do any scaling or anything */
- cinfo.image_width = inSz.width;
- cinfo.image_height = inSz.height;
- cinfo.input_components = 3;
- cinfo.in_color_space = JCS_YCbCr;
-
- /* Initialize defaults and then override what we want */
- jpeg_set_defaults(&cinfo);
-
- jpeg_set_quality(&cinfo, jpegQuality, 1);
- jpeg_set_colorspace(&cinfo, JCS_YCbCr);
- cinfo.raw_data_in = 1;
- cinfo.dct_method = JDCT_IFAST;
-
- /* Configure sampling factors. The sampling factor is JPEG subsampling 420
- * because the source format is YUV420. Note that libjpeg sampling factors
- * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
- * 1 V value for each 2 Y values */
- cinfo.comp_info[0].h_samp_factor = 2;
- cinfo.comp_info[0].v_samp_factor = 2;
- cinfo.comp_info[1].h_samp_factor = 1;
- cinfo.comp_info[1].v_samp_factor = 1;
- cinfo.comp_info[2].h_samp_factor = 1;
- cinfo.comp_info[2].v_samp_factor = 1;
-
- /* Let's not hardcode YUV420 in 6 places... 5 was enough */
- int maxVSampFactor = std::max( {
- cinfo.comp_info[0].v_samp_factor,
- cinfo.comp_info[1].v_samp_factor,
- cinfo.comp_info[2].v_samp_factor
- });
- int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
- cinfo.comp_info[1].v_samp_factor;
-
- /* Start the compressor */
- jpeg_start_compress(&cinfo, TRUE);
-
- /* Compute our macroblock height, so we can pad our input to be vertically
- * macroblock aligned.
- * TODO: Does it need to be horizontally MCU aligned too? */
-
- size_t mcuV = DCTSIZE*maxVSampFactor;
- size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
-
- /* libjpeg uses arrays of row pointers, which makes it really easy to pad
- * data vertically (unfortunately doesn't help horizontally) */
- std::vector<JSAMPROW> yLines (paddedHeight);
- std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
- std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
-
- uint8_t *py = static_cast<uint8_t*>(inLayout.y);
- uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
- uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
-
- for(uint32_t i = 0; i < paddedHeight; i++)
- {
- /* Once we are in the padding territory we still point to the last line
- * effectively replicating it several times ~ CLAMP_TO_EDGE */
- int li = std::min(i, inSz.height - 1);
- yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride);
- if(i < paddedHeight / cVSubSampling)
- {
- crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
- cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
- }
- }
-
- /* If APP1 data was passed in, use it */
- if(app1Buffer && app1Size)
- {
- jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
- static_cast<const JOCTET*>(app1Buffer), app1Size);
- }
-
- /* While we still have padded height left to go, keep giving it one
- * macroblock at a time. */
- while (cinfo.next_scanline < cinfo.image_height) {
- const uint32_t batchSize = DCTSIZE * maxVSampFactor;
- const uint32_t nl = cinfo.next_scanline;
- JSAMPARRAY planes[3]{ &yLines[nl],
- &cbLines[nl/cVSubSampling],
- &crLines[nl/cVSubSampling] };
-
- uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
-
- if (done != batchSize) {
- ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
- __FUNCTION__, done, batchSize, cinfo.next_scanline,
- cinfo.image_height);
- return -1;
- }
- }
-
- /* This will flush everything */
- jpeg_finish_compress(&cinfo);
-
- /* Grab the actual code size and set it */
- actualCodeSize = dmgr.mEncodedSize;
-
- return 0;
-}
-
/*
* TODO: There needs to be a mechanism to discover allocated buffer size
* in the HAL.
@@ -1555,25 +1190,9 @@
}
Size ExternalCameraDeviceSession::getMaxThumbResolution() const {
- Size thumbSize { 0, 0 };
- camera_metadata_ro_entry entry =
- mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
- for(uint32_t i = 0; i < entry.count; i += 2) {
- Size sz { static_cast<uint32_t>(entry.data.i32[i]),
- static_cast<uint32_t>(entry.data.i32[i+1]) };
- if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
- thumbSize = sz;
- }
- }
-
- if (thumbSize.width * thumbSize.height == 0) {
- ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
- }
-
- return thumbSize;
+ return getMaxThumbnailResolution(mCameraCharacteristics);
}
-
ssize_t ExternalCameraDeviceSession::getJpegBufferSize(
uint32_t width, uint32_t height) const {
// Constant from camera3.h
@@ -1616,7 +1235,7 @@
int ExternalCameraDeviceSession::OutputThread::createJpegLocked(
HalStreamBuffer &halBuf,
- const std::shared_ptr<HalRequest>& req)
+ const common::V1_0::helper::CameraMetadata& setting)
{
ATRACE_CALL();
int ret;
@@ -1645,17 +1264,17 @@
Size thumbSize;
bool outputThumbnail = true;
- if (req->setting.exists(ANDROID_JPEG_QUALITY)) {
- camera_metadata_entry entry =
- req->setting.find(ANDROID_JPEG_QUALITY);
+ if (setting.exists(ANDROID_JPEG_QUALITY)) {
+ camera_metadata_ro_entry entry =
+ setting.find(ANDROID_JPEG_QUALITY);
jpegQuality = entry.data.u8[0];
} else {
return lfail("%s: ANDROID_JPEG_QUALITY not set",__FUNCTION__);
}
- if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
- camera_metadata_entry entry =
- req->setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
+ if (setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
+ camera_metadata_ro_entry entry =
+ setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
thumbQuality = entry.data.u8[0];
} else {
return lfail(
@@ -1663,9 +1282,9 @@
__FUNCTION__);
}
- if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
- camera_metadata_entry entry =
- req->setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
+ if (setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
+ camera_metadata_ro_entry entry =
+ setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
thumbSize = Size { static_cast<uint32_t>(entry.data.i32[0]),
static_cast<uint32_t>(entry.data.i32[1])
};
@@ -1732,8 +1351,8 @@
/* Combine camera characteristics with request settings to form EXIF
* metadata */
- common::V1_0::helper::CameraMetadata meta(parent->mCameraCharacteristics);
- meta.append(req->setting);
+ common::V1_0::helper::CameraMetadata meta(mCameraCharacteristics);
+ meta.append(setting);
/* Generate EXIF object */
std::unique_ptr<ExifUtils> utils(ExifUtils::create());
@@ -1838,7 +1457,7 @@
// TODO: see if we can save some computation by converting to YV12 here
uint8_t* inData;
size_t inDataSize;
- if (req->frameIn->map(&inData, &inDataSize) != 0) {
+ if (req->frameIn->getData(&inData, &inDataSize) != 0) {
lk.unlock();
return onDeviceError("%s: V4L2 buffer map failed", __FUNCTION__);
}
@@ -1899,7 +1518,7 @@
// Gralloc lockYCbCr the buffer
switch (halBuf.format) {
case PixelFormat::BLOB: {
- int ret = createJpegLocked(halBuf, req);
+ int ret = createJpegLocked(halBuf, req->setting);
if(ret != 0) {
lk.unlock();
@@ -1949,8 +1568,8 @@
}
Size sz {halBuf.width, halBuf.height};
- ATRACE_BEGIN("formatConvertLocked");
- ret = formatConvertLocked(cropAndScaled, outLayout, sz, outputFourcc);
+ ATRACE_BEGIN("formatConvert");
+ ret = formatConvert(cropAndScaled, outLayout, sz, outputFourcc);
ATRACE_END();
if (ret != 0) {
lk.unlock();
@@ -2055,6 +1674,14 @@
return Status::OK;
}
+void ExternalCameraDeviceSession::OutputThread::clearIntermediateBuffers() {
+ std::lock_guard<std::mutex> lk(mBufferLock);
+ mYu12Frame.clear();
+ mYu12ThumbFrame.clear();
+ mIntermediateBuffers.clear();
+ mBlobBufferSize = 0;
+}
+
Status ExternalCameraDeviceSession::OutputThread::submitRequest(
const std::shared_ptr<HalRequest>& req) {
std::unique_lock<std::mutex> lk(mRequestListLock);
@@ -2090,6 +1717,32 @@
}
}
+std::list<std::shared_ptr<HalRequest>>
+ExternalCameraDeviceSession::OutputThread::switchToOffline() {
+ ATRACE_CALL();
+ std::list<std::shared_ptr<HalRequest>> emptyList;
+ auto parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return emptyList;
+ }
+
+ std::unique_lock<std::mutex> lk(mRequestListLock);
+ std::list<std::shared_ptr<HalRequest>> reqs = std::move(mRequestList);
+ mRequestList.clear();
+ if (mProcessingRequest) {
+ std::chrono::seconds timeout = std::chrono::seconds(kFlushWaitTimeoutSec);
+ auto st = mRequestDoneCond.wait_for(lk, timeout);
+ if (st == std::cv_status::timeout) {
+ ALOGE("%s: wait for inflight request finish timeout!", __FUNCTION__);
+ }
+ }
+ lk.unlock();
+ clearIntermediateBuffers();
+ ALOGV("%s: returning %zu request for offline processing", __FUNCTION__, reqs.size());
+ return reqs;
+}
+
void ExternalCameraDeviceSession::OutputThread::waitForNextRequest(
std::shared_ptr<HalRequest>* out) {
ATRACE_CALL();
@@ -2733,6 +2386,7 @@
return Status::INTERNAL_ERROR;
}
+ mBlobBufferSize = blobBufferSize;
status = mOutputThread->allocateIntermediateBuffers(v4lSize,
mMaxThumbResolution, config.streams, blobBufferSize);
if (status != Status::OK) {
@@ -2916,16 +2570,6 @@
status_t ExternalCameraDeviceSession::fillCaptureResult(
common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) {
- // android.control
- // For USB camera, we don't know the AE state. Set the state to converged to
- // indicate the frame should be good to use. Then apps don't have to wait the
- // AE state.
- const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
- UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1);
-
- const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
- UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
-
bool afTrigger = false;
{
std::lock_guard<std::mutex> lk(mAfTriggerLock);
@@ -2951,46 +2595,10 @@
}
UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1);
- // Set AWB state to converged to indicate the frame should be good to use.
- const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
- UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+ camera_metadata_ro_entry activeArraySize =
+ mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
- UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
-
- camera_metadata_ro_entry active_array_size =
- mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-
- if (active_array_size.count == 0) {
- ALOGE("%s: cannot find active array size!", __FUNCTION__);
- return -EINVAL;
- }
-
- const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
- UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
-
- // This means pipeline latency of X frame intervals. The maximum number is 4.
- const uint8_t requestPipelineMaxDepth = 4;
- UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
-
- // android.scaler
- const int32_t crop_region[] = {
- active_array_size.data.i32[0], active_array_size.data.i32[1],
- active_array_size.data.i32[2], active_array_size.data.i32[3],
- };
- UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region));
-
- // android.sensor
- UPDATE(md, ANDROID_SENSOR_TIMESTAMP, ×tamp, 1);
-
- // android.statistics
- const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
- UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
-
- const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
- UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
-
- return OK;
+ return fillCaptureResultCommon(md, timestamp, activeArraySize);
}
#undef ARRAY_SIZE
diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp
index e25deff..4a6381e 100644
--- a/camera/device/3.4/default/ExternalCameraUtils.cpp
+++ b/camera/device/3.4/default/ExternalCameraUtils.cpp
@@ -18,10 +18,23 @@
#include <log/log.h>
#include <cmath>
+#include <cstring>
#include <sys/mman.h>
#include <linux/videodev2.h>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+#include <jpeglib.h>
+
#include "ExternalCameraUtils.h"
+namespace {
+
+buffer_handle_t sEmptyBuffer = nullptr;
+
+} // Anonymous namespace
+
namespace android {
namespace hardware {
namespace camera {
@@ -29,10 +42,13 @@
namespace V3_4 {
namespace implementation {
+Frame::Frame(uint32_t width, uint32_t height, uint32_t fourcc) :
+ mWidth(width), mHeight(height), mFourcc(fourcc) {}
+
V4L2Frame::V4L2Frame(
uint32_t w, uint32_t h, uint32_t fourcc,
int bufIdx, int fd, uint32_t dataSize, uint64_t offset) :
- mWidth(w), mHeight(h), mFourcc(fourcc),
+ Frame(w, h, fourcc),
mBufferIndex(bufIdx), mFd(fd), mDataSize(dataSize), mOffset(offset) {}
int V4L2Frame::map(uint8_t** data, size_t* dataSize) {
@@ -75,9 +91,13 @@
unmap();
}
+int V4L2Frame::getData(uint8_t** outData, size_t* dataSize) {
+ return map(outData, dataSize);
+}
+
AllocatedFrame::AllocatedFrame(
uint32_t w, uint32_t h) :
- mWidth(w), mHeight(h), mFourcc(V4L2_PIX_FMT_YUV420) {};
+ Frame(w, h, V4L2_PIX_FMT_YUV420) {};
AllocatedFrame::~AllocatedFrame() {}
@@ -106,6 +126,17 @@
return 0;
}
+int AllocatedFrame::getData(uint8_t** outData, size_t* dataSize) {
+ YCbCrLayout layout;
+ int ret = allocate(&layout);
+ if (ret != 0) {
+ return ret;
+ }
+ *outData = mData.data();
+ *dataSize = mData.size();
+ return 0;
+}
+
int AllocatedFrame::getLayout(YCbCrLayout* out) {
IMapper::Rect noCrop = {0, 0,
static_cast<int32_t>(mWidth),
@@ -150,8 +181,520 @@
return durationDenominator / static_cast<double>(durationNumerator);
}
+::android::hardware::camera::common::V1_0::Status importBufferImpl(
+ /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers,
+ /*inout*/HandleImporter& handleImporter,
+ int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf) {
+ using ::android::hardware::camera::common::V1_0::Status;
+ if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
+ if (allowEmptyBuf) {
+ *outBufPtr = &sEmptyBuffer;
+ return Status::OK;
+ } else {
+ ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ }
+
+ CirculatingBuffers& cbs = circulatingBuffers[streamId];
+ if (cbs.count(bufId) == 0) {
+ if (buf == nullptr) {
+ ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ // Register a newly seen buffer
+ buffer_handle_t importedBuf = buf;
+ handleImporter.importBuffer(importedBuf);
+ if (importedBuf == nullptr) {
+ ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId);
+ return Status::INTERNAL_ERROR;
+ } else {
+ cbs[bufId] = importedBuf;
+ }
+ }
+ *outBufPtr = &cbs[bufId];
+ return Status::OK;
+}
+
+uint32_t getFourCcFromLayout(const YCbCrLayout& layout) {
+ intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
+ intptr_t cr = reinterpret_cast<intptr_t>(layout.cr);
+ if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) {
+ // Interleaved format
+ if (layout.cb > layout.cr) {
+ return V4L2_PIX_FMT_NV21;
+ } else {
+ return V4L2_PIX_FMT_NV12;
+ }
+ } else if (layout.chromaStep == 1) {
+ // Planar format
+ if (layout.cb > layout.cr) {
+ return V4L2_PIX_FMT_YVU420; // YV12
+ } else {
+ return V4L2_PIX_FMT_YUV420; // YU12
+ }
+ } else {
+ return FLEX_YUV_GENERIC;
+ }
+}
+
+int getCropRect(
+ CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) {
+ if (out == nullptr) {
+ ALOGE("%s: out is null", __FUNCTION__);
+ return -1;
+ }
+
+ uint32_t inW = inSize.width;
+ uint32_t inH = inSize.height;
+ uint32_t outW = outSize.width;
+ uint32_t outH = outSize.height;
+
+ // Handle special case where aspect ratio is close to input but scaled
+ // dimension is slightly larger than input
+ float arIn = ASPECT_RATIO(inSize);
+ float arOut = ASPECT_RATIO(outSize);
+ if (isAspectRatioClose(arIn, arOut)) {
+ out->left = 0;
+ out->top = 0;
+ out->width = inW;
+ out->height = inH;
+ return 0;
+ }
+
+ if (ct == VERTICAL) {
+ uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
+ if (scaledOutH > inH) {
+ ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d",
+ __FUNCTION__, outW, outH, inW, inH);
+ return -1;
+ }
+ scaledOutH = scaledOutH & ~0x1; // make it multiple of 2
+
+ out->left = 0;
+ out->top = ((inH - scaledOutH) / 2) & ~0x1;
+ out->width = inW;
+ out->height = static_cast<int32_t>(scaledOutH);
+ ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d",
+ __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH));
+ } else {
+ uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH;
+ if (scaledOutW > inW) {
+ ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d",
+ __FUNCTION__, outW, outH, inW, inH);
+ return -1;
+ }
+ scaledOutW = scaledOutW & ~0x1; // make it multiple of 2
+
+ out->left = ((inW - scaledOutW) / 2) & ~0x1;
+ out->top = 0;
+ out->width = static_cast<int32_t>(scaledOutW);
+ out->height = inH;
+ ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d",
+ __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW));
+ }
+
+ return 0;
+}
+
+int formatConvert(
+ const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
+ int ret = 0;
+ switch (format) {
+ case V4L2_PIX_FMT_NV21:
+ ret = libyuv::I420ToNV21(
+ static_cast<uint8_t*>(in.y),
+ in.yStride,
+ static_cast<uint8_t*>(in.cb),
+ in.cStride,
+ static_cast<uint8_t*>(in.cr),
+ in.cStride,
+ static_cast<uint8_t*>(out.y),
+ out.yStride,
+ static_cast<uint8_t*>(out.cr),
+ out.cStride,
+ sz.width,
+ sz.height);
+ if (ret != 0) {
+ ALOGE("%s: convert to NV21 buffer failed! ret %d",
+ __FUNCTION__, ret);
+ return ret;
+ }
+ break;
+ case V4L2_PIX_FMT_NV12:
+ ret = libyuv::I420ToNV12(
+ static_cast<uint8_t*>(in.y),
+ in.yStride,
+ static_cast<uint8_t*>(in.cb),
+ in.cStride,
+ static_cast<uint8_t*>(in.cr),
+ in.cStride,
+ static_cast<uint8_t*>(out.y),
+ out.yStride,
+ static_cast<uint8_t*>(out.cb),
+ out.cStride,
+ sz.width,
+ sz.height);
+ if (ret != 0) {
+ ALOGE("%s: convert to NV12 buffer failed! ret %d",
+ __FUNCTION__, ret);
+ return ret;
+ }
+ break;
+ case V4L2_PIX_FMT_YVU420: // YV12
+ case V4L2_PIX_FMT_YUV420: // YU12
+ // TODO: maybe we can speed up here by somehow save this copy?
+ ret = libyuv::I420Copy(
+ static_cast<uint8_t*>(in.y),
+ in.yStride,
+ static_cast<uint8_t*>(in.cb),
+ in.cStride,
+ static_cast<uint8_t*>(in.cr),
+ in.cStride,
+ static_cast<uint8_t*>(out.y),
+ out.yStride,
+ static_cast<uint8_t*>(out.cb),
+ out.cStride,
+ static_cast<uint8_t*>(out.cr),
+ out.cStride,
+ sz.width,
+ sz.height);
+ if (ret != 0) {
+ ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d",
+ __FUNCTION__, ret);
+ return ret;
+ }
+ break;
+ case FLEX_YUV_GENERIC:
+ // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow.
+ ALOGE("%s: unsupported flexible yuv layout"
+ " y %p cb %p cr %p y_str %d c_str %d c_step %d",
+ __FUNCTION__, out.y, out.cb, out.cr,
+ out.yStride, out.cStride, out.chromaStep);
+ return -1;
+ default:
+ ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format);
+ return -1;
+ }
+ return 0;
+}
+
+int encodeJpegYU12(
+ const Size & inSz, const YCbCrLayout& inLayout,
+ int jpegQuality, const void *app1Buffer, size_t app1Size,
+ void *out, const size_t maxOutSize, size_t &actualCodeSize)
+{
+ /* libjpeg is a C library so we use C-style "inheritance" by
+ * putting libjpeg's jpeg_destination_mgr first in our custom
+ * struct. This allows us to cast jpeg_destination_mgr* to
+ * CustomJpegDestMgr* when we get it passed to us in a callback */
+ struct CustomJpegDestMgr {
+ struct jpeg_destination_mgr mgr;
+ JOCTET *mBuffer;
+ size_t mBufferSize;
+ size_t mEncodedSize;
+ bool mSuccess;
+ } dmgr;
+
+ jpeg_compress_struct cinfo = {};
+ jpeg_error_mgr jerr;
+
+ /* Initialize error handling with standard callbacks, but
+ * then override output_message (to print to ALOG) and
+ * error_exit to set a flag and print a message instead
+ * of killing the whole process */
+ cinfo.err = jpeg_std_error(&jerr);
+
+ cinfo.err->output_message = [](j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ ALOGE("libjpeg error: %s", buffer);
+ };
+ cinfo.err->error_exit = [](j_common_ptr cinfo) {
+ (*cinfo->err->output_message)(cinfo);
+ if(cinfo->client_data) {
+ auto & dmgr =
+ *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
+ dmgr.mSuccess = false;
+ }
+ };
+ /* Now that we initialized some callbacks, let's create our compressor */
+ jpeg_create_compress(&cinfo);
+
+ /* Initialize our destination manager */
+ dmgr.mBuffer = static_cast<JOCTET*>(out);
+ dmgr.mBufferSize = maxOutSize;
+ dmgr.mEncodedSize = 0;
+ dmgr.mSuccess = true;
+ cinfo.client_data = static_cast<void*>(&dmgr);
+
+ /* These lambdas become C-style function pointers and as per C++11 spec
+ * may not capture anything */
+ dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mgr.next_output_byte = dmgr.mBuffer;
+ dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
+ ALOGV("%s:%d jpeg start: %p [%zu]",
+ __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
+ };
+
+ dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
+ ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
+ return 0;
+ };
+
+ dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
+ ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
+ };
+ cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
+
+ /* We are going to be using JPEG in raw data mode, so we are passing
+ * straight subsampled planar YCbCr and it will not touch our pixel
+ * data or do any scaling or anything */
+ cinfo.image_width = inSz.width;
+ cinfo.image_height = inSz.height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_YCbCr;
+
+ /* Initialize defaults and then override what we want */
+ jpeg_set_defaults(&cinfo);
+
+ jpeg_set_quality(&cinfo, jpegQuality, 1);
+ jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+ cinfo.raw_data_in = 1;
+ cinfo.dct_method = JDCT_IFAST;
+
+ /* Configure sampling factors. The sampling factor is JPEG subsampling 420
+ * because the source format is YUV420. Note that libjpeg sampling factors
+ * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
+ * 1 V value for each 2 Y values */
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+
+ /* Let's not hardcode YUV420 in 6 places... 5 was enough */
+ int maxVSampFactor = std::max( {
+ cinfo.comp_info[0].v_samp_factor,
+ cinfo.comp_info[1].v_samp_factor,
+ cinfo.comp_info[2].v_samp_factor
+ });
+ int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
+ cinfo.comp_info[1].v_samp_factor;
+
+ /* Start the compressor */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Compute our macroblock height, so we can pad our input to be vertically
+ * macroblock aligned.
+ * TODO: Does it need to be horizontally MCU aligned too? */
+
+ size_t mcuV = DCTSIZE*maxVSampFactor;
+ size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
+
+ /* libjpeg uses arrays of row pointers, which makes it really easy to pad
+ * data vertically (unfortunately doesn't help horizontally) */
+ std::vector<JSAMPROW> yLines (paddedHeight);
+ std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
+ std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
+
+ uint8_t *py = static_cast<uint8_t*>(inLayout.y);
+ uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
+ uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
+
+ for(uint32_t i = 0; i < paddedHeight; i++)
+ {
+ /* Once we are in the padding territory we still point to the last line
+ * effectively replicating it several times ~ CLAMP_TO_EDGE */
+ int li = std::min(i, inSz.height - 1);
+ yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride);
+ if(i < paddedHeight / cVSubSampling)
+ {
+ crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
+ cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
+ }
+ }
+
+ /* If APP1 data was passed in, use it */
+ if(app1Buffer && app1Size)
+ {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
+ static_cast<const JOCTET*>(app1Buffer), app1Size);
+ }
+
+ /* While we still have padded height left to go, keep giving it one
+ * macroblock at a time. */
+ while (cinfo.next_scanline < cinfo.image_height) {
+ const uint32_t batchSize = DCTSIZE * maxVSampFactor;
+ const uint32_t nl = cinfo.next_scanline;
+ JSAMPARRAY planes[3]{ &yLines[nl],
+ &cbLines[nl/cVSubSampling],
+ &crLines[nl/cVSubSampling] };
+
+ uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
+
+ if (done != batchSize) {
+ ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
+ __FUNCTION__, done, batchSize, cinfo.next_scanline,
+ cinfo.image_height);
+ return -1;
+ }
+ }
+
+ /* This will flush everything */
+ jpeg_finish_compress(&cinfo);
+
+ /* Grab the actual code size and set it */
+ actualCodeSize = dmgr.mEncodedSize;
+
+ return 0;
+}
+
+Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata& chars) {
+ Size thumbSize { 0, 0 };
+ camera_metadata_ro_entry entry =
+ chars.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+ for(uint32_t i = 0; i < entry.count; i += 2) {
+ Size sz { static_cast<uint32_t>(entry.data.i32[i]),
+ static_cast<uint32_t>(entry.data.i32[i+1]) };
+ if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
+ thumbSize = sz;
+ }
+ }
+
+ if (thumbSize.width * thumbSize.height == 0) {
+ ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
+ }
+
+ return thumbSize;
+}
+
+void freeReleaseFences(hidl_vec<V3_2::CaptureResult>& results) {
+ for (auto& result : results) {
+ if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ result.inputBuffer.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ for (auto& buf : result.outputBuffers) {
+ if (buf.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ buf.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ }
+ }
+ return;
+}
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#define UPDATE(md, tag, data, size) \
+do { \
+ if ((md).update((tag), (data), (size))) { \
+ ALOGE("Update " #tag " failed!"); \
+ return BAD_VALUE; \
+ } \
+} while (0)
+
+status_t fillCaptureResultCommon(
+ common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp,
+ camera_metadata_ro_entry& activeArraySize) {
+ if (activeArraySize.count < 4) {
+ ALOGE("%s: cannot find active array size!", __FUNCTION__);
+ return -EINVAL;
+ }
+ // android.control
+ // For USB camera, we don't know the AE state. Set the state to converged to
+ // indicate the frame should be good to use. Then apps don't have to wait the
+ // AE state.
+ const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+ UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1);
+
+ const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
+ UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
+
+ // Set AWB state to converged to indicate the frame should be good to use.
+ const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
+ UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+
+ const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+ UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+ const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+ UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
+
+ // This means pipeline latency of X frame intervals. The maximum number is 4.
+ const uint8_t requestPipelineMaxDepth = 4;
+ UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
+
+ // android.scaler
+ const int32_t crop_region[] = {
+ activeArraySize.data.i32[0], activeArraySize.data.i32[1],
+ activeArraySize.data.i32[2], activeArraySize.data.i32[3],
+ };
+ UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region));
+
+ // android.sensor
+ UPDATE(md, ANDROID_SENSOR_TIMESTAMP, ×tamp, 1);
+
+ // android.statistics
+ const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+ UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
+
+ const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+ UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
+
+ return OK;
+}
+
+#undef ARRAY_SIZE
+#undef UPDATE
+
} // namespace implementation
} // namespace V3_4
+
+namespace V3_6 {
+namespace implementation {
+
+AllocatedV4L2Frame::AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn) :
+ Frame(frameIn->mWidth, frameIn->mHeight, frameIn->mFourcc) {
+ uint8_t* dataIn;
+ size_t dataSize;
+ if (frameIn->getData(&dataIn, &dataSize) != 0) {
+ ALOGE("%s: map input V4L2 frame failed!", __FUNCTION__);
+ return;
+ }
+
+ mData.resize(dataSize);
+ std::memcpy(mData.data(), dataIn, dataSize);
+}
+
+int AllocatedV4L2Frame::getData(uint8_t** outData, size_t* dataSize) {
+ if (outData == nullptr || dataSize == nullptr) {
+ ALOGE("%s: outData(%p)/dataSize(%p) must not be null", __FUNCTION__, outData, dataSize);
+ return -1;
+ }
+
+ *outData = mData.data();
+ *dataSize = mData.size();
+ return 0;
+}
+
+AllocatedV4L2Frame::~AllocatedV4L2Frame() {}
+
+} // namespace implementation
+} // namespace V3_6
} // namespace device
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 71b7c17..ecab9cf 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
-#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
@@ -84,7 +84,8 @@
using ::android::Mutex;
using ::android::base::unique_fd;
-struct ExternalCameraDeviceSession : public virtual RefBase {
+struct ExternalCameraDeviceSession : public virtual RefBase,
+ public virtual OutputThreadInterface {
ExternalCameraDeviceSession(const sp<ICameraDeviceCallback>&,
const ExternalCameraConfig& cfg,
@@ -110,6 +111,82 @@
static const int kMaxStallStream = 1;
static const uint32_t kMaxBytesPerPixel = 2;
+ class OutputThread : public android::Thread {
+ public:
+ OutputThread(wp<OutputThreadInterface> parent, CroppingType,
+ const common::V1_0::helper::CameraMetadata&);
+ virtual ~OutputThread();
+
+ Status allocateIntermediateBuffers(
+ const Size& v4lSize, const Size& thumbSize,
+ const hidl_vec<Stream>& streams,
+ uint32_t blobBufferSize);
+ Status submitRequest(const std::shared_ptr<HalRequest>&);
+ void flush();
+ void dump(int fd);
+ virtual bool threadLoop() override;
+
+ void setExifMakeModel(const std::string& make, const std::string& model);
+
+ // The remaining request list is returned for offline processing
+ std::list<std::shared_ptr<HalRequest>> switchToOffline();
+
+ protected:
+ // Methods to request output buffer in parallel
+ // No-op for device@3.4. Implemented in device@3.5
+ virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; }
+ virtual int waitForBufferRequestDone(
+ /*out*/std::vector<HalStreamBuffer>*) { return 0; }
+
+ static const int kFlushWaitTimeoutSec = 3; // 3 sec
+ static const int kReqWaitTimeoutMs = 33; // 33ms
+ static const int kReqWaitTimesMax = 90; // 33ms * 90 ~= 3 sec
+
+ void waitForNextRequest(std::shared_ptr<HalRequest>* out);
+ void signalRequestDone();
+
+ int cropAndScaleLocked(
+ sp<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int cropAndScaleThumbLocked(
+ sp<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int createJpegLocked(HalStreamBuffer &halBuf,
+ const common::V1_0::helper::CameraMetadata& settings);
+
+ void clearIntermediateBuffers();
+
+ const wp<OutputThreadInterface> mParent;
+ const CroppingType mCroppingType;
+ const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+
+ mutable std::mutex mRequestListLock; // Protect acccess to mRequestList,
+ // mProcessingRequest and mProcessingFrameNumer
+ std::condition_variable mRequestCond; // signaled when a new request is submitted
+ std::condition_variable mRequestDoneCond; // signaled when a request is done processing
+ std::list<std::shared_ptr<HalRequest>> mRequestList;
+ bool mProcessingRequest = false;
+ uint32_t mProcessingFrameNumer = 0;
+
+ // V4L2 frameIn
+ // (MJPG decode)-> mYu12Frame
+ // (Scale)-> mScaledYu12Frames
+ // (Format convert) -> output gralloc frames
+ mutable std::mutex mBufferLock; // Protect access to intermediate buffers
+ sp<AllocatedFrame> mYu12Frame;
+ sp<AllocatedFrame> mYu12ThumbFrame;
+ std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
+ std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
+ YCbCrLayout mYu12FrameLayout;
+ YCbCrLayout mYu12ThumbFrameLayout;
+ uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size
+
+ std::string mExifMake;
+ std::string mExifModel;
+ };
+
protected:
// Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow
@@ -150,27 +227,22 @@
ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb);
protected:
- struct HalStreamBuffer {
- int32_t streamId;
- uint64_t bufferId;
- uint32_t width;
- uint32_t height;
- PixelFormat format;
- V3_2::BufferUsageFlags usage;
- buffer_handle_t* bufPtr;
- int acquireFence;
- bool fenceTimeout;
- };
+ // Methods from OutputThreadInterface
+ virtual Status importBuffer(int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf) override;
- struct HalRequest {
- uint32_t frameNumber;
- common::V1_0::helper::CameraMetadata setting;
- sp<V4L2Frame> frameIn;
- nsecs_t shutterTs;
- std::vector<HalStreamBuffer> buffers;
- };
+ virtual Status processCaptureResult(std::shared_ptr<HalRequest>&) override;
- static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+ virtual Status processCaptureRequestError(const std::shared_ptr<HalRequest>&,
+ /*out*/std::vector<NotifyMsg>* msgs = nullptr,
+ /*out*/std::vector<CaptureResult>* results = nullptr) override;
+
+ virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
+
+ virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) override;
+ // End of OutputThreadInterface methods
Status constructDefaultRequestSettingsRaw(RequestTemplate type,
V3_2::CameraMetadata *outMetadata);
@@ -219,11 +291,6 @@
// Optional argument for ICameraDeviceSession@3.5 impl
bool allowEmptyBuf = false);
- Status importBuffer(int32_t streamId,
- uint64_t bufId, buffer_handle_t buf,
- /*out*/buffer_handle_t** outBufPtr,
- bool allowEmptyBuf);
-
Status importBufferLocked(int32_t streamId,
uint64_t bufId, buffer_handle_t buf,
/*out*/buffer_handle_t** outBufPtr,
@@ -236,106 +303,15 @@
Status processOneCaptureRequest(const CaptureRequest& request);
- Status processCaptureResult(std::shared_ptr<HalRequest>&);
- Status processCaptureRequestError(const std::shared_ptr<HalRequest>&);
void notifyShutter(uint32_t frameNumber, nsecs_t shutterTs);
- void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec);
void invokeProcessCaptureResultCallback(
hidl_vec<CaptureResult> &results, bool tryWriteFmq);
- static void freeReleaseFences(hidl_vec<CaptureResult>&);
Size getMaxJpegResolution() const;
Size getMaxThumbResolution() const;
- ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
-
int waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk);
- class OutputThread : public android::Thread {
- public:
- OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
- virtual ~OutputThread();
-
- Status allocateIntermediateBuffers(
- const Size& v4lSize, const Size& thumbSize,
- const hidl_vec<Stream>& streams,
- uint32_t blobBufferSize);
- Status submitRequest(const std::shared_ptr<HalRequest>&);
- void flush();
- void dump(int fd);
- virtual bool threadLoop() override;
-
- void setExifMakeModel(const std::string& make, const std::string& model);
-
- protected:
- // Methods to request output buffer in parallel
- // No-op for device@3.4. Implemented in device@3.5
- virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; }
- virtual int waitForBufferRequestDone(
- /*out*/std::vector<HalStreamBuffer>*) { return 0; }
-
- static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
- static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
- static_cast<uint32_t>('X') << 24;
- // returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21
- static uint32_t getFourCcFromLayout(const YCbCrLayout&);
- static int getCropRect(
- CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out);
-
- static const int kFlushWaitTimeoutSec = 3; // 3 sec
- static const int kReqWaitTimeoutMs = 33; // 33ms
- static const int kReqWaitTimesMax = 90; // 33ms * 90 ~= 3 sec
-
- void waitForNextRequest(std::shared_ptr<HalRequest>* out);
- void signalRequestDone();
-
- int cropAndScaleLocked(
- sp<AllocatedFrame>& in, const Size& outSize,
- YCbCrLayout* out);
-
- int cropAndScaleThumbLocked(
- sp<AllocatedFrame>& in, const Size& outSize,
- YCbCrLayout* out);
-
- int formatConvertLocked(const YCbCrLayout& in, const YCbCrLayout& out,
- Size sz, uint32_t format);
-
- static int encodeJpegYU12(const Size &inSz,
- const YCbCrLayout& inLayout, int jpegQuality,
- const void *app1Buffer, size_t app1Size,
- void *out, size_t maxOutSize,
- size_t &actualCodeSize);
-
- int createJpegLocked(HalStreamBuffer &halBuf, const std::shared_ptr<HalRequest>& req);
-
- const wp<ExternalCameraDeviceSession> mParent;
- const CroppingType mCroppingType;
-
- mutable std::mutex mRequestListLock; // Protect acccess to mRequestList,
- // mProcessingRequest and mProcessingFrameNumer
- std::condition_variable mRequestCond; // signaled when a new request is submitted
- std::condition_variable mRequestDoneCond; // signaled when a request is done processing
- std::list<std::shared_ptr<HalRequest>> mRequestList;
- bool mProcessingRequest = false;
- uint32_t mProcessingFrameNumer = 0;
-
- // V4L2 frameIn
- // (MJPG decode)-> mYu12Frame
- // (Scale)-> mScaledYu12Frames
- // (Format convert) -> output gralloc frames
- mutable std::mutex mBufferLock; // Protect access to intermediate buffers
- sp<AllocatedFrame> mYu12Frame;
- sp<AllocatedFrame> mYu12ThumbFrame;
- std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
- std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
- YCbCrLayout mYu12FrameLayout;
- YCbCrLayout mYu12ThumbFrameLayout;
- uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size
-
- std::string mExifMake;
- std::string mExifModel;
- };
-
// Protect (most of) HIDL interface methods from synchronized-entering
mutable Mutex mInterfaceLock;
@@ -381,12 +357,6 @@
std::mutex mInflightFramesLock; // protect mInflightFrames
std::unordered_set<uint32_t> mInflightFrames;
- // buffers currently circulating between HAL and camera service
- // key: bufferId sent via HIDL interface
- // value: imported buffer_handle_t
- // Buffer will be imported during processCaptureRequest and will be freed
- // when the its stream is deleted or camera device session is closed
- typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
// Stream ID -> circulating buffers map
std::map<int, CirculatingBuffers> mCirculatingBuffers;
// Protect mCirculatingBuffers, must not lock mLock after acquiring this lock
@@ -395,6 +365,8 @@
std::mutex mAfTriggerLock; // protect mAfTrigger
bool mAfTrigger = false;
+ uint32_t mBlobBufferSize = 0;
+
static HandleImporter sHandleImporter;
/* Beginning of members not changed after initialize() */
@@ -410,6 +382,9 @@
const Size mMaxThumbResolution;
const Size mMaxJpegResolution;
+
+ std::string mExifMake;
+ std::string mExifModel;
/* End of members not changed after initialize() */
private:
@@ -484,4 +459,4 @@
} // namespace hardware
} // namespace android
-#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
index 341c622..74f75eb 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
@@ -17,16 +17,27 @@
#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H
#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H
+#include <android/hardware/camera/common/1.0/types.h>
+#include <android/hardware/camera/device/3.2/types.h>
+#include <android/hardware/graphics/common/1.0/types.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <inttypes.h>
#include <mutex>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "tinyxml2.h" // XML parsing
#include "utils/LightRefBase.h"
+#include "utils/Timers.h"
+#include <CameraMetadata.h>
+#include <HandleImporter.h>
-using android::hardware::graphics::mapper::V2_0::IMapper;
-using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+using ::android::hardware::graphics::mapper::V2_0::IMapper;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
namespace android {
namespace hardware {
@@ -113,16 +124,28 @@
std::vector<FrameRate> frameRates;
};
+// A Base class with basic information about a frame
+struct Frame : public VirtualLightRefBase {
+public:
+ Frame(uint32_t width, uint32_t height, uint32_t fourcc);
+ const uint32_t mWidth;
+ const uint32_t mHeight;
+ const uint32_t mFourcc;
+
+ // getData might involve map/allocation
+ virtual int getData(uint8_t** outData, size_t* dataSize) = 0;
+};
+
// A class provide access to a dequeued V4L2 frame buffer (mostly in MJPG format)
// Also contains necessary information to enqueue the buffer back to V4L2 buffer queue
-class V4L2Frame : public virtual VirtualLightRefBase {
+class V4L2Frame : public Frame {
public:
V4L2Frame(uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd,
uint32_t dataSize, uint64_t offset);
~V4L2Frame() override;
- const uint32_t mWidth;
- const uint32_t mHeight;
- const uint32_t mFourcc;
+
+ virtual int getData(uint8_t** outData, size_t* dataSize) override;
+
const int mBufferIndex; // for later enqueue
int map(uint8_t** data, size_t* dataSize);
int unmap();
@@ -137,13 +160,13 @@
// A RAII class representing a CPU allocated YUV frame used as intermeidate buffers
// when generating output images.
-class AllocatedFrame : public virtual VirtualLightRefBase {
+class AllocatedFrame : public Frame {
public:
- AllocatedFrame(uint32_t w, uint32_t h); // TODO: use Size?
+ AllocatedFrame(uint32_t w, uint32_t h); // only support V4L2_PIX_FMT_YUV420 for now
~AllocatedFrame() override;
- const uint32_t mWidth;
- const uint32_t mHeight;
- const uint32_t mFourcc; // Only support YU12 format for now
+
+ virtual int getData(uint8_t** outData, size_t* dataSize) override;
+
int allocate(YCbCrLayout* out = nullptr);
int getLayout(YCbCrLayout* out);
int getCroppedLayout(const IMapper::Rect&, YCbCrLayout* out); // return non-zero for bad input
@@ -165,8 +188,110 @@
bool isAspectRatioClose(float ar1, float ar2);
+struct HalStreamBuffer {
+ int32_t streamId;
+ uint64_t bufferId;
+ uint32_t width;
+ uint32_t height;
+ ::android::hardware::graphics::common::V1_0::PixelFormat format;
+ ::android::hardware::camera::device::V3_2::BufferUsageFlags usage;
+ buffer_handle_t* bufPtr;
+ int acquireFence;
+ bool fenceTimeout;
+};
+
+struct HalRequest {
+ uint32_t frameNumber;
+ common::V1_0::helper::CameraMetadata setting;
+ sp<Frame> frameIn;
+ nsecs_t shutterTs;
+ std::vector<HalStreamBuffer> buffers;
+};
+
+static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+
+// buffers currently circulating between HAL and camera service
+// key: bufferId sent via HIDL interface
+// value: imported buffer_handle_t
+// Buffer will be imported during processCaptureRequest (or requestStreamBuffer
+// in the case of HAL buffer manager is enabled) and will be freed
+// when the stream is deleted or camera device session is closed
+typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
+
+::android::hardware::camera::common::V1_0::Status importBufferImpl(
+ /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers,
+ /*inout*/HandleImporter& handleImporter,
+ int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf);
+
+static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
+ static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
+ static_cast<uint32_t>('X') << 24;
+
+// returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21
+uint32_t getFourCcFromLayout(const YCbCrLayout&);
+
+using ::android::hardware::camera::external::common::Size;
+int getCropRect(CroppingType ct, const Size& inSize,
+ const Size& outSize, IMapper::Rect* out);
+
+int formatConvert(const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format);
+
+int encodeJpegYU12(const Size &inSz,
+ const YCbCrLayout& inLayout, int jpegQuality,
+ const void *app1Buffer, size_t app1Size,
+ void *out, size_t maxOutSize,
+ size_t &actualCodeSize);
+
+Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata&);
+
+void freeReleaseFences(hidl_vec<V3_2::CaptureResult>&);
+
+status_t fillCaptureResultCommon(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp,
+ camera_metadata_ro_entry& activeArraySize);
+
+// Interface for OutputThread calling back to parent
+struct OutputThreadInterface : public virtual RefBase {
+ virtual ::android::hardware::camera::common::V1_0::Status importBuffer(
+ int32_t streamId, uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr, bool allowEmptyBuf) = 0;
+
+ virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) = 0;
+
+ // Callbacks are fired within the method if msgs/results are nullptr.
+ // Otherwise the callbacks will be returned and caller is responsible to
+ // fire the callback later
+ virtual ::android::hardware::camera::common::V1_0::Status processCaptureRequestError(
+ const std::shared_ptr<HalRequest>&,
+ /*out*/std::vector<V3_2::NotifyMsg>* msgs = nullptr,
+ /*out*/std::vector<V3_2::CaptureResult>* results = nullptr) = 0;
+
+ virtual ::android::hardware::camera::common::V1_0::Status processCaptureResult(
+ std::shared_ptr<HalRequest>&) = 0;
+
+ virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const = 0;
+};
+
} // namespace implementation
} // namespace V3_4
+
+namespace V3_6 {
+namespace implementation {
+
+// A CPU copy of a mapped V4L2Frame. Will map the input V4L2 frame.
+class AllocatedV4L2Frame : public V3_4::implementation::Frame {
+public:
+ AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn);
+ ~AllocatedV4L2Frame() override;
+ virtual int getData(uint8_t** outData, size_t* dataSize) override;
+private:
+ std::vector<uint8_t> mData;
+};
+
+} // namespace implementation
+} // namespace V3_6
} // namespace device
} // namespace camera
} // namespace hardware