Add request tracking.
RequestTracker keeps track of how many buffers are in flight for
each stream, and what frame numbers correspond to what requests/buffers.
BUG: 31044638
TEST: unit tests pass
Change-Id: I8ef3fcacdf8171514ea7f7eaf77301a641bff61e
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
index 12e8436..1211a08 100644
--- a/modules/camera/3_4/Android.mk
+++ b/modules/camera/3_4/Android.mk
@@ -39,10 +39,12 @@
v4l2_src_files := \
camera.cpp \
+ capture_request.cpp \
format_metadata_factory.cpp \
metadata/enum_converter.cpp \
metadata/metadata.cpp \
metadata/metadata_reader.cpp \
+ request_tracker.cpp \
static_properties.cpp \
stream.cpp \
stream_format.cpp \
@@ -70,6 +72,7 @@
metadata/tagged_control_delegate_test.cpp \
metadata/tagged_control_options_test.cpp \
metadata/v4l2_control_delegate_test.cpp \
+ request_tracker_test.cpp \
static_properties_test.cpp \
# V4L2 Camera HAL.
diff --git a/modules/camera/3_4/capture_request.cpp b/modules/camera/3_4/capture_request.cpp
new file mode 100644
index 0000000..00b20cd
--- /dev/null
+++ b/modules/camera/3_4/capture_request.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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 "capture_request.h"
+
+#include <set>
+
+namespace default_camera_hal {
+
+CaptureRequest::CaptureRequest() : CaptureRequest(nullptr) {}
+
+CaptureRequest::CaptureRequest(const camera3_capture_request_t* request) {
+ if (!request) {
+ return;
+ }
+
+ frame_number = request->frame_number;
+
+ // CameraMetadata makes copies of camera_metadata_t through the
+ // assignment operator (the constructor taking a camera_metadata_t*
+ // takes ownership instead).
+ settings = request->settings;
+
+ // camera3_stream_buffer_t can be default copy constructed,
+ // as its pointer values are handles, not ownerships.
+
+ // Copy the input buffer.
+ if (request->input_buffer) {
+ input_buffer =
+ std::make_unique<camera3_stream_buffer_t>(*request->input_buffer);
+ }
+
+ // Safely copy all the output buffers.
+ uint32_t num_output_buffers = request->num_output_buffers;
+ if (num_output_buffers < 0 || !request->output_buffers) {
+ num_output_buffers = 0;
+ }
+ output_buffers.insert(output_buffers.end(),
+ request->output_buffers,
+ request->output_buffers + num_output_buffers);
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/capture_request.h b/modules/camera/3_4/capture_request.h
new file mode 100644
index 0000000..e2f95fa
--- /dev/null
+++ b/modules/camera/3_4/capture_request.h
@@ -0,0 +1,43 @@
+/*
+ * 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 DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
+#define DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
+
+#include <memory>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+#include <hardware/camera3.h>
+
+namespace default_camera_hal {
+
+// A simple wrapper for camera3_capture_request_t,
+// with a constructor that makes a deep copy from the original struct.
+struct CaptureRequest {
+ uint32_t frame_number;
+ android::CameraMetadata settings;
+ std::unique_ptr<camera3_stream_buffer_t> input_buffer;
+ std::vector<camera3_stream_buffer_t> output_buffers;
+
+ CaptureRequest();
+ // Create a deep copy of |request|.
+ CaptureRequest(const camera3_capture_request_t* request);
+};
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
diff --git a/modules/camera/3_4/request_tracker.cpp b/modules/camera/3_4/request_tracker.cpp
new file mode 100644
index 0000000..60715b5
--- /dev/null
+++ b/modules/camera/3_4/request_tracker.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright 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 "request_tracker.h"
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "RequestTracker"
+#include <cutils/log.h>
+
+namespace default_camera_hal {
+
+RequestTracker::RequestTracker() {}
+
+RequestTracker::~RequestTracker() {}
+
+void RequestTracker::SetStreamConfiguration(
+ const camera3_stream_configuration_t& config) {
+ // Clear the old configuration.
+ ClearStreamConfiguration();
+ // Add an entry to the buffer tracking map for each configured stream.
+ for (size_t i = 0; i < config.num_streams; ++i) {
+ buffers_in_flight_.emplace(config.streams[i], 0);
+ }
+}
+
+void RequestTracker::ClearStreamConfiguration() {
+ // The keys of the in flight buffer map are the configured streams.
+ buffers_in_flight_.clear();
+}
+
+// Helper: get the streams used by a request.
+std::set<camera3_stream_t*> RequestStreams(const CaptureRequest& request) {
+ std::set<camera3_stream_t*> result;
+ if (request.input_buffer) {
+ result.insert(request.input_buffer->stream);
+ }
+ for (const auto& output_buffer : request.output_buffers) {
+ result.insert(output_buffer.stream);
+ }
+ return std::move(result);
+}
+
+bool RequestTracker::Add(std::unique_ptr<CaptureRequest> request) {
+ if (!CanAddRequest(*request)) {
+ return false;
+ }
+
+ // Add to the count for each stream used.
+ for (const auto stream : RequestStreams(*request)) {
+ ++buffers_in_flight_[stream];
+ }
+
+ // Store the request.
+ frames_in_flight_[request->frame_number] = std::move(request);
+
+ return true;
+}
+
+bool RequestTracker::Remove(uint32_t frame_number,
+ std::unique_ptr<CaptureRequest>* request) {
+ // Get the request.
+ std::unique_ptr<CaptureRequest> stored_request =
+ std::move(frames_in_flight_[frame_number]);
+ if (!stored_request) {
+ ALOGE("%s: Frame %u is not in flight.", __func__, frame_number);
+ return false;
+ }
+ frames_in_flight_.erase(frame_number);
+
+ // Decrement the counts of used streams.
+ for (const auto stream : RequestStreams(*stored_request)) {
+ --buffers_in_flight_[stream];
+ }
+
+ // Return the request if requested.
+ if (request) {
+ *request = std::move(stored_request);
+ }
+ return true;
+}
+
+void RequestTracker::Clear(
+ std::set<std::unique_ptr<CaptureRequest>>* requests) {
+ // If desired, extract all the currently in-flight requests.
+ if (requests) {
+ for (auto& frame_number_request : frames_in_flight_) {
+ requests->insert(std::move(frame_number_request.second));
+ }
+ }
+
+ // Clear out all tracking.
+ frames_in_flight_.clear();
+ buffers_in_flight_.clear();
+}
+
+bool RequestTracker::CanAddRequest(const CaptureRequest& request) const {
+ // Check that it's not a duplicate.
+ if (frames_in_flight_.count(request.frame_number) > 0) {
+ ALOGE("%s: Already tracking a request with frame number %d.",
+ __func__,
+ request.frame_number);
+ return false;
+ }
+
+ // Check that each stream has space
+ // (which implicitly checks if it is configured).
+ bool result = true;
+ for (const auto stream : RequestStreams(request)) {
+ if (StreamFull(stream)) {
+ ALOGE("%s: Stream %p is full.", __func__, stream);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool RequestTracker::StreamFull(const camera3_stream_t* handle) const {
+ const auto it = buffers_in_flight_.find(handle);
+ if (it == buffers_in_flight_.end()) {
+ // Unconfigured streams are implicitly full.
+ ALOGV("%s: Stream %p is not a configured stream.", __func__, handle);
+ return true;
+ } else {
+ return it->second >= it->first->max_buffers;
+ }
+}
+
+bool RequestTracker::InFlight(uint32_t frame_number) const {
+ return frames_in_flight_.count(frame_number) > 0;
+}
+
+bool RequestTracker::Empty() const {
+ return frames_in_flight_.empty();
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/request_tracker.h b/modules/camera/3_4/request_tracker.h
new file mode 100644
index 0000000..864c2f7
--- /dev/null
+++ b/modules/camera/3_4/request_tracker.h
@@ -0,0 +1,79 @@
+/*
+ * 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 DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
+#define DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include <hardware/camera3.h>
+
+#include "capture_request.h"
+#include "common.h"
+
+namespace default_camera_hal {
+
+// Keep track of what requests and streams are in flight.
+class RequestTracker {
+ public:
+ RequestTracker();
+ virtual ~RequestTracker();
+
+ // Configuration methods. Both have undefined effects on in-flight requests,
+ // and should only be called when empty.
+ // Add configured streams. Replaces the previous configuration if any.
+ virtual void SetStreamConfiguration(
+ const camera3_stream_configuration_t& config);
+ // Reset to no configured streams.
+ virtual void ClearStreamConfiguration();
+
+ // Tracking methods.
+ // Track a request.
+ // False if a request of the same frame number is already being tracked
+ virtual bool Add(std::unique_ptr<CaptureRequest> request);
+ // Stop tracking and retreive a request by frame number.
+ // False if the given frame number is not being tracked.
+ virtual bool Remove(uint32_t frame_number,
+ std::unique_ptr<CaptureRequest>* request = nullptr);
+ // Empty out all requests being tracked.
+ virtual void Clear(
+ std::set<std::unique_ptr<CaptureRequest>>* requests = nullptr);
+
+ // Accessors to check availability.
+ // Check that a request isn't already in flight, and won't overflow any
+ // streams.
+ virtual bool CanAddRequest(const CaptureRequest& request) const;
+ // True if the given stream is already at max capacity.
+ virtual bool StreamFull(const camera3_stream_t* handle) const;
+ // True if a request is being tracked for the given frame number.
+ virtual bool InFlight(uint32_t frame_number) const;
+ // True if no requests being tracked.
+ virtual bool Empty() const;
+
+ private:
+ // Track for each stream, how many buffers are in flight.
+ std::map<const camera3_stream_t*, size_t> buffers_in_flight_;
+ // Track the frames in flight.
+ std::map<uint32_t, std::unique_ptr<CaptureRequest>> frames_in_flight_;
+
+ DISALLOW_COPY_AND_ASSIGN(RequestTracker);
+};
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
diff --git a/modules/camera/3_4/request_tracker_test.cpp b/modules/camera/3_4/request_tracker_test.cpp
new file mode 100644
index 0000000..9a6313f
--- /dev/null
+++ b/modules/camera/3_4/request_tracker_test.cpp
@@ -0,0 +1,258 @@
+/*
+ * 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 "request_tracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace default_camera_hal {
+
+class RequestTrackerTest : public Test {
+ protected:
+ void SetUp() {
+ stream1_.max_buffers = 3;
+ stream2_.max_buffers = 3;
+ dut_.reset(new RequestTracker());
+ streams_ = {&stream1_, &stream2_};
+ camera3_stream_configuration_t config{streams_.size(), streams_.data(), 0};
+ dut_->SetStreamConfiguration(config);
+ }
+
+ std::unique_ptr<CaptureRequest> GenerateCaptureRequest(
+ uint32_t frame, std::vector<camera3_stream_t*> streams) {
+ std::unique_ptr<CaptureRequest> request =
+ std::make_unique<CaptureRequest>();
+
+ // Set the frame number and buffers.
+ request->frame_number = frame;
+ for (const auto stream : streams) {
+ // All we really care about for the buffers is which stream they're for.
+ camera3_stream_buffer_t buffer{stream, nullptr, 0, -1, -1};
+ request->output_buffers.push_back(buffer);
+ }
+
+ return std::move(request);
+ }
+
+ void AddRequest(uint32_t frame,
+ std::vector<camera3_stream_t*> streams,
+ bool expected = true) {
+ std::unique_ptr<CaptureRequest> request =
+ GenerateCaptureRequest(frame, streams);
+ EXPECT_EQ(dut_->CanAddRequest(*request), expected);
+ if (expected) {
+ EXPECT_FALSE(dut_->InFlight(frame));
+ }
+ EXPECT_EQ(dut_->Add(std::move(request)), expected);
+ if (expected) {
+ EXPECT_TRUE(dut_->InFlight(frame));
+ }
+ }
+
+ camera3_stream_t stream1_;
+ camera3_stream_t stream2_;
+ std::vector<camera3_stream_t*> streams_;
+ std::unique_ptr<RequestTracker> dut_;
+};
+
+TEST_F(RequestTrackerTest, AddValid) {
+ uint32_t frame = 34;
+ EXPECT_FALSE(dut_->InFlight(frame));
+ AddRequest(frame, {&stream1_});
+}
+
+TEST_F(RequestTrackerTest, AddInput) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ std::unique_ptr<CaptureRequest> expected = GenerateCaptureRequest(frame, {});
+ // Set the input buffer instead of any outputs.
+ expected->input_buffer.reset(
+ new camera3_stream_buffer_t{&stream1_, nullptr, 0, -1, -1});
+ stream1_.max_buffers = 1;
+
+ EXPECT_TRUE(dut_->Add(std::move(expected)));
+ EXPECT_TRUE(dut_->InFlight(frame));
+ // Should have added to the count of buffers for stream 1.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+}
+
+TEST_F(RequestTrackerTest, AddMultipleStreams) {
+ stream1_.max_buffers = 1;
+ stream2_.max_buffers = 1;
+
+ EXPECT_FALSE(dut_->StreamFull(&stream1_));
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+ // Add a request using both streams.
+ AddRequest(99, {&stream1_, &stream2_});
+
+ // Should both have been counted.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ EXPECT_TRUE(dut_->StreamFull(&stream2_));
+}
+
+TEST_F(RequestTrackerTest, AddUnconfigured) {
+ camera3_stream_t stream;
+ // Unconfigured should be considered full.
+ EXPECT_TRUE(dut_->StreamFull(&stream));
+ AddRequest(1, {&stream}, false);
+}
+
+TEST_F(RequestTrackerTest, AddPastCapacity) {
+ // Set the limit of stream 2 to 1.
+ stream2_.max_buffers = 1;
+
+ for (size_t i = 0; i < stream1_.max_buffers; ++i) {
+ EXPECT_FALSE(dut_->StreamFull(&stream1_));
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+ AddRequest(i, {&stream1_});
+ }
+ // Filled up stream 1.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ // Stream 2 should still not be full since nothing was added.
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+ // Limit has been hit, can't add more.
+ AddRequest(stream1_.max_buffers, {&stream1_, &stream2_}, false);
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ // Should not have added to the count of stream 2.
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+}
+
+TEST_F(RequestTrackerTest, AddDuplicate) {
+ uint32_t frame = 42;
+ AddRequest(frame, {&stream1_});
+ // Can't add a duplicate.
+ AddRequest(frame, {&stream2_}, false);
+}
+
+TEST_F(RequestTrackerTest, RemoveValid) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ std::unique_ptr<CaptureRequest> request =
+ GenerateCaptureRequest(frame, {&stream1_});
+ CaptureRequest* expected = request.get();
+ EXPECT_TRUE(dut_->Add(std::move(request)));
+ EXPECT_TRUE(dut_->InFlight(frame));
+ AddRequest(frame + 1, {&stream1_});
+ EXPECT_FALSE(dut_->Empty());
+
+ // Remove it.
+ request.reset();
+ EXPECT_TRUE(dut_->Remove(frame, &request));
+ // Should have removed only the desired request.
+ EXPECT_FALSE(dut_->Empty());
+
+ // Should have returned the desired request.
+ EXPECT_EQ(request.get(), expected);
+}
+
+TEST_F(RequestTrackerTest, RemoveNoResult) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ AddRequest(frame, {&stream1_});
+ EXPECT_FALSE(dut_->Empty());
+
+ // Remove it, but don't ask for the returned request.
+ EXPECT_TRUE(dut_->Remove(frame));
+ EXPECT_TRUE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, RemoveInvalid) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ AddRequest(frame, {&stream1_});
+ EXPECT_FALSE(dut_->Empty());
+
+ // Try to remove a different one.
+ uint32_t bad_frame = frame + 1;
+ EXPECT_FALSE(dut_->InFlight(bad_frame));
+ EXPECT_FALSE(dut_->Remove(frame + 1));
+ EXPECT_FALSE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, ClearRequests) {
+ // Create some requests.
+ uint32_t frame1 = 42;
+ uint32_t frame2 = frame1 + 1;
+ std::unique_ptr<CaptureRequest> request1 =
+ GenerateCaptureRequest(frame1, {&stream1_});
+ std::unique_ptr<CaptureRequest> request2 =
+ GenerateCaptureRequest(frame2, {&stream2_});
+ std::set<CaptureRequest*> expected;
+ expected.insert(request1.get());
+ expected.insert(request2.get());
+
+ // Insert them.
+ EXPECT_TRUE(dut_->Add(std::move(request1)));
+ EXPECT_TRUE(dut_->Add(std::move(request2)));
+ EXPECT_TRUE(dut_->InFlight(frame1));
+ EXPECT_TRUE(dut_->InFlight(frame2));
+ EXPECT_FALSE(dut_->Empty());
+ std::set<std::unique_ptr<CaptureRequest>> actual_wrapped;
+
+ // Clear them out.
+ dut_->Clear(&actual_wrapped);
+ EXPECT_TRUE(dut_->Empty());
+
+ // Get the hard pointer values for comparison.
+ std::set<CaptureRequest*> actual;
+ for (const auto& actual_request : actual_wrapped) {
+ actual.insert(actual_request.get());
+ }
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(RequestTrackerTest, ClearRequestsNoResult) {
+ // Add some requests.
+ EXPECT_TRUE(dut_->Empty());
+ AddRequest(1, {&stream1_});
+ AddRequest(2, {&stream2_});
+ EXPECT_FALSE(dut_->Empty());
+ // Don't bother getting the cleared requests.
+ dut_->Clear();
+ EXPECT_TRUE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, ClearConfiguration) {
+ EXPECT_FALSE(dut_->StreamFull(&stream1_));
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+ // Clear the configuration.
+ dut_->ClearStreamConfiguration();
+
+ // Both streams should be considered full now, since neither is configured.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ EXPECT_TRUE(dut_->StreamFull(&stream2_));
+}
+
+} // namespace default_camera_hal