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