2 * Copyright (C) <2007> Nokia Corporation
3 * Copyright (C) <2007> Collabora Ltd
4 * @author: Olivier Crete <olivier.crete@collabora.co.uk>
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.
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.
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., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
27 #include <gst/rtp/gstrtpbuffer.h>
28 #include <gst/base/gstadapter.h>
30 #include "gstrtpg723pay.h"
32 #define GST_RTP_PAYLOAD_G723 4
33 #define GST_RTP_PAYLOAD_G723_STRING "4"
35 #define G723_FRAME_DURATION (30 * GST_MSECOND)
37 static gboolean gst_rtp_g723_pay_set_caps (GstRTPBasePayload * payload,
39 static GstFlowReturn gst_rtp_g723_pay_handle_buffer (GstRTPBasePayload *
40 payload, GstBuffer * buf);
42 static GstStaticPadTemplate gst_rtp_g723_pay_sink_template =
43 GST_STATIC_PAD_TEMPLATE ("sink",
46 GST_STATIC_CAPS ("audio/G723, " /* according to RFC 3551 */
47 "channels = (int) 1, " "rate = (int) 8000")
50 static GstStaticPadTemplate gst_rtp_g723_pay_src_template =
51 GST_STATIC_PAD_TEMPLATE ("src",
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\"; "
60 "media = (string) \"audio\", "
61 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
62 "clock-rate = (int) 8000, " "encoding-name = (string) \"G723\"")
65 static void gst_rtp_g723_pay_finalize (GObject * object);
67 static GstStateChangeReturn gst_rtp_g723_pay_change_state (GstElement * element,
68 GstStateChange transition);
70 #define gst_rtp_g723_pay_parent_class parent_class
71 G_DEFINE_TYPE (GstRTPG723Pay, gst_rtp_g723_pay, GST_TYPE_RTP_BASE_PAYLOAD);
74 gst_rtp_g723_pay_class_init (GstRTPG723PayClass * klass)
76 GObjectClass *gobject_class;
77 GstElementClass *gstelement_class;
78 GstRTPBasePayloadClass *payload_class;
80 gobject_class = (GObjectClass *) klass;
81 gstelement_class = (GstElementClass *) klass;
82 payload_class = (GstRTPBasePayloadClass *) klass;
84 gobject_class->finalize = gst_rtp_g723_pay_finalize;
86 gstelement_class->change_state = gst_rtp_g723_pay_change_state;
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));
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>");
98 payload_class->set_caps = gst_rtp_g723_pay_set_caps;
99 payload_class->handle_buffer = gst_rtp_g723_pay_handle_buffer;
103 gst_rtp_g723_pay_init (GstRTPG723Pay * pay)
105 GstRTPBasePayload *payload = GST_RTP_BASE_PAYLOAD (pay);
107 pay->adapter = gst_adapter_new ();
109 payload->pt = GST_RTP_PAYLOAD_G723;
110 gst_rtp_base_payload_set_options (payload, "audio", FALSE, "G723", 8000);
114 gst_rtp_g723_pay_finalize (GObject * object)
118 pay = GST_RTP_G723_PAY (object);
120 g_object_unref (pay->adapter);
123 G_OBJECT_CLASS (parent_class)->finalize (object);
128 gst_rtp_g723_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps)
131 GstStructure *structure;
134 structure = gst_caps_get_structure (caps, 0);
135 if (!gst_structure_get_int (structure, "payload", &pt))
136 pt = GST_RTP_PAYLOAD_G723;
139 payload->dynamic = pt != GST_RTP_PAYLOAD_G723;
141 res = gst_rtp_base_payload_set_outcaps (payload, NULL);
147 gst_rtp_g723_pay_flush (GstRTPG723Pay * pay)
153 GstRTPBuffer rtp = { NULL };
155 avail = gst_adapter_available (pay->adapter);
157 outbuf = gst_rtp_buffer_new_allocate (avail, 0, 0);
159 gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
160 payload = gst_rtp_buffer_get_payload (&rtp);
162 GST_BUFFER_TIMESTAMP (outbuf) = pay->timestamp;
163 GST_BUFFER_DURATION (outbuf) = pay->duration;
165 /* copy G723 data as payload */
166 gst_adapter_copy (pay->adapter, payload, 0, avail);
168 /* flush bytes from adapter */
169 gst_adapter_flush (pay->adapter, avail);
170 pay->timestamp = GST_CLOCK_TIME_NONE;
173 /* set discont and marker */
175 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
176 gst_rtp_buffer_set_marker (&rtp, TRUE);
177 pay->discont = FALSE;
179 gst_rtp_buffer_unmap (&rtp);
181 ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (pay), outbuf);
186 /* 00 high-rate speech (6.3 kb/s) 24
187 * 01 low-rate speech (5.3 kb/s) 20
190 static const guint size_tab[4] = {
195 gst_rtp_g723_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf)
197 GstFlowReturn ret = GST_FLOW_OK;
201 GstClockTime packet_dur, timestamp;
202 guint payload_len, packet_len;
204 pay = GST_RTP_G723_PAY (payload);
206 gst_buffer_map (buf, &map, GST_MAP_READ);
207 timestamp = GST_BUFFER_TIMESTAMP (buf);
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;
217 /* should be one of these sizes */
218 if (map.size != 4 && map.size != 20 && map.size != 24)
221 /* check size by looking at the header bits */
222 HDR = map.data[0] & 0x3;
223 if (size_tab[HDR] != map.size)
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);
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);
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;
244 gst_buffer_unmap (buf, &map);
246 /* add packet to the queue */
247 gst_adapter_push (pay->adapter, buf);
248 pay->duration = packet_dur;
250 /* check if we can flush now */
251 if (pay->duration >= payload->min_ptime) {
252 ret = gst_rtp_g723_pay_flush (pay);
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);
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);
279 static GstStateChangeReturn
280 gst_rtp_g723_pay_change_state (GstElement * element, GstStateChange transition)
282 GstStateChangeReturn ret;
285 pay = GST_RTP_G723_PAY (element);
287 switch (transition) {
288 case GST_STATE_CHANGE_READY_TO_PAUSED:
289 gst_adapter_clear (pay->adapter);
290 pay->timestamp = GST_CLOCK_TIME_NONE;
298 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
300 switch (transition) {
301 case GST_STATE_CHANGE_PAUSED_TO_READY:
302 gst_adapter_clear (pay->adapter);
311 /*Plugin init functions*/
313 gst_rtp_g723_pay_plugin_init (GstPlugin * plugin)
315 return gst_element_register (plugin, "rtpg723pay", GST_RANK_SECONDARY,
316 gst_rtp_g723_pay_get_type ());