2 * Copyright (C) <2020> Mathieu Duponchelle <mathieu@centricular.com>
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.
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.
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.
21 * SECTION:element-rtpst2022-1-fecenc
22 * @see_also: #element-rtpst2022-1-fecdec
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
30 * ## sender / receiver example
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
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
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
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.
68 #include <gst/base/base.h>
69 #include <gst/rtp/gstrtpbuffer.h>
71 #include "gstrtpst2022-1-fecenc.h"
73 GST_DEBUG_CATEGORY_STATIC (gst_rtpst_2022_1_fecenc_debug);
74 #define GST_CAT_DEFAULT gst_rtpst_2022_1_fecenc_debug
86 #define DEFAULT_ROWS 0
87 #define DEFAULT_COLUMNS 0
89 #define DEFAULT_ENABLE_COLUMN TRUE
90 #define DEFAULT_ENABLE_ROW TRUE
94 guint16 target_media_seq; /* The media seqnum we want to send that packet alongside */
95 guint16 seq_base; /* Only used for logging purposes */
101 guint8 *xored_payload;
102 guint32 xored_timestamp;
104 guint16 xored_payload_len;
105 gboolean xored_marker;
106 gboolean xored_padding;
107 gboolean xored_extension;
115 struct _GstRTPST_2022_1_FecEncClass
117 GstElementClass class;
120 struct _GstRTPST_2022_1_FecEnc
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
131 GstPad *row_fec_srcpad;
132 GstPad *column_fec_srcpad;
134 /* The following fields are only accessed on state change or from the
135 * streaming thread, and only settable in state < PAUSED */
142 /* Whether we have pushed initial events on the column FEC source pad */
143 gboolean column_events_pushed;
145 /* The current row FEC packet */
147 /* Tracks the row seqnum */
149 /* Whether we have pushed initial events on the row FEC source pad */
150 gboolean row_events_pushed;
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;
157 /* This field is used to timestamp our FEC packets, we just piggy back */
158 guint32 last_media_timestamp;
160 /* The payload type of the FEC packets */
163 /* The following fields can be changed while PLAYING, and are
164 * protected with the OBJECT_LOCK
166 /* Tracks the property, can be changed while PLAYING */
168 /* Tracks the property, can be changed while PLAYING */
169 gboolean enable_column;
171 /* Array of FecPackets, with size enc->l */
173 /* Index of the current column in the array above */
174 guint current_column;
175 /* Tracks the column seqnum */
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;
182 #define RTP_CAPS "application/x-rtp"
184 static GstStaticPadTemplate fec_src_template =
185 GST_STATIC_PAD_TEMPLATE ("fec_%u",
188 GST_STATIC_CAPS (RTP_CAPS));
190 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
193 GST_STATIC_CAPS (RTP_CAPS));
195 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
198 GST_STATIC_CAPS (RTP_CAPS));
200 #define gst_rtpst_2022_1_fecenc_parent_class parent_class
201 G_DEFINE_TYPE (GstRTPST_2022_1_FecEnc, gst_rtpst_2022_1_fecenc,
203 GST_ELEMENT_REGISTER_DEFINE (rtpst2022_1_fecenc, "rtpst2022-1-fecenc",
204 GST_RANK_NONE, GST_TYPE_RTPST_2022_1_FECENC);
207 free_item (Item * item)
210 gst_buffer_unref (item->buffer);
216 free_fec_packet (FecPacket * packet)
218 if (packet->xored_payload)
219 g_free (packet->xored_payload);
224 _xor_mem (guint8 * restrict dst, const guint8 * restrict src, gsize length)
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));
233 GST_WRITE_UINT64_BE (dst,
234 GST_READ_UINT64_BE (dst) ^ GST_READ_UINT64_BE (src));
236 dst += sizeof (guint64);
237 src += sizeof (guint64);
239 for (i = 0; i < (length % sizeof (guint64)); ++i)
244 fec_packet_update (FecPacket * fec, GstRTPBuffer * rtp)
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),
259 guint plen = gst_rtp_buffer_get_payload_len (rtp);
261 if (fec->payload_len < plen) {
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;
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);
282 push_initial_events (GstRTPST_2022_1_FecEnc * enc, GstPad * pad,
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));
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);
298 gst_segment_init (&segment, GST_FORMAT_TIME);
299 gst_pad_push_event (pad, gst_event_new_segment (&segment));
303 queue_fec_packet (GstRTPST_2022_1_FecEnc * enc, FecPacket * fec, gboolean row)
305 GstBuffer *buffer = gst_rtp_buffer_new_allocate (fec->payload_len + 16, 0, 0);
306 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
310 gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp);
311 data = gst_rtp_buffer_get_payload (&rtp);
312 memset (data, 0x00, 16);
314 gst_bit_writer_init_with_data (&bits, data, 17, FALSE);
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 */
330 memcpy (data + 16, fec->xored_payload, fec->payload_len);
332 gst_bit_writer_reset (&bits);
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);
340 /* We're sending it out immediately */
342 gst_rtp_buffer_set_timestamp (&rtp, enc->last_media_timestamp);
344 gst_rtp_buffer_unmap (&rtp);
346 /* We can send row FEC packets immediately, column packets need
347 * delaying by L <= delay < L * D
353 "Pushing row FEC packet, seq base: %u, media seqnum: %u",
354 fec->seq_base, enc->last_media_seqnum);
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);
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));
365 Item *item = g_malloc0 (sizeof (Item));
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;
374 g_queue_push_tail (&enc->queued_column_packets, item);
379 gst_rtpst_2022_1_fecenc_sink_chain (GstPad * pad, GstObject * parent,
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;
386 if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp)) {
387 GST_ERROR_OBJECT (enc, "Chained buffer isn't valid RTP");
391 if (gst_rtp_buffer_get_ssrc (&rtp) != 0) {
392 GST_ERROR_OBJECT (enc, "Chained buffer must have SSRC == 0");
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");
403 if (!enc->row_events_pushed) {
404 push_initial_events (enc, enc->row_fec_srcpad, "row-fec");
405 enc->row_events_pushed = TRUE;
408 if (!enc->column_events_pushed) {
409 push_initial_events (enc, enc->column_fec_srcpad, "column-fec");
410 enc->column_events_pushed = TRUE;
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;
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));
428 if (enc->enable_column && enc->l && enc->d) {
429 FecPacket *column = g_ptr_array_index (enc->columns, enc->current_column);
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));
438 enc->current_column++;
439 enc->current_column %= enc->l;
442 gst_rtp_buffer_unmap (&rtp);
444 if (g_queue_get_length (&enc->queued_column_packets) > 0) {
445 Item *item = g_queue_peek_head (&enc->queued_column_packets);
447 if (item->target_media_seq == enc->last_media_seqnum) {
448 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
450 g_queue_pop_head (&enc->queued_column_packets);
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);
459 gst_pad_push (enc->column_fec_srcpad, gst_buffer_ref (item->buffer));
460 GST_OBJECT_LOCK (enc);
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));
469 GST_OBJECT_UNLOCK (enc);
471 ret = gst_pad_push (enc->srcpad, buffer);
478 gst_rtp_buffer_unmap (&rtp);
479 gst_buffer_unref (buffer);
480 ret = GST_FLOW_ERROR;
485 gst_rtpst_2022_1_fecenc_iterate_linked_pads (GstPad * pad, GstObject * parent)
487 GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (parent);
488 GstPad *otherpad = NULL;
489 GstIterator *it = NULL;
492 if (pad == enc->srcpad)
493 otherpad = enc->sinkpad;
494 else if (pad == enc->sinkpad)
495 otherpad = enc->srcpad;
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);
508 gst_rtpst_2022_1_fecenc_reset (GstRTPST_2022_1_FecEnc * enc, gboolean allocate)
511 free_fec_packet (enc->row);
516 g_ptr_array_unref (enc->columns);
520 if (enc->row_fec_srcpad) {
521 gst_element_remove_pad (GST_ELEMENT (enc), enc->row_fec_srcpad);
522 enc->row_fec_srcpad = NULL;
525 if (enc->column_fec_srcpad) {
526 gst_element_remove_pad (GST_ELEMENT (enc), enc->column_fec_srcpad);
527 enc->column_fec_srcpad = NULL;
530 g_queue_clear_full (&enc->queued_column_packets, (GDestroyNotify) free_item);
535 enc->row = g_malloc0 (sizeof (FecPacket));
537 g_ptr_array_new_full (enc->l, (GDestroyNotify) free_fec_packet);
539 for (i = 0; i < enc->l; i++) {
540 g_ptr_array_add (enc->columns, g_malloc0 (sizeof (FecPacket)));
543 g_queue_init (&enc->queued_column_packets);
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);
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);
559 gst_element_no_more_pads (GST_ELEMENT (enc));
562 enc->current_column = 0;
563 enc->last_media_seqnum_set = FALSE;
566 static GstStateChangeReturn
567 gst_rtpst_2022_1_fecenc_change_state (GstElement * element,
568 GstStateChange transition)
570 GstStateChangeReturn ret;
571 GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (element);
573 switch (transition) {
574 case GST_STATE_CHANGE_READY_TO_PAUSED:
575 gst_rtpst_2022_1_fecenc_reset (enc, TRUE);
577 case GST_STATE_CHANGE_PAUSED_TO_READY:
578 gst_rtpst_2022_1_fecenc_reset (enc, FALSE);
584 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
590 gst_rtpst_2022_1_fecenc_finalize (GObject * object)
592 GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (object);
594 gst_rtpst_2022_1_fecenc_reset (enc, FALSE);
596 G_OBJECT_CLASS (parent_class)->finalize (object);
600 gst_rtpst_2022_1_fecenc_set_property (GObject * object, guint prop_id,
601 const GValue * value, GParamSpec * pspec)
603 GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (object);
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");
613 enc->l = g_value_get_uint (value);
616 enc->d = g_value_get_uint (value);
619 enc->pt = g_value_get_int (value);
621 case PROP_ENABLE_COLUMN:
622 GST_OBJECT_LOCK (enc);
623 enc->enable_column = g_value_get_boolean (value);
624 if (!enc->enable_column) {
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));
634 enc->current_column = 0;
636 g_queue_clear_full (&enc->queued_column_packets,
637 (GDestroyNotify) free_item);
639 GST_OBJECT_UNLOCK (enc);
641 case PROP_ENABLE_ROW:
642 GST_OBJECT_LOCK (enc);
643 enc->enable_row = g_value_get_boolean (value);
644 GST_OBJECT_UNLOCK (enc);
647 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
653 gst_rtpst_2022_1_fecenc_get_property (GObject * object, guint prop_id,
654 GValue * value, GParamSpec * pspec)
656 GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (object);
660 g_value_set_uint (value, enc->l);
663 g_value_set_uint (value, enc->d);
666 g_value_set_int (value, enc->pt);
668 case PROP_ENABLE_COLUMN:
669 GST_OBJECT_LOCK (enc);
670 g_value_set_boolean (value, enc->enable_column);
671 GST_OBJECT_UNLOCK (enc);
673 case PROP_ENABLE_ROW:
674 GST_OBJECT_LOCK (enc);
675 g_value_set_boolean (value, enc->enable_row);
676 GST_OBJECT_UNLOCK (enc);
679 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
685 gst_2d_fec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
687 GstRTPST_2022_1_FecEnc *enc = GST_RTPST_2022_1_FECENC_CAST (parent);
690 if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP)
691 gst_rtpst_2022_1_fecenc_reset (enc, TRUE);
693 ret = gst_pad_event_default (pad, parent, event);
699 gst_rtpst_2022_1_fecenc_class_init (GstRTPST_2022_1_FecEncClass * klass)
701 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
702 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
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);
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));
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,
722 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
723 GST_PARAM_MUTABLE_READY));
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,
729 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
730 GST_PARAM_MUTABLE_READY));
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));
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",
743 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
744 GST_PARAM_MUTABLE_PLAYING));
746 gstelement_class->change_state =
747 GST_DEBUG_FUNCPTR (gst_rtpst_2022_1_fecenc_change_state);
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>");
754 gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
755 gst_element_class_add_static_pad_template (gstelement_class,
757 gst_element_class_add_static_pad_template (gstelement_class, &src_template);
759 GST_DEBUG_CATEGORY_INIT (gst_rtpst_2022_1_fecenc_debug,
760 "rtpst2022-1-fecenc", 0, "SMPTE 2022-1 FEC encoder element");
764 gst_rtpst_2022_1_fecenc_init (GstRTPST_2022_1_FecEnc * enc)
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);
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);