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-ristrtpdeext
23 * @title: ristrtpdeext
24 * @see_also: ristsrc
25 *
26 * This element removes the RTP header extension. If the RTP header extension
27 * contained information about remove MPEG Transport Stream null packets, it
28 * re-inserts them.
29 *
30 * If, according to the RTP sequence number and the sequence number
31 * extension in the RTP header extension, the packet is more than 2^16
32 * packets before the latest received, it will also drop it because it
33 * is too old for the jitterbuffer to handle properly.
34 */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <gst/rtp/rtp.h>
41
42 #include "gstrist.h"
43
44 GST_DEBUG_CATEGORY_STATIC (gst_rist_rtp_deext_debug);
45 #define GST_CAT_DEFAULT gst_rist_rtp_deext_debug
46
47 enum
48 {
49 PROP_0 = 0,
50 PROP_MAX_EXT_SEQNUM,
51 PROP_HAVE_EXT_SEQNUM
52 };
53
54 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
55 GST_PAD_SRC,
56 GST_PAD_ALWAYS,
57 GST_STATIC_CAPS ("application/x-rtp"));
58
59
60 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
61 GST_PAD_SINK,
62 GST_PAD_ALWAYS,
63 GST_STATIC_CAPS ("application/x-rtp"));
64
65
66 struct _GstRistRtpDeext
67 {
68 GstElement parent;
69
70 GstPad *srcpad, *sinkpad;
71
72 gboolean have_extseqnum;
73 guint32 max_extseqnum;
74 };
75
76 G_DEFINE_TYPE_WITH_CODE (GstRistRtpDeext, gst_rist_rtp_deext, GST_TYPE_ELEMENT,
77 GST_DEBUG_CATEGORY_INIT (gst_rist_rtp_deext_debug, "ristrtpdeext", 0,
78 "RIST RTP De-extension"));
79 GST_ELEMENT_REGISTER_DEFINE (ristrtpdeext, "ristrtpdeext", GST_RANK_NONE,
80 GST_TYPE_RIST_RTP_DEEXT);
81
82 static guint8
bit_count(guint8 value)83 bit_count (guint8 value)
84 {
85 guint8 count = 0;
86
87 while (value > 0) { /* until all bits are zero */
88 if ((value & 1) == 1) /* check lower bit */
89 count++;
90 value >>= 1; /* shift bits, removing lower bit */
91 }
92 return count;
93 }
94
95 static GstFlowReturn
gst_rist_rtp_deext_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)96 gst_rist_rtp_deext_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
97 {
98 GstRistRtpDeext *self = GST_RIST_RTP_DEEXT (parent);
99 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
100 GstBuffer *outbuf;
101 gboolean has_seqnum_ext;
102 gboolean has_drop_null;
103 gboolean ts_packet_size;
104 guint orig_ts_packet_count;
105 guint16 bits;
106 guint8 npd_bits;
107 guint8 num_packets_deleted;
108 guint extlen;
109 gpointer extdata = NULL;
110 guint8 *data = NULL;
111 guint8 *payload;
112 guint plen;
113 guint i;
114 GstMemory *mem = NULL;
115 GstMapInfo map;
116 guint num_restored = 0;
117 guint orig_payload_offset;
118 guint hdrlen;
119
120
121 if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp)) {
122 GST_ELEMENT_ERROR (self, STREAM, MUX, (NULL), ("Could not map RTP buffer"));
123 goto mapping_error;
124 }
125
126 if (!gst_rtp_buffer_get_extension_data (&rtp, &bits, &extdata, &extlen)) {
127 /* Has no extension, let's push out without modifying */
128 gst_rtp_buffer_unmap (&rtp);
129 return gst_pad_push (self->srcpad, buffer);
130 }
131
132 if (bits != ('R' << 8 | 'I')) {
133 gst_rtp_buffer_unmap (&rtp);
134 GST_LOG_OBJECT (self, "Buffer %" GST_PTR_FORMAT
135 " has an extension that's not the RIST one, ignoring", buffer);
136 return gst_pad_push (self->srcpad, buffer);
137 }
138
139 if (extlen != 1) {
140 gst_rtp_buffer_unmap (&rtp);
141 GST_LOG_OBJECT (self, "Buffer %" GST_PTR_FORMAT
142 " has a RIST extension that's not of length 1, ignoring", buffer);
143 return gst_pad_push (self->srcpad, buffer);
144 }
145
146 data = extdata;
147
148 has_drop_null = (data[0] >> 7) & 1; /* N */
149 has_seqnum_ext = (data[0] >> 6) & 1; /* E */
150 orig_ts_packet_count = (data[0] >> 3) & 7; /* Size */
151 ts_packet_size = ((data[1] >> 7) & 1) ? 204 : 188;
152 npd_bits = data[1] & 0x7F;
153
154 num_packets_deleted = bit_count (npd_bits);
155
156 self->have_extseqnum = has_seqnum_ext;
157
158 if (has_seqnum_ext) {
159 guint16 seqnumext_val = GST_READ_UINT16_BE (data + 2);
160 guint32 extseqnum = seqnumext_val << 16 | gst_rtp_buffer_get_seq (&rtp);
161
162 if (extseqnum < self->max_extseqnum &&
163 self->max_extseqnum - extseqnum > G_MAXINT16) {
164 gst_rtp_buffer_unmap (&rtp);
165 gst_buffer_unref (buffer);
166 GST_WARNING_OBJECT (self, "Buffer with extended seqnum %u is more than"
167 " G_MAXINT16 (%u) before the higher received seqnum %u, dropping to"
168 " avoid confusing downstream elements.",
169 extseqnum, G_MAXINT16, self->max_extseqnum);
170 return GST_FLOW_OK;
171 }
172 self->max_extseqnum = MAX (self->max_extseqnum, extseqnum);
173 }
174
175 if (!has_drop_null || num_packets_deleted == 0)
176 goto no_restore;
177
178 payload = gst_rtp_buffer_get_payload (&rtp);
179 plen = gst_rtp_buffer_get_payload_len (&rtp);
180
181 if (plen != 0) {
182 if (plen % 188 == 0) {
183 if (ts_packet_size != 188) {
184 GST_WARNING_OBJECT (self, "RTP Header extension says packet size is"
185 " 204, but payload length is divisible by 188, ignoring header");
186 ts_packet_size = 188;
187 }
188 } else if (plen % 204 == 0) {
189 if (ts_packet_size != 204) {
190 GST_WARNING_OBJECT (self, "RTP Header extension says packet size is"
191 " 188, but payload length is divisible by 204, ignoring header");
192 ts_packet_size = 204;
193 }
194 } else {
195 GST_WARNING_OBJECT (self, "Payload length (%u) is not divisible by 188"
196 " or 204, taking TS packet size from header (%u), not restoring"
197 " null packets", plen, ts_packet_size);
198 goto no_restore;
199 }
200 }
201
202 if ((plen / ts_packet_size) + num_packets_deleted != orig_ts_packet_count) {
203 if (orig_ts_packet_count == 0)
204 GST_LOG_OBJECT (self, "Original number of packet is 0, using NPD bits to"
205 " restore packet size to %d",
206 (plen / ts_packet_size) + num_packets_deleted);
207 else
208 GST_WARNING_OBJECT (self, "The number of deleted packets (%u) + the"
209 " number of transmitted packets (%d) is not equal to the declared"
210 " original packet count (%d), ignoring it", num_packets_deleted,
211 (plen / ts_packet_size), orig_ts_packet_count);
212 orig_ts_packet_count = (plen / ts_packet_size) + num_packets_deleted;
213 }
214
215 GST_LOG_OBJECT (self, "Restoring %u null TS packets for a total"
216 " of %u packets", num_packets_deleted, orig_ts_packet_count);
217
218 mem = gst_allocator_alloc (NULL, orig_ts_packet_count * ts_packet_size, NULL);
219 gst_memory_map (mem, &map, GST_MAP_READWRITE);
220
221 /* Re-create the null packets */
222 for (i = 0; i < orig_ts_packet_count; i++) {
223 gboolean was_deleted = (npd_bits & (1 << (6 - i))) != 0;
224 guint8 *pktdst = map.data + (i * ts_packet_size);
225
226 if (was_deleted) {
227 memset (pktdst, 0, ts_packet_size);
228 pktdst[0] = 0x47;
229 pktdst[1] = 0x1F;
230 pktdst[2] = 0xFF;
231 pktdst[3] = 0x10;
232 num_restored++;
233 } else {
234 guint src_offset = (i - num_restored) * ts_packet_size;
235
236 if (src_offset + ts_packet_size > plen) {
237 GST_WARNING_OBJECT (self, "Invalid NPD bits (0x%x), not enough data in"
238 " the original RTP packet, not restoring TS packet %d", npd_bits,
239 i);
240 } else {
241 memcpy (pktdst, payload + src_offset, ts_packet_size);
242 }
243 }
244 }
245
246 gst_memory_unmap (mem, &map);
247
248 no_restore:
249
250 orig_payload_offset = gst_rtp_buffer_get_header_len (&rtp);
251 hdrlen = orig_payload_offset - (4 + (extlen * 4));
252
253 gst_rtp_buffer_unmap (&rtp);
254
255 /* Create a new buffer without the header extension */
256 outbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, 0, hdrlen);
257
258 /* Unset extension flag, can't use the GstRTPBuffer function as they will
259 * try to look for the extension itself which isn't there if the flag is set.
260 */
261 gst_buffer_map (outbuf, &map, GST_MAP_READWRITE);
262 map.data[0] &= ~0x10;
263 gst_buffer_unmap (outbuf, &map);
264
265 if (mem)
266 gst_buffer_append_memory (outbuf, mem);
267 else
268 gst_buffer_copy_into (outbuf, buffer, GST_BUFFER_COPY_MEMORY,
269 orig_payload_offset, -1);
270
271 gst_buffer_unref (buffer);
272
273 return gst_pad_push (self->srcpad, outbuf);
274
275 mapping_error:
276 gst_buffer_unref (buffer);
277 return GST_FLOW_ERROR;
278 }
279
280 static void
gst_rist_rtp_deext_init(GstRistRtpDeext * self)281 gst_rist_rtp_deext_init (GstRistRtpDeext * self)
282 {
283 self->sinkpad = gst_pad_new_from_static_template (&sink_templ,
284 sink_templ.name_template);
285 self->srcpad = gst_pad_new_from_static_template (&src_templ,
286 src_templ.name_template);
287
288 GST_PAD_SET_PROXY_ALLOCATION (self->sinkpad);
289 GST_PAD_SET_PROXY_CAPS (self->sinkpad);
290 gst_pad_set_chain_function (self->sinkpad, gst_rist_rtp_deext_chain);
291
292 gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
293 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
294 }
295
296 static void
gst_rist_rtp_deext_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)297 gst_rist_rtp_deext_get_property (GObject * object, guint prop_id,
298 GValue * value, GParamSpec * pspec)
299 {
300 GstRistRtpDeext *self = GST_RIST_RTP_DEEXT (object);
301
302 switch (prop_id) {
303 case PROP_MAX_EXT_SEQNUM:
304 g_value_set_uint (value, self->max_extseqnum);
305 break;
306 case PROP_HAVE_EXT_SEQNUM:
307 g_value_set_boolean (value, self->have_extseqnum);
308 break;
309 default:
310 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
311 break;
312 }
313 }
314
315 static void
gst_rist_rtp_deext_class_init(GstRistRtpDeextClass * klass)316 gst_rist_rtp_deext_class_init (GstRistRtpDeextClass * klass)
317 {
318 GstElementClass *element_class = (GstElementClass *) klass;
319 GObjectClass *object_class = (GObjectClass *) klass;
320
321 gst_element_class_set_metadata (element_class,
322 "RIST RTP Extension remover", "Filter/Network",
323 "Removes RIST TR-06-2 RTP Header extension",
324 "Olivier Crete <olivier.crete@collabora.com");
325 gst_element_class_add_static_pad_template (element_class, &src_templ);
326 gst_element_class_add_static_pad_template (element_class, &sink_templ);
327
328 object_class->get_property = gst_rist_rtp_deext_get_property;
329
330 g_object_class_install_property (object_class, PROP_MAX_EXT_SEQNUM,
331 g_param_spec_uint ("max-ext-seqnum",
332 "Maximum Extended Sequence Number",
333 "Largest extended sequence number received", 0, G_MAXUINT, 0,
334 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
335
336 g_object_class_install_property (object_class, PROP_HAVE_EXT_SEQNUM,
337 g_param_spec_boolean ("have-ext-seqnum",
338 "Have extended seqnum",
339 "Has an extended sequence number extension been seen", FALSE,
340 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
341 }
342