blob: c4436ee12b2b7ea93af64790e103e54012f13d09 [file] [log] [blame]
Scott Randolph5c99d852016-11-15 17:01:23 -08001/*
2 * Copyright (C) 2016 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
Scott Randolph83422792017-03-01 20:32:59 -080017#define LOG_TAG "android.hardware.automotive.evs@1.0-service"
Scott Randolph5c99d852016-11-15 17:01:23 -080018
19#include "EvsCamera.h"
20
21#include <ui/GraphicBufferAllocator.h>
22#include <ui/GraphicBufferMapper.h>
23
24
25namespace android {
26namespace hardware {
Scott Randolph83422792017-03-01 20:32:59 -080027namespace automotive {
Scott Randolph5c99d852016-11-15 17:01:23 -080028namespace evs {
29namespace V1_0 {
30namespace implementation {
31
32
33// These are the special camera names for which we'll initialize custom test data
34const char EvsCamera::kCameraName_Backup[] = "backup";
35const char EvsCamera::kCameraName_RightTurn[] = "Right Turn";
36
Scott Randolphdb5a5982017-01-23 12:35:05 -080037// Arbitrary limit on number of graphics buffers allowed to be allocated
38// Safeguards against unreasonable resource consumption and provides a testable limit
39const unsigned MAX_BUFFERS_IN_FLIGHT = 100;
40
Scott Randolph5c99d852016-11-15 17:01:23 -080041
42// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
Scott Randolphdb5a5982017-01-23 12:35:05 -080043// As it stands, if the client dies suddenly, the buffer may be stranded.
Scott Randolph5c99d852016-11-15 17:01:23 -080044
Scott Randolphdb5a5982017-01-23 12:35:05 -080045EvsCamera::EvsCamera(const char *id) :
Scott Randolph83422792017-03-01 20:32:59 -080046 mFramesAllowed(0),
47 mFramesInUse(0),
48 mStreamState(STOPPED) {
Scott Randolphdb5a5982017-01-23 12:35:05 -080049
Scott Randolph5c99d852016-11-15 17:01:23 -080050 ALOGD("EvsCamera instantiated");
51
52 mDescription.cameraId = id;
Scott Randolph5c99d852016-11-15 17:01:23 -080053
54 // Set up dummy data for testing
55 if (mDescription.cameraId == kCameraName_Backup) {
Steven Morelanddb972332017-01-05 14:11:41 -080056 mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_REVERSE);
Scott Randolph5c99d852016-11-15 17:01:23 -080057 mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
58 mDescription.defaultHorResolution = 320; // 1/2 NTSC/VGA
59 mDescription.defaultVerResolution = 240; // 1/2 NTSC/VGA
Scott Randolphdb5a5982017-01-23 12:35:05 -080060 } else if (mDescription.cameraId == kCameraName_RightTurn) {
Scott Randolph5c99d852016-11-15 17:01:23 -080061 // Nothing but the name and the usage hint
Steven Morelanddb972332017-01-05 14:11:41 -080062 mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_RIGHT_TURN);
Scott Randolphdb5a5982017-01-23 12:35:05 -080063 } else {
Scott Randolph5c99d852016-11-15 17:01:23 -080064 // Leave empty for a minimalist camera description without even a hint
65 }
Scott Randolphdb5a5982017-01-23 12:35:05 -080066
67
68 // Set our buffer properties
69 mWidth = (mDescription.defaultHorResolution) ? mDescription.defaultHorResolution : 640;
70 mHeight = (mDescription.defaultVerResolution) ? mDescription.defaultVerResolution : 480;
71
72 mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
Scott Randolph9a773c72017-02-15 16:25:48 -080073 mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
74 GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
Scott Randolph5c99d852016-11-15 17:01:23 -080075}
76
Scott Randolphdb5a5982017-01-23 12:35:05 -080077
Scott Randolph5c99d852016-11-15 17:01:23 -080078EvsCamera::~EvsCamera() {
79 ALOGD("EvsCamera being destroyed");
80 std::lock_guard<std::mutex> lock(mAccessLock);
81
82 // Make sure our output stream is cleaned up
83 // (It really should be already)
84 stopVideoStream();
85
Scott Randolphdb5a5982017-01-23 12:35:05 -080086 // Drop all the graphics buffers we've been using
87 GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
88 for (auto&& rec : mBuffers) {
89 if (rec.inUse) {
90 ALOGE("Error - releasing buffer despite remote ownership");
91 }
92 alloc.free(rec.handle);
93 rec.handle = nullptr;
Scott Randolph5c99d852016-11-15 17:01:23 -080094 }
95
96 ALOGD("EvsCamera destroyed");
97}
98
99
Scott Randolph83422792017-03-01 20:32:59 -0800100// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
Scott Randolph5c99d852016-11-15 17:01:23 -0800101Return<void> EvsCamera::getId(getId_cb id_cb) {
102 ALOGD("getId");
103
104 id_cb(mDescription.cameraId);
105
106 return Void();
107}
108
109
110Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
111 ALOGD("setMaxFramesInFlight");
112 std::lock_guard<std::mutex> lock(mAccessLock);
113
Scott Randolphdb5a5982017-01-23 12:35:05 -0800114 // We cannot function without at least one video buffer to send data
115 if (bufferCount < 1) {
116 ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested");
117 return EvsResult::INVALID_ARG;
Scott Randolph5c99d852016-11-15 17:01:23 -0800118 }
119
Scott Randolphdb5a5982017-01-23 12:35:05 -0800120 // Update our internal state
121 if (setAvailableFrames_Locked(bufferCount)) {
122 return EvsResult::OK;
123 } else {
124 return EvsResult::BUFFER_NOT_AVAILABLE;
125 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800126}
127
Scott Randolphdb5a5982017-01-23 12:35:05 -0800128
Scott Randolph5c99d852016-11-15 17:01:23 -0800129Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) {
130 ALOGD("startVideoStream");
131 std::lock_guard<std::mutex> lock(mAccessLock);
132
Scott Randolph5c99d852016-11-15 17:01:23 -0800133 if (mStreamState != STOPPED) {
134 ALOGE("ignoring startVideoStream call when a stream is already running.");
135 return EvsResult::STREAM_ALREADY_RUNNING;
136 }
137
Scott Randolphdb5a5982017-01-23 12:35:05 -0800138 // If the client never indicated otherwise, configure ourselves for a single streaming buffer
139 if (mFramesAllowed < 1) {
140 if (!setAvailableFrames_Locked(1)) {
141 ALOGE("Failed to start stream because we couldn't get a graphics buffer");
142 return EvsResult::BUFFER_NOT_AVAILABLE;
143 }
144 }
145
Scott Randolph5c99d852016-11-15 17:01:23 -0800146 // Record the user's callback for use when we have a frame ready
147 mStream = stream;
148
Scott Randolph5c99d852016-11-15 17:01:23 -0800149 // Start the frame generation thread
150 mStreamState = RUNNING;
Scott Randolphdb5a5982017-01-23 12:35:05 -0800151 mCaptureThread = std::thread([this](){ generateFrames(); });
Scott Randolph5c99d852016-11-15 17:01:23 -0800152
153 return EvsResult::OK;
154}
155
Scott Randolphdb5a5982017-01-23 12:35:05 -0800156
157Return<void> EvsCamera::doneWithFrame(const BufferDesc& buffer) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800158 ALOGD("doneWithFrame");
Scott Randolphdb5a5982017-01-23 12:35:05 -0800159 { // lock context
Scott Randolph5c99d852016-11-15 17:01:23 -0800160 std::lock_guard <std::mutex> lock(mAccessLock);
161
Scott Randolphdb5a5982017-01-23 12:35:05 -0800162 if (buffer.memHandle == nullptr) {
163 ALOGE("ignoring doneWithFrame called with null handle");
164 } else if (buffer.bufferId >= mBuffers.size()) {
Steven Moreland25197e92017-03-10 21:05:09 -0800165 ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)",
Scott Randolphdb5a5982017-01-23 12:35:05 -0800166 buffer.bufferId, mBuffers.size()-1);
167 } else if (!mBuffers[buffer.bufferId].inUse) {
168 ALOGE("ignoring doneWithFrame called on frame %d which is already free",
169 buffer.bufferId);
170 } else {
171 // Mark the frame as available
172 mBuffers[buffer.bufferId].inUse = false;
173 mFramesInUse--;
Scott Randolph5c99d852016-11-15 17:01:23 -0800174
Scott Randolphdb5a5982017-01-23 12:35:05 -0800175 // If this frame's index is high in the array, try to move it down
176 // to improve locality after mFramesAllowed has been reduced.
177 if (buffer.bufferId >= mFramesAllowed) {
178 // Find an empty slot lower in the array (which should always exist in this case)
179 for (auto&& rec : mBuffers) {
180 if (rec.handle == nullptr) {
181 rec.handle = mBuffers[buffer.bufferId].handle;
182 mBuffers[buffer.bufferId].handle = nullptr;
183 break;
184 }
185 }
186 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800187 }
188 }
189
190 return Void();
191}
192
Scott Randolphdb5a5982017-01-23 12:35:05 -0800193
194Return<void> EvsCamera::stopVideoStream() {
195 ALOGD("stopVideoStream");
196 std::unique_lock <std::mutex> lock(mAccessLock);
197
198 if (mStreamState == RUNNING) {
199 // Tell the GenerateFrames loop we want it to stop
200 mStreamState = STOPPING;
201
202 // Block outside the mutex until the "stop" flag has been acknowledged
203 // We won't send any more frames, but the client might still get some already in flight
204 ALOGD("Waiting for stream thread to end...");
205 lock.unlock();
206 mCaptureThread.join();
207 lock.lock();
208
209 mStreamState = STOPPED;
210 ALOGD("Stream marked STOPPED.");
211 }
212
213 return Void();
214}
215
216
Scott Randolph5c99d852016-11-15 17:01:23 -0800217Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
218 ALOGD("getExtendedInfo");
219 std::lock_guard<std::mutex> lock(mAccessLock);
220
221 // For any single digit value, return the index itself as a test value
222 if (opaqueIdentifier <= 9) {
223 return opaqueIdentifier;
224 }
225
226 // Return zero by default as required by the spec
227 return 0;
228}
229
Scott Randolphdb5a5982017-01-23 12:35:05 -0800230
Scott Randolph5c99d852016-11-15 17:01:23 -0800231Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) {
232 ALOGD("setExtendedInfo");
233 std::lock_guard<std::mutex> lock(mAccessLock);
234
235 // We don't store any device specific information in this implementation
236 return EvsResult::INVALID_ARG;
237}
238
239
Scott Randolphdb5a5982017-01-23 12:35:05 -0800240bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
241 if (bufferCount < 1) {
242 ALOGE("Ignoring request to set buffer count to zero");
243 return false;
244 }
245 if (bufferCount > MAX_BUFFERS_IN_FLIGHT) {
246 ALOGE("Rejecting buffer request in excess of internal limit");
247 return false;
248 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800249
Scott Randolphdb5a5982017-01-23 12:35:05 -0800250 // Is an increase required?
251 if (mFramesAllowed < bufferCount) {
252 // An increase is required
253 unsigned needed = bufferCount - mFramesAllowed;
254 ALOGI("Allocating %d buffers for camera frames", needed);
255
256 unsigned added = increaseAvailableFrames_Locked(needed);
257 if (added != needed) {
258 // If we didn't add all the frames we needed, then roll back to the previous state
259 ALOGE("Rolling back to previous frame queue size");
260 decreaseAvailableFrames_Locked(added);
261 return false;
262 }
263 } else if (mFramesAllowed > bufferCount) {
264 // A decrease is required
265 unsigned framesToRelease = mFramesAllowed - bufferCount;
266 ALOGI("Returning %d camera frame buffers", framesToRelease);
267
268 unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
269 if (released != framesToRelease) {
270 // This shouldn't happen with a properly behaving client because the client
271 // should only make this call after returning sufficient outstanding buffers
272 // to allow a clean resize.
273 ALOGE("Buffer queue shrink failed -- too many buffers currently in use?");
274 }
275 }
276
277 return true;
278}
279
280
281unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
282 // Acquire the graphics buffer allocator
283 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
284
285 unsigned added = 0;
286
287 while (added < numToAdd) {
288 buffer_handle_t memHandle = nullptr;
289 status_t result = alloc.allocate(mWidth, mHeight,
290 mFormat, 1,
Craig Donner7babeec2017-02-06 10:00:22 -0800291 mUsage, mUsage,
Scott Randolphdb5a5982017-01-23 12:35:05 -0800292 &memHandle, &mStride, 0, "EvsCamera");
293 if (result != NO_ERROR) {
294 ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight);
295 break;
296 }
297 if (!memHandle) {
298 ALOGE("We didn't get a buffer handle back from the allocator");
299 break;
300 }
301
302 // Find a place to store the new buffer
303 bool stored = false;
304 for (auto&& rec : mBuffers) {
305 if (rec.handle == nullptr) {
306 // Use this existing entry
307 rec.handle = memHandle;
308 rec.inUse = false;
309 stored = true;
310 break;
311 }
312 }
313 if (!stored) {
314 // Add a BufferRecord wrapping this handle to our set of available buffers
315 mBuffers.emplace_back(memHandle);
316 }
317
318 mFramesAllowed++;
319 added++;
320 }
321
322 return added;
323}
324
325
326unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
327 // Acquire the graphics buffer allocator
328 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
329
330 unsigned removed = 0;
331
332 for (auto&& rec : mBuffers) {
333 // Is this record not in use, but holding a buffer that we can free?
334 if ((rec.inUse == false) && (rec.handle != nullptr)) {
335 // Release buffer and update the record so we can recognize it as "empty"
336 alloc.free(rec.handle);
337 rec.handle = nullptr;
338
339 mFramesAllowed--;
340 removed++;
341
342 if (removed == numToRemove) {
343 break;
344 }
345 }
346 }
347
348 return removed;
349}
350
351
352// This is the asynchronous frame generation thread that runs in parallel with the
353// main serving thread. There is one for each active camera instance.
354void EvsCamera::generateFrames() {
355 ALOGD("Frame generation loop started");
356
357 unsigned idx;
Scott Randolph5c99d852016-11-15 17:01:23 -0800358
359 while (true) {
360 bool timeForFrame = false;
361 // Lock scope
362 {
363 std::lock_guard<std::mutex> lock(mAccessLock);
364
Scott Randolph5c99d852016-11-15 17:01:23 -0800365 if (mStreamState != RUNNING) {
366 // Break out of our main thread loop
367 break;
368 }
369
Scott Randolphdb5a5982017-01-23 12:35:05 -0800370 // Are we allowed to issue another buffer?
371 if (mFramesInUse >= mFramesAllowed) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800372 // Can't do anything right now -- skip this frame
Scott Randolphdb5a5982017-01-23 12:35:05 -0800373 ALOGW("Skipped a frame because too many are in flight\n");
374 } else {
375 // Identify an available buffer to fill
376 for (idx = 0; idx < mBuffers.size(); idx++) {
377 if (!mBuffers[idx].inUse) {
378 if (mBuffers[idx].handle != nullptr) {
379 // Found an available record, so stop looking
380 break;
381 }
382 }
383 }
384 if (idx >= mBuffers.size()) {
385 // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
386 ALOGE("Failed to find an available buffer slot\n");
387 } else {
388 // We're going to make the frame busy
389 mBuffers[idx].inUse = true;
390 mFramesInUse++;
391 timeForFrame = true;
392 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800393 }
394 }
395
396 if (timeForFrame) {
Scott Randolphdb5a5982017-01-23 12:35:05 -0800397 // Assemble the buffer description we'll transmit below
398 BufferDesc buff = {};
399 buff.width = mWidth;
400 buff.height = mHeight;
401 buff.stride = mStride;
402 buff.format = mFormat;
403 buff.usage = mUsage;
404 buff.bufferId = idx;
405 buff.memHandle = mBuffers[idx].handle;
Scott Randolph5c99d852016-11-15 17:01:23 -0800406
Scott Randolphdb5a5982017-01-23 12:35:05 -0800407 // Write test data into the image buffer
408 fillTestFrame(buff);
409
410 // Issue the (asynchronous) callback to the client -- can't be holding the lock
411 auto result = mStream->deliverFrame(buff);
412 if (result.isOk()) {
413 ALOGD("Delivered %p as id %d", buff.memHandle.getNativeHandle(), buff.bufferId);
414 } else {
415 // This can happen if the client dies and is likely unrecoverable.
416 // To avoid consuming resources generating failing calls, we stop sending
417 // frames. Note, however, that the stream remains in the "STREAMING" state
418 // until cleaned up on the main thread.
419 ALOGE("Frame delivery call failed in the transport layer.");
420
421 // Since we didn't actually deliver it, mark the frame as available
422 std::lock_guard<std::mutex> lock(mAccessLock);
423 mBuffers[idx].inUse = false;
424 mFramesInUse--;
425
426 break;
Scott Randolph5c99d852016-11-15 17:01:23 -0800427 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800428 }
429
430 // We arbitrarily choose to generate frames at 10 fps (1/10 * uSecPerSec)
431 usleep(100000);
432 }
433
434 // If we've been asked to stop, send one last NULL frame to signal the actual end of stream
Scott Randolphdb5a5982017-01-23 12:35:05 -0800435 BufferDesc nullBuff = {};
436 auto result = mStream->deliverFrame(nullBuff);
437 if (!result.isOk()) {
438 ALOGE("Error delivering end of stream marker");
439 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800440
441 return;
442}
443
Scott Randolphdb5a5982017-01-23 12:35:05 -0800444
Scott Randolph9a773c72017-02-15 16:25:48 -0800445void EvsCamera::fillTestFrame(const BufferDesc& buff) {
Scott Randolphdb5a5982017-01-23 12:35:05 -0800446 // Lock our output buffer for writing
447 uint32_t *pixels = nullptr;
448 GraphicBufferMapper &mapper = GraphicBufferMapper::get();
449 mapper.lock(buff.memHandle,
450 GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
451 android::Rect(buff.width, buff.height),
452 (void **) &pixels);
453
454 // If we failed to lock the pixel buffer, we're about to crash, but log it first
455 if (!pixels) {
456 ALOGE("Camera failed to gain access to image buffer for writing");
457 }
458
459 // Fill in the test pixels
460 for (unsigned row = 0; row < buff.height; row++) {
461 for (unsigned col = 0; col < buff.width; col++) {
462 // Index into the row to check the pixel at this column.
463 // We expect 0xFF in the LSB channel, a vertical gradient in the
464 // second channel, a horitzontal gradient in the third channel, and
465 // 0xFF in the MSB.
466 // The exception is the very first 32 bits which is used for the
467 // time varying frame signature to avoid getting fooled by a static image.
468 uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
469 ((row & 0xFF) << 8) | // vertical gradient
470 ((col & 0xFF) << 16); // horizontal gradient
471 if ((row | col) == 0) {
472 static uint32_t sFrameTicker = 0;
473 expectedPixel = (sFrameTicker) & 0xFF;
474 sFrameTicker++;
475 }
476 pixels[col] = expectedPixel;
477 }
478 // Point to the next row
Scott Randolph9a773c72017-02-15 16:25:48 -0800479 // NOTE: stride retrieved from gralloc is in units of pixels
480 pixels = pixels + buff.stride;
Scott Randolphdb5a5982017-01-23 12:35:05 -0800481 }
482
483 // Release our output buffer
484 mapper.unlock(buff.memHandle);
485}
486
487
Scott Randolph5c99d852016-11-15 17:01:23 -0800488} // namespace implementation
489} // namespace V1_0
490} // namespace evs
Scott Randolph83422792017-03-01 20:32:59 -0800491} // namespace automotive
Scott Randolph5c99d852016-11-15 17:01:23 -0800492} // namespace hardware
493} // namespace android