1 /*
2  * Copyright (C) 2019 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 #include "DisplayUseCase.h"
17 
18 #include "RenderDirectView.h"
19 #include "Utils.h"
20 
21 #include <hidl/HidlTransportSupport.h>
22 #include <log/log.h>
23 #include <utils/SystemClock.h>
24 
25 namespace android {
26 namespace automotive {
27 namespace evs {
28 namespace support {
29 
30 using ::android::hardware::configureRpcThreadpool;
31 using ::android::hardware::joinRpcThreadpool;
32 using ::android::hardware::automotive::evs::V1_0::DisplayState;
33 using ::android::hardware::automotive::evs::V1_0::EvsResult;
34 
35 // TODO(b/130246434): since we don't support multi-display use case, there
36 // should only be one DisplayUseCase. Add the logic to prevent more than
37 // one DisplayUseCases running at the same time.
DisplayUseCase(string cameraId,BaseRenderCallback * callback)38 DisplayUseCase::DisplayUseCase(string cameraId, BaseRenderCallback* callback) :
39       BaseUseCase(vector<string>(1, cameraId)) {
40     mRenderCallback = callback;
41 }
42 
~DisplayUseCase()43 DisplayUseCase::~DisplayUseCase() {
44     if (mCurrentRenderer != nullptr) {
45         mCurrentRenderer->deactivate();
46         mCurrentRenderer = nullptr;  // It's a smart pointer, so destructs on assignment to null
47     }
48 
49     mIsReadyToRun = false;
50     if (mWorkerThread.joinable()) {
51         mWorkerThread.join();
52     }
53 }
54 
initialize()55 bool DisplayUseCase::initialize() {
56     // Load our configuration information
57     ConfigManager config;
58     if (!config.initialize("/system/etc/automotive/evs_support_lib/camera_config.json")) {
59         ALOGE("Missing or improper configuration for the EVS application.  Exiting.");
60         return false;
61     }
62 
63     // Set thread pool size to one to avoid concurrent events from the HAL.
64     // This pool will handle the EvsCameraStream callbacks.
65     // Note:  This _will_ run in parallel with the EvsListener run() loop below which
66     // runs the application logic that reacts to the async events.
67     configureRpcThreadpool(1, false /* callerWillJoin */);
68 
69     mResourceManager = ResourceManager::getInstance();
70     if (mResourceManager == nullptr) {
71         ALOGE("Failed to get resource manager instance. Initialization failed.");
72         return false;
73     }
74 
75     // Request exclusive access to the EVS display
76     ALOGI("Acquiring EVS Display");
77 
78     mDisplay = mResourceManager->openDisplay();
79     if (mDisplay.get() == nullptr) {
80         ALOGE("EVS Display unavailable.  Exiting.");
81         return false;
82     }
83 
84     ALOGD("Requesting camera list");
85     for (auto&& info : config.getCameras()) {
86         // This use case is currently a single camera use case.
87         // Only one element is available in the camera id list.
88         string cameraId = mCameraIds[0];
89         if (cameraId == info.cameraId) {
90             mStreamHandler = mResourceManager->obtainStreamHandler(cameraId);
91             if (mStreamHandler.get() == nullptr) {
92                 ALOGE("Failed to get a valid StreamHandler for %s", cameraId.c_str());
93                 return false;
94             }
95 
96             mIsInitialized = true;
97             return true;
98         }
99     }
100 
101     ALOGE("Cannot find a match camera. Exiting");
102     return false;
103 }
104 
105 // TODO(b/130246434): if user accidentally call this function twice, there is
106 // no logic to handle that and it will causes issues. For example, the
107 // mWorkerThread will be assigned twice and cause unexpected behavior.
108 // We need to fix this issue.
startVideoStream()109 bool DisplayUseCase::startVideoStream() {
110     // Initialize the use case.
111     if (!mIsInitialized && !initialize()) {
112         ALOGE("There is an error while initializing the use case. Exiting");
113         return false;
114     }
115 
116     ALOGD("Attach use case to StreamHandler");
117     if (mRenderCallback != nullptr) {
118         mStreamHandler->attachRenderCallback(mRenderCallback);
119     }
120 
121     ALOGD("Start video streaming using worker thread");
122     mIsReadyToRun = true;
123     mWorkerThread = std::thread([this]() {
124         // We have a camera assigned to this state for direct view
125         mCurrentRenderer = std::make_unique<RenderDirectView>();
126         if (!mCurrentRenderer) {
127             ALOGE("Failed to construct direct renderer. Exiting.");
128             mIsReadyToRun = false;
129             return;
130         }
131 
132         // Now set the display state based on whether we have a video feed to show
133         // Start the camera stream
134         ALOGD("EvsStartCameraStreamTiming start time: %" PRId64 "ms", android::elapsedRealtime());
135         if (!mCurrentRenderer->activate()) {
136             ALOGE("New renderer failed to activate. Exiting");
137             mIsReadyToRun = false;
138             return;
139         }
140 
141         // Activate the display
142         ALOGD("EvsActivateDisplayTiming start time: %" PRId64 "ms", android::elapsedRealtime());
143         Return<EvsResult> result = mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
144         if (result != EvsResult::OK) {
145             ALOGE("setDisplayState returned an error (%d). Exiting.", (EvsResult)result);
146             mIsReadyToRun = false;
147             return;
148         }
149 
150         if (!mStreamHandler->startStream()) {
151             ALOGE("failed to start stream handler");
152             mIsReadyToRun = false;
153             return;
154         }
155 
156         while (mIsReadyToRun && streamFrame());
157 
158         ALOGD("Worker thread stops.");
159     });
160 
161     return true;
162 }
163 
stopVideoStream()164 void DisplayUseCase::stopVideoStream() {
165     ALOGD("Stop video streaming in worker thread.");
166     mIsReadyToRun = false;
167 
168     if (mStreamHandler == nullptr) {
169         ALOGE("Failed to detach render callback since stream handler is null");
170 
171         // Something may go wrong. Instead of to return this method right away,
172         // we want to finish the remaining logic of this method to try to
173         // release other resources.
174     } else {
175         mStreamHandler->detachRenderCallback();
176     }
177 
178     if (mResourceManager == nullptr) {
179         ALOGE("Failed to release resources since resource manager is null");
180     } else {
181         mResourceManager->releaseStreamHandler(mCameraIds[0]);
182         mStreamHandler = nullptr;
183 
184         mResourceManager->closeDisplay(mDisplay);
185         mDisplay = nullptr;
186 
187         // TODO(b/130246434): with the current logic, the initialize method will
188         // be triggered every time when a pair of
189         // stopVideoStream/startVideoStream is called. We might want to move
190         // some heavy work away from initialize method so increase the
191         // performance.
192 
193         // Sets mIsInitialzed to false so the initialize method will be
194         // triggered when startVideoStream is called again.
195         mIsInitialized = false;
196     }
197     return;
198 }
199 
streamFrame()200 bool DisplayUseCase::streamFrame() {
201     // Get the output buffer we'll use to display the imagery
202     BufferDesc tgtBuffer = {};
203     mDisplay->getTargetBuffer([&tgtBuffer](const BufferDesc& buff) { tgtBuffer = buff; });
204 
205     // TODO(b/130246434): if there is no new display frame available, shall we
206     // still get display buffer? Shall we just skip and keep the display
207     // un-refreshed?
208     // We should explore this option.
209 
210     // If there is no display buffer available, skip it.
211     if (tgtBuffer.memHandle == nullptr) {
212         ALOGW("Didn't get requested output buffer -- skipping this frame.");
213 
214         // Return true since it won't affect next call.
215         return true;
216     } else {
217         // If there is no new display frame available, re-use the old (held)
218         // frame for display.
219         // Otherwise, return the old (held) frame, fetch the newly available
220         // frame from stream handler, and use the new frame for display
221         // purposes.
222         if (!mStreamHandler->newDisplayFrameAvailable()) {
223             ALOGD("No new display frame is available. Re-use the old frame.");
224         } else {
225             ALOGD("Get new display frame, refreshing");
226 
227             // If we already hold a camera image for display purposes, it's
228             // time to return it to evs camera driver.
229             if (mImageBuffer.memHandle.getNativeHandle() != nullptr) {
230                 mStreamHandler->doneWithFrame(mImageBuffer);
231             }
232 
233             // Get the new image we want to use as our display content
234             mImageBuffer = mStreamHandler->getNewDisplayFrame();
235         }
236 
237         // Render the image buffer to the display buffer
238         bool result = mCurrentRenderer->drawFrame(tgtBuffer, mImageBuffer);
239 
240         // Send the finished display buffer back to display driver
241         // Even if the rendering fails, we still want to return the display
242         // buffer.
243         mDisplay->returnTargetBufferForDisplay(tgtBuffer);
244 
245         return result;
246     }
247 }
248 
createDefaultUseCase(string cameraId,BaseRenderCallback * callback)249 DisplayUseCase DisplayUseCase::createDefaultUseCase(string cameraId, BaseRenderCallback* callback) {
250     return DisplayUseCase(cameraId, callback);
251 }
252 
253 }  // namespace support
254 }  // namespace evs
255 }  // namespace automotive
256 }  // namespace android
257