Comment #3 on issue 334 by
yanyuet...@gmail.com: android native crash when used libwebp-0.5.2 online
https://bugs.chromium.org/p/webp/issues/detail?id=334#c3cannot reproduce locally now, the probability of occurrence is very small.I can provide the code follow, "doDecode" maybe be called from 4 threads simultaneously.
----------------------------------------------------------------------------------------------------------
static bool doDecode(JNIEnv *env, BaseStream &inStream, jobject options, uint8_t *output, jbyteArray jOutData) {
if (!output && jOutData) {
output = (uint8_t*)env->GetByteArrayElements(jOutData, 0);
size_t outLen = (size_t)env->GetArrayLength(jOutData);
NULL_RETURN_CHECK(output && outLen > 0, "get element failed from output jbytes");
}
bool ret = false;
uint8_t * chunk;
WebPIDecoder* idec;
volatile uint8_t cancelled = 0;
int status = VP8_STATUS_START_FEATURES;
int sampleSize = -1;
int width, height;
size_t appendSize;
bool neverDecode;
InputType iType;
WebPDecoderConfig* config = new WebPDecoderConfig();
if (!WebPInitDecoderConfig(config)) {
DBE("init WebPDecoderConfig failed");
goto ENDING;
}
width = getIntField(env, options, gOptions_outWidthFieldID);
height = getIntField(env, options, gOptions_outHeightFieldID);
neverDecode = width <= 0 || height <= 0;
if(neverDecode) {
jbyteArray bufferArray = static_cast<jbyteArray>(env->GetObjectField(options, gOptions_headerBufferFieldID));
if (bufferArray) {
uint8_t* bufferAddr = (uint8_t*)env->GetByteArrayElements(bufferArray, 0);
size_t bufferLen = (size_t)env->GetArrayLength(bufferArray);
if (bufferAddr && bufferLen >= REQUEST_MIN_HEADER_BUFFER_SIZE) {
status = WebPGetFeatures(bufferAddr, REQUEST_MIN_HEADER_BUFFER_SIZE, &config->input);
env->ReleaseByteArrayElements(bufferArray, (jbyte*)bufferAddr, 0);
}
}
if (status != VP8_STATUS_OK) {
uint8_t * header;
int size = inStream.nextChunk(&header, REQUEST_MIN_HEADER_BUFFER_SIZE);
if (size > 0) {
status = WebPGetFeatures(header, (size_t) size, &config->input);
inStream.freeChunk(header);
}
inStream.rewind();
if (env->ExceptionCheck()) {
goto ENDING;
}
}
if(status != VP8_STATUS_OK) {
DBE("get webp features failed, err=%d", status);
goto ENDING;
}
}
if (neverDecode) {
width = config->input.width;
height = config->input.height;
sampleSize = getIntField(env, options, gOptions_sampleSizeFieldID);
if (sampleSize > 1) {
width = width / sampleSize;
height = height * width / config->input.width;
}
env->SetIntField(options, gOptions_outWidthFieldID, width);
env->SetIntField(options, gOptions_outHeightFieldID, height);
env->SetBooleanField(options, gOptions_outAlphaFieldID, (jboolean) config->input.has_alpha);
}
ret = getBoolField(env, options, gOptions_justBoundsFieldID);
if (ret || !output) {
goto ENDING;
}
if (sampleSize < 0) {
sampleSize = getIntField(env, options, gOptions_sampleSizeFieldID);
}
if (sampleSize > 1) {
config->options.use_scaling = 1;
config->options.scaled_width = width;
config->options.scaled_height = height;
}
config->options.bypass_filtering = 1;
config->options.no_fancy_upsampling = 0;
config->options.use_threads = 1;
config->output.colorspace = MODE_rgbA;
config->output.is_external_memory = 1;
config->output.u.RGBA.rgba = output;
config->output.u.RGBA.stride = width * 4;
config->output.u.RGBA.size = (size_t) (config->output.u.RGBA.stride * height);
status = VP8_STATUS_START_DECODE;
env->SetLongField(options, gOptions_cancelledPtrFieldID, reinterpret_cast<jlong>(&cancelled));
iType = inStream.inputType();
if (iType == BYTE_ARRAY) {
int size = inStream.nextChunk(&chunk, inStream.available());
if (size > 0) {
status = WebPDecode(chunk, (size_t) size, config);
} else {
DBE("byte array stream available %d", size);
}
} else {
idec = WebPIDecode(NULL, 0, config);
if (idec) {
if (iType == FILE_DESCRIPTOR) {
appendSize = FD_APPEND_CHUNK_SIZE;
} else {
appendSize = STREAM_APPEND_CHUNK_SIZE;
}
chunk = (uint8_t*) calloc(appendSize, sizeof(char));
if (chunk) {
int bytesRead;
do {
bytesRead = inStream.nextChunk(chunk, appendSize);
if (bytesRead < 0) {
break;
}
if (bytesRead > 0) {
//DBD("append decode chunk size=%d", bytesRead);
status = WebPIAppend(idec, chunk, (size_t) bytesRead);
//DBD("append decode result status=%d", status);
}
if (cancelled) {
status = VP8_STATUS_USER_ABORT;
DBW("decoding was cancelled in progress");
break;
}
if (VP8_STATUS_OK == status) { // complete decoding
break;
}
} while (VP8_STATUS_SUSPENDED == status || bytesRead == 0);
free(chunk);
} else {
DBE("calloc new memory failed");
}
WebPIDelete(idec);
} else {
DBE("init webp idec failed");
}
}
if (!cancelled) {
env->SetLongField(options, gOptions_cancelledPtrFieldID, 0);
}
ret = status == VP8_STATUS_OK;
if(!ret) {
DBE("decode webp failed, err=%d, type=%d", status, iType);
}
ENDING:
if (jOutData != NULL) {
env->ReleaseByteArrayElements(jOutData, (jbyte*)output, 0);
}
delete config;
return ret;