Image Sequence Support

* BMFF parser now reads moov box and sample table (stbl), which hold avifs image sequences
* libavif now reads 'avis' brands
* Split avifDecoderRead() into components for image sequences:
  * avifDecoderSetSource()
  * avifDecoderParse()
  * avifDecoderNextImage()
  * avifImageCopy()
  * avifDecoderReset()
* avifDecoderRead() still exists as a simple single-image path
* Added decoder and image timings for image sequences
* Refactored codec API to not require each codec to maintain per-plane decoder instances
* avifImage can now "not own" its planes and directly point at decoder planes to avoid copies
* aviffuzz attempts to decode all images in source material twice (using avifDecoderReset())
* Switch decoder->quality to explicit [minQuantizer, maxQuantizer], update assoc. constants
* Add examples to README
diff --git a/src/write.c b/src/write.c
index e24765b..8bfced6 100644
--- a/src/write.c
+++ b/src/write.c
@@ -28,7 +28,8 @@
     avifEncoder * encoder = (avifEncoder *)avifAlloc(sizeof(avifEncoder));
     memset(encoder, 0, sizeof(avifEncoder));
     encoder->maxThreads = 1;
-    encoder->quality = AVIF_BEST_QUALITY;
+    encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
+    encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
     return encoder;
 }
 
@@ -37,6 +38,15 @@
     avifFree(encoder);
 }
 
+static avifCodec * avifCodecCreateForEncode()
+{
+#ifdef AVIF_CODEC_AOM
+    return avifCodecCreateAOM();
+#else
+    return NULL;
+#endif
+}
+
 avifResult avifEncoderWrite(avifEncoder * encoder, avifImage * image, avifRawData * output)
 {
     if ((image->depth != 8) && (image->depth != 10) && (image->depth != 12)) {
@@ -46,14 +56,20 @@
     avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
     avifRawData colorOBU = AVIF_RAW_DATA_EMPTY;
     avifRawData alphaOBU = AVIF_RAW_DATA_EMPTY;
-    avifCodec * codec = NULL;
+    avifCodec * codec[AVIF_CODEC_PLANES_COUNT];
 
-#ifdef AVIF_CODEC_AOM
-    codec = avifCodecCreateAOM();
-#else
-    // Just bail out early, we're not surviving this function without an encoder compiled in
-    return AVIF_RESULT_NO_CODEC_AVAILABLE;
-#endif
+    codec[AVIF_CODEC_PLANES_COLOR] = avifCodecCreateForEncode();
+    if (!codec[AVIF_CODEC_PLANES_COLOR]) {
+        // Just bail out early, we're not surviving this function without an encoder compiled in
+        return AVIF_RESULT_NO_CODEC_AVAILABLE;
+    }
+
+    avifBool imageIsOpaque = avifImageIsOpaque(image);
+    if (imageIsOpaque) {
+        codec[AVIF_CODEC_PLANES_ALPHA] = NULL;
+    } else {
+        codec[AVIF_CODEC_PLANES_ALPHA] = avifCodecCreateForEncode();
+    }
 
     avifStream s;
     avifStreamStart(&s, output);
@@ -84,16 +100,23 @@
     // -----------------------------------------------------------------------
     // Encode AV1 OBUs
 
-    avifRawData * alphaOBUPtr = &alphaOBU;
-    if (avifImageIsOpaque(image)) {
-        alphaOBUPtr = NULL;
-    }
+    // avifRawData * alphaOBUPtr = &alphaOBU;
+    // if (avifImageIsOpaque(image)) {
+    //     alphaOBUPtr = NULL;
+    // }
 
-    avifResult encodeResult = codec->encodeImage(codec, image, encoder, &colorOBU, alphaOBUPtr);
-    if (encodeResult != AVIF_RESULT_OK) {
-        result = encodeResult;
+    if (!codec[AVIF_CODEC_PLANES_COLOR]->encodeImage(codec[AVIF_CODEC_PLANES_COLOR], image, encoder, &colorOBU, AVIF_FALSE)) {
+        result = AVIF_RESULT_ENCODE_COLOR_FAILED;
         goto writeCleanup;
     }
+
+    if (!imageIsOpaque) {
+        if (!codec[AVIF_CODEC_PLANES_ALPHA]->encodeImage(codec[AVIF_CODEC_PLANES_ALPHA], image, encoder, &alphaOBU, AVIF_TRUE)) {
+            result = AVIF_RESULT_ENCODE_ALPHA_FAILED;
+            goto writeCleanup;
+        }
+    }
+
     avifBool hasAlpha = (alphaOBU.size > 0) ? AVIF_TRUE : AVIF_FALSE;
 
     // -----------------------------------------------------------------------
@@ -257,7 +280,7 @@
             ipmaPush(&ipmaColor, ipcoIndex);
 
             avifCodecConfigurationBox colorConfig;
-            codec->getConfigurationBox(codec, AVIF_CODEC_PLANES_COLOR, &colorConfig);
+            codec[AVIF_CODEC_PLANES_COLOR]->getConfigurationBox(codec[AVIF_CODEC_PLANES_COLOR], &colorConfig);
             writeConfigBox(&s, &colorConfig);
             ++ipcoIndex;
             ipmaPush(&ipmaColor, ipcoIndex);
@@ -271,7 +294,7 @@
                 ipmaPush(&ipmaAlpha, ipcoIndex);
 
                 avifCodecConfigurationBox alphaConfig;
-                codec->getConfigurationBox(codec, AVIF_CODEC_PLANES_ALPHA, &alphaConfig);
+                codec[AVIF_CODEC_PLANES_ALPHA]->getConfigurationBox(codec[AVIF_CODEC_PLANES_ALPHA], &alphaConfig);
                 writeConfigBox(&s, &alphaConfig);
                 ++ipcoIndex;
                 ipmaPush(&ipmaAlpha, ipcoIndex);
@@ -353,8 +376,11 @@
     result = AVIF_RESULT_OK;
 
 writeCleanup:
-    if (codec) {
-        avifCodecDestroy(codec);
+    if (codec[AVIF_CODEC_PLANES_COLOR]) {
+        avifCodecDestroy(codec[AVIF_CODEC_PLANES_COLOR]);
+    }
+    if (codec[AVIF_CODEC_PLANES_ALPHA]) {
+        avifCodecDestroy(codec[AVIF_CODEC_PLANES_ALPHA]);
     }
     avifRawDataFree(&colorOBU);
     avifRawDataFree(&alphaOBU);