gstdepay: check for correct fragment offset
[platform/upstream/gstreamer.git] / gst / rtp / gstrtpspeexpay.c
1 /* GStreamer
2  * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br>
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 <stdlib.h>
25 #include <string.h>
26 #include <gst/rtp/gstrtpbuffer.h>
27
28 #include "gstrtpspeexpay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rtpspeexpay_debug);
31 #define GST_CAT_DEFAULT (rtpspeexpay_debug)
32
33 static GstStaticPadTemplate gst_rtp_speex_pay_sink_template =
34 GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS ("audio/x-speex, "
38         "rate = (int) [ 6000, 48000 ], " "channels = (int) 1")
39     );
40
41 static GstStaticPadTemplate gst_rtp_speex_pay_src_template =
42 GST_STATIC_PAD_TEMPLATE ("src",
43     GST_PAD_SRC,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS ("application/x-rtp, "
46         "media = (string) \"audio\", "
47         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
48         "clock-rate =  (int) [ 6000, 48000 ], "
49         "encoding-name = (string) \"SPEEX\", "
50         "encoding-params = (string) \"1\"")
51     );
52
53 static GstStateChangeReturn gst_rtp_speex_pay_change_state (GstElement *
54     element, GstStateChange transition);
55
56 static gboolean gst_rtp_speex_pay_setcaps (GstRTPBasePayload * payload,
57     GstCaps * caps);
58 static GstCaps *gst_rtp_speex_pay_getcaps (GstRTPBasePayload * payload,
59     GstPad * pad, GstCaps * filter);
60 static GstFlowReturn gst_rtp_speex_pay_handle_buffer (GstRTPBasePayload *
61     payload, GstBuffer * buffer);
62
63 #define gst_rtp_speex_pay_parent_class parent_class
64 G_DEFINE_TYPE (GstRtpSPEEXPay, gst_rtp_speex_pay, GST_TYPE_RTP_BASE_PAYLOAD);
65
66 static void
67 gst_rtp_speex_pay_class_init (GstRtpSPEEXPayClass * klass)
68 {
69   GstElementClass *gstelement_class;
70   GstRTPBasePayloadClass *gstrtpbasepayload_class;
71
72   gstelement_class = (GstElementClass *) klass;
73   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
74
75   gstelement_class->change_state = gst_rtp_speex_pay_change_state;
76
77   gstrtpbasepayload_class->set_caps = gst_rtp_speex_pay_setcaps;
78   gstrtpbasepayload_class->get_caps = gst_rtp_speex_pay_getcaps;
79   gstrtpbasepayload_class->handle_buffer = gst_rtp_speex_pay_handle_buffer;
80
81   gst_element_class_add_pad_template (gstelement_class,
82       gst_static_pad_template_get (&gst_rtp_speex_pay_sink_template));
83   gst_element_class_add_pad_template (gstelement_class,
84       gst_static_pad_template_get (&gst_rtp_speex_pay_src_template));
85   gst_element_class_set_static_metadata (gstelement_class,
86       "RTP Speex payloader", "Codec/Payloader/Network/RTP",
87       "Payload-encodes Speex audio into a RTP packet",
88       "Edgard Lima <edgard.lima@indt.org.br>");
89
90   GST_DEBUG_CATEGORY_INIT (rtpspeexpay_debug, "rtpspeexpay", 0,
91       "Speex RTP Payloader");
92 }
93
94 static void
95 gst_rtp_speex_pay_init (GstRtpSPEEXPay * rtpspeexpay)
96 {
97   GST_RTP_BASE_PAYLOAD (rtpspeexpay)->clock_rate = 8000;
98   GST_RTP_BASE_PAYLOAD_PT (rtpspeexpay) = 110;  /* Create String */
99 }
100
101 static gboolean
102 gst_rtp_speex_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
103 {
104   /* don't configure yet, we wait for the ident packet */
105   return TRUE;
106 }
107
108
109 static GstCaps *
110 gst_rtp_speex_pay_getcaps (GstRTPBasePayload * payload, GstPad * pad,
111     GstCaps * filter)
112 {
113   GstCaps *otherpadcaps;
114   GstCaps *caps;
115
116   otherpadcaps = gst_pad_get_allowed_caps (payload->srcpad);
117   caps = gst_pad_get_pad_template_caps (pad);
118
119   if (otherpadcaps) {
120     if (!gst_caps_is_empty (otherpadcaps)) {
121       GstStructure *ps;
122       GstStructure *s;
123       gint clock_rate;
124
125       ps = gst_caps_get_structure (otherpadcaps, 0);
126       caps = gst_caps_make_writable (caps);
127       s = gst_caps_get_structure (caps, 0);
128
129       if (gst_structure_get_int (ps, "clock-rate", &clock_rate)) {
130         gst_structure_fixate_field_nearest_int (s, "rate", clock_rate);
131       }
132     }
133     gst_caps_unref (otherpadcaps);
134   }
135
136   if (filter) {
137     GstCaps *tcaps = caps;
138
139     caps = gst_caps_intersect_full (filter, tcaps, GST_CAPS_INTERSECT_FIRST);
140     gst_caps_unref (tcaps);
141   }
142
143   return caps;
144 }
145
146 static gboolean
147 gst_rtp_speex_pay_parse_ident (GstRtpSPEEXPay * rtpspeexpay,
148     const guint8 * data, guint size)
149 {
150   guint32 version, header_size, rate, mode, nb_channels;
151   GstRTPBasePayload *payload;
152   gchar *cstr;
153   gboolean res;
154
155   /* we need the header string (8), the version string (20), the version
156    * and the header length. */
157   if (size < 36)
158     goto too_small;
159
160   if (!g_str_has_prefix ((const gchar *) data, "Speex   "))
161     goto wrong_header;
162
163   /* skip header and version string */
164   data += 28;
165
166   version = GST_READ_UINT32_LE (data);
167   if (version != 1)
168     goto wrong_version;
169
170   data += 4;
171   /* ensure sizes */
172   header_size = GST_READ_UINT32_LE (data);
173   if (header_size < 80)
174     goto header_too_small;
175
176   if (size < header_size)
177     goto payload_too_small;
178
179   data += 4;
180   rate = GST_READ_UINT32_LE (data);
181   data += 4;
182   mode = GST_READ_UINT32_LE (data);
183   data += 8;
184   nb_channels = GST_READ_UINT32_LE (data);
185
186   GST_DEBUG_OBJECT (rtpspeexpay, "rate %d, mode %d, nb_channels %d",
187       rate, mode, nb_channels);
188
189   payload = GST_RTP_BASE_PAYLOAD (rtpspeexpay);
190
191   gst_rtp_base_payload_set_options (payload, "audio", FALSE, "SPEEX", rate);
192   cstr = g_strdup_printf ("%d", nb_channels);
193   res = gst_rtp_base_payload_set_outcaps (payload, "encoding-params",
194       G_TYPE_STRING, cstr, NULL);
195   g_free (cstr);
196
197   return res;
198
199   /* ERRORS */
200 too_small:
201   {
202     GST_DEBUG_OBJECT (rtpspeexpay,
203         "ident packet too small, need at least 32 bytes");
204     return FALSE;
205   }
206 wrong_header:
207   {
208     GST_DEBUG_OBJECT (rtpspeexpay,
209         "ident packet does not start with \"Speex   \"");
210     return FALSE;
211   }
212 wrong_version:
213   {
214     GST_DEBUG_OBJECT (rtpspeexpay, "can only handle version 1, have version %d",
215         version);
216     return FALSE;
217   }
218 header_too_small:
219   {
220     GST_DEBUG_OBJECT (rtpspeexpay,
221         "header size too small, need at least 80 bytes, " "got only %d",
222         header_size);
223     return FALSE;
224   }
225 payload_too_small:
226   {
227     GST_DEBUG_OBJECT (rtpspeexpay,
228         "payload too small, need at least %d bytes, got only %d", header_size,
229         size);
230     return FALSE;
231   }
232 }
233
234 static GstFlowReturn
235 gst_rtp_speex_pay_handle_buffer (GstRTPBasePayload * basepayload,
236     GstBuffer * buffer)
237 {
238   GstRtpSPEEXPay *rtpspeexpay;
239   guint payload_len;
240   GstMapInfo map;
241   GstBuffer *outbuf;
242   guint8 *payload;
243   GstClockTime timestamp, duration;
244   GstFlowReturn ret;
245   GstRTPBuffer rtp = { NULL };
246
247   rtpspeexpay = GST_RTP_SPEEX_PAY (basepayload);
248
249   gst_buffer_map (buffer, &map, GST_MAP_READ);
250
251   switch (rtpspeexpay->packet) {
252     case 0:
253       /* ident packet. We need to parse the headers to construct the RTP
254        * properties. */
255       if (!gst_rtp_speex_pay_parse_ident (rtpspeexpay, map.data, map.size))
256         goto parse_error;
257
258       ret = GST_FLOW_OK;
259       goto done;
260     case 1:
261       /* comment packet, we ignore it */
262       ret = GST_FLOW_OK;
263       goto done;
264     default:
265       /* other packets go in the payload */
266       break;
267   }
268
269   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP)) {
270     ret = GST_FLOW_OK;
271     goto done;
272   }
273
274   timestamp = GST_BUFFER_TIMESTAMP (buffer);
275   duration = GST_BUFFER_DURATION (buffer);
276
277   /* FIXME, only one SPEEX frame per RTP packet for now */
278   payload_len = map.size;
279
280   outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
281   /* FIXME, assert for now */
282   g_assert (payload_len <= GST_RTP_BASE_PAYLOAD_MTU (rtpspeexpay));
283
284   /* copy timestamp and duration */
285   GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
286   GST_BUFFER_DURATION (outbuf) = duration;
287
288   gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
289   /* get payload */
290   payload = gst_rtp_buffer_get_payload (&rtp);
291
292   /* copy data in payload */
293   memcpy (&payload[0], map.data, map.size);
294
295   gst_rtp_buffer_unmap (&rtp);
296
297   ret = gst_rtp_base_payload_push (basepayload, outbuf);
298
299 done:
300   gst_buffer_unmap (buffer, &map);
301   gst_buffer_unref (buffer);
302
303   rtpspeexpay->packet++;
304
305   return ret;
306
307   /* ERRORS */
308 parse_error:
309   {
310     GST_ELEMENT_ERROR (rtpspeexpay, STREAM, DECODE, (NULL),
311         ("Error parsing first identification packet."));
312     gst_buffer_unmap (buffer, &map);
313     gst_buffer_unref (buffer);
314     return GST_FLOW_ERROR;
315   }
316 }
317
318 static GstStateChangeReturn
319 gst_rtp_speex_pay_change_state (GstElement * element, GstStateChange transition)
320 {
321   GstRtpSPEEXPay *rtpspeexpay;
322   GstStateChangeReturn ret;
323
324   rtpspeexpay = GST_RTP_SPEEX_PAY (element);
325
326   switch (transition) {
327     case GST_STATE_CHANGE_NULL_TO_READY:
328       break;
329     case GST_STATE_CHANGE_READY_TO_PAUSED:
330       rtpspeexpay->packet = 0;
331       break;
332     default:
333       break;
334   }
335
336   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
337
338   switch (transition) {
339     case GST_STATE_CHANGE_READY_TO_NULL:
340       break;
341     default:
342       break;
343   }
344   return ret;
345 }
346
347 gboolean
348 gst_rtp_speex_pay_plugin_init (GstPlugin * plugin)
349 {
350   return gst_element_register (plugin, "rtpspeexpay",
351       GST_RANK_SECONDARY, GST_TYPE_RTP_SPEEX_PAY);
352 }