Surround View Service implementation
Bug: 150412555
Test: m -j
Change-Id: If92953ccf951103cb62b30038c2b30d216719a2f
diff --git a/surround_view/service-impl/Android.bp b/surround_view/service-impl/Android.bp
new file mode 100644
index 0000000..b4dac9f
--- /dev/null
+++ b/surround_view/service-impl/Android.bp
@@ -0,0 +1,114 @@
+//
+// Copyright 2020 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.
+//
+
+cc_binary {
+ name: "android.automotive.sv.service@1.0-impl",
+ vendor: true,
+ srcs: [
+ "CoreLibSetupHelper.cpp",
+ "SurroundViewService.cpp",
+ "SurroundView2dSession.cpp",
+ "SurroundView3dSession.cpp",
+ "service.cpp",
+ ],
+ init_rc: ["android.automotive.sv.service@1.0-impl.rc"],
+ shared_libs: [
+ "android.hardware.automotive.sv@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libbinder",
+ "libcore_lib_shared",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "libhidlmemory",
+ "libui",
+ "libutils",
+ ],
+ required: [
+ "cam0.png",
+ "cam1.png",
+ "cam2.png",
+ "cam3.png",
+ ],
+ // Disable builds except for arm64 and emulator devices
+ enabled: false,
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ x86: {
+ enabled: true,
+ },
+ x86_64: {
+ enabled: true,
+ },
+ },
+ vintf_fragments: [
+ "manifest_android.hardware.automotive.sv@1.0.xml",
+ ],
+}
+
+cc_prebuilt_library_shared {
+ name: "libcore_lib_shared",
+ proprietary: true,
+ arch: {
+ arm64: {
+ srcs: ["lib/arm64/libcore_lib_shared.so"]
+ },
+ x86: {
+ srcs: ["lib/x86/libcore_lib.so"]
+ },
+ x86_64: {
+ srcs: ["lib/x86-64/libcore_lib.so"]
+ },
+ },
+ shared_libs: [
+ "libEGL",
+ "libGLESv2",
+ "libGLESv3",
+ "libc",
+ "libm",
+ "libdl",
+ "libz",
+ "liblog",
+ ],
+}
+
+prebuilt_etc {
+ name: "cam0.png",
+ src: "test_data/0.png",
+ sub_dir: "automotive/sv",
+}
+
+prebuilt_etc {
+ name: "cam1.png",
+ src: "test_data/1.png",
+ sub_dir: "automotive/sv",
+}
+
+prebuilt_etc {
+ name: "cam2.png",
+ src: "test_data/2.png",
+ sub_dir: "automotive/sv",
+}
+
+prebuilt_etc {
+ name: "cam3.png",
+ src: "test_data/3.png",
+ sub_dir: "automotive/sv",
+}
+
diff --git a/surround_view/service-impl/CoreLibSetupHelper.cpp b/surround_view/service-impl/CoreLibSetupHelper.cpp
new file mode 100644
index 0000000..4812efe
--- /dev/null
+++ b/surround_view/service-impl/CoreLibSetupHelper.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2020 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 "CoreLibSetupHelper.h"
+
+using namespace android_auto::surround_view;
+
+namespace android_auto {
+namespace surround_view {
+
+vector<SurroundViewCameraParams> GetCameras() {
+ std::vector<android_auto::surround_view::SurroundViewCameraParams> cameras;
+
+ // Camera 1.
+ {
+ android_auto::surround_view::SurroundViewCameraParams camera_params;
+
+ camera_params.intrinsics[0] = 608.0026093794693;
+ camera_params.intrinsics[1] = 0.0;
+ camera_params.intrinsics[2] = 968.699544102168;
+ camera_params.intrinsics[3] = 0.0;
+ camera_params.intrinsics[4] = 608.205469489769;
+ camera_params.intrinsics[5] = 476.38843298898996;
+ camera_params.intrinsics[6] = 0.0;
+ camera_params.intrinsics[7] = 0.0;
+ camera_params.intrinsics[8] = 1.0;
+
+ camera_params.distorion[0] = -0.03711481733589263;
+ camera_params.distorion[1] = -0.0014805627895442888;
+ camera_params.distorion[2] = -0.00030212056866592464;
+ camera_params.distorion[3] = -0.00020149538570397933;
+
+ camera_params.rvec[0] = 2.26308;
+ camera_params.rvec[1] = 0.0382788;
+ camera_params.rvec[2] = -0.0220549;
+
+ camera_params.tvec[0] = -7.8028875403817685e-02;
+ camera_params.tvec[1] = 1.4537396465103221e+00;
+ camera_params.tvec[2] = -8.4197165554645001e-02;
+
+ camera_params.size.width = 1920;
+ camera_params.size.height = 1024;
+
+ camera_params.circular_fov = 179;
+
+ cameras.push_back(camera_params);
+ }
+
+ // Camera 2.
+ {
+ android_auto::surround_view::SurroundViewCameraParams camera_params;
+
+ camera_params.intrinsics[0] = 607.8691721095306;
+ camera_params.intrinsics[1] = 0.0;
+ camera_params.intrinsics[2] = 975.5686146375716;
+ camera_params.intrinsics[3] = 0.0;
+ camera_params.intrinsics[4] = 608.0112887189435;
+ camera_params.intrinsics[5] = 481.1938786570715;
+ camera_params.intrinsics[6] = 0.0;
+ camera_params.intrinsics[7] = 0.0;
+ camera_params.intrinsics[8] = 1.0;
+
+ camera_params.distorion[0] = -0.040116809827977926;
+ camera_params.distorion[1] = 0.0028769489398543014;
+ camera_params.distorion[2] = -0.002651039958977229;
+ camera_params.distorion[3] = 0.00024260630476736675;
+
+ camera_params.rvec[0] = 1.67415;
+ camera_params.rvec[1] = -1.74075;
+ camera_params.rvec[2] = 0.789399;
+
+ camera_params.tvec[0] = 2.9715052384687407e-01;
+ camera_params.tvec[1] = 1.1407102692699396e+00;
+ camera_params.tvec[2] = 3.0074545273489206e-01;
+
+ camera_params.size.width = 1920;
+ camera_params.size.height = 1024;
+
+ camera_params.circular_fov = 179;
+
+ cameras.push_back(camera_params);
+ }
+
+ // Camera 3.
+ {
+ android_auto::surround_view::SurroundViewCameraParams camera_params;
+
+ camera_params.intrinsics[0] = 608.557299289448;
+ camera_params.intrinsics[1] = 0.0;
+ camera_params.intrinsics[2] = 960.1949354417656;
+ camera_params.intrinsics[3] = 0.0;
+ camera_params.intrinsics[4] = 608.8093878512448;
+ camera_params.intrinsics[5] = 474.74744054048256;
+ camera_params.intrinsics[6] = 0.0;
+ camera_params.intrinsics[7] = 0.0;
+ camera_params.intrinsics[8] = 1.0;
+
+ camera_params.distorion[0] = -0.03998488563470043;
+ camera_params.distorion[1] = 0.0024786686909103388;
+ camera_params.distorion[2] = -0.002354736769480817;
+ camera_params.distorion[3] = 0.00018369619088506146;
+
+ camera_params.rvec[0] = -0.106409;
+ camera_params.rvec[1] = -2.83697;
+ camera_params.rvec[2] = 1.28629;
+
+ camera_params.tvec[0] = 1.7115269161259747e-01;
+ camera_params.tvec[1] = 1.4376160762596599e+00;
+ camera_params.tvec[2] = -1.9028844233159006e-02;
+
+ camera_params.size.width = 1920;
+ camera_params.size.height = 1024;
+
+ camera_params.circular_fov = 179;
+
+ cameras.push_back(camera_params);
+ }
+
+ // Camera 4.
+ {
+ android_auto::surround_view::SurroundViewCameraParams camera_params;
+
+ camera_params.intrinsics[0] = 608.1221963545495;
+ camera_params.intrinsics[1] = 0.0;
+ camera_params.intrinsics[2] = 943.6280444638576;
+ camera_params.intrinsics[3] = 0.0;
+ camera_params.intrinsics[4] = 608.0523818661524;
+ camera_params.intrinsics[5] = 474.8564698210861;
+ camera_params.intrinsics[6] = 0.0;
+ camera_params.intrinsics[7] = 0.0;
+ camera_params.intrinsics[8] = 1.0;
+
+ camera_params.distorion[0] = -0.038096507459563965;
+ camera_params.distorion[1] = 0.0004008114278766646;
+ camera_params.distorion[2] = -0.0013549275607082035;
+ camera_params.distorion[3] = -5.9961182248325556e-06;
+
+ camera_params.rvec[0] = 1.63019;
+ camera_params.rvec[1] = 1.76475;
+ camera_params.rvec[2] = -0.827941;
+
+ camera_params.tvec[0] = -3.0842691427126512e-01;
+ camera_params.tvec[1] = 1.0884122033556984e+00;
+ camera_params.tvec[2] = 3.4419058255954926e-01;
+
+ camera_params.size.width = 1920;
+ camera_params.size.height = 1024;
+
+ camera_params.circular_fov = 179;
+
+ cameras.push_back(camera_params);
+ }
+ return cameras;
+
+}
+
+SurroundView2dParams Get2dParams() {
+ android_auto::surround_view::Size2dInteger
+ resolution{ /*width=*/ 1024, /*height*/ 768};
+ // make sure resolution has the same ratio with physical_size.
+ // {480 *360 }
+ android_auto::surround_view::Size2dFloat physical_size{8.0, 6.0};
+ android_auto::surround_view::Coordinate2dFloat physical_center{0, 0};
+
+ return android_auto::surround_view::SurroundView2dParams(
+ resolution, physical_size, physical_center);
+}
+
+SurroundView3dParams Get3dParams() {
+ return android_auto::surround_view::SurroundView3dParams(
+ /*plane_radius=*/ 8.0f,
+ /*plane_divisions=*/ 50,
+ /*curve_height=*/ 6.0f,
+ /*curve_divisions=*/ 50,
+ /*angular_divisions=*/ 90,
+ /*curve_coefficient=*/ 3.0f,
+ /*resolution=*/ Size2dInteger(1024, 768));
+}
+
+BoundingBox GetBoundingBox() {
+ return android_auto::surround_view::BoundingBox(
+ /*x=*/ -0.01f,
+ /*y=*/ 0.01f,
+ /*width=*/ 0.01f,
+ /*height=*/ 0.01f);
+}
+
+vector<float> GetUndistortionScales() {
+ return vector<float>{1.0f, 1.0f, 1.0f, 1.0f};
+}
+
+
+} // namespace surround_view
+} // namespace audroid_auto
+
diff --git a/surround_view/service-impl/CoreLibSetupHelper.h b/surround_view/service-impl/CoreLibSetupHelper.h
new file mode 100644
index 0000000..889ebf2
--- /dev/null
+++ b/surround_view/service-impl/CoreLibSetupHelper.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include "core_lib.h"
+
+using namespace std;
+
+// TODO(b/150412555): The helper method should only be used for testing
+// purposes once EVS camera is used.
+namespace android_auto {
+namespace surround_view {
+
+vector<SurroundViewCameraParams> GetCameras();
+
+SurroundView2dParams Get2dParams();
+
+SurroundView3dParams Get3dParams();
+
+BoundingBox GetBoundingBox();
+
+vector<float> GetUndistortionScales();
+
+} // namespace surround_view
+} // namespace android_auto
+
diff --git a/surround_view/service-impl/README b/surround_view/service-impl/README
new file mode 100644
index 0000000..d7b5e7c
--- /dev/null
+++ b/surround_view/service-impl/README
@@ -0,0 +1 @@
+The core_lib.h and the .so files are copied from google3 at Mar 19, 2020.
diff --git a/surround_view/service-impl/SurroundView2dSession.cpp b/surround_view/service-impl/SurroundView2dSession.cpp
new file mode 100644
index 0000000..a2d1fb5
--- /dev/null
+++ b/surround_view/service-impl/SurroundView2dSession.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2020 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.
+ */
+#define LOG_TAG "SurroundViewService"
+
+#include <android-base/logging.h>
+#include <android/hardware_buffer.h>
+#include <utils/SystemClock.h>
+
+#include "SurroundView2dSession.h"
+#include "CoreLibSetupHelper.h"
+
+using namespace android_auto::surround_view;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+static const char kGrayColor = 128;
+static const int kNumChannels = 3;
+static const int kFrameDelayInMilliseconds = 30;
+
+SurroundView2dSession::SurroundView2dSession() :
+ mStreamState(STOPPED) {
+ mEvsCameraIds = {"0", "1", "2", "3"};
+}
+
+// Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession
+Return<SvResult> SurroundView2dSession::startStream(
+ const sp<ISurroundViewStream>& stream) {
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock<mutex> lock(mAccessLock);
+
+ if (!mIsInitialized && !initialize()) {
+ LOG(ERROR) << "There is an error while initializing the use case. "
+ << "Exiting";
+ return SvResult::INTERNAL_ERROR;
+ }
+
+ if (mStreamState != STOPPED) {
+ LOG(ERROR) << "Ignoring startVideoStream call"
+ << "when a stream is already running.";
+ return SvResult::INTERNAL_ERROR;
+ }
+
+ if (stream == nullptr) {
+ LOG(ERROR) << "The input stream is invalid";
+ return SvResult::INTERNAL_ERROR;
+ }
+ mStream = stream;
+
+ LOG(DEBUG) << "Notify SvEvent::STREAM_STARTED";
+ mStream->notify(SvEvent::STREAM_STARTED);
+
+ // Start the frame generation thread
+ mStreamState = RUNNING;
+ mCaptureThread = thread([this](){
+ generateFrames();
+ });
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView2dSession::stopStream() {
+ LOG(DEBUG) << __FUNCTION__;
+ unique_lock<mutex> lock(mAccessLock);
+
+ if (mStreamState == RUNNING) {
+ // Tell the GenerateFrames loop we want it to stop
+ mStreamState = STOPPING;
+
+ // Block outside the mutex until the "stop" flag has been acknowledged
+ // We won't send any more frames, but the client might still get some
+ // already in flight
+ LOG(DEBUG) << __FUNCTION__ << "Waiting for stream thread to end...";
+ lock.unlock();
+ mCaptureThread.join();
+ lock.lock();
+
+ mStreamState = STOPPED;
+ mStream = nullptr;
+ LOG(DEBUG) << "Stream marked STOPPED.";
+ }
+
+ return {};
+}
+
+Return<void> SurroundView2dSession::doneWithFrames(
+ const SvFramesDesc& svFramesDesc){
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock <mutex> lock(mAccessLock);
+
+ framesRecord.inUse = false;
+
+ (void)svFramesDesc;
+ return {};
+}
+
+// Methods from ISurroundView2dSession follow.
+Return<void> SurroundView2dSession::get2dMappingInfo(
+ get2dMappingInfo_cb _hidl_cb) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ _hidl_cb(mInfo);
+ return {};
+}
+
+Return<SvResult> SurroundView2dSession::set2dConfig(
+ const Sv2dConfig& sv2dConfig) {
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock <mutex> lock(mAccessLock);
+
+ if (sv2dConfig.width <=0 || sv2dConfig.width > 4096) {
+ LOG(WARNING) << "The width of 2d config is out of the range (0, 4096]"
+ << "Ignored!";
+ return SvResult::INVALID_ARG;
+ }
+
+ mConfig.width = sv2dConfig.width;
+ mConfig.blending = sv2dConfig.blending;
+ mHeight = mConfig.width * mInfo.height / mInfo.width;
+
+ if (mStream != nullptr) {
+ LOG(DEBUG) << "Notify SvEvent::CONFIG_UPDATED";
+ mStream->notify(SvEvent::CONFIG_UPDATED);
+ }
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView2dSession::get2dConfig(get2dConfig_cb _hidl_cb) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ _hidl_cb(mConfig);
+ return {};
+}
+
+Return<void> SurroundView2dSession::projectCameraPoints(
+ const hidl_vec<Point2dInt>& points2dCamera,
+ const hidl_string& cameraId,
+ projectCameraPoints_cb _hidl_cb) {
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock <mutex> lock(mAccessLock);
+
+ bool cameraIdFound = false;
+ for (auto& evsCameraId : mEvsCameraIds) {
+ if (cameraId == evsCameraId) {
+ cameraIdFound = true;
+ LOG(INFO) << "Camera id found.";
+ break;
+ }
+ }
+
+ if (!cameraIdFound) {
+ LOG(ERROR) << "Camera id not found.";
+ _hidl_cb({});
+ return {};
+ }
+
+ hidl_vec<Point2dFloat> outPoints;
+ outPoints.resize(points2dCamera.size());
+
+ int width = mConfig.width;
+ int height = mHeight;
+ for (int i=0; i<points2dCamera.size(); i++) {
+ // Assuming all the points in the image frame can be projected into 2d
+ // Surround View space. Otherwise cannot.
+ if (points2dCamera[i].x < 0 || points2dCamera[i].x > width-1 ||
+ points2dCamera[i].y < 0 || points2dCamera[i].y > height-1) {
+ LOG(WARNING) << __FUNCTION__
+ << ": gets invalid 2d camera points. Ignored";
+ outPoints[i].isValid = false;
+ outPoints[i].x = 10000;
+ outPoints[i].y = 10000;
+ } else {
+ outPoints[i].isValid = true;
+ outPoints[i].x = 0;
+ outPoints[i].y = 0;
+ }
+ }
+
+ _hidl_cb(outPoints);
+ return {};
+}
+
+void SurroundView2dSession::generateFrames() {
+ int sequenceId = 0;
+
+ while(true) {
+ {
+ scoped_lock<mutex> lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Break out of our main thread loop
+ LOG(INFO) << "StreamState does not equal to RUNNING. "
+ << "Exiting the loop";
+ break;
+ }
+
+ if (mOutputWidth != mConfig.width || mOutputHeight != mHeight) {
+ LOG(DEBUG) << "Config changed. Re-allocate memory."
+ << " Old width: "
+ << mOutputWidth
+ << " Old height: "
+ << mOutputHeight
+ << " New width: "
+ << mConfig.width
+ << " New height: "
+ << mHeight;
+ delete[] static_cast<char*>(mOutputPointer.data_pointer);
+ mOutputWidth = mConfig.width;
+ mOutputHeight = mHeight;
+ mOutputPointer.height = mOutputHeight;
+ mOutputPointer.width = mOutputWidth;
+ mOutputPointer.format = Format::RGB;
+ mOutputPointer.data_pointer =
+ new char[mOutputHeight * mOutputWidth * kNumChannels];
+
+ if (!mOutputPointer.data_pointer) {
+ LOG(ERROR) << "Memory allocation failed. Exiting.";
+ break;
+ }
+
+ Size2dInteger size = Size2dInteger(mOutputWidth, mOutputHeight);
+ mSurroundView->Update2dOutputResolution(size);
+
+ mSvTexture = new GraphicBuffer(mOutputWidth,
+ mOutputHeight,
+ HAL_PIXEL_FORMAT_RGB_888,
+ 1,
+ GRALLOC_USAGE_HW_TEXTURE,
+ "SvTexture");
+ if (mSvTexture->initCheck() == OK) {
+ LOG(INFO) << "Successfully allocated Graphic Buffer";
+ } else {
+ LOG(ERROR) << "Failed to allocate Graphic Buffer";
+ break;
+ }
+ }
+ }
+
+ if (mSurroundView->Get2dSurroundView(mInputPointers, &mOutputPointer)) {
+ LOG(INFO) << "Get2dSurroundView succeeded";
+ } else {
+ LOG(ERROR) << "Get2dSurroundView failed. "
+ << "Using memset to initialize to gray";
+ memset(mOutputPointer.data_pointer, kGrayColor,
+ mOutputHeight * mOutputWidth * kNumChannels);
+ }
+
+ void* textureDataPtr = nullptr;
+ mSvTexture->lock(GRALLOC_USAGE_SW_WRITE_OFTEN
+ | GRALLOC_USAGE_SW_READ_NEVER,
+ &textureDataPtr);
+ if (!textureDataPtr) {
+ LOG(ERROR) << "Failed to gain write access to GraphicBuffer!";
+ break;
+ }
+
+ // Note: there is a chance that the stride of the texture is not the same
+ // as the width. For example, when the input frame is 1920 * 1080, the
+ // width is 1080, but the stride is 2048. So we'd better copy the data line
+ // by line, instead of single memcpy.
+ uint8_t* writePtr = static_cast<uint8_t*>(textureDataPtr);
+ uint8_t* readPtr = static_cast<uint8_t*>(mOutputPointer.data_pointer);
+ const int readStride = mOutputWidth * kNumChannels;
+ const int writeStride = mSvTexture->getStride() * kNumChannels;
+ if (readStride == writeStride) {
+ memcpy(writePtr, readPtr, readStride * mSvTexture->getHeight());
+ } else {
+ for (int i=0; i<mSvTexture->getHeight(); i++) {
+ memcpy(writePtr, readPtr, readStride);
+ writePtr = writePtr + writeStride;
+ readPtr = readPtr + readStride;
+ }
+ }
+ LOG(INFO) << "memcpy finished";
+ mSvTexture->unlock();
+
+ ANativeWindowBuffer* buffer = mSvTexture->getNativeBuffer();
+ LOG(DEBUG) << "ANativeWindowBuffer->handle: "
+ << buffer->handle;
+
+ framesRecord.frames.svBuffers.resize(1);
+ SvBuffer& svBuffer = framesRecord.frames.svBuffers[0];
+ svBuffer.viewId = 0;
+ svBuffer.hardwareBuffer.nativeHandle = buffer->handle;
+ AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<AHardwareBuffer_Desc *>(
+ &svBuffer.hardwareBuffer.description);
+ pDesc->width = mOutputWidth;
+ pDesc->height = mOutputHeight;
+ pDesc->layers = 1;
+ pDesc->usage = GRALLOC_USAGE_HW_TEXTURE;
+ pDesc->stride = mSvTexture->getStride();
+ pDesc->format = HAL_PIXEL_FORMAT_RGB_888;
+ framesRecord.frames.timestampNs = elapsedRealtimeNano();
+ framesRecord.frames.sequenceId = sequenceId++;
+
+ {
+ scoped_lock<mutex> lock(mAccessLock);
+
+ if (framesRecord.inUse) {
+ LOG(DEBUG) << "Notify SvEvent::FRAME_DROPPED";
+ mStream->notify(SvEvent::FRAME_DROPPED);
+ } else {
+ framesRecord.inUse = true;
+ mStream->receiveFrames(framesRecord.frames);
+ }
+ }
+
+ // TODO(b/150412555): adding delays explicitly. This delay should be
+ // removed when EVS camera is used.
+ this_thread::sleep_for(chrono::milliseconds(
+ kFrameDelayInMilliseconds));
+ }
+
+ // If we've been asked to stop, send an event to signal the actual
+ // end of stream
+ LOG(DEBUG) << "Notify SvEvent::STREAM_STOPPED";
+ mStream->notify(SvEvent::STREAM_STOPPED);
+}
+
+bool SurroundView2dSession::initialize() {
+ lock_guard<mutex> lock(mAccessLock, adopt_lock);
+
+ // TODO(b/150412555): ask core-lib team to add API description for "create"
+ // method in the .h file.
+ // The create method will never return a null pointer based the API
+ // description.
+ mSurroundView = unique_ptr<SurroundView>(Create());
+
+ mSurroundView->SetStaticData(GetCameras(), Get2dParams(), Get3dParams(),
+ GetUndistortionScales(), GetBoundingBox());
+
+ // TODO(b/150412555): remove after EVS camera is used
+ mInputPointers = mSurroundView->ReadImages(
+ "/etc/automotive/sv/cam0.png",
+ "/etc/automotive/sv/cam1.png",
+ "/etc/automotive/sv/cam2.png",
+ "/etc/automotive/sv/cam3.png");
+ if (mInputPointers.size() == 4
+ && mInputPointers[0].cpu_data_pointer != nullptr) {
+ LOG(INFO) << "ReadImages succeeded";
+ } else {
+ LOG(ERROR) << "Failed to read images";
+ return false;
+ }
+
+ mOutputWidth = Get2dParams().resolution.width;
+ mOutputHeight = Get2dParams().resolution.height;
+
+ mConfig.width = mOutputWidth;
+ mConfig.blending = SvQuality::HIGH;
+ mHeight = mOutputHeight;
+
+ mOutputPointer.height = mOutputHeight;
+ mOutputPointer.width = mOutputWidth;
+ mOutputPointer.format = mInputPointers[0].format;
+ mOutputPointer.data_pointer = new char[
+ mOutputHeight * mOutputWidth * kNumChannels];
+
+ if (!mOutputPointer.data_pointer) {
+ LOG(ERROR) << "Memory allocation failed. Exiting.";
+ return false;
+ }
+
+ mSvTexture = new GraphicBuffer(mOutputWidth,
+ mOutputHeight,
+ HAL_PIXEL_FORMAT_RGB_888,
+ 1,
+ GRALLOC_USAGE_HW_TEXTURE,
+ "SvTexture");
+
+ //TODO(b/150412555): the 2d mapping info should be read from config file.
+ mInfo.width = 8;
+ mInfo.height = 6;
+ mInfo.center.isValid = true;
+ mInfo.center.x = 0;
+ mInfo.center.y = 0;
+
+ if (mSvTexture->initCheck() == OK) {
+ LOG(INFO) << "Successfully allocated Graphic Buffer";
+ } else {
+ LOG(ERROR) << "Failed to allocate Graphic Buffer";
+ return false;
+ }
+
+ if (mSurroundView->Start2dPipeline()) {
+ LOG(INFO) << "Start2dPipeline succeeded";
+ } else {
+ LOG(ERROR) << "Start2dPipeline failed";
+ return false;
+ }
+
+ mIsInitialized = true;
+ return true;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/surround_view/service-impl/SurroundView2dSession.h b/surround_view/service-impl/SurroundView2dSession.h
new file mode 100644
index 0000000..c9591f1
--- /dev/null
+++ b/surround_view/service-impl/SurroundView2dSession.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include "CoreLibSetupHelper.h"
+#include <thread>
+
+#include <ui/GraphicBuffer.h>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+
+using namespace android_auto::surround_view;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundView2dSession : public ISurroundView2dSession {
+public:
+ SurroundView2dSession();
+
+ // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+ Return<SvResult> startStream(
+ const sp<ISurroundViewStream>& stream) override;
+ Return<void> stopStream() override;
+ Return<void> doneWithFrames(const SvFramesDesc& svFramesDesc) override;
+
+ // Methods from ISurroundView2dSession follow.
+ Return<void> get2dMappingInfo(get2dMappingInfo_cb _hidl_cb) override;
+ Return<SvResult> set2dConfig(const Sv2dConfig& sv2dConfig) override;
+ Return<void> get2dConfig(get2dConfig_cb _hidl_cb) override;
+ Return<void> projectCameraPoints(
+ const hidl_vec<Point2dInt>& points2dCamera,
+ const hidl_string& cameraId,
+ projectCameraPoints_cb _hidl_cb) override;
+
+private:
+ void generateFrames();
+ bool initialize();
+
+ enum StreamStateValues {
+ STOPPED,
+ RUNNING,
+ STOPPING,
+ DEAD,
+ };
+ StreamStateValues mStreamState GUARDED_BY(mAccessLock);
+
+ // Stream subscribed for the session.
+ sp<ISurroundViewStream> mStream GUARDED_BY(mAccessLock);
+
+ Sv2dConfig mConfig GUARDED_BY(mAccessLock);
+ int mHeight GUARDED_BY(mAccessLock);
+ Sv2dMappingInfo mInfo GUARDED_BY(mAccessLock);
+
+ thread mCaptureThread GUARDED_BY(mAccessLock);
+
+ struct FramesRecord {
+ SvFramesDesc frames;
+ bool inUse = false;
+ };
+
+ FramesRecord framesRecord GUARDED_BY(mAccessLock);
+
+ // Synchronization necessary to deconflict mCaptureThread from the main
+ // service thread
+ mutex mAccessLock;
+
+ vector<string> mEvsCameraIds GUARDED_BY(mAccessLock);
+
+ unique_ptr<SurroundView> mSurroundView GUARDED_BY(mAccessLock);
+
+ vector<SurroundViewInputBufferPointers>
+ mInputPointers GUARDED_BY(mAccessLock);
+ SurroundViewResultPointer mOutputPointer GUARDED_BY(mAccessLock);
+ int mOutputWidth, mOutputHeight GUARDED_BY(mAccessLock);
+
+ sp<GraphicBuffer> mSvTexture GUARDED_BY(mAccessLock);
+
+ bool mIsInitialized GUARDED_BY(mAccessLock) = false;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/surround_view/service-impl/SurroundView3dSession.cpp b/surround_view/service-impl/SurroundView3dSession.cpp
new file mode 100644
index 0000000..9eed0cd
--- /dev/null
+++ b/surround_view/service-impl/SurroundView3dSession.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright 2020 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.
+ */
+#define LOG_TAG "SurroundViewService"
+
+#include <android-base/logging.h>
+#include <android/hardware_buffer.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidlmemory/mapping.h>
+#include <set>
+#include <utils/SystemClock.h>
+
+#include "SurroundView3dSession.h"
+#include "sv_3d_params.h"
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::hidl_memory;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+static const char kGrayColor = 128;
+static const int kNumChannels = 4;
+
+SurroundView3dSession::SurroundView3dSession() :
+ mStreamState(STOPPED){
+ mEvsCameraIds = {"0" , "1", "2", "3"};
+}
+
+// Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+Return<SvResult> SurroundView3dSession::startStream(
+ const sp<ISurroundViewStream>& stream) {
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock<mutex> lock(mAccessLock);
+
+ if (!mIsInitialized && !initialize()) {
+ LOG(ERROR) << "There is an error while initializing the use case. "
+ << "Exiting";
+ return SvResult::INTERNAL_ERROR;
+ }
+
+ if (mStreamState != STOPPED) {
+ LOG(ERROR) << "Ignoring startVideoStream call when a stream is "
+ << "already running.";
+ return SvResult::INTERNAL_ERROR;
+ }
+
+ if (mViews.empty()) {
+ LOG(ERROR) << "No views have been set for current Surround View"
+ << "3d Session. Please call setViews before starting"
+ << "the stream.";
+ return SvResult::VIEW_NOT_SET;
+ }
+
+ if (stream == nullptr) {
+ LOG(ERROR) << "The input stream is invalid";
+ return SvResult::INTERNAL_ERROR;
+ }
+ mStream = stream;
+
+ LOG(DEBUG) << "Notify SvEvent::STREAM_STARTED";
+ mStream->notify(SvEvent::STREAM_STARTED);
+
+ // Start the frame generation thread
+ mStreamState = RUNNING;
+ mCaptureThread = thread([this](){
+ generateFrames();
+ });
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::stopStream() {
+ LOG(DEBUG) << __FUNCTION__;
+ unique_lock <mutex> lock(mAccessLock);
+
+ if (mStreamState == RUNNING) {
+ // Tell the GenerateFrames loop we want it to stop
+ mStreamState = STOPPING;
+
+ // Block outside the mutex until the "stop" flag has been acknowledged
+ // We won't send any more frames, but the client might still get some
+ // already in flight
+ LOG(DEBUG) << __FUNCTION__ << ": Waiting for stream thread to end...";
+ lock.unlock();
+ mCaptureThread.join();
+ lock.lock();
+
+ mStreamState = STOPPED;
+ mStream = nullptr;
+ LOG(DEBUG) << "Stream marked STOPPED.";
+ }
+
+ return {};
+}
+
+Return<void> SurroundView3dSession::doneWithFrames(
+ const SvFramesDesc& svFramesDesc){
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock <mutex> lock(mAccessLock);
+
+ framesRecord.inUse = false;
+
+ (void)svFramesDesc;
+ return {};
+}
+
+// Methods from ISurroundView3dSession follow.
+Return<SvResult> SurroundView3dSession::setViews(
+ const hidl_vec<View3d>& views) {
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock <mutex> lock(mAccessLock);
+
+ mViews.resize(views.size());
+ for (int i=0; i<views.size(); i++) {
+ mViews[i] = views[i];
+ }
+
+ return SvResult::OK;
+}
+
+Return<SvResult> SurroundView3dSession::set3dConfig(const Sv3dConfig& sv3dConfig) {
+ LOG(DEBUG) << __FUNCTION__;
+ scoped_lock <mutex> lock(mAccessLock);
+
+ if (sv3dConfig.width <=0 || sv3dConfig.width > 4096) {
+ LOG(WARNING) << "The width of 3d config is out of the range (0, 4096]"
+ << "Ignored!";
+ return SvResult::INVALID_ARG;
+ }
+
+ if (sv3dConfig.height <=0 || sv3dConfig.height > 4096) {
+ LOG(WARNING) << "The height of 3d config is out of the range (0, 4096]"
+ << "Ignored!";
+ return SvResult::INVALID_ARG;
+ }
+
+ mConfig.width = sv3dConfig.width;
+ mConfig.height = sv3dConfig.height;
+ mConfig.carDetails = sv3dConfig.carDetails;
+
+ if (mStream != nullptr) {
+ LOG(DEBUG) << "Notify SvEvent::CONFIG_UPDATED";
+ mStream->notify(SvEvent::CONFIG_UPDATED);
+ }
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::get3dConfig(get3dConfig_cb _hidl_cb) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ _hidl_cb(mConfig);
+ return {};
+}
+
+bool VerifyOverlayData(const OverlaysData& overlaysData) {
+ // Check size of shared memory matches overlaysMemoryDesc.
+ const int kVertexSize = 16;
+ const int kIdSize = 2;
+ int memDescSize = 0;
+ for (auto& overlayMemDesc : overlaysData.overlaysMemoryDesc) {
+ memDescSize += kIdSize + kVertexSize * overlayMemDesc.verticesCount;
+ }
+ if (memDescSize != overlaysData.overlaysMemory.size()) {
+ LOG(ERROR) << "shared memory and overlaysMemoryDesc size mismatch.";
+ return false;
+ }
+
+ // Map memory.
+ sp<IMemory> pSharedMemory = mapMemory(overlaysData.overlaysMemory);
+ if(pSharedMemory == nullptr) {
+ LOG(ERROR) << "mapMemory failed.";
+ return false;
+ }
+
+ // Get Data pointer.
+ uint8_t* pData = static_cast<uint8_t*>(
+ static_cast<void*>(pSharedMemory->getPointer()));
+ if (pData == nullptr) {
+ LOG(ERROR) << "Shared memory getPointer() failed.";
+ return false;
+ }
+
+ int idOffset = 0;
+ set<uint16_t> overlayIdSet;
+ for (auto& overlayMemDesc : overlaysData.overlaysMemoryDesc) {
+
+ if (overlayIdSet.find(overlayMemDesc.id) != overlayIdSet.end()) {
+ LOG(ERROR) << "Duplicate id within memory descriptor.";
+ return false;
+ }
+ overlayIdSet.insert(overlayMemDesc.id);
+
+ if(overlayMemDesc.verticesCount < 3) {
+ LOG(ERROR) << "Less than 3 vertices.";
+ return false;
+ }
+
+ if (overlayMemDesc.overlayPrimitive == OverlayPrimitive::TRIANGLES &&
+ overlayMemDesc.verticesCount % 3 != 0) {
+ LOG(ERROR) << "Triangles primitive does not have vertices "
+ << "multiple of 3.";
+ return false;
+ }
+
+ const uint16_t overlayId = *((uint16_t*)(pData + idOffset));
+
+ if (overlayId != overlayMemDesc.id) {
+ LOG(ERROR) << "Overlay id mismatch "
+ << overlayId
+ << ", "
+ << overlayMemDesc.id;
+ return false;
+ }
+
+ idOffset += kIdSize + (kVertexSize * overlayMemDesc.verticesCount);
+ }
+
+ return true;
+}
+
+// TODO(b/150412555): the overlay related methods are incomplete.
+Return<SvResult> SurroundView3dSession::updateOverlays(
+ const OverlaysData& overlaysData) {
+
+ if(!VerifyOverlayData(overlaysData)) {
+ LOG(ERROR) << "VerifyOverlayData failed.";
+ return SvResult::INVALID_ARG;
+ }
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::projectCameraPointsTo3dSurface(
+ const hidl_vec<Point2dInt>& cameraPoints,
+ const hidl_string& cameraId,
+ projectCameraPointsTo3dSurface_cb _hidl_cb) {
+
+ vector<Point3dFloat> points3d;
+ bool cameraIdFound = false;
+ for (auto& evsCameraId : mEvsCameraIds) {
+ if (cameraId == evsCameraId) {
+ cameraIdFound = true;
+ LOG(INFO) << "Camera id found.";
+ break;
+ }
+ }
+
+ if (!cameraIdFound) {
+ LOG(ERROR) << "Camera id not found.";
+ _hidl_cb(points3d);
+ return {};
+ }
+
+ for (const auto& cameraPoint : cameraPoints) {
+ Point3dFloat point3d;
+ point3d.isValid = (cameraPoint.x >= 0
+ && cameraPoint.x < mConfig.width
+ && cameraPoint.y >= 0
+ && cameraPoint.y < mConfig.height);
+ if (!point3d.isValid) {
+ LOG(WARNING) << "Camera point out of bounds.";
+ }
+ points3d.push_back(point3d);
+ }
+ _hidl_cb(points3d);
+ return {};
+}
+
+void SurroundView3dSession::generateFrames() {
+ int sequenceId = 0;
+
+ // TODO(b/150412555): do not use the setViews for frames generation
+ // since there is a discrepancy between the HIDL APIs and core lib APIs.
+ vector<vector<float>> matrix;
+ matrix.resize(4);
+ for (int i=0; i<4; i++) {
+ matrix[i].resize(4);
+ }
+
+ while(true) {
+ {
+ scoped_lock<mutex> lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Break out of our main thread loop
+ LOG(INFO) << "StreamState does not equal to RUNNING. "
+ << "Exiting the loop";
+ break;
+ }
+
+ if (mOutputWidth != mConfig.width
+ || mOutputHeight != mConfig.height) {
+ LOG(DEBUG) << "Config changed. Re-allocate memory. "
+ << "Old width: "
+ << mOutputWidth
+ << ", old height: "
+ << mOutputHeight
+ << "; New width: "
+ << mConfig.width
+ << ", new height: "
+ << mConfig.height;
+ delete[] static_cast<char*>(mOutputPointer.data_pointer);
+ mOutputWidth = mConfig.width;
+ mOutputHeight = mConfig.height;
+ mOutputPointer.height = mOutputHeight;
+ mOutputPointer.width = mOutputWidth;
+ mOutputPointer.format = Format::RGBA;
+ mOutputPointer.data_pointer =
+ new char[mOutputHeight * mOutputWidth * kNumChannels];
+
+ if (!mOutputPointer.data_pointer) {
+ LOG(ERROR) << "Memory allocation failed. Exiting.";
+ break;
+ }
+
+ Size2dInteger size = Size2dInteger(mOutputWidth, mOutputHeight);
+ mSurroundView->Update3dOutputResolution(size);
+
+ mSvTexture = new GraphicBuffer(mOutputWidth,
+ mOutputHeight,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 1,
+ GRALLOC_USAGE_HW_TEXTURE,
+ "SvTexture");
+ if (mSvTexture->initCheck() == OK) {
+ LOG(INFO) << "Successfully allocated Graphic Buffer";
+ } else {
+ LOG(ERROR) << "Failed to allocate Graphic Buffer";
+ break;
+ }
+ }
+ }
+
+ // TODO(b/150412555): use hard-coded views for now. Change view every 10
+ // frames.
+ int recViewId = sequenceId / 10 % 16;
+ for (int i=0; i<4; i++)
+ for (int j=0; j<4; j++) {
+ matrix[i][j] = kRecViews[recViewId][i*4+j];
+ }
+
+ if (mSurroundView->Get3dSurroundView(
+ mInputPointers, matrix, &mOutputPointer)) {
+ LOG(INFO) << "Get3dSurroundView succeeded";
+ } else {
+ LOG(ERROR) << "Get3dSurroundView failed. "
+ << "Using memset to initialize to gray.";
+ memset(mOutputPointer.data_pointer, kGrayColor,
+ mOutputHeight * mOutputWidth * kNumChannels);
+ }
+
+ void* textureDataPtr = nullptr;
+ mSvTexture->lock(GRALLOC_USAGE_SW_WRITE_OFTEN
+ | GRALLOC_USAGE_SW_READ_NEVER,
+ &textureDataPtr);
+ if (!textureDataPtr) {
+ LOG(ERROR) << "Failed to gain write access to GraphicBuffer!";
+ break;
+ }
+
+ // Note: there is a chance that the stride of the texture is not the
+ // same as the width. For example, when the input frame is 1920 * 1080,
+ // the width is 1080, but the stride is 2048. So we'd better copy the
+ // data line by line, instead of single memcpy.
+ uint8_t* writePtr = static_cast<uint8_t*>(textureDataPtr);
+ uint8_t* readPtr = static_cast<uint8_t*>(mOutputPointer.data_pointer);
+ const int readStride = mOutputWidth * kNumChannels;
+ const int writeStride = mSvTexture->getStride() * kNumChannels;
+ if (readStride == writeStride) {
+ memcpy(writePtr, readPtr, readStride * mSvTexture->getHeight());
+ } else {
+ for (int i=0; i<mSvTexture->getHeight(); i++) {
+ memcpy(writePtr, readPtr, readStride);
+ writePtr = writePtr + writeStride;
+ readPtr = readPtr + readStride;
+ }
+ }
+ LOG(INFO) << "memcpy finished!";
+ mSvTexture->unlock();
+
+ ANativeWindowBuffer* buffer = mSvTexture->getNativeBuffer();
+ LOG(DEBUG) << "ANativeWindowBuffer->handle: " << buffer->handle;
+
+ framesRecord.frames.svBuffers.resize(1);
+ SvBuffer& svBuffer = framesRecord.frames.svBuffers[0];
+ svBuffer.viewId = 0;
+ svBuffer.hardwareBuffer.nativeHandle = buffer->handle;
+ AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<AHardwareBuffer_Desc *>(
+ &svBuffer.hardwareBuffer.description);
+ pDesc->width = mOutputWidth;
+ pDesc->height = mOutputHeight;
+ pDesc->layers = 1;
+ pDesc->usage = GRALLOC_USAGE_HW_TEXTURE;
+ pDesc->stride = mSvTexture->getStride();
+ pDesc->format = HAL_PIXEL_FORMAT_RGBA_8888;
+ framesRecord.frames.timestampNs = elapsedRealtimeNano();
+ framesRecord.frames.sequenceId = sequenceId++;
+
+ {
+ scoped_lock<mutex> lock(mAccessLock);
+
+ if (framesRecord.inUse) {
+ LOG(DEBUG) << "Notify SvEvent::FRAME_DROPPED";
+ mStream->notify(SvEvent::FRAME_DROPPED);
+ } else {
+ framesRecord.inUse = true;
+ mStream->receiveFrames(framesRecord.frames);
+ }
+ }
+ }
+
+ // If we've been asked to stop, send an event to signal the actual end of stream
+ LOG(DEBUG) << "Notify SvEvent::STREAM_STOPPED";
+ mStream->notify(SvEvent::STREAM_STOPPED);
+}
+
+bool SurroundView3dSession::initialize() {
+ lock_guard<mutex> lock(mAccessLock, adopt_lock);
+
+ // TODO(b/150412555): ask core-lib team to add API description for "create"
+ // method in the .h file.
+ // The create method will never return a null pointer based the API
+ // description.
+ mSurroundView = unique_ptr<SurroundView>(Create());
+
+ mSurroundView->SetStaticData(GetCameras(), Get2dParams(), Get3dParams(),
+ GetUndistortionScales(), GetBoundingBox());
+
+ // TODO(b/150412555): remove after EVS camera is used
+ mInputPointers = mSurroundView->ReadImages(
+ "/etc/automotive/sv/cam0.png",
+ "/etc/automotive/sv/cam1.png",
+ "/etc/automotive/sv/cam2.png",
+ "/etc/automotive/sv/cam3.png");
+ if (mInputPointers.size() == 4
+ && mInputPointers[0].cpu_data_pointer != nullptr) {
+ LOG(INFO) << "ReadImages succeeded";
+ } else {
+ LOG(ERROR) << "Failed to read images";
+ return false;
+ }
+
+ mOutputWidth = Get3dParams().resolution.width;
+ mOutputHeight = Get3dParams().resolution.height;
+
+ mConfig.width = mOutputWidth;
+ mConfig.height = mOutputHeight;
+ mConfig.carDetails = SvQuality::HIGH;
+
+ mOutputPointer.height = mOutputHeight;
+ mOutputPointer.width = mOutputWidth;
+ mOutputPointer.format = Format::RGBA;
+ mOutputPointer.data_pointer = new char[
+ mOutputHeight * mOutputWidth * kNumChannels];
+
+ if (!mOutputPointer.data_pointer) {
+ LOG(ERROR) << "Memory allocation failed. Exiting.";
+ return false;
+ }
+
+ mSvTexture = new GraphicBuffer(mOutputWidth,
+ mOutputHeight,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 1,
+ GRALLOC_USAGE_HW_TEXTURE,
+ "SvTexture");
+
+ if (mSvTexture->initCheck() == OK) {
+ LOG(INFO) << "Successfully allocated Graphic Buffer";
+ } else {
+ LOG(ERROR) << "Failed to allocate Graphic Buffer";
+ return false;
+ }
+
+ if (mSurroundView->Start3dPipeline()) {
+ LOG(INFO) << "Start3dPipeline succeeded";
+ } else {
+ LOG(ERROR) << "Start3dPipeline failed";
+ return false;
+ }
+
+ mIsInitialized = true;
+ return true;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/surround_view/service-impl/SurroundView3dSession.h b/surround_view/service-impl/SurroundView3dSession.h
new file mode 100644
index 0000000..12337b6
--- /dev/null
+++ b/surround_view/service-impl/SurroundView3dSession.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include "CoreLibSetupHelper.h"
+#include <thread>
+
+#include <ui/GraphicBuffer.h>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+
+using namespace android_auto::surround_view;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundView3dSession : public ISurroundView3dSession {
+public:
+ SurroundView3dSession();
+
+ // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+ Return<SvResult> startStream(
+ const sp<ISurroundViewStream>& stream) override;
+ Return<void> stopStream() override;
+ Return<void> doneWithFrames(const SvFramesDesc& svFramesDesc) override;
+
+ // Methods from ISurroundView3dSession follow.
+ Return<SvResult> setViews(const hidl_vec<View3d>& views) override;
+ Return<SvResult> set3dConfig(const Sv3dConfig& sv3dConfig) override;
+ Return<void> get3dConfig(get3dConfig_cb _hidl_cb) override;
+ Return<SvResult> updateOverlays(const OverlaysData& overlaysData);
+ Return<void> projectCameraPointsTo3dSurface(
+ const hidl_vec<Point2dInt>& cameraPoints,
+ const hidl_string& cameraId,
+ projectCameraPointsTo3dSurface_cb _hidl_cb);
+
+private:
+ void generateFrames();
+ bool initialize();
+
+ enum StreamStateValues {
+ STOPPED,
+ RUNNING,
+ STOPPING,
+ DEAD,
+ };
+
+ // Stream subscribed for the session.
+ sp<ISurroundViewStream> mStream GUARDED_BY(mAccessLock);
+ StreamStateValues mStreamState GUARDED_BY(mAccessLock);
+
+ thread mCaptureThread; // The thread we'll use to synthesize frames
+
+ struct FramesRecord {
+ SvFramesDesc frames;
+ bool inUse = false;
+ };
+
+ FramesRecord framesRecord GUARDED_BY(mAccessLock);
+
+ // Synchronization necessary to deconflict mCaptureThread from the main service thread
+ mutex mAccessLock;
+
+ vector<View3d> mViews GUARDED_BY(mAccessLock);
+
+ Sv3dConfig mConfig GUARDED_BY(mAccessLock);
+
+ vector<string> mEvsCameraIds GUARDED_BY(mAccessLock);
+
+ unique_ptr<SurroundView> mSurroundView GUARDED_BY(mAccessLock);
+
+ vector<SurroundViewInputBufferPointers>
+ mInputPointers GUARDED_BY(mAccessLock);
+ SurroundViewResultPointer mOutputPointer GUARDED_BY(mAccessLock);
+ int mOutputWidth, mOutputHeight GUARDED_BY(mAccessLock);
+
+ sp<GraphicBuffer> mSvTexture GUARDED_BY(mAccessLock);
+
+ bool mIsInitialized GUARDED_BY(mAccessLock) = false;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/surround_view/service-impl/SurroundViewService.cpp b/surround_view/service-impl/SurroundViewService.cpp
new file mode 100644
index 0000000..8e6a683
--- /dev/null
+++ b/surround_view/service-impl/SurroundViewService.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2020 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.
+ */
+#define LOG_TAG "SurroundViewService"
+
+#include <android-base/logging.h>
+
+#include "CoreLibSetupHelper.h"
+#include "SurroundViewService.h"
+
+using namespace android_auto::surround_view;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+std::mutex SurroundViewService::sLock;
+sp<SurroundViewService> SurroundViewService::sService;
+sp<SurroundView2dSession> SurroundViewService::sSurroundView2dSession;
+sp<SurroundView3dSession> SurroundViewService::sSurroundView3dSession;
+
+const std::string kCameraIds[] = {"0", "1", "2", "3"};
+
+sp<SurroundViewService> SurroundViewService::getInstance() {
+ std::scoped_lock<std::mutex> lock(sLock);
+ if (sService == nullptr) {
+ sService = new SurroundViewService();
+ }
+ return sService;
+}
+
+Return<void> SurroundViewService::getCameraIds(getCameraIds_cb _hidl_cb) {
+ hidl_vec<hidl_string> cameraIds = {kCameraIds[0], kCameraIds[1],
+ kCameraIds[2], kCameraIds[3]};
+ _hidl_cb(cameraIds);
+ return {};
+}
+
+Return<void> SurroundViewService::start2dSession(start2dSession_cb _hidl_cb) {
+ LOG(DEBUG) << __FUNCTION__;
+ std::scoped_lock<std::mutex> lock(sLock);
+
+ if (sSurroundView2dSession != nullptr) {
+ LOG(WARNING) << "Only one 2d session is supported at the same time";
+ _hidl_cb(nullptr, SvResult::INTERNAL_ERROR);
+ } else {
+ sSurroundView2dSession = new SurroundView2dSession();
+ _hidl_cb(sSurroundView2dSession, SvResult::OK);
+ }
+ return {};
+}
+
+Return<SvResult> SurroundViewService::stop2dSession(
+ const sp<ISurroundView2dSession>& sv2dSession) {
+ LOG(DEBUG) << __FUNCTION__;
+ std::scoped_lock<std::mutex> lock(sLock);
+
+ if (sv2dSession != nullptr && sv2dSession == sSurroundView2dSession) {
+ sSurroundView2dSession = nullptr;
+ return SvResult::OK;
+ } else {
+ LOG(ERROR) << __FUNCTION__ << ": Invalid argument";
+ return SvResult::INVALID_ARG;
+ }
+}
+
+Return<void> SurroundViewService::start3dSession(start3dSession_cb _hidl_cb) {
+ LOG(DEBUG) << __FUNCTION__;
+ std::scoped_lock<std::mutex> lock(sLock);
+
+ if (sSurroundView3dSession != nullptr) {
+ LOG(WARNING) << "Only one 3d session is supported at the same time";
+ _hidl_cb(nullptr, SvResult::INTERNAL_ERROR);
+ } else {
+ sSurroundView3dSession = new SurroundView3dSession();
+ _hidl_cb(sSurroundView3dSession, SvResult::OK);
+ }
+ return {};
+}
+
+Return<SvResult> SurroundViewService::stop3dSession(
+ const sp<ISurroundView3dSession>& sv3dSession) {
+ LOG(DEBUG) << __FUNCTION__;
+ std::scoped_lock<std::mutex> lock(sLock);
+
+ if (sv3dSession != nullptr && sv3dSession == sSurroundView3dSession) {
+ sSurroundView3dSession = nullptr;
+ return SvResult::OK;
+ } else {
+ LOG(ERROR) << __FUNCTION__ << ": Invalid argument";
+ return SvResult::INVALID_ARG;
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/surround_view/service-impl/SurroundViewService.h b/surround_view/service-impl/SurroundViewService.h
new file mode 100644
index 0000000..7518463
--- /dev/null
+++ b/surround_view/service-impl/SurroundViewService.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include "SurroundView2dSession.h"
+#include "SurroundView3dSession.h"
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewService.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <thread>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::sp;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundViewService : public ISurroundViewService {
+public:
+ // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewService follow.
+ Return<void> getCameraIds(getCameraIds_cb _hidl_cb) override;
+ Return<void> start2dSession(start2dSession_cb _hidl_cb) override;
+ Return<SvResult> stop2dSession(
+ const sp<ISurroundView2dSession>& sv2dSession) override;
+
+ Return<void> start3dSession(start3dSession_cb _hidl_cb) override;
+ Return<SvResult> stop3dSession(
+ const sp<ISurroundView3dSession>& sv3dSession) override;
+
+ static sp<SurroundViewService> getInstance();
+private:
+ SurroundViewService() {};
+
+ static std::mutex sLock;
+ static sp<SurroundViewService> sService GUARDED_BY(sLock);
+
+ static sp<SurroundView2dSession> sSurroundView2dSession GUARDED_BY(sLock);
+ static sp<SurroundView3dSession> sSurroundView3dSession GUARDED_BY(sLock);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/surround_view/service-impl/android.automotive.sv.service@1.0-impl.rc b/surround_view/service-impl/android.automotive.sv.service@1.0-impl.rc
new file mode 100644
index 0000000..4207e8f
--- /dev/null
+++ b/surround_view/service-impl/android.automotive.sv.service@1.0-impl.rc
@@ -0,0 +1,5 @@
+service sv_service_impl /vendor/bin/android.automotive.sv.service@1.0-impl
+ class hal
+ user automotive_evs
+ group automotive_evs
+ disabled
diff --git a/surround_view/service-impl/core_lib.h b/surround_view/service-impl/core_lib.h
new file mode 100644
index 0000000..04ff43a
--- /dev/null
+++ b/surround_view/service-impl/core_lib.h
@@ -0,0 +1,528 @@
+#ifndef WIRELESS_ANDROID_AUTOMOTIVE_CAML_SURROUND_VIEW_CORE_LIB_H_
+#define WIRELESS_ANDROID_AUTOMOTIVE_CAML_SURROUND_VIEW_CORE_LIB_H_
+
+#include <cstdint>
+#include <vector>
+
+namespace android_auto {
+namespace surround_view {
+
+// bounding box (bb)
+// It is used to describe the car model bounding box in 3D.
+// It assumes z = 0 and only x, y are used in the struct.
+// Of course, it is compatible to the 2d version bounding box and may be used
+// for other bounding box purpose (e.g., 2d bounding box in image).
+struct BoundingBox {
+ // (x,y) is bounding box's top left corner coordinate.
+ float x;
+ float y;
+
+ // (width, height) is the size of the bounding box.
+ float width;
+ float height;
+
+ BoundingBox() : x(0.0f), y(0.0f), width(0.0f), height(0.0f) {}
+
+ BoundingBox(float x_, float y_, float width_, float height_)
+ : x(x_), y(y_), width(width_), height(height_) {}
+
+ BoundingBox(const BoundingBox& bb_)
+ : x(bb_.x), y(bb_.y), width(bb_.width), height(bb_.height) {}
+
+ // Checks if data is valid.
+ bool IsValid() const { return width >= 0 && height >= 0; }
+
+ bool operator==(const BoundingBox& rhs) const {
+ return x == rhs.x && y == rhs.y && width == rhs.width &&
+ height == rhs.height;
+ }
+
+ BoundingBox& operator=(const BoundingBox& rhs) {
+ x = rhs.x;
+ y = rhs.y;
+ width = rhs.width;
+ height = rhs.height;
+ return *this;
+ }
+};
+
+template <typename T>
+struct Coordinate2dBase {
+ // x coordinate.
+ T x;
+
+ // y coordinate.
+ T y;
+
+ Coordinate2dBase() : x(0), y(0) {}
+
+ Coordinate2dBase(T x_, T y_) : x(x_), y(y_) {}
+
+ bool operator==(const Coordinate2dBase& rhs) const {
+ return x == rhs.x && y == rhs.y;
+ }
+
+ Coordinate2dBase& operator=(const Coordinate2dBase& rhs) {
+ x = rhs.x;
+ y = rhs.y;
+ return *this;
+ }
+};
+
+// integer type size.
+typedef Coordinate2dBase<int> Coordinate2dInteger;
+
+// float type size.
+typedef Coordinate2dBase<float> Coordinate2dFloat;
+
+struct Coordinate3dFloat {
+ // x coordinate.
+ float x;
+
+ // y coordinate.
+ float y;
+
+ // z coordinate.
+ float z;
+
+ Coordinate3dFloat() : x(0), y(0), z(0) {}
+
+ Coordinate3dFloat(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
+
+ bool operator==(const Coordinate3dFloat& rhs) const {
+ return x == rhs.x && y == rhs.y;
+ }
+
+ Coordinate3dFloat& operator=(const Coordinate3dFloat& rhs) {
+ x = rhs.x;
+ y = rhs.y;
+ return *this;
+ }
+};
+
+// pixel weight used for illumination assessment
+struct PixelWeight {
+ // x and y are the coordinates (absolute value) in image space.
+ // pixel coordinate x in horizontal direction.
+ float x;
+
+ // pixel coordinate y in vertical direction.
+ float y;
+
+ // pixel weight, range in [0, 1].
+ float weight;
+
+ PixelWeight() : x(-1), y(-1), weight(0) {}
+
+ PixelWeight(int x_, int y_, int weight_) : x(x_), y(y_), weight(weight_) {}
+
+ bool operator==(const PixelWeight& rhs) const {
+ return x == rhs.x && y == rhs.y && weight == rhs.weight;
+ }
+
+ PixelWeight& operator=(const PixelWeight& rhs) {
+ x = rhs.x;
+ y = rhs.y;
+ weight = rhs.weight;
+ return *this;
+ }
+};
+
+// base size 2d type template.
+template <typename T>
+struct Size2dBase {
+ // width of size.
+ T width;
+
+ // height of size.
+ T height;
+
+ Size2dBase() : width(0), height(0) {}
+
+ Size2dBase(T width_, T height_) : width(width_), height(height_) {}
+
+ bool IsValid() const { return width > 0 && height > 0; }
+
+ bool operator==(const Size2dBase& rhs) const {
+ return width == rhs.width && height == rhs.height;
+ }
+
+ Size2dBase& operator=(const Size2dBase& rhs) {
+ width = rhs.width;
+ height = rhs.height;
+ return *this;
+ }
+};
+
+// integer type size.
+typedef Size2dBase<int> Size2dInteger;
+
+// float type size.
+typedef Size2dBase<float> Size2dFloat;
+
+// surround view 2d parameters
+struct SurroundView2dParams {
+ // surround view 2d image resolution (width, height).
+ Size2dInteger resolution;
+
+ // the physical size of surround view 2d area in surround view coordinate.
+ // (surround view coordinate is defined as X rightward, Y forward and
+ // the origin lies on the center of the (symmetric) bowl (ground).
+ // When bowl is not used, surround view coordinate origin lies on the
+ // center of car model bounding box.)
+ // The unit should be consistent with camera extrinsics (translation).
+ Size2dFloat physical_size;
+
+ // the center of surround view 2d area in surround view coordinate
+ // (consistent with extrinsics coordinate).
+ Coordinate2dFloat physical_center;
+
+ SurroundView2dParams()
+ : resolution{0, 0},
+ physical_size{0.0f, 0.0f},
+ physical_center{0.0f, 0.0f} {}
+
+ SurroundView2dParams(Size2dInteger resolution_, Size2dFloat physical_size_,
+ Coordinate2dFloat physical_center_)
+ : resolution(resolution_),
+ physical_size(physical_size_),
+ physical_center(physical_center_) {}
+
+ // Checks if data is valid.
+ bool IsValid() const {
+ return resolution.IsValid() && physical_size.IsValid();
+ }
+
+ bool operator==(const SurroundView2dParams& rhs) const {
+ return resolution == rhs.resolution && physical_size == rhs.physical_size &&
+ physical_center == rhs.physical_center;
+ }
+
+ SurroundView2dParams& operator=(const SurroundView2dParams& rhs) {
+ resolution = rhs.resolution;
+ physical_size = rhs.physical_size;
+ physical_center = rhs.physical_center;
+ return *this;
+ }
+};
+
+// surround view 3d parameters
+struct SurroundView3dParams {
+ // Bowl center is the origin of the surround view coordinate. If surround view
+ // coordinate is different from the global one, a coordinate system
+ // transformation function is required.
+
+ // planar area radius.
+ // Range in (0, +Inf).
+ float plane_radius;
+
+ // the number of divisions on the plane area of bowl, in the direction
+ // of the radius.
+ // Range in [1, +Inf).
+ int plane_divisions;
+
+ // bowl curve curve height.
+ // Range in (0, +Inf).
+ float curve_height;
+
+ // the number of points on bowl curve curve along radius direction.
+ // Range in [1, +Inf).
+ int curve_divisions;
+
+ // the number of points along circle (360 degrees)
+ // Range in [1, +Inf).
+ int angular_divisions;
+
+ // the parabola coefficient of bowl curve curve.
+ // The curve formula is z = a * (x^2 + y^2) for sqrt(x^2 + y^2) >
+ // plane_radius; a is curve_coefficient.
+ // Range in (0, +Inf).
+ float curve_coefficient;
+
+ // render output image size.
+ Size2dInteger resolution;
+
+ SurroundView3dParams()
+ : plane_radius(0.0f),
+ plane_divisions(0),
+ curve_height(0.0f),
+ curve_divisions(0),
+ angular_divisions(0),
+ curve_coefficient(0.0f),
+ resolution(0, 0) {}
+
+ SurroundView3dParams(float plane_radius_, int plane_divisions_,
+ float curve_height_, int curve_divisions_,
+ int angular_divisions_, float curve_coefficient_,
+ Size2dInteger resolution_)
+ : plane_radius(plane_radius_),
+ plane_divisions(plane_divisions_),
+ curve_height(curve_height_),
+ curve_divisions(curve_divisions_),
+ angular_divisions(angular_divisions_),
+ curve_coefficient(curve_coefficient_),
+ resolution(resolution_) {}
+
+ // Checks if data is valid.
+ bool IsValid() const {
+ return plane_radius > 0 && plane_divisions > 0 && curve_height > 0 &&
+ angular_divisions > 0 && curve_coefficient > 0 &&
+ curve_divisions > 0 && resolution.IsValid();
+ }
+
+ bool operator==(const SurroundView3dParams& rhs) const {
+ return plane_radius == rhs.plane_radius &&
+ plane_divisions == rhs.plane_divisions &&
+ curve_height == rhs.curve_height &&
+ curve_divisions == rhs.curve_divisions &&
+ angular_divisions == rhs.angular_divisions &&
+ curve_coefficient == rhs.curve_coefficient &&
+ resolution == rhs.resolution;
+ }
+
+ SurroundView3dParams& operator=(const SurroundView3dParams& rhs) {
+ plane_radius = rhs.plane_radius;
+ plane_divisions = rhs.plane_divisions;
+ curve_height = rhs.curve_height;
+ curve_divisions = rhs.curve_divisions;
+ angular_divisions = rhs.angular_divisions;
+ curve_coefficient = rhs.curve_coefficient;
+ resolution = rhs.resolution;
+ return *this;
+ }
+};
+
+// surround view camera parameters with native types only.
+struct SurroundViewCameraParams {
+ // All calibration data |intrinsics|, |rvec| and |tvec|
+ // follow OpenCV format excepting using native arrays, refer:
+ // https://docs.opencv.org/3.4.0/db/d58/group__calib3d__fisheye.html
+ // camera intrinsics. It is the 1d array of camera matrix(3X3) with row first.
+ float intrinsics[9];
+
+ // lens distortion parameters.
+ float distorion[4];
+
+ // rotation vector.
+ float rvec[3];
+
+ // translation vector.
+ float tvec[3];
+
+ // camera image size (width, height).
+ Size2dInteger size;
+
+ // fisheye circular fov.
+ float circular_fov;
+
+ bool operator==(const SurroundViewCameraParams& rhs) const {
+ return (0 == std::memcmp(intrinsics, rhs.intrinsics, 9 * sizeof(float))) &&
+ (0 == std::memcmp(distorion, rhs.distorion, 4 * sizeof(float))) &&
+ (0 == std::memcmp(rvec, rhs.rvec, 3 * sizeof(float))) &&
+ (0 == std::memcmp(tvec, rhs.tvec, 3 * sizeof(float))) &&
+ size == rhs.size && circular_fov == rhs.circular_fov;
+ }
+
+ SurroundViewCameraParams& operator=(const SurroundViewCameraParams& rhs) {
+ std::memcpy(intrinsics, rhs.intrinsics, 9 * sizeof(float));
+ std::memcpy(distorion, rhs.distorion, 4 * sizeof(float));
+ std::memcpy(rvec, rhs.rvec, 3 * sizeof(float));
+ std::memcpy(tvec, rhs.tvec, 3 * sizeof(float));
+ size = rhs.size;
+ circular_fov = rhs.circular_fov;
+ return *this;
+ }
+};
+
+// 3D vertex of an overlay object.
+struct OverlayVertex {
+ // Position in 3d coordinates in world space in order X,Y,Z.
+ float pos[3];
+ // RGBA values, A is used for transparency.
+ uint8_t rgba[4];
+
+ // normalized texture coordinates, in width and height direction. Range [0,
+ // 1].
+ float tex[2];
+
+ // normalized vertex normal.
+ float nor[3];
+
+ bool operator==(const OverlayVertex& rhs) const {
+ return (0 == std::memcmp(pos, rhs.pos, 3 * sizeof(float))) &&
+ (0 == std::memcmp(rgba, rhs.rgba, 4 * sizeof(uint8_t))) &&
+ (0 == std::memcmp(tex, rhs.tex, 2 * sizeof(float))) &&
+ (0 == std::memcmp(nor, rhs.nor, 3 * sizeof(float)));
+ }
+
+ OverlayVertex& operator=(const OverlayVertex& rhs) {
+ std::memcpy(pos, rhs.pos, 3 * sizeof(float));
+ std::memcpy(rgba, rhs.rgba, 4 * sizeof(uint8_t));
+ std::memcpy(tex, rhs.tex, 2 * sizeof(float));
+ std::memcpy(nor, rhs.nor, 3 * sizeof(float));
+ return *this;
+ }
+};
+
+// Overlay is a list of vertices (may be a single or multiple objects in scene)
+// coming from a single source or type of sensor.
+struct Overlay {
+ // Uniqiue Id identifying each overlay.
+ uint16_t id;
+
+ // List of overlay vertices. 3 consecutive vertices form a triangle.
+ std::vector<OverlayVertex> vertices;
+
+ // Constructor initializing all member.
+ Overlay(uint16_t id_, const std::vector<OverlayVertex>& vertices_) {
+ id = id_;
+ vertices = vertices_;
+ }
+
+ // Default constructor.
+ Overlay() {
+ id = 0;
+ vertices = std::vector<OverlayVertex>();
+ }
+};
+
+enum Format {
+ GRAY = 0,
+ RGB = 1,
+ RGBA = 2,
+};
+
+struct SurroundViewInputBufferPointers {
+ void* gpu_data_pointer;
+ void* cpu_data_pointer;
+ Format format;
+ int width;
+ int height;
+ SurroundViewInputBufferPointers()
+ : gpu_data_pointer(nullptr),
+ cpu_data_pointer(nullptr),
+ width(0),
+ height(0) {}
+ SurroundViewInputBufferPointers(void* gpu_data_pointer_,
+ void* cpu_data_pointer_, Format format_,
+ int width_, int height_)
+ : gpu_data_pointer(gpu_data_pointer_),
+ cpu_data_pointer(cpu_data_pointer_),
+ format(format_),
+ width(width_),
+ height(height_) {}
+};
+
+struct SurroundViewResultPointer {
+ void* data_pointer;
+ Format format;
+ int width;
+ int height;
+ SurroundViewResultPointer() : data_pointer(nullptr), width(0), height(0) {}
+ SurroundViewResultPointer(Format format_, int width_, int height_)
+ : format(format_), width(width_), height(height_) {
+ // default formate is gray.
+ const int byte_per_pixel = format_ == RGB ? 3 : format_ == RGBA ? 4 : 1;
+ data_pointer =
+ static_cast<void*>(new char[width * height * byte_per_pixel]);
+ }
+ ~SurroundViewResultPointer() {
+ if (data_pointer) {
+ // delete[] static_cast<char*>(data_pointer);
+ data_pointer = nullptr;
+ }
+ }
+};
+
+class SurroundView {
+ public:
+ virtual ~SurroundView() = default;
+
+ // Sets SurroundView static data.
+ // For each input, please refer to the definition.
+ virtual bool SetStaticData(
+ const std::vector<SurroundViewCameraParams>& cameras_params,
+ const SurroundView2dParams& surround_view_2d_params,
+ const SurroundView3dParams& surround_view_3d_params,
+ const std::vector<float>& undistortion_focal_length_scales,
+ const BoundingBox& car_model_bb) = 0;
+
+ // Starts 2d pipeline. Returns false if error occurs.
+ virtual bool Start2dPipeline() = 0;
+
+ // Starts 3d pipeline. Returns false if error occurs.
+ virtual bool Start3dPipeline() = 0;
+
+ // Stops 2d pipleline. It releases resource owned by the pipeline.
+ // Returns false if error occurs.
+ virtual void Stop2dPipeline() = 0;
+
+ // Stops 3d pipeline. It releases resource owned by the pipeline.
+ virtual void Stop3dPipeline() = 0;
+
+ // Updates 2d output resolution on-the-fly. Starts2dPipeline() must be called
+ // before this can be called. For quality assurance, the resolution should not
+ // be larger than the original one. This call is not thread safe and there is
+ // no sync between Get2dSurroundView() and this call.
+ virtual bool Update2dOutputResolution(const Size2dInteger& resolution) = 0;
+
+ // Updates 3d output resolution on-the-fly. Starts3dPipeline() must be called
+ // before this can be called. For quality assurance, the resolution should not
+ // be larger than the original one. This call is not thread safe and there is
+ // no sync between Get3dSurroundView() and this call.
+ virtual bool Update3dOutputResolution(const Size2dInteger& resolution) = 0;
+
+ // Projects camera's pixel location to surround view 2d image location.
+ // camera_point is the pixel location in raw camera's space.
+ // camera_index is the camera's index.
+ // surround_view_2d_point is the surround view 2d image pixel location.
+ virtual bool GetProjectionPointFromRawCameraToSurroundView2d(
+ const Coordinate2dInteger& camera_point, int camera_index,
+ Coordinate2dFloat* surround_view_2d_point) = 0;
+
+ // Projects camera's pixel location to surround view 3d bowl coordinate.
+ // camera_point is the pixel location in raw camera's space.
+ // camera_index is the camera's index.
+ // surround_view_3d_point is the surround view 3d vertex.
+ virtual bool GetProjectionPointFromRawCameraToSurroundView3d(
+ const Coordinate2dInteger& camera_point, int camera_index,
+ Coordinate3dFloat* surround_view_3d_point) = 0;
+
+ // Gets 2d surround view image.
+ // It takes input_pointers as input, and output is result_pointer.
+ // Please refer to the definition of SurroundViewInputBufferPointers and
+ // SurroundViewResultPointer.
+ virtual bool Get2dSurroundView(
+ const std::vector<SurroundViewInputBufferPointers>& input_pointers,
+ SurroundViewResultPointer* result_pointer) = 0;
+
+ // Gets 3d surround view image.
+ // It takes input_pointers and view_matrix as input, and output is
+ // result_pointer. view_matrix is 4 x 4 matrix.
+ // Please refer to the definition of
+ // SurroundViewInputBufferPointers and
+ // SurroundViewResultPointer.
+ virtual bool Get3dSurroundView(
+ const std::vector<SurroundViewInputBufferPointers>& input_pointers,
+ const std::vector<std::vector<float>> view_matrix,
+ SurroundViewResultPointer* result_pointer) = 0;
+
+ // Sets 3d overlays.
+ virtual bool Set3dOverlay(const std::vector<Overlay>& overlays) = 0;
+
+ // for test only.
+ // TODO(xxqian): remove thest two fns.
+ virtual std::vector<SurroundViewInputBufferPointers> ReadImages(
+ const char* filename0, const char* filename1, const char* filename2,
+ const char* filename3) = 0;
+
+ virtual void WriteImage(const SurroundViewResultPointer result_pointerer,
+ const char* filename) = 0;
+};
+
+SurroundView* Create();
+
+} // namespace surround_view
+} // namespace android_auto
+
+#endif // WIRELESS_ANDROID_AUTOMOTIVE_CAML_SURROUND_VIEW_CORE_LIB_H_
diff --git a/surround_view/service-impl/lib/arm64/libcore_lib_shared.so b/surround_view/service-impl/lib/arm64/libcore_lib_shared.so
new file mode 100644
index 0000000..0175c16
--- /dev/null
+++ b/surround_view/service-impl/lib/arm64/libcore_lib_shared.so
Binary files differ
diff --git a/surround_view/service-impl/lib/x86-64/libcore_lib.so b/surround_view/service-impl/lib/x86-64/libcore_lib.so
new file mode 100755
index 0000000..96479c6
--- /dev/null
+++ b/surround_view/service-impl/lib/x86-64/libcore_lib.so
Binary files differ
diff --git a/surround_view/service-impl/lib/x86/libcore_lib.so b/surround_view/service-impl/lib/x86/libcore_lib.so
new file mode 100755
index 0000000..96479c6
--- /dev/null
+++ b/surround_view/service-impl/lib/x86/libcore_lib.so
Binary files differ
diff --git a/surround_view/service-impl/manifest_android.hardware.automotive.sv@1.0.xml b/surround_view/service-impl/manifest_android.hardware.automotive.sv@1.0.xml
new file mode 100644
index 0000000..f9e4548
--- /dev/null
+++ b/surround_view/service-impl/manifest_android.hardware.automotive.sv@1.0.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2020 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.
+-->
+<manifest version="1.0" type="device" >
+ <hal format="hidl">
+ <name>android.hardware.automotive.sv</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>ISurroundViewService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/surround_view/service-impl/service.cpp b/surround_view/service-impl/service.cpp
new file mode 100644
index 0000000..a7ce6f3
--- /dev/null
+++ b/surround_view/service-impl/service.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020 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.
+ */
+#define LOG_TAG "SurroundViewService"
+
+#include <android-base/logging.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware_buffer.h>
+#include <hidl/HidlTransportSupport.h>
+#include <thread>
+#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/SystemClock.h>
+
+#include "SurroundViewService.h"
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// implementation:
+using android::hardware::automotive::sv::V1_0::implementation::SurroundViewService;
+
+int main() {
+ LOG(INFO) << "ISurroundViewService default implementation is starting";
+ android::sp<ISurroundViewService> service = SurroundViewService::getInstance();
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ // Register our service -- if somebody is already registered by our name,
+ // they will be killed (their thread pool will throw an exception).
+ android::status_t status = service->registerAsService();
+
+ if (status != android::OK) {
+ LOG(ERROR) << "Could not register default Surround View Service. Status: "
+ << status;
+ }
+
+ joinRpcThreadpool();
+
+ // In normal operation, we don't expect the thread pool to exit
+ LOG(ERROR) << "Surround View Service is shutting down";
+ return 1;
+}
diff --git a/surround_view/service-impl/sv_3d_params.h b/surround_view/service-impl/sv_3d_params.h
new file mode 100644
index 0000000..b0df5fa
--- /dev/null
+++ b/surround_view/service-impl/sv_3d_params.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 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 SV_3D_PARAMS_H
+#define SV_3D_PARAMS_H
+
+#include <vector>
+#include <hidl/HidlSupport.h>
+
+using ::android::hardware::hidl_vec;
+
+static std::vector<android::hardware::hidl_vec<float>> kRecViews = {
+ {0, -0.747409, 0.664364, 0, 1, 0, -0, 0, -0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {-0.382683, -0.690516, 0.613792, 0, 0.92388, -0.286021, 0.254241, 0, 0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {-0.707107, -0.528498, 0.469776, 0, 0.707107, -0.528498, 0.469776, 0, 0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {-0.92388, -0.286021, 0.254241, 0, 0.382683, -0.690516, 0.613792, 0, 0, 0.664364, 0.747409, 0, -1.19209e-07, 1.32873, -4.52598, 1},
+ {-1, 3.26703e-08, -2.90403e-08, 0, -4.37114e-08, -0.747409, 0.664364, 0, 0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {-0.92388, 0.286021, -0.254241, 0, -0.382683, -0.690516, 0.613792, 0, 0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {-0.707107, 0.528498, -0.469776, 0, -0.707107, -0.528498, 0.469776, 0, 0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {-0.382683, 0.690516, -0.613792, 0, -0.92388, -0.286021, 0.254241, 0, 0, 0.664364, 0.747409, 0, 1.19209e-07, 1.32873, -4.52598, 1},
+ {8.74228e-08, 0.747409, -0.664364, 0, -1, 6.53406e-08, -5.80805e-08, 0, 0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {0.382683, 0.690516, -0.613792, 0, -0.92388, 0.286021, -0.254241, 0, 0, 0.664364, 0.747409, 0, 1.19209e-07, 1.32873, -4.52598, 1},
+ {0.707107, 0.528498, -0.469776, 0, -0.707107, 0.528498, -0.469776, 0, 0, 0.664364, 0.747409, 0, 1.19209e-07, 1.32873, -4.52598, 1},
+ {0.92388, 0.286021, -0.254241, 0, -0.382684, 0.690516, -0.613792, 0, 0, 0.664364, 0.747409, 0, 1.19209e-07, 1.32873, -4.52598, 1},
+ {1, -8.91277e-09, 7.92246e-09, 0, 1.19249e-08, 0.747409, -0.664364, 0, -0, 0.664364, 0.747409, 0, 3.55271e-15, 1.32873, -4.52598, 1},
+ {0.92388, -0.286021, 0.254241, 0, 0.382684, 0.690516, -0.613792, 0, -0, 0.664364, 0.747409, 0, -0, 1.32873, -4.52598, 1},
+ {0.707107, -0.528498, 0.469776, 0, 0.707107, 0.528498, -0.469776, 0, -0, 0.664364, 0.747409, 0, -1.19209e-07, 1.32873, -4.52598, 1},
+ {0.382683, -0.690516, 0.613792, 0, 0.92388, 0.286021, -0.254241, 0, -0, 0.664364, 0.747409, 0, -1.19209e-07, 1.32873, -4.52598, 1},
+};
+
+#endif // SV_3D_PARAMS_H
+
diff --git a/surround_view/service-impl/test_data/0.png b/surround_view/service-impl/test_data/0.png
new file mode 100644
index 0000000..283751b
--- /dev/null
+++ b/surround_view/service-impl/test_data/0.png
Binary files differ
diff --git a/surround_view/service-impl/test_data/1.png b/surround_view/service-impl/test_data/1.png
new file mode 100644
index 0000000..55abd66
--- /dev/null
+++ b/surround_view/service-impl/test_data/1.png
Binary files differ
diff --git a/surround_view/service-impl/test_data/2.png b/surround_view/service-impl/test_data/2.png
new file mode 100644
index 0000000..c66dd61
--- /dev/null
+++ b/surround_view/service-impl/test_data/2.png
Binary files differ
diff --git a/surround_view/service-impl/test_data/3.png b/surround_view/service-impl/test_data/3.png
new file mode 100644
index 0000000..f9d2c60
--- /dev/null
+++ b/surround_view/service-impl/test_data/3.png
Binary files differ