From 516d6f9efc61bf38a1c79bf036954bdcb62a52f6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 12 Oct 2023 14:08:46 -0700 Subject: [PATCH] testffmpeg: added support for YUVA formats using swscale Fixes https://github.com/libsdl-org/SDL/issues/8377 --- test/CMakeLists.txt | 1 + test/testffmpeg.c | 69 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 942400ff7..4ce431e73 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -185,6 +185,7 @@ if(HAVE_LIBUDEV_H) add_definitions(-DHAVE_LIBUDEV_H) endif() +set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) include("${SDL3_SOURCE_DIR}/cmake/FindFFmpeg.cmake") if(FFmpeg_FOUND) cmake_push_check_state() diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 80a1dd6d9..962e1c32a 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef HAVE_EGL #include @@ -81,6 +82,11 @@ static ID3D11Device *d3d11_device; static ID3D11DeviceContext *d3d11_context; static const GUID SDL_IID_ID3D11Resource = { 0xdc8e63f3, 0xd12b, 0x4952, { 0xb4, 0x7b, 0x5e, 0x45, 0x02, 0x6a, 0x86, 0x2d } }; #endif +struct SwsContextContainer +{ + struct SwsContext *context; +}; +static const char *SWS_CONTEXT_CONTAINER_PROPERTY = "SWS_CONTEXT_CONTAINER"; static int done; static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver) @@ -278,6 +284,13 @@ static enum AVPixelFormat GetSupportedPixelFormat(AVCodecContext *s, const enum const enum AVPixelFormat *p; for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); + + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + /* We support all memory formats using swscale */ + break; + } + if (SupportedPixelFormat(*p)) { /* We support this format */ break; @@ -341,7 +354,7 @@ static AVCodecContext *OpenVideoStream(AVFormatContext *ic, int stream, const AV } #ifdef __WIN32__ - if (type == AV_HWDEVICE_TYPE_D3D11VA) { + if (d3d11_device && type == AV_HWDEVICE_TYPE_D3D11VA) { AVD3D11VADeviceContext *device_context; context->hw_device_ctx = av_hwdevice_ctx_alloc(type); @@ -400,32 +413,69 @@ static void SetYUVConversionMode(AVFrame *frame) SDL_SetYUVConversionMode(mode); /* FIXME: no support for linear transfer */ } +static void SDLCALL FreeSwsContextContainer(void *userdata, void *value) +{ + struct SwsContextContainer *sws_container = (struct SwsContextContainer *)value; + if (sws_container->context) { + sws_freeContext(sws_container->context); + } + SDL_free(sws_container); +} + static SDL_bool GetTextureForMemoryFrame(AVFrame *frame, SDL_Texture **texture) { int texture_width = 0, texture_height = 0; Uint32 texture_format = SDL_PIXELFORMAT_UNKNOWN; Uint32 frame_format = GetTextureFormat(frame->format); - if (frame_format == SDL_PIXELFORMAT_UNKNOWN) { - SDL_SetError("Unknown pixel format: %s", av_get_pix_fmt_name(frame->format)); - return SDL_FALSE; - } - if (*texture) { SDL_QueryTexture(*texture, &texture_format, NULL, &texture_width, &texture_height); } - if (!*texture || frame_format != texture_format || frame->width != texture_width || frame->height != texture_height) { + if (!*texture || texture_width != frame->width || texture_height != frame->height || + (frame_format != SDL_PIXELFORMAT_UNKNOWN && texture_format != frame_format) || + (frame_format == SDL_PIXELFORMAT_UNKNOWN && texture_format != SDL_PIXELFORMAT_ARGB8888)) { if (*texture) { SDL_DestroyTexture(*texture); } - *texture = SDL_CreateTexture(renderer, frame_format, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); + if (frame_format == SDL_PIXELFORMAT_UNKNOWN) { + *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); + } else { + *texture = SDL_CreateTexture(renderer, frame_format, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); + } if (!*texture) { return SDL_FALSE; } + SDL_SetTextureBlendMode(*texture, SDL_BLENDMODE_BLEND); } switch (frame_format) { + case SDL_PIXELFORMAT_UNKNOWN: + { + SDL_PropertiesID props = SDL_GetTextureProperties(*texture); + struct SwsContextContainer *sws_container = (struct SwsContextContainer *)SDL_GetProperty(props, SWS_CONTEXT_CONTAINER_PROPERTY); + if (!sws_container) { + sws_container = (struct SwsContextContainer *)SDL_calloc(1, sizeof(*sws_container)); + if (!sws_container) { + SDL_OutOfMemory(); + return SDL_FALSE; + } + SDL_SetProperty(props, SWS_CONTEXT_CONTAINER_PROPERTY, sws_container, FreeSwsContextContainer, NULL); + } + sws_container->context = sws_getCachedContext(sws_container->context, frame->width, frame->height, frame->format, frame->width, frame->height, AV_PIX_FMT_BGRA, SWS_POINT, NULL, NULL, NULL); + if (sws_container->context) { + uint8_t *pixels[4]; + int pitch[4]; + if (SDL_LockTexture(*texture, NULL, (void **)&pixels[0], &pitch[0]) == 0) { + sws_scale(sws_container->context, (const uint8_t * const *)frame->data, frame->linesize, 0, frame->height, pixels, pitch); + SDL_UnlockTexture(*texture); + } + } else { + SDL_SetError("Can't initialize the conversion context"); + return SDL_FALSE; + } + break; + } case SDL_PIXELFORMAT_IYUV: if (frame->linesize[0] > 0 && frame->linesize[1] > 0 && frame->linesize[2] > 0) { SDL_UpdateYUVTexture(*texture, NULL, frame->data[0], frame->linesize[0], @@ -652,6 +702,9 @@ static void HandleVideoFrame(AVFrame *frame, double pts) now = (double)(SDL_GetTicks() - video_start) / 1000.0; } + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + DisplayVideoFrame(frame); /* Render any bouncing balls */