upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / rtp / gstrtpceltpay.c
1 /* GStreamer
2  * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.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 details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <gst/rtp/gstrtpbuffer.h>
27
28 #include "gstrtpceltpay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rtpceltpay_debug);
31 #define GST_CAT_DEFAULT (rtpceltpay_debug)
32
33 static GstStaticPadTemplate gst_rtp_celt_pay_sink_template =
34 GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS ("audio/x-celt, "
38         "rate = (int) [ 32000, 64000 ], "
39         "channels = (int) [1, 2], " "frame-size = (int) [ 64, 512 ]")
40     );
41
42 static GstStaticPadTemplate gst_rtp_celt_pay_src_template =
43 GST_STATIC_PAD_TEMPLATE ("src",
44     GST_PAD_SRC,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("application/x-rtp, "
47         "media = (string) \"audio\", "
48         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
49         "clock-rate =  (int) [ 32000, 48000 ], "
50         "encoding-name = (string) \"CELT\"")
51     );
52
53 static void gst_rtp_celt_pay_finalize (GObject * object);
54
55 static GstStateChangeReturn gst_rtp_celt_pay_change_state (GstElement *
56     element, GstStateChange transition);
57
58 static gboolean gst_rtp_celt_pay_setcaps (GstBaseRTPPayload * payload,
59     GstCaps * caps);
60 static GstCaps *gst_rtp_celt_pay_getcaps (GstBaseRTPPayload * payload,
61     GstPad * pad);
62 static GstFlowReturn gst_rtp_celt_pay_handle_buffer (GstBaseRTPPayload *
63     payload, GstBuffer * buffer);
64
65 GST_BOILERPLATE (GstRtpCELTPay, gst_rtp_celt_pay, GstBaseRTPPayload,
66     GST_TYPE_BASE_RTP_PAYLOAD);
67
68 static void
69 gst_rtp_celt_pay_base_init (gpointer klass)
70 {
71   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
72
73   gst_element_class_add_pad_template (element_class,
74       gst_static_pad_template_get (&gst_rtp_celt_pay_sink_template));
75   gst_element_class_add_pad_template (element_class,
76       gst_static_pad_template_get (&gst_rtp_celt_pay_src_template));
77   gst_element_class_set_details_simple (element_class, "RTP CELT payloader",
78       "Codec/Payloader/Network/RTP",
79       "Payload-encodes CELT audio into a RTP packet",
80       "Wim Taymans <wim.taymans@gmail.com>");
81
82   GST_DEBUG_CATEGORY_INIT (rtpceltpay_debug, "rtpceltpay", 0,
83       "CELT RTP Payloader");
84 }
85
86 static void
87 gst_rtp_celt_pay_class_init (GstRtpCELTPayClass * klass)
88 {
89   GObjectClass *gobject_class;
90   GstElementClass *gstelement_class;
91   GstBaseRTPPayloadClass *gstbasertppayload_class;
92
93   gobject_class = (GObjectClass *) klass;
94   gstelement_class = (GstElementClass *) klass;
95   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
96
97   gobject_class->finalize = gst_rtp_celt_pay_finalize;
98
99   gstelement_class->change_state = gst_rtp_celt_pay_change_state;
100
101   gstbasertppayload_class->set_caps = gst_rtp_celt_pay_setcaps;
102   gstbasertppayload_class->get_caps = gst_rtp_celt_pay_getcaps;
103   gstbasertppayload_class->handle_buffer = gst_rtp_celt_pay_handle_buffer;
104 }
105
106 static void
107 gst_rtp_celt_pay_init (GstRtpCELTPay * rtpceltpay, GstRtpCELTPayClass * klass)
108 {
109   rtpceltpay->queue = g_queue_new ();
110 }
111
112 static void
113 gst_rtp_celt_pay_finalize (GObject * object)
114 {
115   GstRtpCELTPay *rtpceltpay;
116
117   rtpceltpay = GST_RTP_CELT_PAY (object);
118
119   g_queue_free (rtpceltpay->queue);
120
121   G_OBJECT_CLASS (parent_class)->finalize (object);
122 }
123
124 static void
125 gst_rtp_celt_pay_clear_queued (GstRtpCELTPay * rtpceltpay)
126 {
127   GstBuffer *buf;
128
129   while ((buf = g_queue_pop_head (rtpceltpay->queue)))
130     gst_buffer_unref (buf);
131
132   rtpceltpay->bytes = 0;
133   rtpceltpay->sbytes = 0;
134   rtpceltpay->qduration = 0;
135 }
136
137 static void
138 gst_rtp_celt_pay_add_queued (GstRtpCELTPay * rtpceltpay, GstBuffer * buffer,
139     guint ssize, guint size, GstClockTime duration)
140 {
141   g_queue_push_tail (rtpceltpay->queue, buffer);
142   rtpceltpay->sbytes += ssize;
143   rtpceltpay->bytes += size;
144   /* only add durations when we have a valid previous duration */
145   if (rtpceltpay->qduration != -1) {
146     if (duration != -1)
147       /* only add valid durations */
148       rtpceltpay->qduration += duration;
149     else
150       /* if we add a buffer without valid duration, our total queued duration
151        * becomes unknown */
152       rtpceltpay->qduration = -1;
153   }
154 }
155
156 static gboolean
157 gst_rtp_celt_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
158 {
159   /* don't configure yet, we wait for the ident packet */
160   return TRUE;
161 }
162
163
164 static GstCaps *
165 gst_rtp_celt_pay_getcaps (GstBaseRTPPayload * payload, GstPad * pad)
166 {
167   GstCaps *otherpadcaps;
168   GstCaps *caps;
169   const gchar *params;
170
171   otherpadcaps = gst_pad_get_allowed_caps (payload->srcpad);
172   caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
173
174   if (otherpadcaps) {
175     if (!gst_caps_is_empty (otherpadcaps)) {
176       GstStructure *ps = gst_caps_get_structure (otherpadcaps, 0);
177       GstStructure *s = gst_caps_get_structure (caps, 0);
178       gint clock_rate = 0, frame_size = 0, channels = 1;
179
180       if (gst_structure_get_int (ps, "clock-rate", &clock_rate)) {
181         gst_structure_fixate_field_nearest_int (s, "rate", clock_rate);
182       }
183
184       if ((params = gst_structure_get_string (ps, "frame-size")))
185         frame_size = atoi (params);
186       if (frame_size)
187         gst_structure_set (s, "frame-size", G_TYPE_INT, frame_size, NULL);
188
189       if ((params = gst_structure_get_string (ps, "encoding-params"))) {
190         channels = atoi (params);
191         gst_structure_fixate_field_nearest_int (s, "channels", channels);
192       }
193
194       GST_DEBUG_OBJECT (payload, "clock-rate=%d frame-size=%d channels=%d",
195           clock_rate, frame_size, channels);
196     }
197     gst_caps_unref (otherpadcaps);
198   }
199
200   return caps;
201 }
202
203 static gboolean
204 gst_rtp_celt_pay_parse_ident (GstRtpCELTPay * rtpceltpay,
205     const guint8 * data, guint size)
206 {
207   guint32 version, header_size, rate, nb_channels, frame_size, overlap;
208   guint32 bytes_per_packet;
209   GstBaseRTPPayload *payload;
210   gchar *cstr, *fsstr;
211   gboolean res;
212
213   /* we need the header string (8), the version string (20), the version
214    * and the header length. */
215   if (size < 36)
216     goto too_small;
217
218   if (!g_str_has_prefix ((const gchar *) data, "CELT    "))
219     goto wrong_header;
220
221   /* skip header and version string */
222   data += 28;
223
224   version = GST_READ_UINT32_LE (data);
225   GST_DEBUG_OBJECT (rtpceltpay, "version %08x", version);
226 #if 0
227   if (version != 1)
228     goto wrong_version;
229 #endif
230
231   data += 4;
232   /* ensure sizes */
233   header_size = GST_READ_UINT32_LE (data);
234   if (header_size < 56)
235     goto header_too_small;
236
237   if (size < header_size)
238     goto payload_too_small;
239
240   data += 4;
241   rate = GST_READ_UINT32_LE (data);
242   data += 4;
243   nb_channels = GST_READ_UINT32_LE (data);
244   data += 4;
245   frame_size = GST_READ_UINT32_LE (data);
246   data += 4;
247   overlap = GST_READ_UINT32_LE (data);
248   data += 4;
249   bytes_per_packet = GST_READ_UINT32_LE (data);
250
251   GST_DEBUG_OBJECT (rtpceltpay, "rate %d, nb_channels %d, frame_size %d",
252       rate, nb_channels, frame_size);
253   GST_DEBUG_OBJECT (rtpceltpay, "overlap %d, bytes_per_packet %d",
254       overlap, bytes_per_packet);
255
256   payload = GST_BASE_RTP_PAYLOAD (rtpceltpay);
257
258   gst_basertppayload_set_options (payload, "audio", FALSE, "CELT", rate);
259   cstr = g_strdup_printf ("%d", nb_channels);
260   fsstr = g_strdup_printf ("%d", frame_size);
261   res = gst_basertppayload_set_outcaps (payload, "encoding-params",
262       G_TYPE_STRING, cstr, "frame-size", G_TYPE_STRING, fsstr, NULL);
263   g_free (cstr);
264   g_free (fsstr);
265
266   return res;
267
268   /* ERRORS */
269 too_small:
270   {
271     GST_DEBUG_OBJECT (rtpceltpay,
272         "ident packet too small, need at least 32 bytes");
273     return FALSE;
274   }
275 wrong_header:
276   {
277     GST_DEBUG_OBJECT (rtpceltpay,
278         "ident packet does not start with \"CELT    \"");
279     return FALSE;
280   }
281 #if 0
282 wrong_version:
283   {
284     GST_DEBUG_OBJECT (rtpceltpay, "can only handle version 1, have version %d",
285         version);
286     return FALSE;
287   }
288 #endif
289 header_too_small:
290   {
291     GST_DEBUG_OBJECT (rtpceltpay,
292         "header size too small, need at least 80 bytes, " "got only %d",
293         header_size);
294     return FALSE;
295   }
296 payload_too_small:
297   {
298     GST_DEBUG_OBJECT (rtpceltpay,
299         "payload too small, need at least %d bytes, got only %d", header_size,
300         size);
301     return FALSE;
302   }
303 }
304
305 static GstFlowReturn
306 gst_rtp_celt_pay_flush_queued (GstRtpCELTPay * rtpceltpay)
307 {
308   GstFlowReturn ret;
309   GstBuffer *buf, *outbuf;
310   guint8 *payload, *spayload;
311   guint payload_len;
312   GstClockTime duration;
313
314   payload_len = rtpceltpay->bytes + rtpceltpay->sbytes;
315   duration = rtpceltpay->qduration;
316
317   GST_DEBUG_OBJECT (rtpceltpay, "flushing out %u, duration %" GST_TIME_FORMAT,
318       payload_len, GST_TIME_ARGS (rtpceltpay->qduration));
319
320   /* get a big enough packet for the sizes + payloads */
321   outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
322
323   GST_BUFFER_DURATION (outbuf) = duration;
324
325   /* point to the payload for size headers and data */
326   spayload = gst_rtp_buffer_get_payload (outbuf);
327   payload = spayload + rtpceltpay->sbytes;
328
329   while ((buf = g_queue_pop_head (rtpceltpay->queue))) {
330     guint size;
331
332     /* copy first timestamp to output */
333     if (GST_BUFFER_TIMESTAMP (outbuf) == -1)
334       GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
335
336     /* write the size to the header */
337     size = GST_BUFFER_SIZE (buf);
338     while (size > 0xff) {
339       *spayload++ = 0xff;
340       size -= 0xff;
341     }
342     *spayload++ = size;
343
344     size = GST_BUFFER_SIZE (buf);
345     /* copy payload */
346     memcpy (payload, GST_BUFFER_DATA (buf), size);
347     payload += size;
348
349     gst_buffer_unref (buf);
350   }
351
352   /* we consumed it all */
353   rtpceltpay->bytes = 0;
354   rtpceltpay->sbytes = 0;
355   rtpceltpay->qduration = 0;
356
357   ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpceltpay), outbuf);
358
359   return ret;
360 }
361
362 static GstFlowReturn
363 gst_rtp_celt_pay_handle_buffer (GstBaseRTPPayload * basepayload,
364     GstBuffer * buffer)
365 {
366   GstFlowReturn ret;
367   GstRtpCELTPay *rtpceltpay;
368   guint size, payload_len;
369   guint8 *data;
370   GstClockTime duration, packet_dur;
371   guint i, ssize, packet_len;
372
373   rtpceltpay = GST_RTP_CELT_PAY (basepayload);
374
375   ret = GST_FLOW_OK;
376
377   size = GST_BUFFER_SIZE (buffer);
378   data = GST_BUFFER_DATA (buffer);
379
380   switch (rtpceltpay->packet) {
381     case 0:
382       /* ident packet. We need to parse the headers to construct the RTP
383        * properties. */
384       if (!gst_rtp_celt_pay_parse_ident (rtpceltpay, data, size))
385         goto parse_error;
386
387       goto done;
388     case 1:
389       /* comment packet, we ignore it */
390       goto done;
391     default:
392       /* other packets go in the payload */
393       break;
394   }
395
396   duration = GST_BUFFER_DURATION (buffer);
397
398   GST_LOG_OBJECT (rtpceltpay,
399       "got buffer of duration %" GST_TIME_FORMAT ", size %u",
400       GST_TIME_ARGS (duration), size);
401
402   /* calculate the size of the size field and the payload */
403   ssize = 1;
404   for (i = size; i > 0xff; i -= 0xff)
405     ssize++;
406
407   GST_DEBUG_OBJECT (rtpceltpay, "bytes for size %u", ssize);
408
409   /* calculate what the new size and duration would be of the packet */
410   payload_len = ssize + size + rtpceltpay->bytes + rtpceltpay->sbytes;
411   if (rtpceltpay->qduration != -1 && duration != -1)
412     packet_dur = rtpceltpay->qduration + duration;
413   else
414     packet_dur = 0;
415
416   packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0);
417
418   if (gst_basertppayload_is_filled (basepayload, packet_len, packet_dur)) {
419     /* size or duration would overflow the packet, flush the queued data */
420     ret = gst_rtp_celt_pay_flush_queued (rtpceltpay);
421   }
422
423   /* queue the packet */
424   gst_rtp_celt_pay_add_queued (rtpceltpay, buffer, ssize, size, duration);
425
426 done:
427   rtpceltpay->packet++;
428
429   return ret;
430
431   /* ERRORS */
432 parse_error:
433   {
434     GST_ELEMENT_ERROR (rtpceltpay, STREAM, DECODE, (NULL),
435         ("Error parsing first identification packet."));
436     return GST_FLOW_ERROR;
437   }
438 }
439
440 static GstStateChangeReturn
441 gst_rtp_celt_pay_change_state (GstElement * element, GstStateChange transition)
442 {
443   GstRtpCELTPay *rtpceltpay;
444   GstStateChangeReturn ret;
445
446   rtpceltpay = GST_RTP_CELT_PAY (element);
447
448   switch (transition) {
449     case GST_STATE_CHANGE_NULL_TO_READY:
450       break;
451     case GST_STATE_CHANGE_READY_TO_PAUSED:
452       rtpceltpay->packet = 0;
453       break;
454     default:
455       break;
456   }
457
458   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
459
460   switch (transition) {
461     case GST_STATE_CHANGE_PAUSED_TO_READY:
462       gst_rtp_celt_pay_clear_queued (rtpceltpay);
463       break;
464     case GST_STATE_CHANGE_READY_TO_NULL:
465       break;
466     default:
467       break;
468   }
469   return ret;
470 }
471
472 gboolean
473 gst_rtp_celt_pay_plugin_init (GstPlugin * plugin)
474 {
475   return gst_element_register (plugin, "rtpceltpay",
476       GST_RANK_SECONDARY, GST_TYPE_RTP_CELT_PAY);
477 }