diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn index 7beca3fa87..efdddda68c 100644 --- a/webrtc/modules/audio_coding/BUILD.gn +++ b/webrtc/modules/audio_coding/BUILD.gn @@ -1100,6 +1100,8 @@ if (rtc_include_tests) { "neteq/tools/audio_sink.h", "neteq/tools/constant_pcm_packet_source.cc", "neteq/tools/constant_pcm_packet_source.h", + "neteq/tools/encode_neteq_input.cc", + "neteq/tools/encode_neteq_input.h", "neteq/tools/fake_decode_from_file.cc", "neteq/tools/fake_decode_from_file.h", "neteq/tools/input_audio_file.cc", diff --git a/webrtc/modules/audio_coding/neteq/neteq.gypi b/webrtc/modules/audio_coding/neteq/neteq.gypi index d9c152e184..f07cee4681 100644 --- a/webrtc/modules/audio_coding/neteq/neteq.gypi +++ b/webrtc/modules/audio_coding/neteq/neteq.gypi @@ -231,6 +231,8 @@ 'tools/audio_sink.cc', 'tools/constant_pcm_packet_source.cc', 'tools/constant_pcm_packet_source.h', + 'tools/encode_neteq_input.cc', + 'tools/encode_neteq_input.h', 'tools/fake_decode_from_file.cc', 'tools/fake_decode_from_file.h', 'tools/input_audio_file.cc', diff --git a/webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.cc b/webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.cc new file mode 100644 index 0000000000..54682166aa --- /dev/null +++ b/webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.cc @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2016 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 "webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.h" + +#include + +#include "webrtc/base/checks.h" + +namespace webrtc { +namespace test { + +EncodeNetEqInput::EncodeNetEqInput(std::unique_ptr input, + std::unique_ptr encoder, + int64_t input_duration_ms) + : input_(std::move(input)), + encoder_(std::move(encoder)), + input_duration_ms_(input_duration_ms) { + CreatePacket(); +} + +rtc::Optional EncodeNetEqInput::NextPacketTime() const { + RTC_DCHECK(packet_data_); + return rtc::Optional(static_cast(packet_data_->time_ms)); +} + +rtc::Optional EncodeNetEqInput::NextOutputEventTime() const { + return rtc::Optional(next_output_event_ms_); +} + +std::unique_ptr EncodeNetEqInput::PopPacket() { + RTC_DCHECK(packet_data_); + // Grab the packet to return... + std::unique_ptr packet_to_return = std::move(packet_data_); + // ... and line up the next packet for future use. + CreatePacket(); + + return packet_to_return; +} + +void EncodeNetEqInput::AdvanceOutputEvent() { + next_output_event_ms_ += kOutputPeriodMs; +} + +rtc::Optional EncodeNetEqInput::NextHeader() const { + RTC_DCHECK(packet_data_); + return rtc::Optional(packet_data_->header.header); +} + +void EncodeNetEqInput::CreatePacket() { + // Create a new PacketData object. + RTC_DCHECK(!packet_data_); + packet_data_.reset(new NetEqInput::PacketData); + RTC_DCHECK_EQ(packet_data_->payload.size(), 0u); + + // Loop until we get a packet. + AudioEncoder::EncodedInfo info; + RTC_DCHECK(!info.send_even_if_empty); + int num_blocks = 0; + while (packet_data_->payload.size() == 0 && !info.send_even_if_empty) { + const size_t num_samples = rtc::CheckedDivExact( + static_cast(encoder_->SampleRateHz() * kOutputPeriodMs), 1000); + std::unique_ptr audio(new int16_t[num_samples]); + RTC_CHECK(input_->Read(num_samples, audio.get())); + + info = encoder_->Encode( + rtp_timestamp_, rtc::ArrayView(audio.get(), num_samples), + &packet_data_->payload); + + rtp_timestamp_ += + num_samples * encoder_->RtpTimestampRateHz() / encoder_->SampleRateHz(); + ++num_blocks; + } + packet_data_->header.header.timestamp = info.encoded_timestamp; + packet_data_->header.header.payloadType = info.payload_type; + packet_data_->header.header.sequenceNumber = sequence_number_++; + packet_data_->time_ms = next_packet_time_ms_; + next_packet_time_ms_ += num_blocks * kOutputPeriodMs; +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.h b/webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.h new file mode 100644 index 0000000000..ab28fd96cf --- /dev/null +++ b/webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 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 WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_ENCODE_NETEQ_INPUT_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_ENCODE_NETEQ_INPUT_H_ + +#include + +#include "webrtc/modules/audio_coding/codecs/audio_encoder.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/neteq_input.h" +#include "webrtc/modules/include/module_common_types.h" + +namespace webrtc { +namespace test { + +// This class provides a NetEqInput that takes audio from an input file and +// encodes it using a given audio encoder. +class EncodeNetEqInput : public NetEqInput { + public: + // The source will end after the given input duration. + EncodeNetEqInput(std::unique_ptr input, + std::unique_ptr encoder, + int64_t input_duration_ms); + + rtc::Optional NextPacketTime() const override; + + rtc::Optional NextOutputEventTime() const override; + + std::unique_ptr PopPacket() override; + + void AdvanceOutputEvent() override; + + bool ended() const override { + return next_output_event_ms_ <= input_duration_ms_; + } + + rtc::Optional NextHeader() const override; + + private: + static constexpr int64_t kOutputPeriodMs = 10; + + void CreatePacket(); + + std::unique_ptr input_; + std::unique_ptr encoder_; + std::unique_ptr packet_data_; + int32_t rtp_timestamp_ = 0; + int16_t sequence_number_ = 0; + int64_t next_packet_time_ms_ = 0; + int64_t next_output_event_ms_ = 0; + const int64_t input_duration_ms_; +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_ENCODE_NETEQ_INPUT_H_ diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_input.h b/webrtc/modules/audio_coding/neteq/tools/neteq_input.h index 8abec6303f..be08a791a8 100644 --- a/webrtc/modules/audio_coding/neteq/tools/neteq_input.h +++ b/webrtc/modules/audio_coding/neteq/tools/neteq_input.h @@ -65,7 +65,9 @@ class NetEqInput { // time). virtual void AdvanceOutputEvent() = 0; - // Returns true if the source has come to an end. + // Returns true if the source has come to an end. An implementation must + // eventually return true from this method, or the test will end up in an + // infinite loop. virtual bool ended() const = 0; // Returns the RTP header for the next packet, i.e., the packet that will be diff --git a/webrtc/test/fuzzers/BUILD.gn b/webrtc/test/fuzzers/BUILD.gn index 82b6ca8d95..0447189e26 100644 --- a/webrtc/test/fuzzers/BUILD.gn +++ b/webrtc/test/fuzzers/BUILD.gn @@ -185,6 +185,19 @@ webrtc_fuzzer_test("audio_decoder_opus_redundant_fuzzer") { ] } +webrtc_fuzzer_test("neteq_rtp_fuzzer") { + sources = [ + "neteq_rtp_fuzzer.cc", + ] + deps = [ + "../../modules/audio_coding:neteq", + "../../modules/audio_coding:neteq_unittest_tools", + "../../modules/audio_coding:pcm16b", + "../../modules/rtp_rtcp", + "../../test:test_support", + ] +} + # TODO(katrielc) Enable in Chromium when CL 2022833002 lands. # Although the dependency on media compiles in standalone, it is # flagged by gn check, so breaks when rolled into Chromium. diff --git a/webrtc/test/fuzzers/neteq_rtp_fuzzer.cc b/webrtc/test/fuzzers/neteq_rtp_fuzzer.cc new file mode 100644 index 0000000000..8d09691528 --- /dev/null +++ b/webrtc/test/fuzzers/neteq_rtp_fuzzer.cc @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016 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 + +#include "webrtc/base/array_view.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_checksum.h" +#include "webrtc/modules/audio_coding/neteq/tools/encode_neteq_input.h" +#include "webrtc/modules/audio_coding/neteq/tools/neteq_test.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" +#include "webrtc/test/testsupport/fileutils.h" + +namespace webrtc { +namespace test { +namespace { +constexpr int kPayloadType = 95; + +class FuzzRtpInput : public NetEqInput { + public: + explicit FuzzRtpInput(rtc::ArrayView data) : data_(data) { + std::unique_ptr audio_input( + new InputAudioFile(ResourcePath("audio_coding/testfile32kHz", "pcm"))); + AudioEncoderPcm16B::Config config; + config.payload_type = kPayloadType; + config.sample_rate_hz = 32000; + std::unique_ptr encoder(new AudioEncoderPcm16B(config)); + input_.reset(new EncodeNetEqInput(std::move(audio_input), + std::move(encoder), + std::numeric_limits::max())); + packet_ = input_->PopPacket(); + FuzzHeader(); + } + + rtc::Optional NextPacketTime() const override { + return rtc::Optional(packet_->time_ms); + } + + rtc::Optional NextOutputEventTime() const override { + return input_->NextOutputEventTime(); + } + + std::unique_ptr PopPacket() override { + RTC_DCHECK(packet_); + std::unique_ptr packet_to_return = std::move(packet_); + packet_ = input_->PopPacket(); + FuzzHeader(); + return packet_to_return; + } + + void AdvanceOutputEvent() override { return input_->AdvanceOutputEvent(); } + + bool ended() const override { return ended_; } + + rtc::Optional NextHeader() const override { + RTC_DCHECK(packet_); + return rtc::Optional(packet_->header.header); + } + + private: + void FuzzHeader() { + constexpr size_t kNumBytesToFuzz = 11; + if (data_ix_ + kNumBytesToFuzz > data_.size()) { + ended_ = true; + return; + } + RTC_DCHECK(packet_); + const size_t start_ix = data_ix_; + packet_->header.header.payloadType = + ByteReader::ReadLittleEndian(&data_[data_ix_]); + packet_->header.header.payloadType &= 0x7F; + data_ix_ += sizeof(uint8_t); + packet_->header.header.sequenceNumber = + ByteReader::ReadLittleEndian(&data_[data_ix_]); + data_ix_ += sizeof(uint16_t); + packet_->header.header.timestamp = + ByteReader::ReadLittleEndian(&data_[data_ix_]); + data_ix_ += sizeof(uint32_t); + packet_->header.header.ssrc = + ByteReader::ReadLittleEndian(&data_[data_ix_]); + data_ix_ += sizeof(uint32_t); + RTC_CHECK_EQ(data_ix_ - start_ix, kNumBytesToFuzz); + } + + bool ended_ = false; + rtc::ArrayView data_; + size_t data_ix_ = 0; + std::unique_ptr input_; + std::unique_ptr packet_; +}; +} // namespace + +void FuzzOneInputTest(const uint8_t* data, size_t size) { + std::unique_ptr input( + new FuzzRtpInput(rtc::ArrayView(data, size))); + std::unique_ptr output(new AudioChecksum); + NetEqTestErrorCallback dummy_callback; // Does nothing with error callbacks. + NetEq::Config config; + NetEqTest::DecoderMap codecs; + codecs[0] = std::make_pair(NetEqDecoder::kDecoderPCMu, "pcmu"); + codecs[8] = std::make_pair(NetEqDecoder::kDecoderPCMa, "pcma"); + codecs[102] = std::make_pair(NetEqDecoder::kDecoderILBC, "ilbc"); + codecs[103] = std::make_pair(NetEqDecoder::kDecoderISAC, "isac"); + codecs[104] = std::make_pair(NetEqDecoder::kDecoderISACswb, "isac-swb"); + codecs[111] = std::make_pair(NetEqDecoder::kDecoderOpus, "opus"); + codecs[93] = std::make_pair(NetEqDecoder::kDecoderPCM16B, "pcm16-nb"); + codecs[94] = std::make_pair(NetEqDecoder::kDecoderPCM16Bwb, "pcm16-wb"); + codecs[96] = + std::make_pair(NetEqDecoder::kDecoderPCM16Bswb48kHz, "pcm16-swb48"); + codecs[9] = std::make_pair(NetEqDecoder::kDecoderG722, "g722"); + codecs[106] = std::make_pair(NetEqDecoder::kDecoderAVT, "avt"); + codecs[117] = std::make_pair(NetEqDecoder::kDecoderRED, "red"); + codecs[13] = std::make_pair(NetEqDecoder::kDecoderCNGnb, "cng-nb"); + codecs[98] = std::make_pair(NetEqDecoder::kDecoderCNGwb, "cng-wb"); + codecs[99] = std::make_pair(NetEqDecoder::kDecoderCNGswb32kHz, "cng-swb32"); + codecs[100] = std::make_pair(NetEqDecoder::kDecoderCNGswb48kHz, "cng-swb48"); + // This is the payload type that will be used for encoding. + codecs[kPayloadType] = + std::make_pair(NetEqDecoder::kDecoderPCM16Bswb32kHz, "pcm16-swb32"); + NetEqTest::ExtDecoderMap ext_codecs; + + NetEqTest test(config, codecs, ext_codecs, std::move(input), + std::move(output), &dummy_callback); + test.Run(); +} + +} // namespace test + +void FuzzOneInput(const uint8_t* data, size_t size) { + test::FuzzOneInputTest(data, size); +} + +} // namespace webrtc