niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 1 | /* |
leozwang@webrtc.org | 39e9659 | 2012-03-01 18:22:48 +0000 | [diff] [blame] | 2 | * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #include "video/rtp_streams_synchronizer.h" |
mflodman@webrtc.org | 511f82e | 2011-11-30 18:31:36 +0000 | [diff] [blame] | 12 | |
Yves Gerey | 3e70781 | 2018-11-28 16:47:49 +0100 | [diff] [blame] | 13 | #include "absl/types/optional.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 14 | #include "call/syncable.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 15 | #include "rtc_base/checks.h" |
Åsa Persson | 74d2b1d | 2020-02-10 16:33:29 +0100 | [diff] [blame^] | 16 | #include "rtc_base/logging.h" |
Steve Anton | 10542f2 | 2019-01-11 09:11:00 -0800 | [diff] [blame] | 17 | #include "rtc_base/time_utils.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 18 | #include "rtc_base/trace_event.h" |
Yves Gerey | 3e70781 | 2018-11-28 16:47:49 +0100 | [diff] [blame] | 19 | #include "system_wrappers/include/rtp_to_ntp_estimator.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 20 | |
| 21 | namespace webrtc { |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 22 | namespace { |
Åsa Persson | 74d2b1d | 2020-02-10 16:33:29 +0100 | [diff] [blame^] | 23 | // Time interval for logging stats. |
| 24 | constexpr int64_t kStatsLogIntervalMs = 10000; |
| 25 | |
asapersson | b7e7b49 | 2016-11-17 02:27:14 -0800 | [diff] [blame] | 26 | bool UpdateMeasurements(StreamSynchronization::Measurements* stream, |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 27 | const Syncable::Info& info) { |
| 28 | RTC_DCHECK(stream); |
| 29 | stream->latest_timestamp = info.latest_received_capture_timestamp; |
| 30 | stream->latest_receive_time_ms = info.latest_receive_time_ms; |
wu@webrtc.org | cd70119 | 2014-04-24 22:10:24 +0000 | [diff] [blame] | 31 | bool new_rtcp_sr = false; |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 32 | if (!stream->rtp_to_ntp.UpdateMeasurements( |
| 33 | info.capture_time_ntp_secs, info.capture_time_ntp_frac, |
| 34 | info.capture_time_source_clock, &new_rtcp_sr)) { |
asapersson | b7e7b49 | 2016-11-17 02:27:14 -0800 | [diff] [blame] | 35 | return false; |
stefan@webrtc.org | 7c3523c | 2012-09-11 07:00:42 +0000 | [diff] [blame] | 36 | } |
asapersson | b7e7b49 | 2016-11-17 02:27:14 -0800 | [diff] [blame] | 37 | return true; |
stefan@webrtc.org | 7c3523c | 2012-09-11 07:00:42 +0000 | [diff] [blame] | 38 | } |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 39 | } // namespace |
stefan@webrtc.org | 7c3523c | 2012-09-11 07:00:42 +0000 | [diff] [blame] | 40 | |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 41 | RtpStreamsSynchronizer::RtpStreamsSynchronizer(Syncable* syncable_video) |
| 42 | : syncable_video_(syncable_video), |
| 43 | syncable_audio_(nullptr), |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 44 | sync_(), |
Åsa Persson | 74d2b1d | 2020-02-10 16:33:29 +0100 | [diff] [blame^] | 45 | last_sync_time_(rtc::TimeNanos()), |
| 46 | last_stats_log_ms_(rtc::TimeMillis()) { |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 47 | RTC_DCHECK(syncable_video); |
Sebastian Jansson | c01367d | 2019-04-08 15:20:44 +0200 | [diff] [blame] | 48 | process_thread_checker_.Detach(); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 49 | } |
| 50 | |
Mirko Bonadei | 8fdcac3 | 2018-08-28 16:30:18 +0200 | [diff] [blame] | 51 | RtpStreamsSynchronizer::~RtpStreamsSynchronizer() = default; |
| 52 | |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 53 | void RtpStreamsSynchronizer::ConfigureSync(Syncable* syncable_audio) { |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 54 | rtc::CritScope lock(&crit_); |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 55 | if (syncable_audio == syncable_audio_) { |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 56 | // This prevents expensive no-ops. |
Peter Boström | 1794b26 | 2016-02-16 14:12:02 +0100 | [diff] [blame] | 57 | return; |
pbos | 8fc7fa7 | 2015-07-15 08:02:58 -0700 | [diff] [blame] | 58 | } |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 59 | |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 60 | syncable_audio_ = syncable_audio; |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 61 | sync_.reset(nullptr); |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 62 | if (syncable_audio_) { |
| 63 | sync_.reset(new StreamSynchronization(syncable_video_->id(), |
| 64 | syncable_audio_->id())); |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 65 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 66 | } |
| 67 | |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 68 | int64_t RtpStreamsSynchronizer::TimeUntilNextProcess() { |
| 69 | RTC_DCHECK_RUN_ON(&process_thread_checker_); |
pkasting@chromium.org | 0b1534c | 2014-12-15 22:09:40 +0000 | [diff] [blame] | 70 | const int64_t kSyncIntervalMs = 1000; |
Niels Möller | d28db7f | 2016-05-10 16:31:47 +0200 | [diff] [blame] | 71 | return kSyncIntervalMs - |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 72 | (rtc::TimeNanos() - last_sync_time_) / rtc::kNumNanosecsPerMillisec; |
mflodman@webrtc.org | 511f82e | 2011-11-30 18:31:36 +0000 | [diff] [blame] | 73 | } |
| 74 | |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 75 | void RtpStreamsSynchronizer::Process() { |
| 76 | RTC_DCHECK_RUN_ON(&process_thread_checker_); |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 77 | last_sync_time_ = rtc::TimeNanos(); |
mflodman@webrtc.org | 511f82e | 2011-11-30 18:31:36 +0000 | [diff] [blame] | 78 | |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 79 | rtc::CritScope lock(&crit_); |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 80 | if (!syncable_audio_) { |
pbos | a26ac92 | 2016-02-25 04:50:01 -0800 | [diff] [blame] | 81 | return; |
mflodman@webrtc.org | 511f82e | 2011-11-30 18:31:36 +0000 | [diff] [blame] | 82 | } |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 83 | RTC_DCHECK(sync_.get()); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 84 | |
Åsa Persson | 74d2b1d | 2020-02-10 16:33:29 +0100 | [diff] [blame^] | 85 | bool log_stats = false; |
| 86 | const int64_t now_ms = rtc::TimeMillis(); |
| 87 | if (now_ms - last_stats_log_ms_ > kStatsLogIntervalMs) { |
| 88 | last_stats_log_ms_ = now_ms; |
| 89 | log_stats = true; |
| 90 | } |
| 91 | |
Danil Chapovalov | b9b146c | 2018-06-15 12:28:07 +0200 | [diff] [blame] | 92 | absl::optional<Syncable::Info> audio_info = syncable_audio_->GetInfo(); |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 93 | if (!audio_info || !UpdateMeasurements(&audio_measurement_, *audio_info)) { |
pbos | a26ac92 | 2016-02-25 04:50:01 -0800 | [diff] [blame] | 94 | return; |
mflodman@webrtc.org | 511f82e | 2011-11-30 18:31:36 +0000 | [diff] [blame] | 95 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 96 | |
asapersson | b0c1b4e | 2016-09-17 01:00:01 -0700 | [diff] [blame] | 97 | int64_t last_video_receive_ms = video_measurement_.latest_receive_time_ms; |
Danil Chapovalov | b9b146c | 2018-06-15 12:28:07 +0200 | [diff] [blame] | 98 | absl::optional<Syncable::Info> video_info = syncable_video_->GetInfo(); |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 99 | if (!video_info || !UpdateMeasurements(&video_measurement_, *video_info)) { |
pbos | a26ac92 | 2016-02-25 04:50:01 -0800 | [diff] [blame] | 100 | return; |
pwestin@webrtc.org | 2853dde | 2012-05-11 11:08:54 +0000 | [diff] [blame] | 101 | } |
stefan@webrtc.org | 7c3523c | 2012-09-11 07:00:42 +0000 | [diff] [blame] | 102 | |
asapersson | b0c1b4e | 2016-09-17 01:00:01 -0700 | [diff] [blame] | 103 | if (last_video_receive_ms == video_measurement_.latest_receive_time_ms) { |
| 104 | // No new video packet has been received since last update. |
| 105 | return; |
| 106 | } |
| 107 | |
stefan@webrtc.org | 7c3523c | 2012-09-11 07:00:42 +0000 | [diff] [blame] | 108 | int relative_delay_ms; |
| 109 | // Calculate how much later or earlier the audio stream is compared to video. |
| 110 | if (!sync_->ComputeRelativeDelay(audio_measurement_, video_measurement_, |
| 111 | &relative_delay_ms)) { |
pbos | a26ac92 | 2016-02-25 04:50:01 -0800 | [diff] [blame] | 112 | return; |
stefan@webrtc.org | 7c3523c | 2012-09-11 07:00:42 +0000 | [diff] [blame] | 113 | } |
| 114 | |
Åsa Persson | 74d2b1d | 2020-02-10 16:33:29 +0100 | [diff] [blame^] | 115 | if (log_stats) { |
| 116 | RTC_LOG(LS_INFO) << "Sync info stats: " << now_ms |
| 117 | << ", {ssrc: " << sync_->audio_stream_id() << ", " |
| 118 | << "cur_delay_ms: " << audio_info->current_delay_ms |
| 119 | << "} {ssrc: " << sync_->video_stream_id() << ", " |
| 120 | << "cur_delay_ms: " << video_info->current_delay_ms |
| 121 | << "} {relative_delay_ms: " << relative_delay_ms << "} "; |
| 122 | } |
| 123 | |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 124 | TRACE_COUNTER1("webrtc", "SyncCurrentVideoDelay", |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 125 | video_info->current_delay_ms); |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 126 | TRACE_COUNTER1("webrtc", "SyncCurrentAudioDelay", |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 127 | audio_info->current_delay_ms); |
hclam@chromium.org | 806dc3b | 2013-04-09 19:54:10 +0000 | [diff] [blame] | 128 | TRACE_COUNTER1("webrtc", "SyncRelativeDelay", relative_delay_ms); |
Åsa Persson | 74d2b1d | 2020-02-10 16:33:29 +0100 | [diff] [blame^] | 129 | |
hclam@chromium.org | 9b23ecb | 2013-06-14 23:30:58 +0000 | [diff] [blame] | 130 | int target_audio_delay_ms = 0; |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 131 | int target_video_delay_ms = video_info->current_delay_ms; |
stefan@webrtc.org | 7c3523c | 2012-09-11 07:00:42 +0000 | [diff] [blame] | 132 | // Calculate the necessary extra audio delay and desired total video |
| 133 | // delay to get the streams in sync. |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 134 | if (!sync_->ComputeDelays(relative_delay_ms, audio_info->current_delay_ms, |
| 135 | &target_audio_delay_ms, &target_video_delay_ms)) { |
pbos | a26ac92 | 2016-02-25 04:50:01 -0800 | [diff] [blame] | 136 | return; |
pwestin@webrtc.org | 2853dde | 2012-05-11 11:08:54 +0000 | [diff] [blame] | 137 | } |
edjee@google.com | 79b0289 | 2013-04-04 19:43:34 +0000 | [diff] [blame] | 138 | |
Åsa Persson | 74d2b1d | 2020-02-10 16:33:29 +0100 | [diff] [blame^] | 139 | if (log_stats) { |
| 140 | RTC_LOG(LS_INFO) << "Sync delay stats: " << now_ms |
| 141 | << ", {ssrc: " << sync_->audio_stream_id() << ", " |
| 142 | << "target_delay_ms: " << target_audio_delay_ms |
| 143 | << "} {ssrc: " << sync_->video_stream_id() << ", " |
| 144 | << "target_delay_ms: " << target_video_delay_ms << "} "; |
| 145 | } |
| 146 | |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 147 | syncable_audio_->SetMinimumPlayoutDelay(target_audio_delay_ms); |
| 148 | syncable_video_->SetMinimumPlayoutDelay(target_video_delay_ms); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 149 | } |
mflodman@webrtc.org | 511f82e | 2011-11-30 18:31:36 +0000 | [diff] [blame] | 150 | |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 151 | // TODO(https://bugs.webrtc.org/7065): Move RtpToNtpEstimator out of |
| 152 | // RtpStreamsSynchronizer and into respective receive stream to always populate |
| 153 | // the estimated playout timestamp. |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 154 | bool RtpStreamsSynchronizer::GetStreamSyncOffsetInMs( |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 155 | uint32_t rtp_timestamp, |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 156 | int64_t render_time_ms, |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 157 | int64_t* video_playout_ntp_ms, |
asapersson | de9e5ff | 2016-11-02 07:14:03 -0700 | [diff] [blame] | 158 | int64_t* stream_offset_ms, |
| 159 | double* estimated_freq_khz) const { |
mflodman | 4cd2790 | 2016-08-05 06:28:45 -0700 | [diff] [blame] | 160 | rtc::CritScope lock(&crit_); |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 161 | if (!syncable_audio_) { |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 162 | return false; |
| 163 | } |
| 164 | |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 165 | uint32_t audio_rtp_timestamp; |
| 166 | int64_t time_ms; |
| 167 | if (!syncable_audio_->GetPlayoutRtpTimestamp(&audio_rtp_timestamp, |
| 168 | &time_ms)) { |
| 169 | return false; |
| 170 | } |
solenberg | 3ebbcb5 | 2017-01-31 03:58:40 -0800 | [diff] [blame] | 171 | |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 172 | int64_t latest_audio_ntp; |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 173 | if (!audio_measurement_.rtp_to_ntp.Estimate(audio_rtp_timestamp, |
asapersson | fe50b4d | 2016-12-22 07:53:51 -0800 | [diff] [blame] | 174 | &latest_audio_ntp)) { |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 175 | return false; |
| 176 | } |
| 177 | |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 178 | syncable_audio_->SetEstimatedPlayoutNtpTimestampMs(latest_audio_ntp, time_ms); |
| 179 | |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 180 | int64_t latest_video_ntp; |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 181 | if (!video_measurement_.rtp_to_ntp.Estimate(rtp_timestamp, |
| 182 | &latest_video_ntp)) { |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 183 | return false; |
| 184 | } |
| 185 | |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 186 | // Current audio ntp. |
| 187 | int64_t now_ms = rtc::TimeMillis(); |
| 188 | latest_audio_ntp += (now_ms - time_ms); |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 189 | |
Åsa Persson | fcf79cc | 2019-10-22 15:23:44 +0200 | [diff] [blame] | 190 | // Remove video playout delay. |
| 191 | int64_t time_to_render_ms = render_time_ms - now_ms; |
| 192 | if (time_to_render_ms > 0) |
| 193 | latest_video_ntp -= time_to_render_ms; |
| 194 | |
| 195 | *video_playout_ntp_ms = latest_video_ntp; |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 196 | *stream_offset_ms = latest_audio_ntp - latest_video_ntp; |
Ilya Nikolaevskiy | 558cabf | 2017-11-14 10:32:15 +0100 | [diff] [blame] | 197 | *estimated_freq_khz = video_measurement_.rtp_to_ntp.params()->frequency_khz; |
asapersson | f8cdd18 | 2016-03-15 01:00:47 -0700 | [diff] [blame] | 198 | return true; |
| 199 | } |
| 200 | |
mflodman@webrtc.org | 511f82e | 2011-11-30 18:31:36 +0000 | [diff] [blame] | 201 | } // namespace webrtc |