[libcamera-devel,v3] cam: sdl_sink: Use libjpeg over SDL2_image
diff mbox series

Message ID 20220718115128.22437-1-ecurtin@redhat.com
State Accepted
Headers show
Series
  • [libcamera-devel,v3] cam: sdl_sink: Use libjpeg over SDL2_image
Related show

Commit Message

Eric Curtin July 18, 2022, 11:51 a.m. UTC
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

Patch
diff mbox series

diff --git a/README.rst b/README.rst
index b9e72d81..47b914f0 100644
--- a/README.rst
+++ b/README.rst
@@ -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
diff --git a/src/cam/jpeg_error_manager.cpp b/src/cam/jpeg_error_manager.cpp
new file mode 100644
index 00000000..7e45e577
--- /dev/null
+++ b/src/cam/jpeg_error_manager.cpp
@@ -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;
+}
diff --git a/src/cam/jpeg_error_manager.h b/src/cam/jpeg_error_manager.h
new file mode 100644
index 00000000..0af28da1
--- /dev/null
+++ b/src/cam/jpeg_error_manager.h
@@ -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_;
+};
diff --git a/src/cam/meson.build b/src/cam/meson.build
index 5957ce14..86cef683 100644
--- a/src/cam/meson.build
+++ b/src/cam/meson.build
@@ -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,
diff --git a/src/cam/sdl_sink.cpp b/src/cam/sdl_sink.cpp
index f8e3e95d..19fdfd6d 100644
--- a/src/cam/sdl_sink.cpp
+++ b/src/cam/sdl_sink.cpp
@@ -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;
diff --git a/src/cam/sdl_texture_mjpg.cpp b/src/cam/sdl_texture_mjpg.cpp
index 69e99ad3..e3fd1a35 100644
--- a/src/cam/sdl_texture_mjpg.cpp
+++ b/src/cam/sdl_texture_mjpg.cpp
@@ -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_);
 }
diff --git a/src/cam/sdl_texture_mjpg.h b/src/cam/sdl_texture_mjpg.h
index b103f801..332c43d7 100644
--- a/src/cam/sdl_texture_mjpg.h
+++ b/src/cam/sdl_texture_mjpg.h
@@ -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_;
 };