demux: xiph: improve old ffmpeg xiph extradata test
[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                                      vlc_fourcc_t i_codec)
30 {
31     switch(i_codec)
32     {
33         case VLC_CODEC_VORBIS:
34             return i_extra >= 6 && GetWBE(extra) == 30;
35         case VLC_CODEC_THEORA:
36             return i_extra >= 6 && GetWBE(extra) == 42;
37         default:
38             return false;
39     }
40 }
41
42 static inline unsigned xiph_CountLavcHeaders(const void *p_extra, unsigned i_extra)
43 {
44     const uint8_t *p = (const uint8_t*) p_extra;
45     const uint8_t *p_end = &p[i_extra];
46     /* Check headers count */
47     for (int i=0; i<3; i++)
48     {
49         if(p_end - p < 2)
50             return 0;
51         uint16_t i_size = GetWBE(p);
52         if(&p[2U + i_size] > p_end)
53             return 0;
54         p += 2 + i_size;
55     }
56     return 3;
57 }
58
59 static inline unsigned xiph_CountHeaders(const void *p_extra, unsigned i_extra)
60 {
61     const uint8_t *p = (const uint8_t*) p_extra;
62     if (!i_extra)
63         return 0;
64     /* First byte is headers count */
65     if(1U + *p > i_extra)
66         return 0;
67     return *p + 1;
68 }
69
70 static inline unsigned xiph_CountUnknownHeaders(const void *p_extra, unsigned i_extra,
71                                                 vlc_fourcc_t i_codec)
72 {
73     if (xiph_IsLavcFormat(p_extra, i_extra, i_codec))
74         return xiph_CountLavcHeaders(p_extra, i_extra);
75     else
76         return xiph_CountHeaders(p_extra, i_extra);
77 }
78
79 static inline int xiph_SplitLavcHeaders(unsigned packet_size[],
80                                         const void *packet[], unsigned *packet_count,
81                                         unsigned i_extra, const void *p_extra)
82 {
83     const uint8_t *current = (const uint8_t *)p_extra;
84     const uint8_t *end = &current[i_extra];
85     if (i_extra < 2)
86         return VLC_EGENERIC;
87     /* Parse the packet count and their sizes */
88     const unsigned count = xiph_CountLavcHeaders(current, i_extra);
89     if(count == 0)
90         return VLC_EGENERIC;
91     if (packet_count)
92         *packet_count = count;
93     /* count is trusted here (xiph_CountHeaders) */
94     for (unsigned i=0; i < count; i++)
95     {
96         /* each payload is prefixed by word size */
97         packet_size[i] = GetWBE(current);
98         if(&current[2U + packet_size[i]] > end)
99             return VLC_EGENERIC;
100         packet[i] = current + 2;
101         current += packet_size[i] + 2;
102     }
103     return VLC_SUCCESS;
104 }
105
106 static inline int xiph_SplitHeaders(unsigned packet_size[],
107                                     const void *packet[], unsigned *packet_count,
108                                     unsigned i_extra, const void *p_extra)
109 {
110     const uint8_t *current = (const uint8_t *)p_extra;
111     const uint8_t *end = &current[i_extra];
112     if (i_extra < 1)
113         return VLC_EGENERIC;
114
115     /* Parse the packet count and their sizes */
116     const unsigned count = xiph_CountHeaders(current, i_extra);
117     if(count == 0)
118         return VLC_EGENERIC;
119
120     if (packet_count)
121         *packet_count = count;
122
123     /* - 1 byte (N-1) packets
124      * - N-1 variable length payload sizes
125      * - N-1 payloads
126      * - Nth packet (remaining)  */
127
128     /* skip count byte header */
129     ++current;
130     /* read sizes for N-1 packets */
131     unsigned total_payload_minus_last = 0;
132     for (unsigned i = 0; i < count - 1; i++)
133     {
134         packet_size[i] = 0;
135         for (;;) {
136             if (current >= end)
137                 return VLC_EGENERIC;
138             packet_size[i] += *current;
139             if (*current++ != 255)
140                 break;
141         }
142         if(UINT_MAX - total_payload_minus_last < packet_size[i])
143             return VLC_EGENERIC;
144         total_payload_minus_last += packet_size[i];
145     }
146     if(current + total_payload_minus_last > end)
147         return VLC_EGENERIC;
148     /* set pointers for N-1 packets */
149     for (unsigned i = 0; i < count - 1; i++)
150     {
151         packet[i] = current;
152         current += packet_size[i];
153     }
154     /* Last packet is remaining size */
155     packet_size[count - 1] = end - current;
156     packet[count - 1] = current;
157
158     return VLC_SUCCESS;
159 }
160
161 static inline int xiph_PackHeaders(int *extra_size, void **extra,
162                                    unsigned packet_size[],
163                                    const void *const packet[],
164                                    unsigned packet_count)
165 {
166     if (packet_count <= 0 || packet_count > XIPH_MAX_HEADER_COUNT)
167         return VLC_EGENERIC;
168
169     /* Compute the size needed for the whole extra data */
170     unsigned payload_size = 0;
171     unsigned header_size = 1;
172     for (unsigned i = 0; i < packet_count; i++) {
173         payload_size += packet_size[i];
174         if (i < packet_count - 1)
175             header_size += 1 + packet_size[i] / 255;
176     }
177
178     /* */
179     *extra_size = header_size + payload_size;
180     *extra = malloc(*extra_size);
181     if (*extra == NULL)
182         return VLC_ENOMEM;
183
184     /* Write the header */
185     uint8_t *current = (uint8_t*)*extra;
186     *current++ = packet_count - 1;
187     for (unsigned i = 0; i < packet_count - 1; i++) {
188         unsigned t = packet_size[i];
189         for (;;) {
190             if (t >= 255) {
191                 *current++ = 255;
192                 t -= 255;
193             } else {
194                 *current++ = t;
195                 break;
196             }
197         }
198     }
199
200     /* Copy the payloads */
201     for (unsigned i = 0; i < packet_count; i++) {
202         if (packet_size[i] > 0) {
203             memcpy(current, packet[i], packet_size[i]);
204             current += packet_size[i];
205         }
206     }
207     assert(current == (uint8_t*)*extra + *extra_size);
208     return VLC_SUCCESS;
209 }
210
211 static inline int xiph_AppendHeaders(int *extra_size, void **extra,
212                                      unsigned size, const void *data)
213 {
214     unsigned packet_size[XIPH_MAX_HEADER_COUNT];
215     const void *packet[XIPH_MAX_HEADER_COUNT];
216     unsigned count;
217
218     if (*extra_size > 0 && *extra) {
219         if (xiph_SplitHeaders(packet_size, packet, &count, *extra_size, *extra))
220             return VLC_EGENERIC;
221     } else {
222         count = 0;
223     }
224     if (count >= XIPH_MAX_HEADER_COUNT)
225         return VLC_EGENERIC;
226
227     void *old = *extra;
228
229     packet_size[count] = size;
230     packet[count]      = (void*)data;
231     if (xiph_PackHeaders(extra_size, extra, packet_size,
232                          packet, count + 1)) {
233         *extra_size = 0;
234         *extra      = NULL;
235     }
236
237     free(old);
238
239     if (*extra_size <= 0)
240         return VLC_EGENERIC;
241     return VLC_SUCCESS;
242 }