blob: 352e0507746c0509e6c54c196a618f730c0c5816 [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/*
12 * This file contains the function where the main decision logic for buffer level
13 * adaptation happens.
14 */
15
16#include "buffer_stats.h"
17
18#include <assert.h>
19
20#include "signal_processing_library.h"
21
22#include "automode.h"
23#include "neteq_defines.h"
24#include "neteq_error_codes.h"
25#include "webrtc_neteq.h"
26
27#define NETEQ_BUFSTAT_20MS_Q7 2560 /* = 20 ms in Q7 */
28
pbos@webrtc.org0946a562013-04-09 00:28:06 +000029uint16_t WebRtcNetEQ_BufstatsDecision(BufstatsInst_t *inst, int16_t frameSize,
30 int32_t cur_size, uint32_t targetTS,
31 uint32_t availableTS, int noPacket,
32 int cngPacket, int prevPlayMode,
33 enum WebRtcNetEQPlayoutMode playoutMode,
34 int timestampsPerCall, int NoOfExpandCalls,
35 int16_t fs_mult,
36 int16_t lastModeBGNonly, int playDtmf)
niklase@google.com470e71d2011-07-07 08:21:25 +000037{
38
39 int currentDelayMs;
pbos@webrtc.org0946a562013-04-09 00:28:06 +000040 int32_t currSizeSamples = cur_size;
turaj@webrtc.org6388c3e2013-02-12 21:42:18 +000041 int extraDelayPacketsQ8 = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +000042
43 /* Avoid overflow if the buffer size should be really large (cur_size is limited 256ms) */
pbos@webrtc.org0946a562013-04-09 00:28:06 +000044 int32_t curr_sizeQ7 = WEBRTC_SPL_LSHIFT_W32(cur_size, 4);
turaj@webrtc.org6388c3e2013-02-12 21:42:18 +000045 int level_limit_hi, level_limit_lo;
niklase@google.com470e71d2011-07-07 08:21:25 +000046
47 inst->Automode_inst.prevTimeScale &= (prevPlayMode == MODE_SUCCESS_ACCELERATE
48 || prevPlayMode == MODE_LOWEN_ACCELERATE || prevPlayMode == MODE_SUCCESS_PREEMPTIVE
49 || prevPlayMode == MODE_LOWEN_PREEMPTIVE);
50
51 if ((prevPlayMode != MODE_RFC3389CNG) && (prevPlayMode != MODE_CODEC_INTERNAL_CNG))
52 {
53 /*
54 * Do not update buffer history if currently playing CNG
55 * since it will bias the filtered buffer level.
56 */
57 WebRtcNetEQ_BufferLevelFilter(cur_size, &(inst->Automode_inst), timestampsPerCall,
58 fs_mult);
59 }
60 else
61 {
62 /* only update time counters */
63 inst->Automode_inst.packetIatCountSamp += timestampsPerCall; /* packet inter-arrival time */
64 inst->Automode_inst.peakIatCountSamp += timestampsPerCall; /* peak inter-arrival time */
65 inst->Automode_inst.timescaleHoldOff >>= 1; /* time-scaling limiter */
66 }
67 cur_size = WEBRTC_SPL_MIN(curr_sizeQ7, WEBRTC_SPL_WORD16_MAX);
68
69 /* Calculate VQmon related variables */
70 /* avgDelay = avgDelay*(511/512) + currentDelay*(1/512) (sample ms delay in Q8) */
pbos@webrtc.org0946a562013-04-09 00:28:06 +000071 inst->avgDelayMsQ8 = (int16_t) (WEBRTC_SPL_MUL_16_16_RSFT(inst->avgDelayMsQ8,511,9)
niklase@google.com470e71d2011-07-07 08:21:25 +000072 + (cur_size >> 9));
73
74 /* Update maximum delay if needed */
75 currentDelayMs = (curr_sizeQ7 >> 7);
76 if (currentDelayMs > inst->maxDelayMs)
77 {
78 inst->maxDelayMs = currentDelayMs;
79 }
80
81 /* NetEQ is on with normal or steaming mode */
82 if (playoutMode == kPlayoutOn || playoutMode == kPlayoutStreaming)
83 {
84 /* Guard for errors, so that it should not get stuck in error mode */
85 if (prevPlayMode == MODE_ERROR)
86 {
87 if (noPacket)
88 {
89 return BUFSTATS_DO_EXPAND;
90 }
91 else
92 {
93 return BUFSTAT_REINIT;
94 }
95 }
96
97 if (prevPlayMode != MODE_EXPAND && prevPlayMode != MODE_FADE_TO_BGN)
98 {
99 inst->w16_noExpand = 1;
100 }
101 else
102 {
103 inst->w16_noExpand = 0;
104 }
105
106 if (cngPacket)
107 {
108 /* signed difference between wanted and available TS */
pbos@webrtc.org0946a562013-04-09 00:28:06 +0000109 int32_t diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS;
henrik.lundin@webrtc.org46796522012-01-25 16:37:41 +0000110 int32_t optimal_level_samp = (inst->Automode_inst.optBufLevel *
111 inst->Automode_inst.packetSpeechLenSamp) >> 8;
112 int32_t excess_waiting_time_samp = -diffTS - optimal_level_samp;
113
114 if (excess_waiting_time_samp > optimal_level_samp / 2)
115 {
116 /* The waiting time for this packet will be longer than 1.5
henrik.lundin@webrtc.orgdcf00642012-01-25 16:48:00 +0000117 * times the wanted buffer delay. Advance the clock to cut
henrik.lundin@webrtc.org46796522012-01-25 16:37:41 +0000118 * waiting time down to the optimal.
119 */
120 inst->uw32_CNGplayedTS += excess_waiting_time_samp;
121 diffTS += excess_waiting_time_samp;
122 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000123
124 if ((diffTS) < 0 && (prevPlayMode == MODE_RFC3389CNG))
125 {
126 /* Not time to play this packet yet. Wait another round before using this
127 * packet. Keep on playing CNG from previous CNG parameters. */
128 return BUFSTATS_DO_RFC3389CNG_NOPACKET;
129 }
130
131 /* otherwise, go for the CNG packet now */
132 return BUFSTATS_DO_RFC3389CNG_PACKET;
133 }
134
135 /*Check for expand/cng */
136 if (noPacket)
137 {
138 if (inst->w16_cngOn == CNG_RFC3389_ON)
139 {
140 /* keep on playing CNG */
141 return BUFSTATS_DO_RFC3389CNG_NOPACKET;
142 }
143 else if (inst->w16_cngOn == CNG_INTERNAL_ON)
144 {
145 /* keep on playing internal CNG */
146 return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
147 }
148 else if (playDtmf == 1)
149 {
150 /* we have not audio data, but can play DTMF */
151 return BUFSTATS_DO_DTMF_ONLY;
152 }
153 else
154 {
155 /* nothing to play => do Expand */
156 return BUFSTATS_DO_EXPAND;
157 }
158 }
159
160 /*
161 * If the expand period was very long, reset NetEQ since it is likely that the
162 * sender was restarted.
163 */
164 if (NoOfExpandCalls > REINIT_AFTER_EXPANDS) return BUFSTAT_REINIT_DECODER;
165
166 /* Calculate extra delay in Q8 packets */
167 if (inst->Automode_inst.extraDelayMs > 0 && inst->Automode_inst.packetSpeechLenSamp
168 > 0)
169 {
turaj@webrtc.org6388c3e2013-02-12 21:42:18 +0000170
niklase@google.com470e71d2011-07-07 08:21:25 +0000171 /* (extra delay in samples in Q8) */
turaj@webrtc.org6388c3e2013-02-12 21:42:18 +0000172 extraDelayPacketsQ8 =
173 ((inst->Automode_inst.extraDelayMs * 8 * fs_mult) << 8) /
174 inst->Automode_inst.packetSpeechLenSamp;
niklase@google.com470e71d2011-07-07 08:21:25 +0000175 }
176
177 /* Check if needed packet is available */
178 if (targetTS == availableTS)
179 {
180
181 /* If last mode was not expand, and there is no DTMF to play */
182 if (inst->w16_noExpand == 1 && playDtmf == 0)
183 {
184 /* If so check for accelerate */
185
186 level_limit_lo = ((inst->Automode_inst.optBufLevel) >> 1) /* 50 % */
187 + ((inst->Automode_inst.optBufLevel) >> 2); /* ... + 25% = 75% */
188
189 /* set upper limit to optBufLevel, but make sure that window is at least 20ms */
190 level_limit_hi = WEBRTC_SPL_MAX(inst->Automode_inst.optBufLevel,
191 level_limit_lo +
192 WebRtcSpl_DivW32W16ResW16((WEBRTC_SPL_MUL(20*8, fs_mult) << 8),
193 inst->Automode_inst.packetSpeechLenSamp));
194
195 /* if extra delay is non-zero, add it */
196 if (extraDelayPacketsQ8 > 0)
197 {
198 level_limit_hi += extraDelayPacketsQ8;
199 level_limit_lo += extraDelayPacketsQ8;
200 }
201
202 if (((inst->Automode_inst.buffLevelFilt >= level_limit_hi) &&
203 (inst->Automode_inst.timescaleHoldOff == 0)) ||
204 (inst->Automode_inst.buffLevelFilt >= level_limit_hi << 2))
205 {
206 /*
207 * Buffer level higher than limit and time-scaling allowed,
208 * OR buffer level _really_ high.
209 */
210 return BUFSTATS_DO_ACCELERATE;
211 }
212 else if ((inst->Automode_inst.buffLevelFilt < level_limit_lo)
213 && (inst->Automode_inst.timescaleHoldOff == 0))
214 {
215 return BUFSTATS_DO_PREEMPTIVE_EXPAND;
216 }
217 }
218 return BUFSTATS_DO_NORMAL;
219 }
220
221 /* Check for Merge */
222 else if (availableTS > targetTS)
223 {
224
225 /* Check that we do not play a packet "too early" */
226 if ((prevPlayMode == MODE_EXPAND)
227 && (availableTS - targetTS
pbos@webrtc.org0946a562013-04-09 00:28:06 +0000228 < (uint32_t) WEBRTC_SPL_MUL_16_16((int16_t)timestampsPerCall,
229 (int16_t)REINIT_AFTER_EXPANDS))
niklase@google.com470e71d2011-07-07 08:21:25 +0000230 && (NoOfExpandCalls < MAX_WAIT_FOR_PACKET)
231 && (availableTS
232 > targetTS
pbos@webrtc.org0946a562013-04-09 00:28:06 +0000233 + WEBRTC_SPL_MUL_16_16((int16_t)timestampsPerCall,
234 (int16_t)NoOfExpandCalls))
niklase@google.com470e71d2011-07-07 08:21:25 +0000235 && (inst->Automode_inst.buffLevelFilt <= inst->Automode_inst.optBufLevel
236 + extraDelayPacketsQ8))
237 {
238 if (playDtmf == 1)
239 {
240 /* we still have DTMF to play, so do not perform expand */
241 return BUFSTATS_DO_DTMF_ONLY;
242 }
243 else
244 {
245 /* nothing to play */
246 return BUFSTATS_DO_EXPAND;
247 }
248 }
249
250 /* If previous was CNG period or BGNonly then no merge is needed */
251 if ((prevPlayMode == MODE_RFC3389CNG) || (prevPlayMode == MODE_CODEC_INTERNAL_CNG)
252 || lastModeBGNonly)
253 {
254 /*
255 * Keep the same delay as before the CNG (or maximum 70 ms in buffer as safety
256 * precaution), but make sure that the number of samples in buffer is no
257 * higher than 4 times the optimal level.
258 */
pbos@webrtc.org0946a562013-04-09 00:28:06 +0000259 int32_t diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS;
turaj@webrtc.org6388c3e2013-02-12 21:42:18 +0000260 int val = ((inst->Automode_inst.optBufLevel +
261 extraDelayPacketsQ8) *
262 inst->Automode_inst.packetSpeechLenSamp) >> 6;
263 if (diffTS >= 0 || val < currSizeSamples)
niklase@google.com470e71d2011-07-07 08:21:25 +0000264 {
265 /* it is time to play this new packet */
266 return BUFSTATS_DO_NORMAL;
267 }
268 else
269 {
270 /* it is too early to play this new packet => keep on playing CNG */
271 if (prevPlayMode == MODE_RFC3389CNG)
272 {
273 return BUFSTATS_DO_RFC3389CNG_NOPACKET;
274 }
275 else if (prevPlayMode == MODE_CODEC_INTERNAL_CNG)
276 {
277 return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
278 }
279 else if (playDtmf == 1)
280 {
281 /* we have not audio data, but can play DTMF */
282 return BUFSTATS_DO_DTMF_ONLY;
283 }
284 else /* lastModeBGNonly */
285 {
286 /* signal expand, but this will result in BGN again */
287 return BUFSTATS_DO_EXPAND;
288 }
289 }
290 }
291
292 /* Do not merge unless we have done a Expand before (for complexity reasons) */
293 if ((inst->w16_noExpand == 0) || ((frameSize < timestampsPerCall) && (cur_size
294 > NETEQ_BUFSTAT_20MS_Q7)))
295 {
296 return BUFSTATS_DO_MERGE;
297 }
298 else if (playDtmf == 1)
299 {
300 /* play DTMF instead of expand */
301 return BUFSTATS_DO_DTMF_ONLY;
302 }
303 else
304 {
305 return BUFSTATS_DO_EXPAND;
306 }
307 }
308 }
309 else
310 { /* kPlayoutOff or kPlayoutFax */
311 if (cngPacket)
312 {
pbos@webrtc.org0946a562013-04-09 00:28:06 +0000313 if (((int32_t) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000314 {
315 /* time to play this packet now */
316 return BUFSTATS_DO_RFC3389CNG_PACKET;
317 }
318 else
319 {
320 /* wait before playing this packet */
321 return BUFSTATS_DO_RFC3389CNG_NOPACKET;
322 }
323 }
324 if (noPacket)
325 {
326 /*
327 * No packet =>
328 * 1. If in CNG mode play as usual
329 * 2. Otherwise use other method to generate data and hold TS value
330 */
331 if (inst->w16_cngOn == CNG_RFC3389_ON)
332 {
333 /* keep on playing CNG */
334 return BUFSTATS_DO_RFC3389CNG_NOPACKET;
335 }
336 else if (inst->w16_cngOn == CNG_INTERNAL_ON)
337 {
338 /* keep on playing internal CNG */
339 return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
340 }
341 else
342 {
343 /* nothing to play => invent some data to play out */
344 if (playoutMode == kPlayoutOff)
345 {
346 return BUFSTATS_DO_ALTERNATIVE_PLC;
347 }
348 else if (playoutMode == kPlayoutFax)
349 {
350 return BUFSTATS_DO_AUDIO_REPETITION;
351 }
352 else
353 {
354 /* UNDEFINED, should not get here... */
355 assert(0);
356 return BUFSTAT_REINIT;
357 }
358 }
359 }
360 else if (targetTS == availableTS)
361 {
362 return BUFSTATS_DO_NORMAL;
363 }
364 else
365 {
pbos@webrtc.org0946a562013-04-09 00:28:06 +0000366 if (((int32_t) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000367 {
368 return BUFSTATS_DO_NORMAL;
369 }
370 else if (playoutMode == kPlayoutOff)
371 {
372 /*
373 * If currently playing CNG, continue with that. Don't increase TS
374 * since uw32_CNGplayedTS will be increased.
375 */
376 if (inst->w16_cngOn == CNG_RFC3389_ON)
377 {
378 return BUFSTATS_DO_RFC3389CNG_NOPACKET;
379 }
380 else if (inst->w16_cngOn == CNG_INTERNAL_ON)
381 {
382 return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
383 }
384 else
385 {
386 /*
387 * Otherwise, do PLC and increase TS while waiting for the time to
388 * play this packet.
389 */
390 return BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS;
391 }
392 }
393 else if (playoutMode == kPlayoutFax)
394 {
395 /*
396 * If currently playing CNG, continue with that don't increase TS since
397 * uw32_CNGplayedTS will be increased.
398 */
399 if (inst->w16_cngOn == CNG_RFC3389_ON)
400 {
401 return BUFSTATS_DO_RFC3389CNG_NOPACKET;
402 }
403 else if (inst->w16_cngOn == CNG_INTERNAL_ON)
404 {
405 return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
406 }
407 else
408 {
409 /*
410 * Otherwise, do audio repetition and increase TS while waiting for the
411 * time to play this packet.
412 */
413 return BUFSTATS_DO_AUDIO_REPETITION_INC_TS;
414 }
415 }
416 else
417 {
418 /* UNDEFINED, should not get here... */
419 assert(0);
420 return BUFSTAT_REINIT;
421 }
422 }
423 }
424 /* We should not get here (but sometimes we do anyway...) */
425 return BUFSTAT_REINIT;
426}
427