Commit 6eb1078add for ffmpeg

commit 6eb1078add7c01a165801bd5bda0cc1a982e3385
Author: romainhedouin <romain.hedouin@epitech.eu>
Date:   Wed Oct 21 18:06:37 2026 +0200

    fate/mpegts: add a test for the PID check in mpegts_probe()

    analyze() counts a sync byte only if it starts a null packet (PID
    0x1FFF) or if its adaptation field control is not 0. The new test builds
    packet sequences by hand and prints the score of the mpegts probe: null
    packets with a zero adaptation field control behind a payload packet and
    the other way round, a mix of both, 188, 192 and 204 byte spacing, and
    packets behind 5 bytes which are not part of the stream.

    It fails without the previous commit, which reads the PID at the current
    sync byte instead of at the start of the buffer. No sample is needed.

    Found during a code review sweep of ffmpeg using an LLM.

    Signed-off-by: romainhedouin <romain.hedouin@epitech.eu>

diff --git a/libavformat/Makefile b/libavformat/Makefile
index e479c5c67c..17da02eee7 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -806,6 +806,7 @@ TESTPROGS-$(CONFIG_SRTP)                 += srtp
 TESTPROGS-$(CONFIG_IMF_DEMUXER)          += imf
 TESTPROGS-$(CONFIG_MP3_MUXER)            += id3v2enc id3v2chap
 TESTPROGS-$(CONFIG_AU_MUXER)             += auenc
+TESTPROGS-$(CONFIG_MPEGTS_DEMUXER)       += mpegts_probe
 TESTPROGS-$(CONFIG_OGG_MUXER)            += ogg_pages

 TOOLS     = aviocat                                                     \
diff --git a/libavformat/tests/.gitignore b/libavformat/tests/.gitignore
index dddaeea25f..b5fd1ff009 100644
--- a/libavformat/tests/.gitignore
+++ b/libavformat/tests/.gitignore
@@ -3,6 +3,7 @@
 /id3v2enc
 /fifo_muxer
 /imf
+/mpegts_probe
 /mkdir
 /noproxy
 /rename
