blob: fedea4ad3c54dea3577041f6aefabbb6b11a667f [file] [log] [blame]
stefan@webrtc.org5f284982012-06-28 07:51:16 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
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
11#include "video_engine/stream_synchronization.h"
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +000012
13#include <assert.h>
14#include <algorithm>
15#include <cmath>
16
stefan@webrtc.org5f284982012-06-28 07:51:16 +000017#include "system_wrappers/interface/trace.h"
18
19namespace webrtc {
20
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +000021const int kMaxVideoDiffMs = 80;
22const int kMaxAudioDiffMs = 80;
23const int kMaxDelay = 1500;
stefan@webrtc.org5f284982012-06-28 07:51:16 +000024
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +000025const double kNtpFracPerMs = 4.294967296E6;
26
27namespace synchronization {
28
29RtcpMeasurement::RtcpMeasurement()
30 : ntp_secs(0), ntp_frac(0), rtp_timestamp(0) {}
31
32RtcpMeasurement::RtcpMeasurement(uint32_t ntp_secs, uint32_t ntp_frac,
33 uint32_t timestamp)
34 : ntp_secs(ntp_secs), ntp_frac(ntp_frac), rtp_timestamp(timestamp) {}
35
36// Calculates the RTP timestamp frequency from two pairs of NTP and RTP
37// timestamps.
38bool CalculateFrequency(
39 int64_t rtcp_ntp_ms1,
40 uint32_t rtp_timestamp1,
41 int64_t rtcp_ntp_ms2,
42 uint32_t rtp_timestamp2,
43 double* frequency_khz) {
44 if (rtcp_ntp_ms1 == rtcp_ntp_ms2) {
45 return false;
46 }
47 assert(rtcp_ntp_ms1 > rtcp_ntp_ms2);
48 *frequency_khz = static_cast<double>(rtp_timestamp1 - rtp_timestamp2) /
49 static_cast<double>(rtcp_ntp_ms1 - rtcp_ntp_ms2);
50 return true;
51}
52
53// Detects if there has been a wraparound between |old_timestamp| and
54// |new_timestamp|, and compensates by adding 2^32 if that is the case.
55bool CompensateForWrapAround(uint32_t new_timestamp,
56 uint32_t old_timestamp,
57 int64_t* compensated_timestamp) {
58 assert(compensated_timestamp);
59 int64_t wraps = synchronization::CheckForWrapArounds(new_timestamp,
60 old_timestamp);
61 if (wraps < 0) {
62 // Reordering, don't use this packet.
63 return false;
64 }
65 *compensated_timestamp = new_timestamp + (wraps << 32);
66 return true;
67}
68
69// Converts an NTP timestamp to a millisecond timestamp.
70int64_t NtpToMs(uint32_t ntp_secs, uint32_t ntp_frac) {
71 const double ntp_frac_ms = static_cast<double>(ntp_frac) / kNtpFracPerMs;
72 return ntp_secs * 1000 + ntp_frac_ms + 0.5;
73}
74
75// Converts |rtp_timestamp| to the NTP time base using the NTP and RTP timestamp
76// pairs in |rtcp|. The converted timestamp is returned in
77// |rtp_timestamp_in_ms|. This function compensates for wrap arounds in RTP
78// timestamps and returns false if it can't do the conversion due to reordering.
79bool RtpToNtpMs(int64_t rtp_timestamp,
80 const synchronization::RtcpList& rtcp,
81 int64_t* rtp_timestamp_in_ms) {
82 assert(rtcp.size() == 2);
83 int64_t rtcp_ntp_ms_new = synchronization::NtpToMs(rtcp.front().ntp_secs,
84 rtcp.front().ntp_frac);
85 int64_t rtcp_ntp_ms_old = synchronization::NtpToMs(rtcp.back().ntp_secs,
86 rtcp.back().ntp_frac);
87 int64_t rtcp_timestamp_new = rtcp.front().rtp_timestamp;
88 int64_t rtcp_timestamp_old = rtcp.back().rtp_timestamp;
89 if (!CompensateForWrapAround(rtcp_timestamp_new,
90 rtcp_timestamp_old,
91 &rtcp_timestamp_new)) {
92 return false;
93 }
94 double freq_khz;
95 if (!CalculateFrequency(rtcp_ntp_ms_new,
96 rtcp_timestamp_new,
97 rtcp_ntp_ms_old,
98 rtcp_timestamp_old,
99 &freq_khz)) {
100 return false;
101 }
102 double offset = rtcp_timestamp_new - freq_khz * rtcp_ntp_ms_new;
103 int64_t rtp_timestamp_unwrapped;
104 if (!CompensateForWrapAround(rtp_timestamp, rtcp_timestamp_old,
105 &rtp_timestamp_unwrapped)) {
106 return false;
107 }
108 double rtp_timestamp_ntp_ms = (static_cast<double>(rtp_timestamp_unwrapped) -
109 offset) / freq_khz + 0.5f;
110 assert(rtp_timestamp_ntp_ms >= 0);
111 *rtp_timestamp_in_ms = rtp_timestamp_ntp_ms;
112 return true;
113}
114
115int CheckForWrapArounds(uint32_t new_timestamp, uint32_t old_timestamp) {
116 if (new_timestamp < old_timestamp) {
117 // This difference should be less than -2^31 if we have had a wrap around
118 // (e.g. |new_timestamp| = 1, |rtcp_rtp_timestamp| = 2^32 - 1). Since it is
119 // cast to a int32_t, it should be positive.
120 if (static_cast<int32_t>(new_timestamp - old_timestamp) > 0) {
121 // Forward wrap around.
122 return 1;
123 }
124 } else if (static_cast<int32_t>(old_timestamp - new_timestamp) > 0) {
125 // This difference should be less than -2^31 if we have had a backward wrap
126 // around. Since it is cast to a int32_t, it should be positive.
127 return -1;
128 }
129 return 0;
130}
131} // namespace synchronization
stefan@webrtc.org5f284982012-06-28 07:51:16 +0000132
133struct ViESyncDelay {
134 ViESyncDelay() {
135 extra_video_delay_ms = 0;
136 last_video_delay_ms = 0;
137 extra_audio_delay_ms = 0;
138 last_sync_delay = 0;
139 network_delay = 120;
140 }
141
142 int extra_video_delay_ms;
143 int last_video_delay_ms;
144 int extra_audio_delay_ms;
145 int last_sync_delay;
146 int network_delay;
147};
148
149StreamSynchronization::StreamSynchronization(int audio_channel_id,
150 int video_channel_id)
151 : channel_delay_(new ViESyncDelay),
152 audio_channel_id_(audio_channel_id),
153 video_channel_id_(video_channel_id) {}
154
155StreamSynchronization::~StreamSynchronization() {
156 delete channel_delay_;
157}
158
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +0000159bool StreamSynchronization::ComputeRelativeDelay(
160 const Measurements& audio_measurement,
161 const Measurements& video_measurement,
162 int* relative_delay_ms) {
163 assert(relative_delay_ms);
164 if (audio_measurement.rtcp.size() < 2 || video_measurement.rtcp.size() < 2) {
165 // We need two RTCP SR reports per stream to do synchronization.
166 return false;
stefan@webrtc.org5f284982012-06-28 07:51:16 +0000167 }
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +0000168 int64_t audio_last_capture_time_ms;
169 if (!synchronization::RtpToNtpMs(audio_measurement.latest_timestamp,
170 audio_measurement.rtcp,
171 &audio_last_capture_time_ms)) {
172 return false;
173 }
174 int64_t video_last_capture_time_ms;
175 if (!synchronization::RtpToNtpMs(video_measurement.latest_timestamp,
176 video_measurement.rtcp,
177 &video_last_capture_time_ms)) {
178 return false;
179 }
180 if (video_last_capture_time_ms < 0) {
181 return false;
182 }
183 // Positive diff means that video_measurement is behind audio_measurement.
184 *relative_delay_ms = video_measurement.latest_receive_time_ms -
185 audio_measurement.latest_receive_time_ms -
186 (video_last_capture_time_ms - audio_last_capture_time_ms);
187 if (*relative_delay_ms > 1000 || *relative_delay_ms < -1000) {
188 return false;
189 }
190 return true;
191}
stefan@webrtc.org5f284982012-06-28 07:51:16 +0000192
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +0000193bool StreamSynchronization::ComputeDelays(int relative_delay_ms,
194 int current_audio_delay_ms,
195 int* extra_audio_delay_ms,
196 int* total_video_delay_target_ms) {
197 assert(extra_audio_delay_ms && total_video_delay_target_ms);
stefan@webrtc.org5f284982012-06-28 07:51:16 +0000198 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_,
199 "Audio delay is: %d for voice channel: %d",
200 current_audio_delay_ms, audio_channel_id_);
201 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_,
202 "Network delay diff is: %d for voice channel: %d",
203 channel_delay_->network_delay, audio_channel_id_);
204 // Calculate the difference between the lowest possible video delay and
205 // the current audio delay.
stefan@webrtc.org5f284982012-06-28 07:51:16 +0000206 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_,
207 "Current diff is: %d for audio channel: %d",
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +0000208 relative_delay_ms, audio_channel_id_);
209
210 int current_diff_ms = *total_video_delay_target_ms - current_audio_delay_ms +
211 relative_delay_ms;
stefan@webrtc.org5f284982012-06-28 07:51:16 +0000212
213 int video_delay_ms = 0;
214 if (current_diff_ms > 0) {
215 // The minimum video delay is longer than the current audio delay.
216 // We need to decrease extra video delay, if we have added extra delay
217 // earlier, or add extra audio delay.
218 if (channel_delay_->extra_video_delay_ms > 0) {
219 // We have extra delay added to ViE. Reduce this delay before adding
220 // extra delay to VoE.
221
222 // This is the desired delay, we can't reduce more than this.
223 video_delay_ms = *total_video_delay_target_ms;
224
225 // Check that we don't reduce the delay more than what is allowed.
226 if (video_delay_ms <
227 channel_delay_->last_video_delay_ms - kMaxVideoDiffMs) {
228 video_delay_ms =
229 channel_delay_->last_video_delay_ms - kMaxVideoDiffMs;
230 channel_delay_->extra_video_delay_ms =
231 video_delay_ms - *total_video_delay_target_ms;
232 } else {
233 channel_delay_->extra_video_delay_ms = 0;
234 }
235 channel_delay_->last_video_delay_ms = video_delay_ms;
236 channel_delay_->last_sync_delay = -1;
237 channel_delay_->extra_audio_delay_ms = 0;
238 } else { // channel_delay_->extra_video_delay_ms > 0
239 // We have no extra video delay to remove, increase the audio delay.
240 if (channel_delay_->last_sync_delay >= 0) {
241 // We have increased the audio delay earlier, increase it even more.
242 int audio_diff_ms = current_diff_ms / 2;
243 if (audio_diff_ms > kMaxAudioDiffMs) {
244 // We only allow a maximum change of KMaxAudioDiffMS for audio
245 // due to NetEQ maximum changes.
246 audio_diff_ms = kMaxAudioDiffMs;
247 }
248 // Increase the audio delay
249 channel_delay_->extra_audio_delay_ms += audio_diff_ms;
250
251 // Don't set a too high delay.
252 if (channel_delay_->extra_audio_delay_ms > kMaxDelay) {
253 channel_delay_->extra_audio_delay_ms = kMaxDelay;
254 }
255
256 // Don't add any extra video delay.
257 video_delay_ms = *total_video_delay_target_ms;
258 channel_delay_->extra_video_delay_ms = 0;
259 channel_delay_->last_video_delay_ms = video_delay_ms;
260 channel_delay_->last_sync_delay = 1;
261 } else { // channel_delay_->last_sync_delay >= 0
262 // First time after a delay change, don't add any extra delay.
263 // This is to not toggle back and forth too much.
264 channel_delay_->extra_audio_delay_ms = 0;
265 // Set minimum video delay
266 video_delay_ms = *total_video_delay_target_ms;
267 channel_delay_->extra_video_delay_ms = 0;
268 channel_delay_->last_video_delay_ms = video_delay_ms;
269 channel_delay_->last_sync_delay = 0;
270 }
271 }
272 } else { // if (current_diffMS > 0)
273 // The minimum video delay is lower than the current audio delay.
274 // We need to decrease possible extra audio delay, or
275 // add extra video delay.
276
277 if (channel_delay_->extra_audio_delay_ms > 0) {
278 // We have extra delay in VoiceEngine
279 // Start with decreasing the voice delay
280 int audio_diff_ms = current_diff_ms / 2;
281 if (audio_diff_ms < -1 * kMaxAudioDiffMs) {
282 // Don't change the delay too much at once.
283 audio_diff_ms = -1 * kMaxAudioDiffMs;
284 }
285 // Add the negative difference.
286 channel_delay_->extra_audio_delay_ms += audio_diff_ms;
287
288 if (channel_delay_->extra_audio_delay_ms < 0) {
289 // Negative values not allowed.
290 channel_delay_->extra_audio_delay_ms = 0;
291 channel_delay_->last_sync_delay = 0;
292 } else {
293 // There is more audio delay to use for the next round.
294 channel_delay_->last_sync_delay = 1;
295 }
296
297 // Keep the video delay at the minimum values.
298 video_delay_ms = *total_video_delay_target_ms;
299 channel_delay_->extra_video_delay_ms = 0;
300 channel_delay_->last_video_delay_ms = video_delay_ms;
301 } else { // channel_delay_->extra_audio_delay_ms > 0
302 // We have no extra delay in VoiceEngine, increase the video delay.
303 channel_delay_->extra_audio_delay_ms = 0;
304
305 // Make the difference positive.
306 int video_diff_ms = -1 * current_diff_ms;
307
308 // This is the desired delay.
309 video_delay_ms = *total_video_delay_target_ms + video_diff_ms;
310 if (video_delay_ms > channel_delay_->last_video_delay_ms) {
311 if (video_delay_ms >
312 channel_delay_->last_video_delay_ms + kMaxVideoDiffMs) {
313 // Don't increase the delay too much at once
314 video_delay_ms =
315 channel_delay_->last_video_delay_ms + kMaxVideoDiffMs;
316 }
317 // Verify we don't go above the maximum allowed delay
318 if (video_delay_ms > kMaxDelay) {
319 video_delay_ms = kMaxDelay;
320 }
321 } else {
322 if (video_delay_ms <
323 channel_delay_->last_video_delay_ms - kMaxVideoDiffMs) {
324 // Don't decrease the delay too much at once
325 video_delay_ms =
326 channel_delay_->last_video_delay_ms - kMaxVideoDiffMs;
327 }
328 // Verify we don't go below the minimum delay
329 if (video_delay_ms < *total_video_delay_target_ms) {
330 video_delay_ms = *total_video_delay_target_ms;
331 }
332 }
333 // Store the values
334 channel_delay_->extra_video_delay_ms =
335 video_delay_ms - *total_video_delay_target_ms;
336 channel_delay_->last_video_delay_ms = video_delay_ms;
337 channel_delay_->last_sync_delay = -1;
338 }
339 }
340
341 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_,
342 "Sync video delay %d ms for video channel and audio delay %d for audio "
343 "channel %d",
344 video_delay_ms, channel_delay_->extra_audio_delay_ms, audio_channel_id_);
345
346 *extra_audio_delay_ms = channel_delay_->extra_audio_delay_ms;
347
348 if (video_delay_ms < 0) {
349 video_delay_ms = 0;
350 }
351 *total_video_delay_target_ms =
352 (*total_video_delay_target_ms > video_delay_ms) ?
353 *total_video_delay_target_ms : video_delay_ms;
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +0000354 return true;
stefan@webrtc.org5f284982012-06-28 07:51:16 +0000355}
356} // namespace webrtc