Bump GLib requirement to >= 2.62
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-good / gst / rtpmanager / gstrtpst2022-1-fecenc.c
1 /* GStreamer
2  * Copyright (C) <2020> Mathieu Duponchelle <mathieu@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:element-rtpst2022-1-fecenc
22  * @see_also: #element-rtpst2022-1-fecdec
23  *
24  * This element takes as input a media stream and up to two FEC
25  * streams as described in SMPTE 2022-1: Forward Error Correction
26  * for Real-Time Video/Audio Transport Over IP Networks, and makes
27  * use of the FEC packets to recover media packets that may have
28  * gotten lost.
29  *
30  * ## sender / receiver example
31  *
32  * ``` shell
33  * gst-launch-1.0 \
34  *   rtpbin name=rtp fec-encoders='fec,0="rtpst2022-1-fecenc\ rows\=5\ columns\=5";' \
35  *   uridecodebin uri=file:///path/to/video/file ! x264enc key-int-max=60 tune=zerolatency ! \
36  *     queue ! mpegtsmux ! rtpmp2tpay ssrc=0 ! rtp.send_rtp_sink_0 \
37  *   rtp.send_rtp_src_0 ! udpsink host=127.0.0.1 port=5000 \
38  *   rtp.send_fec_src_0_0 ! udpsink host=127.0.0.1 port=5002 async=false \
39  *   rtp.send_fec_src_0_1 ! udpsink host=127.0.0.1 port=5004 async=false
40  * ```
41  *
42  * ``` shell
43  * gst-launch-1.0 \
44  *   rtpbin latency=500 fec-decoders='fec,0="rtpst2022-1-fecdec\ size-time\=1000000000";' name=rtp \
45  *   udpsrc address=127.0.0.1 port=5002 caps="application/x-rtp, payload=96" ! queue ! rtp.recv_fec_sink_0_0 \
46  *   udpsrc address=127.0.0.1 port=5004 caps="application/x-rtp, payload=96" ! queue ! rtp.recv_fec_sink_0_1 \
47  *   udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp, media=video, clock-rate=90000, encoding-name=mp2t, payload=33" ! \
48  *     queue ! netsim drop-probability=0.05 ! rtp.recv_rtp_sink_0 \
49  *   rtp. ! decodebin ! videoconvert ! queue ! autovideosink
50  * ```
51  *
52  * With the above command line, as the media packet size is constant,
53  * the fec overhead can be approximated to the number of fec packets
54  * per 2-d matrix of media packet, here 10 fec packets for each 25
55  * media packets.
56  *
57  * Increasing the number of rows and columns will decrease the overhead,
58  * but obviously increase the likelihood of recovery failure for lost
59  * packets on the receiver side.
60  *
61  * Since: 1.20
62  */
63
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
67
68 #include <gst/base/base.h>
69 #include <gst/rtp/gstrtpbuffer.h>
70
71 #include "gstrtpst2022-1-fecenc.h"
72
73 GST_DEBUG_CATEGORY_STATIC (gst_rtpst_2022_1_fecenc_debug);
74 #define GST_CAT_DEFAULT gst_rtpst_2022_1_fecenc_debug
75
76 enum
77 {
78   PROP_0,
79   PROP_COLUMNS,
80   PROP_ROWS,
81   PROP_PT,
82   PROP_ENABLE_COLUMN,
83   PROP_ENABLE_ROW,
84 };
85
86 #define DEFAULT_ROWS 0
87 #define DEFAULT_COLUMNS 0
88 #define DEFAULT_PT 96
89 #define DEFAULT_ENABLE_COLUMN TRUE
90 #define DEFAULT_ENABLE_ROW TRUE
91
92 typedef struct
93 {
94   guint16 target_media_seq;     /* The media seqnum we want to send that packet alongside */
95   guint16 seq_base;             /* Only used for logging purposes */
96   GstBuffer *buffer;
97 } Item;
98
99 typedef struct
100 {
101   guint8 *xored_payload;
102   guint32 xored_timestamp;
103   guint8 xored_pt;
104   guint16 xored_payload_len;
105   gboolean xored_marker;
106   gboolean xored_padding;
107   gboolean xored_extension;
108
109   guint16 seq_base;
110
111   guint16 payload_len;
112   guint n_packets;
113 } FecPacket;
114
115 struct _GstRTPST_2022_1_FecEncClass
116 {
117   GstElementClass class;
118 };
119
120 struct _GstRTPST_2022_1_FecEnc
121 {
122   GstElement element;
123
124   GstPad *srcpad;
125   GstPad *sinkpad;
126
127   /* These pads do not participate in the flow return of the element,
128    * which should continue working even if the sending of FEC packets
129    * fails
130    */
131   GstPad *row_fec_srcpad;
132   GstPad *column_fec_srcpad;
133
134   /* The following fields are only accessed on state change or from the
135    * streaming thread, and only settable in state < PAUSED */
136
137   /* N columns */
138   guint l;
139   /* N rows */
140   guint d;
141
142   /* Whether we have pushed initial events on the column FEC source pad */
143   gboolean column_events_pushed;
144
145   /* The current row FEC packet */
146   FecPacket *row;
147   /* Tracks the row seqnum */
148   guint16 row_seq;
149   /* Whether we have pushed initial events on the row FEC source pad */
150   gboolean row_events_pushed;
151
152   /* These two fields are used to enforce input seqnum consecutiveness,
153    * and to determine when column FEC packets should be pushed */
154   gboolean last_media_seqnum_set;
155   guint16 last_media_seqnum;
156
157   /* This field is used to timestamp our FEC packets, we just piggy back */
158   guint32 last_media_timestamp;
159
160   /* The payload type of the FEC packets */
161   gint pt;
162
163   /* The following fields can be changed while PLAYING, and are
164    * protected with the OBJECT_LOCK
165    */
166   /* Tracks the property, can be changed while PLAYING */
167   gboolean enable_row;
168   /* Tracks the property, can be changed while PLAYING */
169   gboolean enable_column;
170
171   /* Array of FecPackets, with size enc->l */
172   GPtrArray *columns;
173   /* Index of the current column in the array above */
174   guint current_column;
175   /* Tracks the column seqnum */
176   guint16 column_seq;
177   /* Column FEC packets must be delayed to make them more resilient
178    * to loss bursts, we store them here */
179   GQueue queued_column_packets;
180 };
181
182 #define RTP_CAPS "application/x-rtp"
183
184 static GstStaticPadTemplate fec_src_template =
185 GST_STATIC_PAD_TEMPLATE ("fec_%u",
186     GST_PAD_SRC,
187     GST_PAD_SOMETIMES,
188     GST_STATIC_CAPS (RTP_CAPS));
189
190 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
191     GST_PAD_SINK,
192     GST_PAD_ALWAYS,
193     GST_STATIC_CAPS (RTP_CAPS));
194
195 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
196     GST_PAD_SRC,
197     GST_PAD_ALWAYS,
198     GST_STATIC_CAPS (RTP_CAPS));
199
200 #define gst_rtpst_2022_1_fecenc_parent_class parent_class
201 G_DEFINE_TYPE (GstRTPST_2022_1_FecEnc, gst_rtpst_2022_1_fecenc,
202     GST_TYPE_ELEMENT);
203 GST_ELEMENT_REGISTER_DEFINE (rtpst2022_1_fecenc, "rtpst2022-1-fecenc",
204     GST_RANK_NONE, GST_TYPE_RTPST_2022_1_FECENC);
205
206 static void
207 free_item (Item * item)
208 {
209   if (item->buffer)
210     gst_buffer_unref (item->buffer);
211
212   g_free (item);
213 }
214
215 static void
216 free_fec_packet (FecPacket * packet)
217 {
218   if (packet->xored_payload)
219     g_free (packet->xored_payload);
220   g_free (packet);
221 }
222
223 static void
224 _xor_mem (guint8 * restrict dst, const guint8 * restrict src, gsize length)
225 {
226   guint i;
227
228   for (i = 0; i < (length / sizeof (guint64)); ++i) {
229 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
230     GST_WRITE_UINT64_LE (dst,
231         GST_READ_UINT64_LE (dst) ^ GST_READ_UINT64_LE (src));
232 #else
233     GST_WRITE_UINT64_BE (dst,
234         GST_READ_UINT64_BE (dst) ^ GST_READ_UINT64_BE (src));
235 #endif
236     dst += sizeof (guint64);
237     src += sizeof (guint64);
238   }
239   for (i = 0; i < (length % sizeof (guint64)); ++i)
240     dst[i] ^= src[i];
241 }
242
243 static void
244 fec_packet_update (FecPacket * fec, GstRTPBuffer * rtp)
245 {
246   if (fec->n_packets == 0) {
247     fec->seq_base = gst_rtp_buffer_get_seq (rtp);
248     fec->payload_len = gst_rtp_buffer_get_payload_len (rtp);
249     fec->xored_payload_len = gst_rtp_buffer_get_payload_len (rtp);
250     fec->xored_pt = gst_rtp_buffer_get_payload_type (rtp);
251     fec->xored_timestamp = gst_rtp_buffer_get_timestamp (rtp);
252     fec->xored_marker = gst_rtp_buffer_get_marker (rtp);
253     fec->xored_padding = gst_rtp_buffer_get_padding (rtp);
254     fec->xored_extension = gst_rtp_buffer_get_extension (rtp);
255     fec->xored_payload = g_malloc (sizeof (guint8) * fec->payload_len);
256     memcpy (fec->xored_payload, gst_rtp_buffer_get_payload (rtp),
257         fec->payload_len);
258   } else {
259     guint plen = gst_rtp_buffer_get_payload_len (rtp);
260
261     if (fec->payload_len < plen) {
262       fec->xored_payload =
263           g_realloc (fec->xored_payload, sizeof (guint8) * plen);
264       memset (fec->xored_payload + fec->payload_len, 0,
265           plen - fec->payload_len);
266       fec->payload_len = plen;
267     }
268
269     fec->xored_payload_len ^= plen;
270     fec->xored_pt ^= gst_rtp_buffer_get_payload_type (rtp);
271     fec->xored_timestamp ^= gst_rtp_buffer_get_timestamp (rtp);
272     fec->xored_marker ^= gst_rtp_buffer_get_marker (rtp);
273     fec->xored_padding ^= gst_rtp_buffer_get_padding (rtp);
274     fec->xored_extension ^= gst_rtp_buffer_get_extension (rtp);
275     _xor_mem (fec->xored_payload, gst_rtp_buffer_get_payload (rtp), plen);
276   }
277
278   fec->n_packets += 1;
279 }
280
281 static void
282 push_initial_events (GstRTPST_2022_1_FecEnc * enc, GstPad * pad,
283     const gchar * id)
284 {
285   gchar *stream_id;
286   GstCaps *caps;
287   GstSegment segment;
288
289   stream_id = gst_pad_create_stream_id (pad, GST_ELEMENT (enc), id);
290   gst_pad_push_event (pad, gst_event_new_stream_start (stream_id));
291   g_free (stream_id);
292
293   caps = gst_caps_new_simple ("application/x-rtp",
294       "payload", G_TYPE_UINT, enc->pt, "ssrc", G_TYPE_UINT, 0, NULL);
295   gst_pad_push_event (pad, gst_event_new_caps (caps));
296   gst_caps_unref (caps);
297
298   gst_segment_init (&segment, GST_FORMAT_TIME);
299   gst_pad_push_event (pad, gst_event_new_segment (&segment));
300 }
301
302 static void
303 queue_fec_packet (GstRTPST_2022_1_FecEnc * enc, FecPacket * fec, gboolean row)
304 {
305   GstBuffer *buffer = gst_rtp_buffer_new_allocate (fec->payload_len + 16, 0, 0);
306   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
307   GstBitWriter bits;
308   guint8 *data;
309
310   gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp);
311   data = gst_rtp_buffer_get_payload (&rtp);
312   memset (data, 0x00, 16);
313
314   gst_bit_writer_init_with_data (&bits, data, 17, FALSE);
315
316   gst_bit_writer_put_bits_uint16 (&bits, fec->seq_base, 16);    /* SNBase low bits */
317   gst_bit_writer_put_bits_uint16 (&bits, fec->xored_payload_len, 16);   /* Length Recovery */
318   gst_bit_writer_put_bits_uint8 (&bits, 1, 1);  /* E */
319   gst_bit_writer_put_bits_uint8 (&bits, fec->xored_pt, 7);      /* PT recovery */
320   gst_bit_writer_put_bits_uint32 (&bits, 0, 24);        /* Mask */
321   gst_bit_writer_put_bits_uint32 (&bits, fec->xored_timestamp, 32);     /* TS recovery */
322   gst_bit_writer_put_bits_uint8 (&bits, 0, 1);  /* N */
323   gst_bit_writer_put_bits_uint8 (&bits, row ? 1 : 0, 1);        /* D */
324   gst_bit_writer_put_bits_uint8 (&bits, 0, 3);  /* type */
325   gst_bit_writer_put_bits_uint8 (&bits, 0, 3);  /* index */
326   gst_bit_writer_put_bits_uint8 (&bits, row ? 1 : enc->l, 8);   /* Offset */
327   gst_bit_writer_put_bits_uint8 (&bits, fec->n_packets, 8);     /* NA */
328   gst_bit_writer_put_bits_uint8 (&bits, 0, 8);  /* SNBase ext bits */
329
330   memcpy (data + 16, fec->xored_payload, fec->payload_len);
331
332   gst_bit_writer_reset (&bits);
333
334   gst_rtp_buffer_set_payload_type (&rtp, enc->pt);
335   gst_rtp_buffer_set_seq (&rtp, row ? enc->row_seq++ : enc->column_seq++);
336   gst_rtp_buffer_set_marker (&rtp, fec->xored_marker);
337   gst_rtp_buffer_set_padding (&rtp, fec->xored_padding);
338   gst_rtp_buffer_set_extension (&rtp, fec->xored_extension);
339
340   /* We're sending it out immediately */
341   if (row)
342     gst_rtp_buffer_set_timestamp (&rtp, enc->last_media_timestamp);
343
344   gst_rtp_buffer_unmap (&rtp);
345
346   /* We can send row FEC packets immediately, column packets need
347    * delaying by L <= delay < L * D
348    */
349   if (row) {
350     GstFlowReturn ret;
351
352     GST_LOG_OBJECT (enc,
353         "Pushing row FEC packet, seq base: %u, media seqnum: %u",
354         fec->seq_base, enc->last_media_seqnum);
355
356     /* Safe to unlock here */
357     GST_OBJECT_UNLOCK (enc);
358     ret = gst_pad_push (enc->row_fec_srcpad, buffer);
359     GST_OBJECT_LOCK (enc);
360
361     if (ret != GST_FLOW_OK && ret != GST_FLOW_FLUSHING)
362       GST_WARNING_OBJECT (enc->row_fec_srcpad,
363           "Failed to push row FEC packet: %s", gst_flow_get_name (ret));
364   } else {
365     Item *item = g_malloc0 (sizeof (Item));
366
367     item->buffer = buffer;
368     item->seq_base = fec->seq_base;
369     /* Let's get cute and linearize */
370     item->target_media_seq =
371         enc->last_media_seqnum + enc->l - enc->current_column +
372         enc->d * enc->current_column;
373
374     g_queue_push_tail (&enc->queued_column_packets, item);
375   }
376 }
377
378 static GstFlowReturn
379 gst_rtpst_2022_1_fecenc_sink_chain (GstPad * pad, GstObject * parent,
380     GstBuffer * buffer)
381 {
382   GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (parent);
383   GstFlowReturn ret = GST_FLOW_OK;
384   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
385
386   if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp)) {
387     GST_ERROR_OBJECT (enc, "Chained buffer isn't valid RTP");
388     goto error;
389   }
390
391   if (gst_rtp_buffer_get_ssrc (&rtp) != 0) {
392     GST_ERROR_OBJECT (enc, "Chained buffer must have SSRC == 0");
393     goto error;
394   }
395
396   if (enc->last_media_seqnum_set
397       && (guint16) (enc->last_media_seqnum + 1) !=
398       gst_rtp_buffer_get_seq (&rtp)) {
399     GST_ERROR_OBJECT (enc, "consecutive sequence numbers are required");
400     goto error;
401   }
402
403   if (!enc->row_events_pushed) {
404     push_initial_events (enc, enc->row_fec_srcpad, "row-fec");
405     enc->row_events_pushed = TRUE;
406   }
407
408   if (!enc->column_events_pushed) {
409     push_initial_events (enc, enc->column_fec_srcpad, "column-fec");
410     enc->column_events_pushed = TRUE;
411   }
412
413   enc->last_media_timestamp = gst_rtp_buffer_get_timestamp (&rtp);
414   enc->last_media_seqnum = gst_rtp_buffer_get_seq (&rtp);
415   enc->last_media_seqnum_set = TRUE;
416
417   GST_OBJECT_LOCK (enc);
418   if (enc->enable_row && enc->l) {
419     g_assert (enc->row->n_packets < enc->l);
420     fec_packet_update (enc->row, &rtp);
421     if (enc->row->n_packets == enc->l) {
422       queue_fec_packet (enc, enc->row, TRUE);
423       g_free (enc->row->xored_payload);
424       memset (enc->row, 0x00, sizeof (FecPacket));
425     }
426   }
427
428   if (enc->enable_column && enc->l && enc->d) {
429     FecPacket *column = g_ptr_array_index (enc->columns, enc->current_column);
430
431     fec_packet_update (column, &rtp);
432     if (column->n_packets == enc->d) {
433       queue_fec_packet (enc, column, FALSE);
434       g_free (column->xored_payload);
435       memset (column, 0x00, sizeof (FecPacket));
436     }
437
438     enc->current_column++;
439     enc->current_column %= enc->l;
440   }
441
442   gst_rtp_buffer_unmap (&rtp);
443
444   if (g_queue_get_length (&enc->queued_column_packets) > 0) {
445     Item *item = g_queue_peek_head (&enc->queued_column_packets);
446
447     if (item->target_media_seq == enc->last_media_seqnum) {
448       GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
449
450       g_queue_pop_head (&enc->queued_column_packets);
451       GST_LOG_OBJECT (enc,
452           "Pushing column FEC packet, seq base: %u, media seqnum: %u",
453           item->seq_base, enc->last_media_seqnum);
454       gst_rtp_buffer_map (item->buffer, GST_MAP_WRITE, &rtp);
455       gst_rtp_buffer_set_timestamp (&rtp, enc->last_media_timestamp);
456       gst_rtp_buffer_unmap (&rtp);
457       GST_OBJECT_UNLOCK (enc);
458       ret =
459           gst_pad_push (enc->column_fec_srcpad, gst_buffer_ref (item->buffer));
460       GST_OBJECT_LOCK (enc);
461
462       if (ret != GST_FLOW_OK && ret != GST_FLOW_FLUSHING)
463         GST_WARNING_OBJECT (enc->column_fec_srcpad,
464             "Failed to push column FEC packet: %s", gst_flow_get_name (ret));
465
466       free_item (item);
467     }
468   }
469   GST_OBJECT_UNLOCK (enc);
470
471   ret = gst_pad_push (enc->srcpad, buffer);
472
473 done:
474   return ret;
475
476 error:
477   if (rtp.buffer)
478     gst_rtp_buffer_unmap (&rtp);
479   gst_buffer_unref (buffer);
480   ret = GST_FLOW_ERROR;
481   goto done;
482 }
483
484 static GstIterator *
485 gst_rtpst_2022_1_fecenc_iterate_linked_pads (GstPad * pad, GstObject * parent)
486 {
487   GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (parent);
488   GstPad *otherpad = NULL;
489   GstIterator *it = NULL;
490   GValue val = { 0, };
491
492   if (pad == enc->srcpad)
493     otherpad = enc->sinkpad;
494   else if (pad == enc->sinkpad)
495     otherpad = enc->srcpad;
496
497   if (otherpad) {
498     g_value_init (&val, GST_TYPE_PAD);
499     g_value_set_object (&val, otherpad);
500     it = gst_iterator_new_single (GST_TYPE_PAD, &val);
501     g_value_unset (&val);
502   }
503
504   return it;
505 }
506
507 static void
508 gst_rtpst_2022_1_fecenc_reset (GstRTPST_2022_1_FecEnc * enc, gboolean allocate)
509 {
510   if (enc->row) {
511     free_fec_packet (enc->row);
512     enc->row = NULL;
513   }
514
515   if (enc->columns) {
516     g_ptr_array_unref (enc->columns);
517     enc->columns = NULL;
518   }
519
520   if (enc->row_fec_srcpad) {
521     gst_element_remove_pad (GST_ELEMENT (enc), enc->row_fec_srcpad);
522     enc->row_fec_srcpad = NULL;
523   }
524
525   if (enc->column_fec_srcpad) {
526     gst_element_remove_pad (GST_ELEMENT (enc), enc->column_fec_srcpad);
527     enc->column_fec_srcpad = NULL;
528   }
529
530   g_queue_clear_full (&enc->queued_column_packets, (GDestroyNotify) free_item);
531
532   if (allocate) {
533     guint i;
534
535     enc->row = g_malloc0 (sizeof (FecPacket));
536     enc->columns =
537         g_ptr_array_new_full (enc->l, (GDestroyNotify) free_fec_packet);
538
539     for (i = 0; i < enc->l; i++) {
540       g_ptr_array_add (enc->columns, g_malloc0 (sizeof (FecPacket)));
541     }
542
543     g_queue_init (&enc->queued_column_packets);
544
545     enc->column_fec_srcpad =
546         gst_pad_new_from_static_template (&fec_src_template, "fec_0");
547     gst_pad_set_active (enc->column_fec_srcpad, TRUE);
548     gst_pad_set_iterate_internal_links_function (enc->column_fec_srcpad,
549         GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_iterate_linked_pads));
550     gst_element_add_pad (GST_ELEMENT (enc), enc->column_fec_srcpad);
551
552     enc->row_fec_srcpad =
553         gst_pad_new_from_static_template (&fec_src_template, "fec_1");
554     gst_pad_set_active (enc->row_fec_srcpad, TRUE);
555     gst_pad_set_iterate_internal_links_function (enc->row_fec_srcpad,
556         GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_iterate_linked_pads));
557     gst_element_add_pad (GST_ELEMENT (enc), enc->row_fec_srcpad);
558
559     gst_element_no_more_pads (GST_ELEMENT (enc));
560   }
561
562   enc->current_column = 0;
563   enc->last_media_seqnum_set = FALSE;
564 }
565
566 static GstStateChangeReturn
567 gst_rtpst_2022_1_fecenc_change_state (GstElement * element,
568     GstStateChange transition)
569 {
570   GstStateChangeReturn ret;
571   GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (element);
572
573   switch (transition) {
574     case GST_STATE_CHANGE_READY_TO_PAUSED:
575       gst_rtpst_2022_1_fecenc_reset (enc, TRUE);
576       break;
577     case GST_STATE_CHANGE_PAUSED_TO_READY:
578       gst_rtpst_2022_1_fecenc_reset (enc, FALSE);
579       break;
580     default:
581       break;
582   }
583
584   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
585
586   return ret;
587 }
588
589 static void
590 gst_rtpst_2022_1_fecenc_finalize (GObject * object)
591 {
592   GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (object);
593
594   gst_rtpst_2022_1_fecenc_reset (enc, FALSE);
595
596   G_OBJECT_CLASS (parent_class)->finalize (object);
597 }
598
599 static void
600 gst_rtpst_2022_1_fecenc_set_property (GObject * object, guint prop_id,
601     const GValue * value, GParamSpec * pspec)
602 {
603   GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (object);
604
605   if (GST_STATE (enc) > GST_STATE_READY) {
606     GST_ERROR_OBJECT (enc,
607         "rtpst2022-1-fecenc properties can't be changed in PLAYING or PAUSED state");
608     return;
609   }
610
611   switch (prop_id) {
612     case PROP_COLUMNS:
613       enc->l = g_value_get_uint (value);
614       break;
615     case PROP_ROWS:
616       enc->d = g_value_get_uint (value);
617       break;
618     case PROP_PT:
619       enc->pt = g_value_get_int (value);
620       break;
621     case PROP_ENABLE_COLUMN:
622       GST_OBJECT_LOCK (enc);
623       enc->enable_column = g_value_get_boolean (value);
624       if (!enc->enable_column) {
625         guint i;
626
627         if (enc->columns) {
628           for (i = 0; i < enc->l; i++) {
629             FecPacket *column = g_ptr_array_index (enc->columns, i);
630             g_free (column->xored_payload);
631             memset (column, 0x00, sizeof (FecPacket));
632           }
633         }
634         enc->current_column = 0;
635         enc->column_seq = 0;
636         g_queue_clear_full (&enc->queued_column_packets,
637             (GDestroyNotify) free_item);
638       }
639       GST_OBJECT_UNLOCK (enc);
640       break;
641     case PROP_ENABLE_ROW:
642       GST_OBJECT_LOCK (enc);
643       enc->enable_row = g_value_get_boolean (value);
644       GST_OBJECT_UNLOCK (enc);
645       break;
646     default:
647       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
648       break;
649   }
650 }
651
652 static void
653 gst_rtpst_2022_1_fecenc_get_property (GObject * object, guint prop_id,
654     GValue * value, GParamSpec * pspec)
655 {
656   GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (object);
657
658   switch (prop_id) {
659     case PROP_COLUMNS:
660       g_value_set_uint (value, enc->l);
661       break;
662     case PROP_ROWS:
663       g_value_set_uint (value, enc->d);
664       break;
665     case PROP_PT:
666       g_value_set_int (value, enc->pt);
667       break;
668     case PROP_ENABLE_COLUMN:
669       GST_OBJECT_LOCK (enc);
670       g_value_set_boolean (value, enc->enable_column);
671       GST_OBJECT_UNLOCK (enc);
672       break;
673     case PROP_ENABLE_ROW:
674       GST_OBJECT_LOCK (enc);
675       g_value_set_boolean (value, enc->enable_row);
676       GST_OBJECT_UNLOCK (enc);
677       break;
678     default:
679       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
680       break;
681   }
682 }
683
684 static gboolean
685 gst_2d_fec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
686 {
687   GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (parent);
688   gboolean ret;
689
690   if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP)
691     gst_rtpst_2022_1_fecenc_reset (enc, TRUE);
692
693   ret = gst_pad_event_default (pad, parent, event);
694
695   return ret;
696 }
697
698 static void
699 gst_rtpst_2022_1_fecenc_class_init (GstRTPST_2022_1_FecEncClass * klass)
700 {
701   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
702   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
703
704   gobject_class->set_property =
705       GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_set_property);
706   gobject_class->get_property =
707       GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_get_property);
708   gobject_class->finalize =
709       GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_finalize);
710
711   g_object_class_install_property (gobject_class, PROP_COLUMNS,
712       g_param_spec_uint ("columns", "Columns",
713           "Number of columns to apply row FEC on, 0=disabled", 0,
714           255, DEFAULT_COLUMNS,
715           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
716           GST_PARAM_MUTABLE_READY));
717
718   g_object_class_install_property (gobject_class, PROP_ROWS,
719       g_param_spec_uint ("rows", "Rows",
720           "Number of rows to apply column FEC on, 0=disabled", 0,
721           255, DEFAULT_ROWS,
722           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
723           GST_PARAM_MUTABLE_READY));
724
725   g_object_class_install_property (gobject_class, PROP_PT,
726       g_param_spec_int ("pt", "Payload Type",
727           "The payload type of FEC packets", 96,
728           255, DEFAULT_PT,
729           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
730           GST_PARAM_MUTABLE_READY));
731
732   g_object_class_install_property (gobject_class, PROP_ENABLE_COLUMN,
733       g_param_spec_boolean ("enable-column-fec", "Enable Column FEC",
734           "Whether the encoder should compute and send column FEC",
735           DEFAULT_ENABLE_COLUMN,
736           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
737           GST_PARAM_MUTABLE_PLAYING));
738
739   g_object_class_install_property (gobject_class, PROP_ENABLE_ROW,
740       g_param_spec_boolean ("enable-row-fec", "Enable Row FEC",
741           "Whether the encoder should compute and send row FEC",
742           DEFAULT_ENABLE_ROW,
743           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
744           GST_PARAM_MUTABLE_PLAYING));
745
746   gstelement_class->change_state =
747       GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_change_state);
748
749   gst_element_class_set_static_metadata (gstelement_class,
750       "SMPTE 2022-1 FEC encoder", "SMPTE 2022-1 FEC encoding",
751       "performs FEC as described by SMPTE 2022-1",
752       "Mathieu Duponchelle <mathieu@centricular.com>");
753
754   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
755   gst_element_class_add_static_pad_template (gstelement_class,
756       &fec_src_template);
757   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
758
759   GST_DEBUG_CATEGORY_INIT (gst_rtpst_2022_1_fecenc_debug,
760       "rtpst2022-1-fecenc", 0, "SMPTE 2022-1 FEC encoder element");
761 }
762
763 static void
764 gst_rtpst_2022_1_fecenc_init (GstRTPST_2022_1_FecEnc * enc)
765 {
766   enc->srcpad = gst_pad_new_from_static_template (&src_template, "src");
767   gst_pad_use_fixed_caps (enc->srcpad);
768   GST_PAD_SET_PROXY_CAPS (enc->srcpad);
769   gst_pad_set_iterate_internal_links_function (enc->srcpad,
770       GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_iterate_linked_pads));
771   gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
772
773   enc->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
774   GST_PAD_SET_PROXY_CAPS (enc->sinkpad);
775   gst_pad_set_chain_function (enc->sinkpad, gst_rtpst_2022_1_fecenc_sink_chain);
776   gst_pad_set_event_function (enc->sinkpad,
777       GST_DEBUG_FUNCPTR (gst_2d_fec_sink_event));
778   gst_pad_set_iterate_internal_links_function (enc->sinkpad,
779       GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_iterate_linked_pads));
780   gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
781
782   enc->d = 0;
783   enc->l = 0;
784 }