arduino-audio-tools
Loading...
Searching...
No Matches
CodecMPG.h
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
6
7#include "TinyMPGDecoder.h"
8#include "TinyMPGEncoder.h"
9
10
11#ifndef MPG_DEFAULT_ALLOCATOR
12#ifdef ESP32
13# define MPG_DEFAULT_ALLOCATOR tinympg::PSRAMAllocatorESP32<uint8_t>
14#else
15# define MPG_DEFAULT_ALLOCATOR tinympg::StdAllocator<uint8_t>
16#endif
17#endif
18
19
33namespace audio_tools {
34
48class MPGDecoder : public VideoDecoder, public VideoInfoSource {
49 public:
50 MPGDecoder() { decoder_.setCallback(onFrame, this); }
51
53
55
57
66 bool isValid(const uint8_t *data, size_t len) override {
67 for (size_t i = 0; i + 3 < len; i++) {
68 if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 &&
69 data[i + 3] == 0xB3) {
70 return true;
71 }
72 }
73 return false;
74 }
75
76 void setOutput(Print &out) override { p_out = &out; }
77 void setOutput(VideoOutput &out) override { p_out_video = &out; }
78
79 void setVideoFormat(VideoFormat format) override {
80 switch (format) {
85 pixel_format = format;
86 break;
87 default:
88 LOGW("MPGDecoder: unsupported VideoFormat %d", (int)format);
89 break;
90 }
91 }
92
97 void setIgnorePFrames(bool active) { decoder_.setIgnorePFrames(active); }
98 bool ignorePFrames() const { return decoder_.ignorePFrames(); }
99
110 void setByteSwap(bool active) { byte_swap = active; }
111 bool byteSwap() const { return byte_swap; }
112
113 VideoInfo videoInfo() override {
114 VideoInfo info;
115 info.format = pixel_format;
116 info.width = (uint16_t)decoder_.width();
117 info.height = (uint16_t)decoder_.height();
118 return info;
119 }
120
121 bool begin() override {
122 decoder_.begin();
123 return true;
124 }
125
133 void end() override {
134 decoder_.write(nullptr, 0);
135 decoder_.end();
136 }
137
150 size_t write(const uint8_t *data, size_t len) override {
151 frame_emitted = false;
152 decoder_.write(data, len);
153 return len;
154 }
155
164 void flush() override {}
165
166 bool hasError() { return decoder_.hasError(); }
167
171 bool isKeyFrame(const uint8_t *data, size_t len) override {
172 return isMpeg1KeyFrame(data, len);
173 }
174
183 bool hadOutput() const override { return frame_emitted; }
184
185 tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> &driver() { return decoder_; }
186
187 protected:
188 tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> decoder_;
189 Print *p_out = nullptr;
192 bool byte_swap = true; // see setByteSwap()
193 bool frame_emitted = false; // see hadOutput()
195
196 static void onFrame(tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> &decoder,
197 void *userData) {
198 static_cast<MPGDecoder *>(userData)->writeFrame(decoder);
199 }
200
201 void writeFrame(tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> &decoder) {
202 if (p_out == nullptr && p_out_video == nullptr) return;
203
204 size_t w = decoder.width();
205 size_t h = decoder.height();
206 size_t n = 0;
207
208 switch (pixel_format) {
209 case VideoFormat::RGB565: {
210 size_t needed = w * h;
211 if (frame_buffer.size() < needed * 2) {
212 frame_buffer.resize(needed * 2);
213 if (frame_buffer.data() == nullptr) {
214 LOGE("MPGDecoder: frame buffer allocation failed (RGB565)");
215 return;
216 }
217 }
218 n = decoder.toRGB565((uint16_t *)frame_buffer.data(), needed);
219 if (n > 0) {
220 if (byte_swap) {
221 uint16_t *px = (uint16_t *)frame_buffer.data();
222 for (size_t i = 0; i < n; i++) {
223 uint16_t v = px[i];
224 px[i] = (uint16_t)((v << 8) | (v >> 8));
225 }
226 }
228 }
229 break;
230 }
231 case VideoFormat::RGB888: {
232 size_t needed = w * h * 3;
233 if (frame_buffer.size() < needed) {
234 frame_buffer.resize(needed);
235 if (frame_buffer.data() == nullptr) {
236 LOGE("MPGDecoder: frame buffer allocation failed (RGB888)");
237 return;
238 }
239 }
240 n = decoder.toRGB888(frame_buffer.data(), needed);
241 if (n > 0) writeToOutput(frame_buffer.data(), n);
242 break;
243 }
244 case VideoFormat::RGB666: {
245 size_t needed = w * h * 3;
246 if (frame_buffer.size() < needed) {
247 frame_buffer.resize(needed);
248 if (frame_buffer.data() == nullptr) {
249 LOGE("MPGDecoder: frame buffer allocation failed (RGB666)");
250 return;
251 }
252 }
253 n = decoder.toRGB666(frame_buffer.data(), needed);
254 if (n > 0) writeToOutput(frame_buffer.data(), n);
255 break;
256 }
257 case VideoFormat::I420: {
258 size_t needed = w * h + 2 * (w / 2) * (h / 2);
259 if (frame_buffer.size() < needed) {
260 frame_buffer.resize(needed);
261 if (frame_buffer.data() == nullptr) {
262 LOGE("MPGDecoder: frame buffer allocation failed (I420)");
263 return;
264 }
265 }
266 n = decoder.toYUV420(frame_buffer.data(), needed);
267 if (n > 0) writeToOutput(frame_buffer.data(), n);
268 break;
269 }
270 default:
271 break;
272 }
273 if (n > 0) frame_emitted = true;
274 }
275
276 size_t writeToOutput(uint8_t *data, size_t len) {
277 size_t result = 0;
278 if (p_out != nullptr) result = p_out->write((const uint8_t *)data, len);
279 if (p_out_video != nullptr) result += p_out_video->write((const uint8_t *)data, len);
280 return result;
281 }
282};
283
296class MPGEncoder : public VideoEncoder {
297 public:
298 MPGEncoder(int width = 0, int height = 0) {
299 width_ = width;
300 height_ = height;
301 encoder_.setQp(8);
302 if (width_ > 0 && height_ > 0) {
303 encoder_.setSize(width_, height_);
305 }
306 }
307
308 MPGEncoder(Print &out) { setOutput(out); }
309
311
312 void setOutput(Print &out) override { p_out = &out; }
313 void setOutput(VideoOutput &out) { p_out_video = &out; }
314
315 void setVideoFormat(VideoFormat format) override {
316 switch (format) {
322 input_format = format;
323 break;
324 default:
325 LOGW("MPGEncoder: unsupported VideoFormat %d", (int)format);
326 break;
327 }
328 }
329
330 VideoInfo videoInfo() override {
331 VideoInfo info;
333 info.width = (uint16_t)width_;
334 info.height = (uint16_t)height_;
335 return info;
336 }
337
338 void setSize(int width, int height) {
339 width_ = width;
340 height_ = height;
341 encoder_.setSize(width, height);
343 }
344
345 void setQp(int qscale) { encoder_.setQp(qscale); }
346
347 void setFrameRate(int fps) { encoder_.setFrameRate(fps); }
348
349 void setBitstreamBufferSize(size_t bytes) {
351 if (bitstream_buffer.data() == nullptr && bytes > 0) {
352 LOGE("MPGEncoder: bitstream buffer allocation failed");
353 }
354 }
355
356 bool begin() override {
357 if (width_ <= 0 || height_ <= 0) {
358 LOGE("MPGEncoder: invalid size - call setSize() first");
359 return false;
360 }
361 if (!ensureBitstreamBuffer()){
362 LOGE("MPGEncoder: failed to allocate bitstream buffer");
363 return false;
364 }
365 return true;
366 }
367
368 void end() override {}
369
370 size_t write(const uint8_t *data, size_t len) override {
371 if (p_out == nullptr && p_out_video == nullptr) return 0;
372 if (width_ <= 0 || height_ <= 0) {
373 LOGE("MPGEncoder: invalid size - call setSize() first");
374 return 0;
375 }
376 if (!ensureBitstreamBuffer()) {
377 LOGE("MPGEncoder: bitstream buffer not available");
378 return 0;
379 }
380
381 switch (input_format) {
382 case VideoFormat::I420: {
383 size_t y_size = (size_t)width_ * (size_t)height_;
384 size_t uv_size = y_size / 4;
385 size_t min_size = y_size + uv_size + uv_size;
386 if (len < min_size) {
387 LOGE("MPGEncoder: input buffer too small for I420 frame");
388 return 0;
389 }
390 const uint8_t *srcY = data;
391 const uint8_t *srcU = data + y_size;
392 const uint8_t *srcV = data + y_size + uv_size;
393 return writeOut([&](uint8_t *dst, size_t cap) {
394 return encoder_.encodeFrame(srcY, srcU, srcV, dst, cap);
395 });
396 }
398 return writeOut([&](uint8_t *dst, size_t cap) {
399 return encoder_.encodeFrameYuv422(data, dst, cap);
400 });
402 return writeOut([&](uint8_t *dst, size_t cap) {
403 return encoder_.encodeFrameRgb565((const uint16_t *)data, dst, cap);
404 });
406 return writeOut([&](uint8_t *dst, size_t cap) {
407 return encoder_.encodeFrameRgb666(data, dst, cap);
408 });
410 return writeOut([&](uint8_t *dst, size_t cap) {
411 return encoder_.encodeFrameRgb888(data, dst, cap);
412 });
413 default:
414 LOGE("MPGEncoder: unsupported VideoFormat %d", (int)input_format);
415 return 0;
416 }
417 }
418
419 tinympg::TinyMPGEncoder<MPG_DEFAULT_ALLOCATOR> &driver() { return encoder_; }
420
421 protected:
422 static constexpr int kMaxGrowRetries = 4;
423
424 tinympg::TinyMPGEncoder<MPG_DEFAULT_ALLOCATOR> encoder_;
425 Print *p_out = nullptr;
428 int width_ = 0;
429 int height_ = 0;
431
432 template <typename Fn>
433 size_t writeOut(Fn encodeInto) {
434 if (p_out == nullptr && p_out_video == nullptr) return 0;
435 size_t encoded = encodeInto(bitstream_buffer.data(), bitstream_buffer.size());
436 for (int attempt = 0; encoded == 0 && attempt < kMaxGrowRetries; ++attempt) {
438 if (bitstream_buffer.data() == nullptr) {
439 LOGE("MPGEncoder: bitstream buffer grow failed");
440 return 0;
441 }
442 encoded = encodeInto(bitstream_buffer.data(), bitstream_buffer.size());
443 }
444 if (encoded == 0) {
445 LOGE("MPGEncoder: encode failed (status=%d)", (int)encoder_.lastStatus());
446 return 0;
447 }
448 if (p_out != nullptr) p_out->write(bitstream_buffer.data(), encoded);
449 if (p_out_video != nullptr) p_out_video->write(bitstream_buffer.data(), encoded);
450 return encoded;
451 }
452
454 if (width_ <= 0 || height_ <= 0) return false;
455 size_t needed = (size_t)width_ * (size_t)height_ * 2 + 4096;
456 if (bitstream_buffer.size() < needed) {
457 bitstream_buffer.resize(needed);
458 if (bitstream_buffer.data() == nullptr) {
459 return false;
460 }
461 }
462 return bitstream_buffer.data() != nullptr;
463 }
464};
465
466} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Definition Arduino.h:56
virtual size_t write(const uint8_t *data, size_t len)
Definition Arduino.h:120
MPEG-1 (part 2) video decoder wrapper around TinyMPGDecoder.
Definition CodecMPG.h:48
void flush() override
Definition CodecMPG.h:164
void setOutput(Print &out) override
Defines the target each decoded picture is written to.
Definition CodecMPG.h:76
VideoInfo videoInfo() override
Definition CodecMPG.h:113
bool isValid(const uint8_t *data, size_t len) override
Definition CodecMPG.h:66
MPGDecoder()
Definition CodecMPG.h:50
bool byteSwap() const
Definition CodecMPG.h:111
VideoOutput * p_out_video
Definition CodecMPG.h:190
bool byte_swap
Definition CodecMPG.h:192
MPGDecoder(Print &out)
Definition CodecMPG.h:52
bool hadOutput() const override
Definition CodecMPG.h:183
void end() override
Definition CodecMPG.h:133
size_t write(const uint8_t *data, size_t len) override
Definition CodecMPG.h:150
VideoFormat codecFormat() override
Definition CodecMPG.h:56
static void onFrame(tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > &decoder, void *userData)
Definition CodecMPG.h:196
bool frame_emitted
Definition CodecMPG.h:193
void writeFrame(tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > &decoder)
Definition CodecMPG.h:201
void setByteSwap(bool active)
Definition CodecMPG.h:110
bool ignorePFrames() const
Definition CodecMPG.h:98
Print * p_out
Definition CodecMPG.h:189
MPGDecoder(VideoOutput &out)
Definition CodecMPG.h:54
VideoFormat pixel_format
Definition CodecMPG.h:191
size_t writeToOutput(uint8_t *data, size_t len)
Definition CodecMPG.h:276
void setVideoFormat(VideoFormat format) override
Definition CodecMPG.h:79
bool isKeyFrame(const uint8_t *data, size_t len) override
Definition CodecMPG.h:171
tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > & driver()
Definition CodecMPG.h:185
bool begin() override
Initializes the decoder (allocates its picture buffers, etc).
Definition CodecMPG.h:121
void setOutput(VideoOutput &out) override
Definition CodecMPG.h:77
tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > decoder_
Definition CodecMPG.h:188
bool hasError()
Definition CodecMPG.h:166
Vector< uint8_t > frame_buffer
Definition CodecMPG.h:194
void setIgnorePFrames(bool active)
Definition CodecMPG.h:97
MPEG-1 part 2 video encoder wrapper around TinyMPGEncoder.
Definition CodecMPG.h:296
void setOutput(Print &out) override
Defines the target the encoded bitstream is written to.
Definition CodecMPG.h:312
VideoInfo videoInfo() override
Definition CodecMPG.h:330
VideoFormat input_format
Definition CodecMPG.h:427
MPGEncoder(VideoOutput &out)
Definition CodecMPG.h:310
void setSize(int width, int height)
Definition CodecMPG.h:338
VideoOutput * p_out_video
Definition CodecMPG.h:426
int height_
Definition CodecMPG.h:429
size_t writeOut(Fn encodeInto)
Definition CodecMPG.h:433
tinympg::TinyMPGEncoder< tinympg::PSRAMAllocatorESP32< uint8_t > > & driver()
Definition CodecMPG.h:419
void end() override
Releases the encoder's resources.
Definition CodecMPG.h:368
size_t write(const uint8_t *data, size_t len) override
Definition CodecMPG.h:370
static constexpr int kMaxGrowRetries
Definition CodecMPG.h:422
void setOutput(VideoOutput &out)
Definition CodecMPG.h:313
MPGEncoder(int width=0, int height=0)
Definition CodecMPG.h:298
Print * p_out
Definition CodecMPG.h:425
Vector< uint8_t > bitstream_buffer
Definition CodecMPG.h:430
void setVideoFormat(VideoFormat format) override
Definition CodecMPG.h:315
void setFrameRate(int fps)
Definition CodecMPG.h:347
tinympg::TinyMPGEncoder< tinympg::PSRAMAllocatorESP32< uint8_t > > encoder_
Definition CodecMPG.h:424
bool begin() override
Initializes the encoder (allocates its picture buffers, etc).
Definition CodecMPG.h:356
void setQp(int qscale)
Definition CodecMPG.h:345
int width_
Definition CodecMPG.h:428
void setBitstreamBufferSize(size_t bytes)
Definition CodecMPG.h:349
MPGEncoder(Print &out)
Definition CodecMPG.h:308
bool ensureBitstreamBuffer()
Definition CodecMPG.h:453
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
bool resize(size_t newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
int size()
Definition Vector.h:178
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
virtual size_t write(const uint8_t *data, size_t len)=0
VideoFormat
Video codec/pixel-format identifier, shared by two unrelated uses: the (single) video stream of a con...
Definition VideoOutput.h:29
bool isMpeg1KeyFrame(const uint8_t *data, size_t len)
True if the given MPEG-1/2 video access unit's picture header declares picture_coding_type == 1 (I-pi...
Definition Video.h:71
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
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