[libcamera-devel,v9,8/8] android: jpeg: Add JEA implementation
diff mbox series

Message ID 20230116002808.16014-9-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • Add CrOS JEA implementation
Related show

Commit Message

Laurent Pinchart Jan. 16, 2023, 12:28 a.m. UTC
From: Harvey Yang <chenghaoyang@chromium.org>

This patch adds JEA implementation to replace libjpeg in CrOS platform,
where hardware accelerator is available.

Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
---
 src/android/cros/camera3_hal.cpp         |   4 +-
 src/android/cros_mojo_token.h            |  12 +++
 src/android/jpeg/encoder_jea.cpp         | 103 +++++++++++++++++++++++
 src/android/jpeg/encoder_jea.h           |  35 ++++++++
 src/android/jpeg/meson.build             |  13 ++-
 src/android/jpeg/post_processor_jpeg.cpp |   8 ++
 6 files changed, 172 insertions(+), 3 deletions(-)
 create mode 100644 src/android/cros_mojo_token.h
 create mode 100644 src/android/jpeg/encoder_jea.cpp
 create mode 100644 src/android/jpeg/encoder_jea.h

Comments

Cheng-Hao Yang Jan. 18, 2023, 8 a.m. UTC | #1
Thanks Laurent!
Some nits below. After these are addressed, the patches work fine on
Chromebook soraka.

On Mon, Jan 16, 2023 at 8:28 AM Laurent Pinchart <
laurent.pinchart@ideasonboard.com> wrote:

> From: Harvey Yang <chenghaoyang@chromium.org>
>
> This patch adds JEA implementation to replace libjpeg in CrOS platform,
> where hardware accelerator is available.
>
> Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
> ---
>  src/android/cros/camera3_hal.cpp         |   4 +-
>  src/android/cros_mojo_token.h            |  12 +++
>  src/android/jpeg/encoder_jea.cpp         | 103 +++++++++++++++++++++++
>  src/android/jpeg/encoder_jea.h           |  35 ++++++++
>  src/android/jpeg/meson.build             |  13 ++-
>  src/android/jpeg/post_processor_jpeg.cpp |   8 ++
>  6 files changed, 172 insertions(+), 3 deletions(-)
>  create mode 100644 src/android/cros_mojo_token.h
>  create mode 100644 src/android/jpeg/encoder_jea.cpp
>  create mode 100644 src/android/jpeg/encoder_jea.h
>
> diff --git a/src/android/cros/camera3_hal.cpp
> b/src/android/cros/camera3_hal.cpp
> index fb863b5f9aa9..71acb441b0d4 100644
> --- a/src/android/cros/camera3_hal.cpp
> +++ b/src/android/cros/camera3_hal.cpp
> @@ -8,9 +8,11 @@
>  #include <cros-camera/cros_camera_hal.h>
>
>  #include "../camera_hal_manager.h"
> +#include "../cros_mojo_token.h"
>
> -static void set_up([[maybe_unused]] cros::CameraMojoChannelManagerToken
> *token)
> +static void set_up(cros::CameraMojoChannelManagerToken *token)
>  {
> +       gCrosMojoToken = token;
>  }
>
>  static void tear_down()
> diff --git a/src/android/cros_mojo_token.h b/src/android/cros_mojo_token.h
> new file mode 100644
> index 000000000000..043c752a3997
> --- /dev/null
> +++ b/src/android/cros_mojo_token.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2022, Google Inc.
> + *
> + * cros_mojo_token.h - cros-specific mojo token
> + */
> +
> +#pragma once
> +
> +#include <cros-camera/cros_camera_hal.h>
> +
> +inline cros::CameraMojoChannelManagerToken *gCrosMojoToken = nullptr;
> diff --git a/src/android/jpeg/encoder_jea.cpp
> b/src/android/jpeg/encoder_jea.cpp
> new file mode 100644
> index 000000000000..a7076039d0b7
> --- /dev/null
> +++ b/src/android/jpeg/encoder_jea.cpp
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2022, Google Inc.
> + *
> + * encoder_jea.cpp - JPEG encoding using CrOS JEA
> + */
> +
> +#include "encoder_jea.h"
> +
> +#include "libcamera/internal/mapped_framebuffer.h"
> +
> +#include <cros-camera/camera_mojo_channel_manager_token.h>
> +
> +#include "../cros_mojo_token.h"
> +#include "../hal_framebuffer.h"
> +
> +EncoderJea::EncoderJea() = default;
> +
> +EncoderJea::~EncoderJea() = default;
> +
> +int EncoderJea::configure(const libcamera::StreamConfiguration &cfg)
> +{
> +       size_ = cfg.size;
> +
> +       if (jpegCompressor_)
> +               return 0;
> +
> +       if (gCrosMojoToken == nullptr)
> +               return -ENOTSUP;
> +
> +       jpegCompressor_ =
> cros::JpegCompressor::GetInstance(gCrosMojoToken);
> +
> +       return 0;
> +}
> +
> +int EncoderJea::encode(Camera3RequestDescriptor::StreamBuffer
> *streamBuffer,
>

Perhaps use |buffer| instead of |streamBuffer|, to be aligned with the base
class.


> +                      libcamera::Span<const uint8_t> exifData,
> +                      unsigned int quality)
> +{
> +       if (!jpegCompressor_)
> +               return -ENOTSUP;
> +
> +       uint32_t outDataSize = 0;
> +       const HALFrameBuffer *fb =
> +               dynamic_cast<const HALFrameBuffer
> *>(streamBuffer->srcBuffer);
> +
> +       if (!jpegCompressor_->CompressImageFromHandle(fb->handle(),
> +
>  *streamBuffer->camera3Buffer,
> +                                                     size_.width,
> size_.height,
> +                                                     quality,
> exifData.data(),
> +                                                     exifData.size(),
> +                                                     &outDataSize))
> +               return -EBUSY;
> +
> +       return outDataSize;
> +}
> +
> +int EncoderJea::generateThumbnail(const libcamera::FrameBuffer &source,
> +                                 const libcamera::Size &targetSize,
> +                                 unsigned int quality,
> +                                 std::vector<unsigned char> *thumbnail)
> +{
> +       if (!jpegCompressor_)
> +               return -ENOTSUPP;
> +
>

Should be `-ENOTSUP`.


> +       libcamera::MappedFrameBuffer frame(&source,
> +
> libcamera::MappedFrameBuffer::MapFlag::Read);
> +
> +       if (frame.planes().empty())
> +               return -EINVAL;
> +
> +       /* JEA needs consecutive memory. */
> +       unsigned long size = 0, index = 0;
> +       for (const auto &plane : frame.planes())
> +               size += plane.size();
> +
> +       std::vector<uint8_t> data(size);
> +       for (const auto &plane : frame.planes()) {
> +               memcpy(&data[index], plane.data(), plane.size());
> +               index += plane.size();
> +       }
> +
> +       uint32_t outDataSize = 0;
> +
> +       /*
> +        * Since the structure of the App1 segment is like:
> +        *   0xFF [1 byte marker] [2 bytes size] [data]
> +        * And it should not be larger than 64K.
> +        */
> +       constexpr int kApp1MaxDataSize = 65532;
> +       thumbnail->resize(kApp1MaxDataSize);
> +
> +       if (!jpegCompressor_->GenerateThumbnail(data.data(),
> +                                               size_.width, size_.height,
> +                                               targetSize.width,
> +                                               targetSize.height, quality,
> +                                               thumbnail->size(),
> +                                               thumbnail->data(),
> +                                               &outDataSize))
> +               return -EBUSY;
> +
> +       thumbnail->resize(outDataSize);
>

Add `return 0;` here


> +}

