From 29be8034518c0babbf6334d3ef6fc9ffe30e6b70 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Tue, 10 Jan 2023 08:49:30 +0000 Subject: lib/mjpeg_encoder: Add an MJPEG encoder for YUV420 data MJPEG is an extremely useful format given its compression allows high framerates even over limited bandwith USB connections. Add an MJPEG encoder class that converts YUV420 data into MJPEG data. Where a libcamera-source does not support MJPEG natively, convert YUV420 into MJPEG if the user tries to set MJPEG format. Reviewed-by: Kieran Bingham Signed-off-by: Daniel Scally --- include/uvcgadget/meson.build | 1 + include/uvcgadget/mjpeg_encoder.hpp | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 include/uvcgadget/mjpeg_encoder.hpp (limited to 'include/uvcgadget') diff --git a/include/uvcgadget/meson.build b/include/uvcgadget/meson.build index a9e4384..af795c4 100644 --- a/include/uvcgadget/meson.build +++ b/include/uvcgadget/meson.build @@ -9,6 +9,7 @@ uvcgadget_public_headers = files([ 'timer.h', 'v4l2-source.h', 'video-source.h', + 'mjpeg_encoder.hpp', ]) diff --git a/include/uvcgadget/mjpeg_encoder.hpp b/include/uvcgadget/mjpeg_encoder.hpp new file mode 100644 index 0000000..a3e92fc --- /dev/null +++ b/include/uvcgadget/mjpeg_encoder.hpp @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd. + * + * mjpeg_encoder.hpp - mjpeg video encoder. + */ + +#pragma once + +#include +#include +#include +#include +#include + +struct jpeg_compress_struct; +typedef std::function OutputReadyCallback; + +struct StreamInfo +{ + StreamInfo() : width(0), height(0), stride(0) {} + unsigned int width; + unsigned int height; + unsigned int stride; + libcamera::PixelFormat pixel_format; + std::optional colour_space; +}; + +class MjpegEncoder +{ +public: + MjpegEncoder(); + ~MjpegEncoder(); + + void EncodeBuffer(void *mem, void *dest, unsigned int size, + StreamInfo const &info, int64_t timestamp_us, + unsigned int cookie); + StreamInfo getStreamInfo(libcamera::Stream *stream); + void SetOutputReadyCallback(OutputReadyCallback callback) { output_ready_callback_ = callback; } + +private: + static const int NUM_ENC_THREADS = 4; + + void encodeThread(int num); + + /* + * Handle the output buffers in another thread so as not to block the + * encoders. The application can take its time, after which we return + * this buffer to the encoder for re-use. + */ + void outputThread(); + + bool abortEncode_; + bool abortOutput_; + uint64_t index_; + + struct EncodeItem + { + void *mem; + void *dest; + unsigned int size; + StreamInfo info; + int64_t timestamp_us; + uint64_t index; + unsigned int cookie; + }; + + std::queue encode_queue_; + std::mutex encode_mutex_; + std::condition_variable encode_cond_var_; + std::thread encode_thread_[NUM_ENC_THREADS]; + void encodeJPEG(struct jpeg_compress_struct &cinfo, EncodeItem &item, + uint8_t *&encoded_buffer, size_t &buffer_len); + + struct OutputItem + { + void *mem; + size_t bytes_used; + int64_t timestamp_us; + uint64_t index; + unsigned int cookie; + }; + + std::queue output_queue_[NUM_ENC_THREADS]; + std::mutex output_mutex_; + std::condition_variable output_cond_var_; + std::thread output_thread_; + OutputReadyCallback output_ready_callback_ ; +}; -- cgit v1.2.3