static void _parse_wav_peak(ScanData s, Buffer *buf, uint32_t chunk_size, uint8_t big_endian) { uint16_t channels = 0; AV *peaklist = newAV(); SV **entry = my_hv_fetch( info, "channels" ); if ( entry != NULL ) { channels = SvIV(*entry); } // Skip version/timestamp buffer_consume(buf, 8); while ( channels-- ) { HV *peak = newHV(); my_hv_store( peak, "value", newSVnv( big_endian ? buffer_get_float32(buf) : buffer_get_float32_le(buf) ) ); my_hv_store( peak, "position", newSVuv( big_endian ? buffer_get_int(buf) : buffer_get_int_le(buf) ) ); av_push( peaklist, newRV_noinc( (SV *)peak) ); } my_hv_store( info, "peak", newRV_noinc( (SV *)peaklist ) ); }
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; }
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; }
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; } }