From a290bec8f15770cc421c180af76cc86754bb4a2a Mon Sep 17 00:00:00 2001 From: Green Sky Date: Wed, 2 Oct 2024 23:35:33 +0200 Subject: [PATCH] add experimental NV12 to IYUV conversion routine --- .../sdl/video_push_converter.cpp | 48 +++++++++++++++++++ .../sdl/video_push_converter.hpp | 3 ++ 2 files changed, 51 insertions(+) diff --git a/src/frame_streams/sdl/video_push_converter.cpp b/src/frame_streams/sdl/video_push_converter.cpp index df5d71ac..04ecc567 100644 --- a/src/frame_streams/sdl/video_push_converter.cpp +++ b/src/frame_streams/sdl/video_push_converter.cpp @@ -1,5 +1,7 @@ #include "./video_push_converter.hpp" +#include + SDL_Surface* convertYUY2_IYUV(SDL_Surface* surf) { if (surf->format != SDL_PIXELFORMAT_YUY2) { return nullptr; @@ -56,3 +58,49 @@ SDL_Surface* convertYUY2_IYUV(SDL_Surface* surf) { return conv_surf; } +SDL_Surface* convertNV12_IYUV(SDL_Surface* surf) { + if (surf->format != SDL_PIXELFORMAT_NV12) { + return nullptr; + } + if ((surf->w % 2) != 0) { + SDL_SetError("NV12->IYUV does not support odd widths"); + // hmmm, we dont handle odd widths + return nullptr; + } + + SDL_LockSurface(surf); + + SDL_Surface* conv_surf = SDL_CreateSurface(surf->w, surf->h, SDL_PIXELFORMAT_IYUV); + SDL_LockSurface(conv_surf); + + // NV12 is planar Y and interleaved UV in 4:2:0 + // Y is simple, we just copy it over + // U V are ... packed? + + uint8_t* y_plane = static_cast(conv_surf->pixels); + uint8_t* u_plane = static_cast(conv_surf->pixels) + conv_surf->w*conv_surf->h; + uint8_t* v_plane = static_cast(conv_surf->pixels) + conv_surf->w*conv_surf->h + (conv_surf->w/2)*(conv_surf->h/2); + + const uint8_t* nv12_y_plane = static_cast(surf->pixels); + const uint8_t* nv12_uv_data = static_cast(surf->pixels) + surf->w*surf->h; + + // y can be copied as is + std::memcpy( + y_plane, + nv12_y_plane, + surf->w*surf->h + ); + + // we operate at half res + for (int y = 0; y < surf->h/2; y++) { + for (int x = 0; x < surf->w/2; x++) { + u_plane[y*(surf->w/2)+x] = nv12_uv_data[y*(surf->w/2)*2+x*2]; + v_plane[y*(surf->w/2)+x] = nv12_uv_data[y*(surf->w/2)*2+x*2+1]; + } + } + + SDL_UnlockSurface(conv_surf); + SDL_UnlockSurface(surf); + return conv_surf; +} + diff --git a/src/frame_streams/sdl/video_push_converter.hpp b/src/frame_streams/sdl/video_push_converter.hpp index 4a26be95..c0d51671 100644 --- a/src/frame_streams/sdl/video_push_converter.hpp +++ b/src/frame_streams/sdl/video_push_converter.hpp @@ -21,6 +21,7 @@ static bool isFormatYUV(SDL_PixelFormat f) { } SDL_Surface* convertYUY2_IYUV(SDL_Surface* surf); +SDL_Surface* convertNV12_IYUV(SDL_Surface* surf); template struct PushConversionVideoStream : public RealStream { @@ -42,6 +43,8 @@ struct PushConversionVideoStream : public RealStream { //auto end = Message::getTimeMS(); // 3ms //std::cerr << "DTC: timing " << SDL_GetPixelFormatName(converted_surf->format) << "->SDL_PIXELFORMAT_IYUV: " << end-start << "ms\n"; + } else if (surf->format == SDL_PIXELFORMAT_NV12 && _forced_format == SDL_PIXELFORMAT_IYUV) { + surf = convertNV12_IYUV(surf); } else if (isFormatYUV(surf->format)) { // TODO: fix sdl rgb->yuv conversion resulting in too dark (colorspace) issues // https://github.com/libsdl-org/SDL/issues/10877