diff --git a/src/android/jpeg/encoder_jea.h b/src/android/jpeg/encoder_jea.h
> new file mode 100644
> index 000000000000..2eba31c2f73c
> --- /dev/null
> +++ b/src/android/jpeg/encoder_jea.h
> @@ -0,0 +1,35 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2022, Google Inc.
> + *
> + * encoder_jea.h - JPEG encoding using CrOS JEA
> + */
> +
> +#pragma once
> +
> +#include <libcamera/geometry.h>
> +
> +#include <cros-camera/jpeg_compressor.h>
> +
> +#include "encoder.h"
> +
> +class EncoderJea : public Encoder
> +{
> +public:
> +       EncoderJea();
> +       ~EncoderJea();
> +
> +       int configure(const libcamera::StreamConfiguration &cfg) override;
> +       int encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,
> +                  libcamera::Span<const uint8_t> exifData,
> +                  unsigned int quality) override;
> +       int generateThumbnail(const libcamera::FrameBuffer &source,
> +                             const libcamera::Size &targetSize,
> +                             unsigned int quality,
> +                             std::vector<unsigned char> *thumbnail)
> override;
> +
> +private:
> +       libcamera::Size size_;
> +
> +       std::unique_ptr<cros::JpegCompressor> jpegCompressor_;
> +};
> diff --git a/src/android/jpeg/meson.build b/src/android/jpeg/meson.build
> index 08397a87bc46..2b68f54c4228 100644
> --- a/src/android/jpeg/meson.build
> +++ b/src/android/jpeg/meson.build
> @@ -1,8 +1,17 @@
>  # SPDX-License-Identifier: CC0-1.0
>
>  android_hal_sources += files([
> -    'encoder_libjpeg.cpp',
>      'exif.cpp',
>      'post_processor_jpeg.cpp',
> -    'thumbnailer.cpp'
>  ])
> +
> +platform = get_option('android_platform')
> +if platform == 'generic'
> +    android_hal_sources += files([
> +      'encoder_libjpeg.cpp',
> +      'thumbnailer.cpp'
> +    ])
> +elif platform == 'cros'
> +    android_hal_sources += files(['encoder_jea.cpp'])
> +    android_deps += [dependency('libcros_camera')]
> +endif
> diff --git a/src/android/jpeg/post_processor_jpeg.cpp
> b/src/android/jpeg/post_processor_jpeg.cpp
> index 2a22b4a88f4a..f7cc70de1ef1 100644
> --- a/src/android/jpeg/post_processor_jpeg.cpp
> +++ b/src/android/jpeg/post_processor_jpeg.cpp
> @@ -12,7 +12,11 @@
>  #include "../camera_device.h"
>  #include "../camera_metadata.h"
>  #include "../camera_request.h"
> +#if defined(OS_CHROMEOS)
> +#include "encoder_jea.h"
> +#else /* !defined(OS_CHROMEOS) */
>  #include "encoder_libjpeg.h"
> +#endif
>  #include "exif.h"
>
>  #include <libcamera/base/log.h>
> @@ -44,7 +48,11 @@ int PostProcessorJpeg::configure(const
> StreamConfiguration &inCfg,
>
>         streamSize_ = outCfg.size;
>
> +#if defined(OS_CHROMEOS)
> +       encoder_ = std::make_unique<EncoderJea>();
> +#else /* !defined(OS_CHROMEOS) */
>         encoder_ = std::make_unique<EncoderLibJpeg>();
> +#endif
>
>         return encoder_->configure(inCfg);
>  }
> --
> Regards,
>
> Laurent Pinchart
>
>

