blob: 148b7968b68745f77fb4bbfd18406f5702080915 [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;
314 status_t result = alloc.allocate(mWidth, mHeight,
315 mFormat, 1,
Craig Donner7babeec2017-02-06 10:00:22 -0800316 mUsage, mUsage,
Scott Randolphdb5a5982017-01-23 12:35:05 -0800317 &memHandle, &mStride, 0, "EvsCamera");
318 if (result != NO_ERROR) {
319 ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight);
320 break;
321 }
322 if (!memHandle) {
323 ALOGE("We didn't get a buffer handle back from the allocator");
324 break;
325 }
326
327 // Find a place to store the new buffer
328 bool stored = false;
329 for (auto&& rec : mBuffers) {
330 if (rec.handle == nullptr) {
331 // Use this existing entry
332 rec.handle = memHandle;
333 rec.inUse = false;
334 stored = true;
335 break;
336 }
337 }
338 if (!stored) {
339 // Add a BufferRecord wrapping this handle to our set of available buffers
340 mBuffers.emplace_back(memHandle);
341 }
342
343 mFramesAllowed++;
344 added++;
345 }
346
347 return added;
348}
349
350
351unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
352 // Acquire the graphics buffer allocator
353 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
354
355 unsigned removed = 0;
356
357 for (auto&& rec : mBuffers) {
358 // Is this record not in use, but holding a buffer that we can free?
359 if ((rec.inUse == false) && (rec.handle != nullptr)) {
360 // Release buffer and update the record so we can recognize it as "empty"
361 alloc.free(rec.handle);
362 rec.handle = nullptr;
363
364 mFramesAllowed--;
365 removed++;
366
367 if (removed == numToRemove) {
368 break;
369 }
370 }
371 }
372
373 return removed;
374}
375
376
377// This is the asynchronous frame generation thread that runs in parallel with the
378// main serving thread. There is one for each active camera instance.
379void EvsCamera::generateFrames() {
380 ALOGD("Frame generation loop started");
381
382 unsigned idx;
Scott Randolph5c99d852016-11-15 17:01:23 -0800383
384 while (true) {
385 bool timeForFrame = false;
Scott Randolphde9880e2017-03-30 14:04:12 -0700386 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
387
388 // Lock scope for updating shared state
Scott Randolph5c99d852016-11-15 17:01:23 -0800389 {
390 std::lock_guard<std::mutex> lock(mAccessLock);
391
Scott Randolph5c99d852016-11-15 17:01:23 -0800392 if (mStreamState != RUNNING) {
393 // Break out of our main thread loop
394 break;
395 }
396
Scott Randolphdb5a5982017-01-23 12:35:05 -0800397 // Are we allowed to issue another buffer?
398 if (mFramesInUse >= mFramesAllowed) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800399 // Can't do anything right now -- skip this frame
Scott Randolphdb5a5982017-01-23 12:35:05 -0800400 ALOGW("Skipped a frame because too many are in flight\n");
401 } else {
402 // Identify an available buffer to fill
403 for (idx = 0; idx < mBuffers.size(); idx++) {
404 if (!mBuffers[idx].inUse) {
405 if (mBuffers[idx].handle != nullptr) {
406 // Found an available record, so stop looking
407 break;
408 }
409 }
410 }
411 if (idx >= mBuffers.size()) {
412 // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
413 ALOGE("Failed to find an available buffer slot\n");
414 } else {
415 // We're going to make the frame busy
416 mBuffers[idx].inUse = true;
417 mFramesInUse++;
418 timeForFrame = true;
419 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800420 }
421 }
422
423 if (timeForFrame) {
Scott Randolphdb5a5982017-01-23 12:35:05 -0800424 // Assemble the buffer description we'll transmit below
425 BufferDesc buff = {};
426 buff.width = mWidth;
427 buff.height = mHeight;
428 buff.stride = mStride;
429 buff.format = mFormat;
430 buff.usage = mUsage;
431 buff.bufferId = idx;
432 buff.memHandle = mBuffers[idx].handle;
Scott Randolph5c99d852016-11-15 17:01:23 -0800433
Scott Randolphdb5a5982017-01-23 12:35:05 -0800434 // Write test data into the image buffer
435 fillTestFrame(buff);
436
437 // Issue the (asynchronous) callback to the client -- can't be holding the lock
438 auto result = mStream->deliverFrame(buff);
439 if (result.isOk()) {
440 ALOGD("Delivered %p as id %d", buff.memHandle.getNativeHandle(), buff.bufferId);
441 } else {
442 // This can happen if the client dies and is likely unrecoverable.
443 // To avoid consuming resources generating failing calls, we stop sending
444 // frames. Note, however, that the stream remains in the "STREAMING" state
445 // until cleaned up on the main thread.
446 ALOGE("Frame delivery call failed in the transport layer.");
447
448 // Since we didn't actually deliver it, mark the frame as available
449 std::lock_guard<std::mutex> lock(mAccessLock);
450 mBuffers[idx].inUse = false;
451 mFramesInUse--;
452
453 break;
Scott Randolph5c99d852016-11-15 17:01:23 -0800454 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800455 }
456
Scott Randolphde9880e2017-03-30 14:04:12 -0700457 // We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement
458 static const int kTargetFrameRate = 12;
459 static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate;
460 const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
461 const nsecs_t workTimeUs = (now - startTime) / 1000;
462 const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
463 if (sleepDurationUs > 0) {
464 usleep(sleepDurationUs);
465 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800466 }
467
468 // 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 -0800469 BufferDesc nullBuff = {};
470 auto result = mStream->deliverFrame(nullBuff);
471 if (!result.isOk()) {
472 ALOGE("Error delivering end of stream marker");
473 }
Scott Randolph5c99d852016-11-15 17:01:23 -0800474
475 return;
476}
477
Scott Randolphdb5a5982017-01-23 12:35:05 -0800478
Scott Randolph9a773c72017-02-15 16:25:48 -0800479void EvsCamera::fillTestFrame(const BufferDesc& buff) {
Scott Randolphdb5a5982017-01-23 12:35:05 -0800480 // Lock our output buffer for writing
481 uint32_t *pixels = nullptr;
482 GraphicBufferMapper &mapper = GraphicBufferMapper::get();
483 mapper.lock(buff.memHandle,
484 GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
485 android::Rect(buff.width, buff.height),
486 (void **) &pixels);
487
488 // If we failed to lock the pixel buffer, we're about to crash, but log it first
489 if (!pixels) {
490 ALOGE("Camera failed to gain access to image buffer for writing");
491 }
492
493 // Fill in the test pixels
494 for (unsigned row = 0; row < buff.height; row++) {
495 for (unsigned col = 0; col < buff.width; col++) {
496 // Index into the row to check the pixel at this column.
497 // We expect 0xFF in the LSB channel, a vertical gradient in the
498 // second channel, a horitzontal gradient in the third channel, and
499 // 0xFF in the MSB.
500 // The exception is the very first 32 bits which is used for the
501 // time varying frame signature to avoid getting fooled by a static image.
502 uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
503 ((row & 0xFF) << 8) | // vertical gradient
504 ((col & 0xFF) << 16); // horizontal gradient
505 if ((row | col) == 0) {
506 static uint32_t sFrameTicker = 0;
507 expectedPixel = (sFrameTicker) & 0xFF;
508 sFrameTicker++;
509 }
510 pixels[col] = expectedPixel;
511 }
512 // Point to the next row
Scott Randolph9a773c72017-02-15 16:25:48 -0800513 // NOTE: stride retrieved from gralloc is in units of pixels
514 pixels = pixels + buff.stride;
Scott Randolphdb5a5982017-01-23 12:35:05 -0800515 }
516
517 // Release our output buffer
518 mapper.unlock(buff.memHandle);
519}
520
521
Scott Randolph5c99d852016-11-15 17:01:23 -0800522} // namespace implementation
523} // namespace V1_0
524} // namespace evs
Scott Randolph83422792017-03-01 20:32:59 -0800525} // namespace automotive
Scott Randolph5c99d852016-11-15 17:01:23 -0800526} // namespace hardware
527} // namespace android