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