diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp index 2eec0f7f9c..9525ad2635 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp @@ -339,6 +339,21 @@ status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { } else { depthPhoto.mIsLensDistortionValid = 0; } + entry = inputFrame.result.find(ANDROID_JPEG_ORIENTATION); + if (entry.count > 0) { + // The camera jpeg orientation values must be within [0, 90, 180, 270]. + switch (entry.data.i32[0]) { + case 0: + case 90: + case 180: + case 270: + depthPhoto.mOrientation = static_cast (entry.data.i32[0]); + break; + default: + ALOGE("%s: Unexpected jpeg orientation value: %d, default to 0 degrees", + __FUNCTION__, entry.data.i32[0]); + } + } size_t actualJpegSize = 0; res = mDepthPhotoProcess(depthPhoto, finalJpegBufferSize, dstBuffer, &actualJpegSize); diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp index 3af422064f..6d96163b42 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp @@ -224,8 +224,106 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out return ret; } +inline void unpackDepth16(uint16_t value, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + // Android densely packed depth map. The units for the range are in + // millimeters and need to be scaled to meters. + // The confidence value is encoded in the 3 most significant bits. + // The confidence data needs to be additionally normalized with + // values 1.0f, 0.0f representing maximum and minimum confidence + // respectively. + auto point = static_cast(value & 0x1FFF) / 1000.f; + points->push_back(point); + + auto conf = (value >> 13) & 0x7; + float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; + confidence->push_back(normConfidence); + + if (*near > point) { + *near = point; + } + if (*far < point) { + *far = point; + } +} + +// Trivial case, read forward from top,left corner. +void rotate0AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (size_t i = 0; i < inputFrame.mDepthMapHeight; i++) { + for (size_t j = 0; j < inputFrame.mDepthMapWidth; j++) { + unpackDepth16(inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j], points, + confidence, near, far); + } + } +} + +// 90 degrees CW rotation can be applied by starting to read from bottom, left corner +// transposing rows and columns. +void rotate90AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (size_t i = 0; i < inputFrame.mDepthMapWidth; i++) { + for (ssize_t j = inputFrame.mDepthMapHeight-1; j >= 0; j--) { + unpackDepth16(inputFrame.mDepthMapBuffer[j*inputFrame.mDepthMapStride + i], points, + confidence, near, far); + } + } +} + +// 180 CW degrees rotation can be applied by starting to read backwards from bottom, right corner. +void rotate180AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (ssize_t i = inputFrame.mDepthMapHeight-1; i >= 0; i--) { + for (ssize_t j = inputFrame.mDepthMapWidth-1; j >= 0; j--) { + unpackDepth16(inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j], points, + confidence, near, far); + } + } +} + +// 270 degrees CW rotation can be applied by starting to read from top, right corner +// transposing rows and columns. +void rotate270AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (ssize_t i = inputFrame.mDepthMapWidth-1; i >= 0; i--) { + for (size_t j = 0; j < inputFrame.mDepthMapHeight; j++) { + unpackDepth16(inputFrame.mDepthMapBuffer[j*inputFrame.mDepthMapStride + i], points, + confidence, near, far); + } + } +} + +bool rotateAndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + switch (inputFrame.mOrientation) { + case DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES: + rotate0AndUnpack(inputFrame, points, confidence, near, far); + return false; + case DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES: + rotate90AndUnpack(inputFrame, points, confidence, near, far); + return true; + case DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES: + rotate180AndUnpack(inputFrame, points, confidence, near, far); + return false; + case DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES: + rotate270AndUnpack(inputFrame, points, confidence, near, far); + return true; + default: + ALOGE("%s: Unsupported depth photo rotation: %d, default to 0", __FUNCTION__, + inputFrame.mOrientation); + rotate0AndUnpack(inputFrame, points, confidence, near, far); + } + + return false; +} + std::unique_ptr processDepthMapFrame(DepthPhotoInputFrame inputFrame, - ExifOrientation exifOrientation, std::vector> *items /*out*/) { + ExifOrientation exifOrientation, std::vector> *items /*out*/, + bool *switchDimensions /*out*/) { + if ((items == nullptr) || (switchDimensions == nullptr)) { + return nullptr; + } + std::vector points, confidence; size_t pointCount = inputFrame.mDepthMapWidth * inputFrame.mDepthMapHeight; @@ -233,29 +331,21 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra confidence.reserve(pointCount); float near = UINT16_MAX; float far = .0f; - for (size_t i = 0; i < inputFrame.mDepthMapHeight; i++) { - for (size_t j = 0; j < inputFrame.mDepthMapWidth; j++) { - // Android densely packed depth map. The units for the range are in - // millimeters and need to be scaled to meters. - // The confidence value is encoded in the 3 most significant bits. - // The confidence data needs to be additionally normalized with - // values 1.0f, 0.0f representing maximum and minimum confidence - // respectively. - auto value = inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j]; - auto point = static_cast(value & 0x1FFF) / 1000.f; - points.push_back(point); - - auto conf = (value >> 13) & 0x7; - float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; - confidence.push_back(normConfidence); - - if (near > point) { - near = point; - } - if (far < point) { - far = point; - } - } + *switchDimensions = false; + // Physical rotation of depth and confidence maps may be needed in case + // the EXIF orientation is set to 0 degrees and the depth photo orientation + // (source color image) has some different value. + if (exifOrientation == ExifOrientation::ORIENTATION_0_DEGREES) { + *switchDimensions = rotateAndUnpack(inputFrame, &points, &confidence, &near, &far); + } else { + rotate0AndUnpack(inputFrame, &points, &confidence, &near, &far); + } + + size_t width = inputFrame.mDepthMapWidth; + size_t height = inputFrame.mDepthMapHeight; + if (*switchDimensions) { + width = inputFrame.mDepthMapHeight; + height = inputFrame.mDepthMapWidth; } if (near == far) { @@ -281,8 +371,8 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra depthParams.depth_image_data.resize(inputFrame.mMaxJpegSize); depthParams.confidence_data.resize(inputFrame.mMaxJpegSize); size_t actualJpegSize; - auto ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, - pointsQuantized.data(), depthParams.depth_image_data.data(), inputFrame.mMaxJpegSize, + auto ret = encodeGrayscaleJpeg(width, height, pointsQuantized.data(), + depthParams.depth_image_data.data(), inputFrame.mMaxJpegSize, inputFrame.mJpegQuality, exifOrientation, actualJpegSize); if (ret != NO_ERROR) { ALOGE("%s: Depth map compression failed!", __FUNCTION__); @@ -290,8 +380,8 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra } depthParams.depth_image_data.resize(actualJpegSize); - ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, - confidenceQuantized.data(), depthParams.confidence_data.data(), inputFrame.mMaxJpegSize, + ret = encodeGrayscaleJpeg(width, height, confidenceQuantized.data(), + depthParams.confidence_data.data(), inputFrame.mMaxJpegSize, inputFrame.mJpegQuality, exifOrientation, actualJpegSize); if (ret != NO_ERROR) { ALOGE("%s: Confidence map compression failed!", __FUNCTION__); @@ -321,7 +411,9 @@ extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t de ExifOrientation exifOrientation = getExifOrientation( reinterpret_cast (inputFrame.mMainJpegBuffer), inputFrame.mMainJpegSize); - cameraParams->depth_map = processDepthMapFrame(inputFrame, exifOrientation, &items); + bool switchDimensions; + cameraParams->depth_map = processDepthMapFrame(inputFrame, exifOrientation, &items, + &switchDimensions); if (cameraParams->depth_map == nullptr) { ALOGE("%s: Depth map processing failed!", __FUNCTION__); return BAD_VALUE; @@ -333,7 +425,13 @@ extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t de // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] const dynamic_depth::Point focalLength(inputFrame.mInstrinsicCalibration[0], inputFrame.mInstrinsicCalibration[1]); - const Dimension imageSize(inputFrame.mMainJpegWidth, inputFrame.mMainJpegHeight); + size_t width = inputFrame.mMainJpegWidth; + size_t height = inputFrame.mMainJpegHeight; + if (switchDimensions) { + width = inputFrame.mMainJpegHeight; + height = inputFrame.mMainJpegWidth; + } + const Dimension imageSize(width, height); ImagingModelParams imagingParams(focalLength, imageSize); imagingParams.principal_point.x = inputFrame.mInstrinsicCalibration[2]; imagingParams.principal_point.y = inputFrame.mInstrinsicCalibration[3]; diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.h b/services/camera/libcameraservice/common/DepthPhotoProcessor.h index 19889a1f3d..6a2fbffb6b 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.h +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.h @@ -23,19 +23,27 @@ namespace android { namespace camera3 { +enum DepthPhotoOrientation { + DEPTH_ORIENTATION_0_DEGREES = 0, + DEPTH_ORIENTATION_90_DEGREES = 90, + DEPTH_ORIENTATION_180_DEGREES = 180, + DEPTH_ORIENTATION_270_DEGREES = 270, +}; + struct DepthPhotoInputFrame { - const char* mMainJpegBuffer; - size_t mMainJpegSize; - size_t mMainJpegWidth, mMainJpegHeight; - uint16_t* mDepthMapBuffer; - size_t mDepthMapWidth, mDepthMapHeight, mDepthMapStride; - size_t mMaxJpegSize; - uint8_t mJpegQuality; - uint8_t mIsLogical; - float mInstrinsicCalibration[5]; - uint8_t mIsInstrinsicCalibrationValid; - float mLensDistortion[5]; - uint8_t mIsLensDistortionValid; + const char* mMainJpegBuffer; + size_t mMainJpegSize; + size_t mMainJpegWidth, mMainJpegHeight; + uint16_t* mDepthMapBuffer; + size_t mDepthMapWidth, mDepthMapHeight, mDepthMapStride; + size_t mMaxJpegSize; + uint8_t mJpegQuality; + uint8_t mIsLogical; + float mInstrinsicCalibration[5]; + uint8_t mIsInstrinsicCalibrationValid; + float mLensDistortion[5]; + uint8_t mIsLensDistortionValid; + DepthPhotoOrientation mOrientation; DepthPhotoInputFrame() : mMainJpegBuffer(nullptr), @@ -52,7 +60,8 @@ struct DepthPhotoInputFrame { mInstrinsicCalibration{0.f}, mIsInstrinsicCalibrationValid(0), mLensDistortion{0.f}, - mIsLensDistortionValid(0) {} + mIsLensDistortionValid(0), + mOrientation(DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES) {} }; static const char *kDepthPhotoLibrary = "libdepthphoto.so"; diff --git a/services/camera/libcameraservice/tests/DepthProcessorTest.cpp b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp index 9898122e9d..2162514d14 100644 --- a/services/camera/libcameraservice/tests/DepthProcessorTest.cpp +++ b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp @@ -50,7 +50,7 @@ void linkToDepthPhotoLibrary(void **libHandle /*out*/, } void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, bool includeExif, - std::vector *colorJpegBuffer /*out*/) { + bool switchDimensions, std::vector *colorJpegBuffer /*out*/) { ASSERT_NE(colorJpegBuffer, nullptr); std::array colorSourceBuffer; @@ -59,15 +59,23 @@ void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, for (size_t i = 0; i < colorSourceBuffer.size(); i++) { colorSourceBuffer[i] = uniDist(gen); } + + size_t width = kTestBufferWidth; + size_t height = kTestBufferHeight; + if (switchDimensions) { + width = kTestBufferHeight; + height = kTestBufferWidth; + } + NV12Compressor jpegCompressor; if (includeExif) { ASSERT_TRUE(jpegCompressor.compressWithExifOrientation( - reinterpret_cast (colorSourceBuffer.data()), - kTestBufferWidth, kTestBufferHeight, jpegQuality, orientationValue)); + reinterpret_cast (colorSourceBuffer.data()), width, height, + jpegQuality, orientationValue)); } else { ASSERT_TRUE(jpegCompressor.compress( - reinterpret_cast (colorSourceBuffer.data()), - kTestBufferWidth, kTestBufferHeight, jpegQuality)); + reinterpret_cast (colorSourceBuffer.data()), width, height, + jpegQuality)); } *colorJpegBuffer = std::move(jpegCompressor.getCompressedData()); @@ -109,7 +117,7 @@ TEST(DepthProcessorTest, BadInput) { std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, - /*includeExif*/ false, &colorJpegBuffer); + /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer); std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); @@ -153,7 +161,7 @@ TEST(DepthProcessorTest, BasicDepthPhotoValidation) { std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, - /*includeExif*/ false, &colorJpegBuffer); + /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer); std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); @@ -209,12 +217,11 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { for (auto exifOrientation : exifOrientations) { std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true, - &colorJpegBuffer); + /*switchDimensions*/ false, &colorJpegBuffer); if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) { auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; - ASSERT_EQ(NV12Compressor::getExifOrientation( - reinterpret_cast (colorJpegBuffer.data()), - colorJpegBuffer.size(), &jpegExifOrientation), OK); + ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(), + colorJpegBuffer.size(), &jpegExifOrientation), OK); ASSERT_EQ(exifOrientation, jpegExifOrientation); } @@ -252,8 +259,7 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { //Depth and confidence images must have the same EXIF orientation as the source auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; - ASSERT_EQ(NV12Compressor::getExifOrientation( - reinterpret_cast (depthPhotoBuffer.data() + mainJpegSize), + ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize, depthMapSize, &depthJpegExifOrientation), OK); if (exifOrientation == ORIENTATION_UNDEFINED) { // In case of undefined or missing EXIF orientation, always expect 0 degrees in the @@ -265,8 +271,8 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation( - reinterpret_cast (depthPhotoBuffer.data() + mainJpegSize + - depthMapSize), confidenceMapSize, &confidenceJpegExifOrientation), OK); + depthPhotoBuffer.data() + mainJpegSize + depthMapSize, + confidenceMapSize, &confidenceJpegExifOrientation), OK); if (exifOrientation == ORIENTATION_UNDEFINED) { // In case of undefined or missing EXIF orientation, always expect 0 degrees in the // confidence map. @@ -278,3 +284,99 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { dlclose(libHandle); } + +TEST(DepthProcessorTest, TestDephtPhotoPhysicalRotation) { + void *libHandle; + int jpegQuality = 95; + + process_depth_photo_frame processFunc; + linkToDepthPhotoLibrary(&libHandle, &processFunc); + if (libHandle == nullptr) { + // Depth library no present, nothing more to test. + return; + } + + // In case of physical rotation, the EXIF orientation must always be 0. + auto exifOrientation = ExifOrientation::ORIENTATION_0_DEGREES; + DepthPhotoOrientation depthOrientations[] = { + DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES, + DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES, + DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES, + DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES }; + for (auto depthOrientation : depthOrientations) { + std::vector colorJpegBuffer; + bool switchDimensions = false; + size_t expectedWidth = kTestBufferWidth; + size_t expectedHeight = kTestBufferHeight; + if ((depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES) || + (depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES)) { + switchDimensions = true; + expectedWidth = kTestBufferHeight; + expectedHeight = kTestBufferWidth; + } + generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true, + switchDimensions, &colorJpegBuffer); + auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(), colorJpegBuffer.size(), + &jpegExifOrientation), OK); + ASSERT_EQ(exifOrientation, jpegExifOrientation); + + std::array depth16Buffer; + generateDepth16Buffer(&depth16Buffer); + + DepthPhotoInputFrame inputFrame; + inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); + inputFrame.mMainJpegSize = colorJpegBuffer.size(); + // Worst case both depth and confidence maps have the same size as the main color image. + inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; + inputFrame.mMainJpegWidth = kTestBufferWidth; + inputFrame.mMainJpegHeight = kTestBufferHeight; + inputFrame.mJpegQuality = jpegQuality; + inputFrame.mDepthMapBuffer = depth16Buffer.data(); + inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; + inputFrame.mDepthMapHeight = kTestBufferHeight; + inputFrame.mOrientation = depthOrientation; + + std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); + size_t actualDepthPhotoSize = 0; + ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), + &actualDepthPhotoSize), 0); + ASSERT_TRUE((actualDepthPhotoSize > 0) && + (depthPhotoBuffer.size() >= actualDepthPhotoSize)); + + size_t mainJpegSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize, + &mainJpegSize), OK); + ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize)); + size_t depthMapSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize, + actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK); + ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize))); + size_t confidenceMapSize = actualDepthPhotoSize - (mainJpegSize + depthMapSize); + + //Depth and confidence images must have the same EXIF orientation as the source + auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize, + depthMapSize, &depthJpegExifOrientation), OK); + ASSERT_EQ(depthJpegExifOrientation, exifOrientation); + size_t depthMapWidth, depthMapHeight; + ASSERT_EQ(NV12Compressor::getJpegImageDimensions(depthPhotoBuffer.data() + mainJpegSize, + depthMapSize, &depthMapWidth, &depthMapHeight), OK); + ASSERT_EQ(depthMapWidth, expectedWidth); + ASSERT_EQ(depthMapHeight, expectedHeight); + + auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation( + depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize, + &confidenceJpegExifOrientation), OK); + ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation); + size_t confidenceMapWidth, confidenceMapHeight; + ASSERT_EQ(NV12Compressor::getJpegImageDimensions( + depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize, + &confidenceMapWidth, &confidenceMapHeight), OK); + ASSERT_EQ(confidenceMapWidth, expectedWidth); + ASSERT_EQ(confidenceMapHeight, expectedHeight); + } + + dlclose(libHandle); +} diff --git a/services/camera/libcameraservice/tests/NV12Compressor.cpp b/services/camera/libcameraservice/tests/NV12Compressor.cpp index b9f27faa57..0a41a1ff82 100644 --- a/services/camera/libcameraservice/tests/NV12Compressor.cpp +++ b/services/camera/libcameraservice/tests/NV12Compressor.cpp @@ -315,7 +315,37 @@ status_t NV12Compressor::findJpegSize(uint8_t *jpegBuffer, size_t maxSize, size_ return OK; } -status_t NV12Compressor::getExifOrientation(const unsigned char *jpegBuffer, size_t jpegBufferSize, +status_t NV12Compressor::getJpegImageDimensions(uint8_t *jpegBuffer, + size_t jpegBufferSize, size_t *width /*out*/, size_t *height /*out*/) { + if ((jpegBuffer == nullptr) || (width == nullptr) || (height == nullptr) || + (jpegBufferSize == 0u)) { + return BAD_VALUE; + } + + // Scan JPEG buffer until Start of Frame + bool foundSOF = false; + size_t currentPos; + for (currentPos = 0; currentPos <= jpegBufferSize - kMarkerLength; currentPos++) { + if (checkStartOfFrame(jpegBuffer + currentPos)) { + foundSOF = true; + currentPos += kMarkerLength; + break; + } + } + + if (!foundSOF) { + ALOGE("%s: Start of Frame not found", __func__); + return BAD_VALUE; + } + + sof_t *startOfFrame = reinterpret_cast (jpegBuffer + currentPos); + *width = ntohs(startOfFrame->width); + *height = ntohs(startOfFrame->height); + + return OK; +} + +status_t NV12Compressor::getExifOrientation(uint8_t *jpegBuffer, size_t jpegBufferSize, ExifOrientation *exifValue /*out*/) { if ((jpegBuffer == nullptr) || (exifValue == nullptr) || (jpegBufferSize == 0u)) { return BAD_VALUE; diff --git a/services/camera/libcameraservice/tests/NV12Compressor.h b/services/camera/libcameraservice/tests/NV12Compressor.h index 92804c10af..ee22d5e282 100644 --- a/services/camera/libcameraservice/tests/NV12Compressor.h +++ b/services/camera/libcameraservice/tests/NV12Compressor.h @@ -48,13 +48,20 @@ public: */ const std::vector& getCompressedData() const; + // Utility methods static android::status_t findJpegSize(uint8_t *jpegBuffer, size_t maxSize, size_t *size /*out*/); - - static android::status_t getExifOrientation(const unsigned char *jpegBuffer, + static android::status_t getExifOrientation(uint8_t *jpegBuffer, size_t jpegBufferSize, android::camera3::ExifOrientation *exifValue /*out*/); + /* Get Jpeg image dimensions from the first Start Of Frame. Please note that due to the + * way the jpeg buffer is scanned if the image contains a thumbnail, then the size returned + * will be of the thumbnail and not the main image. + */ + static android::status_t getJpegImageDimensions(uint8_t *jpegBuffer, size_t jpegBufferSize, + size_t *width /*out*/, size_t *height /*out*/); + private: struct DestinationManager : jpeg_destination_mgr { @@ -79,14 +86,26 @@ private: static const uint8_t kMarker = 0xFF; // First byte of marker static const uint8_t kStartOfImage = 0xD8; // Start of Image static const uint8_t kEndOfImage = 0xD9; // End of Image + static const uint8_t kStartOfFrame = 0xC0; // Start of Frame struct __attribute__((packed)) segment_t { uint8_t marker[kMarkerLength]; uint16_t length; }; + struct __attribute__((packed)) sof_t { + uint16_t length; + uint8_t precision; + uint16_t height; + uint16_t width; + }; + + // check for start of image marker + static bool checkStartOfFrame(uint8_t* buf) { + return buf[0] == kMarker && buf[1] == kStartOfFrame; + } - // check for Start of Image marker + // check for start of image marker static bool checkJpegStart(uint8_t* buf) { return buf[0] == kMarker && buf[1] == kStartOfImage; }