1 /*
2 * Copyright (c) 2022 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 "fps_controller_process.h"
17
18 #include "dcamera_utils_tools.h"
19 #include "distributed_camera_errno.h"
20 #include "distributed_hardware_log.h"
21
22 namespace OHOS {
23 namespace DistributedHardware {
~FpsControllerProcess()24 FpsControllerProcess::~FpsControllerProcess()
25 {
26 if (isFpsControllerProcess_) {
27 DHLOGD("~DecodeDataProcess : ReleaseProcessNode.");
28 ReleaseProcessNode();
29 }
30 }
31
InitNode(const VideoConfigParams & sourceConfig,const VideoConfigParams & targetConfig,VideoConfigParams & processedConfig)32 int32_t FpsControllerProcess::InitNode(const VideoConfigParams& sourceConfig, const VideoConfigParams& targetConfig,
33 VideoConfigParams& processedConfig)
34 {
35 if (targetConfig.GetFrameRate() > MAX_TARGET_FRAME_RATE) {
36 DHLOGE("The target framerate : %d is greater than the max framerate : %d.",
37 targetConfig.GetFrameRate(), MAX_TARGET_FRAME_RATE);
38 return DCAMERA_BAD_TYPE;
39 }
40 sourceConfig_ = sourceConfig;
41 targetConfig_ = targetConfig;
42 targetFrameRate_ = targetConfig_.GetFrameRate();
43
44 processedConfig_ = sourceConfig;
45 processedConfig = processedConfig_;
46 isFpsControllerProcess_ = true;
47 return DCAMERA_OK;
48 }
49
ReleaseProcessNode()50 void FpsControllerProcess::ReleaseProcessNode()
51 {
52 DHLOGD("Start release [%d] node : FPS controller.", nodeRank_);
53 isFpsControllerProcess_ = false;
54 isFirstFrame_ = false;
55 targetFrameRate_ = 0;
56 lastFrameIncomeTimeMs_ = 0;
57 recentFrameTimeSpanMs_ = -1;
58 keepCorrectionCount_ = 0;
59 keepLessThanDoubleCount_ = 0;
60 keepMoreThanDoubleCount_ = 0;
61 frameRateCorrectionFactor_ = 0.0;
62 frameRateOvershootMdf_ = 0;
63 for (int i = 0; i < INCOME_FRAME_TIME_HISTORY_WINDOWS_SIZE; i++) {
64 incomingFrameTimesMs_[i] = 0;
65 }
66
67 if (nextDataProcess_ != nullptr) {
68 nextDataProcess_->ReleaseProcessNode();
69 nextDataProcess_ = nullptr;
70 }
71 DHLOGD("Release [%d] node : FPS controller end.", nodeRank_);
72 }
73
ProcessData(std::vector<std::shared_ptr<DataBuffer>> & inputBuffers)74 int32_t FpsControllerProcess::ProcessData(std::vector<std::shared_ptr<DataBuffer>>& inputBuffers)
75 {
76 if (inputBuffers.empty()) {
77 DHLOGE("Data buffers is null.");
78 return DCAMERA_BAD_TYPE;
79 }
80 if (!isFpsControllerProcess_) {
81 DHLOGE("Decoder node occurred error.");
82 return DCAMERA_DISABLE_PROCESS;
83 }
84 int64_t timeStampUs = 0;
85 if (!inputBuffers[0]->FindInt64("timeUs", timeStampUs)) {
86 DHLOGE("Find decoder output timestamp failed.");
87 return DCAMERA_BAD_TYPE;
88 }
89
90 std::lock_guard<std::mutex> lck (mtx);
91 int64_t nowTimeMs = GetNowTimeStampMs();
92 UpdateFPSControllerInfo(nowTimeMs);
93
94 float curFrameRate = CalculateFrameRate(nowTimeMs);
95 if (IsDropFrame(curFrameRate)) {
96 DHLOGD("frame control, currect frameRate %u, targetRate %u, drop it", curFrameRate, targetFrameRate_);
97 return DCAMERA_OK;
98 }
99
100 DHLOGD("frame control render PushVideoFrame, frame info width %d height %d, timeStampUs %lld, fps %d",
101 sourceConfig_.GetWidth(), sourceConfig_.GetHeight(), (long long)timeStampUs, curFrameRate);
102 return FpsControllerDone(inputBuffers);
103 }
104
UpdateFPSControllerInfo(int64_t nowMs)105 void FpsControllerProcess::UpdateFPSControllerInfo(int64_t nowMs)
106 {
107 DHLOGD("Frame control, update control info.");
108 if (targetFrameRate_ <= 0) {
109 DHLOGD("Frame control, targetFrameRate_ : %d", targetFrameRate_);
110 return;
111 }
112
113 isFirstFrame_ = false;
114 if (lastFrameIncomeTimeMs_ == 0) {
115 DHLOGD("Frame control, income fisrt frame.");
116 isFirstFrame_ = true;
117 }
118 lastFrameIncomeTimeMs_ = nowMs;
119 recentFrameTimeSpanMs_ = nowMs - lastFrameIncomeTimeMs_;
120 DHLOGD("Frame control, lastFrameIncomeTimeMs_ %lld, receive Frame after last frame(ms): %lld",
121 (long long)lastFrameIncomeTimeMs_, (long long)recentFrameTimeSpanMs_);
122 UpdateIncomingFrameTimes(nowMs);
123 UpdateFrameRateCorrectionFactor(nowMs);
124 return;
125 }
126
UpdateFrameRateCorrectionFactor(int64_t nowMs)127 void FpsControllerProcess::UpdateFrameRateCorrectionFactor(int64_t nowMs)
128 {
129 DHLOGD("Frame control, update FPS correction factor.");
130 if (targetFrameRate_ <= 0) {
131 DHLOGD("Frame control, targetFrameRate_ : %d", targetFrameRate_);
132 return;
133 }
134 if (isFirstFrame_) {
135 DHLOGD("No frame rate correction factor when the first frame.");
136 return;
137 }
138
139 const float minDropFrmValue = 0.5;
140 const float maxDropFrmValue = 1.0;
141 const float msPerSecond = 1000;
142 const float maxInstantaneousFrameRateCoefficient = 1.1;
143 float maxInstantaneousFrameRateThreshold = targetFrameRate_ * maxInstantaneousFrameRateCoefficient;
144 float instantaneousFrameRate = msPerSecond / recentFrameTimeSpanMs_;
145 if (instantaneousFrameRate < 0) {
146 instantaneousFrameRate = -instantaneousFrameRate;
147 }
148 if (instantaneousFrameRate <= maxInstantaneousFrameRateThreshold) {
149 frameRateCorrectionFactor_ = minDropFrmValue;
150 } else {
151 if (keepCorrectionCount_ >= VIDEO_FRAME_DROP_INTERVAL) {
152 frameRateCorrectionFactor_ = maxDropFrmValue;
153 keepCorrectionCount_ = 0;
154 } else {
155 frameRateCorrectionFactor_ = 0;
156 keepCorrectionCount_++;
157 }
158 DHLOGD("Frame control, instantaneousFrameRate %.3f is more than maxInstantaneousFrameRateThreshold %.3f, " +
159 "keepCorrectionCount %d", instantaneousFrameRate, maxInstantaneousFrameRateThreshold,
160 keepCorrectionCount_);
161 }
162
163 DHLOGD("Frame control, targetFramerate %d, maxInstantaneousFrameRateThreshold %.3f," +
164 "instantaneousFrameRate %.3f, frameRateCorrectionFactor %.3f", targetFrameRate_,
165 maxInstantaneousFrameRateThreshold, instantaneousFrameRate, frameRateCorrectionFactor_);
166 return;
167 }
168
UpdateIncomingFrameTimes(int64_t nowMs)169 void FpsControllerProcess::UpdateIncomingFrameTimes(int64_t nowMs)
170 {
171 DHLOGD("Frame control, update incoming frame times array.");
172 if (targetFrameRate_ <= 0) {
173 DHLOGD("Frame control, targetFrameRate_ : %d", targetFrameRate_);
174 return;
175 }
176 if (isFirstFrame_) {
177 incomingFrameTimesMs_[0] = nowMs;
178 return;
179 }
180
181 int64_t intervalNewAndFirst = nowMs - incomingFrameTimesMs_[0];
182 if (intervalNewAndFirst < 0) {
183 intervalNewAndFirst = -intervalNewAndFirst;
184 }
185 if (intervalNewAndFirst > FRMAE_MAX_INTERVAL_TIME_WINDOW_MS) {
186 DHLOGD("frame control, nowMs: %lld mIncomingFrameT[0]: %lld intervalNewAndFirst: %lld",
187 (long long)nowMs, (long long)incomingFrameTimesMs_[0], (long long)intervalNewAndFirst);
188 for (int i = 0; i < INCOME_FRAME_TIME_HISTORY_WINDOWS_SIZE; i++) {
189 incomingFrameTimesMs_[i] = 0;
190 }
191 } else {
192 DHLOGD("frame control shift, nowMs: %lld mIncomingFrameT[0]: %lld intervalNewAndFirst: %lld",
193 (long long)nowMs, (long long)incomingFrameTimesMs_[0], (long long)intervalNewAndFirst);
194 const int32_t windowLeftNum = 2;
195 for (int i = (INCOME_FRAME_TIME_HISTORY_WINDOWS_SIZE - windowLeftNum); i >= 0; --i) {
196 incomingFrameTimesMs_[i + 1] = incomingFrameTimesMs_[i];
197 }
198 }
199 incomingFrameTimesMs_[0] = nowMs;
200 return;
201 }
202
CalculateFrameRate(int64_t nowMs)203 float FpsControllerProcess::CalculateFrameRate(int64_t nowMs)
204 {
205 DHLOGD("Frame control, calculate frame rate.");
206 if (targetFrameRate_ <= 0) {
207 DHLOGE("Frame control, targetFrameRate_ : %d", targetFrameRate_);
208 return 0.0;
209 }
210
211 int32_t num = 0;
212 int32_t validFramesNumber = 0;
213 if (nowMs < 0) {
214 nowMs = -nowMs;
215 }
216 for (; num < INCOME_FRAME_TIME_HISTORY_WINDOWS_SIZE; num++) {
217 if (incomingFrameTimesMs_[num] <= 0 || nowMs - incomingFrameTimesMs_[num] > FRAME_HISTORY_TIME_WINDOWS_MS) {
218 break;
219 } else {
220 validFramesNumber++;
221 }
222 }
223
224 const float msPerSecond = 1000;
225 const int32_t minValidCalculatedFrameRatesNum = 2;
226 int32_t minIncomingFrameNum = targetFrameRate_ / MIN_INCOME_FRAME_NUM_COEFFICIENT;
227 if (validFramesNumber > minIncomingFrameNum && validFramesNumber > minValidCalculatedFrameRatesNum) {
228 int64_t validTotalTimeInterval = (nowMs - incomingFrameTimesMs_[num - 1]);
229 if (validTotalTimeInterval < 0) {
230 validTotalTimeInterval = -validTotalTimeInterval;
231 }
232 if (validTotalTimeInterval > 0) {
233 return validFramesNumber * msPerSecond / validTotalTimeInterval + frameRateCorrectionFactor_;
234 }
235 }
236 return static_cast<float>(validFramesNumber);
237 }
238
IsDropFrame(float incomingFps)239 bool FpsControllerProcess::IsDropFrame(float incomingFps)
240 {
241 DHLOGD("Frame control, IsDropFrame");
242 if (targetFrameRate_ == 0) {
243 DHLOGD("target fps is 0, drop all frame.");
244 return true;
245 }
246 if (incomingFps <= 0) {
247 DHLOGD("incoming fps not more than 0, not drop");
248 return false;
249 }
250 const int32_t incomingFrmRate = static_cast<int32_t>(incomingFps);
251 if (incomingFrmRate > targetFrameRate_) {
252 DHLOGD("incoming fps not more than targetFrameRate_, not drop");
253 return false;
254 }
255 bool isDrop = ReduceFrameRateByUniformStrategy(incomingFrmRate);
256 DHLOGD("drop frame result: %s", isDrop ? "drop" : "no drop");
257 return isDrop;
258 }
259
ReduceFrameRateByUniformStrategy(int32_t incomingFrmRate)260 bool FpsControllerProcess::ReduceFrameRateByUniformStrategy(int32_t incomingFrmRate)
261 {
262 DHLOGD("Frame control, reduce frame rate by uniform rate strategy");
263 if (incomingFrmRate > targetFrameRate_) {
264 DHLOGD("incoming fps not more than targetFrameRate_, not drop");
265 return false;
266 }
267
268 /*
269 * When the actual incoming frame rate correction value is greater than the target frame
270 * rate, the incoming frames are reduced uniformly.
271 */
272 bool isDrop = false;
273 int32_t overshoot = frameRateOvershootMdf_ + (incomingFrmRate - targetFrameRate_);
274 if (overshoot < 0) {
275 overshoot = 0;
276 frameRateOvershootMdf_ = 0;
277 }
278 if (overshoot && DOUBLE_MULTIPLE * overshoot < incomingFrmRate) {
279 /*
280 * When the actual input frame rate is less than or equal to twice the target frame rate,
281 * one frame is dropped every (incomingFrmRate / overshoot) frames.
282 */
283 if (keepMoreThanDoubleCount_) {
284 keepMoreThanDoubleCount_ = 0;
285 return true;
286 }
287 const int32_t dropVar = incomingFrmRate / overshoot;
288 if (keepLessThanDoubleCount_ >= dropVar) {
289 isDrop = true;
290 frameRateOvershootMdf_ = -(incomingFrmRate % overshoot) / OVERSHOOT_MODIFY_COEFFICIENT;
291 keepLessThanDoubleCount_ = 1;
292 } else {
293 keepLessThanDoubleCount_++;
294 }
295 } else {
296 /*
297 * When the actual frame rate is more than twice the target frame rate or the overshoot is
298 * equal to 0, one frame is reserved every (overshoot / targetFrameRate_) frames.
299 */
300 keepLessThanDoubleCount_ = 0;
301 const int32_t dropVar = overshoot / targetFrameRate_;
302 if (keepMoreThanDoubleCount_ < dropVar) {
303 isDrop = true;
304 keepMoreThanDoubleCount_++;
305 } else {
306 frameRateOvershootMdf_ = overshoot % targetFrameRate_;
307 isDrop = false;
308 keepMoreThanDoubleCount_ = 0;
309 }
310 }
311 return isDrop;
312 }
313
FpsControllerDone(std::vector<std::shared_ptr<DataBuffer>> & outputBuffers)314 int32_t FpsControllerProcess::FpsControllerDone(std::vector<std::shared_ptr<DataBuffer>>& outputBuffers)
315 {
316 if (outputBuffers.empty()) {
317 DHLOGE("The received data buffers is empty.");
318 return DCAMERA_BAD_VALUE;
319 }
320
321 if (nextDataProcess_ != nullptr) {
322 DHLOGD("Send to the next node of the FpsController for processing.");
323 int32_t err = nextDataProcess_->ProcessData(outputBuffers);
324 if (err != DCAMERA_OK) {
325 DHLOGE("Someone node after the FpsController processes failed.");
326 }
327 return err;
328 }
329 DHLOGD("The current node is the last node, and Output the processed video buffer");
330 std::shared_ptr<DCameraPipelineSource> targetPipelineSource = callbackPipelineSource_.lock();
331 if (targetPipelineSource == nullptr) {
332 DHLOGE("callbackPipelineSource_ is nullptr.");
333 return DCAMERA_BAD_VALUE;
334 }
335 targetPipelineSource->OnProcessedVideoBuffer(outputBuffers[0]);
336 return DCAMERA_OK;
337 }
338 } // namespace DistributedHardware
339 } // namespace OHOS
340