ext/amrnb/: Update caps with audio/AMR.
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpamrdepay.c
1 /* GStreamer
2  * Copyright (C) <2005> Wim Taymans <wim@fluendo.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 
13  */
14
15 #ifdef HAVE_CONFIG_H
16 #  include "config.h"
17 #endif
18
19 #include <gst/rtp/gstrtpbuffer.h>
20
21 #include <string.h>
22 #include "gstrtpamrdec.h"
23
24 /* elementfactory information */
25 static GstElementDetails gst_rtp_amrdec_details = {
26   "RTP packet parser",
27   "Codec/Parser/Network",
28   "Extracts MPEG audio from RTP packets",
29   "Wim Taymans <wim@fluendo.com>"
30 };
31
32 /* RtpAMRDec signals and args */
33 enum
34 {
35   /* FILL ME */
36   LAST_SIGNAL
37 };
38
39 enum
40 {
41   ARG_0,
42   ARG_FREQUENCY
43 };
44
45 /* input is an RTP packet 
46  *
47  * params see RFC 3267, section 8.1
48  */
49 static GstStaticPadTemplate gst_rtpamrdec_sink_template =
50 GST_STATIC_PAD_TEMPLATE ("sink",
51     GST_PAD_SINK,
52     GST_PAD_ALWAYS,
53     GST_STATIC_CAPS ("application/x-rtp, "
54         "octet-align = (boolean) TRUE, "
55         "crc = (boolean) FALSE, "
56         "robust-sorting = (boolean) FALSE, "
57         "interleaving = (boolean) FALSE, "
58         "channels = (int) 1, " "rate = (int) 8000"
59         /* following options are not needed for a decoder 
60          *
61          "mode-set = (int) [ 0, 7 ], "
62          "mode-change-period = (int) [ 1, MAX ], "
63          "mode-change-neighbor = (boolean) { TRUE, FALSE }, "
64          "maxptime = (int) [ 20, MAX ], "
65          "ptime = (int) [ 20, MAX ]"
66          */
67     )
68     );
69
70 static GstStaticPadTemplate gst_rtpamrdec_src_template =
71 GST_STATIC_PAD_TEMPLATE ("src",
72     GST_PAD_SRC,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS ("audio/AMR, " "channels = (int) 1," "rate = (int) 8000")
75     );
76
77 static void gst_rtpamrdec_class_init (GstRtpAMRDecClass * klass);
78 static void gst_rtpamrdec_base_init (GstRtpAMRDecClass * klass);
79 static void gst_rtpamrdec_init (GstRtpAMRDec * rtpamrdec);
80
81 static gboolean gst_rtpamrdec_sink_setcaps (GstPad * pad, GstCaps * caps);
82 static GstFlowReturn gst_rtpamrdec_chain (GstPad * pad, GstBuffer * buffer);
83
84 static void gst_rtpamrdec_set_property (GObject * object, guint prop_id,
85     const GValue * value, GParamSpec * pspec);
86 static void gst_rtpamrdec_get_property (GObject * object, guint prop_id,
87     GValue * value, GParamSpec * pspec);
88
89 static GstElementStateReturn gst_rtpamrdec_change_state (GstElement * element);
90
91 static GstElementClass *parent_class = NULL;
92
93 static GType
94 gst_rtpamrdec_get_type (void)
95 {
96   static GType rtpamrdec_type = 0;
97
98   if (!rtpamrdec_type) {
99     static const GTypeInfo rtpamrdec_info = {
100       sizeof (GstRtpAMRDecClass),
101       (GBaseInitFunc) gst_rtpamrdec_base_init,
102       NULL,
103       (GClassInitFunc) gst_rtpamrdec_class_init,
104       NULL,
105       NULL,
106       sizeof (GstRtpAMRDec),
107       0,
108       (GInstanceInitFunc) gst_rtpamrdec_init,
109     };
110
111     rtpamrdec_type =
112         g_type_register_static (GST_TYPE_ELEMENT, "GstRtpAMRDec",
113         &rtpamrdec_info, 0);
114   }
115   return rtpamrdec_type;
116 }
117
118 static void
119 gst_rtpamrdec_base_init (GstRtpAMRDecClass * klass)
120 {
121   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
122
123   gst_element_class_add_pad_template (element_class,
124       gst_static_pad_template_get (&gst_rtpamrdec_src_template));
125   gst_element_class_add_pad_template (element_class,
126       gst_static_pad_template_get (&gst_rtpamrdec_sink_template));
127
128   gst_element_class_set_details (element_class, &gst_rtp_amrdec_details);
129 }
130
131 static void
132 gst_rtpamrdec_class_init (GstRtpAMRDecClass * klass)
133 {
134   GObjectClass *gobject_class;
135   GstElementClass *gstelement_class;
136
137   gobject_class = (GObjectClass *) klass;
138   gstelement_class = (GstElementClass *) klass;
139
140   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
141
142   gobject_class->set_property = gst_rtpamrdec_set_property;
143   gobject_class->get_property = gst_rtpamrdec_get_property;
144
145   gstelement_class->change_state = gst_rtpamrdec_change_state;
146 }
147
148 static void
149 gst_rtpamrdec_init (GstRtpAMRDec * rtpamrdec)
150 {
151   GstCaps *srccaps;
152
153   rtpamrdec->srcpad =
154       gst_pad_new_from_template (gst_static_pad_template_get
155       (&gst_rtpamrdec_src_template), "src");
156
157   /* FIXME */
158   srccaps = gst_caps_new_simple ("audio/AMR",
159       "channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, 8000, NULL);
160   gst_pad_set_caps (rtpamrdec->srcpad, srccaps);
161   gst_caps_unref (srccaps);
162
163   gst_element_add_pad (GST_ELEMENT (rtpamrdec), rtpamrdec->srcpad);
164
165   rtpamrdec->sinkpad =
166       gst_pad_new_from_template (gst_static_pad_template_get
167       (&gst_rtpamrdec_sink_template), "sink");
168   gst_pad_set_setcaps_function (rtpamrdec->sinkpad, gst_rtpamrdec_sink_setcaps);
169   gst_pad_set_chain_function (rtpamrdec->sinkpad, gst_rtpamrdec_chain);
170   gst_element_add_pad (GST_ELEMENT (rtpamrdec), rtpamrdec->sinkpad);
171 }
172
173 static gboolean
174 gst_rtpamrdec_sink_setcaps (GstPad * pad, GstCaps * caps)
175 {
176   GstStructure *structure;
177   GstCaps *srccaps;
178   GstRtpAMRDec *rtpamrdec;
179
180   rtpamrdec = GST_RTP_AMR_DEC (GST_OBJECT_PARENT (pad));
181
182   structure = gst_caps_get_structure (caps, 0);
183
184   if (!gst_structure_get_boolean (structure, "octet-align",
185           &rtpamrdec->octet_align))
186     rtpamrdec->octet_align = FALSE;
187
188   /* FIXME, force octect align for now until all elements negotiate 
189    * correctly*/
190   rtpamrdec->octet_align = TRUE;
191
192   if (!gst_structure_get_boolean (structure, "crc", &rtpamrdec->crc))
193     rtpamrdec->crc = FALSE;
194
195   if (rtpamrdec->crc) {
196     /* crc mode implies octet aligned mode */
197     rtpamrdec->octet_align = TRUE;
198   }
199
200   if (!gst_structure_get_boolean (structure, "robust-sorting",
201           &rtpamrdec->robust_sorting))
202     rtpamrdec->robust_sorting = FALSE;
203
204   if (rtpamrdec->robust_sorting) {
205     /* robust_sorting mode implies octet aligned mode */
206     rtpamrdec->octet_align = TRUE;
207   }
208
209   if (!gst_structure_get_boolean (structure, "interleaving",
210           &rtpamrdec->interleaving))
211     rtpamrdec->interleaving = FALSE;
212
213   if (rtpamrdec->interleaving) {
214     /* interleaving mode implies octet aligned mode */
215     rtpamrdec->octet_align = TRUE;
216   }
217
218   if (!gst_structure_get_int (structure, "channels", &rtpamrdec->channels))
219     rtpamrdec->channels = 1;
220   if (!gst_structure_get_int (structure, "rate", &rtpamrdec->rate))
221     rtpamrdec->rate = 8000;
222
223   /* we require 1 channel, 8000 Hz, octet aligned, no CRC, 
224    * no robust sorting, no interleaving for now */
225   if (rtpamrdec->channels != 1)
226     return FALSE;
227   if (rtpamrdec->rate != 8000)
228     return FALSE;
229   if (rtpamrdec->octet_align != TRUE)
230     return FALSE;
231   if (rtpamrdec->crc != FALSE)
232     return FALSE;
233   if (rtpamrdec->robust_sorting != FALSE)
234     return FALSE;
235   if (rtpamrdec->interleaving != FALSE)
236     return FALSE;
237
238   srccaps = gst_caps_new_simple ("audio/AMR",
239       "channels", G_TYPE_INT, rtpamrdec->channels,
240       "rate", G_TYPE_INT, rtpamrdec->rate, NULL);
241   gst_pad_set_caps (rtpamrdec->srcpad, srccaps);
242   gst_caps_unref (srccaps);
243
244   rtpamrdec->negotiated = TRUE;
245
246   return TRUE;
247 }
248
249 static GstFlowReturn
250 gst_rtpamrdec_chain (GstPad * pad, GstBuffer * buf)
251 {
252   GstRtpAMRDec *rtpamrdec;
253   GstBuffer *outbuf;
254   GstFlowReturn ret;
255
256   rtpamrdec = GST_RTP_AMR_DEC (GST_OBJECT_PARENT (pad));
257
258   if (!rtpamrdec->negotiated)
259     goto not_negotiated;
260
261   if (!gst_rtpbuffer_validate (buf))
262     goto bad_packet;
263
264   /* when we get here, 1 channel, 8000 Hz, octet aligned, no CRC, 
265    * no robust sorting, no interleaving data is to be parsed */
266   {
267     gint payload_len;
268     guint8 *payload;
269     guint32 timestamp;
270     guint8 CMR, F, FT, Q;
271
272     payload_len = gst_rtpbuffer_get_payload_len (buf);
273
274     /* need at least 2 bytes for the header */
275     if (payload_len < 2)
276       goto bad_packet;
277
278     payload = gst_rtpbuffer_get_payload (buf);
279
280     /* parse header 
281      *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 
282      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+..
283      * | CMR=6 |R|R|R|R|0|FT#1=5 |Q|P|P|
284      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+..
285      */
286     CMR = (payload[0] & 0xf0) >> 4;
287     F = (payload[1] & 0x80) >> 7;
288     /* we only support 1 packet per RTP packet for now */
289     if (F != 0)
290       goto one_packet_only;
291
292     FT = (payload[1] & 0x78) >> 3;
293     Q = (payload[1] & 0x04) >> 2;
294
295     /* skip packet */
296     if (FT > 9 && FT < 15) {
297       ret = GST_FLOW_OK;
298       goto skip;
299     }
300
301     /* strip header now, leave FT in the data for the decoder */
302     payload_len -= 1;
303     payload += 1;
304
305     timestamp = gst_rtpbuffer_get_timestamp (buf);
306
307     outbuf = gst_buffer_new_and_alloc (payload_len);
308
309     GST_BUFFER_TIMESTAMP (outbuf) = timestamp * GST_SECOND / 8000;
310
311     memcpy (GST_BUFFER_DATA (outbuf), payload, payload_len);
312
313     gst_buffer_set_caps (outbuf, GST_PAD_CAPS (rtpamrdec->srcpad));
314
315     GST_DEBUG ("gst_rtpamrdec_chain: pushing buffer of size %d",
316         GST_BUFFER_SIZE (outbuf));
317     ret = gst_pad_push (rtpamrdec->srcpad, outbuf);
318
319   skip:
320     gst_buffer_unref (buf);
321   }
322
323   return ret;
324
325 not_negotiated:
326   {
327     GST_DEBUG ("not_negotiated");
328     gst_buffer_unref (buf);
329     return GST_FLOW_NOT_NEGOTIATED;
330   }
331 bad_packet:
332   {
333     GST_DEBUG ("Packet did not validate");
334     gst_buffer_unref (buf);
335     return GST_FLOW_ERROR;
336   }
337 one_packet_only:
338   {
339     GST_DEBUG ("One packet per RTP packet only");
340     gst_buffer_unref (buf);
341     return GST_FLOW_ERROR;
342   }
343 }
344
345 static void
346 gst_rtpamrdec_set_property (GObject * object, guint prop_id,
347     const GValue * value, GParamSpec * pspec)
348 {
349   GstRtpAMRDec *rtpamrdec;
350
351   rtpamrdec = GST_RTP_AMR_DEC (object);
352
353   switch (prop_id) {
354     default:
355       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356       break;
357   }
358 }
359
360 static void
361 gst_rtpamrdec_get_property (GObject * object, guint prop_id, GValue * value,
362     GParamSpec * pspec)
363 {
364   GstRtpAMRDec *rtpamrdec;
365
366   rtpamrdec = GST_RTP_AMR_DEC (object);
367
368   switch (prop_id) {
369     default:
370       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371       break;
372   }
373 }
374
375 static GstElementStateReturn
376 gst_rtpamrdec_change_state (GstElement * element)
377 {
378   GstRtpAMRDec *rtpamrdec;
379   gint transition;
380   GstElementStateReturn ret;
381
382   rtpamrdec = GST_RTP_AMR_DEC (element);
383   transition = GST_STATE_TRANSITION (element);
384
385   switch (transition) {
386     case GST_STATE_NULL_TO_READY:
387       break;
388     case GST_STATE_READY_TO_PAUSED:
389       /* FIXME, don't require negotiation until all elements
390        * do */
391       rtpamrdec->negotiated = TRUE;
392       break;
393     default:
394       break;
395   }
396
397   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
398
399   switch (transition) {
400     case GST_STATE_READY_TO_NULL:
401       break;
402     default:
403       break;
404   }
405   return ret;
406 }
407
408 gboolean
409 gst_rtpamrdec_plugin_init (GstPlugin * plugin)
410 {
411   return gst_element_register (plugin, "rtpamrdec",
412       GST_RANK_NONE, GST_TYPE_RTP_AMR_DEC);
413 }