• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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