arduino-audio-tools
Loading...
Searching...
No Matches
CodecH264.h
Go to the documentation of this file.
1#pragma once
2
5#include "TinyH264Decoder.h"
6#include "TinyH264Encoder.h"
7
20#ifndef H264_DEFAULT_ALLOCATOR
21#ifdef ESP32
22#define H264_DEFAULT_ALLOCATOR tinyh264::PSRAMAllocatorESP32<uint8_t>
23#else
24#define H264_DEFAULT_ALLOCATOR tinyh264::StdAllocator<uint8_t>
25#endif
26#endif
27
28namespace audio_tools {
29
59 public:
60 H264Decoder() { decoder_.setCallback(onFrame, this); }
61
63
65
67
76 bool isValid(const uint8_t *data, size_t len) override {
77 for (size_t i = 0; i + 3 < len; i++) {
78 if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
79 uint8_t header = data[i + 3];
80 if ((header & 0x80) == 0) { // forbidden_zero_bit must be 0
81 uint8_t nal_type = header & 0x1F;
82 if (nal_type >= 1 && nal_type <= 23) return true;
83 }
84 i += 2;
85 }
86 }
87 return false;
88 }
89
93 void setOutput(Print &out) override { p_out = &out; }
94 void setOutput(VideoOutput &out) override { p_out_video = &out; }
95
96
103 void setVideoFormat(VideoFormat format) override {
104 switch (format) {
109 pixel_format = format;
110 break;
111 default:
112 LOGW("H264Decoder: unsupported VideoFormat %d", (int)format);
113 break;
114 }
115 }
116
121 VideoInfo videoInfo() override {
122 VideoInfo info;
123 info.format = pixel_format;
124 info.width = (uint16_t)decoder_.width();
125 info.height = (uint16_t)decoder_.height();
126 return info;
127 }
128
131 void setMaxRefFrames(int n) { decoder_.setMaxRefFrames(n); }
132
135 bool begin() override {
136 decoder_.begin();
137 return true;
138 }
139
141 void end() override { decoder_.end(); }
142
147 size_t write(const uint8_t *data, size_t len) override {
148 uint32_t decodeStart = millis();
149 uint32_t outputMsBefore = total_output_ms_;
150 decoder_.write(data, len);
151 uint32_t outputMsDuringThisCall = total_output_ms_ - outputMsBefore;
152 total_decode_ms_ += (millis() - decodeStart) - outputMsDuringThisCall;
153 return len;
154 }
155
158 bool hasError() { return decoder_.hasError(); }
159
161 bool isKeyFrame(const uint8_t *data, size_t len) override {
162 return isH264KeyFrame(data, len);
163 }
164
175 uint64_t totalDecodeMs() const override { return total_decode_ms_; }
176
179 tinyh264::TinyH264Decoder<H264_DEFAULT_ALLOCATOR> &driver() { return decoder_; }
180
181 protected:
182 uint64_t total_decode_ms_ = 0;
183 // Accumulated time spent inside writeToOutput() (pixel conversion +
184 // push to the configured output) since begin() - see totalDecodeMs()'s
185 // own comment. write() reads the delta across its own call and
186 // subtracts it, so total_decode_ms_ stays decode-only regardless of
187 // how expensive the configured output's own write() turns out to be.
188 uint32_t total_output_ms_ = 0;
189
190 tinyh264::TinyH264Decoder<H264_DEFAULT_ALLOCATOR> decoder_;
191 Print *p_out = nullptr;
195
196 static void onFrame(tinyh264::TinyH264Decoder<H264_DEFAULT_ALLOCATOR> &decoder,
197 void *userData) {
198 static_cast<H264Decoder *>(userData)->writeToOutput();
199 }
200
202 if (p_out == nullptr && p_out_video == nullptr) return;
203 // Every decoded picture is always shown - see PacedVideoOutput
204 // (Video/PacedVideoOutput.h) for how pacing/scheduling actually
205 // happens.
206 // Timed separately from decode (see total_output_ms_'s own comment) -
207 // covers both the pixel-format conversion below and the push to
208 // setOutput()'s target, i.e. render, if that target is a real
209 // display.
210 uint32_t outputStart = millis();
211 size_t w = decoder_.width();
212 size_t h = decoder_.height();
213 size_t n = 0;
214 switch (pixel_format) {
215 case VideoFormat::RGB565: {
216 size_t needed = w * h;
217 if (frame_buffer.size() < needed * 2) {
218 frame_buffer.resize(needed * 2);
219 if (frame_buffer.data() == nullptr) {
220 LOGE("H264Decoder: frame buffer allocation failed (RGB565)");
221 break;
222 }
223 }
224 n = decoder_.toRGB565((uint16_t *)frame_buffer.data(), needed);
225 if (n > 0) writeToOutput(frame_buffer.data(), n * 2);
226 break;
227 }
228 case VideoFormat::RGB666: {
229 size_t needed = w * h * 3;
230 if (frame_buffer.size() < needed) {
231 frame_buffer.resize(needed);
232 if (frame_buffer.data() == nullptr) {
233 LOGE("H264Decoder: frame buffer allocation failed (RGB666)");
234 break;
235 }
236 }
237 n = decoder_.toRGB666(frame_buffer.data(), needed);
238 if (n > 0) writeToOutput(frame_buffer.data(), n);
239 break;
240 }
241 case VideoFormat::RGB888: {
242 size_t needed = w * h * 3;
243 if (frame_buffer.size() < needed) {
244 frame_buffer.resize(needed);
245 if (frame_buffer.data() == nullptr) {
246 LOGE("H264Decoder: frame buffer allocation failed (RGB888)");
247 break;
248 }
249 }
250 n = decoder_.toRGB888(frame_buffer.data(), needed);
251 if (n > 0) writeToOutput(frame_buffer.data(), n);
252 break;
253 }
254 case VideoFormat::I420: {
255 size_t needed = w * h + 2 * (w / 2) * (h / 2);
256 if (frame_buffer.size() < needed) {
257 frame_buffer.resize(needed);
258 if (frame_buffer.data() == nullptr) {
259 LOGE("H264Decoder: frame buffer allocation failed (I420)");
260 break;
261 }
262 }
263 n = decoder_.toYUV420(frame_buffer.data(), needed);
264 if (n > 0) writeToOutput(frame_buffer.data(), n);
265 break;
266 }
267 default:
268 break;
269 }
270 total_output_ms_ += millis() - outputStart;
271 }
272
273 size_t writeToOutput(uint8_t *data, size_t len) {
274 size_t result = 0;
275 if (p_out != nullptr) result = p_out->write((const uint8_t *)data, len);
276 if (p_out_video != nullptr) result += p_out_video->write((const uint8_t *)data, len);
277 return result;
278 }
279};
280
306class H264Encoder : public VideoEncoder {
307 public:
310 static constexpr size_t kDefaultBitstreamBufferSize = 16384;
311
312 H264Encoder(int width = 0, int height = 0, int keyframeInterval = 0)
313 : encoder_(width, height, keyframeInterval) {
314 width_ = width;
315 height_ = height;
316 }
317
319 setOutput(out);
320 }
321
323 setOutput(out);
324 }
325
328 void setOutput(Print &out) override { p_out = &out; }
329 void setOutput(VideoOutput &out) { p_out_video = &out; }
330
338 void setVideoFormat(VideoFormat format) override {
339 switch (format) {
345 input_format = format;
346 break;
347 default:
348 LOGW("H264Encoder: unsupported VideoFormat %d", (int)format);
349 break;
350 }
351 }
352
357 VideoInfo videoInfo() override {
358 VideoInfo info;
360 info.width = (uint16_t)width_;
361 info.height = (uint16_t)height_;
362 return info;
363 }
364
366 void setSize(int width, int height) {
367 width_ = width;
368 height_ = height;
369 encoder_.setSize(width, height);
370 }
372 void setQp(int qp) { encoder_.setQp(qp); }
374 void setTargetBitrate(int bitsPerSecond, double fps) {
375 encoder_.setTargetBitrate(bitsPerSecond, fps);
376 }
378 void setKeyframeInterval(int frames) {
379 encoder_.setKeyframeInterval(frames);
380 }
382 void setStride(int strideY, int strideC) {
383 encoder_.setStride(strideY, strideC);
384 }
386 void setPackedStride(int stride) { encoder_.setPackedStride(stride); }
387
393 void setBitstreamBufferSize(size_t size) {
394 if (size > bitstream.size()) {
395 bitstream.resize(size);
396 if (bitstream.data() == nullptr) {
397 LOGE("H264Encoder: bitstream buffer allocation failed");
398 }
399 }
400 }
401
405 bool begin() override { return begin(false); }
407 bool begin(bool reserveColorConversionScratch) {
408 encoder_.begin(reserveColorConversionScratch);
409 return true;
410 }
412 void end() override { encoder_.end(); }
413
415 int lastQp() const { return encoder_.lastQp(); }
416
426 size_t write(const uint8_t *data, size_t len) override {
427 switch (input_format) {
428 case VideoFormat::I420: {
429 size_t ySize = (size_t)width_ * height_;
430 size_t cSize = (size_t)(width_ / 2) * (height_ / 2);
431 if (len < ySize + 2 * cSize) return 0;
432 const uint8_t *srcY = data;
433 const uint8_t *srcU = data + ySize;
434 const uint8_t *srcV = data + ySize + cSize;
435 return writeOut([&](uint8_t *dst, size_t cap) {
436 return encoder_.encodeFrame(srcY, srcU, srcV, dst, cap);
437 });
438 }
440 return writeOut([&](uint8_t *dst, size_t cap) {
441 return encoder_.encodeFrameYuv422(data, dst, cap);
442 });
444 return writeOut([&](uint8_t *dst, size_t cap) {
445 return encoder_.encodeFrameRgb565((const uint16_t *)data, dst, cap);
446 });
448 return writeOut([&](uint8_t *dst, size_t cap) {
449 return encoder_.encodeFrameRgb666(data, dst, cap);
450 });
452 return writeOut([&](uint8_t *dst, size_t cap) {
453 return encoder_.encodeFrameRgb888(data, dst, cap);
454 });
455 default:
456 return 0;
457 }
458 }
459
462 tinyh264::TinyH264Encoder<H264_DEFAULT_ALLOCATOR> &driver() { return encoder_; }
463
464 protected:
467 static constexpr int kMaxGrowRetries = 4;
468
469 tinyh264::TinyH264Encoder<H264_DEFAULT_ALLOCATOR> encoder_;
470 Print *p_out = nullptr;
474 int width_ = 0;
475 int height_ = 0;
476
477 template <typename Fn>
478 size_t writeOut(Fn encodeInto) {
479 if (p_out == nullptr && p_out_video == nullptr) return 0;
480 if (bitstream.size() == 0) {
482 if (bitstream.data() == nullptr) {
483 LOGE("H264Encoder: bitstream buffer allocation failed");
484 return 0;
485 }
486 }
487 size_t n = encodeInto(bitstream.data(), bitstream.size());
488 for (int attempt = 0; n == 0 && attempt < kMaxGrowRetries; ++attempt) {
490 if (bitstream.data() == nullptr) {
491 LOGE("H264Encoder: bitstream buffer grow failed");
492 return 0;
493 }
494 n = encodeInto(bitstream.data(), bitstream.size());
495 }
496 if (p_out !=nullptr && n > 0) p_out->write(bitstream.data(), n);
497 if (p_out_video !=nullptr && n > 0) p_out_video->write(bitstream.data(), n);
498 return n;
499 }
500};
501
502} // 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
H.264 video decoder: wraps TinyH264Decoder (https://github.com/pschatzmann/TinyH264) as a VideoOutput...
Definition CodecH264.h:58
void setOutput(Print &out) override
Definition CodecH264.h:93
uint64_t totalDecodeMs() const override
Definition CodecH264.h:175
VideoInfo videoInfo() override
Definition CodecH264.h:121
bool isValid(const uint8_t *data, size_t len) override
Definition CodecH264.h:76
VideoOutput * p_out_video
Definition CodecH264.h:192
tinyh264::TinyH264Decoder< tinyh264::PSRAMAllocatorESP32< uint8_t > > & driver()
Definition CodecH264.h:179
void writeToOutput()
Definition CodecH264.h:201
void end() override
Releases the decoder's picture buffers - see TinyH264Decoder::end().
Definition CodecH264.h:141
H264Decoder()
Definition CodecH264.h:60
size_t write(const uint8_t *data, size_t len) override
Definition CodecH264.h:147
VideoFormat codecFormat() override
Definition CodecH264.h:66
H264Decoder(VideoOutput &out)
Definition CodecH264.h:64
uint32_t total_output_ms_
Definition CodecH264.h:188
Print * p_out
Definition CodecH264.h:191
VideoFormat pixel_format
Definition CodecH264.h:193
size_t writeToOutput(uint8_t *data, size_t len)
Definition CodecH264.h:273
void setVideoFormat(VideoFormat format) override
Definition CodecH264.h:103
void setMaxRefFrames(int n)
Definition CodecH264.h:131
uint64_t total_decode_ms_
Definition CodecH264.h:182
bool isKeyFrame(const uint8_t *data, size_t len) override
See VideoOutput::isKeyFrame() - scans for an Annex-B IDR slice NAL.
Definition CodecH264.h:161
tinyh264::TinyH264Decoder< tinyh264::PSRAMAllocatorESP32< uint8_t > > decoder_
Definition CodecH264.h:190
H264Decoder(Print &out)
Definition CodecH264.h:62
bool begin() override
Definition CodecH264.h:135
void setOutput(VideoOutput &out) override
Definition CodecH264.h:94
bool hasError()
Definition CodecH264.h:158
Vector< uint8_t > frame_buffer
Definition CodecH264.h:194
static void onFrame(tinyh264::TinyH264Decoder< tinyh264::PSRAMAllocatorESP32< uint8_t > > &decoder, void *userData)
Definition CodecH264.h:196
H.264 video encoder: wraps TinyH264Encoder (https://github.com/pschatzmann/TinyH264)....
Definition CodecH264.h:306
tinyh264::TinyH264Encoder< tinyh264::PSRAMAllocatorESP32< uint8_t > > & driver()
Definition CodecH264.h:462
void setOutput(Print &out) override
Definition CodecH264.h:328
VideoInfo videoInfo() override
Definition CodecH264.h:357
Vector< uint8_t > bitstream
Definition CodecH264.h:472
void setBitstreamBufferSize(size_t size)
Definition CodecH264.h:393
H264Encoder(VideoOutput &out)
Definition CodecH264.h:322
VideoFormat input_format
Definition CodecH264.h:473
H264Encoder(int width=0, int height=0, int keyframeInterval=0)
Definition CodecH264.h:312
void setPackedStride(int stride)
See TinyH264Encoder::setPackedStride()
Definition CodecH264.h:386
bool begin(bool reserveColorConversionScratch)
See TinyH264Encoder::begin()
Definition CodecH264.h:407
void setSize(int width, int height)
See TinyH264Encoder::setSize()
Definition CodecH264.h:366
VideoOutput * p_out_video
Definition CodecH264.h:471
int height_
Definition CodecH264.h:475
H264Encoder(Print &out)
Definition CodecH264.h:318
size_t writeOut(Fn encodeInto)
Definition CodecH264.h:478
tinyh264::TinyH264Encoder< tinyh264::PSRAMAllocatorESP32< uint8_t > > encoder_
Definition CodecH264.h:469
void end() override
See TinyH264Encoder::end()
Definition CodecH264.h:412
size_t write(const uint8_t *data, size_t len) override
Definition CodecH264.h:426
void setTargetBitrate(int bitsPerSecond, double fps)
See TinyH264Encoder::setTargetBitrate()
Definition CodecH264.h:374
static constexpr size_t kDefaultBitstreamBufferSize
Definition CodecH264.h:310
static constexpr int kMaxGrowRetries
Definition CodecH264.h:467
void setQp(int qp)
See TinyH264Encoder::setQp()
Definition CodecH264.h:372
void setOutput(VideoOutput &out)
Definition CodecH264.h:329
Print * p_out
Definition CodecH264.h:470
void setVideoFormat(VideoFormat format) override
Definition CodecH264.h:338
int lastQp() const
The QP actually used by the most recent write() call.
Definition CodecH264.h:415
bool begin() override
Definition CodecH264.h:405
void setKeyframeInterval(int frames)
See TinyH264Encoder::setKeyframeInterval()
Definition CodecH264.h:378
int width_
Definition CodecH264.h:474
void setStride(int strideY, int strideC)
See TinyH264Encoder::setStride()
Definition CodecH264.h:382
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 isH264KeyFrame(const uint8_t *data, size_t len)
True if the given Annex-B H.264 access unit contains an IDR slice NAL unit (nal_unit_type 5) - the re...
Definition Video.h:52
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