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