From a622b6aa3f727c79e6d8753db00d9d7dd95741b9 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Tue, 17 Sep 2024 11:21:11 +0200 Subject: [PATCH] custom YUY2 -> IYUV conversion code improves from 1+60ms to 3ms --- src/debug_tox_call.cpp | 67 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/debug_tox_call.cpp b/src/debug_tox_call.cpp index f9a2610..17b3379 100644 --- a/src/debug_tox_call.cpp +++ b/src/debug_tox_call.cpp @@ -45,6 +45,61 @@ static bool isFormatPlanar(SDL_PixelFormat f) { ; } +static SDL_Surface* convertYUY2_IYUV(SDL_Surface* surf) { + if (surf->format != SDL_PIXELFORMAT_YUY2) { + return nullptr; + } + if ((surf->w % 2) != 0) { + // 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); + + // YUY2 is 4:2:2 packed + // Y is simple, we just copy it over + // U V are double the resolution (vertically), so we avg both + // Packed mode: Y0+U0+Y1+V0 (1 plane) + + 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* yuy2_data = static_cast(surf->pixels); + + for (int y = 0; y < surf->h; y++) { + for (int x = 0; x < surf->w; x += 2) { + // every pixel uses 2 bytes + const uint8_t* yuy2_curser = yuy2_data + y*surf->w*2 + x*2; + uint8_t src_y0 = yuy2_curser[0]; + uint8_t src_u = yuy2_curser[1]; + uint8_t src_y1 = yuy2_curser[2]; + uint8_t src_v = yuy2_curser[3]; + + y_plane[y*conv_surf->w + x] = src_y0; + y_plane[y*conv_surf->w + x+1] = src_y1; + + size_t uv_index = (y/2) * (conv_surf->w/2) + x/2; + if (y % 2 == 0) { + // first write + u_plane[uv_index] = src_u; + v_plane[uv_index] = src_v; + } else { + // second write, mix with existing value + u_plane[uv_index] = (int(u_plane[uv_index]) + int(src_u)) / 2; + v_plane[uv_index] = (int(v_plane[uv_index]) + int(src_v)) / 2; + } + } + } + + SDL_UnlockSurface(conv_surf); + SDL_UnlockSurface(surf); + return conv_surf; +} + struct PushConversionQueuedVideoStream : public QueuedFrameStream2 { SDL_PixelFormat _forced_format {SDL_PIXELFORMAT_IYUV}; @@ -55,19 +110,29 @@ struct PushConversionQueuedVideoStream : public QueuedFrameStream2format != SDL_PIXELFORMAT_IYUV) { //std::cerr << "DTC: need to convert from " << SDL_GetPixelFormatName(converted_surf->format) << " to SDL_PIXELFORMAT_IYUV\n"; - if (isFormatPlanar(converted_surf->format)) { + if (converted_surf->format == SDL_PIXELFORMAT_YUY2) { + // optimized custom impl + + //auto start = Message::getTimeMS(); + converted_surf = convertYUY2_IYUV(converted_surf); + //auto end = Message::getTimeMS(); + // 3ms + //std::cerr << "DTC: timing " << SDL_GetPixelFormatName(converted_surf->format) << "->SDL_PIXELFORMAT_IYUV: " << end-start << "ms\n"; + } else if (isFormatPlanar(converted_surf->format)) { // meh, need to convert to rgb as a stopgap //auto start = Message::getTimeMS(); //SDL_Surface* tmp_conv_surf = SDL_ConvertSurfaceAndColorspace(converted_surf, SDL_PIXELFORMAT_RGBA32, nullptr, SDL_COLORSPACE_RGB_DEFAULT, 0); SDL_Surface* tmp_conv_surf = SDL_ConvertSurfaceAndColorspace(converted_surf, SDL_PIXELFORMAT_RGB24, nullptr, SDL_COLORSPACE_RGB_DEFAULT, 0); //auto end = Message::getTimeMS(); + // 1ms //std::cerr << "DTC: timing " << SDL_GetPixelFormatName(converted_surf->format) << "->SDL_PIXELFORMAT_RGB24: " << end-start << "ms\n"; // TODO: fix sdl rgb->yuv conversion resulting in too dark (colorspace) issues //start = Message::getTimeMS(); converted_surf = SDL_ConvertSurfaceAndColorspace(tmp_conv_surf, SDL_PIXELFORMAT_IYUV, nullptr, SDL_COLORSPACE_YUV_DEFAULT, 0); //end = Message::getTimeMS(); + // 60ms //std::cerr << "DTC: timing SDL_PIXELFORMAT_RGB24->SDL_PIXELFORMAT_IYUV: " << end-start << "ms\n"; SDL_DestroySurface(tmp_conv_surf);