Index: include/ogg.h =================================================================== --- include/ogg.h (revision 904) +++ include/ogg.h (working copy) @@ -21,3 +21,6 @@ static int ogg_find_frame(PerlIO *infile, char *file, int offset); void _parse_vorbis_comments(PerlIO *infile, Buffer *vorbis_buf, HV *tags, int has_framing); int _ogg_binary_search_sample(PerlIO *infile, char *file, HV *info, uint64_t target_sample); + +// this is implemented in flac.c but included here due to ordering of includes +void _flac_parse_streaminfo_ogg(Buffer *buf, HV *info, HV *tags); Index: src/flac.c =================================================================== --- src/flac.c (revision 904) +++ src/flac.c (working copy) @@ -720,6 +720,19 @@ } void +_flac_parse_streaminfo_ogg(Buffer *buf, HV *info, HV *tags) +{ + flacinfo flac; + memset(&flac, 0, sizeof(flac)); + + flac.info = info; + flac.tags = tags; + flac.buf = buf; + + _flac_parse_streaminfo(&flac); +} + +void _flac_parse_application(flacinfo *flac, int len) { HV *app; Index: src/ogg.c =================================================================== --- src/ogg.c (revision 904) +++ src/ogg.c (working copy) @@ -45,6 +45,9 @@ int page = 0; int packets = 0; int streams = 0; + + int flac_serialno = 0; + int vorbis_comments = 0; unsigned char vorbishdr[23]; unsigned char channels; @@ -54,8 +57,6 @@ unsigned int bitrate_nominal = 0; uint64_t granule_pos = 0; - unsigned char vorbis_type = 0; - int i; int err = 0; @@ -159,14 +160,36 @@ if (seeking) { break; } - - _parse_vorbis_comments(infile, &vorbis_buf, tags, 1); + + if (vorbis_comments) { + _parse_vorbis_comments(infile, &vorbis_buf, tags, 1); + DEBUG_TRACE(" parsed vorbis comments\n"); + } - DEBUG_TRACE(" parsed vorbis comments\n"); + if (flac_serialno) { + unsigned char *bptr = buffer_ptr(&vorbis_buf); - buffer_clear(&vorbis_buf); - - break; + DEBUG_TRACE(" parsing flac headers\n"); + + while (buffer_len(&vorbis_buf) >= 4 && !(bptr[0] & 0x80)) { + uint8_t type = bptr[0] & 0x7f; + off_t len = (bptr[1] << 16) | (bptr[2] << 8) | bptr[3]; + buffer_consume(&vorbis_buf, 4); + // only support STREAMINFO and VORBIS COMMENTS at present + if (type == 0 && buffer_len(&vorbis_buf) >= len) { + _flac_parse_streaminfo_ogg(&vorbis_buf, info, tags); + } else if (type == 4 && buffer_len(&vorbis_buf) >= len) { + _parse_vorbis_comments(infile, &vorbis_buf, tags, 0); + } else { + buffer_consume(&vorbis_buf, len); + } + bptr = buffer_ptr(&vorbis_buf); + } + } + + buffer_clear(&vorbis_buf); + break; + } // Number of page segments @@ -210,53 +233,81 @@ buffer_append( &vorbis_buf, buffer_ptr(&ogg_buf), pagelen ); DEBUG_TRACE(" Read %d into vorbis buffer\n", pagelen); - // Process vorbis packet - if ( !vorbis_type ) { - vorbis_type = buffer_get_char(&vorbis_buf); - // Verify 'vorbis' string - if ( strncmp( buffer_ptr(&vorbis_buf), "vorbis", 6 ) ) { - PerlIO_printf(PerlIO_stderr(), "Not a Vorbis file (bad vorbis header): %s\n", file); - goto out; - } - buffer_consume( &vorbis_buf, 6 ); - - DEBUG_TRACE(" Found vorbis packet type %d\n", vorbis_type); - } - - if (vorbis_type == 1) { - // Parse info - // Grab 23-byte Vorbis header - if ( buffer_len(&vorbis_buf) < 23 ) { - PerlIO_printf(PerlIO_stderr(), "Not a Vorbis file (bad vorbis header): %s\n", file); - goto out; - } + // Parse header pages: + // - vorbis pages start : "vorbis" + // - first flac page starts: 0x7F "FLAC" + // - subsequent flac pages - follow same serialno as first flac page - buffer_get(&vorbis_buf, vorbishdr, 23); + if (buffer_len(&vorbis_buf) >= 7 && !strncmp( buffer_ptr(&vorbis_buf) + 1, "vorbis", 6)) { + unsigned vorbis_type = buffer_get_char(&vorbis_buf); + buffer_consume(&vorbis_buf, 6); - my_hv_store( info, "version", newSViv( CONVERT_INT32LE(vorbishdr) ) ); + DEBUG_TRACE(" Found vorbis packet type %d\n", vorbis_type); - channels = vorbishdr[4]; - my_hv_store( info, "channels", newSViv(channels) ); - my_hv_store( info, "stereo", newSViv( channels == 2 ? 1 : 0 ) ); + if (vorbis_type == 1) { + // Parse info + // Grab 23-byte Vorbis header + if ( buffer_len(&vorbis_buf) < 23 ) { + PerlIO_printf(PerlIO_stderr(), "Not a Vorbis file (bad vorbis header): %s\n", file); + goto out; + } + + buffer_get(&vorbis_buf, vorbishdr, 23); - samplerate = CONVERT_INT32LE((vorbishdr+5)); - my_hv_store( info, "samplerate", newSViv(samplerate) ); - my_hv_store( info, "bitrate_upper", newSViv( CONVERT_INT32LE((vorbishdr+9)) ) ); + my_hv_store( info, "ogg_type", newSVpvn("vorbis", 6) ); + + my_hv_store( info, "version", newSViv( CONVERT_INT32LE(vorbishdr) ) ); + + channels = vorbishdr[4]; + my_hv_store( info, "channels", newSViv(channels) ); + my_hv_store( info, "stereo", newSViv( channels == 2 ? 1 : 0 ) ); + + samplerate = CONVERT_INT32LE((vorbishdr+5)); + my_hv_store( info, "samplerate", newSViv(samplerate) ); + my_hv_store( info, "bitrate_upper", newSViv( CONVERT_INT32LE((vorbishdr+9)) ) ); + + bitrate_nominal = CONVERT_INT32LE((vorbishdr+13)); + my_hv_store( info, "bitrate_nominal", newSViv(bitrate_nominal) ); + my_hv_store( info, "bitrate_lower", newSViv( CONVERT_INT32LE((vorbishdr+17)) ) ); + + blocksize_0 = 2 << ((vorbishdr[21] & 0xF0) >> 4); + my_hv_store( info, "blocksize_0", newSViv( blocksize_0 ) ); + my_hv_store( info, "blocksize_1", newSViv( 2 << (vorbishdr[21] & 0x0F) ) ); - bitrate_nominal = CONVERT_INT32LE((vorbishdr+13)); - my_hv_store( info, "bitrate_nominal", newSViv(bitrate_nominal) ); - my_hv_store( info, "bitrate_lower", newSViv( CONVERT_INT32LE((vorbishdr+17)) ) ); + buffer_clear(&vorbis_buf); + + DEBUG_TRACE(" parsed vorbis info header\n"); - blocksize_0 = 2 << ((vorbishdr[21] & 0xF0) >> 4); - my_hv_store( info, "blocksize_0", newSViv( blocksize_0 ) ); - my_hv_store( info, "blocksize_1", newSViv( 2 << (vorbishdr[21] & 0x0F) ) ); + } else if (vorbis_type == 3) { - DEBUG_TRACE(" parsed vorbis info header\n"); + // Comment header + // leave in vorbis_buf so we can accumulate multipage comments + vorbis_comments = 1; - buffer_clear(&vorbis_buf); - vorbis_type = 0; - } - + } else { + + DEBUG_TRACE(" skipping unknown packet type\n"); + + buffer_clear(&vorbis_buf); + } + + } else if (buffer_len(&vorbis_buf) >= 13 && !strncmp( buffer_ptr(&vorbis_buf), "\x7f" "FLAC", 5) && + !strncmp(buffer_ptr(&vorbis_buf) + 9, "fLaC", 4)){ + + buffer_consume(&vorbis_buf, 13); + flac_serialno = serialno; + + my_hv_store( info, "ogg_type", newSVpvn("flac", 4) ); + + DEBUG_TRACE(" oggflac file\n"); + // leave header in vorbis_buf so we can accumulate multipage entries + + } else if (flac_serialno && flac_serialno == serialno) { + + // Comment header + // leave in vorbis_buf so we can accumulate multipage comments + } + // Skip rest of this page buffer_consume( &ogg_buf, pagelen ); } @@ -274,6 +325,11 @@ my_hv_store( info, "serial_number", newSVuv(serialno) ); + // rest of parsing is vorbis specific so bail out now for oggflac + if (flac_serialno) { + goto out; + } + // calculate average bitrate and duration avg_buf_size = blocksize_0 * 2; if ( file_size > avg_buf_size ) {