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::BUFFER_RECYCLE, nullptr);
117 }
118
NotifyMemoryWriteBack()119 int32_t HCodec::NotifyMemoryWriteBack()
120 {
121 SCOPED_TRACE();
122 FUNC_TRACKER();
123 return DoSyncCall(MsgWhat::BUFFER_WRITEBACK, nullptr);
124 }
125
NotifySuspend()126 int32_t HCodec::NotifySuspend()
127 {
128 SCOPED_TRACE();
129 FUNC_TRACKER();
130 DoSyncCall(MsgWhat::SUSPEND, nullptr);
131 return AVCS_ERR_OK;
132 }
133
NotifyResume()134 int32_t HCodec::NotifyResume()
135 {
136 SCOPED_TRACE();
137 FUNC_TRACKER();
138 DoSyncCall(MsgWhat::RESUME, nullptr);
139 return AVCS_ERR_OK;
140 }
141
RecordBufferStatus(OMX_DIRTYPE portIndex,uint32_t bufferId,BufferOwner nextOwner)142 void HCodec::RecordBufferStatus(OMX_DIRTYPE portIndex, uint32_t bufferId, BufferOwner nextOwner)
143 {
144 auto bufferInfo = FindBufferInfoByID(portIndex, bufferId);
145 HLOGI("port[%d] buffer[%u] next owner[%s]", portIndex, bufferId, ToString(nextOwner));
146 if (bufferInfo != nullptr) {
147 bufferInfo->nextStepOwner = nextOwner;
148 }
149 }
150
SwapOutBufferByPortIndex(OMX_DIRTYPE portIndex)151 int32_t HDecoder::SwapOutBufferByPortIndex(OMX_DIRTYPE portIndex)
152 {
153 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
154
155 for (BufferInfo& info : pool) {
156 if (CanSwapOut(portIndex, info) == false) {
157 HLOGD("buf[%u] can't freeze owner[%d] swaped out[%d]", info.bufferId, info.owner, info.hasSwapedOut);
158 continue;
159 }
160 int fd = (portIndex == OMX_DirInput) ? info.avBuffer->memory_->GetFileDescriptor() :
161 info.surfaceBuffer->GetFileDescriptor();
162 if (DmaSwaper::GetInstance().SwapOutDma(pid_, fd) != AVCS_ERR_OK) {
163 HLOGE("prot[%d] bufferId[%d], fd[%d] freeze failed", portIndex, info.bufferId, fd);
164 return ActiveBuffers();
165 }
166 info.hasSwapedOut = true;
167 }
168 return AVCS_ERR_OK;
169 }
170
SwapInBufferByPortIndex(OMX_DIRTYPE portIndex)171 int32_t HDecoder::SwapInBufferByPortIndex(OMX_DIRTYPE portIndex)
172 {
173 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
174
175 for (BufferInfo& info : pool) {
176 if (info.hasSwapedOut == true) {
177 int fd = (portIndex == OMX_DirInput) ? info.avBuffer->memory_->GetFileDescriptor() :
178 info.surfaceBuffer->GetFileDescriptor();
179 if (DmaSwaper::GetInstance().SwapInDma(pid_, fd) != AVCS_ERR_OK) {
180 HLOGE("buffer fd[%d] swap in error", fd);
181 return AVCS_ERR_UNKNOWN;
182 }
183 info.hasSwapedOut = false;
184 }
185 }
186 return AVCS_ERR_OK;
187 }
188
CanSwapOut(OMX_DIRTYPE portIndex,BufferInfo & info)189 bool HDecoder::CanSwapOut(OMX_DIRTYPE portIndex, BufferInfo& info)
190 {
191 if (portIndex == OMX_DirInput) {
192 if (info.owner == BufferOwner::OWNED_BY_USER || info.hasSwapedOut) {
193 return false;
194 }
195 }
196 if (portIndex == OMX_DirOutput) {
197 if (currSurface_.surface_) {
198 return !(info.owner == BufferOwner::OWNED_BY_SURFACE ||
199 info.hasSwapedOut || info.surfaceBuffer == nullptr);
200 } else {
201 return !(info.owner == BufferOwner::OWNED_BY_SURFACE || info.surfaceBuffer == nullptr ||
202 info.owner == BufferOwner::OWNED_BY_USER || info.hasSwapedOut);
203 }
204 }
205 return true;
206 }
207
FreezeBuffers()208 int32_t HDecoder::FreezeBuffers()
209 {
210 if (isSecure_) {
211 return AVCS_ERR_OK;
212 }
213 OMX_CONFIG_BOOLEANTYPE param {};
214 InitOMXParam(param);
215 param.bEnabled = OMX_TRUE;
216 if (!SetParameter(OMX_IndexParamBufferRecycle, param)) {
217 HLOGE("failed to set decoder to background to freeze buffers");
218 return AVCS_ERR_UNKNOWN;
219 }
220 if (SwapOutBufferByPortIndex(OMX_DirInput) != AVCS_ERR_OK) {
221 return AVCS_ERR_UNKNOWN;
222 }
223 if (SwapOutBufferByPortIndex(OMX_DirOutput) != AVCS_ERR_OK) {
224 return AVCS_ERR_UNKNOWN;
225 }
226 HLOGI("freeze buffers success");
227 return AVCS_ERR_OK;
228 }
229
DecreaseFreq()230 int32_t HDecoder::DecreaseFreq()
231 {
232 OMX_CONFIG_BOOLEANTYPE param {};
233 InitOMXParam(param);
234 param.bEnabled = OMX_TRUE;
235 if (!SetParameter(OMX_IndexParamFreqUpdate, param)) {
236 HLOGE("failed to set decoder to background to decrease freq");
237 return AVCS_ERR_UNKNOWN;
238 }
239 HLOGI("Decrease Freq success");
240 return AVCS_ERR_OK;
241 }
242
ActiveBuffers()243 int32_t HDecoder::ActiveBuffers()
244 {
245 if (SwapInBufferByPortIndex(OMX_DirInput) != AVCS_ERR_OK) {
246 return AVCS_ERR_UNKNOWN;
247 }
248 if (SwapInBufferByPortIndex(OMX_DirOutput) != AVCS_ERR_OK) {
249 return AVCS_ERR_UNKNOWN;
250 }
251 OMX_CONFIG_BOOLEANTYPE param {};
252 InitOMXParam(param);
253 param.bEnabled = OMX_FALSE;
254 if (!SetParameter(OMX_IndexParamBufferRecycle, param)) {
255 HLOGE("failed to set OMX_IndexParamBufferRecycle");
256 return AVCS_ERR_UNKNOWN;
257 }
258 HLOGI("buffers active success");
259 return AVCS_ERR_OK;
260 }
261
RecoverFreq()262 int32_t HDecoder::RecoverFreq()
263 {
264 OMX_CONFIG_BOOLEANTYPE param {};
265 InitOMXParam(param);
266 param.bEnabled = OMX_FALSE;
267 if (!SetParameter(OMX_IndexParamFreqUpdate, param)) {
268 HLOGE("failed to set OMX_IndexParamFreqUpdate");
269 return AVCS_ERR_UNKNOWN;
270 }
271 HLOGI("Recover Freq success");
272 return AVCS_ERR_OK;
273 }
274
SubmitBuffersToNextOwner()275 void HDecoder::SubmitBuffersToNextOwner()
276 {
277 while (!inputBufIdQueueToOmx_.empty()) {
278 uint32_t bufferId = inputBufIdQueueToOmx_.front();
279 inputBufIdQueueToOmx_.pop();
280 auto bufInfo = FindBufferInfoByID(OMX_DirInput, bufferId);
281 if (bufInfo != nullptr) {
282 OnQueueInputBuffer(RESUBMIT_BUFFER, bufInfo);
283 }
284 }
285 for (BufferInfo& info : inputBufferPool_) {
286 if (info.nextStepOwner == BufferOwner::OWNED_BY_OMX) {
287 HLOGI("bufferId = %d, input buffer next owner is omx", info.bufferId);
288 } else if (info.nextStepOwner == BufferOwner::OWNED_BY_USER) {
289 HLOGI("bufferId = %d, input buffer next owner is user", info.bufferId);
290 if (!inputPortEos_) {
291 NotifyUserToFillThisInBuffer(info);
292 }
293 }
294 info.nextStepOwner = BufferOwner::OWNER_CNT;
295 }
296
297 for (BufferInfo& info : outputBufferPool_) {
298 if (info.nextStepOwner == BufferOwner::OWNED_BY_OMX) {
299 NotifyOmxToFillThisOutBuffer(info);
300 HLOGI("bufferId = %d, output buffer next owner is omx", info.bufferId);
301 } else if (info.nextStepOwner == BufferOwner::OWNED_BY_USER) {
302 optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, info.bufferId);
303 if (!idx.has_value()) {
304 return;
305 }
306 HLOGI("bufferId = %d, output buffer next owner is user", info.bufferId);
307 OnOMXFillBufferDone(RESUBMIT_BUFFER, info, idx.value());
308 } else if (info.nextStepOwner == BufferOwner::OWNED_BY_SURFACE) {
309 if (info.omxBuffer->filledLen != 0) {
310 NotifySurfaceToRenderOutputBuffer(info);
311 }
312 SurfaceModeSubmitBuffer();
313 }
314 info.nextStepOwner = BufferOwner::OWNER_CNT;
315 }
316 }
317
OnBufferRecycle(const MsgInfo & info)318 void HCodec::RunningState::OnBufferRecycle(const MsgInfo &info)
319 {
320 if (codec_->disableDmaSwap_) {
321 SLOGI("hcodec dma swap has been disabled!");
322 ReplyErrorCode(info.id, AVCS_ERR_OK);
323 return;
324 }
325 SLOGI("begin to buffer recycle");
326 int32_t errCode = codec_->FreezeBuffers();
327 if (errCode == AVCS_ERR_OK) {
328 codec_->ChangeStateTo(codec_->frozenState_);
329 }
330 ReplyErrorCode(info.id, errCode);
331 }
332
333 /**************************** FrozenState Start ********************************/
OnMsgReceived(const MsgInfo & info)334 void HCodec::FrozenState::OnMsgReceived(const MsgInfo &info)
335 {
336 switch (info.type) {
337 case MsgWhat::FORCE_SHUTDOWN: {
338 codec_->ChangeStateTo(codec_->stoppingState_);
339 return;
340 }
341 case MsgWhat::SET_PARAMETERS:
342 OnSetParameters(info);
343 return;
344 case MsgWhat::QUEUE_INPUT_BUFFER: {
345 uint32_t bufferId = 0;
346 (void)info.param->GetValue(BUFFER_ID, bufferId);
347 codec_->OnQueueInputBuffer(info, inputMode_);
348 codec_->RecordBufferStatus(OMX_DirInput, bufferId, OWNED_BY_OMX);
349 codec_->inputBufIdQueueToOmx_.push(bufferId);
350 return;
351 }
352 case MsgWhat::RENDER_OUTPUT_BUFFER: {
353 uint32_t bufferId = 0;
354 (void)info.param->GetValue(BUFFER_ID, bufferId);
355 codec_->OnRenderOutputBuffer(info, outputMode_);
356 codec_->RecordBufferStatus(OMX_DirOutput, bufferId, OWNED_BY_OMX);
357 return;
358 }
359 case MsgWhat::RELEASE_OUTPUT_BUFFER: {
360 uint32_t bufferId = 0;
361 (void)info.param->GetValue(BUFFER_ID, bufferId);
362 codec_->OnReleaseOutputBuffer(info, outputMode_);
363 codec_->RecordBufferStatus(OMX_DirOutput, bufferId, OWNED_BY_OMX);
364 return;
365 }
366 case MsgWhat::OMX_EMPTY_BUFFER_DONE: {
367 uint32_t bufferId = 0;
368 (void)info.param->GetValue(BUFFER_ID, bufferId);
369 codec_->OnOMXEmptyBufferDone(bufferId, inputMode_);
370 codec_->RecordBufferStatus(OMX_DirInput, bufferId, OWNED_BY_USER);
371 return;
372 }
373 case MsgWhat::OMX_FILL_BUFFER_DONE: {
374 OmxCodecBuffer omxBuffer;
375 (void)info.param->GetValue("omxBuffer", omxBuffer);
376 codec_->OnOMXFillBufferDone(omxBuffer, outputMode_);
377 codec_->RecordBufferStatus(OMX_DirOutput, omxBuffer.bufferId, OWNED_BY_OMX);
378 return;
379 }
380 case MsgWhat::BUFFER_WRITEBACK: {
381 OnBufferWriteback(info);
382 return;
383 }
384 case MsgWhat::SUSPEND:{
385 OnSuspend(info);
386 break;
387 }
388 case MsgWhat::RESUME:{
389 OnResume(info);
390 return;
391 }
392 case MsgWhat::GET_BUFFER_FROM_SURFACE: {
393 SLOGD("defer GET_BUFFER_FROM_SURFACE");
394 codec_->DeferMessage(info);
395 return;
396 }
397 case MsgWhat::CODEC_EVENT: {
398 codec_->DeferMessage(info);
399 SLOGI("deferring codec event");
400 return;
401 }
402 default: {
403 BaseState::OnMsgReceived(info);
404 }
405 }
406 }
407
OnBufferWriteback(const MsgInfo & info)408 void HCodec::FrozenState::OnBufferWriteback(const MsgInfo &info)
409 {
410 SLOGI("begin to write back buffers");
411 int32_t errCode = codec_->ActiveBuffers();
412 if (errCode == AVCS_ERR_OK) {
413 codec_->SubmitBuffersToNextOwner();
414 codec_->ChangeStateTo(codec_->runningState_);
415 }
416 ReplyErrorCode(info.id, errCode);
417 }
418
OnShutDown(const MsgInfo & info)419 void HCodec::FrozenState::OnShutDown(const MsgInfo &info)
420 {
421 (void)codec_->ActiveBuffers();
422 codec_->isShutDownFromRunning_ = true;
423 codec_->notifyCallerAfterShutdownComplete_ = true;
424 codec_->keepComponentAllocated_ = (info.type == MsgWhat::STOP);
425 codec_->isBufferCirculating_ = false;
426 codec_->PrintAllBufferInfo();
427 SLOGI("receive %s msg, begin to set omx to idle", info.type == MsgWhat::RELEASE ? "release" : "stop");
428 int32_t ret = codec_->compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {});
429 if (ret == HDF_SUCCESS) {
430 codec_->ReplyToSyncMsgLater(info);
431 codec_->ChangeStateTo(codec_->stoppingState_);
432 } else {
433 SLOGE("set omx to idle failed, ret=%d", ret);
434 ReplyErrorCode(info.id, AVCS_ERR_UNKNOWN);
435 }
436 }
437
438 /**************************** FrozenState End ********************************/
439 }