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 #include "ifeeding_smoother.h"
16 #include "distributed_camera_constants.h"
17 #include <sys/prctl.h>
18 #include "dcamera_utils_tools.h"
19 #include <cstdlib>
20 #include "distributed_hardware_log.h"
21 #include "smoother_constants.h"
22 
23 namespace OHOS {
24 namespace DistributedHardware {
~IFeedingSmoother()25 IFeedingSmoother::~IFeedingSmoother()
26 {
27     std::lock_guard<std::mutex> lock(stateMutex_);
28     if (state_ == SMOOTH_START) {
29         StopSmooth();
30     }
31 }
32 
PushData(const std::shared_ptr<IFeedableData> & data)33 void IFeedingSmoother::PushData(const std::shared_ptr<IFeedableData>& data)
34 {
35     {
36         std::lock_guard<std::mutex> lock(stateMutex_);
37         if (state_ == SMOOTH_STOP) {
38             DHLOGD("Smoother stop, push data failed.");
39             return;
40         }
41     }
42     {
43         std::lock_guard<std::mutex> lock(queueMutex_);
44         dataQueue_.push(data);
45     }
46     if (statistician_ != nullptr) {
47         statistician_->CalProcessTime(data);
48     }
49     smoothCon_.notify_one();
50 }
51 
StartSmooth()52 int32_t IFeedingSmoother::StartSmooth()
53 {
54     {
55         std::lock_guard<std::mutex> lock(stateMutex_);
56         if (state_ == SMOOTH_START) {
57             DHLOGD("Smoother is started.");
58             return SMOOTH_IS_STARTED;
59         }
60         state_ = SMOOTH_START;
61     }
62     InitTimeStatistician();
63     PrepareSmooth();
64     smoothThread_ = std::thread([this]() { this->LooperSmooth(); });
65     return SMOOTH_SUCCESS;
66 }
67 
LooperSmooth()68 void IFeedingSmoother::LooperSmooth()
69 {
70     prctl(PR_SET_NAME, LOOPER_SMOOTH.c_str());
71     while (state_ == SMOOTH_START) {
72         std::shared_ptr<IFeedableData> data = nullptr;
73         {
74             std::unique_lock<std::mutex> lock(queueMutex_);
75             smoothCon_.wait(lock, [this] {
76                 return (!dataQueue_.empty() || this->state_ == SMOOTH_STOP);
77             });
78             if (state_ == SMOOTH_STOP) {
79                 continue;
80             }
81             data = dataQueue_.front();
82         }
83         SmoothFeeding(data);
84         int32_t ret = NotifySmoothFinished(data);
85         if (ret == NOTIFY_FAILED) {
86             DHLOGD("Smoother listener notify producer failed.");
87             return;
88         }
89         {
90             std::lock_guard<std::mutex> lock(queueMutex_);
91             dataQueue_.pop();
92         }
93     }
94 }
95 
SmoothFeeding(const std::shared_ptr<IFeedableData> & data)96 void IFeedingSmoother::SmoothFeeding(const std::shared_ptr<IFeedableData>& data)
97 {
98     int64_t enterTime = GetNowTimeStampUs();
99     SetClockTime(enterTime);
100     int64_t timeStamp = data->GetTimeStamp();
101     if (timeStamp == 0) {
102         return;
103     }
104     if (!CheckIsProcessInDynamicBalance() || !CheckIsTimeInit()) {
105         RecordTime(enterTime, timeStamp);
106         return;
107     }
108 
109     if (!CheckIsBaselineInit()) {
110         InitBaseline(timeStamp, clockTime_);
111     }
112     int64_t interval = timeStamp - lastTimeStamp_;
113     int64_t elapse = enterTime - leaveTime_;
114     int64_t render = enterTime - lastEnterTime_;
115     int64_t delta = render - sleep_ - elapse;
116     delta_ += delta;
117     int64_t clock = timeStampBaseline_ + clockTime_ - clockBaseline_;
118     sleep_ = interval - elapse;
119     AdjustSleepTime(interval);
120     SyncClock(timeStamp, interval, clock);
121     {
122         std::unique_lock<std::mutex> lock(sleepMutex_);
123         sleepCon_.wait_for(lock, std::chrono::microseconds(sleep_), [this] {
124             return (this->state_ == SMOOTH_STOP);
125         });
126         if (state_ == SMOOTH_STOP) {
127             DHLOGD("Notify to interrupt sleep.");
128             return;
129         }
130     }
131     RecordTime(enterTime, timeStamp);
132 }
133 
CheckIsProcessInDynamicBalance()134 bool IFeedingSmoother::CheckIsProcessInDynamicBalance()
135 {
136     if (isInDynamicBalance_.load()) {
137         return true;
138     }
139     if (CheckIsProcessInDynamicBalanceOnce()) {
140         dynamicBalanceCount_++;
141     } else {
142         dynamicBalanceCount_ = 0;
143     }
144     if (dynamicBalanceCount_ >= dynamicBalanceThre_) {
145         isInDynamicBalance_.store(true);
146         return true;
147     }
148     return false;
149 }
150 
CheckIsProcessInDynamicBalanceOnce()151 bool IFeedingSmoother::CheckIsProcessInDynamicBalanceOnce()
152 {
153     if (statistician_ == nullptr) {
154         return false;
155     }
156     int64_t feedInterval = statistician_->GetFeedInterval();
157     int64_t averFeedInterval = statistician_->GetAverFeedInterval();
158     int64_t averTimeStamapInterval = statistician_->GetAverTimeStampInterval();
159     int64_t averIntervalDiff = averFeedInterval - averTimeStamapInterval;
160     int64_t feedOnceDiff = feedInterval - averFeedInterval;
161     return (averFeedInterval != 0) && (averTimeStamapInterval != 0) &&
162         (llabs(averIntervalDiff) < averIntervalDiffThre_) && (llabs(feedOnceDiff) < feedOnceDiffThre_);
163 }
164 
CheckIsBaselineInit()165 bool IFeedingSmoother::CheckIsBaselineInit()
166 {
167     return isBaselineInit_.load();
168 }
169 
CheckIsTimeInit()170 bool IFeedingSmoother::CheckIsTimeInit()
171 {
172     return isTimeInit_.load();
173 }
174 
InitBaseline(const int64_t timeStampBaseline,const int64_t clockBaseline)175 void IFeedingSmoother::InitBaseline(const int64_t timeStampBaseline, const int64_t clockBaseline)
176 {
177     SetTimeStampBaseline(timeStampBaseline);
178     SetClockBaseline(clockBaseline + bufferTime_);
179     isBaselineInit_.store(true);
180 }
181 
NotifySmoothFinished(const std::shared_ptr<IFeedableData> & data)182 int32_t IFeedingSmoother::NotifySmoothFinished(const std::shared_ptr<IFeedableData>& data)
183 {
184     if (listener_ == nullptr) {
185         DHLOGE("Smoother listener is nullptr.");
186         return NOTIFY_FAILED;
187     }
188     return listener_->OnSmoothFinished(data);
189 }
190 
AdjustSleepTime(const int64_t interval)191 void IFeedingSmoother::AdjustSleepTime(const int64_t interval)
192 {
193     const int64_t adjustThre = interval * adjustSleepFactor_;
194     if (delta_ > adjustThre && sleep_ > 0) {
195         int64_t sleep = sleep_ - adjustThre;
196         delta_ -= (sleep < 0) ? sleep_ : adjustThre;
197         sleep_ = sleep;
198         DHLOGD("Delta more than thre, adjust sleep to %{public}" PRId64" us.", sleep_);
199     } else if (delta_ < -adjustThre) {
200         sleep_ += delta_;
201         delta_ = 0;
202         DHLOGD("Delta less than negative thre, adjust sleep to %{public}" PRId64" us.", sleep_);
203     }
204 }
205 
SyncClock(const int64_t timeStamp,const int64_t interval,const int64_t clock)206 void IFeedingSmoother::SyncClock(const int64_t timeStamp, const int64_t interval, const int64_t clock)
207 {
208     const int64_t waitThre = interval * waitClockFactor_;
209     const int64_t trackThre = interval * trackClockFactor_;
210     int64_t offset = timeStamp - sleep_ - clock;
211     if (offset > waitThre || offset < -trackThre) {
212         sleep_ += offset;
213         DHLOGD("Offset is not in the threshold range, adjust sleep to %{public}" PRId64" us.", sleep_);
214     }
215     if (sleep_ < 0) {
216         sleep_ = 0;
217         DHLOGD("Sleep less than zero, adjust sleep to zero.");
218     }
219     DHLOGD("Offset is %{public}" PRId64" us, sleep is %{public}" PRId64" us after syncing clock.", offset, sleep_);
220 }
221 
RecordTime(const int64_t enterTime,const int64_t timeStamp)222 void IFeedingSmoother::RecordTime(const int64_t enterTime, const int64_t timeStamp)
223 {
224     lastEnterTime_ = enterTime;
225     lastTimeStamp_ = timeStamp;
226     leaveTime_ = GetNowTimeStampUs();
227     isTimeInit_.store(true);
228 }
229 
StopSmooth()230 int32_t IFeedingSmoother::StopSmooth()
231 {
232     {
233         std::lock_guard<std::mutex> lock(stateMutex_);
234         if (state_ == SMOOTH_STOP) {
235             DHLOGD("Smooth is stoped.");
236             return SMOOTH_IS_STOPED;
237         }
238         state_ = SMOOTH_STOP;
239     }
240     statistician_ = nullptr;
241     UnregisterListener();
242     smoothCon_.notify_one();
243     sleepCon_.notify_one();
244     smoothThread_.join();
245     std::queue<std::shared_ptr<IFeedableData>>().swap(dataQueue_);
246     DHLOGD("Stop smooth success.");
247     return SMOOTH_SUCCESS;
248 }
249 
InitTimeStatistician()250 void IFeedingSmoother::InitTimeStatistician()
251 {
252     if (statistician_ != nullptr) {
253         return;
254     }
255     statistician_ = std::make_shared<TimeStatistician>();
256 }
257 
RegisterListener(const std::shared_ptr<FeedingSmootherListener> & listener)258 void IFeedingSmoother::RegisterListener(const std::shared_ptr<FeedingSmootherListener>& listener)
259 {
260     listener_ = listener;
261 }
262 
UnregisterListener()263 void IFeedingSmoother::UnregisterListener()
264 {
265     listener_ = nullptr;
266 }
267 
SetBufferTime(const int32_t time)268 void IFeedingSmoother::SetBufferTime(const int32_t time)
269 {
270     bufferTime_ = time;
271 }
272 
SetDynamicBalanceThre(const uint8_t thre)273 void IFeedingSmoother::SetDynamicBalanceThre(const uint8_t thre)
274 {
275     dynamicBalanceThre_ = thre;
276 }
277 
SetProcessDynamicBalanceState(const bool state)278 void IFeedingSmoother::SetProcessDynamicBalanceState(const bool state)
279 {
280     isInDynamicBalance_.store(state);
281 }
282 
SetAverIntervalDiffThre(const uint32_t thre)283 void IFeedingSmoother::SetAverIntervalDiffThre(const uint32_t thre)
284 {
285     averIntervalDiffThre_ = thre;
286 }
287 
SetFeedOnceDiffThre(const uint32_t thre)288 void IFeedingSmoother::SetFeedOnceDiffThre(const uint32_t thre)
289 {
290     feedOnceDiffThre_ = thre;
291 }
292 
SetTimeStampBaseline(const int64_t timeStmapBaseline)293 void IFeedingSmoother::SetTimeStampBaseline(const int64_t timeStmapBaseline)
294 {
295     timeStampBaseline_ = timeStmapBaseline;
296 }
297 
SetClockBaseline(const int64_t clockBaseline)298 void IFeedingSmoother::SetClockBaseline(const int64_t clockBaseline)
299 {
300     clockBaseline_ = clockBaseline;
301 }
302 
GetBufferTime()303 int64_t IFeedingSmoother::GetBufferTime()
304 {
305     return bufferTime_;
306 }
307 
GetClockTime()308 int64_t IFeedingSmoother::GetClockTime()
309 {
310     return clockTime_;
311 }
312 
SetClockTime(const int64_t clockTime)313 void IFeedingSmoother::SetClockTime(const int64_t clockTime)
314 {
315     clockTime_ = clockTime;
316 }
317 
SetAdjustSleepFactor(const float factor)318 void IFeedingSmoother::SetAdjustSleepFactor(const float factor)
319 {
320     adjustSleepFactor_ = factor;
321 }
322 
SetWaitClockFactor(const float factor)323 void IFeedingSmoother::SetWaitClockFactor(const float factor)
324 {
325     waitClockFactor_ = factor;
326 }
327 
SetTrackClockFactor(const float factor)328 void IFeedingSmoother::SetTrackClockFactor(const float factor)
329 {
330     trackClockFactor_ = factor;
331 }
332 } // namespace DistributedHardware
333 } // namespace OHOS