• 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-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