| /* |
| * Copyright (C) 2014-2017 Intel Corporation |
| * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd |
| * |
| * 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 "ImgEncoderCore" |
| |
| #include "PlatformData.h" |
| #include "ImgEncoderCore.h" |
| #include "LogHelper.h" |
| #include <Utils.h> |
| #include "Camera3V4l2Format.h" |
| #include "ImageScalerCore.h" |
| #include "Exif.h" |
| |
| #include "ColorConverter.h" |
| #include <cros-camera/jpeg_compressor.h> |
| |
| NAMESPACE_DECLARATION { |
| ImgEncoderCore::ImgEncoderCore() : |
| mThumbOutBuf(nullptr), |
| mJpegDataBuf(nullptr), |
| mMainScaled(nullptr), |
| mThumbScaled(nullptr), |
| mJpegSetting(nullptr) |
| { |
| LOG1("@%s", __FUNCTION__); |
| |
| mInternalYU12Size = |
| RESOLUTION_14MP_WIDTH * RESOLUTION_14MP_HEIGHT * 3 / 2; |
| mInternalYU12.reset(new char[mInternalYU12Size]); |
| } |
| |
| ImgEncoderCore::~ImgEncoderCore() |
| { |
| LOG1("@%s", __FUNCTION__); |
| deInit(); |
| } |
| |
| status_t ImgEncoderCore::init(void) |
| { |
| LOG1("@%s", __FUNCTION__); |
| |
| mJpegSetting = new ExifMetaData::JpegSetting; |
| |
| return NO_ERROR; |
| } |
| |
| void ImgEncoderCore::deInit(void) |
| { |
| LOG2("@%s", __FUNCTION__); |
| |
| if (mJpegSetting) { |
| delete mJpegSetting; |
| mJpegSetting = nullptr; |
| } |
| |
| mThumbOutBuf.reset(); |
| |
| mJpegDataBuf.reset(); |
| } |
| |
| /** |
| * thumbBufferDownScale |
| * Downscaling the thumb buffer and allocate the scaled thumb input intermediate |
| * buffer if scaling is needed. |
| * |
| * \param pkg [IN] EncodePackage from the caller |
| */ |
| void ImgEncoderCore::thumbBufferDownScale(EncodePackage & pkg) |
| { |
| LOG2("%s", __FUNCTION__); |
| int thumbwidth = mJpegSetting->thumbWidth; |
| int thumbheight = mJpegSetting->thumbHeight; |
| |
| // Allocate thumbnail input buffer if downscaling is needed. |
| if (thumbwidth != 0) { |
| if (COMPARE_RESOLUTION(pkg.thumb, mThumbOutBuf) != 0) { |
| LOG2("%s: Downscaling for thumbnail: %dx%d -> %dx%d", __FUNCTION__, |
| pkg.thumb->width(), pkg.thumb->height(), |
| mThumbOutBuf->width(), mThumbOutBuf->height()); |
| if (mThumbScaled |
| && (COMPARE_RESOLUTION(mThumbScaled, mThumbOutBuf) != 0 |
| || pkg.thumb->v4l2Fmt() != mThumbScaled->v4l2Fmt())) { |
| mThumbScaled.reset(); |
| } |
| if (!mThumbScaled) { |
| BufferProps props; |
| props.width = thumbwidth; |
| props.height = thumbheight; |
| props.stride = thumbwidth; |
| props.format = pkg.thumb->v4l2Fmt(); |
| props.type = BMT_HEAP; |
| // Using thumbwidth as stride for heap buffer |
| mThumbScaled = std::make_shared<CommonBuffer>(props); |
| if (!mThumbScaled) { |
| LOGE("Error in creating shared_ptr mThumbScaled"); |
| return; |
| } |
| if (mThumbScaled->allocMemory()) { |
| LOGE("Error in allocating buffer with size:%d", mThumbScaled->size()); |
| return; |
| } |
| } |
| ImageScalerCore::downScaleImage(pkg.thumb, mThumbScaled); |
| pkg.thumb = mThumbScaled; |
| } |
| } |
| } |
| |
| /** |
| * mainBufferDownScale |
| * Downscaling the main buffer and allocate the scaled main intermediate |
| * buffer if scaling is needed. |
| * |
| * \param pkg [IN] EncodePackage from the caller |
| */ |
| void ImgEncoderCore::mainBufferDownScale(EncodePackage & pkg) |
| { |
| LOG2("%s", __FUNCTION__); |
| |
| // Allocate buffer for main picture downscale |
| // Compare the resolution, only do downscaling. |
| if (COMPARE_RESOLUTION(pkg.main, pkg.jpegOut) == 1) { |
| LOG2("%s: Downscaling for main picture: %dx%d -> %dx%d", __FUNCTION__, |
| pkg.main->width(), pkg.main->height(), |
| pkg.jpegOut->width(), pkg.jpegOut->height()); |
| if (mMainScaled |
| && (COMPARE_RESOLUTION(mMainScaled, pkg.jpegOut) != 0 |
| || pkg.main->v4l2Fmt() != mMainScaled->v4l2Fmt())) { |
| mMainScaled.reset(); |
| } |
| if (!mMainScaled) { |
| BufferProps props; |
| props.width = pkg.jpegOut->width(); |
| props.height = pkg.jpegOut->height(); |
| props.stride = pkg.jpegOut->width(); |
| props.format = pkg.main->v4l2Fmt(); |
| props.type = BMT_HEAP; |
| // Use pkg.jpegOut->width() as stride for the heap buffer |
| mMainScaled = std::make_shared<CommonBuffer>(props); |
| if (!mMainScaled) { |
| LOGE("Error in creating shared_ptr mMainScaled"); |
| return; |
| } |
| if (mMainScaled->allocMemory()) { |
| LOGE("Error in allocating buffer with size:%d", mMainScaled->size()); |
| return; |
| } |
| } |
| ImageScalerCore::downScaleImage(pkg.main, mMainScaled); |
| pkg.main = mMainScaled; |
| } |
| } |
| |
| /** |
| * allocateBufferAndDownScale |
| * This method downscales the main image and thumbnail if necesary. In case it |
| * needs to scale, it allocates the intermediate buffers where the scaled version |
| * is stored before it is given to the encoders. jpeg.thumbnailSize (0,0) means |
| * JPEG EXIF will not contain a thumbnail. We use thumbwidth to determine if |
| * Thumbnail size is > 0. In case thumbsize is not 0 we create the thumb output |
| * buffer with size provided in the settings. If no thumb input buffer is |
| * provided with the package the main buffer is assinged to the thumb input. |
| * If thumb input buffer is provided in the package then only down scaling is needed |
| * |
| * \param pkg [IN] EncodePackage from the caller |
| */ |
| status_t ImgEncoderCore::allocateBufferAndDownScale(EncodePackage & pkg) |
| { |
| LOG2("%s", __FUNCTION__); |
| |
| int thumbwidth = mJpegSetting->thumbWidth; |
| int thumbheight = mJpegSetting->thumbHeight; |
| |
| // Check if client provided the encoded data buffer |
| if (pkg.encodedData) |
| mJpegDataBuf = pkg.encodedData; |
| |
| // Allocate buffer for main image jpeg output if in first time or resolution changed |
| if (pkg.encodeAll && (!mJpegDataBuf || |
| COMPARE_RESOLUTION(mJpegDataBuf, pkg.jpegOut))) { |
| if (mJpegDataBuf) |
| mJpegDataBuf.reset(); |
| |
| if (!mJpegDataBuf) { |
| LOG1("Allocating jpeg data buffer with %dx%d, stride:%d", pkg.jpegOut->width(), |
| pkg.jpegOut->height(), pkg.jpegOut->stride()); |
| |
| BufferProps props; |
| props.width = pkg.jpegOut->width(); |
| props.height = pkg.jpegOut->height(); |
| props.stride = pkg.jpegOut->stride(); |
| props.format = pkg.jpegOut->v4l2Fmt(); |
| props.size = pkg.jpegOut->size(); |
| props.type = BMT_HEAP; |
| mJpegDataBuf = std::make_shared<CommonBuffer>(props); |
| if (!mJpegDataBuf) { |
| LOGE("Error in creating shared_ptr mJpegDataBuf"); |
| return NO_MEMORY; |
| } |
| if (mJpegDataBuf->allocMemory()) { |
| LOGE("Error in allocating buffer with size:%d", mJpegDataBuf->size()); |
| return NO_MEMORY; |
| } |
| } |
| } |
| |
| // Check if client provided the thumb out data buffer |
| if (pkg.thumbOut) |
| mThumbOutBuf = pkg.thumbOut; |
| |
| // Allocate buffer for thumbnail output |
| if (thumbwidth != 0) { |
| if (!pkg.thumb) |
| pkg.thumb = pkg.main; |
| |
| // Minimum buffer size required for high compression quality |
| int minThumbBufSize = thumbwidth * thumbheight * 2; |
| if (mThumbOutBuf && (mThumbOutBuf->width() != thumbwidth || |
| mThumbOutBuf->height() != thumbheight || |
| mThumbOutBuf->size() < minThumbBufSize)) |
| mThumbOutBuf.reset(); |
| |
| if (!mThumbOutBuf) { |
| LOG1("Allocating thumb data buffer with %dx%d", thumbwidth, thumbheight); |
| |
| if (!pkg.thumb) { |
| LOGE("No source for thumb"); |
| return UNKNOWN_ERROR; |
| } |
| |
| BufferProps props; |
| props.width = thumbwidth; |
| props.height = thumbheight; |
| props.stride = thumbwidth; |
| props.format = pkg.thumb->v4l2Fmt(); |
| props.type = BMT_HEAP; |
| props.size = minThumbBufSize; |
| // Use thumbwidth as stride for the heap buffer |
| mThumbOutBuf = std::make_shared<CommonBuffer>(props); |
| if (!mThumbOutBuf) { |
| LOGE("Error in creating shared_ptr mThumbOutBuf"); |
| return NO_MEMORY; |
| } |
| if (mThumbOutBuf->allocMemory()) { |
| LOGE("Error in allocating buffer with size:%d", mThumbOutBuf->size()); |
| return NO_MEMORY; |
| } |
| } |
| } |
| |
| thumbBufferDownScale(pkg); |
| if (pkg.encodeAll) |
| mainBufferDownScale(pkg); |
| |
| return NO_ERROR; |
| } |
| |
| |
| /** |
| * getJpegSettings |
| * |
| * Get the JPEG settings needed for image encoding from the Exif |
| * metadata and store to internal struct |
| * \param settings [IN] EncodePackage from the caller |
| * \param metaData [IN] exif metadata |
| */ |
| status_t ImgEncoderCore::getJpegSettings(EncodePackage & pkg, ExifMetaData& metaData) |
| { |
| LOG2("@%s:", __FUNCTION__); |
| UNUSED(pkg); |
| status_t status = NO_ERROR; |
| |
| *mJpegSetting = metaData.mJpegSetting; |
| LOG1("jpegQuality=%d,thumbQuality=%d,thumbW=%d,thumbH=%d,orientation=%d", |
| mJpegSetting->jpegQuality, |
| mJpegSetting->jpegThumbnailQuality, |
| mJpegSetting->thumbWidth, |
| mJpegSetting->thumbHeight, |
| mJpegSetting->orientation); |
| |
| return status; |
| } |
| |
| int ImgEncoderCore::doSwEncode(std::shared_ptr<CommonBuffer> srcBuf, |
| int quality, |
| std::shared_ptr<CommonBuffer> destBuf, |
| unsigned int destOffset) |
| { |
| LOG2("@%s", __FUNCTION__); |
| |
| cros::JpegCompressor jpegCompressor; |
| |
| int width = srcBuf->width(); |
| int height = srcBuf->height(); |
| int stride = srcBuf->stride(); |
| void* srcY = srcBuf->data(); |
| void* srcUV = static_cast<unsigned char*>(srcBuf->data()) + stride * height; |
| |
| if (width * height * 3 / 2 > mInternalYU12Size) { |
| mInternalYU12Size = width * height * 3 / 2; |
| mInternalYU12.reset(new char[mInternalYU12Size]); |
| } |
| void* tempBuf = mInternalYU12.get(); |
| |
| switch (srcBuf->v4l2Fmt()) { |
| case V4L2_PIX_FMT_YUYV: |
| YUY2ToP411(width, height, stride, srcY, tempBuf); |
| break; |
| case V4L2_PIX_FMT_NV12: |
| NV12ToP411Separate(width, height, stride, srcY, srcUV, tempBuf); |
| break; |
| case V4L2_PIX_FMT_NV21: |
| NV21ToP411Separate(width, height, stride, srcY, srcUV, tempBuf); |
| break; |
| default: |
| LOGE("%s Unsupported format %d", __FUNCTION__, srcBuf->v4l2Fmt()); |
| return 0; |
| } |
| |
| uint32_t outSize = 0; |
| nsecs_t startTime = systemTime(); |
| void* pDst = static_cast<unsigned char*>(destBuf->data()) + destOffset; |
| bool ret = jpegCompressor.CompressImage(tempBuf, |
| width, height, quality, |
| nullptr, 0, |
| destBuf->size(), pDst, |
| &outSize); |
| LOG1("%s: encoding ret:%d, %dx%d need %" PRId64 "ms, jpeg size %u, quality %d)", |
| __FUNCTION__, ret, destBuf->width(), destBuf->height(), |
| (systemTime() - startTime) / 1000000, outSize, quality); |
| CheckError(ret == false, 0, "@%s, jpegCompressor.CompressImage() fails", |
| __FUNCTION__); |
| |
| return outSize; |
| } |
| |
| /** |
| * encodeSync |
| * |
| * Do HW or SW encoding of the main buffer of the package |
| * Also do SW encoding of the thumb buffer |
| * |
| * \param srcBuf [IN] The input buffer to encode |
| * \param metaData [IN] exif metadata |
| * |
| */ |
| status_t ImgEncoderCore::encodeSync(EncodePackage & package, ExifMetaData& metaData) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1); |
| status_t status = NO_ERROR; |
| int mainSize = 0; |
| int thumbSize = 0; |
| |
| std::lock_guard<std::mutex> l(mEncodeLock); |
| |
| if (package.encodeAll) { |
| if (!package.main) { |
| LOGE("Main buffer for JPEG encoding is nullptr"); |
| return UNKNOWN_ERROR; |
| } |
| |
| if (!package.jpegOut) { |
| LOGE("JPEG output buffer is nullptr"); |
| return UNKNOWN_ERROR; |
| } |
| } |
| |
| getJpegSettings(package, metaData); |
| // Allocate buffers for thumbnail if not present and also |
| // downscale the buffer for main if scaling is needed |
| allocateBufferAndDownScale(package); |
| |
| // Encode thumbnail as JPEG in parallel with the HW encoding started earlier |
| if (package.thumb && mThumbOutBuf) { |
| do { |
| LOG2("Encoding thumbnail with quality %d", |
| mJpegSetting->jpegThumbnailQuality); |
| thumbSize = doSwEncode(package.thumb, |
| mJpegSetting->jpegThumbnailQuality, |
| mThumbOutBuf); |
| mJpegSetting->jpegThumbnailQuality -= 5; |
| } while (thumbSize > 0 && mJpegSetting->jpegThumbnailQuality > 0 && |
| thumbSize > THUMBNAIL_SIZE_LIMITATION); |
| |
| if (thumbSize > 0) { |
| package.thumbOut = mThumbOutBuf; |
| package.thumbSize = thumbSize; |
| } else { |
| // This is not critical, we can continue with main picture image |
| LOGW("Could not encode thumbnail stream!"); |
| } |
| } else { |
| // No thumb is not critical, we can continue with main picture image |
| LOG1("Exif created without thumbnail stream!"); |
| } |
| |
| if (package.encodeAll) { |
| status = NO_ERROR; |
| // Encode main picture with SW encoder |
| mainSize = doSwEncode(package.main, |
| mJpegSetting->jpegQuality, |
| mJpegDataBuf); |
| if (mainSize <= 0) { |
| LOGE("Error while SW encoding JPEG"); |
| status = INVALID_OPERATION; |
| } |
| |
| if (mainSize > 0) { |
| package.encodedData = mJpegDataBuf; |
| package.encodedDataSize = mainSize; |
| } |
| } |
| |
| return status; |
| } |
| |
| } NAMESPACE_DECLARATION_END |