wahwah 1.0.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/wahwah/id3/v2.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
-
5
3
  module WahWah
6
4
  module ID3
7
5
  class V2 < Tag
@@ -14,47 +12,56 @@ module WahWah
14
12
  end
15
13
 
16
14
  private
17
- def parse
18
- @file_io.rewind
19
- @header = V2Header.new(@file_io)
20
15
 
21
- return unless valid?
16
+ def parse
17
+ @file_io.rewind
18
+ @header = V2Header.new(@file_io)
22
19
 
23
- until end_of_tag? do
24
- frame = ID3::Frame.new(@file_io, major_version)
25
- next unless frame.valid?
20
+ return unless valid?
26
21
 
27
- update_attribute(frame)
28
- end
29
- end
22
+ until end_of_tag?
23
+ frame = ID3::Frame.new(@file_io, major_version)
30
24
 
31
- def update_attribute(frame)
32
- name = frame.name
33
- value = frame.value
34
-
35
- case name
36
- when :comment
37
- # Because there may be more than one comment frame in each tag,
38
- # so push it into a array.
39
- @comments.push(value)
40
- when :image
41
- # Because there may be more than one image frame in each tag,
42
- # so push it into a array.
43
- @images.push(value)
44
- when :track, :disc
45
- # Track and disc value may be extended with a "/" character
46
- # and a numeric string containing the total numer.
47
- count, total_count = value.split('/', 2)
48
- instance_variable_set("@#{name}", count)
49
- instance_variable_set("@#{name}_total", total_count) unless total_count.nil?
50
- else
51
- instance_variable_set("@#{name}", value)
25
+ unless frame.valid?
26
+ frame.skip
27
+ next
52
28
  end
29
+
30
+ update_attribute(frame)
53
31
  end
32
+ end
33
+
34
+ def update_attribute(frame)
35
+ name = frame.name
54
36
 
55
- def end_of_tag?
56
- size <= @file_io.pos || file_size <= @file_io.pos
37
+ case name
38
+ when :comment
39
+ # Because there may be more than one comment frame in each tag,
40
+ # so push it into a array.
41
+ @comments.push(frame.value)
42
+ when :image
43
+ # Because there may be more than one image frame in each tag,
44
+ # so push it into a array.
45
+ @images_data.push(frame)
46
+ frame.skip
47
+ when :track, :disc
48
+ # Track and disc value may be extended with a "/" character
49
+ # and a numeric string containing the total numer.
50
+ count, total_count = frame.value.split("/", 2)
51
+ instance_variable_set("@#{name}", count)
52
+ instance_variable_set("@#{name}_total", total_count) unless total_count.nil?
53
+ else
54
+ instance_variable_set("@#{name}", frame.value)
57
55
  end
56
+ end
57
+
58
+ def end_of_tag?
59
+ size <= @file_io.pos || file_size <= @file_io.pos
60
+ end
61
+
62
+ def parse_image_data(image_frame)
63
+ image_frame.value
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -10,9 +10,9 @@ module WahWah
10
10
  # ID3v2 flags %abc00000
11
11
  # ID3v2 size 4 * %0xxxxxxx
12
12
  class V2Header
13
- TAG_ID = 'ID3'
13
+ TAG_ID = "ID3"
14
14
  HEADER_SIZE = 10
15
- HEADER_FORMAT = 'A3CxB8B*'
15
+ HEADER_FORMAT = "A3CxB8B*"
16
16
 
17
17
  attr_reader :major_version, :size
18
18
 
@@ -34,7 +34,7 @@ module WahWah
34
34
  # Size of padding $xx xx xx xx
35
35
 
36
36
  # Skip extended_header
37
- extended_header_size = Helper.id3_size_caculate(file_io.read(4).unpack('B32').first)
37
+ extended_header_size = Helper.id3_size_caculate(file_io.read(4).unpack1("B32"))
38
38
  file_io.seek(extended_header_size - 4, IO::SEEK_CUR)
39
39
  end
