1 /*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <algorithm>
12 #include <cmath>
13 #include <cstring>
14 #include <memory>
15 #include <vector>
16
17 #include "api/array_view.h"
18 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
19 #include "modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h"
20 #include "modules/audio_coding/neteq/tools/audio_checksum.h"
21 #include "modules/audio_coding/neteq/tools/encode_neteq_input.h"
22 #include "modules/audio_coding/neteq/tools/neteq_test.h"
23 #include "modules/rtp_rtcp/source/byte_io.h"
24
25 namespace webrtc {
26 namespace test {
27 namespace {
28 constexpr int kPayloadType = 95;
29
30 class SineGenerator : public EncodeNetEqInput::Generator {
31 public:
SineGenerator(int sample_rate_hz)32 explicit SineGenerator(int sample_rate_hz)
33 : sample_rate_hz_(sample_rate_hz) {}
34
Generate(size_t num_samples)35 rtc::ArrayView<const int16_t> Generate(size_t num_samples) override {
36 if (samples_.size() < num_samples) {
37 samples_.resize(num_samples);
38 }
39
40 rtc::ArrayView<int16_t> output(samples_.data(), num_samples);
41 for (auto& x : output) {
42 x = static_cast<int16_t>(2000.0 * std::sin(phase_));
43 phase_ += 2 * kPi * kFreqHz / sample_rate_hz_;
44 }
45 return output;
46 }
47
48 private:
49 static constexpr int kFreqHz = 300; // The sinewave frequency.
50 const int sample_rate_hz_;
51 const double kPi = std::acos(-1);
52 std::vector<int16_t> samples_;
53 double phase_ = 0.0;
54 };
55
56 class FuzzRtpInput : public NetEqInput {
57 public:
FuzzRtpInput(rtc::ArrayView<const uint8_t> data)58 explicit FuzzRtpInput(rtc::ArrayView<const uint8_t> data) : data_(data) {
59 AudioEncoderPcm16B::Config config;
60 config.payload_type = kPayloadType;
61 config.sample_rate_hz = 32000;
62 std::unique_ptr<AudioEncoder> encoder(new AudioEncoderPcm16B(config));
63 std::unique_ptr<EncodeNetEqInput::Generator> generator(
64 new SineGenerator(config.sample_rate_hz));
65 input_.reset(new EncodeNetEqInput(std::move(generator), std::move(encoder),
66 std::numeric_limits<int64_t>::max()));
67 packet_ = input_->PopPacket();
68 FuzzHeader();
69 MaybeFuzzPayload();
70 }
71
NextPacketTime() const72 absl::optional<int64_t> NextPacketTime() const override {
73 return packet_->time_ms;
74 }
75
NextOutputEventTime() const76 absl::optional<int64_t> NextOutputEventTime() const override {
77 return input_->NextOutputEventTime();
78 }
79
PopPacket()80 std::unique_ptr<PacketData> PopPacket() override {
81 RTC_DCHECK(packet_);
82 std::unique_ptr<PacketData> packet_to_return = std::move(packet_);
83 packet_ = input_->PopPacket();
84 FuzzHeader();
85 MaybeFuzzPayload();
86 return packet_to_return;
87 }
88
AdvanceOutputEvent()89 void AdvanceOutputEvent() override { return input_->AdvanceOutputEvent(); }
90
ended() const91 bool ended() const override { return ended_; }
92
NextHeader() const93 absl::optional<RTPHeader> NextHeader() const override {
94 RTC_DCHECK(packet_);
95 return packet_->header;
96 }
97
98 private:
FuzzHeader()99 void FuzzHeader() {
100 constexpr size_t kNumBytesToFuzz = 11;
101 if (data_ix_ + kNumBytesToFuzz > data_.size()) {
102 ended_ = true;
103 return;
104 }
105 RTC_DCHECK(packet_);
106 const size_t start_ix = data_ix_;
107 packet_->header.payloadType =
108 ByteReader<uint8_t>::ReadLittleEndian(&data_[data_ix_]);
109 packet_->header.payloadType &= 0x7F;
110 data_ix_ += sizeof(uint8_t);
111 packet_->header.sequenceNumber =
112 ByteReader<uint16_t>::ReadLittleEndian(&data_[data_ix_]);
113 data_ix_ += sizeof(uint16_t);
114 packet_->header.timestamp =
115 ByteReader<uint32_t>::ReadLittleEndian(&data_[data_ix_]);
116 data_ix_ += sizeof(uint32_t);
117 packet_->header.ssrc =
118 ByteReader<uint32_t>::ReadLittleEndian(&data_[data_ix_]);
119 data_ix_ += sizeof(uint32_t);
120 RTC_CHECK_EQ(data_ix_ - start_ix, kNumBytesToFuzz);
121 }
122
MaybeFuzzPayload()123 void MaybeFuzzPayload() {
124 // Read one byte of fuzz data to determine how many payload bytes to fuzz.
125 if (data_ix_ + 1 > data_.size()) {
126 ended_ = true;
127 return;
128 }
129 size_t bytes_to_fuzz = data_[data_ix_++];
130
131 // Restrict number of bytes to fuzz to 16; a reasonably low number enough to
132 // cover a few RED headers. Also don't write outside the payload length.
133 bytes_to_fuzz = std::min(bytes_to_fuzz % 16, packet_->payload.size());
134
135 if (bytes_to_fuzz == 0)
136 return;
137
138 if (data_ix_ + bytes_to_fuzz > data_.size()) {
139 ended_ = true;
140 return;
141 }
142
143 std::memcpy(packet_->payload.data(), &data_[data_ix_], bytes_to_fuzz);
144 data_ix_ += bytes_to_fuzz;
145 }
146
147 bool ended_ = false;
148 rtc::ArrayView<const uint8_t> data_;
149 size_t data_ix_ = 0;
150 std::unique_ptr<EncodeNetEqInput> input_;
151 std::unique_ptr<PacketData> packet_;
152 };
153 } // namespace
154
FuzzOneInputTest(const uint8_t * data,size_t size)155 void FuzzOneInputTest(const uint8_t* data, size_t size) {
156 std::unique_ptr<FuzzRtpInput> input(
157 new FuzzRtpInput(rtc::ArrayView<const uint8_t>(data, size)));
158 std::unique_ptr<AudioChecksum> output(new AudioChecksum);
159 NetEqTest::Callbacks callbacks;
160 NetEq::Config config;
161 auto codecs = NetEqTest::StandardDecoderMap();
162 // kPayloadType is the payload type that will be used for encoding. Verify
163 // that it is included in the standard decoder map, and that it points to the
164 // expected decoder type.
165 const auto it = codecs.find(kPayloadType);
166 RTC_CHECK(it != codecs.end());
167 RTC_CHECK(it->second == SdpAudioFormat("L16", 32000, 1));
168
169 NetEqTest test(config, CreateBuiltinAudioDecoderFactory(), codecs,
170 /*text_log=*/nullptr, /*neteq_factory=*/nullptr,
171 std::move(input), std::move(output), callbacks);
172 test.Run();
173 }
174
175 } // namespace test
176
FuzzOneInput(const uint8_t * data,size_t size)177 void FuzzOneInput(const uint8_t* data, size_t size) {
178 if (size > 70000) {
179 return;
180 }
181 test::FuzzOneInputTest(data, size);
182 }
183
184 } // namespace webrtc
185