gstdepay: check for correct fragment offset
[platform/upstream/gstreamer.git] / gst / rtp / gstrtpgstpay.c
1 /* GStreamer
2  * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com>
3  *
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.
8  *
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.
13  *
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <string.h>
25
26 #include <gst/rtp/gstrtpbuffer.h>
27
28 #include "gstrtpgstpay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (gst_rtp_pay_debug);
31 #define GST_CAT_DEFAULT gst_rtp_pay_debug
32
33 /*
34  *  0                   1                   2                   3
35  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
36  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37  * |C| CV  |D|0|0|0|                  MBZ                          |
38  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39  * |                          Frag_offset                          |
40  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41  *
42  * C: caps inlined flag 
43  *   When C set, first part of payload contains caps definition. Caps definition
44  *   starts with variable-length length prefix and then a string of that length.
45  *   the length is encoded in big endian 7 bit chunks, the top 1 bit of a byte
46  *   is the continuation marker and the 7 next bits the data. A continuation
47  *   marker of 1 means that the next byte contains more data. 
48  *
49  * CV: caps version, 0 = caps from SDP, 1 - 7 inlined caps
50  * D: delta unit buffer
51  *
52  *
53  */
54
55 static GstStaticPadTemplate gst_rtp_gst_pay_sink_template =
56 GST_STATIC_PAD_TEMPLATE ("sink",
57     GST_PAD_SINK,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS_ANY);
60
61 static GstStaticPadTemplate gst_rtp_gst_pay_src_template =
62 GST_STATIC_PAD_TEMPLATE ("src",
63     GST_PAD_SRC,
64     GST_PAD_ALWAYS,
65     GST_STATIC_CAPS ("application/x-rtp, "
66         "media = (string) \"application\", "
67         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
68         "clock-rate = (int) 90000, " "encoding-name = (string) \"X-GST\"")
69     );
70
71 static void gst_rtp_gst_pay_finalize (GObject * obj);
72
73 static gboolean gst_rtp_gst_pay_setcaps (GstRTPBasePayload * payload,
74     GstCaps * caps);
75 static GstFlowReturn gst_rtp_gst_pay_handle_buffer (GstRTPBasePayload * payload,
76     GstBuffer * buffer);
77
78 #define gst_rtp_gst_pay_parent_class parent_class
79 G_DEFINE_TYPE (GstRtpGSTPay, gst_rtp_gst_pay, GST_TYPE_RTP_BASE_PAYLOAD);
80
81 static void
82 gst_rtp_gst_pay_class_init (GstRtpGSTPayClass * klass)
83 {
84   GObjectClass *gobject_class;
85   GstElementClass *gstelement_class;
86   GstRTPBasePayloadClass *gstrtpbasepayload_class;
87
88   gobject_class = (GObjectClass *) klass;
89   gstelement_class = (GstElementClass *) klass;
90   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
91
92   gobject_class->finalize = gst_rtp_gst_pay_finalize;
93
94   gst_element_class_add_pad_template (gstelement_class,
95       gst_static_pad_template_get (&gst_rtp_gst_pay_src_template));
96   gst_element_class_add_pad_template (gstelement_class,
97       gst_static_pad_template_get (&gst_rtp_gst_pay_sink_template));
98
99   gst_element_class_set_static_metadata (gstelement_class,
100       "RTP GStreamer payloader", "Codec/Payloader/Network/RTP",
101       "Payload GStreamer buffers as RTP packets",
102       "Wim Taymans <wim.taymans@gmail.com>");
103
104   gstrtpbasepayload_class->set_caps = gst_rtp_gst_pay_setcaps;
105   gstrtpbasepayload_class->handle_buffer = gst_rtp_gst_pay_handle_buffer;
106
107   GST_DEBUG_CATEGORY_INIT (gst_rtp_pay_debug, "rtpgstpay", 0,
108       "rtpgstpay element");
109 }
110
111 static void
112 gst_rtp_gst_pay_init (GstRtpGSTPay * rtpgstpay)
113 {
114 }
115
116 static void
117 gst_rtp_gst_pay_finalize (GObject * obj)
118 {
119   GstRtpGSTPay *rtpgstpay;
120
121   rtpgstpay = GST_RTP_GST_PAY (obj);
122
123   g_free (rtpgstpay->capsstr);
124
125   G_OBJECT_CLASS (parent_class)->finalize (obj);
126 }
127
128 static gboolean
129 gst_rtp_gst_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
130 {
131   GstRtpGSTPay *rtpgstpay;
132   gboolean res;
133   gchar *capsenc, *capsver;
134
135   rtpgstpay = GST_RTP_GST_PAY (payload);
136
137   g_free (rtpgstpay->capsstr);
138   rtpgstpay->capsstr = gst_caps_to_string (caps);
139   rtpgstpay->capslen = strlen (rtpgstpay->capsstr);
140   rtpgstpay->current_CV = rtpgstpay->next_CV;
141
142   /* encode without 0 byte */
143   capsenc = g_base64_encode ((guchar *) rtpgstpay->capsstr, rtpgstpay->capslen);
144   GST_DEBUG_OBJECT (payload, "caps=%s, caps(base64)=%s",
145       rtpgstpay->capsstr, capsenc);
146   /* for 0 byte */
147   rtpgstpay->capslen++;
148
149   capsver = g_strdup_printf ("%d", rtpgstpay->current_CV);
150
151   gst_rtp_base_payload_set_options (payload, "application", TRUE, "X-GST",
152       90000);
153   res =
154       gst_rtp_base_payload_set_outcaps (payload, "caps", G_TYPE_STRING, capsenc,
155       "capsversion", G_TYPE_STRING, capsver, NULL);
156   g_free (capsenc);
157   g_free (capsver);
158
159   return res;
160 }
161
162 static GstFlowReturn
163 gst_rtp_gst_pay_handle_buffer (GstRTPBasePayload * basepayload,
164     GstBuffer * buffer)
165 {
166   GstRtpGSTPay *rtpgstpay;
167   GstMapInfo map;
168   guint8 *ptr;
169   gsize left;
170   GstBuffer *outbuf;
171   GstFlowReturn ret;
172   GstClockTime timestamp;
173   guint32 frag_offset;
174   guint flags;
175   gchar *capsstr;
176   guint capslen;
177   guint capslen_prefix_len;
178
179   rtpgstpay = GST_RTP_GST_PAY (basepayload);
180
181   gst_buffer_map (buffer, &map, GST_MAP_READ);
182   timestamp = GST_BUFFER_TIMESTAMP (buffer);
183
184   ret = GST_FLOW_OK;
185
186   /* caps always from SDP for now */
187   flags = 0;
188   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
189     flags |= (1 << 3);
190
191   capsstr = rtpgstpay->capsstr;
192   capslen = rtpgstpay->capslen;
193   if (capslen) {
194     /* start of buffer, calculate length */
195     capslen_prefix_len = 1;
196     while (capslen >> (7 * capslen_prefix_len))
197       capslen_prefix_len++;
198
199     GST_DEBUG_OBJECT (rtpgstpay, "sending inline caps");
200     rtpgstpay->next_CV++;
201
202     flags |= (1 << 7);
203   } else {
204     capslen_prefix_len = 0;
205   }
206
207   flags |= (rtpgstpay->current_CV << 4);
208
209   /*
210    *  0                   1                   2                   3
211    *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
212    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
213    * |C| CV  |D|X|Y|Z|                  MBZ                          |
214    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
215    * |                          Frag_offset                          |
216    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
217    */
218   frag_offset = 0;
219   ptr = map.data;
220   left = map.size;
221
222   GST_DEBUG_OBJECT (basepayload, "buffer size=%u", left);
223
224   while (left > 0) {
225     guint towrite;
226     guint8 *payload;
227     guint payload_len;
228     guint packet_len;
229     GstRTPBuffer rtp = { NULL };
230
231     /* this will be the total lenght of the packet */
232     packet_len =
233         gst_rtp_buffer_calc_packet_len (8 + capslen + capslen_prefix_len + left,
234         0, 0);
235
236     /* fill one MTU or all available bytes */
237     towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpgstpay));
238
239     /* this is the payload length */
240     payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
241
242     /* create buffer to hold the payload */
243     outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
244
245     gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
246     payload = gst_rtp_buffer_get_payload (&rtp);
247
248     GST_DEBUG_OBJECT (basepayload, "new packet len %u, frag %u", packet_len,
249         frag_offset);
250
251     payload[0] = flags;
252     payload[1] = payload[2] = payload[3] = 0;
253     payload[4] = frag_offset >> 24;
254     payload[5] = frag_offset >> 16;
255     payload[6] = frag_offset >> 8;
256     payload[7] = frag_offset & 0xff;
257
258     payload += 8;
259     payload_len -= 8;
260
261     if (capslen) {
262       guint tocopy;
263
264       /* we need to write caps */
265       if (frag_offset == 0) {
266         /* write caps length */
267         while (capslen_prefix_len) {
268           capslen_prefix_len--;
269           *payload++ = ((capslen_prefix_len > 0) ? 0x80 : 0) |
270               ((capslen >> (7 * capslen_prefix_len)) & 0x7f);
271           payload_len--;
272           frag_offset++;
273         }
274       }
275
276       tocopy = MIN (payload_len, capslen);
277       GST_DEBUG_OBJECT (basepayload, "copy %u bytes from caps to payload",
278           tocopy);
279       memcpy (payload, capsstr, tocopy);
280
281       capsstr += tocopy;
282       capslen -= tocopy;
283       payload += tocopy;
284       payload_len -= tocopy;
285       frag_offset += tocopy;
286
287       if (capslen == 0) {
288         rtpgstpay->capslen = 0;
289         g_free (rtpgstpay->capsstr);
290         rtpgstpay->capsstr = NULL;
291       }
292     }
293
294     if (capslen == 0) {
295       /* no more caps, continue with data */
296       GST_DEBUG_OBJECT (basepayload, "copy %u bytes from buffer to payload",
297           payload_len);
298       memcpy (payload, ptr, payload_len);
299
300       ptr += payload_len;
301       left -= payload_len;
302       frag_offset += payload_len;
303     }
304
305     if (left == 0)
306       gst_rtp_buffer_set_marker (&rtp, TRUE);
307
308     gst_rtp_buffer_unmap (&rtp);
309
310     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
311
312     ret = gst_rtp_base_payload_push (basepayload, outbuf);
313   }
314   gst_buffer_unmap (buffer, &map);
315   gst_buffer_unref (buffer);
316
317   return ret;
318 }
319
320 gboolean
321 gst_rtp_gst_pay_plugin_init (GstPlugin * plugin)
322 {
323   return gst_element_register (plugin, "rtpgstpay",
324       GST_RANK_NONE, GST_TYPE_RTP_GST_PAY);
325 }