2 * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com>
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.
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.
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.
26 #include <gst/rtp/gstrtpbuffer.h>
29 #include "gstrtpvorbispay.h"
31 GST_DEBUG_CATEGORY_STATIC (rtpvorbispay_debug);
32 #define GST_CAT_DEFAULT (rtpvorbispay_debug)
35 * http://www.rfc-editor.org/rfc/rfc5215.txt
38 static GstStaticPadTemplate gst_rtp_vorbis_pay_src_template =
39 GST_STATIC_PAD_TEMPLATE ("src",
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
48 * "encoding-params = (string) <num channels>"
49 * "configuration = (string) ANY"
54 static GstStaticPadTemplate gst_rtp_vorbis_pay_sink_template =
55 GST_STATIC_PAD_TEMPLATE ("sink",
58 GST_STATIC_CAPS ("audio/x-vorbis")
61 GST_BOILERPLATE (GstRtpVorbisPay, gst_rtp_vorbis_pay, GstBaseRTPPayload,
62 GST_TYPE_BASE_RTP_PAYLOAD);
64 static gboolean gst_rtp_vorbis_pay_setcaps (GstBaseRTPPayload * basepayload,
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,
70 static gboolean gst_rtp_vorbis_pay_handle_event (GstPad * pad,
74 gst_rtp_vorbis_pay_base_init (gpointer klass)
76 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
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);
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>");
90 gst_rtp_vorbis_pay_class_init (GstRtpVorbisPayClass * klass)
92 GstElementClass *gstelement_class;
93 GstBaseRTPPayloadClass *gstbasertppayload_class;
95 gstelement_class = (GstElementClass *) klass;
96 gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
98 gstelement_class->change_state = gst_rtp_vorbis_pay_change_state;
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;
104 GST_DEBUG_CATEGORY_INIT (rtpvorbispay_debug, "rtpvorbispay", 0,
105 "Vorbis RTP Payloader");
109 gst_rtp_vorbis_pay_init (GstRtpVorbisPay * rtpvorbispay,
110 GstRtpVorbisPayClass * klass)
112 /* needed because of GST_BOILERPLATE */
116 gst_rtp_vorbis_pay_clear_packet (GstRtpVorbisPay * rtpvorbispay)
118 if (rtpvorbispay->packet)
119 gst_buffer_unref (rtpvorbispay->packet);
120 rtpvorbispay->packet = NULL;
124 gst_rtp_vorbis_pay_cleanup (GstRtpVorbisPay * rtpvorbispay)
126 g_list_foreach (rtpvorbispay->headers, (GFunc) gst_mini_object_unref, NULL);
127 g_list_free (rtpvorbispay->headers);
128 rtpvorbispay->headers = NULL;
130 gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
134 gst_rtp_vorbis_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
136 GstRtpVorbisPay *rtpvorbispay;
138 rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
140 rtpvorbispay->need_headers = TRUE;
146 gst_rtp_vorbis_pay_reset_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT)
150 GST_LOG_OBJECT (rtpvorbispay, "reset packet");
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;
162 gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT,
163 GstClockTime timestamp)
165 GST_LOG_OBJECT (rtpvorbispay, "starting new packet, VDT: %d", VDT);
167 if (rtpvorbispay->packet)
168 gst_buffer_unref (rtpvorbispay->packet);
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;
179 gst_rtp_vorbis_pay_flush_packet (GstRtpVorbisPay * rtpvorbispay)
185 /* check for empty packet */
186 if (!rtpvorbispay->packet || rtpvorbispay->payload_pos <= 4)
189 GST_LOG_OBJECT (rtpvorbispay, "flushing packet");
192 payload = gst_rtp_buffer_get_payload (rtpvorbispay->packet);
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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.
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);
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;
215 GST_BUFFER_DURATION (rtpvorbispay->packet) = rtpvorbispay->payload_duration;
217 /* push, this gives away our ref to the packet, so clear it. */
219 gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpvorbispay),
220 rtpvorbispay->packet);
221 rtpvorbispay->packet = NULL;
227 gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload)
229 GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
231 guint length, size, n_headers, configlen;
232 gchar *cstr, *configuration;
233 guint8 *data, *config;
237 GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
239 if (!rtpvorbispay->headers)
242 /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
243 * | Number of packed headers |
244 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
245 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
248 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
250 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
251 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
253 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
255 * We only construct a config containing 1 packed header like this:
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
275 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
276 * .................................................................
277 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
279 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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;
286 /* count the size of the headers first and update the hash */
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);
294 bsize = GST_BUFFER_SIZE (buf);
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)) {
307 ident = fnv1_hash_32_update (ident, GST_BUFFER_DATA (buf),
308 GST_BUFFER_SIZE (buf));
311 /* packet length is header size + packet length */
312 configlen = size + length;
313 config = data = g_malloc (configlen);
315 /* number of packed headers, we only pack 1 header */
321 ident = fnv1_hash_32_to_24 (ident);
322 rtpvorbispay->payload_ident = ident;
323 GST_DEBUG_OBJECT (rtpvorbispay, "ident 0x%08x", ident);
325 /* take lower 3 bytes */
326 data[4] = (ident >> 16) & 0xff;
327 data[5] = (ident >> 8) & 0xff;
328 data[6] = ident & 0xff;
330 /* store length of all vorbis headers */
331 data[7] = ((length) >> 8) & 0xff;
332 data[8] = (length) & 0xff;
334 /* store number of headers minus one. */
335 data[9] = n_headers - 1;
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;
344 /* only need to store the length when it's not the last header */
345 if (!g_list_next (walk))
348 bsize = GST_BUFFER_SIZE (buf);
358 bsize = GST_BUFFER_SIZE (buf);
359 /* write the size backwards */
363 data[size] = (bsize & 0x7f) | flag;
365 flag = 0x80; /* Flag bit on all bytes of the length except the last */
370 /* copy header data */
371 for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
372 GstBuffer *buf = GST_BUFFER_CAST (walk->data);
374 memcpy (data, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
375 data += GST_BUFFER_SIZE (buf);
378 /* serialize to base64 */
379 configuration = g_base64_encode (config, configlen);
382 /* configure payloader settings */
383 cstr = g_strdup_printf ("%d", rtpvorbispay->channels);
384 gst_basertppayload_set_options (basepayload, "audio", TRUE, "VORBIS",
387 gst_basertppayload_set_outcaps (basepayload, "encoding-params",
388 G_TYPE_STRING, cstr, "configuration", G_TYPE_STRING, configuration, NULL);
390 g_free (configuration);
397 GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
403 gst_rtp_vorbis_pay_parse_id (GstBaseRTPPayload * basepayload, guint8 * data,
406 GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
408 gint32 rate, version;
410 if (G_UNLIKELY (size < 16))
413 if (G_UNLIKELY (memcmp (data, "\001vorbis", 7)))
417 if (G_UNLIKELY ((version = GST_READ_UINT32_LE (data)) != 0))
418 goto invalid_version;
421 if (G_UNLIKELY ((channels = *data++) < 1))
422 goto invalid_channels;
424 if (G_UNLIKELY ((rate = GST_READ_UINT32_LE (data)) < 1))
427 /* all fine, store the values */
428 rtpvorbispay->channels = channels;
429 rtpvorbispay->rate = rate;
436 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
437 ("Identification packet is too short, need at least 16, got %d", size),
443 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
444 ("Invalid header start in identification packet"), (NULL));
449 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
450 ("Invalid version, expected 0, got %d", version), (NULL));
455 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
456 ("Invalid rate %d", rate), (NULL));
461 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
462 ("Invalid channels %d", channels), (NULL));
468 gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * basepayload,
471 GstRtpVorbisPay *rtpvorbispay;
476 GstClockTime duration, newduration, timestamp;
480 guint8 *ppos, *payload;
483 rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
485 size = GST_BUFFER_SIZE (buffer);
486 data = GST_BUFFER_DATA (buffer);
487 duration = GST_BUFFER_DURATION (buffer);
488 timestamp = GST_BUFFER_TIMESTAMP (buffer);
490 GST_LOG_OBJECT (rtpvorbispay, "size %u, duration %" GST_TIME_FORMAT,
491 size, GST_TIME_ARGS (duration));
493 if (G_UNLIKELY (size < 1 || size > 0xffff))
496 /* find packet type */
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;
504 } else if (data[0] == 3) {
507 } else if (data[0] == 5) {
516 if (rtpvorbispay->need_headers) {
517 /* we need to collect the headers and construct a config string from them */
519 GST_DEBUG_OBJECT (rtpvorbispay, "collecting header");
520 /* append header to the list of headers */
521 rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer);
525 if (!gst_rtp_vorbis_pay_finish_headers (basepayload))
527 rtpvorbispay->need_headers = FALSE;
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;
536 newsize = rtpvorbispay->payload_pos + 2 + size;
537 packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0);
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);
547 gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
549 /* create new packet if we must */
550 if (!rtpvorbispay->packet) {
551 gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT, timestamp);
554 payload = gst_rtp_buffer_get_payload (rtpvorbispay->packet);
555 ppos = payload + rtpvorbispay->payload_pos;
560 /* put buffer in packet, it either fits completely or needs to be fragmented
561 * over multiple RTP packets. */
563 plen = MIN (rtpvorbispay->payload_left - 2, size);
565 GST_LOG_OBJECT (rtpvorbispay, "append %u bytes", plen);
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);
575 rtpvorbispay->payload_pos += plen + 2;
576 rtpvorbispay->payload_left -= plen + 2;
580 /* last fragment, set F to 0x3. */
581 rtpvorbispay->payload_F = 0x3;
583 /* fragment continues, set F to 0x2. */
584 rtpvorbispay->payload_F = 0x2;
587 /* fragmented packet starts, set F to 0x1, mark ourselves as
589 rtpvorbispay->payload_F = 0x1;
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);
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;
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;
613 gst_buffer_unref (buffer);
621 GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
622 ("Invalid packet size (1 < %d <= 0xffff)", size), (NULL));
623 gst_buffer_unref (buffer);
628 gst_buffer_unref (buffer);
629 return GST_FLOW_ERROR;
633 GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
634 (NULL), ("Ignoring unknown header received"));
635 gst_buffer_unref (buffer);
640 GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
641 (NULL), ("Error initializing header config"));
642 gst_buffer_unref (buffer);
648 gst_rtp_vorbis_pay_handle_event (GstPad * pad, GstEvent * event)
650 GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (GST_PAD_PARENT (pad));
652 switch (GST_EVENT_TYPE (event)) {
653 case GST_EVENT_FLUSH_STOP:
654 gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
659 /* false to let parent handle event as well */
663 static GstStateChangeReturn
664 gst_rtp_vorbis_pay_change_state (GstElement * element,
665 GstStateChange transition)
667 GstRtpVorbisPay *rtpvorbispay;
668 GstStateChangeReturn ret;
670 rtpvorbispay = GST_RTP_VORBIS_PAY (element);
672 switch (transition) {
677 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
679 switch (transition) {
680 case GST_STATE_CHANGE_PAUSED_TO_READY:
681 gst_rtp_vorbis_pay_cleanup (rtpvorbispay);
690 gst_rtp_vorbis_pay_plugin_init (GstPlugin * plugin)
692 return gst_element_register (plugin, "rtpvorbispay",
693 GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_PAY);