arduino-audio-tools
Loading...
Searching...
No Matches
H264RtpEncoder.h
Go to the documentation of this file.
1/*
2 * Author: Phil Schatzmann
3 *
4 * H.264 RTP Encoder for RTSP Video Streaming
5 * Implements RFC 6184 (packetization-mode=1: Single NAL Unit + FU-A)
6 */
7
8#pragma once
9
13#include "RTSPFormat.h"
14#include "RTSPFragmentQueue.h"
15#include "RTSPVideoEncoder.h"
16
17namespace audio_tools {
18
57 public:
58 H264RtpEncoder() = default;
59
65 H264RtpEncoder(size_t maxFragmentSize) : m_maxFragmentSize(maxFragmentSize) {}
66
68 void setMaxFragmentSize(size_t size) { m_maxFragmentSize = size; }
69
71 void setMaxQueuedFrames(size_t frames) { m_maxQueuedFrames = frames; }
72
77 void setFormat(RTSPFormat &format) override { p_format = &format; }
78
79 // -- IMediaSource --------------------------------------------------
80
81 RTSPFormat &getFormat() override {
82 return p_format != nullptr ? *p_format : default_format;
83 }
84
85 void start() override { begin(); }
86 void stop() override { end(); }
87
88 int packetSize() override { return queuePacketSize(); }
89
92 int readBytes(void *dest, int maxBytes) override {
93 return queueReadBytes(dest, maxBytes);
94 }
95
96 // -- AudioEncoder ----------------------------------------------------
97
98 bool begin() override {
99 LOGI("Starting H264 RTP Encoder");
100 m_isStarted = true;
101 m_sps.clear();
102 m_pps.clear();
103 clearQueue();
104 return true;
105 }
106
107 void end() override {
108 LOGI("Stopping H264 RTP Encoder");
109 m_isStarted = false;
110 clearQueue();
111 }
112
113 void setAudioInfo(AudioInfo info) override {
114 // Video encoder: dimensions/framerate come from the RTSPFormat, not
115 // AudioInfo
116 (void)info;
117 }
118
119 const char *mime() override { return "video/H264"; }
120
131 size_t write(const uint8_t *data, size_t len) override {
132 if (!m_isStarted || len == 0) return 0;
133
134 Vector<size_t> offsets;
135 Vector<size_t> lengths;
136 extractNALs(data, len, offsets, lengths);
137 if (offsets.empty()) {
138 LOGW("H264RtpEncoder: no NAL units found in %u byte buffer", (unsigned)len);
139 return len;
140 }
141
142 // Cache SPS/PPS whenever seen, detect IDR, and count the NAL units
143 // that will actually go out on the wire (SPS/PPS handled exclusively
144 // through the inject-before-IDR path below, so a frame that already
145 // carries its own SPS/PPS - as the first IDR frame from most encoders
146 // does - does not end up sending them a second time from this scan).
147 bool isIdr = false;
148 size_t nonParamNalCount = 0;
149 for (size_t i = 0; i < offsets.size(); i++) {
150 const uint8_t *nal = data + offsets[i];
151 uint8_t type = nal[0] & 0x1F;
152 if (type == 5) {
153 isIdr = true;
154 } else if (type == 7) {
155 cacheSps(nal, lengths[i]);
156 continue;
157 } else if (type == 8) {
158 cachePps(nal, lengths[i]);
159 continue;
160 }
161 ++nonParamNalCount;
162 }
163
164 bool injectParams = isIdr && !m_sps.empty() && !m_pps.empty();
165 size_t totalNals = nonParamNalCount + (injectParams ? 2 : 0);
166 size_t nalIndex = 0;
167
168 if (injectParams) {
169 emitNal(m_sps.data(), m_sps.size(), ++nalIndex == totalNals);
170 emitNal(m_pps.data(), m_pps.size(), ++nalIndex == totalNals);
171 }
172 for (size_t i = 0; i < offsets.size(); i++) {
173 const uint8_t *nal = data + offsets[i];
174 uint8_t type = nal[0] & 0x1F;
175 if (type == 7 || type == 8) continue; // already (re-)sent above
176 emitNal(nal, lengths[i], ++nalIndex == totalNals);
177 }
178
179 return len;
180 }
181
182 operator bool() override { return m_isStarted; }
183
184 protected:
186 RTSPFormatH264 default_format; // fallback so getFormat() is never null
187
188 bool m_isStarted = false;
189 size_t m_maxFragmentSize = 1400; // Typical MTU minus IP/UDP/RTP headers
190
193
194 // Reused scratch buffer for building one FU-A fragment at a time
196
202 size_t findStartCode(const uint8_t *data, size_t len, size_t from) {
203 for (size_t i = from; i + 2 < len; i++) {
204 if (data[i] == 0 && data[i + 1] == 0 &&
205 (data[i + 2] == 1 ||
206 (i + 3 < len && data[i + 2] == 0 && data[i + 3] == 1))) {
207 return i;
208 }
209 }
210 return len;
211 }
212
215 void extractNALs(const uint8_t *data, size_t len, Vector<size_t> &offsets,
216 Vector<size_t> &lengths) {
217 size_t i = 0;
218 while (i < len) {
219 size_t start = findStartCode(data, len, i);
220 if (start == len) break;
221
222 size_t scSize = (data[start + 2] == 1) ? 3 : 4;
223 size_t nalStart = start + scSize;
224 if (nalStart >= len) break;
225
226 size_t next = findStartCode(data, len, nalStart);
227 size_t nalEnd = next; // findStartCode already returns len if none found
228
229 size_t nalLen = nalEnd - nalStart;
230 if (nalLen > 0) {
231 offsets.push_back(nalStart);
232 lengths.push_back(nalLen);
233 }
234 i = nalEnd;
235 }
236 }
237
238 void cacheSps(const uint8_t *nal, size_t len) {
239 m_sps.resize(len);
240 memcpy(m_sps.data(), nal, len);
241 if (len >= 4 && p_format != nullptr) {
242 char hex[7];
243 snprintf(hex, sizeof(hex), "%02X%02X%02X", nal[1], nal[2], nal[3]);
245 }
247 }
248
249 void cachePps(const uint8_t *nal, size_t len) {
250 m_pps.resize(len);
251 memcpy(m_pps.data(), nal, len);
253 }
254
256 if (p_format == nullptr || m_sps.empty() || m_pps.empty()) return;
257 String spsB64 = base64Encode(m_sps.data(), m_sps.size());
258 String ppsB64 = base64Encode(m_pps.data(), m_pps.size());
259 p_format->setSpropParameterSets(spsB64.c_str(), ppsB64.c_str());
260 }
261
262 static String base64Encode(const uint8_t *data, size_t len) {
263 String out;
264 out.reserve(4 * ((len + 2) / 3));
265 size_t i = 0;
266 while (i < len) {
267 uint32_t octetA = i < len ? data[i++] : 0;
268 uint32_t octetB = i < len ? data[i++] : 0;
269 uint32_t octetC = i < len ? data[i++] : 0;
270 uint32_t triple = (octetA << 16) | (octetB << 8) | octetC;
271 out += encoding_table[(triple >> 18) & 0x3F];
272 out += encoding_table[(triple >> 12) & 0x3F];
273 out += encoding_table[(triple >> 6) & 0x3F];
274 out += encoding_table[triple & 0x3F];
275 }
276 int padding = mod_table[len % 3];
277 for (int p = 0; p < padding; p++) {
278 out.setCharAt(out.length() - 1 - p, '=');
279 }
280 return out;
281 }
282
290 void emitNal(const uint8_t *nal, size_t nalLen, bool isLastOfFrame) {
291 if (nalLen == 0) return;
292
293 if (nalLen <= m_maxFragmentSize) {
294 appendFragment(nal, nalLen, isLastOfFrame);
295 return;
296 }
297
298 // FU-A fragmentation
299 uint8_t nalHeader = nal[0];
300 uint8_t nalType = nalHeader & 0x1F;
301 uint8_t nri = nalHeader & 0x60;
302
303 if (m_maxFragmentSize <= 2) {
304 LOGE("H264RtpEncoder: maxFragmentSize too small for FU-A fragmentation");
305 return;
306 }
307 size_t maxPayload = m_maxFragmentSize - 2;
308
309 size_t pos = 1; // skip the original NAL header; FU-A carries its own
310 bool start = true;
311 while (pos < nalLen) {
312 size_t chunk = nalLen - pos;
313 if (chunk > maxPayload) chunk = maxPayload;
314 bool end = (pos + chunk >= nalLen);
315
316 m_scratch.resize(2 + chunk);
317 m_scratch[0] = nri | 28; // FU indicator: NRI + Type=28 (FU-A)
318 m_scratch[1] = (start ? 0x80 : 0) | (end ? 0x40 : 0) | nalType; // FU header
319 memcpy(m_scratch.data() + 2, nal + pos, chunk);
320
321 appendFragment(m_scratch.data(), m_scratch.size(), end && isLastOfFrame);
322
323 pos += chunk;
324 start = false;
325 }
326 }
327};
328
329} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
AudioInfo info
Definition AudioCodecsBase.h:129
H.264 RTP Encoder - Packetizes Annex-B H.264 access units per RFC 6184 for RTSP video streaming.
Definition H264RtpEncoder.h:56
void emitNal(const uint8_t *nal, size_t nalLen, bool isLastOfFrame)
Queue one NAL unit as a Single NAL Unit packet, or split it into FU-A fragments (RFC 6184 ยง5....
Definition H264RtpEncoder.h:290
bool m_isStarted
Definition H264RtpEncoder.h:188
RTSPFormat & getFormat() override
Get the media format configuration.
Definition H264RtpEncoder.h:81
void setFormat(RTSPFormat &format) override
Definition H264RtpEncoder.h:77
Vector< uint8_t > m_sps
Definition H264RtpEncoder.h:191
RTSPFormatH264 default_format
Definition H264RtpEncoder.h:186
size_t findStartCode(const uint8_t *data, size_t len, size_t from)
Locate the next Annex-B start code (00 00 01 or 00 00 00 01) at or after from.
Definition H264RtpEncoder.h:202
void updateSpropParameterSets()
Definition H264RtpEncoder.h:255
void extractNALs(const uint8_t *data, size_t len, Vector< size_t > &offsets, Vector< size_t > &lengths)
Definition H264RtpEncoder.h:215
void setMaxQueuedFrames(size_t frames)
Maximum number of complete frames buffered when the consumer falls behind.
Definition H264RtpEncoder.h:71
H264RtpEncoder(size_t maxFragmentSize)
Constructor with maximum fragment size.
Definition H264RtpEncoder.h:65
void start() override
Initialize media source for streaming.
Definition H264RtpEncoder.h:85
void end() override
Definition H264RtpEncoder.h:107
int packetSize() override
Size of the next queued, ready-to-send fragment, for sources that provide already RTP-payload-ready,...
Definition H264RtpEncoder.h:88
Vector< uint8_t > m_scratch
Definition H264RtpEncoder.h:195
size_t write(const uint8_t *data, size_t len) override
Process one complete Annex-B access unit (all NAL units of one encoded frame) and queue the resulting...
Definition H264RtpEncoder.h:131
void cacheSps(const uint8_t *nal, size_t len)
Definition H264RtpEncoder.h:238
void cachePps(const uint8_t *nal, size_t len)
Definition H264RtpEncoder.h:249
static String base64Encode(const uint8_t *data, size_t len)
Definition H264RtpEncoder.h:262
const char * mime() override
Provides the mime type of the encoded result.
Definition H264RtpEncoder.h:119
void stop() override
Cleanup media source after streaming.
Definition H264RtpEncoder.h:86
void setMaxFragmentSize(size_t size)
Set the maximum RTP payload size per packet.
Definition H264RtpEncoder.h:68
bool begin() override
Definition H264RtpEncoder.h:98
void setAudioInfo(AudioInfo info) override
Defines the sample rate, number of channels and bits per sample.
Definition H264RtpEncoder.h:113
Vector< uint8_t > m_pps
Definition H264RtpEncoder.h:192
int readBytes(void *dest, int maxBytes) override
Definition H264RtpEncoder.h:92
RTSPFormat * p_format
Definition H264RtpEncoder.h:185
size_t m_maxFragmentSize
Definition H264RtpEncoder.h:189
H.264 (AVC) format for RTSP video streaming, per RFC 6184.
Definition RTSPFormat.h:879
Audio Format Definition - Base class for RTSP audio formats.
Definition RTSPFormat.h:57
virtual void setSpropParameterSets(const char *, const char *)
Definition RTSPFormat.h:137
virtual void setProfileLevelId(const char *)
Definition RTSPFormat.h:143
Self-delimited fragment queue shared by packetized RTP video encoders (JPEGRtpEncoder,...
Definition RTSPFragmentQueue.h:36
void appendFragment(const uint8_t *payload, size_t len, bool last)
Definition RTSPFragmentQueue.h:60
void clearQueue()
Definition RTSPFragmentQueue.h:49
int queueReadBytes(void *dest, int maxBytes)
Definition RTSPFragmentQueue.h:102
int queuePacketSize()
Definition RTSPFragmentQueue.h:92
size_t m_maxQueuedFrames
Definition RTSPFragmentQueue.h:43
Common base for RTP video payloaders (JPEGRtpEncoder, H264RtpEncoder, ...): an AudioEncoder that acce...
Definition RTSPVideoEncoder.h:31
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
bool empty()
Definition Vector.h:180
void push_back(T &&value)
Definition Vector.h:182
void clear()
Definition Vector.h:176
bool resize(size_t newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
int size()
Definition Vector.h:178
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
static int mod_table[]
Definition CodecBase64.h:16
static char encoding_table[]
Definition CodecBase64.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:56