From: Kirill A. Korinsky Subject: emulators/retroarch: compatibility with ffmpeg 9.0 To: OpenBSD ports Date: Wed, 12 Aug 2026 21:42:30 +0200 ports@, I'd like to backport a fix for emulators/retroarch which fixes compatibility with ffmpeg-9.0 Build tested against ffmpeg-9.0 and ffmpeg-8.1.2 The fix in upstream: https://github.com/libretro/RetroArch/commit/f5b422d5a81f0b4d263e2f52c8419ea9ce7467f6 Ok? Index: Makefile =================================================================== RCS file: /home/cvs/ports/emulators/retroarch/Makefile,v diff -u -p -r1.34 Makefile --- Makefile 18 Feb 2026 18:32:50 -0000 1.34 +++ Makefile 12 Aug 2026 17:03:59 -0000 @@ -4,7 +4,7 @@ DISTNAME = retroarch-sourceonly-$V PKGNAME = retroarch-$V V = 1.21.0 -REVISION = 0 +REVISION = 1 # crashes when recording videos due to ffmpeg USE_NOBTCFI-amd64 = Yes Index: patches/patch-camera_drivers_ffmpeg_c =================================================================== RCS file: patches/patch-camera_drivers_ffmpeg_c diff -N patches/patch-camera_drivers_ffmpeg_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-camera_drivers_ffmpeg_c 12 Aug 2026 17:07:36 -0000 @@ -0,0 +1,34 @@ +https://github.com/libretro/RetroArch/commit/f5b422d5a81f0b4d263e2f52c8419ea9ce7467f6 + +Index: camera/drivers/ffmpeg.c +--- camera/drivers/ffmpeg.c.orig ++++ camera/drivers/ffmpeg.c +@@ -42,13 +42,26 @@ + #define FFMPEG_CAMERA_DEFAULT_BACKEND "bktr" + #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; Index: patches/patch-record_drivers_record_ffmpeg_c =================================================================== RCS file: /home/cvs/ports/emulators/retroarch/patches/patch-record_drivers_record_ffmpeg_c,v diff -u -p -r1.1 patch-record_drivers_record_ffmpeg_c --- patches/patch-record_drivers_record_ffmpeg_c 21 Oct 2025 12:51:47 -0000 1.1 +++ patches/patch-record_drivers_record_ffmpeg_c 12 Aug 2026 17:06:43 -0000 @@ -1,9 +1,10 @@ https://github.com/libretro/RetroArch/commit/21776a2e59f5f5899ff2198c0df25a95b5020012 +https://github.com/libretro/RetroArch/commit/f5b422d5a81f0b4d263e2f52c8419ea9ce7467f6 Index: record/drivers/record_ffmpeg.c --- record/drivers/record_ffmpeg.c.orig +++ record/drivers/record_ffmpeg.c -@@ -72,6 +72,15 @@ extern "C" { +@@ -72,6 +72,24 @@ extern "C" { #ifndef FFMPEG3 #define FFMPEG3 (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)) #endif @@ -12,6 +13,15 @@ Index: record/drivers/record_ffmpeg.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 @@ -19,7 +29,132 @@ Index: record/drivers/record_ffmpeg.c #define HAVE_CH_LAYOUT (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100)) struct ff_video_info -@@ -951,7 +960,11 @@ static void ffmpeg_free(void *data) +@@ -200,11 +218,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; +@@ -214,30 +269,32 @@ static bool ffmpeg_codec_has_sample_format(enum AVSamp + 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; +@@ -252,21 +309,24 @@ static void ffmpeg_audio_resolve_sample_rate(ffmpeg_t + { + 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); +@@ -275,7 +335,7 @@ static void ffmpeg_audio_resolve_sample_rate(ffmpeg_t + + if (better_rate) + { +- best_rate = codec->supported_samplerates[i]; ++ best_rate = supported_samplerates[i]; + best_diff = diff; + } + } +@@ -951,7 +1011,11 @@ static void ffmpeg_free(void *data) if (handle->audio.codec) { @@ -31,7 +166,7 @@ Index: record/drivers/record_ffmpeg.c av_free(handle->audio.codec); } -@@ -959,7 +972,11 @@ static void ffmpeg_free(void *data) +@@ -959,7 +1023,11 @@ static void ffmpeg_free(void *data) if (handle->video.codec) { -- wbr, Kirill