Patch
diff mbox series

diff --git a/src/android/cros/camera3_hal.cpp b/src/android/cros/camera3_hal.cpp
index fb863b5f9aa9..71acb441b0d4 100644
--- a/src/android/cros/camera3_hal.cpp
+++ b/src/android/cros/camera3_hal.cpp
@@ -8,9 +8,11 @@ 
 #include <cros-camera/cros_camera_hal.h>
 
 #include "../camera_hal_manager.h"
+#include "../cros_mojo_token.h"
 
-static void set_up([[maybe_unused]] cros::CameraMojoChannelManagerToken *token)
+static void set_up(cros::CameraMojoChannelManagerToken *token)
 {
+	gCrosMojoToken = token;
 }
 
 static void tear_down()
diff --git a/src/android/cros_mojo_token.h b/src/android/cros_mojo_token.h
new file mode 100644
index 000000000000..043c752a3997
--- /dev/null
+++ b/src/android/cros_mojo_token.h
@@ -0,0 +1,12 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Google Inc.
+ *
+ * cros_mojo_token.h - cros-specific mojo token
+ */
+
+#pragma once
+
+#include <cros-camera/cros_camera_hal.h>
+
+inline cros::CameraMojoChannelManagerToken *gCrosMojoToken = nullptr;
diff --git a/src/android/jpeg/encoder_jea.cpp b/src/android/jpeg/encoder_jea.cpp
new file mode 100644
index 000000000000..a7076039d0b7
--- /dev/null
+++ b/src/android/jpeg/encoder_jea.cpp
@@ -0,0 +1,103 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Google Inc.
+ *
+ * encoder_jea.cpp - JPEG encoding using CrOS JEA
+ */
+
+#include "encoder_jea.h"
+
+#include "libcamera/internal/mapped_framebuffer.h"
+
+#include <cros-camera/camera_mojo_channel_manager_token.h>
+
+#include "../cros_mojo_token.h"
+#include "../hal_framebuffer.h"
+
+EncoderJea::EncoderJea() = default;
+
+EncoderJea::~EncoderJea() = default;
+
+int EncoderJea::configure(const libcamera::StreamConfiguration &cfg)
+{
+	size_ = cfg.size;
+
+	if (jpegCompressor_)
+		return 0;
+
+	if (gCrosMojoToken == nullptr)
+		return -ENOTSUP;
+
+	jpegCompressor_ = cros::JpegCompressor::GetInstance(gCrosMojoToken);
+
+	return 0;
+}
+
+int EncoderJea::encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,
+		       libcamera::Span<const uint8_t> exifData,
+		       unsigned int quality)
+{
+	if (!jpegCompressor_)
+		return -ENOTSUP;
+
+	uint32_t outDataSize = 0;
+	const HALFrameBuffer *fb =
+		dynamic_cast<const HALFrameBuffer *>(streamBuffer->srcBuffer);
+
+	if (!jpegCompressor_->CompressImageFromHandle(fb->handle(),
+						      *streamBuffer->camera3Buffer,
+						      size_.width, size_.height,
+						      quality, exifData.data(),
+						      exifData.size(),
+						      &outDataSize))
+		return -EBUSY;
+
+	return outDataSize;
+}
+
+int EncoderJea::generateThumbnail(const libcamera::FrameBuffer &source,
+				  const libcamera::Size &targetSize,
+				  unsigned int quality,
+				  std::vector<unsigned char> *thumbnail)
+{
+	if (!jpegCompressor_)
+		return -ENOTSUPP;
+
+	libcamera::MappedFrameBuffer frame(&source,
+					   libcamera::MappedFrameBuffer::MapFlag::Read);
+
+	if (frame.planes().empty())
+		return -EINVAL;
+
+	/* JEA needs consecutive memory. */
+	unsigned long size = 0, index = 0;
+	for (const auto &plane : frame.planes())
+		size += plane.size();
+
+	std::vector<uint8_t> data(size);
+	for (const auto &plane : frame.planes()) {
+		memcpy(&data[index], plane.data(), plane.size());
+		index += plane.size();
+	}
+
+	uint32_t outDataSize = 0;
+
+	/*
+	 * Since the structure of the App1 segment is like:
+	 *   0xFF [1 byte marker] [2 bytes size] [data]
+	 * And it should not be larger than 64K.
+	 */
+	constexpr int kApp1MaxDataSize = 65532;
+	thumbnail->resize(kApp1MaxDataSize);
+
+	if (!jpegCompressor_->GenerateThumbnail(data.data(),
+						size_.width, size_.height,
+						targetSize.width,
+						targetSize.height, quality,
+						thumbnail->size(),
+						thumbnail->data(),
+						&outDataSize))
+		return -EBUSY;
+
+	thumbnail->resize(outDataSize);
+}
diff --git a/src/android/jpeg/encoder_jea.h b/src/android/jpeg/encoder_jea.h
new file mode 100644
index 000000000000..2eba31c2f73c
--- /dev/null
+++ b/src/android/jpeg/encoder_jea.h
@@ -0,0 +1,35 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Google Inc.
+ *
+ * encoder_jea.h - JPEG encoding using CrOS JEA
+ */
+
+#pragma once
+
+#include <libcamera/geometry.h>
+
+#include <cros-camera/jpeg_compressor.h>
+
+#include "encoder.h"
+
+class EncoderJea : public Encoder
+{
+public:
+	EncoderJea();
+	~EncoderJea();
+
+	int configure(const libcamera::StreamConfiguration &cfg) override;
+	int encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,
+		   libcamera::Span<const uint8_t> exifData,
+		   unsigned int quality) override;
+	int generateThumbnail(const libcamera::FrameBuffer &source,
+			      const libcamera::Size &targetSize,
+			      unsigned int quality,
+			      std::vector<unsigned char> *thumbnail) override;
+
+private:
+	libcamera::Size size_;
+
+	std::unique_ptr<cros::JpegCompressor> jpegCompressor_;
+};
diff --git a/src/android/jpeg/meson.build b/src/android/jpeg/meson.build
index 08397a87bc46..2b68f54c4228 100644
--- a/src/android/jpeg/meson.build
+++ b/src/android/jpeg/meson.build
@@ -1,8 +1,17 @@ 
 # SPDX-License-Identifier: CC0-1.0
 
 android_hal_sources += files([
-    'encoder_libjpeg.cpp',
     'exif.cpp',
     'post_processor_jpeg.cpp',
-    'thumbnailer.cpp'
 ])
