static int image_gif_read_buf(GifFileType *gif, GifByteType *data, int len) { image *im = (image *)gif->UserData; //DEBUG_TRACE("GIF read_buf wants %d bytes, %d in buffer\n", len, buffer_len(im->buf)); if (im->fh != NULL) { if ( !_check_buf(im->fh, im->buf, len, MAX(len, BUFFER_SIZE)) ) { warn("Image::Scale not enough GIF data (%s)\n", SvPVX(im->path)); return 0; } } else { if (len > buffer_len(im->buf)) { // read from SV into buffer int sv_readlen = len - buffer_len(im->buf); if (sv_readlen > sv_len(im->sv_data) - im->sv_offset) { warn("Image::Scale not enough GIF data (%s)\n", SvPVX(im->path)); return 0; } DEBUG_TRACE(" Reading %d bytes of SV data @ %d\n", sv_readlen, im->sv_offset); buffer_append(im->buf, SvPVX(im->sv_data) + im->sv_offset, sv_readlen); im->sv_offset += sv_readlen; } } memcpy(data, buffer_ptr(im->buf), len); buffer_consume(im->buf, len); return len; }
int _wavpack_parse_old(wvpinfo *wvp) { int ret = 1; char chunk_id[5]; uint32_t chunk_size; WavpackHeader3 wphdr; WaveHeader3 wavhdr; unsigned char *bptr; uint32_t total_samples; uint32_t song_length_ms; Zero(&wavhdr, sizeof(wavhdr), char); Zero(&wphdr, sizeof(wphdr), char); DEBUG_TRACE("Parsing old WavPack version\n"); // Verify RIFF header if ( strncmp( (char *)buffer_ptr(wvp->buf), "RIFF", 4 ) ) { PerlIO_printf(PerlIO_stderr(), "Invalid WavPack file: missing RIFF header: %s\n", wvp->file); ret = 0; goto out; } buffer_consume(wvp->buf, 4); chunk_size = buffer_get_int_le(wvp->buf); // Check format if ( strncmp( (char *)buffer_ptr(wvp->buf), "WAVE", 4 ) ) { PerlIO_printf(PerlIO_stderr(), "Invalid WavPack file: missing WAVE header: %s\n", wvp->file); ret = 0; goto out; } buffer_consume(wvp->buf, 4); wvp->file_offset += 12; // Verify we have at least 8 bytes if ( !_check_buf(wvp->infile, wvp->buf, 8, WAVPACK_BLOCK_SIZE) ) { ret = 0; goto out; } // loop through all chunks, read fmt, and break at data while ( buffer_len(wvp->buf) >= 8 ) { strncpy( chunk_id, (char *)buffer_ptr(wvp->buf), 4 ); chunk_id[4] = '\0'; buffer_consume(wvp->buf, 4); chunk_size = buffer_get_int_le(wvp->buf); wvp->file_offset += 8; // Adjust for padding if ( chunk_size % 2 ) { chunk_size++; } DEBUG_TRACE(" %s size %d\n", chunk_id, chunk_size); if ( !strcmp( chunk_id, "data" ) ) { break; } wvp->file_offset += chunk_size; if ( !strcmp( chunk_id, "fmt " ) ) { if ( !_check_buf(wvp->infile, wvp->buf, chunk_size, WAV_BLOCK_SIZE) ) { ret = 0; goto out; } if (chunk_size < sizeof(wavhdr)) { ret = 0; goto out; } // Read wav header wavhdr.FormatTag = buffer_get_short_le(wvp->buf); wavhdr.NumChannels = buffer_get_short_le(wvp->buf); wavhdr.SampleRate = buffer_get_int_le(wvp->buf); wavhdr.BytesPerSecond = buffer_get_int_le(wvp->buf); wavhdr.BlockAlign = buffer_get_short_le(wvp->buf); wavhdr.BitsPerSample = buffer_get_short_le(wvp->buf); // Skip rest of fmt chunk if necessary if (chunk_size > 16) { _wavpack_skip(wvp, chunk_size - 16); } } else { // Skip it _wavpack_skip(wvp, chunk_size); } // Verify we have at least 8 bytes if ( !_check_buf(wvp->infile, wvp->buf, 8, WAVPACK_BLOCK_SIZE) ) { ret = 0; goto out; } } // Verify wav header, this code comes from unpack3.c if ( wavhdr.FormatTag != 1 || !wavhdr.NumChannels || wavhdr.NumChannels > 2 || !wavhdr.SampleRate || wavhdr.BitsPerSample < 16 || wavhdr.BitsPerSample > 24 || wavhdr.BlockAlign / wavhdr.NumChannels > 3 || wavhdr.BlockAlign % wavhdr.NumChannels || wavhdr.BlockAlign / wavhdr.NumChannels < (wavhdr.BitsPerSample + 7) / 8 ) { ret = 0; goto out; } // chunk_size here is the size of the data chunk total_samples = chunk_size / wavhdr.NumChannels / ((wavhdr.BitsPerSample > 16) ? 3 : 2); // read WavpackHeader3 (differs for each version) bptr = buffer_ptr(wvp->buf); if ( bptr[0] != 'w' || bptr[1] != 'v' || bptr[2] != 'p' || bptr[3] != 'k' ) { PerlIO_printf(PerlIO_stderr(), "Invalid WavPack file: missing wvpk header: %s\n", wvp->file); ret = 0; goto out; } buffer_consume(wvp->buf, 4); wphdr.ckSize = buffer_get_int_le(wvp->buf); wphdr.version = buffer_get_short_le(wvp->buf); if (wphdr.version >= 2) { wphdr.bits = buffer_get_short_le(wvp->buf); } if (wphdr.version == 3) { wphdr.flags = buffer_get_short_le(wvp->buf); wphdr.shift = buffer_get_short_le(wvp->buf); wphdr.total_samples = buffer_get_int_le(wvp->buf); total_samples = wphdr.total_samples; } DEBUG_TRACE("wvpk header @ %llu:\n", wvp->file_offset); DEBUG_TRACE(" size: %u\n", wphdr.ckSize); DEBUG_TRACE(" version: %d\n", wphdr.version); DEBUG_TRACE(" bits: 0x%x\n", wphdr.bits); DEBUG_TRACE(" flags: 0x%x\n", wphdr.flags); DEBUG_TRACE(" shift: 0x%x\n", wphdr.shift); DEBUG_TRACE(" total_samples: %d\n", wphdr.total_samples); my_hv_store( wvp->info, "encoder_version", newSVuv(wphdr.version) ); my_hv_store( wvp->info, "bits_per_sample", newSVuv(wavhdr.BitsPerSample) ); my_hv_store( wvp->info, "channels", newSVuv(wavhdr.NumChannels) ); my_hv_store( wvp->info, "samplerate", newSVuv(wavhdr.SampleRate) ); my_hv_store( wvp->info, "total_samples", newSVuv(total_samples) ); song_length_ms = ((total_samples * 1.0) / wavhdr.SampleRate) * 1000; my_hv_store( wvp->info, "song_length_ms", newSVuv(song_length_ms) ); my_hv_store( wvp->info, "bitrate", newSVuv( _bitrate(wvp->file_size - wvp->audio_offset, song_length_ms) ) ); out: return ret; }
int image_init(HV *self, image *im) { unsigned char *bptr; char *file = NULL; int ret = 1; if (my_hv_exists(self, "file")) { // Input from file SV *path = *(my_hv_fetch(self, "file")); file = SvPVX(path); im->fh = IoIFP(sv_2io(*(my_hv_fetch(self, "_fh")))); im->path = newSVsv(path); } else { // Input from scalar ref im->fh = NULL; im->path = newSVpv("(data)", 0); im->sv_data = *(my_hv_fetch(self, "data")); if (SvROK(im->sv_data)) im->sv_data = SvRV(im->sv_data); else croak("data is not a scalar ref\n"); } im->pixbuf = NULL; im->outbuf = NULL; im->outbuf_size = 0; im->type = UNKNOWN; im->sv_offset = 0; im->image_offset = 0; im->image_length = 0; im->width = 0; im->height = 0; im->width_padding = 0; im->width_inner = 0; im->height_padding = 0; im->height_inner = 0; im->flipped = 0; im->bpp = 0; im->channels = 0; im->has_alpha = 0; im->orientation = ORIENTATION_NORMAL; im->orientation_orig = ORIENTATION_NORMAL; im->memory_limit = 0; im->target_width = 0; im->target_height = 0; im->keep_aspect = 0; im->resize_type = IMAGE_SCALE_TYPE_GD_FIXED; im->filter = 0; im->bgcolor = 0; im->used = 0; im->palette = NULL; #ifdef HAVE_JPEG im->cinfo = NULL; #endif #ifdef HAVE_PNG im->png_ptr = NULL; im->info_ptr = NULL; #endif #ifdef HAVE_GIF im->gif = NULL; #endif // Read new() options if (my_hv_exists(self, "offset")) { im->image_offset = SvIV(*(my_hv_fetch(self, "offset"))); if (im->fh != NULL) PerlIO_seek(im->fh, im->image_offset, SEEK_SET); } if (my_hv_exists(self, "length")) im->image_length = SvIV(*(my_hv_fetch(self, "length"))); Newz(0, im->buf, sizeof(Buffer), Buffer); buffer_init(im->buf, BUFFER_SIZE); im->memory_used = BUFFER_SIZE; // Determine type of file from magic bytes if (im->fh != NULL) { if ( !_check_buf(im->fh, im->buf, 8, BUFFER_SIZE) ) { image_finish(im); croak("Unable to read image header for %s\n", file); } } else { im->sv_offset = MIN(sv_len(im->sv_data) - im->image_offset, BUFFER_SIZE); buffer_append(im->buf, SvPVX(im->sv_data) + im->image_offset, im->sv_offset); } bptr = buffer_ptr(im->buf); switch (bptr[0]) { case 0xff: if (bptr[1] == 0xd8 && bptr[2] == 0xff) { #ifdef HAVE_JPEG im->type = JPEG; #else image_finish(im); croak("Image::Scale was not built with JPEG support\n"); #endif } break; case 0x89: if (bptr[1] == 'P' && bptr[2] == 'N' && bptr[3] == 'G' && bptr[4] == 0x0d && bptr[5] == 0x0a && bptr[6] == 0x1a && bptr[7] == 0x0a) { #ifdef HAVE_PNG im->type = PNG; #else image_finish(im); croak("Image::Scale was not built with PNG support\n"); #endif } break; case 'G': if (bptr[1] == 'I' && bptr[2] == 'F' && bptr[3] == '8' && (bptr[4] == '7' || bptr[4] == '9') && bptr[5] == 'a') { #ifdef HAVE_GIF im->type = GIF; #else image_finish(im); croak("Image::Scale was not built with GIF support\n"); #endif } break; case 'B': if (bptr[1] == 'M') { im->type = BMP; } break; } DEBUG_TRACE("Image type: %d\n", im->type); // Read image header via type-specific function to determine dimensions switch (im->type) { #ifdef HAVE_JPEG case JPEG: if ( !image_jpeg_read_header(im) ) { ret = 0; goto out; } break; #endif #ifdef HAVE_PNG case PNG: if ( !image_png_read_header(im) ) { ret = 0; goto out; } break; #endif #ifdef HAVE_GIF case GIF: if ( !image_gif_read_header(im) ) { ret = 0; goto out; } break; #endif case BMP: image_bmp_read_header(im); break; case UNKNOWN: warn("Image::Scale unknown file type (%s), first 8 bytes were: %02x %02x %02x %02x %02x %02x %02x %02x\n", SvPVX(im->path), bptr[0], bptr[1], bptr[2], bptr[3], bptr[4], bptr[5], bptr[6], bptr[7]); ret = 0; break; } DEBUG_TRACE("Image dimenensions: %d x %d, channels %d\n", im->width, im->height, im->channels); out: if (ret == 0) image_finish(im); return ret; }
wvpinfo * _wavpack_parse(PerlIO *infile, char *file, HV *info, uint8_t seeking) { int err = 0; int done = 0; u_char *bptr; wvpinfo *wvp; Newz(0, wvp, sizeof(wvpinfo), wvpinfo); Newz(0, wvp->buf, sizeof(Buffer), Buffer); Newz(0, wvp->header, sizeof(WavpackHeader), WavpackHeader); wvp->infile = infile; wvp->file = file; wvp->info = info; wvp->file_offset = 0; wvp->audio_offset = 0; wvp->seeking = seeking ? 1 : 0; buffer_init(wvp->buf, WAVPACK_BLOCK_SIZE); wvp->file_size = _file_size(infile); my_hv_store( info, "file_size", newSVuv(wvp->file_size) ); // Loop through each wvpk block until we find a good one while (!done) { if ( !_check_buf(infile, wvp->buf, 32, WAVPACK_BLOCK_SIZE) ) { err = -1; goto out; } bptr = buffer_ptr(wvp->buf); // If first byte is 'R', assume old version if ( bptr[0] == 'R' ) { if ( !_wavpack_parse_old(wvp) ) { err = -1; goto out; } break; } // May need to read past some junk before wvpk header while ( bptr[0] != 'w' || bptr[1] != 'v' || bptr[2] != 'p' || bptr[3] != 'k' ) { buffer_consume(wvp->buf, 1); wvp->audio_offset++; if ( !buffer_len(wvp->buf) ) { if ( !_check_buf(infile, wvp->buf, 32, WAVPACK_BLOCK_SIZE) ) { PerlIO_printf(PerlIO_stderr(), "Unable to find a valid WavPack block in file: %s\n", file); err = -1; goto out; } } bptr = buffer_ptr(wvp->buf); } if ( _wavpack_parse_block(wvp) ) { done = 1; } } my_hv_store( info, "audio_offset", newSVuv(wvp->audio_offset) ); my_hv_store( info, "audio_size", newSVuv(wvp->file_size - wvp->audio_offset) ); out: buffer_free(wvp->buf); Safefree(wvp->buf); Safefree(wvp->header); return wvp; }
int _wavpack_parse_block(wvpinfo *wvp) { unsigned char *bptr; uint16_t remaining; bptr = buffer_ptr(wvp->buf); // Verify wvpk signature if ( bptr[0] != 'w' || bptr[1] != 'v' || bptr[2] != 'p' || bptr[3] != 'k' ) { DEBUG_TRACE("Invalid wvpk header at %llu\n", wvp->file_offset); return 1; } buffer_consume(wvp->buf, 4); wvp->header->ckSize = buffer_get_int_le(wvp->buf); wvp->header->version = buffer_get_short_le(wvp->buf); wvp->header->track_no = buffer_get_char(wvp->buf); wvp->header->index_no = buffer_get_char(wvp->buf); wvp->header->total_samples = buffer_get_int_le(wvp->buf); wvp->header->block_index = buffer_get_int_le(wvp->buf); wvp->header->block_samples = buffer_get_int_le(wvp->buf); wvp->header->flags = buffer_get_int_le(wvp->buf); wvp->header->crc = buffer_get_int_le(wvp->buf); DEBUG_TRACE("wvpk header @ %llu:\n", wvp->file_offset); DEBUG_TRACE(" size: %u\n", wvp->header->ckSize); DEBUG_TRACE(" version: 0x%x\n", wvp->header->version); DEBUG_TRACE(" track_no: 0x%x\n", wvp->header->track_no); DEBUG_TRACE(" index_no: 0x%x\n", wvp->header->index_no); DEBUG_TRACE(" total_samples: %u\n", wvp->header->total_samples); DEBUG_TRACE(" block_index: %u\n", wvp->header->block_index); DEBUG_TRACE(" block_samples: %u\n", wvp->header->block_samples); DEBUG_TRACE(" flags: 0x%x\n", wvp->header->flags); DEBUG_TRACE(" crc: 0x%x\n", wvp->header->crc); wvp->file_offset += 32; my_hv_store( wvp->info, "encoder_version", newSVuv(wvp->header->version) ); if (wvp->header->version < 0x4) { // XXX old version and not handled by 'R' check above for old version PerlIO_printf(PerlIO_stderr(), "Unsupported old WavPack version: 0x%x\n", wvp->header->version); return 1; } // Read data from flags my_hv_store( wvp->info, "bits_per_sample", newSVuv( 8 * ((wvp->header->flags & 0x3) + 1) ) ); // Encoding mode my_hv_store( wvp->info, (wvp->header->flags & 0x8) ? "hybrid" : "lossless", newSVuv(1) ); { // samplerate, may be overridden by a later ID_SAMPLE_RATE metadata block uint32_t samplerate_index = (wvp->header->flags & 0x7800000) >> 23; if ( samplerate_index >= 0 && samplerate_index < 0xF ) { my_hv_store( wvp->info, "samplerate", newSVuv( wavpack_sample_rates[samplerate_index] ) ); } else { // Default to 44.1 just in case my_hv_store( wvp->info, "samplerate", newSVuv(44100) ); } } // Channels, may be overridden by a later ID_CHANNEL_INFO metadata block my_hv_store( wvp->info, "channels", newSVuv( (wvp->header->flags & 0x4) ? 1 : 2 ) ); // Parse metadata sub-blocks remaining = wvp->header->ckSize - 24; // ckSize is 8 less than the block size // If block_samples is 0, we need to skip to the next block if ( !wvp->header->block_samples ) { wvp->file_offset += remaining; _wavpack_skip(wvp, remaining); return 0; } while (remaining > 0) { // Read sub-block header (2-4 bytes) unsigned char id; uint32_t size; DEBUG_TRACE("remaining: %d\n", remaining); if ( !_check_buf(wvp->infile, wvp->buf, 4, WAVPACK_BLOCK_SIZE) ) { return 0; } id = buffer_get_char(wvp->buf); remaining--; // Size is in words if (id & ID_LARGE) { // 24-bit large size id &= ~ID_LARGE; size = buffer_get_int24_le(wvp->buf) << 1; remaining -= 3; DEBUG_TRACE(" ID_LARGE, changed to %x\n", id); } else { // 8-bit size size = buffer_get_char(wvp->buf) << 1; remaining--; } if (id & ID_ODD_SIZE) { id &= ~ID_ODD_SIZE; size--; DEBUG_TRACE(" ID_ODD_SIZE, changed to %x\n", id); } if ( id == ID_WV_BITSTREAM || !size ) { // Found the bitstream, don't read any farther DEBUG_TRACE(" Sub-Chunk: WV_BITSTREAM (size %u)\n", size); break; } // We only care about 0x27 (ID_SAMPLE_RATE) and 0xd (ID_CHANNEL_INFO) switch (id) { case ID_SAMPLE_RATE: DEBUG_TRACE(" Sub-Chunk: ID_SAMPLE_RATE (size: %u)\n", size); _wavpack_parse_sample_rate(wvp, size); break; case ID_CHANNEL_INFO: DEBUG_TRACE(" Sub-Chunk: ID_CHANNEL_INFO (size: %u)\n", size); _wavpack_parse_channel_info(wvp, size); break; default: // Skip it DEBUG_TRACE(" Sub-Chunk: %x (size: %u) (skipped)\n", id, size); _wavpack_skip(wvp, size); } remaining -= size; // If size was odd, skip a byte if (size & 0x1) { if ( buffer_len(wvp->buf) ) { buffer_consume(wvp->buf, 1); } else { _wavpack_skip(wvp, 1); } remaining--; } } // Calculate bitrate if ( wvp->header->total_samples && wvp->file_size > 0 ) { SV **samplerate = my_hv_fetch( wvp->info, "samplerate" ); if (samplerate != NULL) { uint32_t song_length_ms = ((wvp->header->total_samples * 1.0) / SvIV(*samplerate)) * 1000; my_hv_store( wvp->info, "song_length_ms", newSVuv(song_length_ms) ); my_hv_store( wvp->info, "bitrate", newSVuv( _bitrate(wvp->file_size - wvp->audio_offset, song_length_ms) ) ); my_hv_store( wvp->info, "total_samples", newSVuv(wvp->header->total_samples) ); } } return 1; }
void _parse_wav(PerlIO *infile, Buffer *buf, char *file, uint32_t file_size, HV *info, HV *tags) { uint32_t offset = 12; while ( offset < file_size - 8 ) { char chunk_id[5]; uint32_t chunk_size; // Verify we have at least 8 bytes if ( !_check_buf(infile, buf, 8, WAV_BLOCK_SIZE) ) { return; } strncpy( chunk_id, (char *)buffer_ptr(buf), 4 ); chunk_id[4] = '\0'; buffer_consume(buf, 4); chunk_size = buffer_get_int_le(buf); // Adjust for padding if ( chunk_size % 2 ) { chunk_size++; } offset += 8; DEBUG_TRACE("%s size %d\n", chunk_id, chunk_size); // Seek past data, everything else we parse // XXX: Are there other large chunks we should ignore? if ( !strcmp( chunk_id, "data" ) ) { SV **bitrate; my_hv_store( info, "audio_offset", newSVuv(offset) ); my_hv_store( info, "audio_size", newSVuv(chunk_size) ); // Calculate duration, unless we already know it (i.e. from 'fact') if ( !my_hv_fetch( info, "song_length_ms" ) ) { bitrate = my_hv_fetch( info, "bitrate" ); if (bitrate != NULL) { my_hv_store( info, "song_length_ms", newSVuv( (chunk_size / (SvIV(*bitrate) / 8.)) * 1000 ) ); } } // sanity check size, this is inside the data chunk code // to support setting audio_offset even when the data size is wrong if (chunk_size > file_size - offset) { DEBUG_TRACE("data size > file_size, skipping\n"); return; } // Seek past data if there are more chunks after it if ( file_size > offset + chunk_size ) { PerlIO_seek(infile, offset + chunk_size, SEEK_SET); } buffer_clear(buf); } else if ( !strcmp( chunk_id, "id3 " ) || !strcmp( chunk_id, "ID3 " ) || !strcmp( chunk_id, "ID32" ) ) { // Read header to verify version unsigned char *bptr = buffer_ptr(buf); if ( (bptr[0] == 'I' && bptr[1] == 'D' && bptr[2] == '3') && bptr[3] < 0xff && bptr[4] < 0xff && bptr[6] < 0x80 && bptr[7] < 0x80 && bptr[8] < 0x80 && bptr[9] < 0x80 ) { // Start parsing ID3 from offset parse_id3(infile, file, info, tags, offset, file_size); } // Seek past ID3 and clear buffer PerlIO_seek(infile, offset + chunk_size, SEEK_SET); buffer_clear(buf); } else { // sanity check size if (chunk_size > file_size - offset) { DEBUG_TRACE("chunk_size > file_size, skipping\n"); return; } // Make sure we have enough data if ( !_check_buf(infile, buf, chunk_size, WAV_BLOCK_SIZE) ) { return; } if ( !strcmp( chunk_id, "fmt " ) ) { _parse_wav_fmt(buf, chunk_size, info); } else if ( !strcmp( chunk_id, "LIST" ) ) { _parse_wav_list(buf, chunk_size, tags); } else if ( !strcmp( chunk_id, "PEAK" ) ) { _parse_wav_peak(buf, chunk_size, info, 0); } else if ( !strcmp( chunk_id, "fact" ) ) { // A 4-byte fact chunk in a non-PCM wav is the number of samples // Use it to calculate duration if ( chunk_size == 4 ) { uint32_t num_samples = buffer_get_int_le(buf); SV **samplerate = my_hv_fetch( info, "samplerate" ); if (samplerate != NULL) { my_hv_store( info, "song_length_ms", newSVuv( (num_samples * 1000) / SvIV(*samplerate) ) ); } } else { // Unknown, skip it buffer_consume(buf, chunk_size); } } else { if ( !strcmp(chunk_id, "SAUR") // Wavosour data chunk || !strcmp(chunk_id, "otom") // Wavosaur? || !strcmp(chunk_id, "PAD ") // Padding ) { // Known chunks to skip } else { // Warn about unknown chunks so we can investigate them PerlIO_printf(PerlIO_stderr(), "Unhandled WAV chunk %s size %d (skipped)\n", chunk_id, chunk_size); } buffer_consume(buf, chunk_size); } } offset += chunk_size; } }
void _parse_aiff(PerlIO *infile, Buffer *buf, char *file, uint32_t file_size, HV *info, HV *tags) { uint32_t offset = 12; while ( offset < file_size - 8 ) { char chunk_id[5]; int chunk_size; // Verify we have at least 8 bytes if ( !_check_buf(infile, buf, 8, WAV_BLOCK_SIZE) ) { return; } strncpy( chunk_id, (char *)buffer_ptr(buf), 4 ); chunk_id[4] = '\0'; buffer_consume(buf, 4); chunk_size = buffer_get_int(buf); // Adjust for padding if ( chunk_size % 2 ) { chunk_size++; } offset += 8; DEBUG_TRACE("%s size %d\n", chunk_id, chunk_size); // Seek past SSND, everything else we parse // XXX: Are there other large chunks we should ignore? if ( !strcmp( chunk_id, "SSND" ) ) { my_hv_store( info, "audio_offset", newSVuv(offset) ); my_hv_store( info, "audio_size", newSVuv(chunk_size) ); // Seek past data if there are more chunks after it if ( file_size > offset + chunk_size ) { PerlIO_seek(infile, offset + chunk_size, SEEK_SET); } buffer_clear(buf); } else if ( !strcmp( chunk_id, "id3 " ) || !strcmp( chunk_id, "ID3 " ) || !strcmp( chunk_id, "ID32" ) ) { // Read header to verify version unsigned char *bptr = buffer_ptr(buf); if ( (bptr[0] == 'I' && bptr[1] == 'D' && bptr[2] == '3') && bptr[3] < 0xff && bptr[4] < 0xff && bptr[6] < 0x80 && bptr[7] < 0x80 && bptr[8] < 0x80 && bptr[9] < 0x80 ) { // Start parsing ID3 from offset parse_id3(infile, file, info, tags, offset, file_size); } // Seen ID3 chunks with the chunk size in little-endian instead of big-endian if (chunk_size < 0 || offset + chunk_size > file_size) { break; } // Seek past ID3 and clear buffer DEBUG_TRACE("Seeking past ID3 to %d\n", offset + chunk_size); PerlIO_seek(infile, offset + chunk_size, SEEK_SET); buffer_clear(buf); } else { // Make sure we have enough data if ( !_check_buf(infile, buf, chunk_size, WAV_BLOCK_SIZE) ) { return; } if ( !strcmp( chunk_id, "COMM" ) ) { _parse_aiff_comm(buf, chunk_size, info); } else if ( !strcmp( chunk_id, "PEAK" ) ) { _parse_wav_peak(buf, chunk_size, info, 1); } else { PerlIO_printf(PerlIO_stderr(), "Unhandled AIFF chunk %s size %d (skipped)\n", chunk_id, chunk_size); buffer_consume(buf, chunk_size); } } offset += chunk_size; } }
static int get_wav_metadata(PerlIO *infile, char *file, HV *info, HV *tags) { Buffer buf; off_t file_size; int err = 0; uint32_t chunk_size; file_size = _file_size(infile); buffer_init(&buf, WAV_BLOCK_SIZE); if ( !_check_buf(infile, &buf, 12, WAV_BLOCK_SIZE) ) { err = -1; goto out; } if ( !strncmp( (char *)buffer_ptr(&buf), "RIFF", 4 ) ) { // We've got a RIFF file buffer_consume(&buf, 4); chunk_size = buffer_get_int_le(&buf); // Check format if ( strncmp( (char *)buffer_ptr(&buf), "WAVE", 4 ) ) { PerlIO_printf(PerlIO_stderr(), "Invalid WAV file: missing WAVE header: %s\n", file); err = -1; goto out; } buffer_consume(&buf, 4); my_hv_store( info, "file_size", newSVuv(file_size) ); _parse_wav(infile, &buf, file, file_size, info, tags); } else if ( !strncmp( (char *)buffer_ptr(&buf), "FORM", 4 ) ) { // We've got an AIFF file char *bptr; buffer_consume(&buf, 4); chunk_size = buffer_get_int(&buf); // Check format bptr = buffer_ptr(&buf); if ( bptr[0] == 'A' && bptr[1] == 'I' && bptr[2] == 'F' && (bptr[3] == 'F' || bptr[3] == 'C') ) { buffer_consume(&buf, 4); my_hv_store( info, "file_size", newSVuv(file_size) ); _parse_aiff(infile, &buf, file, file_size, info, tags); } else { PerlIO_printf(PerlIO_stderr(), "Invalid AIFF file: missing AIFF header: %s\n", file); err = -1; goto out; } } else { PerlIO_printf(PerlIO_stderr(), "Invalid WAV file: missing RIFF header: %s\n", file); err = -1; goto out; } out: buffer_free(&buf); if (err) return err; return 0; }