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