1 /*
2 * Copyright (c) 2023-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 "video_sink.h"
17
18 #include <algorithm>
19
20 #include "common/log.h"
21 #include "media_sync_manager.h"
22 #include "osal/task/jobutils.h"
23 #include "syspara/parameters.h"
24
25 namespace {
26 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_SYSTEM_PLAYER, "HiStreamer" };
27 constexpr int64_t LAG_LIMIT_TIME = 100;
28 }
29
30 namespace OHOS {
31 namespace Media {
32 namespace Pipeline {
GetVideoLatencyFixDelay()33 int64_t GetVideoLatencyFixDelay()
34 {
35 constexpr uint64_t defaultValue = 0;
36 static uint64_t fixDelay = OHOS::system::GetUintParameter("debug.media_service.video_sync_fix_delay", defaultValue);
37 MEDIA_LOG_I_SHORT("video_sync_fix_delay, pid:%{public}d, fixdelay: " PUBLIC_LOG_U64, getprocpid(), fixDelay);
38 return (int64_t)fixDelay;
39 }
40
41 /// Video Key Frame Flag
42 constexpr int BUFFER_FLAG_KEY_FRAME = 0x00000002;
43
44 constexpr int64_t WAIT_TIME_US_THRESHOLD = 1500000; // max sleep time 1.5s
45
46 constexpr int64_t SINK_TIME_US_THRESHOLD = 100000; // max sink time 100ms
47
48 constexpr int64_t PER_SINK_TIME_THRESHOLD = 33000; // max per sink time 33ms
49
50 // Video Sync Start Frame
51 constexpr int VIDEO_SINK_START_FRAME = 4;
52
53 constexpr int64_t WAIT_TIME_US_THRESHOLD_WARNING = 40000; // warning threshold 40ms
54
VideoSink()55 VideoSink::VideoSink()
56 {
57 refreshTime_ = 0;
58 syncerPriority_ = IMediaSynchronizer::VIDEO_SINK;
59 fixDelay_ = GetVideoLatencyFixDelay();
60 MEDIA_LOG_I("VideoSink ctor called...");
61 }
62
~VideoSink()63 VideoSink::~VideoSink()
64 {
65 MEDIA_LOG_I("VideoSink dtor called...");
66 this->eventReceiver_ = nullptr;
67 }
68
UpdateTimeAnchorActually(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)69 void VideoSink::UpdateTimeAnchorActually(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer)
70 {
71 auto syncCenter = syncCenter_.lock();
72 FALSE_RETURN(syncCenter != nullptr && buffer != nullptr);
73 int64_t nowCt = syncCenter->GetClockTimeNow();
74 uint64_t latency = 0;
75 (void)GetLatency(latency);
76 syncCenter->UpdateTimeAnchor(nowCt, latency, buffer->pts_ - firstPts_,
77 buffer->pts_, buffer->duration_, this);
78 }
79
DoSyncWrite(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)80 int64_t VideoSink::DoSyncWrite(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer)
81 {
82 FALSE_RETURN_V(buffer != nullptr, false);
83 int64_t waitTime = 0;
84 bool render = true;
85 auto syncCenter = syncCenter_.lock();
86 if ((buffer->flag_ & BUFFER_FLAG_EOS) == 0) {
87 int64_t nowCt = syncCenter ? syncCenter->GetClockTimeNow() : 0;
88 if (isFirstFrame_) {
89 FALSE_RETURN_V(syncCenter != nullptr, false);
90 isFirstFrame_ = false;
91 firstFrameNowct_ = nowCt;
92 firstFramePts_ = buffer->pts_;
93 } else {
94 waitTime = CheckBufferLatenessMayWait(buffer);
95 }
96 if (syncCenter) {
97 uint64_t latency = 0;
98 (void)GetLatency(latency);
99 render = syncCenter->UpdateTimeAnchor(nowCt + waitTime, latency, buffer->pts_ - firstPts_,
100 buffer->pts_, buffer->duration_, this);
101 }
102 lagDetector_.CalcLag(buffer);
103 lastTimeStamp_ = buffer->pts_ - firstPts_;
104 } else {
105 MEDIA_LOG_I("Videosink receives EOS.");
106 if (syncCenter) {
107 syncCenter->ReportEos(this);
108 }
109 return -1;
110 }
111 if ((render && waitTime >= 0) || lastFrameDropped_) {
112 lastFrameDropped_ = false;
113 renderFrameCnt_++;
114 return waitTime > 0 ? waitTime : 0;
115 }
116 lastFrameDropped_ = true;
117 discardFrameCnt_++;
118 return -1;
119 }
120
ResetSyncInfo()121 void VideoSink::ResetSyncInfo()
122 {
123 ResetPrerollReported();
124 isFirstFrame_ = true;
125 lastTimeStamp_ = HST_TIME_NONE;
126 lastBufferTime_ = HST_TIME_NONE;
127 seekFlag_ = false;
128 lastPts_ = HST_TIME_NONE;
129 lastClockTime_ = HST_TIME_NONE;
130 lagDetector_.Reset();
131 }
132
GetLatency(uint64_t & nanoSec)133 Status VideoSink::GetLatency(uint64_t& nanoSec)
134 {
135 nanoSec = 10; // 10 ns
136 return Status::OK;
137 }
138
SetSeekFlag()139 void VideoSink::SetSeekFlag()
140 {
141 seekFlag_ = true; // seek后首次不走平滑
142 }
143
SetLastPts(int64_t lastPts)144 void VideoSink::SetLastPts(int64_t lastPts)
145 {
146 lastPts_ = lastPts;
147 auto syncCenter = syncCenter_.lock();
148 if (syncCenter != nullptr) {
149 lastClockTime_ = syncCenter->GetClockTimeNow();
150 }
151 }
152
CheckBufferLatenessMayWait(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)153 int64_t VideoSink::CheckBufferLatenessMayWait(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer)
154 {
155 FALSE_RETURN_V(buffer != nullptr, true);
156 bool tooLate = false;
157 auto syncCenter = syncCenter_.lock();
158 FALSE_RETURN_V(syncCenter != nullptr, true);
159 auto pts = buffer->pts_ - firstPts_;
160 auto ct4Buffer = syncCenter->GetClockTime(pts);
161 MEDIA_LOG_D("VideoSink cur pts: " PUBLIC_LOG_D64 " us, ct4Buffer: " PUBLIC_LOG_D64 " us, buf_pts: " PUBLIC_LOG_D64
162 " us, fixDelay: " PUBLIC_LOG_D64 " us", pts, ct4Buffer, buffer->pts_, fixDelay_);
163 FALSE_RETURN_V(ct4Buffer != Plugins::HST_TIME_NONE, 0);
164 int64_t waitTimeUs = 0;
165 if (lastBufferTime_ != HST_TIME_NONE && seekFlag_ == false) {
166 int64_t thisBufferTime = lastBufferTime_ + pts - lastTimeStamp_;
167 int64_t deltaTime = ct4Buffer - thisBufferTime;
168 deltaTimeAccu_ = (deltaTimeAccu_ * 9 + deltaTime) / 10; // 9 10 for smoothing
169 if (std::abs(deltaTimeAccu_) < 5 * HST_USECOND) { // 5ms
170 ct4Buffer = thisBufferTime;
171 }
172 MEDIA_LOG_D("lastBfTime:" PUBLIC_LOG_D64" us, lastTS:" PUBLIC_LOG_D64, lastBufferTime_, lastTimeStamp_);
173 } else {
174 seekFlag_ = (seekFlag_ == true) ? false : seekFlag_;
175 }
176 auto nowCt = syncCenter->GetClockTimeNow();
177 uint64_t latency = 0;
178 GetLatency(latency);
179 float speed = GetSpeed(syncCenter->GetPlaybackRate());
180 auto diff = nowCt + (int64_t) latency - ct4Buffer + fixDelay_; // anhor diff
181 auto diff2 = (nowCt - lastClockTime_) - static_cast<int64_t>((buffer->pts_ - lastPts_) / speed); // video diff
182 auto diff3 = diff2 - PER_SINK_TIME_THRESHOLD; // video diff with PER_SINK_TIME_THRESHOLD
183 if (discardFrameCnt_ + renderFrameCnt_ < VIDEO_SINK_START_FRAME) {
184 diff = (nowCt - firstFrameNowct_) - (buffer->pts_ - firstFramePts_);
185 MEDIA_LOG_I("VideoSink first few times diff is " PUBLIC_LOG_D64 " us", diff);
186 } else if (diff < 0 && diff2 < SINK_TIME_US_THRESHOLD && diff < diff3) { // per frame render time reduced by 33ms
187 diff = diff3;
188 }
189 MEDIA_LOG_D("VS ct4Bf:" PUBLIC_LOG_D64 "diff:" PUBLIC_LOG_D64 "nowCt:" PUBLIC_LOG_D64, ct4Buffer, diff, nowCt);
190 if (diff < 0) { // buffer is early, diff < 0 or 0 < diff < 40ms(25Hz) render it
191 waitTimeUs = 0 - diff;
192 MEDIA_LOG_I_FALSE_D((waitTimeUs >= WAIT_TIME_US_THRESHOLD_WARNING),
193 "buffer is too early waitTimeUs: " PUBLIC_LOG_D64, waitTimeUs);
194 if (waitTimeUs > WAIT_TIME_US_THRESHOLD) {
195 waitTimeUs = WAIT_TIME_US_THRESHOLD;
196 }
197 } else if (diff > 0 && Plugins::HstTime2Ms(diff * HST_USECOND) > 40) { // > 40ms, buffer is late
198 tooLate = true;
199 MEDIA_LOG_D("buffer is too late");
200 }
201 lastBufferTime_ = ct4Buffer;
202 bool dropFlag = tooLate && ((buffer->flag_ & BUFFER_FLAG_KEY_FRAME) == 0); // buffer is too late, drop it
203 return dropFlag ? -1 : waitTimeUs;
204 }
205
SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)206 void VideoSink::SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)
207 {
208 MEDIA_LOG_I("VideoSink::SetSyncCenter");
209 syncCenter_ = syncCenter;
210 MediaSynchronousSink::Init();
211 }
212
SetEventReceiver(const std::shared_ptr<EventReceiver> & receiver)213 void VideoSink::SetEventReceiver(const std::shared_ptr<EventReceiver> &receiver)
214 {
215 this->eventReceiver_ = receiver;
216 }
217
SetFirstPts(int64_t pts)218 void VideoSink::SetFirstPts(int64_t pts)
219 {
220 auto syncCenter = syncCenter_.lock();
221 if (firstPts_ == HST_TIME_NONE) {
222 if (syncCenter && syncCenter->GetMediaStartPts() != HST_TIME_NONE) {
223 firstPts_ = syncCenter->GetMediaStartPts();
224 } else {
225 firstPts_ = pts;
226 }
227 MEDIA_LOG_I("video DoSyncWrite set firstPts = " PUBLIC_LOG_D64, firstPts_);
228 }
229 }
230
SetParameter(const std::shared_ptr<Meta> & meta)231 Status VideoSink::SetParameter(const std::shared_ptr<Meta>& meta)
232 {
233 UpdateMediaTimeRange(meta);
234 return Status::OK;
235 }
236
GetSpeed(float speed)237 float VideoSink::GetSpeed(float speed)
238 {
239 if (std::fabs(speed - 0) < 1e-9) {
240 return 1.0f;
241 }
242 return speed;
243 }
244
GetLagInfo(int32_t & lagTimes,int32_t & maxLagDuration,int32_t & avgLagDuration)245 Status VideoSink::GetLagInfo(int32_t& lagTimes, int32_t& maxLagDuration, int32_t& avgLagDuration)
246 {
247 lagDetector_.GetLagInfo(lagTimes, maxLagDuration, avgLagDuration);
248 return Status::OK;
249 }
250
CalcLag(std::shared_ptr<AVBuffer> buffer)251 bool VideoSink::VideoLagDetector::CalcLag(std::shared_ptr<AVBuffer> buffer)
252 {
253 FALSE_RETURN_V(!(buffer->flag_ & (uint32_t)(Plugins::AVBufferFlag::EOS)), false);
254 auto systemTimeMsNow = Plugins::GetCurrentMillisecond();
255 auto systemTimeMsDiff = systemTimeMsNow - lastSystemTimeMs_;
256 auto bufferTimeUsNow = Plugins::Us2Ms(buffer->pts_);
257 auto bufferTimeMsDiff = bufferTimeUsNow - lastBufferTimeMs_;
258 auto lagTimeMs = systemTimeMsDiff - bufferTimeMsDiff;
259 bool isVideoLag = lastSystemTimeMs_ > 0 && lagTimeMs >= LAG_LIMIT_TIME;
260 MEDIA_LOG_I_FALSE_D(isVideoLag,
261 "prePts " PUBLIC_LOG_D64 " curPts " PUBLIC_LOG_D64 " ptsDiff " PUBLIC_LOG_D64 " tDiff " PUBLIC_LOG_D64,
262 lastBufferTimeMs_, bufferTimeUsNow, bufferTimeMsDiff, systemTimeMsDiff);
263 lastSystemTimeMs_ = systemTimeMsNow;
264 lastBufferTimeMs_ = bufferTimeUsNow;
265 if (isVideoLag) {
266 ResolveLagEvent(lagTimeMs);
267 }
268 return isVideoLag;
269 }
270
SetEventReceiver(const std::shared_ptr<EventReceiver> eventReceiver)271 void VideoSink::VideoLagDetector::SetEventReceiver(const std::shared_ptr<EventReceiver> eventReceiver)
272 {
273 eventReceiver_ = eventReceiver;
274 }
275
ResolveLagEvent(const int64_t & lagTimeMs)276 void VideoSink::VideoLagDetector::ResolveLagEvent(const int64_t &lagTimeMs)
277 {
278 lagTimes_++;
279 maxLagDuration_ = std::max(maxLagDuration_, lagTimeMs);
280 totalLagDuration_ += lagTimeMs;
281 FALSE_RETURN(eventReceiver_ != nullptr);
282 eventReceiver_->OnDfxEvent({"VideoSink", DfxEventType::DFX_INFO_PLAYER_VIDEO_LAG, lagTimeMs});
283 }
284
GetLagInfo(int32_t & lagTimes,int32_t & maxLagDuration,int32_t & avgLagDuration)285 void VideoSink::VideoLagDetector::GetLagInfo(int32_t& lagTimes, int32_t& maxLagDuration, int32_t& avgLagDuration)
286 {
287 lagTimes = lagTimes_;
288 maxLagDuration = static_cast<int32_t>(maxLagDuration_);
289 if (lagTimes_ != 0) {
290 avgLagDuration = static_cast<int32_t>(totalLagDuration_ / lagTimes_);
291 } else {
292 avgLagDuration = 0;
293 }
294 }
295
Reset()296 void VideoSink::VideoLagDetector::Reset()
297 {
298 lagTimes_ = 0;
299 maxLagDuration_ = 0;
300 lastSystemTimeMs_ = 0;
301 lastBufferTimeMs_ = 0;
302 totalLagDuration_ = 0;
303 }
304 } // namespace Pipeline
305 } // namespace MEDIA
306 } // namespace OHOS
307