From: Kirill A. Korinsky Subject: emulators/citra: compatibility with ffmpeg 9.0 To: OpenBSD ports Date: Thu, 13 Aug 2026 02:39:17 +0200 ports@, Here another mehanical fix for ffmpeg 9.0 compatibility for emulators/citra Build tested against ffmpeg-9.0 and ffmpeg-8.1.2 Ok? Index: Makefile =================================================================== RCS file: /home/cvs/ports/emulators/citra/Makefile,v diff -u -p -r1.43 Makefile --- Makefile 11 Aug 2026 09:16:14 -0000 1.43 +++ Makefile 12 Aug 2026 23:52:57 -0000 @@ -9,7 +9,7 @@ DISTNAME = citra-unified-source-20240303 V = 2104 PKGNAME = citra-0.0.0.${V} -REVISION = 7 +REVISION = 8 CATEGORIES = emulators Index: patches/patch-src_common_dynamic_library_ffmpeg_cpp =================================================================== RCS file: patches/patch-src_common_dynamic_library_ffmpeg_cpp diff -N patches/patch-src_common_dynamic_library_ffmpeg_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-src_common_dynamic_library_ffmpeg_cpp 13 Aug 2026 00:14:44 -0000 @@ -0,0 +1,53 @@ +ffmpeg-9.0 + +Index: src/common/dynamic_library/ffmpeg.cpp +--- src/common/dynamic_library/ffmpeg.cpp.orig ++++ src/common/dynamic_library/ffmpeg.cpp +@@ -33,14 +33,13 @@ av_hwframe_ctx_alloc_func av_hwframe_ctx_alloc; + av_hwframe_ctx_init_func av_hwframe_ctx_init; + av_hwframe_get_buffer_func av_hwframe_get_buffer; + av_hwframe_transfer_data_func av_hwframe_transfer_data; +-av_int_list_length_for_size_func av_int_list_length_for_size; + #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 53, 100) // lavu 56.53.100 + av_opt_child_class_iterate_func av_opt_child_class_iterate; + #else + av_opt_child_class_next_func av_opt_child_class_next; + #endif + av_opt_next_func av_opt_next; +-av_opt_set_bin_func av_opt_set_bin; ++av_opt_set_array_func av_opt_set_array; + av_pix_fmt_desc_get_func av_pix_fmt_desc_get; + av_pix_fmt_desc_next_func av_pix_fmt_desc_next; + av_sample_fmt_is_planar_func av_sample_fmt_is_planar; +@@ -65,6 +64,7 @@ avcodec_find_encoder_by_name_func avcodec_find_encoder + avcodec_free_context_func avcodec_free_context; + avcodec_get_class_func avcodec_get_class; + avcodec_get_hw_config_func avcodec_get_hw_config; ++avcodec_get_supported_config_func avcodec_get_supported_config; + avcodec_open2_func avcodec_open2; + avcodec_parameters_from_context_func avcodec_parameters_from_context; + avcodec_receive_frame_func avcodec_receive_frame; +@@ -168,14 +168,13 @@ static bool LoadAVUtil() { + LOAD_SYMBOL(avutil, av_hwframe_ctx_init); + LOAD_SYMBOL(avutil, av_hwframe_get_buffer); + LOAD_SYMBOL(avutil, av_hwframe_transfer_data); +- LOAD_SYMBOL(avutil, av_int_list_length_for_size); + #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 53, 100) // lavu 56.53.100 + LOAD_SYMBOL(avutil, av_opt_child_class_iterate); + #else + LOAD_SYMBOL(avutil, av_opt_child_class_next); + #endif + LOAD_SYMBOL(avutil, av_opt_next); +- LOAD_SYMBOL(avutil, av_opt_set_bin); ++ LOAD_SYMBOL(avutil, av_opt_set_array); + LOAD_SYMBOL(avutil, av_pix_fmt_desc_get); + LOAD_SYMBOL(avutil, av_pix_fmt_desc_next); + LOAD_SYMBOL(avutil, av_sample_fmt_is_planar); +@@ -232,6 +231,7 @@ static bool LoadAVCodec() { + LOAD_SYMBOL(avcodec, avcodec_free_context); + LOAD_SYMBOL(avcodec, avcodec_get_class); + LOAD_SYMBOL(avcodec, avcodec_get_hw_config); ++ LOAD_SYMBOL(avcodec, avcodec_get_supported_config); + LOAD_SYMBOL(avcodec, avcodec_open2); + LOAD_SYMBOL(avcodec, avcodec_parameters_from_context); + LOAD_SYMBOL(avcodec, avcodec_receive_frame); Index: patches/patch-src_common_dynamic_library_ffmpeg_h =================================================================== RCS file: patches/patch-src_common_dynamic_library_ffmpeg_h diff -N patches/patch-src_common_dynamic_library_ffmpeg_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-src_common_dynamic_library_ffmpeg_h 13 Aug 2026 00:14:44 -0000 @@ -0,0 +1,55 @@ +ffmpeg-9.0 + +Index: src/common/dynamic_library/ffmpeg.h +--- src/common/dynamic_library/ffmpeg.h.orig ++++ src/common/dynamic_library/ffmpeg.h +@@ -43,14 +43,14 @@ typedef AVBufferRef* (*av_hwframe_ctx_alloc_func)(AVBu + typedef int (*av_hwframe_ctx_init_func)(AVBufferRef*); + typedef int (*av_hwframe_get_buffer_func)(AVBufferRef*, AVFrame*, int); + typedef int (*av_hwframe_transfer_data_func)(AVFrame*, const AVFrame*, int); +-typedef unsigned (*av_int_list_length_for_size_func)(unsigned, const void*, uint64_t); + #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 53, 100) // lavu 56.53.100 + typedef const AVClass* (*av_opt_child_class_iterate_func)(const AVClass*, void**); + #else + typedef const AVClass* (*av_opt_child_class_next_func)(const AVClass*, const AVClass*); + #endif + typedef const AVOption* (*av_opt_next_func)(const void*, const AVOption*); +-typedef int (*av_opt_set_bin_func)(void*, const char*, const uint8_t*, int, int); ++typedef int (*av_opt_set_array_func)(void*, const char*, int, unsigned, unsigned, AVOptionType, ++ const void*); + typedef const AVPixFmtDescriptor* (*av_pix_fmt_desc_get_func)(AVPixelFormat); + typedef const AVPixFmtDescriptor* (*av_pix_fmt_desc_next_func)(const AVPixFmtDescriptor*); + typedef int (*av_sample_fmt_is_planar_func)(AVSampleFormat); +@@ -81,14 +81,13 @@ extern av_hwframe_ctx_alloc_func av_hwframe_ctx_alloc; + extern av_hwframe_ctx_init_func av_hwframe_ctx_init; + extern av_hwframe_get_buffer_func av_hwframe_get_buffer; + extern av_hwframe_transfer_data_func av_hwframe_transfer_data; +-extern av_int_list_length_for_size_func av_int_list_length_for_size; + #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 53, 100) // lavu 56.53.100 + extern av_opt_child_class_iterate_func av_opt_child_class_iterate; + #else + extern av_opt_child_class_next_func av_opt_child_class_next; + #endif + extern av_opt_next_func av_opt_next; +-extern av_opt_set_bin_func av_opt_set_bin; ++extern av_opt_set_array_func av_opt_set_array; + extern av_pix_fmt_desc_get_func av_pix_fmt_desc_get; + extern av_pix_fmt_desc_next_func av_pix_fmt_desc_next; + extern av_sample_fmt_is_planar_func av_sample_fmt_is_planar; +@@ -114,6 +113,8 @@ typedef const AVCodec* (*avcodec_find_encoder_by_name_ + typedef void (*avcodec_free_context_func)(AVCodecContext**); + typedef const AVClass* (*avcodec_get_class_func)(); + typedef const AVCodecHWConfig* (*avcodec_get_hw_config_func)(const AVCodec*, int); ++typedef int (*avcodec_get_supported_config_func)(const AVCodecContext*, const AVCodec*, ++ enum AVCodecConfig, unsigned, const void**, int*); + typedef int (*avcodec_open2_func)(AVCodecContext*, const AVCodec*, AVDictionary**); + typedef int (*avcodec_parameters_from_context_func)(AVCodecParameters* par, const AVCodecContext*); + typedef int (*avcodec_receive_frame_func)(AVCodecContext*, AVFrame*); +@@ -138,6 +139,7 @@ extern avcodec_find_encoder_by_name_func avcodec_find_ + extern avcodec_free_context_func avcodec_free_context; + extern avcodec_get_class_func avcodec_get_class; + extern avcodec_get_hw_config_func avcodec_get_hw_config; ++extern avcodec_get_supported_config_func avcodec_get_supported_config; + extern avcodec_open2_func avcodec_open2; + extern avcodec_parameters_from_context_func avcodec_parameters_from_context; + extern avcodec_receive_frame_func avcodec_receive_frame; Index: patches/patch-src_core_dumping_ffmpeg_backend_cpp =================================================================== RCS file: /home/cvs/ports/emulators/citra/patches/patch-src_core_dumping_ffmpeg_backend_cpp,v diff -u -p -r1.1 patch-src_core_dumping_ffmpeg_backend_cpp --- patches/patch-src_core_dumping_ffmpeg_backend_cpp 21 Oct 2025 12:50:42 -0000 1.1 +++ patches/patch-src_core_dumping_ffmpeg_backend_cpp 13 Aug 2026 00:14:44 -0000 @@ -1,7 +1,187 @@ +ffmpeg-9.0 + Index: src/core/dumping/ffmpeg_backend.cpp --- src/core/dumping/ffmpeg_backend.cpp.orig +++ src/core/dumping/ffmpeg_backend.cpp -@@ -956,7 +956,11 @@ std::string FormatDefaultValue(const AVOption* option, +@@ -106,9 +106,10 @@ FFmpegVideoStream::~FFmpegVideoStream() { + + // This is modified from libavcodec/decode.c + // The original version was broken +-static AVPixelFormat GetPixelFormat(AVCodecContext* avctx, const AVPixelFormat* fmt) { ++static AVPixelFormat GetPixelFormat(AVCodecContext* avctx, const AVPixelFormat* fmt, ++ int num_formats) { + // Choose a software pixel format if any, prefering those in the front of the list +- for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) { ++ for (int i = 0; i < num_formats; i++) { + const AVPixFmtDescriptor* desc = FFmpeg::av_pix_fmt_desc_get(fmt[i]); + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + return fmt[i]; +@@ -118,7 +119,7 @@ static AVPixelFormat GetPixelFormat(AVCodecContext* av + // Finally, traverse the list in order and choose the first entry + // with no external dependencies (if there is no hardware configuration + // information available then this just picks the first entry). +- for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) { ++ for (int i = 0; i < num_formats; i++) { + const AVCodecHWConfig* config; + for (int j = 0;; j++) { + config = FFmpeg::avcodec_get_hw_config(avctx->codec, j); +@@ -142,6 +143,9 @@ static AVPixelFormat GetPixelFormat(AVCodecContext* av + } + + bool FFmpegVideoStream::Init(FFmpegMuxer& muxer, const Layout::FramebufferLayout& layout_) { ++ const AVPixelFormat* pixel_formats; ++ int num_pixel_formats, ret; ++ + InitializeFFmpegLibraries(); + + if (!FFmpegStream::Init(muxer)) { +@@ -160,6 +164,14 @@ bool FFmpegVideoStream::Init(FFmpegMuxer& muxer, const + return false; + } + ++ ret = FFmpeg::avcodec_get_supported_config(codec_context.get(), nullptr, ++ AV_CODEC_CONFIG_PIX_FORMAT, 0, ++ (const void**)&pixel_formats, &num_pixel_formats); ++ if (ret < 0) { ++ LOG_ERROR(Render, "Could not query video encoder pixel formats"); ++ return false; ++ } ++ + // Configure video codec context + codec_context->codec_type = AVMEDIA_TYPE_VIDEO; + codec_context->bit_rate = Settings::values.video_bitrate; +@@ -175,8 +187,8 @@ bool FFmpegVideoStream::Init(FFmpegMuxer& muxer, const + auto pixel_format_opt = FFmpeg::av_dict_get(options, "pixel_format", nullptr, 0); + if (pixel_format_opt) { + sw_pixel_format = FFmpeg::av_get_pix_fmt(pixel_format_opt->value); +- } else if (codec->pix_fmts) { +- sw_pixel_format = GetPixelFormat(codec_context.get(), codec->pix_fmts); ++ } else if (pixel_formats) { ++ sw_pixel_format = GetPixelFormat(codec_context.get(), pixel_formats, num_pixel_formats); + } else { + sw_pixel_format = AV_PIX_FMT_YUV420P; + } +@@ -285,11 +297,22 @@ void FFmpegVideoStream::ProcessFrame(VideoFrame& frame + } + + bool FFmpegVideoStream::InitHWContext(const AVCodec* codec) { +- for (std::size_t i = 0; codec->pix_fmts[i] != AV_PIX_FMT_NONE; ++i) { ++ const AVPixelFormat* pixel_formats; ++ int num_pixel_formats, ret; ++ ++ ret = FFmpeg::avcodec_get_supported_config(codec_context.get(), nullptr, ++ AV_CODEC_CONFIG_PIX_FORMAT, 0, ++ (const void**)&pixel_formats, &num_pixel_formats); ++ if (ret < 0 || !pixel_formats) { ++ LOG_ERROR(Render, "Failed to query HW pixel formats"); ++ return false; ++ } ++ ++ for (int i = 0; i < num_pixel_formats; ++i) { + const AVCodecHWConfig* config; + for (int j = 0;; ++j) { + config = FFmpeg::avcodec_get_hw_config(codec, j); +- if (!config || config->pix_fmt == codec->pix_fmts[i]) { ++ if (!config || config->pix_fmt == pixel_formats[i]) { + break; + } + } +@@ -303,7 +326,7 @@ bool FFmpegVideoStream::InitHWContext(const AVCodec* c + continue; + } + +- codec_context->pix_fmt = codec->pix_fmts[i]; ++ codec_context->pix_fmt = pixel_formats[i]; + + // Create HW device context + AVBufferRef* hw_device_context; +@@ -351,7 +374,7 @@ bool FFmpegVideoStream::InitHWContext(const AVCodec* c + + AVHWFramesContext* hw_frames_context = + reinterpret_cast(hw_frames_context_ref->data); +- hw_frames_context->format = codec->pix_fmts[i]; ++ hw_frames_context->format = pixel_formats[i]; + hw_frames_context->sw_format = sw_pixel_format; + hw_frames_context->width = codec_context->width; + hw_frames_context->height = codec_context->height; +@@ -399,13 +422,9 @@ bool FFmpegVideoStream::InitFilters() { + return false; + } + +- // Point av_opt_set_int_list to correct functions. +-#define av_int_list_length_for_size FFmpeg::av_int_list_length_for_size +-#define av_opt_set_bin FFmpeg::av_opt_set_bin +- +- const AVPixelFormat pix_fmts[] = {sw_pixel_format, AV_PIX_FMT_NONE}; +- if (av_opt_set_int_list(sink_context, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, +- AV_OPT_SEARCH_CHILDREN) < 0) { ++ const AVPixelFormat pixel_formats[] = {sw_pixel_format}; ++ if (FFmpeg::av_opt_set_array(sink_context, "pixel_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, ++ AV_OPT_TYPE_PIXEL_FMT, pixel_formats) < 0) { + LOG_ERROR(Render, "Could not set output pixel format"); + return false; + } +@@ -448,6 +467,10 @@ FFmpegAudioStream::~FFmpegAudioStream() { + } + + bool FFmpegAudioStream::Init(FFmpegMuxer& muxer) { ++ const AVSampleFormat* sample_formats; ++ const int* supported_sample_rates; ++ int num_sample_formats, num_supported_sample_rates, ret; ++ + InitializeFFmpegLibraries(); + + if (!FFmpegStream::Init(muxer)) { +@@ -465,25 +488,40 @@ bool FFmpegAudioStream::Init(FFmpegMuxer& muxer) { + return false; + } + ++ ret = FFmpeg::avcodec_get_supported_config(codec_context.get(), nullptr, ++ AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, ++ (const void**)&sample_formats, &num_sample_formats); ++ if (ret < 0) { ++ LOG_ERROR(Render, "Could not query audio encoder sample formats"); ++ return false; ++ } ++ ++ ret = FFmpeg::avcodec_get_supported_config(codec_context.get(), nullptr, ++ AV_CODEC_CONFIG_SAMPLE_RATE, 0, ++ (const void**)&supported_sample_rates, ++ &num_supported_sample_rates); ++ if (ret < 0) { ++ LOG_ERROR(Render, "Could not query audio encoder sample rates"); ++ return false; ++ } ++ + // Configure audio codec context + codec_context->codec_type = AVMEDIA_TYPE_AUDIO; + codec_context->bit_rate = Settings::values.audio_bitrate; +- if (codec->sample_fmts) { +- codec_context->sample_fmt = codec->sample_fmts[0]; ++ if (sample_formats && num_sample_formats > 0) { ++ codec_context->sample_fmt = sample_formats[0]; + } else { + codec_context->sample_fmt = AV_SAMPLE_FMT_S16P; + } + +- if (codec->supported_samplerates) { +- codec_context->sample_rate = codec->supported_samplerates[0]; ++ if (supported_sample_rates && num_supported_sample_rates > 0) { ++ codec_context->sample_rate = supported_sample_rates[0]; + // Prefer native sample rate if supported +- const int* ptr = codec->supported_samplerates; +- while ((*ptr)) { +- if ((*ptr) == AudioCore::native_sample_rate) { ++ for (int i = 0; i < num_supported_sample_rates; i++) { ++ if (supported_sample_rates[i] == AudioCore::native_sample_rate) { + codec_context->sample_rate = AudioCore::native_sample_rate; + break; + } +- ptr++; + } + } else { + codec_context->sample_rate = AudioCore::native_sample_rate; +@@ -956,7 +994,11 @@ std::string FormatDefaultValue(const AVOption* option, case AV_OPT_TYPE_VIDEO_RATE: { return ToStdString(option->default_val.str); } -- wbr, Kirill