From 1e49eac4d12e8a9204dccd240fdb90c5daa75a5d Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 8 Jul 2026 20:27:27 -0700 Subject: [PATCH] rm libyuv (#38306) * rm libyuv * cleanup --- SConstruct | 2 +- openpilot/common/SConscript | 1 + openpilot/common/yuv.cc | 132 ++++++++++++++++++ openpilot/common/yuv.h | 42 ++++++ openpilot/system/loggerd/SConscript | 1 - .../system/loggerd/encoder/ffmpeg_encoder.cc | 32 ++--- openpilot/tools/cabana/SConscript | 2 +- openpilot/tools/jotpluggler/SConscript | 2 +- openpilot/tools/jotpluggler/runtime.cc | 34 ++--- openpilot/tools/replay/SConscript | 2 +- openpilot/tools/replay/framereader.cc | 14 +- pyproject.toml | 1 - uv.lock | 12 -- 13 files changed, 218 insertions(+), 59 deletions(-) create mode 100644 openpilot/common/yuv.cc create mode 100644 openpilot/common/yuv.h diff --git a/SConstruct b/SConstruct index 0f7854dcd4..7e102add90 100644 --- a/SConstruct +++ b/SConstruct @@ -40,7 +40,7 @@ assert arch in [ "Darwin", # macOS arm64 (x86 not supported) ] -pkg_names = ['acados', 'bzip2', 'capnproto', 'catch2', 'eigen', 'ffmpeg', 'json11', 'libyuv', 'ncurses', 'zeromq', 'zstd'] +pkg_names = ['acados', 'bzip2', 'capnproto', 'catch2', 'eigen', 'ffmpeg', 'json11', 'ncurses', 'zeromq', 'zstd'] pkgs = [importlib.import_module(name) for name in pkg_names] acados = pkgs[pkg_names.index('acados')] ffmpeg = pkgs[pkg_names.index('ffmpeg')] diff --git a/openpilot/common/SConscript b/openpilot/common/SConscript index c9bd1c72d1..68dcc84171 100644 --- a/openpilot/common/SConscript +++ b/openpilot/common/SConscript @@ -5,6 +5,7 @@ common_libs = [ 'swaglog.cc', 'util.cc', 'ratekeeper.cc', + 'yuv.cc', ] _common = env.Library('common', common_libs, LIBS="json11") diff --git a/openpilot/common/yuv.cc b/openpilot/common/yuv.cc new file mode 100644 index 0000000000..a7a64fb594 --- /dev/null +++ b/openpilot/common/yuv.cc @@ -0,0 +1,132 @@ +#include "common/yuv.h" + +#include +#include + +namespace yuv { + +namespace { + +inline uint8_t clamp_u8(int v) { + return static_cast(std::clamp(v, 0, 255)); +} + +void copy_plane(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, + int width, int height) { + if (src_stride == width && dst_stride == width) { + std::memcpy(dst, src, static_cast(width) * height); + return; + } + for (int y = 0; y < height; ++y) { + std::memcpy(dst + y * dst_stride, src + y * src_stride, width); + } +} + +void scale_plane_point(const uint8_t *src, int src_stride, int src_width, int src_height, + uint8_t *dst, int dst_stride, int dst_width, int dst_height) { + if (src_width == dst_width && src_height == dst_height) { + copy_plane(src, src_stride, dst, dst_stride, dst_width, dst_height); + return; + } + for (int y = 0; y < dst_height; ++y) { + const int sy = y * src_height / dst_height; + const uint8_t *src_row = src + sy * src_stride; + uint8_t *dst_row = dst + y * dst_stride; + for (int x = 0; x < dst_width; ++x) { + dst_row[x] = src_row[x * src_width / dst_width]; + } + } +} + +// BT.601 limited range → RGB (integer form used widely, incl. similar to libyuv). +inline void yuv_to_rgb(int y, int u, int v, uint8_t *r, uint8_t *g, uint8_t *b) { + const int c = (y - 16) * 298; + const int d = u - 128; + const int e = v - 128; + *r = clamp_u8((c + 409 * e + 128) >> 8); + *g = clamp_u8((c - 100 * d - 208 * e + 128) >> 8); + *b = clamp_u8((c + 516 * d + 128) >> 8); +} + +} // namespace + +void nv12_to_i420(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_uv, int src_stride_uv, + uint8_t *dst_y, int dst_stride_y, + uint8_t *dst_u, int dst_stride_u, + uint8_t *dst_v, int dst_stride_v, + int width, int height) { + copy_plane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); + + const int uv_width = width / 2; + const int uv_height = height / 2; + for (int y = 0; y < uv_height; ++y) { + const uint8_t *uv = src_uv + y * src_stride_uv; + uint8_t *u = dst_u + y * dst_stride_u; + uint8_t *v = dst_v + y * dst_stride_v; + for (int x = 0; x < uv_width; ++x) { + u[x] = uv[2 * x]; + v[x] = uv[2 * x + 1]; + } + } +} + +void i420_to_nv12(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_u, int src_stride_u, + const uint8_t *src_v, int src_stride_v, + uint8_t *dst_y, int dst_stride_y, + uint8_t *dst_uv, int dst_stride_uv, + int width, int height) { + copy_plane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); + + const int uv_width = width / 2; + const int uv_height = height / 2; + for (int y = 0; y < uv_height; ++y) { + const uint8_t *u = src_u + y * src_stride_u; + const uint8_t *v = src_v + y * src_stride_v; + uint8_t *uv = dst_uv + y * dst_stride_uv; + for (int x = 0; x < uv_width; ++x) { + uv[2 * x] = u[x]; + uv[2 * x + 1] = v[x]; + } + } +} + +void i420_scale(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_u, int src_stride_u, + const uint8_t *src_v, int src_stride_v, + int src_width, int src_height, + uint8_t *dst_y, int dst_stride_y, + uint8_t *dst_u, int dst_stride_u, + uint8_t *dst_v, int dst_stride_v, + int dst_width, int dst_height) { + scale_plane_point(src_y, src_stride_y, src_width, src_height, + dst_y, dst_stride_y, dst_width, dst_height); + scale_plane_point(src_u, src_stride_u, src_width / 2, src_height / 2, + dst_u, dst_stride_u, dst_width / 2, dst_height / 2); + scale_plane_point(src_v, src_stride_v, src_width / 2, src_height / 2, + dst_v, dst_stride_v, dst_width / 2, dst_height / 2); +} + +void nv12_to_rgba(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_uv, int src_stride_uv, + uint8_t *dst_rgba, int dst_stride_rgba, + int width, int height) { + for (int y = 0; y < height; ++y) { + const uint8_t *y_row = src_y + y * src_stride_y; + const uint8_t *uv_row = src_uv + (y / 2) * src_stride_uv; + uint8_t *dst = dst_rgba + y * dst_stride_rgba; + for (int x = 0; x < width; ++x) { + const int uv_x = (x & ~1); + uint8_t r, g, b; + yuv_to_rgb(y_row[x], uv_row[uv_x], uv_row[uv_x + 1], &r, &g, &b); + dst[4 * x + 0] = r; + dst[4 * x + 1] = g; + dst[4 * x + 2] = b; + dst[4 * x + 3] = 255; + } + } +} + +} // namespace yuv diff --git a/openpilot/common/yuv.h b/openpilot/common/yuv.h new file mode 100644 index 0000000000..7ab096f764 --- /dev/null +++ b/openpilot/common/yuv.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +// NV12: Y plane + interleaved UV. I420: planar Y, U, V. + +namespace yuv { + +// Deinterleave NV12 UV into planar I420. +void nv12_to_i420(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_uv, int src_stride_uv, + uint8_t *dst_y, int dst_stride_y, + uint8_t *dst_u, int dst_stride_u, + uint8_t *dst_v, int dst_stride_v, + int width, int height); + +// Interleave planar I420 UV into NV12. +void i420_to_nv12(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_u, int src_stride_u, + const uint8_t *src_v, int src_stride_v, + uint8_t *dst_y, int dst_stride_y, + uint8_t *dst_uv, int dst_stride_uv, + int width, int height); + +// Point-sample scale I420 (equivalent to libyuv::I420Scale + kFilterNone). +void i420_scale(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_u, int src_stride_u, + const uint8_t *src_v, int src_stride_v, + int src_width, int src_height, + uint8_t *dst_y, int dst_stride_y, + uint8_t *dst_u, int dst_stride_u, + uint8_t *dst_v, int dst_stride_v, + int dst_width, int dst_height); + +// Convert NV12 to packed RGBA (R,G,B,A bytes — suitable for GL_RGBA). +// BT.601 limited-range, matching common libyuv defaults. +void nv12_to_rgba(const uint8_t *src_y, int src_stride_y, + const uint8_t *src_uv, int src_stride_uv, + uint8_t *dst_rgba, int dst_stride_rgba, + int width, int height); + +} diff --git a/openpilot/system/loggerd/SConscript b/openpilot/system/loggerd/SConscript index 0bba1d1771..7f6d4faf0c 100644 --- a/openpilot/system/loggerd/SConscript +++ b/openpilot/system/loggerd/SConscript @@ -8,7 +8,6 @@ if arch == "larch64": src += ['encoder/v4l_encoder.cc'] else: src += ['encoder/ffmpeg_encoder.cc'] - libs += ['yuv'] if arch == "Darwin": frameworks += ['VideoToolbox', 'CoreMedia', 'CoreFoundation', 'CoreVideo'] diff --git a/openpilot/system/loggerd/encoder/ffmpeg_encoder.cc b/openpilot/system/loggerd/encoder/ffmpeg_encoder.cc index 40f3bdee42..451445e5a0 100644 --- a/openpilot/system/loggerd/encoder/ffmpeg_encoder.cc +++ b/openpilot/system/loggerd/encoder/ffmpeg_encoder.cc @@ -9,8 +9,6 @@ #define __STDC_CONSTANT_MACROS -#include "libyuv.h" - extern "C" { #include #include @@ -19,6 +17,7 @@ extern "C" { #include "common/swaglog.h" #include "common/util.h" +#include "common/yuv.h" const int env_debug_encoder = (getenv("DEBUG_ENCODER") != NULL) ? atoi(getenv("DEBUG_ENCODER")) : 0; @@ -87,26 +86,25 @@ int FfmpegEncoder::encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra) { uint8_t *cy = convert_buf.data(); uint8_t *cu = cy + in_width * in_height; uint8_t *cv = cu + (in_width / 2) * (in_height / 2); - libyuv::NV12ToI420(buf->y, buf->stride, - buf->uv, buf->stride, - cy, in_width, - cu, in_width/2, - cv, in_width/2, - in_width, in_height); + yuv::nv12_to_i420(buf->y, buf->stride, + buf->uv, buf->stride, + cy, in_width, + cu, in_width/2, + cv, in_width/2, + in_width, in_height); if (downscale_buf.size() > 0) { uint8_t *out_y = downscale_buf.data(); uint8_t *out_u = out_y + frame->width * frame->height; uint8_t *out_v = out_u + (frame->width / 2) * (frame->height / 2); - libyuv::I420Scale(cy, in_width, - cu, in_width/2, - cv, in_width/2, - in_width, in_height, - out_y, frame->width, - out_u, frame->width/2, - out_v, frame->width/2, - frame->width, frame->height, - libyuv::kFilterNone); + yuv::i420_scale(cy, in_width, + cu, in_width/2, + cv, in_width/2, + in_width, in_height, + out_y, frame->width, + out_u, frame->width/2, + out_v, frame->width/2, + frame->width, frame->height); frame->data[0] = out_y; frame->data[1] = out_u; frame->data[2] = out_v; diff --git a/openpilot/tools/cabana/SConscript b/openpilot/tools/cabana/SConscript index aa91514510..ec94fec266 100644 --- a/openpilot/tools/cabana/SConscript +++ b/openpilot/tools/cabana/SConscript @@ -75,7 +75,7 @@ cabana_env = qt_env.Clone() cabana_env['CPPPATH'] += [libusb.INCLUDE_DIR] cabana_env['LIBPATH'] += [libusb.LIB_DIR] -cabana_libs = [cereal, messaging, visionipc, replay_lib] + ffmpeg_libs + ['bz2', 'zstd', 'yuv', 'usb-1.0'] + base_libs +cabana_libs = [cereal, messaging, visionipc, replay_lib] + ffmpeg_libs + ['bz2', 'zstd', 'usb-1.0'] + base_libs opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../../opendbc/dbc").abspath) cabana_env['CXXFLAGS'] += [opendbc_path] diff --git a/openpilot/tools/jotpluggler/SConscript b/openpilot/tools/jotpluggler/SConscript index dda9c8fac4..d5ebaffb98 100644 --- a/openpilot/tools/jotpluggler/SConscript +++ b/openpilot/tools/jotpluggler/SConscript @@ -102,7 +102,7 @@ event_extractors = jot_env.Command("generated_event_extractors.h", [ ) libs = [replay_lib, common, messaging, visionipc, cereal, File(f"{imgui.LIB_DIR}/libimgui.a"), File(f"{imgui.LIB_DIR}/libglfw3.a")] + \ - ffmpeg_libs + ["yuv", "bz2", "zstd", "m", "pthread", "usb-1.0"] + ffmpeg_libs + ["bz2", "zstd", "m", "pthread", "usb-1.0"] if arch == "Darwin": jot_env["FRAMEWORKS"] = ["OpenGL", "Cocoa", "IOKit", "CoreFoundation", "CoreVideo", "CoreMedia", "VideoToolbox"] else: diff --git a/openpilot/tools/jotpluggler/runtime.cc b/openpilot/tools/jotpluggler/runtime.cc index 4e98f96c3d..a1e47c7e8e 100644 --- a/openpilot/tools/jotpluggler/runtime.cc +++ b/openpilot/tools/jotpluggler/runtime.cc @@ -7,7 +7,7 @@ #include "imgui_impl_opengl3.h" #include "imgui_impl_opengl3_loader.h" #include "implot.h" -#include "libyuv.h" +#include "common/yuv.h" #include "msgq_repo/msgq/ipc.h" #include "tools/replay/framereader.h" @@ -1173,14 +1173,14 @@ struct CameraFeedView::Impl { result.width = reader->width; result.height = reader->height; result.rgba.resize(static_cast(result.width) * static_cast(result.height) * 4U, 0); - libyuv::NV12ToABGR(decode_buffer.y, - static_cast(decode_buffer.stride), - decode_buffer.uv, - static_cast(decode_buffer.stride), - result.rgba.data(), - result.width * 4, - result.width, - result.height); + yuv::nv12_to_rgba(decode_buffer.y, + static_cast(decode_buffer.stride), + decode_buffer.uv, + static_cast(decode_buffer.stride), + result.rgba.data(), + result.width * 4, + result.width, + result.height); result.success = true; result.decode_ms = std::chrono::duration(std::chrono::steady_clock::now() - decode_begin).count(); publish_result(*request, std::move(result)); @@ -1203,14 +1203,14 @@ struct CameraFeedView::Impl { .height = reader->height, }; prefetched.rgba.resize(static_cast(prefetched.width) * static_cast(prefetched.height) * 4U, 0); - libyuv::NV12ToABGR(decode_buffer.y, - static_cast(decode_buffer.stride), - decode_buffer.uv, - static_cast(decode_buffer.stride), - prefetched.rgba.data(), - prefetched.width * 4, - prefetched.width, - prefetched.height); + yuv::nv12_to_rgba(decode_buffer.y, + static_cast(decode_buffer.stride), + decode_buffer.uv, + static_cast(decode_buffer.stride), + prefetched.rgba.data(), + prefetched.width * 4, + prefetched.width, + prefetched.height); remember_cached_result(prefetched); } } diff --git a/openpilot/tools/replay/SConscript b/openpilot/tools/replay/SConscript index c1f5618523..643de97bb9 100644 --- a/openpilot/tools/replay/SConscript +++ b/openpilot/tools/replay/SConscript @@ -12,7 +12,7 @@ if arch != "Darwin": replay_lib_src.append("qcom_decoder.cc") replay_lib = replay_env.Library("replay", replay_lib_src, LIBS=base_libs, FRAMEWORKS=base_frameworks) Export('replay_lib') -replay_libs = [replay_lib] + ffmpeg_libs + ['bz2', 'zstd', 'yuv', 'ncurses'] + base_libs +replay_libs = [replay_lib] + ffmpeg_libs + ['bz2', 'zstd', 'ncurses'] + base_libs replay_env.Program("replay", ["main.cc"], LIBS=replay_libs, FRAMEWORKS=base_frameworks) if GetOption('extras'): diff --git a/openpilot/tools/replay/framereader.cc b/openpilot/tools/replay/framereader.cc index 52db658add..7c6f144148 100644 --- a/openpilot/tools/replay/framereader.cc +++ b/openpilot/tools/replay/framereader.cc @@ -6,7 +6,7 @@ #include #include "common/util.h" -#include "libyuv.h" +#include "common/yuv.h" #include "tools/replay/py_downloader.h" #include "tools/replay/util.h" #include "common/hardware/hw.h" @@ -258,12 +258,12 @@ bool FFmpegVideoDecoder::copyBuffer(AVFrame *f, VisionBuf *buf) { memcpy(buf->uv + i*buf->stride, f->data[1] + i*f->linesize[1], width); } } else { - libyuv::I420ToNV12(f->data[0], f->linesize[0], - f->data[1], f->linesize[1], - f->data[2], f->linesize[2], - buf->y, buf->stride, - buf->uv, buf->stride, - width, height); + yuv::i420_to_nv12(f->data[0], f->linesize[0], + f->data[1], f->linesize[1], + f->data[2], f->linesize[2], + buf->y, buf->stride, + buf->uv, buf->stride, + width, height); } return true; } diff --git a/pyproject.toml b/pyproject.toml index 1c4e551b81..a609ccf134 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,6 @@ dependencies = [ "comma-deps-acados", "comma-deps-eigen", "comma-deps-ffmpeg", - "comma-deps-libyuv", "comma-deps-zstd", "comma-deps-ncurses", "comma-deps-zeromq", diff --git a/uv.lock b/uv.lock index 8d14aeb3bb..2ea503628f 100644 --- a/uv.lock +++ b/uv.lock @@ -332,16 +332,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/84/47/f4f2da67db202bacc3fa578fdebdb916f45c97bee0c3d2c5fbe845a6f129/comma_deps_libusb-1.0.29.post93-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:f136dbbfc461d228378731361cc66a90a483e17d55f8ba43f23eeab6bcf41963", size = 93461, upload-time = "2026-07-08T19:32:26.434Z" }, ] -[[package]] -name = "comma-deps-libyuv" -version = "1922.0.post93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/91/f68b07f4ef01860d85e716b5095e10326de74455970cb279d70cdf45ee51/comma_deps_libyuv-1922.0.post93-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f5e0d1550229efdc9aabcaa90b421902322ac7ddadd6e2118596916571c0619b", size = 269554, upload-time = "2026-07-08T19:32:29.709Z" }, - { url = "https://files.pythonhosted.org/packages/ee/6b/ca7ab7c00a16470f03a25c1079a50a2b7933516584056f15fb05385f377c/comma_deps_libyuv-1922.0.post93-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:3c55195bc905144ef157b8d2f8f36b25250d0a489bb07f2964327e48a0009669", size = 268385, upload-time = "2026-07-08T19:32:33.141Z" }, - { url = "https://files.pythonhosted.org/packages/0b/9d/ecdbbeef4c3797091ea990bbe4dcc10389e5d6bff27e20bed3b4e4a85eed/comma_deps_libyuv-1922.0.post93-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:1309769c64394294cbd399c77584f7d674753e6dda39b57243ab156711b6d353", size = 273326, upload-time = "2026-07-08T19:32:36.807Z" }, -] - [[package]] name = "comma-deps-ncurses" version = "6.5.post93" @@ -1004,7 +994,6 @@ dependencies = [ { name = "comma-deps-git-lfs" }, { name = "comma-deps-json11" }, { name = "comma-deps-libusb" }, - { name = "comma-deps-libyuv" }, { name = "comma-deps-ncurses" }, { name = "comma-deps-raylib" }, { name = "comma-deps-zeromq" }, @@ -1088,7 +1077,6 @@ requires-dist = [ { name = "comma-deps-imgui", marker = "extra == 'tools'" }, { name = "comma-deps-json11" }, { name = "comma-deps-libusb" }, - { name = "comma-deps-libyuv" }, { name = "comma-deps-ncurses" }, { name = "comma-deps-raylib" }, { name = "comma-deps-zeromq" },