Snap for 4539010 from 28e0f76353e06b24b7dae54e636dbb5ddc64d1a5 to pi-release
Change-Id: I8b28f5b2d749d8983d564ed9e2d8e18aea36d49f
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
index 05ddf5a..6ecedac 100644
--- a/modules/camera/3_4/Android.mk
+++ b/modules/camera/3_4/Android.mk
@@ -22,21 +22,31 @@
v4l2_shared_libs := \
libbase \
+ libchrome \
libcamera_client \
libcamera_metadata \
libcutils \
+ libexif \
libhardware \
liblog \
libsync \
libutils \
-v4l2_static_libs :=
+v4l2_static_libs := \
+ libyuv_static \
+ libjpeg_static_ndk \
-v4l2_cflags := -fno-short-enums -Wall -Wno-error -Wextra -fvisibility=hidden
+v4l2_cflags := -fno-short-enums -Wall -Wno-error -Wextra -fvisibility=hidden -DHAVE_JPEG
-v4l2_c_includes := $(call include-path-for, camera)
+v4l2_c_includes := $(call include-path-for, camera) \
+ external/libyuv/files/include \
v4l2_src_files := \
+ arc/cached_frame.cpp \
+ arc/exif_utils.cpp \
+ arc/frame_buffer.cpp \
+ arc/image_processor.cpp \
+ arc/jpeg_compressor.cpp \
camera.cpp \
capture_request.cpp \
format_metadata_factory.cpp \
@@ -49,7 +59,6 @@
stream_format.cpp \
v4l2_camera.cpp \
v4l2_camera_hal.cpp \
- v4l2_gralloc.cpp \
v4l2_metadata_factory.cpp \
v4l2_wrapper.cpp \
diff --git a/modules/camera/3_4/README.md b/modules/camera/3_4/README.md
index 30f36d7..168ba97 100644
--- a/modules/camera/3_4/README.md
+++ b/modules/camera/3_4/README.md
@@ -28,7 +28,7 @@
Devices and cameras wishing to use this HAL must meet
the following requirements:
-* The camera must support RGB24, YUV420, and JPEG formats.
+* The camera must support BGR32, YUV420, and JPEG formats.
* The gralloc and other graphics modules used by the device must use
`HAL_PIXEL_FORMAT_RGBA_8888` as the `HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED`
diff --git a/modules/camera/3_4/arc/cached_frame.cpp b/modules/camera/3_4/arc/cached_frame.cpp
new file mode 100644
index 0000000..0489be8
--- /dev/null
+++ b/modules/camera/3_4/arc/cached_frame.cpp
@@ -0,0 +1,218 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "arc/cached_frame.h"
+
+#include <errno.h>
+#include <libyuv.h>
+
+#include "arc/common.h"
+#include "arc/common_types.h"
+
+namespace arc {
+
+using android::CameraMetadata;
+
+CachedFrame::CachedFrame()
+ : source_frame_(nullptr),
+ cropped_buffer_capacity_(0),
+ yu12_frame_(new AllocatedFrameBuffer(0)) {}
+
+CachedFrame::~CachedFrame() { UnsetSource(); }
+
+int CachedFrame::SetSource(const FrameBuffer* frame, int rotate_degree) {
+ source_frame_ = frame;
+ int res = ConvertToYU12();
+ if (res != 0) {
+ return res;
+ }
+
+ if (rotate_degree > 0) {
+ res = CropRotateScale(rotate_degree);
+ }
+ return res;
+}
+
+void CachedFrame::UnsetSource() { source_frame_ = nullptr; }
+
+uint8_t* CachedFrame::GetSourceBuffer() const {
+ return source_frame_->GetData();
+}
+
+size_t CachedFrame::GetSourceDataSize() const {
+ return source_frame_->GetDataSize();
+}
+
+uint32_t CachedFrame::GetSourceFourCC() const {
+ return source_frame_->GetFourcc();
+}
+
+uint8_t* CachedFrame::GetCachedBuffer() const { return yu12_frame_->GetData(); }
+
+uint32_t CachedFrame::GetCachedFourCC() const {
+ return yu12_frame_->GetFourcc();
+}
+
+uint32_t CachedFrame::GetWidth() const { return yu12_frame_->GetWidth(); }
+
+uint32_t CachedFrame::GetHeight() const { return yu12_frame_->GetHeight(); }
+
+size_t CachedFrame::GetConvertedSize(int fourcc) const {
+ return ImageProcessor::GetConvertedSize(fourcc, yu12_frame_->GetWidth(),
+ yu12_frame_->GetHeight());
+}
+
+int CachedFrame::Convert(const CameraMetadata& metadata, FrameBuffer* out_frame,
+ bool video_hack) {
+ if (video_hack && out_frame->GetFourcc() == V4L2_PIX_FMT_YVU420) {
+ out_frame->SetFourcc(V4L2_PIX_FMT_YUV420);
+ }
+
+ FrameBuffer* source_frame = yu12_frame_.get();
+ if (GetWidth() != out_frame->GetWidth() ||
+ GetHeight() != out_frame->GetHeight()) {
+ size_t cache_size = ImageProcessor::GetConvertedSize(
+ yu12_frame_->GetFourcc(), out_frame->GetWidth(),
+ out_frame->GetHeight());
+ if (cache_size == 0) {
+ return -EINVAL;
+ } else if (cache_size > scaled_frame_->GetBufferSize()) {
+ scaled_frame_.reset(new AllocatedFrameBuffer(cache_size));
+ }
+ scaled_frame_->SetWidth(out_frame->GetWidth());
+ scaled_frame_->SetHeight(out_frame->GetHeight());
+ ImageProcessor::Scale(*yu12_frame_.get(), scaled_frame_.get());
+
+ source_frame = scaled_frame_.get();
+ }
+ return ImageProcessor::ConvertFormat(metadata, *source_frame, out_frame);
+}
+
+int CachedFrame::ConvertToYU12() {
+ size_t cache_size = ImageProcessor::GetConvertedSize(
+ V4L2_PIX_FMT_YUV420, source_frame_->GetWidth(),
+ source_frame_->GetHeight());
+ if (cache_size == 0) {
+ return -EINVAL;
+ }
+ yu12_frame_->SetDataSize(cache_size);
+ yu12_frame_->SetFourcc(V4L2_PIX_FMT_YUV420);
+ yu12_frame_->SetWidth(source_frame_->GetWidth());
+ yu12_frame_->SetHeight(source_frame_->GetHeight());
+
+ int res = ImageProcessor::ConvertFormat(CameraMetadata(), *source_frame_,
+ yu12_frame_.get());
+ if (res) {
+ LOGF(ERROR) << "Convert from " << FormatToString(source_frame_->GetFourcc())
+ << " to YU12 fails.";
+ return res;
+ }
+ return 0;
+}
+
+int CachedFrame::CropRotateScale(int rotate_degree) {
+ // TODO(henryhsu): Move libyuv part to ImageProcessor.
+ if (yu12_frame_->GetHeight() % 2 != 0 || yu12_frame_->GetWidth() % 2 != 0) {
+ LOGF(ERROR) << "yu12_frame_ has odd dimension: " << yu12_frame_->GetWidth()
+ << "x" << yu12_frame_->GetHeight();
+ return -EINVAL;
+ }
+
+ if (yu12_frame_->GetHeight() > yu12_frame_->GetWidth()) {
+ LOGF(ERROR) << "yu12_frame_ is tall frame already: "
+ << yu12_frame_->GetWidth() << "x" << yu12_frame_->GetHeight();
+ return -EINVAL;
+ }
+
+ // Step 1: Crop and rotate
+ //
+ // Original frame Cropped frame Rotated frame
+ // -------------------- --------
+ // | | | | | | ---------------
+ // | | | | | | | |
+ // | | | | =======>> | | =======>> | |
+ // | | | | | | ---------------
+ // | | | | | |
+ // -------------------- --------
+ //
+ int cropped_width = yu12_frame_->GetHeight() * yu12_frame_->GetHeight() /
+ yu12_frame_->GetWidth();
+ if (cropped_width % 2 == 1) {
+ // Make cropped_width to the closest even number.
+ cropped_width++;
+ }
+ int cropped_height = yu12_frame_->GetHeight();
+ int margin = (yu12_frame_->GetWidth() - cropped_width) / 2;
+
+ int rotated_height = cropped_width;
+ int rotated_width = cropped_height;
+
+ int rotated_y_stride = rotated_width;
+ int rotated_uv_stride = rotated_width / 2;
+ size_t rotated_size =
+ rotated_y_stride * rotated_height + rotated_uv_stride * rotated_height;
+ if (rotated_size > cropped_buffer_capacity_) {
+ cropped_buffer_.reset(new uint8_t[rotated_size]);
+ cropped_buffer_capacity_ = rotated_size;
+ }
+ uint8_t* rotated_y_plane = cropped_buffer_.get();
+ uint8_t* rotated_u_plane =
+ rotated_y_plane + rotated_y_stride * rotated_height;
+ uint8_t* rotated_v_plane =
+ rotated_u_plane + rotated_uv_stride * rotated_height / 2;
+ libyuv::RotationMode rotation_mode = libyuv::RotationMode::kRotate90;
+ switch (rotate_degree) {
+ case 90:
+ rotation_mode = libyuv::RotationMode::kRotate90;
+ break;
+ case 270:
+ rotation_mode = libyuv::RotationMode::kRotate270;
+ break;
+ default:
+ LOGF(ERROR) << "Invalid rotation degree: " << rotate_degree;
+ return -EINVAL;
+ }
+ // This libyuv method first crops the frame and then rotates it 90 degrees
+ // clockwise.
+ int res = libyuv::ConvertToI420(
+ yu12_frame_->GetData(), yu12_frame_->GetDataSize(), rotated_y_plane,
+ rotated_y_stride, rotated_u_plane, rotated_uv_stride, rotated_v_plane,
+ rotated_uv_stride, margin, 0, yu12_frame_->GetWidth(),
+ yu12_frame_->GetHeight(), cropped_width, cropped_height, rotation_mode,
+ libyuv::FourCC::FOURCC_I420);
+
+ if (res) {
+ LOGF(ERROR) << "ConvertToI420 failed: " << res;
+ return res;
+ }
+
+ // Step 2: Scale
+ //
+ // Final frame
+ // Rotated frame ---------------------
+ // -------------- | |
+ // | | =====>> | |
+ // | | | |
+ // -------------- | |
+ // | |
+ // ---------------------
+ //
+ //
+ res = libyuv::I420Scale(
+ rotated_y_plane, rotated_y_stride, rotated_u_plane, rotated_uv_stride,
+ rotated_v_plane, rotated_uv_stride, rotated_width, rotated_height,
+ yu12_frame_->GetData(), yu12_frame_->GetWidth(),
+ yu12_frame_->GetData() +
+ yu12_frame_->GetWidth() * yu12_frame_->GetHeight(),
+ yu12_frame_->GetWidth() / 2,
+ yu12_frame_->GetData() +
+ yu12_frame_->GetWidth() * yu12_frame_->GetHeight() * 5 / 4,
+ yu12_frame_->GetWidth() / 2, yu12_frame_->GetWidth(),
+ yu12_frame_->GetHeight(), libyuv::FilterMode::kFilterNone);
+ LOGF_IF(ERROR, res) << "I420Scale failed: " << res;
+ return res;
+}
+
+} // namespace arc
diff --git a/modules/camera/3_4/arc/cached_frame.h b/modules/camera/3_4/arc/cached_frame.h
new file mode 100644
index 0000000..fbfcb76
--- /dev/null
+++ b/modules/camera/3_4/arc/cached_frame.h
@@ -0,0 +1,86 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef HAL_USB_CACHED_FRAME_H_
+#define HAL_USB_CACHED_FRAME_H_
+
+#include <memory>
+
+#include <camera/CameraMetadata.h>
+
+#include "arc/image_processor.h"
+
+namespace arc {
+
+// CachedFrame contains a source FrameBuffer and a cached, converted
+// FrameBuffer. The incoming frames would be converted to YU12, the default
+// format of libyuv, to allow convenient processing.
+class CachedFrame {
+ public:
+ CachedFrame();
+ ~CachedFrame();
+
+ // SetSource() doesn't take ownership of |frame|. The caller can only release
+ // |frame| after calling UnsetSource(). SetSource() immediately converts
+ // incoming frame into YU12. Return non-zero values if it encounters errors.
+ // If |rotate_degree| is 90 or 270, |frame| will be cropped, rotated by the
+ // specified amount and scaled.
+ // If |rotate_degree| is -1, |frame| will not be cropped, rotated, and scaled.
+ // This function will return an error if |rotate_degree| is not -1, 90, or
+ // 270.
+ int SetSource(const FrameBuffer* frame, int rotate_degree);
+ void UnsetSource();
+
+ uint8_t* GetSourceBuffer() const;
+ size_t GetSourceDataSize() const;
+ uint32_t GetSourceFourCC() const;
+ uint8_t* GetCachedBuffer() const;
+ uint32_t GetCachedFourCC() const;
+
+ uint32_t GetWidth() const;
+ uint32_t GetHeight() const;
+
+ // Calculate the output buffer size when converting to the specified pixel
+ // format. |fourcc| is defined as V4L2_PIX_FMT_* in linux/videodev2.h. Return
+ // 0 on error.
+ size_t GetConvertedSize(int fourcc) const;
+
+ // Caller should fill everything except |data_size| and |fd| of |out_frame|.
+ // The function will do format conversion and scale to fit |out_frame|
+ // requirement.
+ // If |video_hack| is true, it outputs YU12 when |hal_pixel_format| is YV12
+ // (swapping U/V planes). Caller should fill |fourcc|, |data|, and
+ // Return non-zero error code on failure; return 0 on success.
+ int Convert(const android::CameraMetadata& metadata, FrameBuffer* out_frame,
+ bool video_hack = false);
+
+ private:
+ int ConvertToYU12();
+ // When we have a landscape mounted camera and the current camera activity is
+ // portrait, the frames shown in the activity would be stretched. Therefore,
+ // we want to simulate a native portrait camera. That's why we want to crop,
+ // rotate |rotate_degree| clockwise and scale the frame. HAL would not change
+ // CameraInfo.orientation. Instead, framework would fake the
+ // CameraInfo.orientation. Framework would then tell HAL how much the frame
+ // needs to rotate clockwise by |rotate_degree|.
+ int CropRotateScale(int rotate_degree);
+
+ const FrameBuffer* source_frame_;
+ // const V4L2FrameBuffer* source_frame_;
+
+ // Temporary buffer for cropped and rotated results.
+ std::unique_ptr<uint8_t[]> cropped_buffer_;
+ size_t cropped_buffer_capacity_;
+
+ // Cache YU12 decoded results.
+ std::unique_ptr<AllocatedFrameBuffer> yu12_frame_;
+
+ // Temporary buffer for scaled results.
+ std::unique_ptr<AllocatedFrameBuffer> scaled_frame_;
+};
+
+} // namespace arc
+
+#endif // HAL_USB_CACHED_FRAME_H_
diff --git a/modules/camera/3_4/arc/common.h b/modules/camera/3_4/arc/common.h
new file mode 100644
index 0000000..25e4eb5
--- /dev/null
+++ b/modules/camera/3_4/arc/common.h
@@ -0,0 +1,28 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef INCLUDE_ARC_COMMON_H_
+#define INCLUDE_ARC_COMMON_H_
+
+#include <string>
+
+#include <base/logging.h>
+
+#define LOGF(level) LOG(level) << __FUNCTION__ << "(): "
+#define LOGFID(level, id) LOG(level) << __FUNCTION__ << "(): id: " << id << ": "
+#define LOGF_IF(level, res) LOG_IF(level, res) << __FUNCTION__ << "(): "
+
+#define VLOGF(level) VLOG(level) << __FUNCTION__ << "(): "
+#define VLOGFID(level, id) \
+ VLOG(level) << __FUNCTION__ << "(): id: " << id << ": "
+
+#define VLOGF_ENTER() VLOGF(1) << "enter"
+#define VLOGF_EXIT() VLOGF(1) << "exit"
+
+inline std::string FormatToString(int32_t format) {
+ return std::string(reinterpret_cast<char*>(&format), 4);
+}
+
+#endif // INCLUDE_ARC_COMMON_H_
diff --git a/modules/camera/3_4/arc/common_types.h b/modules/camera/3_4/arc/common_types.h
new file mode 100644
index 0000000..8f62ad6
--- /dev/null
+++ b/modules/camera/3_4/arc/common_types.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef HAL_USB_COMMON_TYPES_H_
+#define HAL_USB_COMMON_TYPES_H_
+
+#include <string>
+#include <vector>
+
+namespace arc {
+
+struct DeviceInfo {
+ // ex: /dev/video0
+ std::string device_path;
+ // USB vender id
+ std::string usb_vid;
+ // USB product id
+ std::string usb_pid;
+ // Some cameras need to wait several frames to output correct images.
+ uint32_t frames_to_skip_after_streamon;
+
+ // Member definitions can be found in https://developer.android.com/
+ // reference/android/hardware/camera2/CameraCharacteristics.html
+ uint32_t lens_facing;
+ int32_t sensor_orientation;
+ float horizontal_view_angle_16_9;
+ float horizontal_view_angle_4_3;
+ std::vector<float> lens_info_available_focal_lengths;
+ float lens_info_minimum_focus_distance;
+ float lens_info_optimal_focus_distance;
+ float vertical_view_angle_16_9;
+ float vertical_view_angle_4_3;
+};
+
+typedef std::vector<DeviceInfo> DeviceInfos;
+
+struct SupportedFormat {
+ uint32_t width;
+ uint32_t height;
+ uint32_t fourcc;
+ // All the supported frame rates in fps with given width, height, and
+ // pixelformat. This is not sorted. For example, suppose width, height, and
+ // fourcc are 640x480 YUYV. If frameRates are 15.0 and 30.0, the camera
+ // supports outputting 640X480 YUYV in 15fps or 30fps.
+ std::vector<float> frameRates;
+};
+
+typedef std::vector<SupportedFormat> SupportedFormats;
+
+} // namespace arc
+
+#endif // HAL_USB_COMMON_TYPES_H_
diff --git a/modules/camera/3_4/arc/exif_utils.cpp b/modules/camera/3_4/arc/exif_utils.cpp
new file mode 100644
index 0000000..aec53c5
--- /dev/null
+++ b/modules/camera/3_4/arc/exif_utils.cpp
@@ -0,0 +1,513 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "arc/exif_utils.h"
+
+#include <cstdlib>
+#include <ctime>
+
+#include <libyuv.h>
+
+#include "arc/common.h"
+
+namespace std {
+
+template <>
+struct default_delete<ExifEntry> {
+ inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); }
+};
+
+} // namespace std
+
+namespace arc {
+
+// This comes from the Exif Version 2.3 standard table 9.
+const uint8_t gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49,
+ 0x49, 0x0, 0x0, 0x0};
+
+static void SetLatitudeOrLongitudeData(unsigned char* data, double num) {
+ // Take the integer part of |num|.
+ ExifLong degrees = static_cast<ExifLong>(num);
+ ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees));
+ ExifLong microseconds =
+ static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
+ exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1});
+ exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {minutes, 1});
+ exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {microseconds, 1000000});
+}
+
+ExifUtils::ExifUtils()
+ : yu12_buffer_(nullptr),
+ yu12_width_(0),
+ yu12_height_(0),
+ thumbnail_width_(0),
+ thumbnail_height_(0),
+ exif_data_(nullptr),
+ app1_buffer_(nullptr),
+ app1_length_(0) {}
+
+ExifUtils::~ExifUtils() { Reset(); }
+
+bool ExifUtils::Initialize(const uint8_t* buffer, uint16_t width,
+ uint16_t height, int quality) {
+ Reset();
+
+ if (width % 2 != 0 || height % 2 != 0) {
+ LOGF(ERROR) << "invalid image size " << width << "x" << height;
+ return false;
+ }
+ if (quality < 1 || quality > 100) {
+ LOGF(ERROR) << "invalid jpeg quality " << quality;
+ return false;
+ }
+ thumbnail_jpeg_quality_ = quality;
+ yu12_buffer_ = buffer;
+ yu12_width_ = width;
+ yu12_height_ = height;
+
+ exif_data_ = exif_data_new();
+ if (exif_data_ == nullptr) {
+ LOGF(ERROR) << "allocate memory for exif_data_ failed";
+ return false;
+ }
+ // Set the image options.
+ exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
+ exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED);
+ exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL);
+
+ // Set image width and length.
+ SetImageWidth(width);
+ SetImageLength(height);
+
+ return true;
+}
+
+bool ExifUtils::SetMaker(const std::string& maker) {
+ size_t entrySize = maker.length() + 1;
+ std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
+ EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, entrySize, entrySize);
+ if (!entry) {
+ LOGF(ERROR) << "Adding Make exif entry failed";
+ return false;
+ }
+ memcpy(entry->data, maker.c_str(), entrySize);
+ return true;
+}
+
+bool ExifUtils::SetModel(const std::string& model) {
+ size_t entrySize = model.length() + 1;
+ std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
+ EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, entrySize, entrySize);
+ if (!entry) {
+ LOGF(ERROR) << "Adding Model exif entry failed";
+ return false;
+ }
+ memcpy(entry->data, model.c_str(), entrySize);
+ return true;
+}
+
+bool ExifUtils::SetDateTime(const struct tm& t) {
+ // The length is 20 bytes including NULL for termination in Exif standard.
+ char str[20];
+ int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i",
+ t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
+ t.tm_min, t.tm_sec);
+ if (result != sizeof(str) - 1) {
+ LOGF(WARNING) << "Input time is invalid";
+ return false;
+ }
+ std::unique_ptr<ExifEntry> entry =
+ AddVariableLengthEntry(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII,
+ sizeof(str), sizeof(str));
+ if (!entry) {
+ LOGF(ERROR) << "Adding DateTime exif entry failed";
+ return false;
+ }
+ memcpy(entry->data, str, sizeof(str));
+ return true;
+}
+
+bool ExifUtils::SetFocalLength(uint32_t numerator, uint32_t denominator) {
+ std::unique_ptr<ExifEntry> entry =
+ AddEntry(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH);
+ if (!entry) {
+ LOGF(ERROR) << "Adding FocalLength exif entry failed";
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {numerator, denominator});
+ return true;
+}
+
+bool ExifUtils::SetGpsLatitude(double latitude) {
+ const ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ LOGF(ERROR) << "Adding GPSLatitudeRef exif entry failed";
+ return false;
+ }
+ if (latitude >= 0) {
+ memcpy(refEntry->data, "N", sizeof("N"));
+ } else {
+ memcpy(refEntry->data, "S", sizeof("S"));
+ latitude *= -1;
+ }
+
+ const ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE);
+ std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ LOGF(ERROR) << "Adding GPSLatitude exif entry failed";
+ return false;
+ }
+ SetLatitudeOrLongitudeData(entry->data, latitude);
+
+ return true;
+}
+
+bool ExifUtils::SetGpsLongitude(double longitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ LOGF(ERROR) << "Adding GPSLongitudeRef exif entry failed";
+ return false;
+ }
+ if (longitude >= 0) {
+ memcpy(refEntry->data, "E", sizeof("E"));
+ } else {
+ memcpy(refEntry->data, "W", sizeof("W"));
+ longitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE);
+ std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ LOGF(ERROR) << "Adding GPSLongitude exif entry failed";
+ return false;
+ }
+ SetLatitudeOrLongitudeData(entry->data, longitude);
+
+ return true;
+}
+
+bool ExifUtils::SetGpsAltitude(double altitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1);
+ if (!refEntry) {
+ LOGF(ERROR) << "Adding GPSAltitudeRef exif entry failed";
+ return false;
+ }
+ if (altitude >= 0) {
+ *refEntry->data = 0;
+ } else {
+ *refEntry->data = 1;
+ altitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE);
+ std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ LOGF(ERROR) << "Adding GPSAltitude exif entry failed";
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(altitude * 1000), 1000});
+
+ return true;
+}
+
+bool ExifUtils::SetGpsTimestamp(const struct tm& t) {
+ const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP);
+ const size_t kGpsDateStampSize = 11;
+ std::unique_ptr<ExifEntry> entry =
+ AddVariableLengthEntry(EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII,
+ kGpsDateStampSize, kGpsDateStampSize);
+ if (!entry) {
+ LOGF(ERROR) << "Adding GPSDateStamp exif entry failed";
+ return false;
+ }
+ int result =
+ snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize,
+ "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
+ if (result != kGpsDateStampSize - 1) {
+ LOGF(WARNING) << "Input time is invalid";
+ return false;
+ }
+
+ const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP);
+ entry = AddVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3,
+ 3 * sizeof(ExifRational));
+ if (!entry) {
+ LOGF(ERROR) << "Adding GPSTimeStamp exif entry failed";
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_hour), 1});
+ exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_min), 1});
+ exif_set_rational(entry->data + 2 * sizeof(ExifRational),
+ EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_sec), 1});
+
+ return true;
+}
+
+bool ExifUtils::SetGpsProcessingMethod(const std::string& method) {
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD);
+ size_t size = sizeof(gExifAsciiPrefix) + method.length();
+ std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_UNDEFINED, size, size);
+ if (!entry) {
+ LOGF(ERROR) << "Adding GPSProcessingMethod exif entry failed";
+ return false;
+ }
+ memcpy(entry->data, gExifAsciiPrefix, sizeof(gExifAsciiPrefix));
+ // Since the exif format is undefined, NULL termination is not necessary.
+ memcpy(entry->data + sizeof(gExifAsciiPrefix), method.c_str(),
+ method.length());
+
+ return true;
+}
+
+bool ExifUtils::SetThumbnailSize(uint16_t width, uint16_t height) {
+ if (width % 2 != 0 || height % 2 != 0) {
+ LOGF(ERROR) << "Invalid thumbnail size " << width << "x" << height;
+ return false;
+ }
+ thumbnail_width_ = width;
+ thumbnail_height_ = height;
+ return true;
+}
+
+bool ExifUtils::SetOrientation(uint16_t orientation) {
+ std::unique_ptr<ExifEntry> entry = AddEntry(EXIF_IFD_0, EXIF_TAG_ORIENTATION);
+ if (!entry) {
+ LOGF(ERROR) << "Adding Orientation exif entry failed";
+ return false;
+ }
+ /*
+ * Orientation value:
+ * 1 2 3 4 5 6 7 8
+ *
+ * 888888 888888 88 88 8888888888 88 88 8888888888
+ * 88 88 88 88 88 88 88 88 88 88 88 88
+ * 8888 8888 8888 8888 88 8888888888 8888888888 88
+ * 88 88 88 88
+ * 88 88 888888 888888
+ */
+ int value = 1;
+ switch (orientation) {
+ case 90:
+ value = 6;
+ break;
+ case 180:
+ value = 3;
+ break;
+ case 270:
+ value = 8;
+ break;
+ default:
+ break;
+ }
+ exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value);
+ return true;
+}
+
+bool ExifUtils::GenerateApp1() {
+ DestroyApp1();
+ if (thumbnail_width_ > 0 && thumbnail_height_ > 0) {
+ if (!GenerateThumbnail()) {
+ LOGF(ERROR) << "Generate thumbnail image failed";
+ return false;
+ }
+ exif_data_->data = const_cast<uint8_t*>(
+ static_cast<const uint8_t*>(compressor_.GetCompressedImagePtr()));
+ exif_data_->size = compressor_.GetCompressedImageSize();
+ }
+ // Save the result into |app1_buffer_|.
+ exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_);
+ if (!app1_length_) {
+ LOGF(ERROR) << "Allocate memory for app1_buffer_ failed";
+ return false;
+ }
+ /*
+ * The JPEG segment size is 16 bits in spec. The size of APP1 segment should
+ * be smaller than 65533 because there are two bytes for segment size field.
+ */
+ if (app1_length_ > 65533) {
+ DestroyApp1();
+ LOGF(ERROR) << "The size of APP1 segment is too large";
+ return false;
+ }
+ return true;
+}
+
+const uint8_t* ExifUtils::GetApp1Buffer() { return app1_buffer_; }
+
+unsigned int ExifUtils::GetApp1Length() { return app1_length_; }
+
+void ExifUtils::Reset() {
+ yu12_buffer_ = nullptr;
+ yu12_width_ = 0;
+ yu12_height_ = 0;
+ thumbnail_width_ = 0;
+ thumbnail_height_ = 0;
+ DestroyApp1();
+ if (exif_data_) {
+ /*
+ * Since we decided to ignore the original APP1, we are sure that there is
+ * no thumbnail allocated by libexif. |exif_data_->data| is actually
+ * allocated by JpegCompressor. Sets |exif_data_->data| to nullptr to
+ * prevent exif_data_unref() destroy it incorrectly.
+ */
+ exif_data_->data = nullptr;
+ exif_data_->size = 0;
+ exif_data_unref(exif_data_);
+ exif_data_ = nullptr;
+ }
+}
+
+std::unique_ptr<ExifEntry> ExifUtils::AddVariableLengthEntry(
+ ExifIfd ifd, ExifTag tag, ExifFormat format, uint64_t components,
+ unsigned int size) {
+ // Remove old entry if exists.
+ exif_content_remove_entry(exif_data_->ifd[ifd],
+ exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ ExifMem* mem = exif_mem_new_default();
+ if (!mem) {
+ LOGF(ERROR) << "Allocate memory for exif entry failed";
+ return nullptr;
+ }
+ std::unique_ptr<ExifEntry> entry(exif_entry_new_mem(mem));
+ if (!entry) {
+ LOGF(ERROR) << "Allocate memory for exif entry failed";
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+ void* tmpBuffer = exif_mem_alloc(mem, size);
+ if (!tmpBuffer) {
+ LOGF(ERROR) << "Allocate memory for exif entry failed";
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+
+ entry->data = static_cast<unsigned char*>(tmpBuffer);
+ entry->tag = tag;
+ entry->format = format;
+ entry->components = components;
+ entry->size = size;
+
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_mem_unref(mem);
+
+ return entry;
+}
+
+std::unique_ptr<ExifEntry> ExifUtils::AddEntry(ExifIfd ifd, ExifTag tag) {
+ std::unique_ptr<ExifEntry> entry(
+ exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ if (entry) {
+ // exif_content_get_entry() won't ref the entry, so we ref here.
+ exif_entry_ref(entry.get());
+ return entry;
+ }
+ entry.reset(exif_entry_new());
+ if (!entry) {
+ LOGF(ERROR) << "Allocate memory for exif entry failed";
+ return nullptr;
+ }
+ entry->tag = tag;
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_entry_initialize(entry.get(), tag);
+ return entry;
+}
+
+bool ExifUtils::SetImageWidth(uint16_t width) {
+ std::unique_ptr<ExifEntry> entry = AddEntry(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH);
+ if (!entry) {
+ LOGF(ERROR) << "Adding ImageWidth exif entry failed";
+ return false;
+ }
+ exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, width);
+ return true;
+}
+
+bool ExifUtils::SetImageLength(uint16_t length) {
+ std::unique_ptr<ExifEntry> entry =
+ AddEntry(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH);
+ if (!entry) {
+ LOGF(ERROR) << "Adding ImageLength exif entry failed";
+ return false;
+ }
+ exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, length);
+ return true;
+}
+
+bool ExifUtils::GenerateThumbnail() {
+ // Resize yuv image to |thumbnail_width_| x |thumbnail_height_|.
+ std::vector<uint8_t> scaled_buffer;
+ if (!GenerateYuvThumbnail(&scaled_buffer)) {
+ LOGF(ERROR) << "Generate YUV thumbnail failed";
+ return false;
+ }
+
+ // Compress thumbnail to JPEG.
+ if (!compressor_.CompressImage(scaled_buffer.data(), thumbnail_width_,
+ thumbnail_height_, thumbnail_jpeg_quality_,
+ NULL, 0)) {
+ LOGF(ERROR) << "Compress thumbnail failed";
+ return false;
+ }
+ return true;
+}
+
+bool ExifUtils::GenerateYuvThumbnail(std::vector<uint8_t>* scaled_buffer) {
+ size_t y_plane_size = yu12_width_ * yu12_height_;
+ const uint8* y_plane = yu12_buffer_;
+ const uint8* u_plane = y_plane + y_plane_size;
+ const uint8* v_plane = u_plane + y_plane_size / 4;
+
+ size_t scaled_y_plane_size = thumbnail_width_ * thumbnail_height_;
+ scaled_buffer->resize(scaled_y_plane_size * 3 / 2);
+ uint8* scaled_y_plane = scaled_buffer->data();
+ uint8* scaled_u_plane = scaled_y_plane + scaled_y_plane_size;
+ uint8* scaled_v_plane = scaled_u_plane + scaled_y_plane_size / 4;
+
+ int result = libyuv::I420Scale(
+ y_plane, yu12_width_, u_plane, yu12_width_ / 2, v_plane, yu12_width_ / 2,
+ yu12_width_, yu12_height_, scaled_y_plane, thumbnail_width_,
+ scaled_u_plane, thumbnail_width_ / 2, scaled_v_plane,
+ thumbnail_width_ / 2, thumbnail_width_, thumbnail_height_,
+ libyuv::kFilterNone);
+ if (result != 0) {
+ LOGF(ERROR) << "Scale I420 image failed";
+ return false;
+ }
+ return true;
+}
+
+void ExifUtils::DestroyApp1() {
+ /*
+ * Since there is no API to access ExifMem in ExifData->priv, we use free
+ * here, which is the default free function in libexif. See
+ * exif_data_save_data() for detail.
+ */
+ free(app1_buffer_);
+ app1_buffer_ = nullptr;
+ app1_length_ = 0;
+}
+
+} // namespace arc
diff --git a/modules/camera/3_4/arc/exif_utils.h b/modules/camera/3_4/arc/exif_utils.h
new file mode 100644
index 0000000..956ee1d
--- /dev/null
+++ b/modules/camera/3_4/arc/exif_utils.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef INCLUDE_ARC_EXIF_UTILS_H_
+#define INCLUDE_ARC_EXIF_UTILS_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+extern "C" {
+#include <libexif/exif-data.h>
+}
+
+#include "arc/jpeg_compressor.h"
+
+namespace arc {
+
+// ExifUtils can generate APP1 segment with tags which caller set. ExifUtils can
+// also add a thumbnail in the APP1 segment if thumbnail size is specified.
+// ExifUtils can be reused with different images by calling initialize().
+//
+// Example of using this class :
+// ExifUtils utils;
+// utils.initialize(inputYU12Buffer, inputWidth, inputHeight,
+// outputJpegQuality);
+// ...
+// // Call ExifUtils functions to set Exif tags.
+// ...
+// utils.generateApp1();
+// unsigned int app1Length = utils.getApp1Length();
+// uint8_t* app1Buffer = new uint8_t[app1Length];
+// memcpy(app1Buffer, utils.getApp1Buffer(), app1Length);
+class ExifUtils {
+ public:
+ ExifUtils();
+ ~ExifUtils();
+
+ // Sets input YU12 image |buffer| with |width| x |height|. |quality| is the
+ // compressed JPEG image quality. The caller should not release |buffer| until
+ // generateApp1() or the destructor is called. initialize() can be called
+ // multiple times. The setting of Exif tags will be cleared.
+ bool Initialize(const uint8_t* buffer, uint16_t width, uint16_t height,
+ int quality);
+
+ // Sets the manufacturer of camera.
+ // Returns false if memory allocation fails.
+ bool SetMaker(const std::string& maker);
+
+ // Sets the model number of camera.
+ // Returns false if memory allocation fails.
+ bool SetModel(const std::string& model);
+
+ // Sets the date and time of image last modified. It takes local time. The
+ // name of the tag is DateTime in IFD0.
+ // Returns false if memory allocation fails.
+ bool SetDateTime(const struct tm& t);
+
+ // Sets the focal length of lens used to take the image in millimeters.
+ // Returns false if memory allocation fails.
+ bool SetFocalLength(uint32_t numerator, uint32_t denominator);
+
+ // Sets the latitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ bool SetGpsLatitude(double latitude);
+
+ // Sets the longitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ bool SetGpsLongitude(double longitude);
+
+ // Sets the altitude in meters.
+ // Returns false if memory allocation fails.
+ bool SetGpsAltitude(double altitude);
+
+ // Sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
+ // Returns false if memory allocation fails.
+ bool SetGpsTimestamp(const struct tm& t);
+
+ // Sets GPS processing method.
+ // Returns false if memory allocation fails.
+ bool SetGpsProcessingMethod(const std::string& method);
+
+ // Since the size of APP1 segment is limited, it is recommended the
+ // resolution of thumbnail is equal to or smaller than 640x480. If the
+ // thumbnail is too big, generateApp1() will return false.
+ // Returns false if |width| or |height| is not even.
+ bool SetThumbnailSize(uint16_t width, uint16_t height);
+
+ // Sets image orientation.
+ // Returns false if memory allocation fails.
+ bool SetOrientation(uint16_t orientation);
+
+ // Generates APP1 segment.
+ // Returns false if generating APP1 segment fails.
+ bool GenerateApp1();
+
+ // Gets buffer of APP1 segment. This method must be called only after calling
+ // generateAPP1().
+ const uint8_t* GetApp1Buffer();
+
+ // Gets length of APP1 segment. This method must be called only after calling
+ // generateAPP1().
+ unsigned int GetApp1Length();
+
+ private:
+ // Resets the pointers and memories.
+ void Reset();
+
+ // Adds a variable length tag to |exif_data_|. It will remove the original one
+ // if the tag exists.
+ // Returns the entry of the tag. The reference count of returned ExifEntry is
+ // two.
+ std::unique_ptr<ExifEntry> AddVariableLengthEntry(ExifIfd ifd, ExifTag tag,
+ ExifFormat format,
+ uint64_t components,
+ unsigned int size);
+
+ // Adds a entry of |tag| in |exif_data_|. It won't remove the original one if
+ // the tag exists.
+ // Returns the entry of the tag. It adds one reference count to returned
+ // ExifEntry.
+ std::unique_ptr<ExifEntry> AddEntry(ExifIfd ifd, ExifTag tag);
+
+ // Sets the width (number of columes) of main image.
+ // Returns false if memory allocation fails.
+ bool SetImageWidth(uint16_t width);
+
+ // Sets the length (number of rows) of main image.
+ // Returns false if memory allocation fails.
+ bool SetImageLength(uint16_t length);
+
+ // Generates a thumbnail. Calls compressor_.getCompressedImagePtr() to get the
+ // result image.
+ // Returns false if failed.
+ bool GenerateThumbnail();
+
+ // Resizes the thumbnail yuv image to |thumbnail_width_| x |thumbnail_height_|
+ // and stores in |scaled_buffer|.
+ // Returns false if scale image failed.
+ bool GenerateYuvThumbnail(std::vector<uint8_t>* scaled_buffer);
+
+ // Destroys the buffer of APP1 segment if exists.
+ void DestroyApp1();
+
+ // The buffer pointer of yuv image (YU12). Not owned by this class.
+ const uint8_t* yu12_buffer_;
+ // The size of yuv image.
+ uint16_t yu12_width_;
+ uint16_t yu12_height_;
+
+ // The size of thumbnail.
+ uint16_t thumbnail_width_;
+ uint16_t thumbnail_height_;
+
+ // The Exif data (APP1). Owned by this class.
+ ExifData* exif_data_;
+ // The raw data of APP1 segment. It's allocated by ExifMem in |exif_data_| but
+ // owned by this class.
+ uint8_t* app1_buffer_;
+ // The length of |app1_buffer_|.
+ unsigned int app1_length_;
+ // The quality of compressed thumbnail image. The size of EXIF thumbnail has
+ // to be smaller than 64KB. If quality is 100, the size may be bigger than
+ // 64KB.
+ int thumbnail_jpeg_quality_;
+
+ // The YU12 to Jpeg compressor.
+ JpegCompressor compressor_;
+};
+
+} // namespace arc
+
+#endif // INCLUDE_ARC_EXIF_UTILS_H_
diff --git a/modules/camera/3_4/arc/frame_buffer.cpp b/modules/camera/3_4/arc/frame_buffer.cpp
new file mode 100644
index 0000000..4ae0fe3
--- /dev/null
+++ b/modules/camera/3_4/arc/frame_buffer.cpp
@@ -0,0 +1,188 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "arc/frame_buffer.h"
+
+#include <sys/mman.h>
+
+#include <utility>
+
+#include "arc/common.h"
+#include "arc/image_processor.h"
+
+namespace arc {
+
+FrameBuffer::FrameBuffer()
+ : data_(nullptr),
+ data_size_(0),
+ buffer_size_(0),
+ width_(0),
+ height_(0),
+ fourcc_(0) {}
+
+FrameBuffer::~FrameBuffer() {}
+
+int FrameBuffer::SetDataSize(size_t data_size) {
+ if (data_size > buffer_size_) {
+ LOGF(ERROR) << "Buffer overflow: Buffer only has " << buffer_size_
+ << ", but data needs " << data_size;
+ return -EINVAL;
+ }
+ data_size_ = data_size;
+ return 0;
+}
+
+AllocatedFrameBuffer::AllocatedFrameBuffer(int buffer_size) {
+ buffer_.reset(new uint8_t[buffer_size]);
+ buffer_size_ = buffer_size;
+ data_ = buffer_.get();
+}
+
+AllocatedFrameBuffer::AllocatedFrameBuffer(uint8_t* buffer, int buffer_size) {
+ buffer_.reset(buffer);
+ buffer_size_ = buffer_size;
+ data_ = buffer_.get();
+}
+
+AllocatedFrameBuffer::~AllocatedFrameBuffer() {}
+
+int AllocatedFrameBuffer::SetDataSize(size_t size) {
+ if (size > buffer_size_) {
+ buffer_.reset(new uint8_t[size]);
+ buffer_size_ = size;
+ data_ = buffer_.get();
+ }
+ data_size_ = size;
+ return 0;
+}
+
+void AllocatedFrameBuffer::Reset() { memset(data_, 0, buffer_size_); }
+
+V4L2FrameBuffer::V4L2FrameBuffer(base::ScopedFD fd, int buffer_size,
+ uint32_t width, uint32_t height,
+ uint32_t fourcc)
+ : fd_(std::move(fd)), is_mapped_(false) {
+ buffer_size_ = buffer_size;
+ width_ = width;
+ height_ = height;
+ fourcc_ = fourcc;
+}
+
+V4L2FrameBuffer::~V4L2FrameBuffer() {
+ if (Unmap()) {
+ LOGF(ERROR) << "Unmap failed";
+ }
+}
+
+int V4L2FrameBuffer::Map() {
+ base::AutoLock l(lock_);
+ if (is_mapped_) {
+ LOGF(ERROR) << "The buffer is already mapped";
+ return -EINVAL;
+ }
+ void* addr = mmap(NULL, buffer_size_, PROT_READ, MAP_SHARED, fd_.get(), 0);
+ if (addr == MAP_FAILED) {
+ LOGF(ERROR) << "mmap() failed: " << strerror(errno);
+ return -EINVAL;
+ }
+ data_ = static_cast<uint8_t*>(addr);
+ is_mapped_ = true;
+ return 0;
+}
+
+int V4L2FrameBuffer::Unmap() {
+ base::AutoLock l(lock_);
+ if (is_mapped_ && munmap(data_, buffer_size_)) {
+ LOGF(ERROR) << "mummap() failed: " << strerror(errno);
+ return -EINVAL;
+ }
+ is_mapped_ = false;
+ return 0;
+}
+
+GrallocFrameBuffer::GrallocFrameBuffer(buffer_handle_t buffer, uint32_t width,
+ uint32_t height, uint32_t fourcc,
+ uint32_t device_buffer_length,
+ uint32_t stream_usage)
+ : buffer_(buffer),
+ is_mapped_(false),
+ device_buffer_length_(device_buffer_length),
+ stream_usage_(stream_usage) {
+ const hw_module_t* module = nullptr;
+ int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+ if (ret || !module) {
+ LOGF(ERROR) << "Failed to get gralloc module.";
+ return;
+ }
+ gralloc_module_ = reinterpret_cast<const gralloc_module_t*>(module);
+ width_ = width;
+ height_ = height;
+ fourcc_ = fourcc;
+}
+
+GrallocFrameBuffer::~GrallocFrameBuffer() {
+ if (Unmap()) {
+ LOGF(ERROR) << "Unmap failed";
+ }
+}
+
+int GrallocFrameBuffer::Map() {
+ base::AutoLock l(lock_);
+ if (is_mapped_) {
+ LOGF(ERROR) << "The buffer is already mapped";
+ return -EINVAL;
+ }
+
+ void* addr;
+ int ret = 0;
+ switch (fourcc_) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUYV:
+ android_ycbcr yuv_data;
+ ret = gralloc_module_->lock_ycbcr(gralloc_module_, buffer_, stream_usage_,
+ 0, 0, width_, height_, &yuv_data);
+ addr = yuv_data.y;
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ ret = gralloc_module_->lock(gralloc_module_, buffer_, stream_usage_, 0, 0,
+ device_buffer_length_, 1, &addr);
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ ret = gralloc_module_->lock(gralloc_module_, buffer_, stream_usage_, 0, 0,
+ width_, height_, &addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret) {
+ LOGF(ERROR) << "Failed to gralloc lock buffer: " << ret;
+ return ret;
+ }
+
+ data_ = static_cast<uint8_t*>(addr);
+ if (fourcc_ == V4L2_PIX_FMT_YVU420 || fourcc_ == V4L2_PIX_FMT_YUV420 ||
+ fourcc_ == V4L2_PIX_FMT_NV21 || fourcc_ == V4L2_PIX_FMT_RGB32 ||
+ fourcc_ == V4L2_PIX_FMT_BGR32) {
+ buffer_size_ = ImageProcessor::GetConvertedSize(fourcc_, width_, height_);
+ }
+
+ is_mapped_ = true;
+ return 0;
+}
+
+int GrallocFrameBuffer::Unmap() {
+ base::AutoLock l(lock_);
+ if (is_mapped_ && gralloc_module_->unlock(gralloc_module_, buffer_)) {
+ LOGF(ERROR) << "Failed to unmap buffer: ";
+ return -EINVAL;
+ }
+ is_mapped_ = false;
+ return 0;
+}
+
+} // namespace arc
diff --git a/modules/camera/3_4/arc/frame_buffer.h b/modules/camera/3_4/arc/frame_buffer.h
new file mode 100644
index 0000000..3efeb3b
--- /dev/null
+++ b/modules/camera/3_4/arc/frame_buffer.h
@@ -0,0 +1,135 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef HAL_USB_FRAME_BUFFER_H_
+#define HAL_USB_FRAME_BUFFER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include <base/files/scoped_file.h>
+#include <base/synchronization/lock.h>
+
+#include <hardware/gralloc.h>
+
+namespace arc {
+
+class FrameBuffer {
+ public:
+ FrameBuffer();
+ virtual ~FrameBuffer();
+
+ // If mapped successfully, the address will be assigned to |data_| and return
+ // 0. Otherwise, returns -EINVAL.
+ virtual int Map() = 0;
+
+ // Unmaps the mapped address. Returns 0 for success.
+ virtual int Unmap() = 0;
+
+ uint8_t* GetData() const { return data_; }
+ size_t GetDataSize() const { return data_size_; }
+ size_t GetBufferSize() const { return buffer_size_; }
+ uint32_t GetWidth() const { return width_; }
+ uint32_t GetHeight() const { return height_; }
+ uint32_t GetFourcc() const { return fourcc_; }
+
+ void SetFourcc(uint32_t fourcc) { fourcc_ = fourcc; }
+ virtual int SetDataSize(size_t data_size);
+
+ protected:
+ uint8_t* data_;
+
+ // The number of bytes used in the buffer.
+ size_t data_size_;
+
+ // The number of bytes allocated in the buffer.
+ size_t buffer_size_;
+
+ // Frame resolution.
+ uint32_t width_;
+ uint32_t height_;
+
+ // This is V4L2_PIX_FMT_* in linux/videodev2.h.
+ uint32_t fourcc_;
+};
+
+// AllocatedFrameBuffer is used for the buffer from hal malloc-ed. User should
+// be aware to manage the memory.
+class AllocatedFrameBuffer : public FrameBuffer {
+ public:
+ explicit AllocatedFrameBuffer(int buffer_size);
+ explicit AllocatedFrameBuffer(uint8_t* buffer, int buffer_size);
+ ~AllocatedFrameBuffer() override;
+
+ // No-op for the two functions.
+ int Map() override { return 0; }
+ int Unmap() override { return 0; }
+
+ void SetWidth(uint32_t width) { width_ = width; }
+ void SetHeight(uint32_t height) { height_ = height; }
+ int SetDataSize(size_t data_size) override;
+ void Reset();
+
+ private:
+ std::unique_ptr<uint8_t[]> buffer_;
+};
+
+// V4L2FrameBuffer is used for the buffer from V4L2CameraDevice. Maps the fd
+// in constructor. Unmaps and closes the fd in destructor.
+class V4L2FrameBuffer : public FrameBuffer {
+ public:
+ V4L2FrameBuffer(base::ScopedFD fd, int buffer_size, uint32_t width,
+ uint32_t height, uint32_t fourcc);
+ // Unmaps |data_| and closes |fd_|.
+ ~V4L2FrameBuffer();
+
+ int Map() override;
+ int Unmap() override;
+ int GetFd() const { return fd_.get(); }
+
+ private:
+ // File descriptor of V4L2 frame buffer.
+ base::ScopedFD fd_;
+
+ bool is_mapped_;
+
+ // Lock to guard |is_mapped_|.
+ base::Lock lock_;
+};
+
+// GrallocFrameBuffer is used for the buffer from Android framework. Uses
+// CameraBufferMapper to lock and unlock the buffer.
+class GrallocFrameBuffer : public FrameBuffer {
+ public:
+ GrallocFrameBuffer(buffer_handle_t buffer, uint32_t width, uint32_t height,
+ uint32_t fourcc, uint32_t device_buffer_length,
+ uint32_t stream_usage);
+ ~GrallocFrameBuffer();
+
+ int Map() override;
+ int Unmap() override;
+
+ private:
+ // The currently used buffer for |buffer_mapper_| operations.
+ buffer_handle_t buffer_;
+
+ // Used to import gralloc buffer.
+ const gralloc_module_t* gralloc_module_;
+
+ bool is_mapped_;
+
+ // Lock to guard |is_mapped_|.
+ base::Lock lock_;
+
+ // Camera stream and device buffer context.
+ uint32_t device_buffer_length_;
+ uint32_t stream_usage_;
+};
+
+} // namespace arc
+
+#endif // HAL_USB_FRAME_BUFFER_H_
diff --git a/modules/camera/3_4/arc/image_processor.cpp b/modules/camera/3_4/arc/image_processor.cpp
new file mode 100644
index 0000000..f0fee91
--- /dev/null
+++ b/modules/camera/3_4/arc/image_processor.cpp
@@ -0,0 +1,491 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "arc/image_processor.h"
+
+#include <errno.h>
+#include <libyuv.h>
+#include <time.h>
+
+#include "arc/common.h"
+#include "arc/common_types.h"
+#include "arc/exif_utils.h"
+#include "arc/jpeg_compressor.h"
+
+namespace arc {
+
+using android::CameraMetadata;
+
+/*
+ * Formats have different names in different header files. Here is the mapping
+ * table:
+ *
+ * android_pixel_format_t videodev2.h FOURCC in libyuv
+ * -----------------------------------------------------------------------------
+ * HAL_PIXEL_FORMAT_YV12 = V4L2_PIX_FMT_YVU420 = FOURCC_YV12
+ * HAL_PIXEL_FORMAT_YCrCb_420_SP = V4L2_PIX_FMT_NV21 = FOURCC_NV21
+ * HAL_PIXEL_FORMAT_RGBA_8888 = V4L2_PIX_FMT_RGB32 = FOURCC_BGR4
+ * HAL_PIXEL_FORMAT_YCbCr_422_I = V4L2_PIX_FMT_YUYV = FOURCC_YUYV
+ * = FOURCC_YUY2
+ * V4L2_PIX_FMT_YUV420 = FOURCC_I420
+ * = FOURCC_YU12
+ * V4L2_PIX_FMT_MJPEG = FOURCC_MJPG
+ *
+ * Camera device generates FOURCC_YUYV and FOURCC_MJPG.
+ * Preview needs FOURCC_ARGB format.
+ * Software video encoder needs FOURCC_YU12.
+ * CTS requires FOURCC_YV12 and FOURCC_NV21 for applications.
+ *
+ * Android stride requirement:
+ * YV12 horizontal stride should be a multiple of 16 pixels. See
+ * android.graphics.ImageFormat.YV12.
+ * The stride of ARGB, YU12, and NV21 are always equal to the width.
+ *
+ * Conversion Path:
+ * MJPG/YUYV (from camera) -> YU12 -> ARGB (preview)
+ * -> NV21 (apps)
+ * -> YV12 (apps)
+ * -> YU12 (video encoder)
+ */
+
+// YV12 horizontal stride should be a multiple of 16 pixels for each plane.
+// |dst_stride_uv| is the pixel stride of u or v plane.
+static int YU12ToYV12(const void* yv12, void* yu12, int width, int height,
+ int dst_stride_y, int dst_stride_uv);
+static int YU12ToNV21(const void* yv12, void* nv21, int width, int height);
+static bool ConvertToJpeg(const CameraMetadata& metadata,
+ const FrameBuffer& in_frame, FrameBuffer* out_frame);
+static bool SetExifTags(const CameraMetadata& metadata, ExifUtils* utils);
+
+// How precise the float-to-rational conversion for EXIF tags would be.
+static const int kRationalPrecision = 10000;
+
+// Default JPEG quality settings.
+static const int DEFAULT_JPEG_QUALITY = 80;
+
+inline static size_t Align16(size_t value) { return (value + 15) & ~15; }
+
+size_t ImageProcessor::GetConvertedSize(int fourcc, uint32_t width,
+ uint32_t height) {
+ if ((width % 2) || (height % 2)) {
+ LOGF(ERROR) << "Width or height is not even (" << width << " x " << height
+ << ")";
+ return 0;
+ }
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_YVU420: // YV12
+ return Align16(width) * height + Align16(width / 2) * height;
+ case V4L2_PIX_FMT_YUV420: // YU12
+ // Fall-through.
+ case V4L2_PIX_FMT_NV21: // NV21
+ return width * height * 3 / 2;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ return width * height * 4;
+ default:
+ LOGF(ERROR) << "Pixel format " << FormatToString(fourcc)
+ << " is unsupported.";
+ return 0;
+ }
+}
+
+bool ImageProcessor::SupportsConversion(uint32_t from_fourcc,
+ uint32_t to_fourcc) {
+ switch (from_fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ return (to_fourcc == V4L2_PIX_FMT_YUV420);
+ case V4L2_PIX_FMT_YUV420:
+ return (
+ to_fourcc == V4L2_PIX_FMT_YUV420 ||
+ to_fourcc == V4L2_PIX_FMT_YVU420 || to_fourcc == V4L2_PIX_FMT_NV21 ||
+ to_fourcc == V4L2_PIX_FMT_RGB32 || to_fourcc == V4L2_PIX_FMT_BGR32 ||
+ to_fourcc == V4L2_PIX_FMT_JPEG);
+ case V4L2_PIX_FMT_MJPEG:
+ return (to_fourcc == V4L2_PIX_FMT_YUV420);
+ default:
+ return false;
+ }
+}
+
+int ImageProcessor::ConvertFormat(const CameraMetadata& metadata,
+ const FrameBuffer& in_frame,
+ FrameBuffer* out_frame) {
+ if ((in_frame.GetWidth() % 2) || (in_frame.GetHeight() % 2)) {
+ LOGF(ERROR) << "Width or height is not even (" << in_frame.GetWidth()
+ << " x " << in_frame.GetHeight() << ")";
+ return -EINVAL;
+ }
+
+ size_t data_size = GetConvertedSize(
+ out_frame->GetFourcc(), in_frame.GetWidth(), in_frame.GetHeight());
+
+ if (out_frame->SetDataSize(data_size)) {
+ LOGF(ERROR) << "Set data size failed";
+ return -EINVAL;
+ }
+
+ if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUYV) {
+ switch (out_frame->GetFourcc()) {
+ case V4L2_PIX_FMT_YUV420: // YU12
+ {
+ int res = libyuv::YUY2ToI420(
+ in_frame.GetData(), /* src_yuy2 */
+ in_frame.GetWidth() * 2, /* src_stride_yuy2 */
+ out_frame->GetData(), /* dst_y */
+ out_frame->GetWidth(), /* dst_stride_y */
+ out_frame->GetData() +
+ out_frame->GetWidth() * out_frame->GetHeight(), /* dst_u */
+ out_frame->GetWidth() / 2, /* dst_stride_u */
+ out_frame->GetData() + out_frame->GetWidth() *
+ out_frame->GetHeight() * 5 /
+ 4, /* dst_v */
+ out_frame->GetWidth() / 2, /* dst_stride_v */
+ in_frame.GetWidth(), in_frame.GetHeight());
+ LOGF_IF(ERROR, res) << "YUY2ToI420() for YU12 returns " << res;
+ return res ? -EINVAL : 0;
+ }
+ default:
+ LOGF(ERROR) << "Destination pixel format "
+ << FormatToString(out_frame->GetFourcc())
+ << " is unsupported for YUYV source format.";
+ return -EINVAL;
+ }
+ } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420) {
+ // V4L2_PIX_FMT_YVU420 is YV12. I420 is usually referred to YU12
+ // (V4L2_PIX_FMT_YUV420), and YV12 is similar to YU12 except that U/V
+ // planes are swapped.
+ switch (out_frame->GetFourcc()) {
+ case V4L2_PIX_FMT_YVU420: // YV12
+ {
+ int ystride = Align16(in_frame.GetWidth());
+ int uvstride = Align16(in_frame.GetWidth() / 2);
+ int res = YU12ToYV12(in_frame.GetData(), out_frame->GetData(),
+ in_frame.GetWidth(), in_frame.GetHeight(), ystride,
+ uvstride);
+ LOGF_IF(ERROR, res) << "YU12ToYV12() returns " << res;
+ return res ? -EINVAL : 0;
+ }
+ case V4L2_PIX_FMT_YUV420: // YU12
+ {
+ memcpy(out_frame->GetData(), in_frame.GetData(),
+ in_frame.GetDataSize());
+ return 0;
+ }
+ case V4L2_PIX_FMT_NV21: // NV21
+ {
+ // TODO(henryhsu): Use libyuv::I420ToNV21.
+ int res = YU12ToNV21(in_frame.GetData(), out_frame->GetData(),
+ in_frame.GetWidth(), in_frame.GetHeight());
+ LOGF_IF(ERROR, res) << "YU12ToNV21() returns " << res;
+ return res ? -EINVAL : 0;
+ }
+ case V4L2_PIX_FMT_BGR32: {
+ int res = libyuv::I420ToABGR(
+ in_frame.GetData(), /* src_y */
+ in_frame.GetWidth(), /* src_stride_y */
+ in_frame.GetData() +
+ in_frame.GetWidth() * in_frame.GetHeight(), /* src_u */
+ in_frame.GetWidth() / 2, /* src_stride_u */
+ in_frame.GetData() +
+ in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, /* src_v */
+ in_frame.GetWidth() / 2, /* src_stride_v */
+ out_frame->GetData(), /* dst_abgr */
+ out_frame->GetWidth() * 4, /* dst_stride_abgr */
+ in_frame.GetWidth(), in_frame.GetHeight());
+ LOGF_IF(ERROR, res) << "I420ToABGR() returns " << res;
+ return res ? -EINVAL : 0;
+ }
+ case V4L2_PIX_FMT_RGB32: {
+ int res = libyuv::I420ToARGB(
+ in_frame.GetData(), /* src_y */
+ in_frame.GetWidth(), /* src_stride_y */
+ in_frame.GetData() +
+ in_frame.GetWidth() * in_frame.GetHeight(), /* src_u */
+ in_frame.GetWidth() / 2, /* src_stride_u */
+ in_frame.GetData() +
+ in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, /* src_v */
+ in_frame.GetWidth() / 2, /* src_stride_v */
+ out_frame->GetData(), /* dst_argb */
+ out_frame->GetWidth() * 4, /* dst_stride_argb */
+ in_frame.GetWidth(), in_frame.GetHeight());
+ LOGF_IF(ERROR, res) << "I420ToARGB() returns " << res;
+ return res ? -EINVAL : 0;
+ }
+ case V4L2_PIX_FMT_JPEG: {
+ bool res = ConvertToJpeg(metadata, in_frame, out_frame);
+ LOGF_IF(ERROR, !res) << "ConvertToJpeg() returns " << res;
+ return res ? -EINVAL : 0;
+ }
+ default:
+ LOGF(ERROR) << "Destination pixel format "
+ << FormatToString(out_frame->GetFourcc())
+ << " is unsupported for YU12 source format.";
+ return -EINVAL;
+ }
+ } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_MJPEG) {
+ switch (out_frame->GetFourcc()) {
+ case V4L2_PIX_FMT_YUV420: // YU12
+ {
+ int res = libyuv::MJPGToI420(
+ in_frame.GetData(), /* sample */
+ in_frame.GetDataSize(), /* sample_size */
+ out_frame->GetData(), /* dst_y */
+ out_frame->GetWidth(), /* dst_stride_y */
+ out_frame->GetData() +
+ out_frame->GetWidth() * out_frame->GetHeight(), /* dst_u */
+ out_frame->GetWidth() / 2, /* dst_stride_u */
+ out_frame->GetData() + out_frame->GetWidth() *
+ out_frame->GetHeight() * 5 /
+ 4, /* dst_v */
+ out_frame->GetWidth() / 2, /* dst_stride_v */
+ in_frame.GetWidth(), in_frame.GetHeight(), out_frame->GetWidth(),
+ out_frame->GetHeight());
+ LOGF_IF(ERROR, res) << "MJPEGToI420() returns " << res;
+ return res ? -EINVAL : 0;
+ }
+ default:
+ LOGF(ERROR) << "Destination pixel format "
+ << FormatToString(out_frame->GetFourcc())
+ << " is unsupported for MJPEG source format.";
+ return -EINVAL;
+ }
+ } else {
+ LOGF(ERROR) << "Convert format doesn't support source format "
+ << FormatToString(in_frame.GetFourcc());
+ return -EINVAL;
+ }
+}
+
+int ImageProcessor::Scale(const FrameBuffer& in_frame, FrameBuffer* out_frame) {
+ if (in_frame.GetFourcc() != V4L2_PIX_FMT_YUV420) {
+ LOGF(ERROR) << "Pixel format " << FormatToString(in_frame.GetFourcc())
+ << " is unsupported.";
+ return -EINVAL;
+ }
+
+ size_t data_size = GetConvertedSize(
+ in_frame.GetFourcc(), out_frame->GetWidth(), out_frame->GetHeight());
+
+ if (out_frame->SetDataSize(data_size)) {
+ LOGF(ERROR) << "Set data size failed";
+ return -EINVAL;
+ }
+ out_frame->SetFourcc(in_frame.GetFourcc());
+
+ VLOGF(1) << "Scale image from " << in_frame.GetWidth() << "x"
+ << in_frame.GetHeight() << " to " << out_frame->GetWidth() << "x"
+ << out_frame->GetHeight();
+
+ int ret = libyuv::I420Scale(
+ in_frame.GetData(), in_frame.GetWidth(),
+ in_frame.GetData() + in_frame.GetWidth() * in_frame.GetHeight(),
+ in_frame.GetWidth() / 2,
+ in_frame.GetData() + in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4,
+ in_frame.GetWidth() / 2, in_frame.GetWidth(), in_frame.GetHeight(),
+ out_frame->GetData(), out_frame->GetWidth(),
+ out_frame->GetData() + out_frame->GetWidth() * out_frame->GetHeight(),
+ out_frame->GetWidth() / 2,
+ out_frame->GetData() +
+ out_frame->GetWidth() * out_frame->GetHeight() * 5 / 4,
+ out_frame->GetWidth() / 2, out_frame->GetWidth(), out_frame->GetHeight(),
+ libyuv::FilterMode::kFilterNone);
+ LOGF_IF(ERROR, ret) << "I420Scale failed: " << ret;
+ return ret;
+}
+
+static int YU12ToYV12(const void* yu12, void* yv12, int width, int height,
+ int dst_stride_y, int dst_stride_uv) {
+ if ((width % 2) || (height % 2)) {
+ LOGF(ERROR) << "Width or height is not even (" << width << " x " << height
+ << ")";
+ return -EINVAL;
+ }
+ if (dst_stride_y < width || dst_stride_uv < width / 2) {
+ LOGF(ERROR) << "Y plane stride (" << dst_stride_y
+ << ") or U/V plane stride (" << dst_stride_uv
+ << ") is invalid for width " << width;
+ return -EINVAL;
+ }
+
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(yu12);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(yv12);
+ const uint8_t* u_src = src + width * height;
+ uint8_t* u_dst = dst + dst_stride_y * height + dst_stride_uv * height / 2;
+ const uint8_t* v_src = src + width * height * 5 / 4;
+ uint8_t* v_dst = dst + dst_stride_y * height;
+
+ return libyuv::I420Copy(src, width, u_src, width / 2, v_src, width / 2, dst,
+ dst_stride_y, u_dst, dst_stride_uv, v_dst,
+ dst_stride_uv, width, height);
+}
+
+static int YU12ToNV21(const void* yu12, void* nv21, int width, int height) {
+ if ((width % 2) || (height % 2)) {
+ LOGF(ERROR) << "Width or height is not even (" << width << " x " << height
+ << ")";
+ return -EINVAL;
+ }
+
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(yu12);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(nv21);
+ const uint8_t* u_src = src + width * height;
+ const uint8_t* v_src = src + width * height * 5 / 4;
+ uint8_t* vu_dst = dst + width * height;
+
+ memcpy(dst, src, width * height);
+
+ for (int i = 0; i < height / 2; i++) {
+ for (int j = 0; j < width / 2; j++) {
+ *vu_dst++ = *v_src++;
+ *vu_dst++ = *u_src++;
+ }
+ }
+ return 0;
+}
+
+static bool ConvertToJpeg(const CameraMetadata& metadata,
+ const FrameBuffer& in_frame, FrameBuffer* out_frame) {
+ ExifUtils utils;
+ int jpeg_quality, thumbnail_jpeg_quality;
+ camera_metadata_ro_entry entry;
+
+ if (metadata.exists(ANDROID_JPEG_QUALITY)) {
+ entry = metadata.find(ANDROID_JPEG_QUALITY);
+ jpeg_quality = entry.data.u8[0];
+ } else {
+ LOGF(ERROR) << "Could not find jpeg quality in metadata, defaulting to "
+ << DEFAULT_JPEG_QUALITY;
+ jpeg_quality = DEFAULT_JPEG_QUALITY;
+ }
+ if (metadata.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
+ entry = metadata.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
+ thumbnail_jpeg_quality = entry.data.u8[0];
+ } else {
+ thumbnail_jpeg_quality = jpeg_quality;
+ }
+
+ if (!utils.Initialize(in_frame.GetData(), in_frame.GetWidth(),
+ in_frame.GetHeight(), thumbnail_jpeg_quality)) {
+ LOGF(ERROR) << "ExifUtils initialization failed.";
+ return false;
+ }
+ if (!SetExifTags(metadata, &utils)) {
+ LOGF(ERROR) << "Setting Exif tags failed.";
+ return false;
+ }
+ if (!utils.GenerateApp1()) {
+ LOGF(ERROR) << "Generating APP1 segment failed.";
+ return false;
+ }
+ JpegCompressor compressor;
+ if (!compressor.CompressImage(in_frame.GetData(), in_frame.GetWidth(),
+ in_frame.GetHeight(), jpeg_quality,
+ utils.GetApp1Buffer(), utils.GetApp1Length())) {
+ LOGF(ERROR) << "JPEG image compression failed";
+ return false;
+ }
+ size_t buffer_length = compressor.GetCompressedImageSize();
+ memcpy(out_frame->GetData(), compressor.GetCompressedImagePtr(),
+ buffer_length);
+ return true;
+}
+
+static bool SetExifTags(const CameraMetadata& metadata, ExifUtils* utils) {
+ time_t raw_time = 0;
+ struct tm time_info;
+ bool time_available = time(&raw_time) != -1;
+ localtime_r(&raw_time, &time_info);
+ if (!utils->SetDateTime(time_info)) {
+ LOGF(ERROR) << "Setting data time failed.";
+ return false;
+ }
+
+ float focal_length;
+ camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH);
+ if (entry.count) {
+ focal_length = entry.data.f[0];
+ } else {
+ LOGF(ERROR) << "Cannot find focal length in metadata.";
+ return false;
+ }
+ if (!utils->SetFocalLength(
+ static_cast<uint32_t>(focal_length * kRationalPrecision),
+ kRationalPrecision)) {
+ LOGF(ERROR) << "Setting focal length failed.";
+ return false;
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES);
+ if (entry.count < 3) {
+ LOGF(ERROR) << "Gps coordinates in metadata is not complete.";
+ return false;
+ }
+ if (!utils->SetGpsLatitude(entry.data.d[0])) {
+ LOGF(ERROR) << "Setting gps latitude failed.";
+ return false;
+ }
+ if (!utils->SetGpsLongitude(entry.data.d[1])) {
+ LOGF(ERROR) << "Setting gps longitude failed.";
+ return false;
+ }
+ if (!utils->SetGpsAltitude(entry.data.d[2])) {
+ LOGF(ERROR) << "Setting gps altitude failed.";
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD);
+ std::string method_str(reinterpret_cast<const char*>(entry.data.u8));
+ if (!utils->SetGpsProcessingMethod(method_str)) {
+ LOGF(ERROR) << "Setting gps processing method failed.";
+ return false;
+ }
+ }
+
+ if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP);
+ time_t timestamp = static_cast<time_t>(entry.data.i64[0]);
+ if (gmtime_r(×tamp, &time_info)) {
+ if (!utils->SetGpsTimestamp(time_info)) {
+ LOGF(ERROR) << "Setting gps timestamp failed.";
+ return false;
+ }
+ } else {
+ LOGF(ERROR) << "Time tranformation failed.";
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_ORIENTATION)) {
+ entry = metadata.find(ANDROID_JPEG_ORIENTATION);
+ if (!utils->SetOrientation(entry.data.i32[0])) {
+ LOGF(ERROR) << "Setting orientation failed.";
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
+ entry = metadata.find(ANDROID_JPEG_THUMBNAIL_SIZE);
+ if (entry.count < 2) {
+ LOGF(ERROR) << "Thumbnail size in metadata is not complete.";
+ return false;
+ }
+ int thumbnail_width = entry.data.i32[0];
+ int thumbnail_height = entry.data.i32[1];
+ if (thumbnail_width > 0 && thumbnail_height > 0) {
+ if (!utils->SetThumbnailSize(static_cast<uint16_t>(thumbnail_width),
+ static_cast<uint16_t>(thumbnail_height))) {
+ LOGF(ERROR) << "Setting thumbnail size failed.";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace arc
diff --git a/modules/camera/3_4/arc/image_processor.h b/modules/camera/3_4/arc/image_processor.h
new file mode 100644
index 0000000..323680a
--- /dev/null
+++ b/modules/camera/3_4/arc/image_processor.h
@@ -0,0 +1,48 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef HAL_USB_IMAGE_PROCESSOR_H_
+#define HAL_USB_IMAGE_PROCESSOR_H_
+
+#include <string>
+
+// FourCC pixel formats (defined as V4L2_PIX_FMT_*).
+#include <linux/videodev2.h>
+// Declarations of HAL_PIXEL_FORMAT_XXX.
+#include <system/graphics.h>
+
+#include <camera/CameraMetadata.h>
+#include "frame_buffer.h"
+
+namespace arc {
+
+// V4L2_PIX_FMT_YVU420(YV12) in ImageProcessor has alignment requirement.
+// The stride of Y, U, and V planes should a multiple of 16 pixels.
+struct ImageProcessor {
+ // Calculate the output buffer size when converting to the specified pixel
+ // format. |fourcc| is defined as V4L2_PIX_FMT_* in linux/videodev2.h.
+ // Return 0 on error.
+ static size_t GetConvertedSize(int fourcc, uint32_t width, uint32_t height);
+
+ // Return whether this class supports the provided conversion.
+ static bool SupportsConversion(uint32_t from_fourcc, uint32_t to_fourcc);
+
+ // Convert format from |in_frame.fourcc| to |out_frame->fourcc|. Caller should
+ // fill |data|, |buffer_size|, |width|, and |height| of |out_frame|. The
+ // function will fill |out_frame->data_size|. Return non-zero error code on
+ // failure; return 0 on success.
+ static int ConvertFormat(const android::CameraMetadata& metadata,
+ const FrameBuffer& in_frame, FrameBuffer* out_frame);
+
+ // Scale image size according to |in_frame| and |out_frame|. Only support
+ // V4L2_PIX_FMT_YUV420 format. Caller should fill |data|, |width|, |height|,
+ // and |buffer_size| of |out_frame|. The function will fill |data_size| and
+ // |fourcc| of |out_frame|.
+ static int Scale(const FrameBuffer& in_frame, FrameBuffer* out_frame);
+};
+
+} // namespace arc
+
+#endif // HAL_USB_IMAGE_PROCESSOR_H_
diff --git a/modules/camera/3_4/arc/jpeg_compressor.cpp b/modules/camera/3_4/arc/jpeg_compressor.cpp
new file mode 100644
index 0000000..7c61b40
--- /dev/null
+++ b/modules/camera/3_4/arc/jpeg_compressor.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "arc/jpeg_compressor.h"
+
+#include <memory>
+
+#include <errno.h>
+
+#include "arc/common.h"
+
+namespace arc {
+
+// The destination manager that can access |result_buffer_| in JpegCompressor.
+struct destination_mgr {
+ public:
+ struct jpeg_destination_mgr mgr;
+ JpegCompressor* compressor;
+};
+
+JpegCompressor::JpegCompressor() {}
+
+JpegCompressor::~JpegCompressor() {}
+
+bool JpegCompressor::CompressImage(const void* image, int width, int height,
+ int quality, const void* app1Buffer,
+ unsigned int app1Size) {
+ if (width % 8 != 0 || height % 2 != 0) {
+ LOGF(ERROR) << "Image size can not be handled: " << width << "x" << height;
+ return false;
+ }
+
+ result_buffer_.clear();
+ if (!Encode(image, width, height, quality, app1Buffer, app1Size)) {
+ return false;
+ }
+ LOGF(INFO) << "Compressed JPEG: " << (width * height * 12) / 8 << "[" << width
+ << "x" << height << "] -> " << result_buffer_.size() << " bytes";
+ return true;
+}
+
+const void* JpegCompressor::GetCompressedImagePtr() {
+ return result_buffer_.data();
+}
+
+size_t JpegCompressor::GetCompressedImageSize() {
+ return result_buffer_.size();
+}
+
+void JpegCompressor::InitDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->compressor->result_buffer_;
+ buffer.resize(kBlockSize);
+ dest->mgr.next_output_byte = &buffer[0];
+ dest->mgr.free_in_buffer = buffer.size();
+}
+
+boolean JpegCompressor::EmptyOutputBuffer(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->compressor->result_buffer_;
+ size_t oldsize = buffer.size();
+ buffer.resize(oldsize + kBlockSize);
+ dest->mgr.next_output_byte = &buffer[oldsize];
+ dest->mgr.free_in_buffer = kBlockSize;
+ return true;
+}
+
+void JpegCompressor::TerminateDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->compressor->result_buffer_;
+ buffer.resize(buffer.size() - dest->mgr.free_in_buffer);
+}
+
+void JpegCompressor::OutputErrorMessage(j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ LOGF(ERROR) << buffer;
+}
+
+bool JpegCompressor::Encode(const void* inYuv, int width, int height,
+ int jpegQuality, const void* app1Buffer,
+ unsigned int app1Size) {
+ jpeg_compress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ // Override output_message() to print error log with ALOGE().
+ cinfo.err->output_message = &OutputErrorMessage;
+ jpeg_create_compress(&cinfo);
+ SetJpegDestination(&cinfo);
+
+ SetJpegCompressStruct(width, height, jpegQuality, &cinfo);
+ jpeg_start_compress(&cinfo, TRUE);
+
+ if (app1Buffer != nullptr && app1Size > 0) {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
+ static_cast<const JOCTET*>(app1Buffer), app1Size);
+ }
+
+ if (!Compress(&cinfo, static_cast<const uint8_t*>(inYuv))) {
+ return false;
+ }
+ jpeg_finish_compress(&cinfo);
+ return true;
+}
+
+void JpegCompressor::SetJpegDestination(jpeg_compress_struct* cinfo) {
+ destination_mgr* dest =
+ static_cast<struct destination_mgr*>((*cinfo->mem->alloc_small)(
+ (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(destination_mgr)));
+ dest->compressor = this;
+ dest->mgr.init_destination = &InitDestination;
+ dest->mgr.empty_output_buffer = &EmptyOutputBuffer;
+ dest->mgr.term_destination = &TerminateDestination;
+ cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest);
+}
+
+void JpegCompressor::SetJpegCompressStruct(int width, int height, int quality,
+ jpeg_compress_struct* cinfo) {
+ cinfo->image_width = width;
+ cinfo->image_height = height;
+ cinfo->input_components = 3;
+ cinfo->in_color_space = JCS_YCbCr;
+ jpeg_set_defaults(cinfo);
+
+ jpeg_set_quality(cinfo, quality, TRUE);
+ jpeg_set_colorspace(cinfo, JCS_YCbCr);
+ cinfo->raw_data_in = TRUE;
+ cinfo->dct_method = JDCT_IFAST;
+
+ // Configure sampling factors. The sampling factor is JPEG subsampling 420
+ // because the source format is YUV420.
+ 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;
+}
+
+bool JpegCompressor::Compress(jpeg_compress_struct* cinfo, const uint8_t* yuv) {
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPROW cb[kCompressBatchSize / 2];
+ JSAMPROW cr[kCompressBatchSize / 2];
+ JSAMPARRAY planes[3]{y, cb, cr};
+
+ size_t y_plane_size = cinfo->image_width * cinfo->image_height;
+ size_t uv_plane_size = y_plane_size / 4;
+ uint8_t* y_plane = const_cast<uint8_t*>(yuv);
+ uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size);
+ uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->next_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ // cb, cr only have half scanlines
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ size_t scanline = cinfo->next_scanline / 2 + i;
+ if (scanline < cinfo->image_height / 2) {
+ int offset = scanline * (cinfo->image_width / 2);
+ cb[i] = u_plane + offset;
+ cr[i] = v_plane + offset;
+ } else {
+ cb[i] = cr[i] = empty.get();
+ }
+ }
+
+ int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize) {
+ LOGF(ERROR) << "Number of processed lines does not equal input lines.";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace arc
diff --git a/modules/camera/3_4/arc/jpeg_compressor.h b/modules/camera/3_4/arc/jpeg_compressor.h
new file mode 100644
index 0000000..378f3cd
--- /dev/null
+++ b/modules/camera/3_4/arc/jpeg_compressor.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef INCLUDE_ARC_JPEG_COMPRESSOR_H_
+#define INCLUDE_ARC_JPEG_COMPRESSOR_H_
+
+// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
+#include <cstdio>
+#include <string>
+#include <vector>
+
+extern "C" {
+#include <jerror.h>
+#include <jpeglib.h>
+}
+
+namespace arc {
+
+// Encapsulates a converter from YU12 to JPEG format. This class is not
+// thread-safe.
+class JpegCompressor {
+ public:
+ JpegCompressor();
+ ~JpegCompressor();
+
+ // Compresses YU12 image to JPEG format. After calling this method, call
+ // GetCompressedImagePtr() to get the image. |quality| is the resulted jpeg
+ // image quality. It ranges from 1 (poorest quality) to 100 (highest quality).
+ // |app1Buffer| is the buffer of APP1 segment (exif) which will be added to
+ // the compressed image. Returns false if errors occur during compression.
+ bool CompressImage(const void* image, int width, int height, int quality,
+ const void* app1Buffer, unsigned int app1Size);
+
+ // Returns the compressed JPEG buffer pointer. This method must be called only
+ // after calling CompressImage().
+ const void* GetCompressedImagePtr();
+
+ // Returns the compressed JPEG buffer size. This method must be called only
+ // after calling CompressImage().
+ size_t GetCompressedImageSize();
+
+ private:
+ // InitDestination(), EmptyOutputBuffer() and TerminateDestination() are
+ // callback functions to be passed into jpeg library.
+ static void InitDestination(j_compress_ptr cinfo);
+ static boolean EmptyOutputBuffer(j_compress_ptr cinfo);
+ static void TerminateDestination(j_compress_ptr cinfo);
+ static void OutputErrorMessage(j_common_ptr cinfo);
+
+ // Returns false if errors occur.
+ bool Encode(const void* inYuv, int width, int height, int jpegQuality,
+ const void* app1Buffer, unsigned int app1Size);
+ void SetJpegDestination(jpeg_compress_struct* cinfo);
+ void SetJpegCompressStruct(int width, int height, int quality,
+ jpeg_compress_struct* cinfo);
+ // Returns false if errors occur.
+ bool Compress(jpeg_compress_struct* cinfo, const uint8_t* yuv);
+
+ // The block size for encoded jpeg image buffer.
+ static const int kBlockSize = 16384;
+ // Process 16 lines of Y and 16 lines of U/V each time.
+ // We must pass at least 16 scanlines according to libjpeg documentation.
+ static const int kCompressBatchSize = 16;
+
+ // The buffer that holds the compressed result.
+ std::vector<JOCTET> result_buffer_;
+};
+
+} // namespace arc
+
+#endif // INCLUDE_ARC_JPEG_COMPRESSOR_H_
diff --git a/modules/camera/3_4/format_metadata_factory.cpp b/modules/camera/3_4/format_metadata_factory.cpp
index a08f9a8..db03678 100644
--- a/modules/camera/3_4/format_metadata_factory.cpp
+++ b/modules/camera/3_4/format_metadata_factory.cpp
@@ -16,6 +16,7 @@
#include "format_metadata_factory.h"
+#include "arc/image_processor.h"
#include "metadata/array_vector.h"
#include "metadata/partial_metadata_factory.h"
#include "metadata/property.h"
@@ -35,6 +36,7 @@
HAL_LOGE("Failed to get device formats.");
return res;
}
+
for (auto v4l2_format : v4l2_formats) {
int32_t hal_format = StreamFormat::V4L2ToHalPixelFormat(v4l2_format);
if (hal_format < 0) {
@@ -44,21 +46,17 @@
result_formats->insert(hal_format);
}
- // In addition to well-defined formats, there may be an
- // "Implementation Defined" format chosen by the HAL (in this
- // case what that means is managed by the StreamFormat class).
-
- // Get the V4L2 format for IMPLEMENTATION_DEFINED.
- int v4l2_format = StreamFormat::HalToV4L2PixelFormat(
- HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
- // If it's available, add IMPLEMENTATION_DEFINED to the result set.
- if (v4l2_format && v4l2_formats.count(v4l2_format) > 0) {
- result_formats->insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
- }
-
return 0;
}
+static int FpsRangesCompare(std::array<int32_t, 2> a,
+ std::array<int32_t, 2> b) {
+ if (a[1] == b[1]) {
+ return a[0] > b[0];
+ }
+ return a[1] > b[1];
+}
+
int AddFormatComponents(
std::shared_ptr<V4L2Wrapper> device,
std::insert_iterator<PartialMetadataSet> insertion_point) {
@@ -71,19 +69,39 @@
return res;
}
- // Requirements check: need to support YCbCr_420_888, JPEG,
- // and "Implementation Defined".
+ std::set<int32_t> unsupported_hal_formats;
if (hal_formats.find(HAL_PIXEL_FORMAT_YCbCr_420_888) == hal_formats.end()) {
- HAL_LOGE("YCbCr_420_888 not supported by device.");
- return -ENODEV;
- } else if (hal_formats.find(HAL_PIXEL_FORMAT_BLOB) == hal_formats.end()) {
- HAL_LOGE("JPEG not supported by device.");
- return -ENODEV;
- } else if (hal_formats.find(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) ==
- hal_formats.end()) {
- HAL_LOGE("HAL implementation defined format not supported by device.");
- return -ENODEV;
+ HAL_LOGW("YCbCr_420_888 (0x%x) not directly supported by device.",
+ HAL_PIXEL_FORMAT_YCbCr_420_888);
+ hal_formats.insert(HAL_PIXEL_FORMAT_YCbCr_420_888);
+ unsupported_hal_formats.insert(HAL_PIXEL_FORMAT_YCbCr_420_888);
}
+ if (hal_formats.find(HAL_PIXEL_FORMAT_BLOB) == hal_formats.end()) {
+ HAL_LOGW("JPEG (0x%x) not directly supported by device.",
+ HAL_PIXEL_FORMAT_BLOB);
+ hal_formats.insert(HAL_PIXEL_FORMAT_BLOB);
+ unsupported_hal_formats.insert(HAL_PIXEL_FORMAT_BLOB);
+ }
+
+ // As hal_formats is populated by reading and converting V4L2 formats to the
+ // matching HAL formats, we will never see an implementation defined format in
+ // the list. We populate it ourselves and map it to a qualified format. If no
+ // qualified formats exist, this will be the first available format.
+ hal_formats.insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ unsupported_hal_formats.insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+
+ // Qualified formats are the set of formats supported by this camera that the
+ // image processor can translate into the YU12 format. We additionally check
+ // that the conversion from YU12 to the desired hal format is supported.
+ std::vector<uint32_t> qualified_formats;
+ res = device->GetQualifiedFormats(&qualified_formats);
+ if (res && unsupported_hal_formats.size() > 1) {
+ HAL_LOGE(
+ "Failed to retrieve qualified formats, cannot perform conversions.");
+ return res;
+ }
+
+ HAL_LOGI("Supports %d qualified formats.", qualified_formats.size());
// Find sizes and frame/stall durations for all formats.
// We also want to find the smallest max frame duration amongst all formats,
@@ -96,7 +114,7 @@
// Stall durations are {format, width, height, duration} (duration in ns).
ArrayVector<int64_t, 4> stall_durations;
int64_t min_max_frame_duration = std::numeric_limits<int64_t>::max();
- int64_t max_min_frame_duration_yuv = std::numeric_limits<int64_t>::min();
+ std::vector<std::array<int32_t, 2>> fps_ranges;
for (auto hal_format : hal_formats) {
// Get the corresponding V4L2 format.
uint32_t v4l2_format = StreamFormat::HalToV4L2PixelFormat(hal_format);
@@ -105,6 +123,42 @@
// came from translating a bunch of V4L2 formats above.
HAL_LOGE("Couldn't find V4L2 format for HAL format %d", hal_format);
return -ENODEV;
+ } else if (unsupported_hal_formats.find(hal_format) !=
+ unsupported_hal_formats.end()) {
+ if (hal_format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ if (qualified_formats.size() != 0) {
+ v4l2_format = qualified_formats[0];
+ } else if (unsupported_hal_formats.size() == 1) {
+ v4l2_format = StreamFormat::HalToV4L2PixelFormat(
+ HAL_PIXEL_FORMAT_YCbCr_420_888);
+ } else {
+ // No-op. If there are no qualified formats, and implementation
+ // defined is not the only unsupported format, then other unsupported
+ // formats will throw an error.
+ }
+ HAL_LOGW(
+ "Implementation-defined format is set to V4L2 pixel format 0x%x",
+ v4l2_format);
+ } else if (qualified_formats.size() == 0) {
+ HAL_LOGE(
+ "Camera does not support required format: 0x%x, and there are no "
+ "qualified"
+ "formats to transform from.",
+ hal_format);
+ return -ENODEV;
+ } else if (!arc::ImageProcessor::SupportsConversion(V4L2_PIX_FMT_YUV420,
+ v4l2_format)) {
+ HAL_LOGE(
+ "The image processor does not support conversion to required "
+ "format: 0x%x",
+ hal_format);
+ return -ENODEV;
+ } else {
+ v4l2_format = qualified_formats[0];
+ HAL_LOGW(
+ "Hal format 0x%x will be converted from V4L2 pixel format 0x%x",
+ hal_format, v4l2_format);
+ }
}
// Get the available sizes for this format.
@@ -160,39 +214,25 @@
if (size_max_frame_duration < min_max_frame_duration) {
min_max_frame_duration = size_max_frame_duration;
}
- // We only care about the largest min frame duration
- // (smallest max frame rate) for YUV sizes.
- if (hal_format == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
- size_min_frame_duration > max_min_frame_duration_yuv) {
- max_min_frame_duration_yuv = size_min_frame_duration;
+ // ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES will contain all
+ // the fps ranges for YUV_420_888 only since YUV_420_888 format is
+ // the default camera format by Android.
+ if (hal_format == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+ // Convert from frame durations measured in ns.
+ // Min, max fps supported by all YUV formats.
+ const int32_t min_fps = 1000000000 / size_max_frame_duration;
+ const int32_t max_fps = 1000000000 / size_min_frame_duration;
+ if (std::find(fps_ranges.begin(), fps_ranges.end(),
+ std::array<int32_t, 2>{min_fps, max_fps}) ==
+ fps_ranges.end()) {
+ fps_ranges.push_back({min_fps, max_fps});
+ }
}
}
}
- // Convert from frame durations measured in ns.
- // Min fps supported by all formats.
- int32_t min_fps = 1000000000 / min_max_frame_duration;
- if (min_fps > 15) {
- HAL_LOGE("Minimum FPS %d is larger than HAL max allowable value of 15",
- min_fps);
- return -ENODEV;
- }
- // Max fps supported by all YUV formats.
- int32_t max_yuv_fps = 1000000000 / max_min_frame_duration_yuv;
- // ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES should be at minimum
- // {mi, ma}, {ma, ma} where mi and ma are min and max frame rates for
- // YUV_420_888. Min should be at most 15.
- std::vector<std::array<int32_t, 2>> fps_ranges;
- fps_ranges.push_back({{min_fps, max_yuv_fps}});
-
- std::array<int32_t, 2> video_fps_range;
- int32_t video_fps = 30;
- if (video_fps >= max_yuv_fps) {
- video_fps_range = {{max_yuv_fps, max_yuv_fps}};
- } else {
- video_fps_range = {{video_fps, video_fps}};
- }
- fps_ranges.push_back(video_fps_range);
+ // Sort fps ranges in descending order.
+ std::sort(fps_ranges.begin(), fps_ranges.end(), FpsRangesCompare);
// Construct the metadata components.
insertion_point = std::make_unique<Property<ArrayVector<int32_t, 4>>>(
@@ -208,10 +248,9 @@
// TODO(b/31019725): This should probably not be a NoEffect control.
insertion_point = NoEffectMenuControl<std::array<int32_t, 2>>(
ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
- ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
- fps_ranges,
- {{CAMERA3_TEMPLATE_VIDEO_RECORD, video_fps_range},
- {OTHER_TEMPLATES, fps_ranges[0]}});
+ ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fps_ranges,
+ {{CAMERA3_TEMPLATE_VIDEO_RECORD, fps_ranges.front()},
+ {OTHER_TEMPLATES, fps_ranges.back()}});
return 0;
}
diff --git a/modules/camera/3_4/format_metadata_factory.h b/modules/camera/3_4/format_metadata_factory.h
index 4cf5952..23c1777 100644
--- a/modules/camera/3_4/format_metadata_factory.h
+++ b/modules/camera/3_4/format_metadata_factory.h
@@ -17,6 +17,7 @@
#ifndef V4L2_CAMERA_HAL_FORMAT_METADATA_FACTORY_H_
#define V4L2_CAMERA_HAL_FORMAT_METADATA_FACTORY_H_
+#include <algorithm>
#include <iterator>
#include <memory>
#include <set>
diff --git a/modules/camera/3_4/format_metadata_factory_test.cpp b/modules/camera/3_4/format_metadata_factory_test.cpp
index d37b09f..fe5d67f 100644
--- a/modules/camera/3_4/format_metadata_factory_test.cpp
+++ b/modules/camera/3_4/format_metadata_factory_test.cpp
@@ -46,10 +46,12 @@
};
TEST_F(FormatMetadataFactoryTest, GetFormatMetadata) {
- std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420};
+ std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YUYV};
std::map<uint32_t, std::set<std::array<int32_t, 2>>> sizes{
{V4L2_PIX_FMT_JPEG, {{{10, 20}}, {{30, 60}}, {{120, 240}}}},
- {V4L2_PIX_FMT_YUV420, {{{1, 2}}, {{3, 6}}, {{12, 24}}}}};
+ {V4L2_PIX_FMT_YUV420, {{{1, 2}}, {{3, 6}}, {{12, 24}}}},
+ {V4L2_PIX_FMT_YUYV, {{{20, 40}}, {{80, 160}}, {{320, 640}}}}};
// These need to be on the correct order of magnitude,
// as there is a check for min fps > 15.
std::map<uint32_t, std::map<std::array<int32_t, 2>, std::array<int64_t, 2>>>
@@ -60,19 +62,24 @@
{V4L2_PIX_FMT_YUV420,
{{{{1, 2}}, {{10000000000, 20000000000}}},
{{{3, 6}}, {{11000000000, 21000000000}}},
- {{{12, 24}}, {{10500000000, 19000000000}}}}}};
+ {{{12, 24}}, {{10500000000, 19000000000}}}}},
+ {V4L2_PIX_FMT_YUYV,
+ {{{{20, 40}}, {{11000000000, 22000000000}}},
+ {{{80, 160}}, {{13000000000, 25000000000}}},
+ {{{320, 640}}, {{10100000000, 19000000000}}}}}};
+ // The camera must report at least one qualified format.
+ std::vector<uint32_t> qualified_formats = {V4L2_PIX_FMT_YUYV};
// Device must support IMPLEMENTATION_DEFINED (as well as JPEG & YUV).
- // Just duplicate the values from another format.
- uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat(
- HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
- formats.insert(imp_defined_format);
- sizes[imp_defined_format] = sizes[V4L2_PIX_FMT_YUV420];
- durations[imp_defined_format] = durations[V4L2_PIX_FMT_YUV420];
+ // For USB cameras, we assume that this format will not be present, and it
+ // will default to a qualified format or one of the other required formats.
EXPECT_CALL(*mock_device_, GetFormats(_))
.WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
+ EXPECT_CALL(*mock_device_, GetQualifiedFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(qualified_formats), Return(0)));
+
for (auto format : formats) {
std::set<std::array<int32_t, 2>> format_sizes = sizes[format];
EXPECT_CALL(*mock_device_, GetFormatFrameSizes(format, _))
@@ -94,7 +101,7 @@
for (auto& component : components) {
android::CameraMetadata metadata;
component->PopulateStaticFields(&metadata);
- ASSERT_EQ(metadata.entryCount(), 1);
+ ASSERT_EQ(metadata.entryCount(), 1u);
int32_t tag = component->StaticTags()[0];
switch (tag) {
case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS: // Fall through.
@@ -119,39 +126,71 @@
}
}
-TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingJpeg) {
- uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat(
- HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
- std::set<uint32_t> formats{V4L2_PIX_FMT_YUV420, imp_defined_format};
- EXPECT_CALL(*mock_device_, GetFormats(_))
- .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
- PartialMetadataSet components;
- ASSERT_EQ(AddFormatComponents(mock_device_,
- std::inserter(components, components.end())),
- -ENODEV);
-}
+TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingRequired) {
+ std::set<uint32_t> formats{V4L2_PIX_FMT_YUYV};
+ std::map<uint32_t, std::set<std::array<int32_t, 2>>> sizes{
+ {V4L2_PIX_FMT_YUYV, {{{640, 480}}, {{320, 240}}}}};
+ std::map<uint32_t, std::map<std::array<int32_t, 2>, std::array<int64_t, 2>>>
+ durations{{V4L2_PIX_FMT_YUYV,
+ {{{{640, 480}}, {{100000000, 200000000}}},
+ {{{320, 240}}, {{100000000, 200000000}}}}}};
-TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingYuv) {
- uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat(
- HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
- std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, imp_defined_format};
EXPECT_CALL(*mock_device_, GetFormats(_))
.WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
- PartialMetadataSet components;
- ASSERT_EQ(AddFormatComponents(mock_device_,
- std::inserter(components, components.end())),
- -ENODEV);
-}
+ // If a qualified format is present, we expect that required fields are
+ // populated as if they are supported.
+ std::vector<uint32_t> qualified_formats = {V4L2_PIX_FMT_YUYV};
-TEST_F(FormatMetadataFactoryTest,
- GetFormatMetadataMissingImplementationDefined) {
- std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420};
- EXPECT_CALL(*mock_device_, GetFormats(_))
- .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
+ EXPECT_CALL(*mock_device_, GetQualifiedFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(qualified_formats), Return(0)));
+
+ for (auto format : formats) {
+ std::set<std::array<int32_t, 2>> format_sizes = sizes[format];
+ EXPECT_CALL(*mock_device_, GetFormatFrameSizes(format, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(format_sizes), Return(0)));
+ for (auto size : format_sizes) {
+ EXPECT_CALL(*mock_device_, GetFormatFrameDurationRange(format, size, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<2>(durations[format][size]), Return(0)));
+ }
+ }
+
+ // Check that all required formats are present.
PartialMetadataSet components;
ASSERT_EQ(AddFormatComponents(mock_device_,
std::inserter(components, components.end())),
- -ENODEV);
+ 0);
+
+ std::vector<std::array<int32_t, 2>> target_fps_ranges{{{5, 10}}, {{10, 10}}};
+ for (auto& component : components) {
+ android::CameraMetadata metadata;
+ component->PopulateStaticFields(&metadata);
+ ASSERT_EQ(metadata.entryCount(), 1u);
+ int32_t tag = component->StaticTags()[0];
+ switch (tag) {
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS: // Fall through.
+ case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS: // Fall through.
+ case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS: // Fall through.
+ // Two sizes per format, four elements per config.
+ // # formats + 3 for YUV420, JPEG, IMPLEMENTATION_DEFINED.
+ ExpectMetadataTagCount(metadata, tag, (formats.size() + 3) * 2 * 4);
+ break;
+ case ANDROID_SENSOR_INFO_MAX_FRAME_DURATION:
+ // The lowest max duration from above.
+ ExpectMetadataEq(metadata, tag, 200000000);
+ break;
+ case ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES:
+ // 2 ranges ({min, max} and {max, max}), each with a min and max.
+ ExpectMetadataTagCount(metadata, tag, 4);
+ ExpectMetadataEq(metadata, tag, target_fps_ranges);
+ break;
+ default:
+ FAIL() << "Unexpected component created.";
+ break;
+ }
+ }
}
} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/control_test.cpp b/modules/camera/3_4/metadata/control_test.cpp
index f76376c..6284330 100644
--- a/modules/camera/3_4/metadata/control_test.cpp
+++ b/modules/camera/3_4/metadata/control_test.cpp
@@ -64,15 +64,15 @@
virtual void ExpectTags() {
if (use_options_ && report_options_) {
- ASSERT_EQ(control_->StaticTags().size(), 1);
+ ASSERT_EQ(control_->StaticTags().size(), 1u);
EXPECT_EQ(control_->StaticTags()[0], options_tag_);
} else {
EXPECT_TRUE(control_->StaticTags().empty());
}
// Controls use the same delgate, and thus tag, for getting and setting.
- ASSERT_EQ(control_->ControlTags().size(), 1);
+ ASSERT_EQ(control_->ControlTags().size(), 1u);
EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
- ASSERT_EQ(control_->DynamicTags().size(), 1);
+ ASSERT_EQ(control_->DynamicTags().size(), 1u);
EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
}
@@ -81,10 +81,10 @@
android::CameraMetadata metadata;
ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
if (use_options_ && report_options_) {
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
ExpectMetadataEq(metadata, options_tag_, options);
} else {
- EXPECT_EQ(metadata.entryCount(), 0);
+ EXPECT_EQ(metadata.entryCount(), 0u);
// Shouldn't be expecting any options.
EXPECT_TRUE(options.empty());
}
@@ -93,7 +93,7 @@
virtual void ExpectValue(uint8_t value) {
android::CameraMetadata metadata;
ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
ExpectMetadataEq(metadata, delegate_tag_, value);
}
diff --git a/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp b/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp
index 9ca3a38..3537ed2 100644
--- a/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp
+++ b/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp
@@ -44,11 +44,11 @@
}
virtual void ExpectControlTags() {
- ASSERT_EQ(control_->StaticTags().size(), 1);
+ ASSERT_EQ(control_->StaticTags().size(), 1u);
EXPECT_EQ(control_->StaticTags()[0], options_tag_);
- ASSERT_EQ(control_->ControlTags().size(), 1);
+ ASSERT_EQ(control_->ControlTags().size(), 1u);
EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
- ASSERT_EQ(control_->DynamicTags().size(), 1);
+ ASSERT_EQ(control_->DynamicTags().size(), 1u);
EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
}
@@ -56,14 +56,14 @@
// Options should be available.
android::CameraMetadata metadata;
ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
ExpectMetadataEq(metadata, options_tag_, options);
}
virtual void ExpectControlValue(uint8_t value) {
android::CameraMetadata metadata;
ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
ExpectMetadataEq(metadata, delegate_tag_, value);
}
@@ -84,14 +84,14 @@
uint8_t value = 13;
std::unique_ptr<State<uint8_t>> state = FixedState(delegate_tag_, value);
- ASSERT_EQ(state->StaticTags().size(), 0);
- ASSERT_EQ(state->ControlTags().size(), 0);
- ASSERT_EQ(state->DynamicTags().size(), 1);
+ ASSERT_EQ(state->StaticTags().size(), 0u);
+ ASSERT_EQ(state->ControlTags().size(), 0u);
+ ASSERT_EQ(state->DynamicTags().size(), 1u);
EXPECT_EQ(state->DynamicTags()[0], delegate_tag_);
android::CameraMetadata metadata;
ASSERT_EQ(state->PopulateDynamicFields(&metadata), 0);
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
ExpectMetadataEq(metadata, delegate_tag_, value);
}
diff --git a/modules/camera/3_4/metadata/property_test.cpp b/modules/camera/3_4/metadata/property_test.cpp
index 8e947ea..80f8eb8 100644
--- a/modules/camera/3_4/metadata/property_test.cpp
+++ b/modules/camera/3_4/metadata/property_test.cpp
@@ -49,9 +49,9 @@
Property<int32_t> property(int_tag_, 1);
// Should have only the single tag it was constructed with.
- EXPECT_EQ(property.ControlTags().size(), 0);
- EXPECT_EQ(property.DynamicTags().size(), 0);
- ASSERT_EQ(property.StaticTags().size(), 1);
+ EXPECT_EQ(property.ControlTags().size(), 0u);
+ EXPECT_EQ(property.DynamicTags().size(), 0u);
+ ASSERT_EQ(property.StaticTags().size(), 1u);
// The macro doesn't like the int_tag_ variable being passed in directly.
int32_t expected_tag = int_tag_;
EXPECT_EQ(property.StaticTags()[0], expected_tag);
@@ -68,7 +68,7 @@
// Check the results.
// Should only have added 1 entry.
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
// Should have added the right entry.
ExpectMetadataEq(metadata, int_tag_, data);
}
@@ -86,7 +86,7 @@
// Check the results.
// Should only have added 1 entry.
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
// Should have added the right entry.
ExpectMetadataEq(metadata, float_tag_, data);
}
@@ -102,7 +102,7 @@
// Check the results.
// Should only have added 1 entry.
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
// Should have added the right entry.
ExpectMetadataEq(metadata, float_tag_, data);
}
@@ -120,7 +120,7 @@
// Check the results.
// Should only have added 1 entry.
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
// Should have added the right entry.
ExpectMetadataEq(metadata, byte_tag_, data);
}
diff --git a/modules/camera/3_4/metadata/state_test.cpp b/modules/camera/3_4/metadata/state_test.cpp
index 5c308bc..ecc1d15 100644
--- a/modules/camera/3_4/metadata/state_test.cpp
+++ b/modules/camera/3_4/metadata/state_test.cpp
@@ -59,7 +59,7 @@
PrepareState();
EXPECT_TRUE(state_->StaticTags().empty());
EXPECT_TRUE(state_->ControlTags().empty());
- ASSERT_EQ(state_->DynamicTags().size(), 1);
+ ASSERT_EQ(state_->DynamicTags().size(), 1u);
EXPECT_EQ(state_->DynamicTags()[0], tag_);
}
@@ -79,7 +79,7 @@
android::CameraMetadata metadata;
ASSERT_EQ(state_->PopulateDynamicFields(&metadata), 0);
- EXPECT_EQ(metadata.entryCount(), 1);
+ EXPECT_EQ(metadata.entryCount(), 1u);
ExpectMetadataEq(metadata, tag_, expected);
}
diff --git a/modules/camera/3_4/metadata/test_common.h b/modules/camera/3_4/metadata/test_common.h
index 489990f..42e44f0 100644
--- a/modules/camera/3_4/metadata/test_common.h
+++ b/modules/camera/3_4/metadata/test_common.h
@@ -80,9 +80,9 @@
// Vector of arrays.
template <typename T, size_t N>
-static int ExpectMetadataEq(const android::CameraMetadata& metadata,
- int32_t tag,
- const std::vector<std::array<T, N>>& expected) {
+static void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag,
+ const std::vector<std::array<T, N>>& expected) {
// Convert to array vector so we know all the elements are contiguous.
ArrayVector<T, N> array_vector;
for (const auto& array : expected) {
diff --git a/modules/camera/3_4/stream_format.cpp b/modules/camera/3_4/stream_format.cpp
index 70900ab..5f35e42 100644
--- a/modules/camera/3_4/stream_format.cpp
+++ b/modules/camera/3_4/stream_format.cpp
@@ -20,10 +20,21 @@
#include <system/graphics.h>
+#include "arc/image_processor.h"
#include "common.h"
namespace v4l2_camera_hal {
+using arc::SupportedFormat;
+using arc::SupportedFormats;
+
+static const std::vector<uint32_t> GetSupportedFourCCs() {
+ // The preference of supported fourccs in the list is from high to low.
+ static const std::vector<uint32_t> kSupportedFourCCs = {V4L2_PIX_FMT_YUYV,
+ V4L2_PIX_FMT_MJPEG};
+ return kSupportedFourCCs;
+}
+
StreamFormat::StreamFormat(int format, uint32_t width, uint32_t height)
// TODO(b/30000211): multiplanar support.
: type_(V4L2_BUF_TYPE_VIDEO_CAPTURE),
@@ -42,6 +53,14 @@
bytes_per_line_(format.fmt.pix.bytesperline),
min_buffer_size_(format.fmt.pix.sizeimage) {}
+StreamFormat::StreamFormat(const arc::SupportedFormat& format)
+ : type_(V4L2_BUF_TYPE_VIDEO_CAPTURE),
+ v4l2_pixel_format_(format.fourcc),
+ width_(format.width),
+ height_(format.height),
+ bytes_per_line_(0),
+ min_buffer_size_(0) {}
+
void StreamFormat::FillFormatRequest(v4l2_format* format) const {
memset(format, 0, sizeof(*format));
format->type = type_;
@@ -57,7 +76,7 @@
case V4L2_PIX_FMT_JPEG:
return kFormatCategoryStalling;
case V4L2_PIX_FMT_YUV420: // Fall through.
- case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR32:
return kFormatCategoryNonStalling;
default:
// Note: currently no supported RAW formats.
@@ -79,47 +98,127 @@
int StreamFormat::V4L2ToHalPixelFormat(uint32_t v4l2_pixel_format) {
// Translate V4L2 format to HAL format.
- int hal_pixel_format = -1;
switch (v4l2_pixel_format) {
+ case V4L2_PIX_FMT_BGR32:
+ return HAL_PIXEL_FORMAT_RGBA_8888;
case V4L2_PIX_FMT_JPEG:
- hal_pixel_format = HAL_PIXEL_FORMAT_BLOB;
- break;
+ return HAL_PIXEL_FORMAT_BLOB;
+ case V4L2_PIX_FMT_NV21:
+ return HAL_PIXEL_FORMAT_YCrCb_420_SP;
case V4L2_PIX_FMT_YUV420:
- hal_pixel_format = HAL_PIXEL_FORMAT_YCbCr_420_888;
- break;
- case V4L2_PIX_FMT_RGB24:
- hal_pixel_format = HAL_PIXEL_FORMAT_RGBA_8888;
- break;
+ return HAL_PIXEL_FORMAT_YCbCr_420_888;
+ case V4L2_PIX_FMT_YUYV:
+ return HAL_PIXEL_FORMAT_YCbCr_422_I;
+ case V4L2_PIX_FMT_YVU420:
+ return HAL_PIXEL_FORMAT_YV12;
default:
// Unrecognized format.
HAL_LOGV("Unrecognized v4l2 pixel format %u", v4l2_pixel_format);
break;
}
- return hal_pixel_format;
+ return -1;
}
uint32_t StreamFormat::HalToV4L2PixelFormat(int hal_pixel_format) {
- // Translate HAL format to V4L2 format.
- uint32_t v4l2_pixel_format = 0;
switch (hal_pixel_format) {
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: // fall-through.
- case HAL_PIXEL_FORMAT_RGBA_8888:
- // Should be RGB32, but RPi doesn't support that.
- // For now we accept that the colors will be off.
- v4l2_pixel_format = V4L2_PIX_FMT_RGB24;
- break;
- case HAL_PIXEL_FORMAT_YCbCr_420_888:
- v4l2_pixel_format = V4L2_PIX_FMT_YUV420;
- break;
case HAL_PIXEL_FORMAT_BLOB:
- v4l2_pixel_format = V4L2_PIX_FMT_JPEG;
- break;
+ return V4L2_PIX_FMT_JPEG;
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: // Fall-through
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return V4L2_PIX_FMT_BGR32;
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ // This is a flexible YUV format that depends on platform. Different
+ // platform may have different format. It can be YVU420 or NV12. Now we
+ // return YVU420 first.
+ // TODO(): call drm_drv.get_fourcc() to get correct format.
+ return V4L2_PIX_FMT_YUV420;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ return V4L2_PIX_FMT_YUYV;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ return V4L2_PIX_FMT_NV21;
+ case HAL_PIXEL_FORMAT_YV12:
+ return V4L2_PIX_FMT_YVU420;
default:
- // Unrecognized format.
- HAL_LOGV("Unrecognized HAL pixel format %d", hal_pixel_format);
+ HAL_LOGV("Pixel format 0x%x is unsupported.", hal_pixel_format);
break;
}
- return v4l2_pixel_format;
+ return -1;
+}
+
+// Copy the qualified format into out_format and return true if there is a
+// proper and fitting format in the given format lists.
+bool StreamFormat::FindBestFitFormat(const SupportedFormats& supported_formats,
+ const SupportedFormats& qualified_formats,
+ uint32_t fourcc, uint32_t width,
+ uint32_t height,
+ SupportedFormat* out_format) {
+ // Match exact format and resolution if possible.
+ for (const auto& format : supported_formats) {
+ if (format.fourcc == fourcc && format.width == width &&
+ format.height == height) {
+ if (out_format != NULL) {
+ *out_format = format;
+ }
+ return true;
+ }
+ }
+ // All conversions will be done through CachedFrame for now, which will
+ // immediately convert the qualified format into YU12 (YUV420). We check
+ // here that the conversion between YU12 and |fourcc| is supported.
+ if (!arc::ImageProcessor::SupportsConversion(V4L2_PIX_FMT_YUV420, fourcc)) {
+ HAL_LOGE("Conversion between YU12 and 0x%x not supported.", fourcc);
+ return false;
+ }
+
+ // Choose the qualified format with a matching resolution.
+ for (const auto& format : qualified_formats) {
+ if (format.width == width && format.height == height) {
+ if (out_format != NULL) {
+ *out_format = format;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// Copy corresponding format into out_format and return true by matching
+// resolution |width|x|height| in |formats|.
+bool StreamFormat::FindFormatByResolution(const SupportedFormats& formats,
+ uint32_t width, uint32_t height,
+ SupportedFormat* out_format) {
+ for (const auto& format : formats) {
+ if (format.width == width && format.height == height) {
+ if (out_format != NULL) {
+ *out_format = format;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+SupportedFormats StreamFormat::GetQualifiedFormats(
+ const SupportedFormats& supported_formats) {
+ // The preference of supported fourccs in the list is from high to low.
+ const std::vector<uint32_t> supported_fourccs = GetSupportedFourCCs();
+ SupportedFormats qualified_formats;
+ for (const auto& supported_fourcc : supported_fourccs) {
+ for (const auto& supported_format : supported_formats) {
+ if (supported_format.fourcc != supported_fourcc) {
+ continue;
+ }
+
+ // Skip if |qualified_formats| already has the same resolution with a more
+ // preferred fourcc.
+ if (FindFormatByResolution(qualified_formats, supported_format.width,
+ supported_format.height, NULL)) {
+ continue;
+ }
+ qualified_formats.push_back(supported_format);
+ }
+ }
+ return qualified_formats;
}
} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/stream_format.h b/modules/camera/3_4/stream_format.h
index 66c5965..720e380 100644
--- a/modules/camera/3_4/stream_format.h
+++ b/modules/camera/3_4/stream_format.h
@@ -21,6 +21,7 @@
#include <linux/videodev2.h>
+#include "arc/common_types.h"
#include "common.h"
namespace v4l2_camera_hal {
@@ -36,6 +37,7 @@
public:
StreamFormat(int format, uint32_t width, uint32_t height);
StreamFormat(const v4l2_format& format);
+ StreamFormat(const arc::SupportedFormat& format);
virtual ~StreamFormat() = default;
// Only uint32_t members, use default generated copy and assign.
@@ -44,6 +46,9 @@
// Accessors.
inline uint32_t type() const { return type_; };
+ inline uint32_t width() const { return width_; };
+ inline uint32_t height() const { return height_; };
+ inline uint32_t v4l2_pixel_format() const { return v4l2_pixel_format_; }
inline uint32_t bytes_per_line() const { return bytes_per_line_; };
bool operator==(const StreamFormat& other) const;
@@ -55,6 +60,18 @@
// Returns -1 for unrecognized.
static int V4L2ToHalPixelFormat(uint32_t v4l2_pixel_format);
+ // ARC++ SupportedFormat Helpers
+ static bool FindBestFitFormat(const arc::SupportedFormats& supported_formats,
+ const arc::SupportedFormats& qualified_formats,
+ uint32_t fourcc, uint32_t width,
+ uint32_t height,
+ arc::SupportedFormat* out_format);
+ static bool FindFormatByResolution(const arc::SupportedFormats& formats,
+ uint32_t width, uint32_t height,
+ arc::SupportedFormat* out_format);
+ static arc::SupportedFormats GetQualifiedFormats(
+ const arc::SupportedFormats& supported_formats);
+
private:
uint32_t type_;
uint32_t v4l2_pixel_format_;
diff --git a/modules/camera/3_4/v4l2_camera.cpp b/modules/camera/3_4/v4l2_camera.cpp
index 22406c9..98b8062 100644
--- a/modules/camera/3_4/v4l2_camera.cpp
+++ b/modules/camera/3_4/v4l2_camera.cpp
@@ -123,16 +123,7 @@
int V4L2Camera::flushBuffers() {
HAL_LOG_ENTER();
-
- int res = device_->StreamOff();
-
- // This is not strictly necessary, but prevents a buildup of aborted
- // requests in the in_flight_ map. These should be cleared
- // whenever the stream is turned off.
- std::lock_guard<std::mutex> guard(in_flight_lock_);
- in_flight_.clear();
-
- return res;
+ return device_->StreamOff();
}
int V4L2Camera::initStaticInfo(android::CameraMetadata* out) {
@@ -262,55 +253,41 @@
}
// Actually enqueue the buffer for capture.
- {
- std::lock_guard<std::mutex> guard(in_flight_lock_);
-
- uint32_t index;
- res = device_->EnqueueBuffer(&request->output_buffers[0], &index);
- if (res) {
- HAL_LOGE("Device failed to enqueue buffer.");
- completeRequest(request, res);
- return true;
- }
-
- // Make sure the stream is on (no effect if already on).
- res = device_->StreamOn();
- if (res) {
- HAL_LOGE("Device failed to turn on stream.");
- // Don't really want to send an error for only the request here,
- // since this is a full device error.
- // TODO: Should trigger full flush.
- return true;
- }
-
- // Note: the request should be dequeued/flushed from the device
- // before removal from in_flight_.
- in_flight_.emplace(index, request);
- buffers_in_flight_.notify_one();
+ res = device_->EnqueueRequest(request);
+ if (res) {
+ HAL_LOGE("Device failed to enqueue buffer.");
+ completeRequest(request, res);
+ return true;
}
+ // Make sure the stream is on (no effect if already on).
+ res = device_->StreamOn();
+ if (res) {
+ HAL_LOGE("Device failed to turn on stream.");
+ // Don't really want to send an error for only the request here,
+ // since this is a full device error.
+ // TODO: Should trigger full flush.
+ return true;
+ }
+
+ std::unique_lock<std::mutex> lock(in_flight_lock_);
+ in_flight_buffer_count_++;
+ buffers_in_flight_.notify_one();
return true;
}
bool V4L2Camera::dequeueRequestBuffers() {
// Dequeue a buffer.
- uint32_t result_index;
+ std::shared_ptr<default_camera_hal::CaptureRequest> request;
int res;
{
- std::lock_guard<std::mutex> guard(in_flight_lock_);
- res = device_->DequeueBuffer(&result_index);
+ std::unique_lock<std::mutex> lock(in_flight_lock_);
+ res = device_->DequeueRequest(&request);
if (!res) {
- // Find the associated request and complete it.
- auto index_request = in_flight_.find(result_index);
- if (index_request != in_flight_.end()) {
- completeRequest(index_request->second, 0);
- in_flight_.erase(index_request);
- } else {
- HAL_LOGW(
- "Dequeued non in-flight buffer index %d. "
- "This buffer may have been flushed from the HAL but not the device.",
- index_request->first);
+ if (request) {
+ completeRequest(request, res);
+ in_flight_buffer_count_--;
}
return true;
}
@@ -320,7 +297,7 @@
// EAGAIN just means nothing to dequeue right now.
// Wait until something is available before looping again.
std::unique_lock<std::mutex> lock(in_flight_lock_);
- while (in_flight_.empty()) {
+ while (in_flight_buffer_count_ == 0) {
buffers_in_flight_.wait(lock);
}
} else {
@@ -350,10 +327,11 @@
std::lock_guard<std::mutex> guard(in_flight_lock_);
// The framework should be enforcing this, but doesn't hurt to be safe.
- if (!in_flight_.empty()) {
+ if (device_->GetInFlightBufferCount() != 0) {
HAL_LOGE("Can't set device format while frames are in flight.");
return -EINVAL;
}
+ in_flight_buffer_count_ = 0;
// stream_config should have been validated; assume at least 1 stream.
camera3_stream_t* stream = stream_config->streams[0];
diff --git a/modules/camera/3_4/v4l2_camera.h b/modules/camera/3_4/v4l2_camera.h
index 1db8d40..fc2adb3 100644
--- a/modules/camera/3_4/v4l2_camera.h
+++ b/modules/camera/3_4/v4l2_camera.h
@@ -99,9 +99,7 @@
std::queue<std::shared_ptr<default_camera_hal::CaptureRequest>>
request_queue_;
std::mutex in_flight_lock_;
- // Maps buffer index : request.
- std::map<uint32_t, std::shared_ptr<default_camera_hal::CaptureRequest>>
- in_flight_;
+ uint32_t in_flight_buffer_count_;
// Threads require holding an Android strong pointer.
android::sp<android::Thread> buffer_enqueuer_;
android::sp<android::Thread> buffer_dequeuer_;
diff --git a/modules/camera/3_4/v4l2_gralloc.cpp b/modules/camera/3_4/v4l2_gralloc.cpp
deleted file mode 100644
index 2fcef35..0000000
--- a/modules/camera/3_4/v4l2_gralloc.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * 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.
- */
-
-#include "v4l2_gralloc.h"
-
-#include <linux/videodev2.h>
-
-#include <cstdlib>
-
-#include <hardware/camera3.h>
-#include <hardware/gralloc.h>
-#include <system/graphics.h>
-
-#include "common.h"
-#include "stream_format.h"
-
-namespace v4l2_camera_hal {
-
-// Copy |height| lines from |src| to |dest|,
-// where |src| and |dest| may have different line lengths.
-void copyWithPadding(uint8_t* dest,
- const uint8_t* src,
- size_t dest_stride,
- size_t src_stride,
- size_t height) {
- size_t copy_stride = dest_stride;
- if (copy_stride > src_stride) {
- // Adding padding, not reducing. 0 out the extra memory.
- memset(dest, 0, src_stride * height);
- copy_stride = src_stride;
- }
- uint8_t* dest_line_start = dest;
- const uint8_t* src_line_start = src;
- for (size_t row = 0; row < height;
- ++row, dest_line_start += dest_stride, src_line_start += src_stride) {
- memcpy(dest_line_start, src_line_start, copy_stride);
- }
-}
-
-V4L2Gralloc* V4L2Gralloc::NewV4L2Gralloc() {
- // Initialize and check the gralloc module.
- const hw_module_t* module = nullptr;
- int res = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (res || !module) {
- HAL_LOGE("Couldn't get gralloc module.");
- return nullptr;
- }
- const gralloc_module_t* gralloc =
- reinterpret_cast<const gralloc_module_t*>(module);
-
- // This class only supports Gralloc v0, not Gralloc V1.
- if (gralloc->common.module_api_version > GRALLOC_MODULE_API_VERSION_0_3) {
- HAL_LOGE(
- "Invalid gralloc version %x. Only 0.3 (%x) "
- "and below are supported by this HAL.",
- gralloc->common.module_api_version,
- GRALLOC_MODULE_API_VERSION_0_3);
- return nullptr;
- }
-
- return new V4L2Gralloc(gralloc);
-}
-
-// Private. As checked by above factory, module will be non-null
-// and a supported version.
-V4L2Gralloc::V4L2Gralloc(const gralloc_module_t* module) : mModule(module) {}
-
-V4L2Gralloc::~V4L2Gralloc() {
- // Unlock buffers that are still locked.
- unlockAllBuffers();
-}
-
-int V4L2Gralloc::lock(const camera3_stream_buffer_t* camera_buffer,
- uint32_t bytes_per_line,
- v4l2_buffer* device_buffer) {
- // Lock the camera buffer (varies depending on if the buffer is YUV or not).
- std::unique_ptr<BufferData> buffer_data(
- new BufferData{camera_buffer, nullptr, bytes_per_line});
- buffer_handle_t buffer = *camera_buffer->buffer;
- void* data;
- camera3_stream_t* stream = camera_buffer->stream;
- int ret = 0;
- switch (StreamFormat::HalToV4L2PixelFormat(stream->format)) {
- // TODO(b/30119452): support more YCbCr formats.
- case V4L2_PIX_FMT_YUV420:
- android_ycbcr yuv_data;
- ret = mModule->lock_ycbcr(mModule,
- buffer,
- stream->usage,
- 0,
- 0,
- stream->width,
- stream->height,
- &yuv_data);
- if (ret) {
- HAL_LOGE("Failed to lock ycbcr buffer: %d", ret);
- return ret;
- }
-
- // Check if gralloc format matches v4l2 format
- // (same padding, not interleaved, contiguous).
- if (yuv_data.ystride == bytes_per_line &&
- yuv_data.cstride == bytes_per_line / 2 && yuv_data.chroma_step == 1 &&
- (reinterpret_cast<uint8_t*>(yuv_data.cb) ==
- reinterpret_cast<uint8_t*>(yuv_data.y) +
- (stream->height * yuv_data.ystride)) &&
- (reinterpret_cast<uint8_t*>(yuv_data.cr) ==
- reinterpret_cast<uint8_t*>(yuv_data.cb) +
- (stream->height / 2 * yuv_data.cstride))) {
- // If so, great, point to the beginning.
- data = yuv_data.y;
- } else {
- // If not, allocate a contiguous buffer of appropriate size
- // (to be transformed back upon unlock).
- data = new uint8_t[device_buffer->length];
- // Make a dynamically-allocated copy of yuv_data,
- // since it will be needed at transform time.
- buffer_data->transform_dest.reset(new android_ycbcr(yuv_data));
- }
- break;
- case V4L2_PIX_FMT_JPEG:
- // Jpeg buffers are just contiguous blobs; lock length * 1.
- ret = mModule->lock(mModule,
- buffer,
- stream->usage,
- 0,
- 0,
- device_buffer->length,
- 1,
- &data);
- if (ret) {
- HAL_LOGE("Failed to lock jpeg buffer: %d", ret);
- return ret;
- }
- break;
- case V4L2_PIX_FMT_RGB24: // Fall-through.
- case V4L2_PIX_FMT_BGR32: // Fall-through.
- case V4L2_PIX_FMT_RGB32:
- // RGB formats have nice agreed upon representation. Unless using android
- // flex formats.
- ret = mModule->lock(mModule,
- buffer,
- stream->usage,
- 0,
- 0,
- stream->width,
- stream->height,
- &data);
- if (ret) {
- HAL_LOGE("Failed to lock RGB buffer: %d", ret);
- return ret;
- }
- break;
- default:
- return -EINVAL;
- }
-
- if (!data) {
- ALOGE("Gralloc lock returned null ptr");
- return -ENODEV;
- }
-
- // Set up the device buffer.
- static_assert(sizeof(unsigned long) >= sizeof(void*),
- "void* must be able to fit in the v4l2_buffer m.userptr "
- "field (unsigned long) for this code to work");
- device_buffer->m.userptr = reinterpret_cast<unsigned long>(data);
-
- // Note the mapping of data:buffer info for when unlock is called.
- mBufferMap.emplace(data, buffer_data.release());
-
- return 0;
-}
-
-int V4L2Gralloc::unlock(const v4l2_buffer* device_buffer) {
- // TODO(b/30000211): support multi-planar data (video_capture_mplane).
- if (device_buffer->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- return -EINVAL;
- }
-
- void* data = reinterpret_cast<void*>(device_buffer->m.userptr);
-
- // Find and pop the matching entry in the map.
- auto map_entry = mBufferMap.find(data);
- if (map_entry == mBufferMap.end()) {
- HAL_LOGE("No matching buffer for data at %p", data);
- return -EINVAL;
- }
- std::unique_ptr<const BufferData> buffer_data(map_entry->second);
- mBufferMap.erase(map_entry);
-
- const camera3_stream_buffer_t* camera_buffer = buffer_data->camera_buffer;
- const buffer_handle_t buffer = *camera_buffer->buffer;
-
- if (StreamFormat::HalToV4L2PixelFormat(camera_buffer->stream->format) == V4L2_PIX_FMT_RGB24) {
- // Convert RGB24 to RGB32.
- size_t rgb_size = camera_buffer->stream->width * camera_buffer->stream->height;
- uint8_t* tail_rgb24 = (uint8_t*)data + 3 * rgb_size - 1;
- uint8_t* tail_rgb32 = (uint8_t*)data + 4 * rgb_size - 1;
- for (int i = 0; i < rgb_size; i++) {
- *(tail_rgb32--) = 0xff;
- *(tail_rgb32--) = *(tail_rgb24--);
- *(tail_rgb32--) = *(tail_rgb24--);
- *(tail_rgb32--) = *(tail_rgb24--);
- }
- }
-
- // Check for transform.
- if (buffer_data->transform_dest) {
- HAL_LOGV("Transforming V4L2 YUV to gralloc YUV.");
- // In this case data was allocated by this class, put it in a unique_ptr
- // to ensure it gets cleaned up no matter which way this function exits.
- std::unique_ptr<uint8_t[]> data_cleanup(reinterpret_cast<uint8_t*>(data));
-
- uint32_t bytes_per_line = buffer_data->v4l2_bytes_per_line;
- android_ycbcr* yuv_data = buffer_data->transform_dest.get();
-
- // Should only occur in error situations.
- if (device_buffer->bytesused == 0) {
- return -EINVAL;
- }
-
- // Transform V4L2 to Gralloc, copying each plane to the correct place,
- // adjusting padding, and interleaving if necessary.
- uint32_t height = camera_buffer->stream->height;
- // Y data first.
- size_t y_len = bytes_per_line * height;
- if (yuv_data->ystride == bytes_per_line) {
- // Data should match exactly.
- memcpy(yuv_data->y, data, y_len);
- } else {
- HAL_LOGV("Changing padding on Y plane from %u to %u.",
- bytes_per_line,
- yuv_data->ystride);
- // Wrong padding from V4L2.
- copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->y),
- reinterpret_cast<uint8_t*>(data),
- yuv_data->ystride,
- bytes_per_line,
- height);
- }
- // C data.
- // TODO(b/30119452): These calculations assume YCbCr_420_888.
- size_t c_len = y_len / 4;
- uint32_t c_bytes_per_line = bytes_per_line / 2;
- // V4L2 is packed, meaning the data is stored as contiguous {y, cb, cr}.
- uint8_t* cb_device = reinterpret_cast<uint8_t*>(data) + y_len;
- uint8_t* cr_device = cb_device + c_len;
- size_t step = yuv_data->chroma_step;
- if (step == 1) {
- // Still planar.
- if (yuv_data->cstride == c_bytes_per_line) {
- // Data should match exactly.
- memcpy(yuv_data->cb, cb_device, c_len);
- memcpy(yuv_data->cr, cr_device, c_len);
- } else {
- HAL_LOGV("Changing padding on C plane from %u to %u.",
- c_bytes_per_line,
- yuv_data->cstride);
- // Wrong padding from V4L2.
- copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->cb),
- cb_device,
- yuv_data->cstride,
- c_bytes_per_line,
- height / 2);
- copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->cr),
- cr_device,
- yuv_data->cstride,
- c_bytes_per_line,
- height / 2);
- }
- } else {
- // Desire semiplanar (cb and cr interleaved).
- HAL_LOGV("Interleaving cb and cr. Padding going from %u to %u.",
- c_bytes_per_line,
- yuv_data->cstride);
- uint32_t c_height = height / 2;
- uint32_t c_width = camera_buffer->stream->width / 2;
- // Zero out destination
- uint8_t* cb_gralloc = reinterpret_cast<uint8_t*>(yuv_data->cb);
- uint8_t* cr_gralloc = reinterpret_cast<uint8_t*>(yuv_data->cr);
- memset(cb_gralloc, 0, c_width * c_height * step);
-
- // Interleaving means we need to copy the cb and cr bytes one by one.
- for (size_t line = 0; line < c_height; ++line,
- cb_gralloc += yuv_data->cstride,
- cr_gralloc += yuv_data->cstride,
- cb_device += c_bytes_per_line,
- cr_device += c_bytes_per_line) {
- for (size_t i = 0; i < c_width; ++i) {
- *(cb_gralloc + (i * step)) = *(cb_device + i);
- *(cr_gralloc + (i * step)) = *(cr_device + i);
- }
- }
- }
- }
-
- // Unlock.
- int res = mModule->unlock(mModule, buffer);
- if (res) {
- HAL_LOGE("Failed to unlock buffer at %p", buffer);
- return -ENODEV;
- }
-
- return 0;
-}
-
-int V4L2Gralloc::unlockAllBuffers() {
- HAL_LOG_ENTER();
-
- bool failed = false;
- for (auto const& entry : mBufferMap) {
- int res = mModule->unlock(mModule, *entry.second->camera_buffer->buffer);
- if (res) {
- failed = true;
- }
- // When there is a transform to be made, the buffer returned by lock()
- // is dynamically allocated (to hold the pre-transform data).
- if (entry.second->transform_dest) {
- delete[] reinterpret_cast<uint8_t*>(entry.first);
- }
- // The BufferData entry is always dynamically allocated in lock().
- delete entry.second;
- }
- mBufferMap.clear();
-
- // If any unlock failed, return error.
- if (failed) {
- return -ENODEV;
- }
-
- return 0;
-}
-
-} // namespace default_camera_hal
diff --git a/modules/camera/3_4/v4l2_gralloc.h b/modules/camera/3_4/v4l2_gralloc.h
deleted file mode 100644
index 82129e3..0000000
--- a/modules/camera/3_4/v4l2_gralloc.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * 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.
- */
-
-#ifndef V4L2_CAMERA_HAL_V4L2_GRALLOC_H_
-#define V4L2_CAMERA_HAL_V4L2_GRALLOC_H_
-
-#include <linux/videodev2.h>
-
-#include <unordered_map>
-
-#include <hardware/camera3.h>
-#include <hardware/gralloc.h>
-#include <system/graphics.h>
-
-namespace v4l2_camera_hal {
-
-// Generously allow up to 6MB (the largest JPEG on the RPi camera is about 5MB).
-static constexpr size_t V4L2_MAX_JPEG_SIZE = 6000000;
-
-// V4L2Gralloc is a wrapper around relevant parts of a gralloc module,
-// with some assistive transformations.
-class V4L2Gralloc {
- public:
- // Use this method to create V4L2Gralloc objects. Functionally equivalent
- // to "new V4L2Gralloc", except that it may return nullptr in case of failure.
- static V4L2Gralloc* NewV4L2Gralloc();
- virtual ~V4L2Gralloc();
-
- // Lock a camera buffer. Uses device buffer length, sets user pointer.
- int lock(const camera3_stream_buffer_t* camera_buffer,
- uint32_t bytes_per_line,
- v4l2_buffer* device_buffer);
- // Unlock a buffer that was locked by this helper (equality determined
- // based on buffer user pointer, not the specific object).
- int unlock(const v4l2_buffer* device_buffer);
- // Release all held locks.
- int unlockAllBuffers();
-
- private:
- // Constructor is private to allow failing on bad input.
- // Use NewV4L2Gralloc instead.
- V4L2Gralloc(const gralloc_module_t* module);
-
- const gralloc_module_t* mModule;
-
- struct BufferData {
- const camera3_stream_buffer_t* camera_buffer;
- // Below fields only used when a ycbcr format transform is necessary.
- std::unique_ptr<android_ycbcr> transform_dest; // nullptr if no transform.
- uint32_t v4l2_bytes_per_line;
- };
- // Map data pointer : BufferData about that buffer.
- std::unordered_map<void*, const BufferData*> mBufferMap;
-};
-
-} // namespace default_camera_hal
-
-#endif // V4L2_CAMERA_HAL_V4L2_GRALLOC_H_
diff --git a/modules/camera/3_4/v4l2_metadata_factory.cpp b/modules/camera/3_4/v4l2_metadata_factory.cpp
index 3d9cfbb..bc8806f 100644
--- a/modules/camera/3_4/v4l2_metadata_factory.cpp
+++ b/modules/camera/3_4/v4l2_metadata_factory.cpp
@@ -27,7 +27,6 @@
#include "metadata/partial_metadata_factory.h"
#include "metadata/property.h"
#include "metadata/scaling_converter.h"
-#include "v4l2_gralloc.h"
namespace v4l2_camera_hal {
@@ -37,6 +36,8 @@
const int64_t kV4L2ExposureTimeStepNs = 100000;
// According to spec, each unit of V4L2_CID_ISO_SENSITIVITY is ISO/1000.
const int32_t kV4L2SensitivityDenominator = 1000;
+// Generously allow up to 6MB (the largest size on the RPi Camera is about 5MB).
+const size_t kV4L2MaxJpegSize = 6000000;
int GetV4L2Metadata(std::shared_ptr<V4L2Wrapper> device,
std::unique_ptr<Metadata>* result) {
@@ -433,10 +434,9 @@
ANDROID_JPEG_THUMBNAIL_SIZE,
ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
{{{0, 0}}}));
- // TODO(b/31022752): Get this from the device,
- // not constant (from V4L2Gralloc.h).
+ // TODO(b/31022752): Get this from the device, not constant.
components.insert(std::unique_ptr<PartialMetadataInterface>(
- new Property<int32_t>(ANDROID_JPEG_MAX_SIZE, V4L2_MAX_JPEG_SIZE)));
+ new Property<int32_t>(ANDROID_JPEG_MAX_SIZE, kV4L2MaxJpegSize)));
// TODO(b/31021672): Other JPEG controls (GPS, quality, orientation).
// TODO(b/29939583): V4L2 can only support 1 stream at a time.
// For now, just reporting minimum allowable for LIMITED devices.
diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp
index 36d0411..d715e7e 100644
--- a/modules/camera/3_4/v4l2_wrapper.cpp
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -29,12 +29,15 @@
#include <android-base/unique_fd.h>
-#include "common.h"
-#include "stream_format.h"
-#include "v4l2_gralloc.h"
+#include "arc/cached_frame.h"
namespace v4l2_camera_hal {
+using arc::AllocatedFrameBuffer;
+using arc::SupportedFormat;
+using arc::SupportedFormats;
+using default_camera_hal::CaptureRequest;
+
const int32_t kStandardSizes[][2] = {
{4096, 2160}, // 4KDCI (for USB camera)
{3840, 2160}, // 4KUHD (for USB camera)
@@ -50,20 +53,11 @@
};
V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) {
- std::unique_ptr<V4L2Gralloc> gralloc(V4L2Gralloc::NewV4L2Gralloc());
- if (!gralloc) {
- HAL_LOGE("Failed to initialize gralloc helper.");
- return nullptr;
- }
-
- return new V4L2Wrapper(device_path, std::move(gralloc));
+ return new V4L2Wrapper(device_path);
}
-V4L2Wrapper::V4L2Wrapper(const std::string device_path,
- std::unique_ptr<V4L2Gralloc> gralloc)
- : device_path_(std::move(device_path)),
- gralloc_(std::move(gralloc)),
- connection_count_(0) {}
+V4L2Wrapper::V4L2Wrapper(const std::string device_path)
+ : device_path_(std::move(device_path)), connection_count_(0) {}
V4L2Wrapper::~V4L2Wrapper() {}
@@ -97,6 +91,10 @@
// (Alternatively, better hotplugging support may make this unecessary
// by disabling cameras that get disconnected and checking newly connected
// cameras, so Connect() is never called on an unsupported camera)
+
+ supported_formats_ = GetSupportedFormats();
+ qualified_formats_ = StreamFormat::GetQualifiedFormats(supported_formats_);
+
return 0;
}
@@ -114,15 +112,16 @@
--connection_count_;
if (connection_count_ > 0) {
HAL_LOGV("Disconnected from camera device %s. %d connections remain.",
- device_path_.c_str());
+ device_path_.c_str(), connection_count_);
return;
}
device_fd_.reset(-1); // Includes close().
format_.reset();
- buffers_.clear();
- // Closing the device releases all queued buffers back to the user.
- gralloc_->unlockAllBuffers();
+ {
+ std::lock_guard<std::mutex> buffer_lock(buffer_queue_lock_);
+ buffers_.clear();
+ }
}
// Helper function. Should be used instead of ioctl throughout this class.
@@ -146,7 +145,7 @@
int32_t type = format_->type();
if (IoctlLocked(VIDIOC_STREAMON, &type) < 0) {
- HAL_LOGE("STREAMON fails: %s", strerror(errno));
+ HAL_LOGE("STREAMON fails (%d): %s", errno, strerror(errno));
return -ENODEV;
}
@@ -164,20 +163,16 @@
int32_t type = format_->type();
int res = IoctlLocked(VIDIOC_STREAMOFF, &type);
// Calling STREAMOFF releases all queued buffers back to the user.
- int gralloc_res = gralloc_->unlockAllBuffers();
// No buffers in flight.
- for (size_t i = 0; i < buffers_.size(); ++i) {
- buffers_[i] = false;
- }
if (res < 0) {
HAL_LOGE("STREAMOFF fails: %s", strerror(errno));
return -ENODEV;
}
- if (gralloc_res < 0) {
- HAL_LOGE("Failed to unlock all buffers after turning stream off.");
- return gralloc_res;
+ std::lock_guard<std::mutex> lock(buffer_queue_lock_);
+ for (auto& buffer : buffers_) {
+ buffer.active = false;
+ buffer.request.reset();
}
-
HAL_LOGV("Stream turned off.");
return 0;
}
@@ -316,6 +311,36 @@
return 0;
}
+const SupportedFormats V4L2Wrapper::GetSupportedFormats() {
+ SupportedFormats formats;
+ std::set<uint32_t> pixel_formats;
+ int res = GetFormats(&pixel_formats);
+ if (res) {
+ HAL_LOGE("Failed to get device formats.");
+ return formats;
+ }
+
+ arc::SupportedFormat supported_format;
+ std::set<std::array<int32_t, 2>> frame_sizes;
+
+ for (auto pixel_format : pixel_formats) {
+ supported_format.fourcc = pixel_format;
+
+ frame_sizes.clear();
+ res = GetFormatFrameSizes(pixel_format, &frame_sizes);
+ if (res) {
+ HAL_LOGE("Failed to get frame sizes for format: 0x%x", pixel_format);
+ continue;
+ }
+ for (auto frame_size : frame_sizes) {
+ supported_format.width = frame_size[0];
+ supported_format.height = frame_size[1];
+ formats.push_back(supported_format);
+ }
+ }
+ return formats;
+}
+
int V4L2Wrapper::GetFormats(std::set<uint32_t>* v4l2_formats) {
HAL_LOG_ENTER();
@@ -336,6 +361,22 @@
return 0;
}
+int V4L2Wrapper::GetQualifiedFormats(std::vector<uint32_t>* v4l2_formats) {
+ HAL_LOG_ENTER();
+ if (!connected()) {
+ HAL_LOGE(
+ "Device is not connected, qualified formats may not have been set.");
+ return -EINVAL;
+ }
+ v4l2_formats->clear();
+ std::set<uint32_t> unique_fourccs;
+ for (auto& format : qualified_formats_) {
+ unique_fourccs.insert(format.fourcc);
+ }
+ v4l2_formats->assign(unique_fourccs.begin(), unique_fourccs.end());
+ return 0;
+}
+
int V4L2Wrapper::GetFormatFrameSizes(uint32_t v4l2_format,
std::set<std::array<int32_t, 2>>* sizes) {
v4l2_frmsizeenum size_query;
@@ -459,8 +500,6 @@
return 0;
}
- // Not in the correct format, set the new one.
-
if (format_) {
// If we had an old format, first request 0 buffers to inform the device
// we're no longer using any previously "allocated" buffers from the old
@@ -473,19 +512,34 @@
}
}
+ // Select the matching format, or if not available, select a qualified format
+ // we can convert from.
+ SupportedFormat format;
+ if (!StreamFormat::FindBestFitFormat(supported_formats_, qualified_formats_,
+ desired_format.v4l2_pixel_format(),
+ desired_format.width(),
+ desired_format.height(), &format)) {
+ HAL_LOGE(
+ "Unable to find supported resolution in list, "
+ "width: %d, height: %d",
+ desired_format.width(), desired_format.height());
+ return -EINVAL;
+ }
+
// Set the camera to the new format.
v4l2_format new_format;
- desired_format.FillFormatRequest(&new_format);
+ const StreamFormat resolved_format(format);
+ resolved_format.FillFormatRequest(&new_format);
+
// TODO(b/29334616): When async, this will need to check if the stream
// is on, and if so, lock it off while setting format.
-
if (IoctlLocked(VIDIOC_S_FMT, &new_format) < 0) {
HAL_LOGE("S_FMT failed: %s", strerror(errno));
return -ENODEV;
}
// Check that the driver actually set to the requested values.
- if (desired_format != new_format) {
+ if (resolved_format != new_format) {
HAL_LOGE("Device doesn't support desired stream configuration.");
return -EINVAL;
}
@@ -512,28 +566,22 @@
int res = IoctlLocked(VIDIOC_REQBUFS, &req_buffers);
// Calling REQBUFS releases all queued buffers back to the user.
- int gralloc_res = gralloc_->unlockAllBuffers();
if (res < 0) {
HAL_LOGE("REQBUFS failed: %s", strerror(errno));
return -ENODEV;
}
- if (gralloc_res < 0) {
- HAL_LOGE("Failed to unlock all buffers when setting up new buffers.");
- return gralloc_res;
- }
// V4L2 will set req_buffers.count to a number of buffers it can handle.
if (num_requested > 0 && req_buffers.count < 1) {
HAL_LOGE("REQBUFS claims it can't handle any buffers.");
return -ENODEV;
}
- buffers_.resize(req_buffers.count, false);
-
+ buffers_.resize(req_buffers.count);
return 0;
}
-int V4L2Wrapper::EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer,
- uint32_t* enqueued_index) {
+int V4L2Wrapper::EnqueueRequest(
+ std::shared_ptr<default_camera_hal::CaptureRequest> request) {
if (!format_) {
HAL_LOGE("Stream format must be set before enqueuing buffers.");
return -ENODEV;
@@ -545,9 +593,8 @@
int index = -1;
{
std::lock_guard<std::mutex> guard(buffer_queue_lock_);
- for (int i = 0; i < buffers_.size(); ++i) {
- if (!buffers_[i]) {
- buffers_[i] = true;
+ for (size_t i = 0; i < buffers_.size(); ++i) {
+ if (!buffers_[i].active) {
index = i;
break;
}
@@ -572,36 +619,40 @@
HAL_LOGE("QUERYBUF fails: %s", strerror(errno));
// Return buffer index.
std::lock_guard<std::mutex> guard(buffer_queue_lock_);
- buffers_[index] = false;
+ buffers_[index].active = false;
return -ENODEV;
}
- // Lock the buffer for writing (fills in the user pointer field).
- int res =
- gralloc_->lock(camera_buffer, format_->bytes_per_line(), &device_buffer);
- if (res) {
- HAL_LOGE("Gralloc failed to lock buffer.");
- // Return buffer index.
+ // Setup our request context and fill in the user pointer field.
+ RequestContext* request_context;
+ void* data;
+ {
std::lock_guard<std::mutex> guard(buffer_queue_lock_);
- buffers_[index] = false;
- return res;
+ request_context = &buffers_[index];
+ request_context->camera_buffer->SetDataSize(device_buffer.length);
+ request_context->camera_buffer->Reset();
+ request_context->camera_buffer->SetFourcc(format_->v4l2_pixel_format());
+ request_context->camera_buffer->SetWidth(format_->width());
+ request_context->camera_buffer->SetHeight(format_->height());
+ request_context->request = request;
+ data = request_context->camera_buffer->GetData();
}
+ device_buffer.m.userptr = reinterpret_cast<unsigned long>(data);
+
+ // Pass the buffer to the camera.
if (IoctlLocked(VIDIOC_QBUF, &device_buffer) < 0) {
HAL_LOGE("QBUF fails: %s", strerror(errno));
- gralloc_->unlock(&device_buffer);
- // Return buffer index.
- std::lock_guard<std::mutex> guard(buffer_queue_lock_);
- buffers_[index] = false;
return -ENODEV;
}
- if (enqueued_index) {
- *enqueued_index = index;
- }
+ // Mark the buffer as in flight.
+ std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+ request_context->active = true;
+
return 0;
}
-int V4L2Wrapper::DequeueBuffer(uint32_t* dequeued_index) {
+int V4L2Wrapper::DequeueRequest(std::shared_ptr<CaptureRequest>* request) {
if (!format_) {
HAL_LOGV(
"Format not set, so stream can't be on, "
@@ -625,23 +676,63 @@
}
}
- // Mark the buffer as no longer in flight.
- {
- std::lock_guard<std::mutex> guard(buffer_queue_lock_);
- buffers_[buffer.index] = false;
+ std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+ RequestContext* request_context = &buffers_[buffer.index];
+
+ // Lock the camera stream buffer for painting.
+ const camera3_stream_buffer_t* stream_buffer =
+ &request_context->request->output_buffers[0];
+ uint32_t fourcc =
+ StreamFormat::HalToV4L2PixelFormat(stream_buffer->stream->format);
+
+ if (request) {
+ *request = request_context->request;
}
- // Now that we're done painting the buffer, we can unlock it.
- res = gralloc_->unlock(&buffer);
+ // Note that the device buffer length is passed to the output frame. If the
+ // GrallocFrameBuffer does not have support for the transformation to
+ // |fourcc|, it will assume that the amount of data to lock is based on
+ // |buffer.length|, otherwise it will use the ImageProcessor::ConvertedSize.
+ arc::GrallocFrameBuffer output_frame(
+ *stream_buffer->buffer, stream_buffer->stream->width,
+ stream_buffer->stream->height, fourcc, buffer.length,
+ stream_buffer->stream->usage);
+ res = output_frame.Map();
if (res) {
- HAL_LOGE("Gralloc failed to unlock buffer after dequeueing.");
- return res;
+ HAL_LOGE("Failed to map output frame.");
+ request_context->request.reset();
+ return -EINVAL;
+ }
+ if (request_context->camera_buffer->GetFourcc() == fourcc &&
+ request_context->camera_buffer->GetWidth() ==
+ stream_buffer->stream->width &&
+ request_context->camera_buffer->GetHeight() ==
+ stream_buffer->stream->height) {
+ // If no format conversion needs to be applied, directly copy the data over.
+ memcpy(output_frame.GetData(), request_context->camera_buffer->GetData(),
+ request_context->camera_buffer->GetDataSize());
+ } else {
+ // Perform the format conversion.
+ arc::CachedFrame cached_frame;
+ cached_frame.SetSource(request_context->camera_buffer.get(), 0);
+ cached_frame.Convert(request_context->request->settings, &output_frame);
}
- if (dequeued_index) {
- *dequeued_index = buffer.index;
- }
+ request_context->request.reset();
+ // Mark the buffer as not in flight.
+ request_context->active = false;
return 0;
}
+int V4L2Wrapper::GetInFlightBufferCount() {
+ int count = 0;
+ std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+ for (auto& buffer : buffers_) {
+ if (buffer.active) {
+ count++;
+ }
+ }
+ return count;
+}
+
} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/v4l2_wrapper.h b/modules/camera/3_4/v4l2_wrapper.h
index c3ad272..4a8d517 100644
--- a/modules/camera/3_4/v4l2_wrapper.h
+++ b/modules/camera/3_4/v4l2_wrapper.h
@@ -26,11 +26,14 @@
#include <android-base/unique_fd.h>
+#include "arc/common_types.h"
+#include "arc/frame_buffer.h"
+#include "capture_request.h"
#include "common.h"
#include "stream_format.h"
-#include "v4l2_gralloc.h"
namespace v4l2_camera_hal {
+
class V4L2Wrapper {
public:
// Use this method to create V4L2Wrapper objects. Functionally equivalent
@@ -67,8 +70,10 @@
int32_t* result = nullptr);
// Manage format.
virtual int GetFormats(std::set<uint32_t>* v4l2_formats);
+ virtual int GetQualifiedFormats(std::vector<uint32_t>* v4l2_formats);
virtual int GetFormatFrameSizes(uint32_t v4l2_format,
std::set<std::array<int32_t, 2>>* sizes);
+
// Durations are returned in ns.
virtual int GetFormatFrameDurationRange(
uint32_t v4l2_format,
@@ -77,15 +82,16 @@
virtual int SetFormat(const StreamFormat& desired_format,
uint32_t* result_max_buffers);
// Manage buffers.
- virtual int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer,
- uint32_t* enqueued_index = nullptr);
- virtual int DequeueBuffer(uint32_t* dequeued_index = nullptr);
+ virtual int EnqueueRequest(
+ std::shared_ptr<default_camera_hal::CaptureRequest> request);
+ virtual int DequeueRequest(
+ std::shared_ptr<default_camera_hal::CaptureRequest>* request);
+ virtual int GetInFlightBufferCount();
private:
// Constructor is private to allow failing on bad input.
// Use NewV4L2Wrapper instead.
- V4L2Wrapper(const std::string device_path,
- std::unique_ptr<V4L2Gralloc> gralloc);
+ V4L2Wrapper(const std::string device_path);
// Connect or disconnect to the device. Access by creating/destroying
// a V4L2Wrapper::Connection object.
@@ -99,20 +105,19 @@
inline bool connected() { return device_fd_.get() >= 0; }
+ // Format management.
+ const arc::SupportedFormats GetSupportedFormats();
+
// The camera device path. For example, /dev/video0.
const std::string device_path_;
// The opened device fd.
android::base::unique_fd device_fd_;
// The underlying gralloc module.
- std::unique_ptr<V4L2Gralloc> gralloc_;
+ // std::unique_ptr<V4L2Gralloc> gralloc_;
// Whether or not the device supports the extended control query.
bool extended_query_supported_;
// The format this device is set up for.
std::unique_ptr<StreamFormat> format_;
- // Map indecies to buffer status. True if the index is in-flight.
- // |buffers_.size()| will always be the maximum number of buffers this device
- // can handle in its current format.
- std::vector<bool> buffers_;
// Lock protecting use of the buffer tracker.
std::mutex buffer_queue_lock_;
// Lock protecting use of the device.
@@ -121,6 +126,28 @@
std::mutex connection_lock_;
// Reference count connections.
int connection_count_;
+ // Supported formats.
+ arc::SupportedFormats supported_formats_;
+ // Qualified formats.
+ arc::SupportedFormats qualified_formats_;
+
+ class RequestContext {
+ public:
+ RequestContext()
+ : active(false),
+ camera_buffer(std::make_shared<arc::AllocatedFrameBuffer>(0)){};
+ ~RequestContext(){};
+ // Indicates whether this request context is in use.
+ bool active;
+ // Buffer handles of the context.
+ std::shared_ptr<arc::AllocatedFrameBuffer> camera_buffer;
+ std::shared_ptr<default_camera_hal::CaptureRequest> request;
+ };
+
+ // Map of in flight requests.
+ // |buffers_.size()| will always be the maximum number of buffers this device
+ // can handle in its current format.
+ std::vector<RequestContext> buffers_;
friend class Connection;
friend class V4L2WrapperMock;
diff --git a/modules/camera/3_4/v4l2_wrapper_mock.h b/modules/camera/3_4/v4l2_wrapper_mock.h
index d423cc5..f98bc94 100644
--- a/modules/camera/3_4/v4l2_wrapper_mock.h
+++ b/modules/camera/3_4/v4l2_wrapper_mock.h
@@ -27,7 +27,7 @@
class V4L2WrapperMock : public V4L2Wrapper {
public:
- V4L2WrapperMock() : V4L2Wrapper("", nullptr){};
+ V4L2WrapperMock() : V4L2Wrapper(""){};
MOCK_METHOD0(StreamOn, int());
MOCK_METHOD0(StreamOff, int());
MOCK_METHOD2(QueryControl,
@@ -36,17 +36,15 @@
MOCK_METHOD3(SetControl,
int(uint32_t control_id, int32_t desired, int32_t* result));
MOCK_METHOD1(GetFormats, int(std::set<uint32_t>*));
+ MOCK_METHOD1(GetQualifiedFormats, int(std::vector<uint32_t>*));
MOCK_METHOD2(GetFormatFrameSizes,
int(uint32_t, std::set<std::array<int32_t, 2>>*));
MOCK_METHOD3(GetFormatFrameDurationRange,
int(uint32_t,
const std::array<int32_t, 2>&,
std::array<int64_t, 2>*));
- MOCK_METHOD4(SetFormat,
- int(int format,
- uint32_t width,
- uint32_t height,
- uint32_t* result_max_buffers));
+ MOCK_METHOD2(SetFormat, int(const StreamFormat& desired_format,
+ uint32_t* result_max_buffers));
MOCK_METHOD2(EnqueueBuffer,
int(const camera3_stream_buffer_t* camera_buffer,
uint32_t* enqueued_index));