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