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