|  | /* | 
|  | * 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 |