1 /* GStreamer plugin for forward error correction
2 * Copyright (C) 2017 Pexip
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author: Mikhail Fludkov <misha@pexip.com>
19 */
20
21 #include <string.h>
22 #include "rtpulpfeccommon.h"
23
24 #define MIN_RTP_HEADER_LEN 12
25
26 typedef struct
27 {
28 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
29 unsigned int csrc_count:4; /* CSRC count */
30 unsigned int extension:1; /* header extension flag */
31 unsigned int padding:1; /* padding flag */
32 unsigned int version:2; /* protocol version */
33 unsigned int payload_type:7; /* payload type */
34 unsigned int marker:1; /* marker bit */
35 #elif G_BYTE_ORDER == G_BIG_ENDIAN
36 unsigned int version:2; /* protocol version */
37 unsigned int padding:1; /* padding flag */
38 unsigned int extension:1; /* header extension flag */
39 unsigned int csrc_count:4; /* CSRC count */
40 unsigned int marker:1; /* marker bit */
41 unsigned int payload_type:7; /* payload type */
42 #else
43 #error "G_BYTE_ORDER should be big or little endian."
44 #endif
45 unsigned int seq:16; /* sequence number */
46 unsigned int timestamp:32; /* timestamp */
47 unsigned int ssrc:32; /* synchronization source */
48 guint8 csrclist[4]; /* optional CSRC list, 32 bits each */
49 } RtpHeader;
50
51 static gsize
fec_level_hdr_get_size(gboolean l_bit)52 fec_level_hdr_get_size (gboolean l_bit)
53 {
54 return sizeof (RtpUlpFecLevelHeader) - (l_bit ? 0 : 4);
55 }
56
57 static guint64
fec_level_hdr_get_mask(RtpUlpFecLevelHeader const * fec_lvl_hdr,gboolean l_bit)58 fec_level_hdr_get_mask (RtpUlpFecLevelHeader const *fec_lvl_hdr, gboolean l_bit)
59 {
60 return ((guint64) g_ntohs (fec_lvl_hdr->mask) << 32) |
61 (l_bit ? g_ntohl (fec_lvl_hdr->mask_continued) : 0);
62 }
63
64 static void
fec_level_hdr_set_mask(RtpUlpFecLevelHeader * fec_lvl_hdr,gboolean l_bit,guint64 mask)65 fec_level_hdr_set_mask (RtpUlpFecLevelHeader * fec_lvl_hdr, gboolean l_bit,
66 guint64 mask)
67 {
68 fec_lvl_hdr->mask = g_htons (mask >> 32);
69 if (l_bit)
70 fec_lvl_hdr->mask_continued = g_htonl (mask);
71 }
72
73 static guint16
fec_level_hdr_get_protection_len(RtpUlpFecLevelHeader * fec_lvl_hdr)74 fec_level_hdr_get_protection_len (RtpUlpFecLevelHeader * fec_lvl_hdr)
75 {
76 return g_ntohs (fec_lvl_hdr->protection_len);
77 }
78
79 static void
fec_level_hdr_set_protection_len(RtpUlpFecLevelHeader * fec_lvl_hdr,guint16 len)80 fec_level_hdr_set_protection_len (RtpUlpFecLevelHeader * fec_lvl_hdr,
81 guint16 len)
82 {
83 fec_lvl_hdr->protection_len = g_htons (len);
84 }
85
86 static RtpUlpFecLevelHeader *
fec_hdr_get_level_hdr(RtpUlpFecHeader const * fec_hdr)87 fec_hdr_get_level_hdr (RtpUlpFecHeader const *fec_hdr)
88 {
89 return (RtpUlpFecLevelHeader *) (fec_hdr + 1);
90 }
91
92 static guint64
fec_hdr_get_mask(RtpUlpFecHeader const * fec_hdr)93 fec_hdr_get_mask (RtpUlpFecHeader const *fec_hdr)
94 {
95 return fec_level_hdr_get_mask (fec_hdr_get_level_hdr (fec_hdr), fec_hdr->L);
96 }
97
98 static guint16
fec_hdr_get_seq_base(RtpUlpFecHeader const * fec_hdr,gboolean is_ulpfec,guint16 fec_seq)99 fec_hdr_get_seq_base (RtpUlpFecHeader const *fec_hdr, gboolean is_ulpfec,
100 guint16 fec_seq)
101 {
102 guint16 seq = g_ntohs (fec_hdr->seq);
103 if (is_ulpfec)
104 return seq;
105 return fec_seq - seq;
106 }
107
108 static guint16
fec_hdr_get_packets_len_recovery(RtpUlpFecHeader const * fec_hdr)109 fec_hdr_get_packets_len_recovery (RtpUlpFecHeader const *fec_hdr)
110 {
111 return g_htons (fec_hdr->len);
112 }
113
114 static guint32
fec_hdr_get_timestamp_recovery(RtpUlpFecHeader const * fec_hdr)115 fec_hdr_get_timestamp_recovery (RtpUlpFecHeader const *fec_hdr)
116 {
117 return g_ntohl (fec_hdr->timestamp);
118 }
119
120 static void
_xor_mem(guint8 * restrict dst,const guint8 * restrict src,gsize length)121 _xor_mem (guint8 * restrict dst, const guint8 * restrict src, gsize length)
122 {
123 guint i;
124
125 for (i = 0; i < (length / sizeof (guint64)); ++i) {
126 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
127 GST_WRITE_UINT64_LE (dst,
128 GST_READ_UINT64_LE (dst) ^ GST_READ_UINT64_LE (src));
129 #else
130 GST_WRITE_UINT64_BE (dst,
131 GST_READ_UINT64_BE (dst) ^ GST_READ_UINT64_BE (src));
132 #endif
133 dst += sizeof (guint64);
134 src += sizeof (guint64);
135 }
136 for (i = 0; i < (length % sizeof (guint64)); ++i)
137 dst[i] ^= src[i];
138 }
139
140 guint16
rtp_ulpfec_hdr_get_protection_len(RtpUlpFecHeader const * fec_hdr)141 rtp_ulpfec_hdr_get_protection_len (RtpUlpFecHeader const *fec_hdr)
142 {
143 return fec_level_hdr_get_protection_len (fec_hdr_get_level_hdr (fec_hdr));
144 }
145
146 RtpUlpFecHeader *
rtp_ulpfec_buffer_get_fechdr(GstRTPBuffer * rtp)147 rtp_ulpfec_buffer_get_fechdr (GstRTPBuffer * rtp)
148 {
149 return (RtpUlpFecHeader *) gst_rtp_buffer_get_payload (rtp);
150 }
151
152 guint64
rtp_ulpfec_buffer_get_mask(GstRTPBuffer * rtp)153 rtp_ulpfec_buffer_get_mask (GstRTPBuffer * rtp)
154 {
155 return fec_hdr_get_mask (rtp_ulpfec_buffer_get_fechdr (rtp));
156 }
157
158 guint16
rtp_ulpfec_buffer_get_seq_base(GstRTPBuffer * rtp)159 rtp_ulpfec_buffer_get_seq_base (GstRTPBuffer * rtp)
160 {
161 return g_ntohs (rtp_ulpfec_buffer_get_fechdr (rtp)->seq);
162 }
163
164 guint
rtp_ulpfec_get_headers_len(gboolean fec_mask_long)165 rtp_ulpfec_get_headers_len (gboolean fec_mask_long)
166 {
167 return sizeof (RtpUlpFecHeader) + fec_level_hdr_get_size (fec_mask_long);
168 }
169
170 #define ONE_64BIT G_GUINT64_CONSTANT(1)
171
172 guint64
rtp_ulpfec_packet_mask_from_seqnum(guint16 seq,guint16 fec_seq_base,gboolean fec_mask_long)173 rtp_ulpfec_packet_mask_from_seqnum (guint16 seq,
174 guint16 fec_seq_base, gboolean fec_mask_long)
175 {
176 gint seq_delta = gst_rtp_buffer_compare_seqnum (fec_seq_base, seq);
177 if (seq_delta >= 0
178 && seq_delta <= RTP_ULPFEC_SEQ_BASE_OFFSET_MAX (fec_mask_long)) {
179 return ONE_64BIT << (RTP_ULPFEC_SEQ_BASE_OFFSET_MAX (TRUE) - seq_delta);
180 }
181 return 0;
182 }
183
184 gboolean
rtp_ulpfec_mask_is_long(guint64 mask)185 rtp_ulpfec_mask_is_long (guint64 mask)
186 {
187 return (mask & 0xffffffff) ? TRUE : FALSE;
188 }
189
190 gboolean
rtp_ulpfec_buffer_is_valid(GstRTPBuffer * rtp)191 rtp_ulpfec_buffer_is_valid (GstRTPBuffer * rtp)
192 {
193 guint payload_len = gst_rtp_buffer_get_payload_len (rtp);
194 RtpUlpFecHeader *fec_hdr;
195 guint fec_hdrs_len;
196 guint fec_packet_len;
197
198 if (payload_len < sizeof (RtpUlpFecHeader))
199 goto toosmall;
200
201 fec_hdr = rtp_ulpfec_buffer_get_fechdr (rtp);
202 if (fec_hdr->E)
203 goto invalidcontent;
204
205 fec_hdrs_len = rtp_ulpfec_get_headers_len (fec_hdr->L);
206 if (payload_len < fec_hdrs_len)
207 goto toosmall;
208
209 fec_packet_len = fec_hdrs_len + rtp_ulpfec_hdr_get_protection_len (fec_hdr);
210 if (fec_packet_len != payload_len)
211 goto lengthmismatch;
212
213 return TRUE;
214 toosmall:
215 GST_WARNING ("FEC packet too small");
216 return FALSE;
217
218 lengthmismatch:
219 GST_WARNING ("invalid FEC packet (declared length %u, real length %u)",
220 fec_packet_len, payload_len);
221 return FALSE;
222
223 invalidcontent:
224 GST_WARNING ("FEC Header contains invalid fields: %u", fec_hdr->E);
225 return FALSE;
226 }
227
228
229 void
rtp_buffer_to_ulpfec_bitstring(GstRTPBuffer * rtp,GArray * dst_arr,gboolean fec_buffer,gboolean fec_mask_long)230 rtp_buffer_to_ulpfec_bitstring (GstRTPBuffer * rtp, GArray * dst_arr,
231 gboolean fec_buffer, gboolean fec_mask_long)
232 {
233 if (G_UNLIKELY (fec_buffer)) {
234 guint payload_len = gst_rtp_buffer_get_payload_len (rtp);
235 g_array_set_size (dst_arr, MAX (payload_len, dst_arr->len));
236 memcpy (dst_arr->data, gst_rtp_buffer_get_payload (rtp), payload_len);
237 } else {
238 const guint8 *src = rtp->data[0];
239 guint len = gst_rtp_buffer_get_packet_len (rtp) - MIN_RTP_HEADER_LEN;
240 guint dst_offset = rtp_ulpfec_get_headers_len (fec_mask_long);
241 guint src_offset = MIN_RTP_HEADER_LEN;
242 guint8 *dst;
243
244 g_array_set_size (dst_arr, MAX (dst_offset + len, dst_arr->len));
245 dst = (guint8 *) dst_arr->data;
246
247 *((guint64 *) dst) ^= *((const guint64 *) src);
248 ((RtpUlpFecHeader *) dst)->len ^= g_htons (len);
249 _xor_mem (dst + dst_offset, src + src_offset, len);
250 }
251 }
252
253 GstBuffer *
rtp_ulpfec_bitstring_to_media_rtp_buffer(GArray * arr,gboolean fec_mask_long,guint32 ssrc,guint16 seq)254 rtp_ulpfec_bitstring_to_media_rtp_buffer (GArray * arr,
255 gboolean fec_mask_long, guint32 ssrc, guint16 seq)
256 {
257 guint fec_hdrs_len = rtp_ulpfec_get_headers_len (fec_mask_long);
258 guint payload_len =
259 fec_hdr_get_packets_len_recovery ((RtpUlpFecHeader *) arr->data);
260 GstMapInfo ret_info = GST_MAP_INFO_INIT;
261 GstMemory *ret_mem;
262 GstBuffer *ret;
263
264 if (payload_len > arr->len - fec_hdrs_len)
265 return NULL; // Not enough data
266
267 ret_mem = gst_allocator_alloc (NULL, MIN_RTP_HEADER_LEN + payload_len, NULL);
268 gst_memory_map (ret_mem, &ret_info, GST_MAP_READWRITE);
269
270 /* Filling 12 bytes of RTP header */
271 *((guint64 *) ret_info.data) = *((guint64 *) arr->data);
272 ((RtpHeader *) ret_info.data)->version = 2;
273 ((RtpHeader *) ret_info.data)->seq = g_htons (seq);
274 ((RtpHeader *) ret_info.data)->ssrc = g_htonl (ssrc);
275 /* Filling payload */
276 memcpy (ret_info.data + MIN_RTP_HEADER_LEN,
277 arr->data + fec_hdrs_len, payload_len);
278
279 gst_memory_unmap (ret_mem, &ret_info);
280 ret = gst_buffer_new ();
281 gst_buffer_append_memory (ret, ret_mem);
282 return ret;
283 }
284
285 GstBuffer *
rtp_ulpfec_bitstring_to_fec_rtp_buffer(GArray * arr,guint16 seq_base,gboolean fec_mask_long,guint64 fec_mask,gboolean marker,guint8 pt,guint16 seq,guint32 timestamp,guint32 ssrc)286 rtp_ulpfec_bitstring_to_fec_rtp_buffer (GArray * arr,
287 guint16 seq_base, gboolean fec_mask_long, guint64 fec_mask,
288 gboolean marker, guint8 pt, guint16 seq, guint32 timestamp, guint32 ssrc)
289 {
290 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
291 GstBuffer *ret;
292
293 /* Filling FEC headers */
294 {
295 RtpUlpFecHeader *hdr = (RtpUlpFecHeader *) arr->data;
296 RtpUlpFecLevelHeader *lvlhdr;
297 hdr->E = 0;
298 hdr->L = fec_mask_long;
299 hdr->seq = g_htons (seq_base);
300
301 lvlhdr = fec_hdr_get_level_hdr (hdr);
302 fec_level_hdr_set_protection_len (lvlhdr,
303 arr->len - rtp_ulpfec_get_headers_len (fec_mask_long));
304 fec_level_hdr_set_mask (lvlhdr, fec_mask_long, fec_mask);
305 }
306
307 /* Filling RTP header, copying payload */
308 ret = gst_rtp_buffer_new_allocate (arr->len, 0, 0);
309 if (!gst_rtp_buffer_map (ret, GST_MAP_READWRITE, &rtp))
310 g_assert_not_reached ();
311
312 gst_rtp_buffer_set_marker (&rtp, marker);
313 gst_rtp_buffer_set_payload_type (&rtp, pt);
314 gst_rtp_buffer_set_seq (&rtp, seq);
315 gst_rtp_buffer_set_timestamp (&rtp, timestamp);
316 gst_rtp_buffer_set_ssrc (&rtp, ssrc);
317
318 memcpy (gst_rtp_buffer_get_payload (&rtp), arr->data, arr->len);
319
320 gst_rtp_buffer_unmap (&rtp);
321
322 return ret;
323 }
324
325 /**
326 * rtp_ulpfec_map_info_map:
327 * @buffer: (transfer: full) #GstBuffer
328 * @info: #RtpUlpFecMapInfo
329 *
330 * Maps the contents of @buffer into @info. If @buffer made of many #GstMemory
331 * objects, merges them together to create a new buffer made of single
332 * continious #GstMemory.
333 *
334 * Returns: %TRUE if @buffer could be mapped
335 **/
336 gboolean
rtp_ulpfec_map_info_map(GstBuffer * buffer,RtpUlpFecMapInfo * info)337 rtp_ulpfec_map_info_map (GstBuffer * buffer, RtpUlpFecMapInfo * info)
338 {
339 /* We need to make sure we are working with continious memory chunk.
340 * If not merge all memories together */
341 if (gst_buffer_n_memory (buffer) > 1) {
342 GstBuffer *new_buffer = gst_buffer_new ();
343 GstMemory *mem = gst_buffer_get_all_memory (buffer);
344 gst_buffer_append_memory (new_buffer, mem);
345
346 /* We supposed to own the old buffer, but we don't use it here, so unref */
347 gst_buffer_unref (buffer);
348 buffer = new_buffer;
349 }
350
351 if (!gst_rtp_buffer_map (buffer,
352 GST_MAP_READ | GST_RTP_BUFFER_MAP_FLAG_SKIP_PADDING, &info->rtp)) {
353 /* info->rtp.buffer = NULL is an indication for rtp_ulpfec_map_info_unmap()
354 * that mapping has failed */
355 g_assert (NULL == info->rtp.buffer);
356 gst_buffer_unref (buffer);
357 return FALSE;
358 }
359 return TRUE;
360 }
361
362 /**
363 * rtp_ulpfec_map_info_unmap:
364 * @info: #RtpUlpFecMapInfo
365 *
366 * Unmap @info previously mapped with rtp_ulpfec_map_info_map() and unrefs the
367 * buffer. For convenience can even be called even if rtp_ulpfec_map_info_map
368 * returned FALSE
369 **/
370 void
rtp_ulpfec_map_info_unmap(RtpUlpFecMapInfo * info)371 rtp_ulpfec_map_info_unmap (RtpUlpFecMapInfo * info)
372 {
373 GstBuffer *buffer = info->rtp.buffer;
374
375 if (buffer) {
376 gst_rtp_buffer_unmap (&info->rtp);
377 gst_buffer_unref (buffer);
378 }
379 }
380
381 #ifndef GST_DISABLE_GST_DEBUG
382 void
rtp_ulpfec_log_rtppacket(GstDebugCategory * cat,GstDebugLevel level,gpointer object,const gchar * name,GstRTPBuffer * rtp)383 rtp_ulpfec_log_rtppacket (GstDebugCategory * cat, GstDebugLevel level,
384 gpointer object, const gchar * name, GstRTPBuffer * rtp)
385 {
386 guint seq;
387 guint ssrc;
388 guint timestamp;
389 guint pt;
390
391 if (level > gst_debug_category_get_threshold (cat))
392 return;
393
394 seq = gst_rtp_buffer_get_seq (rtp);
395 ssrc = gst_rtp_buffer_get_ssrc (rtp);
396 timestamp = gst_rtp_buffer_get_timestamp (rtp);
397 pt = gst_rtp_buffer_get_payload_type (rtp);
398
399 GST_CAT_LEVEL_LOG (cat, level, object,
400 "%-22s: [%c%c%c%c] ssrc=0x%08x pt=%u tstamp=%u seq=%u size=%u(%u,%u)",
401 name,
402 gst_rtp_buffer_get_marker (rtp) ? 'M' : ' ',
403 gst_rtp_buffer_get_extension (rtp) ? 'X' : ' ',
404 gst_rtp_buffer_get_padding (rtp) ? 'P' : ' ',
405 gst_rtp_buffer_get_csrc_count (rtp) > 0 ? 'C' : ' ',
406 ssrc, pt, timestamp, seq,
407 gst_rtp_buffer_get_packet_len (rtp),
408 gst_rtp_buffer_get_packet_len (rtp) - MIN_RTP_HEADER_LEN,
409 gst_rtp_buffer_get_payload_len (rtp));
410 }
411 #endif /* GST_DISABLE_GST_DEBUG */
412
413 #ifndef GST_DISABLE_GST_DEBUG
414 void
rtp_ulpfec_log_fec_packet(GstDebugCategory * cat,GstDebugLevel level,gpointer object,GstRTPBuffer * fecrtp)415 rtp_ulpfec_log_fec_packet (GstDebugCategory * cat, GstDebugLevel level,
416 gpointer object, GstRTPBuffer * fecrtp)
417 {
418 RtpUlpFecHeader *fec_hdr;
419 RtpUlpFecLevelHeader *fec_level_hdr;
420
421 if (level > gst_debug_category_get_threshold (cat))
422 return;
423
424 fec_hdr = gst_rtp_buffer_get_payload (fecrtp);
425 GST_CAT_LEVEL_LOG (cat, level, object,
426 "%-22s: [%c%c%c%c%c%c] pt=%u tstamp=%u seq=%u recovery_len=%u",
427 "fec header",
428 fec_hdr->E ? 'E' : ' ',
429 fec_hdr->L ? 'L' : ' ',
430 fec_hdr->P ? 'P' : ' ',
431 fec_hdr->X ? 'X' : ' ',
432 fec_hdr->CC ? 'C' : ' ',
433 fec_hdr->M ? 'M' : ' ',
434 fec_hdr->pt,
435 fec_hdr_get_timestamp_recovery (fec_hdr),
436 fec_hdr_get_seq_base (fec_hdr, TRUE,
437 gst_rtp_buffer_get_seq (fecrtp)),
438 fec_hdr_get_packets_len_recovery (fec_hdr));
439
440 fec_level_hdr = fec_hdr_get_level_hdr (fec_hdr);
441 GST_CAT_LEVEL_LOG (cat, level, object,
442 "%-22s: protection_len=%u mask=0x%012" G_GINT64_MODIFIER "x",
443 "fec level header",
444 g_ntohs (fec_level_hdr->protection_len),
445 fec_level_hdr_get_mask (fec_level_hdr, fec_hdr->L));
446 }
447 #endif /* GST_DISABLE_GST_DEBUG */
448