| /* |
| * Copyright 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "PipelineWatcher" |
| |
| #include <numeric> |
| |
| #include <log/log.h> |
| |
| #include "PipelineWatcher.h" |
| |
| namespace android { |
| |
| PipelineWatcher &PipelineWatcher::inputDelay(uint32_t value) { |
| mInputDelay = value; |
| return *this; |
| } |
| |
| PipelineWatcher &PipelineWatcher::pipelineDelay(uint32_t value) { |
| mPipelineDelay = value; |
| return *this; |
| } |
| |
| PipelineWatcher &PipelineWatcher::outputDelay(uint32_t value) { |
| mOutputDelay = value; |
| return *this; |
| } |
| |
| PipelineWatcher &PipelineWatcher::smoothnessFactor(uint32_t value) { |
| mSmoothnessFactor = value; |
| return *this; |
| } |
| |
| PipelineWatcher &PipelineWatcher::tunneled(bool value) { |
| mTunneled = value; |
| return *this; |
| } |
| |
| void PipelineWatcher::onWorkQueued( |
| uint64_t frameIndex, |
| std::vector<std::shared_ptr<C2Buffer>> &&buffers, |
| const Clock::time_point &queuedAt) { |
| ALOGV("onWorkQueued(frameIndex=%llu, buffers(size=%zu), queuedAt=%lld)", |
| (unsigned long long)frameIndex, |
| buffers.size(), |
| (long long)queuedAt.time_since_epoch().count()); |
| auto it = mFramesInPipeline.find(frameIndex); |
| if (it != mFramesInPipeline.end()) { |
| ALOGD("onWorkQueued: Duplicate frame index (%llu); previous entry removed", |
| (unsigned long long)frameIndex); |
| (void)mFramesInPipeline.erase(it); |
| } |
| (void)mFramesInPipeline.try_emplace(frameIndex, std::move(buffers), queuedAt); |
| } |
| |
| std::shared_ptr<C2Buffer> PipelineWatcher::onInputBufferReleased( |
| uint64_t frameIndex, size_t arrayIndex) { |
| ALOGV("onInputBufferReleased(frameIndex=%llu, arrayIndex=%zu)", |
| (unsigned long long)frameIndex, arrayIndex); |
| auto it = mFramesInPipeline.find(frameIndex); |
| if (it == mFramesInPipeline.end()) { |
| ALOGD("onInputBufferReleased: frameIndex not found (%llu); ignored", |
| (unsigned long long)frameIndex); |
| return nullptr; |
| } |
| if (it->second.buffers.size() <= arrayIndex) { |
| ALOGD("onInputBufferReleased: buffers at %llu: size %zu, requested index: %zu", |
| (unsigned long long)frameIndex, it->second.buffers.size(), arrayIndex); |
| return nullptr; |
| } |
| std::shared_ptr<C2Buffer> buffer(std::move(it->second.buffers[arrayIndex])); |
| ALOGD_IF(!buffer, "onInputBufferReleased: buffer already released (%llu:%zu)", |
| (unsigned long long)frameIndex, arrayIndex); |
| return buffer; |
| } |
| |
| void PipelineWatcher::onWorkDone(uint64_t frameIndex) { |
| ALOGV("onWorkDone(frameIndex=%llu)", (unsigned long long)frameIndex); |
| auto it = mFramesInPipeline.find(frameIndex); |
| if (it == mFramesInPipeline.end()) { |
| if (!mTunneled) { |
| ALOGD("onWorkDone: frameIndex not found (%llu); ignored", |
| (unsigned long long)frameIndex); |
| } else { |
| ALOGV("onWorkDone: frameIndex not found (%llu); ignored", |
| (unsigned long long)frameIndex); |
| } |
| return; |
| } |
| (void)mFramesInPipeline.erase(it); |
| } |
| |
| void PipelineWatcher::flush() { |
| ALOGV("flush"); |
| mFramesInPipeline.clear(); |
| } |
| |
| bool PipelineWatcher::pipelineFull() const { |
| if (mFramesInPipeline.size() >= |
| mInputDelay + mPipelineDelay + mOutputDelay + mSmoothnessFactor) { |
| ALOGV("pipelineFull: too many frames in pipeline (%zu)", mFramesInPipeline.size()); |
| return true; |
| } |
| size_t sizeWithInputReleased = std::count_if( |
| mFramesInPipeline.begin(), |
| mFramesInPipeline.end(), |
| [](const decltype(mFramesInPipeline)::value_type &value) { |
| for (const std::shared_ptr<C2Buffer> &buffer : value.second.buffers) { |
| if (buffer) { |
| return false; |
| } |
| } |
| return true; |
| }); |
| if (sizeWithInputReleased >= |
| mPipelineDelay + mOutputDelay + mSmoothnessFactor) { |
| ALOGV("pipelineFull: too many frames in pipeline, with input released (%zu)", |
| sizeWithInputReleased); |
| return true; |
| } |
| |
| size_t sizeWithInputsPending = mFramesInPipeline.size() - sizeWithInputReleased; |
| if (sizeWithInputsPending > mPipelineDelay + mInputDelay + mSmoothnessFactor) { |
| ALOGV("pipelineFull: too many inputs pending (%zu) in pipeline, with inputs released (%zu)", |
| sizeWithInputsPending, sizeWithInputReleased); |
| return true; |
| } |
| ALOGV("pipeline has room (total: %zu, input released: %zu)", |
| mFramesInPipeline.size(), sizeWithInputReleased); |
| return false; |
| } |
| |
| PipelineWatcher::Clock::duration PipelineWatcher::elapsed( |
| const PipelineWatcher::Clock::time_point &now, size_t n) const { |
| if (mFramesInPipeline.size() <= n) { |
| return Clock::duration::zero(); |
| } |
| std::vector<Clock::duration> durations; |
| for (const decltype(mFramesInPipeline)::value_type &value : mFramesInPipeline) { |
| Clock::duration elapsed = now - value.second.queuedAt; |
| ALOGV("elapsed: frameIndex = %llu elapsed = %lldms", |
| (unsigned long long)value.first, |
| std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count()); |
| durations.push_back(elapsed); |
| } |
| std::nth_element(durations.begin(), durations.begin() + n, durations.end(), |
| std::greater<Clock::duration>()); |
| return durations[n]; |
| } |
| |
| } // namespace android |