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