blob: 4cb1716907f6d5f2fc624a1adcbc0b92ad46a2ec [file] [log] [blame]
// Copyright 2022 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
package org.aomedia.avif.android;
import android.graphics.Bitmap;
import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
/**
* An AVIF Decoder. AVIF Specification: https://aomediacodec.github.io/av1-avif/.
*
* <p>There are two ways to use this class.
*
* <p>1) As a static utility class.
*
* <p>This class can be accessed statically without instantiating an object. This is useful to
* simply sniff and decode still AVIF images without having to maintain any decoder state. The
* following are the methods that can be accessed this way: {@link isAvifImage}, {@link getInfo} and
* {@link decode}. The {@link Info} inner class is used only in this case.
*
* <p>2) As an instantiated regular class.
*
* <p>When used this way, the {@link create} method must be used to create an instance of this class
* with a valid AVIF image. This will create a long running underlying decoder object which will be
* used to decode the image(s). Using the returned object, other public methods of the class can be
* called to get information about the image and to get the individual decoded frames. When the
* decoder object is no longer needed, {@link release} must be called to release the underlying
* decoder.
*
* <p>This is useful for decoding animated AVIF images and obtaining each decoded frame one after
* the other.
*
* <p>NOTE: The API for using this as an instantiated regular class is still under development and
* might change.
*/
@SuppressWarnings("CatchAndPrintStackTrace")
public class AvifDecoder {
static {
try {
System.loadLibrary("avif_android");
} catch (UnsatisfiedLinkError exception) {
exception.printStackTrace();
}
}
private long decoder;
private int width;
private int height;
private int depth;
private boolean alphaPresent;
private int frameCount;
private int repetitionCount;
private double[] frameDurations;
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(). */
public static class Info {
public int width;
public int height;
public int depth;
public boolean alphaPresent;
}
/**
* Returns true if the bytes in the buffer seem like an AVIF image.
*
* @param buffer The encoded image. buffer.position() must be 0.
* @return true if the bytes seem like an AVIF image, false otherwise.
*/
public static boolean isAvifImage(ByteBuffer buffer) {
return AvifDecoder.isAvifImage(buffer, buffer.remaining());
}
private static native boolean isAvifImage(ByteBuffer encoded, int length);
/**
* Parses the AVIF header and populates the Info.
*
* @param encoded The encoded AVIF image. encoded.position() must be 0.
* @param length Length of the encoded buffer.
* @param info Output parameter whose fields will be populated.
* @return true on success and false on failure.
*/
public static native boolean getInfo(ByteBuffer encoded, int length, Info info);
/**
* Decodes the AVIF image into the bitmap.
*
* @param encoded The encoded AVIF image. encoded.position() must be 0.
* @param length Length of the encoded buffer.
* @param bitmap The decoded pixels will be copied into the bitmap.
* @return true on success and false on failure. A few possible reasons for failure are: 1) Input
* was not valid AVIF. 2) Bitmap was not large enough to store the decoded image.
*/
public static boolean decode(ByteBuffer encoded, int length, Bitmap bitmap) {
return decode(encoded, length, bitmap, 0);
}
/**
* Decodes the AVIF image into the bitmap.
*
* @param encoded The encoded AVIF image. encoded.position() must be 0.
* @param length Length of the encoded buffer.
* @param bitmap The decoded pixels will be copied into the bitmap.
* @param threads Number of threads to be used for the AVIF decode. 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 true on success and false on failure. A few possible reasons for failure are: 1) Input
* was not valid AVIF. 2) Bitmap was not large enough to store the decoded image. 3) Negative
* value was passed for the threads parameter.
*/
public static native boolean decode(ByteBuffer encoded, int length, Bitmap bitmap, int threads);
/** Get the width of the image. */
public int getWidth() {
return width;
}
/** Get the height of the image. */
public int getHeight() {
return height;
}
/** Get the depth (bit depth) of the image. */
public int getDepth() {
return depth;
}
/** Returns true if the image contains a transparency/alpha channel, false otherwise. */
public boolean getAlphaPresent() {
return alphaPresent;
}
/** Get the number of frames in the image. */
public int getFrameCount() {
return frameCount;
}
/**
* Get the number of repetitions for an animated image (see repetitionCount in avif.h for
* details).
*/
public int getRepetitionCount() {
return repetitionCount;
}
/** Get the duration for each frame in the image. */
public double[] getFrameDurations() {
return frameDurations;
}
/** Releases the underlying decoder object. */
public void release() {
if (decoder != 0) {
destroyDecoder(decoder);
}
decoder = 0;
}
/**
* Create and return an AvifDecoder.
*
* @param encoded The encoded AVIF image. encoded.position() must be 0. The memory of this
* ByteBuffer must be kept alive until release() is called.
* @return null on failure. AvifDecoder object on success.
*/
@Nullable
public static AvifDecoder create(ByteBuffer encoded) {
return create(encoded, /* threads= */ 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;
}
/**
* Decodes the next frame of the animated AVIF into the bitmap.
*
* @param bitmap The decoded pixels will be copied into the bitmap.
* @return 0 (AVIF_RESULT_OK) on success and some other avifStatus on failure. For a list of all
* possible status codes, see the avifResult enum on avif.h in libavif's C source code. A
* String describing the return value can be obtained by calling {@link resultToString} with
* the return value of this function.
*/
public int nextFrame(Bitmap bitmap) {
return nextFrame(decoder, bitmap);
}
private native int nextFrame(long decoder, Bitmap bitmap);
/**
* Get the 0-based index of the frame that will be returned by the next call to {@link nextFrame}.
* If the returned value is same as {@link getFrameCount}, then the next call to {@link nextFrame}
* will fail.
*/
public int nextFrameIndex() {
return nextFrameIndex(decoder);
}
private native int nextFrameIndex(long decoder);
/**
* Decodes the nth frame of the animated AVIF into the bitmap.
*
* <p>Note that calling this method will change the behavior of subsequent calls to {@link
* nextFrame}. {@link nextFrame} will start outputting the frame after this one.
*
* @param bitmap The decoded pixels will be copied into the bitmap.
* @param n The zero-based index of the frame to be decoded.
* @return 0 (AVIF_RESULT_OK) on success and some other avifStatus on failure. For a list of all
* possible status codes, see the avifResult enum on avif.h in libavif's C source code. A
* String describing the return value can be obtained by calling {@link resultToString} with
* the return value of this function.
*/
public int nthFrame(int n, Bitmap bitmap) {
return nthFrame(decoder, n, bitmap);
}
private native int nthFrame(long decoder, int n, Bitmap bitmap);
/**
* Returns a String describing an avifResult enum value.
*
* @param result The avifResult value. Typically this is the return value of {@link nextFrame} or
* {@link nthFrame}.
* @return A String containing the description of the avifResult.
*/
public static native String resultToString(int result);
/**
* Returns a String that contains information about the libavif version, underlying codecs and
* libyuv version (if available).
*/
public static native String versionString();
private native long createDecoder(ByteBuffer encoded, int length, int threads);
private native void destroyDecoder(long decoder);
}