rtpvrawpay: Add missing break
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpsbcdepay.c
1 /*
2  * GStreamer RTP SBC depayloader
3  *
4  * Copyright (C) 2012  Collabora Ltd.
5  *   @author: Arun Raghavan <arun.raghavan@collabora.co.uk>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <gst/rtp/gstrtpbuffer.h>
28 #include "gstrtpsbcdepay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rtpsbcdepay_debug);
31 #define GST_CAT_DEFAULT (rtpsbcdepay_debug)
32
33 static GstStaticPadTemplate gst_rtp_sbc_depay_src_template =
34 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
35     GST_STATIC_CAPS ("audio/x-sbc, "
36         "rate = (int) { 16000, 32000, 44100, 48000 }, "
37         "channels = (int) [ 1, 2 ], "
38         "mode = (string) { mono, dual, stereo, joint }, "
39         "blocks = (int) { 4, 8, 12, 16 }, "
40         "subbands = (int) { 4, 8 }, "
41         "allocation-method = (string) { snr, loudness }, "
42         "bitpool = (int) [ 2, 64 ]")
43     );
44
45 static GstStaticPadTemplate gst_rtp_sbc_depay_sink_template =
46 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
47     GST_STATIC_CAPS ("application/x-rtp, "
48         "media = (string) audio,"
49         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
50         "clock-rate = (int) { 16000, 32000, 44100, 48000 },"
51         "encoding-name = (string) SBC")
52     );
53
54 #define gst_rtp_sbc_depay_parent_class parent_class
55 G_DEFINE_TYPE (GstRtpSbcDepay, gst_rtp_sbc_depay, GST_TYPE_RTP_BASE_DEPAYLOAD);
56
57 static void gst_rtp_sbc_depay_finalize (GObject * object);
58
59 static gboolean gst_rtp_sbc_depay_setcaps (GstRTPBaseDepayload * base,
60     GstCaps * caps);
61 static GstBuffer *gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base,
62     GstBuffer * in);
63
64 static void
65 gst_rtp_sbc_depay_class_init (GstRtpSbcDepayClass * klass)
66 {
67   GstRTPBaseDepayloadClass *gstbasertpdepayload_class =
68       GST_RTP_BASE_DEPAYLOAD_CLASS (klass);
69   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
70   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
71
72   gobject_class->finalize = gst_rtp_sbc_depay_finalize;
73
74   gstbasertpdepayload_class->set_caps = gst_rtp_sbc_depay_setcaps;
75   gstbasertpdepayload_class->process = gst_rtp_sbc_depay_process;
76
77   gst_element_class_add_pad_template (element_class,
78       gst_static_pad_template_get (&gst_rtp_sbc_depay_src_template));
79   gst_element_class_add_pad_template (element_class,
80       gst_static_pad_template_get (&gst_rtp_sbc_depay_sink_template));
81
82   GST_DEBUG_CATEGORY_INIT (rtpsbcdepay_debug, "rtpsbcdepay", 0,
83       "SBC Audio RTP Depayloader");
84
85   gst_element_class_set_static_metadata (element_class,
86       "RTP SBC audio depayloader",
87       "Codec/Depayloader/Network/RTP",
88       "Extracts SBC audio from RTP packets",
89       "Arun Raghavan <arun.raghavan@collabora.co.uk>");
90 }
91
92 static void
93 gst_rtp_sbc_depay_init (GstRtpSbcDepay * rtpsbcdepay)
94 {
95   rtpsbcdepay->adapter = gst_adapter_new ();
96 }
97
98 static void
99 gst_rtp_sbc_depay_finalize (GObject * object)
100 {
101   GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (object);
102
103   gst_object_unref (depay->adapter);
104
105   G_OBJECT_CLASS (parent_class)->finalize (object);
106 }
107
108 /* FIXME: This duplicates similar functionality rtpsbcpay, but there isn't a
109  * simple way to consolidate the two. This is best done by moving the function
110  * to the codec-utils library in gst-plugins-base when these elements move to
111  * GStreamer. */
112 static int
113 gst_rtp_sbc_depay_get_params (GstRtpSbcDepay * depay, const guint8 * data,
114     gint size, int *framelen, int *samples)
115 {
116   int blocks, channel_mode, channels, subbands, bitpool;
117   int length;
118
119   if (size < 3) {
120     /* Not enough data for the header */
121     return -1;
122   }
123
124   /* Sanity check */
125   if (data[0] != 0x9c) {
126     GST_WARNING_OBJECT (depay, "Bad packet: couldn't find syncword");
127     return -2;
128   }
129
130   blocks = (data[1] >> 4) & 0x3;
131   blocks = (blocks + 1) * 4;
132   channel_mode = (data[1] >> 2) & 0x3;
133   channels = channel_mode ? 2 : 1;
134   subbands = (data[1] & 0x1);
135   subbands = (subbands + 1) * 4;
136   bitpool = data[2];
137
138   length = 4 + ((4 * subbands * channels) / 8);
139
140   if (channel_mode == 0 || channel_mode == 1) {
141     /* Mono || Dual channel */
142     length += ((blocks * channels * bitpool)
143         + 4 /* round up */ ) / 8;
144   } else {
145     /* Stereo || Joint stereo */
146     gboolean joint = (channel_mode == 3);
147
148     length += ((joint * subbands) + (blocks * bitpool)
149         + 4 /* round up */ ) / 8;
150   }
151
152   *framelen = length;
153   *samples = blocks * subbands;
154
155   return 0;
156 }
157
158 static gboolean
159 gst_rtp_sbc_depay_setcaps (GstRTPBaseDepayload * base, GstCaps * caps)
160 {
161   GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (base);
162   GstStructure *structure;
163   GstCaps *outcaps, *oldcaps;
164
165   structure = gst_caps_get_structure (caps, 0);
166
167   if (!gst_structure_get_int (structure, "clock-rate", &depay->rate))
168     goto bad_caps;
169
170   outcaps = gst_caps_new_simple ("audio/x-sbc", "rate", G_TYPE_INT,
171       depay->rate, NULL);
172
173   gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (base), outcaps);
174
175   oldcaps = gst_pad_get_current_caps (GST_RTP_BASE_DEPAYLOAD_SINKPAD (base));
176   if (oldcaps && !gst_caps_can_intersect (oldcaps, caps)) {
177     /* Caps have changed, flush old data */
178     gst_adapter_clear (depay->adapter);
179   }
180
181   gst_caps_unref (outcaps);
182
183   return TRUE;
184
185 bad_caps:
186   GST_WARNING_OBJECT (depay, "Can't support the caps we got: %"
187       GST_PTR_FORMAT, caps);
188   return FALSE;
189 }
190
191 static GstBuffer *
192 gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstBuffer * in)
193 {
194   GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (base);
195   GstBuffer *data = NULL;
196   GstRTPBuffer rtp = { NULL };
197
198   gboolean fragment, start, last;
199   guint8 nframes;
200   guint8 *payload;
201   guint payload_len;
202
203   gst_rtp_buffer_map (in, GST_MAP_READ, &rtp);
204
205   GST_LOG_OBJECT (depay, "Got %" G_GSIZE_FORMAT " bytes",
206       gst_buffer_get_size (in));
207
208   if (gst_rtp_buffer_get_marker (&rtp)) {
209     /* Marker isn't supposed to be set */
210     GST_WARNING_OBJECT (depay, "Marker bit was set");
211     goto bad_packet;
212   }
213
214   payload = gst_rtp_buffer_get_payload (&rtp);
215   payload_len = gst_rtp_buffer_get_payload_len (&rtp);
216
217   fragment = payload[0] & 0x80;
218   start = payload[0] & 0x40;
219   last = payload[0] & 0x20;
220   nframes = payload[0] & 0x0f;
221
222   payload += 1;
223   payload_len -= 1;
224
225   data = gst_rtp_buffer_get_payload_subbuffer (&rtp, 1, -1);
226
227   if (fragment) {
228     /* Got a packet with a fragment */
229     GST_LOG_OBJECT (depay, "Got fragment");
230
231     if (start && gst_adapter_available (depay->adapter)) {
232       GST_WARNING_OBJECT (depay, "Missing last fragment");
233       gst_adapter_clear (depay->adapter);
234
235     } else if (!start && !gst_adapter_available (depay->adapter)) {
236       GST_WARNING_OBJECT (depay, "Missing start fragment");
237       gst_buffer_unref (data);
238       data = NULL;
239       goto out;
240     }
241
242     gst_adapter_push (depay->adapter, data);
243
244     if (last) {
245       data = gst_adapter_take_buffer (depay->adapter,
246           gst_adapter_available (depay->adapter));
247     } else
248       data = NULL;
249
250   } else {
251     /* !fragment */
252     gint framelen, samples;
253
254     GST_LOG_OBJECT (depay, "Got %d frames", nframes);
255
256     if (gst_rtp_sbc_depay_get_params (depay, payload,
257             payload_len, &framelen, &samples) < 0) {
258       gst_adapter_clear (depay->adapter);
259       goto bad_packet;
260     }
261
262     GST_LOG_OBJECT (depay, "Got payload of %d", payload_len);
263
264     if (nframes * framelen > (gint) payload_len) {
265       GST_WARNING_OBJECT (depay, "Short packet");
266       goto bad_packet;
267     } else if (nframes * framelen < (gint) payload_len) {
268       GST_WARNING_OBJECT (depay, "Junk at end of packet");
269     }
270   }
271
272 out:
273   gst_rtp_buffer_unmap (&rtp);
274   return data;
275
276 bad_packet:
277   GST_ELEMENT_WARNING (depay, STREAM, DECODE,
278       ("Received invalid RTP payload, dropping"), (NULL));
279   goto out;
280 }
281
282 gboolean
283 gst_rtp_sbc_depay_plugin_init (GstPlugin * plugin)
284 {
285   return gst_element_register (plugin, "rtpsbcdepay", GST_RANK_SECONDARY,
286       GST_TYPE_RTP_SBC_DEPAY);
287 }