• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &param);
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, &param);
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 }