blob: e0782ec77995d4552336ad2641bbf2240123d012 [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"
Scott Randolphde9880e2017-03-30 14:04:12 -070020#include "EvsEnumerator.h"
Scott Randolph5c99d852016-11-15 17:01:23 -080021
22#include <ui/GraphicBufferAllocator.h>
23#include <ui/GraphicBufferMapper.h>
24
25
26namespace android {
27namespace hardware {
Scott Randolph83422792017-03-01 20:32:59 -080028namespace automotive {
Scott Randolph5c99d852016-11-15 17:01:23 -080029namespace evs {
30namespace V1_0 {
31namespace implementation {
32
33
Scott Randolphde9880e2017-03-30 14:04:12 -070034// Special camera names for which we'll initialize alternate test data
Scott Randolph5c99d852016-11-15 17:01:23 -080035const char EvsCamera::kCameraName_Backup[] = "backup";
Scott Randolphde9880e2017-03-30 14:04:12 -070036
Scott Randolph5c99d852016-11-15 17:01:23 -080037
Scott Randolphdb5a5982017-01-23 12:35:05 -080038// 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
Scott Randolph5c99d852016-11-15 17:01:23 -080042
Scott Randolphdb5a5982017-01-23 12:35:05 -080043EvsCamera::EvsCamera(const char *id) :
Scott Randolph83422792017-03-01 20:32:59 -080044 mFramesAllowed(0),
45 mFramesInUse(0),
46 mStreamState(STOPPED) {
Scott Randolphdb5a5982017-01-23 12:35:05 -080047
Scott Randolph5c99d852016-11-15 17:01:23 -080048 ALOGD("EvsCamera instantiated");
49
50 mDescription.cameraId = id;
Scott Randolph5c99d852016-11-15 17:01:23 -080051
52 // Set up dummy data for testing
53 if (mDescription.cameraId == kCameraName_Backup) {
Scott Randolphde9880e2017-03-30 14:04:12 -070054 mWidth = 640; // full NTSC/VGA
55 mHeight = 480; // full NTSC/VGA
56 mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
Scott Randolphdb5a5982017-01-23 12:35:05 -080057 } else {
Scott Randolphde9880e2017-03-30 14:04:12 -070058 mWidth = 320; // 1/2 NTSC/VGA
59 mHeight = 240; // 1/2 NTSC/VGA
Scott Randolph5c99d852016-11-15 17:01:23 -080060 }
Scott Randolphdb5a5982017-01-23 12:35:05 -080061
Scott Randolphdb5a5982017-01-23 12:35:05 -080062 mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
Scott Randolph9a773c72017-02-15 16:25:48 -080063 mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
64 GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
Scott Randolph5c99d852016-11-15 17:01:23 -080065}
66
Scott Randolphdb5a5982017-01-23 12:35:05 -080067
Scott Randolph5c99d852016-11-15 17:01:23 -080068EvsCamera::~EvsCamera() {
69 ALOGD("EvsCamera being destroyed");
Scott Randolphde9880e2017-03-30 14:04:12 -070070 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");
Scott Randolph5c99d852016-11-15 17:01:23 -080080
81 // Make sure our output stream is cleaned up
82 // (It really should be already)
83 stopVideoStream();
84
Scott Randolphde9880e2017-03-30 14:04:12 -070085 // Claim the lock while we work on internal state
86 std::lock_guard <std::mutex> lock(mAccessLock);
87
Scott Randolphdb5a5982017-01-23 12:35:05 -080088 // Drop all the graphics buffers we've been using
Scott Randolphde9880e2017-03-30 14:04:12 -070089 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;
Scott Randolphdb5a5982017-01-23 12:35:05 -080097 }
Scott Randolphde9880e2017-03-30 14:04:12 -070098 mBuffers.clear();
Scott Randolph5c99d852016-11-15 17:01:23 -080099 }
100
Scott Randolphde9880e2017-03-30 14:04:12 -0700101 // Put this object into an unrecoverable error state since somebody else
102 // is going to own the underlying camera now
103 mStreamState = DEAD;
Scott Randolph5c99d852016-11-15 17:01:23 -0800104}
105
106
Scott Randolph83422792017-03-01 20:32:59 -0800107// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
Scott Randolphde9880e2017-03-30 14:04:12 -0700108Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
109 ALOGD("getCameraInfo");
Scott Randolph5c99d852016-11-15 17:01:23 -0800110
Scott Randolphde9880e2017-03-30 14:04:12 -0700111 // Send back our self description
112 _hidl_cb(mDescription);
Scott Randolph5c99d852016-11-15 17:01:23 -0800113 return Void();
114}
115
116
117Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
118 ALOGD("setMaxFramesInFlight");
119 std::lock_guard<std::mutex> lock(mAccessLock);
120
Scott Randolphde9880e2017-03-30 14:04:12 -0700121 // 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
Scott Randolphdb5a5982017-01-23 12:35:05 -0800127 // 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;
Scott Randolph5c99d852016-11-15 17:01:23 -0800131 }
132
Scott Randolphdb5a5982017-01-23 12:35:05 -0800133 // Update our internal state
134 if (setAvailableFrames_Locked(bufferCount)) {
135 return EvsResult::OK;
136 } else {
137 return EvsResult::BUFFER_NOT_AVAILABLE;
138 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800139}
140
Scott Randolphdb5a5982017-01-23 12:35:05 -0800141
Scott Randolph5c99d852016-11-15 17:01:23 -0800142Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) {
143 ALOGD("startVideoStream");
144 std::lock_guard<std::mutex> lock(mAccessLock);
145
Scott Randolphde9880e2017-03-30 14:04:12 -0700146 // 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 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800151 if (mStreamState != STOPPED) {
152 ALOGE("ignoring startVideoStream call when a stream is already running.");
153 return EvsResult::STREAM_ALREADY_RUNNING;
154 }
155
Scott Randolphdb5a5982017-01-23 12:35:05 -0800156 // 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
Scott Randolph5c99d852016-11-15 17:01:23 -0800164 // Record the user's callback for use when we have a frame ready
165 mStream = stream;
166
Scott Randolph5c99d852016-11-15 17:01:23 -0800167 // Start the frame generation thread
168 mStreamState = RUNNING;
Scott Randolphdb5a5982017-01-23 12:35:05 -0800169 mCaptureThread = std::thread([this](){ generateFrames(); });
Scott Randolph5c99d852016-11-15 17:01:23 -0800170
171 return EvsResult::OK;
172}
173
Scott Randolphdb5a5982017-01-23 12:35:05 -0800174
175Return<void> EvsCamera::doneWithFrame(const BufferDesc& buffer) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800176 ALOGD("doneWithFrame");
Scott Randolphdb5a5982017-01-23 12:35:05 -0800177 { // lock context
Scott Randolph5c99d852016-11-15 17:01:23 -0800178 std::lock_guard <std::mutex> lock(mAccessLock);
179
Scott Randolphdb5a5982017-01-23 12:35:05 -0800180 if (buffer.memHandle == nullptr) {
181 ALOGE("ignoring doneWithFrame called with null handle");
182 } else if (buffer.bufferId >= mBuffers.size()) {
Steven Moreland25197e92017-03-10 21:05:09 -0800183 ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)",
Scott Randolphdb5a5982017-01-23 12:35:05 -0800184 buffer.bufferId, mBuffers.size()-1);
185 } else if (!mBuffers[buffer.bufferId].inUse) {
186 ALOGE("ignoring doneWithFrame called on frame %d which is already free",
187 buffer.bufferId);
188 } else {
189 // Mark the frame as available
190 mBuffers[buffer.bufferId].inUse = false;
191 mFramesInUse--;
Scott Randolph5c99d852016-11-15 17:01:23 -0800192
Scott Randolphdb5a5982017-01-23 12:35:05 -0800193 // If this frame's index is high in the array, try to move it down
194 // to improve locality after mFramesAllowed has been reduced.
195 if (buffer.bufferId >= mFramesAllowed) {
196 // Find an empty slot lower in the array (which should always exist in this case)
197 for (auto&& rec : mBuffers) {
198 if (rec.handle == nullptr) {
199 rec.handle = mBuffers[buffer.bufferId].handle;
200 mBuffers[buffer.bufferId].handle = nullptr;
201 break;
202 }
203 }
204 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800205 }
206 }
207
208 return Void();
209}
210
Scott Randolphdb5a5982017-01-23 12:35:05 -0800211
212Return<void> EvsCamera::stopVideoStream() {
213 ALOGD("stopVideoStream");
214 std::unique_lock <std::mutex> lock(mAccessLock);
215
216 if (mStreamState == RUNNING) {
217 // Tell the GenerateFrames loop we want it to stop
218 mStreamState = STOPPING;
219
220 // Block outside the mutex until the "stop" flag has been acknowledged
221 // We won't send any more frames, but the client might still get some already in flight
222 ALOGD("Waiting for stream thread to end...");
223 lock.unlock();
224 mCaptureThread.join();
225 lock.lock();
226
227 mStreamState = STOPPED;
Scott Randolphde9880e2017-03-30 14:04:12 -0700228 mStream = nullptr;
Scott Randolphdb5a5982017-01-23 12:35:05 -0800229 ALOGD("Stream marked STOPPED.");
230 }
231
232 return Void();
233}
234
235
Scott Randolph5c99d852016-11-15 17:01:23 -0800236Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
237 ALOGD("getExtendedInfo");
238 std::lock_guard<std::mutex> lock(mAccessLock);
239
240 // For any single digit value, return the index itself as a test value
241 if (opaqueIdentifier <= 9) {
242 return opaqueIdentifier;
243 }
244
245 // Return zero by default as required by the spec
246 return 0;
247}
248
Scott Randolphdb5a5982017-01-23 12:35:05 -0800249
Scott Randolph5c99d852016-11-15 17:01:23 -0800250Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) {
251 ALOGD("setExtendedInfo");
252 std::lock_guard<std::mutex> lock(mAccessLock);
253
Scott Randolphde9880e2017-03-30 14:04:12 -0700254 // If we've been displaced by another owner of the camera, then we can't do anything else
255 if (mStreamState == DEAD) {
256 ALOGE("ignoring setExtendedInfo call when camera has been lost.");
257 return EvsResult::OWNERSHIP_LOST;
258 }
259
Scott Randolph5c99d852016-11-15 17:01:23 -0800260 // We don't store any device specific information in this implementation
261 return EvsResult::INVALID_ARG;
262}
263
264
Scott Randolphdb5a5982017-01-23 12:35:05 -0800265bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
266 if (bufferCount < 1) {
267 ALOGE("Ignoring request to set buffer count to zero");
268 return false;
269 }
270 if (bufferCount > MAX_BUFFERS_IN_FLIGHT) {
271 ALOGE("Rejecting buffer request in excess of internal limit");
272 return false;
273 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800274
Scott Randolphdb5a5982017-01-23 12:35:05 -0800275 // Is an increase required?
276 if (mFramesAllowed < bufferCount) {
277 // An increase is required
278 unsigned needed = bufferCount - mFramesAllowed;
279 ALOGI("Allocating %d buffers for camera frames", needed);
280
281 unsigned added = increaseAvailableFrames_Locked(needed);
282 if (added != needed) {
283 // If we didn't add all the frames we needed, then roll back to the previous state
284 ALOGE("Rolling back to previous frame queue size");
285 decreaseAvailableFrames_Locked(added);
286 return false;
287 }
288 } else if (mFramesAllowed > bufferCount) {
289 // A decrease is required
290 unsigned framesToRelease = mFramesAllowed - bufferCount;
291 ALOGI("Returning %d camera frame buffers", framesToRelease);
292
293 unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
294 if (released != framesToRelease) {
295 // This shouldn't happen with a properly behaving client because the client
296 // should only make this call after returning sufficient outstanding buffers
297 // to allow a clean resize.
298 ALOGE("Buffer queue shrink failed -- too many buffers currently in use?");
299 }
300 }
301
302 return true;
303}
304
305
306unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
307 // Acquire the graphics buffer allocator
308 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
309
310 unsigned added = 0;
311
312 while (added < numToAdd) {
313 buffer_handle_t memHandle = nullptr;
Chris Forbesf90642b2017-04-19 16:09:51 -0700314 status_t result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage,
Scott Randolphdb5a5982017-01-23 12:35:05 -0800315 &memHandle, &mStride, 0, "EvsCamera");
316 if (result != NO_ERROR) {
317 ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight);
318 break;
319 }
320 if (!memHandle) {
321 ALOGE("We didn't get a buffer handle back from the allocator");
322 break;
323 }
324
325 // Find a place to store the new buffer
326 bool stored = false;
327 for (auto&& rec : mBuffers) {
328 if (rec.handle == nullptr) {
329 // Use this existing entry
330 rec.handle = memHandle;
331 rec.inUse = false;
332 stored = true;
333 break;
334 }
335 }
336 if (!stored) {
337 // Add a BufferRecord wrapping this handle to our set of available buffers
338 mBuffers.emplace_back(memHandle);
339 }
340
341 mFramesAllowed++;
342 added++;
343 }
344
345 return added;
346}
347
348
349unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
350 // Acquire the graphics buffer allocator
351 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
352
353 unsigned removed = 0;
354
355 for (auto&& rec : mBuffers) {
356 // Is this record not in use, but holding a buffer that we can free?
357 if ((rec.inUse == false) && (rec.handle != nullptr)) {
358 // Release buffer and update the record so we can recognize it as "empty"
359 alloc.free(rec.handle);
360 rec.handle = nullptr;
361
362 mFramesAllowed--;
363 removed++;
364
365 if (removed == numToRemove) {
366 break;
367 }
368 }
369 }
370
371 return removed;
372}
373
374
375// This is the asynchronous frame generation thread that runs in parallel with the
376// main serving thread. There is one for each active camera instance.
377void EvsCamera::generateFrames() {
378 ALOGD("Frame generation loop started");
379
380 unsigned idx;
Scott Randolph5c99d852016-11-15 17:01:23 -0800381
382 while (true) {
383 bool timeForFrame = false;
Scott Randolphde9880e2017-03-30 14:04:12 -0700384 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
385
386 // Lock scope for updating shared state
Scott Randolph5c99d852016-11-15 17:01:23 -0800387 {
388 std::lock_guard<std::mutex> lock(mAccessLock);
389
Scott Randolph5c99d852016-11-15 17:01:23 -0800390 if (mStreamState != RUNNING) {
391 // Break out of our main thread loop
392 break;
393 }
394
Scott Randolphdb5a5982017-01-23 12:35:05 -0800395 // Are we allowed to issue another buffer?
396 if (mFramesInUse >= mFramesAllowed) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800397 // Can't do anything right now -- skip this frame
Scott Randolphdb5a5982017-01-23 12:35:05 -0800398 ALOGW("Skipped a frame because too many are in flight\n");
399 } else {
400 // Identify an available buffer to fill
401 for (idx = 0; idx < mBuffers.size(); idx++) {
402 if (!mBuffers[idx].inUse) {
403 if (mBuffers[idx].handle != nullptr) {
404 // Found an available record, so stop looking
405 break;
406 }
407 }
408 }
409 if (idx >= mBuffers.size()) {
410 // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
411 ALOGE("Failed to find an available buffer slot\n");
412 } else {
413 // We're going to make the frame busy
414 mBuffers[idx].inUse = true;
415 mFramesInUse++;
416 timeForFrame = true;
417 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800418 }
419 }
420
421 if (timeForFrame) {
Scott Randolphdb5a5982017-01-23 12:35:05 -0800422 // Assemble the buffer description we'll transmit below
423 BufferDesc buff = {};
424 buff.width = mWidth;
425 buff.height = mHeight;
426 buff.stride = mStride;
427 buff.format = mFormat;
428 buff.usage = mUsage;
429 buff.bufferId = idx;
430 buff.memHandle = mBuffers[idx].handle;
Scott Randolph5c99d852016-11-15 17:01:23 -0800431
Scott Randolphdb5a5982017-01-23 12:35:05 -0800432 // Write test data into the image buffer
433 fillTestFrame(buff);
434
435 // Issue the (asynchronous) callback to the client -- can't be holding the lock
436 auto result = mStream->deliverFrame(buff);
437 if (result.isOk()) {
438 ALOGD("Delivered %p as id %d", buff.memHandle.getNativeHandle(), buff.bufferId);
439 } else {
440 // This can happen if the client dies and is likely unrecoverable.
441 // To avoid consuming resources generating failing calls, we stop sending
442 // frames. Note, however, that the stream remains in the "STREAMING" state
443 // until cleaned up on the main thread.
444 ALOGE("Frame delivery call failed in the transport layer.");
445
446 // Since we didn't actually deliver it, mark the frame as available
447 std::lock_guard<std::mutex> lock(mAccessLock);
448 mBuffers[idx].inUse = false;
449 mFramesInUse--;
450
451 break;
Scott Randolph5c99d852016-11-15 17:01:23 -0800452 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800453 }
454
Scott Randolphde9880e2017-03-30 14:04:12 -0700455 // We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement
456 static const int kTargetFrameRate = 12;
457 static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate;
458 const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
459 const nsecs_t workTimeUs = (now - startTime) / 1000;
460 const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
461 if (sleepDurationUs > 0) {
462 usleep(sleepDurationUs);
463 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800464 }
465
466 // 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 -0800467 BufferDesc nullBuff = {};
468 auto result = mStream->deliverFrame(nullBuff);
469 if (!result.isOk()) {
470 ALOGE("Error delivering end of stream marker");
471 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800472
473 return;
474}
475
Scott Randolphdb5a5982017-01-23 12:35:05 -0800476
Scott Randolph9a773c72017-02-15 16:25:48 -0800477void EvsCamera::fillTestFrame(const BufferDesc& buff) {
Scott Randolphdb5a5982017-01-23 12:35:05 -0800478 // Lock our output buffer for writing
479 uint32_t *pixels = nullptr;
480 GraphicBufferMapper &mapper = GraphicBufferMapper::get();
481 mapper.lock(buff.memHandle,
482 GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
483 android::Rect(buff.width, buff.height),
484 (void **) &pixels);
485
486 // If we failed to lock the pixel buffer, we're about to crash, but log it first
487 if (!pixels) {
488 ALOGE("Camera failed to gain access to image buffer for writing");
489 }
490
491 // Fill in the test pixels
492 for (unsigned row = 0; row < buff.height; row++) {
493 for (unsigned col = 0; col < buff.width; col++) {
494 // Index into the row to check the pixel at this column.
495 // We expect 0xFF in the LSB channel, a vertical gradient in the
496 // second channel, a horitzontal gradient in the third channel, and
497 // 0xFF in the MSB.
498 // The exception is the very first 32 bits which is used for the
499 // time varying frame signature to avoid getting fooled by a static image.
500 uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
501 ((row & 0xFF) << 8) | // vertical gradient
502 ((col & 0xFF) << 16); // horizontal gradient
503 if ((row | col) == 0) {
504 static uint32_t sFrameTicker = 0;
505 expectedPixel = (sFrameTicker) & 0xFF;
506 sFrameTicker++;
507 }
508 pixels[col] = expectedPixel;
509 }
510 // Point to the next row
Scott Randolph9a773c72017-02-15 16:25:48 -0800511 // NOTE: stride retrieved from gralloc is in units of pixels
512 pixels = pixels + buff.stride;
Scott Randolphdb5a5982017-01-23 12:35:05 -0800513 }
514
515 // Release our output buffer
516 mapper.unlock(buff.memHandle);
517}
518
519
Scott Randolph5c99d852016-11-15 17:01:23 -0800520} // namespace implementation
521} // namespace V1_0
522} // namespace evs
Scott Randolph83422792017-03-01 20:32:59 -0800523} // namespace automotive
Scott Randolph5c99d852016-11-15 17:01:23 -0800524} // namespace hardware
525} // namespace android