Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / gst / rtp / gstrtpg723pay.c
1 /* GStreamer
2  * Copyright (C) <2007> Nokia Corporation
3  * Copyright (C) <2007> Collabora Ltd
4  *  @author: Olivier Crete <olivier.crete@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <gst/rtp/gstrtpbuffer.h>
28 #include <gst/base/gstadapter.h>
29
30 #include "gstrtpg723pay.h"
31
32 #define GST_RTP_PAYLOAD_G723 4
33 #define GST_RTP_PAYLOAD_G723_STRING "4"
34
35 #define G723_FRAME_DURATION (30 * GST_MSECOND)
36
37 static gboolean gst_rtp_g723_pay_set_caps (GstBaseRTPPayload * payload,
38     GstCaps * caps);
39 static GstFlowReturn gst_rtp_g723_pay_handle_buffer (GstBaseRTPPayload *
40     payload, GstBuffer * buf);
41
42 static GstStaticPadTemplate gst_rtp_g723_pay_sink_template =
43 GST_STATIC_PAD_TEMPLATE ("sink",
44     GST_PAD_SINK,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("audio/G723, "     /* according to RFC 3551 */
47         "channels = (int) 1, " "rate = (int) 8000")
48     );
49
50 static GstStaticPadTemplate gst_rtp_g723_pay_src_template =
51     GST_STATIC_PAD_TEMPLATE ("src",
52     GST_PAD_SRC,
53     GST_PAD_ALWAYS,
54     GST_STATIC_CAPS ("application/x-rtp, "
55         "media = (string) \"audio\", "
56         "payload = (int) " GST_RTP_PAYLOAD_G723_STRING ", "
57         "clock-rate = (int) 8000, "
58         "encoding-name = (string) \"G723\"; "
59         "application/x-rtp, "
60         "media = (string) \"audio\", "
61         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
62         "clock-rate = (int) 8000, " "encoding-name = (string) \"G723\"")
63     );
64
65 static void gst_rtp_g723_pay_finalize (GObject * object);
66
67 static GstStateChangeReturn gst_rtp_g723_pay_change_state (GstElement * element,
68     GstStateChange transition);
69
70 GST_BOILERPLATE (GstRTPG723Pay, gst_rtp_g723_pay, GstBaseRTPPayload,
71     GST_TYPE_BASE_RTP_PAYLOAD);
72
73 static void
74 gst_rtp_g723_pay_base_init (gpointer klass)
75 {
76   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
77
78   gst_element_class_add_static_pad_template (element_class,
79       &gst_rtp_g723_pay_sink_template);
80   gst_element_class_add_static_pad_template (element_class,
81       &gst_rtp_g723_pay_src_template);
82   gst_element_class_set_details_simple (element_class, "RTP G.723 payloader",
83       "Codec/Payloader/Network/RTP",
84       "Packetize G.723 audio into RTP packets",
85       "Wim Taymans <wim.taymans@gmail.com>");
86 }
87
88 static void
89 gst_rtp_g723_pay_class_init (GstRTPG723PayClass * klass)
90 {
91   GObjectClass *gobject_class;
92   GstElementClass *gstelement_class;
93   GstBaseRTPPayloadClass *payload_class;
94
95   gobject_class = (GObjectClass *) klass;
96   gstelement_class = (GstElementClass *) klass;
97   payload_class = (GstBaseRTPPayloadClass *) klass;
98
99   gobject_class->finalize = gst_rtp_g723_pay_finalize;
100
101   gstelement_class->change_state = gst_rtp_g723_pay_change_state;
102
103   payload_class->set_caps = gst_rtp_g723_pay_set_caps;
104   payload_class->handle_buffer = gst_rtp_g723_pay_handle_buffer;
105 }
106
107 static void
108 gst_rtp_g723_pay_init (GstRTPG723Pay * pay, GstRTPG723PayClass * klass)
109 {
110   GstBaseRTPPayload *payload = GST_BASE_RTP_PAYLOAD (pay);
111
112   pay->adapter = gst_adapter_new ();
113
114   payload->pt = GST_RTP_PAYLOAD_G723;
115   gst_basertppayload_set_options (payload, "audio", FALSE, "G723", 8000);
116 }
117
118 static void
119 gst_rtp_g723_pay_finalize (GObject * object)
120 {
121   GstRTPG723Pay *pay;
122
123   pay = GST_RTP_G723_PAY (object);
124
125   g_object_unref (pay->adapter);
126   pay->adapter = NULL;
127
128   G_OBJECT_CLASS (parent_class)->finalize (object);
129 }
130
131
132 static gboolean
133 gst_rtp_g723_pay_set_caps (GstBaseRTPPayload * payload, GstCaps * caps)
134 {
135   gboolean res;
136   GstStructure *structure;
137   gint pt;
138
139   structure = gst_caps_get_structure (caps, 0);
140   if (!gst_structure_get_int (structure, "payload", &pt))
141     pt = GST_RTP_PAYLOAD_G723;
142
143   payload->pt = pt;
144   payload->dynamic = pt != GST_RTP_PAYLOAD_G723;
145
146   res = gst_basertppayload_set_outcaps (payload, NULL);
147
148   return res;
149 }
150
151 static GstFlowReturn
152 gst_rtp_g723_pay_flush (GstRTPG723Pay * pay)
153 {
154   GstBuffer *outbuf;
155   GstFlowReturn ret;
156   guint8 *payload;
157   guint avail;
158
159   avail = gst_adapter_available (pay->adapter);
160
161   outbuf = gst_rtp_buffer_new_allocate (avail, 0, 0);
162   payload = gst_rtp_buffer_get_payload (outbuf);
163
164   GST_BUFFER_TIMESTAMP (outbuf) = pay->timestamp;
165   GST_BUFFER_DURATION (outbuf) = pay->duration;
166
167   /* copy G723 data as payload */
168   gst_adapter_copy (pay->adapter, payload, 0, avail);
169
170   /* flush bytes from adapter */
171   gst_adapter_flush (pay->adapter, avail);
172   pay->timestamp = GST_CLOCK_TIME_NONE;
173   pay->duration = 0;
174
175   /* set discont and marker */
176   if (pay->discont) {
177     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
178     gst_rtp_buffer_set_marker (outbuf, TRUE);
179     pay->discont = FALSE;
180   }
181
182   ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (pay), outbuf);
183
184   return ret;
185 }
186
187 /* 00    high-rate speech (6.3 kb/s)            24
188  * 01    low-rate speech  (5.3 kb/s)            20
189  * 10    SID frame                               4
190  * 11    reserved                                0  */
191 static const guint size_tab[4] = {
192   24, 20, 4, 0
193 };
194
195 static GstFlowReturn
196 gst_rtp_g723_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf)
197 {
198   GstFlowReturn ret = GST_FLOW_OK;
199   guint8 *data;
200   guint size;
201   guint8 HDR;
202   GstRTPG723Pay *pay;
203   GstClockTime packet_dur, timestamp;
204   guint payload_len, packet_len;
205
206   pay = GST_RTP_G723_PAY (payload);
207
208   size = GST_BUFFER_SIZE (buf);
209   data = GST_BUFFER_DATA (buf);
210   timestamp = GST_BUFFER_TIMESTAMP (buf);
211
212   if (GST_BUFFER_IS_DISCONT (buf)) {
213     /* flush everything on discont */
214     gst_adapter_clear (pay->adapter);
215     pay->timestamp = GST_CLOCK_TIME_NONE;
216     pay->duration = 0;
217     pay->discont = TRUE;
218   }
219
220   /* should be one of these sizes */
221   if (size != 4 && size != 20 && size != 24)
222     goto invalid_size;
223
224   /* check size by looking at the header bits */
225   HDR = data[0] & 0x3;
226   if (size_tab[HDR] != size)
227     goto wrong_size;
228
229   /* calculate packet size and duration */
230   payload_len = gst_adapter_available (pay->adapter) + size;
231   packet_dur = pay->duration + G723_FRAME_DURATION;
232   packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0);
233
234   if (gst_basertppayload_is_filled (payload, packet_len, packet_dur)) {
235     /* size or duration would overflow the packet, flush the queued data */
236     ret = gst_rtp_g723_pay_flush (pay);
237   }
238
239   /* update timestamp, we keep the timestamp for the first packet in the adapter
240    * but are able to calculate it from next packets. */
241   if (timestamp != GST_CLOCK_TIME_NONE && pay->timestamp == GST_CLOCK_TIME_NONE) {
242     if (timestamp > pay->duration)
243       pay->timestamp = timestamp - pay->duration;
244     else
245       pay->timestamp = 0;
246   }
247
248   /* add packet to the queue */
249   gst_adapter_push (pay->adapter, buf);
250   pay->duration = packet_dur;
251
252   /* check if we can flush now */
253   if (pay->duration >= payload->min_ptime) {
254     ret = gst_rtp_g723_pay_flush (pay);
255   }
256
257   return ret;
258
259   /* WARNINGS */
260 invalid_size:
261   {
262     GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE,
263         ("Invalid input buffer size"),
264         ("Input size should be 4, 20 or 24, got %u", size));
265     gst_buffer_unref (buf);
266     return GST_FLOW_OK;
267   }
268 wrong_size:
269   {
270     GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE,
271         ("Wrong input buffer size"),
272         ("Expected input buffer size %u but got %u", size_tab[HDR], size));
273     gst_buffer_unref (buf);
274     return GST_FLOW_OK;
275   }
276 }
277
278 static GstStateChangeReturn
279 gst_rtp_g723_pay_change_state (GstElement * element, GstStateChange transition)
280 {
281   GstStateChangeReturn ret;
282   GstRTPG723Pay *pay;
283
284   pay = GST_RTP_G723_PAY (element);
285
286   switch (transition) {
287     case GST_STATE_CHANGE_READY_TO_PAUSED:
288       gst_adapter_clear (pay->adapter);
289       pay->timestamp = GST_CLOCK_TIME_NONE;
290       pay->duration = 0;
291       pay->discont = TRUE;
292       break;
293     default:
294       break;
295   }
296
297   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
298
299   switch (transition) {
300     case GST_STATE_CHANGE_PAUSED_TO_READY:
301       gst_adapter_clear (pay->adapter);
302       break;
303     default:
304       break;
305   }
306
307   return ret;
308 }
309
310 /*Plugin init functions*/
311 gboolean
312 gst_rtp_g723_pay_plugin_init (GstPlugin * plugin)
313 {
314   return gst_element_register (plugin, "rtpg723pay", GST_RANK_SECONDARY,
315       gst_rtp_g723_pay_get_type ());
316 }