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