diff --git a/libavformat/tests/mpegts_probe.c b/libavformat/tests/mpegts_probe.c
new file mode 100644
index 0000000000..b45c46cd1f
--- /dev/null
+++ b/libavformat/tests/mpegts_probe.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Feed hand made packet sequences to the MPEG-TS probe.
+ *
+ * analyze() only counts the sync bytes of null packets (PID 0x1FFF) and of
+ * packets whose adaptation field control (afc) is not 0. Packets are given as
+ * a string with one character each, see put_packet(). The score tells which
+ * packets were counted: 100 if all of them, 0 if (almost) none.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libavutil/mem.h"
+#include "libavformat/avformat.h"
+#include "libavformat/demux.h"
+
+extern const FFInputFormat ff_mpegts_demuxer;
+
+#define NB_PACKETS   12
+#define MAX_PREFIX   8
+#define MAX_STRIDE   204
+
+/*
+ * D: PID 0x100, adaptation field control 1 (payload only)
+ * d: PID 0x100, adaptation field control 0 (reserved)
+ * N: PID 0x1FFF, adaptation field control 1
+ * n: PID 0x1FFF, adaptation field control 0
+ * L: PID 0x1FF, adaptation field control 0 (low byte of the null PID)
+ * H: PID 0x1F00, adaptation field control 0 (high byte of the null PID)
+ */
+static void put_packet(uint8_t *p, char type)
+{
+    int pid = 0x1FFF;
+    int afc = (type == 'D' || type == 'N') ? 1 : 0;
+
+    if (type == 'D' || type == 'd')
+        pid = 0x100;
+    else if (type == 'L')
+        pid = 0x1FF;
+    else if (type == 'H')
+        pid = 0x1F00;
+
+    p[0] = 0x47;
+    p[1] = pid >> 8;
+    p[2] = pid & 0xFF;
+    p[3] = afc << 4;
+}
+
+static void test(const char *name, int prefix_size, int prefix_fill,
+                 int stride, const char *packets)
+{
+    uint8_t *buf = av_mallocz(MAX_PREFIX + NB_PACKETS * MAX_STRIDE +
+                              AVPROBE_PADDING_SIZE);
+    AVProbeData pd = { .buf = buf };
+    int i;
+
+    if (!buf)
+        return;
+
+    /* Neither the filler nor the padding contains a sync byte. */
+    memset(buf, prefix_fill, prefix_size);
+    memset(buf + prefix_size, 0xFF, NB_PACKETS * stride);
+    for (i = 0; i < NB_PACKETS; i++)
+        put_packet(buf + prefix_size + i * stride, packets[i]);
+    pd.buf_size = prefix_size + NB_PACKETS * stride;
+
+    printf("%s: %d\n", name, ff_mpegts_demuxer.read_probe(&pd));
+
+    av_free(buf);
+}
+
+int main(void)
+{
+    /* Packets at the start of the buffer, 188, 192 or 204 bytes apart. */
+    test("payload",                             0, 0x00, 188, "DDDDDDDDDDDD");
+    test("null, afc 0",                         0, 0x00, 188, "nnnnnnnnnnnn");
+    test("payload, then null with afc 0",       0, 0x00, 188, "Dnnnnnnnnnnn");
+    test("payload, then null with afc 0, 192",  0, 0x00, 192, "Dnnnnnnnnnnn");
+    test("payload, then null with afc 0, 204",  0, 0x00, 204, "Dnnnnnnnnnnn");
+    test("null, then payload with afc 0",       0, 0x00, 188, "Nddddddddddd");
+    test("payload and null with afc 0, mixed",  0, 0x00, 188, "DnDnDnDnDnDn");
+    test("payload, a few null with afc 0",      0, 0x00, 188, "DDnDDnDDnDDD");
+    test("null, then PID 0x1FF with afc 0",     0, 0x00, 188, "nLLLLLLLLLLL");
+    test("null, then PID 0x1F00 with afc 0",    0, 0x00, 188, "nHHHHHHHHHHH");
+
+    /* The packets follow 5 bytes which are not part of the stream. */
+    test("null with afc 0, after 0x00",         5, 0x00, 188, "nnnnnnnnnnnn");
+    test("payload with afc 0, after 0xFF",      5, 0xFF, 188, "dddddddddddd");
+    test("payload, after 0xFF",                 5, 0xFF, 188, "DDDDDDDDDDDD");
+
+    return 0;
+}
diff --git a/tests/fate/libavformat.mak b/tests/fate/libavformat.mak
index e7854626c3..6daa10df86 100644
--- a/tests/fate/libavformat.mak
+++ b/tests/fate/libavformat.mak
@@ -76,6 +76,10 @@ FATE_LIBAVFORMAT-$(CONFIG_IMF_DEMUXER) += fate-imf
 fate-imf: libavformat/tests/imf$(EXESUF)
 fate-imf: CMD = run libavformat/tests/imf$(EXESUF)

+FATE_LIBAVFORMAT-$(CONFIG_MPEGTS_DEMUXER) += fate-mpegts-probe-pid
+fate-mpegts-probe-pid: libavformat/tests/mpegts_probe$(EXESUF)
+fate-mpegts-probe-pid: CMD = run libavformat/tests/mpegts_probe$(EXESUF)
+
 FATE_LIBAVFORMAT += fate-seek_utils
 fate-seek_utils: libavformat/tests/seek_utils$(EXESUF)
 fate-seek_utils: CMD = run libavformat/tests/seek_utils$(EXESUF)
diff --git a/tests/ref/fate/mpegts-probe-pid b/tests/ref/fate/mpegts-probe-pid
new file mode 100644
index 0000000000..ca1f60bd08
--- /dev/null
+++ b/tests/ref/fate/mpegts-probe-pid
@@ -0,0 +1,13 @@
+payload: 100
+null, afc 0: 100
+payload, then null with afc 0: 100
+payload, then null with afc 0, 192: 100
+payload, then null with afc 0, 204: 100
+null, then payload with afc 0: 0
+payload and null with afc 0, mixed: 100
+payload, a few null with afc 0: 100
+null, then PID 0x1FF with afc 0: 0
+null, then PID 0x1F00 with afc 0: 0
+null with afc 0, after 0x00: 100
+payload with afc 0, after 0xFF: 0
+payload, after 0xFF: 100