Mercurial > audlegacy-plugins
comparison src/flac113/libflac/ogg_decoder_aspect.c @ 715:a9b178bc4ae4 trunk
[svn] Import flac. Please test.
| author | js |
|---|---|
| date | Sat, 24 Feb 2007 10:20:58 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 714:e758e9d4f861 | 715:a9b178bc4ae4 |
|---|---|
| 1 /* libFLAC - Free Lossless Audio Codec | |
| 2 * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions | |
| 6 * are met: | |
| 7 * | |
| 8 * - Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * | |
| 11 * - Redistributions in binary form must reproduce the above copyright | |
| 12 * notice, this list of conditions and the following disclaimer in the | |
| 13 * documentation and/or other materials provided with the distribution. | |
| 14 * | |
| 15 * - Neither the name of the Xiph.org Foundation nor the names of its | |
| 16 * contributors may be used to endorse or promote products derived from | |
| 17 * this software without specific prior written permission. | |
| 18 * | |
| 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |
| 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
| 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 30 */ | |
| 31 | |
| 32 #if HAVE_CONFIG_H | |
| 33 # include <config.h> | |
| 34 #endif | |
| 35 | |
| 36 #include <string.h> /* for memcpy() */ | |
| 37 #include "FLAC/assert.h" | |
| 38 #include "private/ogg_decoder_aspect.h" | |
| 39 #include "private/ogg_mapping.h" | |
| 40 | |
| 41 #ifdef max | |
| 42 #undef max | |
| 43 #endif | |
| 44 #define max(x,y) ((x)>(y)?(x):(y)) | |
| 45 | |
| 46 /*********************************************************************** | |
| 47 * | |
| 48 * Public class methods | |
| 49 * | |
| 50 ***********************************************************************/ | |
| 51 | |
| 52 FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect) | |
| 53 { | |
| 54 /* we will determine the serial number later if necessary */ | |
| 55 if(ogg_stream_init(&aspect->stream_state, aspect->serial_number) != 0) | |
| 56 return false; | |
| 57 | |
| 58 if(ogg_sync_init(&aspect->sync_state) != 0) | |
| 59 return false; | |
| 60 | |
| 61 aspect->version_major = ~(0u); | |
| 62 aspect->version_minor = ~(0u); | |
| 63 | |
| 64 aspect->need_serial_number = aspect->use_first_serial_number; | |
| 65 | |
| 66 aspect->end_of_stream = false; | |
| 67 aspect->have_working_page = false; | |
| 68 | |
| 69 return true; | |
| 70 } | |
| 71 | |
| 72 void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect) | |
| 73 { | |
| 74 (void)ogg_sync_clear(&aspect->sync_state); | |
| 75 (void)ogg_stream_clear(&aspect->stream_state); | |
| 76 } | |
| 77 | |
| 78 void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value) | |
| 79 { | |
| 80 aspect->use_first_serial_number = false; | |
| 81 aspect->serial_number = value; | |
| 82 } | |
| 83 | |
| 84 void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect) | |
| 85 { | |
| 86 aspect->use_first_serial_number = true; | |
| 87 } | |
| 88 | |
| 89 void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect) | |
| 90 { | |
| 91 (void)ogg_stream_reset(&aspect->stream_state); | |
| 92 (void)ogg_sync_reset(&aspect->sync_state); | |
| 93 aspect->end_of_stream = false; | |
| 94 aspect->have_working_page = false; | |
| 95 } | |
| 96 | |
| 97 void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect) | |
| 98 { | |
| 99 FLAC__ogg_decoder_aspect_flush(aspect); | |
| 100 | |
| 101 if(aspect->use_first_serial_number) | |
| 102 aspect->need_serial_number = true; | |
| 103 } | |
| 104 | |
| 105 FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data) | |
| 106 { | |
| 107 static const size_t OGG_BYTES_CHUNK = 8192; | |
| 108 const size_t bytes_requested = *bytes; | |
| 109 | |
| 110 /* | |
| 111 * The FLAC decoding API uses pull-based reads, whereas Ogg decoding | |
| 112 * is push-based. In libFLAC, when you ask to decode a frame, the | |
| 113 * decoder will eventually call the read callback to supply some data, | |
| 114 * but how much it asks for depends on how much free space it has in | |
| 115 * its internal buffer. It does not try to grow its internal buffer | |
| 116 * to accomodate a whole frame because then the internal buffer size | |
| 117 * could not be limited, which is necessary in embedded applications. | |
| 118 * | |
| 119 * Ogg however grows its internal buffer until a whole page is present; | |
| 120 * only then can you get decoded data out. So we can't just ask for | |
| 121 * the same number of bytes from Ogg, then pass what's decoded down to | |
| 122 * libFLAC. If what libFLAC is asking for will not contain a whole | |
| 123 * page, then we will get no data from ogg_sync_pageout(), and at the | |
| 124 * same time cannot just read more data from the client for the purpose | |
| 125 * of getting a whole decoded page because the decoded size might be | |
| 126 * larger than libFLAC's internal buffer. | |
| 127 * | |
| 128 * Instead, whenever this read callback wrapper is called, we will | |
| 129 * continually request data from the client until we have at least one | |
| 130 * page, and manage pages internally so that we can send pieces of | |
| 131 * pages down to libFLAC in such a way that we obey its size | |
| 132 * requirement. To limit the amount of callbacks, we will always try | |
| 133 * to read in enough pages to return the full number of bytes | |
| 134 * requested. | |
| 135 */ | |
| 136 *bytes = 0; | |
| 137 while (*bytes < bytes_requested && !aspect->end_of_stream) { | |
| 138 if (aspect->have_working_page) { | |
| 139 if (aspect->have_working_packet) { | |
| 140 size_t n = bytes_requested - *bytes; | |
| 141 if ((size_t)aspect->working_packet.bytes <= n) { | |
| 142 /* the rest of the packet will fit in the buffer */ | |
| 143 n = aspect->working_packet.bytes; | |
| 144 memcpy(buffer, aspect->working_packet.packet, n); | |
| 145 *bytes += n; | |
| 146 buffer += n; | |
| 147 aspect->have_working_packet = false; | |
| 148 } | |
| 149 else { | |
| 150 /* only n bytes of the packet will fit in the buffer */ | |
| 151 memcpy(buffer, aspect->working_packet.packet, n); | |
| 152 *bytes += n; | |
| 153 buffer += n; | |
| 154 aspect->working_packet.packet += n; | |
| 155 aspect->working_packet.bytes -= n; | |
| 156 } | |
| 157 } | |
| 158 else { | |
| 159 /* try and get another packet */ | |
| 160 const int ret = ogg_stream_packetout(&aspect->stream_state, &aspect->working_packet); | |
| 161 if (ret > 0) { | |
| 162 aspect->have_working_packet = true; | |
| 163 /* if it is the first header packet, check for magic and a supported Ogg FLAC mapping version */ | |
| 164 if (aspect->working_packet.bytes > 0 && aspect->working_packet.packet[0] == FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE) { | |
| 165 const FLAC__byte *b = aspect->working_packet.packet; | |
| 166 const unsigned header_length = | |
| 167 FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + | |
| 168 FLAC__OGG_MAPPING_MAGIC_LENGTH + | |
| 169 FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + | |
| 170 FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH + | |
| 171 FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH; | |
| 172 if (aspect->working_packet.bytes < (long)header_length) | |
| 173 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; | |
| 174 b += FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH; | |
| 175 if (memcmp(b, FLAC__OGG_MAPPING_MAGIC, FLAC__OGG_MAPPING_MAGIC_LENGTH)) | |
| 176 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; | |
| 177 b += FLAC__OGG_MAPPING_MAGIC_LENGTH; | |
| 178 aspect->version_major = (unsigned)(*b); | |
| 179 b += FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH; | |
| 180 aspect->version_minor = (unsigned)(*b); | |
| 181 if (aspect->version_major != 1) | |
| 182 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION; | |
| 183 aspect->working_packet.packet += header_length; | |
| 184 aspect->working_packet.bytes -= header_length; | |
| 185 } | |
| 186 } | |
| 187 else if (ret == 0) { | |
| 188 aspect->have_working_page = false; | |
| 189 } | |
| 190 else { /* ret < 0 */ | |
| 191 /* lost sync, we'll leave the working page for the next call */ | |
| 192 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; | |
| 193 } | |
| 194 } | |
| 195 } | |
| 196 else { | |
| 197 /* try and get another page */ | |
| 198 const int ret = ogg_sync_pageout(&aspect->sync_state, &aspect->working_page); | |
| 199 if (ret > 0) { | |
| 200 /* got a page, grab the serial number if necessary */ | |
| 201 if(aspect->need_serial_number) { | |
| 202 aspect->stream_state.serialno = aspect->serial_number = ogg_page_serialno(&aspect->working_page); | |
| 203 aspect->need_serial_number = false; | |
| 204 } | |
| 205 if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) { | |
| 206 aspect->have_working_page = true; | |
| 207 aspect->have_working_packet = false; | |
| 208 } | |
| 209 /* else do nothing, could be a page from another stream */ | |
| 210 } | |
| 211 else if (ret == 0) { | |
| 212 /* need more data */ | |
| 213 const size_t ogg_bytes_to_read = max(bytes_requested - *bytes, OGG_BYTES_CHUNK); | |
| 214 char *oggbuf = ogg_sync_buffer(&aspect->sync_state, ogg_bytes_to_read); | |
| 215 | |
| 216 if(0 == oggbuf) { | |
| 217 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; | |
| 218 } | |
| 219 else { | |
| 220 size_t ogg_bytes_read = ogg_bytes_to_read; | |
| 221 | |
| 222 switch(read_callback(decoder, (FLAC__byte*)oggbuf, &ogg_bytes_read, client_data)) { | |
| 223 case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: | |
| 224 break; | |
| 225 case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: | |
| 226 aspect->end_of_stream = true; | |
| 227 break; | |
| 228 case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: | |
| 229 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; | |
| 230 default: | |
| 231 FLAC__ASSERT(0); | |
| 232 } | |
| 233 | |
| 234 if(ogg_sync_wrote(&aspect->sync_state, ogg_bytes_read) < 0) { | |
| 235 /* double protection; this will happen if the read callback returns more bytes than the max requested, which would overflow Ogg's internal buffer */ | |
| 236 FLAC__ASSERT(0); | |
| 237 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; | |
| 238 } | |
| 239 } | |
| 240 } | |
| 241 else { /* ret < 0 */ | |
| 242 /* lost sync, bail out */ | |
| 243 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; | |
| 244 } | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 if (aspect->end_of_stream && *bytes == 0) { | |
| 249 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; | |
| 250 } | |
| 251 | |
| 252 return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; | |
| 253 } |
