rtpvrawpay: Add missing break
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpvorbispay.c
1 /* GStreamer
2  * Copyright (C) <2006> 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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <string.h>
25
26 #include <gst/rtp/gstrtpbuffer.h>
27
28 #include "fnv1hash.h"
29 #include "gstrtpvorbispay.h"
30
31 GST_DEBUG_CATEGORY_STATIC (rtpvorbispay_debug);
32 #define GST_CAT_DEFAULT (rtpvorbispay_debug)
33
34 /* references:
35  * http://www.rfc-editor.org/rfc/rfc5215.txt
36  */
37
38 static GstStaticPadTemplate gst_rtp_vorbis_pay_src_template =
39 GST_STATIC_PAD_TEMPLATE ("src",
40     GST_PAD_SRC,
41     GST_PAD_ALWAYS,
42     GST_STATIC_CAPS ("application/x-rtp, "
43         "media = (string) \"audio\", "
44         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
45         "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"VORBIS\""
46         /* All required parameters
47          *
48          * "encoding-params = (string) <num channels>"
49          * "configuration = (string) ANY"
50          */
51     )
52     );
53
54 static GstStaticPadTemplate gst_rtp_vorbis_pay_sink_template =
55 GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("audio/x-vorbis")
59     );
60
61 #define DEFAULT_CONFIG_INTERVAL 0
62
63 enum
64 {
65   PROP_0,
66   PROP_CONFIG_INTERVAL
67 };
68
69 #define gst_rtp_vorbis_pay_parent_class parent_class
70 G_DEFINE_TYPE (GstRtpVorbisPay, gst_rtp_vorbis_pay, GST_TYPE_RTP_BASE_PAYLOAD);
71
72 static gboolean gst_rtp_vorbis_pay_setcaps (GstRTPBasePayload * basepayload,
73     GstCaps * caps);
74 static GstStateChangeReturn gst_rtp_vorbis_pay_change_state (GstElement *
75     element, GstStateChange transition);
76 static GstFlowReturn gst_rtp_vorbis_pay_handle_buffer (GstRTPBasePayload * pad,
77     GstBuffer * buffer);
78 static gboolean gst_rtp_vorbis_pay_sink_event (GstRTPBasePayload * payload,
79     GstEvent * event);
80
81 static gboolean gst_rtp_vorbis_pay_parse_id (GstRTPBasePayload * basepayload,
82     guint8 * data, guint size);
83 static gboolean gst_rtp_vorbis_pay_finish_headers (GstRTPBasePayload *
84     basepayload);
85
86 static void gst_rtp_vorbis_pay_set_property (GObject * object, guint prop_id,
87     const GValue * value, GParamSpec * pspec);
88 static void gst_rtp_vorbis_pay_get_property (GObject * object, guint prop_id,
89     GValue * value, GParamSpec * pspec);
90
91 static void
92 gst_rtp_vorbis_pay_class_init (GstRtpVorbisPayClass * klass)
93 {
94   GObjectClass *gobject_class;
95   GstElementClass *gstelement_class;
96   GstRTPBasePayloadClass *gstrtpbasepayload_class;
97
98   gobject_class = (GObjectClass *) klass;
99   gstelement_class = (GstElementClass *) klass;
100   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
101
102   gstelement_class->change_state = gst_rtp_vorbis_pay_change_state;
103
104   gstrtpbasepayload_class->set_caps = gst_rtp_vorbis_pay_setcaps;
105   gstrtpbasepayload_class->handle_buffer = gst_rtp_vorbis_pay_handle_buffer;
106   gstrtpbasepayload_class->sink_event = gst_rtp_vorbis_pay_sink_event;
107
108   gobject_class->set_property = gst_rtp_vorbis_pay_set_property;
109   gobject_class->get_property = gst_rtp_vorbis_pay_get_property;
110
111   gst_element_class_add_pad_template (gstelement_class,
112       gst_static_pad_template_get (&gst_rtp_vorbis_pay_src_template));
113   gst_element_class_add_pad_template (gstelement_class,
114       gst_static_pad_template_get (&gst_rtp_vorbis_pay_sink_template));
115
116   gst_element_class_set_static_metadata (gstelement_class,
117       "RTP Vorbis depayloader",
118       "Codec/Payloader/Network/RTP",
119       "Payload-encode Vorbis audio into RTP packets (RFC 5215)",
120       "Wim Taymans <wimi.taymans@gmail.com>");
121
122   GST_DEBUG_CATEGORY_INIT (rtpvorbispay_debug, "rtpvorbispay", 0,
123       "Vorbis RTP Payloader");
124
125   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL,
126       g_param_spec_uint ("config-interval", "Config Send Interval",
127           "Send Config Insertion Interval in seconds (configuration headers "
128           "will be multiplexed in the data stream when detected.) (0 = disabled)",
129           0, 3600, DEFAULT_CONFIG_INTERVAL,
130           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
131       );
132 }
133
134 static void
135 gst_rtp_vorbis_pay_init (GstRtpVorbisPay * rtpvorbispay)
136 {
137   rtpvorbispay->last_config = GST_CLOCK_TIME_NONE;
138 }
139
140 static void
141 gst_rtp_vorbis_pay_clear_packet (GstRtpVorbisPay * rtpvorbispay)
142 {
143   if (rtpvorbispay->packet)
144     gst_buffer_unref (rtpvorbispay->packet);
145   rtpvorbispay->packet = NULL;
146 }
147
148 static void
149 gst_rtp_vorbis_pay_cleanup (GstRtpVorbisPay * rtpvorbispay)
150 {
151   g_list_foreach (rtpvorbispay->headers, (GFunc) gst_mini_object_unref, NULL);
152   g_list_free (rtpvorbispay->headers);
153   rtpvorbispay->headers = NULL;
154
155   gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
156
157   if (rtpvorbispay->config_data)
158     g_free (rtpvorbispay->config_data);
159   rtpvorbispay->config_data = NULL;
160   rtpvorbispay->last_config = GST_CLOCK_TIME_NONE;
161 }
162
163 static gboolean
164 gst_rtp_vorbis_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
165 {
166   GstRtpVorbisPay *rtpvorbispay;
167   GstStructure *s;
168   const GValue *array;
169   gint asize, i;
170   GstBuffer *buf;
171   GstMapInfo map;
172
173   rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
174
175   s = gst_caps_get_structure (caps, 0);
176
177   rtpvorbispay->need_headers = TRUE;
178
179   if ((array = gst_structure_get_value (s, "streamheader")) == NULL)
180     goto done;
181
182   if (G_VALUE_TYPE (array) != GST_TYPE_ARRAY)
183     goto done;
184
185   if ((asize = gst_value_array_get_size (array)) < 3)
186     goto done;
187
188   for (i = 0; i < asize; i++) {
189     const GValue *value;
190
191     value = gst_value_array_get_value (array, i);
192     if ((buf = gst_value_get_buffer (value)) == NULL)
193       goto null_buffer;
194
195     gst_buffer_map (buf, &map, GST_MAP_READ);
196     if (map.size < 1)
197       goto invalid_streamheader;
198
199     /* no data packets allowed */
200     if ((map.data[0] & 1) == 0)
201       goto invalid_streamheader;
202
203     /* we need packets with id 1, 3, 5 */
204     if (map.data[0] != (i * 2) + 1)
205       goto invalid_streamheader;
206
207     if (i == 0) {
208       /* identification, we need to parse this in order to get the clock rate. */
209       if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, map.data,
210                   map.size)))
211         goto parse_id_failed;
212     }
213     GST_DEBUG_OBJECT (rtpvorbispay, "collecting header %d", i);
214     rtpvorbispay->headers =
215         g_list_append (rtpvorbispay->headers, gst_buffer_ref (buf));
216     gst_buffer_unmap (buf, &map);
217   }
218   if (!gst_rtp_vorbis_pay_finish_headers (basepayload))
219     goto finish_failed;
220
221 done:
222   return TRUE;
223
224   /* ERRORS */
225 null_buffer:
226   {
227     GST_WARNING_OBJECT (rtpvorbispay, "streamheader with null buffer received");
228     return FALSE;
229   }
230 invalid_streamheader:
231   {
232     GST_WARNING_OBJECT (rtpvorbispay, "unable to parse initial header");
233     gst_buffer_unmap (buf, &map);
234     return FALSE;
235   }
236 parse_id_failed:
237   {
238     GST_WARNING_OBJECT (rtpvorbispay, "unable to parse initial header");
239     gst_buffer_unmap (buf, &map);
240     return FALSE;
241   }
242 finish_failed:
243   {
244     GST_WARNING_OBJECT (rtpvorbispay, "unable to finish headers");
245     return FALSE;
246   }
247 }
248
249 static void
250 gst_rtp_vorbis_pay_reset_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT)
251 {
252   guint payload_len;
253   GstRTPBuffer rtp = { NULL };
254
255   GST_LOG_OBJECT (rtpvorbispay, "reset packet");
256
257   rtpvorbispay->payload_pos = 4;
258   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_READ, &rtp);
259   payload_len = gst_rtp_buffer_get_payload_len (&rtp);
260   gst_rtp_buffer_unmap (&rtp);
261   rtpvorbispay->payload_left = payload_len - 4;
262   rtpvorbispay->payload_duration = 0;
263   rtpvorbispay->payload_F = 0;
264   rtpvorbispay->payload_VDT = VDT;
265   rtpvorbispay->payload_pkts = 0;
266 }
267
268 static void
269 gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT,
270     GstClockTime timestamp)
271 {
272   GST_LOG_OBJECT (rtpvorbispay, "starting new packet, VDT: %d", VDT);
273
274   if (rtpvorbispay->packet)
275     gst_buffer_unref (rtpvorbispay->packet);
276
277   /* new packet allocate max packet size */
278   rtpvorbispay->packet =
279       gst_rtp_buffer_new_allocate_len (GST_RTP_BASE_PAYLOAD_MTU
280       (rtpvorbispay), 0, 0);
281   gst_rtp_vorbis_pay_reset_packet (rtpvorbispay, VDT);
282
283   GST_BUFFER_TIMESTAMP (rtpvorbispay->packet) = timestamp;
284 }
285
286 static GstFlowReturn
287 gst_rtp_vorbis_pay_flush_packet (GstRtpVorbisPay * rtpvorbispay)
288 {
289   GstFlowReturn ret;
290   guint8 *payload;
291   guint hlen;
292   GstRTPBuffer rtp = { NULL };
293
294   /* check for empty packet */
295   if (!rtpvorbispay->packet || rtpvorbispay->payload_pos <= 4)
296     return GST_FLOW_OK;
297
298   GST_LOG_OBJECT (rtpvorbispay, "flushing packet");
299
300   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
301
302   /* fix header */
303   payload = gst_rtp_buffer_get_payload (&rtp);
304   /*
305    *  0                   1                   2                   3
306    *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
307    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
308    * |                     Ident                     | F |VDT|# pkts.|
309    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
310    *
311    * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
312    * VDT: Vorbis data type (0=vorbis, 1=config, 2=comment, 3=reserved)
313    * pkts: number of packets.
314    */
315   payload[0] = (rtpvorbispay->payload_ident >> 16) & 0xff;
316   payload[1] = (rtpvorbispay->payload_ident >> 8) & 0xff;
317   payload[2] = (rtpvorbispay->payload_ident) & 0xff;
318   payload[3] = (rtpvorbispay->payload_F & 0x3) << 6 |
319       (rtpvorbispay->payload_VDT & 0x3) << 4 |
320       (rtpvorbispay->payload_pkts & 0xf);
321
322   gst_rtp_buffer_unmap (&rtp);
323
324   /* shrink the buffer size to the last written byte */
325   hlen = gst_rtp_buffer_calc_header_len (0);
326   gst_buffer_resize (rtpvorbispay->packet, 0, hlen + rtpvorbispay->payload_pos);
327
328   GST_BUFFER_DURATION (rtpvorbispay->packet) = rtpvorbispay->payload_duration;
329
330   /* push, this gives away our ref to the packet, so clear it. */
331   ret =
332       gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpvorbispay),
333       rtpvorbispay->packet);
334   rtpvorbispay->packet = NULL;
335
336   return ret;
337 }
338
339 static gboolean
340 gst_rtp_vorbis_pay_finish_headers (GstRTPBasePayload * basepayload)
341 {
342   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
343   GList *walk;
344   guint length, size, n_headers, configlen, extralen;
345   gchar *cstr, *configuration;
346   guint8 *data, *config;
347   guint32 ident;
348   gboolean res;
349
350   GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
351
352   if (!rtpvorbispay->headers)
353     goto no_headers;
354
355   /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356    * |                     Number of packed headers                  |
357    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
359    * |                          Packed header                        |
360    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
361    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
362    * |                          Packed header                        |
363    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365    * |                          ....                                 |
366    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367    *
368    * We only construct a config containing 1 packed header like this:
369    *
370    *  0                   1                   2                   3
371    *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
372    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
373    * |                   Ident                       | length       ..
374    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
375    * ..              | n. of headers |    length1    |    length2   ..
376    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377    * ..              |             Identification Header            ..
378    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
379    * .................................................................
380    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
381    * ..              |         Comment Header                       ..
382    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
383    * .................................................................
384    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
385    * ..                        Comment Header                        |
386    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
387    * |                          Setup Header                        ..
388    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389    * .................................................................
390    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391    * ..                         Setup Header                         |
392    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393    */
394
395   /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for
396    * the ident, 2 bytes for length, 1 byte for n. of headers. */
397   size = 4 + 3 + 2 + 1;
398
399   /* count the size of the headers first and update the hash */
400   length = 0;
401   n_headers = 0;
402   ident = fnv1_hash_32_new ();
403   extralen = 1;
404   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
405     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
406     GstMapInfo map;
407     guint bsize;
408
409     bsize = gst_buffer_get_size (buf);
410     length += bsize;
411     n_headers++;
412
413     /* count number of bytes needed for length fields, we don't need this for
414      * the last header. */
415     if (g_list_next (walk)) {
416       do {
417         size++;
418         extralen++;
419         bsize >>= 7;
420       } while (bsize);
421     }
422     /* update hash */
423     gst_buffer_map (buf, &map, GST_MAP_READ);
424     ident = fnv1_hash_32_update (ident, map.data, map.size);
425     gst_buffer_unmap (buf, &map);
426   }
427
428   /* packet length is header size + packet length */
429   configlen = size + length;
430   config = data = g_malloc (configlen);
431
432   /* number of packed headers, we only pack 1 header */
433   data[0] = 0;
434   data[1] = 0;
435   data[2] = 0;
436   data[3] = 1;
437
438   ident = fnv1_hash_32_to_24 (ident);
439   rtpvorbispay->payload_ident = ident;
440   GST_DEBUG_OBJECT (rtpvorbispay, "ident 0x%08x", ident);
441
442   /* take lower 3 bytes */
443   data[4] = (ident >> 16) & 0xff;
444   data[5] = (ident >> 8) & 0xff;
445   data[6] = ident & 0xff;
446
447   /* store length of all vorbis headers */
448   data[7] = ((length) >> 8) & 0xff;
449   data[8] = (length) & 0xff;
450
451   /* store number of headers minus one. */
452   data[9] = n_headers - 1;
453   data += 10;
454
455   /* store length for each header */
456   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
457     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
458     guint bsize, size, temp;
459     guint flag;
460
461     /* only need to store the length when it's not the last header */
462     if (!g_list_next (walk))
463       break;
464
465     bsize = gst_buffer_get_size (buf);
466
467     /* calc size */
468     size = 0;
469     do {
470       size++;
471       bsize >>= 7;
472     } while (bsize);
473     temp = size;
474
475     bsize = gst_buffer_get_size (buf);
476     /* write the size backwards */
477     flag = 0;
478     while (size) {
479       size--;
480       data[size] = (bsize & 0x7f) | flag;
481       bsize >>= 7;
482       flag = 0x80;              /* Flag bit on all bytes of the length except the last */
483     }
484     data += temp;
485   }
486
487   /* copy header data */
488   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
489     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
490
491     gst_buffer_extract (buf, 0, data, gst_buffer_get_size (buf));
492     data += gst_buffer_get_size (buf);
493     gst_buffer_unref (buf);
494   }
495   g_list_free (rtpvorbispay->headers);
496   rtpvorbispay->headers = NULL;
497   rtpvorbispay->need_headers = FALSE;
498
499   /* serialize to base64 */
500   configuration = g_base64_encode (config, configlen);
501
502   /* store for later re-sending */
503   if (rtpvorbispay->config_data)
504     g_free (rtpvorbispay->config_data);
505   rtpvorbispay->config_size = configlen - 4 - 3 - 2;
506   rtpvorbispay->config_data = g_malloc (rtpvorbispay->config_size);
507   rtpvorbispay->config_extra_len = extralen;
508   memcpy (rtpvorbispay->config_data, config + 4 + 3 + 2,
509       rtpvorbispay->config_size);
510
511   g_free (config);
512
513   /* configure payloader settings */
514   cstr = g_strdup_printf ("%d", rtpvorbispay->channels);
515   gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "VORBIS",
516       rtpvorbispay->rate);
517   res =
518       gst_rtp_base_payload_set_outcaps (basepayload, "encoding-params",
519       G_TYPE_STRING, cstr, "configuration", G_TYPE_STRING, configuration, NULL);
520   g_free (cstr);
521   g_free (configuration);
522
523   return res;
524
525   /* ERRORS */
526 no_headers:
527   {
528     GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
529     return FALSE;
530   }
531 }
532
533 static gboolean
534 gst_rtp_vorbis_pay_parse_id (GstRTPBasePayload * basepayload, guint8 * data,
535     guint size)
536 {
537   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
538   guint8 channels;
539   gint32 rate, version;
540
541   if (G_UNLIKELY (size < 16))
542     goto too_short;
543
544   if (G_UNLIKELY (memcmp (data, "\001vorbis", 7)))
545     goto invalid_start;
546   data += 7;
547
548   if (G_UNLIKELY ((version = GST_READ_UINT32_LE (data)) != 0))
549     goto invalid_version;
550   data += 4;
551
552   if (G_UNLIKELY ((channels = *data++) < 1))
553     goto invalid_channels;
554
555   if (G_UNLIKELY ((rate = GST_READ_UINT32_LE (data)) < 1))
556     goto invalid_rate;
557
558   /* all fine, store the values */
559   rtpvorbispay->channels = channels;
560   rtpvorbispay->rate = rate;
561
562   return TRUE;
563
564   /* ERRORS */
565 too_short:
566   {
567     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
568         ("Identification packet is too short, need at least 16, got %d", size),
569         (NULL));
570     return FALSE;
571   }
572 invalid_start:
573   {
574     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
575         ("Invalid header start in identification packet"), (NULL));
576     return FALSE;
577   }
578 invalid_version:
579   {
580     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
581         ("Invalid version, expected 0, got %d", version), (NULL));
582     return FALSE;
583   }
584 invalid_rate:
585   {
586     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
587         ("Invalid rate %d", rate), (NULL));
588     return FALSE;
589   }
590 invalid_channels:
591   {
592     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
593         ("Invalid channels %d", channels), (NULL));
594     return FALSE;
595   }
596 }
597
598 static GstFlowReturn
599 gst_rtp_vorbis_pay_payload_buffer (GstRtpVorbisPay * rtpvorbispay, guint8 VDT,
600     guint8 * data, guint size, GstClockTime timestamp, GstClockTime duration,
601     guint not_in_length)
602 {
603   GstFlowReturn ret = GST_FLOW_OK;
604   guint newsize;
605   guint packet_len;
606   GstClockTime newduration;
607   gboolean flush;
608   guint plen;
609   guint8 *ppos, *payload;
610   gboolean fragmented;
611   GstRTPBuffer rtp = { NULL };
612
613   /* size increases with packet length and 2 bytes size eader. */
614   newduration = rtpvorbispay->payload_duration;
615   if (duration != GST_CLOCK_TIME_NONE)
616     newduration += duration;
617
618   newsize = rtpvorbispay->payload_pos + 2 + size;
619   packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0);
620
621   /* check buffer filled against length and max latency */
622   flush = gst_rtp_base_payload_is_filled (GST_RTP_BASE_PAYLOAD (rtpvorbispay),
623       packet_len, newduration);
624   /* we can store up to 15 vorbis packets in one RTP packet. */
625   flush |= (rtpvorbispay->payload_pkts == 15);
626   /* flush if we have a new VDT */
627   if (rtpvorbispay->packet)
628     flush |= (rtpvorbispay->payload_VDT != VDT);
629   if (flush)
630     ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
631
632   /* create new packet if we must */
633   if (!rtpvorbispay->packet) {
634     gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT, timestamp);
635   }
636
637   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
638   payload = gst_rtp_buffer_get_payload (&rtp);
639   ppos = payload + rtpvorbispay->payload_pos;
640   fragmented = FALSE;
641
642   /* put buffer in packet, it either fits completely or needs to be fragmented
643    * over multiple RTP packets. */
644   do {
645     plen = MIN (rtpvorbispay->payload_left - 2, size);
646
647     GST_LOG_OBJECT (rtpvorbispay, "append %u bytes", plen);
648
649     /* data is copied in the payload with a 2 byte length header */
650     ppos[0] = ((plen - not_in_length) >> 8) & 0xff;
651     ppos[1] = ((plen - not_in_length) & 0xff);
652     if (plen)
653       memcpy (&ppos[2], data, plen);
654
655     /* only first (only) configuration cuts length field */
656     /* NOTE: spec (if any) is not clear on this ... */
657     not_in_length = 0;
658
659     size -= plen;
660     data += plen;
661
662     rtpvorbispay->payload_pos += plen + 2;
663     rtpvorbispay->payload_left -= plen + 2;
664
665     if (fragmented) {
666       if (size == 0)
667         /* last fragment, set F to 0x3. */
668         rtpvorbispay->payload_F = 0x3;
669       else
670         /* fragment continues, set F to 0x2. */
671         rtpvorbispay->payload_F = 0x2;
672     } else {
673       if (size > 0) {
674         /* fragmented packet starts, set F to 0x1, mark ourselves as
675          * fragmented. */
676         rtpvorbispay->payload_F = 0x1;
677         fragmented = TRUE;
678       }
679     }
680     if (fragmented) {
681       gst_rtp_buffer_unmap (&rtp);
682       /* fragmented packets are always flushed and have ptks of 0 */
683       rtpvorbispay->payload_pkts = 0;
684       ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
685
686       if (size > 0) {
687         /* start new packet and get pointers. VDT stays the same. */
688         gst_rtp_vorbis_pay_init_packet (rtpvorbispay,
689             rtpvorbispay->payload_VDT, timestamp);
690         gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
691         payload = gst_rtp_buffer_get_payload (&rtp);
692         ppos = payload + rtpvorbispay->payload_pos;
693       }
694     } else {
695       /* unfragmented packet, update stats for next packet, size == 0 and we
696        * exit the while loop */
697       rtpvorbispay->payload_pkts++;
698       if (duration != GST_CLOCK_TIME_NONE)
699         rtpvorbispay->payload_duration += duration;
700     }
701   } while (size);
702
703   if (rtp.buffer)
704     gst_rtp_buffer_unmap (&rtp);
705
706   return ret;
707 }
708
709 static GstFlowReturn
710 gst_rtp_vorbis_pay_handle_buffer (GstRTPBasePayload * basepayload,
711     GstBuffer * buffer)
712 {
713   GstRtpVorbisPay *rtpvorbispay;
714   GstFlowReturn ret;
715   GstMapInfo map;
716   gsize size;
717   guint8 *data;
718   GstClockTime duration, timestamp;
719   guint8 VDT;
720
721   rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
722
723   gst_buffer_map (buffer, &map, GST_MAP_READ);
724   data = map.data;
725   size = map.size;
726   duration = GST_BUFFER_DURATION (buffer);
727   timestamp = GST_BUFFER_TIMESTAMP (buffer);
728
729   GST_LOG_OBJECT (rtpvorbispay, "size %" G_GSIZE_FORMAT
730       ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration));
731
732   if (G_UNLIKELY (size < 1))
733     goto wrong_size;
734
735   /* find packet type */
736   if (data[0] & 1) {
737     /* header */
738     if (data[0] == 1) {
739       /* identification, we need to parse this in order to get the clock rate. */
740       if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, data, size)))
741         goto parse_id_failed;
742       VDT = 1;
743     } else if (data[0] == 3) {
744       /* comment */
745       VDT = 2;
746     } else if (data[0] == 5) {
747       /* setup */
748       VDT = 1;
749     } else
750       goto unknown_header;
751   } else
752     /* data */
753     VDT = 0;
754
755   /* we need to collect the headers and construct a config string from them */
756   if (VDT != 0) {
757     GST_DEBUG_OBJECT (rtpvorbispay, "collecting header");
758     /* append header to the list of headers */
759     gst_buffer_unmap (buffer, &map);
760     rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer);
761     ret = GST_FLOW_OK;
762     goto done;
763   } else if (rtpvorbispay->headers) {
764     if (rtpvorbispay->need_headers) {
765       if (!gst_rtp_vorbis_pay_finish_headers (basepayload))
766         goto header_error;
767     } else {
768       g_list_free_full (rtpvorbispay->headers,
769           (GDestroyNotify) gst_buffer_unref);
770       rtpvorbispay->headers = NULL;
771     }
772   }
773
774   /* there is a config request, see if we need to insert it */
775   if (rtpvorbispay->config_interval > 0 && rtpvorbispay->config_data) {
776     gboolean send_config = FALSE;
777
778     if (rtpvorbispay->last_config != -1) {
779       guint64 diff;
780
781       GST_LOG_OBJECT (rtpvorbispay,
782           "now %" GST_TIME_FORMAT ", last config %" GST_TIME_FORMAT,
783           GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtpvorbispay->last_config));
784
785       /* calculate diff between last config in milliseconds */
786       if (timestamp > rtpvorbispay->last_config) {
787         diff = timestamp - rtpvorbispay->last_config;
788       } else {
789         diff = 0;
790       }
791
792       GST_DEBUG_OBJECT (rtpvorbispay,
793           "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
794
795       /* bigger than interval, queue config */
796       /* FIXME should convert timestamps to running time */
797       if (GST_TIME_AS_SECONDS (diff) >= rtpvorbispay->config_interval) {
798         GST_DEBUG_OBJECT (rtpvorbispay, "time to send config");
799         send_config = TRUE;
800       }
801     } else {
802       /* no known previous config time, send now */
803       GST_DEBUG_OBJECT (rtpvorbispay, "no previous config time, send now");
804       send_config = TRUE;
805     }
806
807     if (send_config) {
808       /* we need to send config now first */
809       /* different TDT type forces flush */
810       gst_rtp_vorbis_pay_payload_buffer (rtpvorbispay, 1,
811           rtpvorbispay->config_data, rtpvorbispay->config_size,
812           timestamp, GST_CLOCK_TIME_NONE, rtpvorbispay->config_extra_len);
813
814       if (timestamp != -1) {
815         rtpvorbispay->last_config = timestamp;
816       }
817     }
818   }
819
820   ret = gst_rtp_vorbis_pay_payload_buffer (rtpvorbispay, VDT, data, size,
821       timestamp, duration, 0);
822
823   gst_buffer_unmap (buffer, &map);
824   gst_buffer_unref (buffer);
825
826 done:
827   return ret;
828
829   /* ERRORS */
830 wrong_size:
831   {
832     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
833         ("Invalid packet size (1 < %" G_GSIZE_FORMAT ")", size), (NULL));
834     gst_buffer_unmap (buffer, &map);
835     gst_buffer_unref (buffer);
836     return GST_FLOW_OK;
837   }
838 parse_id_failed:
839   {
840     gst_buffer_unmap (buffer, &map);
841     gst_buffer_unref (buffer);
842     return GST_FLOW_ERROR;
843   }
844 unknown_header:
845   {
846     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
847         (NULL), ("Ignoring unknown header received"));
848     gst_buffer_unmap (buffer, &map);
849     gst_buffer_unref (buffer);
850     return GST_FLOW_OK;
851   }
852 header_error:
853   {
854     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
855         (NULL), ("Error initializing header config"));
856     gst_buffer_unmap (buffer, &map);
857     gst_buffer_unref (buffer);
858     return GST_FLOW_OK;
859   }
860 }
861
862 static gboolean
863 gst_rtp_vorbis_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
864 {
865   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (payload);
866
867   switch (GST_EVENT_TYPE (event)) {
868     case GST_EVENT_FLUSH_STOP:
869       gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
870       break;
871     default:
872       break;
873   }
874   /* false to let parent handle event as well */
875   return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event);
876 }
877
878 static GstStateChangeReturn
879 gst_rtp_vorbis_pay_change_state (GstElement * element,
880     GstStateChange transition)
881 {
882   GstRtpVorbisPay *rtpvorbispay;
883   GstStateChangeReturn ret;
884
885   rtpvorbispay = GST_RTP_VORBIS_PAY (element);
886
887   switch (transition) {
888     default:
889       break;
890   }
891
892   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
893
894   switch (transition) {
895     case GST_STATE_CHANGE_PAUSED_TO_READY:
896       gst_rtp_vorbis_pay_cleanup (rtpvorbispay);
897       break;
898     default:
899       break;
900   }
901   return ret;
902 }
903
904 static void
905 gst_rtp_vorbis_pay_set_property (GObject * object, guint prop_id,
906     const GValue * value, GParamSpec * pspec)
907 {
908   GstRtpVorbisPay *rtpvorbispay;
909
910   rtpvorbispay = GST_RTP_VORBIS_PAY (object);
911
912   switch (prop_id) {
913     case PROP_CONFIG_INTERVAL:
914       rtpvorbispay->config_interval = g_value_get_uint (value);
915       break;
916     default:
917       break;
918   }
919 }
920
921 static void
922 gst_rtp_vorbis_pay_get_property (GObject * object, guint prop_id,
923     GValue * value, GParamSpec * pspec)
924 {
925   GstRtpVorbisPay *rtpvorbispay;
926
927   rtpvorbispay = GST_RTP_VORBIS_PAY (object);
928
929   switch (prop_id) {
930     case PROP_CONFIG_INTERVAL:
931       g_value_set_uint (value, rtpvorbispay->config_interval);
932       break;
933     default:
934       break;
935   }
936 }
937
938 gboolean
939 gst_rtp_vorbis_pay_plugin_init (GstPlugin * plugin)
940 {
941   return gst_element_register (plugin, "rtpvorbispay",
942       GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_PAY);
943 }