1 /*
2 * Copyright (c) 2025 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 #include <vector>
17 #include <algorithm>
18 #include <thread>
19 #include <fcntl.h>
20 #include <string>
21 #include <sys/ioctl.h>
22 #include <sys/mman.h>
23 #include "codec_omx_ext.h"
24 #include "hcodec_list.h"
25 #include "hencoder.h"
26 #include "hdecoder.h"
27 #include "hitrace_meter.h"
28 #include "hcodec_log.h"
29 #include "hcodec_dfx.h"
30 #include "hcodec_utils.h"
31
32 namespace OHOS::MediaAVCodec {
33 using namespace std;
34 using namespace CodecHDI;
35 using namespace Media;
36
37 #define DMA_DEVICE_FILE "/dev/dma_reclaim"
38 #define DMA_BUF_RECLAIM_IOC_MAGIC 'd'
39 #define DMA_BUF_RECLAIM_FD \
40 _IOWR(DMA_BUF_RECLAIM_IOC_MAGIC, 0x07, int)
41 #define DMA_BUF_RESUME_FD \
42 _IOWR(DMA_BUF_RECLAIM_IOC_MAGIC, 0x08, int)
43
44 struct DmaBufIoctlSwPara {
45 pid_t pid;
46 unsigned long ino;
47 unsigned int fd;
48 };
49
50 class DmaSwaper {
51 public:
SwapOutDma(pid_t pid,int bufFd)52 int32_t SwapOutDma(pid_t pid, int bufFd)
53 {
54 if (reclaimDriverFd_ <= 0) {
55 return AVCS_ERR_UNKNOWN;
56 }
57 DmaBufIoctlSwPara param {
58 .pid = pid,
59 .ino = 0,
60 .fd = bufFd
61 };
62 return ioctl(reclaimDriverFd_, DMA_BUF_RECLAIM_FD, ¶m);
63 }
64
SwapInDma(pid_t pid,int bufFd)65 int32_t SwapInDma(pid_t pid, int bufFd)
66 {
67 if (reclaimDriverFd_ <= 0) {
68 return AVCS_ERR_UNKNOWN;
69 }
70 DmaBufIoctlSwPara param {
71 .pid = pid,
72 .ino = 0,
73 .fd = bufFd
74 };
75 return ioctl(reclaimDriverFd_, DMA_BUF_RESUME_FD, ¶m);
76 }
77
GetInstance()78 static DmaSwaper& GetInstance()
79 {
80 static DmaSwaper swaper;
81 return swaper;
82 }
83 private:
DmaSwaper()84 DmaSwaper()
85 {
86 if (reclaimDriverFd_ > 0) {
87 LOGE("already initialized!");
88 return;
89 }
90 reclaimDriverFd_ = open(DMA_DEVICE_FILE, O_RDWR | O_CLOEXEC | O_NONBLOCK);
91 if (reclaimDriverFd_ <= 0) {
92 LOGE("fail to open device");
93 }
94 }
95
~DmaSwaper()96 ~DmaSwaper()
97 {
98 if (reclaimDriverFd_ <= 0) {
99 LOGE("invalid fd!");
100 return;
101 }
102 close(reclaimDriverFd_);
103 reclaimDriverFd_ = -1;
104 }
105
106 DmaSwaper(const DmaSwaper &dmaSwaper) = delete;
107 const DmaSwaper &operator=(const DmaSwaper &dmaSwaper) = delete;
108 int reclaimDriverFd_ = -1;
109 };
110
111
NotifyMemoryRecycle()112 int32_t HCodec::NotifyMemoryRecycle()
113 {
114 SCOPED_TRACE();
115 FUNC_TRACKER();
116 return DoSyncCall(MsgWhat::FREEZE, nullptr);
117 }
118
NotifyMemoryWriteBack()119 int32_t HCodec::NotifyMemoryWriteBack()
120 {
121 SCOPED_TRACE();
122 FUNC_TRACKER();
123 return DoSyncCall(MsgWhat::ACTIVE, nullptr);
124 }
125
RecordBufferStatus(OMX_DIRTYPE portIndex,uint32_t bufferId,BufferOwner nextOwner)126 void HCodec::RecordBufferStatus(OMX_DIRTYPE portIndex, uint32_t bufferId, BufferOwner nextOwner)
127 {
128 auto bufferInfo = FindBufferInfoByID(portIndex, bufferId);
129 HLOGI("port[%d] buffer[%u] next owner[%s]", portIndex, bufferId, ToString(nextOwner));
130 if (bufferInfo != nullptr) {
131 bufferInfo->nextStepOwner = nextOwner;
132 }
133 }
134
SwapOutBufferByPortIndex(OMX_DIRTYPE portIndex)135 int32_t HDecoder::SwapOutBufferByPortIndex(OMX_DIRTYPE portIndex)
136 {
137 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
138
139 for (BufferInfo& info : pool) {
140 if (CanSwapOut(portIndex, info) == false) {
141 HLOGD("buf[%u] can't freeze owner[%d] swaped out[%d]", info.bufferId, info.owner, info.hasSwapedOut);
142 continue;
143 }
144 int fd = (portIndex == OMX_DirInput) ? info.avBuffer->memory_->GetFileDescriptor() :
145 info.surfaceBuffer->GetFileDescriptor();
146 if (DmaSwaper::GetInstance().SwapOutDma(pid_, fd) != AVCS_ERR_OK) {
147 HLOGE("prot[%d] bufferId[%d], fd[%d] freeze failed", portIndex, info.bufferId, fd);
148 return ActiveBuffers();
149 }
150 info.hasSwapedOut = true;
151 }
152 return AVCS_ERR_OK;
153 }
154
SwapInBufferByPortIndex(OMX_DIRTYPE portIndex)155 int32_t HDecoder::SwapInBufferByPortIndex(OMX_DIRTYPE portIndex)
156 {
157 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
158
159 for (BufferInfo& info : pool) {
160 if (info.hasSwapedOut == true) {
161 int fd = (portIndex == OMX_DirInput) ? info.avBuffer->memory_->GetFileDescriptor() :
162 info.surfaceBuffer->GetFileDescriptor();
163 if (DmaSwaper::GetInstance().SwapInDma(pid_, fd) != AVCS_ERR_OK) {
164 HLOGE("buffer fd[%d] swap in error", fd);
165 return AVCS_ERR_UNKNOWN;
166 }
167 info.hasSwapedOut = false;
168 }
169 }
170 return AVCS_ERR_OK;
171 }
172
CanSwapOut(OMX_DIRTYPE portIndex,BufferInfo & info)173 bool HDecoder::CanSwapOut(OMX_DIRTYPE portIndex, BufferInfo& info)
174 {
175 if (portIndex == OMX_DirInput) {
176 if (info.owner == BufferOwner::OWNED_BY_USER || info.hasSwapedOut) {
177 return false;
178 }
179 }
180 if (portIndex == OMX_DirOutput) {
181 if (currSurface_.surface_) {
182 return !(info.owner == BufferOwner::OWNED_BY_SURFACE ||
183 info.hasSwapedOut || info.surfaceBuffer == nullptr);
184 } else {
185 return !(info.owner == BufferOwner::OWNED_BY_SURFACE || info.surfaceBuffer == nullptr ||
186 info.owner == BufferOwner::OWNED_BY_USER || info.hasSwapedOut);
187 }
188 }
189 return true;
190 }
191
FreezeBuffers()192 int32_t HDecoder::FreezeBuffers()
193 {
194 if (isSecure_) {
195 return AVCS_ERR_OK;
196 }
197 OMX_CONFIG_BOOLEANTYPE param {};
198 InitOMXParam(param);
199 param.bEnabled = OMX_TRUE;
200 if (!SetParameter(OMX_IndexParamSwitchGround, param)) {
201 HLOGE("failed to set decoder to background");
202 return AVCS_ERR_UNKNOWN;
203 }
204 if (SwapOutBufferByPortIndex(OMX_DirInput) != AVCS_ERR_OK) {
205 return AVCS_ERR_UNKNOWN;
206 }
207 if (SwapOutBufferByPortIndex(OMX_DirOutput) != AVCS_ERR_OK) {
208 return AVCS_ERR_UNKNOWN;
209 }
210 HLOGI("freeze buffers success");
211 return AVCS_ERR_OK;
212 }
213
ActiveBuffers()214 int32_t HDecoder::ActiveBuffers()
215 {
216 if (SwapInBufferByPortIndex(OMX_DirInput) != AVCS_ERR_OK) {
217 return AVCS_ERR_UNKNOWN;
218 }
219 if (SwapInBufferByPortIndex(OMX_DirOutput) != AVCS_ERR_OK) {
220 return AVCS_ERR_UNKNOWN;
221 }
222 OMX_CONFIG_BOOLEANTYPE param {};
223 InitOMXParam(param);
224 param.bEnabled = OMX_FALSE;
225 if (!SetParameter(OMX_IndexParamSwitchGround, param)) {
226 HLOGE("failed to set OMX_IndexParamSwitchGround");
227 return AVCS_ERR_UNKNOWN;
228 }
229 HLOGI("buffers active success");
230 return AVCS_ERR_OK;
231 }
232
SubmitBuffersToNextOwner()233 void HDecoder::SubmitBuffersToNextOwner()
234 {
235 for (BufferInfo& info : inputBufferPool_) {
236 if (info.nextStepOwner == BufferOwner::OWNED_BY_OMX) {
237 HLOGI("bufferId = %d, input buffer next owner is omx", info.bufferId);
238 OnQueueInputBuffer(RESUBMIT_BUFFER, &info);
239 } else if (info.nextStepOwner == BufferOwner::OWNED_BY_USER) {
240 HLOGI("bufferId = %d, input buffer next owner is user", info.bufferId);
241 if (!inputPortEos_) {
242 NotifyUserToFillThisInBuffer(info);
243 }
244 }
245 info.nextStepOwner = BufferOwner::OWNER_CNT;
246 }
247
248 for (BufferInfo& info : outputBufferPool_) {
249 if (info.nextStepOwner == BufferOwner::OWNED_BY_OMX) {
250 NotifyOmxToFillThisOutBuffer(info);
251 HLOGI("bufferId = %d, output buffer next owner is omx", info.bufferId);
252 } else if (info.nextStepOwner == BufferOwner::OWNED_BY_USER) {
253 optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, info.bufferId);
254 if (!idx.has_value()) {
255 return;
256 }
257 HLOGI("bufferId = %d, output buffer next owner is user", info.bufferId);
258 OnOMXFillBufferDone(RESUBMIT_BUFFER, info, idx.value());
259 } else if (info.nextStepOwner == BufferOwner::OWNED_BY_SURFACE) {
260 if (info.omxBuffer->filledLen != 0) {
261 NotifySurfaceToRenderOutputBuffer(info);
262 }
263 SurfaceModeSubmitBuffer();
264 }
265 info.nextStepOwner = BufferOwner::OWNER_CNT;
266 }
267 }
268
OnFreeze(const MsgInfo & info)269 void HCodec::RunningState::OnFreeze(const MsgInfo &info)
270 {
271 if (codec_->disableDmaSwap_) {
272 SLOGI("hcodec dma swap has been disabled!");
273 ReplyErrorCode(info.id, AVCS_ERR_OK);
274 return;
275 }
276 SLOGI("begin to freeze this codec");
277 int32_t errCode = codec_->FreezeBuffers();
278 if (errCode == AVCS_ERR_OK) {
279 codec_->ChangeStateTo(codec_->frozenState_);
280 }
281 ReplyErrorCode(info.id, errCode);
282 }
283
284 /**************************** FrozenState Start ********************************/
OnMsgReceived(const MsgInfo & info)285 void HCodec::FrozenState::OnMsgReceived(const MsgInfo &info)
286 {
287 switch (info.type) {
288 case MsgWhat::FORCE_SHUTDOWN: {
289 codec_->ChangeStateTo(codec_->stoppingState_);
290 return;
291 }
292 case MsgWhat::SET_PARAMETERS:
293 OnSetParameters(info);
294 return;
295 case MsgWhat::QUEUE_INPUT_BUFFER: {
296 uint32_t bufferId = 0;
297 (void)info.param->GetValue(BUFFER_ID, bufferId);
298 codec_->OnQueueInputBuffer(info, inputMode_);
299 codec_->RecordBufferStatus(OMX_DirInput, bufferId, OWNED_BY_OMX);
300 return;
301 }
302 case MsgWhat::RENDER_OUTPUT_BUFFER: {
303 uint32_t bufferId = 0;
304 (void)info.param->GetValue(BUFFER_ID, bufferId);
305 codec_->OnRenderOutputBuffer(info, outputMode_);
306 codec_->RecordBufferStatus(OMX_DirOutput, bufferId, OWNED_BY_OMX);
307 return;
308 }
309 case MsgWhat::RELEASE_OUTPUT_BUFFER: {
310 uint32_t bufferId = 0;
311 (void)info.param->GetValue(BUFFER_ID, bufferId);
312 codec_->OnReleaseOutputBuffer(info, outputMode_);
313 codec_->RecordBufferStatus(OMX_DirOutput, bufferId, OWNED_BY_OMX);
314 return;
315 }
316 case MsgWhat::OMX_EMPTY_BUFFER_DONE: {
317 uint32_t bufferId = 0;
318 (void)info.param->GetValue(BUFFER_ID, bufferId);
319 codec_->OnOMXEmptyBufferDone(bufferId, inputMode_);
320 codec_->RecordBufferStatus(OMX_DirInput, bufferId, OWNED_BY_USER);
321 return;
322 }
323 case MsgWhat::OMX_FILL_BUFFER_DONE: {
324 OmxCodecBuffer omxBuffer;
325 (void)info.param->GetValue("omxBuffer", omxBuffer);
326 codec_->OnOMXFillBufferDone(omxBuffer, outputMode_);
327 codec_->RecordBufferStatus(OMX_DirOutput, omxBuffer.bufferId, OWNED_BY_OMX);
328 return;
329 }
330 case MsgWhat::ACTIVE: {
331 OnActive(info);
332 return;
333 }
334 case MsgWhat::GET_BUFFER_FROM_SURFACE: {
335 SLOGD("defer GET_BUFFER_FROM_SURFACE");
336 codec_->DeferMessage(info);
337 return;
338 }
339 case MsgWhat::CODEC_EVENT: {
340 codec_->DeferMessage(info);
341 SLOGI("deferring codec event");
342 return;
343 }
344 default: {
345 BaseState::OnMsgReceived(info);
346 }
347 }
348 }
349
OnActive(const MsgInfo & info)350 void HCodec::FrozenState::OnActive(const MsgInfo &info)
351 {
352 SLOGI("begin to active this codec");
353 int32_t errCode = codec_->ActiveBuffers();
354 if (errCode == AVCS_ERR_OK) {
355 codec_->SubmitBuffersToNextOwner();
356 codec_->ChangeStateTo(codec_->runningState_);
357 }
358 ReplyErrorCode(info.id, errCode);
359 }
360
OnShutDown(const MsgInfo & info)361 void HCodec::FrozenState::OnShutDown(const MsgInfo &info)
362 {
363 codec_->isShutDownFromRunning_ = true;
364 codec_->notifyCallerAfterShutdownComplete_ = true;
365 codec_->keepComponentAllocated_ = (info.type == MsgWhat::STOP);
366 codec_->isBufferCirculating_ = false;
367 codec_->PrintAllBufferInfo();
368 SLOGI("receive %s msg, begin to set omx to idle", info.type == MsgWhat::RELEASE ? "release" : "stop");
369 int32_t ret = codec_->compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {});
370 if (ret == HDF_SUCCESS) {
371 codec_->ReplyToSyncMsgLater(info);
372 codec_->ChangeStateTo(codec_->stoppingState_);
373 } else {
374 SLOGE("set omx to idle failed, ret=%d", ret);
375 ReplyErrorCode(info.id, AVCS_ERR_UNKNOWN);
376 }
377 }
378
379 /**************************** FrozenState End ********************************/
380 }