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 <android/hardware/graphics/common/1.0/types.h>
14 #include <base/bind.h>
15 #include <base/memory/ptr_util.h>
16 #include <base/time/time.h>
17 #include <log/log.h>
18 
19 #include <v4l2_codec2/common/VideoTypes.h>
20 #include <v4l2_codec2/plugin_store/C2VdaBqBlockPool.h>
21 #include <v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h>
22 #include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
23 
24 using android::hardware::graphics::common::V1_0::BufferUsage;
25 
26 namespace android {
27 
28 // static
getBufferIdFromGraphicBlock(C2BlockPool & blockPool,const C2Block2D & block)29 std::optional<uint32_t> VideoFramePool::getBufferIdFromGraphicBlock(C2BlockPool& blockPool,
30                                                                     const C2Block2D& block) {
31     ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
32 
33     if (blockPool.getAllocatorId() == android::V4L2AllocatorId::V4L2_BUFFERPOOL) {
34         return C2VdaPooledBlockPool::getBufferIdFromGraphicBlock(block);
35     } else if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
36         C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool);
37         return bqPool->getBufferIdFromGraphicBlock(block);
38     }
39 
40     ALOGE("%s(): unknown allocator ID: %u", __func__, blockPool.getAllocatorId());
41     return std::nullopt;
42 }
43 
44 // static
requestNewBufferSet(C2BlockPool & blockPool,int32_t bufferCount,const ui::Size & size,uint32_t format,C2MemoryUsage usage)45 c2_status_t VideoFramePool::requestNewBufferSet(C2BlockPool& blockPool, int32_t bufferCount,
46                                                 const ui::Size& size, uint32_t format,
47                                                 C2MemoryUsage usage) {
48     ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
49 
50     if (blockPool.getAllocatorId() == android::V4L2AllocatorId::V4L2_BUFFERPOOL) {
51         C2VdaPooledBlockPool* bpPool = static_cast<C2VdaPooledBlockPool*>(&blockPool);
52         return bpPool->requestNewBufferSet(bufferCount);
53     } else if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
54         C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool);
55         return bqPool->requestNewBufferSet(bufferCount, size.width, size.height, format, usage);
56     }
57 
58     ALOGE("%s(): unknown allocator ID: %u", __func__, blockPool.getAllocatorId());
59     return C2_BAD_VALUE;
60 }
61 
62 // static
setNotifyBlockAvailableCb(C2BlockPool & blockPool,::base::OnceClosure cb)63 bool VideoFramePool::setNotifyBlockAvailableCb(C2BlockPool& blockPool, ::base::OnceClosure cb) {
64     ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
65 
66     if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
67         C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool);
68         return bqPool->setNotifyBlockAvailableCb(std::move(cb));
69     }
70     return false;
71 }
72 
73 // static
Create(std::shared_ptr<C2BlockPool> blockPool,const size_t numBuffers,const ui::Size & size,HalPixelFormat pixelFormat,bool isSecure,scoped_refptr<::base::SequencedTaskRunner> taskRunner)74 std::unique_ptr<VideoFramePool> VideoFramePool::Create(
75         std::shared_ptr<C2BlockPool> blockPool, const size_t numBuffers, const ui::Size& size,
76         HalPixelFormat pixelFormat, bool isSecure,
77         scoped_refptr<::base::SequencedTaskRunner> taskRunner) {
78     ALOG_ASSERT(blockPool != nullptr);
79 
80     uint64_t usage = static_cast<uint64_t>(BufferUsage::VIDEO_DECODER);
81     if (isSecure) {
82         usage |= C2MemoryUsage::READ_PROTECTED;
83     } else if (blockPool->getAllocatorId() == android::V4L2AllocatorId::V4L2_BUFFERPOOL) {
84         // CPU access to buffers is only required in byte buffer mode.
85         usage |= C2MemoryUsage::CPU_READ;
86     }
87     const C2MemoryUsage memoryUsage(usage);
88 
89     if (requestNewBufferSet(*blockPool, numBuffers, size, static_cast<uint32_t>(pixelFormat),
90                             memoryUsage) != C2_OK) {
91         return nullptr;
92     }
93 
94     std::unique_ptr<VideoFramePool> pool = ::base::WrapUnique(new VideoFramePool(
95             std::move(blockPool), size, pixelFormat, memoryUsage, std::move(taskRunner)));
96     if (!pool->initialize()) return nullptr;
97     return pool;
98 }
99 
VideoFramePool(std::shared_ptr<C2BlockPool> blockPool,const ui::Size & size,HalPixelFormat pixelFormat,C2MemoryUsage memoryUsage,scoped_refptr<::base::SequencedTaskRunner> taskRunner)100 VideoFramePool::VideoFramePool(std::shared_ptr<C2BlockPool> blockPool, const ui::Size& size,
101                                HalPixelFormat pixelFormat, C2MemoryUsage memoryUsage,
102                                scoped_refptr<::base::SequencedTaskRunner> taskRunner)
103       : mBlockPool(std::move(blockPool)),
104         mSize(size),
105         mPixelFormat(pixelFormat),
106         mMemoryUsage(memoryUsage),
107         mClientTaskRunner(std::move(taskRunner)) {
108     ALOGV("%s(size=%dx%d)", __func__, size.width, size.height);
109     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
110     DCHECK(mBlockPool);
111     DCHECK(mClientTaskRunner);
112 }
113 
initialize()114 bool VideoFramePool::initialize() {
115     if (!mFetchThread.Start()) {
116         ALOGE("Fetch thread failed to start.");
117         return false;
118     }
119     mFetchTaskRunner = mFetchThread.task_runner();
120 
121     mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
122     mFetchWeakThis = mFetchWeakThisFactory.GetWeakPtr();
123 
124     return true;
125 }
126 
~VideoFramePool()127 VideoFramePool::~VideoFramePool() {
128     ALOGV("%s()", __func__);
129     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
130 
131     mClientWeakThisFactory.InvalidateWeakPtrs();
132 
133     if (mFetchThread.IsRunning()) {
134         mFetchTaskRunner->PostTask(FROM_HERE,
135                                    ::base::BindOnce(&VideoFramePool::destroyTask, mFetchWeakThis));
136         mFetchThread.Stop();
137     }
138 }
139 
destroyTask()140 void VideoFramePool::destroyTask() {
141     ALOGV("%s()", __func__);
142     ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
143 
144     mFetchWeakThisFactory.InvalidateWeakPtrs();
145 }
146 
getVideoFrame(GetVideoFrameCB cb)147 bool VideoFramePool::getVideoFrame(GetVideoFrameCB cb) {
148     ALOGV("%s()", __func__);
149     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
150 
151     if (mOutputCb) {
152         return false;
153     }
154 
155     mOutputCb = std::move(cb);
156     mFetchTaskRunner->PostTask(
157             FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis));
158     return true;
159 }
160 
161 // static
getVideoFrameTaskThunk(scoped_refptr<::base::SequencedTaskRunner> taskRunner,std::optional<::base::WeakPtr<VideoFramePool>> weakPool)162 void VideoFramePool::getVideoFrameTaskThunk(
163         scoped_refptr<::base::SequencedTaskRunner> taskRunner,
164         std::optional<::base::WeakPtr<VideoFramePool>> weakPool) {
165     ALOGV("%s()", __func__);
166     ALOG_ASSERT(weakPool);
167 
168     taskRunner->PostTask(FROM_HERE,
169                          ::base::BindOnce(&VideoFramePool::getVideoFrameTask, *weakPool));
170 }
171 
getVideoFrameTask()172 void VideoFramePool::getVideoFrameTask() {
173     ALOGV("%s()", __func__);
174     ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
175 
176     // Variables used to exponential backoff retry when buffer fetching times out.
177     constexpr size_t kFetchRetryDelayInit = 64;    // Initial delay: 64us
178     constexpr size_t kFetchRetryDelayMax = 16384;  // Max delay: 16ms (1 frame at 60fps)
179     static size_t sNumRetries = 0;
180     static size_t sDelay = kFetchRetryDelayInit;
181 
182     std::shared_ptr<C2GraphicBlock> block;
183     c2_status_t err = mBlockPool->fetchGraphicBlock(
184             mSize.width, mSize.height, static_cast<uint32_t>(mPixelFormat), mMemoryUsage, &block);
185     if (err == C2_TIMED_OUT || err == C2_BLOCKING) {
186         if (setNotifyBlockAvailableCb(*mBlockPool,
187                                       ::base::BindOnce(&VideoFramePool::getVideoFrameTaskThunk,
188                                                        mFetchTaskRunner, mFetchWeakThis))) {
189             ALOGV("%s(): fetchGraphicBlock() timeout, waiting for block available.", __func__);
190         } else {
191             ALOGV("%s(): fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", __func__, sDelay,
192                   sNumRetries + 1);
193             mFetchTaskRunner->PostDelayedTask(
194                     FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis),
195                     ::base::TimeDelta::FromMicroseconds(sDelay));
196 
197             sDelay = std::min(sDelay * 2, kFetchRetryDelayMax);  // Exponential backoff
198             sNumRetries++;
199         }
200 
201         return;
202     }
203 
204     // Reset to the default value.
205     sNumRetries = 0;
206     sDelay = kFetchRetryDelayInit;
207 
208     std::optional<FrameWithBlockId> frameWithBlockId;
209     if (err == C2_OK) {
210         ALOG_ASSERT(block != nullptr);
211         std::optional<uint32_t> bufferId = getBufferIdFromGraphicBlock(*mBlockPool, *block);
212         std::unique_ptr<VideoFrame> frame = VideoFrame::Create(std::move(block));
213         // Only pass the frame + id pair if both have successfully been obtained.
214         // Otherwise exit the loop so a nullopt is passed to the client.
215         if (bufferId && frame) {
216             frameWithBlockId = std::make_pair(std::move(frame), *bufferId);
217         } else {
218             ALOGE("%s(): Failed to generate VideoFrame or get the buffer id.", __func__);
219         }
220     } else {
221         ALOGE("%s(): Failed to fetch block, err=%d", __func__, err);
222     }
223 
224     mClientTaskRunner->PostTask(
225             FROM_HERE, ::base::BindOnce(&VideoFramePool::onVideoFrameReady, mClientWeakThis,
226                                         std::move(frameWithBlockId)));
227 }
228 
onVideoFrameReady(std::optional<FrameWithBlockId> frameWithBlockId)229 void VideoFramePool::onVideoFrameReady(std::optional<FrameWithBlockId> frameWithBlockId) {
230     ALOGV("%s()", __func__);
231     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
232 
233     if (!frameWithBlockId) {
234         ALOGE("Failed to get GraphicBlock, abandoning all pending requests.");
235         mClientWeakThisFactory.InvalidateWeakPtrs();
236         mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
237     }
238 
239     ALOG_ASSERT(mOutputCb);
240     std::move(mOutputCb).Run(std::move(frameWithBlockId));
241 }
242 
243 }  // namespace android
244