34917ab63770cf4b1fb43d2e13301576396ea73e
[vlc.git] / modules / demux / xiph.h
1 /*****************************************************************************
2  * xiph.h: Xiph helpers
3  *****************************************************************************
4  * Copyright (C) 2010 Laurent Aimar
5  *
6  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #include <assert.h>
24 #include <limits.h>
25 #define XIPH_MAX_HEADER_COUNT (256)
26
27 /* Temp ffmpeg vorbis format */
28 static inline bool xiph_IsLavcFormat(const void *extra, unsigned i_extra)
29 {
30     return (i_extra >= 6 && GetWBE(extra) == 30);
31 }
32
33 static inline unsigned xiph_CountLavcHeaders(const void *p_extra, unsigned i_extra)
34 {
35     const uint8_t *p = (const uint8_t*) p_extra;
36     const uint8_t *p_end = &p[i_extra];
37     /* Check headers count */
38     for (int i=0; i<3; i++)
39     {
40         if(p_end - p < 2)
41             return 0;
42         uint16_t i_size = GetWBE(p);
43         if(&p[2U + i_size] > p_end)
44             return 0;
45         p += 2 + i_size;
46     }
47     return 3;
48 }
49
50 static inline unsigned xiph_CountHeaders(const void *p_extra, unsigned i_extra)
51 {
52     const uint8_t *p = (const uint8_t*) p_extra;
53     if (!i_extra)
54         return 0;
55     /* First byte is headers count */
56     if(1U + *p > i_extra)
57         return 0;
58     return *p + 1;
59 }
60
61 static inline unsigned xiph_CountUnknownHeaders(const void *p_extra, unsigned i_extra)
62 {
63     if (xiph_IsLavcFormat(p_extra, i_extra))
64         return xiph_CountLavcHeaders(p_extra, i_extra);
65     else
66         return xiph_CountHeaders(p_extra, i_extra);
67 }
68
69 static inline int xiph_SplitLavcHeaders(unsigned packet_size[],
70                                         const void *packet[], unsigned *packet_count,
71                                         unsigned i_extra, const void *p_extra)
72 {
73     const uint8_t *current = (const uint8_t *)p_extra;
74     const uint8_t *end = &current[i_extra];
75     if (i_extra < 2)
76         return VLC_EGENERIC;
77     /* Parse the packet count and their sizes */
78     const unsigned count = xiph_CountLavcHeaders(current, i_extra);
79     if(count == 0)
80         return VLC_EGENERIC;
81     if (packet_count)
82         *packet_count = count;
83     /* count is trusted here (xiph_CountHeaders) */
84     for (unsigned i=0; i < count; i++)
85     {
86         /* each payload is prefixed by word size */
87         packet_size[i] = GetWBE(current);
88         if(&current[2U + packet_size[i]] > end)
89             return VLC_EGENERIC;
90         packet[i] = current + 2;
91         current += packet_size[i] + 2;
92     }
93     return VLC_SUCCESS;
94 }
95
96 static inline int xiph_SplitHeaders(unsigned packet_size[],
97                                     const void *packet[], unsigned *packet_count,
98                                     unsigned i_extra, const void *p_extra)
99 {
100     const uint8_t *current = (const uint8_t *)p_extra;
101     const uint8_t *end = &current[i_extra];
102     if (i_extra < 1)
103         return VLC_EGENERIC;
104
105     /* Parse the packet count and their sizes */
106     const unsigned count = xiph_CountHeaders(current, i_extra);
107     if(count == 0)
108         return VLC_EGENERIC;
109
110     if (packet_count)
111         *packet_count = count;
112
113     /* - 1 byte (N-1) packets
114      * - N-1 variable length payload sizes
115      * - N-1 payloads
116      * - Nth packet (remaining)  */
117
118     /* skip count byte header */
119     ++current;
120     /* read sizes for N-1 packets */
121     unsigned total_payload_minus_last = 0;
122     for (unsigned i = 0; i < count - 1; i++)
123     {
124         packet_size[i] = 0;
125         for (;;) {
126             if (current >= end)
127                 return VLC_EGENERIC;
128             packet_size[i] += *current;
129             if (*current++ != 255)
130                 break;
131         }
132         if(UINT_MAX - total_payload_minus_last < packet_size[i])
133             return VLC_EGENERIC;
134         total_payload_minus_last += packet_size[i];
135     }
136     if(current + total_payload_minus_last > end)
137         return VLC_EGENERIC;
138     /* set pointers for N-1 packets */
139     for (unsigned i = 0; i < count - 1; i++)
140     {
141         packet[i] = current;
142         current += packet_size[i];
143     }
144     /* Last packet is remaining size */
145     packet_size[count - 1] = end - current;
146     packet[count - 1] = current;
147
148     return VLC_SUCCESS;
149 }
150
151 static inline int xiph_PackHeaders(int *extra_size, void **extra,
152                                    unsigned packet_size[],
153                                    const void *const packet[],
154                                    unsigned packet_count)
155 {
156     if (packet_count <= 0 || packet_count > XIPH_MAX_HEADER_COUNT)
157         return VLC_EGENERIC;
158
159     /* Compute the size needed for the whole extra data */
160     unsigned payload_size = 0;
161     unsigned header_size = 1;
162     for (unsigned i = 0; i < packet_count; i++) {
163         payload_size += packet_size[i];
164         if (i < packet_count - 1)
165             header_size += 1 + packet_size[i] / 255;
166     }
167
168     /* */
169     *extra_size = header_size + payload_size;
170     *extra = malloc(*extra_size);
171     if (*extra == NULL)
172         return VLC_ENOMEM;
173
174     /* Write the header */
175     uint8_t *current = (uint8_t*)*extra;
176     *current++ = packet_count - 1;
177     for (unsigned i = 0; i < packet_count - 1; i++) {
178         unsigned t = packet_size[i];
179         for (;;) {
180             if (t >= 255) {
181                 *current++ = 255;
182                 t -= 255;
183             } else {
184                 *current++ = t;
185                 break;
186             }
187         }
188     }
189
190     /* Copy the payloads */
191     for (unsigned i = 0; i < packet_count; i++) {
192         if (packet_size[i] > 0) {
193             memcpy(current, packet[i], packet_size[i]);
194             current += packet_size[i];
195         }
196     }
197     assert(current == (uint8_t*)*extra + *extra_size);
198     return VLC_SUCCESS;
199 }
200
201 static inline int xiph_AppendHeaders(int *extra_size, void **extra,
202                                      unsigned size, const void *data)
203 {
204     unsigned packet_size[XIPH_MAX_HEADER_COUNT];
205     const void *packet[XIPH_MAX_HEADER_COUNT];
206     unsigned count;
207
208     if (*extra_size > 0 && *extra) {
209         if (xiph_SplitHeaders(packet_size, packet, &count, *extra_size, *extra))
210             return VLC_EGENERIC;
211     } else {
212         count = 0;
213     }
214     if (count >= XIPH_MAX_HEADER_COUNT)
215         return VLC_EGENERIC;
216
217     void *old = *extra;
218
219     packet_size[count] = size;
220     packet[count]      = (void*)data;
221     if (xiph_PackHeaders(extra_size, extra, packet_size,
222                          packet, count + 1)) {
223         *extra_size = 0;
224         *extra      = NULL;
225     }
226
227     free(old);
228
229     if (*extra_size <= 0)
230         return VLC_EGENERIC;
231     return VLC_SUCCESS;
232 }