From ac5303a5b9cdc17e4ebddfdc3203a4c325d42c52 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Wed, 25 Jun 2025 08:46:23 +0200 Subject: [PATCH 01/20] hw_base_encode: Support refs option to limit number of references Set default value to 0 to keep the old behavior of using maximum number of references. --- libavcodec/d3d12va_encode_hevc.c | 1 + libavcodec/hw_base_encode.c | 5 +++++ libavcodec/vaapi_encode_av1.c | 1 + libavcodec/vaapi_encode_h264.c | 1 + libavcodec/vaapi_encode_h265.c | 1 + libavcodec/vaapi_encode_vp8.c | 1 + libavcodec/vaapi_encode_vp9.c | 1 + libavcodec/vulkan_encode_av1.c | 1 + libavcodec/vulkan_encode_h264.c | 1 + libavcodec/vulkan_encode_h265.c | 1 + 10 files changed, 14 insertions(+) diff --git a/libavcodec/d3d12va_encode_hevc.c b/libavcodec/d3d12va_encode_hevc.c index ce5d1bf110..0aa1a1d3a4 100644 --- a/libavcodec/d3d12va_encode_hevc.c +++ b/libavcodec/d3d12va_encode_hevc.c @@ -762,6 +762,7 @@ static const FFCodecDefault d3d12va_encode_hevc_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/hw_base_encode.c b/libavcodec/hw_base_encode.c index 33a30c8d10..927aeb4bad 100644 --- a/libavcodec/hw_base_encode.c +++ b/libavcodec/hw_base_encode.c @@ -666,6 +666,11 @@ int ff_hw_base_init_gop_structure(FFHWBaseEncodeContext *ctx, AVCodecContext *av ctx->ref_l0 = FFMIN(ref_l0, MAX_PICTURE_REFERENCES); ctx->ref_l1 = FFMIN(ref_l1, MAX_PICTURE_REFERENCES); + if (avctx->refs > 0) { + ctx->ref_l0 = FFMIN(ctx->ref_l0, avctx->refs); + ctx->ref_l1 = FFMIN(ctx->ref_l1, avctx->refs); + } + if (flags & FF_HW_FLAG_INTRA_ONLY || avctx->gop_size <= 1) { av_log(avctx, AV_LOG_VERBOSE, "Using intra frames only.\n"); ctx->gop_size = 1; diff --git a/libavcodec/vaapi_encode_av1.c b/libavcodec/vaapi_encode_av1.c index 3d8f29d599..9d837f5c6b 100644 --- a/libavcodec/vaapi_encode_av1.c +++ b/libavcodec/vaapi_encode_av1.c @@ -1027,6 +1027,7 @@ static const FFCodecDefault vaapi_encode_av1_defaults[] = { { "g", "120" }, { "qmin", "1" }, { "qmax", "255" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index 0cd5b0ac50..27f6551719 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -1153,6 +1153,7 @@ static const FFCodecDefault vaapi_encode_h264_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index 1539bfbf79..baf0d77c8a 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -1184,6 +1184,7 @@ static const FFCodecDefault vaapi_encode_h265_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c index 125d760b9b..69a56a17af 100644 --- a/libavcodec/vaapi_encode_vp8.c +++ b/libavcodec/vaapi_encode_vp8.c @@ -235,6 +235,7 @@ static const FFCodecDefault vaapi_encode_vp8_defaults[] = { { "g", "120" }, { "qmin", "-1" }, { "qmax", "-1" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c index e88967a053..ca8de541d9 100644 --- a/libavcodec/vaapi_encode_vp9.c +++ b/libavcodec/vaapi_encode_vp9.c @@ -292,6 +292,7 @@ static const FFCodecDefault vaapi_encode_vp9_defaults[] = { { "g", "250" }, { "qmin", "-1" }, { "qmax", "-1" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vulkan_encode_av1.c b/libavcodec/vulkan_encode_av1.c index 840092cf6d..08ffbfa393 100644 --- a/libavcodec/vulkan_encode_av1.c +++ b/libavcodec/vulkan_encode_av1.c @@ -1367,6 +1367,7 @@ static const FFCodecDefault vulkan_encode_av1_defaults[] = { { "g", "300" }, { "qmin", "1" }, { "qmax", "255" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vulkan_encode_h264.c b/libavcodec/vulkan_encode_h264.c index 8bbb4639e6..942e911fb7 100644 --- a/libavcodec/vulkan_encode_h264.c +++ b/libavcodec/vulkan_encode_h264.c @@ -1634,6 +1634,7 @@ static const FFCodecDefault vulkan_encode_h264_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, + { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vulkan_encode_h265.c b/libavcodec/vulkan_encode_h265.c index f0ec852557..c30b7e8f93 100644 --- a/libavcodec/vulkan_encode_h265.c +++ b/libavcodec/vulkan_encode_h265.c @@ -1761,6 +1761,7 @@ static const FFCodecDefault vulkan_encode_h265_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, + { "refs", "0" }, { NULL }, }; From c9e93df4eed93fe0044c52d953688c4180de1d48 Mon Sep 17 00:00:00 2001 From: Oliver Chang Date: Thu, 14 Aug 2025 22:11:41 -0700 Subject: [PATCH 02/20] avcodec/prores_raw: Fix heap buffer overflow When dimensions differ from context, those were updated using ff_set_dimensions, however this overwrote the aligned coded_width and coded_height that were set before, leading to a buffer overflow when writing the frame data. Fixes: OssFuzz 438771336 Fixes: Heap-buffer-overflow Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg Signed-off-by: Marvin Scholz Reviewed-by: Marvin Scholz --- libavcodec/prores_raw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libavcodec/prores_raw.c b/libavcodec/prores_raw.c index 748e176815..b2aa97ddda 100644 --- a/libavcodec/prores_raw.c +++ b/libavcodec/prores_raw.c @@ -367,9 +367,6 @@ static int decode_frame(AVCodecContext *avctx, if ((w & 1) || (h & 1)) return AVERROR_INVALIDDATA; - avctx->coded_width = FFALIGN(w, 16); - avctx->coded_height = FFALIGN(h, 16); - if (w != avctx->width || h != avctx->height) { av_log(avctx, AV_LOG_WARNING, "picture resolution change: %ix%i -> %ix%i\n", avctx->width, avctx->height, w, h); @@ -377,6 +374,9 @@ static int decode_frame(AVCodecContext *avctx, return ret; } + avctx->coded_width = FFALIGN(w, 16); + avctx->coded_height = FFALIGN(h, 16); + enum AVPixelFormat pix_fmt = AV_PIX_FMT_BAYER_RGGB16; if (pix_fmt != s->pix_fmt) { s->pix_fmt = pix_fmt; From 2a22972db3b390d82dedbdbb5f44cc09a43912b5 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Fri, 15 Aug 2025 17:55:05 +0200 Subject: [PATCH 03/20] avcodec/utvideodec: Clear plane_start array in pack mode the array is passed into decode_plane() without being initialized or used Fixes: use of uninitialized memory Fixes: 438780119/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_UTVIDEO_DEC_fuzzer-5464037027807232 Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg Signed-off-by: Michael Niedermayer --- libavcodec/utvideodec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/utvideodec.c b/libavcodec/utvideodec.c index 934945c1be..bc02ac44d5 100644 --- a/libavcodec/utvideodec.c +++ b/libavcodec/utvideodec.c @@ -585,7 +585,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, int buf_size = avpkt->size; UtvideoContext *c = avctx->priv_data; int i, j; - const uint8_t *plane_start[5]; + const uint8_t *plane_start[5] = {NULL}; int plane_size, max_slice_size = 0, slice_start, slice_end, slice_size; int ret; GetByteContext gb; From 6e4805056851338fa00768931f29d4c6281495b9 Mon Sep 17 00:00:00 2001 From: wangbin Date: Fri, 15 Aug 2025 23:14:47 +0800 Subject: [PATCH 04/20] configure: fix -L flags for lld-link --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 416cd2d1d6..6c1d6868ea 100755 --- a/configure +++ b/configure @@ -5174,7 +5174,7 @@ probe_cc(){ # but we can force it back to gnu mode and get the version from there. _ident=$($_cc -flavor gnu --version 2>/dev/null) _ld_o='-out:$@' - _flags_filter=msvc_flags + _flags_filter=msvc_flags_link _ld_lib='%.lib' _ld_path='-libpath:' elif VSLANG=1033 $_cc -nologo- 2>&1 | grep -q ^Microsoft || { $_cc -v 2>&1 | grep -q clang && $_cc -? > /dev/null 2>&1; }; then From 5ff2500514a0224b7c2822d3b4e950b473f5bcff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Fri, 15 Aug 2025 19:23:31 +0200 Subject: [PATCH 05/20] avcodec/x86/Makefile: add missing x86/proresdsp.o for prores raw --- libavcodec/x86/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile index ebf458453e..ebd2bdb310 100644 --- a/libavcodec/x86/Makefile +++ b/libavcodec/x86/Makefile @@ -170,6 +170,7 @@ X86ASM-OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp.o X86ASM-OBJS-$(CONFIG_MPEG4_DECODER) += x86/xvididct.o X86ASM-OBJS-$(CONFIG_PNG_DECODER) += x86/pngdsp.o X86ASM-OBJS-$(CONFIG_PRORES_DECODER) += x86/proresdsp.o +X86ASM-OBJS-$(CONFIG_PRORES_RAW_DECODER) += x86/proresdsp.o X86ASM-OBJS-$(CONFIG_RV40_DECODER) += x86/rv40dsp.o X86ASM-OBJS-$(CONFIG_SBC_ENCODER) += x86/sbcdsp.o X86ASM-OBJS-$(CONFIG_SVQ1_ENCODER) += x86/svq1enc.o From 1f4fed5cc3be0737305e342f753c42716d6bf432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Tue, 4 Mar 2025 13:49:56 +0100 Subject: [PATCH 06/20] w32pthreads: add support for setting thread name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kacper Michajłow --- compat/w32pthreads.h | 35 +++++++++++++++++++++++++++++++++++ libavutil/thread.h | 2 ++ 2 files changed, 37 insertions(+) diff --git a/compat/w32pthreads.h b/compat/w32pthreads.h index fd6428e29f..6f2734b470 100644 --- a/compat/w32pthreads.h +++ b/compat/w32pthreads.h @@ -44,6 +44,7 @@ #include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/time.h" +#include "libavutil/wchar_filename.h" typedef struct pthread_t { void *handle; @@ -209,4 +210,38 @@ static inline int pthread_setcancelstate(int state, int *oldstate) return 0; } +static inline int win32_thread_setname(const char *name) +{ +#if !HAVE_UWP + typedef HRESULT (WINAPI *SetThreadDescriptionFn)(HANDLE, PCWSTR); + + // Although SetThreadDescription lives in kernel32.dll, on Windows Server 2016, + // Windows 10 LTSB 2016 and Windows 10 version 1607, it was only available in + // kernelbase.dll. So, load it from there for maximum coverage. + HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll"); + if (!kernelbase) + return AVERROR(ENOSYS); + + SetThreadDescriptionFn pSetThreadDescription = + (SetThreadDescriptionFn)GetProcAddress(kernelbase, "SetThreadDescription"); + if (!pSetThreadDescription) + return AVERROR(ENOSYS); + + wchar_t *wname; + if (utf8towchar(name, &wname) < 0) + return AVERROR(ENOMEM); + + HRESULT hr = pSetThreadDescription(GetCurrentThread(), wname); + av_free(wname); + return SUCCEEDED(hr) ? 0 : AVERROR(EINVAL); +#else + // UWP is not supported because we cannot use LoadLibrary/GetProcAddress to + // detect the availability of the SetThreadDescription API. There is a small + // gap in Windows builds 1507-1607 where it was not available. UWP allows + // querying the availability of APIs with QueryOptionalDelayLoadedAPI, but it + // requires /DELAYLOAD:kernel32.dll during linking, and we cannot enforce that. + return AVERROR(ENOSYS); +#endif +} + #endif /* COMPAT_W32PTHREADS_H */ diff --git a/libavutil/thread.h b/libavutil/thread.h index 2c00c7cc35..184e2d8c5f 100644 --- a/libavutil/thread.h +++ b/libavutil/thread.h @@ -229,6 +229,8 @@ static inline int ff_thread_setname(const char *name) #endif #elif HAVE_PTHREAD_SET_NAME_NP pthread_set_name_np(pthread_self(), name); +#elif HAVE_W32THREADS + ret = win32_thread_setname(name); #else ret = AVERROR(ENOSYS); #endif From 3a8b3dfeca2ddbee9999262f3240bfe05b55c66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Mon, 28 Jul 2025 19:07:32 +0200 Subject: [PATCH 07/20] avformat/tls_openssl: use ascii - (0x2D) instead of 0x2010 hyphen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Too much AI is bad for you... Fixes: 167e343bbe75515a80db8ee72ffa0c607c944a00 Signed-off-by: Kacper Michajłow --- libavformat/tls_openssl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c index bab2e711c6..a1073da216 100644 --- a/libavformat/tls_openssl.c +++ b/libavformat/tls_openssl.c @@ -35,8 +35,8 @@ #include /** - * Returns a heap‐allocated null‐terminated string containing - * the PEM‐encoded public key. Caller must free. + * Returns a heap-allocated null-terminated string containing + * the PEM-encoded public key. Caller must free. */ static char *pkey_to_pem_string(EVP_PKEY *pkey) { BIO *mem = NULL; @@ -61,7 +61,7 @@ static char *pkey_to_pem_string(EVP_PKEY *pkey) { if (!pem_str) goto err; - // Copy data & NUL‐terminate + // Copy data & NUL-terminate memcpy(pem_str, bptr->data, bptr->length); pem_str[bptr->length] = '\0'; @@ -427,7 +427,7 @@ error: /** - * Deserialize a PEM‐encoded private or public key from a NUL-terminated C string. + * Deserialize a PEM-encoded private or public key from a NUL-terminated C string. * * @param pem_str The PEM text, e.g. * "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----\n" @@ -458,7 +458,7 @@ static EVP_PKEY *pkey_from_pem_string(const char *pem_str, int is_priv) } /** - * Deserialize a PEM‐encoded certificate from a NUL-terminated C string. + * Deserialize a PEM-encoded certificate from a NUL-terminated C string. * * @param pem_str The PEM text, e.g. * "-----BEGIN CERTIFICATE-----\n…\n-----END CERTIFICATE-----\n" From 113c9c6cf36a703afc5ef10bbb40f248df521425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Wed, 30 Jul 2025 20:08:38 +0200 Subject: [PATCH 08/20] configure: require at least OpenSSL 1.1.1 (LTS) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f256487cd8f29f24036efa5d91a84a26b048861a bumped requirement to 1.1.0 for OPENSSL_init_ssl. Bump this again to 1.1.1, because it was an LTS version. Although it has no mainline support anymore, it still has paid/premium support. 1.1.0 has no support at all. Motivated for use of BIO_read_ex() for next commits. Signed-off-by: Kacper Michajłow --- Changelog | 1 + configure | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 0b0e6ecbf4..98b259f17f 100644 --- a/Changelog +++ b/Changelog @@ -8,6 +8,7 @@ version 8.0: - Whisper filter - Drop support for OpenSSL < 1.1.0 - Enable TLS peer certificate verification by default (on next major version bump) +- Drop support for OpenSSL < 1.1.1 - yasm support dropped, users need to use nasm - VVC VAAPI decoder - RealVideo 6.0 decoder diff --git a/configure b/configure index 6c1d6868ea..e1809a3e58 100755 --- a/configure +++ b/configure @@ -7260,10 +7260,10 @@ enabled omx && require_headers OMX_Core.h && \ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0" openssl/ssl.h OPENSSL_init_ssl && { enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } || { enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } || - check_pkg_config openssl "openssl >= 1.1.0" openssl/ssl.h OPENSSL_init_ssl || + check_pkg_config openssl "openssl >= 1.1.1" openssl/ssl.h OPENSSL_init_ssl || check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto || check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto -lws2_32 -lgdi32 || - die "ERROR: openssl (>= 1.1.0) not found"; } + die "ERROR: openssl (>= 1.1.1) not found"; } enabled pocketsphinx && require_pkg_config pocketsphinx pocketsphinx pocketsphinx/pocketsphinx.h ps_init enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/rk_mpi.h mpp_create && require_pkg_config rockchip_mpp "rockchip_mpp >= 1.3.7" rockchip/rk_mpi.h mpp_create && From 4676f97928c1c38753d4f5da4ec13a75d5f22944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Tue, 29 Jul 2025 23:55:33 +0200 Subject: [PATCH 09/20] avformat/tls_openssl: clean keys serialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was unnecessary convoluted, remove not needed memory allocations, snprintf. Also fixes posibility to call snprinft with NULL as %s input. Signed-off-by: Kacper Michajłow --- libavformat/tls_openssl.c | 107 +++++++++++++------------------------- 1 file changed, 36 insertions(+), 71 deletions(-) diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c index a1073da216..ba5895316a 100644 --- a/libavformat/tls_openssl.c +++ b/libavformat/tls_openssl.c @@ -35,80 +35,57 @@ #include /** - * Returns a heap-allocated null-terminated string containing - * the PEM-encoded public key. Caller must free. + * Convert an EVP_PKEY to a PEM string. */ -static char *pkey_to_pem_string(EVP_PKEY *pkey) { - BIO *mem = NULL; - BUF_MEM *bptr = NULL; - char *pem_str = NULL; +static int pkey_to_pem_string(EVP_PKEY *pkey, char *out, size_t out_sz) +{ + BIO *mem = NULL; + size_t read_bytes = 0; + + if (!pkey || !out || !out_sz) + goto done; - // Create a memory BIO if (!(mem = BIO_new(BIO_s_mem()))) - goto err; + goto done; - // Write public key in PEM form if (!PEM_write_bio_PrivateKey(mem, pkey, NULL, NULL, 0, NULL, NULL)) - goto err; + goto done; - // Extract pointer/length - BIO_get_mem_ptr(mem, &bptr); - if (!bptr || !bptr->length) - goto err; + if (!BIO_read_ex(mem, out, out_sz - 1, &read_bytes)) + goto done; - // Allocate string (+1 for NUL) - pem_str = av_malloc(bptr->length + 1); - if (!pem_str) - goto err; - - // Copy data & NUL-terminate - memcpy(pem_str, bptr->data, bptr->length); - pem_str[bptr->length] = '\0'; - -cleanup: +done: BIO_free(mem); - return pem_str; - -err: - // error path: free and return NULL - free(pem_str); - pem_str = NULL; - goto cleanup; + if (out && out_sz) + out[read_bytes] = '\0'; + return read_bytes; } /** - * Serialize an X509 certificate to a av_malloc’d PEM string. - * Caller must free the returned pointer. + * Convert an X509 certificate to a PEM string. */ -static char *cert_to_pem_string(X509 *cert) +static int cert_to_pem_string(X509 *cert, char *out, size_t out_sz) { - BIO *mem = BIO_new(BIO_s_mem()); - BUF_MEM *bptr = NULL; - char *out = NULL; + BIO *mem = NULL; + size_t read_bytes = 0; - if (!mem) goto err; + if (!cert || !out || !out_sz) + goto done; + + if (!(mem = BIO_new(BIO_s_mem()))) + goto done; - /* Write the PEM certificate */ if (!PEM_write_bio_X509(mem, cert)) - goto err; + goto done; - BIO_get_mem_ptr(mem, &bptr); - if (!bptr || !bptr->length) goto err; + if (!BIO_read_ex(mem, out, out_sz - 1, &read_bytes)) + goto done; - out = av_malloc(bptr->length + 1); - if (!out) goto err; - - memcpy(out, bptr->data, bptr->length); - out[bptr->length] = '\0'; - -cleanup: +done: BIO_free(mem); - return out; - -err: - free(out); - out = NULL; - goto cleanup; + if (out && out_sz) + out[read_bytes] = '\0'; + return read_bytes; } @@ -165,7 +142,6 @@ int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t ke AVBPrint key_bp, cert_bp; EVP_PKEY *pkey = NULL; X509 *cert = NULL; - char *key_tem = NULL, *cert_tem = NULL; /* To prevent a crash during cleanup, always initialize it. */ av_bprint_init(&key_bp, 1, MAX_CERTIFICATE_SIZE); @@ -211,11 +187,8 @@ int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t ke goto end; } - key_tem = pkey_to_pem_string(pkey); - cert_tem = cert_to_pem_string(cert); - - snprintf(key_buf, key_sz, "%s", key_tem); - snprintf(cert_buf, cert_sz, "%s", cert_tem); + pkey_to_pem_string(pkey, key_buf, key_sz); + cert_to_pem_string(cert, cert_buf, cert_sz); /* Generate fingerprint. */ if (fingerprint) { @@ -232,8 +205,6 @@ end: av_bprint_finalize(&key_bp, NULL); BIO_free(cert_b); av_bprint_finalize(&cert_bp, NULL); - av_free(key_tem); - av_free(cert_tem); EVP_PKEY_free(pkey); X509_free(cert); return ret; @@ -403,7 +374,6 @@ int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cer int ret = 0; EVP_PKEY *pkey = NULL; X509 *cert = NULL; - char *key_tem = NULL, *cert_tem = NULL; ret = openssl_gen_private_key(&pkey); if (ret < 0) goto error; @@ -411,14 +381,9 @@ int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cer ret = openssl_gen_certificate(pkey, &cert, fingerprint); if (ret < 0) goto error; - key_tem = pkey_to_pem_string(pkey); - cert_tem = cert_to_pem_string(cert); + pkey_to_pem_string(pkey, key_buf, key_sz); + cert_to_pem_string(cert, cert_buf, cert_sz); - snprintf(key_buf, key_sz, "%s", key_tem); - snprintf(cert_buf, cert_sz, "%s", cert_tem); - - av_free(key_tem); - av_free(cert_tem); error: X509_free(cert); EVP_PKEY_free(pkey); From 61d00509244d7503b3ad467c719da2662d11b6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Wed, 30 Jul 2025 00:23:20 +0200 Subject: [PATCH 10/20] avformat/tls_openssl: simplify fingerprint generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kacper Michajłow --- libavformat/tls_openssl.c | 63 ++++++++++----------------------------- 1 file changed, 16 insertions(+), 47 deletions(-) diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c index ba5895316a..0f2dbc8da6 100644 --- a/libavformat/tls_openssl.c +++ b/libavformat/tls_openssl.c @@ -91,48 +91,26 @@ done: /** * Generate a SHA-256 fingerprint of an X.509 certificate. - * - * @param ctx AVFormatContext for logging (can be NULL) - * @param cert X509 certificate to fingerprint - * @return Newly allocated fingerprint string in "AA:BB:CC:…" format, - * or NULL on error (logs via av_log if ctx is not NULL). - * Caller must free() the returned string. */ -static char *generate_fingerprint(X509 *cert) +static int x509_fingerprint(X509 *cert, char **fingerprint) { unsigned char md[EVP_MAX_MD_SIZE]; int n = 0; - AVBPrint fingerprint; - char *result = NULL; - int i; - - /* To prevent a crash during cleanup, always initialize it. */ - av_bprint_init(&fingerprint, 0, AV_BPRINT_SIZE_UNLIMITED); + AVBPrint buf; if (X509_digest(cert, EVP_sha256(), md, &n) != 1) { - av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint, %s\n", ERR_error_string(ERR_get_error(), NULL)); - goto end; + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint, %s\n", + ERR_error_string(ERR_get_error(), NULL)); + return AVERROR(ENOMEM); } - for (i = 0; i < n; i++) { - av_bprintf(&fingerprint, "%02X", md[i]); - if (i + 1 < n) - av_bprintf(&fingerprint, ":"); - } + av_bprint_init(&buf, n*3, n*3); - if (!fingerprint.str || !strlen(fingerprint.str)) { - av_log(NULL, AV_LOG_ERROR, "TLS: Fingerprint is empty\n"); - goto end; - } + for (int i = 0; i < n - 1; i++) + av_bprintf(&buf, "%02X:", md[i]); + av_bprintf(&buf, "%02X", md[n - 1]); - result = av_strdup(fingerprint.str); - if (!result) { - av_log(NULL, AV_LOG_ERROR, "TLS: Out of memory generating fingerprint\n"); - } - -end: - av_bprint_finalize(&fingerprint, NULL); - return result; + return av_bprint_finalize(&buf, fingerprint); } int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint) @@ -190,15 +168,9 @@ int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t ke pkey_to_pem_string(pkey, key_buf, key_sz); cert_to_pem_string(cert, cert_buf, cert_sz); - /* Generate fingerprint. */ - if (fingerprint) { - *fingerprint = generate_fingerprint(cert); - if (!*fingerprint) { - av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint from %s\n", cert_url); - ret = AVERROR(EIO); - goto end; - } - } + ret = x509_fingerprint(cert, fingerprint); + if (ret < 0) + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint from %s\n", cert_url); end: BIO_free(key_b); @@ -347,12 +319,9 @@ static int openssl_gen_certificate(EVP_PKEY *pkey, X509 **cert, char **fingerpri goto einval_end; } - if (fingerprint) { - *fingerprint = generate_fingerprint(*cert); - if (!*fingerprint) { - goto enomem_end; - } - } + ret = x509_fingerprint(*cert, fingerprint); + if (ret < 0) + goto end; goto end; enomem_end: From 50affd2b09ca7ebf6beb287a087947be887b2417 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Fri, 15 Aug 2025 19:49:19 +0200 Subject: [PATCH 11/20] avcodec/rv60dec: clear pu_info pu_info is read uninitialized on damaged input and at that point the following codepath is dependant on the uninitialized data. In one of these pathes out of array accesses happen. None of this is replicatable Less uninitialized data also should result in more reproducable reports Fixes: Use of uninitialized memory Fixes: 418335931/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_RV60_fuzzer-5103986067963904 Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg Signed-off-by: Michael Niedermayer --- libavcodec/rv60dec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavcodec/rv60dec.c b/libavcodec/rv60dec.c index 4a3d9067db..208fbc68f7 100644 --- a/libavcodec/rv60dec.c +++ b/libavcodec/rv60dec.c @@ -308,6 +308,8 @@ static int update_dimensions_clear_info(RV60Context *s, int width, int height) if ((ret = av_reallocp_array(&s->blk_info, s->blk_stride * (s->cu_height << 4), sizeof(s->blk_info[0]))) < 0) return ret; + memset(s->pu_info, 0, s->pu_stride * (s->cu_height << 3) * sizeof(s->pu_info[0])); + for (int j = 0; j < s->cu_height << 4; j++) for (int i = 0; i < s->cu_width << 4; i++) s->blk_info[j*s->blk_stride + i].mv.mvref = MVREF_NONE; From 37507c6a78ab63bbf8dda1c0525545d30877bca2 Mon Sep 17 00:00:00 2001 From: Jack Lau Date: Mon, 21 Jul 2025 15:47:37 +0800 Subject: [PATCH 12/20] avformat/whip: remove DTLSState enum This patch aims to simplify the dtls handshake process since dtls handshake use force block mode We can just use the return code instead of DTLSState enum Signed-off-by: Jack Lau --- libavformat/tls.h | 15 ---------- libavformat/tls_openssl.c | 7 ----- libavformat/tls_schannel.c | 7 ----- libavformat/whip.c | 61 ++++++++------------------------------ 4 files changed, 13 insertions(+), 77 deletions(-) diff --git a/libavformat/tls.h b/libavformat/tls.h index 4d4999aa7c..2afa248f4c 100644 --- a/libavformat/tls.h +++ b/libavformat/tls.h @@ -34,17 +34,6 @@ */ #define MAX_CERTIFICATE_SIZE 8192 -enum DTLSState { - DTLS_STATE_NONE, - - /* Whether DTLS handshake is finished. */ - DTLS_STATE_FINISHED, - /* Whether DTLS session is closed. */ - DTLS_STATE_CLOSED, - /* Whether DTLS handshake is failed. */ - DTLS_STATE_FAILED, -}; - typedef struct TLSShared { char *ca_file; int verify; @@ -65,8 +54,6 @@ typedef struct TLSShared { int is_dtls; int use_srtp; - enum DTLSState state; - /* The certificate and private key content used for DTLS handshake */ char* cert_buf; char* key_buf; @@ -112,8 +99,6 @@ int ff_tls_set_external_socket(URLContext *h, URLContext *sock); int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t materials_sz); -int ff_dtls_state(URLContext *h); - int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint); int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint); diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c index 0f2dbc8da6..db7147e491 100644 --- a/libavformat/tls_openssl.c +++ b/libavformat/tls_openssl.c @@ -476,12 +476,6 @@ int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t ma return 0; } -int ff_dtls_state(URLContext *h) -{ - TLSContext *c = h->priv_data; - return c->tls_shared.state; -} - static int print_ssl_error(URLContext *h, int ret) { TLSContext *c = h->priv_data; @@ -656,7 +650,6 @@ static int dtls_handshake(URLContext *h) goto end; ret = 0; - c->tls_shared.state = DTLS_STATE_FINISHED; end: return ret; } diff --git a/libavformat/tls_schannel.c b/libavformat/tls_schannel.c index b60e3100be..b854f484fa 100644 --- a/libavformat/tls_schannel.c +++ b/libavformat/tls_schannel.c @@ -681,12 +681,6 @@ int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t ma #endif } -int ff_dtls_state(URLContext *h) -{ - TLSContext *c = h->priv_data; - return c->tls_shared.state; -} - static void init_sec_buffer(SecBuffer *buffer, unsigned long type, void *data, unsigned long size) { @@ -1111,7 +1105,6 @@ static int tls_handshake(URLContext *h) #endif c->connected = 1; - s->state = DTLS_STATE_FINISHED; fail: return ret; diff --git a/libavformat/whip.c b/libavformat/whip.c index 65fd3b39b2..e2fa774c6f 100644 --- a/libavformat/whip.c +++ b/libavformat/whip.c @@ -198,9 +198,6 @@ typedef struct WHIPContext { /* The state of the RTC connection. */ enum WHIPState state; - /* The callback return value for DTLS. */ - int dtls_ret; - int dtls_closed; /* Parameters for the input audio and video codecs. */ AVCodecParameters *audio_par; @@ -348,41 +345,6 @@ static av_cold int certificate_key_init(AVFormatContext *s) return ret; } -/** - * When DTLS state change. - */ -static int dtls_context_on_state(AVFormatContext *s, const char* type, const char* desc) -{ - int ret = 0; - WHIPContext *whip = s->priv_data; - int state = ff_dtls_state(whip->dtls_uc); - - if (state == DTLS_STATE_CLOSED) { - whip->dtls_closed = 1; - av_log(whip, AV_LOG_VERBOSE, "DTLS session closed, type=%s, desc=%s, elapsed=%dms\n", - type ? type : "", desc ? desc : "", ELAPSED(whip->whip_starttime, av_gettime())); - goto error; - } - - if (state == DTLS_STATE_FAILED) { - whip->state = WHIP_STATE_FAILED; - av_log(whip, AV_LOG_ERROR, "DTLS session failed, type=%s, desc=%s\n", - type ? type : "", desc ? desc : ""); - whip->dtls_ret = AVERROR(EIO); - goto error; - } - - if (state == DTLS_STATE_FINISHED && whip->state < WHIP_STATE_DTLS_FINISHED) { - whip->state = WHIP_STATE_DTLS_FINISHED; - whip->whip_dtls_time = av_gettime(); - av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n", - ELAPSED(whip->whip_starttime, av_gettime())); - return ret; - } -error: - return -1; -} - static av_cold int dtls_initialize(AVFormatContext *s) { WHIPContext *whip = s->priv_data; @@ -1326,9 +1288,18 @@ next_packet: /* If got any DTLS messages, handle it. */ if (is_dtls_packet(whip->buf, ret) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_DTLS_CONNECTING) { whip->state = WHIP_STATE_DTLS_CONNECTING; - if ((ret = ffurl_handshake(whip->dtls_uc)) < 0) + ret = ffurl_handshake(whip->dtls_uc); + if (ret < 0) { + whip->state = WHIP_STATE_FAILED; + av_log(whip, AV_LOG_VERBOSE, "DTLS session failed\n"); goto end; - dtls_context_on_state(s, NULL, NULL); + } + if (!ret) { + whip->state = WHIP_STATE_DTLS_FINISHED; + whip->whip_dtls_time = av_gettime(); + av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n", + ELAPSED(whip->whip_starttime, whip->whip_dtls_time)); + } goto next_packet; } } @@ -1771,10 +1742,8 @@ static av_cold int whip_init(AVFormatContext *s) goto end; end: - if (ret < 0 && whip->state < WHIP_STATE_FAILED) + if (ret < 0) whip->state = WHIP_STATE_FAILED; - if (ret >= 0 && whip->state >= WHIP_STATE_FAILED && whip->dtls_ret < 0) - ret = whip->dtls_ret; return ret; } @@ -1822,12 +1791,8 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt) } end: - if (ret < 0 && whip->state < WHIP_STATE_FAILED) + if (ret < 0) whip->state = WHIP_STATE_FAILED; - if (ret >= 0 && whip->state >= WHIP_STATE_FAILED && whip->dtls_ret < 0) - ret = whip->dtls_ret; - if (ret >= 0 && whip->dtls_closed) - ret = AVERROR(EIO); return ret; } From 30e695692041888d727159c6279f433540453c3f Mon Sep 17 00:00:00 2001 From: Leo Izen Date: Wed, 13 Aug 2025 22:54:04 -0400 Subject: [PATCH 13/20] .forgejo/CODEOWNERS: add myself to JPEG XL and PNG Add myself to JPEG XL and PNG decoders/encoders/parsers/etc. as a suggested reviewer. I'm the maintainer of the JXL files and I know the PNG code very well. Signed-off-by: Leo Izen --- .forgejo/CODEOWNERS | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS index a4ea6dd50f..0d310fb71f 100644 --- a/.forgejo/CODEOWNERS +++ b/.forgejo/CODEOWNERS @@ -20,9 +20,10 @@ libavcodec/.*ffv1.* @lynne @michaelni libavcodec/golomb.* @michaelni libavcodec/.*h266.* @frankplow @NuoMi @jianhuaw libavcodec/h26x/.* @frankplow @NuoMi @jianhuaw -libavcodec/.*jpegxl.* @lynne -libavcodec/.*jxl.* @lynne +libavcodec/.*jpegxl.* @lynne @Traneptora +libavcodec/.*jxl.* @lynne @Traneptora libavcodec/.*opus.* @lynne +libavcodec/.*png.* @Traneptora libavcodec/.*prores.* @lynne libavcodec/rangecoder.* @michaelni libavcodec/ratecontrol.* @michaelni @@ -45,6 +46,8 @@ libavfilter/vsrc_mandelbrot.* @michaelni # avformat # ======= libavformat/iamf.* @jamrial +libavformat/.*jpegxl.* @Traneptora +libavformat/.*jxl.* @Traneptora # avutil # ====== From 1d06e8ddcd8c14232d0a3c2b1c21e50b232549b4 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Fri, 15 Aug 2025 20:39:49 +0800 Subject: [PATCH 14/20] avfilter/af_whisper: fix broken output for multibyte character text + 1 can break a multibyte character, e.g., Chinese in UTF-8. There is no space at the beginning in this case. --- libavfilter/af_whisper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libavfilter/af_whisper.c b/libavfilter/af_whisper.c index 186b624504..bd145d6d1d 100644 --- a/libavfilter/af_whisper.c +++ b/libavfilter/af_whisper.c @@ -215,7 +215,9 @@ static void run_transcription(AVFilterContext *ctx, AVFrame *frame, int samples) for (int i = 0; i < n_segments; ++i) { const char *text = whisper_full_get_segment_text(wctx->ctx_wsp, i); - char *text_cleaned = av_strireplace(text + 1, "[BLANK_AUDIO]", ""); + if (av_isspace(text[0])) + text++; + char *text_cleaned = av_strireplace(text, "[BLANK_AUDIO]", ""); if (av_strnlen(text_cleaned, 1) == 0) { av_freep(&text_cleaned); From 7df92712723dee0ace3596684a90282c0e12a8ef Mon Sep 17 00:00:00 2001 From: Gyan Doshi Date: Sat, 16 Aug 2025 16:12:10 +0530 Subject: [PATCH 15/20] avfilter/whisper: correct option formatting --- libavfilter/af_whisper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavfilter/af_whisper.c b/libavfilter/af_whisper.c index bd145d6d1d..385180b4ed 100644 --- a/libavfilter/af_whisper.c +++ b/libavfilter/af_whisper.c @@ -430,7 +430,8 @@ static int query_formats(const AVFilterContext *ctx, #define HOURS 3600000000 static const AVOption whisper_options[] = { - { "model", "Path to the whisper.cpp model file", OFFSET(model_path), AV_OPT_TYPE_STRING,.flags = FLAGS }, { "language", "Language for transcription ('auto' for auto-detect)", OFFSET(language), AV_OPT_TYPE_STRING, {.str = "auto"}, .flags = FLAGS }, + { "model", "Path to the whisper.cpp model file", OFFSET(model_path), AV_OPT_TYPE_STRING,.flags = FLAGS }, + { "language", "Language for transcription ('auto' for auto-detect)", OFFSET(language), AV_OPT_TYPE_STRING, {.str = "auto"}, .flags = FLAGS }, { "queue", "Audio queue size", OFFSET(queue), AV_OPT_TYPE_DURATION, {.i64 = 3000000}, 20000, HOURS, .flags = FLAGS }, { "use_gpu", "Use GPU for processing", OFFSET(use_gpu), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags = FLAGS }, { "gpu_device", "GPU device to use", OFFSET(gpu_device), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, .flags = FLAGS }, From a28e01a6c16430da689340d0af6eec094020b719 Mon Sep 17 00:00:00 2001 From: James Almer Date: Sat, 16 Aug 2025 14:16:38 -0300 Subject: [PATCH 16/20] avformat/mov: don't use an allocated array for sample_size with HEIF images The array is only ever needed for streams where each sample entry may have a different value. Given that for non animated HEIF there's a single value that applies to the image, use the field defined for that. Fixes: NULL pointer dereference Fixes: 437528618/clusterfuzz-testcase-minimized-ffmpeg_dem_MOV_fuzzer-6537287645331456 Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg Signed-off-by: James Almer --- libavformat/mov.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libavformat/mov.c b/libavformat/mov.c index 86037c6712..b29c41a6b6 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -5456,10 +5456,6 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item) if (!sc->chunk_offsets) goto fail; sc->chunk_count = 1; - sc->sample_sizes = av_malloc_array(1, sizeof(*sc->sample_sizes)); - if (!sc->sample_sizes) - goto fail; - sc->sample_count = 1; sc->stts_data = av_malloc_array(1, sizeof(*sc->stts_data)); if (!sc->stts_data) goto fail; @@ -10471,11 +10467,13 @@ static int mov_parse_heif_items(AVFormatContext *s) st->codecpar->width = item->width; st->codecpar->height = item->height; + sc->sample_size = sc->stsz_sample_size = item->extent_length; + sc->sample_count = 1; + err = sanity_checks(s, sc, item->item_id); - if (err || !sc->sample_count) + if (err) return AVERROR_INVALIDDATA; - sc->sample_sizes[0] = item->extent_length; sc->chunk_offsets[0] = item->extent_offset + offset; if (item->item_id == mov->primary_item_id) From a2cfaf1b916cd4519249031adc37e339baca7a7b Mon Sep 17 00:00:00 2001 From: James Almer Date: Sat, 16 Aug 2025 21:38:32 -0300 Subject: [PATCH 17/20] avformat/mov: pass stream index to sanity_checks on HEIF images Instead of item_id. Same behavior as with standard video tracks. Signed-off-by: James Almer --- libavformat/mov.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/mov.c b/libavformat/mov.c index b29c41a6b6..e9a582e5aa 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -10470,7 +10470,7 @@ static int mov_parse_heif_items(AVFormatContext *s) sc->sample_size = sc->stsz_sample_size = item->extent_length; sc->sample_count = 1; - err = sanity_checks(s, sc, item->item_id); + err = sanity_checks(s, sc, st->index); if (err) return AVERROR_INVALIDDATA; From 134fbfd1dcb59441e38d870ddd231772f4e8e127 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Sun, 17 Aug 2025 15:31:48 +0200 Subject: [PATCH 18/20] avcodec/sanm: Check w,h,left,top The setup code fow w,h,left,top is complex, the code using it also falls in at least 2 different classes, one using left/top the other not. To ensure no out of array access happens we add this clear check. Fixes: out of array access Fixes: 439261995/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_SANM_fuzzer-5383455572819968 Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg Signed-off-by: Michael Niedermayer --- libavcodec/sanm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index a066a864eb..9e99aa9dd9 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -1757,6 +1757,11 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) memset(ctx->fbuf, 0, ctx->frm0_size); } + if (w + FFMAX(left, 0) > ctx->avctx->width || h + FFMAX(top, 0) > ctx->avctx->height) { + avpriv_request_sample(ctx->avctx, "overly large frame\n"); + return AVERROR_PATCHWELCOME; + } + switch (codec) { case 1: case 3: From 8426622bb9584ec4fc32215f006a91e7ddc480cc Mon Sep 17 00:00:00 2001 From: Valerii Zapodovnikov Date: Mon, 4 Aug 2025 23:53:49 +0300 Subject: [PATCH 19/20] avformat/avio: clarify that AVSEEK_FORCE has no effect avio_seek() never supported SEEK_END and returned AVERROR(EINVAL) when specified, so the later check "(whence != SEEK_END || force)" was always true. This also means that AVSEEK_FORCE had no effect since 7a6fe01f99, that is 15 years ago. Rather than changing behaviour, let's document instead that the flag has no effect. Also fixed other commit 41ed7ab45f which confused ORing / passing AVSEEK_FORCE and AVSEEK_SIZE in the docs. Signed-off-by: Valerii Zapodovnikov --- libavformat/avio.h | 6 +++--- libavformat/aviobuf.c | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/libavformat/avio.h b/libavformat/avio.h index ebf611187d..fbcb7dbfc3 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -461,17 +461,17 @@ int avio_put_str16be(AVIOContext *s, const char *str); void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type); /** - * ORing this as the "whence" parameter to a seek function causes it to + * Passing this as the "whence" parameter to a seek function causes it to * return the filesize without seeking anywhere. Supporting this is optional. * If it is not supported then the seek function will return <0. */ #define AVSEEK_SIZE 0x10000 /** - * Passing this flag as the "whence" parameter to a seek function causes it to + * OR'ing this flag into the "whence" parameter to a seek function causes it to * seek by any means (like reopening and linear reading) or other normally unreasonable * means that can be extremely slow. - * This may be ignored by the seek code. + * This is the default and therefore ignored by the seek code since 2010. */ #define AVSEEK_FORCE 0x20000 diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 34743556ae..2e65f50006 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -238,10 +238,9 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) FFIOContext *const ctx = ffiocontext(s); int64_t offset1; int64_t pos; - int force = whence & AVSEEK_FORCE; int buffer_size; int short_seek; - whence &= ~AVSEEK_FORCE; + whence &= ~AVSEEK_FORCE; // force flag does nothing if(!s) return AVERROR(EINVAL); @@ -282,8 +281,7 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) } else if ((!(s->seekable & AVIO_SEEKABLE_NORMAL) || offset1 <= buffer_size + short_seek) && !s->write_flag && offset1 >= 0 && - (!s->direct || !s->seek) && - (whence != SEEK_END || force)) { + (!s->direct || !s->seek)) { while(s->pos < offset && !s->eof_reached) fill_buffer(s); if (s->eof_reached) @@ -300,7 +298,7 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) s->pos = pos; s->eof_reached = 0; fill_buffer(s); - return avio_seek(s, offset, SEEK_SET | force); + return avio_seek(s, offset, SEEK_SET); } else { int64_t res; if (s->write_flag) { From 0226b6fb2c0855eca417feb0a4e3bdf1f1193332 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Thu, 14 Aug 2025 00:21:09 +0200 Subject: [PATCH 20/20] avcodec/sanm: bl16: fix artifacts in larger videos The DOS/Windows decoder precomputes a table of linear offsets of all motion vectors given the current image width. For larger widths (>=762), the pairs starting at indices 1 and 254 of motion_vectors[] will overflow the int16_t, changing the sign. Playing back the 800x600 "jonesopn_8.snm" video of "Indiana Jones and the Infernal Machine" reveals a lot of artifacts and a lot of "Ignoring invalid motion vector (149, -41)->(136, 0), block size = 8" messages, hinting at the wrong direction of the motion vectors. Fix this by doing the calculation that the DOS/Windows players do, let the value overflow and reextract the "new" mvec x/y components. --- libavcodec/sanm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index 9e99aa9dd9..e4308af647 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -2074,6 +2074,20 @@ static int codec2subblock(SANMVideoContext *ctx, int cx, int cy, int blk_size) mx = motion_vectors[opcode][0]; my = motion_vectors[opcode][1]; + /* The original implementation of this codec precomputes a table + * of int16_t all motion vectors for given image width. + * For larger widths, starting with 762 pixels, the calculation of + * mv table indices 1+ and 255- overflow the int16_t, inverting the + * sign of the offset. This is actively exploited in e.g. the + * "jonesopn_8.snm" video of "Indiana Jones and the Infernal Machine". + * Therefore let the overflow happen and extract x/y components from + * the new value. + */ + if (ctx->width > 761) { + index = (int16_t)(my * ctx->width + mx); + mx = index % ctx->width; + my = index / ctx->width; + } if (good_mvec(ctx, cx, cy, mx, my, blk_size)) { copy_block(ctx->frm0 + cx + ctx->pitch * cy, ctx->frm2 + cx + mx + ctx->pitch * (cy + my),