docs: hook up new RTP FEC elements
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpulpfecenc.c
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 /**
22  * SECTION:element-rtpulpfecenc
23  * @short_description: Generic RTP Forward Error Correction (FEC) encoder
24  * @title: rtpulpfecenc
25  *
26  * Generic Forward Error Correction (FEC) encoder using Uneven Level
27  * Protection (ULP) as described in RFC 5109.
28  *
29  * Since: 1.14
30  */
31
32 #include <gst/rtp/gstrtp-enumtypes.h>
33 #include <gst/rtp/gstrtpbuffer.h>
34 #include <string.h>
35
36 #include "rtpulpfeccommon.h"
37 #include "gstrtpulpfecenc.h"
38
39 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
40     GST_PAD_SINK,
41     GST_PAD_ALWAYS,
42     GST_STATIC_CAPS ("application/x-rtp"));
43
44 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
45     GST_PAD_SRC,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS ("application/x-rtp"));
48
49 #define UNDEF_PT                255
50 #define UNDEF_SSRC              0
51
52 #define DEFAULT_PT              UNDEF_PT
53 #define DEFAULT_SSRC            UNDEF_SSRC
54 #define DEFAULT_PCT             0
55 #define DEFAULT_PCT_IMPORTANT   0
56 #define DEFAULT_MULTIPACKET     FALSE
57 #define DEFAULT_MUX_SEQ         FALSE
58
59 #define PACKETS_BUF_MAX_LENGTH  (RTP_ULPFEC_PROTECTED_PACKETS_MAX(TRUE))
60
61 GST_DEBUG_CATEGORY (gst_rtp_ulpfec_enc_debug);
62 #define GST_CAT_DEFAULT (gst_rtp_ulpfec_enc_debug)
63
64 G_DEFINE_TYPE (GstRtpUlpFecEnc, gst_rtp_ulpfec_enc, GST_TYPE_ELEMENT);
65
66 enum
67 {
68   PROP_0,
69   PROP_SSRC,
70   PROP_PT,
71   PROP_MULTIPACKET,
72   PROP_MUX_SEQ,
73   PROP_PROTECTED,
74   PROP_PERCENTAGE,
75   PROP_PERCENTAGE_IMPORTANT,
76 };
77
78 #define RTP_FEC_MAP_INFO_NTH(ctx, data) (&g_array_index (\
79     ((GstRtpUlpFecEncStreamCtx *)ctx)->info_arr, \
80     RtpUlpFecMapInfo, \
81     GPOINTER_TO_UINT(data)))
82
83 static void
84 gst_rtp_ulpfec_enc_stream_ctx_start (GstRtpUlpFecEncStreamCtx * ctx,
85     GQueue * packets, guint fec_packets)
86 {
87   GList *it = packets->tail;
88
89   g_array_set_size (ctx->info_arr, packets->length);
90
91   for (guint i = 0; i < packets->length; ++i) {
92     GstBuffer *buffer = it->data;
93     RtpUlpFecMapInfo *info = RTP_FEC_MAP_INFO_NTH (ctx, i);
94
95     if (!rtp_ulpfec_map_info_map (gst_buffer_ref (buffer), info))
96       g_assert_not_reached ();
97
98     GST_LOG_RTP_PACKET (ctx->parent, "rtp header (incoming)", &info->rtp);
99
100     it = g_list_previous (it);
101   }
102
103   ctx->fec_packets = fec_packets;
104   ctx->fec_packet_idx = 0;
105 }
106
107 static void
108 gst_rtp_ulpfec_enc_stream_ctx_stop (GstRtpUlpFecEncStreamCtx * ctx)
109 {
110   g_array_set_size (ctx->info_arr, 0);
111   g_array_set_size (ctx->scratch_buf, 0);
112
113   ctx->fec_packets = 0;
114   ctx->fec_packet_idx = 0;
115 }
116
117 static void
118     gst_rtp_ulpfec_enc_stream_ctx_get_protection_parameters
119     (GstRtpUlpFecEncStreamCtx * ctx, guint16 * dst_seq_base, guint64 * dst_mask,
120     guint * dst_start, guint * dst_end)
121 {
122   guint media_packets = ctx->info_arr->len;
123   guint start = ctx->fec_packet_idx * media_packets / ctx->fec_packets;
124   guint end =
125       ((ctx->fec_packet_idx + 1) * media_packets + ctx->fec_packets -
126       1) / ctx->fec_packets - 1;
127   guint len = end - start + 1;
128   guint64 mask = 0;
129   guint16 seq_base = 0;
130
131   len = MIN (len, RTP_ULPFEC_PROTECTED_PACKETS_MAX (TRUE));
132   end = start + len - 1;
133
134   for (guint i = start; i <= end; ++i) {
135     RtpUlpFecMapInfo *info = RTP_FEC_MAP_INFO_NTH (ctx, i);
136     guint16 seq = gst_rtp_buffer_get_seq (&info->rtp);
137
138     if (mask) {
139       gint diff = gst_rtp_buffer_compare_seqnum (seq_base, seq);
140       if (diff < 0) {
141         seq_base = seq;
142         mask = mask >> (-diff);
143       }
144       mask |= rtp_ulpfec_packet_mask_from_seqnum (seq, seq_base, TRUE);
145     } else {
146       seq_base = seq;
147       mask = rtp_ulpfec_packet_mask_from_seqnum (seq, seq_base, TRUE);
148     }
149   }
150
151   *dst_start = start;
152   *dst_end = end;
153   *dst_mask = mask;
154   *dst_seq_base = seq_base;
155 }
156
157 static GstBuffer *
158 gst_rtp_ulpfec_enc_stream_ctx_protect (GstRtpUlpFecEncStreamCtx * ctx,
159     guint8 pt, guint16 seq, guint32 timestamp, guint32 ssrc)
160 {
161   guint end = 0;
162   guint start = 0;
163   guint64 fec_mask = 0;
164   guint16 seq_base = 0;
165   GstBuffer *ret;
166   guint64 tmp_mask;
167   gboolean fec_mask_long;
168
169   if (ctx->fec_packet_idx >= ctx->fec_packets)
170     return NULL;
171
172   g_array_set_size (ctx->scratch_buf, 0);
173   gst_rtp_ulpfec_enc_stream_ctx_get_protection_parameters (ctx, &seq_base,
174       &fec_mask, &start, &end);
175
176   tmp_mask = fec_mask;
177   fec_mask_long = rtp_ulpfec_mask_is_long (fec_mask);
178   for (guint i = start; i <= end; ++i) {
179     RtpUlpFecMapInfo *info = RTP_FEC_MAP_INFO_NTH (ctx, i);
180     guint64 packet_mask =
181         rtp_ulpfec_packet_mask_from_seqnum (gst_rtp_buffer_get_seq (&info->rtp),
182         seq_base,
183         TRUE);
184
185     if (tmp_mask & packet_mask) {
186       tmp_mask ^= packet_mask;
187       rtp_buffer_to_ulpfec_bitstring (&info->rtp, ctx->scratch_buf, FALSE,
188           fec_mask_long);
189     }
190   }
191
192   g_assert (tmp_mask == 0ULL);
193   ret =
194       rtp_ulpfec_bitstring_to_fec_rtp_buffer (ctx->scratch_buf, seq_base,
195       fec_mask_long, fec_mask, FALSE, pt, seq, timestamp, ssrc);
196   ++ctx->fec_packet_idx;
197   return ret;
198 }
199
200 static void
201 gst_rtp_ulpfec_enc_stream_ctx_report_budget (GstRtpUlpFecEncStreamCtx * ctx)
202 {
203   GST_TRACE_OBJECT (ctx->parent, "budget = %f budget_important = %f",
204       ctx->budget, ctx->budget_important);
205 }
206
207 static void
208 gst_rtp_ulpfec_enc_stream_ctx_increment_budget (GstRtpUlpFecEncStreamCtx * ctx,
209     GstBuffer * buffer)
210 {
211   if (ctx->percentage == 0 && ctx->percentage_important == 0) {
212     if (ctx->budget > 0) {
213       ctx->budget = 0;
214       ctx->budget_important = 0;
215     }
216     if (ctx->budget < 0)
217       ctx->budget += ctx->budget_inc;
218
219     return;
220   }
221   ctx->budget += ctx->budget_inc;
222
223   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_NON_DROPPABLE)) {
224     ctx->budget_important += ctx->budget_inc_important;
225   }
226
227   gst_rtp_ulpfec_enc_stream_ctx_report_budget (ctx);
228 }
229
230 static void
231 gst_rtp_ulpfec_enc_stream_ctx_decrement_budget (GstRtpUlpFecEncStreamCtx * ctx,
232     guint fec_packets_num)
233 {
234   if (ctx->budget_important >= 1.)
235     ctx->budget_important -= fec_packets_num;
236   ctx->budget -= fec_packets_num;
237
238   gst_rtp_ulpfec_enc_stream_ctx_report_budget (ctx);
239 }
240
241 static guint
242 gst_rtp_ulpfec_enc_stream_ctx_get_fec_packets_num (GstRtpUlpFecEncStreamCtx *
243     ctx)
244 {
245   g_assert_cmpfloat (ctx->budget_important, >=, 0.);
246
247   if (ctx->budget_important >= 1.)
248     return ctx->budget_important;
249   return ctx->budget > 0. ? (guint) ctx->budget : 0;
250 }
251
252 static void
253 gst_rtp_ulpfec_enc_stream_ctx_free_packets_buf (GstRtpUlpFecEncStreamCtx * ctx)
254 {
255   while (ctx->packets_buf.length)
256     gst_buffer_unref (g_queue_pop_tail (&ctx->packets_buf));
257 }
258
259 static void
260 gst_rtp_ulpfec_enc_stream_ctx_prepend_to_fec_buffer (GstRtpUlpFecEncStreamCtx *
261     ctx, GstRTPBuffer * rtp, guint buf_max_size)
262 {
263   GList *new_head;
264   if (ctx->packets_buf.length == buf_max_size) {
265     new_head = g_queue_pop_tail_link (&ctx->packets_buf);
266   } else {
267     new_head = g_list_alloc ();
268   }
269
270   gst_buffer_replace ((GstBuffer **) & new_head->data, rtp->buffer);
271   g_queue_push_head_link (&ctx->packets_buf, new_head);
272
273   g_assert_cmpint (ctx->packets_buf.length, <=, buf_max_size);
274 }
275
276 static GstFlowReturn
277 gst_rtp_ulpfec_enc_stream_ctx_push_fec_packets (GstRtpUlpFecEncStreamCtx * ctx,
278     guint8 pt, guint16 seq, guint32 timestamp, guint32 ssrc)
279 {
280   GstFlowReturn ret = GST_FLOW_OK;
281   guint fec_packets_num =
282       gst_rtp_ulpfec_enc_stream_ctx_get_fec_packets_num (ctx);
283
284   if (fec_packets_num) {
285     guint fec_packets_pushed = 0;
286     GstBuffer *latest_packet = ctx->packets_buf.head->data;
287     GstBuffer *fec = NULL;
288
289     gst_rtp_ulpfec_enc_stream_ctx_start (ctx, &ctx->packets_buf,
290         fec_packets_num);
291
292     while (NULL != (fec =
293             gst_rtp_ulpfec_enc_stream_ctx_protect (ctx, pt,
294                 seq + fec_packets_pushed, timestamp, ssrc))) {
295       gst_buffer_copy_into (fec, latest_packet, GST_BUFFER_COPY_TIMESTAMPS, 0,
296           -1);
297
298       ret = gst_pad_push (ctx->srcpad, fec);
299       if (GST_FLOW_OK == ret)
300         ++fec_packets_pushed;
301       else
302         break;
303     }
304
305     gst_rtp_ulpfec_enc_stream_ctx_stop (ctx);
306
307     g_assert_cmpint (fec_packets_pushed, <=, fec_packets_num);
308
309     ctx->num_packets_protected += ctx->packets_buf.length;
310     ctx->num_packets_fec += fec_packets_pushed;
311     ctx->seqnum_offset += fec_packets_pushed;
312     ctx->seqnum += fec_packets_pushed;
313   }
314
315   gst_rtp_ulpfec_enc_stream_ctx_decrement_budget (ctx, fec_packets_num);
316   return ret;
317 }
318
319 static void
320 gst_rtp_ulpfec_enc_stream_ctx_cache_packet (GstRtpUlpFecEncStreamCtx * ctx,
321     GstRTPBuffer * rtp, gboolean * dst_empty_packet_buffer,
322     gboolean * dst_push_fec)
323 {
324   if (ctx->multipacket) {
325     gst_rtp_ulpfec_enc_stream_ctx_prepend_to_fec_buffer (ctx, rtp,
326         PACKETS_BUF_MAX_LENGTH);
327     gst_rtp_ulpfec_enc_stream_ctx_increment_budget (ctx, rtp->buffer);
328
329     *dst_empty_packet_buffer = gst_rtp_buffer_get_marker (rtp);
330     *dst_push_fec = *dst_empty_packet_buffer;
331   } else {
332     gboolean push_fec;
333
334     gst_rtp_ulpfec_enc_stream_ctx_prepend_to_fec_buffer (ctx, rtp, 1);
335
336     push_fec = ctx->fec_nth == 0 ? FALSE :
337         0 == (ctx->num_packets_received % ctx->fec_nth);
338
339     ctx->budget = push_fec ? 1 : 0;
340     ctx->budget_important = 0;
341
342     *dst_push_fec = push_fec;
343     *dst_empty_packet_buffer = FALSE;
344   }
345 }
346
347 static void
348 gst_rtp_ulpfec_enc_stream_ctx_configure (GstRtpUlpFecEncStreamCtx * ctx,
349     guint pt, guint32 fec_ssrc,
350     guint percentage, guint percentage_important, gboolean multipacket,
351     gboolean mux_seq)
352 {
353   ctx->pt = pt;
354   ctx->fec_ssrc = fec_ssrc;
355   ctx->percentage = percentage;
356   ctx->percentage_important = percentage_important;
357   ctx->multipacket = multipacket;
358   ctx->mux_seq = mux_seq;
359
360   ctx->fec_nth = percentage ? 100 / percentage : 0;
361   if (percentage) {
362     ctx->budget_inc = percentage / 100.;
363     ctx->budget_inc_important = percentage > percentage_important ?
364         ctx->budget_inc : percentage_important / 100.;
365   }
366 /*
367    else {
368     ctx->budget_inc = 0.0;
369   }
370 */
371   ctx->budget_inc_important = percentage > percentage_important ?
372       ctx->budget_inc : percentage_important / 100.;
373 }
374
375 static GstRtpUlpFecEncStreamCtx *
376 gst_rtp_ulpfec_enc_stream_ctx_new (guint ssrc,
377     GstElement * parent, GstPad * srcpad,
378     guint pt, guint32 fec_ssrc,
379     guint percentage, guint percentage_important, gboolean multipacket,
380     gboolean mux_seq)
381 {
382   GstRtpUlpFecEncStreamCtx *ctx = g_new0 (GstRtpUlpFecEncStreamCtx, 1);
383
384   ctx->ssrc = ssrc;
385   ctx->parent = parent;
386   ctx->srcpad = srcpad;
387
388   ctx->seqnum = g_random_int_range (0, G_MAXUINT16 / 2);
389
390   ctx->info_arr = g_array_new (FALSE, TRUE, sizeof (RtpUlpFecMapInfo));
391   g_array_set_clear_func (ctx->info_arr,
392       (GDestroyNotify) rtp_ulpfec_map_info_unmap);
393   ctx->parent = parent;
394   ctx->scratch_buf = g_array_new (FALSE, TRUE, sizeof (guint8));
395   gst_rtp_ulpfec_enc_stream_ctx_configure (ctx, pt, fec_ssrc,
396       percentage, percentage_important, multipacket, mux_seq);
397
398   return ctx;
399 }
400
401 static void
402 gst_rtp_ulpfec_enc_stream_ctx_free (GstRtpUlpFecEncStreamCtx * ctx)
403 {
404   if (ctx->num_packets_received) {
405     GST_INFO_OBJECT (ctx->parent, "Actual FEC overhead is %4.2f%% (%u/%u)\n",
406         ctx->num_packets_fec * (double) 100. / ctx->num_packets_received,
407         ctx->num_packets_fec, ctx->num_packets_received);
408   }
409   gst_rtp_ulpfec_enc_stream_ctx_free_packets_buf (ctx);
410
411   g_assert (0 == ctx->info_arr->len);
412   g_array_free (ctx->info_arr, TRUE);
413   g_array_free (ctx->scratch_buf, TRUE);
414   g_slice_free1 (sizeof (GstRtpUlpFecEncStreamCtx), ctx);
415 }
416
417 static GstFlowReturn
418 gst_rtp_ulpfec_enc_stream_ctx_process (GstRtpUlpFecEncStreamCtx * ctx,
419     GstBuffer * buffer)
420 {
421   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
422   GstFlowReturn ret;
423   gboolean push_fec = FALSE;
424   gboolean empty_packet_buffer = FALSE;
425
426   ctx->num_packets_received++;
427
428   if (ctx->mux_seq && ctx->seqnum_offset > 0) {
429     buffer = gst_buffer_make_writable (buffer);
430     if (!gst_rtp_buffer_map (buffer,
431             GST_MAP_READWRITE | GST_RTP_BUFFER_MAP_FLAG_SKIP_PADDING, &rtp))
432       g_assert_not_reached ();
433     gst_rtp_buffer_set_seq (&rtp,
434         gst_rtp_buffer_get_seq (&rtp) + ctx->seqnum_offset);
435   } else {
436     if (!gst_rtp_buffer_map (buffer,
437             GST_MAP_READ | GST_RTP_BUFFER_MAP_FLAG_SKIP_PADDING, &rtp))
438       g_assert_not_reached ();
439   }
440
441   gst_rtp_ulpfec_enc_stream_ctx_cache_packet (ctx, &rtp, &empty_packet_buffer,
442       &push_fec);
443
444   if (push_fec) {
445     guint32 fec_timestamp = gst_rtp_buffer_get_timestamp (&rtp);
446     guint32 fec_ssrc =
447         ctx->fec_ssrc ==
448         UNDEF_SSRC ? gst_rtp_buffer_get_ssrc (&rtp) : ctx->fec_ssrc;
449     guint16 fec_seq =
450         ctx->mux_seq ? gst_rtp_buffer_get_seq (&rtp) + 1 : ctx->seqnum;
451
452     gst_rtp_buffer_unmap (&rtp);
453
454     ret = gst_pad_push (ctx->srcpad, buffer);
455     if (GST_FLOW_OK == ret)
456       ret =
457           gst_rtp_ulpfec_enc_stream_ctx_push_fec_packets (ctx, ctx->pt, fec_seq,
458           fec_timestamp, fec_ssrc);
459   } else {
460     gst_rtp_buffer_unmap (&rtp);
461     ret = gst_pad_push (ctx->srcpad, buffer);
462   }
463
464   if (empty_packet_buffer)
465     gst_rtp_ulpfec_enc_stream_ctx_free_packets_buf (ctx);
466
467   return ret;
468 }
469
470 static GstRtpUlpFecEncStreamCtx *
471 gst_rtp_ulpfec_enc_aquire_ctx (GstRtpUlpFecEnc * fec, guint ssrc)
472 {
473   GstRtpUlpFecEncStreamCtx *ctx;
474
475   GST_OBJECT_LOCK (fec);
476   ctx = g_hash_table_lookup (fec->ssrc_to_ctx, GUINT_TO_POINTER (ssrc));
477   if (ctx == NULL) {
478     ctx =
479         gst_rtp_ulpfec_enc_stream_ctx_new (ssrc, GST_ELEMENT_CAST (fec),
480         fec->srcpad, fec->pt, fec->ssrc, fec->percentage,
481         fec->percentage_important, fec->multipacket, fec->mux_seq);
482     g_hash_table_insert (fec->ssrc_to_ctx, GUINT_TO_POINTER (ssrc), ctx);
483   }
484   GST_OBJECT_UNLOCK (fec);
485
486   return ctx;
487 }
488
489 static GstFlowReturn
490 gst_rtp_ulpfec_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
491 {
492   GstRtpUlpFecEnc *fec = GST_RTP_ULPFEC_ENC (parent);
493   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
494   GstFlowReturn ret;
495   guint ssrc = 0;
496   GstRtpUlpFecEncStreamCtx *ctx;
497
498   if (fec->pt == UNDEF_PT)
499     return gst_pad_push (fec->srcpad, buffer);
500
501   /* FIXME: avoid this additional mapping of the buffer to get the
502      ssrc! */
503   if (!gst_rtp_buffer_map (buffer,
504           GST_MAP_READ | GST_RTP_BUFFER_MAP_FLAG_SKIP_PADDING, &rtp)) {
505     g_assert_not_reached ();
506   }
507   ssrc = gst_rtp_buffer_get_ssrc (&rtp);
508   gst_rtp_buffer_unmap (&rtp);
509
510   ctx = gst_rtp_ulpfec_enc_aquire_ctx (fec, ssrc);
511
512   ret = gst_rtp_ulpfec_enc_stream_ctx_process (ctx, buffer);
513
514   /* FIXME: does not work for mulitple ssrcs */
515   fec->num_packets_protected = ctx->num_packets_protected;
516
517   return ret;
518 }
519
520 static void
521 gst_rtp_ulpfec_enc_configure_ctx (gpointer key, gpointer value,
522     gpointer user_data)
523 {
524   GstRtpUlpFecEnc *fec = user_data;
525   GstRtpUlpFecEncStreamCtx *ctx = value;
526
527   gst_rtp_ulpfec_enc_stream_ctx_configure (ctx, fec->pt, fec->ssrc,
528       fec->percentage, fec->percentage_important,
529       fec->multipacket, fec->mux_seq);
530 }
531
532 static void
533 gst_rtp_ulpfec_enc_set_property (GObject * object, guint prop_id,
534     const GValue * value, GParamSpec * pspec)
535 {
536   GstRtpUlpFecEnc *fec = GST_RTP_ULPFEC_ENC (object);
537
538   switch (prop_id) {
539     case PROP_PT:
540       fec->pt = g_value_get_uint (value);
541       break;
542     case PROP_SSRC:
543       fec->ssrc = g_value_get_uint (value);
544       break;
545     case PROP_MULTIPACKET:
546       fec->multipacket = g_value_get_boolean (value);
547       break;
548     case PROP_PERCENTAGE:
549       fec->percentage = g_value_get_uint (value);
550       break;
551     case PROP_PERCENTAGE_IMPORTANT:
552       fec->percentage_important = g_value_get_uint (value);
553       break;
554     case PROP_MUX_SEQ:
555       fec->mux_seq = g_value_get_boolean (value);
556       break;
557     default:
558       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
559       break;
560   }
561
562   GST_OBJECT_LOCK (fec);
563   g_hash_table_foreach (fec->ssrc_to_ctx, gst_rtp_ulpfec_enc_configure_ctx,
564       fec);
565   GST_OBJECT_UNLOCK (fec);
566 }
567
568 static void
569 gst_rtp_ulpfec_enc_get_property (GObject * object, guint prop_id,
570     GValue * value, GParamSpec * pspec)
571 {
572   GstRtpUlpFecEnc *fec = GST_RTP_ULPFEC_ENC (object);
573   switch (prop_id) {
574     case PROP_PT:
575       g_value_set_uint (value, fec->pt);
576       break;
577     case PROP_SSRC:
578       g_value_set_uint (value, fec->ssrc);
579       break;
580     case PROP_PROTECTED:
581       g_value_set_uint (value, fec->num_packets_protected);
582       break;
583     case PROP_PERCENTAGE:
584       g_value_set_uint (value, fec->percentage);
585       break;
586     case PROP_PERCENTAGE_IMPORTANT:
587       g_value_set_uint (value, fec->percentage_important);
588       break;
589     case PROP_MULTIPACKET:
590       g_value_set_boolean (value, fec->multipacket);
591       break;
592     case PROP_MUX_SEQ:
593       g_value_set_boolean (value, fec->mux_seq);
594       break;
595     default:
596       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
597       break;
598   }
599 }
600
601 static void
602 gst_rtp_ulpfec_enc_dispose (GObject * obj)
603 {
604   GstRtpUlpFecEnc *fec = GST_RTP_ULPFEC_ENC (obj);
605
606   g_hash_table_destroy (fec->ssrc_to_ctx);
607
608   G_OBJECT_CLASS (gst_rtp_ulpfec_enc_parent_class)->dispose (obj);
609 }
610
611 static void
612 gst_rtp_ulpfec_enc_init (GstRtpUlpFecEnc * fec)
613 {
614   fec->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
615   gst_element_add_pad (GST_ELEMENT (fec), fec->srcpad);
616
617   fec->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
618   GST_PAD_SET_PROXY_CAPS (fec->sinkpad);
619   GST_PAD_SET_PROXY_ALLOCATION (fec->sinkpad);
620   gst_pad_set_chain_function (fec->sinkpad,
621       GST_DEBUG_FUNCPTR (gst_rtp_ulpfec_enc_chain));
622   gst_element_add_pad (GST_ELEMENT (fec), fec->sinkpad);
623
624   fec->ssrc_to_ctx = g_hash_table_new_full (NULL, NULL, NULL,
625       (GDestroyNotify) gst_rtp_ulpfec_enc_stream_ctx_free);
626 }
627
628 static void
629 gst_rtp_ulpfec_enc_class_init (GstRtpUlpFecEncClass * klass)
630 {
631   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
632   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
633
634   GST_DEBUG_CATEGORY_INIT (gst_rtp_ulpfec_enc_debug, "rtpulpfecenc", 0,
635       "FEC encoder element");
636
637   gst_element_class_add_pad_template (element_class,
638       gst_static_pad_template_get (&srctemplate));
639   gst_element_class_add_pad_template (element_class,
640       gst_static_pad_template_get (&sinktemplate));
641
642   gst_element_class_set_static_metadata (element_class,
643       "RTP FEC Encoder",
644       "Codec/Payloader/Network/RTP",
645       "Encodes RTP FEC (RFC5109)", "Mikhail Fludkov <misha@pexip.com>");
646
647   gobject_class->set_property =
648       GST_DEBUG_FUNCPTR (gst_rtp_ulpfec_enc_set_property);
649   gobject_class->get_property =
650       GST_DEBUG_FUNCPTR (gst_rtp_ulpfec_enc_get_property);
651   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_rtp_ulpfec_enc_dispose);
652
653   g_object_class_install_property (gobject_class, PROP_SSRC,
654       g_param_spec_uint ("ssrc", "SSRC",
655           "The SSRC to use on FEC'd packets", 0, G_MAXUINT32, DEFAULT_SSRC,
656           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
657
658   g_object_class_install_property (gobject_class, PROP_PT,
659       g_param_spec_uint ("pt", "payload type",
660           "The payload type of FEC packets", 0, 255, DEFAULT_PT,
661           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
662
663   g_object_class_install_property (gobject_class, PROP_MULTIPACKET,
664       g_param_spec_boolean ("multipacket", "Multipacket",
665           "Apply FEC on multiple packets", DEFAULT_MULTIPACKET,
666           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
667
668   g_object_class_install_property (gobject_class, PROP_MUX_SEQ,
669       g_param_spec_boolean ("mux-seq", "Mux seq",
670           "Mux seqnum for media and fec packets in same seqnum space",
671           DEFAULT_MUX_SEQ,
672           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
673
674   g_object_class_install_property (gobject_class, PROP_PERCENTAGE,
675       g_param_spec_uint ("percentage", "Percentage",
676           "FEC overhead percentage for the whole stream", 0, 100, DEFAULT_PCT,
677           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
678
679   g_object_class_install_property (gobject_class, PROP_PERCENTAGE_IMPORTANT,
680       g_param_spec_uint ("percentage-important", "Percentage important",
681           "FEC overhead percentage for important packets",
682           0, 100, DEFAULT_PCT_IMPORTANT,
683           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
684
685   g_object_class_install_property (gobject_class, PROP_PROTECTED,
686       g_param_spec_uint ("protected", "Protected",
687           "Count of protected packets", 0, G_MAXUINT32, 0,
688           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
689 }