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