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