Added configuration of max delay to ACM and NetEq
R=turaj@webrtc.org Review URL: https://webrtc-codereview.appspot.com/1964004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4499 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@@ -660,6 +660,19 @@ class AudioCodingModule: public Module {
|
|||||||
//
|
//
|
||||||
virtual int SetMinimumPlayoutDelay(int time_ms) = 0;
|
virtual int SetMinimumPlayoutDelay(int time_ms) = 0;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// int SetMaximumPlayoutDelay()
|
||||||
|
// Set a maximum for the playout delay
|
||||||
|
//
|
||||||
|
// Input:
|
||||||
|
// -time_ms : maximum delay in milliseconds.
|
||||||
|
//
|
||||||
|
// Return value:
|
||||||
|
// -1 if failed to set the delay,
|
||||||
|
// 0 if the maximum delay is set.
|
||||||
|
//
|
||||||
|
virtual int SetMaximumPlayoutDelay(int time_ms) = 0;
|
||||||
|
|
||||||
//
|
//
|
||||||
// The shortest latency, in milliseconds, required by jitter buffer. This
|
// The shortest latency, in milliseconds, required by jitter buffer. This
|
||||||
// is computed based on inter-arrival times and playout mode of NetEq. The
|
// is computed based on inter-arrival times and playout mode of NetEq. The
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ ACMNetEQ::ACMNetEQ()
|
|||||||
min_of_buffer_size_bytes_(0),
|
min_of_buffer_size_bytes_(0),
|
||||||
per_packet_overhead_bytes_(0),
|
per_packet_overhead_bytes_(0),
|
||||||
av_sync_(false),
|
av_sync_(false),
|
||||||
minimum_delay_ms_(0) {
|
minimum_delay_ms_(0),
|
||||||
|
maximum_delay_ms_(0) {
|
||||||
for (int n = 0; n < MAX_NUM_SLAVE_NETEQ + 1; n++) {
|
for (int n = 0; n < MAX_NUM_SLAVE_NETEQ + 1; n++) {
|
||||||
is_initialized_[n] = false;
|
is_initialized_[n] = false;
|
||||||
ptr_vadinst_[n] = NULL;
|
ptr_vadinst_[n] = NULL;
|
||||||
@@ -1074,6 +1075,10 @@ int16_t ACMNetEQ::AddSlave(const WebRtcNetEQDecoder* used_codecs,
|
|||||||
// Set minimum delay.
|
// Set minimum delay.
|
||||||
if (minimum_delay_ms_ > 0)
|
if (minimum_delay_ms_ > 0)
|
||||||
WebRtcNetEQ_SetMinimumDelay(inst_[slave_idx], minimum_delay_ms_);
|
WebRtcNetEQ_SetMinimumDelay(inst_[slave_idx], minimum_delay_ms_);
|
||||||
|
|
||||||
|
// Set maximum delay.
|
||||||
|
if (maximum_delay_ms_ > 0)
|
||||||
|
WebRtcNetEQ_SetMaximumDelay(inst_[slave_idx], maximum_delay_ms_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1109,6 +1114,17 @@ int ACMNetEQ::SetMinimumDelay(int minimum_delay_ms) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ACMNetEQ::SetMaximumDelay(int maximum_delay_ms) {
|
||||||
|
CriticalSectionScoped lock(neteq_crit_sect_);
|
||||||
|
for (int i = 0; i < num_slaves_ + 1; ++i) {
|
||||||
|
assert(is_initialized_[i]);
|
||||||
|
if (WebRtcNetEQ_SetMaximumDelay(inst_[i], maximum_delay_ms) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
maximum_delay_ms_ = maximum_delay_ms;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ACMNetEQ::LeastRequiredDelayMs() const {
|
int ACMNetEQ::LeastRequiredDelayMs() const {
|
||||||
CriticalSectionScoped lock(neteq_crit_sect_);
|
CriticalSectionScoped lock(neteq_crit_sect_);
|
||||||
assert(is_initialized_[0]);
|
assert(is_initialized_[0]);
|
||||||
|
|||||||
@@ -300,6 +300,11 @@ class ACMNetEQ {
|
|||||||
//
|
//
|
||||||
int SetMinimumDelay(int minimum_delay_ms);
|
int SetMinimumDelay(int minimum_delay_ms);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Set a maximum delay in NetEq.
|
||||||
|
//
|
||||||
|
int SetMaximumDelay(int maximum_delay_ms);
|
||||||
|
|
||||||
//
|
//
|
||||||
// The shortest latency, in milliseconds, required by jitter buffer. This
|
// The shortest latency, in milliseconds, required by jitter buffer. This
|
||||||
// is computed based on inter-arrival times and playout mode of NetEq. The
|
// is computed based on inter-arrival times and playout mode of NetEq. The
|
||||||
@@ -384,6 +389,7 @@ class ACMNetEQ {
|
|||||||
bool av_sync_;
|
bool av_sync_;
|
||||||
|
|
||||||
int minimum_delay_ms_;
|
int minimum_delay_ms_;
|
||||||
|
int maximum_delay_ms_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@@ -2205,13 +2205,7 @@ int AudioCodingModuleImpl::InitStereoSlave() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimum playout delay (Used for lip-sync).
|
|
||||||
int AudioCodingModuleImpl::SetMinimumPlayoutDelay(int time_ms) {
|
int AudioCodingModuleImpl::SetMinimumPlayoutDelay(int time_ms) {
|
||||||
if ((time_ms < 0) || (time_ms > 10000)) {
|
|
||||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_,
|
|
||||||
"Delay must be in the range of 0-10000 milliseconds.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
CriticalSectionScoped lock(acm_crit_sect_);
|
CriticalSectionScoped lock(acm_crit_sect_);
|
||||||
// Don't let the extra delay modified while accumulating buffers in NetEq.
|
// Don't let the extra delay modified while accumulating buffers in NetEq.
|
||||||
@@ -2221,6 +2215,10 @@ int AudioCodingModuleImpl::SetMinimumPlayoutDelay(int time_ms) {
|
|||||||
return neteq_.SetMinimumDelay(time_ms);
|
return neteq_.SetMinimumDelay(time_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AudioCodingModuleImpl::SetMaximumPlayoutDelay(int time_ms) {
|
||||||
|
return neteq_.SetMaximumDelay(time_ms);
|
||||||
|
}
|
||||||
|
|
||||||
// Get Dtmf playout status.
|
// Get Dtmf playout status.
|
||||||
bool AudioCodingModuleImpl::DtmfPlayoutStatus() const {
|
bool AudioCodingModuleImpl::DtmfPlayoutStatus() const {
|
||||||
#ifndef WEBRTC_CODEC_AVT
|
#ifndef WEBRTC_CODEC_AVT
|
||||||
|
|||||||
@@ -175,7 +175,10 @@ class AudioCodingModuleImpl : public AudioCodingModule {
|
|||||||
// is the max of |time_ms| and the required delay dictated by the channel.
|
// is the max of |time_ms| and the required delay dictated by the channel.
|
||||||
int SetMinimumPlayoutDelay(int time_ms);
|
int SetMinimumPlayoutDelay(int time_ms);
|
||||||
|
|
||||||
//
|
// NetEq maximum playout delay. The actual target delay is the min of
|
||||||
|
// |time_ms| and the required delay dictated by the channel.
|
||||||
|
int SetMaximumPlayoutDelay(int time_ms);
|
||||||
|
|
||||||
// The shortest latency, in milliseconds, required by jitter buffer. This
|
// The shortest latency, in milliseconds, required by jitter buffer. This
|
||||||
// is computed based on inter-arrival times and playout mode of NetEq. The
|
// is computed based on inter-arrival times and playout mode of NetEq. The
|
||||||
// actual delay is the maximum of least-required-delay and the minimum-delay
|
// actual delay is the maximum of least-required-delay and the minimum-delay
|
||||||
|
|||||||
@@ -94,6 +94,10 @@ class TargetDelayTest : public ::testing::Test {
|
|||||||
return acm_->SetMinimumPlayoutDelay(delay_ms);
|
return acm_->SetMinimumPlayoutDelay(delay_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SetMaximumDelay(int delay_ms) {
|
||||||
|
return acm_->SetMaximumPlayoutDelay(delay_ms);
|
||||||
|
}
|
||||||
|
|
||||||
int GetCurrentOptimalDelayMs() {
|
int GetCurrentOptimalDelayMs() {
|
||||||
ACMNetworkStatistics stats;
|
ACMNetworkStatistics stats;
|
||||||
acm_->NetworkStatistics(&stats);
|
acm_->NetworkStatistics(&stats);
|
||||||
@@ -170,4 +174,21 @@ TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(RequiredDelayAtCorrectRange)) {
|
|||||||
required_delay, 1);
|
required_delay, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(TargetDelayBufferMinMax)) {
|
||||||
|
const int kTargetMinDelayMs = kNum10msPerFrame * 10;
|
||||||
|
ASSERT_EQ(0, SetMinimumDelay(kTargetMinDelayMs));
|
||||||
|
for (int m = 0; m < 30; ++m) // Run enough iterations to fill up the buffer.
|
||||||
|
Run(true);
|
||||||
|
int clean_optimal_delay = GetCurrentOptimalDelayMs();
|
||||||
|
EXPECT_EQ(kTargetMinDelayMs, clean_optimal_delay);
|
||||||
|
|
||||||
|
const int kTargetMaxDelayMs = 2 * (kNum10msPerFrame * 10);
|
||||||
|
ASSERT_EQ(0, SetMaximumDelay(kTargetMaxDelayMs));
|
||||||
|
for (int n = 0; n < 30; ++n) // Run enough iterations to fill up the buffer.
|
||||||
|
Run(false);
|
||||||
|
|
||||||
|
int capped_optimal_delay = GetCurrentOptimalDelayMs();
|
||||||
|
EXPECT_EQ(kTargetMaxDelayMs, capped_optimal_delay);
|
||||||
|
}
|
||||||
|
|
||||||
} // webrtc
|
} // webrtc
|
||||||
|
|||||||
@@ -224,6 +224,10 @@ int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen,
|
|||||||
*/
|
*/
|
||||||
int32_t minimum_delay_q8 = ((inst->minimum_delay_ms *
|
int32_t minimum_delay_q8 = ((inst->minimum_delay_ms *
|
||||||
(fsHz / 1000)) << 8) / packetLenSamp;
|
(fsHz / 1000)) << 8) / packetLenSamp;
|
||||||
|
|
||||||
|
int32_t maximum_delay_q8 = ((inst->maximum_delay_ms *
|
||||||
|
(fsHz / 1000)) << 8) / packetLenSamp;
|
||||||
|
|
||||||
inst->optBufLevel = tempvar;
|
inst->optBufLevel = tempvar;
|
||||||
|
|
||||||
if (streamingMode != 0)
|
if (streamingMode != 0)
|
||||||
@@ -239,6 +243,12 @@ int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen,
|
|||||||
inst->optBufLevel = WEBRTC_SPL_MAX(inst->optBufLevel,
|
inst->optBufLevel = WEBRTC_SPL_MAX(inst->optBufLevel,
|
||||||
minimum_delay_q8);
|
minimum_delay_q8);
|
||||||
|
|
||||||
|
if (maximum_delay_q8 > 0) {
|
||||||
|
// Make sure that max is at least one packet length.
|
||||||
|
maximum_delay_q8 = WEBRTC_SPL_MAX(maximum_delay_q8, (1 << 8));
|
||||||
|
inst->optBufLevel = WEBRTC_SPL_MIN(inst->optBufLevel,
|
||||||
|
maximum_delay_q8);
|
||||||
|
}
|
||||||
/*********/
|
/*********/
|
||||||
/* Limit */
|
/* Limit */
|
||||||
/*********/
|
/*********/
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ typedef struct
|
|||||||
|
|
||||||
int minimum_delay_ms; /* Desired delay, NetEq maintains this amount of
|
int minimum_delay_ms; /* Desired delay, NetEq maintains this amount of
|
||||||
delay unless jitter statistics suggests a higher value. */
|
delay unless jitter statistics suggests a higher value. */
|
||||||
|
int maximum_delay_ms; /* Max desired delay, NetEq will not go above this
|
||||||
|
amount of delay even if jitter statistics suggests a higher value. */
|
||||||
|
|
||||||
int required_delay_q8; /* Smallest delay required. This is computed
|
int required_delay_q8; /* Smallest delay required. This is computed
|
||||||
according to inter-arrival time and playout mode. It has the same unit
|
according to inter-arrival time and playout mode. It has the same unit
|
||||||
as |optBufLevel|. */
|
as |optBufLevel|. */
|
||||||
@@ -121,7 +124,6 @@ typedef struct
|
|||||||
int16_t cSumIatQ8; /* cumulative sum of inter-arrival times */
|
int16_t cSumIatQ8; /* cumulative sum of inter-arrival times */
|
||||||
int16_t maxCSumIatQ8; /* max cumulative sum IAT */
|
int16_t maxCSumIatQ8; /* max cumulative sum IAT */
|
||||||
uint32_t maxCSumUpdateTimer;/* time elapsed since maximum was observed */
|
uint32_t maxCSumUpdateTimer;/* time elapsed since maximum was observed */
|
||||||
|
|
||||||
} AutomodeInst_t;
|
} AutomodeInst_t;
|
||||||
|
|
||||||
/*************/
|
/*************/
|
||||||
|
|||||||
@@ -316,6 +316,13 @@ int WebRtcNetEQ_RecInSyncRTP(void* inst,
|
|||||||
*/
|
*/
|
||||||
int WebRtcNetEQ_SetMinimumDelay(void *inst, int minimum_delay_ms);
|
int WebRtcNetEQ_SetMinimumDelay(void *inst, int minimum_delay_ms);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a maximum latency for the jitter buffer. The overall delay is the min of
|
||||||
|
* |maximum_delay_ms| and the latency that is internally computed based on the
|
||||||
|
* inter-arrival times.
|
||||||
|
*/
|
||||||
|
int WebRtcNetEQ_SetMaximumDelay(void *inst, int maximum_delay_ms);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the least required delay in milliseconds given inter-arrival times
|
* Get the least required delay in milliseconds given inter-arrival times
|
||||||
* and playout mode.
|
* and playout mode.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ int WebRtcNetEQ_McuReset(MCUInst_t *inst)
|
|||||||
inst->one_desc = 0;
|
inst->one_desc = 0;
|
||||||
inst->BufferStat_inst.Automode_inst.extraDelayMs = 0;
|
inst->BufferStat_inst.Automode_inst.extraDelayMs = 0;
|
||||||
inst->BufferStat_inst.Automode_inst.minimum_delay_ms = 0;
|
inst->BufferStat_inst.Automode_inst.minimum_delay_ms = 0;
|
||||||
|
inst->BufferStat_inst.Automode_inst.maximum_delay_ms = 10000;
|
||||||
inst->NetEqPlayoutMode = kPlayoutOn;
|
inst->NetEqPlayoutMode = kPlayoutOn;
|
||||||
inst->av_sync = 0;
|
inst->av_sync = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -438,6 +438,8 @@ int WebRtcNetEQ_Init(void *inst, uint16_t fs)
|
|||||||
NetEqMainInst->MCUinst.one_desc = 0;
|
NetEqMainInst->MCUinst.one_desc = 0;
|
||||||
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = 0;
|
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = 0;
|
||||||
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms = 0;
|
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms = 0;
|
||||||
|
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms =
|
||||||
|
10000;
|
||||||
NetEqMainInst->MCUinst.NoOfExpandCalls = 0;
|
NetEqMainInst->MCUinst.NoOfExpandCalls = 0;
|
||||||
NetEqMainInst->MCUinst.fs = fs;
|
NetEqMainInst->MCUinst.fs = fs;
|
||||||
|
|
||||||
@@ -521,12 +523,10 @@ int WebRtcNetEQ_SetAVTPlayout(void *inst, int PlayoutAVTon)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs)
|
int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs) {
|
||||||
{
|
|
||||||
MainInst_t *NetEqMainInst = (MainInst_t*) inst;
|
MainInst_t *NetEqMainInst = (MainInst_t*) inst;
|
||||||
if (NetEqMainInst == NULL) return (-1);
|
if (NetEqMainInst == NULL) return (-1);
|
||||||
if ((DelayInMs < 0) || (DelayInMs > 10000))
|
if ((DelayInMs < 0) || (DelayInMs > 10000)) {
|
||||||
{
|
|
||||||
NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE;
|
NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE;
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
@@ -536,17 +536,39 @@ int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs)
|
|||||||
|
|
||||||
int WebRtcNetEQ_SetMinimumDelay(void *inst, int minimum_delay_ms) {
|
int WebRtcNetEQ_SetMinimumDelay(void *inst, int minimum_delay_ms) {
|
||||||
MainInst_t *NetEqMainInst = (MainInst_t*) inst;
|
MainInst_t *NetEqMainInst = (MainInst_t*) inst;
|
||||||
if (NetEqMainInst == NULL)
|
if (NetEqMainInst == NULL) return -1;
|
||||||
return -1;
|
|
||||||
if (minimum_delay_ms < 0 || minimum_delay_ms > 10000) {
|
if (minimum_delay_ms < 0 || minimum_delay_ms > 10000) {
|
||||||
NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE;
|
NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if ((NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms >
|
||||||
|
0) && (minimum_delay_ms >
|
||||||
|
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms)) {
|
||||||
|
NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms =
|
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms =
|
||||||
minimum_delay_ms;
|
minimum_delay_ms;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int WebRtcNetEQ_SetMaximumDelay(void *inst, int maximum_delay_ms) {
|
||||||
|
MainInst_t *NetEqMainInst = (MainInst_t*) inst;
|
||||||
|
if (NetEqMainInst == NULL) return -1;
|
||||||
|
if (maximum_delay_ms < 0 || maximum_delay_ms > 10000) {
|
||||||
|
NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (maximum_delay_ms <
|
||||||
|
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms) {
|
||||||
|
NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms =
|
||||||
|
maximum_delay_ms;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int WebRtcNetEQ_SetPlayoutMode(void *inst, enum WebRtcNetEQPlayoutMode playoutMode)
|
int WebRtcNetEQ_SetPlayoutMode(void *inst, enum WebRtcNetEQPlayoutMode playoutMode)
|
||||||
{
|
{
|
||||||
MainInst_t *NetEqMainInst = (MainInst_t*) inst;
|
MainInst_t *NetEqMainInst = (MainInst_t*) inst;
|
||||||
|
|||||||
Reference in New Issue
Block a user