40
40
  end
@@ -46,7 +46,7 @@ module WahWah
46
46
  # The second bit in flags byte indicates whether or not the header
47
47
  # is followed by an extended header.
48
48
  def has_extended_header?
49
- @flags[1] == '1'
49
+ @flags[1] == "1"
50
50
  end
51
51
  end
52
52
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WahWah
4
+ module LazyRead
5
+ def self.prepended(base)
6
+ base.class_eval do
7
+ attr_reader :size
8
+ end
9
+ end
10
+
11
+ def initialize(file_io, *arg)
12
+ @file_io = file_io
13
+ super(*arg)
14
+ @position = @file_io.pos
15
+ @data = get_data if @file_io.is_a?(StringIO)
16
+ end
17
+
18
+ def data
19
+ if @file_io.closed? && @file_io.is_a?(File)
20
+ @file_io = File.open(@file_io.path)
21
+ @data = get_data
22
+ @file_io.close
23
+ end
24
+
25
+ @data ||= get_data
26
+ end
27
+
28
+ def skip
29
+ @file_io.seek(@position)
30
+ @file_io.seek(size, IO::SEEK_CUR)
31
+ end
32
+
33
+ private
34
+
35
+ def get_data
36
+ @file_io.seek(@position)
37
+ @file_io.read(size)
38
+ end
39
+ end
40
+ end
@@ -46,41 +46,41 @@ module WahWah
46
46
  HEADER_SIZE = 4
47
47
 
