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]
