@@ -92,8 +92,8 @@ for cam: [optional]
tool:
- libdrm-dev: Enables the KMS sink
+ - libjpeg-dev: Enables MJPEG on the SDL sink
- libsdl2-dev: Enables the SDL sink
- - libsdl2-image-dev: Supports MJPEG on the SDL sink
for qcam: [optional]
qtbase5-dev libqt5core5a libqt5gui5 libqt5widgets5 qttools5-dev-tools libtiff-dev
new file mode 100644
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2022, Ideas on Board Oy
+ *
+ * jpeg_error_manager.cpp - JPEG Error Manager
+ */
+
+#include "jpeg_error_manager.h"
+
+static void errorExit(j_common_ptr cinfo)
+{
+ JpegErrorManager *self =
+ reinterpret_cast<JpegErrorManager *>(cinfo->err);
+ longjmp(self->escape_, 1);
+}
+
+static void outputMessage([[maybe_unused]] j_common_ptr cinfo)
+{
+}
+
+JpegErrorManager::JpegErrorManager(struct jpeg_decompress_struct &cinfo)
+{
+ cinfo.err = jpeg_std_error(&errmgr_);
+ errmgr_.error_exit = errorExit;
+ errmgr_.output_message = outputMessage;
+}
new file mode 100644
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2022, Ideas on Board Oy
+ *
+ * jpeg_error_manager.h - JPEG Error Manager
+ */
+
+#pragma once
+
+#include <setjmp.h>
+#include <stdio.h>
+
+#include <jpeglib.h>
+
+struct JpegErrorManager {
+ JpegErrorManager(struct jpeg_decompress_struct &cinfo);
+
+ /* Order very important for reinterpret_cast */
+ struct jpeg_error_mgr errmgr_;
+ jmp_buf escape_;
+};
@@ -24,8 +24,8 @@ cam_sources = files([
cam_cpp_args = []
libdrm = dependency('libdrm', required : false)
+libjpeg = dependency('libjpeg', required : false)
libsdl2 = dependency('SDL2', required : false)
-libsdl2_image = dependency('SDL2_image', required : false)
if libdrm.found()
cam_cpp_args += [ '-DHAVE_KMS' ]
@@ -43,9 +43,10 @@ if libsdl2.found()
'sdl_texture_yuyv.cpp'
])
- if libsdl2_image.found()
- cam_cpp_args += ['-DHAVE_SDL_IMAGE']
+ if libjpeg.found()
+ cam_cpp_args += ['-DHAVE_LIBJPEG']
cam_sources += files([
+ 'jpeg_error_manager.cpp',
'sdl_texture_mjpg.cpp'
])
endif
@@ -57,8 +58,8 @@ cam = executable('cam', cam_sources,
libcamera_public,
libdrm,
libevent,
+ libjpeg,
libsdl2,
- libsdl2_image,
libyaml,
],
cpp_args : cam_cpp_args,
@@ -21,7 +21,7 @@
#include "event_loop.h"
#include "image.h"
-#ifdef HAVE_SDL_IMAGE
+#ifdef HAVE_LIBJPEG
#include "sdl_texture_mjpg.h"
#endif
#include "sdl_texture_yuyv.h"
@@ -62,7 +62,7 @@ int SDLSink::configure(const libcamera::CameraConfiguration &config)
rect_.h = cfg.size.height;
switch (cfg.pixelFormat) {
-#ifdef HAVE_SDL_IMAGE
+#ifdef HAVE_LIBJPEG
case libcamera::formats::MJPEG:
texture_ = std::make_unique<SDLTextureMJPG>(rect_);
break;
@@ -7,19 +7,52 @@
#include "sdl_texture_mjpg.h"
-#include <SDL2/SDL_image.h>
+#include "jpeg_error_manager.h"
+
+#include <iostream>
using namespace libcamera;
SDLTextureMJPG::SDLTextureMJPG(const SDL_Rect &rect)
- : SDLTexture(rect, SDL_PIXELFORMAT_RGB24, 0)
+ : SDLTexture(rect, SDL_PIXELFORMAT_RGB24, rect.w * 3),
+ rgb_(std::make_unique<unsigned char[]>(pitch_ * rect.h))
+{
+}
+
+int SDLTextureMJPG::decompress(const unsigned char *jpeg,
+ unsigned long jpeg_size)
{
+ struct jpeg_decompress_struct cinfo;
+ JpegErrorManager jpegErrorManager(cinfo);
+ if (setjmp(jpegErrorManager.escape_)) {
+ /* libjpeg found an error */
+ jpeg_destroy_decompress(&cinfo);
+ std::cerr << "JPEG decompression error" << std::endl;
+ return -EINVAL;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_mem_src(&cinfo, jpeg, jpeg_size);
+
+ jpeg_read_header(&cinfo, TRUE);
+
+ jpeg_start_decompress(&cinfo);
+
+ for (int i = 0; cinfo.output_scanline < cinfo.output_height; ++i) {
+ JSAMPROW rowptr = rgb_.get() + i * pitch_;
+ jpeg_read_scanlines(&cinfo, &rowptr, 1);
+ }
+
+ jpeg_finish_decompress(&cinfo);
+
+ jpeg_destroy_decompress(&cinfo);
+
+ return 0;
}
void SDLTextureMJPG::update(const Span<uint8_t> &data)
{
- SDL_RWops *bufferStream = SDL_RWFromMem(data.data(), data.size());
- SDL_Surface *frame = IMG_Load_RW(bufferStream, 0);
- SDL_UpdateTexture(ptr_, nullptr, frame->pixels, frame->pitch);
- SDL_FreeSurface(frame);
+ decompress(data.data(), data.size());
+ SDL_UpdateTexture(ptr_, nullptr, rgb_.get(), pitch_);
}
@@ -13,5 +13,11 @@ class SDLTextureMJPG : public SDLTexture
{
public:
SDLTextureMJPG(const SDL_Rect &rect);
+
void update(const libcamera::Span<uint8_t> &data) override;
+
+private:
+ int decompress(const unsigned char *jpeg, unsigned long jpeg_size);
+
+ std::unique_ptr<unsigned char[]> rgb_;
};
We were using the libjpeg functionality of SDL2_image only, instead just use libjpeg directly to reduce our dependancy count, it is a more commonly available library. Signed-off-by: Eric Curtin <ecurtin@redhat.com> --- Changes in v3: - create JpegErrorManager struct - change c cast to reinterpret_cast Changes in v2: - alphabetically sorted various orderings - pitch_ is const again - added setjmp logic for error handling in libjpeg - rgbdata_ to unique_ptr and renamed to rgb_ - removed a copy from buffer to rgb_ - removed a destructor --- README.rst | 2 +- src/cam/jpeg_error_manager.cpp | 26 ++++++++++++++++++++ src/cam/jpeg_error_manager.h | 21 ++++++++++++++++ src/cam/meson.build | 9 ++++--- src/cam/sdl_sink.cpp | 4 +-- src/cam/sdl_texture_mjpg.cpp | 45 +++++++++++++++++++++++++++++----- src/cam/sdl_texture_mjpg.h | 6 +++++ 7 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 src/cam/jpeg_error_manager.cpp create mode 100644 src/cam/jpeg_error_manager.h