1 /*
2 * Copyright (c) 2024 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 <fstream>
17 #include "hardware/imagecodec/image_codec.h"
18 #include "hardware/imagecodec/image_codec_log.h"
19 #include "image_log.h"
20
21 namespace OHOS::ImagePlugin {
22 using namespace std;
23
PrintAllBufferInfo()24 void ImageCodec::PrintAllBufferInfo()
25 {
26 HLOGI("------------INPUT-----------");
27 for (const BufferInfo& info : inputBufferPool_) {
28 HLOGI("inBufId = %{public}u, owner = %{public}s", info.bufferId, ToString(info.owner));
29 }
30 HLOGI("----------------------------");
31 HLOGI("------------OUTPUT----------");
32 for (const BufferInfo& info : outputBufferPool_) {
33 HLOGI("outBufId = %{public}u, owner = %{public}s", info.bufferId, ToString(info.owner));
34 }
35 HLOGI("----------------------------");
36 }
37
CountOwner(bool isInput)38 std::array<uint32_t, ImageCodec::OWNER_CNT> ImageCodec::CountOwner(bool isInput)
39 {
40 std::array<uint32_t, OWNER_CNT> arr;
41 arr.fill(0);
42 const vector<BufferInfo>& pool = isInput ? inputBufferPool_ : outputBufferPool_;
43 for (const BufferInfo &info : pool) {
44 arr[info.owner]++;
45 }
46 return arr;
47 }
48
ChangeOwner(BufferInfo & info,BufferOwner newOwner)49 void ImageCodec::ChangeOwner(BufferInfo& info, BufferOwner newOwner)
50 {
51 if (!debugMode_) {
52 info.owner = newOwner;
53 return;
54 }
55 BufferOwner oldOwner = info.owner;
56 const char* oldOwnerStr = ToString(oldOwner);
57 const char* newOwnerStr = ToString(newOwner);
58 const char* idStr = info.isInput ? "inBufId" : "outBufId";
59
60 // calculate hold time
61 auto now = chrono::steady_clock::now();
62 uint64_t holdUs = static_cast<uint64_t>(
63 chrono::duration_cast<chrono::microseconds>(now - info.lastOwnerChangeTime).count());
64 double holdMs = holdUs / US_TO_MS;
65 TotalCntAndCost& holdRecord = info.isInput ? inputHoldTimeRecord_[oldOwner][newOwner] :
66 outputHoldTimeRecord_[oldOwner][newOwner];
67 holdRecord.totalCnt++;
68 holdRecord.totalCostUs += holdUs;
69 double aveHoldMs = holdRecord.totalCostUs / US_TO_MS / holdRecord.totalCnt;
70
71 // now change owner
72 info.lastOwnerChangeTime = now;
73 info.owner = newOwner;
74 std::array<uint32_t, OWNER_CNT> arr = CountOwner(info.isInput);
75 HLOGI("%{public}s = %{public}u, after hold %{public}.1f ms (%{public}.1f ms), %{public}s -> %{public}s, "
76 "%{public}u/%{public}u/%{public}u",
77 idStr, info.bufferId, holdMs, aveHoldMs, oldOwnerStr, newOwnerStr,
78 arr[OWNED_BY_US], arr[OWNED_BY_USER], arr[OWNED_BY_OMX]);
79
80 if (info.isInput && oldOwner == OWNED_BY_US && newOwner == OWNED_BY_OMX) {
81 UpdateInputRecord(info, now);
82 }
83 if (!info.isInput && oldOwner == OWNED_BY_OMX && newOwner == OWNED_BY_US) {
84 UpdateOutputRecord(info, now);
85 }
86 }
87
UpdateInputRecord(const BufferInfo & info,std::chrono::time_point<std::chrono::steady_clock> now)88 void ImageCodec::UpdateInputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)
89 {
90 bool cond = !info.IsValidFrame();
91 CHECK_ERROR_RETURN(cond);
92 inTimeMap_[info.omxBuffer->pts] = now;
93 if (inTotalCnt_ == 0) {
94 firstInTime_ = now;
95 }
96 inTotalCnt_++;
97
98 uint64_t fromFirstInToNow = chrono::duration_cast<chrono::microseconds>(now - firstInTime_).count();
99 if (fromFirstInToNow == 0) {
100 HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x",
101 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag);
102 } else {
103 double inFps = inTotalCnt_ * US_TO_S / fromFirstInToNow;
104 HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x, in fps %{public}.2f",
105 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag, inFps);
106 }
107 }
108
UpdateOutputRecord(const BufferInfo & info,std::chrono::time_point<std::chrono::steady_clock> now)109 void ImageCodec::UpdateOutputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)
110 {
111 bool cond = !info.IsValidFrame();
112 CHECK_ERROR_RETURN(cond);
113 auto it = inTimeMap_.find(info.omxBuffer->pts);
114 cond = it == inTimeMap_.end();
115 CHECK_ERROR_RETURN(cond);
116 if (outRecord_.totalCnt == 0) {
117 firstOutTime_ = now;
118 }
119 outRecord_.totalCnt++;
120
121 uint64_t fromInToOut = chrono::duration_cast<chrono::microseconds>(now - it->second).count();
122 inTimeMap_.erase(it);
123 outRecord_.totalCostUs += fromInToOut;
124 double oneFrameCostMs = fromInToOut / US_TO_MS;
125 double averageCostMs = outRecord_.totalCostUs / US_TO_MS / outRecord_.totalCnt;
126
127 uint64_t fromFirstOutToNow = chrono::duration_cast<chrono::microseconds>(now - firstOutTime_).count();
128 if (fromFirstOutToNow == 0) {
129 HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x, "
130 "cost %{public}.2f ms (%{public}.2f ms)",
131 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag,
132 oneFrameCostMs, averageCostMs);
133 } else {
134 double outFps = outRecord_.totalCnt * US_TO_S / fromFirstOutToNow;
135 HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x, "
136 "cost %{public}.2f ms (%{public}.2f ms), out fps %{public}.2f",
137 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag,
138 oneFrameCostMs, averageCostMs, outFps);
139 }
140 }
141
IsValidFrame() const142 bool ImageCodec::BufferInfo::IsValidFrame() const
143 {
144 bool cond = omxBuffer->flag & OMX_BUFFERFLAG_EOS;
145 CHECK_ERROR_RETURN_RET(cond, false);
146 cond = omxBuffer->flag & OMX_BUFFERFLAG_CODECCONFIG;
147 CHECK_ERROR_RETURN_RET(cond, false);
148 cond = omxBuffer->filledLen == 0;
149 CHECK_ERROR_RETURN_RET(cond, false);
150 return true;
151 }
152
Dump(const string & prefix,bool dumpMode) const153 void ImageCodec::BufferInfo::Dump(const string& prefix, bool dumpMode) const
154 {
155 if (dumpMode && !isInput) {
156 Dump(prefix + "_Output");
157 }
158 }
159
Dump(const string & prefix) const160 void ImageCodec::BufferInfo::Dump(const string& prefix) const
161 {
162 if (surfaceBuffer) {
163 DumpSurfaceBuffer(prefix);
164 } else {
165 DumpLinearBuffer(prefix);
166 }
167 }
168
DumpSurfaceBuffer(const std::string & prefix) const169 void ImageCodec::BufferInfo::DumpSurfaceBuffer(const std::string& prefix) const
170 {
171 const char* va = reinterpret_cast<const char*>(surfaceBuffer->GetVirAddr());
172 if (va == nullptr) {
173 LOGW("surface buffer has null va");
174 return;
175 }
176 bool eos = (omxBuffer->flag & OMX_BUFFERFLAG_EOS);
177 bool cond = eos || omxBuffer->filledLen == 0;
178 CHECK_ERROR_RETURN(cond);
179 int w = surfaceBuffer->GetWidth();
180 int h = surfaceBuffer->GetHeight();
181 int alignedW = surfaceBuffer->GetStride();
182 uint32_t totalSize = surfaceBuffer->GetSize();
183 if (w <= 0 || h <= 0 || alignedW <= 0 || w > alignedW) {
184 LOGW("invalid buffer dimension");
185 return;
186 }
187 std::optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(
188 static_cast<GraphicPixelFormat>(surfaceBuffer->GetFormat()));
189 if (fmt == nullopt) {
190 LOGW("invalid fmt=%{public}d", surfaceBuffer->GetFormat());
191 return;
192 }
193
194 char name[128];
195 int ret = sprintf_s(name, sizeof(name), "%s/%s_%dx%d(%d)_fmt%s_pts%" PRId64 ".yuv",
196 DUMP_PATH, prefix.c_str(), w, h, alignedW, fmt->strFmt.c_str(), omxBuffer->pts);
197 if (ret > 0) {
198 ofstream ofs(name, ios::binary);
199 if (ofs.is_open()) {
200 ofs.write(va, totalSize);
201 } else {
202 LOGW("cannot open %{public}s", name);
203 }
204 }
205 // if we unmap here, flush cache will fail
206 }
207
DumpLinearBuffer(const string & prefix) const208 void ImageCodec::BufferInfo::DumpLinearBuffer(const string& prefix) const
209 {
210 if (imgCodecBuffer == nullptr) {
211 LOGW("invalid imgCodecBuffer");
212 return;
213 }
214 const char* va = reinterpret_cast<const char*>(imgCodecBuffer->GetAddr());
215 if (va == nullptr) {
216 LOGW("null va");
217 return;
218 }
219 bool eos = (omxBuffer->flag & OMX_BUFFERFLAG_EOS);
220 if (eos || omxBuffer->filledLen == 0) {
221 return;
222 }
223
224 char name[128];
225 int ret = 0;
226 if (isInput) {
227 ret = sprintf_s(name, sizeof(name), "%s/%s.bin", DUMP_PATH, prefix.c_str());
228 } else {
229 ret = sprintf_s(name, sizeof(name), "%s/%s_(%d)_pts%" PRId64 ".yuv",
230 DUMP_PATH, prefix.c_str(), imgCodecBuffer->GetStride(), omxBuffer->pts);
231 }
232 if (ret <= 0) {
233 LOGW("sprintf_s failed");
234 return;
235 }
236 std::ios_base::openmode mode = isInput ? (ios::binary | ios::app) : ios::binary;
237 ofstream ofs(name, mode);
238 if (ofs.is_open()) {
239 ofs.write(va, omxBuffer->filledLen);
240 } else {
241 LOGW("cannot open %{public}s", name);
242 }
243 }
244 } // namespace OHOS::ImagePlugin