diff --git a/retroarch-ffmpeg9.patch b/retroarch-ffmpeg9.patch new file mode 100644 index 0000000..f82f70d --- /dev/null +++ b/retroarch-ffmpeg9.patch @@ -0,0 +1,207 @@ +From f5b422d5a81f0b4d263e2f52c8419ea9ce7467f6 Mon Sep 17 00:00:00 2001 +From: LibretroAdmin +Date: Mon, 10 Aug 2026 21:42:29 +0000 +Subject: [PATCH] ffmpeg: support FFmpeg 9 (lavc 63), version-gated back to + existing floor + +MSYS2 updated mingw-w64-ffmpeg to 9.0, which removes the deprecated +AVCodec.sample_fmts / AVCodec.supported_samplerates arrays and breaks +the MSYS2 CI build in record_ffmpeg.c. + +record_ffmpeg: add accessors that use avcodec_get_supported_config() +where available (lavc >= 61.13.100, FFmpeg 7.1+) and fall back to the +legacy AVCodec struct members on older versions. NULL now means +unrestricted instead of being dereferenced, hardening both paths. + +camera/ffmpeg: version-gate const-ness of the AVInputFormat/AVCodec +pointers; lavf 59 (FFmpeg 5.0) made that API const-correct, lavf 58 +(FFmpeg 4.x, the driver's minimum) warns on const pointers. + +Verified: compile matrix with production flags against FFmpeg 3.4.13, +4.4.5, 5.1.6, 6.1.2, 7.1.1, 8.1.2 and 9.0 all clean; full configure + +make against FFmpeg 9.0 builds and links; resolver logic runtime-tested +under ASan/UBSan against FFmpeg 9.0 (new path) and 6.1.2 (legacy path) +with identical results and no sanitizer findings. +--- + camera/drivers/ffmpeg.c | 17 +++++++- + record/drivers/record_ffmpeg.c | 71 +++++++++++++++++++++++++++++----- + 2 files changed, 76 insertions(+), 12 deletions(-) + +diff --git a/camera/drivers/ffmpeg.c b/camera/drivers/ffmpeg.c +index 2c0e68adade4..f12f55690465 100644 +--- a/camera/drivers/ffmpeg.c ++++ b/camera/drivers/ffmpeg.c +@@ -58,13 +58,26 @@ extern "C" { + #define FFMPEG_CAMERA_DEFAULT_BACKEND "lavfi" + #endif + ++/* lavf 59 (FFmpeg 5.0) made the demuxer/codec discovery API const-correct: ++ * av_find_input_format() returns const AVInputFormat*, avformat_open_input() ++ * accepts one, and av_find_best_stream() takes const AVCodec**. Older ++ * versions use mutable pointers throughout, so a single const-qualified ++ * declaration cannot satisfy both. */ ++#if LIBAVFORMAT_VERSION_MAJOR >= 59 ++typedef const AVInputFormat ffmpeg_camera_input_format_t; ++typedef const AVCodec ffmpeg_camera_codec_t; ++#else ++typedef AVInputFormat ffmpeg_camera_input_format_t; ++typedef AVCodec ffmpeg_camera_codec_t; ++#endif ++ + typedef struct ffmpeg_camera + { + sthread_t *poll_thread; + AVFormatContext *format_context; + AVCodecContext *decoder_context; +- const AVCodec *decoder; +- const AVInputFormat *input_format; /* owned by ffmpeg, don't free it */ ++ ffmpeg_camera_codec_t *decoder; /* owned by ffmpeg, don't free it */ ++ ffmpeg_camera_input_format_t *input_format; /* owned by ffmpeg, don't free it */ + AVDictionary *options; + AVPacket *packet; + AVFrame *camera_frame; +diff --git a/record/drivers/record_ffmpeg.c b/record/drivers/record_ffmpeg.c +index fc801af0b34e..5f72f2b87241 100644 +--- a/record/drivers/record_ffmpeg.c ++++ b/record/drivers/record_ffmpeg.c +@@ -79,6 +79,15 @@ extern "C" { + #define FFMPEG8 (LIBAVCODEC_VERSION_MAJOR >= 62) + #endif + ++/* avcodec_get_supported_config() was added in lavc 61.13.100 (FFmpeg 7.1) ++ * and the AVCodec.sample_fmts / AVCodec.supported_samplerates arrays it ++ * replaces were deprecated at the same time, then removed entirely in ++ * lavc 63 (FFmpeg 9). Use the new API as soon as it is available so a ++ * single codepath covers FFmpeg 7.1 through 9+, and keep the old struct ++ * members for FFmpeg 7.0 and older. */ ++#define HAVE_AVCODEC_GET_SUPPORTED_CONFIG \ ++ (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)) ++ + #ifndef AV_INPUT_BUFFER_MIN_SIZE + #define AV_INPUT_BUFFER_MIN_SIZE 16384 + #endif +@@ -216,11 +225,48 @@ typedef struct ffmpeg + + AVFormatContext *ctx; + ++/* Returns the encoder's list of supported sample formats, terminated by ++ * AV_SAMPLE_FMT_NONE, or NULL if the encoder does not restrict sample ++ * formats (or the list could not be queried). */ ++static const enum AVSampleFormat *ffmpeg_codec_sample_formats( ++ const AVCodec *codec) ++{ ++#if HAVE_AVCODEC_GET_SUPPORTED_CONFIG ++ const void *fmts = NULL; ++ if (avcodec_get_supported_config(NULL, codec, ++ AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, &fmts, NULL) < 0) ++ return NULL; ++ return (const enum AVSampleFormat*)fmts; ++#else ++ return codec->sample_fmts; ++#endif ++} ++ ++/* Returns the encoder's list of supported sample rates, terminated by 0, ++ * or NULL if the encoder does not restrict sample rates (or the list ++ * could not be queried). */ ++static const int *ffmpeg_codec_supported_samplerates(const AVCodec *codec) ++{ ++#if HAVE_AVCODEC_GET_SUPPORTED_CONFIG ++ const void *rates = NULL; ++ if (avcodec_get_supported_config(NULL, codec, ++ AV_CODEC_CONFIG_SAMPLE_RATE, 0, &rates, NULL) < 0) ++ return NULL; ++ return (const int*)rates; ++#else ++ return codec->supported_samplerates; ++#endif ++} ++ + static bool ffmpeg_codec_has_sample_format(enum AVSampleFormat fmt, + const enum AVSampleFormat *fmts) + { + unsigned i; + ++ /* A NULL list means the encoder does not restrict sample formats. */ ++ if (!fmts) ++ return true; ++ + for (i = 0; fmts[i] != AV_SAMPLE_FMT_NONE; i++) + if (fmt == fmts[i]) + return true; +@@ -230,30 +276,32 @@ static bool ffmpeg_codec_has_sample_format(enum AVSampleFormat fmt, + static void ffmpeg_audio_resolve_format(struct ff_audio_info *audio, + const AVCodec *codec) + { ++ const enum AVSampleFormat *sample_fmts = ffmpeg_codec_sample_formats(codec); ++ + audio->codec->sample_fmt = AV_SAMPLE_FMT_NONE; + +- if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_FLTP, codec->sample_fmts)) ++ if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_FLTP, sample_fmts)) + { + audio->codec->sample_fmt = AV_SAMPLE_FMT_FLTP; + audio->use_float = true; + audio->is_planar = true; + RARCH_LOG("[FFmpeg] Using sample format FLTP.\n"); + } +- else if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_FLT, codec->sample_fmts)) ++ else if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_FLT, sample_fmts)) + { + audio->codec->sample_fmt = AV_SAMPLE_FMT_FLT; + audio->use_float = true; + audio->is_planar = false; + RARCH_LOG("[FFmpeg] Using sample format FLT.\n"); + } +- else if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_S16P, codec->sample_fmts)) ++ else if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_S16P, sample_fmts)) + { + audio->codec->sample_fmt = AV_SAMPLE_FMT_S16P; + audio->use_float = false; + audio->is_planar = true; + RARCH_LOG("[FFmpeg] Using sample format S16P.\n"); + } +- else if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_S16, codec->sample_fmts)) ++ else if (ffmpeg_codec_has_sample_format(AV_SAMPLE_FMT_S16, sample_fmts)) + { + audio->codec->sample_fmt = AV_SAMPLE_FMT_S16; + audio->use_float = false; +@@ -268,21 +316,24 @@ static void ffmpeg_audio_resolve_sample_rate(ffmpeg_t *handle, + { + struct ff_config_param *params = &handle->config; + struct record_params *param = &handle->params; ++ const int *supported_samplerates = ffmpeg_codec_supported_samplerates(codec); + +- /* We'll have to force resampling to some supported sampling rate. */ +- if (codec->supported_samplerates && !params->sample_rate) ++ /* We'll have to force resampling to some supported sampling rate. ++ * A NULL list means the encoder accepts any sample rate, in which ++ * case the input rate is kept as-is. */ ++ if (supported_samplerates && !params->sample_rate) + { + unsigned i; + int input_rate = (int)param->samplerate; + + /* Favor closest sampling rate, but always prefer ratio > 1.0. */ +- int best_rate = codec->supported_samplerates[0]; ++ int best_rate = supported_samplerates[0]; + int best_diff = best_rate - input_rate; + +- for (i = 1; codec->supported_samplerates[i]; i++) ++ for (i = 1; supported_samplerates[i]; i++) + { + bool better_rate = false; +- int diff = codec->supported_samplerates[i] - input_rate; ++ int diff = supported_samplerates[i] - input_rate; + + if (best_diff < 0) + better_rate = (diff > best_diff); +@@ -291,7 +342,7 @@ static void ffmpeg_audio_resolve_sample_rate(ffmpeg_t *handle, + + if (better_rate) + { +- best_rate = codec->supported_samplerates[i]; ++ best_rate = supported_samplerates[i]; + best_diff = diff; + } + } diff --git a/retroarch.spec b/retroarch.spec index 14543ee..cca7a32 100644 --- a/retroarch.spec +++ b/retroarch.spec @@ -126,6 +126,10 @@ Patch: 0003_use_system_zstd.patch # https://github.com/libretro/RetroArch/pull/17563 Patch: https://github.com/libretro/RetroArch/pull/17563.patch#/0002-Support-for-newer-glslang-versions-without-SPIRV-and-HLSL-libraries.patch +# Support FFmpeg 9 +# https://github.com/libretro/RetroArch/issues/19351 +Patch: https://github.com/libretro/RetroArch/commit/f5b422d5a81f0b4d263e2f52c8419ea9ce7467f6.patch#/retroarch-ffmpeg9.patch + BuildRequires: desktop-file-utils BuildRequires: gcc-c++ >= 7 BuildRequires: git-core