1 /*
2 * Copyright (c) 2022-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define HST_LOG_TAG "AsyncMode"
17
18 #include "async_mode.h"
19 #include "common/plugin_utils.h"
20 #include "utils/dump_buffer.h"
21 #include "osal/utils/util.h"
22 #if !defined(OHOS_LITE) && defined(VIDEO_SUPPORT)
23 #include "plugin/common/surface_memory.h"
24 #endif
25
26 namespace {
27 constexpr uint32_t DEFAULT_TRY_DECODE_TIME = 5;
28 constexpr uint32_t DEFAULT_TRY_RENDER_TIME = 5;
29 }
30
31 namespace OHOS {
32 namespace Media {
33 namespace Pipeline {
AsyncMode(std::string name)34 AsyncMode::AsyncMode(std::string name) : CodecMode(std::move(name))
35 {
36 MEDIA_LOG_I(PUBLIC_LOG_S " ThreadMode: ASYNC", codecName_.c_str());
37 }
38
~AsyncMode()39 AsyncMode::~AsyncMode()
40 {
41 MEDIA_LOG_D("Async mode dtor called");
42 }
43
Release()44 ErrorCode AsyncMode::Release()
45 {
46 MEDIA_LOG_I("AsyncMode Release start.");
47 stopped_ = true;
48
49 // 先停止线程 然后释放bufferQ 如果顺序反过来 可能导致线程访问已经释放的锁
50 if (handleFrameTask_) {
51 handleFrameTask_->Stop();
52 handleFrameTask_.reset();
53 }
54 if (pushTask_ != nullptr) {
55 pushTask_->Stop();
56 pushTask_.reset();
57 }
58 if (inBufQue_) {
59 inBufQue_->SetActive(false);
60 inBufQue_.reset();
61 }
62 {
63 OSAL::ScopedLock l(renderMutex_);
64 while (!outBufQue_.empty()) {
65 outBufQue_.pop();
66 }
67 }
68 MEDIA_LOG_I("AsyncMode Release end.");
69 return ErrorCode::SUCCESS;
70 }
71
Configure()72 ErrorCode AsyncMode::Configure()
73 {
74 stopped_ = false;
75 FALSE_LOG_MSG_W(QueueAllBufferInPoolToPluginLocked() == ErrorCode::SUCCESS,
76 "Can not configure all output buffers to plugin before start.");
77 FAIL_RETURN(CodecMode::Configure());
78 if (handleFrameTask_) {
79 handleFrameTask_->Start();
80 }
81 if (pushTask_) {
82 pushTask_->Start();
83 }
84 return ErrorCode::SUCCESS;
85 }
86
PushData(const std::string & inPort,const AVBufferPtr & buffer,int64_t offset)87 ErrorCode AsyncMode::PushData(const std::string &inPort, const AVBufferPtr& buffer, int64_t offset)
88 {
89 DUMP_BUFFER2LOG("AsyncMode in", buffer, offset);
90 inBufQue_->Push(buffer);
91 return ErrorCode::SUCCESS;
92 }
93
Stop()94 ErrorCode AsyncMode::Stop()
95 {
96 MEDIA_LOG_I("AsyncMode stop start.");
97 stopped_ = true;
98 pushTask_->Stop();
99 inBufQue_->SetActive(false);
100 {
101 OSAL::ScopedLock l(renderMutex_);
102 while (!outBufQue_.empty()) {
103 outBufQue_.pop();
104 }
105 }
106 if (handleFrameTask_) {
107 handleFrameTask_->Stop();
108 }
109 outBufPool_.reset();
110 MEDIA_LOG_I("AsyncMode stop end.");
111 return ErrorCode::SUCCESS;
112 }
113
FlushStart()114 void AsyncMode::FlushStart()
115 {
116 MEDIA_LOG_D("AsyncMode FlushStart entered.");
117 stopped_ = true; // thread will pause, should not enter endless loop
118 if (inBufQue_) {
119 inBufQue_->SetActive(false);
120 }
121 if (handleFrameTask_) {
122 handleFrameTask_->Pause();
123 }
124 if (pushTask_) {
125 pushTask_->Pause();
126 }
127 MEDIA_LOG_D("AsyncMode FlushStart exit.");
128 }
129
FlushEnd()130 void AsyncMode::FlushEnd()
131 {
132 MEDIA_LOG_I("AsyncMode FlushEnd entered");
133 stopped_ = false;
134 if (inBufQue_) {
135 inBufQue_->SetActive(true);
136 }
137 if (handleFrameTask_) {
138 handleFrameTask_->Start();
139 }
140 if (pushTask_) {
141 pushTask_->Start();
142 }
143 if (plugin_) {
144 QueueAllBufferInPoolToPluginLocked();
145 }
146 }
147
HandleFrame()148 ErrorCode AsyncMode::HandleFrame()
149 {
150 MEDIA_LOG_DD("AsyncMode handle frame called");
151 auto oneBuffer = inBufQue_->Pop(200); // timeout 200 ms
152 if (oneBuffer == nullptr) {
153 MEDIA_LOG_DD("decoder find nullptr in esBufferQ");
154 return ErrorCode::ERROR_INVALID_PARAMETER_VALUE;
155 }
156 Plugin::Status status = Plugin::Status::OK;
157 do {
158 DUMP_BUFFER2LOG("AsyncMode QueueInput to Plugin", oneBuffer, -1);
159 status = plugin_->QueueInputBuffer(oneBuffer, 0);
160 if (status == Plugin::Status::OK || status == Plugin::Status::END_OF_STREAM
161 || status != Plugin::Status::ERROR_AGAIN || stopped_) {
162 break;
163 }
164 MEDIA_LOG_DD("Send data to plugin error: " PUBLIC_LOG_D32, status);
165 OSAL::SleepFor(DEFAULT_TRY_DECODE_TIME);
166 } while (true);
167 MEDIA_LOG_DD("Async handle frame finished");
168 return TranslatePluginStatus(status);
169 }
170
FinishFrame()171 ErrorCode AsyncMode::FinishFrame()
172 {
173 MEDIA_LOG_DD("FinishFrame begin");
174 bool isRendered = false;
175 {
176 OSAL::ScopedLock l(renderMutex_);
177 std::shared_ptr<AVBuffer> frameBuffer = nullptr;
178 if (!outBufQue_.empty()) {
179 frameBuffer = outBufQue_.front();
180 }
181 if (frameBuffer != nullptr) {
182 auto oPort = outPorts_[0];
183 if (oPort->GetWorkMode() == WorkMode::PUSH) {
184 DUMP_BUFFER2LOG("AsyncMode PushData to Sink", frameBuffer, -1);
185 oPort->PushData(frameBuffer, -1);
186 isRendered = true;
187 outBufQue_.pop();
188 } else {
189 MEDIA_LOG_W("decoder out port works in pull mode");
190 return ErrorCode::ERROR_INVALID_OPERATION;
191 }
192 frameBuffer.reset();
193 }
194 }
195 auto newOutBuffer = outBufPool_->AllocateBufferNonBlocking();
196 if (CheckBufferValidity(newOutBuffer) == ErrorCode::SUCCESS) {
197 newOutBuffer->Reset();
198 plugin_->QueueOutputBuffer(newOutBuffer, 0);
199 }
200 if (!isRendered) {
201 OSAL::SleepFor(DEFAULT_TRY_RENDER_TIME);
202 }
203 MEDIA_LOG_DD("AsyncMode finish frame success");
204 return ErrorCode::SUCCESS;
205 }
206
OnOutputBufferDone(const std::shared_ptr<Plugin::Buffer> & buffer)207 void AsyncMode::OnOutputBufferDone(const std::shared_ptr<Plugin::Buffer>& buffer)
208 {
209 OSAL::ScopedLock l(renderMutex_);
210 outBufQue_.push(buffer);
211 }
212
Prepare()213 ErrorCode AsyncMode::Prepare()
214 {
215 MEDIA_LOG_I("AsyncMode prepare called.");
216 if (!inBufQue_) {
217 inBufQue_ = std::make_shared<BlockingQueue<AVBufferPtr>>("asyncFilterInBufQue", GetInBufferPoolSize());
218 } else {
219 inBufQue_->SetActive(true);
220 }
221 if (!handleFrameTask_) {
222 handleFrameTask_ = std::make_shared<OSAL::Task>(codecName_ + "AsyncHandleFrame");
223 handleFrameTask_->RegisterHandler([this] { (void)HandleFrame(); });
224 }
225 if (!pushTask_) {
226 pushTask_ = std::make_shared<OSAL::Task>(codecName_ + "AsyncPush");
227 pushTask_->RegisterHandler([this] { (void)FinishFrame(); });
228 }
229 return ErrorCode::SUCCESS;
230 }
231
QueueAllBufferInPoolToPluginLocked()232 ErrorCode AsyncMode::QueueAllBufferInPoolToPluginLocked()
233 {
234 ErrorCode err = ErrorCode::SUCCESS;
235 while (!outBufPool_->Empty()) {
236 auto buf = outBufPool_->AllocateBuffer();
237 if (CheckBufferValidity(buf) != ErrorCode::SUCCESS) {
238 MEDIA_LOG_W("cannot allocate buffer in buffer pool");
239 continue;
240 }
241 err = TranslatePluginStatus(plugin_->QueueOutputBuffer(buf, -1));
242 if (err != ErrorCode::SUCCESS) {
243 MEDIA_LOG_W("Queue output buffer error, plugin doesn't support queue all out buffers.");
244 break;
245 }
246 }
247 return err;
248 }
249
CheckBufferValidity(std::shared_ptr<AVBuffer> & buffer)250 ErrorCode AsyncMode::CheckBufferValidity(std::shared_ptr<AVBuffer>& buffer)
251 {
252 if (buffer == nullptr) {
253 return ErrorCode::ERROR_INVALID_PARAMETER_VALUE;
254 }
255 auto memory = buffer->GetMemory();
256 if (memory == nullptr) {
257 return ErrorCode::ERROR_INVALID_PARAMETER_VALUE;
258 }
259 #if !defined(OHOS_LITE) && defined(VIDEO_SUPPORT)
260 if (memory->GetMemoryType() == Plugin::MemoryType::SURFACE_BUFFER) {
261 std::shared_ptr<Plugin::SurfaceMemory> surfaceMemory =
262 Plugin::ReinterpretPointerCast<Plugin::SurfaceMemory>(memory);
263
264 // trigger surface memory to request surface buffer again when it is surface buffer
265 FALSE_RETURN_V_MSG_E(surfaceMemory->GetSurfaceBuffer() != nullptr, ErrorCode::ERROR_NO_MEMORY,
266 "get surface buffer fail");
267 }
268 #endif
269 return ErrorCode::SUCCESS;
270 }
271 } // namespace Pipeline
272 } // namespace Media
273 } // namespace OHOS