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