Added avifDecoderNthImageTiming() for querying frame timing without needing to decode the frame
diff --git a/src/read.c b/src/read.c
index 60f2b23..04b131a 100644
--- a/src/read.c
+++ b/src/read.c
@@ -2280,20 +2280,46 @@
     if (decoder->data->sourceSampleTable) {
         // Decoding from a track! Provide timing information.
 
-        decoder->imageTiming.timescale = decoder->timescale;
-        decoder->imageTiming.ptsInTimescales = 0;
-        for (int imageIndex = 0; imageIndex < decoder->imageIndex; ++imageIndex) {
-            decoder->imageTiming.ptsInTimescales += avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, imageIndex);
+        avifResult timingResult = avifDecoderNthImageTiming(decoder, decoder->imageIndex, &decoder->imageTiming);
+        if (timingResult != AVIF_RESULT_OK) {
+            return timingResult;
         }
-        decoder->imageTiming.durationInTimescales = avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, decoder->imageIndex);
+    }
+    return AVIF_RESULT_OK;
+}
 
-        if (decoder->imageTiming.timescale > 0) {
-            decoder->imageTiming.pts = (double)decoder->imageTiming.ptsInTimescales / (double)decoder->imageTiming.timescale;
-            decoder->imageTiming.duration = (double)decoder->imageTiming.durationInTimescales / (double)decoder->imageTiming.timescale;
-        } else {
-            decoder->imageTiming.pts = 0.0;
-            decoder->imageTiming.duration = 0.0;
-        }
+avifResult avifDecoderNthImageTiming(avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming)
+{
+    if (!decoder->data) {
+        // Nothing has been parsed yet
+        return AVIF_RESULT_NO_CONTENT;
+    }
+
+    if ((int)frameIndex >= decoder->imageCount) {
+        // Impossible index
+        return AVIF_RESULT_NO_IMAGES_REMAINING;
+    }
+
+    if (!decoder->data->sourceSampleTable) {
+        // There isn't any real timing associated with this decode, so
+        // just hand back the defaults chosen in avifDecoderReset().
+        memcpy(outTiming, &decoder->imageTiming, sizeof(avifImageTiming));
+        return AVIF_RESULT_OK;
+    }
+
+    decoder->imageTiming.timescale = decoder->timescale;
+    decoder->imageTiming.ptsInTimescales = 0;
+    for (int imageIndex = 0; imageIndex < (int)frameIndex; ++imageIndex) {
+        decoder->imageTiming.ptsInTimescales += avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, imageIndex);
+    }
+    decoder->imageTiming.durationInTimescales = avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, frameIndex);
+
+    if (decoder->imageTiming.timescale > 0) {
+        decoder->imageTiming.pts = (double)decoder->imageTiming.ptsInTimescales / (double)decoder->imageTiming.timescale;
+        decoder->imageTiming.duration = (double)decoder->imageTiming.durationInTimescales / (double)decoder->imageTiming.timescale;
+    } else {
+        decoder->imageTiming.pts = 0.0;
+        decoder->imageTiming.duration = 0.0;
     }
     return AVIF_RESULT_OK;
 }