rtpvp8: port some more to new memory API
[platform/upstream/gstreamer.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 (GstRTPBasePayload * payload,
38     GstCaps * caps);
39 static GstFlowReturn gst_rtp_g723_pay_handle_buffer (GstRTPBasePayload *
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 #define gst_rtp_g723_pay_parent_class parent_class
71 G_DEFINE_TYPE (GstRTPG723Pay, gst_rtp_g723_pay, GST_TYPE_RTP_BASE_PAYLOAD);
72
73 static void
74 gst_rtp_g723_pay_class_init (GstRTPG723PayClass * klass)
75 {
76   GObjectClass *gobject_class;
77   GstElementClass *gstelement_class;
78   GstRTPBasePayloadClass *payload_class;
79
80   gobject_class = (GObjectClass *) klass;
81   gstelement_class = (GstElementClass *) klass;
82   payload_class = (GstRTPBasePayloadClass *) klass;
83
84   gobject_class->finalize = gst_rtp_g723_pay_finalize;
85
86   gstelement_class->change_state = gst_rtp_g723_pay_change_state;
87
88   gst_element_class_add_pad_template (gstelement_class,
89       gst_static_pad_template_get (&gst_rtp_g723_pay_sink_template));
90   gst_element_class_add_pad_template (gstelement_class,
91       gst_static_pad_template_get (&gst_rtp_g723_pay_src_template));
92
93   gst_element_class_set_static_metadata (gstelement_class,
94       "RTP G.723 payloader", "Codec/Payloader/Network/RTP",
95       "Packetize G.723 audio into RTP packets",
96       "Wim Taymans <wim.taymans@gmail.com>");
97
98   payload_class->set_caps = gst_rtp_g723_pay_set_caps;
99   payload_class->handle_buffer = gst_rtp_g723_pay_handle_buffer;
100 }
101
102 static void
103 gst_rtp_g723_pay_init (GstRTPG723Pay * pay)
104 {
105   GstRTPBasePayload *payload = GST_RTP_BASE_PAYLOAD (pay);
106
107   pay->adapter = gst_adapter_new ();
108
109   payload->pt = GST_RTP_PAYLOAD_G723;
110   gst_rtp_base_payload_set_options (payload, "audio", FALSE, "G723", 8000);
111 }
112
113 static void
114 gst_rtp_g723_pay_finalize (GObject * object)
115 {
116   GstRTPG723Pay *pay;
117
118   pay = GST_RTP_G723_PAY (object);
119
120   g_object_unref (pay->adapter);
121   pay->adapter = NULL;
122
123   G_OBJECT_CLASS (parent_class)->finalize (object);
124 }
125
126
127 static gboolean
128 gst_rtp_g723_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps)
129 {
130   gboolean res;
131   GstStructure *structure;
132   gint pt;
133
134   structure = gst_caps_get_structure (caps, 0);
135   if (!gst_structure_get_int (structure, "payload", &pt))
136     pt = GST_RTP_PAYLOAD_G723;
137
138   payload->pt = pt;
139   payload->dynamic = pt != GST_RTP_PAYLOAD_G723;
140
141   res = gst_rtp_base_payload_set_outcaps (payload, NULL);
142
143   return res;
144 }
145
146 static GstFlowReturn
147 gst_rtp_g723_pay_flush (GstRTPG723Pay * pay)
148 {
149   GstBuffer *outbuf;
150   GstFlowReturn ret;
151   guint8 *payload;
152   guint avail;
153   GstRTPBuffer rtp = { NULL };
154
155   avail = gst_adapter_available (pay->adapter);
156
157   outbuf = gst_rtp_buffer_new_allocate (avail, 0, 0);
158
159   gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
160   payload = gst_rtp_buffer_get_payload (&rtp);
161
162   GST_BUFFER_TIMESTAMP (outbuf) = pay->timestamp;
163   GST_BUFFER_DURATION (outbuf) = pay->duration;
164
165   /* copy G723 data as payload */
166   gst_adapter_copy (pay->adapter, payload, 0, avail);
167
168   /* flush bytes from adapter */
169   gst_adapter_flush (pay->adapter, avail);
170   pay->timestamp = GST_CLOCK_TIME_NONE;
171   pay->duration = 0;
172
173   /* set discont and marker */
174   if (pay->discont) {
175     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
176     gst_rtp_buffer_set_marker (&rtp, TRUE);
177     pay->discont = FALSE;
178   }
179   gst_rtp_buffer_unmap (&rtp);
180
181   ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (pay), outbuf);
182
183   return ret;
184 }
185
186 /* 00    high-rate speech (6.3 kb/s)            24
187  * 01    low-rate speech  (5.3 kb/s)            20
188  * 10    SID frame                               4
189  * 11    reserved                                0  */
190 static const guint size_tab[4] = {
191   24, 20, 4, 0
192 };
193
194 static GstFlowReturn
195 gst_rtp_g723_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf)
196 {
197   GstFlowReturn ret = GST_FLOW_OK;
198   GstMapInfo map;
199   guint8 HDR;
200   GstRTPG723Pay *pay;
201   GstClockTime packet_dur, timestamp;
202   guint payload_len, packet_len;
203
204   pay = GST_RTP_G723_PAY (payload);
205
206   gst_buffer_map (buf, &map, GST_MAP_READ);
207   timestamp = GST_BUFFER_TIMESTAMP (buf);
208
209   if (GST_BUFFER_IS_DISCONT (buf)) {
210     /* flush everything on discont */
211     gst_adapter_clear (pay->adapter);
212     pay->timestamp = GST_CLOCK_TIME_NONE;
213     pay->duration = 0;
214     pay->discont = TRUE;
215   }
216
217   /* should be one of these sizes */
218   if (map.size != 4 && map.size != 20 && map.size != 24)
219     goto invalid_size;
220
221   /* check size by looking at the header bits */
222   HDR = map.data[0] & 0x3;
223   if (size_tab[HDR] != map.size)
224     goto wrong_size;
225
226   /* calculate packet size and duration */
227   payload_len = gst_adapter_available (pay->adapter) + map.size;
228   packet_dur = pay->duration + G723_FRAME_DURATION;
229   packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0);
230
231   if (gst_rtp_base_payload_is_filled (payload, packet_len, packet_dur)) {
232     /* size or duration would overflow the packet, flush the queued data */
233     ret = gst_rtp_g723_pay_flush (pay);
234   }
235
236   /* update timestamp, we keep the timestamp for the first packet in the adapter
237    * but are able to calculate it from next packets. */
238   if (timestamp != GST_CLOCK_TIME_NONE && pay->timestamp == GST_CLOCK_TIME_NONE) {
239     if (timestamp > pay->duration)
240       pay->timestamp = timestamp - pay->duration;
241     else
242       pay->timestamp = 0;
243   }
244   gst_buffer_unmap (buf, &map);
245
246   /* add packet to the queue */
247   gst_adapter_push (pay->adapter, buf);
248   pay->duration = packet_dur;
249
250   /* check if we can flush now */
251   if (pay->duration >= payload->min_ptime) {
252     ret = gst_rtp_g723_pay_flush (pay);
253   }
254
255   return ret;
256
257   /* WARNINGS */
258 invalid_size:
259   {
260     GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE,
261         ("Invalid input buffer size"),
262         ("Input size should be 4, 20 or 24, got %" G_GSIZE_FORMAT, map.size));
263     gst_buffer_unmap (buf, &map);
264     gst_buffer_unref (buf);
265     return GST_FLOW_OK;
266   }
267 wrong_size:
268   {
269     GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE,
270         ("Wrong input buffer size"),
271         ("Expected input buffer size %u but got %" G_GSIZE_FORMAT,
272             size_tab[HDR], map.size));
273     gst_buffer_unmap (buf, &map);
274     gst_buffer_unref (buf);
275     return GST_FLOW_OK;
276   }
277 }
278
279 static GstStateChangeReturn
280 gst_rtp_g723_pay_change_state (GstElement * element, GstStateChange transition)
281 {
282   GstStateChangeReturn ret;
283   GstRTPG723Pay *pay;
284
285   pay = GST_RTP_G723_PAY (element);
286
287   switch (transition) {
288     case GST_STATE_CHANGE_READY_TO_PAUSED:
289       gst_adapter_clear (pay->adapter);
290       pay->timestamp = GST_CLOCK_TIME_NONE;
291       pay->duration = 0;
292       pay->discont = TRUE;
293       break;
294     default:
295       break;
296   }
297
298   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
299
300   switch (transition) {
301     case GST_STATE_CHANGE_PAUSED_TO_READY:
302       gst_adapter_clear (pay->adapter);
303       break;
304     default:
305       break;
306   }
307
308   return ret;
309 }
310
311 /*Plugin init functions*/
312 gboolean
313 gst_rtp_g723_pay_plugin_init (GstPlugin * plugin)
314 {
315   return gst_element_register (plugin, "rtpg723pay", GST_RANK_SECONDARY,
316       gst_rtp_g723_pay_get_type ());
317 }