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
56 void setOutput(Print &out) override { p_out = &out; }
57 void setOutput(VideoOutput &out) { p_out_video = &out; }
58
59 void setVideoFormat(VideoFormat format) override {
60 switch (format) {
65 pixel_format = format;
66 break;
67 default:
68 LOGW("MPGDecoder: unsupported VideoFormat %d", (int)format);
69 break;
70 }
71 }
72
73 VideoInfo videoInfo() override {
74 VideoInfo info;
75 info.format = pixel_format;
76 info.width = (uint16_t)decoder_.width();
77 info.height = (uint16_t)decoder_.height();
78 return info;
79 }
80
81 bool begin() override {
82 decoder_.begin();
83 return true;
84 }
85
86 void end() override { decoder_.end(); }
87
88 size_t write(const uint8_t *data, size_t len) override {
89 decoder_.write(data, len);
90 return len;
91 }
92
93 void flush() override {}
94
95 bool hasError() { return decoder_.hasError(); }
96
97 tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> &driver() { return decoder_; }
98
99 protected:
100 tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> decoder_;
101 Print *p_out = nullptr;
105
106 static void onFrame(tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> &decoder,
107 void *userData) {
108 static_cast<MPGDecoder *>(userData)->writeFrame(decoder);
109 }
110
111 void writeFrame(tinympg::TinyMPGDecoder<MPG_DEFAULT_ALLOCATOR> &decoder) {
112 if (p_out == nullptr && p_out_video == nullptr) return;
113
114 size_t w = decoder.width();
115 size_t h = decoder.height();
116 size_t n = 0;
117
118 switch (pixel_format) {
119 case VideoFormat::RGB565: {
120 size_t needed = w * h;
121 if (frame_buffer.size() < needed * 2) {
122 frame_buffer.resize(needed * 2);
123 if (frame_buffer.data() == nullptr) {
124 LOGE("MPGDecoder: frame buffer allocation failed (RGB565)");
125 return;
126 }
127 }
128 n = decoder.toRGB565((uint16_t *)frame_buffer.data(), needed);
129 if (n > 0) writeToOutput(frame_buffer.data(), n * 2);
130 break;
131 }
132 case VideoFormat::RGB888: {
133 size_t needed = w * h * 3;
134 if (frame_buffer.size() < needed) {
135 frame_buffer.resize(needed);
136 if (frame_buffer.data() == nullptr) {
137 LOGE("MPGDecoder: frame buffer allocation failed (RGB888)");
138 return;
139 }
140 }
141 n = decoder.toRGB888(frame_buffer.data(), needed);
142 if (n > 0) writeToOutput(frame_buffer.data(), n);
143 break;
144 }
145 case VideoFormat::RGB666: {
146 size_t needed = w * h * 3;
147 if (frame_buffer.size() < needed) {
148 frame_buffer.resize(needed);
149 if (frame_buffer.data() == nullptr) {
150 LOGE("MPGDecoder: frame buffer allocation failed (RGB666)");
151 return;
152 }
153 }
154 n = decoder.toRGB666(frame_buffer.data(), needed);
155 if (n > 0) writeToOutput(frame_buffer.data(), n);
156 break;
157 }
158 case VideoFormat::I420: {
159 size_t needed = w * h + 2 * (w / 2) * (h / 2);
160 if (frame_buffer.size() < needed) {
161 frame_buffer.resize(needed);
162 if (frame_buffer.data() == nullptr) {
163 LOGE("MPGDecoder: frame buffer allocation failed (I420)");
164 return;
165 }
166 }
167 n = decoder.toYUV420(frame_buffer.data(), needed);
168 if (n > 0) writeToOutput(frame_buffer.data(), n);
169 break;
170 }
171 default:
172 break;
173 }
174 }
175
176 size_t writeToOutput(uint8_t *data, size_t len) {
177 size_t result = 0;
178 if (p_out != nullptr) result = p_out->write((const uint8_t *)data, len);
179 if (p_out_video != nullptr) result += p_out_video->write((const uint8_t *)data, len);
180 return result;
181 }
182};
183
196class MPGEncoder : public VideoEncoder {
197 public:
198 MPGEncoder(int width = 0, int height = 0) {
199 width_ = width;
200 height_ = height;
201 encoder_.setQp(8);
202 if (width_ > 0 && height_ > 0) {
203 encoder_.setSize(width_, height_);
205 }
206 }
207
208 MPGEncoder(Print &out) { setOutput(out); }
209
211
212 void setOutput(Print &out) override { p_out = &out; }
213 void setOutput(VideoOutput &out) { p_out_video = &out; }
214
215 void setVideoFormat(VideoFormat format) override {
216 switch (format) {
222 input_format = format;
223 break;
224 default:
225 LOGW("MPGEncoder: unsupported VideoFormat %d", (int)format);
226 break;
227 }
228 }
229
230 VideoInfo videoInfo() override {
231 VideoInfo info;
233 info.width = (uint16_t)width_;
234 info.height = (uint16_t)height_;
235 return info;
236 }
237
238 void setSize(int width, int height) {
239 width_ = width;
240 height_ = height;
241 encoder_.setSize(width, height);
243 }
244
245 void setQp(int qscale) { encoder_.setQp(qscale); }
246
247 void setFrameRate(int fps) { encoder_.setFrameRate(fps); }
248
249 void setBitstreamBufferSize(size_t bytes) {
251 if (bitstream_buffer.data() == nullptr && bytes > 0) {
252 LOGE("MPGEncoder: bitstream buffer allocation failed");
253 }
254 }
255
256 bool begin() override {
257 if (width_ <= 0 || height_ <= 0) {
258 LOGE("MPGEncoder: invalid size - call setSize() first");
259 return false;
260 }
261 if (!ensureBitstreamBuffer()){
262 LOGE("MPGEncoder: failed to allocate bitstream buffer");
263 return false;
264 }
265 return true;
266 }
267
268 void end() override {}
269
270 size_t write(const uint8_t *data, size_t len) override {
271 if (p_out == nullptr && p_out_video == nullptr) return 0;
272 if (width_ <= 0 || height_ <= 0) {
273 LOGE("MPGEncoder: invalid size - call setSize() first");
274 return 0;
275 }
276 if (!ensureBitstreamBuffer()) {
277 LOGE("MPGEncoder: bitstream buffer not available");
278 return 0;
279 }
280
281 switch (input_format) {
282 case VideoFormat::I420: {
283 size_t y_size = (size_t)width_ * (size_t)height_;
284 size_t uv_size = y_size / 4;
285 size_t min_size = y_size + uv_size + uv_size;
286 if (len < min_size) {
287 LOGE("MPGEncoder: input buffer too small for I420 frame");
288 return 0;
289 }
290 const uint8_t *srcY = data;
291 const uint8_t *srcU = data + y_size;
292 const uint8_t *srcV = data + y_size + uv_size;
293 return writeOut([&](uint8_t *dst, size_t cap) {
294 return encoder_.encodeFrame(srcY, srcU, srcV, dst, cap);
295 });
296 }
298 return writeOut([&](uint8_t *dst, size_t cap) {
299 return encoder_.encodeFrameYuv422(data, dst, cap);
300 });
302 return writeOut([&](uint8_t *dst, size_t cap) {
303 return encoder_.encodeFrameRgb565((const uint16_t *)data, dst, cap);
304 });
306 return writeOut([&](uint8_t *dst, size_t cap) {
307 return encoder_.encodeFrameRgb666(data, dst, cap);
308 });
310 return writeOut([&](uint8_t *dst, size_t cap) {
311 return encoder_.encodeFrameRgb888(data, dst, cap);
312 });
313 default:
314 LOGE("MPGEncoder: unsupported VideoFormat %d", (int)input_format);
315 return 0;
316 }
317 }
318
319 tinympg::TinyMPGEncoder<MPG_DEFAULT_ALLOCATOR> &driver() { return encoder_; }
320
321 protected:
322 static constexpr int kMaxGrowRetries = 4;
323
324 tinympg::TinyMPGEncoder<MPG_DEFAULT_ALLOCATOR> encoder_;
325 Print *p_out = nullptr;
328 int width_ = 0;
329 int height_ = 0;
331
332 template <typename Fn>
333 size_t writeOut(Fn encodeInto) {
334 if (p_out == nullptr && p_out_video == nullptr) return 0;
335 size_t encoded = encodeInto(bitstream_buffer.data(), bitstream_buffer.size());
336 for (int attempt = 0; encoded == 0 && attempt < kMaxGrowRetries; ++attempt) {
338 if (bitstream_buffer.data() == nullptr) {
339 LOGE("MPGEncoder: bitstream buffer grow failed");
340 return 0;
341 }
342 encoded = encodeInto(bitstream_buffer.data(), bitstream_buffer.size());
343 }
344 if (encoded == 0) {
345 LOGE("MPGEncoder: encode failed (status=%d)", (int)encoder_.lastStatus());
346 return 0;
347 }
348 if (p_out != nullptr) p_out->write(bitstream_buffer.data(), encoded);
349 if (p_out_video != nullptr) p_out_video->write(bitstream_buffer.data(), encoded);
350 return encoded;
351 }
352
354 if (width_ <= 0 || height_ <= 0) return false;
355 size_t needed = (size_t)width_ * (size_t)height_ * 2 + 4096;
356 if (bitstream_buffer.size() < needed) {
357 bitstream_buffer.resize(needed);
358 if (bitstream_buffer.data() == nullptr) {
359 return false;
360 }
361 }
362 return bitstream_buffer.data() != nullptr;
363 }
364};
365
366} // 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:93
void setOutput(Print &out) override
Defines the target each decoded picture is written to.
Definition CodecMPG.h:56
VideoInfo videoInfo() override
Definition CodecMPG.h:73
MPGDecoder()
Definition CodecMPG.h:50
VideoOutput * p_out_video
Definition CodecMPG.h:102
MPGDecoder(Print &out)
Definition CodecMPG.h:52
void end() override
Releases the decoder's resources.
Definition CodecMPG.h:86
size_t write(const uint8_t *data, size_t len) override
Definition CodecMPG.h:88
static void onFrame(tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > &decoder, void *userData)
Definition CodecMPG.h:106
void writeFrame(tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > &decoder)
Definition CodecMPG.h:111
void setOutput(VideoOutput &out)
Definition CodecMPG.h:57
Print * p_out
Definition CodecMPG.h:101
MPGDecoder(VideoOutput &out)
Definition CodecMPG.h:54
VideoFormat pixel_format
Definition CodecMPG.h:103
size_t writeToOutput(uint8_t *data, size_t len)
Definition CodecMPG.h:176
void setVideoFormat(VideoFormat format) override
Definition CodecMPG.h:59
tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > & driver()
Definition CodecMPG.h:97
bool begin() override
Initializes the decoder (allocates its picture buffers, etc).
Definition CodecMPG.h:81
tinympg::TinyMPGDecoder< tinympg::PSRAMAllocatorESP32< uint8_t > > decoder_
Definition CodecMPG.h:100
bool hasError()
Definition CodecMPG.h:95
Vector< uint8_t > frame_buffer
Definition CodecMPG.h:104
MPEG-1 part 2 video encoder wrapper around TinyMPGEncoder.
Definition CodecMPG.h:196
void setOutput(Print &out) override
Defines the target the encoded bitstream is written to.
Definition CodecMPG.h:212
VideoInfo videoInfo() override
Definition CodecMPG.h:230
VideoFormat input_format
Definition CodecMPG.h:327
MPGEncoder(VideoOutput &out)
Definition CodecMPG.h:210
void setSize(int width, int height)
Definition CodecMPG.h:238
VideoOutput * p_out_video
Definition CodecMPG.h:326
int height_
Definition CodecMPG.h:329
size_t writeOut(Fn encodeInto)
Definition CodecMPG.h:333
tinympg::TinyMPGEncoder< tinympg::PSRAMAllocatorESP32< uint8_t > > & driver()
Definition CodecMPG.h:319
void end() override
Releases the encoder's resources.
Definition CodecMPG.h:268
size_t write(const uint8_t *data, size_t len) override
Definition CodecMPG.h:270
static constexpr int kMaxGrowRetries
Definition CodecMPG.h:322
void setOutput(VideoOutput &out)
Definition CodecMPG.h:213
MPGEncoder(int width=0, int height=0)
Definition CodecMPG.h:198
Print * p_out
Definition CodecMPG.h:325
Vector< uint8_t > bitstream_buffer
Definition CodecMPG.h:330
void setVideoFormat(VideoFormat format) override
Definition CodecMPG.h:215
void setFrameRate(int fps)
Definition CodecMPG.h:247
tinympg::TinyMPGEncoder< tinympg::PSRAMAllocatorESP32< uint8_t > > encoder_
Definition CodecMPG.h:324
bool begin() override
Initializes the encoder (allocates its picture buffers, etc).
Definition CodecMPG.h:256
void setQp(int qscale)
Definition CodecMPG.h:245
int width_
Definition CodecMPG.h:328
void setBitstreamBufferSize(size_t bytes)
Definition CodecMPG.h:249
MPGEncoder(Print &out)
Definition CodecMPG.h:208
bool ensureBitstreamBuffer()
Definition CodecMPG.h:353
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:59
Definition Video.h:176
Abstract class for video playback. This class is used to assemble a complete video frame in memory....
Definition Video.h:192
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 Video.h:43
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 Video.h:102
uint16_t height
Frame height in pixels.
Definition Video.h:106
uint16_t width
Frame width in pixels.
Definition Video.h:104
VideoFormat format
Video codec - VideoFormat::UNKNOWN if not (yet) determined.
Definition Video.h:111