ui: improve hardware decoder detection

This commit is contained in:
itsmattkc
2026-01-14 19:08:41 -08:00
parent 95b705a405
commit 9aecca7943
+93 -51
View File
@@ -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;