You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
9.1 KiB
229 lines
9.1 KiB
/******************************************************************************
|
|
*
|
|
* 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 <math.h>
|
|
#include <stdlib.h>
|
|
#include <algorithm>
|
|
|
|
#include "FLAC/stream_encoder.h"
|
|
#include "audio_utils/primitives.h"
|
|
#include "share/compat.h"
|
|
|
|
constexpr int kMinSampleRate = 1;
|
|
constexpr int kFramesPerBlock = 1152;
|
|
constexpr int kMaxSampleRate = 655350;
|
|
constexpr uint8_t kMinNumChannels = 1;
|
|
constexpr uint8_t kMaxNumChannels = 2;
|
|
constexpr uint8_t kMinCompressionLevel = 0;
|
|
constexpr uint8_t kMaxCompressionLevel = 8;
|
|
|
|
enum Encoding { PCM_16, PCM_FLOAT };
|
|
|
|
enum {
|
|
IDX_SAMPLE_RATE_INDEX_1 = 0,
|
|
IDX_SAMPLE_RATE_INDEX_2,
|
|
IDX_SAMPLE_RATE_INDEX_3,
|
|
IDX_CHANNEL,
|
|
IDX_COMPRESSION_LEVEL,
|
|
IDX_PCM,
|
|
IDX_SET_VERIFY,
|
|
IDX_SET_STREAMABLE_SUBSET,
|
|
IDX_SET_DO_MID_SIDE_STEREO,
|
|
IDX_SET_LOOSE_MID_SIDE_STEREO,
|
|
IDX_SET_MAX_LPC_ORDER,
|
|
IDX_SET_COEFF_PRECISION,
|
|
IDX_SET_COEFF_PREC_SEARCH,
|
|
IDX_SET_DO_ESCAPE_CODING,
|
|
IDX_SET_DO_EXHAUSTIVE_MODEL_SEARCH,
|
|
IDX_SET_MIN_RESIDUAL_PARTITION_ORDER,
|
|
IDX_SET_MAX_RESIDUAL_PARTITION_ORDER,
|
|
IDX_SET_RICE_PARAMETER_SEARCH_DIST,
|
|
IDX_SET_TOTAL_SAMPLES_ESTIMATE,
|
|
IDX_LAST
|
|
};
|
|
|
|
class Codec {
|
|
public:
|
|
~Codec() { deInitEncoder(); }
|
|
bool initEncoder(uint8_t **dataPtr, size_t *sizePtr);
|
|
void encodeFrames(const uint8_t *data, size_t size);
|
|
void deInitEncoder();
|
|
static FLAC__StreamEncoderWriteStatus flacEncoderWriteCallback(
|
|
const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes,
|
|
unsigned samples, unsigned current_frame, void *client_data);
|
|
|
|
private:
|
|
bool verifyStateAndReturn();
|
|
FLAC__StreamEncoder *mFlacStreamEncoder = nullptr;
|
|
uint32_t mChannels = 0;
|
|
uint32_t mPcmEncodingInfo = 0;
|
|
FLAC__int32 mInputBufferPcm32[kFramesPerBlock * kMaxNumChannels] = {};
|
|
};
|
|
|
|
FLAC__StreamEncoderWriteStatus Codec::flacEncoderWriteCallback(const FLAC__StreamEncoder *encoder,
|
|
const FLAC__byte buffer[],
|
|
size_t bytes, unsigned samples,
|
|
unsigned current_frame,
|
|
void *client_data) {
|
|
(void)encoder;
|
|
(void)buffer;
|
|
(void)bytes;
|
|
(void)samples;
|
|
(void)current_frame;
|
|
(void)client_data;
|
|
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
|
|
}
|
|
|
|
bool Codec::verifyStateAndReturn() {
|
|
FLAC__StreamEncoderState state = FLAC__stream_encoder_get_state(mFlacStreamEncoder);
|
|
if (state == FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR) {
|
|
FLAC__stream_encoder_get_verify_decoder_state(mFlacStreamEncoder);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename type1, typename type2, typename type3>
|
|
auto generateNumberInRangeFromData(type1 data, type2 min, type3 max) -> decltype(max) {
|
|
return (data % (1 + max - min)) + min;
|
|
}
|
|
|
|
bool Codec::initEncoder(uint8_t **dataPtr, size_t *sizePtr) {
|
|
uint8_t *data = *dataPtr;
|
|
mFlacStreamEncoder = FLAC__stream_encoder_new();
|
|
if (!mFlacStreamEncoder) {
|
|
return false;
|
|
}
|
|
|
|
// Clubbing 3 bytes of data to ensure sample rate in the range [1, 655350]
|
|
uint32_t tempValue = (data[IDX_SAMPLE_RATE_INDEX_1] << 16) |
|
|
(data[IDX_SAMPLE_RATE_INDEX_2] << 8) | data[IDX_SAMPLE_RATE_INDEX_3];
|
|
uint32_t sampleRate = generateNumberInRangeFromData(tempValue, kMinSampleRate, kMaxSampleRate);
|
|
FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, sampleRate);
|
|
|
|
mChannels = generateNumberInRangeFromData(data[IDX_CHANNEL], kMinNumChannels, kMaxNumChannels);
|
|
FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mChannels);
|
|
|
|
int compression = generateNumberInRangeFromData(data[IDX_COMPRESSION_LEVEL],
|
|
kMinCompressionLevel, kMaxCompressionLevel);
|
|
FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, compression);
|
|
|
|
uint32_t pcmEncodingInfo =
|
|
generateNumberInRangeFromData(data[IDX_PCM], (int)PCM_16, (int)PCM_FLOAT);
|
|
mPcmEncodingInfo = pcmEncodingInfo;
|
|
uint32_t bitsPerSample = (mPcmEncodingInfo == PCM_FLOAT) ? 24 : 16;
|
|
FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, bitsPerSample);
|
|
|
|
int ver = data[IDX_SET_VERIFY] % 2;
|
|
FLAC__stream_encoder_set_verify(mFlacStreamEncoder, ver);
|
|
|
|
int streamableSubset = data[IDX_SET_STREAMABLE_SUBSET] % 2;
|
|
FLAC__stream_encoder_set_streamable_subset(mFlacStreamEncoder, streamableSubset);
|
|
|
|
int doMidSideStereo = data[IDX_SET_DO_MID_SIDE_STEREO] % 2;
|
|
FLAC__stream_encoder_set_do_mid_side_stereo(mFlacStreamEncoder, doMidSideStereo);
|
|
|
|
int looseMidSideStereo = data[IDX_SET_LOOSE_MID_SIDE_STEREO] % 2;
|
|
FLAC__stream_encoder_set_loose_mid_side_stereo(mFlacStreamEncoder, looseMidSideStereo);
|
|
|
|
int maxLpcOrder = data[IDX_SET_MAX_LPC_ORDER] % 2;
|
|
FLAC__stream_encoder_set_max_lpc_order(mFlacStreamEncoder, maxLpcOrder);
|
|
|
|
int coeffPrec = data[IDX_SET_COEFF_PRECISION] % 2;
|
|
FLAC__stream_encoder_set_qlp_coeff_precision(mFlacStreamEncoder, coeffPrec);
|
|
|
|
int coeffPrecSearch = data[IDX_SET_COEFF_PREC_SEARCH] % 2;
|
|
FLAC__stream_encoder_set_do_qlp_coeff_prec_search(mFlacStreamEncoder, coeffPrecSearch);
|
|
|
|
int escCoding = data[IDX_SET_DO_ESCAPE_CODING] % 2;
|
|
FLAC__stream_encoder_set_do_escape_coding(mFlacStreamEncoder, escCoding);
|
|
|
|
int exhaustiveModelSearch = data[IDX_SET_DO_EXHAUSTIVE_MODEL_SEARCH] % 2;
|
|
FLAC__stream_encoder_set_do_exhaustive_model_search(mFlacStreamEncoder, exhaustiveModelSearch);
|
|
|
|
int minResidualPartitionOrder = data[IDX_SET_MIN_RESIDUAL_PARTITION_ORDER] % 2;
|
|
FLAC__stream_encoder_set_min_residual_partition_order(mFlacStreamEncoder,
|
|
minResidualPartitionOrder);
|
|
|
|
int maxResidualPartitionOrder = data[IDX_SET_MAX_RESIDUAL_PARTITION_ORDER] % 2;
|
|
FLAC__stream_encoder_set_max_residual_partition_order(mFlacStreamEncoder,
|
|
maxResidualPartitionOrder);
|
|
|
|
int riceParam = data[IDX_SET_RICE_PARAMETER_SEARCH_DIST] % 2;
|
|
FLAC__stream_encoder_set_rice_parameter_search_dist(mFlacStreamEncoder, riceParam);
|
|
|
|
int totalSamplesEstimate = data[IDX_SET_TOTAL_SAMPLES_ESTIMATE] % 2;
|
|
FLAC__stream_encoder_set_total_samples_estimate(mFlacStreamEncoder, totalSamplesEstimate);
|
|
|
|
FLAC__StreamEncoderInitStatus status = FLAC__stream_encoder_init_stream(
|
|
mFlacStreamEncoder, flacEncoderWriteCallback /*write_callback*/, nullptr /*seek_callback*/,
|
|
nullptr /*tell_callback*/, nullptr /*metadata_callback*/, (void *)this /*client_data*/);
|
|
|
|
if (status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
|
|
return verifyStateAndReturn();
|
|
}
|
|
|
|
// 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) {
|
|
size_t sampleSize = (mPcmEncodingInfo == PCM_FLOAT) ? sizeof(float) : sizeof(int16_t);
|
|
size_t frameSize = mChannels * sampleSize;
|
|
do {
|
|
const size_t bytesConsumed = std::min(kFramesPerBlock * frameSize, size);
|
|
const unsigned inputFrames = bytesConsumed / frameSize;
|
|
const unsigned inputSamples = inputFrames * mChannels;
|
|
if (mPcmEncodingInfo == PCM_FLOAT) {
|
|
const float *const pcmFloat = reinterpret_cast<const float *>(data);
|
|
memcpy_to_q8_23_from_float_with_clamp(mInputBufferPcm32, pcmFloat, inputSamples);
|
|
} else {
|
|
const int16_t *const pcm16 = reinterpret_cast<const int16_t *>(data);
|
|
for (unsigned i = 0; i < inputSamples; ++i) {
|
|
mInputBufferPcm32[i] = (FLAC__int32)pcm16[i];
|
|
}
|
|
}
|
|
FLAC__stream_encoder_process_interleaved(mFlacStreamEncoder, mInputBufferPcm32,
|
|
inputFrames);
|
|
data += bytesConsumed;
|
|
size -= bytesConsumed;
|
|
} while (size > 0);
|
|
}
|
|
|
|
void Codec::deInitEncoder() {
|
|
if (mFlacStreamEncoder) {
|
|
FLAC__stream_encoder_finish(mFlacStreamEncoder);
|
|
FLAC__stream_encoder_delete(mFlacStreamEncoder);
|
|
mFlacStreamEncoder = 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;
|
|
}
|