FFmpeg
rtpenc_av1.c
Go to the documentation of this file.
1 /*
2  * Packetization for RTP Payload Format For AV1 (v1.0)
3  * https://aomediacodec.github.io/av1-rtp-spec/
4  * Copyright (c) 2024 Axis Communications
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 /**
24  * @file
25  * @brief AV1 / RTP packetization code (RTP Payload Format For AV1 (v1.0))
26  * @author Chris Hodges <chris.hodges@axis.com>
27  * @note This will remove TDs and OBU size fields
28  */
29 
30 #include "avformat.h"
31 #include "rtpenc.h"
32 #include "libavcodec/av1.h"
33 #include "rtp_av1.h"
34 
35 // enable tracing of packet data
36 //#define RTPENC_AV1_VERBOSE_TRACE
37 
38 // enable searching for sequence header as workaround for AV1 encoders
39 // that do not set AV_PKT_FLAG_KEY correctly
40 #define RTPENC_AV1_SEARCH_SEQ_HEADER 1
41 
42 void ff_rtp_send_av1(AVFormatContext *ctx, const uint8_t *frame_buf, int frame_size, int is_keyframe) {
43  uint8_t aggr_hdr = 0;
44  int last_packet_of_frame = 0;
45  RTPMuxContext *rtp_ctx = ctx->priv_data;
46  const uint8_t *obu_ptr = frame_buf;
47  int start_new_packet = 0;
48  unsigned int num_obus = 0;
49  unsigned int rem_pkt_size = rtp_ctx->max_payload_size - 1;
50  uint8_t *pkt_ptr = NULL;
51 
52  const uint8_t *curr_obu_ptr = NULL;
53  uint32_t curr_elem_size = 0;
54  int curr_obu_hdr = -1;
55  int curr_obu_ext = -1;
56  const uint8_t *last_obu_ptr = NULL;
57  uint32_t last_elem_size = 0;
58  int last_obu_hdr = -1;
59  int last_obu_ext = -1;
60 
61  rtp_ctx->timestamp = rtp_ctx->cur_timestamp;
62 
63  /* The payload structure is supposed to be straight-forward, but there are a
64  * couple of edge cases to be tackled and make things very complex.
65  * These are mainly due to:
66  * - the OBU element size being optional for the last element, but MANDATORY
67  * if there are more than 3 elements
68  * - the size field of the element is made up of a variable number of
69  * LEB bytes
70  * - the latter in combination with the desire to fill the max packet size
71  * could cause a catch22
72  * - if there's less than 2 bytes remaining (depending on the required LEB),
73  * one would not have space for the payload of an element and must instead
74  * start the next packet
75  * - if there's less than 3 bytes remaining, the header byte plus the
76  * optional extension byte will not fit in the fragment making the
77  * handling even more complicated
78  * - as some OBU types are supposed to be filtered out, it is hard to decide
79  * via the remaining length whether the outputted OBU element will
80  * actually be the last one
81  *
82  * There are two major ways to tackle that: Pre-parsing of all OBUs within a
83  * frame (adds memory complexity) or lazy copying of the prior element.
84  * Here, the latter is implemented.
85  */
86 
87  if (is_keyframe) {
88 #if RTPENC_AV1_SEARCH_SEQ_HEADER
89  /* search for OBU_SEQUENCE_HEADER to get a better indication that
90  * the frame was marked as keyframe is really a KEY_FRAME and not
91  * a INTRA_ONLY frame. This might be unnecessary if the AV1 parser/
92  * encoder always correctly specifies AV_PKT_FLAG_KEY.
93  *
94  * Note: Spec does NOT prohibit resending bit-identical
95  * OBU_SEQUENCE_HEADER for ANY kind of frame, though!
96  */
97  int rem_size = frame_size;
98  const uint8_t *buf_ptr = frame_buf;
99  while (rem_size > 0) {
100  uint32_t obu_size;
101  uint8_t obu_hdr = *buf_ptr++;
102  uint8_t obu_type = (obu_hdr >> AV1S_OBU_TYPE) & AV1M_OBU_TYPE;
103  int num_lebs;
104 
105  if (obu_type == AV1_OBU_SEQUENCE_HEADER) {
106  av_log(ctx, AV_LOG_DEBUG, "Marking FIRST packet\n");
107  aggr_hdr |= AV1F_AGGR_HDR_FIRST_PKT;
108  break;
109  }
110  if (!(obu_hdr & AV1F_OBU_HAS_SIZE_FIELD)) {
111  break;
112  }
113  rem_size--;
114  // read out explicit OBU size
115  num_lebs = parse_leb(ctx, buf_ptr, rem_size, &obu_size);
116  if (!num_lebs) {
117  break;
118  }
119  buf_ptr += num_lebs + obu_size;
120  rem_size -= num_lebs + obu_size;
121  }
122 #else // RTPENC_AV1_SEARCH_SEQ_HEADER
123  av_log(ctx, AV_LOG_DEBUG, "Marking FIRST packet\n");
124  aggr_hdr |= AV1F_AGGR_HDR_FIRST_PKT;
125 #endif // RTPENC_AV1_SEARCH_SEQ_HEADER
126  }
127  rem_pkt_size = rtp_ctx->max_payload_size - 1;
128  pkt_ptr = rtp_ctx->buf + 1;
129 
130 #ifdef RTPENC_AV1_VERBOSE_TRACE
131  av_log(ctx, AV_LOG_TRACE, "AV1 Frame %d in (%x), size=%d:\n",
132  rtp_ctx->seq, rtp_ctx->flags, frame_size);
133  av_hex_dump_log(ctx, AV_LOG_TRACE, frame_buf, FFMIN(frame_size, 128));
134 #endif
135 
136  while (frame_size) {
137  uint32_t obu_size;
138  int num_lebs = 0;
139  int ext_byte = -1;
140 
141  uint8_t obu_hdr = *obu_ptr++;
142  uint8_t obu_type = (obu_hdr >> AV1S_OBU_TYPE) & AV1M_OBU_TYPE;
143  frame_size--;
144 
145  if (obu_hdr & AV1F_OBU_FORBIDDEN) {
146  av_log(ctx, AV_LOG_ERROR, "Forbidden bit set in AV1 OBU header (0x%02x)\n", obu_hdr);
147  return;
148  }
149 
150  if (obu_hdr & AV1F_OBU_EXTENSION_FLAG) {
151  if (!frame_size) {
152  av_log(ctx, AV_LOG_ERROR, "Out of data for AV1 OBU header extension byte\n");
153  return;
154  }
155  ext_byte = *obu_ptr++;
156  frame_size--;
157  }
158 
159  if (obu_hdr & AV1F_OBU_HAS_SIZE_FIELD) {
160  obu_hdr &= ~AV1F_OBU_HAS_SIZE_FIELD; // remove size field
161  // read out explicit OBU size
162  num_lebs = parse_leb(ctx, obu_ptr, frame_size, &obu_size);
163  if (!num_lebs) {
164  return;
165  }
166  obu_ptr += num_lebs;
167  frame_size -= num_lebs;
168  } else {
169  av_log(ctx, AV_LOG_ERROR, "Cannot handle AV1 OBUs without size fields\n");
170  return;
171  }
172 
173  if ((long) obu_size > frame_size) {
174  av_log(ctx, AV_LOG_ERROR, "AV1 OBU size %d larger than remaining frame size %d\n", obu_size, frame_size);
175  return;
176  }
177 
178  if (obu_size > 0xfffffffd) {
179  av_log(ctx, AV_LOG_ERROR, "AV1 OBU size 0x%x might overflow (attack?)\n", obu_size);
180  return;
181  }
182 
183  frame_size -= obu_size;
184 
185  if ((obu_type == AV1_OBU_TEMPORAL_DELIMITER) ||
186  (obu_type == AV1_OBU_TILE_LIST) ||
187  (obu_type == AV1_OBU_PADDING)) {
188  // ignore and remove according to spec (note that OBU_PADDING is not
189  // mentioned in spec, but it does not make sense to transmit it).
190  obu_ptr += obu_size;
191  // additional handling if the ignored OBU was the last one
192  if (!frame_size) {
193  // we're done, flush the last packet, set RTP marker bit
194  last_packet_of_frame = 1;
195  goto flush_last_packet;
196  }
197  continue;
198  }
199 
200  /* if the last OBU had a temporal or spatial ID, they need to match to
201  * current; otherwise start new packet */
202  if ((last_obu_ext >= 0) && (curr_obu_ext != last_obu_ext)) {
203  start_new_packet = 1;
204  }
205 
206 flush_last_packet:
207  last_obu_ptr = curr_obu_ptr;
208  last_elem_size = curr_elem_size;
209  last_obu_hdr = curr_obu_hdr;
210  last_obu_ext = curr_obu_ext;
211 
212  curr_obu_ptr = obu_ptr; // behind header
213  curr_elem_size = obu_size + 1 + ((ext_byte >= 0) ? 1 : 0);
214  curr_obu_hdr = obu_hdr;
215  curr_obu_ext = ext_byte;
216 
217  obu_ptr += obu_size;
218 
219  if (last_obu_ptr) {
220  unsigned int first_elem_with_size = last_elem_size + calc_leb_size(last_elem_size);
221  // check if last packet fits completely and has reasonable space for
222  // at least a fragment of the next
223  if (!last_packet_of_frame && (first_elem_with_size + 10 < rem_pkt_size)) {
224  num_lebs = write_leb(pkt_ptr, last_elem_size);
225  pkt_ptr += num_lebs;
226  rem_pkt_size -= num_lebs;
227  } else {
228  if ((num_obus >= 3) && (last_packet_of_frame || (first_elem_with_size <= rem_pkt_size))) {
229  // last fits with forced size, but nothing else
230  num_lebs = write_leb(pkt_ptr, last_elem_size);
231  pkt_ptr += num_lebs;
232  rem_pkt_size -= num_lebs;
233  }
234  // force new packet
235  start_new_packet = 1;
236  }
237 
238  // write header and optional extension byte (if not a continued fragment)
239  if (last_obu_hdr >= 0) {
240  *pkt_ptr++ = last_obu_hdr;
241  last_elem_size--;
242  rem_pkt_size--;
243  if (last_obu_ext >= 0) {
244  *pkt_ptr++ = last_obu_ext;
245  last_elem_size--;
246  rem_pkt_size--;
247  }
248  }
249  // copy payload
250  memcpy(pkt_ptr, last_obu_ptr, last_elem_size);
251  pkt_ptr += last_elem_size;
252  rem_pkt_size -= last_elem_size;
253  num_obus++;
254  }
255 
256  if (start_new_packet || last_packet_of_frame) {
257  if (num_obus < 4) {
258  aggr_hdr |= num_obus << AV1S_AGGR_HDR_NUM_OBUS;
259  }
260  rtp_ctx->buf[0] = aggr_hdr;
261 
262 #ifdef RTPENC_AV1_VERBOSE_TRACE
263  av_log(ctx, AV_LOG_TRACE, "Sending NON-FRAG packet no %d, %ld/%d, %d OBUs (marker=%d)\n",
264  ((RTPMuxContext *) ctx->priv_data)->seq,
265  pkt_ptr - rtp_ctx->buf, rtp_ctx->max_payload_size, num_obus, last_packet_of_frame);
266  av_hex_dump_log(ctx, AV_LOG_TRACE, rtp_ctx->buf, FFMIN(pkt_ptr - rtp_ctx->buf, 64));
267  av_log(ctx, AV_LOG_TRACE, "... end at offset %lx:\n", FFMAX((pkt_ptr - rtp_ctx->buf) - 64, 0));
268  av_hex_dump_log(ctx, AV_LOG_TRACE, rtp_ctx->buf + FFMAX((pkt_ptr - rtp_ctx->buf) - 64, 0), FFMIN(pkt_ptr - rtp_ctx->buf, 64));
269 #endif
270 
271  ff_rtp_send_data(ctx, rtp_ctx->buf, pkt_ptr - rtp_ctx->buf, last_packet_of_frame);
272 
273  rem_pkt_size = rtp_ctx->max_payload_size - 1;
274  pkt_ptr = rtp_ctx->buf + 1;
275  aggr_hdr = 0;
276  num_obus = 0;
277  }
278 
279  if (last_packet_of_frame) {
280  break;
281  }
282 
283  // check if element needs to be fragmented, otherwise we will deal with
284  // it in the next iteration
285  if ((curr_elem_size > rem_pkt_size) ||
286  ((num_obus >= 3) && (curr_elem_size + calc_leb_size(curr_elem_size)) > rem_pkt_size)) {
287  uint32_t frag_size = rem_pkt_size;
288 
289  // if there are going more than 3 OBU elements, we are obliged to
290  // have the length field for the last
291  if (num_obus >= 3) {
292  // that's an upper limit of LEBs
293  num_lebs = calc_leb_size(rem_pkt_size - 1);
294  frag_size -= num_lebs;
295 
296  // write a fixed number of LEBs, in case the frag_size could
297  // now be specified with one less byte
298  write_leb_n(pkt_ptr, frag_size, num_lebs);
299  pkt_ptr += num_lebs;
300  rem_pkt_size -= num_lebs;
301  }
302 
303  // write header and optional extension byte
304  *pkt_ptr++ = curr_obu_hdr;
305  curr_elem_size--;
306  rem_pkt_size--;
307  if (curr_obu_ext >= 0) {
308  *pkt_ptr++ = curr_obu_ext;
309  curr_elem_size--;
310  rem_pkt_size--;
311  }
312 
313  // disable header writing for final fragment
314  curr_obu_hdr = -1;
315  curr_obu_ext = -1;
316 
317  // send more full packet sized fragments
318  do {
319  // copy payload
320  memcpy(pkt_ptr, curr_obu_ptr, rem_pkt_size);
321  pkt_ptr += rem_pkt_size;
322  curr_obu_ptr += rem_pkt_size;
323  curr_elem_size -= rem_pkt_size;
324  num_obus++;
325 
326  aggr_hdr |= AV1F_AGGR_HDR_LAST_FRAG;
327  if (num_obus < 4) {
328  aggr_hdr |= num_obus << AV1S_AGGR_HDR_NUM_OBUS;
329  }
330  rtp_ctx->buf[0] = aggr_hdr;
331 
332 #ifdef RTPENC_AV1_VERBOSE_TRACE
333  av_log(ctx, AV_LOG_DEBUG, "Sending FRAG packet no %d, %ld/%d, %d OBUs\n",
334  ((RTPMuxContext *) ctx->priv_data)->seq,
335  pkt_ptr - rtp_ctx->buf, rtp_ctx->max_payload_size, num_obus);
336  av_hex_dump_log(ctx, AV_LOG_TRACE, rtp_ctx->buf, FFMIN(pkt_ptr - rtp_ctx->buf, 64));
337  av_log(ctx, AV_LOG_TRACE, "... end at offset %lx:\n", FFMAX((pkt_ptr - rtp_ctx->buf) - 64, 0));
338  av_hex_dump_log(ctx, AV_LOG_TRACE, rtp_ctx->buf + FFMAX((pkt_ptr - rtp_ctx->buf) - 64, 0), FFMIN(pkt_ptr - rtp_ctx->buf, 64));
339 #endif
340 
341  ff_rtp_send_data(ctx, rtp_ctx->buf, pkt_ptr - rtp_ctx->buf, 0);
342  rem_pkt_size = rtp_ctx->max_payload_size - 1;
343  pkt_ptr = rtp_ctx->buf + 1;
344 
345  aggr_hdr = AV1F_AGGR_HDR_FRAG_CONT;
346  num_obus = 0;
347  } while (curr_elem_size > rem_pkt_size);
348  start_new_packet = 0;
349  }
350 
351  if (!frame_size) {
352  // we're done, flush the last packet, set RTP marker bit
353  last_packet_of_frame = 1;
354  goto flush_last_packet;
355  }
356  }
357 }
write_leb_n
static void write_leb_n(uint8_t *lebptr, uint32_t length, unsigned int num_lebs)
write out fixed number of LEB bytes (may have "unused" bytes)
Definition: rtp_av1.h:83
RTPMuxContext::seq
int seq
Definition: rtpenc.h:34
ff_rtp_send_data
void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m)
Definition: rtpenc.c:347
AV1S_OBU_TYPE
#define AV1S_OBU_TYPE
Definition: rtp_av1.h:39
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
AV1_OBU_TEMPORAL_DELIMITER
@ AV1_OBU_TEMPORAL_DELIMITER
Definition: av1.h:31
write_leb
static unsigned int write_leb(uint8_t *lebptr, uint32_t length)
write out variable number of LEB bytes for the given length
Definition: rtp_av1.h:68
AV1F_AGGR_HDR_FIRST_PKT
#define AV1F_AGGR_HDR_FIRST_PKT
Definition: rtp_av1.h:55
AV1F_OBU_FORBIDDEN
#define AV1F_OBU_FORBIDDEN
Definition: rtp_av1.h:38
AV_LOG_TRACE
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:235
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:209
AV1F_OBU_HAS_SIZE_FIELD
#define AV1F_OBU_HAS_SIZE_FIELD
Definition: rtp_av1.h:44
frame_size
int frame_size
Definition: mxfenc.c:2446
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:230
ctx
AVFormatContext * ctx
Definition: movenc.c:49
RTPMuxContext::flags
int flags
Definition: rtpenc.h:61
AV1F_OBU_EXTENSION_FLAG
#define AV1F_OBU_EXTENSION_FLAG
Definition: rtp_av1.h:42
RTPMuxContext::cur_timestamp
uint32_t cur_timestamp
Definition: rtpenc.h:37
AVFormatContext
Format I/O context.
Definition: avformat.h:1300
NULL
#define NULL
Definition: coverity.c:32
ff_rtp_send_av1
void ff_rtp_send_av1(AVFormatContext *ctx, const uint8_t *frame_buf, int frame_size, int is_keyframe)
Definition: rtpenc_av1.c:42
RTPMuxContext
Definition: rtpenc.h:27
RTPMuxContext::buf
uint8_t * buf
Definition: rtpenc.h:49
RTPMuxContext::timestamp
uint32_t timestamp
Definition: rtpenc.h:35
AV1_OBU_SEQUENCE_HEADER
@ AV1_OBU_SEQUENCE_HEADER
Definition: av1.h:30
av1.h
AV1_OBU_PADDING
@ AV1_OBU_PADDING
Definition: av1.h:39
calc_leb_size
static unsigned int calc_leb_size(uint32_t length)
calculate number of required LEB bytes for the given length
Definition: rtp_av1.h:58
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
AV1M_OBU_TYPE
#define AV1M_OBU_TYPE
Definition: rtp_av1.h:40
parse_leb
static unsigned int parse_leb(void *logctx, const uint8_t *buf_ptr, uint32_t buffer_size, uint32_t *obu_size)
securely parse LEB bytes and return the resulting encoded length
Definition: rtp_av1.h:95
rtpenc.h
AV1_OBU_TILE_LIST
@ AV1_OBU_TILE_LIST
Definition: av1.h:37
av_hex_dump_log
void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size)
Send a nice hexadecimal dump of a buffer to the log.
Definition: dump.c:87
avformat.h
AV1F_AGGR_HDR_FRAG_CONT
#define AV1F_AGGR_HDR_FRAG_CONT
Definition: rtp_av1.h:49
rtp_av1.h
shared defines and functions for AV1 RTP dec/enc
RTPMuxContext::max_payload_size
int max_payload_size
Definition: rtpenc.h:38
RTPMuxContext::buf_ptr
uint8_t * buf_ptr
Definition: rtpenc.h:50
AV1F_AGGR_HDR_LAST_FRAG
#define AV1F_AGGR_HDR_LAST_FRAG
Definition: rtp_av1.h:51
AV1S_AGGR_HDR_NUM_OBUS
#define AV1S_AGGR_HDR_NUM_OBUS
Definition: rtp_av1.h:52
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVFormatContext::priv_data
void * priv_data
Format private data.
Definition: avformat.h:1328
is_keyframe
static int is_keyframe(NalUnitType naltype)
Definition: libx265.c:94