/src/flac/src/libFLAC/ogg_decoder_aspect.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* libFLAC - Free Lossless Audio Codec |
2 | | * Copyright (C) 2002-2009 Josh Coalson |
3 | | * Copyright (C) 2011-2025 Xiph.Org Foundation |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions |
7 | | * are met: |
8 | | * |
9 | | * - Redistributions of source code must retain the above copyright |
10 | | * notice, this list of conditions and the following disclaimer. |
11 | | * |
12 | | * - Redistributions in binary form must reproduce the above copyright |
13 | | * notice, this list of conditions and the following disclaimer in the |
14 | | * documentation and/or other materials provided with the distribution. |
15 | | * |
16 | | * - Neither the name of the Xiph.org Foundation nor the names of its |
17 | | * contributors may be used to endorse or promote products derived from |
18 | | * this software without specific prior written permission. |
19 | | * |
20 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
21 | | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
22 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
23 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR |
24 | | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
25 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
26 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
27 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
28 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
29 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | | */ |
32 | | |
33 | | #ifdef HAVE_CONFIG_H |
34 | | # include <config.h> |
35 | | #endif |
36 | | |
37 | | #include <string.h> /* for memcpy() */ |
38 | | #include "FLAC/assert.h" |
39 | | #include "share/alloc.h" /* for free() */ |
40 | | #include "private/ogg_decoder_aspect.h" |
41 | | #include "private/ogg_mapping.h" |
42 | | #include "private/macros.h" |
43 | | |
44 | | static FLAC__OggDecoderAspectReadStatus read_more_data_(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspectReadCallbackProxy read_callback, size_t bytes_requested, const FLAC__StreamDecoder *decoder, void *client_data); |
45 | | |
46 | | /*********************************************************************** |
47 | | * |
48 | | * Public class methods |
49 | | * |
50 | | ***********************************************************************/ |
51 | | |
52 | | static FLAC__OggDecoderAspectReadStatus read_more_data_(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspectReadCallbackProxy read_callback, size_t bytes_requested, const FLAC__StreamDecoder *decoder, void *client_data) |
53 | 135k | { |
54 | 135k | static const size_t OGG_BYTES_CHUNK = 8192; |
55 | 135k | const size_t ogg_bytes_to_read = flac_max(bytes_requested, OGG_BYTES_CHUNK); |
56 | 135k | char *oggbuf = ogg_sync_buffer(&aspect->sync_state, ogg_bytes_to_read); |
57 | | |
58 | 135k | if(0 == oggbuf) { |
59 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; |
60 | 0 | } |
61 | 135k | else { |
62 | 135k | size_t ogg_bytes_read = ogg_bytes_to_read; |
63 | | |
64 | 135k | switch(read_callback(decoder, (FLAC__byte*)oggbuf, &ogg_bytes_read, client_data)) { |
65 | 105k | case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: |
66 | 105k | break; |
67 | 21.8k | case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: |
68 | 21.8k | aspect->end_of_stream = true; |
69 | 21.8k | break; |
70 | 7.93k | case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: |
71 | 7.93k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; |
72 | 0 | default: |
73 | 0 | FLAC__ASSERT(0); |
74 | 135k | } |
75 | | |
76 | 127k | if(ogg_sync_wrote(&aspect->sync_state, ogg_bytes_read) < 0) { |
77 | | /* double protection; this will happen if the read callback returns more bytes than the max requested, which would overflow Ogg's internal buffer */ |
78 | 0 | FLAC__ASSERT(0); |
79 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
80 | 0 | } |
81 | 127k | } |
82 | 127k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; |
83 | 135k | } |
84 | | |
85 | | static FLAC__OggDecoderAspectReadStatus process_page_(FLAC__OggDecoderAspect *aspect, FLAC__StreamDecoderTellCallback tell_callback, const FLAC__StreamDecoder *decoder, void *client_data) |
86 | 728k | { |
87 | | /* got a page, grab the serial number if necessary */ |
88 | 728k | if(aspect->need_serial_number) { |
89 | | /* Check whether not FLAC. The next if is somewhat confusing: check |
90 | | * whether the length of the next page body agrees with the length |
91 | | * of a FLAC 'header' possibly contained in that page */ |
92 | 692k | if(aspect->working_page.body_len > (long)(1 + FLAC__OGG_MAPPING_MAGIC_LENGTH) && |
93 | 692k | aspect->working_page.body[0] == FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE && |
94 | 692k | memcmp((&aspect->working_page.body) + 1, FLAC__OGG_MAPPING_MAGIC, FLAC__OGG_MAPPING_MAGIC_LENGTH)) { |
95 | 4.92k | aspect->bos_flag_seen = true; |
96 | 4.92k | aspect->serial_number = ogg_page_serialno(&aspect->working_page); |
97 | 4.92k | ogg_stream_reset_serialno(&aspect->stream_state, aspect->serial_number); |
98 | 4.92k | aspect->need_serial_number = false; |
99 | | |
100 | 4.92k | if(aspect->current_linknumber_advance_read >= aspect->number_of_links_detected) { |
101 | 1.97k | FLAC__uint64 tell_offset; |
102 | 1.97k | aspect->number_of_links_detected = aspect->current_linknumber_advance_read + 1; |
103 | 1.97k | aspect->linkdetails[aspect->current_linknumber_advance_read].serial_number = aspect->serial_number; |
104 | 1.97k | if(tell_callback != 0) { |
105 | 1.85k | if(tell_callback(decoder, &tell_offset, client_data) == FLAC__STREAM_DECODER_TELL_STATUS_OK) |
106 | 1.60k | aspect->linkdetails[aspect->current_linknumber_advance_read].start_byte = tell_offset - aspect->sync_state.fill + aspect->sync_state.returned |
107 | 1.60k | - aspect->working_page.header_len - aspect->working_page.body_len; |
108 | 1.85k | } |
109 | 1.97k | } |
110 | 4.92k | } |
111 | 692k | } |
112 | 728k | if(aspect->beginning_of_link) { |
113 | 673k | if(aspect->bos_flag_seen && !ogg_page_bos(&aspect->working_page)) { |
114 | | /* Page does not have BOS flag, which means we're done scanning for other serial numbers */ |
115 | 6.03k | aspect->beginning_of_link = false; |
116 | 6.03k | } |
117 | 673k | } |
118 | 728k | if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) { |
119 | 206k | aspect->have_working_page = true; |
120 | 206k | aspect->have_working_packet = false; |
121 | 206k | } |
122 | 522k | else if(aspect->beginning_of_link) { |
123 | | /* At the beginning of a link, store the serial numbers of all other streams, to make |
124 | | * finding the end of a link through seeking possible */ |
125 | 494k | if(ogg_page_bos(&aspect->working_page)) { |
126 | 487k | aspect->bos_flag_seen = true; |
127 | 487k | if(aspect->current_linknumber_advance_read >= aspect->number_of_links_indexed) { |
128 | 481k | FLAC__OggDecoderAspect_LinkDetails * current_link = &aspect->linkdetails[aspect->current_linknumber_advance_read]; |
129 | | /* Reallocate in chunks of 4 */ |
130 | 481k | if((current_link->number_of_other_streams) % 4 == 0) { |
131 | 121k | long * tmpptr = NULL; |
132 | 121k | if(NULL == (tmpptr = safe_realloc_nofree_mul_2op_(current_link->other_serial_numbers, 4+current_link->number_of_other_streams, sizeof(long)))) { |
133 | 72 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; |
134 | 72 | } |
135 | 121k | current_link->other_serial_numbers = tmpptr; |
136 | 121k | } |
137 | 481k | current_link->other_serial_numbers[current_link->number_of_other_streams] = ogg_page_serialno(&aspect->working_page); |
138 | 481k | current_link->number_of_other_streams++; |
139 | 481k | } |
140 | 487k | } |
141 | | /* No BOS flag seen yet, these pages might be still from previous link */ |
142 | 494k | } |
143 | | /* else do nothing, could be a page from another stream */ |
144 | 728k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; |
145 | 728k | } |
146 | | |
147 | | static FLAC__bool check_size_of_link_allocation_(FLAC__OggDecoderAspect *aspect) |
148 | 100k | { |
149 | | /* double on reallocating */ |
150 | 100k | if(aspect->current_linknumber >= aspect->number_of_links_allocated || aspect->current_linknumber_advance_read >= aspect->number_of_links_allocated) { |
151 | 953 | FLAC__OggDecoderAspect_LinkDetails * tmpptr = NULL; |
152 | 953 | if(NULL == (tmpptr = safe_realloc_nofree_mul_2op_(aspect->linkdetails,2*aspect->number_of_links_allocated,sizeof(FLAC__OggDecoderAspect_LinkDetails)))) { |
153 | 4 | return false; |
154 | 4 | } |
155 | 949 | aspect->linkdetails = tmpptr; |
156 | 949 | memset(aspect->linkdetails + aspect->number_of_links_allocated, 0, aspect->number_of_links_allocated * sizeof(FLAC__OggDecoderAspect_LinkDetails)); |
157 | 949 | aspect->number_of_links_allocated *= 2; |
158 | 949 | } |
159 | 100k | return true; |
160 | 100k | } |
161 | | |
162 | | FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect) |
163 | 10.9k | { |
164 | | /* we will determine the serial number later if necessary */ |
165 | 10.9k | if(ogg_stream_init(&aspect->stream_state, aspect->serial_number) != 0) |
166 | 0 | return false; |
167 | | |
168 | 10.9k | if(ogg_sync_init(&aspect->sync_state) != 0) |
169 | 0 | return false; |
170 | | |
171 | 10.9k | aspect->version_major = ~(0u); |
172 | 10.9k | aspect->version_minor = ~(0u); |
173 | | |
174 | 10.9k | aspect->need_serial_number = aspect->use_first_serial_number || aspect->decode_chained_stream; |
175 | | |
176 | 10.9k | aspect->end_of_stream = false; |
177 | 10.9k | aspect->have_working_page = false; |
178 | 10.9k | aspect->end_of_link = false; |
179 | | |
180 | 10.9k | aspect->current_linknumber = 0; |
181 | 10.9k | aspect->current_linknumber_advance_read = 0; |
182 | 10.9k | aspect->number_of_links_indexed = 0; |
183 | 10.9k | aspect->number_of_links_detected = 0; |
184 | 10.9k | aspect->number_of_links_allocated = 0; |
185 | | |
186 | 10.9k | if(NULL == (aspect->linkdetails = safe_realloc_mul_2op_(NULL,4,sizeof(FLAC__OggDecoderAspect_LinkDetails)))) |
187 | 6 | return false; |
188 | 10.9k | memset(aspect->linkdetails, 0, 4 * sizeof(FLAC__OggDecoderAspect_LinkDetails)); |
189 | | |
190 | 10.9k | aspect->number_of_links_allocated = 4; |
191 | | |
192 | 10.9k | return true; |
193 | 10.9k | } |
194 | | |
195 | | void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect) |
196 | 10.9k | { |
197 | 10.9k | uint32_t i; |
198 | 10.9k | (void)ogg_sync_clear(&aspect->sync_state); |
199 | 10.9k | (void)ogg_stream_clear(&aspect->stream_state); |
200 | 10.9k | if(NULL != aspect->linkdetails) { |
201 | 117k | for(i = 0; i < aspect->number_of_links_allocated; i++) |
202 | 106k | free(aspect->linkdetails[i].other_serial_numbers); |
203 | 10.9k | free(aspect->linkdetails); |
204 | 10.9k | } |
205 | 10.9k | aspect->linkdetails = NULL; |
206 | 10.9k | } |
207 | | |
208 | | void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value) |
209 | 249 | { |
210 | 249 | aspect->use_first_serial_number = false; |
211 | 249 | aspect->serial_number = value; |
212 | 249 | } |
213 | | |
214 | | void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect) |
215 | 310k | { |
216 | 310k | aspect->use_first_serial_number = true; |
217 | 310k | aspect->decode_chained_stream = false; |
218 | 310k | } |
219 | | |
220 | | void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect) |
221 | 86.6k | { |
222 | 86.6k | (void)ogg_stream_reset(&aspect->stream_state); |
223 | 86.6k | (void)ogg_sync_reset(&aspect->sync_state); |
224 | 86.6k | aspect->end_of_stream = false; |
225 | 86.6k | aspect->have_working_page = false; |
226 | 86.6k | aspect->end_of_link = false; |
227 | 86.6k | } |
228 | | |
229 | | void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect) |
230 | 29.7k | { |
231 | 29.7k | FLAC__ogg_decoder_aspect_flush(aspect); |
232 | 29.7k | aspect->current_linknumber = 0; |
233 | 29.7k | aspect->current_linknumber_advance_read = 0; |
234 | | |
235 | 29.7k | if(aspect->use_first_serial_number || aspect->decode_chained_stream) |
236 | 29.7k | aspect->need_serial_number = true; |
237 | | |
238 | 29.7k | aspect->beginning_of_link = true; |
239 | 29.7k | aspect->bos_flag_seen = false; |
240 | 29.7k | } |
241 | | |
242 | | void FLAC__ogg_decoder_aspect_next_link(FLAC__OggDecoderAspect* aspect) |
243 | 68.2k | { |
244 | 68.2k | aspect->end_of_link = false; |
245 | 68.2k | aspect->current_linknumber++; |
246 | 68.2k | aspect->beginning_of_link = true; |
247 | 68.2k | aspect->bos_flag_seen = false; |
248 | 68.2k | } |
249 | | |
250 | | void FLAC__ogg_decoder_aspect_set_decode_chained_stream(FLAC__OggDecoderAspect* aspect, FLAC__bool value) |
251 | 23.4k | { |
252 | 23.4k | aspect->decode_chained_stream = value; |
253 | 23.4k | } |
254 | | |
255 | | FLAC__bool FLAC__ogg_decoder_aspect_get_decode_chained_stream(FLAC__OggDecoderAspect* aspect) |
256 | 28.7k | { |
257 | 28.7k | return aspect->decode_chained_stream; |
258 | 28.7k | } |
259 | | |
260 | | FLAC__OggDecoderAspect_TargetLink * FLAC__ogg_decoder_aspect_get_target_link(FLAC__OggDecoderAspect* aspect, FLAC__uint64 target_sample) |
261 | 4.24k | { |
262 | | /* This returns the link containing the seek target if known. In |
263 | | * effect, this function always returns NULL if no links have been |
264 | | * indexed */ |
265 | | |
266 | 4.24k | uint32_t current_link = 0; |
267 | 4.24k | uint32_t total_samples = 0; |
268 | | |
269 | 1.38M | while(current_link < aspect->number_of_links_indexed) { |
270 | 1.38M | total_samples += aspect->linkdetails[current_link].samples; |
271 | 1.38M | if(target_sample < total_samples) { |
272 | 1.29k | aspect->target_link.serial_number = aspect->linkdetails[current_link].serial_number; |
273 | 1.29k | aspect->target_link.start_byte = aspect->linkdetails[current_link].start_byte; |
274 | 1.29k | aspect->target_link.samples_in_preceding_links = total_samples - aspect->linkdetails[current_link].samples; |
275 | 1.29k | aspect->target_link.end_byte = aspect->linkdetails[current_link].end_byte; |
276 | 1.29k | aspect->target_link.samples_this_link = aspect->linkdetails[current_link].samples; |
277 | 1.29k | aspect->target_link.linknumber = current_link; |
278 | 1.29k | return &aspect->target_link; |
279 | 1.29k | } |
280 | 1.38M | current_link++; |
281 | 1.38M | } |
282 | 2.94k | return NULL; |
283 | 4.24k | } |
284 | | |
285 | | void FLAC__ogg_decoder_aspect_set_seek_parameters(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspect_TargetLink *target_link) |
286 | 1.55k | { |
287 | 1.55k | if(target_link == 0) { |
288 | 254 | aspect->is_seeking = false; |
289 | 254 | } |
290 | 1.29k | else { |
291 | 1.29k | aspect->need_serial_number = false; |
292 | 1.29k | aspect->current_linknumber = target_link->linknumber; |
293 | 1.29k | aspect->current_linknumber_advance_read = target_link->linknumber; |
294 | 1.29k | aspect->serial_number = target_link->serial_number; |
295 | 1.29k | ogg_stream_reset_serialno(&aspect->stream_state, aspect->serial_number); |
296 | 1.29k | aspect->is_seeking = true; |
297 | 1.29k | } |
298 | 1.55k | } |
299 | | |
300 | | |
301 | | FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, FLAC__StreamDecoderTellCallback tell_callback, const FLAC__StreamDecoder *decoder, void *client_data) |
302 | 483k | { |
303 | 483k | const size_t bytes_requested = *bytes; |
304 | | |
305 | 483k | const uint32_t header_length = |
306 | 483k | FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + |
307 | 483k | FLAC__OGG_MAPPING_MAGIC_LENGTH + |
308 | 483k | FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + |
309 | 483k | FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH + |
310 | 483k | FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH; |
311 | | |
312 | | /* |
313 | | * The FLAC decoding API uses pull-based reads, whereas Ogg decoding |
314 | | * is push-based. In libFLAC, when you ask to decode a frame, the |
315 | | * decoder will eventually call the read callback to supply some data, |
316 | | * but how much it asks for depends on how much free space it has in |
317 | | * its internal buffer. It does not try to grow its internal buffer |
318 | | * to accommodate a whole frame because then the internal buffer size |
319 | | * could not be limited, which is necessary in embedded applications. |
320 | | * |
321 | | * Ogg however grows its internal buffer until a whole page is present; |
322 | | * only then can you get decoded data out. So we can't just ask for |
323 | | * the same number of bytes from Ogg, then pass what's decoded down to |
324 | | * libFLAC. If what libFLAC is asking for will not contain a whole |
325 | | * page, then we will get no data from ogg_sync_pageout(), and at the |
326 | | * same time cannot just read more data from the client for the purpose |
327 | | * of getting a whole decoded page because the decoded size might be |
328 | | * larger than libFLAC's internal buffer. |
329 | | * |
330 | | * Instead, whenever this read callback wrapper is called, we will |
331 | | * continually request data from the client until we have at least one |
332 | | * page, and manage pages internally so that we can send pieces of |
333 | | * pages down to libFLAC in such a way that we obey its size |
334 | | * requirement. To limit the amount of callbacks, we will always try |
335 | | * to read in enough pages to return the full number of bytes |
336 | | * requested. |
337 | | */ |
338 | 483k | *bytes = 0; |
339 | 4.13M | while (*bytes < bytes_requested && !aspect->end_of_stream) { |
340 | 4.09M | if (aspect->end_of_link && aspect->have_working_page) { |
341 | | /* we've now consumed all packets of this link and have checked that a new page follows it */ |
342 | 71.9k | if(*bytes > 0) |
343 | 3.08k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; |
344 | 68.8k | else |
345 | 68.8k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_LINK; |
346 | 71.9k | } |
347 | 4.02M | else if (aspect->have_working_page) { |
348 | 3.03M | if (aspect->have_working_packet) { |
349 | 1.41M | size_t n = bytes_requested - *bytes; |
350 | 1.41M | if ((size_t)aspect->working_packet.bytes <= n) { |
351 | | /* the rest of the packet will fit in the buffer */ |
352 | 1.41M | n = aspect->working_packet.bytes; |
353 | 1.41M | memcpy(buffer, aspect->working_packet.packet, n); |
354 | 1.41M | *bytes += n; |
355 | 1.41M | buffer += n; |
356 | 1.41M | aspect->have_working_packet = false; |
357 | 1.41M | if(aspect->working_packet.e_o_s) { |
358 | 77.6k | if(!aspect->decode_chained_stream) |
359 | 3.57k | aspect->end_of_stream = true; |
360 | 74.0k | else { |
361 | 74.0k | aspect->end_of_link = true; |
362 | 74.0k | aspect->current_linknumber_advance_read = aspect->current_linknumber + 1; |
363 | 74.0k | if(!check_size_of_link_allocation_(aspect)) |
364 | 3 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; |
365 | 74.0k | if(aspect->current_linknumber >= aspect->number_of_links_indexed) { |
366 | 45.8k | FLAC__uint64 tell_offset; |
367 | 45.8k | FLAC__ASSERT(aspect->current_linknumber == aspect->number_of_links_indexed); |
368 | 45.8k | aspect->linkdetails[aspect->current_linknumber].samples = aspect->working_packet.granulepos; |
369 | 45.8k | if(tell_callback != 0) { |
370 | 45.7k | if(tell_callback(decoder, &tell_offset, client_data) == FLAC__STREAM_DECODER_TELL_STATUS_OK) |
371 | 41.8k | aspect->linkdetails[aspect->current_linknumber].end_byte = tell_offset - aspect->sync_state.fill + aspect->sync_state.returned; |
372 | 45.7k | } |
373 | 45.8k | aspect->number_of_links_indexed++; |
374 | 45.8k | aspect->need_serial_number = true; |
375 | 45.8k | } |
376 | 74.0k | if(!aspect->is_seeking) |
377 | 61.4k | aspect->need_serial_number = true; |
378 | 74.0k | aspect->have_working_page = false; /* e-o-s packet ends page */ |
379 | 74.0k | } |
380 | 77.6k | } |
381 | 1.41M | } |
382 | 4.56k | else { |
383 | | /* only n bytes of the packet will fit in the buffer */ |
384 | 4.56k | memcpy(buffer, aspect->working_packet.packet, n); |
385 | 4.56k | *bytes += n; |
386 | 4.56k | buffer += n; |
387 | 4.56k | aspect->working_packet.packet += n; |
388 | 4.56k | aspect->working_packet.bytes -= n; |
389 | 4.56k | } |
390 | 1.41M | } |
391 | 1.61M | else { |
392 | | /* try and get another packet */ |
393 | 1.61M | const int ret = ogg_stream_packetout(&aspect->stream_state, &aspect->working_packet); |
394 | 1.61M | if (ret > 0) { |
395 | 1.41M | aspect->have_working_packet = true; |
396 | | /* if it is the first header packet, check for magic and a supported Ogg FLAC mapping version */ |
397 | 1.41M | if (aspect->working_packet.bytes > 0 && aspect->working_packet.packet[0] == FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE) { |
398 | 5.56k | const FLAC__byte *b = aspect->working_packet.packet; |
399 | 5.56k | if (aspect->working_packet.bytes < (long)header_length) |
400 | 529 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; |
401 | 5.03k | b += FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH; |
402 | 5.03k | if (memcmp(b, FLAC__OGG_MAPPING_MAGIC, FLAC__OGG_MAPPING_MAGIC_LENGTH)) |
403 | 256 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; |
404 | 4.78k | b += FLAC__OGG_MAPPING_MAGIC_LENGTH; |
405 | 4.78k | aspect->version_major = (uint32_t)(*b); |
406 | 4.78k | b += FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH; |
407 | 4.78k | aspect->version_minor = (uint32_t)(*b); |
408 | 4.78k | if (aspect->version_major != 1) |
409 | 505 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION; |
410 | 4.27k | aspect->working_packet.packet += header_length; |
411 | 4.27k | aspect->working_packet.bytes -= header_length; |
412 | 4.27k | } |
413 | 1.41M | } |
414 | 197k | else if (ret == 0) { |
415 | 73.8k | aspect->have_working_page = false; |
416 | 73.8k | } |
417 | 123k | else { /* ret < 0 */ |
418 | | /* lost sync, we'll leave the working page for the next call */ |
419 | 123k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; |
420 | 123k | } |
421 | 1.61M | } |
422 | 3.03M | } |
423 | 992k | else { |
424 | | /* try and get another page */ |
425 | 992k | const int ret = ogg_sync_pageout(&aspect->sync_state, &aspect->working_page); |
426 | 992k | if (ret > 0) { |
427 | 624k | FLAC__OggDecoderAspectReadStatus status = process_page_(aspect, tell_callback, decoder, client_data); |
428 | 624k | if(status != FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK) |
429 | 21 | return status; |
430 | 624k | } |
431 | 367k | else if (ret == 0) { |
432 | | /* need more data */ |
433 | 123k | FLAC__OggDecoderAspectReadStatus status = read_more_data_(aspect, read_callback, bytes_requested - *bytes, decoder, client_data); |
434 | 123k | if(status != FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK) |
435 | 7.93k | return status; |
436 | 123k | } |
437 | 244k | else { /* ret < 0 */ |
438 | | /* lost sync, bail out */ |
439 | 244k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; |
440 | 244k | } |
441 | 992k | } |
442 | 4.09M | } |
443 | | |
444 | 35.0k | if (aspect->end_of_stream && *bytes == 0) { |
445 | 21.8k | aspect->linkdetails[aspect->current_linknumber].is_last = true; |
446 | 21.8k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; |
447 | 21.8k | } |
448 | | |
449 | 13.2k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; |
450 | 35.0k | } |
451 | | |
452 | | FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_skip_link(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspectReadCallbackProxy read_callback, FLAC__StreamDecoderSeekCallback seek_callback, FLAC__StreamDecoderTellCallback tell_callback, FLAC__StreamDecoderLengthCallback length_callback, const FLAC__StreamDecoder *decoder, void *client_data) |
453 | 14.9k | { |
454 | 14.9k | if(seek_callback == NULL || tell_callback == NULL || length_callback == NULL) |
455 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_CALLBACKS_NONFUNCTIONAL; |
456 | | |
457 | | /* This extra check is here, because allocation failures while reading cannot always be |
458 | | * properly passed down the chain with the current API. So, instead, check again */ |
459 | 14.9k | if(!check_size_of_link_allocation_(aspect)) |
460 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; |
461 | | |
462 | 14.9k | if(aspect->current_linknumber < aspect->number_of_links_indexed) { |
463 | 11.1k | if(aspect->linkdetails[aspect->current_linknumber].is_last) { |
464 | | /* Seek to end of stream */ |
465 | 766 | FLAC__StreamDecoderLengthStatus lstatus; |
466 | 766 | FLAC__StreamDecoderSeekStatus sstatus; |
467 | 766 | uint64_t stream_length = 0; |
468 | | |
469 | 766 | lstatus = length_callback(decoder, &stream_length, client_data); |
470 | 766 | if(lstatus == FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) |
471 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_CALLBACKS_NONFUNCTIONAL; |
472 | 766 | if(lstatus == FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR) |
473 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
474 | | |
475 | 766 | sstatus = seek_callback(decoder, stream_length, client_data); |
476 | 766 | if(sstatus == FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED) |
477 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_CALLBACKS_NONFUNCTIONAL; |
478 | 766 | if(sstatus == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) |
479 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
480 | | |
481 | 766 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; |
482 | | |
483 | 766 | } |
484 | 10.4k | else { |
485 | 10.4k | FLAC__StreamDecoderSeekStatus status; |
486 | 10.4k | status = seek_callback(decoder, aspect->linkdetails[aspect->current_linknumber].end_byte,client_data); |
487 | 10.4k | if(status == FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED) |
488 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_CALLBACKS_NONFUNCTIONAL; |
489 | 10.4k | if(status == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) |
490 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
491 | 10.4k | FLAC__ogg_decoder_aspect_flush(aspect); |
492 | 10.4k | aspect->beginning_of_link = true; |
493 | 10.4k | aspect->need_serial_number = true; |
494 | 10.4k | aspect->bos_flag_seen = false; |
495 | 10.4k | aspect->current_linknumber++; |
496 | 10.4k | aspect->current_linknumber_advance_read = aspect->current_linknumber; |
497 | 10.4k | if(!check_size_of_link_allocation_(aspect)) |
498 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; |
499 | 10.4k | aspect->have_working_page = false; |
500 | 10.4k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; |
501 | 10.4k | } |
502 | 11.1k | } |
503 | 3.72k | else |
504 | 3.72k | { |
505 | | /* End of current link is unknown, go search for it */ |
506 | 3.72k | const uint32_t max_page_size = 65307; |
507 | 3.72k | uint64_t stream_length = 0; |
508 | 3.72k | uint64_t current_pos = 0; |
509 | 3.72k | uint64_t page_pos = 0; |
510 | 3.72k | uint64_t target_pos = 0; |
511 | 3.72k | uint64_t left_pos = 0; |
512 | 3.72k | uint64_t right_pos = 0; |
513 | 3.72k | FLAC__bool did_a_seek; |
514 | 3.72k | FLAC__bool seek_to_left_pos = false; |
515 | 3.72k | FLAC__bool keep_reading = false; |
516 | 3.72k | FLAC__bool find_bos_twice = aspect->need_serial_number; |
517 | 3.72k | int ret = 0; |
518 | | |
519 | 3.72k | { |
520 | 3.72k | FLAC__StreamDecoderLengthStatus lstatus; |
521 | 3.72k | FLAC__StreamDecoderTellStatus tstatus; |
522 | | |
523 | 3.72k | lstatus = length_callback(decoder, &stream_length, client_data); |
524 | 3.72k | if(lstatus == FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) |
525 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_CALLBACKS_NONFUNCTIONAL; |
526 | 3.72k | if(lstatus == FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR) |
527 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
528 | | |
529 | 3.72k | tstatus = tell_callback(decoder, ¤t_pos, client_data); |
530 | 3.72k | if(tstatus == FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED) |
531 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_CALLBACKS_NONFUNCTIONAL; |
532 | 3.72k | if(tstatus == FLAC__STREAM_DECODER_TELL_STATUS_ERROR) |
533 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
534 | 3.72k | } |
535 | | |
536 | | |
537 | 3.72k | current_pos = current_pos - aspect->sync_state.fill + aspect->sync_state.returned; |
538 | 3.72k | left_pos = current_pos; |
539 | 3.72k | right_pos = stream_length; |
540 | | |
541 | 118k | while(1){ |
542 | 118k | FLAC__bool seek_was_to_current_link = true; |
543 | | |
544 | 118k | if(right_pos <= left_pos || right_pos - left_pos < 9) { |
545 | | /* FLAC frame is at least 9 byte in size */ |
546 | 1.17k | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
547 | 1.17k | } |
548 | 117k | target_pos = left_pos + (right_pos - left_pos)/2; |
549 | | |
550 | 117k | if(keep_reading) { |
551 | | /* To not end up in a loop, we need to keep reading until are able to change left_pos */ |
552 | 2.69k | did_a_seek = false; |
553 | 2.69k | } |
554 | 115k | else if(current_pos < target_pos && current_pos + aspect->sync_state.fill - aspect->sync_state.returned > target_pos) { |
555 | | /* Target location is already in buffer, keep reading */ |
556 | 11.2k | did_a_seek = false; |
557 | 11.2k | } |
558 | 103k | else if(current_pos < target_pos && current_pos + max_page_size > target_pos) { |
559 | | /* Target is very close to current location, just reading is probably faster than seeking */ |
560 | 36.2k | did_a_seek = false; |
561 | 36.2k | } |
562 | 67.6k | else if(aspect->beginning_of_link) { |
563 | | /* Still need to save all serial numbers at start of stream, so don't seek yet */ |
564 | 65.7k | did_a_seek = false; |
565 | 65.7k | } |
566 | 1.86k | else { |
567 | 1.86k | if(seek_to_left_pos || target_pos - left_pos < max_page_size) { |
568 | | /* Seek to start of bisect area */ |
569 | 1.35k | target_pos = left_pos; |
570 | 1.35k | keep_reading = true; |
571 | 1.35k | seek_to_left_pos = false; |
572 | 1.35k | } |
573 | | /* Seek */ |
574 | 1.86k | if(seek_callback(decoder, target_pos, client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) |
575 | 0 | return false; |
576 | 1.86k | did_a_seek = true; |
577 | 1.86k | FLAC__ASSERT(tell_callback(decoder, ¤t_pos, client_data) == FLAC__STREAM_DECODER_TELL_STATUS_OK); |
578 | 1.86k | FLAC__ASSERT(current_pos == target_pos); |
579 | 1.86k | current_pos = target_pos; |
580 | 1.86k | (void)ogg_stream_reset(&aspect->stream_state); |
581 | 1.86k | (void)ogg_sync_reset(&aspect->sync_state); |
582 | 1.86k | } |
583 | | |
584 | | /* Get a page, resynchronize if necessary */ |
585 | 279k | while((ret = ogg_sync_pageseek(&aspect->sync_state, &aspect->working_page)) <= 0 && !aspect->end_of_stream) { |
586 | 161k | if(ret < 0) |
587 | 150k | current_pos -= ret; |
588 | 11.5k | else { |
589 | | /* need more data */ |
590 | 11.5k | FLAC__OggDecoderAspectReadStatus status = read_more_data_(aspect, read_callback, 0, decoder, client_data); |
591 | 11.5k | if(status != FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK) |
592 | 0 | return status; |
593 | 11.5k | } |
594 | 161k | } |
595 | | |
596 | 117k | page_pos = current_pos; |
597 | 117k | current_pos += aspect->working_page.header_len + aspect->working_page.body_len; |
598 | | |
599 | | /* Check whether the page serial number belongs to this link or another link */ |
600 | 117k | if (ret > 0) { |
601 | 115k | if(!aspect->beginning_of_link) { |
602 | | /* got a page, check the serial number */ |
603 | 11.9k | long serial_number = ogg_page_serialno(&aspect->working_page); |
604 | 11.9k | uint32_t i; |
605 | 11.9k | seek_was_to_current_link = false; |
606 | 11.9k | if(serial_number == aspect->linkdetails[aspect->current_linknumber].serial_number) { |
607 | | /* This page belongs to current link */ |
608 | 5.81k | seek_was_to_current_link = true; |
609 | 5.81k | } |
610 | 2.67M | for(i = 0; i < aspect->linkdetails[aspect->current_linknumber].number_of_other_streams; i++) { |
611 | 2.66M | if(serial_number == aspect->linkdetails[aspect->current_linknumber].other_serial_numbers[i]) { |
612 | | /* This page belongs to the current link */ |
613 | 2.28M | seek_was_to_current_link = true; |
614 | 2.28M | } |
615 | 2.66M | } |
616 | 11.9k | if(ogg_page_serialno(&aspect->working_page) == aspect->linkdetails[aspect->current_linknumber].serial_number && ogg_page_eos(&aspect->working_page)) { |
617 | | /* Found EOS */ |
618 | 706 | aspect->linkdetails[aspect->current_linknumber].end_byte = current_pos; |
619 | 706 | aspect->linkdetails[aspect->current_linknumber].samples = ogg_page_granulepos(&aspect->working_page); |
620 | | |
621 | 706 | aspect->number_of_links_indexed++; |
622 | 706 | aspect->current_linknumber_advance_read = aspect->current_linknumber + 1; |
623 | 706 | if(!check_size_of_link_allocation_(aspect)) |
624 | 1 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; |
625 | | |
626 | 705 | aspect->need_serial_number = true; |
627 | | |
628 | | /* Now, continue loop to check whether we are at end of stream or another link follows */ |
629 | 705 | FLAC__ogg_decoder_aspect_next_link(aspect); |
630 | 705 | continue; |
631 | 706 | } |
632 | 11.1k | if(seek_was_to_current_link) { |
633 | | /* If the seek landed on a page with the serial number of the stream we're interested in, we can be sure |
634 | | * the EOS page will be later in the stream. If the seek landed on a stream with a different serial number |
635 | | * however, we cannot be sure. It could be the EOS page of that stream has already been seen. In that case |
636 | | * something else needs to be done, else we could end up in an endless loop */ |
637 | 9.57k | if(ogg_page_serialno(&aspect->working_page) == aspect->linkdetails[aspect->current_linknumber].serial_number) { |
638 | 5.11k | left_pos = current_pos; |
639 | 5.11k | keep_reading = false; |
640 | 5.11k | } |
641 | 4.46k | else { |
642 | 4.46k | seek_to_left_pos = true; |
643 | 4.46k | } |
644 | 9.57k | } |
645 | 1.62k | else if(keep_reading) { |
646 | | /* We read from the left_pos but found nothing interesting, so we can move left_pos up */ |
647 | 1.07k | left_pos = current_pos; |
648 | 1.07k | } |
649 | 549 | else if(did_a_seek) { |
650 | 338 | if(right_pos <= page_pos) { |
651 | | /* Ended up somewhere we've already been */ |
652 | 45 | seek_to_left_pos = true; |
653 | 45 | } |
654 | 293 | else |
655 | 293 | right_pos = page_pos; |
656 | 338 | } |
657 | 211 | else { |
658 | | /* Read forward but found an unknown serial number */ |
659 | 211 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
660 | 211 | } |
661 | 11.1k | } |
662 | 103k | else { /* aspect->beginning_of_link == true */ |
663 | 103k | if(aspect->end_of_stream) { |
664 | 6 | if(aspect->current_linknumber == 0) |
665 | 5 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; |
666 | 1 | aspect->current_linknumber--; |
667 | 1 | aspect->linkdetails[aspect->current_linknumber].is_last = true; |
668 | 1 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; |
669 | 6 | } |
670 | 103k | else { |
671 | | /* We can end up here for three reasons: |
672 | | * 1) We've found the end of the link and are looking whether a new one follows it. If that is the case, |
673 | | * we need to finish after we found it |
674 | | * 2) We've just started skipping this link, and need to look for other BOS pages first. If that is the case, |
675 | | * we need to continue after we found them |
676 | | * 3) We've don't know anything about the link that needs to be skipped yet, and we need to process the BOS |
677 | | * pages first, and process the BOS pages of the next link */ |
678 | 103k | FLAC__bool need_to_finish = aspect->need_serial_number && !find_bos_twice; |
679 | 103k | FLAC__OggDecoderAspectReadStatus status = process_page_(aspect, tell_callback, decoder, client_data); |
680 | 103k | if(status != FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK) |
681 | 51 | return status; |
682 | 103k | if(!aspect->need_serial_number) { |
683 | 1.92k | if(need_to_finish) { |
684 | | /* Found start of next link, we're done */ |
685 | 544 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; |
686 | 544 | } |
687 | 1.37k | find_bos_twice = false; |
688 | 1.37k | } |
689 | 103k | if(!aspect->beginning_of_link) { |
690 | | /* Done scanning BOS pages, move up left_pos */ |
691 | 1.43k | left_pos = page_pos; |
692 | 1.43k | } |
693 | 103k | } |
694 | 103k | } |
695 | 115k | } |
696 | 1.93k | else if(aspect->end_of_stream) { |
697 | 1.93k | if(aspect->beginning_of_link && !aspect->bos_flag_seen) { |
698 | | /* We were looking for the next link, but found end of stream instead */ |
699 | 939 | if(aspect->current_linknumber == 0) |
700 | 805 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; |
701 | 134 | aspect->current_linknumber--; |
702 | 134 | aspect->linkdetails[aspect->current_linknumber].is_last = true; |
703 | 134 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; |
704 | 939 | } |
705 | 995 | else if(did_a_seek) { |
706 | | /* Seeking to target_pos did no result in finding a page, set right_pos to that value */ |
707 | 198 | right_pos = target_pos; |
708 | 198 | } |
709 | 797 | else { |
710 | | /* We expected to find the EOS page without seeking, but ended up at the end of the stream */ |
711 | 797 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; |
712 | 797 | } |
713 | 1.93k | } |
714 | 0 | else if(ret == 0) { |
715 | | /* ogg error */ |
716 | 0 | return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; |
717 | 0 | } |
718 | 117k | } |
719 | 3.72k | } |
720 | 14.9k | } |