• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (c) 2023-2025 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, "VideoSink" };
27 constexpr int64_t LAG_LIMIT_TIME = 100;
28 constexpr int32_t DROP_FRAME_CONTINUOUSLY_MAX_CNT = 2;
29 }
30 
31 namespace OHOS {
32 namespace Media {
33 namespace Pipeline {
GetvideoLatencyFixDelay()34 int64_t GetvideoLatencyFixDelay()
35 {
36     constexpr uint64_t defaultValue = 0;
37     static uint64_t fixDelay = OHOS::system::GetUintParameter("debug.media_service.video_sync_fix_delay", defaultValue);
38     MEDIA_LOG_I_SHORT("video_sync_fix_delay, pid:%{public}d, fixdelay: " PUBLIC_LOG_U64, getprocpid(), fixDelay);
39     return (int64_t)fixDelay;
40 }
41 
42 /// Video Key Frame Flag
43 constexpr int BUFFER_FLAG_KEY_FRAME = 0x00000002;
44 
45 // Video Sync Start Frame
46 constexpr int VIDEO_SINK_START_FRAME = 4;
47 
48 constexpr int64_t WAIT_TIME_US_THRESHOLD = 1500000; // max sleep time 1.5s
49 
50 constexpr int64_t SINK_TIME_US_THRESHOLD = 100000; // max sink time 100ms
51 
52 constexpr int64_t PER_SINK_TIME_THRESHOLD = 33000; // max per sink time 33ms
53 
54 constexpr int64_t DELTA_TIME_THRESHOLD = 5000; // max delta time 5ms
55 
VideoSink()56 VideoSink::VideoSink()
57 {
58     refreshTime_ = 0;
59     syncerPriority_ = IMediaSynchronizer::VIDEO_SINK;
60     fixDelay_ = GetvideoLatencyFixDelay();
61     MEDIA_LOG_I_SHORT("ctor");
62 }
63 
~VideoSink()64 VideoSink::~VideoSink()
65 {
66     MEDIA_LOG_I_SHORT("dtor");
67     this->eventReceiver_ = nullptr;
68 }
69 
UpdateTimeAnchorIfNeeded(int64_t nowCt,int64_t waitTime,const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)70 void VideoSink::UpdateTimeAnchorIfNeeded(int64_t nowCt, int64_t waitTime,
71     const std::shared_ptr<OHOS::Media::AVBuffer>& buffer)
72 {
73     auto syncCenter = syncCenter_.lock();
74     FALSE_RETURN(syncCenter != nullptr && buffer != nullptr);
75     syncCenter->SetLastVideoBufferPts(buffer->pts_ - firstPts_);
76     syncCenter->SetLastVideoBufferAbsPts(buffer->pts_);
77     if (!needUpdateTimeAnchor_) {
78         return;
79     }
80     uint64_t latency = 0;
81     (void)GetLatency(latency);
82     Pipeline::IMediaSyncCenter::IMediaTime iMediaTime = {buffer->pts_ - firstPts_, buffer->pts_, buffer->duration_};
83     syncCenter->UpdateTimeAnchor(nowCt + waitTime, latency, iMediaTime, this);
84     needUpdateTimeAnchor_ = false;
85 }
86 
UpdateTimeAnchorActually(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer,int64_t renderDelay)87 void VideoSink::UpdateTimeAnchorActually(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer, int64_t renderDelay)
88 {
89     auto syncCenter = syncCenter_.lock();
90     FALSE_RETURN(syncCenter != nullptr && buffer != nullptr);
91     syncCenter->SetLastVideoBufferPts(buffer->pts_ - firstPts_);
92     int64_t ct4Buffer = syncCenter->GetClockTimeNow() + (renderDelay > 0 ? renderDelay : 0);
93     uint64_t latency = 0;
94     (void)GetLatency(latency);
95     Pipeline::IMediaSyncCenter::IMediaTime iMediaTime = {buffer->pts_ - firstPts_, buffer->pts_, buffer->duration_};
96     syncCenter->UpdateTimeAnchor(ct4Buffer, latency, iMediaTime, this);
97 }
98 
DoSyncWrite(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)99 int64_t VideoSink::DoSyncWrite(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer)
100 {
101     FALSE_RETURN_V(buffer != nullptr, 0);
102     int64_t waitTime = 0;
103     bool render = true;
104     auto syncCenter = syncCenter_.lock();
105     if ((buffer->flag_ & BUFFER_FLAG_EOS) == 0) {
106         int64_t nowCt = syncCenter ? syncCenter->GetClockTimeNow() : 0;
107         if (isFirstFrame_) {
108             FALSE_RETURN_V(syncCenter != nullptr, false);
109             isFirstFrame_ = false;
110             firstFrameClockTime_  = nowCt;
111             firstFramePts_ = buffer->pts_;
112         } else {
113             waitTime = CheckBufferLatenessMayWait(buffer);
114         }
115         UpdateTimeAnchorIfNeeded(nowCt, waitTime, buffer);
116         lagDetector_.CalcLag(buffer);
117         lastBufferRelativePts_ = buffer->pts_ - firstPts_;
118     } else {
119         MEDIA_LOG_I_SHORT("Video sink EOS");
120         if (syncCenter) {
121             syncCenter->ReportEos(this);
122         }
123         return -1;
124     }
125     if ((render && waitTime >= 0) || dropFrameContinuouslyCnt_.load() >= DROP_FRAME_CONTINUOUSLY_MAX_CNT) {
126         dropFrameContinuouslyCnt_.store(0);
127         renderFrameCnt_++;
128         return waitTime > 0 ? waitTime : 0;
129     }
130     dropFrameContinuouslyCnt_.fetch_add(1);
131     discardFrameCnt_++;
132     PerfRecord(waitTime);
133     return -1;
134 }
135 
SetPerfRecEnabled(bool isPerfRecEnabled)136 Status VideoSink::SetPerfRecEnabled(bool isPerfRecEnabled)
137 {
138     isPerfRecEnabled_ = isPerfRecEnabled;
139     return Status::OK;
140 }
141 
PerfRecord(int64_t waitTime)142 void VideoSink::PerfRecord(int64_t waitTime)
143 {
144     FALSE_RETURN_NOLOG(isPerfRecEnabled_);
145     FALSE_RETURN_MSG(eventReceiver_ != nullptr, "Report perf failed, callback is nullptr");
146     FALSE_RETURN_NOLOG(perfRecorder_.Record(Plugins::Us2Ms(waitTime)) == PerfRecorder::FULL);
147     eventReceiver_->OnDfxEvent({ "VSINK", DfxEventType::DFX_INFO_PERF_REPORT, perfRecorder_.GetMainPerfData() });
148     perfRecorder_.Reset();
149 }
150 
ResetSyncInfo()151 void VideoSink::ResetSyncInfo()
152 {
153     ResetPrerollReported();
154     isFirstFrame_ = true;
155     lastBufferRelativePts_ = HST_TIME_NONE;
156     lastBufferAnchoredClockTime_ = HST_TIME_NONE;
157     seekFlag_ = false;
158     lastPts_ = HST_TIME_NONE;
159     lastClockTime_ = HST_TIME_NONE;
160     needUpdateTimeAnchor_ = true;
161     lagDetector_.Reset();
162     perfRecorder_.Reset();
163 }
164 
GetLatency(uint64_t & nanoSec)165 Status VideoSink::GetLatency(uint64_t& nanoSec)
166 {
167     nanoSec = 10; // 10 ns
168     return Status::OK;
169 }
170 
SetSeekFlag()171 void VideoSink::SetSeekFlag()
172 {
173     seekFlag_ = true;
174 }
175 
SetLastPts(int64_t lastPts,int64_t renderDelay)176 void VideoSink::SetLastPts(int64_t lastPts, int64_t renderDelay)
177 {
178     lastPts_ = lastPts;
179     auto syncCenter = syncCenter_.lock();
180     if (syncCenter != nullptr) {
181         lastClockTime_ = syncCenter->GetClockTimeNow() + (renderDelay > 0 ? renderDelay : 0);
182     }
183 }
184 
CalcBufferDiff(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer,int64_t bufferAnchoredClockTime,int64_t currentClockTime,float playbackRate)185 int64_t VideoSink::CalcBufferDiff(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer, int64_t bufferAnchoredClockTime,
186     int64_t currentClockTime, float playbackRate)
187 {
188     uint64_t latency = 0;
189     GetLatency(latency);
190     //  the diff between the current clock time and the buffer's
191     //  anchored clock time, adjusted by latency and a fixed delay
192     auto anchorDiff = currentClockTime + (int64_t) latency - bufferAnchoredClockTime + fixDelay_;
193     //  the diff between the actual duration of the previous video frame and the theoretically calculated duration
194     //  based on the PTS, considering the playback rate
195     auto videoDiff = (currentClockTime - lastClockTime_)
196         - static_cast<int64_t>((buffer->pts_ - lastPts_) / AdjustPlaybackRate(playbackRate));
197     // render time per frame reduced by PER_SINK_TIME_THRESHOLD
198     auto thresholdAdjustedVideoDiff = videoDiff - PER_SINK_TIME_THRESHOLD;
199 
200     auto diff = anchorDiff;
201     if (discardFrameCnt_ + renderFrameCnt_ < VIDEO_SINK_START_FRAME) {
202         diff = (currentClockTime - firstFrameClockTime_) - (buffer->pts_ - firstFramePts_);
203         MEDIA_LOG_I("VideoSink first few times diff is " PUBLIC_LOG_D64 " us", diff);
204     } else if (diff < 0 && videoDiff < SINK_TIME_US_THRESHOLD && diff < thresholdAdjustedVideoDiff) {
205         diff = thresholdAdjustedVideoDiff;
206     }
207     MEDIA_LOG_D("VS ct4Bf:" PUBLIC_LOG_D64 " diff:" PUBLIC_LOG_D64 " nowCt:" PUBLIC_LOG_D64,
208         bufferAnchoredClockTime, diff, currentClockTime);
209     return diff;
210 }
211 
CheckBufferLatenessMayWait(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)212 int64_t VideoSink::CheckBufferLatenessMayWait(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer)
213 {
214     auto syncCenter = syncCenter_.lock();
215     FALSE_RETURN_V(buffer != nullptr, true);
216     FALSE_RETURN_V(syncCenter != nullptr, true);
217 
218     auto relativePts = buffer->pts_ - firstPts_;
219     auto bufferAnchoredClockTime = syncCenter->GetAnchoredClockTime(relativePts);
220     MEDIA_LOG_D("VideoSink cur pts: " PUBLIC_LOG_D64 " us, bufferAnchoredClockTime: " PUBLIC_LOG_D64
221         " us, buf_pts: " PUBLIC_LOG_D64 " us, fixDelay: " PUBLIC_LOG_D64 " us", relativePts,
222         bufferAnchoredClockTime, buffer->pts_, fixDelay_);
223     FALSE_RETURN_V(bufferAnchoredClockTime != Plugins::HST_TIME_NONE, 0);
224 
225     if (lastBufferAnchoredClockTime_ != HST_TIME_NONE && seekFlag_ == false) {
226         int64_t currentBufferRelativeClockTime = lastBufferAnchoredClockTime_ + relativePts - lastBufferRelativePts_;
227         int64_t deltaTime = bufferAnchoredClockTime - currentBufferRelativeClockTime;
228         deltaTimeAccu_ = SmoothDeltaTime(deltaTimeAccu_, deltaTime);
229         if (std::abs(deltaTimeAccu_) < DELTA_TIME_THRESHOLD) {
230             bufferAnchoredClockTime = currentBufferRelativeClockTime;
231         }
232         MEDIA_LOG_D("lastBfTime:" PUBLIC_LOG_D64" us, lastPts:" PUBLIC_LOG_D64,
233             lastBufferAnchoredClockTime_, lastBufferRelativePts_);
234     } else {
235         seekFlag_ = (seekFlag_ == true) ? false : seekFlag_;
236     }
237 
238     auto diff = CalcBufferDiff(buffer, bufferAnchoredClockTime,
239         syncCenter->GetClockTimeNow(), syncCenter->GetPlaybackRate());
240 
241     bool tooLate = false;
242     int64_t waitTimeUs = 0;
243     if (diff < 0) { // buffer is early, diff < 0 or 0 < diff < 40ms(25Hz) render it
244         waitTimeUs = 0 - diff;
245         MEDIA_LOG_D_SHORT("buffer is too early waitTimeUs: " PUBLIC_LOG_D64, waitTimeUs);
246         if (waitTimeUs > WAIT_TIME_US_THRESHOLD) {
247             waitTimeUs = WAIT_TIME_US_THRESHOLD;
248         }
249     } else if (diff > 0 && Plugins::HstTime2Ms(diff * HST_USECOND) > 40) { // > 40ms, buffer is late
250         tooLate = true;
251         MEDIA_LOG_D_SHORT("buffer is too late");
252     }
253     lastBufferAnchoredClockTime_ = bufferAnchoredClockTime;
254     bool dropFlag = tooLate && ((buffer->flag_ & BUFFER_FLAG_KEY_FRAME) == 0); // buffer is too late, drop it
255     return dropFlag ? -1 : waitTimeUs;
256 }
257 
SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)258 void VideoSink::SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)
259 {
260     MEDIA_LOG_D_SHORT("VideoSink::SetSyncCenter");
261     syncCenter_ = syncCenter;
262     MediaSynchronousSink::Init();
263 }
264 
SetEventReceiver(const std::shared_ptr<EventReceiver> & receiver)265 void VideoSink::SetEventReceiver(const std::shared_ptr<EventReceiver> &receiver)
266 {
267     this->eventReceiver_ = receiver;
268     lagDetector_.SetEventReceiver(receiver);
269 }
270 
SetFirstPts(int64_t pts)271 void VideoSink::SetFirstPts(int64_t pts)
272 {
273     auto syncCenter = syncCenter_.lock();
274     if (firstPts_ == HST_TIME_NONE) {
275         if (syncCenter && syncCenter->GetMediaStartPts() != HST_TIME_NONE) {
276             firstPts_ = syncCenter->GetMediaStartPts();
277         } else {
278             firstPts_ = pts;
279         }
280         MEDIA_LOG_I("video DoSyncWrite set firstPts = " PUBLIC_LOG_D64, firstPts_);
281     }
282 }
283 
SetParameter(const std::shared_ptr<Meta> & meta)284 Status VideoSink::SetParameter(const std::shared_ptr<Meta>& meta)
285 {
286     UpdateMediaTimeRange(meta);
287     return Status::OK;
288 }
289 
AdjustPlaybackRate(float playbackRate)290 float VideoSink::AdjustPlaybackRate(float playbackRate)
291 {
292     static constexpr float MIN_PLAYBACK_RATE = 1e-9;
293     if (std::fabs(playbackRate) < MIN_PLAYBACK_RATE) {
294         return 1.0f;
295     }
296     return playbackRate;
297 }
298 
SmoothDeltaTime(int64_t accumulatedDeltaTime,int64_t currentDeltaTime)299 int64_t VideoSink::SmoothDeltaTime(int64_t accumulatedDeltaTime, int64_t currentDeltaTime)
300 {
301     static constexpr int64_t SMOOTHING_FACTOR = 9;
302     return (accumulatedDeltaTime * SMOOTHING_FACTOR + currentDeltaTime) / (SMOOTHING_FACTOR + 1);
303 }
304 
GetLagInfo(int32_t & lagTimes,int32_t & maxLagDuration,int32_t & avgLagDuration)305 Status VideoSink::GetLagInfo(int32_t& lagTimes, int32_t& maxLagDuration, int32_t& avgLagDuration)
306 {
307     lagDetector_.GetLagInfo(lagTimes, maxLagDuration, avgLagDuration);
308     return Status::OK;
309 }
310 
CalcLag(std::shared_ptr<AVBuffer> buffer)311 bool VideoSink::VideoLagDetector::CalcLag(std::shared_ptr<AVBuffer> buffer)
312 {
313     FALSE_RETURN_V(!(buffer->flag_ & (uint32_t)(Plugins::AVBufferFlag::EOS)), false);
314     auto systemTimeMsNow = Plugins::GetCurrentMillisecond();
315     auto systemTimeMsDiff = systemTimeMsNow - lastSystemTimeMs_;
316     auto bufferTimeUsNow = Plugins::Us2Ms(buffer->pts_);
317     auto bufferTimeMsDiff = bufferTimeUsNow - lastBufferTimeMs_;
318     auto lagTimeMs = systemTimeMsDiff - bufferTimeMsDiff;
319     bool isVideoLag = lastSystemTimeMs_ > 0 && lagTimeMs >= LAG_LIMIT_TIME;
320     MEDIA_LOG_I_FALSE_D(isVideoLag,
321         "prePts " PUBLIC_LOG_D64 " curPts " PUBLIC_LOG_D64 " ptsDiff " PUBLIC_LOG_D64 " tDiff " PUBLIC_LOG_D64,
322         lastBufferTimeMs_, bufferTimeUsNow, bufferTimeMsDiff, systemTimeMsDiff);
323     lastSystemTimeMs_ = systemTimeMsNow;
324     lastBufferTimeMs_ = bufferTimeUsNow;
325     if (isVideoLag) {
326         ResolveLagEvent(lagTimeMs);
327     }
328     return isVideoLag;
329 }
330 
SetEventReceiver(const std::shared_ptr<EventReceiver> eventReceiver)331 void VideoSink::VideoLagDetector::SetEventReceiver(const std::shared_ptr<EventReceiver> eventReceiver)
332 {
333     eventReceiver_ = eventReceiver;
334 }
335 
ResolveLagEvent(const int64_t & lagTimeMs)336 void VideoSink::VideoLagDetector::ResolveLagEvent(const int64_t &lagTimeMs)
337 {
338     lagTimes_++;
339     maxLagDuration_ = std::max(maxLagDuration_, lagTimeMs);
340     totalLagDuration_ += lagTimeMs;
341     FALSE_RETURN(eventReceiver_ != nullptr);
342     eventReceiver_->OnDfxEvent({"VideoSink", DfxEventType::DFX_INFO_PLAYER_VIDEO_LAG, lagTimeMs});
343 }
344 
GetLagInfo(int32_t & lagTimes,int32_t & maxLagDuration,int32_t & avgLagDuration)345 void VideoSink::VideoLagDetector::GetLagInfo(int32_t& lagTimes, int32_t& maxLagDuration, int32_t& avgLagDuration)
346 {
347     lagTimes = lagTimes_;
348     maxLagDuration = static_cast<int32_t>(maxLagDuration_);
349     if (lagTimes_ != 0) {
350         avgLagDuration = static_cast<int32_t>(totalLagDuration_ / lagTimes_);
351     } else {
352         avgLagDuration = 0;
353     }
354 }
355 
Reset()356 void VideoSink::VideoLagDetector::Reset()
357 {
358     lagTimes_ = 0;
359     maxLagDuration_ = 0;
360     lastSystemTimeMs_ = 0;
361     lastBufferTimeMs_ = 0;
362     totalLagDuration_ = 0;
363 }
364 } // namespace Pipeline
365 } // namespace MEDIA
366 } // namespace OHOS
367