blob: 62d9826f24f127aa25702835630887a765d8b15a [file] [log] [blame]
Changyeon Jo2400b692019-07-18 21:32:48 -07001/*
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
17#define LOG_TAG "android.hardware.automotive.evs@1.1-service"
18
19#include "EvsCamera.h"
20#include "EvsEnumerator.h"
21
22#include <ui/GraphicBufferAllocator.h>
23#include <ui/GraphicBufferMapper.h>
24
25
26namespace android {
27namespace hardware {
28namespace automotive {
29namespace evs {
30namespace V1_1 {
31namespace implementation {
32
33
34// Special camera names for which we'll initialize alternate test data
35const char EvsCamera::kCameraName_Backup[] = "backup";
36
37
38// Arbitrary limit on number of graphics buffers allowed to be allocated
39// Safeguards against unreasonable resource consumption and provides a testable limit
40const unsigned MAX_BUFFERS_IN_FLIGHT = 100;
41
42
43EvsCamera::EvsCamera(const char *id) :
44 mFramesAllowed(0),
45 mFramesInUse(0),
46 mStreamState(STOPPED) {
47
48 ALOGD("EvsCamera instantiated");
49
50 mDescription.cameraId = id;
51
52 // Set up dummy data for testing
53 if (mDescription.cameraId == kCameraName_Backup) {
54 mWidth = 640; // full NTSC/VGA
55 mHeight = 480; // full NTSC/VGA
56 mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
57 } else {
58 mWidth = 320; // 1/2 NTSC/VGA
59 mHeight = 240; // 1/2 NTSC/VGA
60 }
61
62 mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
63 mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
64 GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
65}
66
67
68EvsCamera::~EvsCamera() {
69 ALOGD("EvsCamera being destroyed");
70 forceShutdown();
71}
72
73
74//
75// This gets called if another caller "steals" ownership of the camera
76//
77void EvsCamera::forceShutdown()
78{
79 ALOGD("EvsCamera forceShutdown");
80
81 // Make sure our output stream is cleaned up
82 // (It really should be already)
83 stopVideoStream();
84
85 // Claim the lock while we work on internal state
86 std::lock_guard <std::mutex> lock(mAccessLock);
87
88 // Drop all the graphics buffers we've been using
89 if (mBuffers.size() > 0) {
90 GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
91 for (auto&& rec : mBuffers) {
92 if (rec.inUse) {
93 ALOGE("Error - releasing buffer despite remote ownership");
94 }
95 alloc.free(rec.handle);
96 rec.handle = nullptr;
97 }
98 mBuffers.clear();
99 }
100
101 // Put this object into an unrecoverable error state since somebody else
102 // is going to own the underlying camera now
103 mStreamState = DEAD;
104}
105
106
107// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
108Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
109 ALOGD("getCameraInfo");
110
111 // Send back our self description
112 _hidl_cb(mDescription);
113 return Void();
114}
115
116
117Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
118 ALOGD("setMaxFramesInFlight");
119 std::lock_guard<std::mutex> lock(mAccessLock);
120
121 // If we've been displaced by another owner of the camera, then we can't do anything else
122 if (mStreamState == DEAD) {
123 ALOGE("ignoring setMaxFramesInFlight call when camera has been lost.");
124 return EvsResult::OWNERSHIP_LOST;
125 }
126
127 // We cannot function without at least one video buffer to send data
128 if (bufferCount < 1) {
129 ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested");
130 return EvsResult::INVALID_ARG;
131 }
132
133 // Update our internal state
134 if (setAvailableFrames_Locked(bufferCount)) {
135 return EvsResult::OK;
136 } else {
137 return EvsResult::BUFFER_NOT_AVAILABLE;
138 }
139}
140
141
142Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream_1_0>& stream) {
143 ALOGD("startVideoStream");
144 std::lock_guard<std::mutex> lock(mAccessLock);
145
146 // If we've been displaced by another owner of the camera, then we can't do anything else
147 if (mStreamState == DEAD) {
148 ALOGE("ignoring startVideoStream call when camera has been lost.");
149 return EvsResult::OWNERSHIP_LOST;
150 }
151 if (mStreamState != STOPPED) {
152 ALOGE("ignoring startVideoStream call when a stream is already running.");
153 return EvsResult::STREAM_ALREADY_RUNNING;
154 }
155
156 // If the client never indicated otherwise, configure ourselves for a single streaming buffer
157 if (mFramesAllowed < 1) {
158 if (!setAvailableFrames_Locked(1)) {
159 ALOGE("Failed to start stream because we couldn't get a graphics buffer");
160 return EvsResult::BUFFER_NOT_AVAILABLE;
161 }
162 }
163
164 // Record the user's callback for use when we have a frame ready
165 mStream = IEvsCameraStream_1_1::castFrom(stream).withDefault(nullptr);
166 if (mStream == nullptr) {
167 ALOGE("Default implementation does not support v1.0 IEvsCameraStream");
168 return EvsResult::INVALID_ARG;
169 }
170
171 // Start the frame generation thread
172 mStreamState = RUNNING;
173 mCaptureThread = std::thread([this](){ generateFrames(); });
174
175 return EvsResult::OK;
176}
177
178
179Return<void> EvsCamera::doneWithFrame(const BufferDesc_1_0& buffer) {
180 std::lock_guard <std::mutex> lock(mAccessLock);
181 returnBuffer(buffer.bufferId, buffer.memHandle);
182
183 return Void();
184}
185
186
187Return<void> EvsCamera::stopVideoStream() {
188 ALOGD("stopVideoStream");
189 std::unique_lock <std::mutex> lock(mAccessLock);
190
191 if (mStreamState == RUNNING) {
192 // Tell the GenerateFrames loop we want it to stop
193 mStreamState = STOPPING;
194
195 // Block outside the mutex until the "stop" flag has been acknowledged
196 // We won't send any more frames, but the client might still get some already in flight
197 ALOGD("Waiting for stream thread to end..");
198 lock.unlock();
199 mCaptureThread.join();
200 lock.lock();
201
202 mStreamState = STOPPED;
203 mStream = nullptr;
204 ALOGD("Stream marked STOPPED.");
205 }
206
207 return Void();
208}
209
210
211Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
212 ALOGD("getExtendedInfo");
213 std::lock_guard<std::mutex> lock(mAccessLock);
214
215 // For any single digit value, return the index itself as a test value
216 if (opaqueIdentifier <= 9) {
217 return opaqueIdentifier;
218 }
219
220 // Return zero by default as required by the spec
221 return 0;
222}
223
224
225Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) {
226 ALOGD("setExtendedInfo");
227 std::lock_guard<std::mutex> lock(mAccessLock);
228
229 // If we've been displaced by another owner of the camera, then we can't do anything else
230 if (mStreamState == DEAD) {
231 ALOGE("ignoring setExtendedInfo call when camera has been lost.");
232 return EvsResult::OWNERSHIP_LOST;
233 }
234
235 // We don't store any device specific information in this implementation
236 return EvsResult::INVALID_ARG;
237}
238
239
240// Methods from ::android::hardware::automotive::evs::V1_1::IEvsCamera follow.
241Return<EvsResult> EvsCamera::doneWithFrame_1_1(const BufferDesc_1_1& bufDesc) {
242 std::lock_guard <std::mutex> lock(mAccessLock);
243 returnBuffer(bufDesc.bufferId, bufDesc.buffer.nativeHandle);
244
245 return EvsResult::OK;
246}
247
248
249Return<EvsResult> EvsCamera::pauseVideoStream() {
250 // Default implementation does not support this.
251 return EvsResult::UNDERLYING_SERVICE_ERROR;
252}
253
254
255Return<EvsResult> EvsCamera::resumeVideoStream() {
256 // Default implementation does not support this.
257 return EvsResult::UNDERLYING_SERVICE_ERROR;
258}
259
260
261bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
262 if (bufferCount < 1) {
263 ALOGE("Ignoring request to set buffer count to zero");
264 return false;
265 }
266 if (bufferCount > MAX_BUFFERS_IN_FLIGHT) {
267 ALOGE("Rejecting buffer request in excess of internal limit");
268 return false;
269 }
270
271 // Is an increase required?
272 if (mFramesAllowed < bufferCount) {
273 // An increase is required
274 unsigned needed = bufferCount - mFramesAllowed;
275 ALOGI("Allocating %d buffers for camera frames", needed);
276
277 unsigned added = increaseAvailableFrames_Locked(needed);
278 if (added != needed) {
279 // If we didn't add all the frames we needed, then roll back to the previous state
280 ALOGE("Rolling back to previous frame queue size");
281 decreaseAvailableFrames_Locked(added);
282 return false;
283 }
284 } else if (mFramesAllowed > bufferCount) {
285 // A decrease is required
286 unsigned framesToRelease = mFramesAllowed - bufferCount;
287 ALOGI("Returning %d camera frame buffers", framesToRelease);
288
289 unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
290 if (released != framesToRelease) {
291 // This shouldn't happen with a properly behaving client because the client
292 // should only make this call after returning sufficient outstanding buffers
293 // to allow a clean resize.
294 ALOGE("Buffer queue shrink failed -- too many buffers currently in use?");
295 }
296 }
297
298 return true;
299}
300
301
302unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
303 // Acquire the graphics buffer allocator
304 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
305
306 unsigned added = 0;
307
308 while (added < numToAdd) {
309 buffer_handle_t memHandle = nullptr;
310 status_t result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage,
311 &memHandle, &mStride, 0, "EvsCamera");
312 if (result != NO_ERROR) {
313 ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight);
314 break;
315 }
316 if (!memHandle) {
317 ALOGE("We didn't get a buffer handle back from the allocator");
318 break;
319 }
320
321 // Find a place to store the new buffer
322 bool stored = false;
323 for (auto&& rec : mBuffers) {
324 if (rec.handle == nullptr) {
325 // Use this existing entry
326 rec.handle = memHandle;
327 rec.inUse = false;
328 stored = true;
329 break;
330 }
331 }
332 if (!stored) {
333 // Add a BufferRecord wrapping this handle to our set of available buffers
334 mBuffers.emplace_back(memHandle);
335 }
336
337 mFramesAllowed++;
338 added++;
339 }
340
341 return added;
342}
343
344
345unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
346 // Acquire the graphics buffer allocator
347 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
348
349 unsigned removed = 0;
350
351 for (auto&& rec : mBuffers) {
352 // Is this record not in use, but holding a buffer that we can free?
353 if ((rec.inUse == false) && (rec.handle != nullptr)) {
354 // Release buffer and update the record so we can recognize it as "empty"
355 alloc.free(rec.handle);
356 rec.handle = nullptr;
357
358 mFramesAllowed--;
359 removed++;
360
361 if (removed == numToRemove) {
362 break;
363 }
364 }
365 }
366
367 return removed;
368}
369
370
371// This is the asynchronous frame generation thread that runs in parallel with the
372// main serving thread. There is one for each active camera instance.
373void EvsCamera::generateFrames() {
374 ALOGD("Frame generation loop started");
375
376 unsigned idx;
377
378 while (true) {
379 bool timeForFrame = false;
380 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
381
382 // Lock scope for updating shared state
383 {
384 std::lock_guard<std::mutex> lock(mAccessLock);
385
386 if (mStreamState != RUNNING) {
387 // Break out of our main thread loop
388 break;
389 }
390
391 // Are we allowed to issue another buffer?
392 if (mFramesInUse >= mFramesAllowed) {
393 // Can't do anything right now -- skip this frame
394 ALOGW("Skipped a frame because too many are in flight\n");
395 } else {
396 // Identify an available buffer to fill
397 for (idx = 0; idx < mBuffers.size(); idx++) {
398 if (!mBuffers[idx].inUse) {
399 if (mBuffers[idx].handle != nullptr) {
400 // Found an available record, so stop looking
401 break;
402 }
403 }
404 }
405 if (idx >= mBuffers.size()) {
406 // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
407 ALOGE("Failed to find an available buffer slot\n");
408 } else {
409 // We're going to make the frame busy
410 mBuffers[idx].inUse = true;
411 mFramesInUse++;
412 timeForFrame = true;
413 }
414 }
415 }
416
417 if (timeForFrame) {
418 // Assemble the buffer description we'll transmit below
419 BufferDesc_1_1 newBuffer = {};
420 AHardwareBuffer_Desc* pDesc =
421 reinterpret_cast<AHardwareBuffer_Desc *>(&newBuffer.buffer.description);
422 pDesc->width = mWidth;
423 pDesc->height = mHeight;
424 pDesc->layers = 1;
425 pDesc->format = mFormat;
426 pDesc->usage = mUsage;
427 pDesc->stride = mStride;
428 newBuffer.buffer.nativeHandle = mBuffers[idx].handle;
429 newBuffer.pixelSize = sizeof(uint32_t);
430 newBuffer.bufferId = idx;
431
432 // Write test data into the image buffer
433 fillTestFrame(newBuffer);
434
435 // Issue the (asynchronous) callback to the client -- can't be holding the lock
436 EvsEvent event;
437 event.buffer(newBuffer);
438 auto result = mStream->notifyEvent(event);
439 if (result.isOk()) {
440 ALOGD("Delivered %p as id %d",
441 newBuffer.buffer.nativeHandle.getNativeHandle(), newBuffer.bufferId);
442 } else {
443 // This can happen if the client dies and is likely unrecoverable.
444 // To avoid consuming resources generating failing calls, we stop sending
445 // frames. Note, however, that the stream remains in the "STREAMING" state
446 // until cleaned up on the main thread.
447 ALOGE("Frame delivery call failed in the transport layer.");
448
449 // Since we didn't actually deliver it, mark the frame as available
450 std::lock_guard<std::mutex> lock(mAccessLock);
451 mBuffers[idx].inUse = false;
452 mFramesInUse--;
453
454 break;
455 }
456 }
457
458 // We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement
459 static const int kTargetFrameRate = 12;
460 static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate;
461 const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
462 const nsecs_t workTimeUs = (now - startTime) / 1000;
463 const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
464 if (sleepDurationUs > 0) {
465 usleep(sleepDurationUs);
466 }
467 }
468
469 // If we've been asked to stop, send an event to signal the actual end of stream
470 EvsEvent event;
471 event.info(EvsEventType::STREAM_STOPPED);
472 auto result = mStream->notifyEvent(event);
473 if (!result.isOk()) {
474 ALOGE("Error delivering end of stream marker");
475 }
476
477 return;
478}
479
480
481void EvsCamera::fillTestFrame(const BufferDesc_1_1& buff) {
482 // Lock our output buffer for writing
483 uint32_t *pixels = nullptr;
484 const AHardwareBuffer_Desc* pDesc =
485 reinterpret_cast<const AHardwareBuffer_Desc *>(&buff.buffer.description);
486 GraphicBufferMapper &mapper = GraphicBufferMapper::get();
487 mapper.lock(buff.buffer.nativeHandle,
488 GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
489 android::Rect(pDesc->width, pDesc->height),
490 (void **) &pixels);
491
492 // If we failed to lock the pixel buffer, we're about to crash, but log it first
493 if (!pixels) {
494 ALOGE("Camera failed to gain access to image buffer for writing");
495 }
496
497 // Fill in the test pixels
498 for (unsigned row = 0; row < pDesc->height; row++) {
499 for (unsigned col = 0; col < pDesc->width; col++) {
500 // Index into the row to check the pixel at this column.
501 // We expect 0xFF in the LSB channel, a vertical gradient in the
502 // second channel, a horitzontal gradient in the third channel, and
503 // 0xFF in the MSB.
504 // The exception is the very first 32 bits which is used for the
505 // time varying frame signature to avoid getting fooled by a static image.
506 uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
507 ((row & 0xFF) << 8) | // vertical gradient
508 ((col & 0xFF) << 16); // horizontal gradient
509 if ((row | col) == 0) {
510 static uint32_t sFrameTicker = 0;
511 expectedPixel = (sFrameTicker) & 0xFF;
512 sFrameTicker++;
513 }
514 pixels[col] = expectedPixel;
515 }
516 // Point to the next row
517 // NOTE: stride retrieved from gralloc is in units of pixels
518 pixels = pixels + pDesc->stride;
519 }
520
521 // Release our output buffer
522 mapper.unlock(buff.buffer.nativeHandle);
523}
524
525
526void EvsCamera::fillTestFrame(const BufferDesc_1_0& buff) {
527 BufferDesc_1_1 newBufDesc = {};
528 AHardwareBuffer_Desc desc = {
529 buff.width, // width
530 buff.height, // height
531 1, // layers, always 1 for EVS
532 buff.format, // One of AHardwareBuffer_Format
533 buff.usage, // Combination of AHardwareBuffer_UsageFlags
534 buff.stride, // Row stride in pixels
535 0, // Reserved
536 0 // Reserved
537 };
538 memcpy(&desc, &newBufDesc.buffer.description, sizeof(desc));
539 newBufDesc.buffer.nativeHandle = buff.memHandle;
540 newBufDesc.pixelSize = buff.pixelSize;
541 newBufDesc.bufferId = buff.bufferId;
542
543 return fillTestFrame(newBufDesc);
544}
545
546
547void EvsCamera::returnBuffer(const uint32_t bufferId, const buffer_handle_t memHandle) {
548 std::lock_guard <std::mutex> lock(mAccessLock);
549
550 if (memHandle == nullptr) {
551 ALOGE("ignoring doneWithFrame called with null handle");
552 } else if (bufferId >= mBuffers.size()) {
553 ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)",
554 bufferId, mBuffers.size()-1);
555 } else if (!mBuffers[bufferId].inUse) {
556 ALOGE("ignoring doneWithFrame called on frame %d which is already free",
557 bufferId);
558 } else {
559 // Mark the frame as available
560 mBuffers[bufferId].inUse = false;
561 mFramesInUse--;
562
563 // If this frame's index is high in the array, try to move it down
564 // to improve locality after mFramesAllowed has been reduced.
565 if (bufferId >= mFramesAllowed) {
566 // Find an empty slot lower in the array (which should always exist in this case)
567 for (auto&& rec : mBuffers) {
568 if (rec.handle == nullptr) {
569 rec.handle = mBuffers[bufferId].handle;
570 mBuffers[bufferId].handle = nullptr;
571 break;
572 }
573 }
574 }
575 }
576}
577
578
579} // namespace implementation
580} // namespace V1_0
581} // namespace evs
582} // namespace automotive
583} // namespace hardware
584} // namespace android