]> git.sesse.net Git - vlc/blob - modules/access/rtp/xiph.c
Update LGPL license blurb, choosing v2.1+.
[vlc] / modules / access / rtp / xiph.c
1 /**
2  * @file rtpxiph.c
3  * @brief Real-Time Protocol (RTP) Xiph payloads receival
4  */
5 /*****************************************************************************
6  * Copyright © 2008 Rémi Denis-Courmont
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26 #include <stdarg.h>
27 #include <assert.h>
28
29 #include <vlc_common.h>
30 #include <vlc_demux.h>
31 #include <vlc_aout.h>
32 #include <vlc_network.h>
33 #ifdef HAVE_POLL
34 # include <poll.h>
35 #endif
36 #include <vlc_plugin.h>
37
38 #include "../../demux/xiph.h"
39
40 #include "rtp.h"
41
42 /* PT=dynamic
43  * vorbis: Xiph Vorbis audio (draft-ietf-avt-rtp-vorbis-09, RFC FIXME)
44  */
45 typedef struct rtp_vorbis_t
46 {
47     es_out_id_t *id;
48     block_t     *block;
49     uint32_t     ident;
50 } rtp_vorbis_t;
51
52 static void *vorbis_init (demux_t *demux)
53 {
54     rtp_vorbis_t *self = malloc (sizeof (*self));
55
56     if (self == NULL)
57         return NULL;
58
59     self->id = NULL;
60     self->block = NULL;
61     self->ident = 0xffffffff; /* impossible value on the wire */
62     (void)demux;
63     return self;
64 }
65
66 static void vorbis_destroy (demux_t *demux, void *data)
67 {
68     rtp_vorbis_t *self = data;
69
70     if (!data)
71         return;
72     if (self->block)
73     {
74         self->block->i_flags |= BLOCK_FLAG_CORRUPTED;
75         codec_decode (demux, self->id, self->block);
76     }
77     codec_destroy (demux, self->id);
78     free (self);
79 }
80
81 /* Convert configuration from RTP to VLC format */
82 static ssize_t vorbis_header (void **pextra, const uint8_t *buf, size_t len)
83 {
84     /* Headers number */
85     if (len == 0)
86           return -1; /* Invalid */
87     unsigned hcount = 1 + *buf++;
88     len--;
89     if (hcount != 3)
90           return -1; /* Invalid */
91
92     /* Header lengths */
93     uint16_t idlen = 0, cmtlen = 0, setuplen = 0;
94     do
95     {
96         if (len == 0)
97             return -1;
98         idlen = (idlen << 7) | (*buf & 0x7f);
99         len--;
100     }
101     while (*buf++ & 0x80);
102     do
103     {
104         if (len == 0)
105             return -1;
106         cmtlen = (cmtlen << 7) | (*buf & 0x7f);
107         len--;
108     }
109     while (*buf++ & 0x80);
110     if (len < idlen + cmtlen)
111         return -1;
112     setuplen = len - (idlen + cmtlen);
113
114     /* Create the VLC extra format header */
115     unsigned sizes[3] = {
116         idlen, cmtlen, setuplen
117     };
118     void *payloads[3] = {
119         buf + 0,
120         buf + idlen,
121         buf + cmtlen
122     };
123     void *extra;
124     int  extra_size;
125     if (xiph_PackHeaders (&extra_size, &extra, sizes, payloads, 3))
126         return -1;;
127     *pextra = extra;
128     return extra_size;
129 }
130
131
132 static void vorbis_decode (demux_t *demux, void *data, block_t *block)
133 {
134     rtp_vorbis_t *self = data;
135
136     if (!data || block->i_buffer < 4)
137         goto drop;
138
139     /* 32-bits Vorbis RTP header (§2.2) */
140     uint32_t ident = GetDWBE (block->p_buffer);
141     block->i_buffer -= 4;
142     block->p_buffer += 4;
143
144     unsigned fragtype = (ident >> 6) & 3;
145     unsigned datatype = (ident >> 4) & 3;
146     unsigned pkts = (ident) & 15;
147     ident >>= 8;
148
149     /* Vorbis RTP defragmentation */
150     if (self->block && (block->i_flags & BLOCK_FLAG_DISCONTINUITY))
151     {   /* Screwed! discontinuity within a fragmented packet */
152         msg_Warn (demux, "discontinuity in fragmented Vorbis packet");
153         block_Release (self->block);
154         self->block = NULL;
155     }
156
157     if (fragtype <= 1)
158     {
159         if (self->block) /* Invalid first fragment */
160         {
161             block_Release (self->block);
162             self->block = NULL;
163         }
164     }
165     else
166     {
167         if (!self->block)
168             goto drop; /* Invalid non-first fragment */
169     }
170
171     if (fragtype > 0)
172     {   /* Fragment */
173         if (pkts > 0 || block->i_buffer < 2)
174             goto drop;
175
176         size_t fraglen = GetWBE (block->p_buffer);
177         if (block->i_buffer < (fraglen + 2))
178             goto drop; /* Invalid payload length */
179         block->i_buffer = fraglen;
180         if (fragtype == 1)/* Keep first fragment */
181         {
182             block->i_buffer += 2;
183             self->block = block;
184         }
185         else
186         {   /* Append non-first fragment */
187             size_t len = self->block->i_buffer;
188             self->block = block_Realloc (self->block, 0, len + fraglen);
189             if (!self->block)
190             {
191                 block_Release (block);
192                 return;
193             }
194             memcpy (self->block->p_buffer + len, block->p_buffer + 2,
195                     fraglen);
196             block_Release (block);
197         }
198         if (fragtype < 3)
199             return; /* Non-last fragment */
200
201         /* Last fragment reached, process it */
202         block = self->block;
203         self->block = NULL;
204         SetWBE (block->p_buffer, block->i_buffer - 2);
205         pkts = 1;
206     }
207
208     /* Vorbis RTP payload packets processing */
209     while (pkts > 0)
210     {
211         if (block->i_buffer < 2)
212             goto drop;
213
214         size_t len = GetWBE (block->p_buffer);
215         block->i_buffer -= 2;
216         block->p_buffer += 2;
217         if (block->i_buffer < len)
218             goto drop;
219
220         switch (datatype)
221         {
222             case 0: /* Raw audio frame */
223             {
224                 if (self->ident != ident)
225                     break; /* Ignore raw without configuration */
226                 block_t *raw = block_Alloc (len);
227                 memcpy (raw->p_buffer, block->p_buffer, len);
228                 raw->i_pts = block->i_pts; /* FIXME: what about pkts > 1 */
229                 codec_decode (demux, self->id, raw);
230                 break;
231             }
232
233             case 1: /* Packed configuration frame (§3.1.1) */
234             {
235                 if (self->ident == ident)
236                     break; /* Ignore config retransmission */
237
238                 void *extv;
239                 ssize_t extc = vorbis_header (&extv, block->p_buffer, len);
240                 if (extc < 0)
241                     break;
242
243                 es_format_t fmt;
244                 es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_VORBIS);
245                 fmt.p_extra = extv;
246                 fmt.i_extra = extc;
247                 codec_destroy (demux, self->id);
248                 msg_Dbg (demux, "Vorbis packed configuration received "
249                          "(%06"PRIx32")", ident);
250                 self->ident = ident;
251                 self->id = codec_init (demux, &fmt);
252                 break;
253             }
254         }
255
256         block->i_buffer -= len;
257         block->p_buffer += len;
258         pkts--;
259     }
260
261 drop:
262     block_Release (block);
263 }