1 /*
2 * Copyright (C) 2017 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 "StreamHandler.h"
18
19 #include "Frame.h"
20 #include "ResourceManager.h"
21
22 #include <cutils/native_handle.h>
23 #include <log/log.h>
24 #include <ui/GraphicBufferAllocator.h>
25 #include <ui/GraphicBufferMapper.h>
26
27 #include <stdio.h>
28 #include <string.h>
29
30 namespace android {
31 namespace automotive {
32 namespace evs {
33 namespace support {
34
35 using ::android::hardware::automotive::evs::V1_0::EvsResult;
36 using ::std::lock_guard;
37 using ::std::unique_lock;
38
StreamHandler(android::sp<IEvsCamera> pCamera)39 StreamHandler::StreamHandler(android::sp<IEvsCamera> pCamera) :
40 mCamera(pCamera), mAnalyzeCallback(nullptr), mAnalyzerRunning(false) {
41 // We rely on the camera having at least two buffers available since we'll hold one and
42 // expect the camera to be able to capture a new image in the background.
43 pCamera->setMaxFramesInFlight(2);
44 }
45
46 // TODO(b/130246343): investigate further to make sure the resources are cleaned
47 // up properly in the shutdown logic.
shutdown()48 void StreamHandler::shutdown() {
49 // Tell the camera to stop streaming.
50 // This will result in a null frame being delivered when the stream actually stops.
51 mCamera->stopVideoStream();
52
53 // Wait until the stream has actually stopped
54 unique_lock<mutex> lock(mLock);
55 if (mRunning) {
56 mSignal.wait(lock, [this]() { return !mRunning; });
57 }
58
59 // At this point, the receiver thread is no longer running, so we can safely drop
60 // our remote object references so they can be freed
61 mCamera = nullptr;
62 }
63
startStream()64 bool StreamHandler::startStream() {
65 lock_guard<mutex> lock(mLock);
66
67 if (!mRunning) {
68 // Tell the camera to start streaming
69 Return<EvsResult> result = mCamera->startVideoStream(this);
70 if (result != EvsResult::OK) {
71 return false;
72 }
73
74 // Mark ourselves as running
75 mRunning = true;
76 }
77
78 return true;
79 }
80
newDisplayFrameAvailable()81 bool StreamHandler::newDisplayFrameAvailable() {
82 lock_guard<mutex> lock(mLock);
83 return (mReadyBuffer >= 0);
84 }
85
getNewDisplayFrame()86 const BufferDesc& StreamHandler::getNewDisplayFrame() {
87 lock_guard<mutex> lock(mLock);
88
89 if (mHeldBuffer >= 0) {
90 ALOGE("Ignored call for new frame while still holding the old one.");
91 } else {
92 if (mReadyBuffer < 0) {
93 ALOGE("Returning invalid buffer because we don't have any. "
94 " Call newDisplayFrameAvailable first?");
95 mReadyBuffer = 0; // This is a lie!
96 }
97
98 // Move the ready buffer into the held position, and clear the ready position
99 mHeldBuffer = mReadyBuffer;
100 mReadyBuffer = -1;
101 }
102
103 if (mRenderCallback == nullptr) {
104 return mOriginalBuffers[mHeldBuffer];
105 } else {
106 return mProcessedBuffers[mHeldBuffer];
107 }
108 }
109
doneWithFrame(const BufferDesc & buffer)110 void StreamHandler::doneWithFrame(const BufferDesc& buffer) {
111 lock_guard<mutex> lock(mLock);
112
113 // We better be getting back the buffer we original delivered!
114 if ((mHeldBuffer < 0) || (buffer.bufferId != mOriginalBuffers[mHeldBuffer].bufferId)) {
115 ALOGE("StreamHandler::doneWithFrame got an unexpected buffer!");
116 ALOGD("Held buffer id: %d, input buffer id: %d", mOriginalBuffers[mHeldBuffer].bufferId,
117 buffer.bufferId);
118 return;
119 }
120
121 // Send the buffer back to the underlying camera
122 mCamera->doneWithFrame(mOriginalBuffers[mHeldBuffer]);
123
124 // Clear the held position
125 mHeldBuffer = -1;
126 }
127
deliverFrame(const BufferDesc & buffer)128 Return<void> StreamHandler::deliverFrame(const BufferDesc& buffer) {
129 ALOGD("Received a frame from the camera. NativeHandle:%p, buffer id:%d",
130 buffer.memHandle.getNativeHandle(), buffer.bufferId);
131
132 // Take the lock to protect our frame slots and running state variable
133 {
134 lock_guard<mutex> lock(mLock);
135
136 if (buffer.memHandle.getNativeHandle() == nullptr) {
137 // Signal that the last frame has been received and the stream is stopped
138 mRunning = false;
139 } else {
140 // Do we already have a "ready" frame?
141 if (mReadyBuffer >= 0) {
142 // Send the previously saved buffer back to the camera unused
143 mCamera->doneWithFrame(mOriginalBuffers[mReadyBuffer]);
144
145 // We'll reuse the same ready buffer index
146 } else if (mHeldBuffer >= 0) {
147 // The client is holding a buffer, so use the other slot for "on deck"
148 mReadyBuffer = 1 - mHeldBuffer;
149 } else {
150 // This is our first buffer, so just pick a slot
151 mReadyBuffer = 0;
152 }
153
154 // Save this frame until our client is interested in it
155 mOriginalBuffers[mReadyBuffer] = buffer;
156
157 // If render callback is not null, process the frame with render
158 // callback.
159 if (mRenderCallback != nullptr) {
160 processFrame(mOriginalBuffers[mReadyBuffer], mProcessedBuffers[mReadyBuffer]);
161 } else {
162 ALOGI("Render callback is null in deliverFrame.");
163 }
164
165 // If analyze callback is not null and the analyze thread is
166 // available, copy the frame and run the analyze callback in
167 // analyze thread.
168 {
169 std::shared_lock<std::shared_mutex> analyzerLock(mAnalyzerLock);
170 if (mAnalyzeCallback != nullptr && !mAnalyzerRunning) {
171 copyAndAnalyzeFrame(mOriginalBuffers[mReadyBuffer]);
172 }
173 }
174 }
175 }
176
177 // Notify anybody who cares that things have changed
178 mSignal.notify_all();
179
180 return Void();
181 }
182
attachRenderCallback(BaseRenderCallback * callback)183 void StreamHandler::attachRenderCallback(BaseRenderCallback* callback) {
184 ALOGD("StreamHandler::attachRenderCallback");
185
186 lock_guard<mutex> lock(mLock);
187
188 if (mRenderCallback != nullptr) {
189 ALOGW("Ignored! There should only be one render callback");
190 return;
191 }
192 mRenderCallback = callback;
193 }
194
detachRenderCallback()195 void StreamHandler::detachRenderCallback() {
196 ALOGD("StreamHandler::detachRenderCallback");
197
198 lock_guard<mutex> lock(mLock);
199
200 mRenderCallback = nullptr;
201 }
202
attachAnalyzeCallback(BaseAnalyzeCallback * callback)203 void StreamHandler::attachAnalyzeCallback(BaseAnalyzeCallback* callback) {
204 ALOGD("StreamHandler::attachAnalyzeCallback");
205
206 if (mAnalyzeCallback != nullptr) {
207 ALOGW("Ignored! There should only be one analyze callcack");
208 return;
209 }
210
211 {
212 lock_guard<std::shared_mutex> lock(mAnalyzerLock);
213 mAnalyzeCallback = callback;
214 }
215 }
216
detachAnalyzeCallback()217 void StreamHandler::detachAnalyzeCallback() {
218 ALOGD("StreamHandler::detachAnalyzeCallback");
219
220 {
221 std::unique_lock<std::shared_mutex> lock(mAnalyzerLock);
222
223 // Wait until current running analyzer ends
224 mAnalyzerSignal.wait(lock, [this] { return !mAnalyzerRunning; });
225 mAnalyzeCallback = nullptr;
226 }
227 }
228
isSameFormat(const BufferDesc & input,const BufferDesc & output)229 bool isSameFormat(const BufferDesc& input, const BufferDesc& output) {
230 return input.width == output.width && input.height == output.height &&
231 input.format == output.format && input.usage == output.usage &&
232 input.stride == output.stride && input.pixelSize == output.pixelSize;
233 }
234
allocate(BufferDesc & buffer)235 bool allocate(BufferDesc& buffer) {
236 ALOGD("StreamHandler::allocate");
237 buffer_handle_t handle;
238 android::GraphicBufferAllocator& alloc(android::GraphicBufferAllocator::get());
239 android::status_t result =
240 alloc.allocate(buffer.width, buffer.height, buffer.format, 1, buffer.usage, &handle,
241 &buffer.stride, 0, "EvsDisplay");
242 if (result != android::NO_ERROR) {
243 ALOGE("Error %d allocating %d x %d graphics buffer", result, buffer.width, buffer.height);
244 return false;
245 }
246
247 // The reason that we have to check null for "handle" is because that the
248 // above "result" might not cover all the failure scenarios.
249 // By looking into Gralloc4.cpp (and 3, 2, as well), it turned out that if
250 // there is anything that goes wrong in the process of buffer importing (see
251 // Ln 385 in Gralloc4.cpp), the error won't be covered by the above "result"
252 // we got from "allocate" method. In other words, it means that there is
253 // still a chance that the "result" is "NO_ERROR" but the handle is nullptr
254 // (that means buffer importing failed).
255 if (!handle) {
256 ALOGE("We didn't get a buffer handle back from the allocator");
257 return false;
258 }
259
260 buffer.memHandle = hidl_handle(handle);
261 return true;
262 }
263
processFrame(const BufferDesc & input,BufferDesc & output)264 bool StreamHandler::processFrame(const BufferDesc& input, BufferDesc& output) {
265 ALOGD("StreamHandler::processFrame");
266 if (!isSameFormat(input, output) || output.memHandle.getNativeHandle() == nullptr) {
267 output.width = input.width;
268 output.height = input.height;
269 output.format = input.format;
270 output.usage = input.usage;
271 output.stride = input.stride;
272 output.pixelSize = input.pixelSize;
273
274 // free the allocated output frame handle if it is not null
275 if (output.memHandle.getNativeHandle() != nullptr) {
276 GraphicBufferAllocator::get().free(output.memHandle);
277 }
278
279 if (!allocate(output)) {
280 ALOGE("Error allocating buffer");
281 return false;
282 }
283 }
284 output.bufferId = input.bufferId;
285
286 // Create a GraphicBuffer from the existing handle
287 sp<GraphicBuffer> inputBuffer =
288 new GraphicBuffer(input.memHandle, GraphicBuffer::CLONE_HANDLE, input.width,
289 input.height, input.format, 1, // layer count
290 GRALLOC_USAGE_HW_TEXTURE, input.stride);
291
292 if (inputBuffer.get() == nullptr) {
293 ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
294 // Returning "true" in this error condition because we already released
295 // the previous image (if any) and so the texture may change in
296 // unpredictable ways now!
297 return false;
298 }
299
300 // Lock the input GraphicBuffer and map it to a pointer. If we failed to
301 // lock, return false.
302 void* inputDataPtr;
303 inputBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, &inputDataPtr);
304
305 // Unlock the buffer and return if lock did not succeed.
306 if (!inputDataPtr) {
307 ALOGE("Failed to gain read access to image buffer");
308
309 // The program reaches at here when it fails to lock the buffer. But
310 // it is still safer to unlock it. The reason is as described in "lock"
311 // method in Gralloc.h: "The ownership of acquireFence is always
312 // transferred to the callee, even on errors."
313 // And even if the buffer was not locked, it does not harm anything
314 // given the comment for "unlock" method in IMapper.hal:
315 // "`BAD_BUFFER` if the buffer is invalid or not locked."
316 inputBuffer->unlock();
317 return false;
318 }
319
320 // Lock the allocated buffer in output BufferDesc and map it to a pointer
321 void* outputDataPtr = nullptr;
322 android::GraphicBufferMapper& mapper = android::GraphicBufferMapper::get();
323 mapper.lock(output.memHandle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
324 android::Rect(output.width, output.height), (void**)&outputDataPtr);
325
326 // If we failed to lock the pixel buffer, return false, and unlock both
327 // input and output buffers.
328 if (!outputDataPtr) {
329 ALOGE("Failed to gain write access to image buffer");
330
331 // Please refer to the previous "if" block for why we want to unlock
332 // the buffers even if the buffer locking fails.
333 inputBuffer->unlock();
334 mapper.unlock(output.memHandle);
335 return false;
336 }
337
338 // Wrap the raw data and copied data, and pass them to the callback.
339 Frame inputFrame = {.width = input.width,
340 .height = input.height,
341 .stride = input.stride,
342 .data = (uint8_t*)inputDataPtr};
343
344 Frame outputFrame = {.width = output.width,
345 .height = output.height,
346 .stride = output.stride,
347 .data = (uint8_t*)outputDataPtr};
348
349 mRenderCallback->render(inputFrame, outputFrame);
350
351 // Unlock the buffers after all changes to the buffer are completed.
352 inputBuffer->unlock();
353 mapper.unlock(output.memHandle);
354
355 return true;
356 }
357
copyAndAnalyzeFrame(const BufferDesc & input)358 bool StreamHandler::copyAndAnalyzeFrame(const BufferDesc& input) {
359 ALOGD("StreamHandler::copyAndAnalyzeFrame");
360
361 // TODO(b/130246434): make the following into a method. Some lines are
362 // duplicated with processFrame, move them into new methods as well.
363 if (!isSameFormat(input, mAnalyzeBuffer) ||
364 mAnalyzeBuffer.memHandle.getNativeHandle() == nullptr) {
365 mAnalyzeBuffer.width = input.width;
366 mAnalyzeBuffer.height = input.height;
367 mAnalyzeBuffer.format = input.format;
368 mAnalyzeBuffer.usage = input.usage;
369 mAnalyzeBuffer.stride = input.stride;
370 mAnalyzeBuffer.pixelSize = input.pixelSize;
371 mAnalyzeBuffer.bufferId = input.bufferId;
372
373 // free the allocated output frame handle if it is not null
374 if (mAnalyzeBuffer.memHandle.getNativeHandle() != nullptr) {
375 GraphicBufferAllocator::get().free(mAnalyzeBuffer.memHandle);
376 }
377
378 if (!allocate(mAnalyzeBuffer)) {
379 ALOGE("Error allocating buffer");
380 return false;
381 }
382 }
383
384 // create a GraphicBuffer from the existing handle
385 sp<GraphicBuffer> inputBuffer =
386 new GraphicBuffer(input.memHandle, GraphicBuffer::CLONE_HANDLE, input.width,
387 input.height, input.format, 1, // layer count
388 GRALLOC_USAGE_HW_TEXTURE, input.stride);
389
390 if (inputBuffer.get() == nullptr) {
391 ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
392 // Returning "true" in this error condition because we already released the
393 // previous image (if any) and so the texture may change in unpredictable
394 // ways now!
395 return false;
396 }
397
398 // Lock the input GraphicBuffer and map it to a pointer. If we failed to
399 // lock, return false.
400 void* inputDataPtr;
401 inputBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, &inputDataPtr);
402 if (!inputDataPtr) {
403 ALOGE("Failed to gain read access to imageGraphicBuffer");
404 inputBuffer->unlock();
405 return false;
406 }
407
408 // Lock the allocated buffer in output BufferDesc and map it to a pointer
409 void* analyzeDataPtr = nullptr;
410 android::GraphicBufferMapper::get().lock(mAnalyzeBuffer.memHandle,
411 GRALLOC_USAGE_SW_WRITE_OFTEN |
412 GRALLOC_USAGE_SW_READ_NEVER,
413 android::Rect(mAnalyzeBuffer.width,
414 mAnalyzeBuffer.height),
415 (void**)&analyzeDataPtr);
416
417 // If we failed to lock the pixel buffer, return false, and unlock both
418 // input and output buffers.
419 if (!analyzeDataPtr) {
420 ALOGE("Camera failed to gain access to image buffer for analyzing");
421 return false;
422 }
423
424 // Wrap the raw data and copied data, and pass them to the callback.
425 Frame analyzeFrame = {
426 .width = mAnalyzeBuffer.width,
427 .height = mAnalyzeBuffer.height,
428 .stride = mAnalyzeBuffer.stride,
429 .data = (uint8_t*)analyzeDataPtr,
430 };
431
432 memcpy(analyzeDataPtr, inputDataPtr, mAnalyzeBuffer.stride * mAnalyzeBuffer.height * 4);
433
434 // Unlock the buffers after all changes to the buffer are completed.
435 inputBuffer->unlock();
436
437 mAnalyzerRunning = true;
438 std::thread([this, analyzeFrame]() {
439 ALOGD("StreamHandler: Analyze Thread starts");
440
441 std::shared_lock<std::shared_mutex> lock(mAnalyzerLock);
442 if (this->mAnalyzeCallback != nullptr) {
443 this->mAnalyzeCallback->analyze(analyzeFrame);
444 android::GraphicBufferMapper::get().unlock(this->mAnalyzeBuffer.memHandle);
445 }
446 this->mAnalyzerRunning = false;
447 mAnalyzerSignal.notify_one();
448 ALOGD("StreamHandler: Analyze Thread ends");
449 }).detach();
450
451 return true;
452 }
453
454 } // namespace support
455 } // namespace evs
456 } // namespace automotive
457 } // namespace android
458