aboutsummaryrefslogtreecommitdiff
path: root/fuzzer/opus_enc_fuzzer.cpp
blob: c2258fde84d697f646f2a7b04e951780a9fb5b1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/******************************************************************************
 *
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
 */

#include <algorithm>

#include "opus.h"

using namespace std;

constexpr int kFrameDuration = 50;
constexpr int kMaxPacket = 1500;
constexpr int kMinBitRate = 500;
constexpr int kMaxBitRate = 512000;

constexpr opus_int32 kSampleRates[] = {8000, 12000, 16000, 24000, 48000};
constexpr size_t kSampleRatesSize = size(kSampleRates);

#ifndef MULTISTREAM
constexpr int kChannels[] = {1, 2};
constexpr size_t kChannelsSize = size(kChannels);
#endif

constexpr int kApplications[] = {OPUS_APPLICATION_VOIP, OPUS_APPLICATION_AUDIO,
                                 OPUS_APPLICATION_RESTRICTED_LOWDELAY};
constexpr size_t kApplicationsSize = size(kApplications);

constexpr int kSignals[] = {OPUS_AUTO, OPUS_SIGNAL_VOICE, OPUS_SIGNAL_MUSIC};
constexpr size_t kSignalsSize = size(kSignals);

constexpr int kSetDTX[] = {0, 1};
constexpr size_t kSetDTXSize = size(kSetDTX);

constexpr int kSetVBR[] = {0, 1};
constexpr size_t kSetVBRSize = size(kSetVBR);

constexpr int kSetInbandFec[] = {0, 1};
constexpr size_t kSetInbandFecSize = size(kSetInbandFec);

constexpr int kSetVBRConstraint[] = {0, 1};
constexpr size_t kSetVBRConstraintSize = size(kSetVBRConstraint);

constexpr int kSetPredDisable[] = {0, 1};
constexpr size_t kSetPredDisableSize = size(kSetPredDisable);

