Add scaling converters and secondary converters.
Scaling converters scale, as the name implies.
The two wrapping converters, map & ranged sanitize
data for sending to V4L2, either converting a value
to be within a map, or within a range, respectively.
(These are intended for use with V4L2 int menu and int
controls, respectively).
TEST: Unit tests pass
BUG: 30140438
Change-Id: Ie7d86118e00c0b59e6457fc16449228706098952
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
index 4c77968..820a993 100644
--- a/modules/camera/3_4/Android.mk
+++ b/modules/camera/3_4/Android.mk
@@ -55,8 +55,10 @@
metadata/enum_converter_test.cpp \
metadata/fixed_property_test.cpp \
metadata/ignored_control_test.cpp \
+ metadata/map_converter_test.cpp \
metadata/metadata_test.cpp \
metadata/optioned_control_test.cpp \
+ metadata/ranged_converter_test.cpp \
metadata/v4l2_enum_control_test.cpp \
# V4L2 Camera HAL.
diff --git a/modules/camera/3_4/metadata/converter_interface.h b/modules/camera/3_4/metadata/converter_interface.h
index 38ede00..ca6a0f2 100644
--- a/modules/camera/3_4/metadata/converter_interface.h
+++ b/modules/camera/3_4/metadata/converter_interface.h
@@ -22,14 +22,14 @@
namespace v4l2_camera_hal {
// A ConverterInterface converts metadata values to V4L2 values vice-versa.
-template <typename T_metadata, typename T_V4L2>
+template <typename TMetadata, typename TV4L2>
class ConverterInterface {
public:
virtual ~ConverterInterface(){};
// Convert.
- virtual int MetadataToV4L2(T_metadata value, T_V4L2* conversion) = 0;
- virtual int V4L2ToMetadata(T_V4L2 value, T_metadata* conversion) = 0;
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) = 0;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) = 0;
};
} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/converter_interface_mock.h b/modules/camera/3_4/metadata/converter_interface_mock.h
new file mode 100644
index 0000000..3f7e6f7
--- /dev/null
+++ b/modules/camera/3_4/metadata/converter_interface_mock.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+// Mock for converter interfaces.
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_MOCK_H_
+#define V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+template <typename TMetadata, typename TV4L2>
+class ConverterInterfaceMock : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ ConverterInterfaceMock(){};
+ MOCK_METHOD2_T(MetadataToV4L2, int(TMetadata, TV4L2*));
+ MOCK_METHOD2_T(V4L2ToMetadata, int(TV4L2, TMetadata*));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_MOCK_H_
diff --git a/modules/camera/3_4/metadata/map_converter.h b/modules/camera/3_4/metadata/map_converter.h
new file mode 100644
index 0000000..b1734b5
--- /dev/null
+++ b/modules/camera/3_4/metadata/map_converter.h
@@ -0,0 +1,139 @@
+/*
+ * 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_METADATA_MAP_CONVERTER_H_
+#define V4L2_CAMERA_HAL_METADATA_MAP_CONVERTER_H_
+
+#include <errno.h>
+
+#include <map>
+#include <memory>
+
+#include "../common.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A MapConverter fits values converted by a wrapped converter
+// to a map entry corresponding to the key with the nearest value.
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+class MapConverter : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ MapConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TMapKey>> wrapped_converter,
+ std::map<TMapKey, TV4L2> conversion_map);
+
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) override;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) override;
+
+ private:
+ std::shared_ptr<ConverterInterface<TMetadata, TMapKey>> wrapped_converter_;
+ std::map<TMapKey, TV4L2> conversion_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MapConverter);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+MapConverter<TMetadata, TV4L2, TMapKey>::MapConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TMapKey>> wrapped_converter,
+ std::map<TMapKey, TV4L2> conversion_map)
+ : wrapped_converter_(std::move(wrapped_converter)),
+ conversion_map_(conversion_map) {
+ HAL_LOG_ENTER();
+}
+
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+int MapConverter<TMetadata, TV4L2, TMapKey>::MetadataToV4L2(TMetadata value,
+ TV4L2* conversion) {
+ HAL_LOG_ENTER();
+
+ if (conversion_map_.empty()) {
+ HAL_LOGE("Empty conversion map.");
+ return -EINVAL;
+ }
+
+ TMapKey raw_conversion = 0;
+ int res = wrapped_converter_->MetadataToV4L2(value, &raw_conversion);
+ if (res) {
+ HAL_LOGE("Failed to perform underlying conversion.");
+ return res;
+ }
+
+ // Find nearest key.
+ auto kv = conversion_map_.lower_bound(raw_conversion);
+ // lower_bound finds the first >= element.
+ if (kv == conversion_map_.begin()) {
+ // Searching for less than the smallest key, so that will be the nearest.
+ *conversion = kv->second;
+ } else if (kv == conversion_map_.end()) {
+ // Searching for greater than the largest key, so that will be the nearest.
+ --kv;
+ *conversion = kv->second;
+ } else {
+ // Since kv points to the first >= element, either that or the previous
+ // element will be nearest.
+ *conversion = kv->second;
+ TMapKey diff = kv->first - raw_conversion;
+
+ // Now compare to the previous. This element will be < raw conversion,
+ // so reverse the order of the subtraction.
+ --kv;
+ if (raw_conversion - kv->first < diff) {
+ *conversion = kv->second;
+ }
+ }
+
+ return 0;
+}
+
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+int MapConverter<TMetadata, TV4L2, TMapKey>::V4L2ToMetadata(
+ TV4L2 value, TMetadata* conversion) {
+ HAL_LOG_ENTER();
+
+ // Unfortunately no bi-directional map lookup in C++.
+ // Breaking on second, not first found so that a warning
+ // can be given if there are multiple values.
+ size_t count = 0;
+ int res;
+ for (auto kv : conversion_map_) {
+ if (kv.second == value) {
+ ++count;
+ if (count == 1) {
+ // First match.
+ res = wrapped_converter_->V4L2ToMetadata(kv.first, conversion);
+ } else {
+ // second match.
+ break;
+ }
+ }
+ }
+
+ if (count == 0) {
+ HAL_LOGE("Couldn't find map conversion of V4L2 value %d.", value);
+ return -EINVAL;
+ } else if (count > 1) {
+ HAL_LOGW("Multiple map conversions found for V4L2 value %d, using first.",
+ value);
+ }
+ return res;
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_MAP_CONVERTER_H_
diff --git a/modules/camera/3_4/metadata/map_converter_test.cpp b/modules/camera/3_4/metadata/map_converter_test.cpp
new file mode 100644
index 0000000..0361810
--- /dev/null
+++ b/modules/camera/3_4/metadata/map_converter_test.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "map_converter.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "converter_interface_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class MapConverterTest : public Test {
+ protected:
+ virtual void SetUp() {
+ converter_.reset(new ConverterInterfaceMock<int, int32_t>());
+ dut_.reset(new MapConverter<int, int32_t, int32_t>(converter_, map_));
+ }
+
+ virtual void ExpectConvertToV4L2(int32_t converted, int32_t expected) {
+ int initial = 99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _))
+ .WillOnce(DoAll(SetArgPointee<1>(converted), Return(0)));
+
+ int32_t actual = expected + 1; // Initialize to non-expected value.
+ ASSERT_EQ(dut_->MetadataToV4L2(initial, &actual), 0);
+ EXPECT_EQ(actual, expected);
+ }
+
+ std::shared_ptr<ConverterInterfaceMock<int, int32_t>> converter_;
+ std::unique_ptr<MapConverter<int, int32_t, int32_t>> dut_;
+
+ const std::map<int32_t, int32_t> map_{{10, 1}, {40, 4}, {20, 2}, {30, 3}};
+};
+
+TEST_F(MapConverterTest, NormalConversionToV4L2) {
+ // A value that matches the map perfectly.
+ auto kv = map_.begin();
+ ExpectConvertToV4L2(kv->first, kv->second);
+}
+
+TEST_F(MapConverterTest, RoundingDownConversionToV4L2) {
+ // A value that's in range but not an exact key value.
+ auto kv = map_.begin();
+ ExpectConvertToV4L2(kv->first + 1, kv->second);
+}
+
+TEST_F(MapConverterTest, RoundingUpConversionToV4L2) {
+ // A value that's in range but not an exact key value.
+ auto kv = map_.begin();
+ ++kv;
+ ExpectConvertToV4L2(kv->first - 1, kv->second);
+}
+
+TEST_F(MapConverterTest, ClampUpConversionToV4L2) {
+ // A value that's below range.
+ auto kv = map_.begin();
+ ExpectConvertToV4L2(kv->first - 1, kv->second);
+}
+
+TEST_F(MapConverterTest, ClampDownConversionToV4L2) {
+ // A value that's above range (even after fitting to step).
+ auto kv = map_.rbegin();
+ ExpectConvertToV4L2(kv->first + 1, kv->second);
+}
+
+TEST_F(MapConverterTest, ConversionErrorToV4L2) {
+ int initial = 99;
+ int err = -99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _)).WillOnce(Return(err));
+
+ int32_t unused;
+ EXPECT_EQ(dut_->MetadataToV4L2(initial, &unused), err);
+}
+
+TEST_F(MapConverterTest, NormalConversionToMetadata) {
+ auto kv = map_.begin();
+ int expected = 99;
+ EXPECT_CALL(*converter_, V4L2ToMetadata(kv->first, _))
+ .WillOnce(DoAll(SetArgPointee<1>(expected), Return(0)));
+
+ int actual = expected + 1; // Initialize to non-expected value.
+ ASSERT_EQ(dut_->V4L2ToMetadata(kv->second, &actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MapConverterTest, NotFoundConversionToMetadata) {
+ int unused;
+ ASSERT_EQ(dut_->V4L2ToMetadata(100, &unused), -EINVAL);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/ranged_converter.h b/modules/camera/3_4/metadata/ranged_converter.h
new file mode 100644
index 0000000..115ac2a
--- /dev/null
+++ b/modules/camera/3_4/metadata/ranged_converter.h
@@ -0,0 +1,102 @@
+/*
+ * 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_METADATA_RANGED_CONVERTER_H_
+#define V4L2_CAMERA_HAL_METADATA_RANGED_CONVERTER_H_
+
+#include <memory>
+
+#include "../common.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// An RangedConverter fits values converted by a wrapped converter
+// to a stepped range (when going from metadata -> v4l2. The other
+// direction remains unchanged).
+template <typename TMetadata, typename TV4L2>
+class RangedConverter : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ RangedConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> wrapped_converter,
+ TV4L2 min,
+ TV4L2 max,
+ TV4L2 step);
+
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) override;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) override;
+
+ private:
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> wrapped_converter_;
+ const TV4L2 min_;
+ const TV4L2 max_;
+ const TV4L2 step_;
+
+ DISALLOW_COPY_AND_ASSIGN(RangedConverter);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename TMetadata, typename TV4L2>
+RangedConverter<TMetadata, TV4L2>::RangedConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> wrapped_converter,
+ TV4L2 min,
+ TV4L2 max,
+ TV4L2 step)
+ : wrapped_converter_(std::move(wrapped_converter)),
+ min_(min),
+ max_(max),
+ step_(step) {
+ HAL_LOG_ENTER();
+}
+
+template <typename TMetadata, typename TV4L2>
+int RangedConverter<TMetadata, TV4L2>::MetadataToV4L2(TMetadata value,
+ TV4L2* conversion) {
+ HAL_LOG_ENTER();
+
+ TV4L2 raw_conversion = 0;
+ int res = wrapped_converter_->MetadataToV4L2(value, &raw_conversion);
+ if (res) {
+ HAL_LOGE("Failed to perform underlying conversion.");
+ return res;
+ }
+
+ // Round down to step (steps start at min_).
+ raw_conversion -= (raw_conversion - min_) % step_;
+
+ // Clamp to range.
+ if (raw_conversion < min_) {
+ raw_conversion = min_;
+ } else if (raw_conversion > max_) {
+ raw_conversion = max_;
+ }
+
+ *conversion = raw_conversion;
+ return 0;
+}
+
+template <typename TMetadata, typename TV4L2>
+int RangedConverter<TMetadata, TV4L2>::V4L2ToMetadata(TV4L2 value,
+ TMetadata* conversion) {
+ HAL_LOG_ENTER();
+
+ return wrapped_converter_->V4L2ToMetadata(value, conversion);
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_RANGED_CONVERTER_H_
diff --git a/modules/camera/3_4/metadata/ranged_converter_test.cpp b/modules/camera/3_4/metadata/ranged_converter_test.cpp
new file mode 100644
index 0000000..2b5ccc6
--- /dev/null
+++ b/modules/camera/3_4/metadata/ranged_converter_test.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 "ranged_converter.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "converter_interface_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class RangedConverterTest : public Test {
+ protected:
+ virtual void SetUp() {
+ converter_.reset(new ConverterInterfaceMock<int, int32_t>());
+ dut_.reset(
+ new RangedConverter<int, int32_t>(converter_, min_, max_, step_));
+ }
+
+ virtual void ExpectConvert(int32_t converted, int32_t expected) {
+ int initial = 99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _))
+ .WillOnce(DoAll(SetArgPointee<1>(converted), Return(0)));
+
+ int32_t actual = expected + 1; // Initialize to non-expected value.
+ ASSERT_EQ(dut_->MetadataToV4L2(initial, &actual), 0);
+ EXPECT_EQ(actual, expected);
+ }
+
+ std::shared_ptr<ConverterInterfaceMock<int, int32_t>> converter_;
+ std::unique_ptr<RangedConverter<int, int32_t>> dut_;
+
+ const int32_t min_ = -11;
+ const int32_t max_ = 10;
+ const int32_t step_ = 3;
+};
+
+TEST_F(RangedConverterTest, NormalConversion) {
+ // A value that's in range and on step.
+ ExpectConvert(max_ - step_, max_ - step_);
+}
+
+TEST_F(RangedConverterTest, RoundingConversion) {
+ // A value that's in range but off step.
+ ExpectConvert(max_ - step_ + 1, max_ - step_);
+}
+
+TEST_F(RangedConverterTest, ClampUpConversion) {
+ // A value that's below range.
+ ExpectConvert(min_ - 1, min_);
+}
+
+TEST_F(RangedConverterTest, ClampDownConversion) {
+ // A value that's above range (even after fitting to step).
+ ExpectConvert(max_ + step_, max_);
+}
+
+TEST_F(RangedConverterTest, ConversionError) {
+ int initial = 99;
+ int err = -99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _)).WillOnce(Return(err));
+
+ int32_t unused;
+ EXPECT_EQ(dut_->MetadataToV4L2(initial, &unused), err);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/scaling_converter.h b/modules/camera/3_4/metadata/scaling_converter.h
new file mode 100644
index 0000000..42b08a7
--- /dev/null
+++ b/modules/camera/3_4/metadata/scaling_converter.h
@@ -0,0 +1,76 @@
+/*
+ * 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_METADATA_SCALING_CONVERTER_H_
+#define V4L2_CAMERA_HAL_METADATA_SCALING_CONVERTER_H_
+
+#include "../common.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// An ScalingConverter scales values up or down.
+template <typename TMetadata, typename TV4L2>
+class ScalingConverter : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ ScalingConverter(TMetadata v4l2_to_metadata_numerator,
+ TMetadata v4l2_to_metadata_denominator);
+
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) override;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) override;
+
+ private:
+ const TMetadata v4l2_to_metadata_numerator_;
+ const TMetadata v4l2_to_metadata_denominator_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScalingConverter);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename TMetadata, typename TV4L2>
+ScalingConverter<TMetadata, TV4L2>::ScalingConverter(
+ TMetadata v4l2_to_metadata_numerator,
+ TMetadata v4l2_to_metadata_denominator)
+ : v4l2_to_metadata_numerator_(v4l2_to_metadata_numerator),
+ v4l2_to_metadata_denominator_(v4l2_to_metadata_denominator),
+{
+ HAL_LOG_ENTER();
+}
+
+template <typename TMetadata, typename TV4L2>
+int ScalingConverter<TMetadata, TV4L2>::MetadataToV4L2(TMetadata value,
+ TV4L2* conversion) {
+ HAL_LOG_ENTER();
+
+ *conversion = static_cast<TV4L2>(value * v4l2_to_metadata_denominator_ /
+ v4l2_to_metadata_numerator_);
+ return 0;
+}
+
+template <typename TMetadata, typename TV4L2>
+int ScalingConverter<TMetadata, TV4L2>::V4L2ToMetadata(TV4L2 value,
+ TMetadata* conversion) {
+ HAL_LOG_ENTER();
+
+ *conversion = static_cast<TMetadata>(value) * v4l2_to_metadata_numerator_ /
+ v4l2_to_metadata_denominator_;
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_SCALING_CONVERTER_H_