gui: add support for v4l2request hardware decoding (orange pi)

This commit is contained in:
itsmattkc committed 2026-07-29 21:33:50 -07:00
1 parent dc28618301
commit 063e3cba67
3 files changed
+83 -2

No files matched your search

+8
View File
@@ -403,6 +403,14 @@ target_link_libraries(vanillagui PRIVATE
${_vanilla_gui_libs}
)
if (LINUX)
option(VANILLA_BUILD_USE_V4L2REQUEST "Enable V4L2 Request API (Orange Pi/Cedrus)" OFF)
if (VANILLA_BUILD_USE_V4L2REQUEST)
target_compile_definitions(vanillagui PRIVATE VANILLA_V4L2REQUEST_AVAILABLE)
target_link_libraries(vanillagui PRIVATE udev drm)
endif()
endif()
if (APPLE)
target_link_libraries(vanillagui PRIVATE "-framework VideoToolbox" "-framework CoreMedia" iconv)
endif()
+61
View File
@@ -18,6 +18,13 @@
#include "ui/ui_anim.h"
#include "ui/ui_util.h"
#ifdef VANILLA_V4L2REQUEST_AVAILABLE
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/videodev2.h>
#endif
static char vpi_toast_string[VPI_TOAST_MAX_LEN];
static struct timeval vpi_toast_expiry;
static int vpi_toast_number = 0;
@@ -223,11 +230,50 @@ typedef struct {
enum HwDecoderType {
HWDEC_TYPE_NVDEC,
HWDEC_TYPE_VAAPI,
HWDEC_TYPE_V4L2REQUEST,
HWDEC_TYPE_DRM,
HWDEC_TYPE_SOFTWARE,
HWDEC_TYPE_COUNT
};
#ifdef VANILLA_V4L2REQUEST_AVAILABLE
static int is_v4l2request_available(void)
{
char path[32];
for (int i = 0; i < 64; i++) {
snprintf(path, sizeof(path), "/dev/video%d", i);
int fd = open(path, O_RDWR | O_NONBLOCK);
if (fd < 0) {
continue;
}
int found = 0;
for (int t = 0; t < 2; t++) {
struct v4l2_fmtdesc desc;
memset(&desc, 0, sizeof(desc));
desc.type = (t == 0) ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
: V4L2_BUF_TYPE_VIDEO_OUTPUT;
for (desc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &desc) == 0; desc.index++) {
if (desc.pixelformat == V4L2_PIX_FMT_H264_SLICE) {
found = 1;
break;
}
}
if (found) {
break;
}
}
close(fd);
if (found) {
return 1;
}
}
return 0;
}
#endif // VANILLA_V4L2REQUEST_AVAILABLE
void vpi_decode_exit(vpi_decode_state_t *s)
{
if (s->frame)
@@ -303,6 +349,8 @@ int vpi_decode_init(vpi_decode_state_t *s)
{
int ffmpeg_err;
// av_log_set_level(AV_LOG_VERBOSE);
// Initialize decoding context, preferring hardware decoding when available
hwdec_t decoders[HWDEC_TYPE_COUNT];
@@ -314,6 +362,10 @@ int vpi_decode_init(vpi_decode_state_t *s)
decoders[HWDEC_TYPE_VAAPI].codec = avcodec_find_decoder(AV_CODEC_ID_H264);
decoders[HWDEC_TYPE_VAAPI].get_format = vaapi_get_format;
decoders[HWDEC_TYPE_V4L2REQUEST].name = "V4L2 Request";
decoders[HWDEC_TYPE_V4L2REQUEST].codec = avcodec_find_decoder(AV_CODEC_ID_H264);
decoders[HWDEC_TYPE_V4L2REQUEST].get_format = drm_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;
@@ -345,6 +397,15 @@ int vpi_decode_init(vpi_decode_state_t *s)
}
}
#ifdef VANILLA_V4L2REQUEST_AVAILABLE
if (r != VANILLA_SUCCESS && is_v4l2request_available()) {
vpi_decode_exit(s);
if ((ffmpeg_err = av_hwdevice_ctx_create(&s->hw_device_ctx, AV_HWDEVICE_TYPE_V4L2REQUEST, NULL, NULL, 0)) >= 0) {
r = open_decoder(s, &decoders[HWDEC_TYPE_V4L2REQUEST]);
}
}
#endif // VANILLA_V4L2REQUEST_AVAILABLE
// See if we can create a DRM context (Raspberry Pi, et al.)
if (r != VANILLA_SUCCESS) {
vpi_decode_exit(s);
+14 -2
View File
@@ -82,6 +82,7 @@ typedef struct {
SDL_Texture *toast_tex;
struct timeval toast_expiry;
AVFrame *frame;
AVFrame *held_frame; // keeps the displayed dmabuf alive until the next frame replaces it
SDL_Texture *pw_tex;
uint8_t audio_buffer[AUDIO_BUFFER_COUNT][AUDIO_BUFFER_SIZE];
@@ -888,6 +889,7 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen)
#endif
sdl_ctx->frame = av_frame_alloc();
sdl_ctx->held_frame = av_frame_alloc();
// Initialize gamepad lookup tables
init_gamepad(ctx);
@@ -903,6 +905,7 @@ void vui_close_sdl(vui_context_t *ctx)
}
av_frame_free(&sdl_ctx->frame);
av_frame_free(&sdl_ctx->held_frame);
#ifdef VANILLA_NX_IMU
vpi_nx_imu_quit();
@@ -1798,7 +1801,14 @@ int get_texture_from_drm_prime_frame(vui_sdl_context_t *sdl_ctx, AVFrame *f)
}
}
EGLAttrib use_fmt = layer->format == DRM_FORMAT_YUV420 ? DRM_FORMAT_R8 : layer->format;
EGLAttrib use_fmt;
if (layer->format == DRM_FORMAT_YUV420) {
use_fmt = DRM_FORMAT_R8;
} else if (layer->format == DRM_FORMAT_NV12) {
use_fmt = (image_index == 0) ? DRM_FORMAT_R8 : DRM_FORMAT_GR88;
} else {
use_fmt = layer->format;
}
int w = f->width;
int h = f->height;
@@ -1966,7 +1976,9 @@ int vui_update_sdl(vui_context_t *vui)
get_texture_from_cpu_frame(sdl_ctx, sdl_ctx->frame);
break;
}
av_frame_unref(sdl_ctx->frame);
av_frame_unref(sdl_ctx->held_frame);
av_frame_move_ref(sdl_ctx->held_frame, sdl_ctx->frame);
if (sdl_ctx->game_tex) {
if (handle_final_blit) {