Tanmay Patil | b97cceb | 2020-02-07 16:48:39 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2020 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 | #include "EvsUltrasonicsArray.h" |
| 18 | |
| 19 | #include <android-base/logging.h> |
| 20 | #include <hidlmemory/mapping.h> |
| 21 | #include <log/log.h> |
| 22 | #include <time.h> |
| 23 | #include <utils/SystemClock.h> |
| 24 | #include <utils/Timers.h> |
| 25 | |
| 26 | namespace android { |
| 27 | namespace hardware { |
| 28 | namespace automotive { |
| 29 | namespace evs { |
| 30 | namespace V1_1 { |
| 31 | namespace implementation { |
| 32 | |
| 33 | // Arbitrary limit on number of data frames allowed to be allocated |
| 34 | // Safeguards against unreasonable resource consumption and provides a testable limit |
| 35 | const unsigned int kMaximumDataFramesInFlight = 100; |
| 36 | |
| 37 | const uint32_t kMaxReadingsPerSensor = 5; |
| 38 | const uint32_t kMaxReceiversCount = 3; |
| 39 | |
| 40 | const unsigned int kSharedMemoryMaxSize = |
| 41 | kMaxReadingsPerSensor * kMaxReceiversCount * 2 * sizeof(float); |
| 42 | |
| 43 | // Target frame rate in frames per second. |
| 44 | const int kTargetFrameRate = 10; |
| 45 | |
| 46 | namespace { |
| 47 | |
| 48 | void fillDummyArrayDesc(UltrasonicsArrayDesc& arrayDesc) { |
| 49 | arrayDesc.maxReadingsPerSensorCount = kMaxReadingsPerSensor; |
| 50 | arrayDesc.maxReceiversCount = kMaxReceiversCount; |
| 51 | |
| 52 | const int kSensorCount = 3; |
| 53 | const float kMaxRange = 4000; // 4 metres. |
| 54 | const float kAngleOfMeasurement = 0.261799; // 15 degrees. |
| 55 | |
| 56 | std::vector<UltrasonicSensor> sensors(kSensorCount); |
| 57 | |
| 58 | // Sensor pointing forward on left side of front bumper. |
| 59 | sensors[0].maxRange = kMaxRange; |
| 60 | sensors[0].angleOfMeasurement = kAngleOfMeasurement; |
| 61 | sensors[0].pose = {{1, 0, 0, 0}, {-1000, 2000, 200}}; |
| 62 | |
| 63 | // Sensor pointing forward on center of front bumper. |
| 64 | sensors[1].maxRange = kMaxRange; |
| 65 | sensors[1].angleOfMeasurement = kAngleOfMeasurement; |
| 66 | sensors[1].pose = {{1, 0, 0, 0}, {0, 2000, 200}}; |
| 67 | |
| 68 | // Sensor pointing forward on right side of front bumper. |
| 69 | sensors[2].maxRange = kMaxRange; |
| 70 | sensors[2].angleOfMeasurement = kAngleOfMeasurement; |
| 71 | sensors[2].pose = {{1, 0, 0, 0}, {1000, 2000, 200}}; |
| 72 | |
| 73 | arrayDesc.sensors = sensors; |
| 74 | } |
| 75 | |
| 76 | // Struct used by SerializeWaveformData(). |
| 77 | struct WaveformData { |
| 78 | uint8_t receiverId; |
| 79 | std::vector<std::pair<float, float>> readings; |
| 80 | }; |
| 81 | |
| 82 | // Serializes data provided in waveformDataList to a shared memory data pointer. |
| 83 | // TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data. |
| 84 | void SerializeWaveformData(const std::vector<WaveformData>& waveformDataList, uint8_t* pData) { |
| 85 | for (auto& waveformData : waveformDataList) { |
| 86 | // Set Id |
| 87 | memcpy(pData, &waveformData.receiverId, sizeof(uint8_t)); |
| 88 | pData += sizeof(uint8_t); |
| 89 | |
| 90 | for (auto& reading : waveformData.readings) { |
| 91 | // Set the time of flight. |
| 92 | memcpy(pData, &reading.first, sizeof(float)); |
| 93 | pData += sizeof(float); |
| 94 | |
| 95 | // Set the resonance. |
| 96 | memcpy(pData, &reading.second, sizeof(float)); |
| 97 | pData += sizeof(float); |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | // Fills dataFrameDesc with dummy data. |
| 103 | bool fillDummyDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) { |
| 104 | dataFrameDesc.timestampNs = elapsedRealtimeNano(); |
| 105 | |
| 106 | const std::vector<uint8_t> transmittersIdList = {0}; |
| 107 | dataFrameDesc.transmittersIdList = transmittersIdList; |
| 108 | |
| 109 | const std::vector<uint8_t> recvIdList = {0, 1, 2}; |
| 110 | dataFrameDesc.receiversIdList = recvIdList; |
| 111 | |
| 112 | const std::vector<uint32_t> receiversReadingsCountList = {2, 2, 4}; |
| 113 | dataFrameDesc.receiversReadingsCountList = receiversReadingsCountList; |
| 114 | |
| 115 | const std::vector<WaveformData> waveformDataList = { |
| 116 | {recvIdList[0], { {1000, 0.1f}, {2000, 0.8f} }}, |
| 117 | {recvIdList[1], { {1000, 0.1f}, {2000, 1.0f} }}, |
| 118 | {recvIdList[2], { {1000, 0.1f}, {2000, 0.2f}, {4000, 0.2f}, {5000, 0.1f} }} |
| 119 | }; |
| 120 | |
| 121 | if (pIMemory.get() == nullptr) { |
| 122 | return false; |
| 123 | } |
| 124 | |
| 125 | uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer()); |
| 126 | |
| 127 | pIMemory->update(); |
| 128 | SerializeWaveformData(waveformDataList, pData); |
| 129 | pIMemory->commit(); |
| 130 | |
| 131 | return true; |
| 132 | } |
| 133 | |
| 134 | } // namespace |
| 135 | |
| 136 | EvsUltrasonicsArray::EvsUltrasonicsArray(const char* deviceName) |
| 137 | : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) { |
| 138 | LOG(DEBUG) << "EvsUltrasonicsArray instantiated"; |
| 139 | |
| 140 | // Set up dummy data for description. |
| 141 | mArrayDesc.ultrasonicsArrayId = deviceName; |
| 142 | fillDummyArrayDesc(mArrayDesc); |
| 143 | |
| 144 | // Assign allocator. |
| 145 | mShmemAllocator = IAllocator::getService("ashmem"); |
| 146 | if (mShmemAllocator.get() == nullptr) { |
| 147 | LOG(ERROR) << "SurroundViewHidlTest getService ashmem failed"; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | sp<EvsUltrasonicsArray> EvsUltrasonicsArray::Create(const char* deviceName) { |
| 152 | return sp<EvsUltrasonicsArray>(new EvsUltrasonicsArray(deviceName)); |
| 153 | } |
| 154 | |
| 155 | EvsUltrasonicsArray::~EvsUltrasonicsArray() { |
| 156 | LOG(DEBUG) << "EvsUltrasonicsArray being destroyed"; |
| 157 | forceShutdown(); |
| 158 | } |
| 159 | |
| 160 | // This gets called if another caller "steals" ownership of the ultrasonic array. |
| 161 | void EvsUltrasonicsArray::forceShutdown() { |
| 162 | LOG(DEBUG) << "EvsUltrasonicsArray forceShutdown"; |
| 163 | |
| 164 | // Make sure our output stream is cleaned up |
| 165 | // (It really should be already) |
| 166 | stopStream(); |
| 167 | |
| 168 | // Claim the lock while we work on internal state |
| 169 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 170 | |
| 171 | // Drop all the data frames we've been using |
| 172 | for (auto&& dataFrame : mDataFrames) { |
| 173 | if (dataFrame.inUse) { |
| 174 | LOG(ERROR) << "Error - releasing data frame despite remote ownership"; |
| 175 | } |
| 176 | dataFrame.sharedMemory.clear(); |
| 177 | } |
| 178 | mDataFrames.clear(); |
| 179 | |
| 180 | // Put this object into an unrecoverable error state since somebody else |
| 181 | // is going to own the underlying ultrasonic array now |
| 182 | mStreamState = DEAD; |
| 183 | } |
| 184 | |
| 185 | UltrasonicsArrayDesc EvsUltrasonicsArray::GetDummyArrayDesc(const char* deviceName) { |
| 186 | UltrasonicsArrayDesc ultrasonicsArrayDesc; |
| 187 | ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName; |
| 188 | fillDummyArrayDesc(ultrasonicsArrayDesc); |
| 189 | return ultrasonicsArrayDesc; |
| 190 | } |
| 191 | |
| 192 | Return<void> EvsUltrasonicsArray::getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) { |
| 193 | LOG(DEBUG) << "EvsUltrasonicsArray getUltrasonicsArrayInfo"; |
| 194 | |
| 195 | // Return the description for the get info callback. |
| 196 | _get_info_cb(mArrayDesc); |
| 197 | |
| 198 | return Void(); |
| 199 | } |
| 200 | |
| 201 | Return<EvsResult> EvsUltrasonicsArray::setMaxFramesInFlight(uint32_t bufferCount) { |
| 202 | LOG(DEBUG) << "EvsUltrasonicsArray setMaxFramesInFlight"; |
| 203 | |
| 204 | // Lock mutex for performing changes to available frames. |
| 205 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 206 | |
| 207 | // We cannot function without at least one buffer to send data. |
| 208 | if (bufferCount < 1) { |
| 209 | LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested"; |
| 210 | return EvsResult::INVALID_ARG; |
| 211 | } |
| 212 | |
| 213 | // Update our internal state of buffer count. |
| 214 | if (setAvailableFrames_Locked(bufferCount)) { |
| 215 | return EvsResult::OK; |
| 216 | } else { |
| 217 | return EvsResult::BUFFER_NOT_AVAILABLE; |
| 218 | } |
| 219 | |
| 220 | return EvsResult::OK; |
| 221 | } |
| 222 | |
| 223 | Return<void> EvsUltrasonicsArray::doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) { |
| 224 | LOG(DEBUG) << "EvsUltrasonicsArray doneWithFrame"; |
| 225 | |
| 226 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 227 | |
| 228 | if (dataFrameDesc.dataFrameId >= mDataFrames.size()) { |
| 229 | LOG(ERROR) << "ignoring doneWithFrame called with invalid dataFrameId " |
| 230 | << dataFrameDesc.dataFrameId << "(max is " << mDataFrames.size() - 1 << ")"; |
| 231 | return Void(); |
| 232 | } |
| 233 | |
| 234 | if (!mDataFrames[dataFrameDesc.dataFrameId].inUse) { |
| 235 | LOG(ERROR) << "ignoring doneWithFrame called on frame " << dataFrameDesc.dataFrameId |
| 236 | << "which is already free"; |
| 237 | return Void(); |
| 238 | } |
| 239 | |
| 240 | // Mark the frame as available |
| 241 | mDataFrames[dataFrameDesc.dataFrameId].inUse = false; |
| 242 | mFramesInUse--; |
| 243 | |
| 244 | // If this frame's index is high in the array, try to move it down |
| 245 | // to improve locality after mFramesAllowed has been reduced. |
| 246 | if (dataFrameDesc.dataFrameId >= mFramesAllowed) { |
| 247 | // Find an empty slot lower in the array (which should always exist in this case) |
| 248 | for (auto&& dataFrame : mDataFrames) { |
| 249 | if (!dataFrame.sharedMemory.IsValid()) { |
| 250 | dataFrame.sharedMemory = mDataFrames[dataFrameDesc.dataFrameId].sharedMemory; |
| 251 | mDataFrames[dataFrameDesc.dataFrameId].sharedMemory.clear(); |
| 252 | return Void(); |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | return Void(); |
| 258 | } |
| 259 | |
| 260 | Return<EvsResult> EvsUltrasonicsArray::startStream( |
| 261 | const ::android::sp<IEvsUltrasonicsArrayStream>& stream) { |
| 262 | LOG(DEBUG) << "EvsUltrasonicsArray startStream"; |
| 263 | |
| 264 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 265 | |
| 266 | if (mStreamState != STOPPED) { |
| 267 | LOG(ERROR) << "ignoring startStream call when a stream is already running."; |
| 268 | return EvsResult::STREAM_ALREADY_RUNNING; |
| 269 | } |
| 270 | |
| 271 | // If the client never indicated otherwise, configure ourselves for a single streaming buffer |
| 272 | if (mFramesAllowed < 1) { |
| 273 | if (!setAvailableFrames_Locked(1)) { |
| 274 | LOG(ERROR) |
| 275 | << "Failed to start stream because we couldn't get shared memory data buffer"; |
| 276 | return EvsResult::BUFFER_NOT_AVAILABLE; |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | // Record the user's callback for use when we have a frame ready |
| 281 | mStream = stream; |
| 282 | |
| 283 | // Start the frame generation thread |
| 284 | mStreamState = RUNNING; |
| 285 | mCaptureThread = std::thread([this]() { generateDataFrames(); }); |
| 286 | |
| 287 | return EvsResult::OK; |
| 288 | } |
| 289 | |
| 290 | Return<void> EvsUltrasonicsArray::stopStream() { |
| 291 | LOG(DEBUG) << "EvsUltrasonicsArray stopStream"; |
| 292 | |
| 293 | bool streamStateStopping = false; |
| 294 | { |
| 295 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 296 | if (mStreamState == RUNNING) { |
| 297 | // Tell the GenerateFrames loop we want it to stop |
| 298 | mStreamState = STOPPING; |
| 299 | streamStateStopping = true; |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | if (streamStateStopping) { |
| 304 | // Block outside the mutex until the "stop" flag has been acknowledged |
| 305 | // We won't send any more frames, but the client might still get some already in flight |
| 306 | LOG(DEBUG) << "Waiting for stream thread to end..."; |
| 307 | mCaptureThread.join(); |
| 308 | } |
| 309 | |
| 310 | { |
| 311 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 312 | mStreamState = STOPPED; |
| 313 | mStream = nullptr; |
| 314 | LOG(DEBUG) << "Stream marked STOPPED."; |
| 315 | } |
| 316 | |
| 317 | return Void(); |
| 318 | } |
| 319 | |
| 320 | bool EvsUltrasonicsArray::setAvailableFrames_Locked(unsigned bufferCount) { |
| 321 | if (bufferCount < 1) { |
| 322 | LOG(ERROR) << "Ignoring request to set buffer count to zero"; |
| 323 | return false; |
| 324 | } |
| 325 | if (bufferCount > kMaximumDataFramesInFlight) { |
| 326 | LOG(ERROR) << "Rejecting buffer request in excess of internal limit"; |
| 327 | return false; |
| 328 | } |
| 329 | |
| 330 | // Is an increase required? |
| 331 | if (mFramesAllowed < bufferCount) { |
| 332 | // An increase is required |
| 333 | unsigned needed = bufferCount - mFramesAllowed; |
| 334 | LOG(INFO) << "Number of data frame buffers to add: " << needed; |
| 335 | |
| 336 | unsigned added = increaseAvailableFrames_Locked(needed); |
| 337 | if (added != needed) { |
| 338 | // If we didn't add all the frames we needed, then roll back to the previous state |
| 339 | LOG(ERROR) << "Rolling back to previous frame queue size"; |
| 340 | decreaseAvailableFrames_Locked(added); |
| 341 | return false; |
| 342 | } |
| 343 | } else if (mFramesAllowed > bufferCount) { |
| 344 | // A decrease is required |
| 345 | unsigned framesToRelease = mFramesAllowed - bufferCount; |
| 346 | LOG(INFO) << "Number of data frame buffers to reduce: " << framesToRelease; |
| 347 | |
| 348 | unsigned released = decreaseAvailableFrames_Locked(framesToRelease); |
| 349 | if (released != framesToRelease) { |
| 350 | // This shouldn't happen with a properly behaving client because the client |
| 351 | // should only make this call after returning sufficient outstanding buffers |
| 352 | // to allow a clean resize. |
| 353 | LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?"; |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | return true; |
| 358 | } |
| 359 | |
| 360 | EvsUltrasonicsArray::SharedMemory EvsUltrasonicsArray::allocateAndMapSharedMemory() { |
| 361 | SharedMemory sharedMemory; |
| 362 | |
| 363 | // Check shared memory allocator is valid. |
| 364 | if (mShmemAllocator.get() == nullptr) { |
| 365 | LOG(ERROR) << "Shared memory allocator not initialized."; |
| 366 | return SharedMemory(); |
| 367 | } |
| 368 | |
| 369 | // Allocate memory. |
| 370 | bool allocateSuccess = false; |
| 371 | Return<void> result = mShmemAllocator->allocate(kSharedMemoryMaxSize, |
| 372 | [&](bool success, const hidl_memory& hidlMem) { |
| 373 | if (!success) { |
| 374 | return; |
| 375 | } |
| 376 | allocateSuccess = success; |
| 377 | sharedMemory.hidlMemory = hidlMem; |
| 378 | }); |
| 379 | |
| 380 | // Check result of allocated memory. |
| 381 | if (!result.isOk() || !allocateSuccess) { |
| 382 | LOG(ERROR) << "Shared memory allocation failed."; |
| 383 | return SharedMemory(); |
| 384 | } |
| 385 | |
| 386 | // Map shared memory. |
| 387 | sharedMemory.pIMemory = mapMemory(sharedMemory.hidlMemory); |
| 388 | if (sharedMemory.pIMemory.get() == nullptr) { |
| 389 | LOG(ERROR) << "Shared memory mapping failed."; |
| 390 | return SharedMemory(); |
| 391 | } |
| 392 | |
| 393 | // Return success. |
| 394 | return sharedMemory; |
| 395 | } |
| 396 | |
| 397 | unsigned EvsUltrasonicsArray::increaseAvailableFrames_Locked(unsigned numToAdd) { |
| 398 | unsigned added = 0; |
| 399 | |
| 400 | while (added < numToAdd) { |
| 401 | SharedMemory sharedMemory = allocateAndMapSharedMemory(); |
| 402 | |
| 403 | // If allocate and map fails, break. |
| 404 | if (!sharedMemory.IsValid()) { |
| 405 | break; |
| 406 | } |
| 407 | |
| 408 | // Find a place to store the new buffer |
| 409 | bool stored = false; |
| 410 | for (auto&& dataFrame : mDataFrames) { |
| 411 | if (!dataFrame.sharedMemory.IsValid()) { |
| 412 | // Use this existing entry |
| 413 | dataFrame.sharedMemory = sharedMemory; |
| 414 | dataFrame.inUse = false; |
| 415 | stored = true; |
| 416 | break; |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | if (!stored) { |
| 421 | // Add a BufferRecord wrapping this handle to our set of available buffers |
| 422 | mDataFrames.emplace_back(sharedMemory); |
| 423 | } |
| 424 | |
| 425 | mFramesAllowed++; |
| 426 | added++; |
| 427 | } |
| 428 | |
| 429 | return added; |
| 430 | } |
| 431 | |
| 432 | unsigned EvsUltrasonicsArray::decreaseAvailableFrames_Locked(unsigned numToRemove) { |
| 433 | unsigned removed = 0; |
| 434 | |
| 435 | for (auto&& dataFrame : mDataFrames) { |
| 436 | // Is this record not in use, but holding a buffer that we can free? |
| 437 | if (!dataFrame.inUse && dataFrame.sharedMemory.IsValid()) { |
| 438 | // Release buffer and update the record so we can recognize it as "empty" |
| 439 | dataFrame.sharedMemory.clear(); |
| 440 | |
| 441 | mFramesAllowed--; |
| 442 | removed++; |
| 443 | |
| 444 | if (removed == numToRemove) { |
| 445 | break; |
| 446 | } |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | return removed; |
| 451 | } |
| 452 | |
| 453 | // This is the asynchronous data frame generation thread that runs in parallel with the |
| 454 | // main serving thread. There is one for each active ultrasonic array instance. |
| 455 | void EvsUltrasonicsArray::generateDataFrames() { |
| 456 | LOG(DEBUG) << "Data frame generation loop started"; |
| 457 | |
| 458 | unsigned idx = 0; |
| 459 | |
| 460 | while (true) { |
| 461 | bool timeForFrame = false; |
| 462 | |
| 463 | nsecs_t startTime = elapsedRealtimeNano(); |
| 464 | |
| 465 | // Lock scope for updating shared state |
| 466 | { |
| 467 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 468 | |
| 469 | if (mStreamState != RUNNING) { |
| 470 | // Break out of our main thread loop |
| 471 | break; |
| 472 | } |
| 473 | |
| 474 | // Are we allowed to issue another buffer? |
| 475 | if (mFramesInUse >= mFramesAllowed) { |
| 476 | // Can't do anything right now -- skip this frame |
| 477 | LOG(WARNING) << "Skipped a frame because too many are in flight"; |
| 478 | } else { |
| 479 | // Identify an available buffer to fill |
| 480 | for (idx = 0; idx < mDataFrames.size(); idx++) { |
| 481 | if (!mDataFrames[idx].inUse && mDataFrames[idx].sharedMemory.IsValid()) { |
| 482 | // Found an available record, so stop looking |
| 483 | break; |
| 484 | } |
| 485 | } |
| 486 | if (idx >= mDataFrames.size()) { |
| 487 | // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed |
| 488 | LOG(ERROR) << "Failed to find an available buffer slot"; |
| 489 | } else { |
| 490 | // We're going to make the frame busy |
| 491 | mDataFrames[idx].inUse = true; |
| 492 | mFramesInUse++; |
| 493 | timeForFrame = true; |
| 494 | } |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | if (timeForFrame) { |
| 499 | // Assemble the buffer description we'll transmit below |
| 500 | UltrasonicsDataFrameDesc dummyDataFrameDesc; |
| 501 | dummyDataFrameDesc.dataFrameId = idx; |
| 502 | dummyDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory; |
| 503 | |
| 504 | // Fill dummy waveform data. |
| 505 | fillDummyDataFrame(dummyDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory); |
| 506 | |
| 507 | // Issue the (asynchronous) callback to the client -- can't be holding the lock |
| 508 | auto result = mStream->deliverDataFrame(dummyDataFrameDesc); |
| 509 | if (result.isOk()) { |
| 510 | LOG(DEBUG) << "Delivered data frame id: " << dummyDataFrameDesc.dataFrameId; |
| 511 | } else { |
| 512 | // This can happen if the client dies and is likely unrecoverable. |
| 513 | // To avoid consuming resources generating failing calls, we stop sending |
| 514 | // frames. Note, however, that the stream remains in the "STREAMING" state |
| 515 | // until cleaned up on the main thread. |
| 516 | LOG(ERROR) << "Frame delivery call failed in the transport layer."; |
| 517 | |
| 518 | // Since we didn't actually deliver it, mark the frame as available |
| 519 | std::lock_guard<std::mutex> lock(mAccessLock); |
| 520 | mDataFrames[idx].inUse = false; |
| 521 | mFramesInUse--; |
| 522 | |
| 523 | break; |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | // Sleep to generate frames at kTargetFrameRate. |
| 528 | static const nsecs_t kTargetFrameTimeUs = 1000 * 1000 / kTargetFrameRate; |
| 529 | const nsecs_t now = elapsedRealtimeNano(); |
| 530 | const nsecs_t workTimeUs = (now - startTime) / 1000; |
| 531 | const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs; |
| 532 | if (sleepDurationUs > 0) { |
| 533 | usleep(sleepDurationUs); |
| 534 | } |
| 535 | } |
| 536 | |
| 537 | // If we've been asked to stop, send an event to signal the actual end of stream |
| 538 | EvsEventDesc event; |
| 539 | event.aType = EvsEventType::STREAM_STOPPED; |
| 540 | auto result = mStream->notify(event); |
| 541 | if (!result.isOk()) { |
| 542 | LOG(ERROR) << "Error delivering end of stream marker"; |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | } // namespace implementation |
| 547 | } // namespace V1_1 |
| 548 | } // namespace evs |
| 549 | } // namespace automotive |
| 550 | } // namespace hardware |
| 551 | } // namespace android |