blob: aca05faed6ea01d69125ef1dc9fd6e605ebea186 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
2 * Copyright (c) 2011 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 "trace.h"
12#include "internal_defines.h"
13#include "jitter_buffer_common.h"
14#include "timing.h"
15#include "timestamp_extrapolator.h"
16
17namespace webrtc {
18
henrik.lundin@webrtc.org7d8c72e2011-12-21 15:24:01 +000019VCMTiming::VCMTiming(TickTimeBase* clock,
20 WebRtc_Word32 vcmId,
21 WebRtc_Word32 timingId,
22 VCMTiming* masterTiming)
niklase@google.com470e71d2011-07-07 08:21:25 +000023:
stefan@webrtc.org7889a9b2011-12-12 08:18:24 +000024_critSect(CriticalSectionWrapper::CreateCriticalSection()),
niklase@google.com470e71d2011-07-07 08:21:25 +000025_vcmId(vcmId),
henrik.lundin@webrtc.org7d8c72e2011-12-21 15:24:01 +000026_clock(clock),
niklase@google.com470e71d2011-07-07 08:21:25 +000027_timingId(timingId),
28_master(false),
29_tsExtrapolator(),
30_codecTimer(),
31_renderDelayMs(kDefaultRenderDelayMs),
32_minTotalDelayMs(0),
33_requiredDelayMs(0),
34_currentDelayMs(0),
35_prevFrameTimestamp(0)
36{
37 if (masterTiming == NULL)
38 {
39 _master = true;
henrik.lundin@webrtc.org7d8c72e2011-12-21 15:24:01 +000040 _tsExtrapolator = new VCMTimestampExtrapolator(_clock, vcmId, timingId);
niklase@google.com470e71d2011-07-07 08:21:25 +000041 }
42 else
43 {
44 _tsExtrapolator = masterTiming->_tsExtrapolator;
45 }
46}
47
48VCMTiming::~VCMTiming()
49{
50 if (_master)
51 {
52 delete _tsExtrapolator;
53 }
stefan@webrtc.org7889a9b2011-12-12 08:18:24 +000054 delete _critSect;
niklase@google.com470e71d2011-07-07 08:21:25 +000055}
56
57void
58VCMTiming::Reset(WebRtc_Word64 nowMs /* = -1 */)
59{
60 CriticalSectionScoped cs(_critSect);
61 if (nowMs > -1)
62 {
63 _tsExtrapolator->Reset(nowMs);
64 }
65 else
66 {
67 _tsExtrapolator->Reset();
68 }
69 _codecTimer.Reset();
70 _renderDelayMs = kDefaultRenderDelayMs;
71 _minTotalDelayMs = 0;
72 _requiredDelayMs = 0;
73 _currentDelayMs = 0;
74 _prevFrameTimestamp = 0;
75}
76
77void VCMTiming::ResetDecodeTime()
78{
79 _codecTimer.Reset();
80}
81
82void
83VCMTiming::SetRenderDelay(WebRtc_UWord32 renderDelayMs)
84{
85 CriticalSectionScoped cs(_critSect);
86 _renderDelayMs = renderDelayMs;
87}
88
89void
90VCMTiming::SetMinimumTotalDelay(WebRtc_UWord32 minTotalDelayMs)
91{
92 CriticalSectionScoped cs(_critSect);
93 _minTotalDelayMs = minTotalDelayMs;
94}
95
96void
97VCMTiming::SetRequiredDelay(WebRtc_UWord32 requiredDelayMs)
98{
99 CriticalSectionScoped cs(_critSect);
100 if (requiredDelayMs != _requiredDelayMs)
101 {
102 if (_master)
103 {
104 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
105 "Desired jitter buffer level: %u ms", requiredDelayMs);
106 }
107 _requiredDelayMs = requiredDelayMs;
108 }
109}
110
111void VCMTiming::UpdateCurrentDelay(WebRtc_UWord32 frameTimestamp)
112{
113 CriticalSectionScoped cs(_critSect);
114 WebRtc_UWord32 targetDelayMs = TargetDelayInternal();
115
116 // Make sure we try to sync with audio
117 if (targetDelayMs < _minTotalDelayMs)
118 {
119 targetDelayMs = _minTotalDelayMs;
120 }
121
122 if (_currentDelayMs == 0)
123 {
124 // Not initialized, set current delay to target.
125 _currentDelayMs = targetDelayMs;
126 }
127 else if (targetDelayMs != _currentDelayMs)
128 {
129 WebRtc_Word64 delayDiffMs = static_cast<WebRtc_Word64>(targetDelayMs) -
130 _currentDelayMs;
131 // Never change the delay with more than 100 ms every second. If we're changing the
132 // delay in too large steps we will get noticable freezes. By limiting the change we
133 // can increase the delay in smaller steps, which will be experienced as the video is
134 // played in slow motion. When lowering the delay the video will be played at a faster
135 // pace.
136 WebRtc_Word64 maxChangeMs = 0;
137 if (frameTimestamp < 0x0000ffff && _prevFrameTimestamp > 0xffff0000)
138 {
139 // wrap
140 maxChangeMs = kDelayMaxChangeMsPerS * (frameTimestamp +
141 (static_cast<WebRtc_Word64>(1)<<32) - _prevFrameTimestamp) / 90000;
142 }
143 else
144 {
145 maxChangeMs = kDelayMaxChangeMsPerS *
146 (frameTimestamp - _prevFrameTimestamp) / 90000;
147 }
148 if (maxChangeMs <= 0)
149 {
150 // Any changes less than 1 ms are truncated and
151 // will be postponed. Negative change will be due
152 // to reordering and should be ignored.
153 return;
154 }
155 else if (delayDiffMs < -maxChangeMs)
156 {
157 delayDiffMs = -maxChangeMs;
158 }
159 else if (delayDiffMs > maxChangeMs)
160 {
161 delayDiffMs = maxChangeMs;
162 }
163 _currentDelayMs = _currentDelayMs + static_cast<WebRtc_Word32>(delayDiffMs);
164 }
165 _prevFrameTimestamp = frameTimestamp;
166}
167
168void VCMTiming::UpdateCurrentDelay(WebRtc_Word64 renderTimeMs,
169 WebRtc_Word64 actualDecodeTimeMs)
170{
171 CriticalSectionScoped cs(_critSect);
172 WebRtc_UWord32 targetDelayMs = TargetDelayInternal();
173 // Make sure we try to sync with audio
174 if (targetDelayMs < _minTotalDelayMs)
175 {
176 targetDelayMs = _minTotalDelayMs;
177 }
178 WebRtc_Word64 delayedMs = actualDecodeTimeMs -
179 (renderTimeMs - MaxDecodeTimeMs() - _renderDelayMs);
180 if (delayedMs < 0)
181 {
182 return;
183 }
184 else if (_currentDelayMs + delayedMs <= targetDelayMs)
185 {
186 _currentDelayMs += static_cast<WebRtc_UWord32>(delayedMs);
187 }
188 else
189 {
190 _currentDelayMs = targetDelayMs;
191 }
192}
193
194WebRtc_Word32
195VCMTiming::StopDecodeTimer(WebRtc_UWord32 timeStamp,
196 WebRtc_Word64 startTimeMs,
197 WebRtc_Word64 nowMs)
198{
199 CriticalSectionScoped cs(_critSect);
200 const WebRtc_Word32 maxDecTime = MaxDecodeTimeMs();
201 WebRtc_Word32 timeDiffMs = _codecTimer.StopTimer(startTimeMs, nowMs);
202 if (timeDiffMs < 0)
203 {
204 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
205 "Codec timer error: %d", timeDiffMs);
stefan@webrtc.org06887ae2011-10-10 14:17:46 +0000206 assert(false);
niklase@google.com470e71d2011-07-07 08:21:25 +0000207 }
208
209 if (_master)
210 {
211 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
212 "Frame decoded: timeStamp=%u decTime=%d maxDecTime=%u, at %u",
213 timeStamp, timeDiffMs, maxDecTime, MaskWord64ToUWord32(nowMs));
214 }
215 return 0;
216}
217
218void
219VCMTiming::IncomingTimestamp(WebRtc_UWord32 timeStamp, WebRtc_Word64 nowMs)
220{
221 CriticalSectionScoped cs(_critSect);
222 _tsExtrapolator->Update(nowMs, timeStamp, _master);
223}
224
225WebRtc_Word64
226VCMTiming::RenderTimeMs(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const
227{
228 CriticalSectionScoped cs(_critSect);
229 const WebRtc_Word64 renderTimeMs = RenderTimeMsInternal(frameTimestamp, nowMs);
230 if (renderTimeMs < 0)
231 {
232 return renderTimeMs;
233 }
234 if (_master)
235 {
236 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
237 "Render frame %u at %u. Render delay %u, required delay %u,"
238 " max decode time %u, min total delay %u",
239 frameTimestamp, MaskWord64ToUWord32(renderTimeMs), _renderDelayMs,
240 _requiredDelayMs, MaxDecodeTimeMs(),_minTotalDelayMs);
241 }
242 return renderTimeMs;
243}
244
245WebRtc_Word64
246VCMTiming::RenderTimeMsInternal(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const
247{
248 WebRtc_Word64 estimatedCompleteTimeMs =
249 _tsExtrapolator->ExtrapolateLocalTime(frameTimestamp);
250 if (estimatedCompleteTimeMs - nowMs > kMaxVideoDelayMs)
251 {
252 if (_master)
253 {
254 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
255 "Timestamp arrived 2 seconds early, reset statistics",
256 frameTimestamp, estimatedCompleteTimeMs);
257 }
258 return -1;
259 }
260 if (_master)
261 {
262 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
263 "ExtrapolateLocalTime(%u)=%u ms",
264 frameTimestamp, MaskWord64ToUWord32(estimatedCompleteTimeMs));
265 }
266 if (estimatedCompleteTimeMs == -1)
267 {
268 estimatedCompleteTimeMs = nowMs;
269 }
270
271 return estimatedCompleteTimeMs + _currentDelayMs;
272}
273
274// Must be called from inside a critical section
275WebRtc_Word32
276VCMTiming::MaxDecodeTimeMs(FrameType frameType /*= kVideoFrameDelta*/) const
277{
278 const WebRtc_Word32 decodeTimeMs = _codecTimer.RequiredDecodeTimeMs(frameType);
279
280 if (decodeTimeMs < 0)
281 {
282 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
283 "Negative maximum decode time: %d", decodeTimeMs);
284 return -1;
285 }
286 return decodeTimeMs;
287}
288
289WebRtc_UWord32
290VCMTiming::MaxWaitingTime(WebRtc_Word64 renderTimeMs, WebRtc_Word64 nowMs) const
291{
292 CriticalSectionScoped cs(_critSect);
293
294 const WebRtc_Word64 maxWaitTimeMs = renderTimeMs - nowMs -
295 MaxDecodeTimeMs() - _renderDelayMs;
296
297 if (maxWaitTimeMs < 0)
298 {
299 return 0;
300 }
301 return static_cast<WebRtc_UWord32>(maxWaitTimeMs);
302}
303
304bool
305VCMTiming::EnoughTimeToDecode(WebRtc_UWord32 availableProcessingTimeMs) const
306{
307 CriticalSectionScoped cs(_critSect);
308 WebRtc_Word32 maxDecodeTimeMs = MaxDecodeTimeMs();
309 if (maxDecodeTimeMs < 0)
310 {
311 // Haven't decoded any frames yet, try decoding one to get an estimate
312 // of the decode time.
313 return true;
314 }
315 else if (maxDecodeTimeMs == 0)
316 {
317 // Decode time is less than 1, set to 1 for now since
318 // we don't have any better precision. Count ticks later?
319 maxDecodeTimeMs = 1;
320 }
321 return static_cast<WebRtc_Word32>(availableProcessingTimeMs) - maxDecodeTimeMs > 0;
322}
323
324WebRtc_UWord32
325VCMTiming::TargetVideoDelay() const
326{
327 CriticalSectionScoped cs(_critSect);
328 return TargetDelayInternal();
329}
330
331WebRtc_UWord32
332VCMTiming::TargetDelayInternal() const
333{
334 return _requiredDelayMs + MaxDecodeTimeMs() + _renderDelayMs;
335}
336
337}