• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "hcodec.h"
18 #include "hcodec_log.h"
19 #include "hcodec_utils.h"
20 
21 namespace OHOS::MediaAVCodec {
22 using namespace std;
23 
PrintAllBufferInfo()24 void HCodec::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, HCodec::OWNER_CNT> HCodec::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 HCodec::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 = 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/%{public}u",
76           idStr, info.bufferId, holdMs, aveHoldMs, oldOwnerStr, newOwnerStr,
77           arr[OWNED_BY_US], arr[OWNED_BY_USER], arr[OWNED_BY_OMX], arr[OWNED_BY_SURFACE]);
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 HCodec::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 HCodec::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 HCodec::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,DumpMode dumpMode,bool isEncoder) const158 void HCodec::BufferInfo::Dump(const string& prefix, DumpMode dumpMode, bool isEncoder) const
159 {
160     if (isInput) {
161         if (((dumpMode & DUMP_ENCODER_INPUT) && isEncoder) ||
162             ((dumpMode & DUMP_DECODER_INPUT) && !isEncoder)) {
163             Dump(prefix + "_Input");
164         }
165     } else {
166         if (((dumpMode & DUMP_ENCODER_OUTPUT) && isEncoder) ||
167             ((dumpMode & DUMP_DECODER_OUTPUT) && !isEncoder)) {
168             Dump(prefix + "_Output");
169         }
170     }
171 }
172 
Dump(const string & prefix) const173 void HCodec::BufferInfo::Dump(const string& prefix) const
174 {
175     if (surfaceBuffer) {
176         DumpSurfaceBuffer(prefix);
177     } else {
178         DumpLinearBuffer(prefix);
179     }
180 }
181 
DumpSurfaceBuffer(const std::string & prefix) const182 void HCodec::BufferInfo::DumpSurfaceBuffer(const std::string& prefix) const
183 {
184     const char* va = reinterpret_cast<const char*>(surfaceBuffer->GetVirAddr());
185     if (va == nullptr) {
186         LOGW("surface buffer has null va");
187         return;
188     }
189     bool eos = (omxBuffer->flag & OMX_BUFFERFLAG_EOS);
190     if (eos || omxBuffer->filledLen == 0) {
191         return;
192     }
193     int w = surfaceBuffer->GetWidth();
194     int h = surfaceBuffer->GetHeight();
195     int alignedW = surfaceBuffer->GetStride();
196     uint32_t totalSize = surfaceBuffer->GetSize();
197     if (w <= 0 || h <= 0 || alignedW <= 0 || w > alignedW) {
198         LOGW("invalid buffer dimension");
199         return;
200     }
201     std::optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(
202         static_cast<GraphicPixelFormat>(surfaceBuffer->GetFormat()));
203     if (fmt == nullopt) {
204         LOGW("invalid fmt=%{public}d", surfaceBuffer->GetFormat());
205         return;
206     }
207     optional<uint32_t> assumeAlignedH;
208     string suffix;
209     bool dumpAsVideo = true;  // we could only save it as individual image if we don't know aligned height
210     DecideDumpInfo(assumeAlignedH, suffix, dumpAsVideo);
211 
212     char name[128];
213     int ret = 0;
214     if (dumpAsVideo) {
215         ret = sprintf_s(name, sizeof(name), "%s/%s_%dx%d(%dx%d)_fmt%s.%s",
216                         DUMP_PATH, prefix.c_str(), w, h, alignedW, assumeAlignedH.value_or(h),
217                         fmt->strFmt.c_str(), suffix.c_str());
218     } else {
219         ret = sprintf_s(name, sizeof(name), "%s/%s_%dx%d(%d)_fmt%s_pts%" PRId64 ".%s",
220                         DUMP_PATH, prefix.c_str(), w, h, alignedW, fmt->strFmt.c_str(), omxBuffer->pts, suffix.c_str());
221     }
222     if (ret > 0) {
223         ofstream ofs(name, ios::binary | ios::app);
224         if (ofs.is_open()) {
225             ofs.write(va, totalSize);
226         } else {
227             LOGW("cannot open %{public}s", name);
228         }
229     }
230     // if we unmap here, flush cache will fail
231 }
232 
DecideDumpInfo(optional<uint32_t> & assumeAlignedH,string & suffix,bool & dumpAsVideo) const233 void HCodec::BufferInfo::DecideDumpInfo(optional<uint32_t>& assumeAlignedH, string& suffix, bool& dumpAsVideo) const
234 {
235     int h = surfaceBuffer->GetHeight();
236     int alignedW = surfaceBuffer->GetStride();
237     if (alignedW <= 0) {
238         return;
239     }
240     uint32_t totalSize = surfaceBuffer->GetSize();
241     GraphicPixelFormat fmt = static_cast<GraphicPixelFormat>(surfaceBuffer->GetFormat());
242     switch (fmt) {
243         case GRAPHIC_PIXEL_FMT_YCBCR_420_P:
244         case GRAPHIC_PIXEL_FMT_YCRCB_420_SP:
245         case GRAPHIC_PIXEL_FMT_YCBCR_420_SP:
246         case GRAPHIC_PIXEL_FMT_YCBCR_P010:
247         case GRAPHIC_PIXEL_FMT_YCRCB_P010: {
248             suffix = "yuv";
249             if (GetYuv420Size(alignedW, h) == totalSize) {
250                 break;
251             }
252             uint32_t alignedH = totalSize * 2 / 3 / alignedW; // 2 bytes per pixel for UV, 3 bytes per pixel for YUV
253             if (GetYuv420Size(alignedW, alignedH) == totalSize) {
254                 dumpAsVideo = true;
255                 assumeAlignedH = alignedH;
256             } else {
257                 dumpAsVideo = false;
258             }
259             break;
260         }
261         case GRAPHIC_PIXEL_FMT_RGBA_8888: {
262             suffix = "rgba";
263             if (static_cast<uint32_t>(alignedW * h) != totalSize) {
264                 dumpAsVideo = false;
265             }
266             break;
267         }
268         default: {
269             suffix = "bin";
270             dumpAsVideo = false;
271             break;
272         }
273     }
274 }
275 
DumpLinearBuffer(const string & prefix) const276 void HCodec::BufferInfo::DumpLinearBuffer(const string& prefix) const
277 {
278     if (avBuffer == nullptr || avBuffer->memory_ == nullptr) {
279         LOGW("invalid avbuffer");
280         return;
281     }
282     const char* va = reinterpret_cast<const char*>(avBuffer->memory_->GetAddr());
283     if (va == nullptr) {
284         LOGW("null va");
285         return;
286     }
287     bool eos = (omxBuffer->flag & OMX_BUFFERFLAG_EOS);
288     if (eos || omxBuffer->filledLen == 0) {
289         return;
290     }
291 
292     char name[128];
293     int ret = sprintf_s(name, sizeof(name), "%s/%s.bin", DUMP_PATH, prefix.c_str());
294     if (ret <= 0) {
295         LOGW("sprintf_s failed");
296         return;
297     }
298     ofstream ofs(name, ios::binary | ios::app);
299     if (ofs.is_open()) {
300         ofs.write(va, omxBuffer->filledLen);
301     } else {
302         LOGW("cannot open %{public}s", name);
303     }
304 }
305 }