414 lines
15 KiB
C
414 lines
15 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
|