blob: 9dae39389ecbd51206c9792203d819faa798792d [file] [log] [blame]
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* This file contains the function where the main decision logic for buffer level
* adaptation happens.
*/
#include "buffer_stats.h"
#include <assert.h>
#include "signal_processing_library.h"
#include "automode.h"
#include "neteq_defines.h"
#include "neteq_error_codes.h"
#include "webrtc_neteq.h"
#define NETEQ_BUFSTAT_20MS_Q7 2560 /* = 20 ms in Q7 */
WebRtc_UWord16 WebRtcNetEQ_BufstatsDecision(BufstatsInst_t *inst, WebRtc_Word16 frameSize,
WebRtc_Word32 cur_size, WebRtc_UWord32 targetTS,
WebRtc_UWord32 availableTS, int noPacket,
int cngPacket, int prevPlayMode,
enum WebRtcNetEQPlayoutMode playoutMode,
int timestampsPerCall, int NoOfExpandCalls,
WebRtc_Word16 fs_mult,
WebRtc_Word16 lastModeBGNonly, int playDtmf)
{
int currentDelayMs;
WebRtc_Word32 currSizeSamples = cur_size;
WebRtc_Word16 extraDelayPacketsQ8 = 0;
/* Avoid overflow if the buffer size should be really large (cur_size is limited 256ms) */
WebRtc_Word32 curr_sizeQ7 = WEBRTC_SPL_LSHIFT_W32(cur_size, 4);
WebRtc_UWord16 level_limit_hi, level_limit_lo;
inst->Automode_inst.prevTimeScale &= (prevPlayMode == MODE_SUCCESS_ACCELERATE
|| prevPlayMode == MODE_LOWEN_ACCELERATE || prevPlayMode == MODE_SUCCESS_PREEMPTIVE
|| prevPlayMode == MODE_LOWEN_PREEMPTIVE);
if ((prevPlayMode != MODE_RFC3389CNG) && (prevPlayMode != MODE_CODEC_INTERNAL_CNG))
{
/*
* Do not update buffer history if currently playing CNG
* since it will bias the filtered buffer level.
*/
WebRtcNetEQ_BufferLevelFilter(cur_size, &(inst->Automode_inst), timestampsPerCall,
fs_mult);
}
else
{
/* only update time counters */
inst->Automode_inst.packetIatCountSamp += timestampsPerCall; /* packet inter-arrival time */
inst->Automode_inst.peakIatCountSamp += timestampsPerCall; /* peak inter-arrival time */
inst->Automode_inst.timescaleHoldOff >>= 1; /* time-scaling limiter */
}
cur_size = WEBRTC_SPL_MIN(curr_sizeQ7, WEBRTC_SPL_WORD16_MAX);
/* Calculate VQmon related variables */
/* avgDelay = avgDelay*(511/512) + currentDelay*(1/512) (sample ms delay in Q8) */
inst->avgDelayMsQ8 = (WebRtc_Word16) (WEBRTC_SPL_MUL_16_16_RSFT(inst->avgDelayMsQ8,511,9)
+ (cur_size >> 9));
/* Update maximum delay if needed */
currentDelayMs = (curr_sizeQ7 >> 7);
if (currentDelayMs > inst->maxDelayMs)
{
inst->maxDelayMs = currentDelayMs;
}
/* NetEQ is on with normal or steaming mode */
if (playoutMode == kPlayoutOn || playoutMode == kPlayoutStreaming)
{
/* Guard for errors, so that it should not get stuck in error mode */
if (prevPlayMode == MODE_ERROR)
{
if (noPacket)
{
return BUFSTATS_DO_EXPAND;
}
else
{
return BUFSTAT_REINIT;
}
}
if (prevPlayMode != MODE_EXPAND && prevPlayMode != MODE_FADE_TO_BGN)
{
inst->w16_noExpand = 1;
}
else
{
inst->w16_noExpand = 0;
}
if (cngPacket)
{
/* signed difference between wanted and available TS */
WebRtc_Word32 diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS;
if ((diffTS) < 0 && (prevPlayMode == MODE_RFC3389CNG))
{
/* Not time to play this packet yet. Wait another round before using this
* packet. Keep on playing CNG from previous CNG parameters. */
return BUFSTATS_DO_RFC3389CNG_NOPACKET;
}
/* otherwise, go for the CNG packet now */
return BUFSTATS_DO_RFC3389CNG_PACKET;
}
/*Check for expand/cng */
if (noPacket)
{
if (inst->w16_cngOn == CNG_RFC3389_ON)
{
/* keep on playing CNG */
return BUFSTATS_DO_RFC3389CNG_NOPACKET;
}
else if (inst->w16_cngOn == CNG_INTERNAL_ON)
{
/* keep on playing internal CNG */
return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
}
else if (playDtmf == 1)
{
/* we have not audio data, but can play DTMF */
return BUFSTATS_DO_DTMF_ONLY;
}
else
{
/* nothing to play => do Expand */
return BUFSTATS_DO_EXPAND;
}
}
/*
* If the expand period was very long, reset NetEQ since it is likely that the
* sender was restarted.
*/
if (NoOfExpandCalls > REINIT_AFTER_EXPANDS) return BUFSTAT_REINIT_DECODER;
/* Calculate extra delay in Q8 packets */
if (inst->Automode_inst.extraDelayMs > 0 && inst->Automode_inst.packetSpeechLenSamp
> 0)
{
extraDelayPacketsQ8 = WebRtcSpl_DivW32W16ResW16(
(WEBRTC_SPL_MUL(inst->Automode_inst.extraDelayMs, 8 * fs_mult) << 8),
inst->Automode_inst.packetSpeechLenSamp);
/* (extra delay in samples in Q8) */
}
/* Check if needed packet is available */
if (targetTS == availableTS)
{
/* If last mode was not expand, and there is no DTMF to play */
if (inst->w16_noExpand == 1 && playDtmf == 0)
{
/* If so check for accelerate */
level_limit_lo = ((inst->Automode_inst.optBufLevel) >> 1) /* 50 % */
+ ((inst->Automode_inst.optBufLevel) >> 2); /* ... + 25% = 75% */
/* set upper limit to optBufLevel, but make sure that window is at least 20ms */
level_limit_hi = WEBRTC_SPL_MAX(inst->Automode_inst.optBufLevel,
level_limit_lo +
WebRtcSpl_DivW32W16ResW16((WEBRTC_SPL_MUL(20*8, fs_mult) << 8),
inst->Automode_inst.packetSpeechLenSamp));
/* if extra delay is non-zero, add it */
if (extraDelayPacketsQ8 > 0)
{
level_limit_hi += extraDelayPacketsQ8;
level_limit_lo += extraDelayPacketsQ8;
}
if (((inst->Automode_inst.buffLevelFilt >= level_limit_hi) &&
(inst->Automode_inst.timescaleHoldOff == 0)) ||
(inst->Automode_inst.buffLevelFilt >= level_limit_hi << 2))
{
/*
* Buffer level higher than limit and time-scaling allowed,
* OR buffer level _really_ high.
*/
return BUFSTATS_DO_ACCELERATE;
}
else if ((inst->Automode_inst.buffLevelFilt < level_limit_lo)
&& (inst->Automode_inst.timescaleHoldOff == 0))
{
return BUFSTATS_DO_PREEMPTIVE_EXPAND;
}
}
return BUFSTATS_DO_NORMAL;
}
/* Check for Merge */
else if (availableTS > targetTS)
{
/* Check that we do not play a packet "too early" */
if ((prevPlayMode == MODE_EXPAND)
&& (availableTS - targetTS
< (WebRtc_UWord32) WEBRTC_SPL_MUL_16_16((WebRtc_Word16)timestampsPerCall,
(WebRtc_Word16)REINIT_AFTER_EXPANDS))
&& (NoOfExpandCalls < MAX_WAIT_FOR_PACKET)
&& (availableTS
> targetTS
+ WEBRTC_SPL_MUL_16_16((WebRtc_Word16)timestampsPerCall,
(WebRtc_Word16)NoOfExpandCalls))
&& (inst->Automode_inst.buffLevelFilt <= inst->Automode_inst.optBufLevel
+ extraDelayPacketsQ8))
{
if (playDtmf == 1)
{
/* we still have DTMF to play, so do not perform expand */
return BUFSTATS_DO_DTMF_ONLY;
}
else
{
/* nothing to play */
return BUFSTATS_DO_EXPAND;
}
}
/* If previous was CNG period or BGNonly then no merge is needed */
if ((prevPlayMode == MODE_RFC3389CNG) || (prevPlayMode == MODE_CODEC_INTERNAL_CNG)
|| lastModeBGNonly)
{
/*
* Keep the same delay as before the CNG (or maximum 70 ms in buffer as safety
* precaution), but make sure that the number of samples in buffer is no
* higher than 4 times the optimal level.
*/
WebRtc_Word32 diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS;
if (diffTS >= 0
|| (WEBRTC_SPL_MUL_16_16_RSFT( inst->Automode_inst.optBufLevel
+ extraDelayPacketsQ8,
inst->Automode_inst.packetSpeechLenSamp, 6) < currSizeSamples))
{
/* it is time to play this new packet */
return BUFSTATS_DO_NORMAL;
}
else
{
/* it is too early to play this new packet => keep on playing CNG */
if (prevPlayMode == MODE_RFC3389CNG)
{
return BUFSTATS_DO_RFC3389CNG_NOPACKET;
}
else if (prevPlayMode == MODE_CODEC_INTERNAL_CNG)
{
return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
}
else if (playDtmf == 1)
{
/* we have not audio data, but can play DTMF */
return BUFSTATS_DO_DTMF_ONLY;
}
else /* lastModeBGNonly */
{
/* signal expand, but this will result in BGN again */
return BUFSTATS_DO_EXPAND;
}
}
}
/* Do not merge unless we have done a Expand before (for complexity reasons) */
if ((inst->w16_noExpand == 0) || ((frameSize < timestampsPerCall) && (cur_size
> NETEQ_BUFSTAT_20MS_Q7)))
{
return BUFSTATS_DO_MERGE;
}
else if (playDtmf == 1)
{
/* play DTMF instead of expand */
return BUFSTATS_DO_DTMF_ONLY;
}
else
{
return BUFSTATS_DO_EXPAND;
}
}
}
else
{ /* kPlayoutOff or kPlayoutFax */
if (cngPacket)
{
if (((WebRtc_Word32) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0)
{
/* time to play this packet now */
return BUFSTATS_DO_RFC3389CNG_PACKET;
}
else
{
/* wait before playing this packet */
return BUFSTATS_DO_RFC3389CNG_NOPACKET;
}
}
if (noPacket)
{
/*
* No packet =>
* 1. If in CNG mode play as usual
* 2. Otherwise use other method to generate data and hold TS value
*/
if (inst->w16_cngOn == CNG_RFC3389_ON)
{
/* keep on playing CNG */
return BUFSTATS_DO_RFC3389CNG_NOPACKET;
}
else if (inst->w16_cngOn == CNG_INTERNAL_ON)
{
/* keep on playing internal CNG */
return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
}
else
{
/* nothing to play => invent some data to play out */
if (playoutMode == kPlayoutOff)
{
return BUFSTATS_DO_ALTERNATIVE_PLC;
}
else if (playoutMode == kPlayoutFax)
{
return BUFSTATS_DO_AUDIO_REPETITION;
}
else
{
/* UNDEFINED, should not get here... */
assert(0);
return BUFSTAT_REINIT;
}
}
}
else if (targetTS == availableTS)
{
return BUFSTATS_DO_NORMAL;
}
else
{
if (((WebRtc_Word32) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0)
{
return BUFSTATS_DO_NORMAL;
}
else if (playoutMode == kPlayoutOff)
{
/*
* If currently playing CNG, continue with that. Don't increase TS
* since uw32_CNGplayedTS will be increased.
*/
if (inst->w16_cngOn == CNG_RFC3389_ON)
{
return BUFSTATS_DO_RFC3389CNG_NOPACKET;
}
else if (inst->w16_cngOn == CNG_INTERNAL_ON)
{
return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
}
else
{
/*
* Otherwise, do PLC and increase TS while waiting for the time to
* play this packet.
*/
return BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS;
}
}
else if (playoutMode == kPlayoutFax)
{
/*
* If currently playing CNG, continue with that don't increase TS since
* uw32_CNGplayedTS will be increased.
*/
if (inst->w16_cngOn == CNG_RFC3389_ON)
{
return BUFSTATS_DO_RFC3389CNG_NOPACKET;
}
else if (inst->w16_cngOn == CNG_INTERNAL_ON)
{
return BUFSTATS_DO_INTERNAL_CNG_NOPACKET;
}
else
{
/*
* Otherwise, do audio repetition and increase TS while waiting for the
* time to play this packet.
*/
return BUFSTATS_DO_AUDIO_REPETITION_INC_TS;
}
}
else
{
/* UNDEFINED, should not get here... */
assert(0);
return BUFSTAT_REINIT;
}
}
}
/* We should not get here (but sometimes we do anyway...) */
return BUFSTAT_REINIT;
}