xref: /aosp_15_r20/external/v4l2_codec2/components/VideoFramePool.cpp (revision 0ec5a0ec62797f775085659156625e7f1bdb369f)
1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //#define LOG_NDEBUG 0
6 #define LOG_TAG "VideoFramePool"
7 
8 #include <v4l2_codec2/components/VideoFramePool.h>
9 
10 #include <stdint.h>
11 #include <memory>
12 
13 #include <C2BlockInternal.h>
14 #include <bufferpool/BufferPoolTypes.h>
15 
16 #include <android/hardware/graphics/common/1.0/types.h>
17 #include <base/bind.h>
18 #include <base/memory/ptr_util.h>
19 #include <base/time/time.h>
20 #include <log/log.h>
21 
22 #include <v4l2_codec2/common/VideoTypes.h>
23 #include <v4l2_codec2/plugin_store/DmabufHelpers.h>
24 #include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
25 
26 using android::hardware::graphics::common::V1_0::BufferUsage;
27 using android::hardware::media::bufferpool::BufferPoolData;
28 
29 namespace android {
30 
31 // static
getBufferIdFromGraphicBlock(C2BlockPool & blockPool,const C2Block2D & block)32 std::optional<uint32_t> VideoFramePool::getBufferIdFromGraphicBlock(C2BlockPool& blockPool,
33                                                                     const C2Block2D& block) {
34     ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
35 
36     switch (blockPool.getAllocatorId()) {
37     case V4L2AllocatorId::SECURE_GRAPHIC:
38         FALLTHROUGH;
39     case C2PlatformAllocatorStore::BUFFERQUEUE: {
40         auto dmabufId = android::getDmabufId(block.handle()->data[0]);
41         if (!dmabufId) {
42             return std::nullopt;
43         }
44         return dmabufId.value();
45     }
46     case C2PlatformAllocatorStore::GRALLOC:
47         FALLTHROUGH;
48     case V4L2AllocatorId::SECURE_LINEAR: {
49         std::shared_ptr<_C2BlockPoolData> blockPoolData =
50                 _C2BlockFactory::GetGraphicBlockPoolData(block);
51         if (blockPoolData->getType() != _C2BlockPoolData::TYPE_BUFFERPOOL) {
52             ALOGE("Obtained C2GraphicBlock is not bufferpool-backed.");
53             return std::nullopt;
54         }
55         std::shared_ptr<BufferPoolData> bpData;
56         if (!_C2BlockFactory::GetBufferPoolData(blockPoolData, &bpData) || !bpData) {
57             ALOGE("BufferPoolData unavailable in block.");
58             return std::nullopt;
59         }
60         return bpData->mId;
61     }
62     }
63 
64     ALOGE("%s(): unknown allocator ID: %u", __func__, blockPool.getAllocatorId());
65     return std::nullopt;
66 }
67 
68 // static
Create(std::shared_ptr<C2BlockPool> blockPool,const size_t numBuffers,const ui::Size & size,HalPixelFormat pixelFormat,bool isSecure,scoped_refptr<::base::SequencedTaskRunner> taskRunner)69 std::unique_ptr<VideoFramePool> VideoFramePool::Create(
70         std::shared_ptr<C2BlockPool> blockPool, const size_t numBuffers, const ui::Size& size,
71         HalPixelFormat pixelFormat, bool isSecure,
72         scoped_refptr<::base::SequencedTaskRunner> taskRunner) {
73     ALOG_ASSERT(blockPool != nullptr);
74 
75     uint64_t usage = static_cast<uint64_t>(BufferUsage::VIDEO_DECODER);
76     if (isSecure) {
77         usage |= C2MemoryUsage::READ_PROTECTED;
78     } else if (blockPool->getAllocatorId() == C2PlatformAllocatorStore::GRALLOC) {
79         // CPU access to buffers is only required in byte buffer mode.
80         usage |= C2MemoryUsage::CPU_READ;
81     }
82     const C2MemoryUsage memoryUsage(usage);
83 
84     std::unique_ptr<VideoFramePool> pool =
85             ::base::WrapUnique(new VideoFramePool(std::move(blockPool), numBuffers, size,
86                                                   pixelFormat, memoryUsage, std::move(taskRunner)));
87     if (!pool->initialize()) return nullptr;
88     return pool;
89 }
90 
VideoFramePool(std::shared_ptr<C2BlockPool> blockPool,const size_t maxBufferCount,const ui::Size & size,HalPixelFormat pixelFormat,C2MemoryUsage memoryUsage,scoped_refptr<::base::SequencedTaskRunner> taskRunner)91 VideoFramePool::VideoFramePool(std::shared_ptr<C2BlockPool> blockPool, const size_t maxBufferCount,
92                                const ui::Size& size, HalPixelFormat pixelFormat,
93                                C2MemoryUsage memoryUsage,
94                                scoped_refptr<::base::SequencedTaskRunner> taskRunner)
95       : mBlockPool(std::move(blockPool)),
96         mMaxBufferCount(maxBufferCount),
97         mSize(size),
98         mPixelFormat(pixelFormat),
99         mMemoryUsage(memoryUsage),
100         mClientTaskRunner(std::move(taskRunner)) {
101     ALOGV("%s(size=%dx%d)", __func__, size.width, size.height);
102     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
103     DCHECK(mBlockPool);
104     DCHECK(mClientTaskRunner);
105 }
106 
initialize()107 bool VideoFramePool::initialize() {
108     if (!mFetchThread.Start()) {
109         ALOGE("Fetch thread failed to start.");
110         return false;
111     }
112     mFetchTaskRunner = mFetchThread.task_runner();
113 
114     mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
115     mFetchWeakThis = mFetchWeakThisFactory.GetWeakPtr();
116 
117     return true;
118 }
119 
~VideoFramePool()120 VideoFramePool::~VideoFramePool() {
121     ALOGV("%s()", __func__);
122     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
123 
124     mClientWeakThisFactory.InvalidateWeakPtrs();
125 
126     if (mFetchThread.IsRunning()) {
127         mFetchTaskRunner->PostTask(FROM_HERE,
128                                    ::base::BindOnce(&VideoFramePool::destroyTask, mFetchWeakThis));
129         mFetchThread.Stop();
130     }
131 }
132 
destroyTask()133 void VideoFramePool::destroyTask() {
134     ALOGV("%s()", __func__);
135     ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
136 
137     mFetchWeakThisFactory.InvalidateWeakPtrs();
138 }
139 
shouldDropBuffer(uint32_t bufferId)140 bool VideoFramePool::shouldDropBuffer(uint32_t bufferId) {
141     if (mBuffers.size() < mMaxBufferCount) {
142         return false;
143     }
144 
145     if (mBuffers.find(bufferId) != mBuffers.end()) {
146         return false;
147     }
148 
149     return true;
150 }
151 
getVideoFrame(GetVideoFrameCB cb)152 bool VideoFramePool::getVideoFrame(GetVideoFrameCB cb) {
153     ALOGV("%s()", __func__);
154     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
155 
156     if (mOutputCb) {
157         return false;
158     }
159 
160     mOutputCb = std::move(cb);
161     mFetchTaskRunner->PostTask(
162             FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis));
163     return true;
164 }
165 
166 // static
getVideoFrameTaskThunk(scoped_refptr<::base::SequencedTaskRunner> taskRunner,std::optional<::base::WeakPtr<VideoFramePool>> weakPool)167 void VideoFramePool::getVideoFrameTaskThunk(
168         scoped_refptr<::base::SequencedTaskRunner> taskRunner,
169         std::optional<::base::WeakPtr<VideoFramePool>> weakPool) {
170     ALOGV("%s()", __func__);
171     ALOG_ASSERT(weakPool);
172 
173     taskRunner->PostTask(FROM_HERE,
174                          ::base::BindOnce(&VideoFramePool::getVideoFrameTask, *weakPool));
175 }
176 
getVideoFrameTask()177 void VideoFramePool::getVideoFrameTask() {
178     ALOGV("%s()", __func__);
179     ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
180 
181     // Variables used to exponential backoff retry when buffer fetching times out.
182     constexpr size_t kFetchRetryDelayInit = 256;   // Initial delay: 256us
183     constexpr size_t kFetchRetryDelayMax = 16384;  // Max delay: 16ms (1 frame at 60fps)
184     constexpr size_t kFenceWaitTimeoutNs = 16000000;  // 16ms (1 frame at 60fps)
185     static size_t sNumRetries = 0;
186     static size_t sDelay = kFetchRetryDelayInit;
187 
188     C2Fence fence;
189     std::shared_ptr<C2GraphicBlock> block;
190     c2_status_t err = mBlockPool->fetchGraphicBlock(mSize.width, mSize.height,
191                                                     static_cast<uint32_t>(mPixelFormat),
192                                                     mMemoryUsage, &block, &fence);
193     // C2_BLOCKING can be returned either based on the state of the block pool itself
194     // or the state of the underlying buffer queue. If the cause is the underlying
195     // buffer queue, then the block pool returns a null fence. Since a null fence is
196     // immediately ready, we need to delay instead of trying to wait on the fence, to
197     // avoid spinning.
198     //
199     // Unfortunately, a null fence is considered a valid fence, so the best we can do
200     // to detect a null fence is to assume that any fence that is immediately ready
201     // is the null fence. A false positive by racing with a real fence can result in
202     // an unnecessary delay, but the only alternative is to ignore fences altogether
203     // and always delay.
204     if (err == C2_BLOCKING && !fence.ready()) {
205         err = fence.wait(kFenceWaitTimeoutNs);
206         if (err == C2_OK) {
207             ALOGV("%s(): fence wait succeded, retrying now", __func__);
208             mFetchTaskRunner->PostTask(
209                     FROM_HERE,
210                     ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis));
211             return;
212         }
213         ALOGV("%s(): fence wait unsucessful err=%d", __func__, err);
214     } else if (err == C2_OMITTED) {
215         // Fenced version is not supported, try legacy version.
216         err = mBlockPool->fetchGraphicBlock(mSize.width, mSize.height,
217                                             static_cast<uint32_t>(mPixelFormat), mMemoryUsage,
218                                             &block);
219     }
220 
221     std::optional<uint32_t> bufferId;
222     if (err == C2_OK) {
223         bufferId = getBufferIdFromGraphicBlock(*mBlockPool, *block);
224 
225         if (bufferId) {
226             ALOGV("%s(): Got buffer with id = %u", __func__, *bufferId);
227 
228             if (shouldDropBuffer(*bufferId)) {
229                 // We drop buffer, since we got more then needed.
230                 ALOGV("%s(): Dropping allocated buffer with id = %u", __func__, *bufferId);
231                 bufferId = std::nullopt;
232                 block.reset();
233                 err = C2_TIMED_OUT;
234             }
235         }
236     }
237 
238     if (err == C2_TIMED_OUT || err == C2_BLOCKING) {
239         ALOGV("%s(): fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", __func__, sDelay,
240               sNumRetries + 1);
241         mFetchTaskRunner->PostDelayedTask(
242                 FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis),
243                 ::base::TimeDelta::FromMicroseconds(sDelay));
244 
245         sDelay = std::min(sDelay * 4, kFetchRetryDelayMax);  // Exponential backoff
246         sNumRetries++;
247         return;
248     }
249 
250     // Reset to the default value.
251     sNumRetries = 0;
252     sDelay = kFetchRetryDelayInit;
253 
254     if (err != C2_OK) {
255         ALOGE("%s(): Failed to fetch block, err=%d", __func__, err);
256         return;
257     }
258 
259     ALOG_ASSERT(block != nullptr);
260     std::unique_ptr<VideoFrame> frame = VideoFrame::Create(std::move(block));
261     std::optional<FrameWithBlockId> frameWithBlockId;
262 
263     if (bufferId && frame) {
264         // Only pass the frame + id pair if both have successfully been obtained.
265         // Otherwise exit the loop so a nullopt is passed to the client.
266         frameWithBlockId = std::make_pair(std::move(frame), *bufferId);
267         mBuffers.insert(*bufferId);
268     } else {
269         ALOGE("%s(): Failed to generate VideoFrame or get the buffer id.", __func__);
270     }
271 
272     mClientTaskRunner->PostTask(
273             FROM_HERE, ::base::BindOnce(&VideoFramePool::onVideoFrameReady, mClientWeakThis,
274                                         std::move(frameWithBlockId)));
275 }
276 
onVideoFrameReady(std::optional<FrameWithBlockId> frameWithBlockId)277 void VideoFramePool::onVideoFrameReady(std::optional<FrameWithBlockId> frameWithBlockId) {
278     ALOGV("%s()", __func__);
279     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
280 
281     if (!frameWithBlockId) {
282         ALOGE("Failed to get GraphicBlock, abandoning all pending requests.");
283         mClientWeakThisFactory.InvalidateWeakPtrs();
284         mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
285     }
286 
287     ALOG_ASSERT(mOutputCb);
288     std::move(mOutputCb).Run(std::move(frameWithBlockId));
289 }
290 
291 }  // namespace android
292