From cb82722dd558425f2c365ca29e96e8fc63f80ee3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 8 Jul 2026 12:21:22 -0700 Subject: [PATCH] remove libjpeg (#38305) * remove libjpeg * lil smaller * happy linter --- SConstruct | 2 +- openpilot/system/loggerd/SConscript | 2 +- .../system/loggerd/encoder/jpeg_encoder.cc | 118 ++++++++++-------- .../system/loggerd/encoder/jpeg_encoder.h | 21 ++-- pyproject.toml | 1 - uv.lock | 6 - 6 files changed, 78 insertions(+), 72 deletions(-) diff --git a/SConstruct b/SConstruct index b5e2d04c2..d73bb4b22 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', 'libjpeg', 'libyuv', 'ncurses', 'zeromq', 'zstd'] +pkg_names = ['acados', 'bzip2', 'capnproto', 'catch2', 'eigen', 'ffmpeg', 'json11', 'libyuv', 'ncurses', 'zeromq', 'zstd'] pkgs = [importlib.import_module(name) for name in pkg_names] acados = pkgs[pkg_names.index('acados')] acados_include_dirs = [ diff --git a/openpilot/system/loggerd/SConscript b/openpilot/system/loggerd/SConscript index a638704dc..f83e3f04f 100644 --- a/openpilot/system/loggerd/SConscript +++ b/openpilot/system/loggerd/SConscript @@ -21,7 +21,7 @@ logger_lib = env.Library('logger', src) libs.insert(0, logger_lib) env.Program('loggerd', ['loggerd.cc'], LIBS=libs, FRAMEWORKS=frameworks) -env.Program('encoderd', ['encoderd.cc'], LIBS=libs + ["jpeg"], FRAMEWORKS=frameworks) +env.Program('encoderd', ['encoderd.cc'], LIBS=libs, FRAMEWORKS=frameworks) env.Program('bootlog.cc', LIBS=libs, FRAMEWORKS=frameworks) if GetOption('extras'): diff --git a/openpilot/system/loggerd/encoder/jpeg_encoder.cc b/openpilot/system/loggerd/encoder/jpeg_encoder.cc index 6bb946157..79f5e1b80 100644 --- a/openpilot/system/loggerd/encoder/jpeg_encoder.cc +++ b/openpilot/system/loggerd/encoder/jpeg_encoder.cc @@ -3,16 +3,50 @@ #include #include -JpegEncoder::JpegEncoder(const std::string &pusblish_name, int width, int height) - : publish_name(pusblish_name), thumbnail_width(width), thumbnail_height(height) { - yuv_buffer.resize((thumbnail_width * ((thumbnail_height + 15) & ~15) * 3) / 2); - pm = std::make_unique(std::vector{pusblish_name.c_str()}); +#include "common/swaglog.h" + +// Lower qscale = higher quality / bigger files for MJPEG. +constexpr int MJPEG_QSCALE = 7; + +JpegEncoder::JpegEncoder(const std::string &publish_name, int width, int height) + : publish_name(publish_name), thumbnail_width(width), thumbnail_height(height) { + yuv_buffer.resize((thumbnail_width * thumbnail_height * 3) / 2); + pm = std::make_unique(std::vector{publish_name.c_str()}); + + const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); + assert(codec); + + codec_ctx = avcodec_alloc_context3(codec); + assert(codec_ctx); + codec_ctx->width = thumbnail_width; + codec_ctx->height = thumbnail_height; + codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; + codec_ctx->time_base = (AVRational){1, 1}; + codec_ctx->color_range = AVCOL_RANGE_JPEG; + codec_ctx->flags |= AV_CODEC_FLAG_QSCALE; + codec_ctx->global_quality = FF_QP2LAMBDA * MJPEG_QSCALE; + + int err = avcodec_open2(codec_ctx, codec, NULL); + assert(err >= 0); + + frame = av_frame_alloc(); + assert(frame); + frame->format = codec_ctx->pix_fmt; + frame->width = thumbnail_width; + frame->height = thumbnail_height; + frame->linesize[0] = thumbnail_width; + frame->linesize[1] = thumbnail_width / 2; + frame->linesize[2] = thumbnail_width / 2; + frame->color_range = AVCOL_RANGE_JPEG; + + pkt = av_packet_alloc(); + assert(pkt); } JpegEncoder::~JpegEncoder() { - if (out_buffer) { - free(out_buffer); - } + av_packet_free(&pkt); + av_frame_free(&frame); + avcodec_free_context(&codec_ctx); } void JpegEncoder::pushThumbnail(VisionBuf *buf, const VisionIpcBufExtra &extra) { @@ -22,7 +56,7 @@ void JpegEncoder::pushThumbnail(VisionBuf *buf, const VisionIpcBufExtra &extra) auto thumbnaild = msg.initEvent().initThumbnail(); thumbnaild.setFrameId(extra.frame_id); thumbnaild.setTimestampEof(extra.timestamp_eof); - thumbnaild.setThumbnail({out_buffer, out_size}); + thumbnaild.setThumbnail({out_buffer.data(), out_buffer.size()}); pm->send(publish_name.c_str(), msg); } @@ -31,12 +65,11 @@ void JpegEncoder::generateThumbnail(const uint8_t *y_addr, const uint8_t *uv_add int downscale = width / thumbnail_width; assert(downscale * thumbnail_height == height); - // make the buffer big enough. jpeg_write_raw_data requires 16-pixels aligned height to be used. uint8_t *y_plane = yuv_buffer.data(); uint8_t *u_plane = y_plane + thumbnail_width * thumbnail_height; uint8_t *v_plane = u_plane + (thumbnail_width * thumbnail_height) / 4; { - // subsampled conversion from nv12 to yuv + // subsampled conversion from nv12 to yuv420p for (int hy = 0; hy < thumbnail_height / 2; hy++) { for (int hx = 0; hx < thumbnail_width / 2; hx++) { int ix = hx * downscale + (downscale - 1) / 2; @@ -55,51 +88,28 @@ void JpegEncoder::generateThumbnail(const uint8_t *y_addr, const uint8_t *uv_add } void JpegEncoder::compressToJpeg(uint8_t *y_plane, uint8_t *u_plane, uint8_t *v_plane) { - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); + frame->data[0] = y_plane; + frame->data[1] = u_plane; + frame->data[2] = v_plane; + // Required for MJPEG qscale to take effect (global_quality alone is not enough). + frame->quality = FF_QP2LAMBDA * MJPEG_QSCALE; + frame->pts = 0; - if (out_buffer) { - free(out_buffer); - out_buffer = nullptr; - out_size = 0; - } - jpeg_mem_dest(&cinfo, &out_buffer, &out_size); - - cinfo.image_width = thumbnail_width; - cinfo.image_height = thumbnail_height; - cinfo.input_components = 3; - - jpeg_set_defaults(&cinfo); - jpeg_set_colorspace(&cinfo, JCS_YCbCr); - // configure sampling factors for yuv420. - cinfo.comp_info[0].h_samp_factor = 2; // Y - cinfo.comp_info[0].v_samp_factor = 2; - cinfo.comp_info[1].h_samp_factor = 1; // U - cinfo.comp_info[1].v_samp_factor = 1; - cinfo.comp_info[2].h_samp_factor = 1; // V - cinfo.comp_info[2].v_samp_factor = 1; - cinfo.raw_data_in = TRUE; - - jpeg_set_quality(&cinfo, 50, TRUE); - jpeg_start_compress(&cinfo, TRUE); - - JSAMPROW y[16], u[8], v[8]; - JSAMPARRAY planes[3]{y, u, v}; - - for (int line = 0; line < cinfo.image_height; line += 16) { - for (int i = 0; i < 16; ++i) { - y[i] = y_plane + (line + i) * cinfo.image_width; - if (i % 2 == 0) { - int offset = (cinfo.image_width / 2) * ((i + line) / 2); - u[i / 2] = u_plane + offset; - v[i / 2] = v_plane + offset; - } - } - jpeg_write_raw_data(&cinfo, planes, 16); + int err = avcodec_send_frame(codec_ctx, frame); + if (err < 0) { + LOGE("thumbnail avcodec_send_frame error %d", err); + out_buffer.clear(); + return; } - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); + av_packet_unref(pkt); + err = avcodec_receive_packet(codec_ctx, pkt); + if (err < 0) { + LOGE("thumbnail avcodec_receive_packet error %d", err); + out_buffer.clear(); + return; + } + + out_buffer.assign(pkt->data, pkt->data + pkt->size); + av_packet_unref(pkt); } diff --git a/openpilot/system/loggerd/encoder/jpeg_encoder.h b/openpilot/system/loggerd/encoder/jpeg_encoder.h index 3bd1308b4..10cec5265 100644 --- a/openpilot/system/loggerd/encoder/jpeg_encoder.h +++ b/openpilot/system/loggerd/encoder/jpeg_encoder.h @@ -1,18 +1,20 @@ #pragma once -#include -#include -#include #include -#include -#include #include +#include +#include + #include "openpilot/cereal/messaging/messaging.h" #include "msgq/visionipc/visionbuf.h" +extern "C" { +#include +} + class JpegEncoder { public: - JpegEncoder(const std::string &pusblish_name, int width, int height); + JpegEncoder(const std::string &publish_name, int width, int height); ~JpegEncoder(); void pushThumbnail(VisionBuf *buf, const VisionIpcBufExtra &extra); @@ -24,9 +26,10 @@ private: int thumbnail_height; std::string publish_name; std::vector yuv_buffer; + std::vector out_buffer; std::unique_ptr pm; - // JPEG output buffer - unsigned char* out_buffer = nullptr; - unsigned long out_size = 0; + AVCodecContext *codec_ctx = nullptr; + AVFrame *frame = nullptr; + AVPacket *pkt = nullptr; }; diff --git a/pyproject.toml b/pyproject.toml index 99c38023c..57e953986 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,6 @@ dependencies = [ "acados @ git+https://github.com/commaai/dependencies.git@release-acados#subdirectory=acados", "eigen @ git+https://github.com/commaai/dependencies.git@release-eigen#subdirectory=eigen", "ffmpeg @ git+https://github.com/commaai/dependencies.git@release-ffmpeg#subdirectory=ffmpeg", - "libjpeg @ git+https://github.com/commaai/dependencies.git@release-libjpeg#subdirectory=libjpeg", "libyuv @ git+https://github.com/commaai/dependencies.git@release-libyuv#subdirectory=libyuv", "zstd @ git+https://github.com/commaai/dependencies.git@release-zstd#subdirectory=zstd", "ncurses @ git+https://github.com/commaai/dependencies.git@release-ncurses#subdirectory=ncurses", diff --git a/uv.lock b/uv.lock index d64cf10cd..bcd96d53d 100644 --- a/uv.lock +++ b/uv.lock @@ -655,10 +655,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/e8/7923893f08504f28e0b5fb8b21795fc388d0363cfa2f412e2541c9464cb1/lefthook-2.1.9-py3-none-win_arm64.whl", hash = "sha256:6d8e3923c42a04e9375f88314ebc8c86f68879d3d0c9d856b6adbed109890e90", size = 4959833, upload-time = "2026-05-29T08:39:32.882Z" }, ] -[[package]] -name = "libjpeg" -version = "3.1.0" -source = { git = "https://github.com/commaai/dependencies.git?subdirectory=libjpeg&rev=release-libjpeg#3f9a0796d1985576b0cb172456e08bb918f3afeb" } [[package]] name = "libusb" @@ -909,7 +905,6 @@ dependencies = [ { name = "jeepney" }, { name = "json-rpc" }, { name = "json11" }, - { name = "libjpeg" }, { name = "libusb" }, { name = "libyuv" }, { name = "ncurses" }, @@ -997,7 +992,6 @@ requires-dist = [ { name = "jinja2", marker = "extra == 'docs'" }, { name = "json-rpc" }, { name = "json11", git = "https://github.com/commaai/dependencies.git?subdirectory=json11&rev=release-json11" }, - { name = "libjpeg", git = "https://github.com/commaai/dependencies.git?subdirectory=libjpeg&rev=release-libjpeg" }, { name = "libusb", git = "https://github.com/commaai/dependencies.git?subdirectory=libusb&rev=release-libusb" }, { name = "libyuv", git = "https://github.com/commaai/dependencies.git?subdirectory=libyuv&rev=release-libyuv" }, { name = "matplotlib", marker = "extra == 'dev'" },