+
+platform = get_option('android_platform')
+if platform == 'generic'
+    android_hal_sources += files([
+      'encoder_libjpeg.cpp',
+      'thumbnailer.cpp'
+    ])
+elif platform == 'cros'
+    android_hal_sources += files(['encoder_jea.cpp'])
+    android_deps += [dependency('libcros_camera')]
+endif
diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
index 2a22b4a88f4a..f7cc70de1ef1 100644
--- a/src/android/jpeg/post_processor_jpeg.cpp
+++ b/src/android/jpeg/post_processor_jpeg.cpp
@@ -12,7 +12,11 @@ 
 #include "../camera_device.h"
 #include "../camera_metadata.h"
 #include "../camera_request.h"
+#if defined(OS_CHROMEOS)
+#include "encoder_jea.h"
+#else /* !defined(OS_CHROMEOS) */
 #include "encoder_libjpeg.h"
+#endif
 #include "exif.h"
 
 #include <libcamera/base/log.h>
@@ -44,7 +48,11 @@  int PostProcessorJpeg::configure(const StreamConfiguration &inCfg,
 
 	streamSize_ = outCfg.size;
 
+#if defined(OS_CHROMEOS)
+	encoder_ = std::make_unique<EncoderJea>();
+#else /* !defined(OS_CHROMEOS) */
 	encoder_ = std::make_unique<EncoderLibJpeg>();
+#endif
 
 	return encoder_->configure(inCfg);
 }