blob: 9faea41fa21119e305c11b97f65fd38a3d5051a3 [file] [log] [blame]
aleloi8c512822017-06-20 05:26:55 -07001/*
2 * Copyright (c) 2017 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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "test/fuzzers/audio_processing_fuzzer.h"
aleloi8c512822017-06-20 05:26:55 -070012
13#include <algorithm>
14#include <array>
15#include <cmath>
16
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "modules/audio_processing/include/audio_processing.h"
18#include "modules/include/module_common_types.h"
19#include "rtc_base/checks.h"
aleloi8c512822017-06-20 05:26:55 -070020
21namespace webrtc {
22namespace {
23size_t ByteToNativeRate(uint8_t data) {
24 using Rate = AudioProcessing::NativeRate;
25 switch (data % 4) {
26 case 0:
27 // Breaks AEC3.
28 // return static_cast<size_t>(Rate::kSampleRate8kHz);
29 case 1:
30 return static_cast<size_t>(Rate::kSampleRate16kHz);
31 case 2:
32 return static_cast<size_t>(Rate::kSampleRate32kHz);
33 default:
34 return static_cast<size_t>(Rate::kSampleRate48kHz);
35 }
36}
37
38template <class T>
39bool ParseSequence(size_t size,
40 const uint8_t** data,
41 size_t* remaining_size,
42 T* result_data) {
43 const size_t data_size_bytes = sizeof(T) * size;
44 if (data_size_bytes > *remaining_size) {
45 return false;
46 }
47
48 std::copy(*data, *data + data_size_bytes,
49 reinterpret_cast<uint8_t*>(result_data));
50
51 *data += data_size_bytes;
52 *remaining_size -= data_size_bytes;
53 return true;
54}
55
56void FuzzAudioProcessing(const uint8_t* data,
57 size_t size,
58 bool is_float,
59 AudioProcessing* apm) {
60 AudioFrame fixed_frame;
Alex Loiko28d52582017-06-22 11:33:41 +020061 std::array<float, 480> float_frame{};
aleloi8c512822017-06-20 05:26:55 -070062 float* const first_channel = &float_frame[0];
63
64 while (size > 0) {
65 // Decide input/output rate for this iteration.
66 const auto input_rate_byte = ParseByte(&data, &size);
67 const auto output_rate_byte = ParseByte(&data, &size);
68 if (!input_rate_byte || !output_rate_byte) {
69 return;
70 }
71 const auto input_rate_hz = ByteToNativeRate(*input_rate_byte);
72 const auto output_rate_hz = ByteToNativeRate(*output_rate_byte);
73
74 const size_t samples_per_input_channel =
75 rtc::CheckedDivExact(input_rate_hz, 100ul);
76 fixed_frame.samples_per_channel_ = samples_per_input_channel;
77 fixed_frame.sample_rate_hz_ = input_rate_hz;
78
79 // Two channels breaks AEC3.
80 fixed_frame.num_channels_ = 1;
81
82 // Fill the arrays with audio samples from the data.
83 if (is_float) {
84 if (!ParseSequence(samples_per_input_channel, &data, &size,
85 &float_frame[0])) {
86 return;
87 }
88 } else if (!ParseSequence(samples_per_input_channel, &data, &size,
89 fixed_frame.mutable_data())) {
90 return;
91 }
92
93 // Filter obviously wrong values like inf/nan and values that will
94 // lead to inf/nan in calculations. 1e6 leads to DCHECKS failing.
95 for (auto& x : float_frame) {
96 if (!std::isnormal(x) || std::abs(x) > 1e5) {
97 x = 0;
98 }
99 }
100
101 // Make the APM call depending on capture/render mode and float /
102 // fix interface.
103 const auto is_capture = ParseBool(&data, &size);
104 if (!is_capture) {
105 return;
106 }
107 if (*is_capture) {
108 auto apm_return_code =
109 is_float ? (apm->ProcessStream(
110 &first_channel, StreamConfig(input_rate_hz, 1),
111 StreamConfig(output_rate_hz, 1), &first_channel))
112 : (apm->ProcessStream(&fixed_frame));
113 RTC_DCHECK_NE(apm_return_code, AudioProcessing::kBadDataLengthError);
114 } else {
115 auto apm_return_code =
116 is_float ? (apm->ProcessReverseStream(
117 &first_channel, StreamConfig(input_rate_hz, 1),
118 StreamConfig(output_rate_hz, 1), &first_channel))
119 : (apm->ProcessReverseStream(&fixed_frame));
120 RTC_DCHECK_NE(apm_return_code, AudioProcessing::kBadDataLengthError);
121 }
122 }
123}
124
125} // namespace
126
127rtc::Optional<bool> ParseBool(const uint8_t** data, size_t* remaining_size) {
128 if (1 > *remaining_size) {
129 return rtc::Optional<bool>();
130 }
131 auto res = rtc::Optional<bool>((**data) % 2);
132 *data += 1;
133 *remaining_size -= 1;
134 return res;
135}
136
137rtc::Optional<uint8_t> ParseByte(const uint8_t** data, size_t* remaining_size) {
138 if (1 > *remaining_size) {
139 return rtc::Optional<uint8_t>();
140 }
141 auto res = rtc::Optional<uint8_t>((**data));
142 *data += 1;
143 *remaining_size -= 1;
144 return res;
145}
146
147void FuzzAudioProcessing(const uint8_t* data,
148 size_t size,
149 std::unique_ptr<AudioProcessing> apm) {
150 const auto is_float = ParseBool(&data, &size);
151 if (!is_float) {
152 return;
153 }
154
155 FuzzAudioProcessing(data, size, *is_float, apm.get());
156}
157} // namespace webrtc