| Wonsik Kim | ab34ed6 | 2019-01-31 15:28:46 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2019 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_NDEBUG 0 | 
 | 18 | #define LOG_TAG "PipelineWatcher" | 
 | 19 |  | 
 | 20 | #include <numeric> | 
 | 21 |  | 
 | 22 | #include <log/log.h> | 
 | 23 |  | 
 | 24 | #include "PipelineWatcher.h" | 
 | 25 |  | 
 | 26 | namespace android { | 
 | 27 |  | 
 | 28 | PipelineWatcher &PipelineWatcher::inputDelay(uint32_t value) { | 
 | 29 |     mInputDelay = value; | 
 | 30 |     return *this; | 
 | 31 | } | 
 | 32 |  | 
 | 33 | PipelineWatcher &PipelineWatcher::pipelineDelay(uint32_t value) { | 
 | 34 |     mPipelineDelay = value; | 
 | 35 |     return *this; | 
 | 36 | } | 
 | 37 |  | 
 | 38 | PipelineWatcher &PipelineWatcher::outputDelay(uint32_t value) { | 
 | 39 |     mOutputDelay = value; | 
 | 40 |     return *this; | 
 | 41 | } | 
 | 42 |  | 
 | 43 | PipelineWatcher &PipelineWatcher::smoothnessFactor(uint32_t value) { | 
 | 44 |     mSmoothnessFactor = value; | 
 | 45 |     return *this; | 
 | 46 | } | 
 | 47 |  | 
 | 48 | void PipelineWatcher::onWorkQueued( | 
 | 49 |         uint64_t frameIndex, | 
 | 50 |         std::vector<std::shared_ptr<C2Buffer>> &&buffers, | 
 | 51 |         const Clock::time_point &queuedAt) { | 
 | 52 |     ALOGV("onWorkQueued(frameIndex=%llu, buffers(size=%zu), queuedAt=%lld)", | 
 | 53 |           (unsigned long long)frameIndex, | 
 | 54 |           buffers.size(), | 
 | 55 |           (long long)queuedAt.time_since_epoch().count()); | 
 | 56 |     auto it = mFramesInPipeline.find(frameIndex); | 
 | 57 |     if (it != mFramesInPipeline.end()) { | 
 | 58 |         ALOGD("onWorkQueued: Duplicate frame index (%llu); previous entry removed", | 
 | 59 |               (unsigned long long)frameIndex); | 
 | 60 |         (void)mFramesInPipeline.erase(it); | 
 | 61 |     } | 
 | 62 |     (void)mFramesInPipeline.try_emplace(frameIndex, std::move(buffers), queuedAt); | 
 | 63 | } | 
 | 64 |  | 
 | 65 | std::shared_ptr<C2Buffer> PipelineWatcher::onInputBufferReleased( | 
 | 66 |         uint64_t frameIndex, size_t arrayIndex) { | 
 | 67 |     ALOGV("onInputBufferReleased(frameIndex=%llu, arrayIndex=%zu)", | 
 | 68 |           (unsigned long long)frameIndex, arrayIndex); | 
 | 69 |     auto it = mFramesInPipeline.find(frameIndex); | 
 | 70 |     if (it == mFramesInPipeline.end()) { | 
 | 71 |         ALOGD("onInputBufferReleased: frameIndex not found (%llu); ignored", | 
 | 72 |               (unsigned long long)frameIndex); | 
 | 73 |         return nullptr; | 
 | 74 |     } | 
 | 75 |     if (it->second.buffers.size() <= arrayIndex) { | 
 | 76 |         ALOGD("onInputBufferReleased: buffers at %llu: size %zu, requested index: %zu", | 
 | 77 |               (unsigned long long)frameIndex, it->second.buffers.size(), arrayIndex); | 
 | 78 |         return nullptr; | 
 | 79 |     } | 
 | 80 |     std::shared_ptr<C2Buffer> buffer(std::move(it->second.buffers[arrayIndex])); | 
 | 81 |     ALOGD_IF(!buffer, "onInputBufferReleased: buffer already released (%llu:%zu)", | 
 | 82 |              (unsigned long long)frameIndex, arrayIndex); | 
 | 83 |     return buffer; | 
 | 84 | } | 
 | 85 |  | 
 | 86 | void PipelineWatcher::onWorkDone(uint64_t frameIndex) { | 
 | 87 |     ALOGV("onWorkDone(frameIndex=%llu)", (unsigned long long)frameIndex); | 
 | 88 |     auto it = mFramesInPipeline.find(frameIndex); | 
 | 89 |     if (it == mFramesInPipeline.end()) { | 
 | 90 |         ALOGD("onWorkDone: frameIndex not found (%llu); ignored", | 
 | 91 |               (unsigned long long)frameIndex); | 
 | 92 |         return; | 
 | 93 |     } | 
 | 94 |     (void)mFramesInPipeline.erase(it); | 
 | 95 | } | 
 | 96 |  | 
 | 97 | void PipelineWatcher::flush() { | 
| Wonsik Kim | 6254525 | 2021-01-20 11:25:41 -0800 | [diff] [blame] | 98 |     ALOGV("flush"); | 
| Wonsik Kim | ab34ed6 | 2019-01-31 15:28:46 -0800 | [diff] [blame] | 99 |     mFramesInPipeline.clear(); | 
 | 100 | } | 
 | 101 |  | 
 | 102 | bool PipelineWatcher::pipelineFull() const { | 
 | 103 |     if (mFramesInPipeline.size() >= | 
 | 104 |             mInputDelay + mPipelineDelay + mOutputDelay + mSmoothnessFactor) { | 
 | 105 |         ALOGV("pipelineFull: too many frames in pipeline (%zu)", mFramesInPipeline.size()); | 
 | 106 |         return true; | 
 | 107 |     } | 
 | 108 |     size_t sizeWithInputReleased = std::count_if( | 
 | 109 |             mFramesInPipeline.begin(), | 
 | 110 |             mFramesInPipeline.end(), | 
 | 111 |             [](const decltype(mFramesInPipeline)::value_type &value) { | 
 | 112 |                 for (const std::shared_ptr<C2Buffer> &buffer : value.second.buffers) { | 
 | 113 |                     if (buffer) { | 
 | 114 |                         return false; | 
 | 115 |                     } | 
 | 116 |                 } | 
 | 117 |                 return true; | 
 | 118 |             }); | 
 | 119 |     if (sizeWithInputReleased >= | 
 | 120 |             mPipelineDelay + mOutputDelay + mSmoothnessFactor) { | 
 | 121 |         ALOGV("pipelineFull: too many frames in pipeline, with input released (%zu)", | 
 | 122 |               sizeWithInputReleased); | 
 | 123 |         return true; | 
 | 124 |     } | 
| Praveen Chavan | 7892cea | 2019-05-08 19:43:30 -0700 | [diff] [blame] | 125 |  | 
 | 126 |     size_t sizeWithInputsPending = mFramesInPipeline.size() - sizeWithInputReleased; | 
 | 127 |     if (sizeWithInputsPending > mPipelineDelay + mInputDelay + mSmoothnessFactor) { | 
 | 128 |         ALOGV("pipelineFull: too many inputs pending (%zu) in pipeline, with inputs released (%zu)", | 
 | 129 |               sizeWithInputsPending, sizeWithInputReleased); | 
 | 130 |         return true; | 
 | 131 |     } | 
| Wonsik Kim | ab34ed6 | 2019-01-31 15:28:46 -0800 | [diff] [blame] | 132 |     ALOGV("pipeline has room (total: %zu, input released: %zu)", | 
 | 133 |           mFramesInPipeline.size(), sizeWithInputReleased); | 
 | 134 |     return false; | 
 | 135 | } | 
 | 136 |  | 
 | 137 | PipelineWatcher::Clock::duration PipelineWatcher::elapsed( | 
| Wonsik Kim | 4fa4f2b | 2019-02-13 11:02:58 -0800 | [diff] [blame] | 138 |         const PipelineWatcher::Clock::time_point &now, size_t n) const { | 
 | 139 |     if (mFramesInPipeline.size() <= n) { | 
 | 140 |         return Clock::duration::zero(); | 
 | 141 |     } | 
 | 142 |     std::vector<Clock::duration> durations; | 
 | 143 |     for (const decltype(mFramesInPipeline)::value_type &value : mFramesInPipeline) { | 
 | 144 |         Clock::duration elapsed = now - value.second.queuedAt; | 
 | 145 |         ALOGV("elapsed: frameIndex = %llu elapsed = %lldms", | 
 | 146 |               (unsigned long long)value.first, | 
 | 147 |               std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count()); | 
 | 148 |         durations.push_back(elapsed); | 
 | 149 |     } | 
| Sungtak Lee | e151ed6 | 2019-07-16 17:40:40 -0700 | [diff] [blame] | 150 |     std::nth_element(durations.begin(), durations.begin() + n, durations.end(), | 
| Wonsik Kim | 070897f | 2019-02-15 10:38:54 -0800 | [diff] [blame] | 151 |                      std::greater<Clock::duration>()); | 
| Wonsik Kim | 4fa4f2b | 2019-02-13 11:02:58 -0800 | [diff] [blame] | 152 |     return durations[n]; | 
| Wonsik Kim | ab34ed6 | 2019-01-31 15:28:46 -0800 | [diff] [blame] | 153 | } | 
 | 154 |  | 
 | 155 | }  // namespace android |