diff --git a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h index dbc4fe5352..bcf86c1a59 100644 --- a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h +++ b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h @@ -660,6 +660,19 @@ class AudioCodingModule: public Module { // 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 // is computed based on inter-arrival times and playout mode of NetEq. The diff --git a/webrtc/modules/audio_coding/main/source/acm_neteq.cc b/webrtc/modules/audio_coding/main/source/acm_neteq.cc index de2b6a43a3..5418d18025 100644 --- a/webrtc/modules/audio_coding/main/source/acm_neteq.cc +++ b/webrtc/modules/audio_coding/main/source/acm_neteq.cc @@ -49,7 +49,8 @@ ACMNetEQ::ACMNetEQ() min_of_buffer_size_bytes_(0), per_packet_overhead_bytes_(0), 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++) { is_initialized_[n] = false; ptr_vadinst_[n] = NULL; @@ -1074,6 +1075,10 @@ int16_t ACMNetEQ::AddSlave(const WebRtcNetEQDecoder* used_codecs, // Set minimum delay. if (minimum_delay_ms_ > 0) 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; @@ -1109,6 +1114,17 @@ int ACMNetEQ::SetMinimumDelay(int minimum_delay_ms) { 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 { CriticalSectionScoped lock(neteq_crit_sect_); assert(is_initialized_[0]); diff --git a/webrtc/modules/audio_coding/main/source/acm_neteq.h b/webrtc/modules/audio_coding/main/source/acm_neteq.h index 0a04c294e5..511968b287 100644 --- a/webrtc/modules/audio_coding/main/source/acm_neteq.h +++ b/webrtc/modules/audio_coding/main/source/acm_neteq.h @@ -300,6 +300,11 @@ class ACMNetEQ { // 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 // is computed based on inter-arrival times and playout mode of NetEq. The @@ -384,6 +389,7 @@ class ACMNetEQ { bool av_sync_; int minimum_delay_ms_; + int maximum_delay_ms_; }; } // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc index f35ec04c2f..0244352156 100644 --- a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc +++ b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc @@ -2205,13 +2205,7 @@ int AudioCodingModuleImpl::InitStereoSlave() { return 0; } -// Minimum playout delay (Used for lip-sync). 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_); // 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); } +int AudioCodingModuleImpl::SetMaximumPlayoutDelay(int time_ms) { + return neteq_.SetMaximumDelay(time_ms); +} + // Get Dtmf playout status. bool AudioCodingModuleImpl::DtmfPlayoutStatus() const { #ifndef WEBRTC_CODEC_AVT diff --git a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h index a20159bbd2..1038ace582 100644 --- a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h +++ b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h @@ -175,7 +175,10 @@ class AudioCodingModuleImpl : public AudioCodingModule { // is the max of |time_ms| and the required delay dictated by the channel. 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 // 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 diff --git a/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc b/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc index 00806d94df..159e0ca613 100644 --- a/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc +++ b/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc @@ -94,6 +94,10 @@ class TargetDelayTest : public ::testing::Test { return acm_->SetMinimumPlayoutDelay(delay_ms); } + int SetMaximumDelay(int delay_ms) { + return acm_->SetMaximumPlayoutDelay(delay_ms); + } + int GetCurrentOptimalDelayMs() { ACMNetworkStatistics stats; acm_->NetworkStatistics(&stats); @@ -170,4 +174,21 @@ TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(RequiredDelayAtCorrectRange)) { 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 diff --git a/webrtc/modules/audio_coding/neteq/automode.c b/webrtc/modules/audio_coding/neteq/automode.c index 646f164a8e..a922448591 100644 --- a/webrtc/modules/audio_coding/neteq/automode.c +++ b/webrtc/modules/audio_coding/neteq/automode.c @@ -224,6 +224,10 @@ int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen, */ int32_t minimum_delay_q8 = ((inst->minimum_delay_ms * (fsHz / 1000)) << 8) / packetLenSamp; + + int32_t maximum_delay_q8 = ((inst->maximum_delay_ms * + (fsHz / 1000)) << 8) / packetLenSamp; + inst->optBufLevel = tempvar; if (streamingMode != 0) @@ -239,6 +243,12 @@ int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen, inst->optBufLevel = WEBRTC_SPL_MAX(inst->optBufLevel, 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 */ /*********/ diff --git a/webrtc/modules/audio_coding/neteq/automode.h b/webrtc/modules/audio_coding/neteq/automode.h index 49878c08a3..16d72e8d56 100644 --- a/webrtc/modules/audio_coding/neteq/automode.h +++ b/webrtc/modules/audio_coding/neteq/automode.h @@ -91,6 +91,9 @@ typedef struct int minimum_delay_ms; /* Desired delay, NetEq maintains this amount of 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 according to inter-arrival time and playout mode. It has the same unit as |optBufLevel|. */ @@ -121,7 +124,6 @@ typedef struct int16_t cSumIatQ8; /* cumulative sum of inter-arrival times */ int16_t maxCSumIatQ8; /* max cumulative sum IAT */ uint32_t maxCSumUpdateTimer;/* time elapsed since maximum was observed */ - } AutomodeInst_t; /*************/ diff --git a/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h b/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h index 021704c230..c46a3f6270 100644 --- a/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h +++ b/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h @@ -316,6 +316,13 @@ int WebRtcNetEQ_RecInSyncRTP(void* inst, */ 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 * and playout mode. diff --git a/webrtc/modules/audio_coding/neteq/mcu_reset.c b/webrtc/modules/audio_coding/neteq/mcu_reset.c index c8a4cd73a2..ddbb798af8 100644 --- a/webrtc/modules/audio_coding/neteq/mcu_reset.c +++ b/webrtc/modules/audio_coding/neteq/mcu_reset.c @@ -33,6 +33,7 @@ int WebRtcNetEQ_McuReset(MCUInst_t *inst) inst->one_desc = 0; inst->BufferStat_inst.Automode_inst.extraDelayMs = 0; inst->BufferStat_inst.Automode_inst.minimum_delay_ms = 0; + inst->BufferStat_inst.Automode_inst.maximum_delay_ms = 10000; inst->NetEqPlayoutMode = kPlayoutOn; inst->av_sync = 0; diff --git a/webrtc/modules/audio_coding/neteq/webrtc_neteq.c b/webrtc/modules/audio_coding/neteq/webrtc_neteq.c index 8f1e34cfff..de1ccd1e3c 100644 --- a/webrtc/modules/audio_coding/neteq/webrtc_neteq.c +++ b/webrtc/modules/audio_coding/neteq/webrtc_neteq.c @@ -438,6 +438,8 @@ int WebRtcNetEQ_Init(void *inst, uint16_t fs) NetEqMainInst->MCUinst.one_desc = 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.maximum_delay_ms = + 10000; NetEqMainInst->MCUinst.NoOfExpandCalls = 0; NetEqMainInst->MCUinst.fs = fs; @@ -521,32 +523,52 @@ int WebRtcNetEQ_SetAVTPlayout(void *inst, int PlayoutAVTon) #endif } -int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - if ((DelayInMs < 0) || (DelayInMs > 10000)) - { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return (-1); - } - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = DelayInMs; - return (0); +int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs) { + MainInst_t *NetEqMainInst = (MainInst_t*) inst; + if (NetEqMainInst == NULL) return (-1); + if ((DelayInMs < 0) || (DelayInMs > 10000)) { + NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; + return (-1); + } + NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = DelayInMs; + return (0); } int WebRtcNetEQ_SetMinimumDelay(void *inst, int minimum_delay_ms) { MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) - return -1; + if (NetEqMainInst == NULL) return -1; if (minimum_delay_ms < 0 || minimum_delay_ms > 10000) { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return -1; + NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; + 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 = minimum_delay_ms; 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) { MainInst_t *NetEqMainInst = (MainInst_t*) inst;