• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer RIST plugin
2  * Copyright (C) 2019 Net Insight AB
3  *     Author: Olivier Crete <olivier.crete@collabora.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-ristrtpext
23  * @title: ristrtpext
24  * @see_also: ristsink
25  *
26  * This elements adds the RTP header extension defined by the RIST profile.
27  *
28  * If the GstRistRtpExt::drop-null-ts-packets property is set, then it
29  * will try to parse a MPEG Transport Stream inside the RTP packets
30  * and look for "null" packets among the first 7 TS packets and remove
31  * them, and mark their removal in the header.
32  *
33  * If the GstRistRtpExt::sequence-number-extension property is set, it will add
34  * a RTP sequence number roll-over counter to the RTP header extension. This
35  * code assumes that packets inserted to this element are never more than half
36  * of the sequence number space (2^15) away from the latest. Re-transmissions
37  * should therefore be done after processing with this element.
38  *
39  * If the GstRistRtpExt::drop-null-ts-packets and
40  * GstRistRtpExt::sequence-number-extension properties are both FALSE, it is
41  * pass through.
42  */
43 
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47 
48 #include <gst/rtp/rtp.h>
49 
50 #include "gstrist.h"
51 
52 GST_DEBUG_CATEGORY_STATIC (gst_rist_rtp_ext_debug);
53 #define GST_CAT_DEFAULT gst_rist_rtp_ext_debug
54 
55 enum
56 {
57   PROP_DROP_NULL_TS_PACKETS = 1,
58   PROP_SEQUENCE_NUMBER_EXTENSION
59 };
60 
61 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
62     GST_PAD_SRC,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS ("application/x-rtp"));
65 
66 
67 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
68     GST_PAD_SINK,
69     GST_PAD_ALWAYS,
70     GST_STATIC_CAPS ("application/x-rtp"));
71 
72 
73 struct _GstRistRtpExt
74 {
75   GstElement parent;
76 
77   GstPad *srcpad, *sinkpad;
78 
79   gboolean drop_null;
80   gboolean add_seqnumext;
81 
82   guint32 extseqnum;
83 };
84 
85 G_DEFINE_TYPE_WITH_CODE (GstRistRtpExt, gst_rist_rtp_ext, GST_TYPE_ELEMENT,
86     GST_DEBUG_CATEGORY_INIT (gst_rist_rtp_ext_debug, "ristrtpext", 0,
87         "RIST RTP Extension"));
88 GST_ELEMENT_REGISTER_DEFINE (ristrtpext, "ristrtpext", GST_RANK_NONE,
89     GST_TYPE_RIST_RTP_EXT);
90 
91 static GstFlowReturn
gst_rist_rtp_ext_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)92 gst_rist_rtp_ext_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
93 {
94   GstRistRtpExt *self = GST_RIST_RTP_EXT (parent);
95   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
96   gboolean drop_null = self->drop_null;
97   gboolean ts_packet_size = 0;
98   guint ts_packet_count = 0;
99   guint8 npd_bits = 0;
100   gboolean num_packets_deleted = 0;
101   guint8 *data;
102   guint wordlen;
103 
104   if (!self->drop_null && !self->add_seqnumext)
105     return gst_pad_push (self->srcpad, buffer);
106 
107   if (self->drop_null) {
108     if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp)) {
109       GST_ELEMENT_ERROR (self, STREAM, MUX, (NULL),
110           ("Could not map RTP buffer"));
111       goto mapping_error;
112     }
113 
114     if (gst_rtp_buffer_get_payload_type (&rtp) == GST_RTP_PAYLOAD_MP2T) {
115       if (gst_rtp_buffer_get_payload_len (&rtp) % 188 == 0) {
116         ts_packet_size = 188;
117         ts_packet_count = gst_rtp_buffer_get_payload_len (&rtp) / 188;
118       } else if (gst_rtp_buffer_get_payload_len (&rtp) % 204 == 0) {
119         ts_packet_size = 204;
120         ts_packet_count = gst_rtp_buffer_get_payload_len (&rtp) / 204;
121       } else {
122         drop_null = FALSE;
123       }
124     }
125     gst_rtp_buffer_unmap (&rtp);
126   }
127 
128   buffer = gst_buffer_make_writable (buffer);
129 
130   if (!gst_rtp_buffer_map (buffer, GST_MAP_READWRITE, &rtp)) {
131     GST_ELEMENT_ERROR (self, STREAM, MUX, (NULL), ("Could not map RTP buffer"));
132     goto mapping_error;
133   }
134 
135   if (drop_null) {
136     guint8 *data = gst_rtp_buffer_get_payload (&rtp);
137     guint plen = gst_rtp_buffer_get_payload_len (&rtp);
138     guint i;
139 
140     if (gst_rtp_buffer_get_padding (&rtp)) {
141       GST_ELEMENT_ERROR (self, STREAM, MUX, (NULL),
142           ("FIXME: Can not remove null TS packets if RTP padding is present"));
143       goto mapping_error;
144     }
145 
146     for (i = 0; i < MIN (ts_packet_count, 7); i++) {
147       guint offset = (i - num_packets_deleted) * ts_packet_size;
148       guint16 pid;
149 
150       /* Look for sync byte (0x47) at the start of TS packets */
151       if (data[offset] != 0x47) {
152         GST_ELEMENT_ERROR (self, STREAM, MUX, (NULL),
153             ("Buffer does not contain valid MP2T data,"
154                 " the sync byte is not present"));
155         goto error_mapped;
156       }
157 
158       pid = ((data[offset + 1] & 0x1F) << 8) | data[offset + 2];
159       /* is NULL packet (PID == 0x1FFF means null) */
160       if (pid == 0x1FFF) {
161         guint remaining_plen = plen - (num_packets_deleted * ts_packet_size);
162 
163         num_packets_deleted++;
164         npd_bits |= 1 << (6 - i);
165         if (offset + ts_packet_size < remaining_plen)
166           memmove (data + offset, data + offset + ts_packet_size,
167               remaining_plen - offset - ts_packet_size);
168       }
169     }
170   }
171 
172   if (gst_rtp_buffer_get_extension (&rtp)) {
173     GST_ELEMENT_ERROR (self, STREAM, MUX, (NULL),
174         ("RTP buffer already has an extension set"));
175     goto error_mapped;
176   }
177 
178   gst_rtp_buffer_set_extension (&rtp, TRUE);
179   gst_rtp_buffer_set_extension_data (&rtp, 'R' << 8 | 'I', 1);
180   gst_rtp_buffer_get_extension_data (&rtp, NULL, (void **) &data, &wordlen);
181 
182   data[0] = drop_null << 7;
183   data[0] |= self->add_seqnumext << 6;
184   if (ts_packet_count <= 7)
185     data[0] |= (ts_packet_count & 7) << 3;      /* Size */
186 
187   data[1] = (ts_packet_size == 204) << 7;
188   data[1] |= (npd_bits & 0x7F);
189 
190   if (self->add_seqnumext) {
191     guint16 seqnum = gst_rtp_buffer_get_seq (&rtp);
192     guint32 extseqnum;
193 
194     if (GST_BUFFER_IS_DISCONT (buffer))
195       self->extseqnum = -1;
196 
197     extseqnum = gst_rist_rtp_ext_seq (&self->extseqnum, seqnum);
198 
199     GST_WRITE_UINT16_BE (data + 2, (extseqnum >> 16));
200   }
201 
202   gst_rtp_buffer_unmap (&rtp);
203 
204   if (num_packets_deleted != 0)
205     gst_buffer_resize (buffer, 0,
206         gst_buffer_get_size (buffer) - (ts_packet_size * num_packets_deleted));
207 
208   return gst_pad_push (self->srcpad, buffer);
209 
210 mapping_error:
211   gst_buffer_unref (buffer);
212   return GST_FLOW_ERROR;
213 
214 error_mapped:
215   gst_rtp_buffer_unmap (&rtp);
216   gst_buffer_unref (buffer);
217   return GST_FLOW_ERROR;
218 }
219 
220 static void
gst_rist_rtp_ext_init(GstRistRtpExt * self)221 gst_rist_rtp_ext_init (GstRistRtpExt * self)
222 {
223   self->extseqnum = -1;
224 
225   self->sinkpad = gst_pad_new_from_static_template (&sink_templ,
226       sink_templ.name_template);
227   self->srcpad = gst_pad_new_from_static_template (&src_templ,
228       src_templ.name_template);
229 
230   GST_PAD_SET_PROXY_ALLOCATION (self->sinkpad);
231   GST_PAD_SET_PROXY_CAPS (self->sinkpad);
232   gst_pad_set_chain_function (self->sinkpad, gst_rist_rtp_ext_chain);
233 
234   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
235   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
236 }
237 
238 static void
gst_rist_rtp_ext_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)239 gst_rist_rtp_ext_get_property (GObject * object, guint prop_id,
240     GValue * value, GParamSpec * pspec)
241 {
242   GstRistRtpExt *self = GST_RIST_RTP_EXT (object);
243 
244   switch (prop_id) {
245     case PROP_DROP_NULL_TS_PACKETS:
246       g_value_set_boolean (value, self->drop_null);
247       break;
248     case PROP_SEQUENCE_NUMBER_EXTENSION:
249       g_value_set_boolean (value, self->add_seqnumext);
250       break;
251     default:
252       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253       break;
254   }
255 }
256 
257 static void
gst_rist_rtp_ext_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)258 gst_rist_rtp_ext_set_property (GObject * object, guint prop_id,
259     const GValue * value, GParamSpec * pspec)
260 {
261   GstRistRtpExt *self = GST_RIST_RTP_EXT (object);
262 
263   switch (prop_id) {
264     case PROP_DROP_NULL_TS_PACKETS:
265       self->drop_null = g_value_get_boolean (value);
266       break;
267     case PROP_SEQUENCE_NUMBER_EXTENSION:
268       self->add_seqnumext = g_value_get_boolean (value);
269       break;
270     default:
271       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
272       break;
273   }
274 }
275 
276 static void
gst_rist_rtp_ext_class_init(GstRistRtpExtClass * klass)277 gst_rist_rtp_ext_class_init (GstRistRtpExtClass * klass)
278 {
279   GstElementClass *element_class = (GstElementClass *) klass;
280   GObjectClass *object_class = (GObjectClass *) klass;
281 
282   gst_element_class_set_metadata (element_class,
283       "RIST RTP Extension adder", "Filter/Network",
284       "Adds RIST TR-06-2 RTP Header extension",
285       "Olivier Crete <olivier.crete@collabora.com");
286   gst_element_class_add_static_pad_template (element_class, &src_templ);
287   gst_element_class_add_static_pad_template (element_class, &sink_templ);
288 
289   object_class->get_property = gst_rist_rtp_ext_get_property;
290   object_class->set_property = gst_rist_rtp_ext_set_property;
291 
292   g_object_class_install_property (object_class, PROP_DROP_NULL_TS_PACKETS,
293       g_param_spec_boolean ("drop-null-ts-packets", "Drop null TS packets",
294           "Drop null MPEG-TS packet and replace them with a custom header"
295           " extension.", FALSE,
296           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
297   g_object_class_install_property (object_class, PROP_SEQUENCE_NUMBER_EXTENSION,
298       g_param_spec_boolean ("sequence-number-extension",
299           "Sequence Number Extension",
300           "Add sequence number extension to packets.", FALSE,
301           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
302 }
303