blob: f04d6f531201b9e855bbb948a3c1fee570f0f530 [file] [log] [blame]
/*
* Copyright (C) 2014-2019 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "JpegEncode_Task"
#include "Camera3Request.h"
#include "CameraStream.h"
#include "ImageScalerCore.h"
#include "JpegEncodeTask.h"
#include "LogHelper.h"
#include "PlatformData.h"
#include "ProcUnitSettings.h"
namespace cros {
namespace intel {
JpegEncodeTask::JpegEncodeTask(int cameraId):
mImgEncoder(new ImgEncoder(cameraId)),
mJpegMaker(new JpegMaker(cameraId)),
mCameraId(cameraId)
{
HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1, LOG_TAG);
}
JpegEncodeTask::~JpegEncodeTask()
{
HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1, LOG_TAG);
if (!mExifCacheStorage.empty())
LOGE("EXIF cache should be empty at destruction - BUG?");
}
status_t JpegEncodeTask::handleMessageSettings(ProcUnitSettings &procSettings)
{
Camera3Request *req = procSettings.request;
std::shared_ptr<CaptureUnitSettings> capSettings = procSettings.captureSettings;
AAAControls *andr3ACtrl = &procSettings.android3Actrl;
// EXIF data to be mapped to request ID
ExifDataCache exifCache;
CLEAR(exifCache);
if (!req) {
LOGE("JPEG settings, nullptr request!");
return BAD_VALUE;
}
if (capSettings.get() == nullptr) {
LOGE("JPEG settings, nullptr CapU settings");
return BAD_VALUE;
}
int jpegBufCount = req->getBufferCountOfFormat(HAL_PIXEL_FORMAT_BLOB);
if (jpegBufCount <= 0) {
// No JPEG/blob buffers in request, no need to cache EXIF data
return NO_ERROR;
}
// TODO: Search metadata from correct partial!
// Currently only one, CONTROL_UNIT_PARTIAL_RESULT
android::CameraMetadata *partRes = req->getPartialResultBuffer(CONTROL_UNIT_PARTIAL_RESULT);
if (partRes == nullptr) {
LOGE("No partial result for EXIF in request.");
return BAD_VALUE;
}
// Read metadata result for any info useful for EXIF
readExifInfoFromAndroidResult(*partRes, exifCache);
exifCache.flashFired = capSettings->flashFired;
// AIQ focusing distance in mm, EXIF has meters.
// NOTE: For manual focus, at AIQ v2.0_008.006, there is a rouding
// error between AIQ ia_aiq_manual_focus_parameters::manual_focus_distance
// and result 'current_focus_distance'. Will be fixed by AIQ utility
// function to get around the rounding error. Once done, this comment is void
exifCache.focusDistance = capSettings->aiqResults.afResults.current_focus_distance;
// TODO:
// CAM_AE_MODE_SHUTTER_PRIORITY, // AIQ default?
// CAM_AE_MODE_APERTURE_PRIORITY // Only with SOC / other custom 3A control?
if (andr3ACtrl->controlMode == ANDROID_CONTROL_MODE_OFF ||
andr3ACtrl->ae.aeMode == ANDROID_CONTROL_AE_MODE_OFF) {
exifCache.aiqAeMode = CAM_AE_MODE_MANUAL;
} else {
// When android.control.aeMode:
// ON, ON_AUTO_FLASH, ON_ALWAYS_FLASH, ON_AUTO_FLASH_REDEYE
exifCache.aiqAeMode = CAM_AE_MODE_AUTO;
}
MakernoteData mknTmp = capSettings->makernote;
if (mknTmp.data != nullptr && mknTmp.size != 0) {
exifCache.makernote = mknTmp;
} else {
if (mknTmp.data != nullptr) {
// Size = 0 and non-null data is not valid for MKN.
LOGW("Makernote data not nullptr, size %d. Should not happen.", mknTmp.size);
}
// Reset, just in case.
exifCache.makernote.data = nullptr;
exifCache.makernote.size = 0;
}
// Add ID-mapped cache item to vector
mExifCacheStorage.insert(std::make_pair(req->getId(), exifCache));
return NO_ERROR;
}
/**
* Extracts the EXIF-usable pieces of information from Android result metadata
*
* \param[in] result The Android result metadata to extract information from
* \param[out] exifCache The EXIF 'cache' object containing the EXIF info
*/
void JpegEncodeTask::readExifInfoFromAndroidResult(const android::CameraMetadata &result,
ExifDataCache &exifCache) const
{
//# ANDROID_METADATA_Dynamic android.jpeg.orientation read_for_EXIF
camera_metadata_ro_entry_t entry = result.find(ANDROID_JPEG_ORIENTATION);
if (entry.count == 1) {
exifCache.jpegSettings.orientation = *entry.data.i32;
} else {
LOGD("No ANDROID_JPEG_ORIENTATION in results for EXIF");
}
//# ANDROID_METADATA_Dynamic android.jpeg.quality read_for_EXIF
entry = result.find(ANDROID_JPEG_QUALITY);
if (entry.count == 1) {
exifCache.jpegSettings.jpegQuality = *entry.data.u8;
} else {
LOGD("No ANDROID_JPEG_QUALITY in results for EXIF");
exifCache.jpegSettings.jpegQuality = JPEG_QUALITY_DEFAULT;
}
//# ANDROID_METADATA_Dynamic android.jpeg.thumbnailQuality read_for_EXIF
entry = result.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
if (entry.count == 1) {
exifCache.jpegSettings.jpegThumbnailQuality = *entry.data.u8;
} else {
LOGD("No ANDROID_JPEG_THUMBNAIL_QUALITY in results for EXIF");
exifCache.jpegSettings.jpegThumbnailQuality = THUMBNAIL_QUALITY_DEFAULT;
}
//# ANDROID_METADATA_Dynamic android.jpeg.thumbnailSize read_for_EXIF
entry = result.find(ANDROID_JPEG_THUMBNAIL_SIZE);
if (entry.count == 2) {
exifCache.jpegSettings.thumbWidth = entry.data.i32[0];
exifCache.jpegSettings.thumbHeight = entry.data.i32[1];
} else {
LOGD("No ANDROID_JPEG_THUMBNAIL_SIZE in results for EXIF");
}
//# ANDROID_METADATA_Dynamic android.sensor.exposureTime read_for_EXIF
entry = result.find(ANDROID_SENSOR_EXPOSURE_TIME);
if (entry.count == 1) {
// EXIF exposure rational value is in seconds.
// NOTE: the denominator in ExifMaker is usecs, Android data is nsecs.
exifCache.exposureTimeSecs = *entry.data.i64 / 1e3;
} else {
LOGD("No ANDROID_SENSOR_EXPOSURE_TIME in results for EXIF");
}
//# ANDROID_METADATA_Dynamic android.sensor.sensitivity read_for_EXIF
entry = result.find(ANDROID_SENSOR_SENSITIVITY);
if (entry.count == 1) {
exifCache.sensitivity = *entry.data.i32;
} else {
LOGD("No ANDROID_SENSOR_SENSITIVITY in results for EXIF");
}
//# ANDROID_METADATA_Dynamic android.control.awbMode read_for_EXIF
entry = result.find(ANDROID_CONTROL_AWB_MODE);
if (entry.count == 1) {
AwbMode camAwb = convertAwbMode(*entry.data.i32);
exifCache.lightSource = camAwb;
} else {
LOGD("No ANDROID_CONTROL_AWB_MODE in results for EXIF");
}
//# ANDROID_METADATA_Dynamic android.control.aeMode read_for_EXIF
entry = result.find(ANDROID_CONTROL_AE_MODE);
if (entry.count == 1) {
exifCache.aeMode = *entry.data.u8;
} else {
LOGD("No ANDROID_CONTROL_AE_MODE in results for EXIF");
}
//# ANDROID_METADATA_Dynamic android.flash.mode read_for_EXIF
entry = result.find(ANDROID_FLASH_MODE);
if (entry.count == 1) {
exifCache.flashMode = *entry.data.u8;
} else {
LOGD("No ANDROID_FLASH_MODE in results for EXIF");
}
}
status_t
JpegEncodeTask::handleMessageNewJpegInput(ITaskEventListener::PUTaskEvent &msg)
{
HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL2, LOG_TAG);
status_t status = NO_ERROR;
bool isEncoded;
LOG1("begin jpeg encoder");
ExifMetaData exifData;
ExifDataCache exifCache;
CLEAR(exifCache);
// NOTE: MKN fields now nullptr. Will bail later, if exifCache
// data not found from vec.
int reqId = msg.request->getId();
std::map<int, ExifDataCache>::iterator it = mExifCacheStorage.find(reqId);
if (it != mExifCacheStorage.end())
exifCache = it->second;
else
LOGE("EXIF data for req ID %d not cached - BUG.", reqId);
handleISPData(exifData);
// Set 3A-related EXIF info
handleExposureData(exifData, exifCache);
handleIa3ASetting(exifData, exifCache);
handleFlashData(exifData, exifCache);
// GPS handled in JpegMaker::processGpsSettings()
status = handleMakernote(exifData, exifCache);
if (status != OK) {
LOGE("Error setting Makernote EXIF data.");
return status;
}
handleJpegSettings(exifData, exifCache);
status = mJpegMaker->setupExifWithMetaData(msg.buffer->width(), msg.buffer->height(), &exifData,
*msg.request);
bool shouldContainThumbnail = exifCache.jpegSettings.thumbWidth > 0 &&
exifCache.jpegSettings.thumbHeight > 0;
ImgEncoder::EncodePackage thumbnailEncodePackage;
thumbnailEncodePackage.quality = exifCache.jpegSettings.jpegThumbnailQuality;
thumbnailEncodePackage.exifData = nullptr;
thumbnailEncodePackage.exifDataSize = 0;
if (shouldContainThumbnail) {
// Encode for Thumbnail.
if (mThumbnailInput == nullptr
|| mThumbnailInput->width() != exifCache.jpegSettings.thumbWidth
|| mThumbnailInput->height() != exifCache.jpegSettings.thumbHeight
|| mThumbnailInput->v4l2Fmt() != msg.jpegInputbuffer->v4l2Fmt()) {
mThumbnailInput = MemoryUtils::allocateHeapBuffer(
exifCache.jpegSettings.thumbWidth, exifCache.jpegSettings.thumbHeight,
exifCache.jpegSettings.thumbWidth, msg.jpegInputbuffer->v4l2Fmt(),
mCameraId);
}
ImageScalerCore::scaleFrame(msg.jpegInputbuffer, mThumbnailInput);
if (mThumbnailOutput == nullptr
|| mThumbnailOutput->width() != exifCache.jpegSettings.thumbWidth
|| mThumbnailOutput->height() != exifCache.jpegSettings.thumbHeight
|| mThumbnailOutput->v4l2Fmt() != msg.buffer->v4l2Fmt()) {
mThumbnailOutput = MemoryUtils::allocateHeapBuffer(
exifCache.jpegSettings.thumbWidth, exifCache.jpegSettings.thumbHeight,
exifCache.jpegSettings.thumbWidth, msg.buffer->v4l2Fmt(), mCameraId,
exifCache.jpegSettings.thumbWidth * exifCache.jpegSettings.thumbHeight * 3 / 2);
}
thumbnailEncodePackage.input = mThumbnailInput;
thumbnailEncodePackage.output = mThumbnailOutput;
do {
isEncoded = mImgEncoder->encodeSync(thumbnailEncodePackage);
thumbnailEncodePackage.quality -= 5;
} while (thumbnailEncodePackage.encodedDataSize > 0 &&
thumbnailEncodePackage.quality > 0 &&
thumbnailEncodePackage.encodedDataSize > THUMBNAIL_SIZE_LIMITATION);
if (!isEncoded || thumbnailEncodePackage.encodedDataSize == 0) {
// Since it is non-fatal, we can still proceed without thumbnail.
LOG(ERROR) << "Failed to generate thumbnail";
}
}
// Generate Exif with thumbnail.
if (mExifData == nullptr) {
mExifData = std::make_unique<unsigned char[]>(EXIF_SIZE_LIMITATION);
}
uint8_t* finalExifDataPtr = static_cast<uint8_t*>(mExifData.get());
uint32_t finalExifDataSize;
status = mJpegMaker->getExif(thumbnailEncodePackage, finalExifDataPtr, &finalExifDataSize);
CheckError(status != 0, status, "@%s, Failed to get Exif", __FUNCTION__);
// Encode for final output.
ImgEncoder::EncodePackage finalEncodePackage;
finalEncodePackage.input = msg.jpegInputbuffer;
finalEncodePackage.output = msg.buffer;
finalEncodePackage.quality = exifCache.jpegSettings.jpegQuality;
finalEncodePackage.exifData = finalExifDataPtr;
finalEncodePackage.exifDataSize = finalExifDataSize;
isEncoded = mImgEncoder->encodeSync(finalEncodePackage);
CheckError(!isEncoded, UNKNOWN_ERROR, "@%s, Failed to encode main image", __FUNCTION__);
// Write jpeg size to the end of the buffer.
finalEncodePackage.output->lock();
uint8_t* resultPtr =
static_cast<uint8_t*>(finalEncodePackage.output->data()) +
finalEncodePackage.output->size() - sizeof(struct camera3_jpeg_blob);
auto* blob = reinterpret_cast<struct camera3_jpeg_blob*>(resultPtr);
blob->jpeg_blob_id = CAMERA3_JPEG_BLOB_ID;
blob->jpeg_size = finalEncodePackage.encodedDataSize;
finalEncodePackage.output->unlock();
if (!mExifCacheStorage.empty()) {
// Done with the cached EXIF item for this request. Discard.
mExifCacheStorage.erase(reqId);
} else {
LOGE("Attempt to remove item from empty EXIF cache. - BUG");
}
return status;
}
/**
*
* Converts an Android AWB mode into internal
* Camera HAL Awb mode.
*
* \param androidAwb[in] Android specified AWB mode
* \return AWB mode in HAL format
*/
AwbMode JpegEncodeTask::convertAwbMode(const uint8_t androidAwb) const
{
HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL2, LOG_TAG);
AwbMode cameraAwb = CAM_AWB_MODE_NOT_SET;
cameraAwb =
(androidAwb == ANDROID_CONTROL_AWB_MODE_INCANDESCENT) ?
CAM_AWB_MODE_WARM_INCANDESCENT : \
(androidAwb == ANDROID_CONTROL_AWB_MODE_FLUORESCENT) ?
CAM_AWB_MODE_FLUORESCENT : \
(androidAwb == ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT) ?
CAM_AWB_MODE_WARM_FLUORESCENT : \
(androidAwb == ANDROID_CONTROL_AWB_MODE_DAYLIGHT) ?
CAM_AWB_MODE_DAYLIGHT : \
(androidAwb == ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT) ?
CAM_AWB_MODE_CLOUDY : \
(androidAwb == ANDROID_CONTROL_AWB_MODE_TWILIGHT) ?
CAM_AWB_MODE_SUNSET : \
(androidAwb == ANDROID_CONTROL_AWB_MODE_SHADE) ?
CAM_AWB_MODE_SHADOW : \
(androidAwb == ANDROID_CONTROL_AWB_MODE_OFF) ?
CAM_AWB_MODE_OFF : \
CAM_AWB_MODE_AUTO;
return cameraAwb;
}
/**
* handleISPData
*
* handleISPData adds the focal length and default f-number to ExifMetaData via
* ispData structure. The caller is responsible of freeing the allocated ispData
* structure (ExifMetaData destructor does it)
*/
status_t
JpegEncodeTask::handleISPData(ExifMetaData& exifData) const
{
// this gets freed when ExifMetaData is destructed
ExifMetaData::makernote_info *ispData = new ExifMetaData::makernote_info();
ispData->focal_length = EXIF_DEF_FOCAL_LEN_DEN * EXIF_DEF_FOCAL_LEN_NUM;
android::CameraMetadata staticMeta;
staticMeta = PlatformData::getStaticMetadata(mCameraId);
camera_metadata_entry focalLengths = staticMeta.find(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
if (focalLengths.count >= 1) {
uint32_t den = 100;
uint32_t num = (uint32_t)(focalLengths.data.f[0] * den);
ispData->focal_length = num;
}
camera_metadata_entry apertures = staticMeta.find(ANDROID_LENS_INFO_AVAILABLE_APERTURES);
if (apertures.count >= 1) {
uint32_t den = 10;
uint32_t num = (uint32_t)(apertures.data.f[0] * den);
ispData->f_number_curr = num << 16;
} else {
ispData->f_number_curr = EXIF_DEF_FNUMBER_NUM << 16;
}
ispData->f_number_curr += (EXIF_DEF_FNUMBER_DEN & 0xffff);
exifData.mIspMkNote = ispData;
return OK;
}
status_t JpegEncodeTask::handleExposureData(ExifMetaData& exifData,
const ExifDataCache &exifCache) const
{
SensorAeConfig aeConfig;
aeConfig.evBias = 0;
aeConfig.expTime = exifCache.exposureTimeSecs;
aeConfig.aperture_num = 0;
aeConfig.aperture_denum = 0;
aeConfig.fn_num = 0;
aeConfig.fn_denum = 0;
aeConfig.aecApexTv = 0;
aeConfig.aecApexSv = 0;
aeConfig.aecApexAv = 0;
aeConfig.digitalGain = 0;
aeConfig.totalGain = 0;
exifData.saveAeConfig(aeConfig);
return OK;
}
status_t JpegEncodeTask::handleIa3ASetting(ExifMetaData& exifData,
const ExifDataCache &exifCache) const
{
exifData.mIa3ASetting.isoSpeed = exifCache.sensitivity;
exifData.mIa3ASetting.focusDistance = exifCache.focusDistance;
exifData.mIa3ASetting.aeMode = exifCache.aiqAeMode;
exifData.mIa3ASetting.lightSource = exifCache.lightSource;
return OK;
}
status_t JpegEncodeTask::handleFlashData(ExifMetaData& exifData,
const ExifDataCache &exifCache) const
{
exifData.mFlashFired = exifCache.flashFired;
exifData.mV3AeMode = exifCache.aeMode;
exifData.mFlashMode = exifCache.flashMode;
return OK;
}
status_t JpegEncodeTask::handleMakernote(ExifMetaData& exifData,
ExifDataCache &exifCache) const
{
status_t status = OK;
if (exifCache.makernote.data != nullptr && exifCache.makernote.size != 0) {
// NOTE: saveIa3AMkNote() owns and takes a memcpy() of the MKN
exifData.saveIa3AMkNote(exifCache.makernote);
delete[] static_cast<char*>(exifCache.makernote.data);
exifCache.makernote.data = nullptr;
exifCache.makernote.size = 0;
} else if (exifCache.makernote.data == nullptr && exifCache.makernote.size == 0) {
// do nothing
} else {
LOGE("Error writing MKN to ExifMetaData, ptr: %p size: %d.",
exifCache.makernote.data,
exifCache.makernote.size);
status = UNKNOWN_ERROR;
}
return status;
}
status_t JpegEncodeTask::handleJpegSettings(ExifMetaData& exifData,
JpegEncodeTask::ExifDataCache &exifCache) const
{
exifData.mJpegSetting = exifCache.jpegSettings;
return NO_ERROR;
}
} // namespace intel
} // namespace cros