1 /*
2 * Copyright (c) 2023 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 <sstream>
18 #include "hitrace_meter.h"
19 #include "hcodec.h"
20 #include "hcodec_log.h"
21 #include "hcodec_dfx.h"
22 #include "hcodec_utils.h"
23
24 namespace OHOS::MediaAVCodec {
25 using namespace std;
26
ScopedTrace(const std::string &value)27 ScopedTrace::ScopedTrace(const std::string &value)
28 {
29 StartTrace(HITRACE_TAG_ZMEDIA, value);
30 }
31
~ScopedTrace()32 ScopedTrace::~ScopedTrace()
33 {
34 FinishTrace(HITRACE_TAG_ZMEDIA);
35 }
36
FuncTracker(std::string value)37 FuncTracker::FuncTracker(std::string value) : value_(std::move(value))
38 {
39 PLOGI("%s >>", value_.c_str());
40 }
41
~FuncTracker()42 FuncTracker::~FuncTracker()
43 {
44 PLOGI("%s <<", value_.c_str());
45 }
46
OnPrintAllBufferOwner(const MsgInfo& msg)47 void HCodec::OnPrintAllBufferOwner(const MsgInfo& msg)
48 {
49 std::chrono::time_point<std::chrono::steady_clock> lastOwnerChangeTime;
50 msg.param->GetValue(KEY_LAST_OWNER_CHANGE_TIME, lastOwnerChangeTime);
51 if ((lastOwnerChangeTime == lastOwnerChangeTime_) &&
52 (circulateWarnPrintedTimes_ < MAX_CIRCULATE_WARN_TIMES)) {
53 HLOGW("buffer circulate stoped");
54 UpdateOwner();
55 PrintAllBufferInfo();
56 circulateWarnPrintedTimes_++;
57 }
58 ParamSP param = make_shared<ParamBundle>();
59 param->SetValue(KEY_LAST_OWNER_CHANGE_TIME, lastOwnerChangeTime_);
60 SendAsyncMsg(MsgWhat::PRINT_ALL_BUFFER_OWNER, param,
61 THREE_SECONDS_IN_US * (circulateWarnPrintedTimes_ + 1));
62 }
63
PrintAllBufferInfo()64 void HCodec::PrintAllBufferInfo()
65 {
66 auto now = chrono::steady_clock::now();
67 PrintAllBufferInfo(true, now);
68 PrintAllBufferInfo(false, now);
69 }
70
PrintAllBufferInfo(bool isInput, std::chrono::time_point<std::chrono::steady_clock> now)71 void HCodec::PrintAllBufferInfo(bool isInput, std::chrono::time_point<std::chrono::steady_clock> now)
72 {
73 const char* inOutStr = isInput ? " in" : "out";
74 bool eos = isInput ? inputPortEos_ : outputPortEos_;
75 uint64_t cnt = isInput ? inTotalCnt_ : outRecord_.totalCnt;
76 const std::array<int, OWNER_CNT>& arr = isInput ? inputOwner_ : outputOwner_;
77 const vector<BufferInfo>& pool = isInput ? inputBufferPool_ : outputBufferPool_;
78
79 std::stringstream s;
80 for (const BufferInfo& info : pool) {
81 int64_t holdMs = chrono::duration_cast<chrono::milliseconds>(now - info.lastOwnerChangeTime).count();
82 s << info.bufferId << ":" << ToString(info.owner) << "(" << holdMs << "), ";
83 }
84 HLOGI("%s: eos=%d, cnt=%" PRIu64 ", %d/%d/%d/%d, %s", inOutStr, eos, cnt,
85 arr[OWNED_BY_US], arr[OWNED_BY_USER], arr[OWNED_BY_OMX], arr[OWNED_BY_SURFACE], s.str().c_str());
86 }
87
OnGetHidumperInfo()88 std::string HCodec::OnGetHidumperInfo()
89 {
90 auto now = chrono::steady_clock::now();
91 std::stringstream s;
92 auto getbufferCapacity = [](const std::vector<BufferInfo> &pool) -> int32_t {
93 IF_TRUE_RETURN_VAL(pool.empty(), 0);
94 IF_TRUE_RETURN_VAL(pool.front().surfaceBuffer, pool.front().surfaceBuffer->GetSize());
95 IF_TRUE_RETURN_VAL(!(pool.front().avBuffer && pool.front().avBuffer->memory_), 0);
96 return pool.front().avBuffer->memory_->GetCapacity();
97 };
98
99 s << endl;
100 s << " " << compUniqueStr_ << "[" << currState_->GetName() << "]" << endl;
101 s << " " << "------------INPUT-----------" << endl;
102 s << " " << "eos:" << inputPortEos_ << ", etb:" << inTotalCnt_
103 << ", bufferCapacity:" << getbufferCapacity(inputBufferPool_) << endl;
104 for (const BufferInfo& info : inputBufferPool_) {
105 int64_t holdMs = chrono::duration_cast<chrono::milliseconds>(now - info.lastOwnerChangeTime).count();
106 s << " " << "inBufId = " << info.bufferId << ", owner = " << ToString(info.owner)
107 << ", holdMs = " << holdMs << endl;
108 }
109 s << " " << "----------------------------" << endl;
110 s << " " << "------------OUTPUT----------" << endl;
111 s << " " << "eos:" << outputPortEos_ << ", fbd:" << outRecord_.totalCnt
112 << ", bufferCapacity:" << getbufferCapacity(outputBufferPool_) << endl;
113 for (const BufferInfo& info : outputBufferPool_) {
114 int64_t holdMs = chrono::duration_cast<chrono::milliseconds>(now - info.lastOwnerChangeTime).count();
115 s << " " << "outBufId = " << info.bufferId << ", owner = " << ToString(info.owner)
116 << ", holdMs = " << holdMs << endl;
117 }
118 s << " " << "----------------------------" << endl;
119 return s.str();
120 }
121
UpdateOwner()122 void HCodec::UpdateOwner()
123 {
124 UpdateOwner(true);
125 UpdateOwner(false);
126 }
127
UpdateOwner(bool isInput)128 void HCodec::UpdateOwner(bool isInput)
129 {
130 std::array<int, OWNER_CNT>& arr = isInput ? inputOwner_ : outputOwner_;
131 const std::array<std::string, OWNER_CNT>& ownerStr = isInput ? inputOwnerStr_ : outputOwnerStr_;
132 const vector<BufferInfo>& pool = isInput ? inputBufferPool_ : outputBufferPool_;
133
134 arr.fill(0);
135 for (const BufferInfo &info : pool) {
136 arr[info.owner]++;
137 }
138 for (uint32_t owner = 0; owner < static_cast<uint32_t>(OWNER_CNT); owner++) {
139 CountTrace(HITRACE_TAG_ZMEDIA, ownerStr[owner], arr[owner]);
140 }
141 }
142
ReduceOwner(bool isInput, BufferOwner owner)143 void HCodec::ReduceOwner(bool isInput, BufferOwner owner)
144 {
145 std::array<int, OWNER_CNT>& arr = isInput ? inputOwner_ : outputOwner_;
146 const std::array<std::string, OWNER_CNT>& ownerStr = isInput ? inputOwnerStr_ : outputOwnerStr_;
147 arr[owner]--;
148 CountTrace(HITRACE_TAG_ZMEDIA, ownerStr[owner], arr[owner]);
149 }
150
PrintStatistic(bool isInput, std::chrono::time_point<std::chrono::steady_clock> now)151 void HCodec::PrintStatistic(bool isInput, std::chrono::time_point<std::chrono::steady_clock> now)
152 {
153 int64_t fromFirstToNow = chrono::duration_cast<chrono::microseconds>(
154 now - (isInput ? firstInTime_ : firstOutTime_)).count();
155 if (fromFirstToNow == 0) {
156 return;
157 }
158 double fps = PRINT_PER_FRAME * US_TO_S / fromFirstToNow;
159 const std::array<int, OWNER_CNT>& arr = isInput ? inputOwner_ : outputOwner_;
160 double aveHoldMs[OWNER_CNT];
161 for (uint32_t owner = 0; owner < static_cast<uint32_t>(OWNER_CNT); owner++) {
162 TotalCntAndCost& holdRecord = isInput ? inputHoldTimeRecord_[owner] : outputHoldTimeRecord_[owner];
163 aveHoldMs[owner] = (holdRecord.totalCnt == 0) ? -1 : (holdRecord.totalCostUs / US_TO_MS / holdRecord.totalCnt);
164 }
165 HLOGI("%s:%.0f; %d/%d/%d/%d, %.0f/%.0f/%.0f/%.0f", (isInput ? " in" : "out"), fps,
166 arr[OWNED_BY_US], arr[OWNED_BY_USER], arr[OWNED_BY_OMX], arr[OWNED_BY_SURFACE],
167 aveHoldMs[OWNED_BY_US], aveHoldMs[OWNED_BY_USER], aveHoldMs[OWNED_BY_OMX], aveHoldMs[OWNED_BY_SURFACE]);
168 }
169
ChangeOwner(BufferInfo& info, BufferOwner newOwner)170 void HCodec::ChangeOwner(BufferInfo& info, BufferOwner newOwner)
171 {
172 debugMode_ ? ChangeOwnerDebug(info, newOwner) : ChangeOwnerNormal(info, newOwner);
173 }
174
ChangeOwnerNormal(BufferInfo& info, BufferOwner newOwner)175 void HCodec::ChangeOwnerNormal(BufferInfo& info, BufferOwner newOwner)
176 {
177 std::array<int, OWNER_CNT>& arr = info.isInput ? inputOwner_ : outputOwner_;
178 const std::array<std::string, OWNER_CNT>& ownerStr = info.isInput ? inputOwnerStr_ : outputOwnerStr_;
179
180 BufferOwner oldOwner = info.owner;
181 auto now = chrono::steady_clock::now();
182 lastOwnerChangeTime_ = now;
183 circulateWarnPrintedTimes_ = 0;
184 arr[oldOwner]--;
185 arr[newOwner]++;
186 CountTrace(HITRACE_TAG_ZMEDIA, ownerStr[oldOwner], arr[oldOwner]);
187 CountTrace(HITRACE_TAG_ZMEDIA, ownerStr[newOwner], arr[newOwner]);
188
189 uint64_t holdUs = static_cast<uint64_t>(
190 chrono::duration_cast<chrono::microseconds>(now - info.lastOwnerChangeTime).count());
191 TotalCntAndCost& holdRecord = info.isInput ? inputHoldTimeRecord_[oldOwner] :
192 outputHoldTimeRecord_[oldOwner];
193 holdRecord.totalCnt++;
194 holdRecord.totalCostUs += holdUs;
195
196 // now change owner
197 info.lastOwnerChangeTime = now;
198 info.owner = newOwner;
199
200 if (info.isInput && oldOwner == OWNED_BY_US && newOwner == OWNED_BY_OMX) {
201 if (inTotalCnt_ == 0) {
202 firstInTime_ = now;
203 }
204 inTotalCnt_++;
205 if (inTotalCnt_ % PRINT_PER_FRAME == 0) {
206 PrintStatistic(info.isInput, now);
207 firstInTime_ = now;
208 inputHoldTimeRecord_.fill({0, 0});
209 }
210 }
211 if (!info.isInput && oldOwner == OWNED_BY_OMX && newOwner == OWNED_BY_US) {
212 if (outRecord_.totalCnt == 0) {
213 firstOutTime_ = now;
214 }
215 outRecord_.totalCnt++;
216 if (outRecord_.totalCnt % PRINT_PER_FRAME == 0) {
217 PrintStatistic(info.isInput, now);
218 firstOutTime_ = now;
219 outputHoldTimeRecord_.fill({0, 0});
220 }
221 }
222 }
223
ChangeOwnerDebug(BufferInfo& info, BufferOwner newOwner)224 void HCodec::ChangeOwnerDebug(BufferInfo& info, BufferOwner newOwner)
225 {
226 std::array<int, OWNER_CNT>& arr = info.isInput ? inputOwner_ : outputOwner_;
227 const std::array<std::string, OWNER_CNT>& ownerStr = info.isInput ? inputOwnerStr_ : outputOwnerStr_;
228
229 BufferOwner oldOwner = info.owner;
230 auto now = chrono::steady_clock::now();
231 lastOwnerChangeTime_ = now;
232 circulateWarnPrintedTimes_ = 0;
233 arr[oldOwner]--;
234 arr[newOwner]++;
235 CountTrace(HITRACE_TAG_ZMEDIA, ownerStr[oldOwner], arr[oldOwner]);
236 CountTrace(HITRACE_TAG_ZMEDIA, ownerStr[newOwner], arr[newOwner]);
237
238 const char* oldOwnerStr = ToString(oldOwner);
239 const char* newOwnerStr = ToString(newOwner);
240 const char* idStr = info.isInput ? "inBufId" : "outBufId";
241
242 uint64_t holdUs = static_cast<uint64_t>
243 (chrono::duration_cast<chrono::microseconds>(now - info.lastOwnerChangeTime).count());
244 double holdMs = holdUs / US_TO_MS;
245 TotalCntAndCost& holdRecord = info.isInput ? inputHoldTimeRecord_[oldOwner] :
246 outputHoldTimeRecord_[oldOwner];
247 holdRecord.totalCnt++;
248 holdRecord.totalCostUs += holdUs;
249 double aveHoldMs = holdRecord.totalCostUs / US_TO_MS / holdRecord.totalCnt;
250
251 // now change owner
252 info.lastOwnerChangeTime = now;
253 info.owner = newOwner;
254 HLOGI("%s = %u, after hold %.1f ms (%.1f ms), %s -> %s, %d/%d/%d/%d",
255 idStr, info.bufferId, holdMs, aveHoldMs, oldOwnerStr, newOwnerStr,
256 arr[OWNED_BY_US], arr[OWNED_BY_USER], arr[OWNED_BY_OMX], arr[OWNED_BY_SURFACE]);
257
258 if (info.isInput && oldOwner == OWNED_BY_US && newOwner == OWNED_BY_OMX) {
259 UpdateInputRecord(info, now);
260 }
261 if (!info.isInput && oldOwner == OWNED_BY_OMX && newOwner == OWNED_BY_US) {
262 UpdateOutputRecord(info, now);
263 }
264 }
265
UpdateInputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)266 void HCodec::UpdateInputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)
267 {
268 if (!info.IsValidFrame()) {
269 return;
270 }
271 inTimeMap_[info.omxBuffer->pts] = now;
272 if (inTotalCnt_ == 0) {
273 firstInTime_ = now;
274 }
275 inTotalCnt_++;
276
277 uint64_t fromFirstInToNow = static_cast<uint64_t>
278 (chrono::duration_cast<chrono::microseconds>(now - firstInTime_).count());
279 if (fromFirstInToNow == 0) {
280 HLOGI("pts = %" PRId64 ", len = %u, flags = 0x%x",
281 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag);
282 } else {
283 double inFps = inTotalCnt_ * US_TO_S / fromFirstInToNow;
284 HLOGI("pts = %" PRId64 ", len = %u, flags = 0x%x, in fps %.2f",
285 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag, inFps);
286 }
287 }
288
UpdateOutputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)289 void HCodec::UpdateOutputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)
290 {
291 if (!info.IsValidFrame()) {
292 return;
293 }
294 auto it = inTimeMap_.find(info.omxBuffer->pts);
295 if (it == inTimeMap_.end()) {
296 return;
297 }
298 if (outRecord_.totalCnt == 0) {
299 firstOutTime_ = now;
300 }
301 outRecord_.totalCnt++;
302
303 uint64_t fromInToOut = static_cast<uint64_t>
304 (chrono::duration_cast<chrono::microseconds>(now - it->second).count());
305 inTimeMap_.erase(it);
306 outRecord_.totalCostUs += fromInToOut;
307 double oneFrameCostMs = fromInToOut / US_TO_MS;
308 double averageCostMs = outRecord_.totalCostUs / US_TO_MS / outRecord_.totalCnt;
309
310 uint64_t fromFirstOutToNow = static_cast<uint64_t>
311 (chrono::duration_cast<chrono::microseconds>(now - firstOutTime_).count());
312 if (fromFirstOutToNow == 0) {
313 HLOGI("pts = %" PRId64 ", len = %u, flags = 0x%x, "
314 "cost %.2f ms (%.2f ms)",
315 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag,
316 oneFrameCostMs, averageCostMs);
317 } else {
318 double outFps = outRecord_.totalCnt * US_TO_S / fromFirstOutToNow;
319 HLOGI("pts = %" PRId64 ", len = %u, flags = 0x%x, "
320 "cost %.2f ms (%.2f ms), out fps %.2f",
321 info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag,
322 oneFrameCostMs, averageCostMs, outFps);
323 }
324 }
325
IsValidFrame() const326 bool HCodec::BufferInfo::IsValidFrame() const
327 {
328 if (omxBuffer->flag & OMX_BUFFERFLAG_EOS) {
329 return false;
330 }
331 if (omxBuffer->flag & OMX_BUFFERFLAG_CODECCONFIG) {
332 return false;
333 }
334 if (omxBuffer->filledLen == 0) {
335 return false;
336 }
337 return true;
338 }
339
340 #ifdef BUILD_ENG_VERSION
Dump(const string& prefix, uint64_t cnt, DumpMode dumpMode, bool isEncoder) const341 void HCodec::BufferInfo::Dump(const string& prefix, uint64_t cnt, DumpMode dumpMode, bool isEncoder) const
342 {
343 if (isInput) {
344 if (((dumpMode & DUMP_ENCODER_INPUT) && isEncoder) ||
345 ((dumpMode & DUMP_DECODER_INPUT) && !isEncoder)) {
346 Dump(prefix + "_Input", cnt);
347 }
348 } else {
349 if (((dumpMode & DUMP_ENCODER_OUTPUT) && isEncoder) ||
350 ((dumpMode & DUMP_DECODER_OUTPUT) && !isEncoder)) {
351 Dump(prefix + "_Output", cnt);
352 }
353 }
354 }
355
Dump(const string& prefix, uint64_t cnt) const356 void HCodec::BufferInfo::Dump(const string& prefix, uint64_t cnt) const
357 {
358 if (surfaceBuffer) {
359 DumpSurfaceBuffer(prefix, cnt);
360 } else {
361 DumpLinearBuffer(prefix);
362 }
363 }
364
DumpSurfaceBuffer(const std::string& prefix, uint64_t cnt) const365 void HCodec::BufferInfo::DumpSurfaceBuffer(const std::string& prefix, uint64_t cnt) const
366 {
367 if (omxBuffer->filledLen == 0) {
368 return;
369 }
370 const char* va = reinterpret_cast<const char*>(surfaceBuffer->GetVirAddr());
371 IF_TRUE_RETURN_VOID_WITH_MSG(va == nullptr, "null va");
372 int w = surfaceBuffer->GetWidth();
373 int h = surfaceBuffer->GetHeight();
374 int byteStride = surfaceBuffer->GetStride();
375 IF_TRUE_RETURN_VOID_WITH_MSG(byteStride == 0, "stride 0");
376 int alignedH = h;
377 uint32_t totalSize = surfaceBuffer->GetSize();
378 uint32_t seq = surfaceBuffer->GetSeqNum();
379 GraphicPixelFormat graphicFmt = static_cast<GraphicPixelFormat>(surfaceBuffer->GetFormat());
380 std::optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(graphicFmt);
381 IF_TRUE_RETURN_VOID_WITH_MSG(!fmt.has_value(), "unknown fmt %d", graphicFmt);
382
383 string suffix;
384 bool dumpAsVideo = true;
385 DecideDumpInfo(alignedH, totalSize, suffix, dumpAsVideo);
386
387 char name[128];
388 int ret = 0;
389 if (dumpAsVideo) {
390 ret = sprintf_s(name, sizeof(name), "%s/%s_%dx%d(%dx%d)_fmt%s.%s",
391 DUMP_PATH, prefix.c_str(), w, h, byteStride, alignedH,
392 fmt->strFmt.c_str(), suffix.c_str());
393 } else {
394 ret = sprintf_s(name, sizeof(name), "%s/%s_%" PRIu64 "_%dx%d(%d)_fmt%s_pts%" PRId64 "_seq%u.%s",
395 DUMP_PATH, prefix.c_str(), cnt, w, h, byteStride,
396 fmt->strFmt.c_str(), omxBuffer->pts, seq, suffix.c_str());
397 }
398 if (ret > 0) {
399 ofstream ofs(name, ios::binary | ios::app);
400 if (ofs.is_open()) {
401 ofs.write(va, totalSize);
402 } else {
403 LOGW("cannot open %s", name);
404 }
405 }
406 // if we unmap here, flush cache will fail
407 }
408
DecideDumpInfo(int& alignedH, uint32_t& totalSize, string& suffix, bool& dumpAsVideo) const409 void HCodec::BufferInfo::DecideDumpInfo(int& alignedH, uint32_t& totalSize, string& suffix, bool& dumpAsVideo) const
410 {
411 int h = surfaceBuffer->GetHeight();
412 int byteStride = surfaceBuffer->GetStride();
413 GraphicPixelFormat fmt = static_cast<GraphicPixelFormat>(surfaceBuffer->GetFormat());
414 switch (fmt) {
415 case GRAPHIC_PIXEL_FMT_YCBCR_420_P:
416 case GRAPHIC_PIXEL_FMT_YCRCB_420_SP:
417 case GRAPHIC_PIXEL_FMT_YCBCR_420_SP:
418 case GRAPHIC_PIXEL_FMT_YCBCR_P010:
419 case GRAPHIC_PIXEL_FMT_YCRCB_P010: {
420 OH_NativeBuffer_Planes *planes = nullptr;
421 GSError err = surfaceBuffer->GetPlanesInfo(reinterpret_cast<void**>(&planes));
422 if (err != GSERROR_OK || planes == nullptr) { // compressed
423 suffix = "bin";
424 dumpAsVideo = false;
425 return;
426 }
427 alignedH = static_cast<int32_t>(static_cast<int64_t>(planes->planes[1].offset) / byteStride);
428 totalSize = GetYuv420Size(byteStride, alignedH);
429 suffix = "yuv";
430 break;
431 }
432 case GRAPHIC_PIXEL_FMT_RGBA_8888: {
433 totalSize = static_cast<uint32_t>(byteStride * h);
434 suffix = "rgba";
435 break;
436 }
437 default: {
438 suffix = "bin";
439 dumpAsVideo = false;
440 break;
441 }
442 }
443 }
444
DumpLinearBuffer(const string& prefix) const445 void HCodec::BufferInfo::DumpLinearBuffer(const string& prefix) const
446 {
447 if (omxBuffer->filledLen == 0) {
448 return;
449 }
450 if (avBuffer == nullptr || avBuffer->memory_ == nullptr) {
451 LOGW("invalid avbuffer");
452 return;
453 }
454 const char* va = reinterpret_cast<const char*>(avBuffer->memory_->GetAddr());
455 if (va == nullptr) {
456 LOGW("null va");
457 return;
458 }
459
460 char name[128];
461 int ret = sprintf_s(name, sizeof(name), "%s/%s.bin", DUMP_PATH, prefix.c_str());
462 if (ret <= 0) {
463 LOGW("sprintf_s failed");
464 return;
465 }
466 ofstream ofs(name, ios::binary | ios::app);
467 if (ofs.is_open()) {
468 ofs.write(va, omxBuffer->filledLen);
469 } else {
470 LOGW("cannot open %s", name);
471 }
472 }
473 #endif // BUILD_ENG_VERSION
474 }