Merge branch 'master' into 0.11
[platform/upstream/gstreamer.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., 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 <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 gst_rtp_vorbis_pay_parent_class parent_class
62 G_DEFINE_TYPE (GstRtpVorbisPay, gst_rtp_vorbis_pay, GST_TYPE_BASE_RTP_PAYLOAD);
63
64 static gboolean gst_rtp_vorbis_pay_setcaps (GstBaseRTPPayload * basepayload,
65     GstCaps * caps);
66 static GstStateChangeReturn gst_rtp_vorbis_pay_change_state (GstElement *
67     element, GstStateChange transition);
68 static GstFlowReturn gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * pad,
69     GstBuffer * buffer);
70 static gboolean gst_rtp_vorbis_pay_handle_event (GstBaseRTPPayload * payload,
71     GstEvent * event);
72
73 static void
74 gst_rtp_vorbis_pay_class_init (GstRtpVorbisPayClass * klass)
75 {
76   GstElementClass *gstelement_class;
77   GstBaseRTPPayloadClass *gstbasertppayload_class;
78
79   gstelement_class = (GstElementClass *) klass;
80   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
81
82   gstelement_class->change_state = gst_rtp_vorbis_pay_change_state;
83
84   gstbasertppayload_class->set_caps = gst_rtp_vorbis_pay_setcaps;
85   gstbasertppayload_class->handle_buffer = gst_rtp_vorbis_pay_handle_buffer;
86   gstbasertppayload_class->handle_event = gst_rtp_vorbis_pay_handle_event;
87
88   gst_element_class_add_pad_template (gstelement_class,
89       gst_static_pad_template_get (&gst_rtp_vorbis_pay_src_template));
90   gst_element_class_add_pad_template (gstelement_class,
91       gst_static_pad_template_get (&gst_rtp_vorbis_pay_sink_template));
92
93   gst_element_class_set_details_simple (gstelement_class,
94       "RTP Vorbis depayloader",
95       "Codec/Payloader/Network/RTP",
96       "Payload-encode Vorbis audio into RTP packets (RFC 5215)",
97       "Wim Taymans <wimi.taymans@gmail.com>");
98
99   GST_DEBUG_CATEGORY_INIT (rtpvorbispay_debug, "rtpvorbispay", 0,
100       "Vorbis RTP Payloader");
101 }
102
103 static void
104 gst_rtp_vorbis_pay_init (GstRtpVorbisPay * rtpvorbispay)
105 {
106   /* needed because of GST_BOILERPLATE */
107 }
108
109 static void
110 gst_rtp_vorbis_pay_clear_packet (GstRtpVorbisPay * rtpvorbispay)
111 {
112   if (rtpvorbispay->packet)
113     gst_buffer_unref (rtpvorbispay->packet);
114   rtpvorbispay->packet = NULL;
115 }
116
117 static void
118 gst_rtp_vorbis_pay_cleanup (GstRtpVorbisPay * rtpvorbispay)
119 {
120   g_list_foreach (rtpvorbispay->headers, (GFunc) gst_mini_object_unref, NULL);
121   g_list_free (rtpvorbispay->headers);
122   rtpvorbispay->headers = NULL;
123
124   gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
125 }
126
127 static gboolean
128 gst_rtp_vorbis_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
129 {
130   GstRtpVorbisPay *rtpvorbispay;
131
132   rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
133
134   rtpvorbispay->need_headers = TRUE;
135
136   return TRUE;
137 }
138
139 static void
140 gst_rtp_vorbis_pay_reset_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT)
141 {
142   guint payload_len;
143   GstRTPBuffer rtp;
144
145   GST_LOG_OBJECT (rtpvorbispay, "reset packet");
146
147   rtpvorbispay->payload_pos = 4;
148   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_READ, &rtp);
149   payload_len = gst_rtp_buffer_get_payload_len (&rtp);
150   gst_rtp_buffer_unmap (&rtp);
151   rtpvorbispay->payload_left = payload_len - 4;
152   rtpvorbispay->payload_duration = 0;
153   rtpvorbispay->payload_F = 0;
154   rtpvorbispay->payload_VDT = VDT;
155   rtpvorbispay->payload_pkts = 0;
156 }
157
158 static void
159 gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT,
160     GstClockTime timestamp)
161 {
162   GST_LOG_OBJECT (rtpvorbispay, "starting new packet, VDT: %d", VDT);
163
164   if (rtpvorbispay->packet)
165     gst_buffer_unref (rtpvorbispay->packet);
166
167   /* new packet allocate max packet size */
168   rtpvorbispay->packet =
169       gst_rtp_buffer_new_allocate_len (GST_BASE_RTP_PAYLOAD_MTU
170       (rtpvorbispay), 0, 0);
171   gst_rtp_vorbis_pay_reset_packet (rtpvorbispay, VDT);
172   GST_BUFFER_TIMESTAMP (rtpvorbispay->packet) = timestamp;
173 }
174
175 static GstFlowReturn
176 gst_rtp_vorbis_pay_flush_packet (GstRtpVorbisPay * rtpvorbispay)
177 {
178   GstFlowReturn ret;
179   guint8 *payload;
180   guint hlen;
181   GstRTPBuffer rtp;
182
183   /* check for empty packet */
184   if (!rtpvorbispay->packet || rtpvorbispay->payload_pos <= 4)
185     return GST_FLOW_OK;
186
187   GST_LOG_OBJECT (rtpvorbispay, "flushing packet");
188
189   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
190
191   /* fix header */
192   payload = gst_rtp_buffer_get_payload (&rtp);
193   /*
194    *  0                   1                   2                   3
195    *  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
196    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
197    * |                     Ident                     | F |VDT|# pkts.|
198    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
199    *
200    * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
201    * VDT: Vorbis data type (0=vorbis, 1=config, 2=comment, 3=reserved)
202    * pkts: number of packets.
203    */
204   payload[0] = (rtpvorbispay->payload_ident >> 16) & 0xff;
205   payload[1] = (rtpvorbispay->payload_ident >> 8) & 0xff;
206   payload[2] = (rtpvorbispay->payload_ident) & 0xff;
207   payload[3] = (rtpvorbispay->payload_F & 0x3) << 6 |
208       (rtpvorbispay->payload_VDT & 0x3) << 4 |
209       (rtpvorbispay->payload_pkts & 0xf);
210
211   gst_rtp_buffer_unmap (&rtp);
212
213   /* shrink the buffer size to the last written byte */
214   hlen = gst_rtp_buffer_calc_header_len (0);
215   gst_buffer_resize (rtpvorbispay->packet, 0, hlen + rtpvorbispay->payload_pos);
216
217   GST_BUFFER_DURATION (rtpvorbispay->packet) = rtpvorbispay->payload_duration;
218
219   /* push, this gives away our ref to the packet, so clear it. */
220   ret =
221       gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpvorbispay),
222       rtpvorbispay->packet);
223   rtpvorbispay->packet = NULL;
224
225   return ret;
226 }
227
228 static gboolean
229 gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload)
230 {
231   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
232   GList *walk;
233   guint length, size, n_headers, configlen;
234   gchar *cstr, *configuration;
235   guint8 *data, *config;
236   guint32 ident;
237   gboolean res;
238
239   GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
240
241   if (!rtpvorbispay->headers)
242     goto no_headers;
243
244   /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
245    * |                     Number of packed headers                  |
246    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
248    * |                          Packed header                        |
249    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
250    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
251    * |                          Packed header                        |
252    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
253    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
254    * |                          ....                                 |
255    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
256    *
257    * We only construct a config containing 1 packed header like this:
258    *
259    *  0                   1                   2                   3
260    *  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
261    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
262    * |                   Ident                       | length       ..
263    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
264    * ..              | n. of headers |    length1    |    length2   ..
265    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
266    * ..              |             Identification Header            ..
267    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
268    * .................................................................
269    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
270    * ..              |         Comment Header                       ..
271    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
272    * .................................................................
273    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
274    * ..                        Comment Header                        |
275    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
276    * |                          Setup Header                        ..
277    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
278    * .................................................................
279    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
280    * ..                         Setup Header                         |
281    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
282    */
283
284   /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for
285    * the ident, 2 bytes for length, 1 byte for n. of headers. */
286   size = 4 + 3 + 2 + 1;
287
288   /* count the size of the headers first and update the hash */
289   length = 0;
290   n_headers = 0;
291   ident = fnv1_hash_32_new ();
292   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
293     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
294     guint bsize, osize;
295     guint8 *data;
296
297     bsize = osize = gst_buffer_get_size (buf);
298     length += bsize;
299     n_headers++;
300
301     /* count number of bytes needed for length fields, we don't need this for
302      * the last header. */
303     if (g_list_next (walk)) {
304       do {
305         size++;
306         bsize >>= 7;
307       } while (bsize);
308     }
309     /* update hash */
310     data = gst_buffer_map (buf, NULL, NULL, GST_MAP_READ);
311     ident = fnv1_hash_32_update (ident, data, osize);
312     gst_buffer_unmap (buf, data, -1);
313   }
314
315   /* packet length is header size + packet length */
316   configlen = size + length;
317   config = data = g_malloc (configlen);
318
319   /* number of packed headers, we only pack 1 header */
320   data[0] = 0;
321   data[1] = 0;
322   data[2] = 0;
323   data[3] = 1;
324
325   ident = fnv1_hash_32_to_24 (ident);
326   rtpvorbispay->payload_ident = ident;
327   GST_DEBUG_OBJECT (rtpvorbispay, "ident 0x%08x", ident);
328
329   /* take lower 3 bytes */
330   data[4] = (ident >> 16) & 0xff;
331   data[5] = (ident >> 8) & 0xff;
332   data[6] = ident & 0xff;
333
334   /* store length of all vorbis headers */
335   data[7] = ((length) >> 8) & 0xff;
336   data[8] = (length) & 0xff;
337
338   /* store number of headers minus one. */
339   data[9] = n_headers - 1;
340   data += 10;
341
342   /* store length for each header */
343   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
344     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
345     guint bsize, size, temp;
346     guint flag;
347
348     /* only need to store the length when it's not the last header */
349     if (!g_list_next (walk))
350       break;
351
352     bsize = gst_buffer_get_size (buf);
353
354     /* calc size */
355     size = 0;
356     do {
357       size++;
358       bsize >>= 7;
359     } while (bsize);
360     temp = size;
361
362     bsize = gst_buffer_get_size (buf);
363     /* write the size backwards */
364     flag = 0;
365     while (size) {
366       size--;
367       data[size] = (bsize & 0x7f) | flag;
368       bsize >>= 7;
369       flag = 0x80;              /* Flag bit on all bytes of the length except the last */
370     }
371     data += temp;
372   }
373
374   /* copy header data */
375   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
376     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
377
378     gst_buffer_extract (buf, 0, data, gst_buffer_get_size (buf));
379     data += gst_buffer_get_size (buf);
380   }
381
382   /* serialize to base64 */
383   configuration = g_base64_encode (config, configlen);
384   g_free (config);
385
386   /* configure payloader settings */
387   cstr = g_strdup_printf ("%d", rtpvorbispay->channels);
388   gst_basertppayload_set_options (basepayload, "audio", TRUE, "VORBIS",
389       rtpvorbispay->rate);
390   res =
391       gst_basertppayload_set_outcaps (basepayload, "encoding-params",
392       G_TYPE_STRING, cstr, "configuration", G_TYPE_STRING, configuration, NULL);
393   g_free (cstr);
394   g_free (configuration);
395
396   return res;
397
398   /* ERRORS */
399 no_headers:
400   {
401     GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
402     return FALSE;
403   }
404 }
405
406 static gboolean
407 gst_rtp_vorbis_pay_parse_id (GstBaseRTPPayload * basepayload, guint8 * data,
408     guint size)
409 {
410   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
411   guint8 channels;
412   gint32 rate, version;
413
414   if (G_UNLIKELY (size < 16))
415     goto too_short;
416
417   if (G_UNLIKELY (memcmp (data, "\001vorbis", 7)))
418     goto invalid_start;
419   data += 7;
420
421   if (G_UNLIKELY ((version = GST_READ_UINT32_LE (data)) != 0))
422     goto invalid_version;
423   data += 4;
424
425   if (G_UNLIKELY ((channels = *data++) < 1))
426     goto invalid_channels;
427
428   if (G_UNLIKELY ((rate = GST_READ_UINT32_LE (data)) < 1))
429     goto invalid_rate;
430
431   /* all fine, store the values */
432   rtpvorbispay->channels = channels;
433   rtpvorbispay->rate = rate;
434
435   return TRUE;
436
437   /* ERRORS */
438 too_short:
439   {
440     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
441         ("Identification packet is too short, need at least 16, got %d", size),
442         (NULL));
443     return FALSE;
444   }
445 invalid_start:
446   {
447     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
448         ("Invalid header start in identification packet"), (NULL));
449     return FALSE;
450   }
451 invalid_version:
452   {
453     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
454         ("Invalid version, expected 0, got %d", version), (NULL));
455     return FALSE;
456   }
457 invalid_rate:
458   {
459     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
460         ("Invalid rate %d", rate), (NULL));
461     return FALSE;
462   }
463 invalid_channels:
464   {
465     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
466         ("Invalid channels %d", channels), (NULL));
467     return FALSE;
468   }
469 }
470
471 static GstFlowReturn
472 gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * basepayload,
473     GstBuffer * buffer)
474 {
475   GstRtpVorbisPay *rtpvorbispay;
476   GstFlowReturn ret;
477   guint newsize;
478   gsize size;
479   guint8 *data;
480   guint packet_len;
481   GstClockTime duration, newduration, timestamp;
482   gboolean flush;
483   guint8 VDT;
484   guint plen;
485   guint8 *ppos, *payload;
486   gboolean fragmented;
487   GstRTPBuffer rtp;
488
489   rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
490
491   data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
492   duration = GST_BUFFER_DURATION (buffer);
493   timestamp = GST_BUFFER_TIMESTAMP (buffer);
494
495   GST_LOG_OBJECT (rtpvorbispay, "size %" G_GSIZE_FORMAT
496       ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration));
497
498   if (G_UNLIKELY (size < 1 || size > 0xffff))
499     goto wrong_size;
500
501   /* find packet type */
502   if (data[0] & 1) {
503     /* header */
504     if (data[0] == 1) {
505       /* identification, we need to parse this in order to get the clock rate. */
506       if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, data, size)))
507         goto parse_id_failed;
508       VDT = 1;
509     } else if (data[0] == 3) {
510       /* comment */
511       VDT = 2;
512     } else if (data[0] == 5) {
513       /* setup */
514       VDT = 1;
515     } else
516       goto unknown_header;
517   } else
518     /* data */
519     VDT = 0;
520
521   if (rtpvorbispay->need_headers) {
522     /* we need to collect the headers and construct a config string from them */
523     if (VDT != 0) {
524       GST_DEBUG_OBJECT (rtpvorbispay, "collecting header");
525       /* append header to the list of headers */
526       gst_buffer_unmap (buffer, data, -1);
527       rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer);
528       ret = GST_FLOW_OK;
529       goto done;
530     } else {
531       if (!gst_rtp_vorbis_pay_finish_headers (basepayload))
532         goto header_error;
533       rtpvorbispay->need_headers = FALSE;
534     }
535   }
536
537   /* size increases with packet length and 2 bytes size eader. */
538   newduration = rtpvorbispay->payload_duration;
539   if (duration != GST_CLOCK_TIME_NONE)
540     newduration += duration;
541
542   newsize = rtpvorbispay->payload_pos + 2 + size;
543   packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0);
544
545   /* check buffer filled against length and max latency */
546   flush = gst_basertppayload_is_filled (basepayload, packet_len, newduration);
547   /* we can store up to 15 vorbis packets in one RTP packet. */
548   flush |= (rtpvorbispay->payload_pkts == 15);
549   /* flush if we have a new VDT */
550   if (rtpvorbispay->packet)
551     flush |= (rtpvorbispay->payload_VDT != VDT);
552   if (flush)
553     gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
554
555   /* create new packet if we must */
556   if (!rtpvorbispay->packet) {
557     gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT, timestamp);
558   }
559
560   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
561   payload = gst_rtp_buffer_get_payload (&rtp);
562   ppos = payload + rtpvorbispay->payload_pos;
563   fragmented = FALSE;
564
565   ret = GST_FLOW_OK;
566
567   /* put buffer in packet, it either fits completely or needs to be fragmented
568    * over multiple RTP packets. */
569   while (size) {
570     plen = MIN (rtpvorbispay->payload_left - 2, size);
571
572     GST_LOG_OBJECT (rtpvorbispay, "append %u bytes", plen);
573
574     /* data is copied in the payload with a 2 byte length header */
575     ppos[0] = (plen >> 8) & 0xff;
576     ppos[1] = (plen & 0xff);
577     memcpy (&ppos[2], data, plen);
578
579     size -= plen;
580     data += plen;
581
582     rtpvorbispay->payload_pos += plen + 2;
583     rtpvorbispay->payload_left -= plen + 2;
584
585     if (fragmented) {
586       if (size == 0)
587         /* last fragment, set F to 0x3. */
588         rtpvorbispay->payload_F = 0x3;
589       else
590         /* fragment continues, set F to 0x2. */
591         rtpvorbispay->payload_F = 0x2;
592     } else {
593       if (size > 0) {
594         /* fragmented packet starts, set F to 0x1, mark ourselves as
595          * fragmented. */
596         rtpvorbispay->payload_F = 0x1;
597         fragmented = TRUE;
598       }
599     }
600     if (fragmented) {
601       gst_rtp_buffer_unmap (&rtp);
602       /* fragmented packets are always flushed and have ptks of 0 */
603       rtpvorbispay->payload_pkts = 0;
604       ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
605
606       if (size > 0) {
607         /* start new packet and get pointers. VDT stays the same. */
608         gst_rtp_vorbis_pay_init_packet (rtpvorbispay,
609             rtpvorbispay->payload_VDT, timestamp);
610         gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
611         payload = gst_rtp_buffer_get_payload (&rtp);
612         ppos = payload + rtpvorbispay->payload_pos;
613       }
614     } else {
615       /* unfragmented packet, update stats for next packet, size == 0 and we
616        * exit the while loop */
617       rtpvorbispay->payload_pkts++;
618       if (duration != GST_CLOCK_TIME_NONE)
619         rtpvorbispay->payload_duration += duration;
620     }
621   }
622
623   if (rtp.buffer)
624     gst_rtp_buffer_unmap (&rtp);
625
626   gst_buffer_unmap (buffer, data, -1);
627   gst_buffer_unref (buffer);
628
629 done:
630   return ret;
631
632   /* ERRORS */
633 wrong_size:
634   {
635     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
636         ("Invalid packet size (1 < %d <= 0xffff)", size), (NULL));
637     gst_buffer_unmap (buffer, data, -1);
638     gst_buffer_unref (buffer);
639     return GST_FLOW_OK;
640   }
641 parse_id_failed:
642   {
643     gst_buffer_unmap (buffer, data, -1);
644     gst_buffer_unref (buffer);
645     return GST_FLOW_ERROR;
646   }
647 unknown_header:
648   {
649     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
650         (NULL), ("Ignoring unknown header received"));
651     gst_buffer_unmap (buffer, data, -1);
652     gst_buffer_unref (buffer);
653     return GST_FLOW_OK;
654   }
655 header_error:
656   {
657     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
658         (NULL), ("Error initializing header config"));
659     gst_buffer_unmap (buffer, data, -1);
660     gst_buffer_unref (buffer);
661     return GST_FLOW_OK;
662   }
663 }
664
665 static gboolean
666 gst_rtp_vorbis_pay_handle_event (GstBaseRTPPayload * payload, GstEvent * event)
667 {
668   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (payload);
669
670   switch (GST_EVENT_TYPE (event)) {
671     case GST_EVENT_FLUSH_STOP:
672       gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
673       break;
674     default:
675       break;
676   }
677   /* false to let parent handle event as well */
678   return FALSE;
679 }
680
681 static GstStateChangeReturn
682 gst_rtp_vorbis_pay_change_state (GstElement * element,
683     GstStateChange transition)
684 {
685   GstRtpVorbisPay *rtpvorbispay;
686   GstStateChangeReturn ret;
687
688   rtpvorbispay = GST_RTP_VORBIS_PAY (element);
689
690   switch (transition) {
691     default:
692       break;
693   }
694
695   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
696
697   switch (transition) {
698     case GST_STATE_CHANGE_PAUSED_TO_READY:
699       gst_rtp_vorbis_pay_cleanup (rtpvorbispay);
700       break;
701     default:
702       break;
703   }
704   return ret;
705 }
706
707 gboolean
708 gst_rtp_vorbis_pay_plugin_init (GstPlugin * plugin)
709 {
710   return gst_element_register (plugin, "rtpvorbispay",
711       GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_PAY);
712 }