Merge branch '0.11' of ssh://git.freedesktop.org/git/gstreamer/gst-plugins-good into...
[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., 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_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 (GstRTPBasePayload * 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 = { NULL };
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_RTP_BASE_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 = { NULL };
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_rtp_base_payload_push (GST_RTP_BASE_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 (GstRTPBasePayload * 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     GstMapInfo map;
295     guint bsize;
296
297     bsize = 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     gst_buffer_map (buf, &map, GST_MAP_READ);
311     ident = fnv1_hash_32_update (ident, map.data, map.size);
312     gst_buffer_unmap (buf, &map);
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_rtp_base_payload_set_options (basepayload, "audio", TRUE, "VORBIS",
389       rtpvorbispay->rate);
390   res =
391       gst_rtp_base_payload_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 (GstRTPBasePayload * 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 (GstRTPBasePayload * basepayload,
473     GstBuffer * buffer)
474 {
475   GstRtpVorbisPay *rtpvorbispay;
476   GstFlowReturn ret;
477   guint newsize;
478   GstMapInfo map;
479   gsize size;
480   guint8 *data;
481   guint packet_len;
482   GstClockTime duration, newduration, timestamp;
483   gboolean flush;
484   guint8 VDT;
485   guint plen;
486   guint8 *ppos, *payload;
487   gboolean fragmented;
488   GstRTPBuffer rtp = { NULL };
489
490   rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
491
492   gst_buffer_map (buffer, &map, GST_MAP_READ);
493   data = map.data;
494   size = map.size;
495   duration = GST_BUFFER_DURATION (buffer);
496   timestamp = GST_BUFFER_TIMESTAMP (buffer);
497
498   GST_LOG_OBJECT (rtpvorbispay, "size %" G_GSIZE_FORMAT
499       ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration));
500
501   if (G_UNLIKELY (size < 1 || size > 0xffff))
502     goto wrong_size;
503
504   /* find packet type */
505   if (data[0] & 1) {
506     /* header */
507     if (data[0] == 1) {
508       /* identification, we need to parse this in order to get the clock rate. */
509       if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, data, size)))
510         goto parse_id_failed;
511       VDT = 1;
512     } else if (data[0] == 3) {
513       /* comment */
514       VDT = 2;
515     } else if (data[0] == 5) {
516       /* setup */
517       VDT = 1;
518     } else
519       goto unknown_header;
520   } else
521     /* data */
522     VDT = 0;
523
524   if (rtpvorbispay->need_headers) {
525     /* we need to collect the headers and construct a config string from them */
526     if (VDT != 0) {
527       GST_DEBUG_OBJECT (rtpvorbispay, "collecting header");
528       /* append header to the list of headers */
529       gst_buffer_unmap (buffer, &map);
530       rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer);
531       ret = GST_FLOW_OK;
532       goto done;
533     } else {
534       if (!gst_rtp_vorbis_pay_finish_headers (basepayload))
535         goto header_error;
536       rtpvorbispay->need_headers = FALSE;
537     }
538   }
539
540   /* size increases with packet length and 2 bytes size eader. */
541   newduration = rtpvorbispay->payload_duration;
542   if (duration != GST_CLOCK_TIME_NONE)
543     newduration += duration;
544
545   newsize = rtpvorbispay->payload_pos + 2 + size;
546   packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0);
547
548   /* check buffer filled against length and max latency */
549   flush = gst_rtp_base_payload_is_filled (basepayload, packet_len, newduration);
550   /* we can store up to 15 vorbis packets in one RTP packet. */
551   flush |= (rtpvorbispay->payload_pkts == 15);
552   /* flush if we have a new VDT */
553   if (rtpvorbispay->packet)
554     flush |= (rtpvorbispay->payload_VDT != VDT);
555   if (flush)
556     gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
557
558   /* create new packet if we must */
559   if (!rtpvorbispay->packet) {
560     gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT, timestamp);
561   }
562
563   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
564   payload = gst_rtp_buffer_get_payload (&rtp);
565   ppos = payload + rtpvorbispay->payload_pos;
566   fragmented = FALSE;
567
568   ret = GST_FLOW_OK;
569
570   /* put buffer in packet, it either fits completely or needs to be fragmented
571    * over multiple RTP packets. */
572   while (size) {
573     plen = MIN (rtpvorbispay->payload_left - 2, size);
574
575     GST_LOG_OBJECT (rtpvorbispay, "append %u bytes", plen);
576
577     /* data is copied in the payload with a 2 byte length header */
578     ppos[0] = (plen >> 8) & 0xff;
579     ppos[1] = (plen & 0xff);
580     memcpy (&ppos[2], data, plen);
581
582     size -= plen;
583     data += plen;
584
585     rtpvorbispay->payload_pos += plen + 2;
586     rtpvorbispay->payload_left -= plen + 2;
587
588     if (fragmented) {
589       if (size == 0)
590         /* last fragment, set F to 0x3. */
591         rtpvorbispay->payload_F = 0x3;
592       else
593         /* fragment continues, set F to 0x2. */
594         rtpvorbispay->payload_F = 0x2;
595     } else {
596       if (size > 0) {
597         /* fragmented packet starts, set F to 0x1, mark ourselves as
598          * fragmented. */
599         rtpvorbispay->payload_F = 0x1;
600         fragmented = TRUE;
601       }
602     }
603     if (fragmented) {
604       gst_rtp_buffer_unmap (&rtp);
605       /* fragmented packets are always flushed and have ptks of 0 */
606       rtpvorbispay->payload_pkts = 0;
607       ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
608
609       if (size > 0) {
610         /* start new packet and get pointers. VDT stays the same. */
611         gst_rtp_vorbis_pay_init_packet (rtpvorbispay,
612             rtpvorbispay->payload_VDT, timestamp);
613         gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
614         payload = gst_rtp_buffer_get_payload (&rtp);
615         ppos = payload + rtpvorbispay->payload_pos;
616       }
617     } else {
618       /* unfragmented packet, update stats for next packet, size == 0 and we
619        * exit the while loop */
620       rtpvorbispay->payload_pkts++;
621       if (duration != GST_CLOCK_TIME_NONE)
622         rtpvorbispay->payload_duration += duration;
623     }
624   }
625
626   if (rtp.buffer)
627     gst_rtp_buffer_unmap (&rtp);
628
629   gst_buffer_unmap (buffer, &map);
630   gst_buffer_unref (buffer);
631
632 done:
633   return ret;
634
635   /* ERRORS */
636 wrong_size:
637   {
638     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
639         ("Invalid packet size (1 < %" G_GSIZE_FORMAT " <= 0xffff)", size),
640         (NULL));
641     gst_buffer_unmap (buffer, &map);
642     gst_buffer_unref (buffer);
643     return GST_FLOW_OK;
644   }
645 parse_id_failed:
646   {
647     gst_buffer_unmap (buffer, &map);
648     gst_buffer_unref (buffer);
649     return GST_FLOW_ERROR;
650   }
651 unknown_header:
652   {
653     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
654         (NULL), ("Ignoring unknown header received"));
655     gst_buffer_unmap (buffer, &map);
656     gst_buffer_unref (buffer);
657     return GST_FLOW_OK;
658   }
659 header_error:
660   {
661     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
662         (NULL), ("Error initializing header config"));
663     gst_buffer_unmap (buffer, &map);
664     gst_buffer_unref (buffer);
665     return GST_FLOW_OK;
666   }
667 }
668
669 static gboolean
670 gst_rtp_vorbis_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
671 {
672   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (payload);
673
674   switch (GST_EVENT_TYPE (event)) {
675     case GST_EVENT_FLUSH_STOP:
676       gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
677       break;
678     default:
679       break;
680   }
681   /* false to let parent handle event as well */
682   return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event);
683 }
684
685 static GstStateChangeReturn
686 gst_rtp_vorbis_pay_change_state (GstElement * element,
687     GstStateChange transition)
688 {
689   GstRtpVorbisPay *rtpvorbispay;
690   GstStateChangeReturn ret;
691
692   rtpvorbispay = GST_RTP_VORBIS_PAY (element);
693
694   switch (transition) {
695     default:
696       break;
697   }
698
699   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
700
701   switch (transition) {
702     case GST_STATE_CHANGE_PAUSED_TO_READY:
703       gst_rtp_vorbis_pay_cleanup (rtpvorbispay);
704       break;
705     default:
706       break;
707   }
708   return ret;
709 }
710
711 gboolean
712 gst_rtp_vorbis_pay_plugin_init (GstPlugin * plugin)
713 {
714   return gst_element_register (plugin, "rtpvorbispay",
715       GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_PAY);
716 }