ffmpeg-devel
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
December 2025
- 42 participants
- 307 discussions
Previously, the index entry was unconditionally marked as a keyframe, which
is incorrect for inter-coded video (B/P frames). This patch uses the
AV_PKT_FLAG_KEY to correctly flag keyframes.
Please find the patch attached.
1
0
[PR] avformat/img2dec: reject input images too big to fit into a single packet (PR #21334)
by Timo Rothenpieler 31 Dec '25
by Timo Rothenpieler 31 Dec '25
31 Dec '25
PR #21334 opened by Timo Rothenpieler (BtbN)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21334
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21334.patch
Not entirely sure if it should instead use some entirely different
approach here, given that images exceeding 2GB don't seem that crazy
to me, but so far processing such images results in a heap overflow,
since the size addition overflows and a much too small packet is
allocated and its size never checked again when writing into it.
Fixes #YWH-PGM40646-32
From adebaa447e94078d17e44cefe3ade507d6c5b0f2 Mon Sep 17 00:00:00 2001
From: Timo Rothenpieler <timo(a)rothenpieler.org>
Date: Wed, 31 Dec 2025 03:41:21 +0100
Subject: [PATCH] avformat/img2dec: reject input images too big to fit into a
single packet
Not entirely sure if it should instead use some entirely different
approach here, given that images exceeding 2GB don't seem that crazy
to me, but so far processing such images results in a heap overflow,
since the size addition overflows and a much too small packet is
allocated and its size never checked again when writing into it.
Fixes #YWH-PGM40646-32
---
libavformat/img2dec.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c
index 8f1c9013ca..9c2c519e4f 100644
--- a/libavformat/img2dec.c
+++ b/libavformat/img2dec.c
@@ -365,8 +365,10 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt)
VideoDemuxData *s = s1->priv_data;
AVBPrint filename;
int i, res;
- int size[3] = { 0 }, ret[3] = { 0 };
- AVIOContext *f[3] = { NULL };
+ int ret[3] = { 0 };
+ int64_t size[3] = { 0 };
+ int64_t total_size;
+ AVIOContext *f[3] = { NULL };
AVCodecParameters *par = s1->streams[0]->codecpar;
av_bprint_init(&filename, 0, AV_BPRINT_SIZE_UNLIMITED);
@@ -456,7 +458,11 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt)
}
}
- res = av_new_packet(pkt, size[0] + size[1] + size[2]);
+ total_size = size[0] + size[1] + size[2];
+ if (total_size < 0 || total_size > INT_MAX || size[0] > INT_MAX || size[1] > INT_MAX || size[2] > INT_MAX)
+ return AVERROR_INVALIDDATA;
+
+ res = av_new_packet(pkt, total_size);
if (res < 0) {
goto fail;
}
--
2.49.1
1
0
[PR] avcodec/vulkan_decode: fix logic error when checking for encode support (PR #21333)
by Kacper Michajłow 31 Dec '25
by Kacper Michajłow 31 Dec '25
31 Dec '25
PR #21333 opened by Kacper Michajłow (kasper93)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21333
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21333.patch
From 6f513ab06cc80c33eb1c1a396be548b3c781f2bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <kasper93(a)gmail.com>
Date: Wed, 31 Dec 2025 02:56:18 +0100
Subject: [PATCH 1/3] avcodec/vulkan_decode: fix logic error when checking for
encode support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Both FF_VK_EXT_VIDEO_ENCODE_QUEUE and FF_VK_EXT_VIDEO_MAINTENANCE_1 are
required, not only one of them.
Found by VVL.
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
---
libavcodec/vulkan_decode.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c
index eb76d5b052..5ed963eacc 100644
--- a/libavcodec/vulkan_decode.c
+++ b/libavcodec/vulkan_decode.c
@@ -1054,8 +1054,8 @@ static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
- if (ctx->s.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
- FF_VK_EXT_VIDEO_MAINTENANCE_1))
+ if ((ctx->s.extensions & FF_VK_EXT_VIDEO_ENCODE_QUEUE) &&
+ (ctx->s.extensions & FF_VK_EXT_VIDEO_MAINTENANCE_1))
fmt_info.imageUsage |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
}
@@ -1229,8 +1229,8 @@ int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx)
hwfc->usage |= VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
ctx = dec->shared_ctx;
- if (ctx->s.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
- FF_VK_EXT_VIDEO_MAINTENANCE_1))
+ if ((ctx->s.extensions & FF_VK_EXT_VIDEO_ENCODE_QUEUE) &&
+ (ctx->s.extensions & FF_VK_EXT_VIDEO_MAINTENANCE_1))
hwfc->usage |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
}
--
2.49.1
From d9afcfa2a8829ecd127f4bbc09a23a25dd15e499 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <kasper93(a)gmail.com>
Date: Wed, 31 Dec 2025 02:58:58 +0100
Subject: [PATCH 2/3] avfilter/vulkan_filter: fix logic error when checking for
encode support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Both FF_VK_EXT_VIDEO_ENCODE_QUEUE and FF_VK_EXT_VIDEO_MAINTENANCE_1 are
required, not only one of them.
Found by VVL.
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
---
libavfilter/vulkan_filter.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libavfilter/vulkan_filter.c b/libavfilter/vulkan_filter.c
index a9f47741ed..e049efec03 100644
--- a/libavfilter/vulkan_filter.c
+++ b/libavfilter/vulkan_filter.c
@@ -74,8 +74,8 @@ int ff_vk_filter_init_context(AVFilterContext *avctx, FFVulkanContext *s,
/* If format supports hardware encoding, make sure
* the context includes it. */
if (vk_frames->format[1] == VK_FORMAT_UNDEFINED &&
- (s->extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
- FF_VK_EXT_VIDEO_MAINTENANCE_1))) {
+ (s->extensions & FF_VK_EXT_VIDEO_ENCODE_QUEUE) &&
+ (s->extensions & FF_VK_EXT_VIDEO_MAINTENANCE_1)) {
VkFormatProperties3 fprops = {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3,
};
--
2.49.1
From 89c3e83c3b49779eba9468f780bbd71a10f1787c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <kasper93(a)gmail.com>
Date: Wed, 31 Dec 2025 03:00:14 +0100
Subject: [PATCH 3/3] avutil/hwcontext_vulkan: fix logic error when checking
for encode support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Both FF_VK_EXT_VIDEO_ENCODE_QUEUE and FF_VK_EXT_VIDEO_MAINTENANCE_1 are
required, not only one of them.
Found by VVL.
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
---
libavutil/hwcontext_vulkan.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 2011cdec79..706af35287 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -3025,8 +3025,8 @@ static int vulkan_frames_init(AVHWFramesContext *hwfc)
/* Enables encoding of images, if supported by format and extensions */
if ((supported_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) &&
- (p->vkctx.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
- FF_VK_EXT_VIDEO_MAINTENANCE_1)))
+ (p->vkctx.extensions & FF_VK_EXT_VIDEO_ENCODE_QUEUE) &&
+ (p->vkctx.extensions & FF_VK_EXT_VIDEO_MAINTENANCE_1))
hwctx->usage |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
/* Image creation flags.
@@ -3049,8 +3049,8 @@ static int vulkan_frames_init(AVHWFramesContext *hwfc)
* If there's no profile list, or it has no encode operations,
* then allow creating the image with no specific profile. */
if ((hwctx->usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) &&
- (p->vkctx.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
- FF_VK_EXT_VIDEO_MAINTENANCE_1))) {
+ (p->vkctx.extensions & FF_VK_EXT_VIDEO_ENCODE_QUEUE) &&
+ (p->vkctx.extensions & FF_VK_EXT_VIDEO_MAINTENANCE_1)) {
const VkVideoProfileListInfoKHR *pl;
pl = ff_vk_find_struct(hwctx->create_pnext, VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
if (!pl) {
--
2.49.1
1
0
PR #21331 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21331
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21331.patch
From bdfaec89febaabaf05cb4d5df72396b548ce6178 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial(a)gmail.com>
Date: Sat, 27 Dec 2025 17:08:30 -0300
Subject: [PATCH 1/3] avformat/iamf_parse: fix setting denominator in
AVIAMFLayer.demixing_matrix
The format of demixing_matrix is Q15 fixed point values.
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit 03a981445ee89a567e5a98ec88cbbbb8d3e3af72)
---
libavformat/iamf_parse.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavformat/iamf_parse.c b/libavformat/iamf_parse.c
index 8fe7e1bb0e..1050219591 100644
--- a/libavformat/iamf_parse.c
+++ b/libavformat/iamf_parse.c
@@ -487,7 +487,7 @@ static int ambisonics_config(void *s, AVIOContext *pb,
return AVERROR(ENOMEM);
for (int i = 0; i < demixing_matrix_size; i++)
- layer->demixing_matrix[i] = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
+ layer->demixing_matrix[i] = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 15);
for (int i = 0; i < substream_count; i++) {
IAMFSubStream *substream = &audio_element->substreams[i];
--
2.49.1
From c0272bf65474a636262e59625da4c918bbf8e19a Mon Sep 17 00:00:00 2001
From: James Almer <jamrial(a)gmail.com>
Date: Sat, 27 Dec 2025 18:06:32 -0300
Subject: [PATCH 2/3] avformat/iamf_writer: fix writting some ambisonics fields
in Audio Elements
The fields are defined as 8 bit long unsigned ints. Fortunately, writing most sane values
as leb is equivalent, which is why no tests are affected.
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit 21ff60d2cf7dfe00400ffef6a8843736f4cf9a2f)
---
libavformat/iamf_writer.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libavformat/iamf_writer.c b/libavformat/iamf_writer.c
index 27926f859a..d13bd8fecc 100644
--- a/libavformat/iamf_writer.c
+++ b/libavformat/iamf_writer.c
@@ -586,8 +586,8 @@ static int ambisonics_config(const IAMFAudioElement *audio_element,
const AVIAMFLayer *layer = element->layers[0];
ffio_write_leb(dyn_bc, 0); // ambisonics_mode
- ffio_write_leb(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count
- ffio_write_leb(dyn_bc, audio_element->nb_substreams); // substream_count
+ avio_w8(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count
+ avio_w8(dyn_bc, audio_element->nb_substreams); // substream_count
if (layer->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC)
for (int i = 0; i < layer->ch_layout.nb_channels; i++)
--
2.49.1
From 40b336e65043cbde6136ed338a18e3941dd0e6da Mon Sep 17 00:00:00 2001
From: James Almer <jamrial(a)gmail.com>
Date: Sat, 27 Dec 2025 19:11:07 -0300
Subject: [PATCH 3/3] avformat/iamf_writer: check that stream count is
consistent for ambisonic Audio Elements
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit a0fc454871c116d5bd1bbec5138dfe3ffcb1f11d)
---
libavformat/iamf_writer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libavformat/iamf_writer.c b/libavformat/iamf_writer.c
index d13bd8fecc..61d69373d9 100644
--- a/libavformat/iamf_writer.c
+++ b/libavformat/iamf_writer.c
@@ -583,8 +583,12 @@ static int ambisonics_config(const IAMFAudioElement *audio_element,
AVIOContext *dyn_bc)
{
const AVIAMFAudioElement *element = audio_element->celement;
+ const IAMFLayer *ilayer = &audio_element->layers[0];
const AVIAMFLayer *layer = element->layers[0];
+ if (audio_element->nb_substreams != ilayer->substream_count)
+ return AVERROR(EINVAL);
+
ffio_write_leb(dyn_bc, 0); // ambisonics_mode
avio_w8(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count
avio_w8(dyn_bc, audio_element->nb_substreams); // substream_count
--
2.49.1
1
0
PR #21330 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21330
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21330.patch
From af60dd84194b8ae6a3c1bf4ee474bc06bcf7023d Mon Sep 17 00:00:00 2001
From: James Almer <jamrial(a)gmail.com>
Date: Sat, 27 Dec 2025 17:08:30 -0300
Subject: [PATCH 1/3] avformat/iamf_parse: fix setting denominator in
AVIAMFLayer.demixing_matrix
The format of demixing_matrix is Q15 fixed point values.
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit 03a981445ee89a567e5a98ec88cbbbb8d3e3af72)
---
libavformat/iamf_parse.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavformat/iamf_parse.c b/libavformat/iamf_parse.c
index 597d800be0..65dbfa0d1e 100644
--- a/libavformat/iamf_parse.c
+++ b/libavformat/iamf_parse.c
@@ -555,7 +555,7 @@ static int ambisonics_config(void *s, AVIOContext *pb,
return AVERROR(ENOMEM);
for (int i = 0; i < demixing_matrix_size; i++)
- layer->demixing_matrix[i] = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
+ layer->demixing_matrix[i] = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 15);
for (int i = 0; i < substream_count; i++) {
IAMFSubStream *substream = &audio_element->substreams[i];
--
2.49.1
From 06fcee2e2605364bda5613ff28ef5d06e3dc0377 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial(a)gmail.com>
Date: Sat, 27 Dec 2025 18:06:32 -0300
Subject: [PATCH 2/3] avformat/iamf_writer: fix writting some ambisonics fields
in Audio Elements
The fields are defined as 8 bit long unsigned ints. Fortunately, writing most sane values
as leb is equivalent, which is why no tests are affected.
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit 21ff60d2cf7dfe00400ffef6a8843736f4cf9a2f)
---
libavformat/iamf_writer.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libavformat/iamf_writer.c b/libavformat/iamf_writer.c
index e9dd170380..4ad6521f31 100644
--- a/libavformat/iamf_writer.c
+++ b/libavformat/iamf_writer.c
@@ -662,8 +662,8 @@ static int ambisonics_config(const IAMFAudioElement *audio_element,
const AVIAMFLayer *layer = element->layers[0];
ffio_write_leb(dyn_bc, 0); // ambisonics_mode
- ffio_write_leb(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count
- ffio_write_leb(dyn_bc, audio_element->nb_substreams); // substream_count
+ avio_w8(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count
+ avio_w8(dyn_bc, audio_element->nb_substreams); // substream_count
if (layer->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC)
for (int i = 0; i < layer->ch_layout.nb_channels; i++)
--
2.49.1
From bfa334de42a5d81df2bcea67113acf718593aaf6 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial(a)gmail.com>
Date: Sat, 27 Dec 2025 19:11:07 -0300
Subject: [PATCH 3/3] avformat/iamf_writer: check that stream count is
consistent for ambisonic Audio Elements
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit a0fc454871c116d5bd1bbec5138dfe3ffcb1f11d)
---
libavformat/iamf_writer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libavformat/iamf_writer.c b/libavformat/iamf_writer.c
index 4ad6521f31..c2de13b543 100644
--- a/libavformat/iamf_writer.c
+++ b/libavformat/iamf_writer.c
@@ -659,8 +659,12 @@ static int ambisonics_config(const IAMFAudioElement *audio_element,
AVIOContext *dyn_bc)
{
const AVIAMFAudioElement *element = audio_element->celement;
+ const IAMFLayer *ilayer = &audio_element->layers[0];
const AVIAMFLayer *layer = element->layers[0];
+ if (audio_element->nb_substreams != ilayer->substream_count)
+ return AVERROR(EINVAL);
+
ffio_write_leb(dyn_bc, 0); // ambisonics_mode
avio_w8(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count
avio_w8(dyn_bc, audio_element->nb_substreams); // substream_count
--
2.49.1
1
0
PR #21329 opened by frankplow
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21329
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21329.patch
Backport fixes from #21293 and #21317.
From b0f76db8660bb6925e2bc4348d0e8f017fa61c55 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Wed, 24 Dec 2025 15:35:06 +0000
Subject: [PATCH 1/2] lavc/vvc: Error on inter slice with no reference pics
The semantics of sh_num_ref_idx_active_minus1[ i ] state that
When the current slice is a P slice, the value of NumRefIdxActive[ 0 ] shall be greater than 0.
When the current slice is a B slice, both NumRefIdxActive[ 0 ] and NumRefIdxActive[ 1 ] shall be greater than 0.
Fixes: use of uninitialized memory
Fixes: 449549597/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_VVC_fuzzer-5600497089445888
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Reported-by: Michael Niedermayer michael(a)niedermayer.cc
(cherry picked from commit 90f1f797aa9231375e4858df523fbfeda89bfd79)
---
libavcodec/cbs_h266_syntax_template.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
index 8b337c75d0..d9a65a9c19 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -3248,6 +3248,12 @@ static int FUNC(slice_header) (CodedBitstreamContext *ctx, RWContext *rw,
FFMIN(ref_pic_lists->rpl_ref_list[i].num_ref_entries,
pps->pps_num_ref_idx_default_active_minus1[i] + 1);
}
+
+ if (current->num_ref_idx_active[i] <= 0) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR,
+ "Inter slice but no reference pictures available for RPL%d.\n", i);
+ return AVERROR_INVALIDDATA;
+ }
} else {
current->num_ref_idx_active[i] = 0;
}
--
2.49.1
From 6849581e70ce68c7bb096fec062a332ed56aa9c9 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Mon, 29 Dec 2025 22:14:53 +0000
Subject: [PATCH 2/2] lavc/vvc: Prevent OOB write to slice_top_left_ctu_x in
PPS CBS
Prior to the fix, in the case of a tile containing multiple slices
(pps_num_exp_slices_in_tile != 0) the number of slices was temporarily
allowed to exceed pps_num_slices_in_pic_minus1+1 and therefore
VVC_MAX_SLICES. The number of slices was later verified, but while the
current slice index was higher than expected it was used to write to a
array of size VVC_MAX_SLICES, leading to an OOB write.
To rectify this, the patch adds some checks at an earlier stage, to
ensure that the slice index i + j at no point exceeds
pps_num_slices_in_pic_minus1.
Fixes #YWH-PGM40646-30
(cherry picked from commit 72a38c12e5b84ccb30fba88c39ef2a086013af5b)
---
libavcodec/cbs_h266_syntax_template.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
index d9a65a9c19..4f6ae76e27 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -2011,6 +2011,12 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, RWContext *rw,
slice_top_left_ctu_y[i] = ctu_y;
} else {
uint16_t slice_height_in_ctus;
+ int num_uniform_slices;
+
+ if (i + current->pps_num_exp_slices_in_tile[i] >
+ current->pps_num_slices_in_pic_minus1 + 1)
+ return AVERROR_INVALIDDATA;
+
for (j = 0; j < current->pps_num_exp_slices_in_tile[i];
j++) {
ues(pps_exp_slice_height_in_ctus_minus1[i][j], 0,
@@ -2031,6 +2037,13 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, RWContext *rw,
uniform_slice_height = 1 +
(j == 0 ? current->row_height_val[tile_y] - 1:
current->pps_exp_slice_height_in_ctus_minus1[i][j-1]);
+
+ num_uniform_slices = (remaining_height_in_ctbs_y + uniform_slice_height - 1)
+ / uniform_slice_height;
+ if (i + current->pps_num_exp_slices_in_tile[i] + num_uniform_slices >
+ current->pps_num_slices_in_pic_minus1 + 1)
+ return AVERROR_INVALIDDATA;
+
while (remaining_height_in_ctbs_y > uniform_slice_height) {
current->slice_height_in_ctus[i + j] =
uniform_slice_height;
--
2.49.1
1
0
PR #21328 opened by frankplow
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21328
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21328.patch
From 85bff0a31c3abb6cb09b4832c5dbf5fa95017e16 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <zhilizhao(a)tencent.com>
Date: Sun, 22 Sep 2024 11:39:14 +0800
Subject: [PATCH 01/24] avcodec/vvc: Don't use large array on stack
tmp_array in dmvr_hv takes 33024 bytes on stack, which can be
dangerous.
(cherry picked from commit 240c16bbc6ef723139793fea6e94516dfb058db9)
---
libavcodec/vvc/inter_template.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/libavcodec/vvc/inter_template.c b/libavcodec/vvc/inter_template.c
index c073a73e76..aee4994c17 100644
--- a/libavcodec/vvc/inter_template.c
+++ b/libavcodec/vvc/inter_template.c
@@ -472,6 +472,9 @@ static void FUNC(apply_bdof)(uint8_t *_dst, const ptrdiff_t _dst_stride, const i
(filter[0] * src[x] + \
filter[1] * src[x + stride])
+#define DMVR_FILTER2(filter, src0, src1) \
+ (filter[0] * src0 + filter[1] * src1)
+
//8.5.3.2.2 Luma sample bilinear interpolation process
static void FUNC(dmvr)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
const int height, const intptr_t mx, const intptr_t my, const int width)
@@ -541,31 +544,31 @@ static void FUNC(dmvr_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src
static void FUNC(dmvr_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
const int height, const intptr_t mx, const intptr_t my, const int width)
{
- int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE];
- int16_t *tmp = tmp_array;
+ int16_t tmp_array[MAX_PB_SIZE * 2];
+ int16_t *tmp0 = tmp_array;
+ int16_t *tmp1 = tmp_array + MAX_PB_SIZE;
const pixel *src = (const pixel*)_src;
const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
- const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[mx];
+ const int8_t *filter_x = ff_vvc_inter_luma_dmvr_filters[mx];
+ const int8_t *filter_y = ff_vvc_inter_luma_dmvr_filters[my];
const int shift1 = BIT_DEPTH - 6;
const int offset1 = 1 << (shift1 - 1);
const int shift2 = 4;
const int offset2 = 1 << (shift2 - 1);
src -= BILINEAR_EXTRA_BEFORE * src_stride;
- for (int y = 0; y < height + BILINEAR_EXTRA; y++) {
- for (int x = 0; x < width; x++)
- tmp[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1;
- src += src_stride;
- tmp += MAX_PB_SIZE;
- }
+ for (int x = 0; x < width; x++)
+ tmp0[x] = (DMVR_FILTER2(filter_x, src[x], src[x + 1]) + offset1) >> shift1;
+ src += src_stride;
- tmp = tmp_array + BILINEAR_EXTRA_BEFORE * MAX_PB_SIZE;
- filter = ff_vvc_inter_luma_dmvr_filters[my];
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++)
- dst[x] = (DMVR_FILTER(tmp, MAX_PB_SIZE) + offset2) >> shift2;
- tmp += MAX_PB_SIZE;
+ for (int y = 1; y < height + BILINEAR_EXTRA; y++) {
+ for (int x = 0; x < width; x++) {
+ tmp1[x] = (DMVR_FILTER2(filter_x, src[x], src[x + 1]) + offset1) >> shift1;
+ dst[x] = (DMVR_FILTER2(filter_y, tmp0[x], tmp1[x]) + offset2) >> shift2;
+ }
+ src += src_stride;
dst += MAX_PB_SIZE;
+ FFSWAP(int16_t *, tmp0, tmp1);
}
}
--
2.49.1
From f23038f8bd8fde62538e8ebbb4b10a03e92404e0 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Mon, 14 Oct 2024 23:25:32 +0800
Subject: [PATCH 02/24] avcodec/vvc/thread: Check frame to be non NULL
Fixes: NULL pointer dereference
Fixes: 71303/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_VVC_fuzzer-4875859050168320
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Reported-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit b61141056939acfd67e51dbd05c2c8c729d6b31b)
---
libavcodec/vvc/dec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavcodec/vvc/dec.c b/libavcodec/vvc/dec.c
index edf2607f50..522fa8416e 100644
--- a/libavcodec/vvc/dec.c
+++ b/libavcodec/vvc/dec.c
@@ -1000,7 +1000,7 @@ static int vvc_decode_frame(AVCodecContext *avctx, AVFrame *output,
if (ret < 0)
return ret;
- if (!fc->ft)
+ if (!fc->ft || !fc->ref)
return avpkt->size;
ret = submit_frame(s, fc, output, got_output);
--
2.49.1
From 749428672fae2acd93d54431738df8eff37181ce Mon Sep 17 00:00:00 2001
From: Fei Wang <fei.w.wang(a)intel.com>
Date: Mon, 28 Oct 2024 16:55:22 +0800
Subject: [PATCH 03/24] lavc/vvc_refs: Define VVC_FRAME_FLAG* to h header
So that hardware decoder can use the flags too.
Signed-off-by: Fei Wang <fei.w.wang(a)intel.com>
(cherry picked from commit 15a75e8e0425309fdc5a2772ebf622b3705f914a)
---
libavcodec/vvc/refs.c | 4 ----
libavcodec/vvc/refs.h | 5 +++++
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/libavcodec/vvc/refs.c b/libavcodec/vvc/refs.c
index bf093bea98..358e11242a 100644
--- a/libavcodec/vvc/refs.c
+++ b/libavcodec/vvc/refs.c
@@ -29,10 +29,6 @@
#include "refs.h"
-#define VVC_FRAME_FLAG_OUTPUT (1 << 0)
-#define VVC_FRAME_FLAG_SHORT_REF (1 << 1)
-#define VVC_FRAME_FLAG_LONG_REF (1 << 2)
-#define VVC_FRAME_FLAG_BUMPING (1 << 3)
typedef struct FrameProgress {
atomic_int progress[VVC_PROGRESS_LAST];
diff --git a/libavcodec/vvc/refs.h b/libavcodec/vvc/refs.h
index 8ae33d4a9a..e2271ab381 100644
--- a/libavcodec/vvc/refs.h
+++ b/libavcodec/vvc/refs.h
@@ -25,6 +25,11 @@
#include "dec.h"
+#define VVC_FRAME_FLAG_OUTPUT (1 << 0)
+#define VVC_FRAME_FLAG_SHORT_REF (1 << 1)
+#define VVC_FRAME_FLAG_LONG_REF (1 << 2)
+#define VVC_FRAME_FLAG_BUMPING (1 << 3)
+
int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, struct AVFrame *out, int no_output_of_prior_pics_flag, int flush);
void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc);
int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, struct AVFrame **frame);
--
2.49.1
From 1dd8ddd35b543a5f78ba0b8d5f0c8c5aa3a7e105 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Sat, 23 Nov 2024 17:32:32 +0800
Subject: [PATCH 04/24] avcodec/vvcdec: ensure every CTU belongs to a slice
According to section 6.3.3 "Spatial or component-wise partitionings,"
CTUs should fully cover slices with no overlaps, gaps, or additions.
No overlaps are ensured by task_init_parse.
No gaps and no additions are ensured by this patch.
Co-authored-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 5c5a08ecb53b1b9d97512ce398fa395c3fb8c1a8)
---
libavcodec/vvc/thread.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/libavcodec/vvc/thread.c b/libavcodec/vvc/thread.c
index 86a7753c6a..7ff759d889 100644
--- a/libavcodec/vvc/thread.c
+++ b/libavcodec/vvc/thread.c
@@ -822,6 +822,13 @@ int ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc)
}
}
}
+ for (int rs = 0; rs < ft->ctu_count; rs++) {
+ const VVCTask *t = ft->tasks + rs;
+ if (!t->sc) {
+ av_log(s->avctx, AV_LOG_ERROR, "frame %5d, CTU(%d, %d) not belong to any slice\r\n", (int)fc->decode_order, t->rx, t->ry);
+ return AVERROR_INVALIDDATA;
+ }
+ }
frame_thread_add_score(s, ft, 0, 0, VVC_TASK_STAGE_INIT);
return 0;
--
2.49.1
From bedd05abb9fc33f1982685fba9940416de52b2b2 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Sat, 23 Nov 2024 17:32:35 +0800
Subject: [PATCH 05/24] avcodec/vvcdec: inter_data, check the return value from
hls_merge_data
Reported-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit ba89c5b989938fad751ac451d1f6ef813bb42460)
---
libavcodec/vvc/ctu.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libavcodec/vvc/ctu.c b/libavcodec/vvc/ctu.c
index b33ad576cf..634c553814 100644
--- a/libavcodec/vvc/ctu.c
+++ b/libavcodec/vvc/ctu.c
@@ -1778,13 +1778,16 @@ static int inter_data(VVCLocalContext *lc)
pu->general_merge_flag = ff_vvc_general_merge_flag(lc);
if (pu->general_merge_flag) {
- hls_merge_data(lc);
+ ret = hls_merge_data(lc);
} else if (cu->pred_mode == MODE_IBC){
ret = mvp_data_ibc(lc);
} else {
ret = mvp_data(lc);
}
+ if (ret)
+ return ret;
+
if (cu->pred_mode == MODE_IBC)
{
ff_vvc_update_hmvp(lc, mi);
--
2.49.1
From efe5267958667828f9c1c2776771c9ca031419fe Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Sat, 23 Nov 2024 17:32:37 +0800
Subject: [PATCH 06/24] avcodec/vvcdec: schedule next stage only if the current
stage reports no error
If the current stage reports an error, some variables may not be correctly initialized.
Scheduling the next stage could lead to the use of uninitialized variables.
(cherry picked from commit eb67e60cb004c17ecbbd43c6e3c6732c19de5d33)
---
libavcodec/vvc/thread.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libavcodec/vvc/thread.c b/libavcodec/vvc/thread.c
index 7ff759d889..6a623e62b1 100644
--- a/libavcodec/vvc/thread.c
+++ b/libavcodec/vvc/thread.c
@@ -655,9 +655,9 @@ static void task_run_stage(VVCTask *t, VVCContext *s, VVCLocalContext *lc)
"frame %5d, %s(%3d, %3d) failed with %d\r\n",
(int)fc->decode_order, task_name[stage], t->rx, t->ry, ret);
}
+ if (!ret)
+ task_stage_done(t, s);
}
-
- task_stage_done(t, s);
return;
}
--
2.49.1
From 5836dc0622b7c081086b0a047aad265051ea7ee5 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Sat, 23 Nov 2024 17:32:33 +0800
Subject: [PATCH 07/24] avcodec/cbs_h266: improve readability in subpicture
parser
(cherry picked from commit e06515b092225f7d954aa64aedde98df097515ee)
---
libavcodec/cbs_h266_syntax_template.c | 102 +++++++++++++-------------
1 file changed, 50 insertions(+), 52 deletions(-)
diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
index d33629b06c..191e73edd5 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -1057,7 +1057,7 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
H266RawSPS *current)
{
CodedBitstreamH266Context *h266 = ctx->priv_data;
- int err, i, j;
+ int err, i, j, max_width_minus1, max_height_minus1;
unsigned int ctb_log2_size_y, min_cb_log2_size_y,
min_qt_log2_size_intra_y, min_qt_log2_size_inter_y,
ctb_size_y, max_num_merge_cand, tmp_width_val, tmp_height_val;
@@ -1130,6 +1130,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
ctb_log2_size_y);
tmp_height_val = AV_CEIL_RSHIFT(current->sps_pic_height_max_in_luma_samples,
ctb_log2_size_y);
+ max_width_minus1 = tmp_width_val - 1;
+ max_height_minus1 = tmp_height_val - 1;
flag(sps_subpic_info_present_flag);
if (current->sps_subpic_info_present_flag) {
@@ -1147,11 +1149,11 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
if (current->sps_pic_width_max_in_luma_samples > ctb_size_y)
ubs(wlen, sps_subpic_width_minus1[0], 1, 0);
else
- infer(sps_subpic_width_minus1[0], tmp_width_val - 1);
+ infer(sps_subpic_width_minus1[0], max_width_minus1);
if (current->sps_pic_height_max_in_luma_samples > ctb_size_y)
ubs(hlen, sps_subpic_height_minus1[0], 1, 0);
else
- infer(sps_subpic_height_minus1[0], tmp_height_val - 1);
+ infer(sps_subpic_height_minus1[0], max_height_minus1);
if (!current->sps_independent_subpics_flag) {
flags(sps_subpic_treated_as_pic_flag[0], 1, 0);
flags(sps_loop_filter_across_subpic_enabled_flag[0], 1, 0);
@@ -1161,58 +1163,54 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
}
for (i = 1; i <= current->sps_num_subpics_minus1; i++) {
if (!current->sps_subpic_same_size_flag) {
- if (current->sps_pic_width_max_in_luma_samples > ctb_size_y) {
- const int win_right_edge =
- current->sps_pic_width_max_in_luma_samples
- - current->sps_conf_win_right_offset * sub_width_c;
- us(wlen, sps_subpic_ctu_top_left_x[i], 0,
- AV_CEIL_RSHIFT(win_right_edge, ctb_log2_size_y) - 1,
- 1, i);
- } else
+ const int win_right_edge =
+ current->sps_pic_width_max_in_luma_samples -
+ current->sps_conf_win_right_offset * sub_width_c;
+ const int win_bottom_edge =
+ current->sps_pic_height_max_in_luma_samples -
+ current->sps_conf_win_bottom_offset * sub_height_c;
+ const int win_left_edge =
+ current->sps_conf_win_left_offset * sub_width_c;
+ const int win_top_edge =
+ current->sps_conf_win_top_offset * sub_height_c;
+ const int win_left_edge_ctus =
+ AV_CEIL_RSHIFT(win_left_edge, ctb_log2_size_y);
+ const int win_right_edge_ctus =
+ AV_CEIL_RSHIFT(win_right_edge, ctb_log2_size_y);
+ const int win_top_edge_ctus =
+ AV_CEIL_RSHIFT(win_top_edge, ctb_log2_size_y);
+ const int win_bottom_edge_ctus =
+ AV_CEIL_RSHIFT(win_bottom_edge, ctb_log2_size_y);
+ const int min_width =
+ FFMAX(win_left_edge_ctus - current->sps_subpic_ctu_top_left_x[i], 0);
+ const int min_height =
+ FFMAX(win_top_edge_ctus - current->sps_subpic_ctu_top_left_y[i], 0);
+
+ if (current->sps_pic_width_max_in_luma_samples > ctb_size_y)
+ us(wlen, sps_subpic_ctu_top_left_x[i], 0, win_right_edge_ctus - 1, 1, i);
+ else
infer(sps_subpic_ctu_top_left_x[i], 0);
- if (current->sps_pic_height_max_in_luma_samples >
- ctb_size_y) {
- const int win_bottom_edge =
- current->sps_pic_height_max_in_luma_samples
- - current->sps_conf_win_bottom_offset * sub_height_c;
- us(hlen, sps_subpic_ctu_top_left_y[i], 0,
- AV_CEIL_RSHIFT(win_bottom_edge, ctb_log2_size_y) - 1,
- 1, i);
- } else
+
+ if (current->sps_pic_height_max_in_luma_samples > ctb_size_y)
+ us(hlen, sps_subpic_ctu_top_left_y[i], 0, win_bottom_edge_ctus - 1, 1, i);
+ else
infer(sps_subpic_ctu_top_left_y[i], 0);
+
+ max_width_minus1 = tmp_width_val - current->sps_subpic_ctu_top_left_x[i] - 1;
+ max_height_minus1 = tmp_height_val - current->sps_subpic_ctu_top_left_y[i] - 1;
+
if (i < current->sps_num_subpics_minus1 &&
- current->sps_pic_width_max_in_luma_samples >
- ctb_size_y) {
- const int win_left_edge =
- current->sps_conf_win_left_offset * sub_width_c;
- const int win_left_edge_ctus =
- AV_CEIL_RSHIFT(win_left_edge, ctb_log2_size_y);
- us(wlen, sps_subpic_width_minus1[i],
- win_left_edge_ctus > current->sps_subpic_ctu_top_left_x[i]
- ? win_left_edge_ctus - current->sps_subpic_ctu_top_left_x[i]
- : 0,
- MAX_UINT_BITS(wlen), 1, i);
+ current->sps_pic_width_max_in_luma_samples > ctb_size_y) {
+ us(wlen, sps_subpic_width_minus1[i], min_width, MAX_UINT_BITS(wlen), 1, i);
} else {
- infer(sps_subpic_width_minus1[i],
- tmp_width_val -
- current->sps_subpic_ctu_top_left_x[i] - 1);
+ infer(sps_subpic_width_minus1[i], max_width_minus1);
}
+
if (i < current->sps_num_subpics_minus1 &&
- current->sps_pic_height_max_in_luma_samples >
- ctb_size_y) {
- const int win_top_edge =
- current->sps_conf_win_top_offset * sub_height_c;
- const int win_top_edge_ctus =
- AV_CEIL_RSHIFT(win_top_edge, ctb_log2_size_y);
- us(hlen, sps_subpic_height_minus1[i],
- win_top_edge_ctus > current->sps_subpic_ctu_top_left_y[i]
- ? win_top_edge_ctus - current->sps_subpic_ctu_top_left_y[i]
- : 0,
- MAX_UINT_BITS(hlen), 1, i);
+ current->sps_pic_height_max_in_luma_samples > ctb_size_y) {
+ us(hlen, sps_subpic_height_minus1[i], min_height, MAX_UINT_BITS(hlen), 1, i);
} else {
- infer(sps_subpic_height_minus1[i],
- tmp_height_val -
- current->sps_subpic_ctu_top_left_y[i] - 1);
+ infer(sps_subpic_height_minus1[i], max_height_minus1);
}
} else {
int num_subpic_cols = tmp_width_val /
@@ -1245,8 +1243,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
} else {
infer(sps_subpic_ctu_top_left_x[0], 0);
infer(sps_subpic_ctu_top_left_y[0], 0);
- infer(sps_subpic_width_minus1[0], tmp_width_val - 1);
- infer(sps_subpic_height_minus1[0], tmp_height_val - 1);
+ infer(sps_subpic_width_minus1[0], max_width_minus1);
+ infer(sps_subpic_height_minus1[0], max_height_minus1);
}
ue(sps_subpic_id_len_minus1, 0, 15);
if ((1 << (current->sps_subpic_id_len_minus1 + 1)) <
@@ -1273,8 +1271,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
infer(sps_subpic_id_mapping_explicitly_signalled_flag, 0);
infer(sps_subpic_ctu_top_left_x[0], 0);
infer(sps_subpic_ctu_top_left_y[0], 0);
- infer(sps_subpic_width_minus1[0], tmp_width_val - 1);
- infer(sps_subpic_height_minus1[0], tmp_height_val - 1);
+ infer(sps_subpic_width_minus1[0], max_width_minus1);
+ infer(sps_subpic_height_minus1[0], max_height_minus1);
}
--
2.49.1
From 8a33c1f7d98cab9f935eff81431aec5ac93f9439 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Sat, 23 Nov 2024 17:32:34 +0800
Subject: [PATCH 08/24] avcodec/cbs_h266: stricter validation for subpicture's
max width and height
Co-authored-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 98698ed3c24bfd0b1e6e6db943b5f25f6046cee7)
---
libavcodec/cbs_h266_syntax_template.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
index 191e73edd5..bac7129f17 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -1147,11 +1147,11 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
infer(sps_subpic_ctu_top_left_x[0], 0);
infer(sps_subpic_ctu_top_left_y[0], 0);
if (current->sps_pic_width_max_in_luma_samples > ctb_size_y)
- ubs(wlen, sps_subpic_width_minus1[0], 1, 0);
+ us(wlen, sps_subpic_width_minus1[0], 0, max_width_minus1, 1, 0);
else
infer(sps_subpic_width_minus1[0], max_width_minus1);
if (current->sps_pic_height_max_in_luma_samples > ctb_size_y)
- ubs(hlen, sps_subpic_height_minus1[0], 1, 0);
+ us(hlen, sps_subpic_height_minus1[0], 0, max_width_minus1, 1, 0);
else
infer(sps_subpic_height_minus1[0], max_height_minus1);
if (!current->sps_independent_subpics_flag) {
@@ -1201,14 +1201,14 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
if (i < current->sps_num_subpics_minus1 &&
current->sps_pic_width_max_in_luma_samples > ctb_size_y) {
- us(wlen, sps_subpic_width_minus1[i], min_width, MAX_UINT_BITS(wlen), 1, i);
+ us(wlen, sps_subpic_width_minus1[i], min_width, max_width_minus1, 1, i);
} else {
infer(sps_subpic_width_minus1[i], max_width_minus1);
}
if (i < current->sps_num_subpics_minus1 &&
current->sps_pic_height_max_in_luma_samples > ctb_size_y) {
- us(hlen, sps_subpic_height_minus1[i], min_height, MAX_UINT_BITS(hlen), 1, i);
+ us(hlen, sps_subpic_height_minus1[i], min_height, max_height_minus1, 1, i);
} else {
infer(sps_subpic_height_minus1[i], max_height_minus1);
}
--
2.49.1
From 49306d0e316b190ca53b23d3a76fa610af509ad0 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Sat, 23 Nov 2024 17:32:38 +0800
Subject: [PATCH 09/24] avcodec/vvcdec: return error if CTU size > 128
The v3 spec reserves CTU size 256. Currently, we use an uint8_t* table to hold cb_width and cb_height.
If a CTU size of 256 is not split, cb_width and cb_height will overflow to 0.
To avoid switching to uint16_t, rejecting CTU size 256 provides a simple solution.
(cherry picked from commit 4de67e874697271e189022b03cd619329d54603c)
---
libavcodec/vvc/ps.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index ff9a6c7a15..e5d854d899 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -649,6 +649,12 @@ static int decode_ps(VVCParamSets *ps, const CodedBitstreamH266Context *h266, vo
if (ret < 0)
return ret;
+ if (rsps->sps_log2_ctu_size_minus5 > 2) {
+ // CTU > 128 are reserved in vvc spec v3
+ av_log(log_ctx, AV_LOG_ERROR, "CTU size > 128. \n");
+ return AVERROR_PATCHWELCOME;
+ }
+
ret = decode_pps(ps, rpps);
if (ret < 0)
return ret;
--
2.49.1
From 839a452dbbfd6c504f37e894c8518b6ecf138908 Mon Sep 17 00:00:00 2001
From: Chris Warrington <cwarrington(a)synamedia.com>
Date: Mon, 2 Dec 2024 14:27:12 +0000
Subject: [PATCH 10/24] avcodec/vvc decode: ALF filtering without CC-ALF
When a stream has ALF filtering enabled but not CC-ALF, the CC-ALF set indexes alf->ctb_cc_idc are being read uninitialized during ALF filtering.
This change initializes alf->ctb_cc_idc whenever ALF is enabled.
Ref. https://trac.ffmpeg.org/ticket/11325
(cherry picked from commit f80af3657f162ba79b872bc27ae06d75560d58a9)
---
libavcodec/vvc/ctu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavcodec/vvc/ctu.c b/libavcodec/vvc/ctu.c
index 634c553814..764de25761 100644
--- a/libavcodec/vvc/ctu.c
+++ b/libavcodec/vvc/ctu.c
@@ -2289,6 +2289,7 @@ static void alf_params(VVCLocalContext *lc, const int rx, const int ry)
ALFParams *alf = &CTB(fc->tab.alf, rx, ry);
alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0;
+ alf->ctb_cc_idc[0] = alf->ctb_cc_idc[1] = 0;
if (sh->sh_alf_enabled_flag) {
alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA);
if (alf->ctb_flag[LUMA]) {
@@ -2319,7 +2320,6 @@ static void alf_params(VVCLocalContext *lc, const int rx, const int ry)
const uint8_t cc_enabled[] = { sh->sh_alf_cc_cb_enabled_flag, sh->sh_alf_cc_cr_enabled_flag };
const uint8_t cc_aps_id[] = { sh->sh_alf_cc_cb_aps_id, sh->sh_alf_cc_cr_aps_id };
for (int i = 0; i < 2; i++) {
- alf->ctb_cc_idc[i] = 0;
if (cc_enabled[i]) {
const VVCALF *aps = fc->ps.alf_list[cc_aps_id[i]];
alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, aps->num_cc_filters[i]);
--
2.49.1
From bda6ce18542842f7c1c4289595c01bf9994ba7d3 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Fri, 10 Jan 2025 21:29:07 +0000
Subject: [PATCH 11/24] lavc/vvc: Check slice structure
The criteria for slice structure validity is similar to that of
subpicture structure validity that we saw not too long ago [1].
The relationship between tiles and slices must satisfy the following
properties:
* Exhaustivity. All tiles in a picture must belong to a slice. The
tiles cover the picture, so this implies the slices must cover the
picture.
* Mutual exclusivity. No tile may belong to more than one slice, i.e.
slices may not overlap.
In most cases these properties are guaranteed by the syntax. There is
one noticable exception however: when pps_tile_idx_delta_present_flag is
equal to one, each slice is associated with a syntax element
pps_tile_idx_delta_val[i] which "specifies the difference between the
tile index of the tile containing the first CTU in the ( i + 1 )-th
rectangular slice and the tile index of the tile containing the first
CTU in the i-th rectangular slice" [2]. When these syntax elements are
present, the i-th slice can begin anywhere and the usual guarantees
provided by the syntax are lost.
The patch detects slice structures which violate either of the two
properties above, and are therefore invalid, while building the
slice map. Should the slice map be determined to be invalid, an
AVERROR_INVALIDDATA is returned. This prevents issues including
segmentation faults when trying to decode, invalid bitstreams.
[1]: https://ffmpeg.org//pipermail/ffmpeg-devel/2024-October/334470.html
[2]: H.266 (V3) Section 7.4.3.5, Picture parameter set RBSP semantics
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 8bd66a8c9587af61c7b46558be3c4ee317c1af5a)
---
libavcodec/vvc/ps.c | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index e5d854d899..66f14a059c 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -20,6 +20,7 @@
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <stdbool.h>
#include "libavcodec/cbs_h266.h"
#include "libavutil/mem.h"
@@ -460,7 +461,7 @@ static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int *off)
return i;
}
-static void pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, int *off)
+static int pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, int *off, bool *tile_in_slice)
{
const H266RawPPS *r = pps->r;
int rx, ry, tile_x, tile_y;
@@ -470,32 +471,51 @@ static void pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i,
pps->num_ctus_in_slice[i] = 0;
for (int ty = tile_y; ty <= tile_y + r->pps_slice_height_in_tiles_minus1[i]; ty++) {
for (int tx = tile_x; tx <= tile_x + r->pps_slice_width_in_tiles_minus1[i]; tx++) {
+ const int idx = ty * r->num_tile_columns + tx;
+ if (tile_in_slice[idx])
+ return AVERROR_INVALIDDATA;
+ tile_in_slice[idx] = true;
ctu_xy(&rx, &ry, tx, ty, pps);
pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, rx, ry,
r->col_width_val[tx], r->row_height_val[ty]);
}
}
+
+ return 0;
}
-static void pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
+static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
{
const H266RawPPS *r = pps->r;
+ bool tile_in_slice[VVC_MAX_TILES_PER_AU] = {false};
int tile_idx = 0, off = 0;
if (r->pps_single_slice_per_subpic_flag) {
pps_single_slice_per_subpic(pps, sps, &off);
- return;
+ return 0;
}
for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) {
if (!r->pps_slice_width_in_tiles_minus1[i] &&
!r->pps_slice_height_in_tiles_minus1[i]) {
+ if (tile_in_slice[tile_idx])
+ return AVERROR_INVALIDDATA;
+ tile_in_slice[tile_idx] = true;
i = pps_one_tile_slices(pps, tile_idx, i, &off);
} else {
- pps_multi_tiles_slice(pps, tile_idx, i, &off);
+ const int ret = pps_multi_tiles_slice(pps, tile_idx, i, &off, tile_in_slice);
+ if (ret < 0)
+ return ret;
}
tile_idx = next_tile_idx(tile_idx, i, r);
}
+
+ for (int i = 0; i < r->num_tiles_in_pic; i++) {
+ if (!tile_in_slice[i])
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
}
static void pps_no_rect_slice(VVCPPS* pps)
@@ -518,9 +538,9 @@ static int pps_slice_map(VVCPPS *pps, const VVCSPS *sps)
return AVERROR(ENOMEM);
if (pps->r->pps_rect_slice_flag)
- pps_rect_slice(pps, sps);
- else
- pps_no_rect_slice(pps);
+ return pps_rect_slice(pps, sps);
+
+ pps_no_rect_slice(pps);
return 0;
}
--
2.49.1
From 78bd48ece9f9a117b1338f59891ab73d269af2d7 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <zhilizhao(a)tencent.com>
Date: Sun, 19 Jan 2025 00:35:06 +0800
Subject: [PATCH 12/24] avcodec/vvc: Add support for output_corrupt/showall
flags
(cherry picked from commit ea381285e7d9c5fd3f74e19bc699ca98bd653ca5)
---
libavcodec/vvc/refs.c | 21 ++++++++++++++++++++-
libavcodec/vvc/refs.h | 1 +
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/libavcodec/vvc/refs.c b/libavcodec/vvc/refs.c
index 358e11242a..c9cf261355 100644
--- a/libavcodec/vvc/refs.c
+++ b/libavcodec/vvc/refs.c
@@ -21,6 +21,7 @@
*/
#include <stdatomic.h>
+#include <stdbool.h>
#include "libavutil/mem.h"
#include "libavutil/thread.h"
@@ -46,6 +47,8 @@ void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags)
return;
frame->flags &= ~flags;
+ if (!(frame->flags & ~VVC_FRAME_FLAG_CORRUPT))
+ frame->flags = 0;
if (!frame->flags) {
av_frame_unref(frame->frame);
ff_refstruct_unref(&frame->sps);
@@ -242,6 +245,9 @@ int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *out, const
if (nb_output) {
VVCFrame *frame = &fc->DPB[min_idx];
+ if (frame->flags & VVC_FRAME_FLAG_CORRUPT)
+ frame->frame->flags |= AV_FRAME_FLAG_CORRUPT;
+
ret = av_frame_ref(out, frame->frame);
if (frame->flags & VVC_FRAME_FLAG_BUMPING)
ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT | VVC_FRAME_FLAG_BUMPING);
@@ -351,7 +357,7 @@ static VVCFrame *generate_missing_ref(VVCContext *s, VVCFrameContext *fc, int po
frame->poc = poc;
frame->sequence = s->seq_decode;
- frame->flags = 0;
+ frame->flags = VVC_FRAME_FLAG_CORRUPT;
ff_vvc_report_frame_finished(frame);
@@ -386,6 +392,19 @@ static int add_candidate_ref(VVCContext *s, VVCFrameContext *fc, RefPicList *lis
if (ref == fc->ref || list->nb_refs >= VVC_MAX_REF_ENTRIES)
return AVERROR_INVALIDDATA;
+ if (!IS_CVSS(s)) {
+ const bool ref_corrupt = !ref || (ref->flags & VVC_FRAME_FLAG_CORRUPT);
+ const bool recovering = s->no_output_before_recovery_flag && !GDR_IS_RECOVERED(s);
+
+ if (ref_corrupt && !recovering) {
+ if (!(s->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) &&
+ !(s->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL))
+ return AVERROR_INVALIDDATA;
+
+ fc->ref->flags |= VVC_FRAME_FLAG_CORRUPT;
+ }
+ }
+
if (!ref) {
ref = generate_missing_ref(s, fc, poc);
if (!ref)
diff --git a/libavcodec/vvc/refs.h b/libavcodec/vvc/refs.h
index e2271ab381..a3081a76be 100644
--- a/libavcodec/vvc/refs.h
+++ b/libavcodec/vvc/refs.h
@@ -29,6 +29,7 @@
#define VVC_FRAME_FLAG_SHORT_REF (1 << 1)
#define VVC_FRAME_FLAG_LONG_REF (1 << 2)
#define VVC_FRAME_FLAG_BUMPING (1 << 3)
+#define VVC_FRAME_FLAG_CORRUPT (1 << 4)
int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, struct AVFrame *out, int no_output_of_prior_pics_flag, int flush);
void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc);
--
2.49.1
From 056680ed263aaa36550831b2d54049dd918d79c0 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021(a)gmail.com>
Date: Sun, 26 Jan 2025 11:10:21 +0800
Subject: [PATCH 13/24] lavc/vvcdec: ensure slices contain nonzero CTUs
fixes https://github.com/ffvvc/tests/tree/main/fuzz/passed/000323.bit
Co-authored-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit ca3550948c4f1d9fe7dbdc24572b92e588846c7b)
---
libavcodec/vvc/ps.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 66f14a059c..cca6b413e5 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -1224,7 +1224,7 @@ static int sh_alf_aps(const VVCSH *sh, const VVCFrameParamSets *fps)
return 0;
}
-static void sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps)
+static int sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps)
{
const int slice_address = sh->r->sh_slice_address;
@@ -1248,6 +1248,11 @@ static void sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps
sh->num_ctus_in_curr_slice += pps->r->row_height_val[tile_y] * pps->r->col_width_val[tile_x];
}
}
+
+ if (!sh->num_ctus_in_curr_slice)
+ return AVERROR_INVALIDDATA;
+
+ return 0;
}
static void sh_qp_y(VVCSH *sh, const H266RawPPS *pps, const H266RawPictureHeader *ph)
@@ -1344,7 +1349,9 @@ static int sh_derive(VVCSH *sh, const VVCFrameParamSets *fps)
const H266RawPictureHeader *ph = fps->ph.r;
int ret;
- sh_slice_address(sh, sps, fps->pps);
+ ret = sh_slice_address(sh, sps, fps->pps);
+ if (ret < 0)
+ return ret;
ret = sh_alf_aps(sh, fps);
if (ret < 0)
return ret;
--
2.49.1
From 223fee5bb67b30eb1f1f9aaa930c38c7f9a4e438 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sat, 8 Feb 2025 21:42:56 +0000
Subject: [PATCH 14/24] lavc/vvc: Set fc->ref to NULL at top of
decode_nal_units
In the fail: block of decode_nal_units, a check as to whether fc->ref is
nonzero is used. Before this patch, fc->ref was set to NULL in
frame_context_setup. The issue is that, by the time frame_context_setup
is called, falliable functions (namely slices_realloc and
ff_vvc_decode_frame_ps) have already been called. Therefore, there
could arise a situation in which the fc->ref test of decode_nal_units'
fail: block is performed while fc->ref has an invalid value. This seems
to be particularly prevalent in situations where the FrameContexts are
being reused. The patch resolves the issue by moving the assignment of
fc->ref to NULL to the very top of decode_nal_units, before any falliable
functions are called.
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit e417f939da2d04abfe6ad1f93aa47be334b66771)
---
libavcodec/vvc/dec.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libavcodec/vvc/dec.c b/libavcodec/vvc/dec.c
index 522fa8416e..6fefa0deec 100644
--- a/libavcodec/vvc/dec.c
+++ b/libavcodec/vvc/dec.c
@@ -672,8 +672,6 @@ static int frame_context_setup(VVCFrameContext *fc, VVCContext *s)
{
int ret;
- fc->ref = NULL;
-
// copy refs from the last frame
if (s->nb_frames && s->nb_fcs > 1) {
VVCFrameContext *prev = get_frame_context(s, fc, -1);
@@ -877,6 +875,7 @@ static int decode_nal_units(VVCContext *s, VVCFrameContext *fc, AVPacket *avpkt)
int ret = 0;
s->last_eos = s->eos;
s->eos = 0;
+ fc->ref = NULL;
ff_cbs_fragment_reset(frame);
ret = ff_cbs_read_packet(s->cbc, frame, avpkt);
--
2.49.1
From b844985520fedd26b2bce5237aff4c62123b68ae Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sun, 2 Feb 2025 12:10:48 +0000
Subject: [PATCH 15/24] lavc/vvc: Fix derivation of inverse LMCS idx
The clamping of idxYInv from H.266(V3) section 8.8.2.3 was missing.
This could lead to OOB reads from lmcs->pivot or input_pivot.
I also changed the derivation of the forward LMCS idx to use a shift
rather than a division for speed and as this is actually how the
variable is declared in the specification (8.7.5.2).
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 392aa9daf2c730465f6aba5ba2b37e02695de9f7)
---
libavcodec/vvc/ps.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index cca6b413e5..3b972be945 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -786,7 +786,7 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266Raw
//derive lmcs_fwd_lut
for (uint16_t sample = 0; sample < max; sample++) {
- const int idx_y = sample / org_cw;
+ const int idx_y = sample >> shift;
const uint16_t fwd_sample = lmcs_derive_lut_sample(sample, lmcs->pivot,
input_pivot, scale_coeff, idx_y, max);
if (bit_depth > 8)
@@ -802,6 +802,7 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266Raw
uint16_t inv_sample;
while (i <= lmcs->max_bin_idx && sample >= lmcs->pivot[i + 1])
i++;
+ i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1);
inv_sample = lmcs_derive_lut_sample(sample, input_pivot, lmcs->pivot,
inv_scale_coeff, i, max);
--
2.49.1
From 71706b76150c7d24ad0f3c10e21eb6ba5423c7be Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sat, 22 Feb 2025 15:51:54 +0800
Subject: [PATCH 16/24] lavc/vvc: Fix slice map construction for small subpics
In the case pps_subpic_less_than_one_tile_slice is called, the
subpicture is smaller than the tile and so there are multiple
subpictures in the tile. Of course, then, not all the
subpictures can start in the top-left corner as the code before the
patch does. Patch fixes this, so each subpicture starts at the
signalled location as is specified in section 6.5.1 of H.266(V3).
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 93aae172ea909ec96b67077b8b3005886a83d0ae)
---
libavcodec/vvc/ps.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 3b972be945..0cc02443da 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -404,8 +404,8 @@ static void subpic_tiles(int *tile_x, int *tile_y, int *tile_x_end, int *tile_y_
static void pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, const int i, const int tx, const int ty, int *off)
{
pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off,
- pps->col_bd[tx], pps->row_bd[ty],
- pps->r->col_width_val[tx], sps->r->sps_subpic_height_minus1[i] + 1);
+ sps->r->sps_subpic_ctu_top_left_x[i], sps->r->sps_subpic_ctu_top_left_y[i],
+ sps->r->sps_subpic_width_minus1[i] + 1, sps->r->sps_subpic_height_minus1[i] + 1);
}
static void pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, const int tile_y, const int x_end, const int y_end, const int i, int *off)
--
2.49.1
From c94f4aadd6e21dfe7952e09b35f16fe58d655b71 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sat, 22 Feb 2025 15:51:55 +0800
Subject: [PATCH 17/24] lavc/vvc: Ensure subpictures don't overlap
This is essentially a re-implementation of
https://patchwork.ffmpeg.org/project/ffmpeg/patch/20241005223955.54158-1-po…
That patch was not applied last time. Instead we opted to identify
issues which could be caused by invalid subpicture layouts and remedy
those issues where they manifest, either through error detection or code
hardening. This was primarily implemented in the set
https://patchwork.ffmpeg.org/project/ffmpeg/list/?series=13381.
This has worked to some degree, however issues with subpicture layouts
continue to crop up from the fuzzer and I've fixed a number of bugs
related to subpicture layouts since then. I think it's best to return
to the initial plan and simply check if the subpicture layout is valid
initially.
This implementation is also lighter than the first time -- by doing a
bit more logic in pps_subpic_less_than_one_tile_slice, we are able to
store a tile_in_subpic map rather than a ctu_in_subpic map. This
reduces the size of the map to the point it becomes possible to allocate
it on the stack. Similar to 8bd66a8c9587af61c7b46558be3c4ee317c1af5a,
the layout is also validated in the slice map construction code, rather
than in the CBS, which avoids duplicating some logic.
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit d5dbcc00d889fb17948b025a468b00ddbea9e058)
---
libavcodec/vvc/ps.c | 55 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 45 insertions(+), 10 deletions(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 0cc02443da..01b4d69ceb 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -401,25 +401,50 @@ static void subpic_tiles(int *tile_x, int *tile_y, int *tile_x_end, int *tile_y_
(*tile_y_end)++;
}
-static void pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, const int i, const int tx, const int ty, int *off)
+static bool mark_tile_as_used(bool *tile_in_subpic, const int tx, const int ty, const int tile_columns)
{
+ const size_t tile_idx = ty * tile_columns + tx;
+ if (tile_in_subpic[tile_idx]) {
+ /* the tile is covered by other subpictures */
+ return false;
+ }
+ tile_in_subpic[tile_idx] = true;
+ return true;
+}
+
+static int pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, const int i, const int tx, const int ty, int *off, bool *tile_in_subpic)
+{
+ const int subpic_bottom = sps->r->sps_subpic_ctu_top_left_y[i] + sps->r->sps_subpic_height_minus1[i];
+ const int tile_bottom = pps->row_bd[ty] + pps->r->row_height_val[ty] - 1;
+ const bool is_final_subpic_in_tile = subpic_bottom == tile_bottom;
+
+ if (is_final_subpic_in_tile && !mark_tile_as_used(tile_in_subpic, tx, ty, pps->r->num_tile_columns))
+ return AVERROR_INVALIDDATA;
+
pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off,
sps->r->sps_subpic_ctu_top_left_x[i], sps->r->sps_subpic_ctu_top_left_y[i],
sps->r->sps_subpic_width_minus1[i] + 1, sps->r->sps_subpic_height_minus1[i] + 1);
+
+ return 0;
}
-static void pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, const int tile_y, const int x_end, const int y_end, const int i, int *off)
+static int pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, const int tile_y, const int x_end, const int y_end,
+ const int i, int *off, bool *tile_in_subpic)
{
for (int ty = tile_y; ty < y_end; ty++) {
for (int tx = tile_x; tx < x_end; tx++) {
+ if (!mark_tile_as_used(tile_in_subpic, tx, ty, pps->r->num_tile_columns))
+ return AVERROR_INVALIDDATA;
+
pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off,
pps->col_bd[tx], pps->row_bd[ty],
pps->r->col_width_val[tx], pps->r->row_height_val[ty]);
}
}
+ return 0;
}
-static void pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int *off)
+static int pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int *off, bool *tile_in_subpic)
{
int tx, ty, x_end, y_end;
@@ -428,19 +453,30 @@ static void pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int *o
subpic_tiles(&tx, &ty, &x_end, &y_end, sps, pps, i);
if (ty + 1 == y_end && sps->r->sps_subpic_height_minus1[i] + 1 < pps->r->row_height_val[ty])
- pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off);
+ return pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off, tile_in_subpic);
else
- pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, i, off);
+ return pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, i, off, tile_in_subpic);
}
-static void pps_single_slice_per_subpic(VVCPPS *pps, const VVCSPS *sps, int *off)
+static int pps_single_slice_per_subpic(VVCPPS *pps, const VVCSPS *sps, int *off)
{
if (!sps->r->sps_subpic_info_present_flag) {
pps_single_slice_picture(pps, off);
} else {
- for (int i = 0; i < pps->r->pps_num_slices_in_pic_minus1 + 1; i++)
- pps_subpic_slice(pps, sps, i, off);
+ bool tile_in_subpic[VVC_MAX_TILES_PER_AU] = {0};
+ for (int i = 0; i < pps->r->pps_num_slices_in_pic_minus1 + 1; i++) {
+ const int ret = pps_subpic_slice(pps, sps, i, off, tile_in_subpic);
+ if (ret < 0)
+ return ret;
+ }
+
+ // We only use tile_in_subpic to check that the subpictures don't overlap
+ // here; we don't use tile_in_subpic to check that the subpictures cover
+ // every tile. It is possible to avoid doing this work here because the
+ // covering property of subpictures is already guaranteed by the mechanisms
+ // which check every CTU belongs to a slice.
}
+ return 0;
}
static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int *off)
@@ -491,8 +527,7 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
int tile_idx = 0, off = 0;
if (r->pps_single_slice_per_subpic_flag) {
- pps_single_slice_per_subpic(pps, sps, &off);
- return 0;
+ return pps_single_slice_per_subpic(pps, sps, &off);
}
for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) {
--
2.49.1
From e2af7ee96d31056681e0839e04b8624479d68b84 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sun, 23 Feb 2025 12:04:48 +0000
Subject: [PATCH 18/24] lavc/vvc: Fix pps_single_slice_picture
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 600ad36949a70c0187aa671469ebd81212528d61)
---
libavcodec/vvc/ps.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 01b4d69ceb..7ed54344c0 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -368,9 +368,10 @@ static int pps_add_ctus(VVCPPS *pps, int *off, const int rx, const int ry,
static void pps_single_slice_picture(VVCPPS *pps, int *off)
{
+ pps->num_ctus_in_slice[0] = 0;
for (int j = 0; j < pps->r->num_tile_rows; j++) {
for (int i = 0; i < pps->r->num_tile_columns; i++) {
- pps->num_ctus_in_slice[0] = pps_add_ctus(pps, off,
+ pps->num_ctus_in_slice[0] += pps_add_ctus(pps, off,
pps->col_bd[i], pps->row_bd[j],
pps->r->col_width_val[i], pps->r->row_height_val[j]);
}
--
2.49.1
From 28f72f1f7da29f084e40803795f015f7ef3f67aa Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sun, 18 May 2025 14:13:26 +0100
Subject: [PATCH 19/24] lavc/vvc: Detect subpic overlaps at CTU level
In d5dbcc00d889fb17948b025a468b00ddbea9e058, it was hoped that detection
of subpicture overlaps could be performed at the tile level, so as to
avoid introducing per-CTU checks. Unfortunately since that patch,
fuzzing has indicated there are some structures involving
pps_subpic_one_or_more_tiles_slice where tile-level checking is not
sufficient. Performing the check at the CTU level should (touch wood)
be the be-all and and-all of this, as CTUs are the lowest common
denominator of the picture partitioning.
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit a18b2c26962480de3aa2df60c2abe80742c0d9ed)
---
libavcodec/vvc/ps.c | 104 ++++++++++++++++++++++++--------------------
1 file changed, 57 insertions(+), 47 deletions(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 7ed54344c0..0312f37503 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -359,6 +359,8 @@ static int pps_add_ctus(VVCPPS *pps, int *off, const int rx, const int ry,
int start = *off;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
+ if (*off >= pps->ctb_count)
+ return AVERROR_INVALIDDATA;
pps->ctb_addr_in_slice[*off] = ctu_rs(rx + x, ry + y, pps);
(*off)++;
}
@@ -366,16 +368,21 @@ static int pps_add_ctus(VVCPPS *pps, int *off, const int rx, const int ry,
return *off - start;
}
-static void pps_single_slice_picture(VVCPPS *pps, int *off)
+static int pps_single_slice_picture(VVCPPS *pps, int *off)
{
pps->num_ctus_in_slice[0] = 0;
for (int j = 0; j < pps->r->num_tile_rows; j++) {
for (int i = 0; i < pps->r->num_tile_columns; i++) {
- pps->num_ctus_in_slice[0] += pps_add_ctus(pps, off,
+ const int ret = pps_add_ctus(pps, off,
pps->col_bd[i], pps->row_bd[j],
pps->r->col_width_val[i], pps->r->row_height_val[j]);
+ if (ret < 0)
+ return ret;
+ pps->num_ctus_in_slice[0] += ret;
}
}
+
+ return 0;
}
static void subpic_tiles(int *tile_x, int *tile_y, int *tile_x_end, int *tile_y_end,
@@ -402,50 +409,36 @@ static void subpic_tiles(int *tile_x, int *tile_y, int *tile_x_end, int *tile_y_
(*tile_y_end)++;
}
-static bool mark_tile_as_used(bool *tile_in_subpic, const int tx, const int ty, const int tile_columns)
+static int pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, const int i, const int tx, const int ty, int *off)
{
- const size_t tile_idx = ty * tile_columns + tx;
- if (tile_in_subpic[tile_idx]) {
- /* the tile is covered by other subpictures */
- return false;
- }
- tile_in_subpic[tile_idx] = true;
- return true;
-}
-
-static int pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, const int i, const int tx, const int ty, int *off, bool *tile_in_subpic)
-{
- const int subpic_bottom = sps->r->sps_subpic_ctu_top_left_y[i] + sps->r->sps_subpic_height_minus1[i];
- const int tile_bottom = pps->row_bd[ty] + pps->r->row_height_val[ty] - 1;
- const bool is_final_subpic_in_tile = subpic_bottom == tile_bottom;
-
- if (is_final_subpic_in_tile && !mark_tile_as_used(tile_in_subpic, tx, ty, pps->r->num_tile_columns))
- return AVERROR_INVALIDDATA;
-
- pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off,
+ const int ret = pps_add_ctus(pps, off,
sps->r->sps_subpic_ctu_top_left_x[i], sps->r->sps_subpic_ctu_top_left_y[i],
sps->r->sps_subpic_width_minus1[i] + 1, sps->r->sps_subpic_height_minus1[i] + 1);
+ if (ret < 0)
+ return ret;
+ pps->num_ctus_in_slice[i] = ret;
return 0;
}
static int pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, const int tile_y, const int x_end, const int y_end,
- const int i, int *off, bool *tile_in_subpic)
+ const int i, int *off)
{
for (int ty = tile_y; ty < y_end; ty++) {
for (int tx = tile_x; tx < x_end; tx++) {
- if (!mark_tile_as_used(tile_in_subpic, tx, ty, pps->r->num_tile_columns))
- return AVERROR_INVALIDDATA;
-
- pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off,
+ const int ret = pps_add_ctus(pps, off,
pps->col_bd[tx], pps->row_bd[ty],
pps->r->col_width_val[tx], pps->r->row_height_val[ty]);
+ if (ret < 0)
+ return ret;
+
+ pps->num_ctus_in_slice[i] += ret;
}
}
return 0;
}
-static int pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int *off, bool *tile_in_subpic)
+static int pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int *off)
{
int tx, ty, x_end, y_end;
@@ -454,28 +447,25 @@ static int pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int *of
subpic_tiles(&tx, &ty, &x_end, &y_end, sps, pps, i);
if (ty + 1 == y_end && sps->r->sps_subpic_height_minus1[i] + 1 < pps->r->row_height_val[ty])
- return pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off, tile_in_subpic);
+ return pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off);
else
- return pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, i, off, tile_in_subpic);
+ return pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, i, off);
}
static int pps_single_slice_per_subpic(VVCPPS *pps, const VVCSPS *sps, int *off)
{
+ int ret;
+
if (!sps->r->sps_subpic_info_present_flag) {
- pps_single_slice_picture(pps, off);
+ ret = pps_single_slice_picture(pps, off);
+ if (ret < 0)
+ return ret;
} else {
- bool tile_in_subpic[VVC_MAX_TILES_PER_AU] = {0};
for (int i = 0; i < pps->r->pps_num_slices_in_pic_minus1 + 1; i++) {
- const int ret = pps_subpic_slice(pps, sps, i, off, tile_in_subpic);
+ const int ret = pps_subpic_slice(pps, sps, i, off);
if (ret < 0)
return ret;
}
-
- // We only use tile_in_subpic to check that the subpictures don't overlap
- // here; we don't use tile_in_subpic to check that the subpictures cover
- // every tile. It is possible to avoid doing this work here because the
- // covering property of subpictures is already guaranteed by the mechanisms
- // which check every CTU belongs to a slice.
}
return 0;
}
@@ -489,9 +479,13 @@ static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int *off)
ctu_xy(&rx, &ry, tile_x, tile_y, pps);
ctu_y_end = ry + r->row_height_val[tile_y];
while (ry < ctu_y_end) {
+ int ret;
pps->slice_start_offset[i] = *off;
- pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off, rx, ry,
+ ret = pps_add_ctus(pps, off, rx, ry,
r->col_width_val[tile_x], r->slice_height_in_ctus[i]);
+ if (ret < 0)
+ return ret;
+ pps->num_ctus_in_slice[i] = ret;
ry += r->slice_height_in_ctus[i++];
}
i--;
@@ -508,13 +502,17 @@ static int pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, i
pps->num_ctus_in_slice[i] = 0;
for (int ty = tile_y; ty <= tile_y + r->pps_slice_height_in_tiles_minus1[i]; ty++) {
for (int tx = tile_x; tx <= tile_x + r->pps_slice_width_in_tiles_minus1[i]; tx++) {
+ int ret;
const int idx = ty * r->num_tile_columns + tx;
if (tile_in_slice[idx])
return AVERROR_INVALIDDATA;
tile_in_slice[idx] = true;
ctu_xy(&rx, &ry, tx, ty, pps);
- pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, rx, ry,
+ ret = pps_add_ctus(pps, off, rx, ry,
r->col_width_val[tx], r->row_height_val[ty]);
+ if (ret < 0)
+ return ret;
+ pps->num_ctus_in_slice[i] += ret;
}
}
@@ -525,7 +523,7 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
{
const H266RawPPS *r = pps->r;
bool tile_in_slice[VVC_MAX_TILES_PER_AU] = {false};
- int tile_idx = 0, off = 0;
+ int tile_idx = 0, off = 0, ret;
if (r->pps_single_slice_per_subpic_flag) {
return pps_single_slice_per_subpic(pps, sps, &off);
@@ -537,9 +535,12 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
if (tile_in_slice[tile_idx])
return AVERROR_INVALIDDATA;
tile_in_slice[tile_idx] = true;
- i = pps_one_tile_slices(pps, tile_idx, i, &off);
+ ret = pps_one_tile_slices(pps, tile_idx, i, &off);
+ if (ret < 0)
+ return ret;
+ i = ret;
} else {
- const int ret = pps_multi_tiles_slice(pps, tile_idx, i, &off, tile_in_slice);
+ ret = pps_multi_tiles_slice(pps, tile_idx, i, &off, tile_in_slice);
if (ret < 0)
return ret;
}
@@ -554,21 +555,28 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
return 0;
}
-static void pps_no_rect_slice(VVCPPS* pps)
+static int pps_no_rect_slice(VVCPPS* pps)
{
const H266RawPPS* r = pps->r;
int rx, ry, off = 0;
for (int tile_y = 0; tile_y < r->num_tile_rows; tile_y++) {
for (int tile_x = 0; tile_x < r->num_tile_columns; tile_x++) {
+ int ret;
ctu_xy(&rx, &ry, tile_x, tile_y, pps);
- pps_add_ctus(pps, &off, rx, ry, r->col_width_val[tile_x], r->row_height_val[tile_y]);
+ ret = pps_add_ctus(pps, &off, rx, ry, r->col_width_val[tile_x], r->row_height_val[tile_y]);
+ if (ret < 0)
+ return ret;
}
}
+
+ return 0;
}
static int pps_slice_map(VVCPPS *pps, const VVCSPS *sps)
{
+ int ret;
+
pps->ctb_addr_in_slice = av_calloc(pps->ctb_count, sizeof(*pps->ctb_addr_in_slice));
if (!pps->ctb_addr_in_slice)
return AVERROR(ENOMEM);
@@ -576,7 +584,9 @@ static int pps_slice_map(VVCPPS *pps, const VVCSPS *sps)
if (pps->r->pps_rect_slice_flag)
return pps_rect_slice(pps, sps);
- pps_no_rect_slice(pps);
+ ret = pps_no_rect_slice(pps);
+ if (ret < 0)
+ return ret;
return 0;
}
--
2.49.1
From 31d07bd35e5a62905534520393296a7ee81081ea Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sat, 24 May 2025 13:12:25 +0100
Subject: [PATCH 20/24] lavc/vvc: Fix divide-by-zero in LMCS param derivation
Add three missing requirements on bitstream conformance from 7.4.3.19 of
H.266 (V3). Issue found using fuzzing.
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 0382291811ef69e465c096ddf509e0b1e180a5f1)
---
libavcodec/vvc/ps.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 0312f37503..032cc3e853 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -800,7 +800,7 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266Raw
uint16_t input_pivot[LMCS_MAX_BIN_SIZE];
uint16_t scale_coeff[LMCS_MAX_BIN_SIZE];
uint16_t inv_scale_coeff[LMCS_MAX_BIN_SIZE];
- int i, delta_crs;
+ int i, delta_crs, sum_cw = 0;
if (bit_depth > LMCS_MAX_BIT_DEPTH)
return AVERROR_PATCHWELCOME;
@@ -811,8 +811,12 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266Raw
lmcs->max_bin_idx = LMCS_MAX_BIN_SIZE - 1 - rlmcs->lmcs_min_bin_idx;
memset(cw, 0, sizeof(cw));
- for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++)
+ for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) {
cw[i] = org_cw + (1 - 2 * rlmcs->lmcs_delta_sign_cw_flag[i]) * rlmcs->lmcs_delta_abs_cw[i];
+ sum_cw += cw[i];
+ }
+ if (sum_cw > (1 << bit_depth) - 1)
+ return AVERROR_INVALIDDATA;
delta_crs = (1 - 2 * rlmcs->lmcs_delta_sign_crs_flag) * rlmcs->lmcs_delta_abs_crs;
@@ -820,13 +824,20 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266Raw
for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) {
input_pivot[i] = i * org_cw;
lmcs->pivot[i + 1] = lmcs->pivot[i] + cw[i];
+ if (i >= lmcs->min_bin_idx && i <= lmcs->max_bin_idx &&
+ lmcs->pivot[i] % (1 << (bit_depth - 5)) != 0 &&
+ lmcs->pivot[i] >> (bit_depth - 5) == lmcs->pivot[i + 1] >> (bit_depth - 5))
+ return AVERROR_INVALIDDATA;
scale_coeff[i] = (cw[i] * (1 << 11) + off) >> shift;
if (cw[i] == 0) {
inv_scale_coeff[i] = 0;
lmcs->chroma_scale_coeff[i] = (1 << 11);
} else {
+ const int cw_plus_d = cw[i] + delta_crs;
+ if (cw_plus_d < (org_cw >> 3) || cw_plus_d > ((org_cw << 3) - 1))
+ return AVERROR_INVALIDDATA;
inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i];
- lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + delta_crs);
+ lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / cw_plus_d;
}
}
--
2.49.1
From 10a61f27d906237200f3fd68172595fd522ec386 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Sat, 21 Jun 2025 13:05:14 +0100
Subject: [PATCH 21/24] lavc/vvc: Fix condition for using default scaling
factor
Add handling here for
sps_scaling_matrix_for_alternative_colour_space_disabled_flag.
Also add parentheses to make behaviour a little more explicit,
where &&'s precedence over || was relied on previously.
Reported-by: Michael Niedermayer <michael(a)niedermayer.cc>
Signed-off-by: Frank Plowman <post(a)frankplowman.com>
(cherry picked from commit 540a2497d2385f94a661a6bbe0f088636d972783)
---
libavcodec/vvc/intra.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libavcodec/vvc/intra.c b/libavcodec/vvc/intra.c
index 41ed89c946..c08e79df7e 100644
--- a/libavcodec/vvc/intra.c
+++ b/libavcodec/vvc/intra.c
@@ -390,10 +390,10 @@ static const uint8_t* derive_scale_m(const VVCLocalContext *lc, const TransformB
const int log2_matrix_size = (id < 2) ? 1 : (id < 8) ? 2 : 3;
uint8_t *p = scale_m;
- av_assert0(!sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag);
-
if (!rsh->sh_explicit_scaling_list_used_flag || tb->ts ||
- sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx])
+ (sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx]) ||
+ (sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag &&
+ sps->r->sps_scaling_matrix_designated_colour_space_flag == cu->act_enabled_flag))
return ff_vvc_default_scale_m;
if (!sl) {
--
2.49.1
From a4029dae4949b0ae3624895bd0d9209421098589 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Tue, 29 Jul 2025 21:42:26 +0100
Subject: [PATCH 22/24] lavc/vvc: Mark SPS used if multiple CLVSSs use it
Consider the following sequence of NALUs (with some PPSs etc. omitted
for brevity):
1. SPS (ID=0, content=A)
2. IDR (SPS=0)
3. IDR (SPS=0)
4. SPS (ID=0, content=B)
5. TRAIL (SPS=0)
When decode_sps is called for NALU 3., ps->sps_id_used is cleared as
IDRs are one way of forming a CLVSS. Then, old_sps is non-NULL
containing the result of calling decode_sps for NALU 2. We haven't
received any SPSs between NALUs 2. and 3., therefore old_sps and rsps
are identical and the function returns. The issue is that, at this
point, ps->sps_id_used is still zero despite the SPS being used for IDR
3. This results in the check for conflicting SPSs not working properly
when decode_sps is called for NALU 5., allowing prediction between
pictures with different SPSs and probably all sorts of other
shenanigans.
Patch addresses the problem outlined above by also setting
ps->sps_id_used in the early return case.
(cherry picked from commit f82748d5e0320e33d2bc276517467bcf44b19ac4)
---
libavcodec/vvc/ps.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 032cc3e853..cbbb7eb66f 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -239,9 +239,10 @@ static int decode_sps(VVCParamSets *ps, const H266RawSPS *rsps, void *log_ctx, i
}
if (old_sps) {
- if (old_sps->r == rsps || !memcmp(old_sps->r, rsps, sizeof(*old_sps->r)))
+ if (old_sps->r == rsps || !memcmp(old_sps->r, rsps, sizeof(*old_sps->r))) {
+ ps->sps_id_used |= (1 << sps_id);
return 0;
- else if (ps->sps_id_used & (1 << sps_id))
+ } else if (ps->sps_id_used & (1 << sps_id))
return AVERROR_INVALIDDATA;
}
--
2.49.1
From e21fb0a3e844fc27c709ad8ff66932ecf2d40814 Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Wed, 24 Dec 2025 15:35:06 +0000
Subject: [PATCH 23/24] lavc/vvc: Error on inter slice with no reference pics
The semantics of sh_num_ref_idx_active_minus1[ i ] state that
When the current slice is a P slice, the value of NumRefIdxActive[ 0 ] shall be greater than 0.
When the current slice is a B slice, both NumRefIdxActive[ 0 ] and NumRefIdxActive[ 1 ] shall be greater than 0.
Fixes: use of uninitialized memory
Fixes: 449549597/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_VVC_fuzzer-5600497089445888
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Reported-by: Michael Niedermayer michael(a)niedermayer.cc
(cherry picked from commit 90f1f797aa9231375e4858df523fbfeda89bfd79)
---
libavcodec/cbs_h266_syntax_template.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
index bac7129f17..078aaf75df 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -3243,6 +3243,12 @@ static int FUNC(slice_header) (CodedBitstreamContext *ctx, RWContext *rw,
FFMIN(ref_pic_lists->rpl_ref_list[i].num_ref_entries,
pps->pps_num_ref_idx_default_active_minus1[i] + 1);
}
+
+ if (current->num_ref_idx_active[i] <= 0) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR,
+ "Inter slice but no reference pictures available for RPL%d.\n", i);
+ return AVERROR_INVALIDDATA;
+ }
} else {
current->num_ref_idx_active[i] = 0;
}
--
2.49.1
From bfff74732ec655c0d9464119a3be9519a28ca04e Mon Sep 17 00:00:00 2001
From: Frank Plowman <post(a)frankplowman.com>
Date: Mon, 29 Dec 2025 22:14:53 +0000
Subject: [PATCH 24/24] lavc/vvc: Prevent OOB write to slice_top_left_ctu_x in
PPS CBS
Prior to the fix, in the case of a tile containing multiple slices
(pps_num_exp_slices_in_tile != 0) the number of slices was temporarily
allowed to exceed pps_num_slices_in_pic_minus1+1 and therefore
VVC_MAX_SLICES. The number of slices was later verified, but while the
current slice index was higher than expected it was used to write to a
array of size VVC_MAX_SLICES, leading to an OOB write.
To rectify this, the patch adds some checks at an earlier stage, to
ensure that the slice index i + j at no point exceeds
pps_num_slices_in_pic_minus1.
Fixes #YWH-PGM40646-30
(cherry picked from commit 72a38c12e5b84ccb30fba88c39ef2a086013af5b)
---
libavcodec/cbs_h266_syntax_template.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
index 078aaf75df..96d0af9764 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -2017,6 +2017,12 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, RWContext *rw,
slice_top_left_ctu_y[i] = ctu_y;
} else {
uint16_t slice_height_in_ctus;
+ int num_uniform_slices;
+
+ if (i + current->pps_num_exp_slices_in_tile[i] >
+ current->pps_num_slices_in_pic_minus1 + 1)
+ return AVERROR_INVALIDDATA;
+
for (j = 0; j < current->pps_num_exp_slices_in_tile[i];
j++) {
ues(pps_exp_slice_height_in_ctus_minus1[i][j], 0,
@@ -2037,6 +2043,13 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, RWContext *rw,
uniform_slice_height = 1 +
(j == 0 ? current->row_height_val[tile_y] - 1:
current->pps_exp_slice_height_in_ctus_minus1[i][j-1]);
+
+ num_uniform_slices = (remaining_height_in_ctbs_y + uniform_slice_height - 1)
+ / uniform_slice_height;
+ if (i + current->pps_num_exp_slices_in_tile[i] + num_uniform_slices >
+ current->pps_num_slices_in_pic_minus1 + 1)
+ return AVERROR_INVALIDDATA;
+
while (remaining_height_in_ctbs_y > uniform_slice_height) {
current->slice_height_in_ctus[i + j] =
uniform_slice_height;
--
2.49.1
1
0
30 Dec '25
PR #21327 opened by Olivier Crête (ocrete)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21327
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21327.patch
The sqrt operation is costly, so avoid it if possible by comparing squared values.
From 74113a4666cf71a97b7569ef747b99d21c236cea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com>
Date: Tue, 30 Dec 2025 12:55:33 -0500
Subject: [PATCH] libavfilter/vf_chromakey: Avoid sqrt when possible
The sqrt operation is costly, so avoid it if possible by comparing
squared values.
---
libavfilter/vf_chromakey.c | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/libavfilter/vf_chromakey.c b/libavfilter/vf_chromakey.c
index e5a7ac15b5..e741e9e3b5 100644
--- a/libavfilter/vf_chromakey.c
+++ b/libavfilter/vf_chromakey.c
@@ -47,44 +47,44 @@ typedef struct ChromakeyContext {
static uint8_t do_chromakey_pixel(ChromakeyContext *ctx, uint8_t u[9], uint8_t v[9])
{
- double diff = 0.0;
+ double diff_squared = 0.0;
int du, dv, i;
for (i = 0; i < 9; ++i) {
du = (int)u[i] - ctx->chromakey_uv[0];
dv = (int)v[i] - ctx->chromakey_uv[1];
- diff += sqrt((du * du + dv * dv) / (255.0 * 255.0 * 2));
+ diff_squared += (du * du + dv * dv) / (255.0 * 255.0 * 2);
}
- diff /= 9.0;
+ diff_squared /= 81.0;
if (ctx->blend > 0.0001) {
- return av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0) * 255.0;
+ return av_clipd((sqrt(diff_squared) - ctx->similarity) / ctx->blend, 0.0, 1.0) * 255.0;
} else {
- return (diff > ctx->similarity) ? 255 : 0;
+ return (diff_squared > ctx->similarity * ctx->similarity) ? 255 : 0;
}
}
static uint16_t do_chromakey_pixel16(ChromakeyContext *ctx, uint16_t u[9], uint16_t v[9])
{
double max = ctx->max;
- double diff = 0.0;
+ double diff_squared = 0.0;
int du, dv, i;
for (i = 0; i < 9; ++i) {
du = (int)u[i] - ctx->chromakey_uv[0];
dv = (int)v[i] - ctx->chromakey_uv[1];
- diff += sqrt((du * du + dv * dv) / (max * max * 2));
+ diff_squared += (du * du + dv * dv) / (max * max * 2);
}
- diff /= 9.0;
+ diff_squared /= 81.0;
if (ctx->blend > 0.0001) {
- return av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0) * max;
+ return av_clipd((sqrt(diff_squared) - ctx->similarity) / ctx->blend, 0.0, 1.0) * max;
} else {
- return (diff > ctx->similarity) ? max : 0;
+ return (diff_squared > ctx->similarity * ctx->similarity) ? max : 0;
}
}
@@ -189,17 +189,17 @@ static int do_chromahold_slice(AVFilterContext *avctx, void *arg, int jobnr, int
for (x = 0; x < frame->width >> ctx->hsub_log2; ++x) {
int u = frame->data[1][frame->linesize[1] * y + x];
int v = frame->data[2][frame->linesize[2] * y + x];
- double diff;
+ double diff_squared;
int du, dv;
du = u - ctx->chromakey_uv[0];
dv = v - ctx->chromakey_uv[1];
- diff = sqrt((du * du + dv * dv) / (255.0 * 255.0 * 2.0));
+ diff_squared = (du * du + dv * dv) / (255.0 * 255.0 * 2.0);
- alpha = diff > ctx->similarity;
+ alpha = diff_squared > ctx->similarity * ctx->similarity;
if (ctx->blend > 0.0001) {
- double f = 1. - av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0);
+ double f = 1. - av_clipd((sqrt(diff_squared) - ctx->similarity) / ctx->blend, 0.0, 1.0);
frame->data[1][frame->linesize[1] * y + x] = 128 + (u - 128) * f;
frame->data[2][frame->linesize[2] * y + x] = 128 + (v - 128) * f;
@@ -228,17 +228,17 @@ static int do_chromahold16_slice(AVFilterContext *avctx, void *arg, int jobnr, i
for (x = 0; x < frame->width >> ctx->hsub_log2; ++x) {
int u = AV_RN16(&frame->data[1][frame->linesize[1] * y + 2 * x]);
int v = AV_RN16(&frame->data[2][frame->linesize[2] * y + 2 * x]);
- double diff;
+ double diff_squared;
int du, dv;
du = u - ctx->chromakey_uv[0];
dv = v - ctx->chromakey_uv[1];
- diff = sqrt((du * du + dv * dv) / (max * max * 2.0));
+ diff_squared = (du * du + dv * dv) / (max * max * 2.0);
- alpha = diff > ctx->similarity;
+ alpha = diff_squared > ctx->similarity * ctx->similarity;
if (ctx->blend > 0.0001) {
- double f = 1. - av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0);
+ double f = 1. - av_clipd((sqrt(diff_squared) - ctx->similarity) / ctx->blend, 0.0, 1.0);
AV_WN16(&frame->data[1][frame->linesize[1] * y + 2 * x], mid + (u - mid) * f);
AV_WN16(&frame->data[2][frame->linesize[2] * y + 2 * x], mid + (v - mid) * f);
--
2.49.1
1
0
[PATCH] checkasm/riscv: check other ABIs than 64-bit hard double (PR #21324)
by Rémi Denis-Courmont 30 Dec '25
by Rémi Denis-Courmont 30 Dec '25
30 Dec '25
PR #21324 opened by Rémi Denis-Courmont (Courmisch)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21324
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21324.patch
From a665a59e8aa0d25eccc2758a18b5ef1ab2783d89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= <remi(a)remlab.net>
Date: Mon, 22 Dec 2025 22:42:47 +0200
Subject: [PATCH 1/5] checkasm/riscv: factor out the vector clobbers
No functional changes.
---
tests/checkasm/riscv/checkasm.S | 31 +++++++++++++++++--------------
1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/tests/checkasm/riscv/checkasm.S b/tests/checkasm/riscv/checkasm.S
index c4b034ae23..5283596575 100644
--- a/tests/checkasm/riscv/checkasm.S
+++ b/tests/checkasm/riscv/checkasm.S
@@ -79,20 +79,7 @@ func checkasm_get_wrapper, v
.align 2
2: /* <-- Entry point with the Vector extension --> */
lpad 0
- /* Clobber the vectors */
- vsetvli t0, zero, e32, m8, ta, ma
- li t0, 0xdeadbeef
- vmv.v.x v0, t0
- vmv.v.x v8, t0
- vmv.v.x v16, t0
- vmv.v.x v24, t0
-
- /* Clobber the vector configuration */
- li t0, 0 /* Vector length: zero */
- li t2, -4 /* Vector type: illegal */
- vsetvl zero, t0, t2
- csrwi vxrm, 3 /* Rounding mode: round-to-odd */
- csrwi vxsat, 1 /* Saturation: encountered */
+ jal t0, .Lclobber_v
.align 2
3: /* <-- Entry point without the Vector extension --> */
@@ -181,5 +168,21 @@ func checkasm_get_wrapper, v
lla a0, fail_fs_reg
call checkasm_fail_func
j 4b
+
+.Lclobber_v:
+ # Clobber the vector registers
+ vsetvli t1, zero, e32, m8, ta, ma
+ li t1, -0xdeadbeef
+ vmv.v.x v0, t1
+ vmv.v.x v8, t1
+ vmv.v.x v16, t1
+ vmv.v.x v24, t1
+ # Clobber the vector configuration
+ li t1, 0 /* Vector length: zero */
+ li t3, -4 /* Vector type: illegal */
+ vsetvl zero, t1, t3
+ csrwi vxrm, 3 /* Rounding mode: round-to-odd */
+ csrwi vxsat, 1 /* Saturation: encountered */
+ jr t0
endfunc
#endif
--
2.49.1
From 86a8944491729939c352713d32a700a8a25ba96b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= <remi(a)remlab.net>
Date: Mon, 22 Dec 2025 22:47:46 +0200
Subject: [PATCH 2/5] checkasm/riscv: restore stack before failure reporting
The faiure handling code is C and requires correct stack, global and
thread pointers. This restores them before returning to C. At the same
time, we no longer need to abort() afterwards.
---
tests/checkasm/riscv/checkasm.S | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/tests/checkasm/riscv/checkasm.S b/tests/checkasm/riscv/checkasm.S
index 5283596575..adbf0c0b10 100644
--- a/tests/checkasm/riscv/checkasm.S
+++ b/tests/checkasm/riscv/checkasm.S
@@ -155,9 +155,13 @@ func checkasm_get_wrapper, v
ret
5:
+ # checkasm_fail_func() needs valid SP, GP and TP. Restore them.
+ ld sp, 8(t0)
+ ld gp, 16(t0)
+ ld tp, 24(t0)
lla a0, fail_rsvd_reg
call checkasm_fail_func
- tail abort /* The test harness would probably crash anyway */
+ j 4b
6:
lla a0, fail_s_reg
--
2.49.1
From 810b6761425dc46f04cc36ab3733819772b04363 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= <remi(a)remlab.net>
Date: Mon, 22 Dec 2025 22:58:08 +0200
Subject: [PATCH 3/5] checkasm/riscv: print clobbered register name
checkasm_fail_func takes a format string, so we can.
---
tests/checkasm/riscv/checkasm.S | 53 +++++++++++++++++----------------
1 file changed, 27 insertions(+), 26 deletions(-)
diff --git a/tests/checkasm/riscv/checkasm.S b/tests/checkasm/riscv/checkasm.S
index adbf0c0b10..1b8fdfe51a 100644
--- a/tests/checkasm/riscv/checkasm.S
+++ b/tests/checkasm/riscv/checkasm.S
@@ -22,18 +22,6 @@
#if (__riscv_xlen == 64)
-const fail_s_reg
- .asciz "callee-saved integer register clobbered"
-endconst
-
-const fail_fs_reg
- .asciz "callee-saved floating-point register clobbered"
-endconst
-
-const fail_rsvd_reg
- .asciz "unallocatable register clobbered"
-endconst
-
.section .tbss, "waT"
.align 3
.hidden checked_func
@@ -75,6 +63,15 @@ func checkasm_get_wrapper, v
addi sp, sp, 16
ret
+ .pushsection ".rodata", "a"
+.Lfail_s_reg:
+ .asciz "callee-saved integer register S%d clobbered"
+.Lfail_fs_reg:
+ .asciz "callee-saved floating-point register FS%d clobbered"
+.Lfail_rsvd_reg:
+ .asciz "unallocatable register %cP clobbered"
+ .popsection
+
.option norvc
.align 2
2: /* <-- Entry point with the Vector extension --> */
@@ -125,21 +122,25 @@ func checkasm_get_wrapper, v
/* Check special register values */
la.tls.ie t0, saved_regs
add t0, tp, t0
- ld t1, 8(t0)
- bne t1, sp, 5f
- ld t1, 16(t0)
- bne t1, gp, 5f
- ld t1, 24(t0) // If TP was corrupted, we probably will have...
- bne t1, tp, 5f // ...already crashed before we even get here.
+ ld t2, 8(t0) // SP
+ ld t3, 16(t0) // GP
+ ld t4, 24(t0) // TP
+ li a1, 'S'
+ bne t2, sp, .Lfail_xp
+ li a1, 'G'
+ bne t3, gp, .Lfail_xp
+ li a1, 'T'
+ bne t4, tp, .Lfail_xp
/* Check value of saved registers */
li t0, 0xdeadbeef1badf00d
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- bne t0, s\n, 6f
+ li a1, \n
+ bne t0, s\n, .Lfail_s
#ifdef __riscv_float_abi_double
/* TODO: check float ABI single too */
fmv.x.d t1, fs\n
- bne t0, t1, 7f
+ bne t0, t1, .Lfail_fs
#endif
.endr
@@ -154,22 +155,22 @@ func checkasm_get_wrapper, v
.endr
ret
-5:
+.Lfail_xp:
# checkasm_fail_func() needs valid SP, GP and TP. Restore them.
ld sp, 8(t0)
ld gp, 16(t0)
ld tp, 24(t0)
- lla a0, fail_rsvd_reg
+ lla a0, .Lfail_rsvd_reg
call checkasm_fail_func
j 4b
-6:
- lla a0, fail_s_reg
+.Lfail_s:
+ lla a0, .Lfail_s_reg
call checkasm_fail_func
j 4b
-7:
- lla a0, fail_fs_reg
+.Lfail_fs:
+ lla a0, .Lfail_fs_reg
call checkasm_fail_func
j 4b
--
2.49.1
From 494493e729b8ece54f54ecca8726aedb0da8578b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= <remi(a)remlab.net>
Date: Tue, 30 Dec 2025 21:08:23 +0200
Subject: [PATCH 4/5] checkasm/riscv: handle other float ABIs than double
This splits out the integer and floating point handling to add
support for software, single and quad float ABI's.
---
tests/checkasm/checkasm.h | 2 +-
tests/checkasm/riscv/checkasm.S | 179 ++++++++++++++++++++++++++------
2 files changed, 147 insertions(+), 34 deletions(-)
diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h
index a54231dd0d..90aeee27b7 100644
--- a/tests/checkasm/checkasm.h
+++ b/tests/checkasm/checkasm.h
@@ -301,7 +301,7 @@ void checkasm_checked_call(void *func, ...);
void checkasm_set_function(void *);
void *checkasm_get_wrapper(void);
-#if HAVE_RV && (__riscv_xlen == 64) && defined (__riscv_d)
+#if HAVE_RV && (__riscv_xlen == 64)
#define declare_new(ret, ...) \
ret (*checked_call)(__VA_ARGS__) = checkasm_get_wrapper();
#define call_new(...) \
diff --git a/tests/checkasm/riscv/checkasm.S b/tests/checkasm/riscv/checkasm.S
index 1b8fdfe51a..84c081cbfb 100644
--- a/tests/checkasm/riscv/checkasm.S
+++ b/tests/checkasm/riscv/checkasm.S
@@ -20,21 +20,57 @@
#include "libavutil/riscv/asm.S"
+#if defined(__riscv_float_abi_soft)
+.macro flf rd, addr
+.endm
+.macro fsf rs, addr
+.endm
+#define FSZ 0
+#elif defined(__riscv_float_abi_single)
+.macro flf rd, addr
+ flw \rd, \addr
+.endm
+.macro fsf rs, addr
+ fsw \rs, \addr
+.endm
+#define FSZ 4
+#elif defined(__riscv_float_abi_double)
+.macro flf rd, addr
+ fld \rd, \addr
+.endm
+.macro fsf rs, addr
+ fsd \rs, \addr
+.endm
+#define FSZ 8
+#elif defined(__riscv_float_abi_quad)
+.macro flf rd, addr
+ flq \rd, \addr
+.endm
+.macro fsf rs, addr
+ fsq \rs, \addr
+.endm
+#define FSZ 16
+#else
+#error "Unknown float ABI"
+#endif
+
#if (__riscv_xlen == 64)
- .section .tbss, "waT"
+ .pushsection .tbss, "waT"
.align 3
.hidden checked_func
- .hidden saved_regs
checked_func:
.quad 0
-saved_regs:
- /* Space to spill RA, SP, GP, TP, S0-S11 and FS0-FS11 */
- .rept 4 + 12 + 12
- .quad 0
- .endr
+ .align 3
+.Lsaved_xregs:
+ .fill 4 + 12, 8, 0 // RA, SP, GP, TP, S0-S11
+ .align 4
+.Lsaved_fregs:
+ .fill 12, FSZ, 0 // FS0-FS11
+ .fill 1, 8, 0 // RA
+ .popsection
func checkasm_set_function
lpad 0
@@ -53,15 +89,23 @@ func checkasm_get_wrapper, v
call av_get_cpu_flags
andi t0, a0, 8 /* AV_CPU_FLAG_RVV_I32 */
-
- lla a0, 3f
+#ifdef __riscv_float_abi_soft
+ andi t1, a0, 16 /* AV_CPU_FLAG_RVV_F32 (implies F and Zve32x) */
+ lla a0, checkasm_checked_call_i
beqz t0, 1f
- lla a0, 2f
+ lla a0, checkasm_checked_call_iv
+ beqz t1, 1f
+#else
+ lla a0, checkasm_checked_call_if
+ beqz t0, 1f
+#endif
+ lla a0, checkasm_checked_call_ifv
1:
ld ra, 8(sp)
ld fp, (sp)
addi sp, sp, 16
ret
+endfunc
.pushsection ".rodata", "a"
.Lfail_s_reg:
@@ -70,27 +114,33 @@ func checkasm_get_wrapper, v
.asciz "callee-saved floating-point register FS%d clobbered"
.Lfail_rsvd_reg:
.asciz "unallocatable register %cP clobbered"
+#if defined(__riscv_float_abi_soft) || defined(__riscv_float_abi_single)
+ .align 2
+.Lbad_float:
+ .single 123456789
+#elif defined(__riscv_float_abi_double)
+ .align 3
+.Lbad_float:
+ .double 123456789
+#elif defined(__riscv_float_abi_quad)
+ .align 4
+.Lbad_float:
+ .ldouble 123456789
+#endif
.popsection
- .option norvc
- .align 2
-2: /* <-- Entry point with the Vector extension --> */
- lpad 0
- jal t0, .Lclobber_v
-
- .align 2
-3: /* <-- Entry point without the Vector extension --> */
+func checkasm_checked_call_i
+ /* <-- Entry point without the Vector extension --> */
lpad 0
/* Save RA, unallocatable and callee-saved registers */
- la.tls.ie t0, saved_regs
+ la.tls.ie t0, .Lsaved_xregs
add t0, tp, t0
sd ra, (t0)
sd sp, 8(t0)
sd gp, 16(t0)
sd tp, 24(t0)
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- sd s\n, (32 + (16 * \n))(t0)
- fsd fs\n, (40 + (16 * \n))(t0)
+ sd s\n, (32 + (8 * \n))(t0)
.endr
/* Clobber the stack space right below SP */
@@ -107,9 +157,7 @@ func checkasm_get_wrapper, v
.if (\n > 1 && \n < 7)
mv t\n, t0
.endif
- fmv.d.x ft\n, t0
mv s\n, t0
- fmv.d.x fs\n, t0
.endr
/* Call the tested function */
@@ -120,7 +168,7 @@ func checkasm_get_wrapper, v
jalr t3
/* Check special register values */
- la.tls.ie t0, saved_regs
+ la.tls.ie t0, .Lsaved_xregs
add t0, tp, t0
ld t2, 8(t0) // SP
ld t3, 16(t0) // GP
@@ -137,21 +185,15 @@ func checkasm_get_wrapper, v
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
li a1, \n
bne t0, s\n, .Lfail_s
-#ifdef __riscv_float_abi_double
- /* TODO: check float ABI single too */
- fmv.x.d t1, fs\n
- bne t0, t1, .Lfail_fs
-#endif
.endr
4:
/* Restore RA and saved registers */
- la.tls.ie t0, saved_regs
+ la.tls.ie t0, .Lsaved_xregs
add t0, tp, t0
ld ra, (t0)
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- ld s\n, (32 + (16 * \n))(t0)
- fld fs\n, (40 + (16 * \n))(t0)
+ ld s\n, (32 + (8 * \n))(t0)
.endr
ret
@@ -168,11 +210,82 @@ func checkasm_get_wrapper, v
lla a0, .Lfail_s_reg
call checkasm_fail_func
j 4b
+endfunc
+
+#ifndef __riscv_float_abi_soft
+func checkasm_checked_call_if, f
+ lpad 0
+ # Save callee-saved floating point registers and RA
+ la.tls.ie t0, .Lsaved_fregs
+ add t0, t0, tp
+ lla t1, .Lbad_float
+ sd ra, 12 * FSZ(t0)
+ .irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ fsf fs\n, \n * FSZ(t0)
+ .endr
+ # Clobber the saved and temporary floating point registers
+ .irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ flf ft\n, (t1)
+ flf fs\n, (t1)
+ .endr
+
+ jal checkasm_checked_call_i
+
+ # Check value of saved registers
+ lla t1, .Lbad_float
+ flf ft0, (t1)
+ .irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ li a1, \n
+#if defined(__riscv_float_abi_single)
+ feq.s t1, ft0, fs\n
+#elif defined(__riscv_float_abi_double)
+ feq.d t1, ft0, fs\n
+#else
+ feq.q t1, ft0, fs\n
+#endif
+ beqz t1, .Lfail_fs
+ .endr
+
+1: # Restore callee-saved floating point registers and RA
+ la.tls.ie t0, .Lsaved_fregs
+ add t0, t0, tp
+ .irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ flf fs\n, \n * FSZ(t0)
+ .endr
+ ld ra, 12 * FSZ(t0)
+ ret
.Lfail_fs:
lla a0, .Lfail_fs_reg
call checkasm_fail_func
- j 4b
+ j 1b
+endfunc
+#else
+func checkasm_checked_call_if, f
+ lpad 0
+ lla t1, .Lbad_float
+ # Clobber all floating point registers (soft float ABI).
+ .irp n, 0, 1, 2, 3, 4, 5, 6, 7
+ flw fa\n, (t1)
+ .endr
+ .irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ flw ft\n, (t1)
+ flw fs\n, (t1)
+ .endr
+ j checkasm_checked_call_i
+endfunc
+
+func checkasm_checked_call_iv, zve32x
+ lpad 0
+ jal t0, .Lclobber_v
+ j checkasm_checked_call_i
+endfunc
+#endif
+
+func checkasm_checked_call_ifv, zve32x
+ lpad 0
+ jal t0, .Lclobber_v
+ j checkasm_checked_call_if
.Lclobber_v:
# Clobber the vector registers
--
2.49.1
From 50108118944adae533b2b99dfc7548d5a7fa417d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= <remi(a)remlab.net>
Date: Tue, 30 Dec 2025 21:26:46 +0200
Subject: [PATCH 5/5] checkasm/riscv: add call checks for riscv32
---
tests/checkasm/checkasm.h | 2 +-
tests/checkasm/riscv/checkasm.S | 128 +++++++++++++++++++-------------
2 files changed, 79 insertions(+), 51 deletions(-)
diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h
index 90aeee27b7..4bd7f3ee0c 100644
--- a/tests/checkasm/checkasm.h
+++ b/tests/checkasm/checkasm.h
@@ -301,7 +301,7 @@ void checkasm_checked_call(void *func, ...);
void checkasm_set_function(void *);
void *checkasm_get_wrapper(void);
-#if HAVE_RV && (__riscv_xlen == 64)
+#if HAVE_RV
#define declare_new(ret, ...) \
ret (*checked_call)(__VA_ARGS__) = checkasm_get_wrapper();
#define call_new(...) \
diff --git a/tests/checkasm/riscv/checkasm.S b/tests/checkasm/riscv/checkasm.S
index 84c081cbfb..c35c9f1113 100644
--- a/tests/checkasm/riscv/checkasm.S
+++ b/tests/checkasm/riscv/checkasm.S
@@ -20,6 +20,38 @@
#include "libavutil/riscv/asm.S"
+#if (__riscv_xlen == 32)
+.macro lx rd, addr
+ lw \rd, \addr
+.endm
+
+.macro sx rs, addr
+ sw \rs, \addr
+.endm
+#define REG_MAGIC 0xdeadbeef
+#elif (__riscv_xlen == 64)
+.macro lx rd, addr
+ ld \rd, \addr
+.endm
+
+.macro sx rs, addr
+ sd \rs, \addr
+.endm
+#define REG_MAGIC 0xdeadbeef0badf00d
+#else
+.macro lx rd, addr
+ lq \rd, \addr
+.endm
+
+.macro sx rs, addr
+ sq \rs, \addr
+.endm
+#define REG_MAGIC 0xdeadbeef0badf00daaaabbbbccccdddd
+#endif
+#define XSZ (__riscv_xlen / 8)
+#define STACK_ALIGN 16
+#define STACK_SPACE(sz) (((sz) + (STACK_ALIGN - 1)) & -STACK_ALIGN)
+
#if defined(__riscv_float_abi_soft)
.macro flf rd, addr
.endm
@@ -54,38 +86,33 @@
#error "Unknown float ABI"
#endif
-#if (__riscv_xlen == 64)
-
.pushsection .tbss, "waT"
- .align 3
- .hidden checked_func
-
-checked_func:
- .quad 0
-
- .align 3
+ .align 4
+.Lchecked_func:
+ .fill 1, XSZ, 0
+ .align 4
.Lsaved_xregs:
- .fill 4 + 12, 8, 0 // RA, SP, GP, TP, S0-S11
+ .fill 4 + 12, XSZ, 0 // RA, SP, GP, TP, S0-S11
.align 4
.Lsaved_fregs:
.fill 12, FSZ, 0 // FS0-FS11
- .fill 1, 8, 0 // RA
+ .fill 1, XSZ, 0 // RA
.popsection
func checkasm_set_function
lpad 0
- la.tls.ie t0, checked_func
+ la.tls.ie t0, .Lchecked_func
add t0, tp, t0
- sd a0, (t0)
+ sx a0, (t0)
ret
endfunc
func checkasm_get_wrapper, v
lpad 0
- addi sp, sp, -16
- sd fp, (sp)
- sd ra, 8(sp)
- addi fp, sp, 16
+ addi sp, sp, -STACK_SPACE(2 * XSZ)
+ sx fp, (sp)
+ sx ra, XSZ(sp)
+ addi fp, sp, STACK_SPACE(2 * XSZ)
call av_get_cpu_flags
andi t0, a0, 8 /* AV_CPU_FLAG_RVV_I32 */
@@ -101,8 +128,8 @@ func checkasm_get_wrapper, v
#endif
lla a0, checkasm_checked_call_ifv
1:
- ld ra, 8(sp)
- ld fp, (sp)
+ lx ra, XSZ(sp)
+ lx fp, (sp)
addi sp, sp, 16
ret
endfunc
@@ -135,44 +162,46 @@ func checkasm_checked_call_i
/* Save RA, unallocatable and callee-saved registers */
la.tls.ie t0, .Lsaved_xregs
add t0, tp, t0
- sd ra, (t0)
- sd sp, 8(t0)
- sd gp, 16(t0)
- sd tp, 24(t0)
+ sx ra, (t0)
+ sx sp, 1 * XSZ(t0)
+ sx gp, 2 * XSZ(t0)
+ sx tp, 3 * XSZ(t0)
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- sd s\n, (32 + (8 * \n))(t0)
+ sx s\n, (4 + \n) * XSZ(t0)
.endr
/* Clobber the stack space right below SP */
- li t0, 0xdeadbeef1badf00d
- .rept 16
- addi sp, sp, -16
- sd t0, (sp)
- sd t0, 8(sp)
- .endr
- addi sp, sp, 256
+ li t1, REG_MAGIC
+ li t0, 16
+1:
+ addi sp, sp, -XSZ
+ addi t0, t0, -1
+ sx t1, (sp)
+ bnez t0, 1b
- /* Clobber the saved and temporary registers */
+ addi sp, sp, 16 * XSZ
+ # Clobber temporary registers (except T2, FE-CFI label)
+ .irp n, 0, 1, 3, 4, 5, 6
+ mv t\n, t1
+ .endr
+ # Clobber the saved registers
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- .if (\n > 1 && \n < 7)
- mv t\n, t0
- .endif
- mv s\n, t0
+ mv s\n, t1
.endr
/* Call the tested function */
- la.tls.ie t0, checked_func
+ la.tls.ie t0, .Lchecked_func
add t0, tp, t0
- ld t3, (t0)
- sd zero, (t0)
+ lx t3, (t0)
+ sx zero, (t0)
jalr t3
/* Check special register values */
la.tls.ie t0, .Lsaved_xregs
add t0, tp, t0
- ld t2, 8(t0) // SP
- ld t3, 16(t0) // GP
- ld t4, 24(t0) // TP
+ lx t2, 1 * XSZ(t0) // SP
+ lx t3, 2 * XSZ(t0) // GP
+ lx t4, 3 * XSZ(t0) // TP
li a1, 'S'
bne t2, sp, .Lfail_xp
li a1, 'G'
@@ -181,27 +210,27 @@ func checkasm_checked_call_i
bne t4, tp, .Lfail_xp
/* Check value of saved registers */
- li t0, 0xdeadbeef1badf00d
+ li t1, REG_MAGIC
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
li a1, \n
- bne t0, s\n, .Lfail_s
+ bne t1, s\n, .Lfail_s
.endr
4:
/* Restore RA and saved registers */
la.tls.ie t0, .Lsaved_xregs
add t0, tp, t0
- ld ra, (t0)
+ lx ra, (t0)
.irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- ld s\n, (32 + (8 * \n))(t0)
+ lx s\n, (4 + \n) * XSZ(t0)
.endr
ret
.Lfail_xp:
# checkasm_fail_func() needs valid SP, GP and TP. Restore them.
- ld sp, 8(t0)
- ld gp, 16(t0)
- ld tp, 24(t0)
+ lx sp, 1 * XSZ(t0)
+ lx gp, 2 * XSZ(t0)
+ lx tp, 3 * XSZ(t0)
lla a0, .Lfail_rsvd_reg
call checkasm_fail_func
j 4b
@@ -303,4 +332,3 @@ func checkasm_checked_call_ifv, zve32x
csrwi vxsat, 1 /* Saturation: encountered */
jr t0
endfunc
-#endif
--
2.49.1
1
0
PR #21323 opened by bcheng
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21323
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21323.patch
Gets rid of some validation issues, and removes the useless ycbcr sampler entirely.
From e41e21a1ec42a30f2d97a1a1c092797d4f9328b4 Mon Sep 17 00:00:00 2001
From: Benjamin Cheng <benjamin.cheng(a)amd.com>
Date: Tue, 30 Dec 2025 14:15:43 -0500
Subject: [PATCH 1/4] avutil/hwcontext_vulkan: Limit usages for lone DPBs
Lone DPBs will only be requested when the implementation requires
a separate DPB. These images are never given out, and are never used for
anything other than a DPB. Also most implementations requiring these
won't support any other usages on DPBs, so limiting the usages would fix
some validation errors.
---
libavutil/hwcontext_vulkan.c | 59 +++++++++++++++++++-----------------
1 file changed, 31 insertions(+), 28 deletions(-)
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index aa5f72e7f2..182eabceb0 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -2957,6 +2957,9 @@ static int vulkan_frames_init(AVHWFramesContext *hwfc)
const struct FFVkFormatEntry *fmt;
int disable_multiplane = p->disable_multiplane ||
(hwctx->flags & AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE);
+ int is_lone_dpb = ((hwctx->usage & VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR) ||
+ ((hwctx->usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR) &&
+ !(hwctx->usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR)));
/* Defaults */
if (!hwctx->nb_layers)
@@ -3009,37 +3012,37 @@ static int vulkan_frames_init(AVHWFramesContext *hwfc)
return err;
}
- /* Image usage flags */
- hwctx->usage |= supported_usage & (VK_IMAGE_USAGE_TRANSFER_DST_BIT |
- VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
- VK_IMAGE_USAGE_STORAGE_BIT |
- VK_IMAGE_USAGE_SAMPLED_BIT);
+ /* Lone DPB images do not need additional flags. */
+ if (!is_lone_dpb) {
+ /* Image usage flags */
+ hwctx->usage |= supported_usage & (VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_STORAGE_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT);
- if (p->vkctx.extensions & FF_VK_EXT_HOST_IMAGE_COPY &&
- !(p->dprops.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY) &&
- !(p->dprops.driverID == VK_DRIVER_ID_MOLTENVK))
- hwctx->usage |= supported_usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
+ if (p->vkctx.extensions & FF_VK_EXT_HOST_IMAGE_COPY &&
+ !(p->dprops.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY) &&
+ !(p->dprops.driverID == VK_DRIVER_ID_MOLTENVK))
+ hwctx->usage |= supported_usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
- /* Enables encoding of images, if supported by format and extensions */
- if ((supported_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) &&
- (p->vkctx.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
- FF_VK_EXT_VIDEO_MAINTENANCE_1)))
- hwctx->usage |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
+ /* Enables encoding of images, if supported by format and extensions */
+ if ((supported_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) &&
+ (p->vkctx.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
+ FF_VK_EXT_VIDEO_MAINTENANCE_1)))
+ hwctx->usage |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
- /* Image creation flags.
- * Only fill them in automatically if the image is not going to be used as
- * a DPB-only image, and we have SAMPLED/STORAGE bits set. */
- if (!hwctx->img_flags) {
- int is_lone_dpb = ((hwctx->usage & VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR) ||
- ((hwctx->usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR) &&
- !(hwctx->usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR)));
- int sampleable = hwctx->usage & (VK_IMAGE_USAGE_SAMPLED_BIT |
- VK_IMAGE_USAGE_STORAGE_BIT);
- hwctx->img_flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
- if (sampleable && !is_lone_dpb) {
- hwctx->img_flags |= VK_IMAGE_CREATE_ALIAS_BIT;
- if ((fmt->vk_planes > 1) && (hwctx->format[0] == fmt->vkf))
- hwctx->img_flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
+ /* Image creation flags.
+ * Only fill them in automatically if the image is not going to be used as
+ * a DPB-only image, and we have SAMPLED/STORAGE bits set. */
+ if (!hwctx->img_flags) {
+ int sampleable = hwctx->usage & (VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_STORAGE_BIT);
+ hwctx->img_flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+ if (sampleable) {
+ hwctx->img_flags |= VK_IMAGE_CREATE_ALIAS_BIT;
+ if ((fmt->vk_planes > 1) && (hwctx->format[0] == fmt->vkf))
+ hwctx->img_flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
+ }
}
}
--
2.49.1
From 24db09a881c9e0eb5b36dcf693c6ac17e9d56be8 Mon Sep 17 00:00:00 2001
From: Benjamin Cheng <benjamin.cheng(a)amd.com>
Date: Tue, 30 Dec 2025 11:47:46 -0500
Subject: [PATCH 2/4] lavc/vulkan_video: Restrict usages for image views
These image views are used only internally for video coding, so they do
not need all the usages of the image it's created from.
---
libavcodec/vulkan_decode.c | 8 +++++---
libavcodec/vulkan_encode.c | 6 +++---
libavcodec/vulkan_video.c | 9 ++++++++-
libavcodec/vulkan_video.h | 2 +-
4 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c
index de786ad808..31ed14f517 100644
--- a/libavcodec/vulkan_decode.c
+++ b/libavcodec/vulkan_decode.c
@@ -217,7 +217,7 @@ int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic,
err = ff_vk_create_view(&ctx->s, &ctx->common,
&vkpic->view.ref[0], &vkpic->view.aspect_ref[0],
(AVVkFrame *)vkpic->dpb_frame->data[0],
- dpb_hwfc->format[0], !is_current);
+ dpb_hwfc->format[0], VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR);
if (err < 0)
return err;
@@ -231,7 +231,9 @@ int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic,
err = ff_vk_create_view(&ctx->s, &ctx->common,
&vkpic->view.out[0], &vkpic->view.aspect[0],
(AVVkFrame *)pic->data[0],
- hwfc->format[0], !is_current);
+ hwfc->format[0],
+ VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR |
+ (!is_current ? VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR : 0));
if (err < 0)
return err;
@@ -1404,7 +1406,7 @@ int ff_vk_decode_init(AVCodecContext *avctx)
&ctx->common.layered_view,
&ctx->common.layered_aspect,
(AVVkFrame *)ctx->common.layered_frame->data[0],
- s->hwfc->format[0], 1);
+ s->hwfc->format[0], VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR);
if (err < 0)
goto fail;
}
diff --git a/libavcodec/vulkan_encode.c b/libavcodec/vulkan_encode.c
index 7b534ffa30..c820b63666 100644
--- a/libavcodec/vulkan_encode.c
+++ b/libavcodec/vulkan_encode.c
@@ -71,7 +71,7 @@ static int vulkan_encode_init(AVCodecContext *avctx, FFHWBaseEncodePicture *pic)
/* Input image view */
err = ff_vk_create_view(&ctx->s, &ctx->common,
&vp->in.view, &vp->in.aspect,
- vkf, vkfc->format[0], 0);
+ vkf, vkfc->format[0], VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR);
if (err < 0)
return err;
@@ -81,7 +81,7 @@ static int vulkan_encode_init(AVCodecContext *avctx, FFHWBaseEncodePicture *pic)
AVVkFrame *rvkf = (AVVkFrame *)rf->data[0];
err = ff_vk_create_view(&ctx->s, &ctx->common,
&vp->dpb.view, &vp->dpb.aspect,
- rvkf, ctx->pic_format, 1);
+ rvkf, ctx->pic_format, VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
if (err < 0)
return err;
} else {
@@ -635,7 +635,7 @@ static int vulkan_encode_create_dpb(AVCodecContext *avctx, FFVulkanEncodeContext
&ctx->common.layered_view,
&ctx->common.layered_aspect,
(AVVkFrame *)ctx->common.layered_frame->data[0],
- hwfc->format[0], 1);
+ hwfc->format[0], VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
if (err < 0)
return err;
diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c
index d73479d14d..8bc3e9b907 100644
--- a/libavcodec/vulkan_video.c
+++ b/libavcodec/vulkan_video.c
@@ -290,14 +290,21 @@ StdVideoAV1Profile ff_vk_av1_profile_to_vk(int profile)
int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon *common,
VkImageView *view, VkImageAspectFlags *aspect,
- AVVkFrame *src, VkFormat vkf, int is_dpb)
+ AVVkFrame *src, VkFormat vkf, VkImageUsageFlags usage)
{
VkResult ret;
FFVulkanFunctions *vk = &s->vkfn;
VkImageAspectFlags aspect_mask = ff_vk_aspect_bits_from_vkfmt(vkf);
+ int is_dpb = usage & (VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
+ VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
+ VkImageViewUsageCreateInfo usage_create_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
+ .usage = usage,
+ };
VkSamplerYcbcrConversionInfo yuv_sampler_info = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
+ .pNext = &usage_create_info,
.conversion = common->yuv_sampler,
};
VkImageViewCreateInfo img_view_create_info = {
diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h
index 1b29f7adc7..14168b7cc8 100644
--- a/libavcodec/vulkan_video.h
+++ b/libavcodec/vulkan_video.h
@@ -85,7 +85,7 @@ StdVideoAV1Profile ff_vk_av1_profile_to_vk(int profile);
*/
int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon *common,
VkImageView *view, VkImageAspectFlags *aspect,
- AVVkFrame *src, VkFormat vkf, int is_dpb);
+ AVVkFrame *src, VkFormat vkf, VkImageUsageFlags flags);
/**
* Initialize video session, allocating and binding necessary memory.
--
2.49.1
From 2a0671f28ec67ce844bbeb9769ebb7967a9bdeb6 Mon Sep 17 00:00:00 2001
From: Benjamin Cheng <benjamin.cheng(a)amd.com>
Date: Tue, 30 Dec 2025 14:11:52 -0500
Subject: [PATCH 3/4] lavc/vulkan_video: Drop sampler
With the limiting of video usages on the image views, we no longer need
a yuv sampler, since no multi-plane image will be created with the
SAMPLED usage bit.
---
libavcodec/vulkan_decode.c | 3 +--
libavcodec/vulkan_video.c | 32 +-------------------------------
libavcodec/vulkan_video.h | 2 --
3 files changed, 2 insertions(+), 35 deletions(-)
diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c
index 31ed14f517..eb76d5b052 100644
--- a/libavcodec/vulkan_decode.c
+++ b/libavcodec/vulkan_decode.c
@@ -1385,8 +1385,7 @@ int ff_vk_decode_init(AVCodecContext *avctx)
VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
dpb_hwfc->format[0] = s->hwfc->format[0];
dpb_hwfc->tiling = VK_IMAGE_TILING_OPTIMAL;
- dpb_hwfc->usage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
- VK_IMAGE_USAGE_SAMPLED_BIT; /* Shuts validator up. */
+ dpb_hwfc->usage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
if (ctx->common.layered_dpb)
dpb_hwfc->nb_layers = ctx->caps.maxDpbSlots;
diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c
index 8bc3e9b907..9645a2de67 100644
--- a/libavcodec/vulkan_video.c
+++ b/libavcodec/vulkan_video.c
@@ -302,14 +302,9 @@ int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon *common,
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
.usage = usage,
};
- VkSamplerYcbcrConversionInfo yuv_sampler_info = {
- .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
- .pNext = &usage_create_info,
- .conversion = common->yuv_sampler,
- };
VkImageViewCreateInfo img_view_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
- .pNext = &yuv_sampler_info,
+ .pNext = &usage_create_info,
.viewType = common->layered_dpb && is_dpb ?
VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D,
.format = vkf,
@@ -365,12 +360,6 @@ av_cold void ff_vk_video_common_uninit(FFVulkanContext *s,
av_frame_free(&common->layered_frame);
av_buffer_unref(&common->dpb_hwfc_ref);
-
- if (common->yuv_sampler) {
- vk->DestroySamplerYcbcrConversion(s->hwctx->act_dev, common->yuv_sampler,
- s->hwctx->alloc);
- common->yuv_sampler = VK_NULL_HANDLE;
- }
}
av_cold int ff_vk_video_common_init(AVCodecContext *avctx, FFVulkanContext *s,
@@ -383,25 +372,6 @@ av_cold int ff_vk_video_common_init(AVCodecContext *avctx, FFVulkanContext *s,
VkVideoSessionMemoryRequirementsKHR *mem = NULL;
VkBindVideoSessionMemoryInfoKHR *bind_mem = NULL;
- int cxpos = 0, cypos = 0;
- VkSamplerYcbcrConversionCreateInfo yuv_sampler_info = {
- .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
- .components = ff_comp_identity_map,
- .ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY,
- .ycbcrRange = avctx->color_range == AVCOL_RANGE_MPEG, /* Ignored */
- .format = session_create->pictureFormat,
- };
-
- /* Create identity YUV sampler
- * (VkImageViews of YUV image formats require it, even if it does nothing) */
- av_chroma_location_enum_to_pos(&cxpos, &cypos, avctx->chroma_sample_location);
- yuv_sampler_info.xChromaOffset = cxpos >> 7;
- yuv_sampler_info.yChromaOffset = cypos >> 7;
- ret = vk->CreateSamplerYcbcrConversion(s->hwctx->act_dev, &yuv_sampler_info,
- s->hwctx->alloc, &common->yuv_sampler);
- if (ret != VK_SUCCESS)
- return AVERROR_EXTERNAL;
-
/* Create session */
ret = vk->CreateVideoSessionKHR(s->hwctx->act_dev, session_create,
s->hwctx->alloc, &common->session);
diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h
index 14168b7cc8..d63bfae3fc 100644
--- a/libavcodec/vulkan_video.h
+++ b/libavcodec/vulkan_video.h
@@ -34,8 +34,6 @@ typedef struct FFVkVideoSession {
VkDeviceMemory *mem;
uint32_t nb_mem;
- VkSamplerYcbcrConversion yuv_sampler;
-
AVBufferRef *dpb_hwfc_ref;
int layered_dpb;
AVFrame *layered_frame;
--
2.49.1
From e27ba4f25978e8931d8e5475ada7ce701c99ba6a Mon Sep 17 00:00:00 2001
From: Benjamin Cheng <benjamin.cheng(a)amd.com>
Date: Tue, 30 Dec 2025 12:01:19 -0500
Subject: [PATCH 4/4] hwcontext_vulkan: Remove unnecessary validation filters
With the latest changes, I can no longer get these to show up.
---
libavutil/hwcontext_vulkan.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 182eabceb0..2011cdec79 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -733,8 +733,6 @@ static VkBool32 VKAPI_CALL vk_dbg_callback(VkDebugUtilsMessageSeverityFlagBitsEX
switch (data->messageIdNumber) {
case 0x086974c1: /* BestPractices-vkCreateCommandPool-command-buffer-reset */
case 0xfd92477a: /* BestPractices-vkAllocateMemory-small-allocation */
- case 0x618ab1e7: /* VUID-VkImageViewCreateInfo-usage-02275 */
- case 0x30f4ac70: /* VUID-VkImageCreateInfo-pNext-06811 */
return VK_FALSE;
default:
break;
--
2.49.1
1
0