| /****************************************************************************** |
| * |
| * 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; |
| } |