1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/logging.h>
18 #include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
19 #include <android/hardware/automotive/sv/1.0/ISurroundViewService.h>
20 #include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
21 #include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
22 #include <hidl/HidlTransportSupport.h>
23 #include <stdio.h>
24 #include <utils/StrongPointer.h>
25 #include <utils/Log.h>
26 #include <thread>
27
28 #include "SurroundViewServiceCallback.h"
29
30 // libhidl:
31 using android::hardware::configureRpcThreadpool;
32 using android::hardware::joinRpcThreadpool;
33
34 using android::sp;
35 using android::hardware::Return;
36 using android::hardware::automotive::evs::V1_0::EvsResult;
37
38 using BufferDesc_1_0 = android::hardware::automotive::evs::V1_0::BufferDesc;
39 using DisplayState = android::hardware::automotive::evs::V1_0::DisplayState;
40
41 using namespace android::hardware::automotive::sv::V1_0;
42 using namespace android::hardware::automotive::evs::V1_1;
43
44 const int kLowResolutionWidth = 120;
45 const int kLowResolutionHeight = 90;
46
47 enum DemoMode {
48 UNKNOWN,
49 DEMO_2D,
50 DEMO_3D,
51 };
52
53 const float kHorizontalFov = 90;
54
55 // Number of views to generate.
56 const uint32_t kPoseCount = 16;
57
58 // Set of pose rotations expressed in quaternions.
59 // Views are generated about a circle at a height about the car, point towards the center.
60 const float kPoseRot[kPoseCount][4] = {
61 {-0.251292, -0.251292, -0.660948, 0.660948},
62 {0.197439, 0.295488, 0.777193, -0.519304},
63 {0.135998, 0.328329, 0.86357, -0.357702},
64 {0.0693313, 0.348552, 0.916761, -0.182355},
65 {-7.76709e-09, 0.355381, 0.934722, 2.0429e-08},
66 {-0.0693313, 0.348552, 0.916761, 0.182355},
67 {-0.135998, 0.328329, 0.86357, 0.357702},
68 {-0.197439, 0.295488, 0.777193, 0.519304},
69 {-0.251292, 0.251292, 0.660948, 0.660948},
70 {-0.295488, 0.197439, 0.519304, 0.777193},
71 {-0.328329, 0.135998, 0.357702, 0.86357},
72 {-0.348552, 0.0693313, 0.182355, 0.916761},
73 {-0.355381, -2.11894e-09, -5.57322e-09, 0.934722},
74 {-0.348552, -0.0693313, -0.182355, 0.916761},
75 {-0.328329, -0.135998, -0.357702, 0.86357},
76 {-0.295488, -0.197439, -0.519304, 0.777193}
77 };
78
79 // Set of pose translations i.e. positions of the views.
80 // Views are generated about a circle at a height about the car, point towards the center.
81 const float kPoseTrans[kPoseCount][4] = {
82 {4, 0, 2.5},
83 {3.69552, 1.53073, 2.5},
84 {2.82843, 2.82843, 2.5},
85 {1.53073, 3.69552, 2.5},
86 {-1.74846e-07, 4, 2.5},
87 {-1.53073, 3.69552, 2.5},
88 {-2.82843, 2.82843, 2.5},
89 {-3.69552, 1.53073, 2.5},
90 {-4, -3.49691e-07, 2.5},
91 {-3.69552, -1.53073, 2.5},
92 {-2.82843, -2.82843, 2.5},
93 {-1.53073, -3.69552, 2.5},
94 {4.76995e-08, -4, 2.5},
95 {1.53073, -3.69552, 2.5},
96 {2.82843, -2.82843, 2.5},
97 {3.69552, -1.53073, 2.5}
98 };
99
run2dSurroundView(sp<ISurroundViewService> pSurroundViewService,sp<IEvsDisplay> pDisplay)100 bool run2dSurroundView(sp<ISurroundViewService> pSurroundViewService,
101 sp<IEvsDisplay> pDisplay) {
102 LOG(INFO) << "Run 2d Surround View demo";
103
104 // Call HIDL API "start2dSession"
105 sp<ISurroundView2dSession> surroundView2dSession;
106
107 SvResult svResult;
108 pSurroundViewService->start2dSession(
109 [&surroundView2dSession, &svResult](
110 const sp<ISurroundView2dSession>& session, SvResult result) {
111 surroundView2dSession = session;
112 svResult = result;
113 });
114
115 if (surroundView2dSession == nullptr || svResult != SvResult::OK) {
116 LOG(ERROR) << "Failed to start2dSession";
117 return false;
118 } else {
119 LOG(INFO) << "start2dSession succeeded";
120 }
121
122 sp<SurroundViewServiceCallback> sv2dCallback
123 = new SurroundViewServiceCallback(pDisplay, surroundView2dSession);
124
125 // Start 2d stream with callback
126 if (surroundView2dSession->startStream(sv2dCallback) != SvResult::OK) {
127 LOG(ERROR) << "Failed to start 2d stream";
128 return false;
129 }
130
131 // Let the SV algorithm run for 10 seconds for HIGH_QUALITY
132 std::this_thread::sleep_for(std::chrono::seconds(10));
133
134 // Switch to low quality and lower resolution
135 Sv2dConfig config;
136 config.width = kLowResolutionWidth;
137 config.blending = SvQuality::LOW;
138 if (surroundView2dSession->set2dConfig(config) != SvResult::OK) {
139 LOG(ERROR) << "Failed to set2dConfig";
140 return false;
141 }
142
143 // Let the SV algorithm run for 10 seconds for LOW_QUALITY
144 std::this_thread::sleep_for(std::chrono::seconds(10));
145
146 // TODO(b/150412555): wait for the last frame
147 // Stop the 2d stream and session
148 surroundView2dSession->stopStream();
149
150 pSurroundViewService->stop2dSession(surroundView2dSession);
151 surroundView2dSession = nullptr;
152
153 LOG(INFO) << "SV 2D session finished.";
154
155 return true;
156 };
157
158 // Given a valid sv 3d session and pose, viewid and hfov parameters, sets the view.
setView(sp<ISurroundView3dSession> surroundView3dSession,uint32_t viewId,uint32_t poseIndex,float hfov)159 bool setView(sp<ISurroundView3dSession> surroundView3dSession, uint32_t viewId,
160 uint32_t poseIndex, float hfov)
161 {
162 const View3d view3d = {
163 .viewId = viewId,
164 .pose = {
165 .rotation = {.x=kPoseRot[poseIndex][0], .y=kPoseRot[poseIndex][1],
166 .z=kPoseRot[poseIndex][2], .w=kPoseRot[poseIndex][3]},
167 .translation = {.x=kPoseTrans[poseIndex][0], .y=kPoseTrans[poseIndex][1],
168 .z=kPoseTrans[poseIndex][2]},
169 },
170 .horizontalFov = hfov,
171 };
172
173 const std::vector<View3d> views = {view3d};
174 if (surroundView3dSession->setViews(views) != SvResult::OK) {
175 return false;
176 }
177 return true;
178 }
179
run3dSurroundView(sp<ISurroundViewService> pSurroundViewService,sp<IEvsDisplay> pDisplay)180 bool run3dSurroundView(sp<ISurroundViewService> pSurroundViewService,
181 sp<IEvsDisplay> pDisplay) {
182 LOG(INFO) << "Run 3d Surround View demo";
183
184 // Call HIDL API "start3dSession"
185 sp<ISurroundView3dSession> surroundView3dSession;
186
187 SvResult svResult;
188 pSurroundViewService->start3dSession(
189 [&surroundView3dSession, &svResult](
190 const sp<ISurroundView3dSession>& session, SvResult result) {
191 surroundView3dSession = session;
192 svResult = result;
193 });
194
195 if (surroundView3dSession == nullptr || svResult != SvResult::OK) {
196 LOG(ERROR) << "Failed to start3dSession";
197 return false;
198 } else {
199 LOG(INFO) << "start3dSession succeeded";
200 }
201
202 sp<SurroundViewServiceCallback> sv3dCallback
203 = new SurroundViewServiceCallback(pDisplay, surroundView3dSession);
204
205 // A view must be set before the 3d stream is started.
206 if (!setView(surroundView3dSession, 0, 0, kHorizontalFov)) {
207 LOG(ERROR) << "Failed to setView of pose index :" << 0;
208 return false;
209 }
210
211 // Start 3d stream with callback
212 if (surroundView3dSession->startStream(sv3dCallback) != SvResult::OK) {
213 LOG(ERROR) << "Failed to start 3d stream";
214 return false;
215 }
216
217 // Let the SV algorithm run for 10 seconds for HIGH_QUALITY
218 const int totalViewingTimeSecs = 10;
219 const std::chrono::milliseconds
220 perPoseSleepTimeMs(totalViewingTimeSecs * 1000 / kPoseCount);
221 for(uint32_t i = 1; i < kPoseCount; i++) {
222 if (!setView(surroundView3dSession, i, i, kHorizontalFov)) {
223 LOG(WARNING) << "Failed to setView of pose index :" << i;
224 }
225 std::this_thread::sleep_for(perPoseSleepTimeMs);
226 }
227
228 // Switch to low quality and lower resolution
229 Sv3dConfig config;
230 config.width = kLowResolutionWidth;
231 config.height = kLowResolutionHeight;
232 config.carDetails = SvQuality::LOW;
233 if (surroundView3dSession->set3dConfig(config) != SvResult::OK) {
234 LOG(ERROR) << "Failed to set3dConfig";
235 return false;
236 }
237
238 // Let the SV algorithm run for 10 seconds for LOW_QUALITY
239 for(uint32_t i = 0; i < kPoseCount; i++) {
240 if(!setView(surroundView3dSession, i + kPoseCount, i, kHorizontalFov)) {
241 LOG(WARNING) << "Failed to setView of pose index :" << i;
242 }
243 std::this_thread::sleep_for(perPoseSleepTimeMs);
244 }
245
246 // TODO(b/150412555): wait for the last frame
247 // Stop the 3d stream and session
248 surroundView3dSession->stopStream();
249
250 pSurroundViewService->stop3dSession(surroundView3dSession);
251 surroundView3dSession = nullptr;
252
253 LOG(DEBUG) << "SV 3D session finished.";
254
255 return true;
256 };
257
258 // Main entry point
main(int argc,char ** argv)259 int main(int argc, char** argv) {
260 // Start up
261 LOG(INFO) << "SV app starting";
262
263 DemoMode mode = UNKNOWN;
264 for (int i=1; i< argc; i++) {
265 if (strcmp(argv[i], "--use2d") == 0) {
266 mode = DEMO_2D;
267 } else if (strcmp(argv[i], "--use3d") == 0) {
268 mode = DEMO_3D;
269 } else {
270 LOG(WARNING) << "Ignoring unrecognized command line arg: "
271 << argv[i];
272 }
273 }
274
275 if (mode == UNKNOWN) {
276 LOG(ERROR) << "No demo mode is specified. Exiting";
277 return EXIT_FAILURE;
278 }
279
280 // Set thread pool size to one to avoid concurrent events from the HAL.
281 // This pool will handle the SurroundViewStream callbacks.
282 configureRpcThreadpool(1, false /* callerWillJoin */);
283
284 // Try to connect to EVS service
285 LOG(INFO) << "Acquiring EVS Enumerator";
286 sp<IEvsEnumerator> evs = IEvsEnumerator::getService();
287 if (evs == nullptr) {
288 LOG(ERROR) << "getService(default) returned NULL. Exiting.";
289 return EXIT_FAILURE;
290 }
291
292 // Try to connect to SV service
293 LOG(INFO) << "Acquiring SV Service";
294 android::sp<ISurroundViewService> surroundViewService
295 = ISurroundViewService::getService("default");
296
297 if (surroundViewService == nullptr) {
298 LOG(ERROR) << "getService(default) returned NULL.";
299 return EXIT_FAILURE;
300 } else {
301 LOG(INFO) << "Get ISurroundViewService default";
302 }
303
304 // Connect to evs display
305 int displayId;
306 evs->getDisplayIdList([&displayId](auto idList) {
307 displayId = idList[0];
308 });
309
310 LOG(INFO) << "Acquiring EVS Display with ID: "
311 << displayId;
312 sp<IEvsDisplay> display = evs->openDisplay_1_1(displayId);
313 if (display == nullptr) {
314 LOG(ERROR) << "EVS Display unavailable. Exiting.";
315 return EXIT_FAILURE;
316 }
317
318 if (mode == DEMO_2D) {
319 if (!run2dSurroundView(surroundViewService, display)) {
320 LOG(ERROR) << "Something went wrong in 2d surround view demo. "
321 << "Exiting.";
322 return EXIT_FAILURE;
323 }
324 } else if (mode == DEMO_3D) {
325 if (!run3dSurroundView(surroundViewService, display)) {
326 LOG(ERROR) << "Something went wrong in 3d surround view demo. "
327 << "Exiting.";
328 return EXIT_FAILURE;
329 }
330 }
331
332 evs->closeDisplay(display);
333
334 LOG(DEBUG) << "SV sample app finished running successfully";
335 return EXIT_SUCCESS;
336 }
337