|  | /* | 
|  | *  Copyright (c) 2017 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 <cmath> | 
|  | #include <limits> | 
|  | #include <memory> | 
|  | #include <vector> | 
|  |  | 
|  | #include "api/array_view.h" | 
|  | #include "api/audio_codecs/builtin_audio_decoder_factory.h" | 
|  | #include "modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h" | 
|  | #include "modules/audio_coding/neteq/tools/audio_checksum.h" | 
|  | #include "modules/audio_coding/neteq/tools/encode_neteq_input.h" | 
|  | #include "modules/audio_coding/neteq/tools/neteq_test.h" | 
|  | #include "rtc_base/numerics/safe_conversions.h" | 
|  | #include "rtc_base/random.h" | 
|  | #include "test/fuzzers/fuzz_data_helper.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace test { | 
|  | namespace { | 
|  | // Generate a mixture of sine wave and gaussian noise. | 
|  | class SineAndNoiseGenerator : public EncodeNetEqInput::Generator { | 
|  | public: | 
|  | // The noise generator is seeded with a value from the fuzzer data, but 0 is | 
|  | // avoided (since it is not allowed by the Random class). | 
|  | SineAndNoiseGenerator(int sample_rate_hz, FuzzDataHelper* fuzz_data) | 
|  | : sample_rate_hz_(sample_rate_hz), | 
|  | fuzz_data_(*fuzz_data), | 
|  | noise_generator_(fuzz_data_.ReadOrDefaultValueNotZero<uint64_t>(1)) {} | 
|  |  | 
|  | // Generates num_samples of the sine-gaussian mixture. | 
|  | rtc::ArrayView<const int16_t> Generate(size_t num_samples) override { | 
|  | if (samples_.size() < num_samples) { | 
|  | samples_.resize(num_samples); | 
|  | } | 
|  |  | 
|  | rtc::ArrayView<int16_t> output(samples_.data(), num_samples); | 
|  | // Randomize an amplitude between 0 and 32768; use 65000/2 if we are out of | 
|  | // fuzzer data. | 
|  | const float amplitude = fuzz_data_.ReadOrDefaultValue<uint16_t>(65000) / 2; | 
|  | // Randomize a noise standard deviation between 0 and 1999. | 
|  | const float noise_std = fuzz_data_.ReadOrDefaultValue<uint16_t>(0) % 2000; | 
|  | for (auto& x : output) { | 
|  | x = rtc::saturated_cast<int16_t>(amplitude * std::sin(phase_) + | 
|  | noise_generator_.Gaussian(0, noise_std)); | 
|  | phase_ += 2 * kPi * kFreqHz / sample_rate_hz_; | 
|  | } | 
|  | return output; | 
|  | } | 
|  |  | 
|  | private: | 
|  | static constexpr int kFreqHz = 300;  // The sinewave frequency. | 
|  | const int sample_rate_hz_; | 
|  | const double kPi = std::acos(-1); | 
|  | std::vector<int16_t> samples_; | 
|  | double phase_ = 0.0; | 
|  | FuzzDataHelper& fuzz_data_; | 
|  | Random noise_generator_; | 
|  | }; | 
|  |  | 
|  | class FuzzSignalInput : public NetEqInput { | 
|  | public: | 
|  | explicit FuzzSignalInput(FuzzDataHelper* fuzz_data, | 
|  | int sample_rate, | 
|  | uint8_t payload_type) | 
|  | : fuzz_data_(*fuzz_data) { | 
|  | AudioEncoderPcm16B::Config config; | 
|  | config.payload_type = payload_type; | 
|  | config.sample_rate_hz = sample_rate; | 
|  | std::unique_ptr<AudioEncoder> encoder(new AudioEncoderPcm16B(config)); | 
|  | std::unique_ptr<EncodeNetEqInput::Generator> generator( | 
|  | new SineAndNoiseGenerator(config.sample_rate_hz, fuzz_data)); | 
|  | input_.reset(new EncodeNetEqInput(std::move(generator), std::move(encoder), | 
|  | std::numeric_limits<int64_t>::max())); | 
|  | packet_ = input_->PopPacket(); | 
|  |  | 
|  | // Select an output event period. This is how long time we wait between each | 
|  | // call to NetEq::GetAudio. 10 ms is nominal, 9 and 11 ms will both lead to | 
|  | // clock drift (in different directions). | 
|  | constexpr int output_event_periods[] = {9, 10, 11}; | 
|  | output_event_period_ms_ = fuzz_data_.SelectOneOf(output_event_periods); | 
|  | } | 
|  |  | 
|  | std::optional<int64_t> NextPacketTime() const override { | 
|  | return packet_->time_ms; | 
|  | } | 
|  |  | 
|  | std::optional<int64_t> NextOutputEventTime() const override { | 
|  | return next_output_event_ms_; | 
|  | } | 
|  |  | 
|  | std::optional<SetMinimumDelayInfo> NextSetMinimumDelayInfo() const override { | 
|  | return input_->NextSetMinimumDelayInfo(); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<PacketData> PopPacket() override { | 
|  | RTC_DCHECK(packet_); | 
|  | std::unique_ptr<PacketData> packet_to_return = std::move(packet_); | 
|  | do { | 
|  | packet_ = input_->PopPacket(); | 
|  | // If the next value from the fuzzer input is 0, the packet is discarded | 
|  | // and the next one is pulled from the source. | 
|  | } while (fuzz_data_.CanReadBytes(1) && fuzz_data_.Read<uint8_t>() == 0); | 
|  | if (fuzz_data_.CanReadBytes(1)) { | 
|  | // Generate jitter by setting an offset for the arrival time. | 
|  | const int8_t arrival_time_offset_ms = fuzz_data_.Read<int8_t>(); | 
|  | // The arrival time can not be before the previous packets. | 
|  | packet_->time_ms = std::max(packet_to_return->time_ms, | 
|  | packet_->time_ms + arrival_time_offset_ms); | 
|  | } else { | 
|  | // Mark that we are at the end of the test. However, the current packet is | 
|  | // still valid (but it may not have been fuzzed as expected). | 
|  | ended_ = true; | 
|  | } | 
|  | return packet_to_return; | 
|  | } | 
|  |  | 
|  | void AdvanceOutputEvent() override { | 
|  | next_output_event_ms_ += output_event_period_ms_; | 
|  | } | 
|  |  | 
|  | void AdvanceSetMinimumDelay() override { | 
|  | return input_->AdvanceSetMinimumDelay(); | 
|  | } | 
|  |  | 
|  | bool ended() const override { return ended_; } | 
|  |  | 
|  | std::optional<RTPHeader> NextHeader() const override { | 
|  | RTC_DCHECK(packet_); | 
|  | return packet_->header; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool ended_ = false; | 
|  | FuzzDataHelper& fuzz_data_; | 
|  | std::unique_ptr<EncodeNetEqInput> input_; | 
|  | std::unique_ptr<PacketData> packet_; | 
|  | int64_t next_output_event_ms_ = 0; | 
|  | int64_t output_event_period_ms_ = 10; | 
|  | }; | 
|  |  | 
|  | template <class T> | 
|  | bool MapHas(const std::map<int, T>& m, int key, const T& value) { | 
|  | const auto it = m.find(key); | 
|  | return (it != m.end() && it->second == value); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void FuzzOneInputTest(const uint8_t* data, size_t size) { | 
|  | if (size < 1 || size > 65000) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | FuzzDataHelper fuzz_data(rtc::ArrayView<const uint8_t>(data, size)); | 
|  |  | 
|  | // Allowed sample rates and payload types used in the test. | 
|  | std::pair<int, uint8_t> rate_types[] = { | 
|  | {8000, 93}, {16000, 94}, {32000, 95}, {48000, 96}}; | 
|  | const auto rate_type = fuzz_data.SelectOneOf(rate_types); | 
|  | const int sample_rate = rate_type.first; | 
|  | const uint8_t payload_type = rate_type.second; | 
|  |  | 
|  | // Set up the input signal generator. | 
|  | std::unique_ptr<FuzzSignalInput> input( | 
|  | new FuzzSignalInput(&fuzz_data, sample_rate, payload_type)); | 
|  |  | 
|  | // Output sink for the test. | 
|  | std::unique_ptr<AudioChecksum> output(new AudioChecksum); | 
|  |  | 
|  | // Configure NetEq and the NetEqTest object. | 
|  | NetEqTest::Callbacks callbacks; | 
|  | NetEq::Config config; | 
|  | config.enable_fast_accelerate = true; | 
|  | auto codecs = NetEqTest::StandardDecoderMap(); | 
|  | // rate_types contains the payload types that will be used for encoding. | 
|  | // Verify that they all are included in the standard decoder map, and that | 
|  | // they point to the expected decoder types. | 
|  | RTC_CHECK( | 
|  | MapHas(codecs, rate_types[0].second, SdpAudioFormat("l16", 8000, 1))); | 
|  | RTC_CHECK( | 
|  | MapHas(codecs, rate_types[1].second, SdpAudioFormat("l16", 16000, 1))); | 
|  | RTC_CHECK( | 
|  | MapHas(codecs, rate_types[2].second, SdpAudioFormat("l16", 32000, 1))); | 
|  | RTC_CHECK( | 
|  | MapHas(codecs, rate_types[3].second, SdpAudioFormat("l16", 48000, 1))); | 
|  |  | 
|  | NetEqTest test(config, CreateBuiltinAudioDecoderFactory(), codecs, | 
|  | /*text_log=*/nullptr, /*neteq_factory=*/nullptr, | 
|  | std::move(input), std::move(output), callbacks); | 
|  | test.Run(); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  |  | 
|  | void FuzzOneInput(const uint8_t* data, size_t size) { | 
|  | test::FuzzOneInputTest(data, size); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |