lib/mp3file/mp3_file.rb in mp3file-1.1.5 vs lib/mp3file/mp3_file.rb in mp3file-1.2.0

- old
+ new

@@ -123,12 +123,29 @@ # Some files have more than one ID3v2 tag. If we can't find an # MP3 header in the next 4k, try reading another ID3v2 tag and # repeat. @extra_id3v2_tags = [] begin - # Try to find the first MP3 header. - @first_header_offset, @first_header = get_next_header(@file) + # Try to find the first two MP3 headers. + loop do + @first_header_offset, @first_header = get_next_header(@file) + @file.seek(@first_header_offset + @first_header.frame_size, IO::SEEK_SET) + second_header_offset = @file.tell - 4 + second_header = + begin + MP3Header.new(@file) + rescue InvalidMP3HeaderError + nil + end + + # Pretend we didn't read the second header. + @file.seek(@first_header_offset + 4) + + if second_header && @first_header.same_header?(second_header) + break + end + end rescue InvalidMP3FileError if @id3v2_tag end_of_tags = @id3v2_tag.size + @extra_id3v2_tags.map(&:last).map(&:size).reduce(:+).to_i @file.seek(end_of_tags, IO::SEEK_SET) @@ -204,35 +221,45 @@ # / CBR call. Assume that Xing headers, when present in a CBR # file, are called "Info". @vbr = !@xing_header.nil? && @xing_header.name == "Xing" end - # puts "@num_frames = #{@num_frames.inspect}" - # puts "@xing_header = #{@xing_header.inspect}" + # puts "@num_frames (1) = #{@num_frames.inspect}" + # puts "@bitrate (1) = #{@bitrate.inspect}" + # puts "@vbr = #{@vbr.inspect}" + # puts "@xing_header.frames = #{@xing_header && @xing_header.frames.inspect}" + # puts "@xing_header.bytes = #{@xing_header && @xing_header.bytes.inspect}" # puts "@audio_size = #{@audio_size.inspect}" # puts "@first_header size = #{@first_header.frame_size.inspect}" - # Find the number of frames. Prefer the actual frame count we - # did (if we scanned all the frames) over the Xing - # header. Prefer the Xing header over file size math. - @num_frames = @num_frames || - (@xing_header && (@xing_header.frames + 1)) || + # Find the number of frames, in this order: + # 1. The frame count from the Xing (or Info) header. + # 2. The number of frames we actually counted by scanning the + # whole file, which we might not have done. + # 3. Assume it's CBR and divide the file size by the frame size. + @num_frames = + (@xing_header && @xing_header.frames) || + @num_frames || (@audio_size / @first_header.frame_size) + # puts "@num_frames (2) = #{@num_frames.inspect}" + # Figure out the total samples and the time duration. @total_samples = @num_frames * @first_header.samples @length = @total_samples.to_f / @samplerate.to_f + # puts "@total_samples = #{@total_samples.inspect}" + # puts "@length = #{@length.inspect}" + # If the file looks like it's a VBR file, do an averate bitrate # calculation, either using the Xing header's idea of the file # size or the one we found. if @vbr @bitrate = ((@xing_header && @xing_header.bytes) || @audio_size) / @length.to_f * 8 / 1000 end - # puts "@vbr = #{@vbr.inspect}" - # puts "@bitrate = #{@bitrate.inspect}" + # puts "@bitrate (2) = #{@bitrate.inspect}" @file.close end def get_next_header(file, offset = nil) @@ -245,10 +272,10 @@ header_offset = file.tell while header.nil? begin header = MP3Header.new(file) - header_offset = file.tell - 4 + header_offset = file.tell - 4 rescue InvalidMP3HeaderError header_offset += 1 if header_offset - initial_header_offset > 4096 raise InvalidMP3FileError, "Could not find a valid MP3 header in the first 4096 bytes." else