constexpr int kComplexities[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
constexpr size_t kComplexitiesSize = size(kComplexities);

constexpr int kForceChannels[] = {OPUS_AUTO, 1, 2};
constexpr size_t kForceChannelsSize = size(kForceChannels);

constexpr int kMaxBandwidths[] = {OPUS_BANDWIDTH_NARROWBAND, OPUS_BANDWIDTH_MEDIUMBAND,
                                  OPUS_BANDWIDTH_WIDEBAND, OPUS_BANDWIDTH_SUPERWIDEBAND,
                                  OPUS_BANDWIDTH_FULLBAND};
constexpr size_t kMaxBandwidthsSize = size(kMaxBandwidths);

constexpr int kPacketLossPerc[] = {0, 1, 2, 5};
constexpr size_t kPacketLossPercSize = size(kPacketLossPerc);

constexpr int kLsbDepths[] = {8, 24};
constexpr size_t kLsbDepthsSize = size(kLsbDepths);

constexpr int kFrameDurations[] = {
    OPUS_FRAMESIZE_2_5_MS, OPUS_FRAMESIZE_5_MS,   OPUS_FRAMESIZE_10_MS,
    OPUS_FRAMESIZE_20_MS,  OPUS_FRAMESIZE_40_MS,  OPUS_FRAMESIZE_60_MS,
    OPUS_FRAMESIZE_80_MS,  OPUS_FRAMESIZE_100_MS, OPUS_FRAMESIZE_120_MS};
constexpr size_t kFrameDurationsSize = size(kFrameDurations);

#ifdef MULTISTREAM
#include "opus_multistream.h"
#define OPUS_ENC_DATA_TYPE OpusMSEncoder
#define OPUS_ENC_ENCODE_API opus_multistream_encode
#define OPUS_ENC_CTL_API opus_multistream_encoder_ctl
#define OPUS_ENC_CREATE_API ms_opus_encoder_create
#define OPUS_ENC_DESTROY_API opus_multistream_encoder_destroy
static OpusMSEncoder* ms_opus_encoder_create(opus_int32 sampleRate, int channels, int application,
                                             int* error) {
    unsigned char* mapping = (unsigned char*)malloc(sizeof(unsigned char) * channels);
    if (!mapping) {
        *error = 1;
        return nullptr;
    }
    for (unsigned char x = 0; x < channels; ++x) {
        mapping[x] = x;
    }
    OpusMSEncoder* enc = opus_multistream_encoder_create(sampleRate, channels, 1, channels - 1,
                                                         mapping, application, error);
    free(mapping);
    return enc;
}
#else
#define OPUS_ENC_DATA_TYPE OpusEncoder
#define OPUS_ENC_ENCODE_API opus_encode
#define OPUS_ENC_CTL_API opus_encoder_ctl
#define OPUS_ENC_CREATE_API opus_encoder_create
#define OPUS_ENC_DESTROY_API opus_encoder_destroy
#endif

enum {
    IDX_SAMPLE_RATE_INDEX = 0,
    IDX_CHANNEL,
    IDX_BIT_RATE_1,
    IDX_BIT_RATE_2,
    IDX_BIT_RATE_3,
    IDX_COMPLEXITY,
    IDX_APPLICATION,
    IDX_SET_DTX,
    IDX_SET_SIGNAL,
    IDX_SET_VBR,
    IDX_SET_VBR_CONSTRAINT,
    IDX_FORCE_CHANNEL_INDEX,
    IDX_SET_MAX_BANDWIDTH,
    IDX_SET_INBAND_FEC,
    IDX_SET_PACKET_LOSS_PERC,
    IDX_SET_LSB_DEPTH,
    IDX_SET_PREDICTION_DISABLED,
    IDX_FRAME_ENUM,
    IDX_LAST
};

template <typename type1, typename type2, typename type3>
auto generateNumberInRangeFromData(type1 data, type2 min, type3 max) -> decltype(max) {
    return (data % (1 + max - min)) + min;
}

class Codec {
   public:
    ~Codec() { deInitEncoder(); }
    bool initEncoder(uint8_t** dataPtr, size_t* sizePtr);
    void encodeFrames(const uint8_t* data, size_t size);
    void deInitEncoder();

   private:
    OPUS_ENC_DATA_TYPE* mEncoder = nullptr;
    int mChannels = 0;
    int mNumSamplesPerFrame = 0;
    size_t mFrameSize = 0;
    size_t mNumPcmBytesPerInputFrame = 0;
};

int get_frame_size(int frameSizeEnum, int samplingRate) {
    int frameSize = 0;
    switch (frameSizeEnum) {
        case OPUS_FRAMESIZE_2_5_MS:
            frameSize = samplingRate / 400;
            break;
        case OPUS_FRAMESIZE_5_MS:
            frameSize = samplingRate / 200;
            break;
        case OPUS_FRAMESIZE_10_MS:
            frameSize = samplingRate / 100;
            break;
        case OPUS_FRAMESIZE_20_MS:
            frameSize = samplingRate / 50;
            break;
        case OPUS_FRAMESIZE_40_MS:
            frameSize = samplingRate / 25;
            break;
        case OPUS_FRAMESIZE_60_MS:
            frameSize = 3 * samplingRate / 50;
            break;
        case OPUS_FRAMESIZE_80_MS:
            frameSize = 4 * samplingRate / 50;
            break;
        case OPUS_FRAMESIZE_100_MS:
            frameSize = 5 * samplingRate / 50;
            break;
        case OPUS_FRAMESIZE_120_MS:
            frameSize = 6 * samplingRate / 50;
            break;
        default:
            break;
    }
    return frameSize;
}

bool Codec::initEncoder(uint8_t** dataPtr, size_t* sizePtr) {
    uint8_t* data = *dataPtr;

    int sampleRateIndex = data[IDX_SAMPLE_RATE_INDEX] % kSampleRatesSize;
    opus_int32 sampleRate = kSampleRates[sampleRateIndex];

#ifdef MULTISTREAM
    mChannels = generateNumberInRangeFromData(data[IDX_CHANNEL], 1, 255);
#else
    int channelIndex = data[IDX_CHANNEL] % kChannelsSize;
    mChannels = kChannels[channelIndex];
#endif

    mNumSamplesPerFrame = sampleRate / kFrameDuration;
    mNumPcmBytesPerInputFrame = mChannels * mNumSamplesPerFrame * sizeof(int16_t);

    int application = kApplications[data[IDX_APPLICATION] % kApplicationsSize];
    int err = 0;
    mEncoder = OPUS_ENC_CREATE_API(sampleRate, mChannels, application, &err);
    if (err) {
        return false;
    }
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_APPLICATION(application));

    int complexityIndex = data[IDX_COMPLEXITY] % kComplexitiesSize;
    int complexity = kComplexities[complexityIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_COMPLEXITY(complexity));

    int setDTXIndex = data[IDX_SET_DTX] % kSetDTXSize;
    int setDTX = kSetDTX[setDTXIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_DTX(setDTX));

    int signalIndex = data[IDX_SET_SIGNAL] % kSignalsSize;
    int signal = kSignals[signalIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_SIGNAL(signal));

    int setVBRIndex = data[IDX_SET_VBR] % kSetVBRSize;
    int setVBR = kSetVBR[setVBRIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_VBR(setVBR));

    int setVBRConstraintIndex = data[IDX_SET_VBR_CONSTRAINT] % kSetVBRConstraintSize;
    int setVBRConstraint = kSetVBRConstraint[setVBRConstraintIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_VBR_CONSTRAINT(setVBRConstraint));

    // Clubbing 3 bytes of data to ensure bit rate in the range [kMinBitRate, kMaxBitRate]
    uint32_t tempValue =
        (data[IDX_BIT_RATE_1] << 16) | (data[IDX_BIT_RATE_2] << 8) | data[IDX_BIT_RATE_3];
    uint32_t bitRate = generateNumberInRangeFromData(tempValue, kMinBitRate, kMaxBitRate);
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_BITRATE(bitRate));

    int forceChanneIndex = data[IDX_FORCE_CHANNEL_INDEX] % kForceChannelsSize;
    int forceChannel = kForceChannels[forceChanneIndex];
    forceChannel = min(forceChannel, mChannels);
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_FORCE_CHANNELS(forceChannel));

    int maxBandwidthIndex = data[IDX_SET_MAX_BANDWIDTH] % kMaxBandwidthsSize;
    opus_int32 maxBandwidth = kMaxBandwidths[maxBandwidthIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_MAX_BANDWIDTH(maxBandwidth));

    int setInbandFecIndex = data[IDX_SET_INBAND_FEC] % kSetInbandFecSize;
    int setInbandFec = kSetInbandFec[setInbandFecIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_INBAND_FEC(setInbandFec));

    int pktLossIndex = data[IDX_SET_PACKET_LOSS_PERC] % kPacketLossPercSize;
    int pktLoss = kPacketLossPerc[pktLossIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_PACKET_LOSS_PERC(pktLoss));

    int lsbDepthIndex = data[IDX_SET_LSB_DEPTH] % kLsbDepthsSize;
    int lsbDepth = kLsbDepths[lsbDepthIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_LSB_DEPTH(lsbDepth));

    int setPredDisableIndex = data[IDX_SET_PREDICTION_DISABLED] % kSetPredDisableSize;
    int setPredDisable = kSetPredDisable[setPredDisableIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_PREDICTION_DISABLED(setPredDisable));

    int frameSizesEnumIndex = data[IDX_FRAME_ENUM] % kFrameDurationsSize;
    int frameSizeEnum = kFrameDurations[frameSizesEnumIndex];
    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_EXPERT_FRAME_DURATION(frameSizeEnum));

    mFrameSize = get_frame_size(frameSizeEnum, sampleRate);
    if (mFrameSize == 0) {
        return false;
    }

    // Not re-using the data which was used for configuration for encoding
    *dataPtr += IDX_LAST;
    *sizePtr -= IDX_LAST;
    return true;
}

void Codec::encodeFrames(const uint8_t* data, size_t size) {
    opus_int16* inputBuffer = (opus_int16*)data;
    size = size / sizeof(opus_int16);
    size_t offset = 0;
    do {
        size_t frameSize = mFrameSize / mChannels;
        if (frameSize > (size - offset)) {
            frameSize = size - offset;
        }
        unsigned char packet[kMaxPacket];
        (void)OPUS_ENC_ENCODE_API(mEncoder, &inputBuffer[offset], frameSize, packet, kMaxPacket);
        offset += mFrameSize * mChannels;
    } while (offset < size);
}

void Codec::deInitEncoder() {
    if (mEncoder) {
        OPUS_ENC_DESTROY_API(mEncoder);
        mEncoder = nullptr;
    }
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    if (size < IDX_LAST) {
        return 0;
    }
    Codec encoder;
    if (encoder.initEncoder(const_cast<uint8_t**>(&data), &size)) {
        encoder.encodeFrames(data, size);
    }
    return 0;
}