commit 3153a052d2a4712c40d01a18540d56c9625134b9
parent de4606cd6e095cf4c1d7e4bb621c2bb367c790f3
Author: Dale Curtis <dalecurtis@chromium.org>
Date: Fri, 31 Oct 2025 08:57:12 +0000
Bug 1997001 [wpt PR 55741] - Support H.264 SEI recovery points as keyframes, a=testonly
Automatic update from web-platform-tests
Support H.264 SEI recovery points as keyframes
H.264 SEI recovery points with a recovery_frame_cnt=0 are well
supported as keyframes throughout our pipeline. ffmpeg has support
and our hardware decoders have exposed support since 2021. With
Safari and Firefox both supporting these as keyframes, we should
move to add support as well.
Bug: 451536366
Change-Id: I268e060559724fd92c7f53ebb3aa5cee6ed1e3ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7082888
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Eugene Zemtsov <eugene@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1536952}
--
wpt-commits: 9ab188bddfef32d970a8ae38d02577d29d61af7b
wpt-pr: 55741
Diffstat:
5 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/testing/web-platform/tests/webcodecs/README.md b/testing/web-platform/tests/webcodecs/README.md
@@ -101,9 +101,21 @@ Used a [custom tool](https://storage.googleapis.com/dalecurtis/avif2mp4.html) to
ffmpeg -f lavfi -i testsrc=rate=10:n=1 -t 1 -pix_fmt yuv420p -vcodec h264 -tune zerolatency h264.mp4
```
+### h264_sei.mp4
+Similar to the construction of `h264.mp4`, but produces a file where the 5th
+frame is an SEI recovery point with a `recovery_frame_cnt=0`.
+```
+ffmpeg -f lavfi -i testsrc=rate=10:n=1 -t 1 -pix_fmt yuv420p -vcodec h264 -tune zerolatency -x264-params "open-gop=1:keyint=5:bframes=3" h264_sei.mp4
+```
+
### h264.annexb
```
-ffmpeg -i h264.mp4 -codec copy -bsf:v h264_mp4toannexb -f h264 h264.annexb
+ffmpeg -f lavfi -i testsrc=rate=10:n=1 -t 1 -pix_fmt yuv420p -vcodec h264 -tune zerolatency -f h264 h264.annexb
+```
+
+### h264_sei.annexb
+```
+ffmpeg -f lavfi -i testsrc=rate=10:n=1 -t 1 -pix_fmt yuv420p -vcodec h264 -tune zerolatency -x264-params "open-gop=1:keyint=5:bframes=3" -f h264 h264_sei.annexb
```
### h265.mp4
@@ -113,7 +125,7 @@ ffmpeg -f lavfi -i testsrc=rate=10:n=1 -t 1 -pix_fmt yuv420p -vcodec hevc -tag:v
### h265.annexb
```
-ffmpeg -i h265.mp4 -codec copy -bsf:v hevc_mp4toannexb -f hevc h265.annexb
+ffmpeg -f lavfi -i testsrc=rate=10:n=1 -t 1 -pix_fmt yuv420p -vcodec hevc -tag:v hvc1 -tune zerolatency -f hevc h265.annexb
```
### sfx.adts
diff --git a/testing/web-platform/tests/webcodecs/h264_sei.annexb b/testing/web-platform/tests/webcodecs/h264_sei.annexb
Binary files differ.
diff --git a/testing/web-platform/tests/webcodecs/h264_sei.mp4 b/testing/web-platform/tests/webcodecs/h264_sei.mp4
Binary files differ.
diff --git a/testing/web-platform/tests/webcodecs/videoDecoder-codec-specific-setup.js b/testing/web-platform/tests/webcodecs/videoDecoder-codec-specific-setup.js
@@ -74,6 +74,25 @@ const H264_AVC_DATA = {
]
};
+const H264_SEI_AVC_DATA = {
+ src: 'h264_sei.mp4',
+ config: {
+ codec: 'avc1.64000b',
+ description: {offset: 11989, size: 46},
+ codedWidth: 320,
+ codedHeight: 240,
+ displayAspectWidth: 320,
+ displayAspectHeight: 240,
+ },
+ chunks: [
+ {offset: 48, size: 4229}, {offset: 4277, size: 1114},
+ {offset: 5391, size: 320}, {offset: 5711, size: 188},
+ {offset: 5899, size: 173}, {offset: 6072, size: 3694},
+ {offset: 9766, size: 936}, {offset: 10702, size: 345},
+ {offset: 11047, size: 213}, {offset: 11260, size: 210}
+ ]
+};
+
const H264_ANNEXB_DATA = {
src: 'h264.annexb',
config: {
@@ -92,6 +111,24 @@ const H264_ANNEXB_DATA = {
]
};
+const H264_SEI_ANNEXB_DATA = {
+ src: 'h264_sei.annexb',
+ config: {
+ codec: 'avc1.64000b',
+ codedWidth: 320,
+ codedHeight: 240,
+ displayAspectWidth: 320,
+ displayAspectHeight: 240,
+ },
+ chunks: [
+ {offset: 0, size: 4264}, {offset: 4264, size: 1112},
+ {offset: 5376, size: 318}, {offset: 5694, size: 186},
+ {offset: 5880, size: 171}, {offset: 6051, size: 3729},
+ {offset: 9780, size: 934}, {offset: 10714, size: 343},
+ {offset: 11057, size: 211}, {offset: 11268, size: 208}
+ ]
+};
+
const H265_HEVC_DATA = {
src: 'h265.mp4',
config: {
@@ -187,7 +224,9 @@ promise_setup(async () => {
'?vp8': VP8_DATA,
'?vp9': VP9_DATA,
'?h264_avc': H264_AVC_DATA,
+ '?h264_sei_avc': H264_SEI_AVC_DATA,
'?h264_annexb': H264_ANNEXB_DATA,
+ '?h264_sei_annexb': H264_SEI_ANNEXB_DATA,
'?h265_hevc': H265_HEVC_DATA,
'?h265_annexb': H265_ANNEXB_DATA
}[location.search];
diff --git a/testing/web-platform/tests/webcodecs/videoDecoder-h264-sei.https.any.js b/testing/web-platform/tests/webcodecs/videoDecoder-h264-sei.https.any.js
@@ -0,0 +1,26 @@
+// META: global=window,dedicatedworker
+// META: script=videoDecoder-codec-specific-setup.js
+// META: variant=?h264_sei_avc
+// META: variant=?h264_sei_annexb
+
+promise_test(async t => {
+ await checkImplements();
+ const callbacks = {};
+ const decoder = createVideoDecoder(t, callbacks);
+ decoder.configure(CONFIG);
+
+ // Frame 0 is IDR, frame 5 is SEI recovery point.
+ decoder.decode(CHUNKS[5]);
+
+ // First decode the IDR frame to
+ let outputs = 0;
+ callbacks.output = frame => {
+ outputs++;
+ assert_equals(frame.timestamp, CHUNKS[5].timestamp, 'timestamp');
+ assert_equals(frame.duration, CHUNKS[5].duration, 'duration');
+ frame.close();
+ };
+
+ await decoder.flush();
+ assert_equals(outputs, 1, 'outputs');
+}, 'H.264 SEI recovery point frames are treated as keyframes.');