• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, 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 #include <gst/audio/audio.h>
28 
29 #include "gstrtpelements.h"
30 #include "fnv1hash.h"
31 #include "gstrtpvorbispay.h"
32 #include "gstrtputils.h"
33 
34 GST_DEBUG_CATEGORY_STATIC (rtpvorbispay_debug);
35 #define GST_CAT_DEFAULT (rtpvorbispay_debug)
36 
37 /* references:
38  * http://www.rfc-editor.org/rfc/rfc5215.txt
39  */
40 
41 static GstStaticPadTemplate gst_rtp_vorbis_pay_src_template =
42 GST_STATIC_PAD_TEMPLATE ("src",
43     GST_PAD_SRC,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS ("application/x-rtp, "
46         "media = (string) \"audio\", "
47         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
48         "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"VORBIS\""
49         /* All required parameters
50          *
51          * "encoding-params = (string) <num channels>"
52          * "configuration = (string) ANY"
53          */
54     )
55     );
56 
57 static GstStaticPadTemplate gst_rtp_vorbis_pay_sink_template =
58 GST_STATIC_PAD_TEMPLATE ("sink",
59     GST_PAD_SINK,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS ("audio/x-vorbis")
62     );
63 
64 #define DEFAULT_CONFIG_INTERVAL 0
65 
66 enum
67 {
68   PROP_0,
69   PROP_CONFIG_INTERVAL
70 };
71 
72 #define gst_rtp_vorbis_pay_parent_class parent_class
73 G_DEFINE_TYPE (GstRtpVorbisPay, gst_rtp_vorbis_pay, GST_TYPE_RTP_BASE_PAYLOAD);
74 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpvorbispay, "rtpvorbispay",
75     GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_PAY, rtp_element_init (plugin));
76 
77 static gboolean gst_rtp_vorbis_pay_setcaps (GstRTPBasePayload * basepayload,
78     GstCaps * caps);
79 static GstStateChangeReturn gst_rtp_vorbis_pay_change_state (GstElement *
80     element, GstStateChange transition);
81 static GstFlowReturn gst_rtp_vorbis_pay_handle_buffer (GstRTPBasePayload * pad,
82     GstBuffer * buffer);
83 static gboolean gst_rtp_vorbis_pay_sink_event (GstRTPBasePayload * payload,
84     GstEvent * event);
85 
86 static gboolean gst_rtp_vorbis_pay_parse_id (GstRTPBasePayload * basepayload,
87     guint8 * data, guint size);
88 static gboolean gst_rtp_vorbis_pay_finish_headers (GstRTPBasePayload *
89     basepayload);
90 
91 static void gst_rtp_vorbis_pay_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec);
93 static void gst_rtp_vorbis_pay_get_property (GObject * object, guint prop_id,
94     GValue * value, GParamSpec * pspec);
95 
96 static void
gst_rtp_vorbis_pay_class_init(GstRtpVorbisPayClass * klass)97 gst_rtp_vorbis_pay_class_init (GstRtpVorbisPayClass * klass)
98 {
99   GObjectClass *gobject_class;
100   GstElementClass *gstelement_class;
101   GstRTPBasePayloadClass *gstrtpbasepayload_class;
102 
103   gobject_class = (GObjectClass *) klass;
104   gstelement_class = (GstElementClass *) klass;
105   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
106 
107   gstelement_class->change_state = gst_rtp_vorbis_pay_change_state;
108 
109   gstrtpbasepayload_class->set_caps = gst_rtp_vorbis_pay_setcaps;
110   gstrtpbasepayload_class->handle_buffer = gst_rtp_vorbis_pay_handle_buffer;
111   gstrtpbasepayload_class->sink_event = gst_rtp_vorbis_pay_sink_event;
112 
113   gobject_class->set_property = gst_rtp_vorbis_pay_set_property;
114   gobject_class->get_property = gst_rtp_vorbis_pay_get_property;
115 
116   gst_element_class_add_static_pad_template (gstelement_class,
117       &gst_rtp_vorbis_pay_src_template);
118   gst_element_class_add_static_pad_template (gstelement_class,
119       &gst_rtp_vorbis_pay_sink_template);
120 
121   gst_element_class_set_static_metadata (gstelement_class,
122       "RTP Vorbis payloader",
123       "Codec/Payloader/Network/RTP",
124       "Payload-encode Vorbis audio into RTP packets (RFC 5215)",
125       "Wim Taymans <wim.taymans@gmail.com>");
126 
127   GST_DEBUG_CATEGORY_INIT (rtpvorbispay_debug, "rtpvorbispay", 0,
128       "Vorbis RTP Payloader");
129 
130   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL,
131       g_param_spec_uint ("config-interval", "Config Send Interval",
132           "Send Config Insertion Interval in seconds (configuration headers "
133           "will be multiplexed in the data stream when detected.) (0 = disabled)",
134           0, 3600, DEFAULT_CONFIG_INTERVAL,
135           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
136       );
137 }
138 
139 static void
gst_rtp_vorbis_pay_init(GstRtpVorbisPay * rtpvorbispay)140 gst_rtp_vorbis_pay_init (GstRtpVorbisPay * rtpvorbispay)
141 {
142   rtpvorbispay->last_config = GST_CLOCK_TIME_NONE;
143 }
144 
145 static void
gst_rtp_vorbis_pay_clear_packet(GstRtpVorbisPay * rtpvorbispay)146 gst_rtp_vorbis_pay_clear_packet (GstRtpVorbisPay * rtpvorbispay)
147 {
148   if (rtpvorbispay->packet)
149     gst_buffer_unref (rtpvorbispay->packet);
150   rtpvorbispay->packet = NULL;
151   g_list_free_full (rtpvorbispay->packet_buffers,
152       (GDestroyNotify) gst_buffer_unref);
153   rtpvorbispay->packet_buffers = NULL;
154 }
155 
156 static void
gst_rtp_vorbis_pay_cleanup(GstRtpVorbisPay * rtpvorbispay)157 gst_rtp_vorbis_pay_cleanup (GstRtpVorbisPay * rtpvorbispay)
158 {
159   gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
160   g_list_free_full (rtpvorbispay->headers, (GDestroyNotify) gst_buffer_unref);
161   rtpvorbispay->headers = NULL;
162   g_free (rtpvorbispay->config_data);
163   rtpvorbispay->config_data = NULL;
164   rtpvorbispay->last_config = GST_CLOCK_TIME_NONE;
165 }
166 
167 static gboolean
gst_rtp_vorbis_pay_setcaps(GstRTPBasePayload * basepayload,GstCaps * caps)168 gst_rtp_vorbis_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
169 {
170   GstRtpVorbisPay *rtpvorbispay;
171   GstStructure *s;
172   const GValue *array;
173   gint asize, i;
174   GstBuffer *buf;
175   GstMapInfo map;
176 
177   rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
178 
179   s = gst_caps_get_structure (caps, 0);
180 
181   rtpvorbispay->need_headers = TRUE;
182 
183   if ((array = gst_structure_get_value (s, "streamheader")) == NULL)
184     goto done;
185 
186   if (G_VALUE_TYPE (array) != GST_TYPE_ARRAY)
187     goto done;
188 
189   if ((asize = gst_value_array_get_size (array)) < 3)
190     goto done;
191 
192   for (i = 0; i < asize; i++) {
193     const GValue *value;
194 
195     value = gst_value_array_get_value (array, i);
196     if ((buf = gst_value_get_buffer (value)) == NULL)
197       goto null_buffer;
198 
199     gst_buffer_map (buf, &map, GST_MAP_READ);
200     if (map.size < 1)
201       goto invalid_streamheader;
202 
203     /* no data packets allowed */
204     if ((map.data[0] & 1) == 0)
205       goto invalid_streamheader;
206 
207     /* we need packets with id 1, 3, 5 */
208     if (map.data[0] != (i * 2) + 1)
209       goto invalid_streamheader;
210 
211     if (i == 0) {
212       /* identification, we need to parse this in order to get the clock rate. */
213       if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, map.data,
214                   map.size)))
215         goto parse_id_failed;
216     }
217     GST_DEBUG_OBJECT (rtpvorbispay, "collecting header %d", i);
218     rtpvorbispay->headers =
219         g_list_append (rtpvorbispay->headers, gst_buffer_ref (buf));
220     gst_buffer_unmap (buf, &map);
221   }
222   if (!gst_rtp_vorbis_pay_finish_headers (basepayload))
223     goto finish_failed;
224 
225 done:
226   return TRUE;
227 
228   /* ERRORS */
229 null_buffer:
230   {
231     GST_WARNING_OBJECT (rtpvorbispay, "streamheader with null buffer received");
232     return FALSE;
233   }
234 invalid_streamheader:
235   {
236     GST_WARNING_OBJECT (rtpvorbispay, "unable to parse initial header");
237     gst_buffer_unmap (buf, &map);
238     return FALSE;
239   }
240 parse_id_failed:
241   {
242     GST_WARNING_OBJECT (rtpvorbispay, "unable to parse initial header");
243     gst_buffer_unmap (buf, &map);
244     return FALSE;
245   }
246 finish_failed:
247   {
248     GST_WARNING_OBJECT (rtpvorbispay, "unable to finish headers");
249     return FALSE;
250   }
251 }
252 
253 static void
gst_rtp_vorbis_pay_reset_packet(GstRtpVorbisPay * rtpvorbispay,guint8 VDT)254 gst_rtp_vorbis_pay_reset_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT)
255 {
256   guint payload_len;
257   GstRTPBuffer rtp = { NULL };
258 
259   GST_LOG_OBJECT (rtpvorbispay, "reset packet");
260 
261   rtpvorbispay->payload_pos = 4;
262   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_READ, &rtp);
263   payload_len = gst_rtp_buffer_get_payload_len (&rtp);
264   gst_rtp_buffer_unmap (&rtp);
265   rtpvorbispay->payload_left = payload_len - 4;
266   rtpvorbispay->payload_duration = 0;
267   rtpvorbispay->payload_F = 0;
268   rtpvorbispay->payload_VDT = VDT;
269   rtpvorbispay->payload_pkts = 0;
270 }
271 
272 static void
gst_rtp_vorbis_pay_init_packet(GstRtpVorbisPay * rtpvorbispay,guint8 VDT,GstClockTime timestamp)273 gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT,
274     GstClockTime timestamp)
275 {
276   guint len;
277 
278   GST_LOG_OBJECT (rtpvorbispay, "starting new packet, VDT: %d", VDT);
279 
280   gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
281 
282   /* new packet allocate max packet size */
283   len = gst_rtp_buffer_calc_payload_len (GST_RTP_BASE_PAYLOAD_MTU
284       (rtpvorbispay), 0, 0);
285   rtpvorbispay->packet =
286       gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
287       (rtpvorbispay), len, 0, 0);
288   gst_rtp_vorbis_pay_reset_packet (rtpvorbispay, VDT);
289 
290   GST_BUFFER_PTS (rtpvorbispay->packet) = timestamp;
291 }
292 
293 static GstFlowReturn
gst_rtp_vorbis_pay_flush_packet(GstRtpVorbisPay * rtpvorbispay)294 gst_rtp_vorbis_pay_flush_packet (GstRtpVorbisPay * rtpvorbispay)
295 {
296   GstFlowReturn ret;
297   guint8 *payload;
298   guint hlen;
299   GstRTPBuffer rtp = { NULL };
300   GList *l;
301 
302   /* check for empty packet */
303   if (!rtpvorbispay->packet || rtpvorbispay->payload_pos <= 4)
304     return GST_FLOW_OK;
305 
306   GST_LOG_OBJECT (rtpvorbispay, "flushing packet");
307 
308   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
309 
310   /* fix header */
311   payload = gst_rtp_buffer_get_payload (&rtp);
312   /*
313    *  0                   1                   2                   3
314    *  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
315    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
316    * |                     Ident                     | F |VDT|# pkts.|
317    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
318    *
319    * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
320    * VDT: Vorbis data type (0=vorbis, 1=config, 2=comment, 3=reserved)
321    * pkts: number of packets.
322    */
323   payload[0] = (rtpvorbispay->payload_ident >> 16) & 0xff;
324   payload[1] = (rtpvorbispay->payload_ident >> 8) & 0xff;
325   payload[2] = (rtpvorbispay->payload_ident) & 0xff;
326   payload[3] = (rtpvorbispay->payload_F & 0x3) << 6 |
327       (rtpvorbispay->payload_VDT & 0x3) << 4 |
328       (rtpvorbispay->payload_pkts & 0xf);
329 
330   gst_rtp_buffer_unmap (&rtp);
331 
332   /* shrink the buffer size to the last written byte */
333   hlen = gst_rtp_buffer_calc_header_len (0);
334   gst_buffer_resize (rtpvorbispay->packet, 0, hlen + rtpvorbispay->payload_pos);
335 
336   GST_BUFFER_DURATION (rtpvorbispay->packet) = rtpvorbispay->payload_duration;
337 
338   for (l = g_list_last (rtpvorbispay->packet_buffers); l; l = l->prev) {
339     GstBuffer *buf = GST_BUFFER_CAST (l->data);
340     gst_rtp_copy_audio_meta (rtpvorbispay, rtpvorbispay->packet, buf);
341     gst_buffer_unref (buf);
342   }
343   g_list_free (rtpvorbispay->packet_buffers);
344   rtpvorbispay->packet_buffers = NULL;
345 
346   /* push, this gives away our ref to the packet, so clear it. */
347   ret =
348       gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpvorbispay),
349       rtpvorbispay->packet);
350   rtpvorbispay->packet = NULL;
351 
352   return ret;
353 }
354 
355 static gboolean
gst_rtp_vorbis_pay_finish_headers(GstRTPBasePayload * basepayload)356 gst_rtp_vorbis_pay_finish_headers (GstRTPBasePayload * basepayload)
357 {
358   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
359   GList *walk;
360   guint length, size, n_headers, configlen, extralen;
361   gchar *cstr, *configuration;
362   guint8 *data, *config;
363   guint32 ident;
364   gboolean res;
365 
366   GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
367 
368   if (!rtpvorbispay->headers)
369     goto no_headers;
370 
371   /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
372    * |                     Number of packed headers                  |
373    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
374    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
375    * |                          Packed header                        |
376    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
378    * |                          Packed header                        |
379    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
381    * |                          ....                                 |
382    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
383    *
384    * We only construct a config containing 1 packed header like this:
385    *
386    *  0                   1                   2                   3
387    *  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
388    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389    * |                   Ident                       | length       ..
390    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391    * ..              | n. of headers |    length1    |    length2   ..
392    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393    * ..              |             Identification Header            ..
394    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395    * .................................................................
396    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397    * ..              |         Comment Header                       ..
398    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399    * .................................................................
400    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401    * ..                        Comment Header                        |
402    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
403    * |                          Setup Header                        ..
404    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405    * .................................................................
406    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
407    * ..                         Setup Header                         |
408    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409    */
410 
411   /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for
412    * the ident, 2 bytes for length, 1 byte for n. of headers. */
413   size = 4 + 3 + 2 + 1;
414 
415   /* count the size of the headers first and update the hash */
416   length = 0;
417   n_headers = 0;
418   ident = fnv1_hash_32_new ();
419   extralen = 1;
420   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
421     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
422     GstMapInfo map;
423     guint bsize;
424 
425     bsize = gst_buffer_get_size (buf);
426     length += bsize;
427     n_headers++;
428 
429     /* count number of bytes needed for length fields, we don't need this for
430      * the last header. */
431     if (g_list_next (walk)) {
432       do {
433         size++;
434         extralen++;
435         bsize >>= 7;
436       } while (bsize);
437     }
438     /* update hash */
439     gst_buffer_map (buf, &map, GST_MAP_READ);
440     ident = fnv1_hash_32_update (ident, map.data, map.size);
441     gst_buffer_unmap (buf, &map);
442   }
443 
444   /* packet length is header size + packet length */
445   configlen = size + length;
446   config = data = g_malloc (configlen);
447 
448   /* number of packed headers, we only pack 1 header */
449   data[0] = 0;
450   data[1] = 0;
451   data[2] = 0;
452   data[3] = 1;
453 
454   ident = fnv1_hash_32_to_24 (ident);
455   rtpvorbispay->payload_ident = ident;
456   GST_DEBUG_OBJECT (rtpvorbispay, "ident 0x%08x", ident);
457 
458   /* take lower 3 bytes */
459   data[4] = (ident >> 16) & 0xff;
460   data[5] = (ident >> 8) & 0xff;
461   data[6] = ident & 0xff;
462 
463   /* store length of all vorbis headers */
464   data[7] = ((length) >> 8) & 0xff;
465   data[8] = (length) & 0xff;
466 
467   /* store number of headers minus one. */
468   data[9] = n_headers - 1;
469   data += 10;
470 
471   /* store length for each header */
472   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
473     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
474     guint bsize, size, temp;
475     guint flag;
476 
477     /* only need to store the length when it's not the last header */
478     if (!g_list_next (walk))
479       break;
480 
481     bsize = gst_buffer_get_size (buf);
482 
483     /* calc size */
484     size = 0;
485     do {
486       size++;
487       bsize >>= 7;
488     } while (bsize);
489     temp = size;
490 
491     bsize = gst_buffer_get_size (buf);
492     /* write the size backwards */
493     flag = 0;
494     while (size) {
495       size--;
496       data[size] = (bsize & 0x7f) | flag;
497       bsize >>= 7;
498       flag = 0x80;              /* Flag bit on all bytes of the length except the last */
499     }
500     data += temp;
501   }
502 
503   /* copy header data */
504   for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) {
505     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
506 
507     gst_buffer_extract (buf, 0, data, gst_buffer_get_size (buf));
508     data += gst_buffer_get_size (buf);
509   }
510   rtpvorbispay->need_headers = FALSE;
511 
512   /* serialize to base64 */
513   configuration = g_base64_encode (config, configlen);
514 
515   /* store for later re-sending */
516   g_free (rtpvorbispay->config_data);
517   rtpvorbispay->config_size = configlen - 4 - 3 - 2;
518   rtpvorbispay->config_data = g_malloc (rtpvorbispay->config_size);
519   rtpvorbispay->config_extra_len = extralen;
520   memcpy (rtpvorbispay->config_data, config + 4 + 3 + 2,
521       rtpvorbispay->config_size);
522 
523   g_free (config);
524 
525   /* configure payloader settings */
526   cstr = g_strdup_printf ("%d", rtpvorbispay->channels);
527   gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "VORBIS",
528       rtpvorbispay->rate);
529   res =
530       gst_rtp_base_payload_set_outcaps (basepayload, "encoding-params",
531       G_TYPE_STRING, cstr, "configuration", G_TYPE_STRING, configuration, NULL);
532   g_free (cstr);
533   g_free (configuration);
534 
535   return res;
536 
537   /* ERRORS */
538 no_headers:
539   {
540     GST_DEBUG_OBJECT (rtpvorbispay, "finish headers");
541     return FALSE;
542   }
543 }
544 
545 static gboolean
gst_rtp_vorbis_pay_parse_id(GstRTPBasePayload * basepayload,guint8 * data,guint size)546 gst_rtp_vorbis_pay_parse_id (GstRTPBasePayload * basepayload, guint8 * data,
547     guint size)
548 {
549   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
550   guint8 channels;
551   gint32 rate, version;
552 
553   if (G_UNLIKELY (size < 16))
554     goto too_short;
555 
556   if (G_UNLIKELY (memcmp (data, "\001vorbis", 7)))
557     goto invalid_start;
558   data += 7;
559 
560   if (G_UNLIKELY ((version = GST_READ_UINT32_LE (data)) != 0))
561     goto invalid_version;
562   data += 4;
563 
564   if (G_UNLIKELY ((channels = *data++) < 1))
565     goto invalid_channels;
566 
567   if (G_UNLIKELY ((rate = GST_READ_UINT32_LE (data)) < 1))
568     goto invalid_rate;
569 
570   /* all fine, store the values */
571   rtpvorbispay->channels = channels;
572   rtpvorbispay->rate = rate;
573 
574   return TRUE;
575 
576   /* ERRORS */
577 too_short:
578   {
579     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
580         ("Identification packet is too short, need at least 16, got %d", size),
581         (NULL));
582     return FALSE;
583   }
584 invalid_start:
585   {
586     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
587         ("Invalid header start in identification packet"), (NULL));
588     return FALSE;
589   }
590 invalid_version:
591   {
592     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
593         ("Invalid version, expected 0, got %d", version), (NULL));
594     return FALSE;
595   }
596 invalid_rate:
597   {
598     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
599         ("Invalid rate %d", rate), (NULL));
600     return FALSE;
601   }
602 invalid_channels:
603   {
604     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
605         ("Invalid channels %d", channels), (NULL));
606     return FALSE;
607   }
608 }
609 
610 static GstFlowReturn
gst_rtp_vorbis_pay_payload_buffer(GstRtpVorbisPay * rtpvorbispay,guint8 VDT,GstBuffer * buffer,guint8 * data,guint size,GstClockTime timestamp,GstClockTime duration,guint not_in_length)611 gst_rtp_vorbis_pay_payload_buffer (GstRtpVorbisPay * rtpvorbispay, guint8 VDT,
612     GstBuffer * buffer, guint8 * data, guint size, GstClockTime timestamp,
613     GstClockTime duration, guint not_in_length)
614 {
615   GstFlowReturn ret = GST_FLOW_OK;
616   guint newsize;
617   guint packet_len;
618   GstClockTime newduration;
619   gboolean flush;
620   guint plen;
621   guint8 *ppos, *payload;
622   gboolean fragmented;
623   GstRTPBuffer rtp = { NULL };
624 
625   /* size increases with packet length and 2 bytes size eader. */
626   newduration = rtpvorbispay->payload_duration;
627   if (duration != GST_CLOCK_TIME_NONE)
628     newduration += duration;
629 
630   newsize = rtpvorbispay->payload_pos + 2 + size;
631   packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0);
632 
633   /* check buffer filled against length and max latency */
634   flush = gst_rtp_base_payload_is_filled (GST_RTP_BASE_PAYLOAD (rtpvorbispay),
635       packet_len, newduration);
636   /* we can store up to 15 vorbis packets in one RTP packet. */
637   flush |= (rtpvorbispay->payload_pkts == 15);
638   /* flush if we have a new VDT */
639   if (rtpvorbispay->packet)
640     flush |= (rtpvorbispay->payload_VDT != VDT);
641   if (flush)
642     ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
643 
644   if (ret != GST_FLOW_OK)
645     goto done;
646 
647   /* create new packet if we must */
648   if (!rtpvorbispay->packet) {
649     gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT, timestamp);
650   }
651 
652   gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
653   payload = gst_rtp_buffer_get_payload (&rtp);
654   ppos = payload + rtpvorbispay->payload_pos;
655   fragmented = FALSE;
656 
657   /* put buffer in packet, it either fits completely or needs to be fragmented
658    * over multiple RTP packets. */
659   do {
660     plen = MIN (rtpvorbispay->payload_left - 2, size);
661 
662     GST_LOG_OBJECT (rtpvorbispay, "append %u bytes", plen);
663 
664     /* data is copied in the payload with a 2 byte length header */
665     ppos[0] = ((plen - not_in_length) >> 8) & 0xff;
666     ppos[1] = ((plen - not_in_length) & 0xff);
667     if (plen)
668       memcpy (&ppos[2], data, plen);
669 
670     if (buffer) {
671       if (!rtpvorbispay->packet_buffers
672           || rtpvorbispay->packet_buffers->data != (gpointer) buffer)
673         rtpvorbispay->packet_buffers =
674             g_list_prepend (rtpvorbispay->packet_buffers,
675             gst_buffer_ref (buffer));
676     } else {
677       GList *l;
678 
679       for (l = rtpvorbispay->headers; l; l = l->next)
680         rtpvorbispay->packet_buffers =
681             g_list_prepend (rtpvorbispay->packet_buffers,
682             gst_buffer_ref (l->data));
683     }
684 
685     /* only first (only) configuration cuts length field */
686     /* NOTE: spec (if any) is not clear on this ... */
687     not_in_length = 0;
688 
689     size -= plen;
690     data += plen;
691 
692     rtpvorbispay->payload_pos += plen + 2;
693     rtpvorbispay->payload_left -= plen + 2;
694 
695     if (fragmented) {
696       if (size == 0)
697         /* last fragment, set F to 0x3. */
698         rtpvorbispay->payload_F = 0x3;
699       else
700         /* fragment continues, set F to 0x2. */
701         rtpvorbispay->payload_F = 0x2;
702     } else {
703       if (size > 0) {
704         /* fragmented packet starts, set F to 0x1, mark ourselves as
705          * fragmented. */
706         rtpvorbispay->payload_F = 0x1;
707         fragmented = TRUE;
708       }
709     }
710     if (fragmented) {
711       gst_rtp_buffer_unmap (&rtp);
712       /* fragmented packets are always flushed and have ptks of 0 */
713       rtpvorbispay->payload_pkts = 0;
714       ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay);
715 
716       if (size > 0) {
717         /* start new packet and get pointers. VDT stays the same. */
718         gst_rtp_vorbis_pay_init_packet (rtpvorbispay,
719             rtpvorbispay->payload_VDT, timestamp);
720         gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp);
721         payload = gst_rtp_buffer_get_payload (&rtp);
722         ppos = payload + rtpvorbispay->payload_pos;
723       }
724     } else {
725       /* unfragmented packet, update stats for next packet, size == 0 and we
726        * exit the while loop */
727       rtpvorbispay->payload_pkts++;
728       if (duration != GST_CLOCK_TIME_NONE)
729         rtpvorbispay->payload_duration += duration;
730     }
731   } while (size && ret == GST_FLOW_OK);
732 
733   if (rtp.buffer)
734     gst_rtp_buffer_unmap (&rtp);
735 
736 done:
737 
738   return ret;
739 }
740 
741 static GstFlowReturn
gst_rtp_vorbis_pay_handle_buffer(GstRTPBasePayload * basepayload,GstBuffer * buffer)742 gst_rtp_vorbis_pay_handle_buffer (GstRTPBasePayload * basepayload,
743     GstBuffer * buffer)
744 {
745   GstRtpVorbisPay *rtpvorbispay;
746   GstFlowReturn ret;
747   GstMapInfo map;
748   gsize size;
749   guint8 *data;
750   GstClockTime duration, timestamp;
751   guint8 VDT;
752 
753   rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload);
754 
755   gst_buffer_map (buffer, &map, GST_MAP_READ);
756   data = map.data;
757   size = map.size;
758   duration = GST_BUFFER_DURATION (buffer);
759   timestamp = GST_BUFFER_PTS (buffer);
760 
761   GST_LOG_OBJECT (rtpvorbispay, "size %" G_GSIZE_FORMAT
762       ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration));
763 
764   if (G_UNLIKELY (size < 1))
765     goto wrong_size;
766 
767   /* find packet type */
768   if (data[0] & 1) {
769     /* header */
770     if (data[0] == 1) {
771       /* identification, we need to parse this in order to get the clock rate. */
772       if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, data, size)))
773         goto parse_id_failed;
774       VDT = 1;
775     } else if (data[0] == 3) {
776       /* comment */
777       VDT = 2;
778     } else if (data[0] == 5) {
779       /* setup */
780       VDT = 1;
781     } else
782       goto unknown_header;
783   } else
784     /* data */
785     VDT = 0;
786 
787   /* we need to collect the headers and construct a config string from them */
788   if (VDT != 0) {
789     rtpvorbispay->need_headers = TRUE;
790     if (!rtpvorbispay->need_headers && VDT == 1) {
791       GST_INFO_OBJECT (rtpvorbispay, "getting new headers, replace existing");
792       g_list_free_full (rtpvorbispay->headers,
793           (GDestroyNotify) gst_buffer_unref);
794       rtpvorbispay->headers = NULL;
795     }
796     GST_DEBUG_OBJECT (rtpvorbispay, "collecting header");
797     /* append header to the list of headers, or replace
798      * if the same type of header was already in there.
799      *
800      * This prevents storing an infinite amount of e.g. comment headers, there
801      * must only be one */
802     gst_buffer_unmap (buffer, &map);
803 
804     if (rtpvorbispay->headers) {
805       gboolean found = FALSE;
806       GList *l;
807       guint8 new_header_type;
808 
809       gst_buffer_extract (buffer, 0, &new_header_type, 1);
810 
811       for (l = rtpvorbispay->headers; l; l = l->next) {
812         GstBuffer *header = l->data;
813         guint8 header_type;
814 
815         if (gst_buffer_extract (header, 0, &header_type, 1)
816             && header_type == new_header_type) {
817           found = TRUE;
818           gst_buffer_unref (header);
819           l->data = buffer;
820           break;
821         }
822       }
823       if (!found)
824         rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer);
825     } else {
826       rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer);
827     }
828 
829     ret = GST_FLOW_OK;
830     goto done;
831   } else if (rtpvorbispay->headers && rtpvorbispay->need_headers) {
832     if (!gst_rtp_vorbis_pay_finish_headers (basepayload))
833       goto header_error;
834   }
835 
836   /* there is a config request, see if we need to insert it */
837   if (rtpvorbispay->config_interval > 0 && rtpvorbispay->config_data) {
838     gboolean send_config = FALSE;
839     GstClockTime running_time =
840         gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
841         timestamp);
842 
843     if (rtpvorbispay->last_config != -1) {
844       guint64 diff;
845 
846       GST_LOG_OBJECT (rtpvorbispay,
847           "now %" GST_TIME_FORMAT ", last config %" GST_TIME_FORMAT,
848           GST_TIME_ARGS (running_time),
849           GST_TIME_ARGS (rtpvorbispay->last_config));
850 
851       /* calculate diff between last config in milliseconds */
852       if (running_time > rtpvorbispay->last_config) {
853         diff = running_time - rtpvorbispay->last_config;
854       } else {
855         diff = 0;
856       }
857 
858       GST_DEBUG_OBJECT (rtpvorbispay,
859           "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
860 
861       /* bigger than interval, queue config */
862       if (GST_TIME_AS_SECONDS (diff) >= rtpvorbispay->config_interval) {
863         GST_DEBUG_OBJECT (rtpvorbispay, "time to send config");
864         send_config = TRUE;
865       }
866     } else {
867       /* no known previous config time, send now */
868       GST_DEBUG_OBJECT (rtpvorbispay, "no previous config time, send now");
869       send_config = TRUE;
870     }
871 
872     if (send_config) {
873       /* we need to send config now first */
874       /* different TDT type forces flush */
875       gst_rtp_vorbis_pay_payload_buffer (rtpvorbispay, 1,
876           NULL, rtpvorbispay->config_data, rtpvorbispay->config_size,
877           timestamp, GST_CLOCK_TIME_NONE, rtpvorbispay->config_extra_len);
878 
879       if (running_time != -1) {
880         rtpvorbispay->last_config = running_time;
881       }
882     }
883   }
884 
885   ret =
886       gst_rtp_vorbis_pay_payload_buffer (rtpvorbispay, VDT, buffer, data, size,
887       timestamp, duration, 0);
888 
889   gst_buffer_unmap (buffer, &map);
890   gst_buffer_unref (buffer);
891 
892 done:
893   return ret;
894 
895   /* ERRORS */
896 wrong_size:
897   {
898     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
899         ("Invalid packet size (1 < %" G_GSIZE_FORMAT ")", size), (NULL));
900     gst_buffer_unmap (buffer, &map);
901     gst_buffer_unref (buffer);
902     return GST_FLOW_OK;
903   }
904 parse_id_failed:
905   {
906     gst_buffer_unmap (buffer, &map);
907     gst_buffer_unref (buffer);
908     return GST_FLOW_ERROR;
909   }
910 unknown_header:
911   {
912     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
913         (NULL), ("Ignoring unknown header received"));
914     gst_buffer_unmap (buffer, &map);
915     gst_buffer_unref (buffer);
916     return GST_FLOW_OK;
917   }
918 header_error:
919   {
920     GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE,
921         (NULL), ("Error initializing header config"));
922     gst_buffer_unmap (buffer, &map);
923     gst_buffer_unref (buffer);
924     return GST_FLOW_OK;
925   }
926 }
927 
928 static gboolean
gst_rtp_vorbis_pay_sink_event(GstRTPBasePayload * payload,GstEvent * event)929 gst_rtp_vorbis_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
930 {
931   GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (payload);
932 
933   switch (GST_EVENT_TYPE (event)) {
934     case GST_EVENT_FLUSH_STOP:
935       gst_rtp_vorbis_pay_clear_packet (rtpvorbispay);
936       break;
937     default:
938       break;
939   }
940   /* false to let parent handle event as well */
941   return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event);
942 }
943 
944 static GstStateChangeReturn
gst_rtp_vorbis_pay_change_state(GstElement * element,GstStateChange transition)945 gst_rtp_vorbis_pay_change_state (GstElement * element,
946     GstStateChange transition)
947 {
948   GstRtpVorbisPay *rtpvorbispay;
949   GstStateChangeReturn ret;
950 
951   rtpvorbispay = GST_RTP_VORBIS_PAY (element);
952 
953   switch (transition) {
954     default:
955       break;
956   }
957 
958   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
959 
960   switch (transition) {
961     case GST_STATE_CHANGE_PAUSED_TO_READY:
962       gst_rtp_vorbis_pay_cleanup (rtpvorbispay);
963       break;
964     default:
965       break;
966   }
967   return ret;
968 }
969 
970 static void
gst_rtp_vorbis_pay_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)971 gst_rtp_vorbis_pay_set_property (GObject * object, guint prop_id,
972     const GValue * value, GParamSpec * pspec)
973 {
974   GstRtpVorbisPay *rtpvorbispay;
975 
976   rtpvorbispay = GST_RTP_VORBIS_PAY (object);
977 
978   switch (prop_id) {
979     case PROP_CONFIG_INTERVAL:
980       rtpvorbispay->config_interval = g_value_get_uint (value);
981       break;
982     default:
983       break;
984   }
985 }
986 
987 static void
gst_rtp_vorbis_pay_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)988 gst_rtp_vorbis_pay_get_property (GObject * object, guint prop_id,
989     GValue * value, GParamSpec * pspec)
990 {
991   GstRtpVorbisPay *rtpvorbispay;
992 
993   rtpvorbispay = GST_RTP_VORBIS_PAY (object);
994 
995   switch (prop_id) {
996     case PROP_CONFIG_INTERVAL:
997       g_value_set_uint (value, rtpvorbispay->config_interval);
998       break;
999     default:
1000       break;
1001   }
1002 }
1003