48
48
  FRAME_BITRATE_INDEX = {
49
- 'MPEG1 layer1' => [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],
50
- 'MPEG1 layer2' => [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],
51
- 'MPEG1 layer3' => [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
49
+ "MPEG1 layer1" => [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],
50
+ "MPEG1 layer2" => [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],
51
+ "MPEG1 layer3" => [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
52
52
 
53
- 'MPEG2 layer1' => [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
54
- 'MPEG2 layer2' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
55
- 'MPEG2 layer3' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
53
+ "MPEG2 layer1" => [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
54
+ "MPEG2 layer2" => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
55
+ "MPEG2 layer3" => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
56
56
 
57
- 'MPEG2.5 layer1' => [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
58
- 'MPEG2.5 layer2' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
59
- 'MPEG2.5 layer3' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0]
57
+ "MPEG2.5 layer1" => [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
58
+ "MPEG2.5 layer2" => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
59
+ "MPEG2.5 layer3" => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0]
60
60
  }
61
61
 
62
- VERSIONS_INDEX = ['MPEG2.5', nil, 'MPEG2', 'MPEG1']
63
- LAYER_INDEX = [nil, 'layer3', 'layer2', 'layer1']
64
- CHANNEL_MODE_INDEX = ['Stereo', 'Joint Stereo', 'Dual Channel', 'Single Channel']
62
+ VERSIONS_INDEX = ["MPEG2.5", nil, "MPEG2", "MPEG1"]
63
+ LAYER_INDEX = [nil, "layer3", "layer2", "layer1"]
64
+ CHANNEL_MODE_INDEX = ["Stereo", "Joint Stereo", "Dual Channel", "Single Channel"]
65
65
 
66
66
  SAMPLE_RATE_INDEX = {
67
- 'MPEG1' => [44100, 48000, 32000],
68
- 'MPEG2' => [22050, 24000, 16000],
69
- 'MPEG2.5' => [11025, 12000, 8000]
67
+ "MPEG1" => [44100, 48000, 32000],
68
+ "MPEG2" => [22050, 24000, 16000],
69
+ "MPEG2.5" => [11025, 12000, 8000]
70
70
  }
71
71
 
72
72
  SAMPLES_PER_FRAME_INDEX = {
73
- 'MPEG1 layer1' => 384,
74
- 'MPEG1 layer2' => 1152,
75
- 'MPEG1 layer3' => 1152,
73
+ "MPEG1 layer1" => 384,
74
+ "MPEG1 layer2" => 1152,
75
+ "MPEG1 layer3" => 1152,
76
76
 
77
- 'MPEG2 layer1' => 384,
78
- 'MPEG2 layer2' => 1152,
79
- 'MPEG2 layer3' => 576,
77
+ "MPEG2 layer1" => 384,
78
+ "MPEG2 layer2" => 1152,
79
+ "MPEG2 layer3" => 576,
80
80
 
81
- 'MPEG2.5 layer1' => 384,
82
- 'MPEG2.5 layer2' => 1152,
83
- 'MPEG2.5 layer3' => 576
81
+ "MPEG2.5 layer1" => 384,
82
+ "MPEG2.5 layer2" => 1152,
83
+ "MPEG2.5 layer3" => 576
84
84
  }
85
85
 
86
86
  attr_reader :version, :layer, :frame_bitrate, :channel_mode, :sample_rate
@@ -95,13 +95,14 @@ module WahWah
95
95
  break if file_io.eof?
96
96
 
97
97
  header = file_io.read(HEADER_SIZE)
98
- sync_bits = header.unpack('B11').first
98
+ sync_bits = header.unpack1("B11")
99
99
 
100
- if sync_bits == "#{'1' * 11}".b
101
- @header = header.unpack('B*').first
100
+ if sync_bits == ("1" * 11).b
101
+ @header = header.unpack1("B*")
102
102
  @position = offset
103
103
 
104
- parse; break
104
+ parse
105
+ break
105
106
  end
106
107
 
107
108
  offset += 1
@@ -127,15 +128,16 @@ module WahWah
127
128
  end
128
129
 
129
130
  private
130
- def parse
131
- return unless valid?
132
-
133
- @version = VERSIONS_INDEX[@header[11..12].to_i(2)]
134
- @layer = LAYER_INDEX[@header[13..14].to_i(2)]
135
- @frame_bitrate = FRAME_BITRATE_INDEX[kind]&.fetch(@header[16..19].to_i(2))
136
- @channel_mode = CHANNEL_MODE_INDEX[@header[24..25].to_i(2)]
137
- @sample_rate = SAMPLE_RATE_INDEX[@version]&.fetch(@header[20..21].to_i(2))
138
- end
131
+
132
+ def parse
133
+ return unless valid?
134
+
135
+ @version = VERSIONS_INDEX[@header[11..12].to_i(2)]
136
+ @layer = LAYER_INDEX[@header[13..14].to_i(2)]
137
+ @frame_bitrate = FRAME_BITRATE_INDEX[kind]&.fetch(@header[16..19].to_i(2))
138
+ @channel_mode = CHANNEL_MODE_INDEX[@header[24..25].to_i(2)]
139
+ @sample_rate = SAMPLE_RATE_INDEX[@version]&.fetch(@header[20..21].to_i(2))
140
+ end
139
141
  end
140
142
  end
141
143
  end
@@ -30,7 +30,7 @@ module WahWah
30
30
  # you can calculate the length of this field.
31
31
  class VbriHeader
32
32
  HEADER_SIZE = 32
33
- HEADER_FORMAT = 'A4x6NN'
33
+ HEADER_FORMAT = "A4x6NN"
34
34
 
35
35
  attr_reader :frames_count, :bytes_count
36
36
 
@@ -40,7 +40,7 @@ module WahWah
40
40
  end
41
41
 
42
42
  def valid?
43
- @id == 'VBRI'
43
+ @id == "VBRI"
44
44
  end
45
45
  end
46
46
  end
@@ -30,15 +30,15 @@ module WahWah
30
30
  def initialize(file_io, offset = 0)
31
31
  file_io.seek(offset)
32
32
 
33
- @id, @flags = file_io.read(8)&.unpack('A4N')
33
+ @id, @flags = file_io.read(8)&.unpack("A4N")
34
34
  return unless valid?
35
35
 
36
- @frames_count = @flags & 1 == 1 ? file_io.read(4).unpack('N').first : 0
37
- @bytes_count = @flags & 2 == 2 ? file_io.read(4).unpack('N').first : 0
36
+ @frames_count = @flags & 1 == 1 ? file_io.read(4).unpack1("N") : 0
37
+ @bytes_count = @flags & 2 == 2 ? file_io.read(4).unpack1("N") : 0
38
38
  end
39
39
 
40
40
  def valid?
41
- %w(Xing Info).include? @id
41
+ %w[Xing Info].include? @id
42
42
  end
43
43
  end
44
44
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
-
5
3
  module WahWah
6
4
  class Mp3Tag < Tag
7
5
  extend TagDelegate
@@ -40,71 +38,74 @@ module WahWah
40
38
  end
41
39
 
42
40
  def is_vbr?
43
- xing_header.valid? || vbri_header.valid?
41
+ mpeg_frame_header.valid? && (xing_header.valid? || vbri_header.valid?)
44
42
  end
45
43
 
46
44
  private
47
- def parse
48
- @id3_tag = parse_id3_tag
49
- parse_duration if mpeg_frame_header.valid?
50
- end
51
45
 
52
- def parse_id3_tag
53
- id3_v1_tag = ID3::V1.new(@file_io)
54
- id3_v2_tag = ID3::V2.new(@file_io)
46
+ def parse
47
+ @id3_tag = parse_id3_tag
48
+ parse_duration if mpeg_frame_header.valid?
49
+ end
55
50
 
56
- return id3_v2_tag if id3_v2_tag.valid?
57
- id3_v1_tag if id3_v1_tag.valid?
58
- end
51
+ def parse_id3_tag
52
+ id3_v1_tag = ID3::V1.new(@file_io.dup)
53
+ id3_v2_tag = ID3::V2.new(@file_io.dup)
59
54
 
60
- def parse_duration
61
- if is_vbr?
62
- @duration = (frames_count * (mpeg_frame_header.samples_per_frame / sample_rate.to_f)).round
63
- @bitrate = bytes_count * 8 / @duration / 1000 unless @duration.zero?
64
- else
65
- @bitrate = mpeg_frame_header.frame_bitrate
66
- @duration = (file_size - (@id3_tag&.size || 0)) * 8 / (@bitrate * 1000) unless @bitrate.zero?
67
- end
68
- end
55
+ return id3_v2_tag if id3_v2_tag.valid?
56
+ id3_v1_tag if id3_v1_tag.valid?
57
+ end
69
58
 
70
- def mpeg_frame_header
71
- # Because id3v2 tag on the file header so skip id3v2 tag
72
- @mpeg_frame_header ||= Mp3::MpegFrameHeader.new(@file_io, id3v2? ? @id3_tag&.size : 0)
59
+ def parse_duration
60
+ if is_vbr?
61
+ @duration = frames_count * (mpeg_frame_header.samples_per_frame / sample_rate.to_f)
62
+ @bitrate = (bytes_count * 8 / @duration / 1000).round unless @duration.zero?
63
+ else
64
+ @bitrate = mpeg_frame_header.frame_bitrate
65
+ @duration = (file_size - (@id3_tag&.size || 0)) * 8 / (@bitrate * 1000).to_f unless @bitrate.zero?
73
66
  end
67
+ end
74
68
 
75
- def xing_header
76
- @xing_header ||= Mp3::XingHeader.new(@file_io, xing_header_offset)
77
- end
69
+ def mpeg_frame_header
70
+ # Because id3v2 tag on the file header so skip id3v2 tag
71
+ @mpeg_frame_header ||= Mp3::MpegFrameHeader.new(@file_io, id3v2? ? @id3_tag&.size : 0)
72
+ end
78
73
 
79
- def vbri_header
80
- @vbri_header ||= Mp3::VbriHeader.new(@file_io, vbri_header_offset)
81
- end
74
+ def xing_header
75
+ @xing_header ||= Mp3::XingHeader.new(@file_io, xing_header_offset)
76
+ end
82
77
 
83
- def xing_header_offset
84
- mpeg_frame_header_position = mpeg_frame_header.position
85
- mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
86
- mpeg_frame_side_info_size = mpeg_version == 'MPEG1' ?
87
- (channel_mode == 'Single Channel' ? 17 : 32) :
88
- (channel_mode == 'Single Channel' ? 9 : 17)
78
+ def vbri_header
79
+ @vbri_header ||= Mp3::VbriHeader.new(@file_io, vbri_header_offset)
80
+ end
89
81
 
90
- mpeg_frame_header_position + mpeg_frame_header_size + mpeg_frame_side_info_size
82
+ def xing_header_offset
83
+ mpeg_frame_header_position = mpeg_frame_header.position
84
+ mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
85
+ mpeg_frame_side_info_size = if mpeg_version == "MPEG1"
86
+ channel_mode == "Single Channel" ? 17 : 32
87
+ else
88
+ channel_mode == "Single Channel" ? 9 : 17
91
89
  end
92
90
 
93
- def vbri_header_offset
94
- mpeg_frame_header_position = mpeg_frame_header.position
95
- mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
91
+ mpeg_frame_header_position + mpeg_frame_header_size + mpeg_frame_side_info_size
92
+ end
96
93
 
97
- mpeg_frame_header_position + mpeg_frame_header_size + 32
98
- end
94
+ def vbri_header_offset
95
+ mpeg_frame_header_position = mpeg_frame_header.position
96
+ mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
99
97
 
100
- def frames_count
101
- return xing_header.frames_count if xing_header.valid?
102
- vbri_header.frames_count if vbri_header.valid?
103
- end
98
+ mpeg_frame_header_position + mpeg_frame_header_size + 32
99
+ end
104
100
 
105
- def bytes_count
106
- return xing_header.bytes_count if xing_header.valid?
107
- vbri_header.bytes_count if vbri_header.valid?
108
- end
101
+ def frames_count
102
+ return xing_header.frames_count if xing_header.valid?
103
+ vbri_header.frames_count if vbri_header.valid?
104
+ end
105
+
106
+ def bytes_count
107
+ return xing_header.bytes_count if xing_header.valid?
108
+ vbri_header.bytes_count if vbri_header.valid?
109
+ end
109
110
  end
110
111
  end
@@ -3,13 +3,15 @@
3
3
  module WahWah
4
4
  module Mp4
5
5
  class Atom
6
- VERSIONED_ATOMS = %w(meta stsd)
7
- FLAGGED_ATOMS = %w(stsd)
6
+ prepend LazyRead
7
+
8
+ VERSIONED_ATOMS = %w[meta stsd]
9
+ FLAGGED_ATOMS = %w[stsd]
8
10
  HEADER_SIZE = 8
9
11
  HEADER_SIZE_FIELD_SIZE = 4
10
12
  EXTENDED_HEADER_SIZE = 8
11
13
 
12
- attr_reader :size, :type
14
+ attr_reader :type
13
15
 
14
16
  def self.find(file_io, *atom_path)
15
17
  file_io.rewind
@@ -20,14 +22,15 @@ module WahWah
20
22
  atom = new(file_io)
21
23
 
22
24
  next unless atom.valid?
23
- file_io.seek(atom.size, IO::SEEK_CUR); next unless atom.type == atom_type
25
+ file_io.seek(atom.size, IO::SEEK_CUR)
26
+ next unless atom.type == atom_type
24
27
 
25
28
  return atom if atom_path.empty?
26
29
  return atom.find(*atom_path)
27
30
  end
28
31
 
29
32
  # Return empty atom if can not found
30
- new(StringIO.new(''))
33
+ new(StringIO.new(""))
31
34
  end
32
35
 
33
36
  # An atom header consists of the following fields:
@@ -39,25 +42,18 @@ module WahWah
39
42
  # Type:
40
43
  # A 32-bit integer that contains the type of the atom.
41
44
  # This can often be usefully treated as a four-character field with a mnemonic value .
42
- def initialize(file_io)
43
- @size, @type = file_io.read(HEADER_SIZE)&.unpack('Na4')
45
+ def initialize
46
+ @size, @type = @file_io.read(HEADER_SIZE)&.unpack("Na4")
44
47
  return unless valid?
45
48
 
46
49
  # If the size field of an atom is set to 1, the type field is followed by a 64-bit extended size field,
47
50
  # which contains the actual size of the atom as a 64-bit unsigned integer.
48
- @size = file_io.read(EXTENDED_HEADER_SIZE).unpack('Q>').first - EXTENDED_HEADER_SIZE if @size == 1
51
+ @size = @file_io.read(EXTENDED_HEADER_SIZE).unpack1("Q>") - EXTENDED_HEADER_SIZE if @size == 1
49
52
 
50
53
  # If the size field of an atom is set to 0, which is allowed only for a top-level atom,
51
54
  # designates the last atom in the file and indicates that the atom extends to the end of the file.
52
- @size = file_io.size if @size == 0
53
- @size = @size - HEADER_SIZE
54
- @file_io = file_io
55
- @position = file_io.pos
56
- end
57
-
58
- def data
59
- @file_io.seek(@position)
60
- @file_io.read(size)
55
+ @size = @file_io.size if @size == 0
56
+ @size -= HEADER_SIZE
61
57
  end
62
58
 
63
59
  def valid?
@@ -68,38 +64,38 @@ module WahWah
68
64
  child_atom_index = data.index(atom_path.first)
69
65
 
70
66
  # Return empty atom if can not found
71
- return self.class.new(StringIO.new('')) if child_atom_index.nil?
67
+ return self.class.new(StringIO.new("")) if child_atom_index.nil?
72
68
 
73
69
  # Because before atom type field there are 4 bytes of size field,
74
70
  # So the child_atom_index should reduce 4.
75
- self.class.find(StringIO.new(data[child_atom_index - HEADER_SIZE_FIELD_SIZE..-1]), *atom_path)
71
+ self.class.find(StringIO.new(data[child_atom_index - HEADER_SIZE_FIELD_SIZE..]), *atom_path)
76
72
  end
77
73
 
78
-
79
74
  def children
80
75
  @children ||= parse_children_atoms
81
76
  end
82
77
 
83
78
  private
84
- def parse_children_atoms
85
- children_atoms = []
86
- atoms_data = data
87
79
 
88
- # Some atoms data contain extra content before child atom data.
89
- # So reduce those extra content to get child atom data.
90
- atoms_data = atoms_data[4..-1] if VERSIONED_ATOMS.include? type # Skip 4 bytes for version
91
- atoms_data = atoms_data[4..-1] if FLAGGED_ATOMS.include? type # Skip 4 bytes for flag
92
- atoms_data_io = StringIO.new(atoms_data)
80
+ def parse_children_atoms
81
+ children_atoms = []
82
+ atoms_data = data
93
83
 
94
- until atoms_data_io.eof?
95
- atom = self.class.new(atoms_data_io)
96
- children_atoms.push(atom)
84
+ # Some atoms data contain extra content before child atom data.
85
+ # So reduce those extra content to get child atom data.
86
+ atoms_data = atoms_data[4..] if VERSIONED_ATOMS.include? type # Skip 4 bytes for version
87
+ atoms_data = atoms_data[4..] if FLAGGED_ATOMS.include? type # Skip 4 bytes for flag
88
+ atoms_data_io = StringIO.new(atoms_data)
97
89
 
98
- atoms_data_io.seek(atom.size, IO::SEEK_CUR)
99
- end
90
+ until atoms_data_io.eof?
91
+ atom = self.class.new(atoms_data_io)
92
+ children_atoms.push(atom)
100
93
 
101
- children_atoms
94
+ atom.skip
102
95
  end
96
+
97
+ children_atoms
98
+ end
103
99
  end
104
100
  end
105
101
  end