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