From 9aecca7943906a29737412fd78fa170e79d05641 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:08:41 -0800 Subject: [PATCH] ui: improve hardware decoder detection --- gui/menu/menu_game.c | 144 ++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 51 deletions(-) diff --git a/gui/menu/menu_game.c b/gui/menu/menu_game.c index 2180029..9039bec 100644 --- a/gui/menu/menu_game.c +++ b/gui/menu/menu_game.c @@ -208,42 +208,48 @@ typedef struct { AVBufferRef *hw_device_ctx; } vpi_decode_state_t; -int vpi_decode_init(vpi_decode_state_t *s) +typedef struct { + const char *name; + const AVCodec *codec; + enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt); +} hwdec_t; + +enum HwDecoderType { + HWDEC_TYPE_VAAPI, + HWDEC_TYPE_DRM, + HWDEC_TYPE_SOFTWARE, + HWDEC_TYPE_COUNT +}; + +void vpi_decode_exit(vpi_decode_state_t *s) +{ + if (s->frame) + av_frame_free(&s->frame); + + if (s->pkt) + av_packet_free(&s->pkt); + + if (vpi_present_frame) + av_frame_free(&vpi_present_frame); + + if (s->codec_ctx) + avcodec_free_context(&s->codec_ctx); + + if (s->hw_device_ctx) + av_buffer_unref(&s->hw_device_ctx); +} + +int open_decoder(vpi_decode_state_t *s, hwdec_t *dec) { int ffmpeg_err; - // Initialize FFmpeg - const AVCodec *codec = 0; - enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt); - - // Test for VAAPI - if (vpi_egl_available && ((ffmpeg_err = av_hwdevice_ctx_create(&s->hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, 0, 0, 0)) >= 0)) { - vpilog("Decoding: VAAPI\n"); - codec = avcodec_find_decoder(AV_CODEC_ID_H264); - get_format = vaapi_get_format; - } else if ((ffmpeg_err = av_hwdevice_ctx_create(&s->hw_device_ctx, AV_HWDEVICE_TYPE_DRM, "/dev/dri/card0", 0, 0)) >= 0) { - vpilog("Decoding: DRM\n"); - codec = avcodec_find_decoder_by_name("h264_v4l2m2m"); - get_format = drm_get_format; - } - - // If we couldn't find a decoder (regardless of if we found hwaccel), fallback to software - if (!codec) { - if (s->hw_device_ctx) { - av_buffer_unref(&s->hw_device_ctx); - } - vpilog("Decoding: Software\n"); - codec = avcodec_find_decoder(AV_CODEC_ID_H264); - get_format = 0; - } - // If we couldn't find a hardware or software decoder, fail - if (!codec) { + if (!dec->codec) { vpilog("No decoder was available\n"); return VANILLA_ERR_GENERIC; } - s->codec_ctx = avcodec_alloc_context3(codec); + s->codec_ctx = avcodec_alloc_context3(dec->codec); if (!s->codec_ctx) { vpilog("Failed to allocate codec context\n"); return VANILLA_ERR_GENERIC; @@ -253,8 +259,8 @@ int vpi_decode_init(vpi_decode_state_t *s) s->codec_ctx->hw_device_ctx = av_buffer_ref(s->hw_device_ctx); } - if (get_format) { - s->codec_ctx->get_format = get_format; + if (dec->get_format) { + s->codec_ctx->get_format = dec->get_format; } const int BUILD_AVCC = 0; @@ -275,12 +281,68 @@ int vpi_decode_init(vpi_decode_state_t *s) ); } - ffmpeg_err = avcodec_open2(s->codec_ctx, codec, NULL); + ffmpeg_err = avcodec_open2(s->codec_ctx, dec->codec, NULL); if (ffmpeg_err < 0) { vpilog("Failed to open decoder: %i\n", ffmpeg_err); return VANILLA_ERR_GENERIC; } + vpilog("Decoding: %s\n", dec->name); + + return VANILLA_SUCCESS; +} + +int vpi_decode_init(vpi_decode_state_t *s) +{ + int ffmpeg_err; + + // Initialize decoding context, preferring hardware decoding when available + hwdec_t decoders[HWDEC_TYPE_COUNT]; + + decoders[HWDEC_TYPE_VAAPI].name = "VAAPI"; + decoders[HWDEC_TYPE_VAAPI].codec = avcodec_find_decoder(AV_CODEC_ID_H264); + decoders[HWDEC_TYPE_VAAPI].get_format = vaapi_get_format; + + decoders[HWDEC_TYPE_DRM].name = "DRM"; + decoders[HWDEC_TYPE_DRM].codec = avcodec_find_decoder_by_name("h264_v4l2m2m"); + decoders[HWDEC_TYPE_DRM].get_format = drm_get_format; + + decoders[HWDEC_TYPE_SOFTWARE].name = "Software"; + decoders[HWDEC_TYPE_SOFTWARE].codec = avcodec_find_decoder(AV_CODEC_ID_H264); + decoders[HWDEC_TYPE_SOFTWARE].get_format = 0; + + // Discover the most ideal hardware decoder + int r = VANILLA_ERR_GENERIC; + + // See if we can create a VAAPI context (most Linux systems) + if (r != VANILLA_SUCCESS) { + vpi_decode_exit(s); + if (vpi_egl_available + && ((ffmpeg_err = av_hwdevice_ctx_create(&s->hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, 0, 0, 0)) >= 0)) { + r = open_decoder(s, &decoders[0]); + } + } + + // See if we can create a DRM context (Raspberry Pi, et al.) + if (r != VANILLA_SUCCESS) { + vpi_decode_exit(s); + if ((ffmpeg_err = av_hwdevice_ctx_create(&s->hw_device_ctx, AV_HWDEVICE_TYPE_DRM, "/dev/dri/card0", 0, 0)) >= 0) { + r = open_decoder(s, &decoders[1]); + } + } + + // Finally, fallback to a software decoder. + if (r != VANILLA_SUCCESS) { + vpi_decode_exit(s); + r = open_decoder(s, &decoders[2]); + } + + // We don't expect the software decoder to fail, but just in case. + if (r != VANILLA_SUCCESS) { + vpi_decode_exit(s); + return r; + } + AVFrame *decoding_frame = av_frame_alloc(); if (!decoding_frame) { vpilog("Failed to allocate AVFrame\n"); @@ -301,29 +363,9 @@ int vpi_decode_init(vpi_decode_state_t *s) s->frame = av_frame_alloc(); - vpilog("initialized state!\n"); - return VANILLA_SUCCESS; } -void vpi_decode_exit(vpi_decode_state_t *s) -{ - if (s->frame) - av_frame_free(&s->frame); - - if (s->pkt) - av_packet_free(&s->pkt); - - if (vpi_present_frame) - av_frame_free(&vpi_present_frame); - - if (s->codec_ctx) - avcodec_free_context(&s->codec_ctx); - - if (s->hw_device_ctx) - av_buffer_unref(&s->hw_device_ctx); -} - int64_t get_recording_timestamp(AVRational timebase) { struct timeval tv;