| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | /* | 
| 2 |  |  |  |  |  |  | * This program is free software; you can redistribute it and/or modify | 
| 3 |  |  |  |  |  |  | * it under the terms of the GNU General Public License as published by | 
| 4 |  |  |  |  |  |  | * the Free Software Foundation; either version 2 of the License, or | 
| 5 |  |  |  |  |  |  | * (at your option) any later version. | 
| 6 |  |  |  |  |  |  | * | 
| 7 |  |  |  |  |  |  | * This program is distributed in the hope that it will be useful, | 
| 8 |  |  |  |  |  |  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 9 |  |  |  |  |  |  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 10 |  |  |  |  |  |  | * GNU General Public License for more details. | 
| 11 |  |  |  |  |  |  | * | 
| 12 |  |  |  |  |  |  | * You should have received a copy of the GNU General Public License | 
| 13 |  |  |  |  |  |  | * along with this program; if not, write to the Free Software | 
| 14 |  |  |  |  |  |  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
| 15 |  |  |  |  |  |  | */ | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | #include "flac.h" | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | int | 
| 20 | 8 |  |  |  |  |  | get_flac_metadata(PerlIO *infile, char *file, HV *info, HV *tags) | 
| 21 |  |  |  |  |  |  | { | 
| 22 | 8 |  |  |  |  |  | flacinfo *flac = _flac_parse(infile, file, info, tags, 0); | 
| 23 |  |  |  |  |  |  |  | 
| 24 | 8 |  |  |  |  |  | Safefree(flac); | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 8 |  |  |  |  |  | return 0; | 
| 27 |  |  |  |  |  |  | } | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | flacinfo * | 
| 30 | 15 |  |  |  |  |  | _flac_parse(PerlIO *infile, char *file, HV *info, HV *tags, uint8_t seeking) | 
| 31 |  |  |  |  |  |  | { | 
| 32 | 15 |  |  |  |  |  | int err = 0; | 
| 33 | 15 |  |  |  |  |  | int done = 0; | 
| 34 |  |  |  |  |  |  | unsigned char *bptr; | 
| 35 | 15 |  |  |  |  |  | unsigned int id3_size = 0; | 
| 36 |  |  |  |  |  |  | uint32_t song_length_ms; | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | flacinfo *flac; | 
| 39 | 15 |  |  |  |  |  | Newz(0, flac, sizeof(flacinfo), flacinfo); | 
| 40 | 15 |  |  |  |  |  | Newz(0, flac->buf, sizeof(Buffer), Buffer); | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 15 |  |  |  |  |  | flac->infile         = infile; | 
| 43 | 15 |  |  |  |  |  | flac->file           = file; | 
| 44 | 15 |  |  |  |  |  | flac->info           = info; | 
| 45 | 15 |  |  |  |  |  | flac->tags           = tags; | 
| 46 | 15 |  |  |  |  |  | flac->audio_offset   = 0; | 
| 47 | 15 |  |  |  |  |  | flac->seeking        = seeking ? 1 : 0; | 
| 48 | 15 |  |  |  |  |  | flac->num_seekpoints = 0; | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 15 |  |  |  |  |  | buffer_init(flac->buf, FLAC_BLOCK_SIZE); | 
| 51 |  |  |  |  |  |  |  | 
| 52 | 15 |  |  |  |  |  | flac->file_size = _file_size(infile); | 
| 53 |  |  |  |  |  |  |  | 
| 54 | 15 | 50 |  |  |  |  | if ( !_check_buf(infile, flac->buf, 10, FLAC_BLOCK_SIZE) ) { | 
| 55 | 0 |  |  |  |  |  | err = -1; | 
| 56 | 0 |  |  |  |  |  | goto out; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | // Check for ID3 tags | 
| 60 | 15 |  |  |  |  |  | bptr = buffer_ptr(flac->buf); | 
| 61 | 15 | 100 |  |  |  |  | if ( | 
| 62 | 4 | 50 |  |  |  |  | (bptr[0] == 'I' && bptr[1] == 'D' && bptr[2] == '3') && | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 63 | 4 | 50 |  |  |  |  | bptr[3] < 0xff && bptr[4] < 0xff && | 
|  |  | 50 |  |  |  |  |  | 
| 64 | 4 | 50 |  |  |  |  | bptr[6] < 0x80 && bptr[7] < 0x80 && bptr[8] < 0x80 && bptr[9] < 0x80 | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | ) { | 
| 66 |  |  |  |  |  |  | /* found an ID3 header... */ | 
| 67 | 4 |  |  |  |  |  | id3_size = 10 + (bptr[6]<<21) + (bptr[7]<<14) + (bptr[8]<<7) + bptr[9]; | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 4 | 50 |  |  |  |  | if (bptr[5] & 0x10) { | 
| 70 |  |  |  |  |  |  | // footer present | 
| 71 | 0 |  |  |  |  |  | id3_size += 10; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | DEBUG_TRACE("Found ID3v2 tag of size %d\n", id3_size); | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 4 |  |  |  |  |  | flac->audio_offset += id3_size; | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | // seek past ID3, we will parse it later | 
| 79 | 4 | 100 |  |  |  |  | if ( id3_size < buffer_len(flac->buf) ) { | 
| 80 | 1 |  |  |  |  |  | buffer_consume(flac->buf, id3_size); | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  | else { | 
| 83 | 3 |  |  |  |  |  | buffer_clear(flac->buf); | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 3 | 50 |  |  |  |  | if (PerlIO_seek(infile, id3_size, SEEK_SET) < 0) { | 
| 86 | 0 |  |  |  |  |  | err = -1; | 
| 87 | 0 |  |  |  |  |  | goto out; | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 4 | 50 |  |  |  |  | if ( !_check_buf(infile, flac->buf, 4, FLAC_BLOCK_SIZE) ) { | 
| 92 | 0 |  |  |  |  |  | err = -1; | 
| 93 | 0 |  |  |  |  |  | goto out; | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | // Verify fLaC magic | 
| 98 | 15 |  |  |  |  |  | bptr = buffer_ptr(flac->buf); | 
| 99 | 15 | 50 |  |  |  |  | if ( memcmp(bptr, "fLaC", 4) != 0 ) { | 
| 100 | 0 |  |  |  |  |  | PerlIO_printf(PerlIO_stderr(), "Not a valid FLAC file: %s\n", file); | 
| 101 | 0 |  |  |  |  |  | err = -1; | 
| 102 | 0 |  |  |  |  |  | goto out; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 15 |  |  |  |  |  | buffer_consume(flac->buf, 4); | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 15 |  |  |  |  |  | flac->audio_offset += 4; | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | // Parse all metadata blocks | 
| 110 | 71 | 100 |  |  |  |  | while ( !done ) { | 
| 111 |  |  |  |  |  |  | uint8_t type; | 
| 112 |  |  |  |  |  |  | off_t len; | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 57 | 50 |  |  |  |  | if ( !_check_buf(infile, flac->buf, 4, FLAC_BLOCK_SIZE) ) { | 
| 115 | 0 |  |  |  |  |  | err = -1; | 
| 116 | 0 |  |  |  |  |  | goto out; | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  |  | 
| 119 | 57 |  |  |  |  |  | bptr = buffer_ptr(flac->buf); | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 57 | 100 |  |  |  |  | if ( bptr[0] & 0x80 ) { | 
| 122 |  |  |  |  |  |  | // last metadata block flag | 
| 123 | 14 |  |  |  |  |  | done = 1; | 
| 124 |  |  |  |  |  |  | } | 
| 125 |  |  |  |  |  |  |  | 
| 126 | 57 |  |  |  |  |  | type = bptr[0] & 0x7f; | 
| 127 | 57 |  |  |  |  |  | len  = (bptr[1] << 16) | (bptr[2] << 8) | bptr[3]; | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 57 |  |  |  |  |  | buffer_consume(flac->buf, 4); | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | DEBUG_TRACE("Parsing metadata block, type %d, len %d, done %d\n", type, (int)len, done); | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 57 | 100 |  |  |  |  | if ( len > flac->file_size - flac->audio_offset ) { | 
| 134 | 1 |  |  |  |  |  | err = -1; | 
| 135 | 1 |  |  |  |  |  | goto out; | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | // Don't read in the full picture in case we aren't reading artwork | 
| 139 |  |  |  |  |  |  | // Do the same for padding, as it can be quite large in some files | 
| 140 | 56 | 100 |  |  |  |  | if ( type != FLAC_TYPE_PICTURE && type != FLAC_TYPE_PADDING ) { | 
|  |  | 100 |  |  |  |  |  | 
| 141 | 40 | 50 |  |  |  |  | if ( !_check_buf(infile, flac->buf, len, len) ) { | 
| 142 | 0 |  |  |  |  |  | err = -1; | 
| 143 | 0 |  |  |  |  |  | goto out; | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 56 |  |  |  |  |  | flac->audio_offset += 4 + len; | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 56 |  |  |  |  |  | switch (type) { | 
| 150 |  |  |  |  |  |  | case FLAC_TYPE_STREAMINFO: | 
| 151 | 15 |  |  |  |  |  | _flac_parse_streaminfo(flac); | 
| 152 | 15 |  |  |  |  |  | break; | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | case FLAC_TYPE_VORBIS_COMMENT: | 
| 155 | 13 | 100 |  |  |  |  | if ( !flac->seeking ) { | 
| 156 |  |  |  |  |  |  | // Vorbis comment parsing code from ogg.c | 
| 157 | 7 |  |  |  |  |  | _parse_vorbis_comments(flac->infile, flac->buf, tags, 0); | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  | else { | 
| 160 |  |  |  |  |  |  | DEBUG_TRACE("  seeking, not parsing comments\n"); | 
| 161 | 6 |  |  |  |  |  | buffer_consume(flac->buf, len); | 
| 162 |  |  |  |  |  |  | } | 
| 163 | 13 |  |  |  |  |  | break; | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | case FLAC_TYPE_APPLICATION: | 
| 166 | 2 | 100 |  |  |  |  | if ( !flac->seeking ) { | 
| 167 | 1 |  |  |  |  |  | _flac_parse_application(flac, len); | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  | else { | 
| 170 |  |  |  |  |  |  | DEBUG_TRACE("  seeking, skipping application\n"); | 
| 171 | 1 |  |  |  |  |  | buffer_consume(flac->buf, len); | 
| 172 |  |  |  |  |  |  | } | 
| 173 | 2 |  |  |  |  |  | break; | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | case FLAC_TYPE_SEEKTABLE: | 
| 176 | 8 | 100 |  |  |  |  | if (flac->seeking) { | 
| 177 | 3 |  |  |  |  |  | _flac_parse_seektable(flac, len); | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  | else { | 
| 180 |  |  |  |  |  |  | DEBUG_TRACE("  not seeking, skipping seektable\n"); | 
| 181 | 5 |  |  |  |  |  | buffer_consume(flac->buf, len); | 
| 182 |  |  |  |  |  |  | } | 
| 183 | 8 |  |  |  |  |  | break; | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | case FLAC_TYPE_CUESHEET: | 
| 186 | 2 | 100 |  |  |  |  | if ( !flac->seeking ) { | 
| 187 | 1 |  |  |  |  |  | _flac_parse_cuesheet(flac); | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  | else { | 
| 190 |  |  |  |  |  |  | DEBUG_TRACE("  seeking, skipping cuesheet\n"); | 
| 191 | 1 |  |  |  |  |  | buffer_consume(flac->buf, len); | 
| 192 |  |  |  |  |  |  | } | 
| 193 | 2 |  |  |  |  |  | break; | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | case FLAC_TYPE_PICTURE: | 
| 196 | 3 | 100 |  |  |  |  | if ( !flac->seeking ) { | 
| 197 | 2 | 50 |  |  |  |  | if ( !_flac_parse_picture(flac) ) { | 
| 198 | 0 |  |  |  |  |  | goto out; | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  | else { | 
| 202 |  |  |  |  |  |  | DEBUG_TRACE("  seeking, skipping picture\n"); | 
| 203 | 1 |  |  |  |  |  | _flac_skip(flac, len); | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 3 |  |  |  |  |  | break; | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | case FLAC_TYPE_PADDING: | 
| 208 |  |  |  |  |  |  | default: | 
| 209 |  |  |  |  |  |  | DEBUG_TRACE("  unhandled or padding, skipping\n"); | 
| 210 | 13 |  |  |  |  |  | _flac_skip(flac, len); | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  | } | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 14 | 50 |  |  |  |  | song_length_ms = SvIV( *( my_hv_fetch(info, "song_length_ms") ) ); | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 14 | 100 |  |  |  |  | if (song_length_ms > 0) { | 
| 217 | 13 |  |  |  |  |  | my_hv_store( info, "bitrate", newSVuv( _bitrate(flac->file_size - flac->audio_offset, song_length_ms) ) ); | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  | else { | 
| 220 | 1 | 50 |  |  |  |  | if (!seeking) { | 
| 221 |  |  |  |  |  |  | // Find the first/last frames and manually calculate duration and bitrate | 
| 222 |  |  |  |  |  |  | off_t frame_offset; | 
| 223 |  |  |  |  |  |  | uint64_t first_sample; | 
| 224 |  |  |  |  |  |  | uint64_t last_sample; | 
| 225 |  |  |  |  |  |  | uint64_t tmp; | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | DEBUG_TRACE("Manually determining duration/bitrate\n"); | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 1 |  |  |  |  |  | Newz(0, flac->scratch, sizeof(Buffer), Buffer); | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 1 | 50 |  |  |  |  | if ( _flac_first_last_sample(flac, flac->audio_offset, &frame_offset, &first_sample, &tmp, 0) ) { | 
| 232 |  |  |  |  |  |  | DEBUG_TRACE("  First sample: %llu (offset %llu)\n", first_sample, frame_offset); | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | // XXX This last sample isn't really correct, seeking back max_framesize will most likely be several frames | 
| 235 |  |  |  |  |  |  | // from the end, resulting in a slightly shortened duration. Reading backwards through the file | 
| 236 |  |  |  |  |  |  | // would provide a more accurate result | 
| 237 | 1 | 50 |  |  |  |  | if ( _flac_first_last_sample(flac, flac->file_size - flac->max_framesize, &frame_offset, &tmp, &last_sample, 0) ) { | 
| 238 | 1 | 50 |  |  |  |  | if (flac->samplerate) { | 
| 239 | 1 |  |  |  |  |  | song_length_ms = (uint32_t)(( ((last_sample - first_sample) * 1.0) / flac->samplerate) * 1000); | 
| 240 | 1 |  |  |  |  |  | my_hv_store( info, "song_length_ms", newSVuv(song_length_ms) ); | 
| 241 | 1 |  |  |  |  |  | my_hv_store( info, "bitrate", newSVuv( _bitrate(flac->file_size - flac->audio_offset, song_length_ms) ) ); | 
| 242 | 1 |  |  |  |  |  | my_hv_store( info, "total_samples", newSVuv( last_sample - first_sample ) ); | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | DEBUG_TRACE("  Last sample: %llu (offset %llu)\n", last_sample, frame_offset); | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 | 1 |  |  |  |  |  | buffer_free(flac->scratch); | 
| 250 | 1 |  |  |  |  |  | Safefree(flac->scratch); | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  |  | 
| 254 | 14 |  |  |  |  |  | my_hv_store( info, "file_size", newSVuv(flac->file_size) ); | 
| 255 | 14 |  |  |  |  |  | my_hv_store( info, "audio_offset", newSVuv(flac->audio_offset) ); | 
| 256 | 14 |  |  |  |  |  | my_hv_store( info, "audio_size", newSVuv(flac->file_size - flac->audio_offset) ); | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | // Parse ID3 last, due to an issue with libid3tag screwing | 
| 259 |  |  |  |  |  |  | // up the filehandle | 
| 260 | 14 | 100 |  |  |  |  | if (id3_size && !seeking) { | 
|  |  | 100 |  |  |  |  |  | 
| 261 | 2 |  |  |  |  |  | parse_id3(infile, file, info, tags, 0, flac->file_size); | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | out: | 
| 265 | 15 |  |  |  |  |  | buffer_free(flac->buf); | 
| 266 | 15 |  |  |  |  |  | Safefree(flac->buf); | 
| 267 |  |  |  |  |  |  |  | 
| 268 | 15 |  |  |  |  |  | return flac; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | // offset is in ms, does sample-accurate seeking, using seektable if available | 
| 272 |  |  |  |  |  |  | // based on libFLAC seek_to_absolute_sample_ | 
| 273 |  |  |  |  |  |  | static int | 
| 274 | 7 |  |  |  |  |  | flac_find_frame(PerlIO *infile, char *file, int offset) | 
| 275 |  |  |  |  |  |  | { | 
| 276 | 7 |  |  |  |  |  | off_t frame_offset = -1; | 
| 277 |  |  |  |  |  |  | uint64_t target_sample; | 
| 278 |  |  |  |  |  |  | uint32_t approx_bytes_per_frame; | 
| 279 |  |  |  |  |  |  | uint64_t lower_bound, upper_bound, lower_bound_sample, upper_bound_sample; | 
| 280 | 7 |  |  |  |  |  | int64_t pos = -1; | 
| 281 | 7 |  |  |  |  |  | int8_t max_tries = 100; | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | // We need to read all metadata first to get some data we need to calculate | 
| 284 | 7 |  |  |  |  |  | HV *info = newHV(); | 
| 285 | 7 |  |  |  |  |  | HV *tags = newHV(); | 
| 286 | 7 |  |  |  |  |  | flacinfo *flac = _flac_parse(infile, file, info, tags, 1); | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | // Allocate scratch buffer | 
| 289 | 7 |  |  |  |  |  | Newz(0, flac->scratch, sizeof(Buffer), Buffer); | 
| 290 |  |  |  |  |  |  |  | 
| 291 | 7 | 50 |  |  |  |  | if ( !flac->samplerate || !flac->total_samples ) { | 
|  |  | 50 |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | // Can't seek in file without samplerate | 
| 293 |  |  |  |  |  |  | goto out; | 
| 294 |  |  |  |  |  |  | } | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | // Determine target sample we're looking for | 
| 297 | 7 |  |  |  |  |  | target_sample = ((offset - 1) / 10) * (flac->samplerate / 100); | 
| 298 |  |  |  |  |  |  | DEBUG_TRACE("Looking for target sample %llu\n", target_sample); | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 7 | 50 |  |  |  |  | if (flac->min_blocksize == flac->max_blocksize && flac->min_blocksize > 0) | 
|  |  | 50 |  |  |  |  |  | 
| 301 | 7 |  |  |  |  |  | approx_bytes_per_frame = flac->min_blocksize * flac->channels * flac->bits_per_sample/8 + 64; | 
| 302 | 0 | 0 |  |  |  |  | else if (flac->max_framesize > 0) | 
| 303 | 0 |  |  |  |  |  | approx_bytes_per_frame = (flac->max_framesize + flac->min_framesize) / 2 + 1; | 
| 304 |  |  |  |  |  |  | else | 
| 305 | 0 |  |  |  |  |  | approx_bytes_per_frame = 4096 * flac->channels * flac->bits_per_sample/8 + 64; | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | DEBUG_TRACE("approx_bytes_per_frame: %d\n", approx_bytes_per_frame); | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 7 |  |  |  |  |  | lower_bound        = flac->audio_offset; | 
| 310 | 7 |  |  |  |  |  | lower_bound_sample = 0; | 
| 311 | 7 |  |  |  |  |  | upper_bound        = flac->file_size; | 
| 312 | 7 |  |  |  |  |  | upper_bound_sample = flac->total_samples; | 
| 313 |  |  |  |  |  |  |  | 
| 314 | 7 | 100 |  |  |  |  | if (flac->num_seekpoints) { | 
| 315 |  |  |  |  |  |  | // Use seektable to find seek point | 
| 316 |  |  |  |  |  |  | // Start looking at seekpoint 1 | 
| 317 |  |  |  |  |  |  | int i; | 
| 318 | 3 |  |  |  |  |  | uint64_t new_lower_bound        = lower_bound; | 
| 319 | 3 |  |  |  |  |  | uint64_t new_upper_bound        = upper_bound; | 
| 320 | 3 |  |  |  |  |  | uint64_t new_lower_bound_sample = lower_bound_sample; | 
| 321 | 3 |  |  |  |  |  | uint64_t new_upper_bound_sample = upper_bound_sample; | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | DEBUG_TRACE("Checking seektable...\n"); | 
| 324 |  |  |  |  |  |  |  | 
| 325 | 14 | 50 |  |  |  |  | for (i = flac->num_seekpoints - 1; i >= 0; i--) { | 
| 326 | 14 | 100 |  |  |  |  | if ( | 
| 327 | 14 |  |  |  |  |  | flac->seekpoints[i].sample_number != 0xFFFFFFFFFFFFFFFFLL | 
| 328 | 11 | 50 |  |  |  |  | && flac->seekpoints[i].frame_samples > 0 | 
| 329 | 11 | 50 |  |  |  |  | && (flac->total_samples <= 0 || flac->seekpoints[i].sample_number < flac->total_samples) | 
|  |  | 50 |  |  |  |  |  | 
| 330 | 11 | 100 |  |  |  |  | && flac->seekpoints[i].sample_number <= target_sample | 
| 331 |  |  |  |  |  |  | ) | 
| 332 | 3 |  |  |  |  |  | break; | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 3 | 50 |  |  |  |  | if (i >= 0) { | 
| 336 |  |  |  |  |  |  | // we found a seek point | 
| 337 | 3 |  |  |  |  |  | new_lower_bound        = flac->audio_offset + flac->seekpoints[i].stream_offset; | 
| 338 | 3 |  |  |  |  |  | new_lower_bound_sample = flac->seekpoints[i].sample_number; | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | DEBUG_TRACE("  seektable new_lower_bound %llu, new_lower_bound_sample %llu\n", | 
| 341 |  |  |  |  |  |  | new_lower_bound, new_lower_bound_sample); | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | // Find the closest seek point > target_sample | 
| 345 | 26 | 100 |  |  |  |  | for (i = 0; i < flac->num_seekpoints; i++) { | 
| 346 | 25 | 100 |  |  |  |  | if ( | 
| 347 | 25 |  |  |  |  |  | flac->seekpoints[i].sample_number != 0xFFFFFFFFFFFFFFFFLL | 
| 348 | 24 | 50 |  |  |  |  | && flac->seekpoints[i].frame_samples > 0 | 
| 349 | 24 | 50 |  |  |  |  | && (flac->total_samples <= 0 || flac->seekpoints[i].sample_number < flac->total_samples) | 
|  |  | 50 |  |  |  |  |  | 
| 350 | 24 | 100 |  |  |  |  | && flac->seekpoints[i].sample_number > target_sample | 
| 351 |  |  |  |  |  |  | ) | 
| 352 | 2 |  |  |  |  |  | break; | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 3 | 100 |  |  |  |  | if (i < flac->num_seekpoints) { | 
| 356 |  |  |  |  |  |  | // we found a seek point | 
| 357 | 2 |  |  |  |  |  | new_upper_bound        = flac->audio_offset + flac->seekpoints[i].stream_offset; | 
| 358 | 2 |  |  |  |  |  | new_upper_bound_sample = flac->seekpoints[i].sample_number; | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | DEBUG_TRACE("  seektable new_upper_bound %llu, new_upper_bound_sample %llu\n", | 
| 361 |  |  |  |  |  |  | new_upper_bound, new_upper_bound_sample); | 
| 362 |  |  |  |  |  |  | } | 
| 363 |  |  |  |  |  |  |  | 
| 364 | 3 | 50 |  |  |  |  | if (new_upper_bound >= new_lower_bound) { | 
| 365 | 3 |  |  |  |  |  | lower_bound = new_lower_bound; | 
| 366 | 3 |  |  |  |  |  | upper_bound = new_upper_bound; | 
| 367 | 3 |  |  |  |  |  | lower_bound_sample = new_lower_bound_sample; | 
| 368 | 3 |  |  |  |  |  | upper_bound_sample = new_upper_bound_sample; | 
| 369 |  |  |  |  |  |  | } | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  |  | 
| 372 | 7 | 50 |  |  |  |  | if (upper_bound_sample == lower_bound_sample) | 
| 373 | 0 |  |  |  |  |  | upper_bound_sample++; | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 107 | 100 |  |  |  |  | while (max_tries--) { | 
| 376 | 106 |  |  |  |  |  | int ret = -1; | 
| 377 |  |  |  |  |  |  | uint64_t this_frame_sample; | 
| 378 |  |  |  |  |  |  | uint64_t last_sample; | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | // check if bounds are still ok | 
| 381 | 106 | 50 |  |  |  |  | if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) { | 
|  |  | 50 |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | DEBUG_TRACE("Error: out of bounds\n"); | 
| 383 | 0 |  |  |  |  |  | frame_offset = -1; | 
| 384 | 0 |  |  |  |  |  | goto out; | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | // estimate position | 
| 388 | 212 |  |  |  |  |  | pos = (int64_t)lower_bound + (int64_t)( | 
| 389 | 106 |  |  |  |  |  | (double)((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) | 
| 390 | 106 |  |  |  |  |  | / | 
| 391 | 106 |  |  |  |  |  | (double)(upper_bound_sample - lower_bound_sample) | 
| 392 | 106 |  |  |  |  |  | ) - approx_bytes_per_frame; | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | DEBUG_TRACE("Initial pos: %lld\n", pos); | 
| 395 |  |  |  |  |  |  |  | 
| 396 | 106 | 50 |  |  |  |  | if (pos < (int64_t)lower_bound) | 
| 397 | 106 |  |  |  |  |  | pos = lower_bound; | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 106 | 50 |  |  |  |  | if (pos >= (int64_t)upper_bound) | 
| 400 | 0 |  |  |  |  |  | pos = upper_bound - FLAC_FRAME_MAX_HEADER; | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | DEBUG_TRACE("Searching at pos %lld (lb/lbs %llu/%llu, ub/ubs %llu/%llu)\n", | 
| 403 |  |  |  |  |  |  | pos, lower_bound, lower_bound_sample, upper_bound, upper_bound_sample); | 
| 404 |  |  |  |  |  |  |  | 
| 405 | 106 |  |  |  |  |  | ret = _flac_first_last_sample(flac, pos, &frame_offset, &this_frame_sample, &last_sample, target_sample); | 
| 406 |  |  |  |  |  |  |  | 
| 407 | 106 | 50 |  |  |  |  | if (ret < 0) { | 
| 408 |  |  |  |  |  |  | // Error | 
| 409 | 0 |  |  |  |  |  | goto out; | 
| 410 |  |  |  |  |  |  | } | 
| 411 | 106 | 50 |  |  |  |  | else if (ret == 0) { | 
| 412 |  |  |  |  |  |  | // No valid frame found in range pos - flac->max_framesize, adjust bounds and retry | 
| 413 | 0 |  |  |  |  |  | upper_bound = pos; | 
| 414 | 0 |  |  |  |  |  | upper_bound_sample -= flac->min_blocksize; | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | DEBUG_TRACE("  No valid frame found, retrying (ub/ubs %llu/%llu)\n", upper_bound, upper_bound_sample); | 
| 417 |  |  |  |  |  |  |  | 
| 418 | 0 |  |  |  |  |  | continue; | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | // make sure we are not seeking in corrupted stream | 
| 422 | 106 | 50 |  |  |  |  | if (this_frame_sample < lower_bound_sample) { | 
| 423 |  |  |  |  |  |  | DEBUG_TRACE("  Frame at %d, this_frame_sample %llu, < lower_bound_sample %llu, aborting\n", | 
| 424 |  |  |  |  |  |  | (int)frame_offset, this_frame_sample, lower_bound_sample); | 
| 425 |  |  |  |  |  |  |  | 
| 426 | 0 |  |  |  |  |  | goto out; | 
| 427 |  |  |  |  |  |  | } | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | DEBUG_TRACE("    Frame at %d, this_frame_sample %llu, last_sample %llu (target %llu)\n", | 
| 430 |  |  |  |  |  |  | (int)frame_offset, this_frame_sample, last_sample, target_sample); | 
| 431 |  |  |  |  |  |  |  | 
| 432 | 106 | 100 |  |  |  |  | if (target_sample >= this_frame_sample && target_sample < last_sample) { | 
|  |  | 50 |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | DEBUG_TRACE("    Found target frame\n"); | 
| 434 | 6 |  |  |  |  |  | break; | 
| 435 |  |  |  |  |  |  | } | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | // narrow the search | 
| 438 | 100 | 50 |  |  |  |  | if (target_sample < this_frame_sample) { | 
| 439 | 100 |  |  |  |  |  | upper_bound_sample = this_frame_sample; | 
| 440 | 100 |  |  |  |  |  | upper_bound = frame_offset; | 
| 441 | 100 |  |  |  |  |  | approx_bytes_per_frame = 2 * (upper_bound - pos) / 3 + 16; | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | DEBUG_TRACE("    Moving upper_bound to %llu, upper_bound_sample to %llu, approx_bytes_per_frame %d\n", | 
| 444 |  |  |  |  |  |  | upper_bound, upper_bound_sample, approx_bytes_per_frame); | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  | else { | 
| 447 | 0 |  |  |  |  |  | lower_bound_sample = last_sample; | 
| 448 | 0 |  |  |  |  |  | lower_bound = frame_offset + 1; | 
| 449 | 100 |  |  |  |  |  | approx_bytes_per_frame = 2 * (lower_bound - pos) / 3 + 16; | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | DEBUG_TRACE("    Moving lower_bound to %llu, lower_bound_sample to %llu, approx_bytes_per_frame %d\n", | 
| 452 |  |  |  |  |  |  | lower_bound, lower_bound_sample, approx_bytes_per_frame); | 
| 453 |  |  |  |  |  |  | } | 
| 454 |  |  |  |  |  |  | } | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | DEBUG_TRACE("max_tries: %d\n", max_tries); | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | out: | 
| 459 |  |  |  |  |  |  | // Don't leak | 
| 460 | 7 |  |  |  |  |  | SvREFCNT_dec(info); | 
| 461 | 7 |  |  |  |  |  | SvREFCNT_dec(tags); | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | // free seek struct | 
| 464 | 7 |  |  |  |  |  | Safefree(flac->seekpoints); | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | // free scratch buffer | 
| 467 | 7 | 50 |  |  |  |  | if (flac->scratch->alloc) | 
| 468 | 7 |  |  |  |  |  | buffer_free(flac->scratch); | 
| 469 | 7 |  |  |  |  |  | Safefree(flac->scratch); | 
| 470 |  |  |  |  |  |  |  | 
| 471 | 7 |  |  |  |  |  | Safefree(flac); | 
| 472 |  |  |  |  |  |  |  | 
| 473 | 7 |  |  |  |  |  | return frame_offset; | 
| 474 |  |  |  |  |  |  | } | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | // Returns: | 
| 477 |  |  |  |  |  |  | //  1: Found a valid frame | 
| 478 |  |  |  |  |  |  | //  0: Did not find a valid frame | 
| 479 |  |  |  |  |  |  | // -1: Error | 
| 480 |  |  |  |  |  |  | int | 
| 481 | 108 |  |  |  |  |  | _flac_first_last_sample(flacinfo *flac, off_t seek_offset, off_t *frame_offset, uint64_t *first_sample, uint64_t *last_sample, uint64_t target_sample) | 
| 482 |  |  |  |  |  |  | { | 
| 483 |  |  |  |  |  |  | unsigned char *bptr; | 
| 484 |  |  |  |  |  |  | unsigned int buf_size; | 
| 485 | 108 |  |  |  |  |  | int ret = 0; | 
| 486 |  |  |  |  |  |  | uint32_t i; | 
| 487 |  |  |  |  |  |  |  | 
| 488 | 108 |  |  |  |  |  | buffer_init_or_clear(flac->scratch, flac->max_framesize); | 
| 489 |  |  |  |  |  |  |  | 
| 490 | 108 | 50 |  |  |  |  | if (seek_offset > flac->file_size - FLAC_FRAME_MAX_HEADER) { | 
| 491 |  |  |  |  |  |  | DEBUG_TRACE("  Error: seek_offset > file_size - header size\n"); | 
| 492 | 0 |  |  |  |  |  | ret = -1; | 
| 493 | 0 |  |  |  |  |  | goto out; | 
| 494 |  |  |  |  |  |  | } | 
| 495 |  |  |  |  |  |  |  | 
| 496 | 108 | 50 |  |  |  |  | if ( (PerlIO_seek(flac->infile, seek_offset, SEEK_SET)) == -1 ) { | 
| 497 |  |  |  |  |  |  | DEBUG_TRACE("  Error: seek failed\n"); | 
| 498 | 0 |  |  |  |  |  | ret = -1; | 
| 499 | 0 |  |  |  |  |  | goto out; | 
| 500 |  |  |  |  |  |  | } | 
| 501 |  |  |  |  |  |  |  | 
| 502 | 108 | 50 |  |  |  |  | if ( !_check_buf(flac->infile, flac->scratch, FLAC_FRAME_MAX_HEADER, flac->max_framesize) ) { | 
| 503 |  |  |  |  |  |  | DEBUG_TRACE("  Error: read failed\n"); | 
| 504 | 0 |  |  |  |  |  | ret = -1; | 
| 505 | 0 |  |  |  |  |  | goto out; | 
| 506 |  |  |  |  |  |  | } | 
| 507 |  |  |  |  |  |  |  | 
| 508 | 108 |  |  |  |  |  | bptr = buffer_ptr(flac->scratch); | 
| 509 | 108 |  |  |  |  |  | buf_size = buffer_len(flac->scratch); | 
| 510 |  |  |  |  |  |  |  | 
| 511 | 194496 | 50 |  |  |  |  | for (i = 0; i != buf_size - FLAC_HEADER_LEN; i++) { | 
| 512 |  |  |  |  |  |  | // Verify sync and various reserved bits | 
| 513 | 194496 | 100 |  |  |  |  | if ( bptr[i] != 0xFF | 
| 514 | 3201 | 100 |  |  |  |  | || (bptr[i+1] >> 2) != 0x3E | 
| 515 | 266 | 100 |  |  |  |  | || bptr[i+1] & 0x02 | 
| 516 | 166 | 100 |  |  |  |  | || bptr[i+3] & 0x01 | 
| 517 |  |  |  |  |  |  | ) { | 
| 518 | 194338 |  |  |  |  |  | continue; | 
| 519 |  |  |  |  |  |  | } | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | DEBUG_TRACE("Checking frame header @ %d: %0x %0x %0x %0x\n", (int)seek_offset + i, bptr[i], bptr[i+1], bptr[i+2], bptr[i+3]); | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | // Verify we have a valid FLAC frame header | 
| 524 |  |  |  |  |  |  | // and get the first/last sample numbers in the frame if it's valid | 
| 525 | 158 | 100 |  |  |  |  | if ( !_flac_read_frame_header(flac, &bptr[i], first_sample, last_sample) ) { | 
| 526 |  |  |  |  |  |  | DEBUG_TRACE("  Unable to read frame header\n"); | 
| 527 | 2 |  |  |  |  |  | continue; | 
| 528 |  |  |  |  |  |  | } | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | DEBUG_TRACE("  first_sample %llu\n", *first_sample); | 
| 531 |  |  |  |  |  |  |  | 
| 532 | 156 |  |  |  |  |  | *frame_offset = seek_offset + i; | 
| 533 | 156 |  |  |  |  |  | ret = 1; | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | // If looking for a target sample, return the nearest frame found in this buffer | 
| 536 | 156 | 100 |  |  |  |  | if (target_sample) { | 
| 537 | 54 | 50 |  |  |  |  | if (target_sample >= *first_sample && target_sample < *last_sample) { | 
|  |  | 100 |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | // This frame is the one | 
| 539 |  |  |  |  |  |  | break; | 
| 540 |  |  |  |  |  |  | } | 
| 541 | 48 | 50 |  |  |  |  | else if (target_sample < *first_sample) { | 
| 542 |  |  |  |  |  |  | // Too far, return what we have | 
| 543 | 0 |  |  |  |  |  | break; | 
| 544 |  |  |  |  |  |  | } | 
| 545 |  |  |  |  |  |  | } | 
| 546 |  |  |  |  |  |  | else { | 
| 547 |  |  |  |  |  |  | // Not looking for a target sample, return first one found | 
| 548 | 102 |  |  |  |  |  | break; | 
| 549 |  |  |  |  |  |  | } | 
| 550 |  |  |  |  |  |  | } | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | out: | 
| 553 | 108 | 50 |  |  |  |  | if (ret <= 0) | 
| 554 | 0 |  |  |  |  |  | *frame_offset = -1; | 
| 555 |  |  |  |  |  |  |  | 
| 556 | 108 |  |  |  |  |  | return ret; | 
| 557 |  |  |  |  |  |  | } | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | int | 
| 560 | 158 |  |  |  |  |  | _flac_read_frame_header(flacinfo *flac, unsigned char *buf, uint64_t *first_sample, uint64_t *last_sample) | 
| 561 |  |  |  |  |  |  | { | 
| 562 |  |  |  |  |  |  | // A lot of this code is based on libFLAC stream_decoder.c read_frame_header_ | 
| 563 |  |  |  |  |  |  | uint32_t x; | 
| 564 |  |  |  |  |  |  | uint64_t xx; | 
| 565 | 158 |  |  |  |  |  | uint32_t blocksize = 0; | 
| 566 | 158 |  |  |  |  |  | uint32_t blocksize_hint = 0; | 
| 567 | 158 |  |  |  |  |  | uint32_t samplerate_hint = 0; | 
| 568 | 158 |  |  |  |  |  | uint32_t frame_number = 0; | 
| 569 | 158 |  |  |  |  |  | uint8_t  raw_header_len = 4; | 
| 570 |  |  |  |  |  |  | uint8_t  crc8; | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | // Block size | 
| 573 | 158 |  |  |  |  |  | switch(x = buf[2] >> 4) { | 
| 574 |  |  |  |  |  |  | case 0: | 
| 575 | 0 |  |  |  |  |  | return 0; | 
| 576 |  |  |  |  |  |  | case 1: | 
| 577 | 0 |  |  |  |  |  | blocksize = 192; | 
| 578 | 0 |  |  |  |  |  | break; | 
| 579 |  |  |  |  |  |  | case 2: case 3: case 4: case 5: | 
| 580 | 154 |  |  |  |  |  | blocksize = 576 << (x-2); | 
| 581 | 154 |  |  |  |  |  | break; | 
| 582 |  |  |  |  |  |  | case 6: case 7: | 
| 583 | 1 |  |  |  |  |  | blocksize_hint = x; | 
| 584 | 1 |  |  |  |  |  | break; | 
| 585 |  |  |  |  |  |  | case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: | 
| 586 | 3 |  |  |  |  |  | blocksize = 256 << (x-8); | 
| 587 | 3 |  |  |  |  |  | break; | 
| 588 |  |  |  |  |  |  | default: | 
| 589 | 0 |  |  |  |  |  | break; | 
| 590 |  |  |  |  |  |  | } | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | // Sample rate, all we need here is the hint | 
| 593 | 158 |  |  |  |  |  | switch(x = buf[2] & 0x0f) { | 
| 594 |  |  |  |  |  |  | case 12: case 13: case 14: | 
| 595 | 0 |  |  |  |  |  | samplerate_hint = x; | 
| 596 | 0 |  |  |  |  |  | break; | 
| 597 |  |  |  |  |  |  | case 15: | 
| 598 | 0 |  |  |  |  |  | return 0; | 
| 599 |  |  |  |  |  |  | default: | 
| 600 | 158 |  |  |  |  |  | break; | 
| 601 |  |  |  |  |  |  | } | 
| 602 |  |  |  |  |  |  |  | 
| 603 | 158 | 100 |  |  |  |  | if ( buf[1] & 0x01 || flac->min_blocksize != flac->max_blocksize ) { | 
|  |  | 50 |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | // Variable blocksize | 
| 605 |  |  |  |  |  |  | // XXX need test | 
| 606 | 2 | 50 |  |  |  |  | if ( !_flac_read_utf8_uint64(buf, &xx, &raw_header_len) ) | 
| 607 | 0 |  |  |  |  |  | return 0; | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 2 | 50 |  |  |  |  | if ( xx == 0xFFFFFFFFFFFFFFFFLL ) | 
| 610 | 2 |  |  |  |  |  | return 0; | 
| 611 |  |  |  |  |  |  |  | 
| 612 |  |  |  |  |  |  | DEBUG_TRACE("  variable blocksize, first sample %llu\n", xx); | 
| 613 |  |  |  |  |  |  |  | 
| 614 | 0 |  |  |  |  |  | *first_sample = xx; | 
| 615 |  |  |  |  |  |  | } | 
| 616 |  |  |  |  |  |  | else { | 
| 617 |  |  |  |  |  |  | // Fixed blocksize, x = frame number | 
| 618 | 156 | 50 |  |  |  |  | if ( !_flac_read_utf8_uint32(buf, &x, &raw_header_len) ) | 
| 619 | 0 |  |  |  |  |  | return 0; | 
| 620 |  |  |  |  |  |  |  | 
| 621 | 156 | 50 |  |  |  |  | if ( x == 0xFFFFFFFF ) | 
| 622 | 0 |  |  |  |  |  | return 0; | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | DEBUG_TRACE("  fixed blocksize, frame number %d\n", x); | 
| 625 |  |  |  |  |  |  |  | 
| 626 | 156 |  |  |  |  |  | frame_number = x; | 
| 627 |  |  |  |  |  |  | } | 
| 628 |  |  |  |  |  |  |  | 
| 629 | 156 | 100 |  |  |  |  | if (blocksize_hint) { | 
| 630 |  |  |  |  |  |  | DEBUG_TRACE("  blocksize_hint %d\n", blocksize_hint); | 
| 631 | 1 |  |  |  |  |  | x = buf[raw_header_len++]; | 
| 632 | 1 | 50 |  |  |  |  | if (blocksize_hint == 7) { | 
| 633 | 1 |  |  |  |  |  | uint32_t _x = buf[raw_header_len++]; | 
| 634 | 1 |  |  |  |  |  | x = (x << 8) | _x; | 
| 635 |  |  |  |  |  |  | } | 
| 636 | 1 |  |  |  |  |  | blocksize = x + 1; | 
| 637 |  |  |  |  |  |  | } | 
| 638 |  |  |  |  |  |  |  | 
| 639 |  |  |  |  |  |  | DEBUG_TRACE("  blocksize %d\n", blocksize); | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | // XXX need test | 
| 642 | 156 | 50 |  |  |  |  | if (samplerate_hint) { | 
| 643 |  |  |  |  |  |  | DEBUG_TRACE("  samplerate_hint %d\n", samplerate_hint); | 
| 644 | 0 |  |  |  |  |  | raw_header_len++; | 
| 645 | 0 | 0 |  |  |  |  | if (samplerate_hint != 12) { | 
| 646 | 0 |  |  |  |  |  | raw_header_len++; | 
| 647 |  |  |  |  |  |  | } | 
| 648 |  |  |  |  |  |  | } | 
| 649 |  |  |  |  |  |  |  | 
| 650 |  |  |  |  |  |  | // Verify CRC-8 | 
| 651 | 156 |  |  |  |  |  | crc8 = buf[raw_header_len]; | 
| 652 | 156 | 50 |  |  |  |  | if ( _flac_crc8(buf, raw_header_len) != crc8 ) { | 
| 653 |  |  |  |  |  |  | DEBUG_TRACE("  CRC failed\n"); | 
| 654 | 0 |  |  |  |  |  | return 0; | 
| 655 |  |  |  |  |  |  | } | 
| 656 |  |  |  |  |  |  |  | 
| 657 |  |  |  |  |  |  | // Calculate sample number from frame number if needed | 
| 658 | 156 | 100 |  |  |  |  | if (frame_number) { | 
| 659 |  |  |  |  |  |  | // Fixed blocksize, use min_blocksize value as blocksize above may be different if last frame | 
| 660 | 152 |  |  |  |  |  | *first_sample = frame_number * flac->min_blocksize; | 
| 661 |  |  |  |  |  |  | } | 
| 662 |  |  |  |  |  |  | else { | 
| 663 | 4 |  |  |  |  |  | *first_sample = 0; | 
| 664 |  |  |  |  |  |  | } | 
| 665 |  |  |  |  |  |  |  | 
| 666 | 156 |  |  |  |  |  | *last_sample = *first_sample + blocksize; | 
| 667 |  |  |  |  |  |  |  | 
| 668 | 158 |  |  |  |  |  | return 1; | 
| 669 |  |  |  |  |  |  | } | 
| 670 |  |  |  |  |  |  |  | 
| 671 |  |  |  |  |  |  | void | 
| 672 | 15 |  |  |  |  |  | _flac_parse_streaminfo(flacinfo *flac) | 
| 673 |  |  |  |  |  |  | { | 
| 674 |  |  |  |  |  |  | uint64_t tmp; | 
| 675 |  |  |  |  |  |  | SV *md5; | 
| 676 |  |  |  |  |  |  | unsigned char *bptr; | 
| 677 |  |  |  |  |  |  | int i; | 
| 678 |  |  |  |  |  |  | uint32_t song_length_ms; | 
| 679 |  |  |  |  |  |  |  | 
| 680 | 15 |  |  |  |  |  | flac->min_blocksize = buffer_get_short(flac->buf); | 
| 681 | 15 |  |  |  |  |  | my_hv_store( flac->info, "minimum_blocksize", newSVuv(flac->min_blocksize) ); | 
| 682 |  |  |  |  |  |  |  | 
| 683 | 15 |  |  |  |  |  | flac->max_blocksize = buffer_get_short(flac->buf); | 
| 684 | 15 |  |  |  |  |  | my_hv_store( flac->info, "maximum_blocksize", newSVuv(flac->max_blocksize) ); | 
| 685 |  |  |  |  |  |  |  | 
| 686 | 15 |  |  |  |  |  | flac->min_framesize = buffer_get_int24(flac->buf); | 
| 687 | 15 |  |  |  |  |  | my_hv_store( flac->info, "minimum_framesize", newSVuv(flac->min_framesize) ); | 
| 688 |  |  |  |  |  |  |  | 
| 689 | 15 |  |  |  |  |  | flac->max_framesize = buffer_get_int24(flac->buf); | 
| 690 | 15 |  |  |  |  |  | my_hv_store( flac->info, "maximum_framesize", newSVuv(flac->max_framesize) ); | 
| 691 |  |  |  |  |  |  |  | 
| 692 | 15 | 100 |  |  |  |  | if ( !flac->max_framesize ) { | 
| 693 | 1 |  |  |  |  |  | flac->max_framesize = FLAC_MAX_FRAMESIZE; | 
| 694 |  |  |  |  |  |  | } | 
| 695 |  |  |  |  |  |  |  | 
| 696 | 15 |  |  |  |  |  | tmp = buffer_get_int64(flac->buf); | 
| 697 |  |  |  |  |  |  |  | 
| 698 | 15 |  |  |  |  |  | flac->samplerate      = (uint32_t)((tmp >> 44) & 0xFFFFF); | 
| 699 | 15 |  |  |  |  |  | flac->total_samples   = tmp & 0xFFFFFFFFFLL; | 
| 700 | 15 |  |  |  |  |  | flac->channels        = (uint32_t)(((tmp >> 41) & 0x7) + 1); | 
| 701 | 15 |  |  |  |  |  | flac->bits_per_sample = (uint32_t)(((tmp >> 36) & 0x1F) + 1); | 
| 702 |  |  |  |  |  |  |  | 
| 703 | 15 |  |  |  |  |  | my_hv_store( flac->info, "samplerate", newSVuv(flac->samplerate) ); | 
| 704 | 15 |  |  |  |  |  | my_hv_store( flac->info, "channels", newSVuv(flac->channels) ); | 
| 705 | 15 |  |  |  |  |  | my_hv_store( flac->info, "bits_per_sample", newSVuv(flac->bits_per_sample) ); | 
| 706 | 15 |  |  |  |  |  | my_hv_store( flac->info, "total_samples", newSVnv(flac->total_samples) ); | 
| 707 |  |  |  |  |  |  |  | 
| 708 | 15 |  |  |  |  |  | bptr = buffer_ptr(flac->buf); | 
| 709 | 15 |  |  |  |  |  | md5 = newSVpvf("%02x", bptr[0]); | 
| 710 |  |  |  |  |  |  |  | 
| 711 | 240 | 100 |  |  |  |  | for (i = 1; i < 16; i++) { | 
| 712 | 225 |  |  |  |  |  | sv_catpvf(md5, "%02x", bptr[i]); | 
| 713 |  |  |  |  |  |  | } | 
| 714 |  |  |  |  |  |  |  | 
| 715 | 15 |  |  |  |  |  | my_hv_store(flac->info, "audio_md5", md5); | 
| 716 | 15 |  |  |  |  |  | buffer_consume(flac->buf, 16); | 
| 717 |  |  |  |  |  |  |  | 
| 718 | 15 |  |  |  |  |  | song_length_ms = (uint32_t)(( (flac->total_samples * 1.0) / flac->samplerate) * 1000); | 
| 719 | 15 |  |  |  |  |  | my_hv_store( flac->info, "song_length_ms", newSVuv(song_length_ms) ); | 
| 720 | 15 |  |  |  |  |  | } | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | void | 
| 723 | 1 |  |  |  |  |  | _flac_parse_application(flacinfo *flac, int len) | 
| 724 |  |  |  |  |  |  | { | 
| 725 |  |  |  |  |  |  | HV *app; | 
| 726 | 1 |  |  |  |  |  | SV *id = newSVuv( buffer_get_int(flac->buf) ); | 
| 727 | 1 |  |  |  |  |  | SV *data = newSVpvn( buffer_ptr(flac->buf), len - 4 ); | 
| 728 | 1 |  |  |  |  |  | buffer_consume(flac->buf, len - 4); | 
| 729 |  |  |  |  |  |  |  | 
| 730 | 1 | 50 |  |  |  |  | if ( my_hv_exists(flac->tags, "APPLICATION") ) { | 
| 731 |  |  |  |  |  |  | // XXX needs test | 
| 732 | 0 |  |  |  |  |  | SV **entry = my_hv_fetch(flac->tags, "APPLICATION"); | 
| 733 | 0 | 0 |  |  |  |  | if (entry != NULL) { | 
| 734 | 0 |  |  |  |  |  | app = (HV *)SvRV(*entry); | 
| 735 | 0 |  |  |  |  |  | my_hv_store_ent(app, id, data); | 
| 736 |  |  |  |  |  |  | } | 
| 737 |  |  |  |  |  |  | } | 
| 738 |  |  |  |  |  |  | else { | 
| 739 | 1 |  |  |  |  |  | app = newHV(); | 
| 740 |  |  |  |  |  |  |  | 
| 741 | 1 |  |  |  |  |  | my_hv_store_ent(app, id, data); | 
| 742 |  |  |  |  |  |  |  | 
| 743 | 1 |  |  |  |  |  | my_hv_store( flac->tags, "APPLICATION", newRV_noinc( (SV *)app ) ); | 
| 744 |  |  |  |  |  |  | } | 
| 745 |  |  |  |  |  |  |  | 
| 746 | 1 |  |  |  |  |  | SvREFCNT_dec(id); | 
| 747 | 1 |  |  |  |  |  | } | 
| 748 |  |  |  |  |  |  |  | 
| 749 |  |  |  |  |  |  | void | 
| 750 | 3 |  |  |  |  |  | _flac_parse_seektable(flacinfo *flac, int len) | 
| 751 |  |  |  |  |  |  | { | 
| 752 |  |  |  |  |  |  | uint32_t i; | 
| 753 | 3 |  |  |  |  |  | uint32_t count = len / 18; | 
| 754 |  |  |  |  |  |  |  | 
| 755 | 3 |  |  |  |  |  | flac->num_seekpoints = count; | 
| 756 |  |  |  |  |  |  |  | 
| 757 | 3 | 50 |  |  |  |  | New(0, | 
| 758 |  |  |  |  |  |  | flac->seekpoints, | 
| 759 |  |  |  |  |  |  | count * sizeof(*flac->seekpoints), | 
| 760 |  |  |  |  |  |  | struct seekpoint | 
| 761 |  |  |  |  |  |  | ); | 
| 762 |  |  |  |  |  |  |  | 
| 763 | 36 | 100 |  |  |  |  | for (i = 0; i < count; i++) { | 
| 764 | 33 |  |  |  |  |  | flac->seekpoints[i].sample_number = buffer_get_int64(flac->buf); | 
| 765 | 33 |  |  |  |  |  | flac->seekpoints[i].stream_offset = buffer_get_int64(flac->buf); | 
| 766 | 33 |  |  |  |  |  | flac->seekpoints[i].frame_samples = buffer_get_short(flac->buf); | 
| 767 |  |  |  |  |  |  |  | 
| 768 |  |  |  |  |  |  | DEBUG_TRACE( | 
| 769 |  |  |  |  |  |  | "  sample_number %llu stream_offset %llu frame_samples %d\n", | 
| 770 |  |  |  |  |  |  | flac->seekpoints[i].sample_number, | 
| 771 |  |  |  |  |  |  | flac->seekpoints[i].stream_offset, | 
| 772 |  |  |  |  |  |  | flac->seekpoints[i].frame_samples | 
| 773 |  |  |  |  |  |  | ); | 
| 774 |  |  |  |  |  |  | } | 
| 775 | 3 |  |  |  |  |  | } | 
| 776 |  |  |  |  |  |  |  | 
| 777 |  |  |  |  |  |  | void | 
| 778 | 1 |  |  |  |  |  | _flac_parse_cuesheet(flacinfo *flac) | 
| 779 |  |  |  |  |  |  | { | 
| 780 | 1 |  |  |  |  |  | AV *cue = newAV(); | 
| 781 |  |  |  |  |  |  | unsigned char *bptr; | 
| 782 |  |  |  |  |  |  | uint64_t leadin; | 
| 783 |  |  |  |  |  |  | uint8_t is_cd; | 
| 784 |  |  |  |  |  |  | char decimal[21]; | 
| 785 |  |  |  |  |  |  | uint8_t num_tracks; | 
| 786 |  |  |  |  |  |  |  | 
| 787 |  |  |  |  |  |  | // Catalog number, may be empty | 
| 788 | 1 |  |  |  |  |  | bptr = buffer_ptr(flac->buf); | 
| 789 | 1 | 50 |  |  |  |  | if (bptr[0]) { | 
| 790 | 0 |  |  |  |  |  | av_push( cue, newSVpvf("CATALOG %s\n", bptr) ); | 
| 791 |  |  |  |  |  |  | } | 
| 792 | 1 |  |  |  |  |  | buffer_consume(flac->buf, 128); | 
| 793 |  |  |  |  |  |  |  | 
| 794 | 1 |  |  |  |  |  | leadin = buffer_get_int64(flac->buf); | 
| 795 | 1 |  |  |  |  |  | is_cd = (uint8_t)buffer_get_char(flac->buf); | 
| 796 |  |  |  |  |  |  |  | 
| 797 | 1 |  |  |  |  |  | buffer_consume(flac->buf, 258); | 
| 798 |  |  |  |  |  |  |  | 
| 799 | 1 |  |  |  |  |  | num_tracks = (uint8_t)buffer_get_char(flac->buf); | 
| 800 |  |  |  |  |  |  | DEBUG_TRACE("  number of cue tracks: %d\n", num_tracks); | 
| 801 |  |  |  |  |  |  |  | 
| 802 | 1 |  |  |  |  |  | av_push( cue, newSVpvf("FILE \"%s\" FLAC\n", flac->file) ); | 
| 803 |  |  |  |  |  |  |  | 
| 804 | 16 | 100 |  |  |  |  | while (num_tracks--) { | 
| 805 |  |  |  |  |  |  | char isrc[13]; | 
| 806 |  |  |  |  |  |  | uint8_t tmp; | 
| 807 |  |  |  |  |  |  | uint8_t type; | 
| 808 |  |  |  |  |  |  | uint8_t pre; | 
| 809 |  |  |  |  |  |  | uint8_t num_index; | 
| 810 |  |  |  |  |  |  |  | 
| 811 | 15 |  |  |  |  |  | uint64_t track_offset = buffer_get_int64(flac->buf); | 
| 812 | 15 |  |  |  |  |  | uint8_t  tracknum = (uint8_t)buffer_get_char(flac->buf); | 
| 813 |  |  |  |  |  |  |  | 
| 814 | 15 |  |  |  |  |  | buffer_get(flac->buf, isrc, 12); | 
| 815 | 15 |  |  |  |  |  | isrc[12] = '\0'; | 
| 816 |  |  |  |  |  |  |  | 
| 817 | 15 |  |  |  |  |  | tmp = (uint8_t)buffer_get_char(flac->buf); | 
| 818 | 15 |  |  |  |  |  | type = (tmp >> 7) & 0x1; | 
| 819 | 15 |  |  |  |  |  | pre  = (tmp >> 6) & 0x1; | 
| 820 | 15 |  |  |  |  |  | buffer_consume(flac->buf, 13); | 
| 821 |  |  |  |  |  |  |  | 
| 822 | 15 |  |  |  |  |  | num_index = (uint8_t)buffer_get_char(flac->buf); | 
| 823 |  |  |  |  |  |  |  | 
| 824 |  |  |  |  |  |  | DEBUG_TRACE("    track %d: offset %llu, type %d, pre %d, num_index %d\n", tracknum, track_offset, type, pre, num_index); | 
| 825 |  |  |  |  |  |  |  | 
| 826 | 15 | 50 |  |  |  |  | if (tracknum > 0 && tracknum < 100) { | 
|  |  | 100 |  |  |  |  |  | 
| 827 | 14 | 50 |  |  |  |  | av_push( cue, newSVpvf("  TRACK %02u %s\n", | 
| 828 |  |  |  |  |  |  | tracknum, type == 0 ? "AUDIO" : "DATA" | 
| 829 |  |  |  |  |  |  | ) ); | 
| 830 |  |  |  |  |  |  |  | 
| 831 | 14 | 100 |  |  |  |  | if (pre) { | 
| 832 | 1 |  |  |  |  |  | av_push( cue, newSVpv("    FLAGS PRE\n", 0) ); | 
| 833 |  |  |  |  |  |  | } | 
| 834 |  |  |  |  |  |  |  | 
| 835 | 14 | 100 |  |  |  |  | if (isrc[0]) { | 
| 836 | 1 |  |  |  |  |  | av_push( cue, newSVpvf("    ISRC %s\n", isrc) ); | 
| 837 |  |  |  |  |  |  | } | 
| 838 |  |  |  |  |  |  | } | 
| 839 |  |  |  |  |  |  |  | 
| 840 | 33 | 100 |  |  |  |  | while (num_index--) { | 
| 841 |  |  |  |  |  |  | SV *index; | 
| 842 |  |  |  |  |  |  |  | 
| 843 | 18 |  |  |  |  |  | uint64_t index_offset = buffer_get_int64(flac->buf); | 
| 844 | 18 |  |  |  |  |  | uint8_t index_num = (uint8_t)buffer_get_char(flac->buf); | 
| 845 | 18 |  |  |  |  |  | buffer_consume(flac->buf, 3); | 
| 846 |  |  |  |  |  |  |  | 
| 847 |  |  |  |  |  |  | DEBUG_TRACE("      index %d, offset %llu\n", index_num, index_offset); | 
| 848 |  |  |  |  |  |  |  | 
| 849 | 18 |  |  |  |  |  | index = newSVpvf("    INDEX %02u ", index_num); | 
| 850 |  |  |  |  |  |  |  | 
| 851 | 18 | 50 |  |  |  |  | if (is_cd) { | 
| 852 | 18 |  |  |  |  |  | uint64_t frame = ((track_offset + index_offset) / (flac->samplerate / 75)); | 
| 853 |  |  |  |  |  |  | uint8_t m, s, f; | 
| 854 |  |  |  |  |  |  |  | 
| 855 | 18 |  |  |  |  |  | f = (uint8_t)(frame % 75); | 
| 856 | 18 |  |  |  |  |  | frame /= 75; | 
| 857 | 18 |  |  |  |  |  | s = (uint8_t)(frame % 60); | 
| 858 | 18 |  |  |  |  |  | frame /= 60; | 
| 859 | 18 |  |  |  |  |  | m = (uint8_t)frame; | 
| 860 |  |  |  |  |  |  |  | 
| 861 | 18 |  |  |  |  |  | sv_catpvf(index, "%02u:%02u:%02u\n", m, s, f); | 
| 862 |  |  |  |  |  |  | } | 
| 863 |  |  |  |  |  |  | else { | 
| 864 |  |  |  |  |  |  | // XXX need test | 
| 865 | 0 |  |  |  |  |  | sprintf(decimal, "%"PRIu64, track_offset + index_offset); | 
| 866 | 0 |  |  |  |  |  | sv_catpvf(index, "%s\n", decimal); | 
| 867 |  |  |  |  |  |  | } | 
| 868 |  |  |  |  |  |  |  | 
| 869 | 18 |  |  |  |  |  | av_push( cue, index ); | 
| 870 |  |  |  |  |  |  | } | 
| 871 |  |  |  |  |  |  |  | 
| 872 | 15 | 100 |  |  |  |  | if (tracknum == 170) { | 
| 873 |  |  |  |  |  |  | // Add lead-in and lead-out | 
| 874 | 1 |  |  |  |  |  | sprintf(decimal, "%"PRIu64, leadin); | 
| 875 | 1 |  |  |  |  |  | av_push( cue, newSVpvf("REM FLAC__lead-in %s\n", decimal) ); | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | // XXX is tracknum right here? | 
| 878 | 1 |  |  |  |  |  | sprintf(decimal, "%"PRIu64, track_offset); | 
| 879 | 15 |  |  |  |  |  | av_push( cue, newSVpvf("REM FLAC__lead-out %u %s\n", tracknum, decimal) ); | 
| 880 |  |  |  |  |  |  | } | 
| 881 |  |  |  |  |  |  | } | 
| 882 |  |  |  |  |  |  |  | 
| 883 | 1 |  |  |  |  |  | my_hv_store( flac->tags, "CUESHEET_BLOCK", newRV_noinc( (SV *)cue ) ); | 
| 884 | 1 |  |  |  |  |  | } | 
| 885 |  |  |  |  |  |  |  | 
| 886 |  |  |  |  |  |  | int | 
| 887 | 2 |  |  |  |  |  | _flac_parse_picture(flacinfo *flac) | 
| 888 |  |  |  |  |  |  | { | 
| 889 |  |  |  |  |  |  | AV *pictures; | 
| 890 |  |  |  |  |  |  | HV *picture; | 
| 891 | 2 |  |  |  |  |  | int ret = 1; | 
| 892 |  |  |  |  |  |  | uint32_t pic_length; | 
| 893 |  |  |  |  |  |  |  | 
| 894 | 2 |  |  |  |  |  | picture = _decode_flac_picture(flac->infile, flac->buf, &pic_length); | 
| 895 | 2 | 50 |  |  |  |  | if ( !picture ) { | 
| 896 | 0 |  |  |  |  |  | PerlIO_printf(PerlIO_stderr(), "Invalid FLAC file: %s, bad picture block\n", flac->file); | 
| 897 | 0 |  |  |  |  |  | ret = 0; | 
| 898 | 0 |  |  |  |  |  | goto out; | 
| 899 |  |  |  |  |  |  | } | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | // Skip past pic data if necessary | 
| 902 | 2 | 100 |  |  |  |  | if ( _env_true("AUDIO_SCAN_NO_ARTWORK") ) { | 
| 903 | 1 |  |  |  |  |  | my_hv_store( picture, "offset", newSVuv(flac->audio_offset - pic_length) ); | 
| 904 | 1 |  |  |  |  |  | _flac_skip(flac, pic_length); | 
| 905 |  |  |  |  |  |  | } | 
| 906 |  |  |  |  |  |  | else { | 
| 907 | 1 |  |  |  |  |  | buffer_consume(flac->buf, pic_length); | 
| 908 |  |  |  |  |  |  | } | 
| 909 |  |  |  |  |  |  |  | 
| 910 |  |  |  |  |  |  | DEBUG_TRACE("  found picture of length %d\n", pic_length); | 
| 911 |  |  |  |  |  |  |  | 
| 912 | 2 | 50 |  |  |  |  | if ( my_hv_exists(flac->tags, "ALLPICTURES") ) { | 
| 913 | 0 |  |  |  |  |  | SV **entry = my_hv_fetch(flac->tags, "ALLPICTURES"); | 
| 914 | 0 | 0 |  |  |  |  | if (entry != NULL) { | 
| 915 | 0 |  |  |  |  |  | pictures = (AV *)SvRV(*entry); | 
| 916 | 0 |  |  |  |  |  | av_push( pictures, newRV_noinc( (SV *)picture ) ); | 
| 917 |  |  |  |  |  |  | } | 
| 918 |  |  |  |  |  |  | } | 
| 919 |  |  |  |  |  |  | else { | 
| 920 | 2 |  |  |  |  |  | pictures = newAV(); | 
| 921 |  |  |  |  |  |  |  | 
| 922 | 2 |  |  |  |  |  | av_push( pictures, newRV_noinc( (SV *)picture ) ); | 
| 923 |  |  |  |  |  |  |  | 
| 924 | 2 |  |  |  |  |  | my_hv_store( flac->tags, "ALLPICTURES", newRV_noinc( (SV *)pictures ) ); | 
| 925 |  |  |  |  |  |  | } | 
| 926 |  |  |  |  |  |  |  | 
| 927 |  |  |  |  |  |  | out: | 
| 928 | 2 |  |  |  |  |  | return ret; | 
| 929 |  |  |  |  |  |  | } | 
| 930 |  |  |  |  |  |  |  | 
| 931 |  |  |  |  |  |  | /* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ | 
| 932 |  |  |  |  |  |  | uint8_t const _flac_crc8_table[256] = { | 
| 933 |  |  |  |  |  |  | 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, | 
| 934 |  |  |  |  |  |  | 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, | 
| 935 |  |  |  |  |  |  | 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, | 
| 936 |  |  |  |  |  |  | 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, | 
| 937 |  |  |  |  |  |  | 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, | 
| 938 |  |  |  |  |  |  | 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, | 
| 939 |  |  |  |  |  |  | 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, | 
| 940 |  |  |  |  |  |  | 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, | 
| 941 |  |  |  |  |  |  | 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, | 
| 942 |  |  |  |  |  |  | 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, | 
| 943 |  |  |  |  |  |  | 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, | 
| 944 |  |  |  |  |  |  | 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, | 
| 945 |  |  |  |  |  |  | 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, | 
| 946 |  |  |  |  |  |  | 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, | 
| 947 |  |  |  |  |  |  | 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, | 
| 948 |  |  |  |  |  |  | 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, | 
| 949 |  |  |  |  |  |  | 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, | 
| 950 |  |  |  |  |  |  | 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, | 
| 951 |  |  |  |  |  |  | 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, | 
| 952 |  |  |  |  |  |  | 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, | 
| 953 |  |  |  |  |  |  | 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, | 
| 954 |  |  |  |  |  |  | 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, | 
| 955 |  |  |  |  |  |  | 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, | 
| 956 |  |  |  |  |  |  | 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, | 
| 957 |  |  |  |  |  |  | 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, | 
| 958 |  |  |  |  |  |  | 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, | 
| 959 |  |  |  |  |  |  | 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, | 
| 960 |  |  |  |  |  |  | 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, | 
| 961 |  |  |  |  |  |  | 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, | 
| 962 |  |  |  |  |  |  | 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, | 
| 963 |  |  |  |  |  |  | 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, | 
| 964 |  |  |  |  |  |  | 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 | 
| 965 |  |  |  |  |  |  | }; | 
| 966 |  |  |  |  |  |  |  | 
| 967 |  |  |  |  |  |  | uint8_t | 
| 968 | 156 |  |  |  |  |  | _flac_crc8(const unsigned char *buf, unsigned len) | 
| 969 |  |  |  |  |  |  | { | 
| 970 | 156 |  |  |  |  |  | uint8_t crc = 0; | 
| 971 |  |  |  |  |  |  |  | 
| 972 | 938 | 100 |  |  |  |  | while(len--) | 
| 973 | 782 |  |  |  |  |  | crc = _flac_crc8_table[crc ^ *buf++]; | 
| 974 |  |  |  |  |  |  |  | 
| 975 | 156 |  |  |  |  |  | return crc; | 
| 976 |  |  |  |  |  |  | } | 
| 977 |  |  |  |  |  |  |  | 
| 978 |  |  |  |  |  |  | int | 
| 979 | 2 |  |  |  |  |  | _flac_read_utf8_uint64(unsigned char *raw, uint64_t *val, uint8_t *rawlen) | 
| 980 |  |  |  |  |  |  | { | 
| 981 | 2 |  |  |  |  |  | uint64_t v = 0; | 
| 982 |  |  |  |  |  |  | uint32_t x; | 
| 983 |  |  |  |  |  |  | unsigned i; | 
| 984 |  |  |  |  |  |  |  | 
| 985 | 2 |  |  |  |  |  | x = raw[(*rawlen)++]; | 
| 986 |  |  |  |  |  |  |  | 
| 987 | 2 | 50 |  |  |  |  | if(!(x & 0x80)) { /* 0xxxxxxx */ | 
| 988 | 0 |  |  |  |  |  | v = x; | 
| 989 | 0 |  |  |  |  |  | i = 0; | 
| 990 |  |  |  |  |  |  | } | 
| 991 | 2 | 50 |  |  |  |  | else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ | 
|  |  | 50 |  |  |  |  |  | 
| 992 | 2 |  |  |  |  |  | v = x & 0x1F; | 
| 993 | 2 |  |  |  |  |  | i = 1; | 
| 994 |  |  |  |  |  |  | } | 
| 995 | 0 | 0 |  |  |  |  | else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ | 
|  |  | 0 |  |  |  |  |  | 
| 996 | 0 |  |  |  |  |  | v = x & 0x0F; | 
| 997 | 0 |  |  |  |  |  | i = 2; | 
| 998 |  |  |  |  |  |  | } | 
| 999 | 0 | 0 |  |  |  |  | else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1000 | 0 |  |  |  |  |  | v = x & 0x07; | 
| 1001 | 0 |  |  |  |  |  | i = 3; | 
| 1002 |  |  |  |  |  |  | } | 
| 1003 | 0 | 0 |  |  |  |  | else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1004 | 0 |  |  |  |  |  | v = x & 0x03; | 
| 1005 | 0 |  |  |  |  |  | i = 4; | 
| 1006 |  |  |  |  |  |  | } | 
| 1007 | 0 | 0 |  |  |  |  | else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ | 
|  |  | 0 |  |  |  |  |  | 
| 1008 | 0 |  |  |  |  |  | v = x & 0x01; | 
| 1009 | 0 |  |  |  |  |  | i = 5; | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 | 0 | 0 |  |  |  |  | else if(x & 0xFE && !(x & 0x01)) { /* 11111110 */ | 
|  |  | 0 |  |  |  |  |  | 
| 1012 | 0 |  |  |  |  |  | v = 0; | 
| 1013 | 0 |  |  |  |  |  | i = 6; | 
| 1014 |  |  |  |  |  |  | } | 
| 1015 |  |  |  |  |  |  | else { | 
| 1016 | 0 |  |  |  |  |  | *val = 0xffffffffffffffffULL; | 
| 1017 | 0 |  |  |  |  |  | return 1; | 
| 1018 |  |  |  |  |  |  | } | 
| 1019 |  |  |  |  |  |  |  | 
| 1020 | 2 | 50 |  |  |  |  | for( ; i; i--) { | 
| 1021 | 2 |  |  |  |  |  | x = raw[(*rawlen)++]; | 
| 1022 | 2 | 50 |  |  |  |  | if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1023 | 2 |  |  |  |  |  | *val = 0xffffffffffffffffULL; | 
| 1024 | 2 |  |  |  |  |  | return 1; | 
| 1025 |  |  |  |  |  |  | } | 
| 1026 | 0 |  |  |  |  |  | v <<= 6; | 
| 1027 | 0 |  |  |  |  |  | v |= (x & 0x3F); | 
| 1028 |  |  |  |  |  |  | } | 
| 1029 | 0 |  |  |  |  |  | *val = v; | 
| 1030 | 0 |  |  |  |  |  | return 1; | 
| 1031 |  |  |  |  |  |  | } | 
| 1032 |  |  |  |  |  |  |  | 
| 1033 |  |  |  |  |  |  | int | 
| 1034 | 156 |  |  |  |  |  | _flac_read_utf8_uint32(unsigned char *raw, uint32_t *val, uint8_t *rawlen) | 
| 1035 |  |  |  |  |  |  | { | 
| 1036 | 156 |  |  |  |  |  | uint32_t v = 0; | 
| 1037 |  |  |  |  |  |  | uint32_t x; | 
| 1038 |  |  |  |  |  |  | unsigned i; | 
| 1039 |  |  |  |  |  |  |  | 
| 1040 | 156 |  |  |  |  |  | x = raw[(*rawlen)++]; | 
| 1041 |  |  |  |  |  |  |  | 
| 1042 | 156 | 50 |  |  |  |  | if(!(x & 0x80)) { /* 0xxxxxxx */ | 
| 1043 | 156 |  |  |  |  |  | v = x; | 
| 1044 | 156 |  |  |  |  |  | i = 0; | 
| 1045 |  |  |  |  |  |  | } | 
| 1046 | 0 | 0 |  |  |  |  | else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1047 | 0 |  |  |  |  |  | v = x & 0x1F; | 
| 1048 | 0 |  |  |  |  |  | i = 1; | 
| 1049 |  |  |  |  |  |  | } | 
| 1050 | 0 | 0 |  |  |  |  | else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1051 | 0 |  |  |  |  |  | v = x & 0x0F; | 
| 1052 | 0 |  |  |  |  |  | i = 2; | 
| 1053 |  |  |  |  |  |  | } | 
| 1054 | 0 | 0 |  |  |  |  | else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1055 | 0 |  |  |  |  |  | v = x & 0x07; | 
| 1056 | 0 |  |  |  |  |  | i = 3; | 
| 1057 |  |  |  |  |  |  | } | 
| 1058 | 0 | 0 |  |  |  |  | else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1059 | 0 |  |  |  |  |  | v = x & 0x03; | 
| 1060 | 0 |  |  |  |  |  | i = 4; | 
| 1061 |  |  |  |  |  |  | } | 
| 1062 | 0 | 0 |  |  |  |  | else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ | 
|  |  | 0 |  |  |  |  |  | 
| 1063 | 0 |  |  |  |  |  | v = x & 0x01; | 
| 1064 | 0 |  |  |  |  |  | i = 5; | 
| 1065 |  |  |  |  |  |  | } | 
| 1066 |  |  |  |  |  |  | else { | 
| 1067 | 0 |  |  |  |  |  | *val = 0xffffffff; | 
| 1068 | 0 |  |  |  |  |  | return 1; | 
| 1069 |  |  |  |  |  |  | } | 
| 1070 |  |  |  |  |  |  |  | 
| 1071 | 156 | 50 |  |  |  |  | for( ; i; i--) { | 
| 1072 | 0 |  |  |  |  |  | x = raw[(*rawlen)++]; | 
| 1073 | 0 | 0 |  |  |  |  | if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ | 
|  |  | 0 |  |  |  |  |  | 
| 1074 | 0 |  |  |  |  |  | *val = 0xffffffff; | 
| 1075 | 0 |  |  |  |  |  | return 1; | 
| 1076 |  |  |  |  |  |  | } | 
| 1077 | 0 |  |  |  |  |  | v <<= 6; | 
| 1078 | 0 |  |  |  |  |  | v |= (x & 0x3F); | 
| 1079 |  |  |  |  |  |  | } | 
| 1080 | 156 |  |  |  |  |  | *val = v; | 
| 1081 | 156 |  |  |  |  |  | return 1; | 
| 1082 |  |  |  |  |  |  | } | 
| 1083 |  |  |  |  |  |  |  | 
| 1084 |  |  |  |  |  |  | void | 
| 1085 | 15 |  |  |  |  |  | _flac_skip(flacinfo *flac, uint32_t size) | 
| 1086 |  |  |  |  |  |  | { | 
| 1087 | 15 | 100 |  |  |  |  | if ( buffer_len(flac->buf) >= size ) { | 
| 1088 | 2 |  |  |  |  |  | buffer_consume(flac->buf, size); | 
| 1089 |  |  |  |  |  |  |  | 
| 1090 |  |  |  |  |  |  | DEBUG_TRACE("  skipped buffer data size %d\n", size); | 
| 1091 |  |  |  |  |  |  | } | 
| 1092 |  |  |  |  |  |  | else { | 
| 1093 | 13 |  |  |  |  |  | PerlIO_seek(flac->infile, size - buffer_len(flac->buf), SEEK_CUR); | 
| 1094 | 13 |  |  |  |  |  | buffer_clear(flac->buf); | 
| 1095 |  |  |  |  |  |  |  | 
| 1096 |  |  |  |  |  |  | DEBUG_TRACE("  seeked past %d bytes to %d\n", size, (int)PerlIO_tell(flac->infile)); | 
| 1097 |  |  |  |  |  |  | } | 
| 1098 | 15 |  |  |  |  |  | } |