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 <gst/tag/tag.h>
25 #include <gst/rtp/gstrtpbuffer.h>
26 #include <gst/audio/audio.h>
27 
28 #include <string.h>
29 #include "gstrtpvorbisdepay.h"
30 #include "gstrtputils.h"
31 
32 GST_DEBUG_CATEGORY_STATIC (rtpvorbisdepay_debug);
33 #define GST_CAT_DEFAULT (rtpvorbisdepay_debug)
34 
35 /* references:
36  * http://www.rfc-editor.org/rfc/rfc5215.txt
37  */
38 
39 static GstStaticPadTemplate gst_rtp_vorbis_depay_sink_template =
40 GST_STATIC_PAD_TEMPLATE ("sink",
41     GST_PAD_SINK,
42     GST_PAD_ALWAYS,
43     GST_STATIC_CAPS ("application/x-rtp, "
44         "media = (string) \"audio\", "
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_depay_src_template =
55 GST_STATIC_PAD_TEMPLATE ("src",
56     GST_PAD_SRC,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("audio/x-vorbis")
59     );
60 
61 #define gst_rtp_vorbis_depay_parent_class parent_class
62 G_DEFINE_TYPE (GstRtpVorbisDepay, gst_rtp_vorbis_depay,
63     GST_TYPE_RTP_BASE_DEPAYLOAD);
64 
65 static gboolean gst_rtp_vorbis_depay_setcaps (GstRTPBaseDepayload * depayload,
66     GstCaps * caps);
67 static GstBuffer *gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
68     GstRTPBuffer * rtp);
69 
70 static void gst_rtp_vorbis_depay_finalize (GObject * object);
71 
72 static GstStateChangeReturn gst_rtp_vorbis_depay_change_state (GstElement *
73     element, GstStateChange transition);
74 
75 static void
gst_rtp_vorbis_depay_class_init(GstRtpVorbisDepayClass * klass)76 gst_rtp_vorbis_depay_class_init (GstRtpVorbisDepayClass * klass)
77 {
78   GObjectClass *gobject_class;
79   GstElementClass *gstelement_class;
80   GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
81 
82   gobject_class = (GObjectClass *) klass;
83   gstelement_class = (GstElementClass *) klass;
84   gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
85 
86   gobject_class->finalize = gst_rtp_vorbis_depay_finalize;
87 
88   gstelement_class->change_state = gst_rtp_vorbis_depay_change_state;
89 
90   gstrtpbasedepayload_class->process_rtp_packet = gst_rtp_vorbis_depay_process;
91   gstrtpbasedepayload_class->set_caps = gst_rtp_vorbis_depay_setcaps;
92 
93   gst_element_class_add_static_pad_template (gstelement_class,
94       &gst_rtp_vorbis_depay_sink_template);
95   gst_element_class_add_static_pad_template (gstelement_class,
96       &gst_rtp_vorbis_depay_src_template);
97 
98   gst_element_class_set_static_metadata (gstelement_class,
99       "RTP Vorbis depayloader", "Codec/Depayloader/Network/RTP",
100       "Extracts Vorbis Audio from RTP packets (RFC 5215)",
101       "Wim Taymans <wim.taymans@gmail.com>");
102 
103   GST_DEBUG_CATEGORY_INIT (rtpvorbisdepay_debug, "rtpvorbisdepay", 0,
104       "Vorbis RTP Depayloader");
105 }
106 
107 static void
gst_rtp_vorbis_depay_init(GstRtpVorbisDepay * rtpvorbisdepay)108 gst_rtp_vorbis_depay_init (GstRtpVorbisDepay * rtpvorbisdepay)
109 {
110   rtpvorbisdepay->adapter = gst_adapter_new ();
111 }
112 
113 static void
free_config(GstRtpVorbisConfig * conf)114 free_config (GstRtpVorbisConfig * conf)
115 {
116   g_list_free_full (conf->headers, (GDestroyNotify) gst_buffer_unref);
117   g_free (conf);
118 }
119 
120 static void
free_indents(GstRtpVorbisDepay * rtpvorbisdepay)121 free_indents (GstRtpVorbisDepay * rtpvorbisdepay)
122 {
123   g_list_free_full (rtpvorbisdepay->configs, (GDestroyNotify) free_config);
124   rtpvorbisdepay->configs = NULL;
125 }
126 
127 static void
gst_rtp_vorbis_depay_finalize(GObject * object)128 gst_rtp_vorbis_depay_finalize (GObject * object)
129 {
130   GstRtpVorbisDepay *rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (object);
131 
132   g_object_unref (rtpvorbisdepay->adapter);
133 
134   G_OBJECT_CLASS (parent_class)->finalize (object);
135 }
136 
137 static gboolean
gst_rtp_vorbis_depay_has_ident(GstRtpVorbisDepay * rtpvorbisdepay,guint32 ident)138 gst_rtp_vorbis_depay_has_ident (GstRtpVorbisDepay * rtpvorbisdepay,
139     guint32 ident)
140 {
141   GList *walk;
142 
143   for (walk = rtpvorbisdepay->configs; walk; walk = g_list_next (walk)) {
144     GstRtpVorbisConfig *conf = (GstRtpVorbisConfig *) walk->data;
145 
146     if (conf->ident == ident)
147       return TRUE;
148   }
149 
150   return FALSE;
151 }
152 
153 /* takes ownership of confbuf */
154 static gboolean
gst_rtp_vorbis_depay_parse_configuration(GstRtpVorbisDepay * rtpvorbisdepay,GstBuffer * confbuf)155 gst_rtp_vorbis_depay_parse_configuration (GstRtpVorbisDepay * rtpvorbisdepay,
156     GstBuffer * confbuf)
157 {
158   GstBuffer *buf;
159   guint32 num_headers;
160   GstMapInfo map;
161   guint8 *data;
162   gsize size;
163   guint offset;
164   gint i, j;
165 
166   gst_buffer_map (confbuf, &map, GST_MAP_READ);
167   data = map.data;
168   size = map.size;
169 
170   GST_DEBUG_OBJECT (rtpvorbisdepay, "config size %" G_GSIZE_FORMAT, size);
171 
172   /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173    * |                     Number of packed headers                  |
174    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
175    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
176    * |                          Packed header                        |
177    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
178    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
179    * |                          Packed header                        |
180    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
182    * |                          ....                                 |
183    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
184    */
185   if (size < 4)
186     goto too_small;
187 
188   num_headers = GST_READ_UINT32_BE (data);
189   size -= 4;
190   data += 4;
191   offset = 4;
192 
193   GST_DEBUG_OBJECT (rtpvorbisdepay, "have %u headers", num_headers);
194 
195   /*  0                   1                   2                   3
196    *  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
197    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
198    * |                   Ident                       | length       ..
199    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200    * ..              | n. of headers |    length1    |    length2   ..
201    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
202    * ..              |             Identification Header            ..
203    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
204    * .................................................................
205    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
206    * ..              |         Comment Header                       ..
207    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
208    * .................................................................
209    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
210    * ..                        Comment Header                        |
211    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
212    * |                          Setup Header                        ..
213    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
214    * .................................................................
215    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
216    * ..                         Setup Header                         |
217    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
218    */
219   for (i = 0; i < num_headers; i++) {
220     guint32 ident;
221     guint16 length;
222     guint8 n_headers, b;
223     GstRtpVorbisConfig *conf;
224     guint *h_sizes;
225     guint extra = 1;
226 
227     if (size < 6)
228       goto too_small;
229 
230     ident = (data[0] << 16) | (data[1] << 8) | data[2];
231     length = (data[3] << 8) | data[4];
232     n_headers = data[5];
233     size -= 6;
234     data += 6;
235     offset += 6;
236 
237     GST_DEBUG_OBJECT (rtpvorbisdepay,
238         "header %d, ident 0x%08x, length %u, left %" G_GSIZE_FORMAT, i, ident,
239         length, size);
240 
241     /* FIXME check if we already got this ident */
242 
243     /* length might also include count of following size fields */
244     if (size < length && size + 1 != length)
245       goto too_small;
246 
247     if (gst_rtp_vorbis_depay_has_ident (rtpvorbisdepay, ident)) {
248       size -= length;
249       data += length;
250       offset += length;
251       continue;
252     }
253 
254     /* read header sizes we read 2 sizes, the third size (for which we allocate
255      * space) must be derived from the total packed header length. */
256     h_sizes = g_newa (guint, n_headers + 1);
257     for (j = 0; j < n_headers; j++) {
258       guint h_size;
259 
260       h_size = 0;
261       do {
262         if (size < 1)
263           goto too_small;
264         b = *data++;
265         offset++;
266         extra++;
267         size--;
268         h_size = (h_size << 7) | (b & 0x7f);
269       } while (b & 0x80);
270       GST_DEBUG_OBJECT (rtpvorbisdepay, "headers %d: size: %u", j, h_size);
271 
272       if (length < h_size)
273         goto too_small;
274 
275       h_sizes[j] = h_size;
276       length -= h_size;
277     }
278     /* last header length is the remaining space */
279     GST_DEBUG_OBJECT (rtpvorbisdepay, "last header size: %u", length);
280     h_sizes[j] = length;
281 
282     GST_DEBUG_OBJECT (rtpvorbisdepay, "preparing headers");
283     conf = g_new0 (GstRtpVorbisConfig, 1);
284     conf->ident = ident;
285 
286     for (j = 0; j <= n_headers; j++) {
287       guint h_size;
288 
289       h_size = h_sizes[j];
290       if (size < h_size) {
291         if (j != n_headers || size + extra != h_size) {
292           free_config (conf);
293           goto too_small;
294         } else {
295           /* otherwise means that overall length field contained total length,
296            * including extra fields */
297           h_size -= extra;
298         }
299       }
300 
301       GST_DEBUG_OBJECT (rtpvorbisdepay, "reading header %d, size %u", j,
302           h_size);
303 
304       buf = gst_buffer_copy_region (confbuf, GST_BUFFER_COPY_ALL, offset,
305           h_size);
306       conf->headers = g_list_append (conf->headers, buf);
307       offset += h_size;
308       size -= h_size;
309     }
310     rtpvorbisdepay->configs = g_list_append (rtpvorbisdepay->configs, conf);
311   }
312 
313   gst_buffer_unmap (confbuf, &map);
314   gst_buffer_unref (confbuf);
315 
316   return TRUE;
317 
318   /* ERRORS */
319 too_small:
320   {
321     GST_DEBUG_OBJECT (rtpvorbisdepay, "configuration too small");
322     gst_buffer_unmap (confbuf, &map);
323     gst_buffer_unref (confbuf);
324     return FALSE;
325   }
326 }
327 
328 static gboolean
gst_rtp_vorbis_depay_parse_inband_configuration(GstRtpVorbisDepay * rtpvorbisdepay,guint ident,guint8 * configuration,guint size,guint length)329 gst_rtp_vorbis_depay_parse_inband_configuration (GstRtpVorbisDepay *
330     rtpvorbisdepay, guint ident, guint8 * configuration, guint size,
331     guint length)
332 {
333   GstBuffer *confbuf;
334   GstMapInfo map;
335 
336   if (G_UNLIKELY (size < 4))
337     return FALSE;
338 
339   /* transform inline to out-of-band and parse that one */
340   confbuf = gst_buffer_new_and_alloc (size + 9);
341   gst_buffer_map (confbuf, &map, GST_MAP_WRITE);
342   /* 1 header */
343   GST_WRITE_UINT32_BE (map.data, 1);
344   /* write Ident */
345   GST_WRITE_UINT24_BE (map.data + 4, ident);
346   /* write sort-of-length */
347   GST_WRITE_UINT16_BE (map.data + 7, length);
348   /* copy remainder */
349   memcpy (map.data + 9, configuration, size);
350   gst_buffer_unmap (confbuf, &map);
351 
352   return gst_rtp_vorbis_depay_parse_configuration (rtpvorbisdepay, confbuf);
353 }
354 
355 static gboolean
gst_rtp_vorbis_depay_setcaps(GstRTPBaseDepayload * depayload,GstCaps * caps)356 gst_rtp_vorbis_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
357 {
358   GstStructure *structure;
359   GstRtpVorbisDepay *rtpvorbisdepay;
360   GstCaps *srccaps;
361   const gchar *configuration;
362   gint clock_rate;
363   gboolean res;
364 
365   rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (depayload);
366 
367   structure = gst_caps_get_structure (caps, 0);
368 
369   /* get clockrate */
370   if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
371     goto no_rate;
372 
373   /* read and parse configuration string */
374   configuration = gst_structure_get_string (structure, "configuration");
375   if (configuration) {
376     GstBuffer *confbuf;
377     guint8 *data;
378     gsize size;
379 
380     /* deserialize base64 to buffer */
381     data = g_base64_decode (configuration, &size);
382 
383     confbuf = gst_buffer_new ();
384     gst_buffer_append_memory (confbuf,
385         gst_memory_new_wrapped (0, data, size, 0, size, data, g_free));
386     if (!gst_rtp_vorbis_depay_parse_configuration (rtpvorbisdepay, confbuf))
387       goto invalid_configuration;
388   } else {
389     GST_WARNING_OBJECT (rtpvorbisdepay, "no configuration specified");
390   }
391 
392   /* caps seem good, configure element */
393   depayload->clock_rate = clock_rate;
394 
395   /* set caps on pad and on header */
396   srccaps = gst_caps_new_empty_simple ("audio/x-vorbis");
397   res = gst_pad_set_caps (depayload->srcpad, srccaps);
398   gst_caps_unref (srccaps);
399 
400   return res;
401 
402   /* ERRORS */
403 invalid_configuration:
404   {
405     GST_ERROR_OBJECT (rtpvorbisdepay, "invalid configuration specified");
406     return FALSE;
407   }
408 no_rate:
409   {
410     GST_ERROR_OBJECT (rtpvorbisdepay, "no clock-rate specified");
411     return FALSE;
412   }
413 }
414 
415 static gboolean
gst_rtp_vorbis_depay_switch_codebook(GstRtpVorbisDepay * rtpvorbisdepay,guint32 ident)416 gst_rtp_vorbis_depay_switch_codebook (GstRtpVorbisDepay * rtpvorbisdepay,
417     guint32 ident)
418 {
419   GList *walk;
420   gboolean res = FALSE;
421 
422   GST_DEBUG_OBJECT (rtpvorbisdepay, "Looking up code book ident 0x%08x", ident);
423   for (walk = rtpvorbisdepay->configs; walk; walk = g_list_next (walk)) {
424     GstRtpVorbisConfig *conf = (GstRtpVorbisConfig *) walk->data;
425 
426     if (conf->ident == ident) {
427       GList *headers;
428 
429       /* FIXME, remove pads, create new pad.. */
430 
431       /* push out all the headers */
432       for (headers = conf->headers; headers; headers = g_list_next (headers)) {
433         GstBuffer *header = GST_BUFFER_CAST (headers->data);
434 
435         gst_buffer_ref (header);
436         gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpvorbisdepay),
437             header);
438       }
439       /* remember the current config */
440       rtpvorbisdepay->config = conf;
441       res = TRUE;
442     }
443   }
444   if (!res) {
445     /* we don't know about the headers, figure out an alternative method for
446      * getting the codebooks. FIXME, fail for now. */
447   }
448   return res;
449 }
450 
451 static GstBuffer *
gst_rtp_vorbis_depay_process(GstRTPBaseDepayload * depayload,GstRTPBuffer * rtp)452 gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
453     GstRTPBuffer * rtp)
454 {
455   GstRtpVorbisDepay *rtpvorbisdepay;
456   GstBuffer *outbuf;
457   GstFlowReturn ret;
458   gint payload_len;
459   GstBuffer *payload_buffer = NULL;
460   guint8 *payload;
461   GstMapInfo map;
462   guint32 header, ident;
463   guint8 F, VDT, packets;
464   guint length;
465 
466   rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (depayload);
467 
468   payload_len = gst_rtp_buffer_get_payload_len (rtp);
469 
470   GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len);
471 
472   /* we need at least 4 bytes for the packet header */
473   if (G_UNLIKELY (payload_len < 4))
474     goto packet_short;
475 
476   payload = gst_rtp_buffer_get_payload (rtp);
477   header = GST_READ_UINT32_BE (payload);
478   /*
479    *  0                   1                   2                   3
480    *  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
481    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
482    * |                     Ident                     | F |VDT|# pkts.|
483    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
484    *
485    * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
486    * VDT: Vorbis data type (0=vorbis, 1=config, 2=comment, 3=reserved)
487    * pkts: number of packets.
488    */
489   VDT = (header & 0x30) >> 4;
490   if (G_UNLIKELY (VDT == 3))
491     goto ignore_reserved;
492 
493   GST_DEBUG_OBJECT (depayload, "header: 0x%08x", header);
494   ident = (header >> 8) & 0xffffff;
495   F = (header & 0xc0) >> 6;
496   packets = (header & 0xf);
497 
498   if (VDT == 0) {
499     gboolean do_switch = FALSE;
500 
501     /* we have a raw payload, find the codebook for the ident */
502     if (!rtpvorbisdepay->config) {
503       /* we don't have an active codebook, find the codebook and
504        * activate it */
505       GST_DEBUG_OBJECT (rtpvorbisdepay, "No active codebook, switching");
506       do_switch = TRUE;
507     } else if (rtpvorbisdepay->config->ident != ident) {
508       /* codebook changed */
509       GST_DEBUG_OBJECT (rtpvorbisdepay, "codebook changed, switching");
510       do_switch = TRUE;
511     }
512     if (do_switch) {
513       if (!gst_rtp_vorbis_depay_switch_codebook (rtpvorbisdepay, ident))
514         goto switch_failed;
515     }
516   }
517 
518   GST_DEBUG_OBJECT (depayload, "ident: %u, F: %d, VDT: %d, packets: %d", ident,
519       F, VDT, packets);
520 
521   /* fragmented packets, assemble */
522   if (F != 0) {
523     GstBuffer *vdata;
524 
525     if (F == 1) {
526       /* if we start a packet, clear adapter and start assembling. */
527       gst_adapter_clear (rtpvorbisdepay->adapter);
528       GST_DEBUG_OBJECT (depayload, "start assemble");
529       rtpvorbisdepay->assembling = TRUE;
530     }
531 
532     if (!rtpvorbisdepay->assembling)
533       goto no_output;
534 
535     /* skip header and length. */
536     vdata = gst_rtp_buffer_get_payload_subbuffer (rtp, 6, -1);
537 
538     GST_DEBUG_OBJECT (depayload, "assemble vorbis packet");
539     gst_adapter_push (rtpvorbisdepay->adapter, vdata);
540 
541     /* packet is not complete, we are done */
542     if (F != 3)
543       goto no_output;
544 
545     /* construct assembled buffer */
546     length = gst_adapter_available (rtpvorbisdepay->adapter);
547     payload_buffer = gst_adapter_take_buffer (rtpvorbisdepay->adapter, length);
548   } else {
549     payload_buffer = gst_rtp_buffer_get_payload_subbuffer (rtp, 4, -1);
550     length = 0;
551   }
552 
553   GST_DEBUG_OBJECT (depayload, "assemble done");
554 
555   gst_buffer_map (payload_buffer, &map, GST_MAP_READ);
556   payload = map.data;
557   payload_len = map.size;
558 
559   /* we not assembling anymore now */
560   rtpvorbisdepay->assembling = FALSE;
561   gst_adapter_clear (rtpvorbisdepay->adapter);
562 
563   /* payload now points to a length with that many vorbis data bytes.
564    * Iterate over the packets and send them out.
565    *
566    *  0                   1                   2                   3
567    *  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
568    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
569    * |             length            |          vorbis data         ..
570    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
571    * ..                        vorbis data                           |
572    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
573    * |            length             |   next vorbis packet data    ..
574    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
575    * ..                        vorbis data                           |
576    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*
577    */
578   while (payload_len > 2) {
579     /* If length is not 0, we have a reassembled packet for which we
580      * calculated the length already and don't have to skip over the
581      * length field anymore
582      */
583     if (length == 0) {
584       length = GST_READ_UINT16_BE (payload);
585       payload += 2;
586       payload_len -= 2;
587     }
588 
589     GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length,
590         payload_len);
591 
592     /* skip packet if something odd happens */
593     if (G_UNLIKELY (length > payload_len))
594       goto length_short;
595 
596     /* handle in-band configuration */
597     if (G_UNLIKELY (VDT == 1)) {
598       GST_DEBUG_OBJECT (rtpvorbisdepay, "in-band configuration");
599       if (!gst_rtp_vorbis_depay_parse_inband_configuration (rtpvorbisdepay,
600               ident, payload, payload_len, length))
601         goto invalid_configuration;
602       goto no_output;
603     }
604 
605     /* create buffer for packet */
606     outbuf =
607         gst_buffer_copy_region (payload_buffer, GST_BUFFER_COPY_ALL,
608         payload - map.data, length);
609 
610     payload += length;
611     payload_len -= length;
612     /* make sure to read next length */
613     length = 0;
614 
615     ret = gst_rtp_base_depayload_push (depayload, outbuf);
616     if (ret != GST_FLOW_OK)
617       break;
618   }
619 
620   gst_buffer_unmap (payload_buffer, &map);
621   gst_buffer_unref (payload_buffer);
622 
623   return NULL;
624 
625 no_output:
626   {
627     if (payload_buffer) {
628       gst_buffer_unmap (payload_buffer, &map);
629       gst_buffer_unref (payload_buffer);
630     }
631     return NULL;
632   }
633   /* ERORRS */
634 switch_failed:
635   {
636     GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE,
637         (NULL), ("Could not switch codebooks"));
638     return NULL;
639   }
640 packet_short:
641   {
642     GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE,
643         (NULL), ("Packet was too short (%d < 4)", payload_len));
644     return NULL;
645   }
646 ignore_reserved:
647   {
648     GST_WARNING_OBJECT (rtpvorbisdepay, "reserved VDT ignored");
649     return NULL;
650   }
651 length_short:
652   {
653     GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE,
654         (NULL), ("Packet contains invalid data"));
655     if (payload_buffer) {
656       gst_buffer_unmap (payload_buffer, &map);
657       gst_buffer_unref (payload_buffer);
658     }
659     return NULL;
660   }
661 invalid_configuration:
662   {
663     /* fatal, as we otherwise risk carrying on without output */
664     GST_ELEMENT_ERROR (rtpvorbisdepay, STREAM, DECODE,
665         (NULL), ("Packet contains invalid configuration"));
666     if (payload_buffer) {
667       gst_buffer_unmap (payload_buffer, &map);
668       gst_buffer_unref (payload_buffer);
669     }
670     return NULL;
671   }
672 }
673 
674 static GstStateChangeReturn
gst_rtp_vorbis_depay_change_state(GstElement * element,GstStateChange transition)675 gst_rtp_vorbis_depay_change_state (GstElement * element,
676     GstStateChange transition)
677 {
678   GstRtpVorbisDepay *rtpvorbisdepay;
679   GstStateChangeReturn ret;
680 
681   rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (element);
682 
683   switch (transition) {
684     case GST_STATE_CHANGE_NULL_TO_READY:
685       break;
686     case GST_STATE_CHANGE_READY_TO_PAUSED:
687       break;
688     default:
689       break;
690   }
691 
692   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
693 
694   switch (transition) {
695     case GST_STATE_CHANGE_PAUSED_TO_READY:
696       free_indents (rtpvorbisdepay);
697       break;
698     case GST_STATE_CHANGE_READY_TO_NULL:
699       break;
700     default:
701       break;
702   }
703   return ret;
704 }
705 
706 gboolean
gst_rtp_vorbis_depay_plugin_init(GstPlugin * plugin)707 gst_rtp_vorbis_depay_plugin_init (GstPlugin * plugin)
708 {
709   return gst_element_register (plugin, "rtpvorbisdepay",
710       GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_DEPAY);
711 }
712