Scott Randolph | 5c99d85 | 2016-11-15 17:01:23 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 25 | namespace android { |
| 26 | namespace hardware { |
| 27 | namespace evs { |
| 28 | namespace V1_0 { |
| 29 | namespace implementation { |
| 30 | |
| 31 | |
| 32 | // These are the special camera names for which we'll initialize custom test data |
| 33 | const char EvsCamera::kCameraName_Backup[] = "backup"; |
| 34 | const char EvsCamera::kCameraName_RightTurn[] = "Right Turn"; |
| 35 | |
| 36 | |
| 37 | // TODO(b/31632518): Need to get notification when our client dies so we can close the camera. |
| 38 | // As it stands, if the client dies suddently, the buffer may be stranded. |
| 39 | // As possible work around would be to give the client a HIDL object to exclusively hold |
| 40 | // and use it's destructor to perform some work in the server side. |
| 41 | |
| 42 | EvsCamera::EvsCamera(const char *id) { |
| 43 | ALOGD("EvsCamera instantiated"); |
| 44 | |
| 45 | mDescription.cameraId = id; |
| 46 | mFrameBusy = false; |
| 47 | mStreamState = STOPPED; |
| 48 | |
| 49 | // Set up dummy data for testing |
| 50 | if (mDescription.cameraId == kCameraName_Backup) { |
Steven Moreland | db97233 | 2017-01-05 14:11:41 -0800 | [diff] [blame] | 51 | mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_REVERSE); |
Scott Randolph | 5c99d85 | 2016-11-15 17:01:23 -0800 | [diff] [blame] | 52 | mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value |
| 53 | mDescription.defaultHorResolution = 320; // 1/2 NTSC/VGA |
| 54 | mDescription.defaultVerResolution = 240; // 1/2 NTSC/VGA |
| 55 | } |
| 56 | else if (mDescription.cameraId == kCameraName_RightTurn) { |
| 57 | // Nothing but the name and the usage hint |
Steven Moreland | db97233 | 2017-01-05 14:11:41 -0800 | [diff] [blame] | 58 | mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_RIGHT_TURN); |
Scott Randolph | 5c99d85 | 2016-11-15 17:01:23 -0800 | [diff] [blame] | 59 | } |
| 60 | else { |
| 61 | // Leave empty for a minimalist camera description without even a hint |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | EvsCamera::~EvsCamera() { |
| 66 | ALOGD("EvsCamera being destroyed"); |
| 67 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 68 | |
| 69 | // Make sure our output stream is cleaned up |
| 70 | // (It really should be already) |
| 71 | stopVideoStream(); |
| 72 | |
| 73 | // Drop the graphics buffer we've been using |
| 74 | if (mBuffer) { |
| 75 | // Drop the graphics buffer we've been using |
| 76 | GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); |
| 77 | alloc.free(mBuffer); |
| 78 | } |
| 79 | |
| 80 | ALOGD("EvsCamera destroyed"); |
| 81 | } |
| 82 | |
| 83 | |
| 84 | // Methods from ::android::hardware::evs::V1_0::IEvsCamera follow. |
| 85 | Return<void> EvsCamera::getId(getId_cb id_cb) { |
| 86 | ALOGD("getId"); |
| 87 | |
| 88 | id_cb(mDescription.cameraId); |
| 89 | |
| 90 | return Void(); |
| 91 | } |
| 92 | |
| 93 | |
| 94 | Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) { |
| 95 | ALOGD("setMaxFramesInFlight"); |
| 96 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 97 | |
| 98 | // TODO: Update our stored value |
| 99 | |
| 100 | // TODO: Adjust our buffer count right now if we can. Otherwise, it'll adjust in doneWithFrame |
| 101 | |
| 102 | // For now we support only one! |
| 103 | if (bufferCount != 1) { |
| 104 | return EvsResult::BUFFER_NOT_AVAILABLE; |
| 105 | } |
| 106 | |
| 107 | return EvsResult::OK; |
| 108 | } |
| 109 | |
| 110 | Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) { |
| 111 | ALOGD("startVideoStream"); |
| 112 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 113 | |
| 114 | // We only support a single stream at a time |
| 115 | if (mStreamState != STOPPED) { |
| 116 | ALOGE("ignoring startVideoStream call when a stream is already running."); |
| 117 | return EvsResult::STREAM_ALREADY_RUNNING; |
| 118 | } |
| 119 | |
| 120 | // Record the user's callback for use when we have a frame ready |
| 121 | mStream = stream; |
| 122 | |
| 123 | // Allocate a graphics buffer into which we'll put our test images |
| 124 | if (!mBuffer) { |
| 125 | mWidth = (mDescription.defaultHorResolution) ? mDescription.defaultHorResolution : 640; |
| 126 | mHeight = (mDescription.defaultVerResolution) ? mDescription.defaultVerResolution : 480; |
| 127 | // TODO: What about stride? Assume no padding for now... |
| 128 | mStride = 4* mWidth; // Special cased to assume 4 byte pixels with no padding for now |
| 129 | |
| 130 | ALOGD("Allocating buffer for camera frame"); |
| 131 | GraphicBufferAllocator &alloc(GraphicBufferAllocator::get()); |
| 132 | status_t result = alloc.allocate(mWidth, mHeight, |
| 133 | HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_HW_TEXTURE, |
| 134 | &mBuffer, &mStride, 0, "EvsCamera"); |
| 135 | if (result != NO_ERROR) { |
| 136 | ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight); |
| 137 | return EvsResult::BUFFER_NOT_AVAILABLE; |
| 138 | } |
| 139 | if (!mBuffer) { |
| 140 | ALOGE("We didn't get a buffer handle back from the allocator"); |
| 141 | return EvsResult::BUFFER_NOT_AVAILABLE; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | // Start the frame generation thread |
| 146 | mStreamState = RUNNING; |
| 147 | mCaptureThread = std::thread([this](){GenerateFrames();}); |
| 148 | |
| 149 | return EvsResult::OK; |
| 150 | } |
| 151 | |
Steven Moreland | db97233 | 2017-01-05 14:11:41 -0800 | [diff] [blame] | 152 | Return<EvsResult> EvsCamera::doneWithFrame(uint32_t /* frameId */, const hidl_handle& bufferHandle) { |
Scott Randolph | 5c99d85 | 2016-11-15 17:01:23 -0800 | [diff] [blame] | 153 | ALOGD("doneWithFrame"); |
| 154 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 155 | |
| 156 | if (!bufferHandle) |
| 157 | { |
| 158 | ALOGE("ignoring doneWithFrame called with invalid handle"); |
| 159 | return EvsResult::INVALID_ARG; |
| 160 | } |
| 161 | |
| 162 | // TODO: Track which frames we've delivered and validate this is one of them |
| 163 | |
| 164 | // Mark the frame buffer as available for a new frame |
| 165 | mFrameBusy = false; |
| 166 | |
| 167 | // TODO: If we currently have too many buffers, drop this one |
| 168 | |
| 169 | return EvsResult::OK; |
| 170 | } |
| 171 | |
| 172 | Return<void> EvsCamera::stopVideoStream() { |
| 173 | ALOGD("stopVideoStream"); |
| 174 | |
| 175 | bool waitForJoin = false; |
| 176 | // Lock scope |
| 177 | { |
| 178 | std::lock_guard <std::mutex> lock(mAccessLock); |
| 179 | |
| 180 | if (mStreamState == RUNNING) { |
| 181 | // Tell the GenerateFrames loop we want it to stop |
| 182 | mStreamState = STOPPING; |
| 183 | |
| 184 | // Note that we asked the thread to stop and should wait for it do so |
| 185 | waitForJoin = true; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | if (waitForJoin) { |
| 190 | // Block outside the mutex until the "stop" flag has been acknowledged |
| 191 | // NOTE: We won't send any more frames, but the client might still get one already in flight |
| 192 | ALOGD("Waiting for stream thread to end..."); |
| 193 | mCaptureThread.join(); |
| 194 | |
| 195 | // Lock scope |
| 196 | { |
| 197 | std::lock_guard <std::mutex> lock(mAccessLock); |
| 198 | mStreamState = STOPPED; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | return Void(); |
| 203 | } |
| 204 | |
| 205 | Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) { |
| 206 | ALOGD("getExtendedInfo"); |
| 207 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 208 | |
| 209 | // For any single digit value, return the index itself as a test value |
| 210 | if (opaqueIdentifier <= 9) { |
| 211 | return opaqueIdentifier; |
| 212 | } |
| 213 | |
| 214 | // Return zero by default as required by the spec |
| 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) { |
| 219 | ALOGD("setExtendedInfo"); |
| 220 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 221 | |
| 222 | // We don't store any device specific information in this implementation |
| 223 | return EvsResult::INVALID_ARG; |
| 224 | } |
| 225 | |
| 226 | |
| 227 | void EvsCamera::GenerateFrames() { |
| 228 | ALOGD("Frame generate loop started"); |
| 229 | |
| 230 | uint32_t frameNumber; |
| 231 | |
| 232 | while (true) { |
| 233 | bool timeForFrame = false; |
| 234 | // Lock scope |
| 235 | { |
| 236 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 237 | |
| 238 | // Tick the frame counter -- rollover is tolerated |
| 239 | frameNumber = mFrameId++; |
| 240 | |
| 241 | if (mStreamState != RUNNING) { |
| 242 | // Break out of our main thread loop |
| 243 | break; |
| 244 | } |
| 245 | |
| 246 | if (mFrameBusy) { |
| 247 | // Can't do anything right now -- skip this frame |
| 248 | ALOGW("Skipped a frame because client hasn't returned a buffer\n"); |
| 249 | } |
| 250 | else { |
| 251 | // We're going to make the frame busy |
| 252 | mFrameBusy = true; |
| 253 | timeForFrame = true; |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | if (timeForFrame) { |
| 258 | // Lock our output buffer for writing |
| 259 | uint32_t *pixels = nullptr; |
| 260 | GraphicBufferMapper &mapper = GraphicBufferMapper::get(); |
| 261 | mapper.lock(mBuffer, |
| 262 | GRALLOC_USAGE_SW_WRITE_OFTEN, |
| 263 | android::Rect(mWidth, mHeight), |
| 264 | (void **) &pixels); |
| 265 | |
| 266 | // If we failed to lock the pixel buffer, we're about to crash, but log it first |
| 267 | if (!pixels) { |
| 268 | ALOGE("Camera failed to gain access to image buffer for writing"); |
| 269 | } |
| 270 | |
| 271 | // Fill in the test pixels |
| 272 | for (unsigned row = 0; row < mHeight; row++) { |
| 273 | for (unsigned col = 0; col < mWidth; col++) { |
| 274 | // Index into the row to set the pixel at this column |
| 275 | // (We're making vertical gradient in the green channel, and |
| 276 | // horitzontal gradient in the blue channel) |
| 277 | pixels[col] = 0xFF0000FF | ((row & 0xFF) << 16) | ((col & 0xFF) << 8); |
| 278 | } |
| 279 | // Point to the next row |
| 280 | pixels = pixels + (mStride / sizeof(*pixels)); |
| 281 | } |
| 282 | |
| 283 | // Release our output buffer |
| 284 | mapper.unlock(mBuffer); |
| 285 | |
| 286 | // Issue the (asynchronous) callback to the client |
| 287 | mStream->deliverFrame(frameNumber, mBuffer); |
| 288 | ALOGD("Delivered %p as frame %d", mBuffer, frameNumber); |
| 289 | } |
| 290 | |
| 291 | // We arbitrarily choose to generate frames at 10 fps (1/10 * uSecPerSec) |
| 292 | usleep(100000); |
| 293 | } |
| 294 | |
| 295 | // If we've been asked to stop, send one last NULL frame to signal the actual end of stream |
| 296 | mStream->deliverFrame(frameNumber, nullptr); |
| 297 | |
| 298 | return; |
| 299 | } |
| 300 | |
| 301 | } // namespace implementation |
| 302 | } // namespace V1_0 |
| 303 | } // namespace evs |
| 304 | } // namespace hardware |
| 305 | } // namespace android |