OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 case DelaySource::kDelayAgnostic: | 59 case DelaySource::kDelayAgnostic: |
60 RTC_HISTOGRAM_COUNTS("WebRTC.Audio.AecDelayAdjustmentMsAgnosticValue", | 60 RTC_HISTOGRAM_COUNTS("WebRTC.Audio.AecDelayAdjustmentMsAgnosticValue", |
61 moved_ms, kMinDelayLogValue, kMaxDelayLogValue, | 61 moved_ms, kMinDelayLogValue, kMaxDelayLogValue, |
62 kNumDelayLogBuckets); | 62 kNumDelayLogBuckets); |
63 return; | 63 return; |
64 } | 64 } |
65 } | 65 } |
66 } // namespace | 66 } // namespace |
67 | 67 |
68 // Buffer size (samples) | 68 // Buffer size (samples) |
69 static const size_t kBufSizePartitions = 250; // 1 second of audio in 16 kHz. | 69 static const size_t kBufferSizeBlocks = 250; // 1 second of audio in 16 kHz. |
70 | 70 |
71 // Metrics | 71 // Metrics |
72 static const size_t kSubCountLen = 4; | 72 static const size_t kSubCountLen = 4; |
73 static const size_t kCountLen = 50; | 73 static const size_t kCountLen = 50; |
74 static const int kDelayMetricsAggregationWindow = 1250; // 5 seconds at 16 kHz. | 74 static const int kDelayMetricsAggregationWindow = 1250; // 5 seconds at 16 kHz. |
75 | 75 |
76 // Divergence metric is based on audio level, which gets updated every | 76 // Divergence metric is based on audio level, which gets updated every |
77 // |kSubCountLen + 1| * PART_LEN samples. Divergence metric takes the statistics | 77 // |kSubCountLen + 1| * PART_LEN samples. Divergence metric takes the statistics |
78 // of |kDivergentFilterFractionAggregationWindowSize| audio levels. The | 78 // of |kDivergentFilterFractionAggregationWindowSize| audio levels. The |
79 // following value corresponds to 1 second at 16 kHz. | 79 // following value corresponds to 1 second at 16 kHz. |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 } | 177 } |
178 | 178 |
179 // TODO(minyue): Due to a legacy bug, |framelevel| and |averagelevel| use a | 179 // TODO(minyue): Due to a legacy bug, |framelevel| and |averagelevel| use a |
180 // window, of which the length is 1 unit longer than indicated. Remove "+1" when | 180 // window, of which the length is 1 unit longer than indicated. Remove "+1" when |
181 // the code is refactored. | 181 // the code is refactored. |
182 PowerLevel::PowerLevel() | 182 PowerLevel::PowerLevel() |
183 : framelevel(kSubCountLen + 1), | 183 : framelevel(kSubCountLen + 1), |
184 averagelevel(kCountLen + 1) { | 184 averagelevel(kCountLen + 1) { |
185 } | 185 } |
186 | 186 |
| 187 BlockBuffer::BlockBuffer() { |
| 188 buffer_ = WebRtc_CreateBuffer(kBufferSizeBlocks, sizeof(float) * PART_LEN); |
| 189 RTC_CHECK(buffer_); |
| 190 ReInit(); |
| 191 } |
| 192 |
| 193 BlockBuffer::~BlockBuffer() { |
| 194 WebRtc_FreeBuffer(buffer_); |
| 195 } |
| 196 |
| 197 void BlockBuffer::ReInit() { |
| 198 WebRtc_InitBuffer(buffer_); |
| 199 } |
| 200 |
| 201 void BlockBuffer::Insert(const float block[PART_LEN]) { |
| 202 WebRtc_WriteBuffer(buffer_, block, 1); |
| 203 } |
| 204 |
| 205 void BlockBuffer::ExtractExtendedBlock(float extended_block[PART_LEN2]) { |
| 206 float* block_ptr = NULL; |
| 207 RTC_DCHECK_LT(0u, AvaliableSpace()); |
| 208 |
| 209 // Extract the previous block. |
| 210 WebRtc_MoveReadPtr(buffer_, -1); |
| 211 WebRtc_ReadBuffer(buffer_, reinterpret_cast<void**>(&block_ptr), |
| 212 &extended_block[0], 1); |
| 213 if (block_ptr != &extended_block[0]) { |
| 214 memcpy(&extended_block[0], block_ptr, PART_LEN * sizeof(float)); |
| 215 } |
| 216 |
| 217 // Extract the current block. |
| 218 WebRtc_ReadBuffer(buffer_, reinterpret_cast<void**>(&block_ptr), |
| 219 &extended_block[PART_LEN], 1); |
| 220 if (block_ptr != &extended_block[PART_LEN]) { |
| 221 memcpy(&extended_block[PART_LEN], block_ptr, PART_LEN * sizeof(float)); |
| 222 } |
| 223 } |
| 224 |
| 225 int BlockBuffer::AdjustSize(int buffer_size_decrease) { |
| 226 return WebRtc_MoveReadPtr(buffer_, buffer_size_decrease); |
| 227 } |
| 228 |
| 229 size_t BlockBuffer::Size() { |
| 230 return static_cast<int>(WebRtc_available_read(buffer_)); |
| 231 } |
| 232 |
| 233 size_t BlockBuffer::AvaliableSpace() { |
| 234 return WebRtc_available_write(buffer_); |
| 235 } |
| 236 |
187 DivergentFilterFraction::DivergentFilterFraction() | 237 DivergentFilterFraction::DivergentFilterFraction() |
188 : count_(0), | 238 : count_(0), |
189 occurrence_(0), | 239 occurrence_(0), |
190 fraction_(-1.0) { | 240 fraction_(-1.0) { |
191 } | 241 } |
192 | 242 |
193 void DivergentFilterFraction::Reset() { | 243 void DivergentFilterFraction::Reset() { |
194 Clear(); | 244 Clear(); |
195 fraction_ = -1.0; | 245 fraction_ = -1.0; |
196 } | 246 } |
(...skipping 653 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
850 self->delay_quality_threshold)) { | 900 self->delay_quality_threshold)) { |
851 int delay = last_delay - WebRtc_lookahead(self->delay_estimator); | 901 int delay = last_delay - WebRtc_lookahead(self->delay_estimator); |
852 // Allow for a slack in the actual delay, defined by a |lower_bound| and an | 902 // Allow for a slack in the actual delay, defined by a |lower_bound| and an |
853 // |upper_bound|. The adaptive echo cancellation filter is currently | 903 // |upper_bound|. The adaptive echo cancellation filter is currently |
854 // |num_partitions| (of 64 samples) long. If the delay estimate is negative | 904 // |num_partitions| (of 64 samples) long. If the delay estimate is negative |
855 // or at least 3/4 of the filter length we open up for correction. | 905 // or at least 3/4 of the filter length we open up for correction. |
856 const int lower_bound = 0; | 906 const int lower_bound = 0; |
857 const int upper_bound = self->num_partitions * 3 / 4; | 907 const int upper_bound = self->num_partitions * 3 / 4; |
858 const int do_correction = delay <= lower_bound || delay > upper_bound; | 908 const int do_correction = delay <= lower_bound || delay > upper_bound; |
859 if (do_correction == 1) { | 909 if (do_correction == 1) { |
860 int available_read = | 910 int available_read = self->farend_block_buffer_.Size(); |
861 static_cast<int>(WebRtc_available_read(self->far_time_buf)); | |
862 // With |shift_offset| we gradually rely on the delay estimates. For | 911 // With |shift_offset| we gradually rely on the delay estimates. For |
863 // positive delays we reduce the correction by |shift_offset| to lower the | 912 // positive delays we reduce the correction by |shift_offset| to lower the |
864 // risk of pushing the AEC into a non causal state. For negative delays | 913 // risk of pushing the AEC into a non causal state. For negative delays |
865 // we rely on the values up to a rounding error, hence compensate by 1 | 914 // we rely on the values up to a rounding error, hence compensate by 1 |
866 // element to make sure to push the delay into the causal region. | 915 // element to make sure to push the delay into the causal region. |
867 delay_correction = -delay; | 916 delay_correction = -delay; |
868 delay_correction += delay > self->shift_offset ? self->shift_offset : 1; | 917 delay_correction += delay > self->shift_offset ? self->shift_offset : 1; |
869 self->shift_offset--; | 918 self->shift_offset--; |
870 self->shift_offset = (self->shift_offset <= 1 ? 1 : self->shift_offset); | 919 self->shift_offset = (self->shift_offset <= 1 ? 1 : self->shift_offset); |
871 if (delay_correction > available_read - self->mult - 1) { | 920 if (delay_correction > available_read - self->mult - 1) { |
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1099 aec->overdrive_scaling = | 1148 aec->overdrive_scaling = |
1100 0.9f * aec->overdrive_scaling + 0.1f * aec->overDrive; | 1149 0.9f * aec->overdrive_scaling + 0.1f * aec->overDrive; |
1101 } | 1150 } |
1102 | 1151 |
1103 // Apply the overdrive. | 1152 // Apply the overdrive. |
1104 WebRtcAec_Overdrive(aec->overdrive_scaling, hNlFb, hNl); | 1153 WebRtcAec_Overdrive(aec->overdrive_scaling, hNlFb, hNl); |
1105 } | 1154 } |
1106 | 1155 |
1107 static void EchoSuppression(AecCore* aec, | 1156 static void EchoSuppression(AecCore* aec, |
1108 float* nearend_extended_block_lowest_band, | 1157 float* nearend_extended_block_lowest_band, |
1109 float farend[PART_LEN2], | 1158 float farend_extended_block[PART_LEN2], |
1110 float* echo_subtractor_output, | 1159 float* echo_subtractor_output, |
1111 float output[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { | 1160 float output[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { |
1112 float efw[2][PART_LEN1]; | 1161 float efw[2][PART_LEN1]; |
1113 float xfw[2][PART_LEN1]; | 1162 float xfw[2][PART_LEN1]; |
1114 float dfw[2][PART_LEN1]; | 1163 float dfw[2][PART_LEN1]; |
1115 float comfortNoiseHband[2][PART_LEN1]; | 1164 float comfortNoiseHband[2][PART_LEN1]; |
1116 float fft[PART_LEN2]; | 1165 float fft[PART_LEN2]; |
1117 float nlpGainHband; | 1166 float nlpGainHband; |
1118 int i; | 1167 int i; |
1119 size_t j; | 1168 size_t j; |
(...skipping 18 matching lines...) Expand all Loading... |
1138 StoreAsComplex(fft, dfw); | 1187 StoreAsComplex(fft, dfw); |
1139 | 1188 |
1140 // Windowed echo suppressor output ffts. | 1189 // Windowed echo suppressor output ffts. |
1141 WindowData(fft, aec->eBuf); | 1190 WindowData(fft, aec->eBuf); |
1142 aec_rdft_forward_128(fft); | 1191 aec_rdft_forward_128(fft); |
1143 StoreAsComplex(fft, efw); | 1192 StoreAsComplex(fft, efw); |
1144 | 1193 |
1145 // NLP | 1194 // NLP |
1146 | 1195 |
1147 // Convert far-end partition to the frequency domain with windowing. | 1196 // Convert far-end partition to the frequency domain with windowing. |
1148 WindowData(fft, farend); | 1197 WindowData(fft, farend_extended_block); |
1149 Fft(fft, xfw); | 1198 Fft(fft, xfw); |
1150 xfw_ptr = &xfw[0][0]; | 1199 xfw_ptr = &xfw[0][0]; |
1151 | 1200 |
1152 // Buffer far. | 1201 // Buffer far. |
1153 memcpy(aec->xfwBuf, xfw_ptr, sizeof(float) * 2 * PART_LEN1); | 1202 memcpy(aec->xfwBuf, xfw_ptr, sizeof(float) * 2 * PART_LEN1); |
1154 | 1203 |
1155 aec->delayEstCtr++; | 1204 aec->delayEstCtr++; |
1156 if (aec->delayEstCtr == delayEstInterval) { | 1205 if (aec->delayEstCtr == delayEstInterval) { |
1157 aec->delayEstCtr = 0; | 1206 aec->delayEstCtr = 0; |
1158 aec->delayIdx = WebRtcAec_PartitionDelay(aec->num_partitions, aec->wfBuf); | 1207 aec->delayIdx = WebRtcAec_PartitionDelay(aec->num_partitions, aec->wfBuf); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1232 } | 1281 } |
1233 } | 1282 } |
1234 | 1283 |
1235 // Copy the current block to the old position. | 1284 // Copy the current block to the old position. |
1236 memcpy(aec->eBuf, aec->eBuf + PART_LEN, sizeof(float) * PART_LEN); | 1285 memcpy(aec->eBuf, aec->eBuf + PART_LEN, sizeof(float) * PART_LEN); |
1237 | 1286 |
1238 memmove(aec->xfwBuf + PART_LEN1, aec->xfwBuf, | 1287 memmove(aec->xfwBuf + PART_LEN1, aec->xfwBuf, |
1239 sizeof(aec->xfwBuf) - sizeof(complex_t) * PART_LEN1); | 1288 sizeof(aec->xfwBuf) - sizeof(complex_t) * PART_LEN1); |
1240 } | 1289 } |
1241 | 1290 |
1242 static void ProcessBlock(AecCore* aec, | 1291 static void ProcessNearendBlock( |
1243 float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN], | 1292 AecCore* aec, |
1244 float output_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { | 1293 float farend_extended_block_lowest_band[PART_LEN2], |
| 1294 float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN], |
| 1295 float output_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { |
1245 size_t i; | 1296 size_t i; |
1246 | 1297 |
1247 float fft[PART_LEN2]; | 1298 float fft[PART_LEN2]; |
1248 float nearend_extended_block_lowest_band[PART_LEN2]; | 1299 float nearend_extended_block_lowest_band[PART_LEN2]; |
1249 float x_fft[2][PART_LEN1]; | 1300 float farend_fft[2][PART_LEN1]; |
1250 float df[2][PART_LEN1]; | 1301 float nearend_fft[2][PART_LEN1]; |
1251 float far_spectrum = 0.0f; | 1302 float far_spectrum = 0.0f; |
1252 float near_spectrum = 0.0f; | 1303 float near_spectrum = 0.0f; |
1253 float abs_far_spectrum[PART_LEN1]; | 1304 float abs_far_spectrum[PART_LEN1]; |
1254 float abs_near_spectrum[PART_LEN1]; | 1305 float abs_near_spectrum[PART_LEN1]; |
1255 | 1306 |
1256 const float gPow[2] = {0.9f, 0.1f}; | 1307 const float gPow[2] = {0.9f, 0.1f}; |
1257 | 1308 |
1258 // Noise estimate constants. | 1309 // Noise estimate constants. |
1259 const int noiseInitBlocks = 500 * aec->mult; | 1310 const int noiseInitBlocks = 500 * aec->mult; |
1260 const float step = 0.1f; | 1311 const float step = 0.1f; |
1261 const float ramp = 1.0002f; | 1312 const float ramp = 1.0002f; |
1262 const float gInitNoise[2] = {0.999f, 0.001f}; | 1313 const float gInitNoise[2] = {0.999f, 0.001f}; |
1263 | 1314 |
1264 float farend[PART_LEN2]; | |
1265 float* farend_ptr = NULL; | |
1266 float echo_subtractor_output[PART_LEN]; | 1315 float echo_subtractor_output[PART_LEN]; |
1267 float* x_fft_ptr = NULL; | |
1268 | 1316 |
1269 // We should always have at least one element stored in |far_buf|. | 1317 aec->data_dumper->DumpWav("aec_far", PART_LEN, |
1270 assert(WebRtc_available_read(aec->far_time_buf) > 0); | 1318 &farend_extended_block_lowest_band[PART_LEN], |
1271 WebRtc_ReadBuffer(aec->far_time_buf, reinterpret_cast<void**>(&farend_ptr), | |
1272 farend, 1); | |
1273 | |
1274 aec->data_dumper->DumpWav("aec_far", PART_LEN, &farend_ptr[PART_LEN], | |
1275 std::min(aec->sampFreq, 16000), 1); | 1319 std::min(aec->sampFreq, 16000), 1); |
1276 aec->data_dumper->DumpWav("aec_near", PART_LEN, &nearend_block[0][0], | 1320 aec->data_dumper->DumpWav("aec_near", PART_LEN, &nearend_block[0][0], |
1277 std::min(aec->sampFreq, 16000), 1); | 1321 std::min(aec->sampFreq, 16000), 1); |
1278 | 1322 |
1279 if (aec->metricsMode == 1) { | 1323 if (aec->metricsMode == 1) { |
1280 // Update power levels | 1324 // Update power levels |
1281 UpdateLevel(&aec->farlevel, | 1325 UpdateLevel( |
1282 CalculatePower(&farend_ptr[PART_LEN], PART_LEN)); | 1326 &aec->farlevel, |
| 1327 CalculatePower(&farend_extended_block_lowest_band[PART_LEN], PART_LEN)); |
1283 UpdateLevel(&aec->nearlevel, | 1328 UpdateLevel(&aec->nearlevel, |
1284 CalculatePower(&nearend_block[0][0], PART_LEN)); | 1329 CalculatePower(&nearend_block[0][0], PART_LEN)); |
1285 } | 1330 } |
1286 | 1331 |
1287 // Convert far-end signal to the frequency domain. | 1332 // Convert far-end signal to the frequency domain. |
1288 memcpy(fft, farend_ptr, sizeof(float) * PART_LEN2); | 1333 memcpy(fft, farend_extended_block_lowest_band, sizeof(float) * PART_LEN2); |
1289 Fft(fft, x_fft); | 1334 Fft(fft, farend_fft); |
1290 x_fft_ptr = &x_fft[0][0]; | |
1291 | 1335 |
1292 // Form extended nearend frame. | 1336 // Form extended nearend frame. |
1293 memcpy(&nearend_extended_block_lowest_band[0], | 1337 memcpy(&nearend_extended_block_lowest_band[0], |
1294 &aec->previous_nearend_block[0][0], sizeof(float) * PART_LEN); | 1338 &aec->previous_nearend_block[0][0], sizeof(float) * PART_LEN); |
1295 memcpy(&nearend_extended_block_lowest_band[PART_LEN], &nearend_block[0][0], | 1339 memcpy(&nearend_extended_block_lowest_band[PART_LEN], &nearend_block[0][0], |
1296 sizeof(float) * PART_LEN); | 1340 sizeof(float) * PART_LEN); |
1297 | 1341 |
1298 // Near fft | 1342 // Convert near-end signal to the frequency domain. |
1299 memcpy(fft, nearend_extended_block_lowest_band, sizeof(float) * PART_LEN2); | 1343 memcpy(fft, nearend_extended_block_lowest_band, sizeof(float) * PART_LEN2); |
1300 Fft(fft, df); | 1344 Fft(fft, nearend_fft); |
1301 | 1345 |
1302 // Power smoothing. | 1346 // Power smoothing. |
1303 if (aec->refined_adaptive_filter_enabled) { | 1347 if (aec->refined_adaptive_filter_enabled) { |
1304 for (i = 0; i < PART_LEN1; ++i) { | 1348 for (i = 0; i < PART_LEN1; ++i) { |
1305 far_spectrum = (x_fft_ptr[i] * x_fft_ptr[i]) + | 1349 far_spectrum = farend_fft[0][i] * farend_fft[0][i] + |
1306 (x_fft_ptr[PART_LEN1 + i] * x_fft_ptr[PART_LEN1 + i]); | 1350 farend_fft[1][i] * farend_fft[1][i]; |
1307 // Calculate the magnitude spectrum. | 1351 // Calculate the magnitude spectrum. |
1308 abs_far_spectrum[i] = sqrtf(far_spectrum); | 1352 abs_far_spectrum[i] = sqrtf(far_spectrum); |
1309 } | 1353 } |
1310 RegressorPower(aec->num_partitions, aec->xfBufBlockPos, aec->xfBuf, | 1354 RegressorPower(aec->num_partitions, aec->xfBufBlockPos, aec->xfBuf, |
1311 aec->xPow); | 1355 aec->xPow); |
1312 } else { | 1356 } else { |
1313 for (i = 0; i < PART_LEN1; ++i) { | 1357 for (i = 0; i < PART_LEN1; ++i) { |
1314 far_spectrum = (x_fft_ptr[i] * x_fft_ptr[i]) + | 1358 far_spectrum = farend_fft[0][i] * farend_fft[0][i] + |
1315 (x_fft_ptr[PART_LEN1 + i] * x_fft_ptr[PART_LEN1 + i]); | 1359 farend_fft[1][i] * farend_fft[1][i]; |
1316 aec->xPow[i] = | 1360 aec->xPow[i] = |
1317 gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions * far_spectrum; | 1361 gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions * far_spectrum; |
1318 // Calculate the magnitude spectrum. | 1362 // Calculate the magnitude spectrum. |
1319 abs_far_spectrum[i] = sqrtf(far_spectrum); | 1363 abs_far_spectrum[i] = sqrtf(far_spectrum); |
1320 } | 1364 } |
1321 } | 1365 } |
1322 | 1366 |
1323 for (i = 0; i < PART_LEN1; ++i) { | 1367 for (i = 0; i < PART_LEN1; ++i) { |
1324 near_spectrum = df[0][i] * df[0][i] + df[1][i] * df[1][i]; | 1368 near_spectrum = nearend_fft[0][i] * nearend_fft[0][i] + |
| 1369 nearend_fft[1][i] * nearend_fft[1][i]; |
1325 aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum; | 1370 aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum; |
1326 // Calculate the magnitude spectrum. | 1371 // Calculate the magnitude spectrum. |
1327 abs_near_spectrum[i] = sqrtf(near_spectrum); | 1372 abs_near_spectrum[i] = sqrtf(near_spectrum); |
1328 } | 1373 } |
1329 | 1374 |
1330 // Estimate noise power. Wait until dPow is more stable. | 1375 // Estimate noise power. Wait until dPow is more stable. |
1331 if (aec->noiseEstCtr > 50) { | 1376 if (aec->noiseEstCtr > 50) { |
1332 for (i = 0; i < PART_LEN1; i++) { | 1377 for (i = 0; i < PART_LEN1; i++) { |
1333 if (aec->dPow[i] < aec->dMinPow[i]) { | 1378 if (aec->dPow[i] < aec->dMinPow[i]) { |
1334 aec->dMinPow[i] = | 1379 aec->dMinPow[i] = |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1370 if (aec->delay_metrics_delivered == 1 && | 1415 if (aec->delay_metrics_delivered == 1 && |
1371 aec->num_delay_values >= kDelayMetricsAggregationWindow) { | 1416 aec->num_delay_values >= kDelayMetricsAggregationWindow) { |
1372 UpdateDelayMetrics(aec); | 1417 UpdateDelayMetrics(aec); |
1373 } | 1418 } |
1374 } | 1419 } |
1375 } | 1420 } |
1376 | 1421 |
1377 // Perform echo subtraction. | 1422 // Perform echo subtraction. |
1378 EchoSubtraction(aec->num_partitions, aec->extended_filter_enabled, | 1423 EchoSubtraction(aec->num_partitions, aec->extended_filter_enabled, |
1379 &aec->extreme_filter_divergence, aec->filter_step_size, | 1424 &aec->extreme_filter_divergence, aec->filter_step_size, |
1380 aec->error_threshold, &x_fft[0][0], &aec->xfBufBlockPos, | 1425 aec->error_threshold, &farend_fft[0][0], &aec->xfBufBlockPos, |
1381 aec->xfBuf, &nearend_block[0][0], aec->xPow, aec->wfBuf, | 1426 aec->xfBuf, &nearend_block[0][0], aec->xPow, aec->wfBuf, |
1382 echo_subtractor_output); | 1427 echo_subtractor_output); |
1383 aec->data_dumper->DumpRaw("aec_h_fft", PART_LEN1 * aec->num_partitions, | 1428 aec->data_dumper->DumpRaw("aec_h_fft", PART_LEN1 * aec->num_partitions, |
1384 &aec->wfBuf[0][0]); | 1429 &aec->wfBuf[0][0]); |
1385 aec->data_dumper->DumpRaw("aec_h_fft", PART_LEN1 * aec->num_partitions, | 1430 aec->data_dumper->DumpRaw("aec_h_fft", PART_LEN1 * aec->num_partitions, |
1386 &aec->wfBuf[1][0]); | 1431 &aec->wfBuf[1][0]); |
1387 | 1432 |
1388 aec->data_dumper->DumpWav("aec_out_linear", PART_LEN, echo_subtractor_output, | 1433 aec->data_dumper->DumpWav("aec_out_linear", PART_LEN, echo_subtractor_output, |
1389 std::min(aec->sampFreq, 16000), 1); | 1434 std::min(aec->sampFreq, 16000), 1); |
1390 | 1435 |
1391 if (aec->metricsMode == 1) { | 1436 if (aec->metricsMode == 1) { |
1392 UpdateLevel(&aec->linoutlevel, | 1437 UpdateLevel(&aec->linoutlevel, |
1393 CalculatePower(echo_subtractor_output, PART_LEN)); | 1438 CalculatePower(echo_subtractor_output, PART_LEN)); |
1394 } | 1439 } |
1395 | 1440 |
1396 // Perform echo suppression. | 1441 // Perform echo suppression. |
1397 EchoSuppression(aec, nearend_extended_block_lowest_band, farend_ptr, | 1442 EchoSuppression(aec, nearend_extended_block_lowest_band, |
1398 echo_subtractor_output, output_block); | 1443 farend_extended_block_lowest_band, echo_subtractor_output, |
| 1444 output_block); |
1399 | 1445 |
1400 if (aec->metricsMode == 1) { | 1446 if (aec->metricsMode == 1) { |
1401 UpdateLevel(&aec->nlpoutlevel, | 1447 UpdateLevel(&aec->nlpoutlevel, |
1402 CalculatePower(&output_block[0][0], PART_LEN)); | 1448 CalculatePower(&output_block[0][0], PART_LEN)); |
1403 UpdateMetrics(aec); | 1449 UpdateMetrics(aec); |
1404 } | 1450 } |
1405 | 1451 |
1406 // Store the nearend signal until the next frame. | 1452 // Store the nearend signal until the next frame. |
1407 for (i = 0; i < aec->num_bands; ++i) { | 1453 for (i = 0; i < aec->num_bands; ++i) { |
1408 memcpy(&aec->previous_nearend_block[i][0], &nearend_block[i][0], | 1454 memcpy(&aec->previous_nearend_block[i][0], &nearend_block[i][0], |
(...skipping 10 matching lines...) Expand all Loading... |
1419 if (!aec) { | 1465 if (!aec) { |
1420 return NULL; | 1466 return NULL; |
1421 } | 1467 } |
1422 aec->nearend_buffer_size = 0; | 1468 aec->nearend_buffer_size = 0; |
1423 memset(&aec->nearend_buffer[0], 0, sizeof(aec->nearend_buffer)); | 1469 memset(&aec->nearend_buffer[0], 0, sizeof(aec->nearend_buffer)); |
1424 // Start the output buffer with zeros to be able to produce | 1470 // Start the output buffer with zeros to be able to produce |
1425 // a full output frame in the first frame. | 1471 // a full output frame in the first frame. |
1426 aec->output_buffer_size = PART_LEN - (FRAME_LEN - PART_LEN); | 1472 aec->output_buffer_size = PART_LEN - (FRAME_LEN - PART_LEN); |
1427 memset(&aec->output_buffer[0], 0, sizeof(aec->output_buffer)); | 1473 memset(&aec->output_buffer[0], 0, sizeof(aec->output_buffer)); |
1428 | 1474 |
1429 // Create far-end buffers. | |
1430 // For bit exactness with legacy code, each element in |far_time_buf| is | |
1431 // supposed to contain |PART_LEN2| samples with an overlap of |PART_LEN| | |
1432 // samples from the last frame. | |
1433 // TODO(minyue): reduce |far_time_buf| to non-overlapped |PART_LEN| samples. | |
1434 aec->far_time_buf = | |
1435 WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * PART_LEN2); | |
1436 if (!aec->far_time_buf) { | |
1437 WebRtcAec_FreeAec(aec); | |
1438 return NULL; | |
1439 } | |
1440 | |
1441 aec->delay_estimator_farend = | 1475 aec->delay_estimator_farend = |
1442 WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks); | 1476 WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks); |
1443 if (aec->delay_estimator_farend == NULL) { | 1477 if (aec->delay_estimator_farend == NULL) { |
1444 WebRtcAec_FreeAec(aec); | 1478 WebRtcAec_FreeAec(aec); |
1445 return NULL; | 1479 return NULL; |
1446 } | 1480 } |
1447 // We create the delay_estimator with the same amount of maximum lookahead as | 1481 // We create the delay_estimator with the same amount of maximum lookahead as |
1448 // the delay history size (kHistorySizeBlocks) for symmetry reasons. | 1482 // the delay history size (kHistorySizeBlocks) for symmetry reasons. |
1449 aec->delay_estimator = WebRtc_CreateDelayEstimator( | 1483 aec->delay_estimator = WebRtc_CreateDelayEstimator( |
1450 aec->delay_estimator_farend, kHistorySizeBlocks); | 1484 aec->delay_estimator_farend, kHistorySizeBlocks); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1494 aec_rdft_init(); | 1528 aec_rdft_init(); |
1495 | 1529 |
1496 return aec; | 1530 return aec; |
1497 } | 1531 } |
1498 | 1532 |
1499 void WebRtcAec_FreeAec(AecCore* aec) { | 1533 void WebRtcAec_FreeAec(AecCore* aec) { |
1500 if (aec == NULL) { | 1534 if (aec == NULL) { |
1501 return; | 1535 return; |
1502 } | 1536 } |
1503 | 1537 |
1504 WebRtc_FreeBuffer(aec->far_time_buf); | |
1505 | |
1506 WebRtc_FreeDelayEstimator(aec->delay_estimator); | 1538 WebRtc_FreeDelayEstimator(aec->delay_estimator); |
1507 WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend); | 1539 WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend); |
1508 | 1540 |
1509 delete aec; | 1541 delete aec; |
1510 } | 1542 } |
1511 | 1543 |
1512 static void SetAdaptiveFilterStepSize(AecCore* aec) { | 1544 static void SetAdaptiveFilterStepSize(AecCore* aec) { |
1513 // Extended filter adaptation parameter. | 1545 // Extended filter adaptation parameter. |
1514 // TODO(ajm): No narrowband tuning yet. | 1546 // TODO(ajm): No narrowband tuning yet. |
1515 const float kExtendedMu = 0.4f; | 1547 const float kExtendedMu = 0.4f; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1560 aec->num_bands = (size_t)(sampFreq / 16000); | 1592 aec->num_bands = (size_t)(sampFreq / 16000); |
1561 } | 1593 } |
1562 | 1594 |
1563 // Start the output buffer with zeros to be able to produce | 1595 // Start the output buffer with zeros to be able to produce |
1564 // a full output frame in the first frame. | 1596 // a full output frame in the first frame. |
1565 aec->output_buffer_size = PART_LEN - (FRAME_LEN - PART_LEN); | 1597 aec->output_buffer_size = PART_LEN - (FRAME_LEN - PART_LEN); |
1566 memset(&aec->output_buffer[0], 0, sizeof(aec->output_buffer)); | 1598 memset(&aec->output_buffer[0], 0, sizeof(aec->output_buffer)); |
1567 aec->nearend_buffer_size = 0; | 1599 aec->nearend_buffer_size = 0; |
1568 memset(&aec->nearend_buffer[0], 0, sizeof(aec->nearend_buffer)); | 1600 memset(&aec->nearend_buffer[0], 0, sizeof(aec->nearend_buffer)); |
1569 | 1601 |
1570 // Initialize far-end buffers. | 1602 // Initialize far-end buffer. |
1571 WebRtc_InitBuffer(aec->far_time_buf); | 1603 aec->farend_block_buffer_.ReInit(); |
1572 | 1604 |
1573 aec->system_delay = 0; | 1605 aec->system_delay = 0; |
1574 | 1606 |
1575 if (WebRtc_InitDelayEstimatorFarend(aec->delay_estimator_farend) != 0) { | 1607 if (WebRtc_InitDelayEstimatorFarend(aec->delay_estimator_farend) != 0) { |
1576 return -1; | 1608 return -1; |
1577 } | 1609 } |
1578 if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) { | 1610 if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) { |
1579 return -1; | 1611 return -1; |
1580 } | 1612 } |
1581 aec->delay_logging_enabled = 0; | 1613 aec->delay_logging_enabled = 0; |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1680 | 1712 |
1681 aec->extreme_filter_divergence = 0; | 1713 aec->extreme_filter_divergence = 0; |
1682 | 1714 |
1683 // Metrics disabled by default | 1715 // Metrics disabled by default |
1684 aec->metricsMode = 0; | 1716 aec->metricsMode = 0; |
1685 InitMetrics(aec); | 1717 InitMetrics(aec); |
1686 | 1718 |
1687 return 0; | 1719 return 0; |
1688 } | 1720 } |
1689 | 1721 |
1690 // For bit exactness with a legacy code, |farend| is supposed to contain | 1722 void WebRtcAec_BufferFarendBlock(AecCore* aec, const float* farend) { |
1691 // |PART_LEN2| samples with an overlap of |PART_LEN| samples from the last | |
1692 // frame. | |
1693 // TODO(minyue): reduce |farend| to non-overlapped |PART_LEN| samples. | |
1694 void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend) { | |
1695 // Check if the buffer is full, and in that case flush the oldest data. | 1723 // Check if the buffer is full, and in that case flush the oldest data. |
1696 if (WebRtc_available_write(aec->far_time_buf) < 1) { | 1724 if (aec->farend_block_buffer_.AvaliableSpace() < 1) { |
1697 WebRtcAec_MoveFarReadPtr(aec, 1); | 1725 aec->farend_block_buffer_.AdjustSize(1); |
1698 } | 1726 } |
1699 | 1727 aec->farend_block_buffer_.Insert(farend); |
1700 WebRtc_WriteBuffer(aec->far_time_buf, farend, 1); | |
1701 } | 1728 } |
1702 | 1729 |
1703 int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements) { | 1730 int WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(AecCore* aec, |
1704 int elements_moved = WebRtc_MoveReadPtr(aec->far_time_buf, elements); | 1731 int buffer_size_decrease) { |
1705 aec->system_delay -= elements_moved * PART_LEN; | 1732 int achieved_buffer_size_decrease = |
1706 return elements_moved; | 1733 aec->farend_block_buffer_.AdjustSize(buffer_size_decrease); |
| 1734 aec->system_delay -= achieved_buffer_size_decrease * PART_LEN; |
| 1735 return achieved_buffer_size_decrease; |
1707 } | 1736 } |
1708 | 1737 |
1709 void FormNearendBlock( | 1738 void FormNearendBlock( |
1710 size_t nearend_start_index, | 1739 size_t nearend_start_index, |
1711 size_t num_bands, | 1740 size_t num_bands, |
1712 const float* const* nearend_frame, | 1741 const float* const* nearend_frame, |
1713 size_t num_samples_from_nearend_frame, | 1742 size_t num_samples_from_nearend_frame, |
1714 const float nearend_buffer[NUM_HIGH_BANDS_MAX + 1] | 1743 const float nearend_buffer[NUM_HIGH_BANDS_MAX + 1] |
1715 [PART_LEN - (FRAME_LEN - PART_LEN)], | 1744 [PART_LEN - (FRAME_LEN - PART_LEN)], |
1716 float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { | 1745 float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1818 // allow one algorithm to be turned on. | 1847 // allow one algorithm to be turned on. |
1819 | 1848 |
1820 assert(aec->num_bands == num_bands); | 1849 assert(aec->num_bands == num_bands); |
1821 | 1850 |
1822 for (size_t j = 0; j < num_samples; j += FRAME_LEN) { | 1851 for (size_t j = 0; j < num_samples; j += FRAME_LEN) { |
1823 // 1) At most we process |aec->mult|+1 partitions in 10 ms. Make sure we | 1852 // 1) At most we process |aec->mult|+1 partitions in 10 ms. Make sure we |
1824 // have enough far-end data for that by stuffing the buffer if the | 1853 // have enough far-end data for that by stuffing the buffer if the |
1825 // |system_delay| indicates others. | 1854 // |system_delay| indicates others. |
1826 if (aec->system_delay < FRAME_LEN) { | 1855 if (aec->system_delay < FRAME_LEN) { |
1827 // We don't have enough data so we rewind 10 ms. | 1856 // We don't have enough data so we rewind 10 ms. |
1828 WebRtcAec_MoveFarReadPtr(aec, -(aec->mult + 1)); | 1857 WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(aec, -(aec->mult + 1)); |
1829 } | 1858 } |
1830 | 1859 |
1831 if (!aec->delay_agnostic_enabled) { | 1860 if (!aec->delay_agnostic_enabled) { |
1832 // 2 a) Compensate for a possible change in the system delay. | 1861 // 2 a) Compensate for a possible change in the system delay. |
1833 | 1862 |
1834 // TODO(bjornv): Investigate how we should round the delay difference; | 1863 // TODO(bjornv): Investigate how we should round the delay difference; |
1835 // right now we know that incoming |knownDelay| is underestimated when | 1864 // right now we know that incoming |knownDelay| is underestimated when |
1836 // it's less than |aec->knownDelay|. We therefore, round (-32) in that | 1865 // it's less than |aec->knownDelay|. We therefore, round (-32) in that |
1837 // direction. In the other direction, we don't have this situation, but | 1866 // direction. In the other direction, we don't have this situation, but |
1838 // might flush one partition too little. This can cause non-causality, | 1867 // might flush one partition too little. This can cause non-causality, |
1839 // which should be investigated. Maybe, allow for a non-symmetric | 1868 // which should be investigated. Maybe, allow for a non-symmetric |
1840 // rounding, like -16. | 1869 // rounding, like -16. |
1841 int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; | 1870 int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; |
1842 int moved_elements = WebRtc_MoveReadPtr(aec->far_time_buf, move_elements); | 1871 int moved_elements = aec->farend_block_buffer_.AdjustSize(move_elements); |
1843 MaybeLogDelayAdjustment(moved_elements * (aec->sampFreq == 8000 ? 8 : 4), | 1872 MaybeLogDelayAdjustment(moved_elements * (aec->sampFreq == 8000 ? 8 : 4), |
1844 DelaySource::kSystemDelay); | 1873 DelaySource::kSystemDelay); |
1845 aec->knownDelay -= moved_elements * PART_LEN; | 1874 aec->knownDelay -= moved_elements * PART_LEN; |
1846 } else { | 1875 } else { |
1847 // 2 b) Apply signal based delay correction. | 1876 // 2 b) Apply signal based delay correction. |
1848 int move_elements = SignalBasedDelayCorrection(aec); | 1877 int move_elements = SignalBasedDelayCorrection(aec); |
1849 int moved_elements = WebRtc_MoveReadPtr(aec->far_time_buf, move_elements); | 1878 int moved_elements = aec->farend_block_buffer_.AdjustSize(move_elements); |
1850 MaybeLogDelayAdjustment(moved_elements * (aec->sampFreq == 8000 ? 8 : 4), | 1879 MaybeLogDelayAdjustment(moved_elements * (aec->sampFreq == 8000 ? 8 : 4), |
1851 DelaySource::kDelayAgnostic); | 1880 DelaySource::kDelayAgnostic); |
1852 int far_near_buffer_diff = | 1881 int far_near_buffer_diff = |
1853 WebRtc_available_read(aec->far_time_buf) - | 1882 aec->farend_block_buffer_.Size() - |
1854 (aec->nearend_buffer_size + FRAME_LEN) / PART_LEN; | 1883 (aec->nearend_buffer_size + FRAME_LEN) / PART_LEN; |
1855 WebRtc_SoftResetDelayEstimator(aec->delay_estimator, moved_elements); | 1884 WebRtc_SoftResetDelayEstimator(aec->delay_estimator, moved_elements); |
1856 WebRtc_SoftResetDelayEstimatorFarend(aec->delay_estimator_farend, | 1885 WebRtc_SoftResetDelayEstimatorFarend(aec->delay_estimator_farend, |
1857 moved_elements); | 1886 moved_elements); |
1858 aec->signal_delay_correction += moved_elements; | 1887 aec->signal_delay_correction += moved_elements; |
1859 // If we rely on reported system delay values only, a buffer underrun here | 1888 // If we rely on reported system delay values only, a buffer underrun here |
1860 // can never occur since we've taken care of that in 1) above. Here, we | 1889 // can never occur since we've taken care of that in 1) above. Here, we |
1861 // apply signal based delay correction and can therefore end up with | 1890 // apply signal based delay correction and can therefore end up with |
1862 // buffer underruns since the delay estimation can be wrong. We therefore | 1891 // buffer underruns since the delay estimation can be wrong. We therefore |
1863 // stuff the buffer with enough elements if needed. | 1892 // stuff the buffer with enough elements if needed. |
1864 if (far_near_buffer_diff < 0) { | 1893 if (far_near_buffer_diff < 0) { |
1865 WebRtcAec_MoveFarReadPtr(aec, far_near_buffer_diff); | 1894 WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(aec, |
| 1895 far_near_buffer_diff); |
1866 } | 1896 } |
1867 } | 1897 } |
1868 | 1898 |
1869 static_assert( | 1899 static_assert( |
1870 16 == (FRAME_LEN - PART_LEN), | 1900 16 == (FRAME_LEN - PART_LEN), |
1871 "These constants need to be properly related for this code to work"); | 1901 "These constants need to be properly related for this code to work"); |
1872 float output_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]; | 1902 float output_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]; |
1873 float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]; | 1903 float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]; |
| 1904 float farend_extended_block_lowest_band[PART_LEN2]; |
1874 | 1905 |
1875 // Form and process a block of nearend samples, buffer the output block of | 1906 // Form and process a block of nearend samples, buffer the output block of |
1876 // samples. | 1907 // samples. |
| 1908 aec->farend_block_buffer_.ExtractExtendedBlock( |
| 1909 farend_extended_block_lowest_band); |
1877 FormNearendBlock(j, num_bands, nearend, PART_LEN - aec->nearend_buffer_size, | 1910 FormNearendBlock(j, num_bands, nearend, PART_LEN - aec->nearend_buffer_size, |
1878 aec->nearend_buffer, nearend_block); | 1911 aec->nearend_buffer, nearend_block); |
1879 ProcessBlock(aec, nearend_block, output_block); | 1912 ProcessNearendBlock(aec, farend_extended_block_lowest_band, nearend_block, |
| 1913 output_block); |
1880 BufferOutputBlock(num_bands, output_block, &aec->output_buffer_size, | 1914 BufferOutputBlock(num_bands, output_block, &aec->output_buffer_size, |
1881 aec->output_buffer); | 1915 aec->output_buffer); |
1882 | 1916 |
1883 if ((FRAME_LEN - PART_LEN + aec->nearend_buffer_size) == PART_LEN) { | 1917 if ((FRAME_LEN - PART_LEN + aec->nearend_buffer_size) == PART_LEN) { |
1884 // When possible (every fourth frame) form and process a second block of | 1918 // When possible (every fourth frame) form and process a second block of |
1885 // nearend samples, buffer the output block of samples. | 1919 // nearend samples, buffer the output block of samples. |
| 1920 aec->farend_block_buffer_.ExtractExtendedBlock( |
| 1921 farend_extended_block_lowest_band); |
1886 FormNearendBlock(j + FRAME_LEN - PART_LEN, num_bands, nearend, PART_LEN, | 1922 FormNearendBlock(j + FRAME_LEN - PART_LEN, num_bands, nearend, PART_LEN, |
1887 aec->nearend_buffer, nearend_block); | 1923 aec->nearend_buffer, nearend_block); |
1888 ProcessBlock(aec, nearend_block, output_block); | 1924 ProcessNearendBlock(aec, farend_extended_block_lowest_band, nearend_block, |
| 1925 output_block); |
1889 BufferOutputBlock(num_bands, output_block, &aec->output_buffer_size, | 1926 BufferOutputBlock(num_bands, output_block, &aec->output_buffer_size, |
1890 aec->output_buffer); | 1927 aec->output_buffer); |
1891 | 1928 |
1892 // Reset the buffer size as there are no samples left in the nearend input | 1929 // Reset the buffer size as there are no samples left in the nearend input |
1893 // to buffer. | 1930 // to buffer. |
1894 aec->nearend_buffer_size = 0; | 1931 aec->nearend_buffer_size = 0; |
1895 } else { | 1932 } else { |
1896 // Buffer the remaining samples in the nearend input. | 1933 // Buffer the remaining samples in the nearend input. |
1897 aec->nearend_buffer_size += FRAME_LEN - PART_LEN; | 1934 aec->nearend_buffer_size += FRAME_LEN - PART_LEN; |
1898 BufferNearendFrame(j, num_bands, nearend, aec->nearend_buffer_size, | 1935 BufferNearendFrame(j, num_bands, nearend, aec->nearend_buffer_size, |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2011 | 2048 |
2012 int WebRtcAec_system_delay(AecCore* self) { | 2049 int WebRtcAec_system_delay(AecCore* self) { |
2013 return self->system_delay; | 2050 return self->system_delay; |
2014 } | 2051 } |
2015 | 2052 |
2016 void WebRtcAec_SetSystemDelay(AecCore* self, int delay) { | 2053 void WebRtcAec_SetSystemDelay(AecCore* self, int delay) { |
2017 assert(delay >= 0); | 2054 assert(delay >= 0); |
2018 self->system_delay = delay; | 2055 self->system_delay = delay; |
2019 } | 2056 } |
2020 } // namespace webrtc | 2057 } // namespace webrtc |
OLD | NEW |