diff options
Diffstat (limited to 'third_party/libwebrtc/modules/audio_coding/test')
26 files changed, 4942 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/audio_coding/test/Channel.cc b/third_party/libwebrtc/modules/audio_coding/test/Channel.cc new file mode 100644 index 0000000000..35aa6cb6b4 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/Channel.cc @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/Channel.h" + +#include <iostream> + +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +int32_t Channel::SendData(AudioFrameType frameType, + uint8_t payloadType, + uint32_t timeStamp, + const uint8_t* payloadData, + size_t payloadSize, + int64_t absolute_capture_timestamp_ms) { + RTPHeader rtp_header; + int32_t status; + size_t payloadDataSize = payloadSize; + + rtp_header.markerBit = false; + rtp_header.ssrc = 0; + rtp_header.sequenceNumber = + (external_sequence_number_ < 0) + ? _seqNo++ + : static_cast<uint16_t>(external_sequence_number_); + rtp_header.payloadType = payloadType; + rtp_header.timestamp = (external_send_timestamp_ < 0) + ? timeStamp + : static_cast<uint32_t>(external_send_timestamp_); + + if (frameType == AudioFrameType::kEmptyFrame) { + // When frame is empty, we should not transmit it. The frame size of the + // next non-empty frame will be based on the previous frame size. + _useLastFrameSize = _lastFrameSizeSample > 0; + return 0; + } + + memcpy(_payloadData, payloadData, payloadDataSize); + if (_isStereo) { + if (_leftChannel) { + _rtp_header = rtp_header; + _leftChannel = false; + } else { + rtp_header = _rtp_header; + _leftChannel = true; + } + } + + _channelCritSect.Lock(); + if (_saveBitStream) { + // fwrite(payloadData, sizeof(uint8_t), payloadSize, _bitStreamFile); + } + + if (!_isStereo) { + CalcStatistics(rtp_header, payloadSize); + } + _useLastFrameSize = false; + _lastInTimestamp = timeStamp; + _totalBytes += payloadDataSize; + _channelCritSect.Unlock(); + + if (_useFECTestWithPacketLoss) { + _packetLoss += 1; + if (_packetLoss == 3) { + _packetLoss = 0; + return 0; + } + } + + if (num_packets_to_drop_ > 0) { + num_packets_to_drop_--; + return 0; + } + + status = + _receiverACM->IncomingPacket(_payloadData, payloadDataSize, rtp_header); + + return status; +} + +// TODO(turajs): rewite this method. +void Channel::CalcStatistics(const RTPHeader& rtp_header, size_t payloadSize) { + int n; + if ((rtp_header.payloadType != _lastPayloadType) && + (_lastPayloadType != -1)) { + // payload-type is changed. + // we have to terminate the calculations on the previous payload type + // we ignore the last packet in that payload type just to make things + // easier. + for (n = 0; n < MAX_NUM_PAYLOADS; n++) { + if (_lastPayloadType == _payloadStats[n].payloadType) { + _payloadStats[n].newPacket = true; + break; + } + } + } + _lastPayloadType = rtp_header.payloadType; + + bool newPayload = true; + ACMTestPayloadStats* currentPayloadStr = NULL; + for (n = 0; n < MAX_NUM_PAYLOADS; n++) { + if (rtp_header.payloadType == _payloadStats[n].payloadType) { + newPayload = false; + currentPayloadStr = &_payloadStats[n]; + break; + } + } + + if (!newPayload) { + if (!currentPayloadStr->newPacket) { + if (!_useLastFrameSize) { + _lastFrameSizeSample = + (uint32_t)((uint32_t)rtp_header.timestamp - + (uint32_t)currentPayloadStr->lastTimestamp); + } + RTC_DCHECK_GT(_lastFrameSizeSample, 0); + int k = 0; + for (; k < MAX_NUM_FRAMESIZES; ++k) { + if ((currentPayloadStr->frameSizeStats[k].frameSizeSample == + _lastFrameSizeSample) || + (currentPayloadStr->frameSizeStats[k].frameSizeSample == 0)) { + break; + } + } + if (k == MAX_NUM_FRAMESIZES) { + // New frame size found but no space to count statistics on it. Skip it. + printf("No memory to store statistics for payload %d : frame size %d\n", + _lastPayloadType, _lastFrameSizeSample); + return; + } + ACMTestFrameSizeStats* currentFrameSizeStats = + &(currentPayloadStr->frameSizeStats[k]); + currentFrameSizeStats->frameSizeSample = (int16_t)_lastFrameSizeSample; + + // increment the number of encoded samples. + currentFrameSizeStats->totalEncodedSamples += _lastFrameSizeSample; + // increment the number of recveived packets + currentFrameSizeStats->numPackets++; + // increment the total number of bytes (this is based on + // the previous payload we don't know the frame-size of + // the current payload. + currentFrameSizeStats->totalPayloadLenByte += + currentPayloadStr->lastPayloadLenByte; + // store the maximum payload-size (this is based on + // the previous payload we don't know the frame-size of + // the current payload. + if (currentFrameSizeStats->maxPayloadLen < + currentPayloadStr->lastPayloadLenByte) { + currentFrameSizeStats->maxPayloadLen = + currentPayloadStr->lastPayloadLenByte; + } + // store the current values for the next time + currentPayloadStr->lastTimestamp = rtp_header.timestamp; + currentPayloadStr->lastPayloadLenByte = payloadSize; + } else { + currentPayloadStr->newPacket = false; + currentPayloadStr->lastPayloadLenByte = payloadSize; + currentPayloadStr->lastTimestamp = rtp_header.timestamp; + currentPayloadStr->payloadType = rtp_header.payloadType; + memset(currentPayloadStr->frameSizeStats, 0, + MAX_NUM_FRAMESIZES * sizeof(ACMTestFrameSizeStats)); + } + } else { + n = 0; + while (_payloadStats[n].payloadType != -1) { + n++; + } + // first packet + _payloadStats[n].newPacket = false; + _payloadStats[n].lastPayloadLenByte = payloadSize; + _payloadStats[n].lastTimestamp = rtp_header.timestamp; + _payloadStats[n].payloadType = rtp_header.payloadType; + memset(_payloadStats[n].frameSizeStats, 0, + MAX_NUM_FRAMESIZES * sizeof(ACMTestFrameSizeStats)); + } +} + +Channel::Channel(int16_t chID) + : _receiverACM(NULL), + _seqNo(0), + _bitStreamFile(NULL), + _saveBitStream(false), + _lastPayloadType(-1), + _isStereo(false), + _leftChannel(true), + _lastInTimestamp(0), + _useLastFrameSize(false), + _lastFrameSizeSample(0), + _packetLoss(0), + _useFECTestWithPacketLoss(false), + _beginTime(rtc::TimeMillis()), + _totalBytes(0), + external_send_timestamp_(-1), + external_sequence_number_(-1), + num_packets_to_drop_(0) { + int n; + int k; + for (n = 0; n < MAX_NUM_PAYLOADS; n++) { + _payloadStats[n].payloadType = -1; + _payloadStats[n].newPacket = true; + for (k = 0; k < MAX_NUM_FRAMESIZES; k++) { + _payloadStats[n].frameSizeStats[k].frameSizeSample = 0; + _payloadStats[n].frameSizeStats[k].maxPayloadLen = 0; + _payloadStats[n].frameSizeStats[k].numPackets = 0; + _payloadStats[n].frameSizeStats[k].totalPayloadLenByte = 0; + _payloadStats[n].frameSizeStats[k].totalEncodedSamples = 0; + } + } + if (chID >= 0) { + _saveBitStream = true; + rtc::StringBuilder ss; + ss.AppendFormat("bitStream_%d.dat", chID); + _bitStreamFile = fopen(ss.str().c_str(), "wb"); + } else { + _saveBitStream = false; + } +} + +Channel::~Channel() {} + +void Channel::RegisterReceiverACM(AudioCodingModule* acm) { + _receiverACM = acm; + return; +} + +void Channel::ResetStats() { + int n; + int k; + _channelCritSect.Lock(); + _lastPayloadType = -1; + for (n = 0; n < MAX_NUM_PAYLOADS; n++) { + _payloadStats[n].payloadType = -1; + _payloadStats[n].newPacket = true; + for (k = 0; k < MAX_NUM_FRAMESIZES; k++) { + _payloadStats[n].frameSizeStats[k].frameSizeSample = 0; + _payloadStats[n].frameSizeStats[k].maxPayloadLen = 0; + _payloadStats[n].frameSizeStats[k].numPackets = 0; + _payloadStats[n].frameSizeStats[k].totalPayloadLenByte = 0; + _payloadStats[n].frameSizeStats[k].totalEncodedSamples = 0; + } + } + _beginTime = rtc::TimeMillis(); + _totalBytes = 0; + _channelCritSect.Unlock(); +} + +uint32_t Channel::LastInTimestamp() { + uint32_t timestamp; + _channelCritSect.Lock(); + timestamp = _lastInTimestamp; + _channelCritSect.Unlock(); + return timestamp; +} + +double Channel::BitRate() { + double rate; + uint64_t currTime = rtc::TimeMillis(); + _channelCritSect.Lock(); + rate = ((double)_totalBytes * 8.0) / (double)(currTime - _beginTime); + _channelCritSect.Unlock(); + return rate; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/Channel.h b/third_party/libwebrtc/modules/audio_coding/test/Channel.h new file mode 100644 index 0000000000..7a8829e1d2 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/Channel.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_CHANNEL_H_ +#define MODULES_AUDIO_CODING_TEST_CHANNEL_H_ + +#include <stdio.h> + +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/include/module_common_types.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { + +#define MAX_NUM_PAYLOADS 50 +#define MAX_NUM_FRAMESIZES 6 + +// TODO(turajs): Write constructor for this structure. +struct ACMTestFrameSizeStats { + uint16_t frameSizeSample; + size_t maxPayloadLen; + uint32_t numPackets; + uint64_t totalPayloadLenByte; + uint64_t totalEncodedSamples; + double rateBitPerSec; + double usageLenSec; +}; + +// TODO(turajs): Write constructor for this structure. +struct ACMTestPayloadStats { + bool newPacket; + int16_t payloadType; + size_t lastPayloadLenByte; + uint32_t lastTimestamp; + ACMTestFrameSizeStats frameSizeStats[MAX_NUM_FRAMESIZES]; +}; + +class Channel : public AudioPacketizationCallback { + public: + Channel(int16_t chID = -1); + ~Channel() override; + + int32_t SendData(AudioFrameType frameType, + uint8_t payloadType, + uint32_t timeStamp, + const uint8_t* payloadData, + size_t payloadSize, + int64_t absolute_capture_timestamp_ms) override; + + void RegisterReceiverACM(AudioCodingModule* acm); + + void ResetStats(); + + void SetIsStereo(bool isStereo) { _isStereo = isStereo; } + + uint32_t LastInTimestamp(); + + void SetFECTestWithPacketLoss(bool usePacketLoss) { + _useFECTestWithPacketLoss = usePacketLoss; + } + + double BitRate(); + + void set_send_timestamp(uint32_t new_send_ts) { + external_send_timestamp_ = new_send_ts; + } + + void set_sequence_number(uint16_t new_sequence_number) { + external_sequence_number_ = new_sequence_number; + } + + void set_num_packets_to_drop(int new_num_packets_to_drop) { + num_packets_to_drop_ = new_num_packets_to_drop; + } + + private: + void CalcStatistics(const RTPHeader& rtp_header, size_t payloadSize); + + AudioCodingModule* _receiverACM; + uint16_t _seqNo; + // 60msec * 32 sample(max)/msec * 2 description (maybe) * 2 bytes/sample + uint8_t _payloadData[60 * 32 * 2 * 2]; + + Mutex _channelCritSect; + FILE* _bitStreamFile; + bool _saveBitStream; + int16_t _lastPayloadType; + ACMTestPayloadStats _payloadStats[MAX_NUM_PAYLOADS]; + bool _isStereo; + RTPHeader _rtp_header; + bool _leftChannel; + uint32_t _lastInTimestamp; + bool _useLastFrameSize; + uint32_t _lastFrameSizeSample; + // FEC Test variables + int16_t _packetLoss; + bool _useFECTestWithPacketLoss; + uint64_t _beginTime; + uint64_t _totalBytes; + + // External timing info, defaulted to -1. Only used if they are + // non-negative. + int64_t external_send_timestamp_; + int32_t external_sequence_number_; + int num_packets_to_drop_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_CHANNEL_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/EncodeDecodeTest.cc b/third_party/libwebrtc/modules/audio_coding/test/EncodeDecodeTest.cc new file mode 100644 index 0000000000..8d4bcce8df --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/EncodeDecodeTest.cc @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/EncodeDecodeTest.h" + +#include <stdio.h> +#include <stdlib.h> + +#include <memory> + +#include "absl/strings/string_view.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "modules/audio_coding/include/audio_coding_module.h" +#include "rtc_base/strings/string_builder.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +namespace { +// Buffer size for stereo 48 kHz audio. +constexpr size_t kWebRtc10MsPcmAudio = 960; + +} // namespace + +TestPacketization::TestPacketization(RTPStream* rtpStream, uint16_t frequency) + : _rtpStream(rtpStream), _frequency(frequency), _seqNo(0) {} + +TestPacketization::~TestPacketization() {} + +int32_t TestPacketization::SendData(const AudioFrameType /* frameType */, + const uint8_t payloadType, + const uint32_t timeStamp, + const uint8_t* payloadData, + const size_t payloadSize, + int64_t absolute_capture_timestamp_ms) { + _rtpStream->Write(payloadType, timeStamp, _seqNo++, payloadData, payloadSize, + _frequency); + return 1; +} + +Sender::Sender() + : _acm(NULL), _pcmFile(), _audioFrame(), _packetization(NULL) {} + +void Sender::Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view in_file_name, + int in_sample_rate, + int payload_type, + SdpAudioFormat format) { + // Open input file + const std::string file_name = webrtc::test::ResourcePath(in_file_name, "pcm"); + _pcmFile.Open(file_name, in_sample_rate, "rb"); + if (format.num_channels == 2) { + _pcmFile.ReadStereo(true); + } + // Set test length to 500 ms (50 blocks of 10 ms each). + _pcmFile.SetNum10MsBlocksToRead(50); + // Fast-forward 1 second (100 blocks) since the file starts with silence. + _pcmFile.FastForward(100); + + acm->SetEncoder(CreateBuiltinAudioEncoderFactory()->MakeAudioEncoder( + payload_type, format, absl::nullopt)); + _packetization = new TestPacketization(rtpStream, format.clockrate_hz); + EXPECT_EQ(0, acm->RegisterTransportCallback(_packetization)); + + _acm = acm; +} + +void Sender::Teardown() { + _pcmFile.Close(); + delete _packetization; +} + +bool Sender::Add10MsData() { + if (!_pcmFile.EndOfFile()) { + EXPECT_GT(_pcmFile.Read10MsData(_audioFrame), 0); + int32_t ok = _acm->Add10MsData(_audioFrame); + EXPECT_GE(ok, 0); + return ok >= 0 ? true : false; + } + return false; +} + +void Sender::Run() { + while (true) { + if (!Add10MsData()) { + break; + } + } +} + +Receiver::Receiver() + : _playoutLengthSmpls(kWebRtc10MsPcmAudio), + _payloadSizeBytes(MAX_INCOMING_PAYLOAD) {} + +void Receiver::Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view out_file_name, + size_t channels, + int file_num) { + EXPECT_EQ(0, acm->InitializeReceiver()); + + if (channels == 1) { + acm->SetReceiveCodecs({{103, {"ISAC", 16000, 1}}, + {104, {"ISAC", 32000, 1}}, + {107, {"L16", 8000, 1}}, + {108, {"L16", 16000, 1}}, + {109, {"L16", 32000, 1}}, + {0, {"PCMU", 8000, 1}}, + {8, {"PCMA", 8000, 1}}, + {102, {"ILBC", 8000, 1}}, + {9, {"G722", 8000, 1}}, + {120, {"OPUS", 48000, 2}}, + {13, {"CN", 8000, 1}}, + {98, {"CN", 16000, 1}}, + {99, {"CN", 32000, 1}}}); + } else { + ASSERT_EQ(channels, 2u); + acm->SetReceiveCodecs({{111, {"L16", 8000, 2}}, + {112, {"L16", 16000, 2}}, + {113, {"L16", 32000, 2}}, + {110, {"PCMU", 8000, 2}}, + {118, {"PCMA", 8000, 2}}, + {119, {"G722", 8000, 2}}, + {120, {"OPUS", 48000, 2, {{"stereo", "1"}}}}}); + } + + int playSampFreq; + std::string file_name; + rtc::StringBuilder file_stream; + file_stream << webrtc::test::OutputPath() << out_file_name << file_num + << ".pcm"; + file_name = file_stream.str(); + _rtpStream = rtpStream; + + playSampFreq = 32000; + _pcmFile.Open(file_name, 32000, "wb+"); + + _realPayloadSizeBytes = 0; + _playoutBuffer = new int16_t[kWebRtc10MsPcmAudio]; + _frequency = playSampFreq; + _acm = acm; + _firstTime = true; +} + +void Receiver::Teardown() { + delete[] _playoutBuffer; + _pcmFile.Close(); +} + +bool Receiver::IncomingPacket() { + if (!_rtpStream->EndOfFile()) { + if (_firstTime) { + _firstTime = false; + _realPayloadSizeBytes = _rtpStream->Read(&_rtpHeader, _incomingPayload, + _payloadSizeBytes, &_nextTime); + if (_realPayloadSizeBytes == 0) { + if (_rtpStream->EndOfFile()) { + _firstTime = true; + return true; + } else { + return false; + } + } + } + + EXPECT_EQ(0, _acm->IncomingPacket(_incomingPayload, _realPayloadSizeBytes, + _rtpHeader)); + _realPayloadSizeBytes = _rtpStream->Read(&_rtpHeader, _incomingPayload, + _payloadSizeBytes, &_nextTime); + if (_realPayloadSizeBytes == 0 && _rtpStream->EndOfFile()) { + _firstTime = true; + } + } + return true; +} + +bool Receiver::PlayoutData() { + AudioFrame audioFrame; + bool muted; + int32_t ok = _acm->PlayoutData10Ms(_frequency, &audioFrame, &muted); + if (muted) { + ADD_FAILURE(); + return false; + } + EXPECT_EQ(0, ok); + if (ok < 0) { + return false; + } + if (_playoutLengthSmpls == 0) { + return false; + } + _pcmFile.Write10MsData(audioFrame.data(), audioFrame.samples_per_channel_ * + audioFrame.num_channels_); + return true; +} + +void Receiver::Run() { + uint8_t counter500Ms = 50; + uint32_t clock = 0; + + while (counter500Ms > 0) { + if (clock == 0 || clock >= _nextTime) { + EXPECT_TRUE(IncomingPacket()); + if (clock == 0) { + clock = _nextTime; + } + } + if ((clock % 10) == 0) { + if (!PlayoutData()) { + clock++; + continue; + } + } + if (_rtpStream->EndOfFile()) { + counter500Ms--; + } + clock++; + } +} + +EncodeDecodeTest::EncodeDecodeTest() = default; + +void EncodeDecodeTest::Perform() { + const std::map<int, SdpAudioFormat> send_codecs = { + {103, {"ISAC", 16000, 1}}, {104, {"ISAC", 32000, 1}}, + {107, {"L16", 8000, 1}}, {108, {"L16", 16000, 1}}, + {109, {"L16", 32000, 1}}, {0, {"PCMU", 8000, 1}}, + {8, {"PCMA", 8000, 1}}, +#ifdef WEBRTC_CODEC_ILBC + {102, {"ILBC", 8000, 1}}, +#endif + {9, {"G722", 8000, 1}}}; + int file_num = 0; + for (const auto& send_codec : send_codecs) { + RTPFile rtpFile; + std::unique_ptr<AudioCodingModule> acm(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))); + + std::string fileName = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "encode_decode_rtp"); + rtpFile.Open(fileName.c_str(), "wb+"); + rtpFile.WriteHeader(); + Sender sender; + sender.Setup(acm.get(), &rtpFile, "audio_coding/testfile32kHz", 32000, + send_codec.first, send_codec.second); + sender.Run(); + sender.Teardown(); + rtpFile.Close(); + + rtpFile.Open(fileName.c_str(), "rb"); + rtpFile.ReadHeader(); + Receiver receiver; + receiver.Setup(acm.get(), &rtpFile, "encodeDecode_out", 1, file_num); + receiver.Run(); + receiver.Teardown(); + rtpFile.Close(); + + file_num++; + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/EncodeDecodeTest.h b/third_party/libwebrtc/modules/audio_coding/test/EncodeDecodeTest.h new file mode 100644 index 0000000000..89b76440ef --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/EncodeDecodeTest.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_ENCODEDECODETEST_H_ +#define MODULES_AUDIO_CODING_TEST_ENCODEDECODETEST_H_ + +#include <stdio.h> +#include <string.h> + +#include "absl/strings/string_view.h" +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_coding/test/PCMFile.h" +#include "modules/audio_coding/test/RTPFile.h" +#include "modules/include/module_common_types.h" + +namespace webrtc { + +#define MAX_INCOMING_PAYLOAD 8096 + +// TestPacketization callback which writes the encoded payloads to file +class TestPacketization : public AudioPacketizationCallback { + public: + TestPacketization(RTPStream* rtpStream, uint16_t frequency); + ~TestPacketization(); + int32_t SendData(AudioFrameType frameType, + uint8_t payloadType, + uint32_t timeStamp, + const uint8_t* payloadData, + size_t payloadSize, + int64_t absolute_capture_timestamp_ms) override; + + private: + static void MakeRTPheader(uint8_t* rtpHeader, + uint8_t payloadType, + int16_t seqNo, + uint32_t timeStamp, + uint32_t ssrc); + RTPStream* _rtpStream; + int32_t _frequency; + int16_t _seqNo; +}; + +class Sender { + public: + Sender(); + void Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view in_file_name, + int in_sample_rate, + int payload_type, + SdpAudioFormat format); + void Teardown(); + void Run(); + bool Add10MsData(); + + protected: + AudioCodingModule* _acm; + + private: + PCMFile _pcmFile; + AudioFrame _audioFrame; + TestPacketization* _packetization; +}; + +class Receiver { + public: + Receiver(); + virtual ~Receiver() {} + void Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view out_file_name, + size_t channels, + int file_num); + void Teardown(); + void Run(); + virtual bool IncomingPacket(); + bool PlayoutData(); + + private: + PCMFile _pcmFile; + int16_t* _playoutBuffer; + uint16_t _playoutLengthSmpls; + int32_t _frequency; + bool _firstTime; + + protected: + AudioCodingModule* _acm; + uint8_t _incomingPayload[MAX_INCOMING_PAYLOAD]; + RTPStream* _rtpStream; + RTPHeader _rtpHeader; + size_t _realPayloadSizeBytes; + size_t _payloadSizeBytes; + uint32_t _nextTime; +}; + +class EncodeDecodeTest { + public: + EncodeDecodeTest(); + void Perform(); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_ENCODEDECODETEST_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/PCMFile.cc b/third_party/libwebrtc/modules/audio_coding/test/PCMFile.cc new file mode 100644 index 0000000000..e069a42de1 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/PCMFile.cc @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/PCMFile.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "test/gtest.h" + +namespace webrtc { + +#define MAX_FILE_NAME_LENGTH_BYTE 500 + +PCMFile::PCMFile() + : pcm_file_(NULL), + samples_10ms_(160), + frequency_(16000), + end_of_file_(false), + auto_rewind_(false), + rewinded_(false), + read_stereo_(false), + save_stereo_(false) { + timestamp_ = + (((uint32_t)rand() & 0x0000FFFF) << 16) | ((uint32_t)rand() & 0x0000FFFF); +} + +PCMFile::PCMFile(uint32_t timestamp) + : pcm_file_(NULL), + samples_10ms_(160), + frequency_(16000), + end_of_file_(false), + auto_rewind_(false), + rewinded_(false), + read_stereo_(false), + save_stereo_(false) { + timestamp_ = timestamp; +} + +PCMFile::~PCMFile() { + if (pcm_file_) { + fclose(pcm_file_); + } +} + +int16_t PCMFile::ChooseFile(std::string* file_name, + int16_t max_len, + uint16_t* frequency_hz) { + char tmp_name[MAX_FILE_NAME_LENGTH_BYTE]; + + EXPECT_TRUE(fgets(tmp_name, MAX_FILE_NAME_LENGTH_BYTE, stdin) != NULL); + tmp_name[MAX_FILE_NAME_LENGTH_BYTE - 1] = '\0'; + int16_t n = 0; + + // Removing trailing spaces. + while ((isspace(static_cast<unsigned char>(tmp_name[n])) || + iscntrl(static_cast<unsigned char>(tmp_name[n]))) && + (static_cast<unsigned char>(tmp_name[n]) != 0) && + (n < MAX_FILE_NAME_LENGTH_BYTE)) { + n++; + } + if (n > 0) { + memmove(tmp_name, &tmp_name[n], MAX_FILE_NAME_LENGTH_BYTE - n); + } + + // Removing trailing spaces. + n = (int16_t)(strlen(tmp_name) - 1); + if (n >= 0) { + while ((isspace(static_cast<unsigned char>(tmp_name[n])) || + iscntrl(static_cast<unsigned char>(tmp_name[n]))) && + (n >= 0)) { + n--; + } + } + if (n >= 0) { + tmp_name[n + 1] = '\0'; + } + + int16_t len = (int16_t)strlen(tmp_name); + if (len > max_len) { + return -1; + } + if (len > 0) { + std::string tmp_string(tmp_name, len + 1); + *file_name = tmp_string; + } + printf("Enter the sampling frequency (in Hz) of the above file [%u]: ", + *frequency_hz); + EXPECT_TRUE(fgets(tmp_name, 10, stdin) != NULL); + uint16_t tmp_frequency = (uint16_t)atoi(tmp_name); + if (tmp_frequency > 0) { + *frequency_hz = tmp_frequency; + } + return 0; +} + +void PCMFile::Open(absl::string_view file_name, + uint16_t frequency, + absl::string_view mode, + bool auto_rewind) { + if ((pcm_file_ = fopen(std::string(file_name).c_str(), + std::string(mode).c_str())) == NULL) { + printf("Cannot open file %s.\n", std::string(file_name).c_str()); + ADD_FAILURE() << "Unable to read file"; + } + frequency_ = frequency; + samples_10ms_ = (uint16_t)(frequency_ / 100); + auto_rewind_ = auto_rewind; + end_of_file_ = false; + rewinded_ = false; +} + +int32_t PCMFile::SamplingFrequency() const { + return frequency_; +} + +uint16_t PCMFile::PayloadLength10Ms() const { + return samples_10ms_; +} + +int32_t PCMFile::Read10MsData(AudioFrame& audio_frame) { + uint16_t channels = 1; + if (read_stereo_) { + channels = 2; + } + + int32_t payload_size = + (int32_t)fread(audio_frame.mutable_data(), sizeof(uint16_t), + samples_10ms_ * channels, pcm_file_); + if (payload_size < samples_10ms_ * channels) { + int16_t* frame_data = audio_frame.mutable_data(); + for (int k = payload_size; k < samples_10ms_ * channels; k++) { + frame_data[k] = 0; + } + if (auto_rewind_) { + rewind(pcm_file_); + rewinded_ = true; + } else { + end_of_file_ = true; + } + } + audio_frame.samples_per_channel_ = samples_10ms_; + audio_frame.sample_rate_hz_ = frequency_; + audio_frame.num_channels_ = channels; + audio_frame.timestamp_ = timestamp_; + timestamp_ += samples_10ms_; + ++blocks_read_; + if (num_10ms_blocks_to_read_ && blocks_read_ >= *num_10ms_blocks_to_read_) + end_of_file_ = true; + return samples_10ms_; +} + +void PCMFile::Write10MsData(const AudioFrame& audio_frame) { + if (audio_frame.num_channels_ == 1) { + if (!save_stereo_) { + if (fwrite(audio_frame.data(), sizeof(uint16_t), + audio_frame.samples_per_channel_, pcm_file_) != + static_cast<size_t>(audio_frame.samples_per_channel_)) { + return; + } + } else { + const int16_t* frame_data = audio_frame.data(); + int16_t* stereo_audio = new int16_t[2 * audio_frame.samples_per_channel_]; + for (size_t k = 0; k < audio_frame.samples_per_channel_; k++) { + stereo_audio[k << 1] = frame_data[k]; + stereo_audio[(k << 1) + 1] = frame_data[k]; + } + if (fwrite(stereo_audio, sizeof(int16_t), + 2 * audio_frame.samples_per_channel_, pcm_file_) != + static_cast<size_t>(2 * audio_frame.samples_per_channel_)) { + return; + } + delete[] stereo_audio; + } + } else { + if (fwrite(audio_frame.data(), sizeof(int16_t), + audio_frame.num_channels_ * audio_frame.samples_per_channel_, + pcm_file_) != + static_cast<size_t>(audio_frame.num_channels_ * + audio_frame.samples_per_channel_)) { + return; + } + } +} + +void PCMFile::Write10MsData(const int16_t* playout_buffer, + size_t length_smpls) { + if (fwrite(playout_buffer, sizeof(uint16_t), length_smpls, pcm_file_) != + length_smpls) { + return; + } +} + +void PCMFile::Close() { + fclose(pcm_file_); + pcm_file_ = NULL; + blocks_read_ = 0; +} + +void PCMFile::FastForward(int num_10ms_blocks) { + const int channels = read_stereo_ ? 2 : 1; + long num_bytes_to_move = + num_10ms_blocks * sizeof(int16_t) * samples_10ms_ * channels; + int error = fseek(pcm_file_, num_bytes_to_move, SEEK_CUR); + RTC_DCHECK_EQ(error, 0); +} + +void PCMFile::Rewind() { + rewind(pcm_file_); + end_of_file_ = false; + blocks_read_ = 0; +} + +bool PCMFile::Rewinded() { + return rewinded_; +} + +void PCMFile::SaveStereo(bool is_stereo) { + save_stereo_ = is_stereo; +} + +void PCMFile::ReadStereo(bool is_stereo) { + read_stereo_ = is_stereo; +} + +void PCMFile::SetNum10MsBlocksToRead(int value) { + num_10ms_blocks_to_read_ = value; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/PCMFile.h b/third_party/libwebrtc/modules/audio_coding/test/PCMFile.h new file mode 100644 index 0000000000..5320aa63d0 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/PCMFile.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_PCMFILE_H_ +#define MODULES_AUDIO_CODING_TEST_PCMFILE_H_ + +#include <stdio.h> +#include <stdlib.h> + +#include <string> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/audio/audio_frame.h" + +namespace webrtc { + +class PCMFile { + public: + PCMFile(); + PCMFile(uint32_t timestamp); + ~PCMFile(); + + void Open(absl::string_view filename, + uint16_t frequency, + absl::string_view mode, + bool auto_rewind = false); + + int32_t Read10MsData(AudioFrame& audio_frame); + + void Write10MsData(const int16_t* playout_buffer, size_t length_smpls); + void Write10MsData(const AudioFrame& audio_frame); + + uint16_t PayloadLength10Ms() const; + int32_t SamplingFrequency() const; + void Close(); + bool EndOfFile() const { return end_of_file_; } + // Moves forward the specified number of 10 ms blocks. If a limit has been set + // with SetNum10MsBlocksToRead, fast-forwarding does not count towards this + // limit. + void FastForward(int num_10ms_blocks); + void Rewind(); + static int16_t ChooseFile(std::string* file_name, + int16_t max_len, + uint16_t* frequency_hz); + bool Rewinded(); + void SaveStereo(bool is_stereo = true); + void ReadStereo(bool is_stereo = true); + // If set, the reading will stop after the specified number of blocks have + // been read. When that has happened, EndOfFile() will return true. Calling + // Rewind() will reset the counter and start over. + void SetNum10MsBlocksToRead(int value); + + private: + FILE* pcm_file_; + uint16_t samples_10ms_; + int32_t frequency_; + bool end_of_file_; + bool auto_rewind_; + bool rewinded_; + uint32_t timestamp_; + bool read_stereo_; + bool save_stereo_; + absl::optional<int> num_10ms_blocks_to_read_; + int blocks_read_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_PCMFILE_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/PacketLossTest.cc b/third_party/libwebrtc/modules/audio_coding/test/PacketLossTest.cc new file mode 100644 index 0000000000..799e9c5b1f --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/PacketLossTest.cc @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2014 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. + */ + +#include "modules/audio_coding/test/PacketLossTest.h" + +#include <memory> + +#include "absl/strings/string_view.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "rtc_base/strings/string_builder.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +ReceiverWithPacketLoss::ReceiverWithPacketLoss() + : loss_rate_(0), + burst_length_(1), + packet_counter_(0), + lost_packet_counter_(0), + burst_lost_counter_(burst_length_) {} + +void ReceiverWithPacketLoss::Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view out_file_name, + int channels, + int file_num, + int loss_rate, + int burst_length) { + loss_rate_ = loss_rate; + burst_length_ = burst_length; + burst_lost_counter_ = burst_length_; // To prevent first packet gets lost. + rtc::StringBuilder ss; + ss << out_file_name << "_" << loss_rate_ << "_" << burst_length_ << "_"; + Receiver::Setup(acm, rtpStream, ss.str(), channels, file_num); +} + +bool ReceiverWithPacketLoss::IncomingPacket() { + if (!_rtpStream->EndOfFile()) { + if (packet_counter_ == 0) { + _realPayloadSizeBytes = _rtpStream->Read(&_rtpHeader, _incomingPayload, + _payloadSizeBytes, &_nextTime); + if (_realPayloadSizeBytes == 0) { + if (_rtpStream->EndOfFile()) { + packet_counter_ = 0; + return true; + } else { + return false; + } + } + } + + if (!PacketLost()) { + _acm->IncomingPacket(_incomingPayload, _realPayloadSizeBytes, _rtpHeader); + } + packet_counter_++; + _realPayloadSizeBytes = _rtpStream->Read(&_rtpHeader, _incomingPayload, + _payloadSizeBytes, &_nextTime); + if (_realPayloadSizeBytes == 0 && _rtpStream->EndOfFile()) { + packet_counter_ = 0; + lost_packet_counter_ = 0; + } + } + return true; +} + +bool ReceiverWithPacketLoss::PacketLost() { + if (burst_lost_counter_ < burst_length_) { + lost_packet_counter_++; + burst_lost_counter_++; + return true; + } + + if (lost_packet_counter_ * 100 < loss_rate_ * packet_counter_) { + lost_packet_counter_++; + burst_lost_counter_ = 1; + return true; + } + return false; +} + +SenderWithFEC::SenderWithFEC() : expected_loss_rate_(0) {} + +void SenderWithFEC::Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view in_file_name, + int payload_type, + SdpAudioFormat format, + int expected_loss_rate) { + Sender::Setup(acm, rtpStream, in_file_name, format.clockrate_hz, payload_type, + format); + EXPECT_TRUE(SetFEC(true)); + EXPECT_TRUE(SetPacketLossRate(expected_loss_rate)); +} + +bool SenderWithFEC::SetFEC(bool enable_fec) { + bool success = false; + _acm->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* enc) { + if (*enc && (*enc)->SetFec(enable_fec)) { + success = true; + } + }); + return success; +} + +bool SenderWithFEC::SetPacketLossRate(int expected_loss_rate) { + if (_acm->SetPacketLossRate(expected_loss_rate) == 0) { + expected_loss_rate_ = expected_loss_rate; + return true; + } + return false; +} + +PacketLossTest::PacketLossTest(int channels, + int expected_loss_rate, + int actual_loss_rate, + int burst_length) + : channels_(channels), + in_file_name_(channels_ == 1 ? "audio_coding/testfile32kHz" + : "audio_coding/teststereo32kHz"), + sample_rate_hz_(32000), + expected_loss_rate_(expected_loss_rate), + actual_loss_rate_(actual_loss_rate), + burst_length_(burst_length) {} + +void PacketLossTest::Perform() { +#ifndef WEBRTC_CODEC_OPUS + return; +#else + RTPFile rtpFile; + std::unique_ptr<AudioCodingModule> acm(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))); + SdpAudioFormat send_format = SdpAudioFormat("opus", 48000, 2); + if (channels_ == 2) { + send_format.parameters = {{"stereo", "1"}}; + } + + std::string fileName = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "packet_loss_test"); + rtpFile.Open(fileName.c_str(), "wb+"); + rtpFile.WriteHeader(); + SenderWithFEC sender; + sender.Setup(acm.get(), &rtpFile, in_file_name_, 120, send_format, + expected_loss_rate_); + sender.Run(); + sender.Teardown(); + rtpFile.Close(); + + rtpFile.Open(fileName.c_str(), "rb"); + rtpFile.ReadHeader(); + ReceiverWithPacketLoss receiver; + receiver.Setup(acm.get(), &rtpFile, "packetLoss_out", channels_, 15, + actual_loss_rate_, burst_length_); + receiver.Run(); + receiver.Teardown(); + rtpFile.Close(); +#endif +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/PacketLossTest.h b/third_party/libwebrtc/modules/audio_coding/test/PacketLossTest.h new file mode 100644 index 0000000000..d841d65a1b --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/PacketLossTest.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_PACKETLOSSTEST_H_ +#define MODULES_AUDIO_CODING_TEST_PACKETLOSSTEST_H_ + +#include <string> + +#include "absl/strings/string_view.h" +#include "modules/audio_coding/test/EncodeDecodeTest.h" + +namespace webrtc { + +class ReceiverWithPacketLoss : public Receiver { + public: + ReceiverWithPacketLoss(); + void Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view out_file_name, + int channels, + int file_num, + int loss_rate, + int burst_length); + bool IncomingPacket() override; + + protected: + bool PacketLost(); + int loss_rate_; + int burst_length_; + int packet_counter_; + int lost_packet_counter_; + int burst_lost_counter_; +}; + +class SenderWithFEC : public Sender { + public: + SenderWithFEC(); + void Setup(AudioCodingModule* acm, + RTPStream* rtpStream, + absl::string_view in_file_name, + int payload_type, + SdpAudioFormat format, + int expected_loss_rate); + bool SetPacketLossRate(int expected_loss_rate); + bool SetFEC(bool enable_fec); + + protected: + int expected_loss_rate_; +}; + +class PacketLossTest { + public: + PacketLossTest(int channels, + int expected_loss_rate_, + int actual_loss_rate, + int burst_length); + void Perform(); + + protected: + int channels_; + std::string in_file_name_; + int sample_rate_hz_; + int expected_loss_rate_; + int actual_loss_rate_; + int burst_length_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_PACKETLOSSTEST_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/RTPFile.cc b/third_party/libwebrtc/modules/audio_coding/test/RTPFile.cc new file mode 100644 index 0000000000..0c2ab3c443 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/RTPFile.cc @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "RTPFile.h" + +#include <stdlib.h> + +#include <limits> + +#include "absl/strings/string_view.h" + +#ifdef WIN32 +#include <Winsock2.h> +#else +#include <arpa/inet.h> +#endif + +// TODO(tlegrand): Consider removing usage of gtest. +#include "test/gtest.h" + +namespace webrtc { + +void RTPStream::ParseRTPHeader(RTPHeader* rtp_header, + const uint8_t* rtpHeader) { + rtp_header->payloadType = rtpHeader[1]; + rtp_header->sequenceNumber = + (static_cast<uint16_t>(rtpHeader[2]) << 8) | rtpHeader[3]; + rtp_header->timestamp = (static_cast<uint32_t>(rtpHeader[4]) << 24) | + (static_cast<uint32_t>(rtpHeader[5]) << 16) | + (static_cast<uint32_t>(rtpHeader[6]) << 8) | + rtpHeader[7]; + rtp_header->ssrc = (static_cast<uint32_t>(rtpHeader[8]) << 24) | + (static_cast<uint32_t>(rtpHeader[9]) << 16) | + (static_cast<uint32_t>(rtpHeader[10]) << 8) | + rtpHeader[11]; +} + +void RTPStream::MakeRTPheader(uint8_t* rtpHeader, + uint8_t payloadType, + int16_t seqNo, + uint32_t timeStamp, + uint32_t ssrc) { + rtpHeader[0] = 0x80; + rtpHeader[1] = payloadType; + rtpHeader[2] = (seqNo >> 8) & 0xFF; + rtpHeader[3] = seqNo & 0xFF; + rtpHeader[4] = timeStamp >> 24; + rtpHeader[5] = (timeStamp >> 16) & 0xFF; + rtpHeader[6] = (timeStamp >> 8) & 0xFF; + rtpHeader[7] = timeStamp & 0xFF; + rtpHeader[8] = ssrc >> 24; + rtpHeader[9] = (ssrc >> 16) & 0xFF; + rtpHeader[10] = (ssrc >> 8) & 0xFF; + rtpHeader[11] = ssrc & 0xFF; +} + +RTPPacket::RTPPacket(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, + const uint8_t* payloadData, + size_t payloadSize, + uint32_t frequency) + : payloadType(payloadType), + timeStamp(timeStamp), + seqNo(seqNo), + payloadSize(payloadSize), + frequency(frequency) { + if (payloadSize > 0) { + this->payloadData = new uint8_t[payloadSize]; + memcpy(this->payloadData, payloadData, payloadSize); + } +} + +RTPPacket::~RTPPacket() { + delete[] payloadData; +} + +void RTPBuffer::Write(const uint8_t payloadType, + const uint32_t timeStamp, + const int16_t seqNo, + const uint8_t* payloadData, + const size_t payloadSize, + uint32_t frequency) { + RTPPacket* packet = new RTPPacket(payloadType, timeStamp, seqNo, payloadData, + payloadSize, frequency); + MutexLock lock(&mutex_); + _rtpQueue.push(packet); +} + +size_t RTPBuffer::Read(RTPHeader* rtp_header, + uint8_t* payloadData, + size_t payloadSize, + uint32_t* offset) { + RTPPacket* packet; + { + MutexLock lock(&mutex_); + packet = _rtpQueue.front(); + _rtpQueue.pop(); + } + rtp_header->markerBit = 1; + rtp_header->payloadType = packet->payloadType; + rtp_header->sequenceNumber = packet->seqNo; + rtp_header->ssrc = 0; + rtp_header->timestamp = packet->timeStamp; + if (packet->payloadSize > 0 && payloadSize >= packet->payloadSize) { + memcpy(payloadData, packet->payloadData, packet->payloadSize); + } else { + return 0; + } + *offset = (packet->timeStamp / (packet->frequency / 1000)); + + return packet->payloadSize; +} + +bool RTPBuffer::EndOfFile() const { + MutexLock lock(&mutex_); + return _rtpQueue.empty(); +} + +void RTPFile::Open(absl::string_view filename, absl::string_view mode) { + std::string filename_str = std::string(filename); + if ((_rtpFile = fopen(filename_str.c_str(), std::string(mode).c_str())) == + NULL) { + printf("Cannot write file %s.\n", filename_str.c_str()); + ADD_FAILURE() << "Unable to write file"; + exit(1); + } +} + +void RTPFile::Close() { + if (_rtpFile != NULL) { + fclose(_rtpFile); + _rtpFile = NULL; + } +} + +void RTPFile::WriteHeader() { + // Write data in a format that NetEQ and RTP Play can parse + fprintf(_rtpFile, "#!RTPencode%s\n", "1.0"); + uint32_t dummy_variable = 0; + // should be converted to network endian format, but does not matter when 0 + EXPECT_EQ(1u, fwrite(&dummy_variable, 4, 1, _rtpFile)); + EXPECT_EQ(1u, fwrite(&dummy_variable, 4, 1, _rtpFile)); + EXPECT_EQ(1u, fwrite(&dummy_variable, 4, 1, _rtpFile)); + EXPECT_EQ(1u, fwrite(&dummy_variable, 2, 1, _rtpFile)); + EXPECT_EQ(1u, fwrite(&dummy_variable, 2, 1, _rtpFile)); + fflush(_rtpFile); +} + +void RTPFile::ReadHeader() { + uint32_t start_sec, start_usec, source; + uint16_t port, padding; + char fileHeader[40]; + EXPECT_TRUE(fgets(fileHeader, 40, _rtpFile) != 0); + EXPECT_EQ(1u, fread(&start_sec, 4, 1, _rtpFile)); + start_sec = ntohl(start_sec); + EXPECT_EQ(1u, fread(&start_usec, 4, 1, _rtpFile)); + start_usec = ntohl(start_usec); + EXPECT_EQ(1u, fread(&source, 4, 1, _rtpFile)); + source = ntohl(source); + EXPECT_EQ(1u, fread(&port, 2, 1, _rtpFile)); + port = ntohs(port); + EXPECT_EQ(1u, fread(&padding, 2, 1, _rtpFile)); + padding = ntohs(padding); +} + +void RTPFile::Write(const uint8_t payloadType, + const uint32_t timeStamp, + const int16_t seqNo, + const uint8_t* payloadData, + const size_t payloadSize, + uint32_t frequency) { + /* write RTP packet to file */ + uint8_t rtpHeader[12]; + MakeRTPheader(rtpHeader, payloadType, seqNo, timeStamp, 0); + ASSERT_LE(12 + payloadSize + 8, std::numeric_limits<u_short>::max()); + uint16_t lengthBytes = htons(static_cast<u_short>(12 + payloadSize + 8)); + uint16_t plen = htons(static_cast<u_short>(12 + payloadSize)); + uint32_t offsetMs; + + offsetMs = (timeStamp / (frequency / 1000)); + offsetMs = htonl(offsetMs); + EXPECT_EQ(1u, fwrite(&lengthBytes, 2, 1, _rtpFile)); + EXPECT_EQ(1u, fwrite(&plen, 2, 1, _rtpFile)); + EXPECT_EQ(1u, fwrite(&offsetMs, 4, 1, _rtpFile)); + EXPECT_EQ(1u, fwrite(&rtpHeader, 12, 1, _rtpFile)); + EXPECT_EQ(payloadSize, fwrite(payloadData, 1, payloadSize, _rtpFile)); +} + +size_t RTPFile::Read(RTPHeader* rtp_header, + uint8_t* payloadData, + size_t payloadSize, + uint32_t* offset) { + uint16_t lengthBytes; + uint16_t plen; + uint8_t rtpHeader[12]; + size_t read_len = fread(&lengthBytes, 2, 1, _rtpFile); + /* Check if we have reached end of file. */ + if ((read_len == 0) && feof(_rtpFile)) { + _rtpEOF = true; + return 0; + } + EXPECT_EQ(1u, fread(&plen, 2, 1, _rtpFile)); + EXPECT_EQ(1u, fread(offset, 4, 1, _rtpFile)); + lengthBytes = ntohs(lengthBytes); + plen = ntohs(plen); + *offset = ntohl(*offset); + EXPECT_GT(plen, 11); + + EXPECT_EQ(1u, fread(rtpHeader, 12, 1, _rtpFile)); + ParseRTPHeader(rtp_header, rtpHeader); + EXPECT_EQ(lengthBytes, plen + 8); + + if (plen == 0) { + return 0; + } + if (lengthBytes < 20) { + return 0; + } + if (payloadSize < static_cast<size_t>((lengthBytes - 20))) { + return 0; + } + lengthBytes -= 20; + EXPECT_EQ(lengthBytes, fread(payloadData, 1, lengthBytes, _rtpFile)); + return lengthBytes; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/RTPFile.h b/third_party/libwebrtc/modules/audio_coding/test/RTPFile.h new file mode 100644 index 0000000000..b796491da9 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/RTPFile.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_RTPFILE_H_ +#define MODULES_AUDIO_CODING_TEST_RTPFILE_H_ + +#include <stdio.h> + +#include <queue> + +#include "absl/strings/string_view.h" +#include "api/rtp_headers.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RTPStream { + public: + virtual ~RTPStream() {} + + virtual void Write(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, + const uint8_t* payloadData, + size_t payloadSize, + uint32_t frequency) = 0; + + // Returns the packet's payload size. Zero should be treated as an + // end-of-stream (in the case that EndOfFile() is true) or an error. + virtual size_t Read(RTPHeader* rtp_Header, + uint8_t* payloadData, + size_t payloadSize, + uint32_t* offset) = 0; + virtual bool EndOfFile() const = 0; + + protected: + void MakeRTPheader(uint8_t* rtpHeader, + uint8_t payloadType, + int16_t seqNo, + uint32_t timeStamp, + uint32_t ssrc); + + void ParseRTPHeader(RTPHeader* rtp_header, const uint8_t* rtpHeader); +}; + +class RTPPacket { + public: + RTPPacket(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, + const uint8_t* payloadData, + size_t payloadSize, + uint32_t frequency); + + ~RTPPacket(); + + uint8_t payloadType; + uint32_t timeStamp; + int16_t seqNo; + uint8_t* payloadData; + size_t payloadSize; + uint32_t frequency; +}; + +class RTPBuffer : public RTPStream { + public: + RTPBuffer() = default; + + ~RTPBuffer() = default; + + void Write(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, + const uint8_t* payloadData, + size_t payloadSize, + uint32_t frequency) override; + + size_t Read(RTPHeader* rtp_header, + uint8_t* payloadData, + size_t payloadSize, + uint32_t* offset) override; + + bool EndOfFile() const override; + + private: + mutable Mutex mutex_; + std::queue<RTPPacket*> _rtpQueue RTC_GUARDED_BY(&mutex_); +}; + +class RTPFile : public RTPStream { + public: + ~RTPFile() {} + + RTPFile() : _rtpFile(NULL), _rtpEOF(false) {} + + void Open(absl::string_view outFilename, absl::string_view mode); + + void Close(); + + void WriteHeader(); + + void ReadHeader(); + + void Write(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, + const uint8_t* payloadData, + size_t payloadSize, + uint32_t frequency) override; + + size_t Read(RTPHeader* rtp_header, + uint8_t* payloadData, + size_t payloadSize, + uint32_t* offset) override; + + bool EndOfFile() const override { return _rtpEOF; } + + private: + FILE* _rtpFile; + bool _rtpEOF; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_RTPFILE_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestAllCodecs.cc b/third_party/libwebrtc/modules/audio_coding/test/TestAllCodecs.cc new file mode 100644 index 0000000000..e93df346f1 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestAllCodecs.cc @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/TestAllCodecs.h" + +#include <cstdio> +#include <limits> +#include <string> + +#include "absl/strings/match.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "modules/audio_coding/include/audio_coding_module_typedefs.h" +#include "modules/include/module_common_types.h" +#include "rtc_base/logging.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/strings/string_builder.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +// Description of the test: +// In this test we set up a one-way communication channel from a participant +// called "a" to a participant called "b". +// a -> channel_a_to_b -> b +// +// The test loops through all available mono codecs, encode at "a" sends over +// the channel, and decodes at "b". + +#define CHECK_ERROR(f) \ + do { \ + EXPECT_GE(f, 0) << "Error Calling API"; \ + } while (0) + +namespace { +const size_t kVariableSize = std::numeric_limits<size_t>::max(); +} + +namespace webrtc { + +// Class for simulating packet handling. +TestPack::TestPack() + : receiver_acm_(NULL), + sequence_number_(0), + timestamp_diff_(0), + last_in_timestamp_(0), + total_bytes_(0), + payload_size_(0) {} + +TestPack::~TestPack() {} + +void TestPack::RegisterReceiverACM(AudioCodingModule* acm) { + receiver_acm_ = acm; + return; +} + +int32_t TestPack::SendData(AudioFrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + size_t payload_size, + int64_t absolute_capture_timestamp_ms) { + RTPHeader rtp_header; + int32_t status; + + rtp_header.markerBit = false; + rtp_header.ssrc = 0; + rtp_header.sequenceNumber = sequence_number_++; + rtp_header.payloadType = payload_type; + rtp_header.timestamp = timestamp; + + if (frame_type == AudioFrameType::kEmptyFrame) { + // Skip this frame. + return 0; + } + + // Only run mono for all test cases. + memcpy(payload_data_, payload_data, payload_size); + + status = + receiver_acm_->IncomingPacket(payload_data_, payload_size, rtp_header); + + payload_size_ = payload_size; + timestamp_diff_ = timestamp - last_in_timestamp_; + last_in_timestamp_ = timestamp; + total_bytes_ += payload_size; + return status; +} + +size_t TestPack::payload_size() { + return payload_size_; +} + +uint32_t TestPack::timestamp_diff() { + return timestamp_diff_; +} + +void TestPack::reset_payload_size() { + payload_size_ = 0; +} + +TestAllCodecs::TestAllCodecs() + : acm_a_(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))), + acm_b_(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))), + channel_a_to_b_(NULL), + test_count_(0), + packet_size_samples_(0), + packet_size_bytes_(0) {} + +TestAllCodecs::~TestAllCodecs() { + if (channel_a_to_b_ != NULL) { + delete channel_a_to_b_; + channel_a_to_b_ = NULL; + } +} + +void TestAllCodecs::Perform() { + const std::string file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + infile_a_.Open(file_name, 32000, "rb"); + + acm_a_->InitializeReceiver(); + acm_b_->InitializeReceiver(); + + acm_b_->SetReceiveCodecs({{103, {"ISAC", 16000, 1}}, + {104, {"ISAC", 32000, 1}}, + {107, {"L16", 8000, 1}}, + {108, {"L16", 16000, 1}}, + {109, {"L16", 32000, 1}}, + {111, {"L16", 8000, 2}}, + {112, {"L16", 16000, 2}}, + {113, {"L16", 32000, 2}}, + {0, {"PCMU", 8000, 1}}, + {110, {"PCMU", 8000, 2}}, + {8, {"PCMA", 8000, 1}}, + {118, {"PCMA", 8000, 2}}, + {102, {"ILBC", 8000, 1}}, + {9, {"G722", 8000, 1}}, + {119, {"G722", 8000, 2}}, + {120, {"OPUS", 48000, 2, {{"stereo", "1"}}}}, + {13, {"CN", 8000, 1}}, + {98, {"CN", 16000, 1}}, + {99, {"CN", 32000, 1}}}); + + // Create and connect the channel + channel_a_to_b_ = new TestPack; + acm_a_->RegisterTransportCallback(channel_a_to_b_); + channel_a_to_b_->RegisterReceiverACM(acm_b_.get()); + + // All codecs are tested for all allowed sampling frequencies, rates and + // packet sizes. + test_count_++; + OpenOutFile(test_count_); + char codec_g722[] = "G722"; + RegisterSendCodec('A', codec_g722, 16000, 64000, 160, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_g722, 16000, 64000, 320, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_g722, 16000, 64000, 480, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_g722, 16000, 64000, 640, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_g722, 16000, 64000, 800, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_g722, 16000, 64000, 960, 0); + Run(channel_a_to_b_); + outfile_b_.Close(); +#ifdef WEBRTC_CODEC_ILBC + test_count_++; + OpenOutFile(test_count_); + char codec_ilbc[] = "ILBC"; + RegisterSendCodec('A', codec_ilbc, 8000, 13300, 240, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_ilbc, 8000, 13300, 480, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_ilbc, 8000, 15200, 160, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_ilbc, 8000, 15200, 320, 0); + Run(channel_a_to_b_); + outfile_b_.Close(); +#endif +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + test_count_++; + OpenOutFile(test_count_); + char codec_isac[] = "ISAC"; + RegisterSendCodec('A', codec_isac, 16000, -1, 480, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_isac, 16000, -1, 960, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_isac, 16000, 15000, 480, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_isac, 16000, 32000, 960, kVariableSize); + Run(channel_a_to_b_); + outfile_b_.Close(); +#endif +#ifdef WEBRTC_CODEC_ISAC + test_count_++; + OpenOutFile(test_count_); + RegisterSendCodec('A', codec_isac, 32000, -1, 960, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_isac, 32000, 56000, 960, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_isac, 32000, 37000, 960, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_isac, 32000, 32000, 960, kVariableSize); + Run(channel_a_to_b_); + outfile_b_.Close(); +#endif + test_count_++; + OpenOutFile(test_count_); + char codec_l16[] = "L16"; + RegisterSendCodec('A', codec_l16, 8000, 128000, 80, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_l16, 8000, 128000, 160, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_l16, 8000, 128000, 240, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_l16, 8000, 128000, 320, 0); + Run(channel_a_to_b_); + outfile_b_.Close(); + + test_count_++; + OpenOutFile(test_count_); + RegisterSendCodec('A', codec_l16, 16000, 256000, 160, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_l16, 16000, 256000, 320, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_l16, 16000, 256000, 480, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_l16, 16000, 256000, 640, 0); + Run(channel_a_to_b_); + outfile_b_.Close(); + + test_count_++; + OpenOutFile(test_count_); + RegisterSendCodec('A', codec_l16, 32000, 512000, 320, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_l16, 32000, 512000, 640, 0); + Run(channel_a_to_b_); + outfile_b_.Close(); + + test_count_++; + OpenOutFile(test_count_); + char codec_pcma[] = "PCMA"; + RegisterSendCodec('A', codec_pcma, 8000, 64000, 80, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 160, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 240, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 320, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 400, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 480, 0); + Run(channel_a_to_b_); + + char codec_pcmu[] = "PCMU"; + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 80, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 160, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 240, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 320, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 400, 0); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 480, 0); + Run(channel_a_to_b_); + outfile_b_.Close(); +#ifdef WEBRTC_CODEC_OPUS + test_count_++; + OpenOutFile(test_count_); + char codec_opus[] = "OPUS"; + RegisterSendCodec('A', codec_opus, 48000, 6000, 480, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_opus, 48000, 20000, 480 * 2, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_opus, 48000, 32000, 480 * 4, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_opus, 48000, 48000, 480, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_opus, 48000, 64000, 480 * 4, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_opus, 48000, 96000, 480 * 6, kVariableSize); + Run(channel_a_to_b_); + RegisterSendCodec('A', codec_opus, 48000, 500000, 480 * 2, kVariableSize); + Run(channel_a_to_b_); + outfile_b_.Close(); +#endif +} + +// Register Codec to use in the test +// +// Input: side - which ACM to use, 'A' or 'B' +// codec_name - name to use when register the codec +// sampling_freq_hz - sampling frequency in Herz +// rate - bitrate in bytes +// packet_size - packet size in samples +// extra_byte - if extra bytes needed compared to the bitrate +// used when registering, can be an internal header +// set to kVariableSize if the codec is a variable +// rate codec +void TestAllCodecs::RegisterSendCodec(char side, + char* codec_name, + int32_t sampling_freq_hz, + int rate, + int packet_size, + size_t extra_byte) { + // Store packet-size in samples, used to validate the received packet. + // If G.722, store half the size to compensate for the timestamp bug in the + // RFC for G.722. + // If iSAC runs in adaptive mode, packet size in samples can change on the + // fly, so we exclude this test by setting `packet_size_samples_` to -1. + int clockrate_hz = sampling_freq_hz; + size_t num_channels = 1; + if (absl::EqualsIgnoreCase(codec_name, "G722")) { + packet_size_samples_ = packet_size / 2; + clockrate_hz = sampling_freq_hz / 2; + } else if (absl::EqualsIgnoreCase(codec_name, "ISAC") && (rate == -1)) { + packet_size_samples_ = -1; + } else if (absl::EqualsIgnoreCase(codec_name, "OPUS")) { + packet_size_samples_ = packet_size; + num_channels = 2; + } else { + packet_size_samples_ = packet_size; + } + + // Store the expected packet size in bytes, used to validate the received + // packet. If variable rate codec (extra_byte == -1), set to -1. + if (extra_byte != kVariableSize) { + // Add 0.875 to always round up to a whole byte + packet_size_bytes_ = + static_cast<size_t>(static_cast<float>(packet_size * rate) / + static_cast<float>(sampling_freq_hz * 8) + + 0.875) + + extra_byte; + } else { + // Packets will have a variable size. + packet_size_bytes_ = kVariableSize; + } + + // Set pointer to the ACM where to register the codec. + AudioCodingModule* my_acm = NULL; + switch (side) { + case 'A': { + my_acm = acm_a_.get(); + break; + } + case 'B': { + my_acm = acm_b_.get(); + break; + } + default: { + break; + } + } + ASSERT_TRUE(my_acm != NULL); + + auto factory = CreateBuiltinAudioEncoderFactory(); + constexpr int payload_type = 17; + SdpAudioFormat format = {codec_name, clockrate_hz, num_channels}; + format.parameters["ptime"] = rtc::ToString(rtc::CheckedDivExact( + packet_size, rtc::CheckedDivExact(sampling_freq_hz, 1000))); + my_acm->SetEncoder( + factory->MakeAudioEncoder(payload_type, format, absl::nullopt)); +} + +void TestAllCodecs::Run(TestPack* channel) { + AudioFrame audio_frame; + + int32_t out_freq_hz = outfile_b_.SamplingFrequency(); + size_t receive_size; + uint32_t timestamp_diff; + channel->reset_payload_size(); + int error_count = 0; + int counter = 0; + // Set test length to 500 ms (50 blocks of 10 ms each). + infile_a_.SetNum10MsBlocksToRead(50); + // Fast-forward 1 second (100 blocks) since the file starts with silence. + infile_a_.FastForward(100); + + while (!infile_a_.EndOfFile()) { + // Add 10 msec to ACM. + infile_a_.Read10MsData(audio_frame); + CHECK_ERROR(acm_a_->Add10MsData(audio_frame)); + + // Verify that the received packet size matches the settings. + receive_size = channel->payload_size(); + if (receive_size) { + if ((receive_size != packet_size_bytes_) && + (packet_size_bytes_ != kVariableSize)) { + error_count++; + } + + // Verify that the timestamp is updated with expected length. The counter + // is used to avoid problems when switching codec or frame size in the + // test. + timestamp_diff = channel->timestamp_diff(); + if ((counter > 10) && + (static_cast<int>(timestamp_diff) != packet_size_samples_) && + (packet_size_samples_ > -1)) + error_count++; + } + + // Run received side of ACM. + bool muted; + CHECK_ERROR(acm_b_->PlayoutData10Ms(out_freq_hz, &audio_frame, &muted)); + ASSERT_FALSE(muted); + + // Write output speech to file. + outfile_b_.Write10MsData(audio_frame.data(), + audio_frame.samples_per_channel_); + + // Update loop counter + counter++; + } + + EXPECT_EQ(0, error_count); + + if (infile_a_.EndOfFile()) { + infile_a_.Rewind(); + } +} + +void TestAllCodecs::OpenOutFile(int test_number) { + std::string filename = webrtc::test::OutputPath(); + rtc::StringBuilder test_number_str; + test_number_str << test_number; + filename += "testallcodecs_out_"; + filename += test_number_str.str(); + filename += ".pcm"; + outfile_b_.Open(filename, 32000, "wb"); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestAllCodecs.h b/third_party/libwebrtc/modules/audio_coding/test/TestAllCodecs.h new file mode 100644 index 0000000000..0c276414e4 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestAllCodecs.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_TESTALLCODECS_H_ +#define MODULES_AUDIO_CODING_TEST_TESTALLCODECS_H_ + +#include <memory> + +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_coding/test/PCMFile.h" + +namespace webrtc { + +class TestPack : public AudioPacketizationCallback { + public: + TestPack(); + ~TestPack(); + + void RegisterReceiverACM(AudioCodingModule* acm); + + int32_t SendData(AudioFrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + size_t payload_size, + int64_t absolute_capture_timestamp_ms) override; + + size_t payload_size(); + uint32_t timestamp_diff(); + void reset_payload_size(); + + private: + AudioCodingModule* receiver_acm_; + uint16_t sequence_number_; + uint8_t payload_data_[60 * 32 * 2 * 2]; + uint32_t timestamp_diff_; + uint32_t last_in_timestamp_; + uint64_t total_bytes_; + size_t payload_size_; +}; + +class TestAllCodecs { + public: + TestAllCodecs(); + ~TestAllCodecs(); + + void Perform(); + + private: + // The default value of '-1' indicates that the registration is based only on + // codec name, and a sampling frequency matching is not required. + // This is useful for codecs which support several sampling frequency. + // Note! Only mono mode is tested in this test. + void RegisterSendCodec(char side, + char* codec_name, + int32_t sampling_freq_hz, + int rate, + int packet_size, + size_t extra_byte); + + void Run(TestPack* channel); + void OpenOutFile(int test_number); + + std::unique_ptr<AudioCodingModule> acm_a_; + std::unique_ptr<AudioCodingModule> acm_b_; + TestPack* channel_a_to_b_; + PCMFile infile_a_; + PCMFile outfile_b_; + int test_count_; + int packet_size_samples_; + size_t packet_size_bytes_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_TESTALLCODECS_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestRedFec.cc b/third_party/libwebrtc/modules/audio_coding/test/TestRedFec.cc new file mode 100644 index 0000000000..892fbc83d6 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestRedFec.cc @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/TestRedFec.h" + +#include <memory> +#include <utility> + +#include "absl/strings/match.h" +#include "api/audio_codecs/L16/audio_decoder_L16.h" +#include "api/audio_codecs/L16/audio_encoder_L16.h" +#include "api/audio_codecs/audio_decoder_factory_template.h" +#include "api/audio_codecs/audio_encoder_factory_template.h" +#include "api/audio_codecs/g711/audio_decoder_g711.h" +#include "api/audio_codecs/g711/audio_encoder_g711.h" +#include "api/audio_codecs/g722/audio_decoder_g722.h" +#include "api/audio_codecs/g722/audio_encoder_g722.h" +#include "api/audio_codecs/isac/audio_decoder_isac_float.h" +#include "api/audio_codecs/isac/audio_encoder_isac_float.h" +#include "api/audio_codecs/opus/audio_decoder_opus.h" +#include "api/audio_codecs/opus/audio_encoder_opus.h" +#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h" +#include "modules/audio_coding/codecs/red/audio_encoder_copy_red.h" +#include "modules/audio_coding/include/audio_coding_module_typedefs.h" +#include "rtc_base/strings/string_builder.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +TestRedFec::TestRedFec() + : encoder_factory_(CreateAudioEncoderFactory<AudioEncoderG711, + AudioEncoderG722, + AudioEncoderIsacFloat, + AudioEncoderL16, + AudioEncoderOpus>()), + decoder_factory_(CreateAudioDecoderFactory<AudioDecoderG711, + AudioDecoderG722, + AudioDecoderIsacFloat, + AudioDecoderL16, + AudioDecoderOpus>()), + _acmA(AudioCodingModule::Create( + AudioCodingModule::Config(decoder_factory_))), + _acmB(AudioCodingModule::Create( + AudioCodingModule::Config(decoder_factory_))), + _channelA2B(NULL), + _testCntr(0) {} + +TestRedFec::~TestRedFec() { + if (_channelA2B != NULL) { + delete _channelA2B; + _channelA2B = NULL; + } +} + +void TestRedFec::Perform() { + const std::string file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + _inFileA.Open(file_name, 32000, "rb"); + + ASSERT_EQ(0, _acmA->InitializeReceiver()); + ASSERT_EQ(0, _acmB->InitializeReceiver()); + + // Create and connect the channel + _channelA2B = new Channel; + _acmA->RegisterTransportCallback(_channelA2B); + _channelA2B->RegisterReceiverACM(_acmB.get()); + + RegisterSendCodec(_acmA, {"L16", 8000, 1}, Vad::kVadAggressive, true); + + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + // Switch to another 8 kHz codec; RED should remain switched on. + RegisterSendCodec(_acmA, {"PCMU", 8000, 1}, Vad::kVadAggressive, true); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + // Switch to a 16 kHz codec; RED should be switched off. + RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false); + + OpenOutFile(_testCntr); + RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false); + Run(); + RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false); + Run(); + _outFileB.Close(); + + RegisterSendCodec(_acmA, {"ISAC", 16000, 1}, Vad::kVadVeryAggressive, false); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + // Switch to a 32 kHz codec; RED should be switched off. + RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, Vad::kVadVeryAggressive, false); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, absl::nullopt, false); + + _channelA2B->SetFECTestWithPacketLoss(true); + // Following tests are under packet losses. + + // Switch to a 16 kHz codec; RED should be switched off. + RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false); + + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + // Switch to a 16 kHz codec, RED should have been switched off. + RegisterSendCodec(_acmA, {"ISAC", 16000, 1}, Vad::kVadVeryAggressive, false); + + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + // Switch to a 32 kHz codec, RED should have been switched off. + RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, Vad::kVadVeryAggressive, false); + + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, absl::nullopt, false); + + RegisterSendCodec(_acmA, {"opus", 48000, 2}, absl::nullopt, false); + + // _channelA2B imposes 25% packet loss rate. + EXPECT_EQ(0, _acmA->SetPacketLossRate(25)); + + _acmA->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* enc) { + EXPECT_EQ(true, (*enc)->SetFec(true)); + }); + + OpenOutFile(_testCntr); + Run(); + + // Switch to L16 with RED. + RegisterSendCodec(_acmA, {"L16", 8000, 1}, absl::nullopt, true); + Run(); + + // Switch to Opus again. + RegisterSendCodec(_acmA, {"opus", 48000, 2}, absl::nullopt, false); + _acmA->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* enc) { + EXPECT_EQ(true, (*enc)->SetFec(false)); + }); + Run(); + + _acmA->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* enc) { + EXPECT_EQ(true, (*enc)->SetFec(true)); + }); + _outFileB.Close(); +} + +void TestRedFec::RegisterSendCodec( + const std::unique_ptr<AudioCodingModule>& acm, + const SdpAudioFormat& codec_format, + absl::optional<Vad::Aggressiveness> vad_mode, + bool use_red) { + constexpr int payload_type = 17, cn_payload_type = 27, red_payload_type = 37; + const auto& other_acm = &acm == &_acmA ? _acmB : _acmA; + + auto encoder = encoder_factory_->MakeAudioEncoder(payload_type, codec_format, + absl::nullopt); + EXPECT_NE(encoder, nullptr); + std::map<int, SdpAudioFormat> receive_codecs = {{payload_type, codec_format}}; + if (!absl::EqualsIgnoreCase(codec_format.name, "opus")) { + if (vad_mode.has_value()) { + AudioEncoderCngConfig config; + config.speech_encoder = std::move(encoder); + config.num_channels = 1; + config.payload_type = cn_payload_type; + config.vad_mode = vad_mode.value(); + encoder = CreateComfortNoiseEncoder(std::move(config)); + receive_codecs.emplace(std::make_pair( + cn_payload_type, SdpAudioFormat("CN", codec_format.clockrate_hz, 1))); + } + if (use_red) { + AudioEncoderCopyRed::Config config; + config.payload_type = red_payload_type; + config.speech_encoder = std::move(encoder); + encoder = std::make_unique<AudioEncoderCopyRed>(std::move(config), + field_trials_); + receive_codecs.emplace( + std::make_pair(red_payload_type, + SdpAudioFormat("red", codec_format.clockrate_hz, 1))); + } + } + acm->SetEncoder(std::move(encoder)); + other_acm->SetReceiveCodecs(receive_codecs); +} + +void TestRedFec::Run() { + AudioFrame audioFrame; + int32_t outFreqHzB = _outFileB.SamplingFrequency(); + // Set test length to 500 ms (50 blocks of 10 ms each). + _inFileA.SetNum10MsBlocksToRead(50); + // Fast-forward 1 second (100 blocks) since the file starts with silence. + _inFileA.FastForward(100); + + while (!_inFileA.EndOfFile()) { + EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0); + EXPECT_GE(_acmA->Add10MsData(audioFrame), 0); + bool muted; + EXPECT_EQ(0, _acmB->PlayoutData10Ms(outFreqHzB, &audioFrame, &muted)); + ASSERT_FALSE(muted); + _outFileB.Write10MsData(audioFrame.data(), audioFrame.samples_per_channel_); + } + _inFileA.Rewind(); +} + +void TestRedFec::OpenOutFile(int16_t test_number) { + std::string file_name; + rtc::StringBuilder file_stream; + file_stream << webrtc::test::OutputPath(); + file_stream << "TestRedFec_outFile_"; + file_stream << test_number << ".pcm"; + file_name = file_stream.str(); + _outFileB.Open(file_name, 16000, "wb"); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestRedFec.h b/third_party/libwebrtc/modules/audio_coding/test/TestRedFec.h new file mode 100644 index 0000000000..dbadd88487 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestRedFec.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_TESTREDFEC_H_ +#define MODULES_AUDIO_CODING_TEST_TESTREDFEC_H_ + +#include <memory> +#include <string> + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "common_audio/vad/include/vad.h" +#include "modules/audio_coding/test/Channel.h" +#include "modules/audio_coding/test/PCMFile.h" +#include "test/scoped_key_value_config.h" + +namespace webrtc { + +class TestRedFec { + public: + explicit TestRedFec(); + ~TestRedFec(); + + void Perform(); + + private: + void RegisterSendCodec(const std::unique_ptr<AudioCodingModule>& acm, + const SdpAudioFormat& codec_format, + absl::optional<Vad::Aggressiveness> vad_mode, + bool use_red); + void Run(); + void OpenOutFile(int16_t testNumber); + + test::ScopedKeyValueConfig field_trials_; + const rtc::scoped_refptr<AudioEncoderFactory> encoder_factory_; + const rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_; + std::unique_ptr<AudioCodingModule> _acmA; + std::unique_ptr<AudioCodingModule> _acmB; + + Channel* _channelA2B; + + PCMFile _inFileA; + PCMFile _outFileB; + int16_t _testCntr; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_TESTREDFEC_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestStereo.cc b/third_party/libwebrtc/modules/audio_coding/test/TestStereo.cc new file mode 100644 index 0000000000..599fafb602 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestStereo.cc @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/TestStereo.h" + +#include <string> + +#include "absl/strings/match.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "modules/audio_coding/include/audio_coding_module_typedefs.h" +#include "modules/include/module_common_types.h" +#include "rtc_base/strings/string_builder.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +// Class for simulating packet handling +TestPackStereo::TestPackStereo() + : receiver_acm_(NULL), + seq_no_(0), + timestamp_diff_(0), + last_in_timestamp_(0), + total_bytes_(0), + payload_size_(0), + lost_packet_(false) {} + +TestPackStereo::~TestPackStereo() {} + +void TestPackStereo::RegisterReceiverACM(AudioCodingModule* acm) { + receiver_acm_ = acm; + return; +} + +int32_t TestPackStereo::SendData(const AudioFrameType frame_type, + const uint8_t payload_type, + const uint32_t timestamp, + const uint8_t* payload_data, + const size_t payload_size, + int64_t absolute_capture_timestamp_ms) { + RTPHeader rtp_header; + int32_t status = 0; + + rtp_header.markerBit = false; + rtp_header.ssrc = 0; + rtp_header.sequenceNumber = seq_no_++; + rtp_header.payloadType = payload_type; + rtp_header.timestamp = timestamp; + if (frame_type == AudioFrameType::kEmptyFrame) { + // Skip this frame + return 0; + } + + if (lost_packet_ == false) { + status = + receiver_acm_->IncomingPacket(payload_data, payload_size, rtp_header); + + if (frame_type != AudioFrameType::kAudioFrameCN) { + payload_size_ = static_cast<int>(payload_size); + } else { + payload_size_ = -1; + } + + timestamp_diff_ = timestamp - last_in_timestamp_; + last_in_timestamp_ = timestamp; + total_bytes_ += payload_size; + } + return status; +} + +uint16_t TestPackStereo::payload_size() { + return static_cast<uint16_t>(payload_size_); +} + +uint32_t TestPackStereo::timestamp_diff() { + return timestamp_diff_; +} + +void TestPackStereo::reset_payload_size() { + payload_size_ = 0; +} + +void TestPackStereo::set_codec_mode(enum StereoMonoMode mode) { + codec_mode_ = mode; +} + +void TestPackStereo::set_lost_packet(bool lost) { + lost_packet_ = lost; +} + +TestStereo::TestStereo() + : acm_a_(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))), + acm_b_(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))), + channel_a2b_(NULL), + test_cntr_(0), + pack_size_samp_(0), + pack_size_bytes_(0), + counter_(0) {} + +TestStereo::~TestStereo() { + if (channel_a2b_ != NULL) { + delete channel_a2b_; + channel_a2b_ = NULL; + } +} + +void TestStereo::Perform() { + uint16_t frequency_hz; + int audio_channels; + int codec_channels; + + // Open both mono and stereo test files in 32 kHz. + const std::string file_name_stereo = + webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"); + const std::string file_name_mono = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + frequency_hz = 32000; + in_file_stereo_ = new PCMFile(); + in_file_mono_ = new PCMFile(); + in_file_stereo_->Open(file_name_stereo, frequency_hz, "rb"); + in_file_stereo_->ReadStereo(true); + in_file_mono_->Open(file_name_mono, frequency_hz, "rb"); + in_file_mono_->ReadStereo(false); + + // Create and initialize two ACMs, one for each side of a one-to-one call. + ASSERT_TRUE((acm_a_.get() != NULL) && (acm_b_.get() != NULL)); + EXPECT_EQ(0, acm_a_->InitializeReceiver()); + EXPECT_EQ(0, acm_b_->InitializeReceiver()); + + acm_b_->SetReceiveCodecs({{103, {"ISAC", 16000, 1}}, + {104, {"ISAC", 32000, 1}}, + {107, {"L16", 8000, 1}}, + {108, {"L16", 16000, 1}}, + {109, {"L16", 32000, 1}}, + {111, {"L16", 8000, 2}}, + {112, {"L16", 16000, 2}}, + {113, {"L16", 32000, 2}}, + {0, {"PCMU", 8000, 1}}, + {110, {"PCMU", 8000, 2}}, + {8, {"PCMA", 8000, 1}}, + {118, {"PCMA", 8000, 2}}, + {102, {"ILBC", 8000, 1}}, + {9, {"G722", 8000, 1}}, + {119, {"G722", 8000, 2}}, + {120, {"OPUS", 48000, 2, {{"stereo", "1"}}}}, + {13, {"CN", 8000, 1}}, + {98, {"CN", 16000, 1}}, + {99, {"CN", 32000, 1}}}); + + // Create and connect the channel. + channel_a2b_ = new TestPackStereo; + EXPECT_EQ(0, acm_a_->RegisterTransportCallback(channel_a2b_)); + channel_a2b_->RegisterReceiverACM(acm_b_.get()); + + char codec_pcma_temp[] = "PCMA"; + RegisterSendCodec('A', codec_pcma_temp, 8000, 64000, 80, 2); + + // + // Test Stereo-To-Stereo for all codecs. + // + audio_channels = 2; + codec_channels = 2; + + // All codecs are tested for all allowed sampling frequencies, rates and + // packet sizes. + channel_a2b_->set_codec_mode(kStereo); + test_cntr_++; + OpenOutFile(test_cntr_); + char codec_g722[] = "G722"; + RegisterSendCodec('A', codec_g722, 16000, 64000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_g722, 16000, 64000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_g722, 16000, 64000, 480, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_g722, 16000, 64000, 640, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_g722, 16000, 64000, 800, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_g722, 16000, 64000, 960, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + channel_a2b_->set_codec_mode(kStereo); + test_cntr_++; + OpenOutFile(test_cntr_); + char codec_l16[] = "L16"; + RegisterSendCodec('A', codec_l16, 8000, 128000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_l16, 8000, 128000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_l16, 8000, 128000, 240, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_l16, 8000, 128000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 16000, 256000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_l16, 16000, 256000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_l16, 16000, 256000, 480, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_l16, 16000, 256000, 640, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 32000, 512000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_l16, 32000, 512000, 640, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#ifdef PCMA_AND_PCMU + channel_a2b_->set_codec_mode(kStereo); + audio_channels = 2; + codec_channels = 2; + test_cntr_++; + OpenOutFile(test_cntr_); + char codec_pcma[] = "PCMA"; + RegisterSendCodec('A', codec_pcma, 8000, 64000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 240, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 400, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 480, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + char codec_pcmu[] = "PCMU"; + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 240, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 400, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 480, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif +#ifdef WEBRTC_CODEC_OPUS + channel_a2b_->set_codec_mode(kStereo); + audio_channels = 2; + codec_channels = 2; + test_cntr_++; + OpenOutFile(test_cntr_); + + char codec_opus[] = "opus"; + // Run Opus with 10 ms frame size. + RegisterSendCodec('A', codec_opus, 48000, 64000, 480, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + // Run Opus with 20 ms frame size. + RegisterSendCodec('A', codec_opus, 48000, 64000, 480 * 2, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + // Run Opus with 40 ms frame size. + RegisterSendCodec('A', codec_opus, 48000, 64000, 480 * 4, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + // Run Opus with 60 ms frame size. + RegisterSendCodec('A', codec_opus, 48000, 64000, 480 * 6, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + // Run Opus with 20 ms frame size and different bitrates. + RegisterSendCodec('A', codec_opus, 48000, 40000, 960, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_opus, 48000, 510000, 960, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif + // + // Test Mono-To-Stereo for all codecs. + // + audio_channels = 1; + codec_channels = 2; + + test_cntr_++; + channel_a2b_->set_codec_mode(kStereo); + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_g722, 16000, 64000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + channel_a2b_->set_codec_mode(kStereo); + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 8000, 128000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 16000, 256000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 32000, 512000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#ifdef PCMA_AND_PCMU + test_cntr_++; + channel_a2b_->set_codec_mode(kStereo); + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif +#ifdef WEBRTC_CODEC_OPUS + // Keep encode and decode in stereo. + test_cntr_++; + channel_a2b_->set_codec_mode(kStereo); + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_opus, 48000, 64000, 960, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + + // Encode in mono, decode in stereo mode. + RegisterSendCodec('A', codec_opus, 48000, 64000, 960, 1); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif + + // + // Test Stereo-To-Mono for all codecs. + // + audio_channels = 2; + codec_channels = 1; + channel_a2b_->set_codec_mode(kMono); + + // Run stereo audio and mono codec. + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_g722, 16000, 64000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 8000, 128000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 16000, 256000, 160, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_l16, 32000, 512000, 320, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#ifdef PCMA_AND_PCMU + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_pcmu, 8000, 64000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_pcma, 8000, 64000, 80, codec_channels); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif +#ifdef WEBRTC_CODEC_OPUS + test_cntr_++; + OpenOutFile(test_cntr_); + // Encode and decode in mono. + RegisterSendCodec('A', codec_opus, 48000, 32000, 960, codec_channels); + acm_b_->SetReceiveCodecs({{120, {"OPUS", 48000, 2}}}); + Run(channel_a2b_, audio_channels, codec_channels); + + // Encode in stereo, decode in mono. + RegisterSendCodec('A', codec_opus, 48000, 32000, 960, 2); + Run(channel_a2b_, audio_channels, codec_channels); + + out_file_.Close(); + + // Test switching between decoding mono and stereo for Opus. + + // Decode in mono. + test_cntr_++; + OpenOutFile(test_cntr_); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); + // Decode in stereo. + test_cntr_++; + OpenOutFile(test_cntr_); + acm_b_->SetReceiveCodecs({{120, {"OPUS", 48000, 2, {{"stereo", "1"}}}}}); + Run(channel_a2b_, audio_channels, 2); + out_file_.Close(); + // Decode in mono. + test_cntr_++; + OpenOutFile(test_cntr_); + acm_b_->SetReceiveCodecs({{120, {"OPUS", 48000, 2}}}); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif + + // Delete the file pointers. + delete in_file_stereo_; + delete in_file_mono_; +} + +// Register Codec to use in the test +// +// Input: side - which ACM to use, 'A' or 'B' +// codec_name - name to use when register the codec +// sampling_freq_hz - sampling frequency in Herz +// rate - bitrate in bytes +// pack_size - packet size in samples +// channels - number of channels; 1 for mono, 2 for stereo +void TestStereo::RegisterSendCodec(char side, + char* codec_name, + int32_t sampling_freq_hz, + int rate, + int pack_size, + int channels) { + // Store packet size in samples, used to validate the received packet + pack_size_samp_ = pack_size; + + // Store the expected packet size in bytes, used to validate the received + // packet. Add 0.875 to always round up to a whole byte. + pack_size_bytes_ = (uint16_t)(static_cast<float>(pack_size * rate) / + static_cast<float>(sampling_freq_hz * 8) + + 0.875); + + // Set pointer to the ACM where to register the codec + AudioCodingModule* my_acm = NULL; + switch (side) { + case 'A': { + my_acm = acm_a_.get(); + break; + } + case 'B': { + my_acm = acm_b_.get(); + break; + } + default: + break; + } + ASSERT_TRUE(my_acm != NULL); + + auto encoder_factory = CreateBuiltinAudioEncoderFactory(); + const int clockrate_hz = absl::EqualsIgnoreCase(codec_name, "g722") + ? sampling_freq_hz / 2 + : sampling_freq_hz; + const std::string ptime = rtc::ToString(rtc::CheckedDivExact( + pack_size, rtc::CheckedDivExact(sampling_freq_hz, 1000))); + SdpAudioFormat::Parameters params = {{"ptime", ptime}}; + RTC_CHECK(channels == 1 || channels == 2); + if (absl::EqualsIgnoreCase(codec_name, "opus")) { + if (channels == 2) { + params["stereo"] = "1"; + } + channels = 2; + params["maxaveragebitrate"] = rtc::ToString(rate); + } + constexpr int payload_type = 17; + auto encoder = encoder_factory->MakeAudioEncoder( + payload_type, SdpAudioFormat(codec_name, clockrate_hz, channels, params), + absl::nullopt); + EXPECT_NE(nullptr, encoder); + my_acm->SetEncoder(std::move(encoder)); + + send_codec_name_ = codec_name; +} + +void TestStereo::Run(TestPackStereo* channel, + int in_channels, + int out_channels, + int percent_loss) { + AudioFrame audio_frame; + + int32_t out_freq_hz_b = out_file_.SamplingFrequency(); + uint16_t rec_size; + uint32_t time_stamp_diff; + channel->reset_payload_size(); + int error_count = 0; + int variable_bytes = 0; + int variable_packets = 0; + // Set test length to 500 ms (50 blocks of 10 ms each). + in_file_mono_->SetNum10MsBlocksToRead(50); + in_file_stereo_->SetNum10MsBlocksToRead(50); + // Fast-forward 1 second (100 blocks) since the files start with silence. + in_file_stereo_->FastForward(100); + in_file_mono_->FastForward(100); + + while (true) { + // Simulate packet loss by setting `packet_loss_` to "true" in + // `percent_loss` percent of the loops. + if (percent_loss > 0) { + if (counter_ == floor((100 / percent_loss) + 0.5)) { + counter_ = 0; + channel->set_lost_packet(true); + } else { + channel->set_lost_packet(false); + } + counter_++; + } + + // Add 10 msec to ACM + if (in_channels == 1) { + if (in_file_mono_->EndOfFile()) { + break; + } + in_file_mono_->Read10MsData(audio_frame); + } else { + if (in_file_stereo_->EndOfFile()) { + break; + } + in_file_stereo_->Read10MsData(audio_frame); + } + EXPECT_GE(acm_a_->Add10MsData(audio_frame), 0); + + // Verify that the received packet size matches the settings. + rec_size = channel->payload_size(); + if ((0 < rec_size) & (rec_size < 65535)) { + if (strcmp(send_codec_name_, "opus") == 0) { + // Opus is a variable rate codec, hence calculate the average packet + // size, and later make sure the average is in the right range. + variable_bytes += rec_size; + variable_packets++; + } else { + // For fixed rate codecs, check that packet size is correct. + if ((rec_size != pack_size_bytes_ * out_channels) && + (pack_size_bytes_ < 65535)) { + error_count++; + } + } + // Verify that the timestamp is updated with expected length + time_stamp_diff = channel->timestamp_diff(); + if ((counter_ > 10) && (time_stamp_diff != pack_size_samp_)) { + error_count++; + } + } + + // Run receive side of ACM + bool muted; + EXPECT_EQ(0, acm_b_->PlayoutData10Ms(out_freq_hz_b, &audio_frame, &muted)); + ASSERT_FALSE(muted); + + // Write output speech to file + out_file_.Write10MsData( + audio_frame.data(), + audio_frame.samples_per_channel_ * audio_frame.num_channels_); + } + + EXPECT_EQ(0, error_count); + + // Check that packet size is in the right range for variable rate codecs, + // such as Opus. + if (variable_packets > 0) { + variable_bytes /= variable_packets; + EXPECT_NEAR(variable_bytes, pack_size_bytes_, 18); + } + + if (in_file_mono_->EndOfFile()) { + in_file_mono_->Rewind(); + } + if (in_file_stereo_->EndOfFile()) { + in_file_stereo_->Rewind(); + } + // Reset in case we ended with a lost packet + channel->set_lost_packet(false); +} + +void TestStereo::OpenOutFile(int16_t test_number) { + std::string file_name; + rtc::StringBuilder file_stream; + file_stream << webrtc::test::OutputPath() << "teststereo_out_" << test_number + << ".pcm"; + file_name = file_stream.str(); + out_file_.Open(file_name, 32000, "wb"); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestStereo.h b/third_party/libwebrtc/modules/audio_coding/test/TestStereo.h new file mode 100644 index 0000000000..4c50a4b555 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestStereo.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_TESTSTEREO_H_ +#define MODULES_AUDIO_CODING_TEST_TESTSTEREO_H_ + +#include <math.h> + +#include <memory> + +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_coding/test/PCMFile.h" + +#define PCMA_AND_PCMU + +namespace webrtc { + +enum StereoMonoMode { kNotSet, kMono, kStereo }; + +class TestPackStereo : public AudioPacketizationCallback { + public: + TestPackStereo(); + ~TestPackStereo(); + + void RegisterReceiverACM(AudioCodingModule* acm); + + int32_t SendData(AudioFrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + size_t payload_size, + int64_t absolute_capture_timestamp_ms) override; + + uint16_t payload_size(); + uint32_t timestamp_diff(); + void reset_payload_size(); + void set_codec_mode(StereoMonoMode mode); + void set_lost_packet(bool lost); + + private: + AudioCodingModule* receiver_acm_; + int16_t seq_no_; + uint32_t timestamp_diff_; + uint32_t last_in_timestamp_; + uint64_t total_bytes_; + int payload_size_; + StereoMonoMode codec_mode_; + // Simulate packet losses + bool lost_packet_; +}; + +class TestStereo { + public: + TestStereo(); + ~TestStereo(); + + void Perform(); + + private: + // The default value of '-1' indicates that the registration is based only on + // codec name and a sampling frequncy matching is not required. This is useful + // for codecs which support several sampling frequency. + void RegisterSendCodec(char side, + char* codec_name, + int32_t samp_freq_hz, + int rate, + int pack_size, + int channels); + + void Run(TestPackStereo* channel, + int in_channels, + int out_channels, + int percent_loss = 0); + void OpenOutFile(int16_t test_number); + + std::unique_ptr<AudioCodingModule> acm_a_; + std::unique_ptr<AudioCodingModule> acm_b_; + + TestPackStereo* channel_a2b_; + + PCMFile* in_file_stereo_; + PCMFile* in_file_mono_; + PCMFile out_file_; + int16_t test_cntr_; + uint16_t pack_size_samp_; + uint16_t pack_size_bytes_; + int counter_; + char* send_codec_name_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_TESTSTEREO_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc b/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc new file mode 100644 index 0000000000..cb05deb92a --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/TestVADDTX.h" + +#include <string> + +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "api/audio_codecs/audio_decoder_factory_template.h" +#include "api/audio_codecs/audio_encoder_factory_template.h" +#include "api/audio_codecs/ilbc/audio_decoder_ilbc.h" +#include "api/audio_codecs/ilbc/audio_encoder_ilbc.h" +#include "api/audio_codecs/isac/audio_decoder_isac_float.h" +#include "api/audio_codecs/isac/audio_encoder_isac_float.h" +#include "api/audio_codecs/opus/audio_decoder_opus.h" +#include "api/audio_codecs/opus/audio_encoder_opus.h" +#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h" +#include "modules/audio_coding/test/PCMFile.h" +#include "rtc_base/strings/string_builder.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +MonitoringAudioPacketizationCallback::MonitoringAudioPacketizationCallback( + AudioPacketizationCallback* next) + : next_(next) { + ResetStatistics(); +} + +int32_t MonitoringAudioPacketizationCallback::SendData( + AudioFrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + size_t payload_len_bytes, + int64_t absolute_capture_timestamp_ms) { + counter_[static_cast<int>(frame_type)]++; + return next_->SendData(frame_type, payload_type, timestamp, payload_data, + payload_len_bytes, absolute_capture_timestamp_ms); +} + +void MonitoringAudioPacketizationCallback::PrintStatistics() { + printf("\n"); + printf("kEmptyFrame %u\n", + counter_[static_cast<int>(AudioFrameType::kEmptyFrame)]); + printf("kAudioFrameSpeech %u\n", + counter_[static_cast<int>(AudioFrameType::kAudioFrameSpeech)]); + printf("kAudioFrameCN %u\n", + counter_[static_cast<int>(AudioFrameType::kAudioFrameCN)]); + printf("\n\n"); +} + +void MonitoringAudioPacketizationCallback::ResetStatistics() { + memset(counter_, 0, sizeof(counter_)); +} + +void MonitoringAudioPacketizationCallback::GetStatistics(uint32_t* counter) { + memcpy(counter, counter_, sizeof(counter_)); +} + +TestVadDtx::TestVadDtx() + : encoder_factory_(CreateAudioEncoderFactory<AudioEncoderIlbc, + AudioEncoderIsacFloat, + AudioEncoderOpus>()), + decoder_factory_(CreateAudioDecoderFactory<AudioDecoderIlbc, + AudioDecoderIsacFloat, + AudioDecoderOpus>()), + acm_send_(AudioCodingModule::Create( + AudioCodingModule::Config(decoder_factory_))), + acm_receive_(AudioCodingModule::Create( + AudioCodingModule::Config(decoder_factory_))), + channel_(std::make_unique<Channel>()), + packetization_callback_( + std::make_unique<MonitoringAudioPacketizationCallback>( + channel_.get())) { + EXPECT_EQ( + 0, acm_send_->RegisterTransportCallback(packetization_callback_.get())); + channel_->RegisterReceiverACM(acm_receive_.get()); +} + +bool TestVadDtx::RegisterCodec(const SdpAudioFormat& codec_format, + absl::optional<Vad::Aggressiveness> vad_mode) { + constexpr int payload_type = 17, cn_payload_type = 117; + bool added_comfort_noise = false; + + auto encoder = encoder_factory_->MakeAudioEncoder(payload_type, codec_format, + absl::nullopt); + if (vad_mode.has_value() && + !absl::EqualsIgnoreCase(codec_format.name, "opus")) { + AudioEncoderCngConfig config; + config.speech_encoder = std::move(encoder); + config.num_channels = 1; + config.payload_type = cn_payload_type; + config.vad_mode = vad_mode.value(); + encoder = CreateComfortNoiseEncoder(std::move(config)); + added_comfort_noise = true; + } + channel_->SetIsStereo(encoder->NumChannels() > 1); + acm_send_->SetEncoder(std::move(encoder)); + + std::map<int, SdpAudioFormat> receive_codecs = {{payload_type, codec_format}}; + acm_receive_->SetReceiveCodecs(receive_codecs); + + return added_comfort_noise; +} + +// Encoding a file and see if the numbers that various packets occur follow +// the expectation. +void TestVadDtx::Run(absl::string_view in_filename, + int frequency, + int channels, + absl::string_view out_filename, + bool append, + const int* expects) { + packetization_callback_->ResetStatistics(); + + PCMFile in_file; + in_file.Open(in_filename, frequency, "rb"); + in_file.ReadStereo(channels > 1); + // Set test length to 1000 ms (100 blocks of 10 ms each). + in_file.SetNum10MsBlocksToRead(100); + // Fast-forward both files 500 ms (50 blocks). The first second of the file is + // silence, but we want to keep half of that to test silence periods. + in_file.FastForward(50); + + PCMFile out_file; + if (append) { + out_file.Open(out_filename, kOutputFreqHz, "ab"); + } else { + out_file.Open(out_filename, kOutputFreqHz, "wb"); + } + + uint16_t frame_size_samples = in_file.PayloadLength10Ms(); + AudioFrame audio_frame; + while (!in_file.EndOfFile()) { + in_file.Read10MsData(audio_frame); + audio_frame.timestamp_ = time_stamp_; + time_stamp_ += frame_size_samples; + EXPECT_GE(acm_send_->Add10MsData(audio_frame), 0); + bool muted; + acm_receive_->PlayoutData10Ms(kOutputFreqHz, &audio_frame, &muted); + ASSERT_FALSE(muted); + out_file.Write10MsData(audio_frame); + } + + in_file.Close(); + out_file.Close(); + +#ifdef PRINT_STAT + packetization_callback_->PrintStatistics(); +#endif + + uint32_t stats[3]; + packetization_callback_->GetStatistics(stats); + packetization_callback_->ResetStatistics(); + + for (const auto& st : stats) { + int i = &st - stats; // Calculate the current position in stats. + switch (expects[i]) { + case 0: { + EXPECT_EQ(0u, st) << "stats[" << i << "] error."; + break; + } + case 1: { + EXPECT_GT(st, 0u) << "stats[" << i << "] error."; + break; + } + } + } +} + +// Following is the implementation of TestWebRtcVadDtx. +TestWebRtcVadDtx::TestWebRtcVadDtx() : output_file_num_(0) {} + +void TestWebRtcVadDtx::Perform() { + RunTestCases({"ISAC", 16000, 1}); + RunTestCases({"ISAC", 32000, 1}); + RunTestCases({"ILBC", 8000, 1}); + RunTestCases({"opus", 48000, 2}); +} + +// Test various configurations on VAD/DTX. +void TestWebRtcVadDtx::RunTestCases(const SdpAudioFormat& codec_format) { + Test(/*new_outfile=*/true, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, absl::nullopt)); + + Test(/*new_outfile=*/false, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadAggressive)); + + Test(/*new_outfile=*/false, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadLowBitrate)); + + Test(/*new_outfile=*/false, /*expect_dtx_enabled=*/RegisterCodec( + codec_format, Vad::kVadVeryAggressive)); + + Test(/*new_outfile=*/false, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadNormal)); +} + +// Set the expectation and run the test. +void TestWebRtcVadDtx::Test(bool new_outfile, bool expect_dtx_enabled) { + int expects[] = {-1, 1, expect_dtx_enabled, 0, 0}; + if (new_outfile) { + output_file_num_++; + } + rtc::StringBuilder out_filename; + out_filename << webrtc::test::OutputPath() << "testWebRtcVadDtx_outFile_" + << output_file_num_ << ".pcm"; + Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1, + out_filename.str(), !new_outfile, expects); +} + +// Following is the implementation of TestOpusDtx. +void TestOpusDtx::Perform() { + int expects[] = {0, 1, 0, 0, 0}; + + // Register Opus as send codec + std::string out_filename = + webrtc::test::OutputPath() + "testOpusDtx_outFile_mono.pcm"; + RegisterCodec({"opus", 48000, 2}, absl::nullopt); + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(false); + }); + + Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1, + out_filename, false, expects); + + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(true); + }); + expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1; + expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1; + Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1, + out_filename, true, expects); + + // Register stereo Opus as send codec + out_filename = webrtc::test::OutputPath() + "testOpusDtx_outFile_stereo.pcm"; + RegisterCodec({"opus", 48000, 2, {{"stereo", "1"}}}, absl::nullopt); + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(false); + }); + expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 0; + expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 0; + Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000, + 2, out_filename, false, expects); + + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(true); + // The default bitrate will not generate frames recognized as CN on desktop + // since the frames will be encoded as CELT. Set a low target bitrate to get + // consistent behaviour across platforms. + (*encoder_ptr)->OnReceivedTargetAudioBitrate(24000); + }); + + expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1; + expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1; + Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000, + 2, out_filename, true, expects); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.h b/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.h new file mode 100644 index 0000000000..d81ae28beb --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.h @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_TESTVADDTX_H_ +#define MODULES_AUDIO_CODING_TEST_TESTVADDTX_H_ + +#include <memory> + +#include "absl/strings/string_view.h" +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "common_audio/vad/include/vad.h" +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_coding/include/audio_coding_module_typedefs.h" +#include "modules/audio_coding/test/Channel.h" + +namespace webrtc { + +// This class records the frame type, and delegates actual sending to the +// `next_` AudioPacketizationCallback. +class MonitoringAudioPacketizationCallback : public AudioPacketizationCallback { + public: + explicit MonitoringAudioPacketizationCallback( + AudioPacketizationCallback* next); + + int32_t SendData(AudioFrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + size_t payload_len_bytes, + int64_t absolute_capture_timestamp_ms) override; + + void PrintStatistics(); + void ResetStatistics(); + void GetStatistics(uint32_t* stats); + + private: + // 0 - kEmptyFrame + // 1 - kAudioFrameSpeech + // 2 - kAudioFrameCN + uint32_t counter_[3]; + AudioPacketizationCallback* const next_; +}; + +// TestVadDtx is to verify that VAD/DTX perform as they should. It runs through +// an audio file and check if the occurrence of various packet types follows +// expectation. TestVadDtx needs its derived class to implement the Perform() +// to put the test together. +class TestVadDtx { + public: + static const int kOutputFreqHz = 16000; + + TestVadDtx(); + + protected: + // Returns true iff CN was added. + bool RegisterCodec(const SdpAudioFormat& codec_format, + absl::optional<Vad::Aggressiveness> vad_mode); + + // Encoding a file and see if the numbers that various packets occur follow + // the expectation. Saves result to a file. + // expects[x] means + // -1 : do not care, + // 0 : there have been no packets of type `x`, + // 1 : there have been packets of type `x`, + // with `x` indicates the following packet types + // 0 - kEmptyFrame + // 1 - kAudioFrameSpeech + // 2 - kAudioFrameCN + void Run(absl::string_view in_filename, + int frequency, + int channels, + absl::string_view out_filename, + bool append, + const int* expects); + + const rtc::scoped_refptr<AudioEncoderFactory> encoder_factory_; + const rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_; + std::unique_ptr<AudioCodingModule> acm_send_; + std::unique_ptr<AudioCodingModule> acm_receive_; + std::unique_ptr<Channel> channel_; + std::unique_ptr<MonitoringAudioPacketizationCallback> packetization_callback_; + uint32_t time_stamp_ = 0x12345678; +}; + +// TestWebRtcVadDtx is to verify that the WebRTC VAD/DTX perform as they should. +class TestWebRtcVadDtx final : public TestVadDtx { + public: + TestWebRtcVadDtx(); + + void Perform(); + + private: + void RunTestCases(const SdpAudioFormat& codec_format); + void Test(bool new_outfile, bool expect_dtx_enabled); + + int output_file_num_; +}; + +// TestOpusDtx is to verify that the Opus DTX performs as it should. +class TestOpusDtx final : public TestVadDtx { + public: + void Perform(); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_TESTVADDTX_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/Tester.cc b/third_party/libwebrtc/modules/audio_coding/test/Tester.cc new file mode 100644 index 0000000000..113dbe059e --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/Tester.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2012 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. + */ + +#include <stdio.h> + +#include <string> +#include <vector> + +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_coding/test/EncodeDecodeTest.h" +#include "modules/audio_coding/test/PacketLossTest.h" +#include "modules/audio_coding/test/TestAllCodecs.h" +#include "modules/audio_coding/test/TestRedFec.h" +#include "modules/audio_coding/test/TestStereo.h" +#include "modules/audio_coding/test/TestVADDTX.h" +#include "modules/audio_coding/test/TwoWayCommunication.h" +#include "modules/audio_coding/test/iSACTest.h" +#include "modules/audio_coding/test/opus_test.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +TEST(AudioCodingModuleTest, TestAllCodecs) { + webrtc::TestAllCodecs().Perform(); +} + +#if defined(WEBRTC_ANDROID) +TEST(AudioCodingModuleTest, DISABLED_TestEncodeDecode) { +#else +TEST(AudioCodingModuleTest, TestEncodeDecode) { +#endif + webrtc::EncodeDecodeTest().Perform(); +} + +TEST(AudioCodingModuleTest, TestRedFec) { + webrtc::TestRedFec().Perform(); +} + +#if defined(WEBRTC_ANDROID) +TEST(AudioCodingModuleTest, DISABLED_TestIsac) { +#else +TEST(AudioCodingModuleTest, TestIsac) { +#endif + webrtc::ISACTest().Perform(); +} + +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) && \ + defined(WEBRTC_CODEC_ILBC) +#if defined(WEBRTC_ANDROID) +TEST(AudioCodingModuleTest, DISABLED_TwoWayCommunication) { +#else +TEST(AudioCodingModuleTest, TwoWayCommunication) { +#endif + webrtc::TwoWayCommunication().Perform(); +} +#endif + +// Disabled on ios as flaky, see https://crbug.com/webrtc/7057 +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) +TEST(AudioCodingModuleTest, DISABLED_TestStereo) { +#else +TEST(AudioCodingModuleTest, TestStereo) { +#endif + webrtc::TestStereo().Perform(); +} + +TEST(AudioCodingModuleTest, TestWebRtcVadDtx) { + webrtc::TestWebRtcVadDtx().Perform(); +} + +TEST(AudioCodingModuleTest, TestOpusDtx) { + webrtc::TestOpusDtx().Perform(); +} + +// Disabled on ios as flaky, see https://crbug.com/webrtc/7057 +#if defined(WEBRTC_IOS) +TEST(AudioCodingModuleTest, DISABLED_TestOpus) { +#else +TEST(AudioCodingModuleTest, TestOpus) { +#endif + webrtc::OpusTest().Perform(); +} + +TEST(AudioCodingModuleTest, TestPacketLoss) { + webrtc::PacketLossTest(1, 10, 10, 1).Perform(); +} + +TEST(AudioCodingModuleTest, TestPacketLossBurst) { + webrtc::PacketLossTest(1, 10, 10, 2).Perform(); +} + +// Disabled on ios as flake, see https://crbug.com/webrtc/7057 +#if defined(WEBRTC_IOS) +TEST(AudioCodingModuleTest, DISABLED_TestPacketLossStereo) { +#else +TEST(AudioCodingModuleTest, TestPacketLossStereo) { +#endif + webrtc::PacketLossTest(2, 10, 10, 1).Perform(); +} + +// Disabled on ios as flake, see https://crbug.com/webrtc/7057 +#if defined(WEBRTC_IOS) +TEST(AudioCodingModuleTest, DISABLED_TestPacketLossStereoBurst) { +#else +TEST(AudioCodingModuleTest, TestPacketLossStereoBurst) { +#endif + webrtc::PacketLossTest(2, 10, 10, 2).Perform(); +} + +// The full API test is too long to run automatically on bots, but can be used +// for offline testing. User interaction is needed. +#ifdef ACM_TEST_FULL_API +TEST(AudioCodingModuleTest, TestAPI) { + webrtc::APITest().Perform(); +} +#endif diff --git a/third_party/libwebrtc/modules/audio_coding/test/TwoWayCommunication.cc b/third_party/libwebrtc/modules/audio_coding/test/TwoWayCommunication.cc new file mode 100644 index 0000000000..b42415a21a --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TwoWayCommunication.cc @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "TwoWayCommunication.h" + +#include <stdio.h> +#include <string.h> + +#include <memory> + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "modules/audio_coding/test/PCMFile.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +#define MAX_FILE_NAME_LENGTH_BYTE 500 + +TwoWayCommunication::TwoWayCommunication() + : _acmA(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))), + _acmRefA(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))) { + AudioCodingModule::Config config; + // The clicks will be more obvious if time-stretching is not allowed. + // TODO(henrik.lundin) Really? + config.neteq_config.for_test_no_time_stretching = true; + config.decoder_factory = CreateBuiltinAudioDecoderFactory(); + _acmB.reset(AudioCodingModule::Create(config)); + _acmRefB.reset(AudioCodingModule::Create(config)); +} + +TwoWayCommunication::~TwoWayCommunication() { + delete _channel_A2B; + delete _channel_B2A; + delete _channelRef_A2B; + delete _channelRef_B2A; + _inFileA.Close(); + _inFileB.Close(); + _outFileA.Close(); + _outFileB.Close(); + _outFileRefA.Close(); + _outFileRefB.Close(); +} + +void TwoWayCommunication::SetUpAutotest( + AudioEncoderFactory* const encoder_factory, + const SdpAudioFormat& format1, + const int payload_type1, + const SdpAudioFormat& format2, + const int payload_type2) { + //--- Set A codecs + _acmA->SetEncoder( + encoder_factory->MakeAudioEncoder(payload_type1, format1, absl::nullopt)); + _acmA->SetReceiveCodecs({{payload_type2, format2}}); + + //--- Set ref-A codecs + _acmRefA->SetEncoder( + encoder_factory->MakeAudioEncoder(payload_type1, format1, absl::nullopt)); + _acmRefA->SetReceiveCodecs({{payload_type2, format2}}); + + //--- Set B codecs + _acmB->SetEncoder( + encoder_factory->MakeAudioEncoder(payload_type2, format2, absl::nullopt)); + _acmB->SetReceiveCodecs({{payload_type1, format1}}); + + //--- Set ref-B codecs + _acmRefB->SetEncoder( + encoder_factory->MakeAudioEncoder(payload_type2, format2, absl::nullopt)); + _acmRefB->SetReceiveCodecs({{payload_type1, format1}}); + + uint16_t frequencyHz; + + //--- Input A and B + std::string in_file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + frequencyHz = 16000; + _inFileA.Open(in_file_name, frequencyHz, "rb"); + _inFileB.Open(in_file_name, frequencyHz, "rb"); + + //--- Output A + std::string output_file_a = webrtc::test::OutputPath() + "outAutotestA.pcm"; + frequencyHz = 16000; + _outFileA.Open(output_file_a, frequencyHz, "wb"); + std::string output_ref_file_a = + webrtc::test::OutputPath() + "ref_outAutotestA.pcm"; + _outFileRefA.Open(output_ref_file_a, frequencyHz, "wb"); + + //--- Output B + std::string output_file_b = webrtc::test::OutputPath() + "outAutotestB.pcm"; + frequencyHz = 16000; + _outFileB.Open(output_file_b, frequencyHz, "wb"); + std::string output_ref_file_b = + webrtc::test::OutputPath() + "ref_outAutotestB.pcm"; + _outFileRefB.Open(output_ref_file_b, frequencyHz, "wb"); + + //--- Set A-to-B channel + _channel_A2B = new Channel; + _acmA->RegisterTransportCallback(_channel_A2B); + _channel_A2B->RegisterReceiverACM(_acmB.get()); + //--- Do the same for the reference + _channelRef_A2B = new Channel; + _acmRefA->RegisterTransportCallback(_channelRef_A2B); + _channelRef_A2B->RegisterReceiverACM(_acmRefB.get()); + + //--- Set B-to-A channel + _channel_B2A = new Channel; + _acmB->RegisterTransportCallback(_channel_B2A); + _channel_B2A->RegisterReceiverACM(_acmA.get()); + //--- Do the same for reference + _channelRef_B2A = new Channel; + _acmRefB->RegisterTransportCallback(_channelRef_B2A); + _channelRef_B2A->RegisterReceiverACM(_acmRefA.get()); +} + +void TwoWayCommunication::Perform() { + const SdpAudioFormat format1("ISAC", 16000, 1); + const SdpAudioFormat format2("L16", 8000, 1); + constexpr int payload_type1 = 17, payload_type2 = 18; + + auto encoder_factory = CreateBuiltinAudioEncoderFactory(); + + SetUpAutotest(encoder_factory.get(), format1, payload_type1, format2, + payload_type2); + + unsigned int msecPassed = 0; + unsigned int secPassed = 0; + + int32_t outFreqHzA = _outFileA.SamplingFrequency(); + int32_t outFreqHzB = _outFileB.SamplingFrequency(); + + AudioFrame audioFrame; + + // In the following loop we tests that the code can handle misuse of the APIs. + // In the middle of a session with data flowing between two sides, called A + // and B, APIs will be called, and the code should continue to run, and be + // able to recover. + while (!_inFileA.EndOfFile() && !_inFileB.EndOfFile()) { + msecPassed += 10; + EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0); + EXPECT_GE(_acmA->Add10MsData(audioFrame), 0); + EXPECT_GE(_acmRefA->Add10MsData(audioFrame), 0); + + EXPECT_GT(_inFileB.Read10MsData(audioFrame), 0); + + EXPECT_GE(_acmB->Add10MsData(audioFrame), 0); + EXPECT_GE(_acmRefB->Add10MsData(audioFrame), 0); + bool muted; + EXPECT_EQ(0, _acmA->PlayoutData10Ms(outFreqHzA, &audioFrame, &muted)); + ASSERT_FALSE(muted); + _outFileA.Write10MsData(audioFrame); + EXPECT_EQ(0, _acmRefA->PlayoutData10Ms(outFreqHzA, &audioFrame, &muted)); + ASSERT_FALSE(muted); + _outFileRefA.Write10MsData(audioFrame); + EXPECT_EQ(0, _acmB->PlayoutData10Ms(outFreqHzB, &audioFrame, &muted)); + ASSERT_FALSE(muted); + _outFileB.Write10MsData(audioFrame); + EXPECT_EQ(0, _acmRefB->PlayoutData10Ms(outFreqHzB, &audioFrame, &muted)); + ASSERT_FALSE(muted); + _outFileRefB.Write10MsData(audioFrame); + + // Update time counters each time a second of data has passed. + if (msecPassed >= 1000) { + msecPassed = 0; + secPassed++; + } + // Re-register send codec on side B. + if (((secPassed % 5) == 4) && (msecPassed >= 990)) { + _acmB->SetEncoder(encoder_factory->MakeAudioEncoder( + payload_type2, format2, absl::nullopt)); + } + // Initialize receiver on side A. + if (((secPassed % 7) == 6) && (msecPassed == 0)) + EXPECT_EQ(0, _acmA->InitializeReceiver()); + // Re-register codec on side A. + if (((secPassed % 7) == 6) && (msecPassed >= 990)) { + _acmA->SetReceiveCodecs({{payload_type2, format2}}); + } + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/TwoWayCommunication.h b/third_party/libwebrtc/modules/audio_coding/test/TwoWayCommunication.h new file mode 100644 index 0000000000..b7eb9e5583 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TwoWayCommunication.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_TWOWAYCOMMUNICATION_H_ +#define MODULES_AUDIO_CODING_TEST_TWOWAYCOMMUNICATION_H_ + +#include <memory> + +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/audio_codecs/audio_format.h" +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_coding/test/Channel.h" +#include "modules/audio_coding/test/PCMFile.h" + +namespace webrtc { + +class TwoWayCommunication { + public: + TwoWayCommunication(); + ~TwoWayCommunication(); + + void Perform(); + + private: + void SetUpAutotest(AudioEncoderFactory* const encoder_factory, + const SdpAudioFormat& format1, + int payload_type1, + const SdpAudioFormat& format2, + int payload_type2); + + std::unique_ptr<AudioCodingModule> _acmA; + std::unique_ptr<AudioCodingModule> _acmB; + + std::unique_ptr<AudioCodingModule> _acmRefA; + std::unique_ptr<AudioCodingModule> _acmRefB; + + Channel* _channel_A2B; + Channel* _channel_B2A; + + Channel* _channelRef_A2B; + Channel* _channelRef_B2A; + + PCMFile _inFileA; + PCMFile _inFileB; + + PCMFile _outFileA; + PCMFile _outFileB; + + PCMFile _outFileRefA; + PCMFile _outFileRefB; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_TWOWAYCOMMUNICATION_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/iSACTest.cc b/third_party/libwebrtc/modules/audio_coding/test/iSACTest.cc new file mode 100644 index 0000000000..246c485afe --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/iSACTest.cc @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "modules/audio_coding/test/iSACTest.h" + +#include <stdio.h> +#include <string.h> + +#include "absl/strings/match.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/isac/audio_encoder_isac_float.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/time_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +using ::testing::AnyOf; +using ::testing::Eq; +using ::testing::StrCaseEq; + +namespace { + +constexpr int kISAC16kPayloadType = 103; +constexpr int kISAC32kPayloadType = 104; +const SdpAudioFormat kISAC16kFormat = {"ISAC", 16000, 1}; +const SdpAudioFormat kISAC32kFormat = {"ISAC", 32000, 1}; + +AudioEncoderIsacFloat::Config TweakConfig( + AudioEncoderIsacFloat::Config config, + const ACMTestISACConfig& test_config) { + if (test_config.currentRateBitPerSec > 0) { + config.bit_rate = test_config.currentRateBitPerSec; + } + if (test_config.currentFrameSizeMsec != 0) { + config.frame_size_ms = test_config.currentFrameSizeMsec; + } + EXPECT_THAT(config.IsOk(), Eq(true)); + return config; +} + +void SetISACConfigDefault(ACMTestISACConfig& isacConfig) { + isacConfig.currentRateBitPerSec = 0; + isacConfig.currentFrameSizeMsec = 0; + isacConfig.encodingMode = -1; + isacConfig.initRateBitPerSec = 0; + isacConfig.initFrameSizeInMsec = 0; + isacConfig.enforceFrameSize = false; +} + +} // namespace + +ISACTest::ISACTest() + : _acmA(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))), + _acmB(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))) {} + +ISACTest::~ISACTest() {} + +void ISACTest::Setup() { + // Register both iSAC-wb & iSAC-swb in both sides as receiver codecs. + std::map<int, SdpAudioFormat> receive_codecs = { + {kISAC16kPayloadType, kISAC16kFormat}, + {kISAC32kPayloadType, kISAC32kFormat}}; + _acmA->SetReceiveCodecs(receive_codecs); + _acmB->SetReceiveCodecs(receive_codecs); + + //--- Set A-to-B channel + _channel_A2B.reset(new Channel); + EXPECT_EQ(0, _acmA->RegisterTransportCallback(_channel_A2B.get())); + _channel_A2B->RegisterReceiverACM(_acmB.get()); + + //--- Set B-to-A channel + _channel_B2A.reset(new Channel); + EXPECT_EQ(0, _acmB->RegisterTransportCallback(_channel_B2A.get())); + _channel_B2A->RegisterReceiverACM(_acmA.get()); + + file_name_swb_ = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + + _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat), + kISAC16kPayloadType)); + _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat), + kISAC32kPayloadType)); + + _inFileA.Open(file_name_swb_, 32000, "rb"); + // Set test length to 500 ms (50 blocks of 10 ms each). + _inFileA.SetNum10MsBlocksToRead(50); + // Fast-forward 1 second (100 blocks) since the files start with silence. + _inFileA.FastForward(100); + std::string fileNameA = webrtc::test::OutputPath() + "testisac_a.pcm"; + std::string fileNameB = webrtc::test::OutputPath() + "testisac_b.pcm"; + _outFileA.Open(fileNameA, 32000, "wb"); + _outFileB.Open(fileNameB, 32000, "wb"); + + while (!_inFileA.EndOfFile()) { + Run10ms(); + } + + _inFileA.Close(); + _outFileA.Close(); + _outFileB.Close(); +} + +void ISACTest::Perform() { + Setup(); + + int16_t testNr = 0; + ACMTestISACConfig wbISACConfig; + ACMTestISACConfig swbISACConfig; + + SetISACConfigDefault(wbISACConfig); + SetISACConfigDefault(swbISACConfig); + + wbISACConfig.currentRateBitPerSec = -1; + swbISACConfig.currentRateBitPerSec = -1; + testNr++; + EncodeDecode(testNr, wbISACConfig, swbISACConfig); + + SetISACConfigDefault(wbISACConfig); + SetISACConfigDefault(swbISACConfig); + testNr++; + EncodeDecode(testNr, wbISACConfig, swbISACConfig); + + testNr++; + SwitchingSamplingRate(testNr, 4); +} + +void ISACTest::Run10ms() { + AudioFrame audioFrame; + EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0); + EXPECT_GE(_acmA->Add10MsData(audioFrame), 0); + EXPECT_GE(_acmB->Add10MsData(audioFrame), 0); + bool muted; + EXPECT_EQ(0, _acmA->PlayoutData10Ms(32000, &audioFrame, &muted)); + ASSERT_FALSE(muted); + _outFileA.Write10MsData(audioFrame); + EXPECT_EQ(0, _acmB->PlayoutData10Ms(32000, &audioFrame, &muted)); + ASSERT_FALSE(muted); + _outFileB.Write10MsData(audioFrame); +} + +void ISACTest::EncodeDecode(int testNr, + ACMTestISACConfig& wbISACConfig, + ACMTestISACConfig& swbISACConfig) { + // Files in Side A and B + _inFileA.Open(file_name_swb_, 32000, "rb", true); + _inFileB.Open(file_name_swb_, 32000, "rb", true); + + std::string file_name_out; + rtc::StringBuilder file_stream_a; + rtc::StringBuilder file_stream_b; + file_stream_a << webrtc::test::OutputPath(); + file_stream_b << webrtc::test::OutputPath(); + file_stream_a << "out_iSACTest_A_" << testNr << ".pcm"; + file_stream_b << "out_iSACTest_B_" << testNr << ".pcm"; + file_name_out = file_stream_a.str(); + _outFileA.Open(file_name_out, 32000, "wb"); + file_name_out = file_stream_b.str(); + _outFileB.Open(file_name_out, 32000, "wb"); + + // Side A is sending super-wideband, and side B is sending wideband. + _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + TweakConfig(*AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat), + swbISACConfig), + kISAC32kPayloadType)); + _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + TweakConfig(*AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat), + wbISACConfig), + kISAC16kPayloadType)); + + _channel_A2B->ResetStats(); + _channel_B2A->ResetStats(); + + while (!(_inFileA.EndOfFile() || _inFileA.Rewinded())) { + Run10ms(); + } + + _channel_A2B->ResetStats(); + _channel_B2A->ResetStats(); + + _outFileA.Close(); + _outFileB.Close(); + _inFileA.Close(); + _inFileB.Close(); +} + +void ISACTest::SwitchingSamplingRate(int testNr, int maxSampRateChange) { + // Files in Side A + _inFileA.Open(file_name_swb_, 32000, "rb"); + _inFileB.Open(file_name_swb_, 32000, "rb"); + + std::string file_name_out; + rtc::StringBuilder file_stream_a; + rtc::StringBuilder file_stream_b; + file_stream_a << webrtc::test::OutputPath(); + file_stream_b << webrtc::test::OutputPath(); + file_stream_a << "out_iSACTest_A_" << testNr << ".pcm"; + file_stream_b << "out_iSACTest_B_" << testNr << ".pcm"; + file_name_out = file_stream_a.str(); + _outFileA.Open(file_name_out, 32000, "wb"); + file_name_out = file_stream_b.str(); + _outFileB.Open(file_name_out, 32000, "wb"); + + // Start with side A sending super-wideband and side B seding wideband. + // Toggle sending wideband/super-wideband in this test. + _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat), + kISAC32kPayloadType)); + _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat), + kISAC16kPayloadType)); + + int numSendCodecChanged = 0; + while (numSendCodecChanged < (maxSampRateChange << 1)) { + Run10ms(); + if (_inFileA.EndOfFile()) { + if (_inFileA.SamplingFrequency() == 16000) { + // Switch side A to send super-wideband. + _inFileA.Close(); + _inFileA.Open(file_name_swb_, 32000, "rb"); + _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat), + kISAC32kPayloadType)); + } else { + // Switch side A to send wideband. + _inFileA.Close(); + _inFileA.Open(file_name_swb_, 32000, "rb"); + _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat), + kISAC16kPayloadType)); + } + numSendCodecChanged++; + } + + if (_inFileB.EndOfFile()) { + if (_inFileB.SamplingFrequency() == 16000) { + // Switch side B to send super-wideband. + _inFileB.Close(); + _inFileB.Open(file_name_swb_, 32000, "rb"); + _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat), + kISAC32kPayloadType)); + } else { + // Switch side B to send wideband. + _inFileB.Close(); + _inFileB.Open(file_name_swb_, 32000, "rb"); + _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder( + *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat), + kISAC16kPayloadType)); + } + numSendCodecChanged++; + } + } + _outFileA.Close(); + _outFileB.Close(); + _inFileA.Close(); + _inFileB.Close(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/iSACTest.h b/third_party/libwebrtc/modules/audio_coding/test/iSACTest.h new file mode 100644 index 0000000000..f6efeeac1c --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/iSACTest.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_ISACTEST_H_ +#define MODULES_AUDIO_CODING_TEST_ISACTEST_H_ + +#include <string.h> + +#include <memory> + +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_coding/test/Channel.h" +#include "modules/audio_coding/test/PCMFile.h" + +namespace webrtc { + +struct ACMTestISACConfig { + int32_t currentRateBitPerSec; + int16_t currentFrameSizeMsec; + int16_t encodingMode; + uint32_t initRateBitPerSec; + int16_t initFrameSizeInMsec; + bool enforceFrameSize; +}; + +class ISACTest { + public: + ISACTest(); + ~ISACTest(); + + void Perform(); + + private: + void Setup(); + + void Run10ms(); + + void EncodeDecode(int testNr, + ACMTestISACConfig& wbISACConfig, + ACMTestISACConfig& swbISACConfig); + + void SwitchingSamplingRate(int testNr, int maxSampRateChange); + + std::unique_ptr<AudioCodingModule> _acmA; + std::unique_ptr<AudioCodingModule> _acmB; + + std::unique_ptr<Channel> _channel_A2B; + std::unique_ptr<Channel> _channel_B2A; + + PCMFile _inFileA; + PCMFile _inFileB; + + PCMFile _outFileA; + PCMFile _outFileB; + + std::string file_name_swb_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_ISACTEST_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/opus_test.cc b/third_party/libwebrtc/modules/audio_coding/test/opus_test.cc new file mode 100644 index 0000000000..6822bc3d72 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/opus_test.cc @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "modules/audio_coding/test/opus_test.h" + +#include <string> + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "modules/audio_coding/codecs/opus/opus_interface.h" +#include "modules/audio_coding/include/audio_coding_module_typedefs.h" +#include "modules/audio_coding/test/TestStereo.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +OpusTest::OpusTest() + : acm_receiver_(AudioCodingModule::Create( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))), + channel_a2b_(NULL), + counter_(0), + payload_type_(255), + rtp_timestamp_(0) {} + +OpusTest::~OpusTest() { + if (channel_a2b_ != NULL) { + delete channel_a2b_; + channel_a2b_ = NULL; + } + if (opus_mono_encoder_ != NULL) { + WebRtcOpus_EncoderFree(opus_mono_encoder_); + opus_mono_encoder_ = NULL; + } + if (opus_stereo_encoder_ != NULL) { + WebRtcOpus_EncoderFree(opus_stereo_encoder_); + opus_stereo_encoder_ = NULL; + } + if (opus_mono_decoder_ != NULL) { + WebRtcOpus_DecoderFree(opus_mono_decoder_); + opus_mono_decoder_ = NULL; + } + if (opus_stereo_decoder_ != NULL) { + WebRtcOpus_DecoderFree(opus_stereo_decoder_); + opus_stereo_decoder_ = NULL; + } +} + +void OpusTest::Perform() { +#ifndef WEBRTC_CODEC_OPUS + // Opus isn't defined, exit. + return; +#else + uint16_t frequency_hz; + size_t audio_channels; + int16_t test_cntr = 0; + + // Open both mono and stereo test files in 32 kHz. + const std::string file_name_stereo = + webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"); + const std::string file_name_mono = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + frequency_hz = 32000; + in_file_stereo_.Open(file_name_stereo, frequency_hz, "rb"); + in_file_stereo_.ReadStereo(true); + in_file_mono_.Open(file_name_mono, frequency_hz, "rb"); + in_file_mono_.ReadStereo(false); + + // Create Opus encoders for mono and stereo. + ASSERT_GT(WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1, 0, 48000), -1); + ASSERT_GT(WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2, 1, 48000), -1); + + // Create Opus decoders for mono and stereo for stand-alone testing of Opus. + ASSERT_GT(WebRtcOpus_DecoderCreate(&opus_mono_decoder_, 1, 48000), -1); + ASSERT_GT(WebRtcOpus_DecoderCreate(&opus_stereo_decoder_, 2, 48000), -1); + WebRtcOpus_DecoderInit(opus_mono_decoder_); + WebRtcOpus_DecoderInit(opus_stereo_decoder_); + + ASSERT_TRUE(acm_receiver_.get() != NULL); + EXPECT_EQ(0, acm_receiver_->InitializeReceiver()); + + // Register Opus stereo as receiving codec. + constexpr int kOpusPayloadType = 120; + const SdpAudioFormat kOpusFormatStereo("opus", 48000, 2, {{"stereo", "1"}}); + payload_type_ = kOpusPayloadType; + acm_receiver_->SetReceiveCodecs({{kOpusPayloadType, kOpusFormatStereo}}); + + // Create and connect the channel. + channel_a2b_ = new TestPackStereo; + channel_a2b_->RegisterReceiverACM(acm_receiver_.get()); + + // + // Test Stereo. + // + + channel_a2b_->set_codec_mode(kStereo); + audio_channels = 2; + test_cntr++; + OpenOutFile(test_cntr); + + // Run Opus with 2.5 ms frame size. + Run(channel_a2b_, audio_channels, 64000, 120); + + // Run Opus with 5 ms frame size. + Run(channel_a2b_, audio_channels, 64000, 240); + + // Run Opus with 10 ms frame size. + Run(channel_a2b_, audio_channels, 64000, 480); + + // Run Opus with 20 ms frame size. + Run(channel_a2b_, audio_channels, 64000, 960); + + // Run Opus with 40 ms frame size. + Run(channel_a2b_, audio_channels, 64000, 1920); + + // Run Opus with 60 ms frame size. + Run(channel_a2b_, audio_channels, 64000, 2880); + + out_file_.Close(); + out_file_standalone_.Close(); + + // + // Test Opus stereo with packet-losses. + // + + test_cntr++; + OpenOutFile(test_cntr); + + // Run Opus with 20 ms frame size, 1% packet loss. + Run(channel_a2b_, audio_channels, 64000, 960, 1); + + // Run Opus with 20 ms frame size, 5% packet loss. + Run(channel_a2b_, audio_channels, 64000, 960, 5); + + // Run Opus with 20 ms frame size, 10% packet loss. + Run(channel_a2b_, audio_channels, 64000, 960, 10); + + out_file_.Close(); + out_file_standalone_.Close(); + + // + // Test Mono. + // + channel_a2b_->set_codec_mode(kMono); + audio_channels = 1; + test_cntr++; + OpenOutFile(test_cntr); + + // Register Opus mono as receiving codec. + const SdpAudioFormat kOpusFormatMono("opus", 48000, 2); + acm_receiver_->SetReceiveCodecs({{kOpusPayloadType, kOpusFormatMono}}); + + // Run Opus with 2.5 ms frame size. + Run(channel_a2b_, audio_channels, 32000, 120); + + // Run Opus with 5 ms frame size. + Run(channel_a2b_, audio_channels, 32000, 240); + + // Run Opus with 10 ms frame size. + Run(channel_a2b_, audio_channels, 32000, 480); + + // Run Opus with 20 ms frame size. + Run(channel_a2b_, audio_channels, 32000, 960); + + // Run Opus with 40 ms frame size. + Run(channel_a2b_, audio_channels, 32000, 1920); + + // Run Opus with 60 ms frame size. + Run(channel_a2b_, audio_channels, 32000, 2880); + + out_file_.Close(); + out_file_standalone_.Close(); + + // + // Test Opus mono with packet-losses. + // + test_cntr++; + OpenOutFile(test_cntr); + + // Run Opus with 20 ms frame size, 1% packet loss. + Run(channel_a2b_, audio_channels, 64000, 960, 1); + + // Run Opus with 20 ms frame size, 5% packet loss. + Run(channel_a2b_, audio_channels, 64000, 960, 5); + + // Run Opus with 20 ms frame size, 10% packet loss. + Run(channel_a2b_, audio_channels, 64000, 960, 10); + + // Close the files. + in_file_stereo_.Close(); + in_file_mono_.Close(); + out_file_.Close(); + out_file_standalone_.Close(); +#endif +} + +void OpusTest::Run(TestPackStereo* channel, + size_t channels, + int bitrate, + size_t frame_length, + int percent_loss) { + AudioFrame audio_frame; + int32_t out_freq_hz_b = out_file_.SamplingFrequency(); + const size_t kBufferSizeSamples = 480 * 12 * 2; // 120 ms stereo audio. + int16_t audio[kBufferSizeSamples]; + int16_t out_audio[kBufferSizeSamples]; + int16_t audio_type; + size_t written_samples = 0; + size_t read_samples = 0; + size_t decoded_samples = 0; + bool first_packet = true; + uint32_t start_time_stamp = 0; + + channel->reset_payload_size(); + counter_ = 0; + + // Set encoder rate. + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_mono_encoder_, bitrate)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_stereo_encoder_, bitrate)); + +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM) + // If we are on Android, iOS and/or ARM, use a lower complexity setting as + // default. + const int kOpusComplexity5 = 5; + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_mono_encoder_, kOpusComplexity5)); + EXPECT_EQ(0, + WebRtcOpus_SetComplexity(opus_stereo_encoder_, kOpusComplexity5)); +#endif + + // Fast-forward 1 second (100 blocks) since the files start with silence. + in_file_stereo_.FastForward(100); + in_file_mono_.FastForward(100); + + // Limit the runtime to 1000 blocks of 10 ms each. + for (size_t audio_length = 0; audio_length < 1000; audio_length += 10) { + bool lost_packet = false; + + // Get 10 msec of audio. + if (channels == 1) { + if (in_file_mono_.EndOfFile()) { + break; + } + in_file_mono_.Read10MsData(audio_frame); + } else { + if (in_file_stereo_.EndOfFile()) { + break; + } + in_file_stereo_.Read10MsData(audio_frame); + } + + // If input audio is sampled at 32 kHz, resampling to 48 kHz is required. + EXPECT_EQ(480, resampler_.Resample10Msec( + audio_frame.data(), audio_frame.sample_rate_hz_, 48000, + channels, kBufferSizeSamples - written_samples, + &audio[written_samples])); + written_samples += 480 * channels; + + // Sometimes we need to loop over the audio vector to produce the right + // number of packets. + size_t loop_encode = + (written_samples - read_samples) / (channels * frame_length); + + if (loop_encode > 0) { + const size_t kMaxBytes = 1000; // Maximum number of bytes for one packet. + size_t bitstream_len_byte; + uint8_t bitstream[kMaxBytes]; + for (size_t i = 0; i < loop_encode; i++) { + int bitstream_len_byte_int = WebRtcOpus_Encode( + (channels == 1) ? opus_mono_encoder_ : opus_stereo_encoder_, + &audio[read_samples], frame_length, kMaxBytes, bitstream); + ASSERT_GE(bitstream_len_byte_int, 0); + bitstream_len_byte = static_cast<size_t>(bitstream_len_byte_int); + + // Simulate packet loss by setting `packet_loss_` to "true" in + // `percent_loss` percent of the loops. + // TODO(tlegrand): Move handling of loss simulation to TestPackStereo. + if (percent_loss > 0) { + if (counter_ == floor((100 / percent_loss) + 0.5)) { + counter_ = 0; + lost_packet = true; + channel->set_lost_packet(true); + } else { + lost_packet = false; + channel->set_lost_packet(false); + } + counter_++; + } + + // Run stand-alone Opus decoder, or decode PLC. + if (channels == 1) { + if (!lost_packet) { + decoded_samples += WebRtcOpus_Decode( + opus_mono_decoder_, bitstream, bitstream_len_byte, + &out_audio[decoded_samples * channels], &audio_type); + } else { + // Call decoder PLC. + constexpr int kPlcDurationMs = 10; + constexpr int kPlcSamples = 48 * kPlcDurationMs; + size_t total_plc_samples = 0; + while (total_plc_samples < frame_length) { + int ret = WebRtcOpus_Decode( + opus_mono_decoder_, NULL, 0, + &out_audio[decoded_samples * channels], &audio_type); + EXPECT_EQ(ret, kPlcSamples); + decoded_samples += ret; + total_plc_samples += ret; + } + EXPECT_EQ(total_plc_samples, frame_length); + } + } else { + if (!lost_packet) { + decoded_samples += WebRtcOpus_Decode( + opus_stereo_decoder_, bitstream, bitstream_len_byte, + &out_audio[decoded_samples * channels], &audio_type); + } else { + // Call decoder PLC. + constexpr int kPlcDurationMs = 10; + constexpr int kPlcSamples = 48 * kPlcDurationMs; + size_t total_plc_samples = 0; + while (total_plc_samples < frame_length) { + int ret = WebRtcOpus_Decode( + opus_stereo_decoder_, NULL, 0, + &out_audio[decoded_samples * channels], &audio_type); + EXPECT_EQ(ret, kPlcSamples); + decoded_samples += ret; + total_plc_samples += ret; + } + EXPECT_EQ(total_plc_samples, frame_length); + } + } + + // Send data to the channel. "channel" will handle the loss simulation. + channel->SendData(AudioFrameType::kAudioFrameSpeech, payload_type_, + rtp_timestamp_, bitstream, bitstream_len_byte, 0); + if (first_packet) { + first_packet = false; + start_time_stamp = rtp_timestamp_; + } + rtp_timestamp_ += static_cast<uint32_t>(frame_length); + read_samples += frame_length * channels; + } + if (read_samples == written_samples) { + read_samples = 0; + written_samples = 0; + } + } + + // Run received side of ACM. + bool muted; + ASSERT_EQ( + 0, acm_receiver_->PlayoutData10Ms(out_freq_hz_b, &audio_frame, &muted)); + ASSERT_FALSE(muted); + + // Write output speech to file. + out_file_.Write10MsData( + audio_frame.data(), + audio_frame.samples_per_channel_ * audio_frame.num_channels_); + + // Write stand-alone speech to file. + out_file_standalone_.Write10MsData(out_audio, decoded_samples * channels); + + if (audio_frame.timestamp_ > start_time_stamp) { + // Number of channels should be the same for both stand-alone and + // ACM-decoding. + EXPECT_EQ(audio_frame.num_channels_, channels); + } + + decoded_samples = 0; + } + + if (in_file_mono_.EndOfFile()) { + in_file_mono_.Rewind(); + } + if (in_file_stereo_.EndOfFile()) { + in_file_stereo_.Rewind(); + } + // Reset in case we ended with a lost packet. + channel->set_lost_packet(false); +} + +void OpusTest::OpenOutFile(int test_number) { + std::string file_name; + std::stringstream file_stream; + file_stream << webrtc::test::OutputPath() << "opustest_out_" << test_number + << ".pcm"; + file_name = file_stream.str(); + out_file_.Open(file_name, 48000, "wb"); + file_stream.str(""); + file_name = file_stream.str(); + file_stream << webrtc::test::OutputPath() << "opusstandalone_out_" + << test_number << ".pcm"; + file_name = file_stream.str(); + out_file_standalone_.Open(file_name, 48000, "wb"); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/test/opus_test.h b/third_party/libwebrtc/modules/audio_coding/test/opus_test.h new file mode 100644 index 0000000000..c69f922adb --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/opus_test.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_CODING_TEST_OPUS_TEST_H_ +#define MODULES_AUDIO_CODING_TEST_OPUS_TEST_H_ + +#include <math.h> + +#include <memory> + +#include "modules/audio_coding/acm2/acm_resampler.h" +#include "modules/audio_coding/codecs/opus/opus_interface.h" +#include "modules/audio_coding/test/PCMFile.h" +#include "modules/audio_coding/test/TestStereo.h" + +namespace webrtc { + +class OpusTest { + public: + OpusTest(); + ~OpusTest(); + + void Perform(); + + private: + void Run(TestPackStereo* channel, + size_t channels, + int bitrate, + size_t frame_length, + int percent_loss = 0); + + void OpenOutFile(int test_number); + + std::unique_ptr<AudioCodingModule> acm_receiver_; + TestPackStereo* channel_a2b_; + PCMFile in_file_stereo_; + PCMFile in_file_mono_; + PCMFile out_file_; + PCMFile out_file_standalone_; + int counter_; + uint8_t payload_type_; + uint32_t rtp_timestamp_; + acm2::ACMResampler resampler_; + WebRtcOpusEncInst* opus_mono_encoder_; + WebRtcOpusEncInst* opus_stereo_encoder_; + WebRtcOpusDecInst* opus_mono_decoder_; + WebRtcOpusDecInst* opus_stereo_decoder_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_TEST_OPUS_TEST_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/test/target_delay_unittest.cc b/third_party/libwebrtc/modules/audio_coding/test/target_delay_unittest.cc new file mode 100644 index 0000000000..5eccdcf8eb --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/target_delay_unittest.cc @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2013 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. + */ + +#include <memory> + +#include "api/audio/audio_frame.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/rtp_headers.h" +#include "modules/audio_coding/acm2/acm_receiver.h" +#include "modules/audio_coding/codecs/pcm16b/pcm16b.h" +#include "modules/audio_coding/include/audio_coding_module.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +class TargetDelayTest : public ::testing::Test { + protected: + TargetDelayTest() + : receiver_( + AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory())) {} + + ~TargetDelayTest() {} + + void SetUp() { + constexpr int pltype = 108; + std::map<int, SdpAudioFormat> receive_codecs = { + {pltype, {"L16", kSampleRateHz, 1}}}; + receiver_.SetCodecs(receive_codecs); + + rtp_header_.payloadType = pltype; + rtp_header_.timestamp = 0; + rtp_header_.ssrc = 0x12345678; + rtp_header_.markerBit = false; + rtp_header_.sequenceNumber = 0; + + int16_t audio[kFrameSizeSamples]; + const int kRange = 0x7FF; // 2047, easy for masking. + for (size_t n = 0; n < kFrameSizeSamples; ++n) + audio[n] = (rand() & kRange) - kRange / 2; + WebRtcPcm16b_Encode(audio, kFrameSizeSamples, payload_); + } + + void OutOfRangeInput() { + EXPECT_EQ(-1, SetMinimumDelay(-1)); + EXPECT_EQ(-1, SetMinimumDelay(10001)); + } + + void TargetDelayBufferMinMax() { + const int kTargetMinDelayMs = kNum10msPerFrame * 10; + ASSERT_EQ(0, SetMinimumDelay(kTargetMinDelayMs)); + for (int m = 0; m < 30; ++m) // Run enough iterations to fill 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 the buffer. + Run(false); + + int capped_optimal_delay = GetCurrentOptimalDelayMs(); + EXPECT_EQ(kTargetMaxDelayMs, capped_optimal_delay); + } + + private: + static const int kSampleRateHz = 16000; + static const int kNum10msPerFrame = 2; + static const size_t kFrameSizeSamples = 320; // 20 ms @ 16 kHz. + // payload-len = frame-samples * 2 bytes/sample. + static const int kPayloadLenBytes = 320 * 2; + // Inter-arrival time in number of packets in a jittery channel. One is no + // jitter. + static const int kInterarrivalJitterPacket = 2; + + void Push() { + rtp_header_.timestamp += kFrameSizeSamples; + rtp_header_.sequenceNumber++; + ASSERT_EQ(0, receiver_.InsertPacket(rtp_header_, + rtc::ArrayView<const uint8_t>( + payload_, kFrameSizeSamples * 2))); + } + + // Pull audio equivalent to the amount of audio in one RTP packet. + void Pull() { + AudioFrame frame; + bool muted; + for (int k = 0; k < kNum10msPerFrame; ++k) { // Pull one frame. + ASSERT_EQ(0, receiver_.GetAudio(-1, &frame, &muted)); + ASSERT_FALSE(muted); + // Had to use ASSERT_TRUE, ASSERT_EQ generated error. + ASSERT_TRUE(kSampleRateHz == frame.sample_rate_hz_); + ASSERT_EQ(1u, frame.num_channels_); + ASSERT_TRUE(kSampleRateHz / 100 == frame.samples_per_channel_); + } + } + + void Run(bool clean) { + for (int n = 0; n < 10; ++n) { + for (int m = 0; m < 5; ++m) { + Push(); + Pull(); + } + + if (!clean) { + for (int m = 0; m < 10; ++m) { // Long enough to trigger delay change. + Push(); + for (int n = 0; n < kInterarrivalJitterPacket; ++n) + Pull(); + } + } + } + } + + int SetMinimumDelay(int delay_ms) { + return receiver_.SetMinimumDelay(delay_ms); + } + + int SetMaximumDelay(int delay_ms) { + return receiver_.SetMaximumDelay(delay_ms); + } + + int GetCurrentOptimalDelayMs() { + NetworkStatistics stats; + receiver_.GetNetworkStatistics(&stats); + return stats.preferredBufferSize; + } + + acm2::AcmReceiver receiver_; + RTPHeader rtp_header_; + uint8_t payload_[kPayloadLenBytes]; +}; + +// Flaky on iOS: webrtc:7057. +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) +#define MAYBE_OutOfRangeInput DISABLED_OutOfRangeInput +#else +#define MAYBE_OutOfRangeInput OutOfRangeInput +#endif +TEST_F(TargetDelayTest, MAYBE_OutOfRangeInput) { + OutOfRangeInput(); +} + +// Flaky on iOS: webrtc:7057. +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) +#define MAYBE_TargetDelayBufferMinMax DISABLED_TargetDelayBufferMinMax +#else +#define MAYBE_TargetDelayBufferMinMax TargetDelayBufferMinMax +#endif +TEST_F(TargetDelayTest, MAYBE_TargetDelayBufferMinMax) { + TargetDelayBufferMinMax(); +} + +} // namespace webrtc |