• 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, "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     PerfRecord(waitTime);
119     return -1;
120 }
121 
SetPerfRecEnabled(bool isPerfRecEnabled)122 Status VideoSink::SetPerfRecEnabled(bool isPerfRecEnabled)
123 {
124     isPerfRecEnabled_ = isPerfRecEnabled;
125     return Status::OK;
126 }
127 
PerfRecord(int64_t waitTime)128 void VideoSink::PerfRecord(int64_t waitTime)
129 {
130     FALSE_RETURN_NOLOG(isPerfRecEnabled_);
131     FALSE_RETURN_MSG(eventReceiver_ != nullptr, "Report perf failed, callback is nullptr");
132     FALSE_RETURN_NOLOG(perfRecorder_.Record(Plugins::Us2Ms(waitTime)) == PerfRecorder::FULL);
133     eventReceiver_->OnDfxEvent({ "VSINK", DfxEventType::DFX_INFO_PERF_REPORT, perfRecorder_.GetMainPerfData() });
134     perfRecorder_.Reset();
135 }
136 
ResetSyncInfo()137 void VideoSink::ResetSyncInfo()
138 {
139     ResetPrerollReported();
140     isFirstFrame_ = true;
141     lastTimeStamp_ = HST_TIME_NONE;
142     lastBufferTime_ = HST_TIME_NONE;
143     seekFlag_ = false;
144     lastPts_ = HST_TIME_NONE;
145     lastClockTime_ = HST_TIME_NONE;
146     lagDetector_.Reset();
147     perfRecorder_.Reset();
148 }
149 
GetLatency(uint64_t & nanoSec)150 Status VideoSink::GetLatency(uint64_t& nanoSec)
151 {
152     nanoSec = 10; // 10 ns
153     return Status::OK;
154 }
155 
SetSeekFlag()156 void VideoSink::SetSeekFlag()
157 {
158     seekFlag_ = true;  // seek后首次不走平滑
159 }
160 
SetLastPts(int64_t lastPts)161 void VideoSink::SetLastPts(int64_t lastPts)
162 {
163     lastPts_ = lastPts;
164     auto syncCenter = syncCenter_.lock();
165     if (syncCenter != nullptr) {
166         lastClockTime_ = syncCenter->GetClockTimeNow();
167     }
168 }
169 
CheckBufferLatenessMayWait(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)170 int64_t VideoSink::CheckBufferLatenessMayWait(const std::shared_ptr<OHOS::Media::AVBuffer>& buffer)
171 {
172     FALSE_RETURN_V(buffer != nullptr, true);
173     bool tooLate = false;
174     auto syncCenter = syncCenter_.lock();
175     FALSE_RETURN_V(syncCenter != nullptr, true);
176     auto pts = buffer->pts_ - firstPts_;
177     auto ct4Buffer = syncCenter->GetClockTime(pts);
178     MEDIA_LOG_D("VideoSink cur pts: " PUBLIC_LOG_D64 " us, ct4Buffer: " PUBLIC_LOG_D64 " us, buf_pts: " PUBLIC_LOG_D64
179         " us, fixDelay: " PUBLIC_LOG_D64 " us", pts, ct4Buffer, buffer->pts_, fixDelay_);
180     FALSE_RETURN_V(ct4Buffer != Plugins::HST_TIME_NONE, 0);
181     int64_t waitTimeUs = 0;
182     if (lastBufferTime_ != HST_TIME_NONE && seekFlag_ == false) {
183         int64_t thisBufferTime = lastBufferTime_ + pts - lastTimeStamp_;
184         int64_t deltaTime = ct4Buffer - thisBufferTime;
185         deltaTimeAccu_ = (deltaTimeAccu_ * 9 + deltaTime) / 10; // 9 10 for smoothing
186         if (std::abs(deltaTimeAccu_) < 5 * HST_USECOND) { // 5ms
187             ct4Buffer = thisBufferTime;
188         }
189         MEDIA_LOG_D("lastBfTime:" PUBLIC_LOG_D64" us, lastTS:" PUBLIC_LOG_D64, lastBufferTime_, lastTimeStamp_);
190     } else {
191         seekFlag_ = (seekFlag_ == true) ? false : seekFlag_;
192     }
193     auto nowCt = syncCenter->GetClockTimeNow();
194     uint64_t latency = 0;
195     GetLatency(latency);
196     float speed = GetSpeed(syncCenter->GetPlaybackRate());
197     auto diff = nowCt + (int64_t) latency - ct4Buffer + fixDelay_; // anhor diff
198     auto diff2 = (nowCt - lastClockTime_) - static_cast<int64_t>((buffer->pts_ - lastPts_) / speed); // video diff
199     auto diff3 = diff2 - PER_SINK_TIME_THRESHOLD; // video diff with PER_SINK_TIME_THRESHOLD
200     if (discardFrameCnt_ + renderFrameCnt_ < VIDEO_SINK_START_FRAME) {
201         diff = (nowCt - firstFrameNowct_) - (buffer->pts_ - firstFramePts_);
202         MEDIA_LOG_I("VideoSink first few times diff is " PUBLIC_LOG_D64 " us", diff);
203     } else if (diff < 0 && diff2 < SINK_TIME_US_THRESHOLD && diff < diff3) { // per frame render time reduced by 33ms
204         diff = diff3;
205     }
206     MEDIA_LOG_D("VS ct4Bf:" PUBLIC_LOG_D64 "diff:" PUBLIC_LOG_D64 "nowCt:" PUBLIC_LOG_D64, ct4Buffer, diff, nowCt);
207     if (diff < 0) { // buffer is early, diff < 0 or 0 < diff < 40ms(25Hz) render it
208         waitTimeUs = 0 - diff;
209         MEDIA_LOG_I_FALSE_D((waitTimeUs >= WAIT_TIME_US_THRESHOLD_WARNING),
210             "buffer is too early waitTimeUs: " PUBLIC_LOG_D64, waitTimeUs);
211         if (waitTimeUs > WAIT_TIME_US_THRESHOLD) {
212             waitTimeUs = WAIT_TIME_US_THRESHOLD;
213         }
214     } else if (diff > 0 && Plugins::HstTime2Ms(diff * HST_USECOND) > 40) { // > 40ms, buffer is late
215         tooLate = true;
216         MEDIA_LOG_D("buffer is too late");
217     }
218     lastBufferTime_ = ct4Buffer;
219     bool dropFlag = tooLate && ((buffer->flag_ & BUFFER_FLAG_KEY_FRAME) == 0); // buffer is too late, drop it
220     return dropFlag ? -1 : waitTimeUs;
221 }
222 
SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)223 void VideoSink::SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)
224 {
225     MEDIA_LOG_I("VideoSink::SetSyncCenter");
226     syncCenter_ = syncCenter;
227     MediaSynchronousSink::Init();
228 }
229 
SetEventReceiver(const std::shared_ptr<EventReceiver> & receiver)230 void VideoSink::SetEventReceiver(const std::shared_ptr<EventReceiver> &receiver)
231 {
232     this->eventReceiver_ = receiver;
233     lagDetector_.SetEventReceiver(receiver);
234 }
235 
SetFirstPts(int64_t pts)236 void VideoSink::SetFirstPts(int64_t pts)
237 {
238     auto syncCenter = syncCenter_.lock();
239     if (firstPts_ == HST_TIME_NONE) {
240         if (syncCenter && syncCenter->GetMediaStartPts() != HST_TIME_NONE) {
241             firstPts_ = syncCenter->GetMediaStartPts();
242         } else {
243             firstPts_ = pts;
244         }
245         MEDIA_LOG_I("video DoSyncWrite set firstPts = " PUBLIC_LOG_D64, firstPts_);
246     }
247 }
248 
SetParameter(const std::shared_ptr<Meta> & meta)249 Status VideoSink::SetParameter(const std::shared_ptr<Meta>& meta)
250 {
251     UpdateMediaTimeRange(meta);
252     return Status::OK;
253 }
254 
GetSpeed(float speed)255 float VideoSink::GetSpeed(float speed)
256 {
257     if (std::fabs(speed - 0) < 1e-9) {
258         return 1.0f;
259     }
260     return speed;
261 }
262 
GetLagInfo(int32_t & lagTimes,int32_t & maxLagDuration,int32_t & avgLagDuration)263 Status VideoSink::GetLagInfo(int32_t& lagTimes, int32_t& maxLagDuration, int32_t& avgLagDuration)
264 {
265     lagDetector_.GetLagInfo(lagTimes, maxLagDuration, avgLagDuration);
266     return Status::OK;
267 }
268 
CalcLag(std::shared_ptr<AVBuffer> buffer)269 bool VideoSink::VideoLagDetector::CalcLag(std::shared_ptr<AVBuffer> buffer)
270 {
271     FALSE_RETURN_V(!(buffer->flag_ & (uint32_t)(Plugins::AVBufferFlag::EOS)), false);
272     auto systemTimeMsNow = Plugins::GetCurrentMillisecond();
273     auto systemTimeMsDiff = systemTimeMsNow - lastSystemTimeMs_;
274     auto bufferTimeUsNow = Plugins::Us2Ms(buffer->pts_);
275     auto bufferTimeMsDiff = bufferTimeUsNow - lastBufferTimeMs_;
276     auto lagTimeMs = systemTimeMsDiff - bufferTimeMsDiff;
277     bool isVideoLag = lastSystemTimeMs_ > 0 && lagTimeMs >= LAG_LIMIT_TIME;
278     MEDIA_LOG_I_FALSE_D(isVideoLag,
279         "prePts " PUBLIC_LOG_D64 " curPts " PUBLIC_LOG_D64 " ptsDiff " PUBLIC_LOG_D64 " tDiff " PUBLIC_LOG_D64,
280         lastBufferTimeMs_, bufferTimeUsNow, bufferTimeMsDiff, systemTimeMsDiff);
281     lastSystemTimeMs_ = systemTimeMsNow;
282     lastBufferTimeMs_ = bufferTimeUsNow;
283     if (isVideoLag) {
284         ResolveLagEvent(lagTimeMs);
285     }
286     return isVideoLag;
287 }
288 
SetEventReceiver(const std::shared_ptr<EventReceiver> eventReceiver)289 void VideoSink::VideoLagDetector::SetEventReceiver(const std::shared_ptr<EventReceiver> eventReceiver)
290 {
291     eventReceiver_ = eventReceiver;
292 }
293 
ResolveLagEvent(const int64_t & lagTimeMs)294 void VideoSink::VideoLagDetector::ResolveLagEvent(const int64_t &lagTimeMs)
295 {
296     lagTimes_++;
297     maxLagDuration_ = std::max(maxLagDuration_, lagTimeMs);
298     totalLagDuration_ += lagTimeMs;
299     FALSE_RETURN(eventReceiver_ != nullptr);
300     eventReceiver_->OnDfxEvent({"VideoSink", DfxEventType::DFX_INFO_PLAYER_VIDEO_LAG, lagTimeMs});
301 }
302 
GetLagInfo(int32_t & lagTimes,int32_t & maxLagDuration,int32_t & avgLagDuration)303 void VideoSink::VideoLagDetector::GetLagInfo(int32_t& lagTimes, int32_t& maxLagDuration, int32_t& avgLagDuration)
304 {
305     lagTimes = lagTimes_;
306     maxLagDuration = static_cast<int32_t>(maxLagDuration_);
307     if (lagTimes_ != 0) {
308         avgLagDuration = static_cast<int32_t>(totalLagDuration_ / lagTimes_);
309     } else {
310         avgLagDuration = 0;
311     }
312 }
313 
Reset()314 void VideoSink::VideoLagDetector::Reset()
315 {
316     lagTimes_ = 0;
317     maxLagDuration_ = 0;
318     lastSystemTimeMs_ = 0;
319     lastBufferTimeMs_ = 0;
320     totalLagDuration_ = 0;
321 }
322 } // namespace Pipeline
323 } // namespace MEDIA
324 } // namespace OHOS
325