diff options
Diffstat (limited to 'fuzzer/opus_enc_fuzzer.cpp')
-rw-r--r-- | fuzzer/opus_enc_fuzzer.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/fuzzer/opus_enc_fuzzer.cpp b/fuzzer/opus_enc_fuzzer.cpp new file mode 100644 index 00000000..c2258fde --- /dev/null +++ b/fuzzer/opus_enc_fuzzer.cpp @@ -0,0 +1,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; +} |