android_jni: Make threads configurable
Add a variant of AvifDecoder.create that lets the users pass number
of threads explicitly.
GOOGLE_INTERNAL_CL: 525519742
diff --git a/android_jni/avifandroidjni/src/androidTest/java/org/aomedia/avif/android/AnimatedImageTest.java b/android_jni/avifandroidjni/src/androidTest/java/org/aomedia/avif/android/AnimatedImageTest.java
index 5851452..a368bf3 100644
--- a/android_jni/avifandroidjni/src/androidTest/java/org/aomedia/avif/android/AnimatedImageTest.java
+++ b/android_jni/avifandroidjni/src/androidTest/java/org/aomedia/avif/android/AnimatedImageTest.java
@@ -32,6 +32,7 @@
public final int frameCount;
public final int repetitionCount;
public final double frameDuration;
+ public final int threads;
public Image(
String filename,
@@ -41,7 +42,8 @@
boolean alphaPresent,
int frameCount,
int repetitionCount,
- double frameDuration) {
+ double frameDuration,
+ int threads) {
this.filename = filename;
this.width = width;
this.height = height;
@@ -50,14 +52,15 @@
this.frameCount = frameCount;
this.repetitionCount = repetitionCount;
this.frameDuration = frameDuration;
+ this.threads = threads;
}
}
private static final Image[] IMAGES = {
// Parameter ordering: filename, width, height, depth, alphaPresent, frameCount,
- // repetitionCount, frameDuration.
- new Image("alpha_video.avif", 640, 480, 8, true, 48, -2, 0.04),
- new Image("Chimera-AV1-10bit-480x270.avif", 480, 270, 10, false, 95, -2, 0.04),
+ // repetitionCount, frameDuration, threads.
+ new Image("alpha_video.avif", 640, 480, 8, true, 48, -2, 0.04, 1),
+ new Image("Chimera-AV1-10bit-480x270.avif", 480, 270, 10, false, 95, -2, 0.04, 2),
};
private static final String ASSET_DIRECTORY = "animated_avif";
@@ -88,7 +91,7 @@
public void testAnimatedAvifDecode() throws IOException {
ByteBuffer buffer = getBuffer();
assertThat(buffer).isNotNull();
- AvifDecoder decoder = AvifDecoder.create(buffer);
+ AvifDecoder decoder = AvifDecoder.create(buffer, image.threads);
assertThat(decoder).isNotNull();
assertThat(decoder.getWidth()).isEqualTo(image.width);
assertThat(decoder.getHeight()).isEqualTo(image.height);
diff --git a/android_jni/avifandroidjni/src/main/java/org/aomedia/avif/android/AvifDecoder.java b/android_jni/avifandroidjni/src/main/java/org/aomedia/avif/android/AvifDecoder.java
index 55633fe..8ada59c 100644
--- a/android_jni/avifandroidjni/src/main/java/org/aomedia/avif/android/AvifDecoder.java
+++ b/android_jni/avifandroidjni/src/main/java/org/aomedia/avif/android/AvifDecoder.java
@@ -53,8 +53,8 @@
private int repetitionCount;
private double[] frameDurations;
- private AvifDecoder(ByteBuffer encoded) {
- decoder = createDecoder(encoded, encoded.remaining());
+ private AvifDecoder(ByteBuffer encoded, int threads) {
+ decoder = createDecoder(encoded, encoded.remaining(), threads);
}
/** Contains information about the AVIF Image. This class is only used for getInfo(). */
@@ -171,7 +171,23 @@
*/
@Nullable
public static AvifDecoder create(ByteBuffer encoded) {
- AvifDecoder decoder = new AvifDecoder(encoded);
+ return create(encoded, 1);
+ }
+
+ /**
+ * Create and return an AvifDecoder with the specified number of threads.
+ *
+ * @param encoded The encoded AVIF image. encoded.position() must be 0. The memory of this
+ * ByteBuffer must be kept alive until release() is called.
+ * @param threads Number of threads to be used by the decoder. Zero means use number of CPU cores
+ * as the thread count. Negative values are invalid. When this value is > 0, it is simply
+ * mapped to the maxThreads parameter in libavif. For more details, see the documentation for
+ * maxThreads variable in avif.h.
+ * @return null on failure. AvifDecoder object on success.
+ */
+ @Nullable
+ public static AvifDecoder create(ByteBuffer encoded, int threads) {
+ AvifDecoder decoder = new AvifDecoder(encoded, threads);
return (decoder.decoder == 0) ? null : decoder;
}
@@ -205,7 +221,7 @@
private native boolean nthFrame(long decoder, int n, Bitmap bitmap);
- private native long createDecoder(ByteBuffer encoded, int length);
+ private native long createDecoder(ByteBuffer encoded, int length, int threads);
private native void destroyDecoder(long decoder);
}
diff --git a/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc b/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc
index a3f887e..173ad39 100644
--- a/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc
+++ b/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc
@@ -152,6 +152,10 @@
return AvifImageToBitmap(env, decoder, bitmap);
}
+int getThreadCount(int threads) {
+ return (threads == 0) ? android_getCpuCount() : threads;
+}
+
} // namespace
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
@@ -199,15 +203,18 @@
const uint8_t* const buffer =
static_cast<const uint8_t*>(env->GetDirectBufferAddress(encoded));
AvifDecoderWrapper decoder;
- if (!CreateDecoderAndParse(
- &decoder, buffer, length,
- (threads == 0) ? android_getCpuCount() : threads)) {
+ if (!CreateDecoderAndParse(&decoder, buffer, length,
+ getThreadCount(threads))) {
return false;
}
return DecodeNextImage(env, &decoder, bitmap);
}
-FUNC(jlong, createDecoder, jobject encoded, int length) {
+FUNC(jlong, createDecoder, jobject encoded, jint length, jint threads) {
+ if (threads < 0) {
+ LOGE("Invalid value for threads (%d).", threads);
+ return 0;
+ }
const uint8_t* const buffer =
static_cast<const uint8_t*>(env->GetDirectBufferAddress(encoded));
std::unique_ptr<AvifDecoderWrapper> decoder(new (std::nothrow)
@@ -215,8 +222,8 @@
if (decoder == nullptr) {
return 0;
}
- // TODO(b/272577342): Make threads configurable.
- if (!CreateDecoderAndParse(decoder.get(), buffer, length, /*threads=*/1)) {
+ if (!CreateDecoderAndParse(decoder.get(), buffer, length,
+ getThreadCount(threads))) {
return 0;
}
const jclass avif_decoder_class =