blob: c62f7b6dc9b26f3f566b79b8b831d15ff0a580f9 [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;
72 mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE;
Scott Randolph5c99d852016-11-15 17:01:23 -080073}
74
Scott Randolphdb5a5982017-01-23 12:35:05 -080075
Scott Randolph5c99d852016-11-15 17:01:23 -080076EvsCamera::~EvsCamera() {
77 ALOGD("EvsCamera being destroyed");
78 std::lock_guard<std::mutex> lock(mAccessLock);
79
80 // Make sure our output stream is cleaned up
81 // (It really should be already)
82 stopVideoStream();
83
Scott Randolphdb5a5982017-01-23 12:35:05 -080084 // Drop all the graphics buffers we've been using
85 GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
86 for (auto&& rec : mBuffers) {
87 if (rec.inUse) {
88 ALOGE("Error - releasing buffer despite remote ownership");
89 }
90 alloc.free(rec.handle);
91 rec.handle = nullptr;
Scott Randolph5c99d852016-11-15 17:01:23 -080092 }
93
94 ALOGD("EvsCamera destroyed");
95}
96
97
98// Methods from ::android::hardware::evs::V1_0::IEvsCamera follow.
99Return<void> EvsCamera::getId(getId_cb id_cb) {
100 ALOGD("getId");
101
102 id_cb(mDescription.cameraId);
103
104 return Void();
105}
106
107
108Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
109 ALOGD("setMaxFramesInFlight");
110 std::lock_guard<std::mutex> lock(mAccessLock);
111
Scott Randolphdb5a5982017-01-23 12:35:05 -0800112 // We cannot function without at least one video buffer to send data
113 if (bufferCount < 1) {
114 ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested");
115 return EvsResult::INVALID_ARG;
Scott Randolph5c99d852016-11-15 17:01:23 -0800116 }
117
Scott Randolphdb5a5982017-01-23 12:35:05 -0800118 // Update our internal state
119 if (setAvailableFrames_Locked(bufferCount)) {
120 return EvsResult::OK;
121 } else {
122 return EvsResult::BUFFER_NOT_AVAILABLE;
123 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800124}
125
Scott Randolphdb5a5982017-01-23 12:35:05 -0800126
Scott Randolph5c99d852016-11-15 17:01:23 -0800127Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) {
128 ALOGD("startVideoStream");
129 std::lock_guard<std::mutex> lock(mAccessLock);
130
Scott Randolph5c99d852016-11-15 17:01:23 -0800131 if (mStreamState != STOPPED) {
132 ALOGE("ignoring startVideoStream call when a stream is already running.");
133 return EvsResult::STREAM_ALREADY_RUNNING;
134 }
135
Scott Randolphdb5a5982017-01-23 12:35:05 -0800136 // If the client never indicated otherwise, configure ourselves for a single streaming buffer
137 if (mFramesAllowed < 1) {
138 if (!setAvailableFrames_Locked(1)) {
139 ALOGE("Failed to start stream because we couldn't get a graphics buffer");
140 return EvsResult::BUFFER_NOT_AVAILABLE;
141 }
142 }
143
Scott Randolph5c99d852016-11-15 17:01:23 -0800144 // Record the user's callback for use when we have a frame ready
145 mStream = stream;
146
Scott Randolph5c99d852016-11-15 17:01:23 -0800147 // Start the frame generation thread
148 mStreamState = RUNNING;
Scott Randolphdb5a5982017-01-23 12:35:05 -0800149 mCaptureThread = std::thread([this](){ generateFrames(); });
Scott Randolph5c99d852016-11-15 17:01:23 -0800150
151 return EvsResult::OK;
152}
153
Scott Randolphdb5a5982017-01-23 12:35:05 -0800154
155Return<void> EvsCamera::doneWithFrame(const BufferDesc& buffer) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800156 ALOGD("doneWithFrame");
Scott Randolphdb5a5982017-01-23 12:35:05 -0800157 { // lock context
Scott Randolph5c99d852016-11-15 17:01:23 -0800158 std::lock_guard <std::mutex> lock(mAccessLock);
159
Scott Randolphdb5a5982017-01-23 12:35:05 -0800160 if (buffer.memHandle == nullptr) {
161 ALOGE("ignoring doneWithFrame called with null handle");
162 } else if (buffer.bufferId >= mBuffers.size()) {
163 ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %lu)",
164 buffer.bufferId, mBuffers.size()-1);
165 } else if (!mBuffers[buffer.bufferId].inUse) {
166 ALOGE("ignoring doneWithFrame called on frame %d which is already free",
167 buffer.bufferId);
168 } else {
169 // Mark the frame as available
170 mBuffers[buffer.bufferId].inUse = false;
171 mFramesInUse--;
Scott Randolph5c99d852016-11-15 17:01:23 -0800172
Scott Randolphdb5a5982017-01-23 12:35:05 -0800173 // If this frame's index is high in the array, try to move it down
174 // to improve locality after mFramesAllowed has been reduced.
175 if (buffer.bufferId >= mFramesAllowed) {
176 // Find an empty slot lower in the array (which should always exist in this case)
177 for (auto&& rec : mBuffers) {
178 if (rec.handle == nullptr) {
179 rec.handle = mBuffers[buffer.bufferId].handle;
180 mBuffers[buffer.bufferId].handle = nullptr;
181 break;
182 }
183 }
184 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800185 }
186 }
187
188 return Void();
189}
190
Scott Randolphdb5a5982017-01-23 12:35:05 -0800191
192Return<void> EvsCamera::stopVideoStream() {
193 ALOGD("stopVideoStream");
194 std::unique_lock <std::mutex> lock(mAccessLock);
195
196 if (mStreamState == RUNNING) {
197 // Tell the GenerateFrames loop we want it to stop
198 mStreamState = STOPPING;
199
200 // Block outside the mutex until the "stop" flag has been acknowledged
201 // We won't send any more frames, but the client might still get some already in flight
202 ALOGD("Waiting for stream thread to end...");
203 lock.unlock();
204 mCaptureThread.join();
205 lock.lock();
206
207 mStreamState = STOPPED;
208 ALOGD("Stream marked STOPPED.");
209 }
210
211 return Void();
212}
213
214
Scott Randolph5c99d852016-11-15 17:01:23 -0800215Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
216 ALOGD("getExtendedInfo");
217 std::lock_guard<std::mutex> lock(mAccessLock);
218
219 // For any single digit value, return the index itself as a test value
220 if (opaqueIdentifier <= 9) {
221 return opaqueIdentifier;
222 }
223
224 // Return zero by default as required by the spec
225 return 0;
226}
227
Scott Randolphdb5a5982017-01-23 12:35:05 -0800228
Scott Randolph5c99d852016-11-15 17:01:23 -0800229Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) {
230 ALOGD("setExtendedInfo");
231 std::lock_guard<std::mutex> lock(mAccessLock);
232
233 // We don't store any device specific information in this implementation
234 return EvsResult::INVALID_ARG;
235}
236
237
Scott Randolphdb5a5982017-01-23 12:35:05 -0800238bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
239 if (bufferCount < 1) {
240 ALOGE("Ignoring request to set buffer count to zero");
241 return false;
242 }
243 if (bufferCount > MAX_BUFFERS_IN_FLIGHT) {
244 ALOGE("Rejecting buffer request in excess of internal limit");
245 return false;
246 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800247
Scott Randolphdb5a5982017-01-23 12:35:05 -0800248 // Is an increase required?
249 if (mFramesAllowed < bufferCount) {
250 // An increase is required
251 unsigned needed = bufferCount - mFramesAllowed;
252 ALOGI("Allocating %d buffers for camera frames", needed);
253
254 unsigned added = increaseAvailableFrames_Locked(needed);
255 if (added != needed) {
256 // If we didn't add all the frames we needed, then roll back to the previous state
257 ALOGE("Rolling back to previous frame queue size");
258 decreaseAvailableFrames_Locked(added);
259 return false;
260 }
261 } else if (mFramesAllowed > bufferCount) {
262 // A decrease is required
263 unsigned framesToRelease = mFramesAllowed - bufferCount;
264 ALOGI("Returning %d camera frame buffers", framesToRelease);
265
266 unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
267 if (released != framesToRelease) {
268 // This shouldn't happen with a properly behaving client because the client
269 // should only make this call after returning sufficient outstanding buffers
270 // to allow a clean resize.
271 ALOGE("Buffer queue shrink failed -- too many buffers currently in use?");
272 }
273 }
274
275 return true;
276}
277
278
279unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
280 // Acquire the graphics buffer allocator
281 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
282
283 unsigned added = 0;
284
285 while (added < numToAdd) {
286 buffer_handle_t memHandle = nullptr;
287 status_t result = alloc.allocate(mWidth, mHeight,
288 mFormat, 1,
289 mUsage,
290 &memHandle, &mStride, 0, "EvsCamera");
291 if (result != NO_ERROR) {
292 ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight);
293 break;
294 }
295 if (!memHandle) {
296 ALOGE("We didn't get a buffer handle back from the allocator");
297 break;
298 }
299
300 // Find a place to store the new buffer
301 bool stored = false;
302 for (auto&& rec : mBuffers) {
303 if (rec.handle == nullptr) {
304 // Use this existing entry
305 rec.handle = memHandle;
306 rec.inUse = false;
307 stored = true;
308 break;
309 }
310 }
311 if (!stored) {
312 // Add a BufferRecord wrapping this handle to our set of available buffers
313 mBuffers.emplace_back(memHandle);
314 }
315
316 mFramesAllowed++;
317 added++;
318 }
319
320 return added;
321}
322
323
324unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
325 // Acquire the graphics buffer allocator
326 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
327
328 unsigned removed = 0;
329
330 for (auto&& rec : mBuffers) {
331 // Is this record not in use, but holding a buffer that we can free?
332 if ((rec.inUse == false) && (rec.handle != nullptr)) {
333 // Release buffer and update the record so we can recognize it as "empty"
334 alloc.free(rec.handle);
335 rec.handle = nullptr;
336
337 mFramesAllowed--;
338 removed++;
339
340 if (removed == numToRemove) {
341 break;
342 }
343 }
344 }
345
346 return removed;
347}
348
349
350// This is the asynchronous frame generation thread that runs in parallel with the
351// main serving thread. There is one for each active camera instance.
352void EvsCamera::generateFrames() {
353 ALOGD("Frame generation loop started");
354
355 unsigned idx;
Scott Randolph5c99d852016-11-15 17:01:23 -0800356
357 while (true) {
358 bool timeForFrame = false;
359 // Lock scope
360 {
361 std::lock_guard<std::mutex> lock(mAccessLock);
362
Scott Randolph5c99d852016-11-15 17:01:23 -0800363 if (mStreamState != RUNNING) {
364 // Break out of our main thread loop
365 break;
366 }
367
Scott Randolphdb5a5982017-01-23 12:35:05 -0800368 // Are we allowed to issue another buffer?
369 if (mFramesInUse >= mFramesAllowed) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800370 // Can't do anything right now -- skip this frame
Scott Randolphdb5a5982017-01-23 12:35:05 -0800371 ALOGW("Skipped a frame because too many are in flight\n");
372 } else {
373 // Identify an available buffer to fill
374 for (idx = 0; idx < mBuffers.size(); idx++) {
375 if (!mBuffers[idx].inUse) {
376 if (mBuffers[idx].handle != nullptr) {
377 // Found an available record, so stop looking
378 break;
379 }
380 }
381 }
382 if (idx >= mBuffers.size()) {
383 // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
384 ALOGE("Failed to find an available buffer slot\n");
385 } else {
386 // We're going to make the frame busy
387 mBuffers[idx].inUse = true;
388 mFramesInUse++;
389 timeForFrame = true;
390 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800391 }
392 }
393
394 if (timeForFrame) {
Scott Randolphdb5a5982017-01-23 12:35:05 -0800395 // Assemble the buffer description we'll transmit below
396 BufferDesc buff = {};
397 buff.width = mWidth;
398 buff.height = mHeight;
399 buff.stride = mStride;
400 buff.format = mFormat;
401 buff.usage = mUsage;
402 buff.bufferId = idx;
403 buff.memHandle = mBuffers[idx].handle;
Scott Randolph5c99d852016-11-15 17:01:23 -0800404
Scott Randolphdb5a5982017-01-23 12:35:05 -0800405 // Write test data into the image buffer
406 fillTestFrame(buff);
407
408 // Issue the (asynchronous) callback to the client -- can't be holding the lock
409 auto result = mStream->deliverFrame(buff);
410 if (result.isOk()) {
411 ALOGD("Delivered %p as id %d", buff.memHandle.getNativeHandle(), buff.bufferId);
412 } else {
413 // This can happen if the client dies and is likely unrecoverable.
414 // To avoid consuming resources generating failing calls, we stop sending
415 // frames. Note, however, that the stream remains in the "STREAMING" state
416 // until cleaned up on the main thread.
417 ALOGE("Frame delivery call failed in the transport layer.");
418
419 // Since we didn't actually deliver it, mark the frame as available
420 std::lock_guard<std::mutex> lock(mAccessLock);
421 mBuffers[idx].inUse = false;
422 mFramesInUse--;
423
424 break;
Scott Randolph5c99d852016-11-15 17:01:23 -0800425 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800426 }
427
428 // We arbitrarily choose to generate frames at 10 fps (1/10 * uSecPerSec)
429 usleep(100000);
430 }
431
432 // 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 -0800433 BufferDesc nullBuff = {};
434 auto result = mStream->deliverFrame(nullBuff);
435 if (!result.isOk()) {
436 ALOGE("Error delivering end of stream marker");
437 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800438
439 return;
440}
441
Scott Randolphdb5a5982017-01-23 12:35:05 -0800442
443void EvsCamera::fillTestFrame(BufferDesc buff) {
444 // Lock our output buffer for writing
445 uint32_t *pixels = nullptr;
446 GraphicBufferMapper &mapper = GraphicBufferMapper::get();
447 mapper.lock(buff.memHandle,
448 GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
449 android::Rect(buff.width, buff.height),
450 (void **) &pixels);
451
452 // If we failed to lock the pixel buffer, we're about to crash, but log it first
453 if (!pixels) {
454 ALOGE("Camera failed to gain access to image buffer for writing");
455 }
456
457 // Fill in the test pixels
458 for (unsigned row = 0; row < buff.height; row++) {
459 for (unsigned col = 0; col < buff.width; col++) {
460 // Index into the row to check the pixel at this column.
461 // We expect 0xFF in the LSB channel, a vertical gradient in the
462 // second channel, a horitzontal gradient in the third channel, and
463 // 0xFF in the MSB.
464 // The exception is the very first 32 bits which is used for the
465 // time varying frame signature to avoid getting fooled by a static image.
466 uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
467 ((row & 0xFF) << 8) | // vertical gradient
468 ((col & 0xFF) << 16); // horizontal gradient
469 if ((row | col) == 0) {
470 static uint32_t sFrameTicker = 0;
471 expectedPixel = (sFrameTicker) & 0xFF;
472 sFrameTicker++;
473 }
474 pixels[col] = expectedPixel;
475 }
476 // Point to the next row
477 pixels = pixels + (buff.stride / sizeof(*pixels));
478 }
479
480 // Release our output buffer
481 mapper.unlock(buff.memHandle);
482}
483
484
Scott Randolph5c99d852016-11-15 17:01:23 -0800485} // namespace implementation
486} // namespace V1_0
487} // namespace evs
488} // namespace hardware
489} // namespace android