This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit bfcf9fcb3735a62648a968caad55d6b37c9998a2
Author:     Romain Beauxis <[email protected]>
AuthorDate: Sun Aug 2 11:40:58 2026 -0500
Commit:     Romain Beauxis <[email protected]>
CommitDate: Fri Aug 21 03:38:24 2026 +0000

    avformat: factor out iTunSMPB parsing
    
    Apple encoders store gapless playback information in this tag, in the
    metadata of mp4 files and, as an ID3v2 comment, of mp3 files, so move the
    parsing to a helper both demuxers can share.
    
    The sample counts are widened to the 64 bits the tag can write, scanned
    unsigned as it writes them, and rejected past what can be a sample count,
    so that callers can add and rescale the three without overflowing.
---
 libavformat/demux.h       | 22 ++++++++++++++++++++++
 libavformat/demux_utils.c | 32 ++++++++++++++++++++++++++++++++
 libavformat/mov.c         |  7 ++++---
 3 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/libavformat/demux.h b/libavformat/demux.h
index f09afc849f..73ecd494b8 100644
--- a/libavformat/demux.h
+++ b/libavformat/demux.h
@@ -384,6 +384,28 @@ int ff_get_extradata(void *logctx, AVCodecParameters *par, 
AVIOContext *pb, int
  */
 int ff_find_stream_index(const AVFormatContext *s, int id);
 
+/**
+ * Parse the gapless playback information Apple encoders store in an iTunSMPB
+ * tag. The tag is a list of hexadecimal fields; the ones of interest are
+ *
+ *   <reserved> <priming> <remainder> <valid samples>
+ *
+ * The decoded stream holds the three sample counts back to back, the priming
+ * and remainder samples being encoder artifacts.
+ *
+ * The counts are returned small enough to be added and rescaled without
+ * overflowing.
+ *
+ * @param value     tag contents
+ * @param priming   number of samples to discard at the start of the stream
+ * @param remainder number of samples to discard at the end of the stream
+ * @param samples   number of valid samples between the two
+ * @return >= 0 if OK, AVERROR_INVALIDDATA if the tag could not be parsed or
+ *         holds counts too large to be sample counts
+ */
+int ff_itunes_parse_smpb(const char *value, int64_t *priming,
+                         int64_t *remainder, int64_t *samples);
+
 int ff_buffer_packet(AVFormatContext *s, AVPacket *pkt);
 
 #endif /* AVFORMAT_DEMUX_H */
diff --git a/libavformat/demux_utils.c b/libavformat/demux_utils.c
index 0f22304e56..b1fb2726f6 100644
--- a/libavformat/demux_utils.c
+++ b/libavformat/demux_utils.c
@@ -19,9 +19,12 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include <inttypes.h>
+
 #include "libavutil/mem.h"
 
 #include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
 #include "libavcodec/bytestream.h"
 #include "packet_internal.h"
 #include "avformat.h"
@@ -360,3 +363,32 @@ int ff_find_stream_index(const AVFormatContext *s, int id)
             return i;
     return -1;
 }
+
+/* Over nine months at 44.1 kHz; keeps sums and rescalings in range. */
+#define SMPB_MAX_SAMPLES (INT64_C(1) << 40)
+
+int ff_itunes_parse_smpb(const char *value, int64_t *priming,
+                         int64_t *remainder, int64_t *samples)
+{
+    uint64_t reserved, p, r, s;
+    int end[4];
+
+    /* 16 digits is the widest an uint64_t can take, and what the tag writes. 
*/
+    if (sscanf(value, "%16"SCNx64"%n %16"SCNx64"%n %16"SCNx64"%n 
%16"SCNx64"%n",
+               &reserved, &end[0], &p, &end[1], &r, &end[2], &s, &end[3]) != 4)
+        return AVERROR_INVALIDDATA;
+
+    /* A field cut short at 16 digits would shift every field after it. */
+    for (int i = 0; i < 4; i++)
+        if (av_isxdigit(value[end[i]]))
+            return AVERROR_INVALIDDATA;
+
+    if (p > SMPB_MAX_SAMPLES || r > SMPB_MAX_SAMPLES || s > SMPB_MAX_SAMPLES)
+        return AVERROR_INVALIDDATA;
+
+    *priming   = p;
+    *remainder = r;
+    *samples   = s;
+
+    return 0;
+}
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 8318082471..f53ce693f8 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -5819,8 +5819,8 @@ static int mov_read_custom(MOVContext *c, AVIOContext 
*pb, MOVAtom atom)
 
     if (mean && key && val) {
         if (strcmp(key, "iTunSMPB") == 0) {
-            int priming, remainder, samples;
-            if(sscanf(val, "%*X %X %X %X", &priming, &remainder, &samples) == 
3){
+            int64_t priming, remainder, samples;
+            if (ff_itunes_parse_smpb(val, &priming, &remainder, &samples) >= 
0) {
                 if(priming>0 && priming<16384)
                     st->codecpar->initial_padding = priming = 
av_rescale_q(priming, st->time_base,
                                                                            
(AVRational){ 1, st->codecpar->sample_rate });
@@ -5836,7 +5836,8 @@ static int mov_read_custom(MOVContext *c, AVIOContext 
*pb, MOVAtom atom)
                         ffstream(st)->last_discard_sample = duration;
                     }
                 }
-                av_log(c->fc, AV_LOG_DEBUG, "Parsed iTunSMPB: priming %d, 
remainder %d samples %d\n",
+                av_log(c->fc, AV_LOG_DEBUG, "Parsed iTunSMPB: priming 
%"PRId64", "
+                       "remainder %"PRId64" samples %"PRId64"\n",
                        priming, remainder, samples);
             }
         }

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to