blob: 4e557b81b13ffcfdc6a4688df8317510ebe3ca33 [file] [log] [blame]
Haoxiang Lib21a13e2019-06-18 13:16:28 -07001/*
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
Haoxiang Li5cb69052019-08-30 16:24:25 -070025#include <ui/GraphicBufferAllocator.h>
26#include <ui/GraphicBufferMapper.h>
27
28#include "Frame.h"
Haoxiang Li3993d002019-09-17 14:42:02 -070029#include "ResourceManager.h"
Haoxiang Li5cb69052019-08-30 16:24:25 -070030
Haoxiang Lid2454a52019-06-18 13:23:12 -070031namespace android {
32namespace automotive {
33namespace evs {
34namespace support {
Haoxiang Lib21a13e2019-06-18 13:16:28 -070035
Haoxiang Li3993d002019-09-17 14:42:02 -070036using ::std::lock_guard;
37using ::std::unique_lock;
38
Haoxiang Lib21a13e2019-06-18 13:16:28 -070039StreamHandler::StreamHandler(android::sp <IEvsCamera> pCamera) :
Changyeon Jod4e24762020-02-28 11:15:29 -080040 mCamera(pCamera),
41 mAnalyzeCallback(nullptr),
42 mAnalyzerRunning(false)
Haoxiang Lib21a13e2019-06-18 13:16:28 -070043{
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
Haoxiang Lic8888592019-09-19 16:39:53 -070049// TODO(b/130246343): investigate further to make sure the resources are cleaned
50// up properly in the shutdown logic.
Haoxiang Lib21a13e2019-06-18 13:16:28 -070051void StreamHandler::shutdown()
52{
Haoxiang Li3993d002019-09-17 14:42:02 -070053 // 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 }
Haoxiang Lib21a13e2019-06-18 13:16:28 -070062
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
69bool StreamHandler::startStream() {
Haoxiang Li3993d002019-09-17 14:42:02 -070070 lock_guard<mutex> lock(mLock);
Haoxiang Lib21a13e2019-06-18 13:16:28 -070071
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
Haoxiang Li5cb69052019-08-30 16:24:25 -070087bool StreamHandler::newDisplayFrameAvailable() {
Haoxiang Li3993d002019-09-17 14:42:02 -070088 lock_guard<mutex> lock(mLock);
Haoxiang Lib21a13e2019-06-18 13:16:28 -070089 return (mReadyBuffer >= 0);
90}
91
92
Haoxiang Li5cb69052019-08-30 16:24:25 -070093const BufferDesc& StreamHandler::getNewDisplayFrame() {
Haoxiang Li3993d002019-09-17 14:42:02 -070094 lock_guard<mutex> lock(mLock);
Haoxiang Lib21a13e2019-06-18 13:16:28 -070095
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. "
Haoxiang Li5cb69052019-08-30 16:24:25 -0700101 " Call newDisplayFrameAvailable first?");
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700102 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
Haoxiang Li5cb69052019-08-30 16:24:25 -0700110 if (mRenderCallback == nullptr) {
111 return mOriginalBuffers[mHeldBuffer];
112 } else {
113 return mProcessedBuffers[mHeldBuffer];
114 }
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700115}
116
117
118void StreamHandler::doneWithFrame(const BufferDesc& buffer) {
Haoxiang Li3993d002019-09-17 14:42:02 -0700119 lock_guard<mutex> lock(mLock);
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700120
121 // We better be getting back the buffer we original delivered!
Haoxiang Li5cb69052019-08-30 16:24:25 -0700122 if ((mHeldBuffer < 0)
123 || (buffer.bufferId != mOriginalBuffers[mHeldBuffer].bufferId)) {
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700124 ALOGE("StreamHandler::doneWithFrame got an unexpected buffer!");
Haoxiang Li3ef42ef2019-09-18 16:49:01 -0700125 ALOGD("Held buffer id: %d, input buffer id: %d",
126 mOriginalBuffers[mHeldBuffer].bufferId, buffer.bufferId);
Haoxiang Li5cb69052019-08-30 16:24:25 -0700127 return;
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700128 }
129
130 // Send the buffer back to the underlying camera
Haoxiang Li5cb69052019-08-30 16:24:25 -0700131 mCamera->doneWithFrame(mOriginalBuffers[mHeldBuffer]);
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700132
133 // Clear the held position
134 mHeldBuffer = -1;
135}
136
137
138Return<void> StreamHandler::deliverFrame(const BufferDesc& buffer) {
Haoxiang Li3ef42ef2019-09-18 16:49:01 -0700139 ALOGD("Received a frame from the camera. NativeHandle:%p, buffer id:%d",
140 buffer.memHandle.getNativeHandle(), buffer.bufferId);
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700141
142 // Take the lock to protect our frame slots and running state variable
143 {
Haoxiang Li3993d002019-09-17 14:42:02 -0700144 lock_guard <mutex> lock(mLock);
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700145
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
Haoxiang Li5cb69052019-08-30 16:24:25 -0700153 mCamera->doneWithFrame(mOriginalBuffers[mReadyBuffer]);
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700154
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
Haoxiang Li5cb69052019-08-30 16:24:25 -0700165 mOriginalBuffers[mReadyBuffer] = buffer;
166
Haoxiang Li3993d002019-09-17 14:42:02 -0700167 // If render callback is not null, process the frame with render
168 // callback.
Haoxiang Li5cb69052019-08-30 16:24:25 -0700169 if (mRenderCallback != nullptr) {
Haoxiang Li3993d002019-09-17 14:42:02 -0700170 processFrame(mOriginalBuffers[mReadyBuffer],
171 mProcessedBuffers[mReadyBuffer]);
Haoxiang Li5cb69052019-08-30 16:24:25 -0700172 } else {
173 ALOGI("Render callback is null in deliverFrame.");
174 }
Haoxiang Li3993d002019-09-17 14:42:02 -0700175
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.
Changyeon Jod4e24762020-02-28 11:15:29 -0800179 {
180 std::shared_lock<std::shared_mutex> analyzerLock(mAnalyzerLock);
181 if (mAnalyzeCallback != nullptr && !mAnalyzerRunning) {
182 copyAndAnalyzeFrame(mOriginalBuffers[mReadyBuffer]);
183 }
Haoxiang Li3993d002019-09-17 14:42:02 -0700184 }
Haoxiang Lib21a13e2019-06-18 13:16:28 -0700185 }
186 }
187
188 // Notify anybody who cares that things have changed
189 mSignal.notify_all();
190
191 return Void();
192}
Haoxiang Lid2454a52019-06-18 13:23:12 -0700193
Haoxiang Li5cb69052019-08-30 16:24:25 -0700194void StreamHandler::attachRenderCallback(BaseRenderCallback* callback) {
195 ALOGD("StreamHandler::attachRenderCallback");
Haoxiang Li3993d002019-09-17 14:42:02 -0700196
197 lock_guard<mutex> lock(mLock);
198
Haoxiang Li5cb69052019-08-30 16:24:25 -0700199 if (mRenderCallback != nullptr) {
200 ALOGW("Ignored! There should only be one render callback");
201 return;
202 }
203 mRenderCallback = callback;
204}
205
206void StreamHandler::detachRenderCallback() {
207 ALOGD("StreamHandler::detachRenderCallback");
Haoxiang Li3993d002019-09-17 14:42:02 -0700208
209 lock_guard<mutex> lock(mLock);
210
211 mRenderCallback = nullptr;
212}
213
214void StreamHandler::attachAnalyzeCallback(BaseAnalyzeCallback* callback) {
215 ALOGD("StreamHandler::attachAnalyzeCallback");
216
Haoxiang Li3993d002019-09-17 14:42:02 -0700217 if (mAnalyzeCallback != nullptr) {
218 ALOGW("Ignored! There should only be one analyze callcack");
Haoxiang Li5cb69052019-08-30 16:24:25 -0700219 return;
220 }
Changyeon Jod4e24762020-02-28 11:15:29 -0800221
222 {
223 lock_guard<std::shared_mutex> lock(mAnalyzerLock);
224 mAnalyzeCallback = callback;
225 }
Haoxiang Li3993d002019-09-17 14:42:02 -0700226}
227
228void StreamHandler::detachAnalyzeCallback() {
229 ALOGD("StreamHandler::detachAnalyzeCallback");
Haoxiang Li3993d002019-09-17 14:42:02 -0700230
Changyeon Jod4e24762020-02-28 11:15:29 -0800231 {
Changyeon Jo5162a6c2020-03-02 14:25:36 -0800232 std::unique_lock<std::shared_mutex> lock(mAnalyzerLock);
233
234 // Wait until current running analyzer ends
235 mAnalyzerSignal.wait(lock, [this] { return !mAnalyzerRunning; });
Changyeon Jod4e24762020-02-28 11:15:29 -0800236 mAnalyzeCallback = nullptr;
237 }
Haoxiang Li5cb69052019-08-30 16:24:25 -0700238}
239
240bool 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
249bool 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
279bool 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;
Haoxiang Li5cb69052019-08-30 16:24:25 -0700290
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 }
Sean (Yingyu) Wan21dc17f2020-01-15 14:52:56 -0800301 output.bufferId = input.bufferId;
Haoxiang Li5cb69052019-08-30 16:24:25 -0700302
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}
Haoxiang Li3993d002019-09-17 14:42:02 -0700382
383bool 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;
Changyeon Jo5162a6c2020-03-02 14:25:36 -0800464 std::thread([this, analyzeFrame]() {
Haoxiang Li3993d002019-09-17 14:42:02 -0700465 ALOGD("StreamHandler: Analyze Thread starts");
466
Changyeon Jod4e24762020-02-28 11:15:29 -0800467 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 }
Haoxiang Li3993d002019-09-17 14:42:02 -0700472 this->mAnalyzerRunning = false;
Changyeon Jo5162a6c2020-03-02 14:25:36 -0800473 mAnalyzerSignal.notify_one();
Haoxiang Li3993d002019-09-17 14:42:02 -0700474 ALOGD("StreamHandler: Analyze Thread ends");
Changyeon Jo5162a6c2020-03-02 14:25:36 -0800475 }).detach();
Haoxiang Li3993d002019-09-17 14:42:02 -0700476
477 return true;
478}
479
Haoxiang Lid2454a52019-06-18 13:23:12 -0700480} // namespace support
481} // namespace evs
482} // namespace automotive
483} // namespace android