arduino-audio-tools
Loading...
Searching...
No Matches
CodecJPEG.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // std::min
4
7#include "TinyJPEGDecoder.h" // https://github.com/pschatzmann/TinyJPEG
8#include "TinyJPEGEncoder.h"
9
16
17namespace audio_tools {
18
55 public:
57 decoder_.setCallback(onBlock);
58 decoder_.setUserData(this);
59 decoder_.setSwapBytes(byte_swap);
60 }
63
65
70 bool isValid(const uint8_t *data, size_t len) override {
71 return len >= 2 && data[0] == 0xFF && data[1] == 0xD8;
72 }
73
77 void setOutput(Print &out) override { p_out = &out; }
78 void setOutput(VideoOutput &out) override { p_out_video = &out; }
79
82 void setVideoFormat(VideoFormat format) override {
83 if (format != VideoFormat::RGB565) {
84 LOGW("MJPEGDecoder: unsupported VideoFormat %d - only RGB565",
85 (int)format);
86 return;
87 }
88 }
89
100 void setByteSwap(bool active) {
101 byte_swap = active;
102 decoder_.setSwapBytes(active);
103 }
104 bool byteSwap() const { return byte_swap; }
105
108 VideoInfo videoInfo() override {
109 VideoInfo info;
111 info.width = width_;
112 info.height = height_;
113 return info;
114 }
115
116 bool begin() override { return true; }
117 void end() override {}
118
122 size_t write(const uint8_t *data, size_t len) override {
123 if (pos == 0) start_ms = millis();
124 // prevent memory fragmentation, change size only if more memory is needed
125 if (img_vector.size() < pos + len) {
126 img_vector.resize(pos + len);
127 }
128 memcpy(&img_vector[pos], data, len);
129 pos += len;
130 return len;
131 }
132
141 void flush() override {
142 if (pos == 0) return;
143 if (p_out == nullptr && p_out_video == nullptr) {
144 pos = 0;
145 return;
146 }
147 uint16_t w = 0, h = 0;
148 if (decoder_.getJpgSize(&w, &h, img_vector.data(), pos) != JDR_OK ||
149 w == 0 || h == 0) {
150 LOGE("MJPEGDecoder: could not determine JPEG size");
151 pos = 0;
152 return;
153 }
154 width_ = w;
155 height_ = h;
156 size_t needed = (size_t)width_ * height_ * 2;
157 if (frame_buffer.size() < needed) {
158 frame_buffer.resize(needed);
159 if (frame_buffer.data() == nullptr) {
160 LOGE("MJPEGDecoder: frame buffer allocation failed");
161 pos = 0;
162 return;
163 }
164 }
165 if (decoder_.drawJpg(0, 0, img_vector.data(), pos) == JDR_OK) {
166 writeToOutput(frame_buffer.data(), needed);
167 }
168 pos = 0;
169 }
170
181 bool isKeyFrame(const uint8_t *data, size_t len) override { return true; }
182
183 protected:
185 size_t pos = 0;
186 uint64_t start_ms = 0;
187 tinyjpeg::TinyJPEGDecoder decoder_;
188 Print *p_out = nullptr;
190 uint16_t width_ = 0;
191 uint16_t height_ = 0;
192 bool byte_swap = true; // see setByteSwap()
193 Vector<uint8_t> frame_buffer; // tightly packed RGB565, 2 bytes/pixel
194
198 static bool onBlock(tinyjpeg::TinyJPEGDecoder &decoder, int16_t x,
199 int16_t y, uint16_t w, uint16_t h, uint16_t *data) {
200 return static_cast<MJPEGDecoder *>(decoder.getUserData())
201 ->writeBlock(x, y, w, h, data);
202 }
203
210 bool writeBlock(int16_t x, int16_t y, uint16_t w, uint16_t h,
211 uint16_t *data) {
212 if (x < 0 || y < 0) return true;
213 uint32_t max_x = width_;
214 uint32_t max_y = height_;
215 if ((uint32_t)x >= max_x || (uint32_t)y >= max_y) return true;
216 uint32_t win_w = std::min((uint32_t)w, max_x - (uint32_t)x);
217 uint32_t win_h = std::min((uint32_t)h, max_y - (uint32_t)y);
218 uint16_t *dst = (uint16_t *)frame_buffer.data();
219 for (uint32_t row = 0; row < win_h; row++) {
220 memcpy(dst + ((uint32_t)y + row) * max_x + (uint32_t)x,
221 data + row * w, win_w * sizeof(uint16_t));
222 }
223 return true;
224 }
225
226 size_t writeToOutput(const uint8_t *data, size_t len) {
227 size_t result = 0;
228 if (p_out != nullptr) result = p_out->write(data, len);
229 if (p_out_video != nullptr) result += p_out_video->write(data, len);
230 return result;
231 }
232};
233
256 public:
258 static constexpr size_t kDefaultBufferSize = 16384;
259
260 MJPEGEncoder(int width = 0, int height = 0) { setSize(width, height); }
263
265 void setOutput(Print &out) override { p_out = &out; }
266 void setOutput(VideoOutput &out) { p_out_video = &out; }
267
271 void setVideoFormat(VideoFormat format) override {
272 switch (format) {
276 input_format = format;
277 break;
278 default:
279 LOGW("MJPEGEncoder: unsupported VideoFormat %d", (int)format);
280 break;
281 }
282 }
283
285 VideoInfo videoInfo() override {
286 VideoInfo info;
288 info.width = (uint16_t)width_;
289 info.height = (uint16_t)height_;
290 return info;
291 }
292
294 void setSize(int width, int height) {
295 width_ = width;
296 height_ = height;
297 }
298
299 void setQuality(uint8_t quality) { encoder_.setQuality(quality); }
301 void setSubsample(bool enable) { encoder_.setSubsample(enable); }
302
305 void setBufferSize(size_t size) {
306 if (size > jpeg.size()) jpeg.resize(size);
307 }
308
309 bool begin() override {
310 if (width_ <= 0 || height_ <= 0) {
311 LOGE("MJPEGEncoder: setSize() was not called");
312 return false;
313 }
314 if (jpeg.size() == 0) jpeg.resize(kDefaultBufferSize);
315 return jpeg.data() != nullptr;
316 }
317
318 void end() override {}
319
324 size_t write(const uint8_t *data, size_t len) override {
325 if (p_out == nullptr && p_out_video == nullptr) return 0;
326 size_t expected = videoFrameSizeBytes(input_format, width_, height_);
327 if (expected == 0 || len < expected) {
328 LOGE("MJPEGEncoder: %u bytes, expected %u", (unsigned)len,
329 (unsigned)expected);
330 return 0;
331 }
332 if (jpeg.size() == 0) jpeg.resize(kDefaultBufferSize);
333 size_t size = 0;
334 JERESULT rc = encode(data, size);
335 // the image didn't fit: grow the buffer and try again
336 for (int attempt = 0; rc == JER_INTR && attempt < kMaxGrowRetries;
337 ++attempt) {
338 jpeg.resize(jpeg.size() * 2);
339 if (jpeg.data() == nullptr) {
340 LOGE("MJPEGEncoder: buffer allocation failed");
341 return 0;
342 }
343 rc = encode(data, size);
344 }
345 if (rc != JER_OK) {
346 LOGE("MJPEGEncoder: encoding failed (%d)", (int)rc);
347 return 0;
348 }
349 if (p_out != nullptr) p_out->write(jpeg.data(), size);
350 if (p_out_video != nullptr) p_out_video->write(jpeg.data(), size);
351 return size;
352 }
353
355 tinyjpeg::TinyJPEGEncoder &driver() { return encoder_; }
356
357 protected:
359 static constexpr int kMaxGrowRetries = 4;
360
361 tinyjpeg::TinyJPEGEncoder encoder_;
362 Print *p_out = nullptr;
366 int width_ = 0;
367 int height_ = 0;
368
369 JERESULT encode(const uint8_t *data, size_t &size) {
370 JEPixelFormat format = input_format == VideoFormat::RGB565 ? JE_FMT_RGB565
372 ? JE_FMT_RGB666
373 : JE_FMT_RGB888;
374 return encoder_.encodeJpg(data, width_, height_, format, jpeg.data(),
375 jpeg.size(), size);
376 }
377};
378
379} // namespace audio_tools
#define LOGW(...)
#define LOGE(...)
uint16_t height_
Definition CodecJPEG.h:191
void flush() override
Definition CodecJPEG.h:141
void setOutput(Print &out) override
Definition CodecJPEG.h:77
VideoInfo videoInfo() override
Definition CodecJPEG.h:108
bool isValid(const uint8_t *data, size_t len) override
Definition CodecJPEG.h:70
static bool onBlock(tinyjpeg::TinyJPEGDecoder &decoder, int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *data)
Definition CodecJPEG.h:198
bool byteSwap() const
Definition CodecJPEG.h:104
Vector< uint8_t > img_vector
Definition CodecJPEG.h:184
VideoOutput * p_out_video
Definition CodecJPEG.h:189
bool byte_swap
Definition CodecJPEG.h:192
size_t pos
Definition CodecJPEG.h:185
bool writeBlock(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *data)
Definition CodecJPEG.h:210
void end() override
Releases the decoder's resources.
Definition CodecJPEG.h:117
uint64_t start_ms
Definition CodecJPEG.h:186
size_t write(const uint8_t *data, size_t len) override
Definition CodecJPEG.h:122
size_t writeToOutput(const uint8_t *data, size_t len)
Definition CodecJPEG.h:226
VideoFormat codecFormat() override
Definition CodecJPEG.h:64
MJPEGDecoder(VideoOutput &out)
Definition CodecJPEG.h:62
void setByteSwap(bool active)
Definition CodecJPEG.h:100
Print * p_out
Definition CodecJPEG.h:188
void setVideoFormat(VideoFormat format) override
Definition CodecJPEG.h:82
bool isKeyFrame(const uint8_t *data, size_t len) override
Definition CodecJPEG.h:181
bool begin() override
Initializes the decoder (allocates its picture buffers, etc).
Definition CodecJPEG.h:116
void setOutput(VideoOutput &out) override
Definition CodecJPEG.h:78
MJPEGDecoder()
Definition CodecJPEG.h:56
uint16_t width_
Definition CodecJPEG.h:190
MJPEGDecoder(Print &out)
Definition CodecJPEG.h:61
Vector< uint8_t > frame_buffer
Definition CodecJPEG.h:193
tinyjpeg::TinyJPEGDecoder decoder_
Definition CodecJPEG.h:187
void setSubsample(bool enable)
4:2:0 chroma subsampling on/off - see TinyJPEGEncoder::setSubsample()
Definition CodecJPEG.h:301
void setOutput(Print &out) override
Defines the target each encoded JPEG image is written to.
Definition CodecJPEG.h:265
VideoInfo videoInfo() override
Reports VideoFormat::MJPEG and the size set via setSize().
Definition CodecJPEG.h:285
tinyjpeg::TinyJPEGEncoder & driver()
Direct access to the wrapped TinyJPEGEncoder.
Definition CodecJPEG.h:355
VideoFormat input_format
Definition CodecJPEG.h:365
MJPEGEncoder(int width=0, int height=0)
Definition CodecJPEG.h:260
void setSize(int width, int height)
Defines the picture size.
Definition CodecJPEG.h:294
VideoOutput * p_out_video
Definition CodecJPEG.h:363
void setQuality(uint8_t quality)
JPEG quality: 1 (smallest) to 100 (best) - see TinyJPEGEncoder::setQuality().
Definition CodecJPEG.h:299
int height_
Definition CodecJPEG.h:367
Vector< uint8_t > jpeg
Definition CodecJPEG.h:364
void end() override
Releases the encoder's resources.
Definition CodecJPEG.h:318
static constexpr size_t kDefaultBufferSize
Default size (bytes) of the internal JPEG buffer - see setBufferSize().
Definition CodecJPEG.h:258
void setBufferSize(size_t size)
Definition CodecJPEG.h:305
size_t write(const uint8_t *data, size_t len) override
Definition CodecJPEG.h:324
MJPEGEncoder(Print &out)
Definition CodecJPEG.h:261
static constexpr int kMaxGrowRetries
Number of times the buffer is doubled before giving up.
Definition CodecJPEG.h:359
MJPEGEncoder(VideoOutput &out)
Definition CodecJPEG.h:262
void setOutput(VideoOutput &out)
Definition CodecJPEG.h:266
Print * p_out
Definition CodecJPEG.h:362
void setVideoFormat(VideoFormat format) override
Definition CodecJPEG.h:271
bool begin() override
Initializes the encoder (allocates its picture buffers, etc).
Definition CodecJPEG.h:309
int width_
Definition CodecJPEG.h:366
tinyjpeg::TinyJPEGEncoder encoder_
Definition CodecJPEG.h:361
JERESULT encode(const uint8_t *data, size_t &size)
Definition CodecJPEG.h:369
Definition CodecOpusOgg.h:57
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Common interface for video decoders (e.g. H264Decoder, H264DecoderESP32S3 - CodecH264....
Definition CodecVideo.h:18
Common interface for video encoders (e.g. H264Encoder, H264EncoderESP32S3 - CodecH264....
Definition CodecVideo.h:89
Provider of VideoInfo (width/height/fps/format) for whoever needs to size buffers/panel setup against...
Definition VideoOutput.h:92
Abstract class for video playback. This class is used to assemble a complete video frame in memory....
Definition VideoOutput.h:107
VideoFormat
Video codec/pixel-format identifier, shared by two unrelated uses: the (single) video stream of a con...
Definition VideoOutput.h:29
size_t videoFrameSizeBytes(VideoFormat format, uint16_t width, uint16_t height)
Fixed per-frame size (bytes) for a raw/uncompressed VideoFormat at the given resolution - 0 for compr...
Definition Video.h:27
@ MJPEG
Definition VideoOutput.h:31
@ RGB888
Definition VideoOutput.h:37
@ RGB565
Definition VideoOutput.h:35
@ RGB666
Definition VideoOutput.h:36
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
uint32_t millis()
Returns the milliseconds since the start.
Definition Arduino.h:260
Basic video information (width/height/codec/frame size), analogous to AudioInfo - common to both Demu...
Definition VideoOutput.h:49
uint16_t height
Frame height in pixels.
Definition VideoOutput.h:53
uint16_t width
Frame width in pixels.
Definition VideoOutput.h:51
VideoFormat format
Video codec - VideoFormat::UNKNOWN if not (yet) determined.
Definition VideoOutput.h:58