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