Commit 3a4f40a5 for libheif

commit 3a4f40a5edc480e3aa9e64bd8411fe36b10c5ad6
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Fri May 15 22:37:49 2026 +0200

    heif-info: show number of editlist repetitions (#1777)

diff --git a/examples/heif_info.cc b/examples/heif_info.cc
index d391abe6..1dd43107 100644
--- a/examples/heif_info.cc
+++ b/examples/heif_info.cc
@@ -1064,6 +1064,22 @@ int main(int argc, char** argv)
         std::cout << "  resolution: " << w << "x" << h << "\n";
       }

+      uint32_t repetitions = heif_track_get_number_of_repetitions(track);
+      std::cout << "  repetitions: ";
+      if (repetitions == 0) {
+        std::cout << "unsupported editlist\n";
+      }
+      else if (repetitions == heif_sequence_track_number_of_repetitions_infinite) {
+        std::cout << "infinite\n";
+      }
+      else {
+        std::cout << repetitions;
+        if (repetitions == 1) {
+          std::cout << " (single playback)";
+        }
+        std::cout << "\n";
+      }
+
       uint32_t sampleEntryType = heif_track_get_sample_entry_type_of_first_cluster(track);
       std::cout << "  sample entry type: " << heif_examples::fourcc_to_string(sampleEntryType) << "\n";

diff --git a/libheif/api/libheif/heif_sequences.h b/libheif/api/libheif/heif_sequences.h
index aee2e3cc..3637cba4 100644
--- a/libheif/api/libheif/heif_sequences.h
+++ b/libheif/api/libheif/heif_sequences.h
@@ -170,8 +170,9 @@ uint32_t heif_track_get_timescale(const heif_track*);
  * How many times the media segment should be played according to the track's edit list.
  *
  * Returns:
- *  - 0 if the edit list is absent or follows a pattern that libheif does not interpret
+ *  - 0 if an edit list box is present but follows a pattern libheif does not interpret
  *    as a loop count. Callers should fall back to a single playback in that case.
+ *  - 1 when no edit list is present. The media plays exactly once.
  *  - `heif_sequence_track_number_of_repetitions_infinite` (= UINT32_MAX) when the file
  *    signals indefinite playback (mvhd duration is the all-1s sentinel together with an
  *    editlist in repeat mode), or when the repetition count does not fit in uint32_t.
diff --git a/libheif/sequences/track.cc b/libheif/sequences/track.cc
index 9a5da292..e08923bb 100644
--- a/libheif/sequences/track.cc
+++ b/libheif/sequences/track.cc
@@ -1088,7 +1088,10 @@ Error Track::init_sample_timing_table()
   if (fallback) {
     m_presentation_timeline = media_timeline;
     m_num_output_samples = media_timeline.size();
-    m_num_repetitions = 0; // editlist absent or not understood
+    // No editlist box at all: the media plays exactly once.
+    // Editlist box present but its pattern isn't one libheif interprets: report
+    // 0 so callers know they should not rely on a repetition count.
+    m_num_repetitions = m_elst ? 0 : 1;
   }

   return {};
diff --git a/libheif/sequences/track.h b/libheif/sequences/track.h
index 494161ca..e7ec2beb 100644
--- a/libheif/sequences/track.h
+++ b/libheif/sequences/track.h
@@ -222,11 +222,12 @@ protected:
   std::vector<SampleTiming> m_presentation_timeline;
   uint64_t m_num_output_samples = 0; // Can be larger than the vector. It then repeats the playback.

-  // How many times the media timeline is repeated as dictated by the editlist.
-  // 0  = no editlist / editlist pattern not supported (caller should assume a single playback).
+  // How many times the media timeline is repeated.
+  // 0  = editlist is present but its pattern is not understood (caller should assume a single playback).
+  // 1  = no editlist: media plays exactly once.
   // UINT32_MAX = infinite (mvhd duration is the indefinite-sentinel and the editlist is in repeat mode).
   // N  = the media segment is played N times.
-  uint32_t m_num_repetitions = 0;
+  uint32_t m_num_repetitions = 1;

   // Continuous counting through all repetitions. You have to take the modulo operation to get the
   // index into m_presentation_timeline SampleTiming table.