wahwah 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/wahwah/asf/object.rb +2 -2
- data/lib/wahwah/asf_tag.rb +199 -198
- data/lib/wahwah/errors.rb +2 -1
- data/lib/wahwah/flac/block.rb +4 -4
- data/lib/wahwah/flac/streaminfo_block.rb +4 -4
- data/lib/wahwah/flac_tag.rb +65 -63
- data/lib/wahwah/helper.rb +8 -8
- data/lib/wahwah/id3/comment_frame_body.rb +1 -1
- data/lib/wahwah/id3/frame.rb +47 -46
- data/lib/wahwah/id3/frame_body.rb +6 -7
- data/lib/wahwah/id3/image_frame_body.rb +5 -5
- data/lib/wahwah/id3/text_frame_body.rb +1 -1
- data/lib/wahwah/id3/v1.rb +59 -58
- data/lib/wahwah/id3/v2.rb +41 -35
- data/lib/wahwah/id3/v2_header.rb +4 -4
- data/lib/wahwah/lazy_read.rb +5 -4
- data/lib/wahwah/mp3/mpeg_frame_header.rb +39 -37
- data/lib/wahwah/mp3/vbri_header.rb +2 -2
- data/lib/wahwah/mp3/xing_header.rb +4 -4
- data/lib/wahwah/mp3_tag.rb +51 -48
- data/lib/wahwah/mp4/atom.rb +25 -24
- data/lib/wahwah/mp4_tag.rb +101 -97
- data/lib/wahwah/ogg/flac_tag.rb +2 -2
- data/lib/wahwah/ogg/opus_tag.rb +3 -3
- data/lib/wahwah/ogg/packets.rb +2 -2
- data/lib/wahwah/ogg/page.rb +3 -3
- data/lib/wahwah/ogg/vorbis_comment.rb +14 -14
- data/lib/wahwah/ogg/vorbis_tag.rb +2 -2
- data/lib/wahwah/ogg_tag.rb +34 -34
- data/lib/wahwah/riff/chunk.rb +7 -6
- data/lib/wahwah/riff_tag.rb +81 -79
- data/lib/wahwah/tag.rb +8 -7
- data/lib/wahwah/version.rb +1 -1
- data/lib/wahwah.rb +58 -58
- metadata +13 -14
@@ -46,41 +46,41 @@ module WahWah
|
|
46
46
|
HEADER_SIZE = 4
|
47
47
|
|
48
48
|
FRAME_BITRATE_INDEX = {
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
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 = [
|
63
|
-
LAYER_INDEX = [nil,
|
64
|
-
CHANNEL_MODE_INDEX = [
|
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
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
73
|
+
"MPEG1 layer1" => 384,
|
74
|
+
"MPEG1 layer2" => 1152,
|
75
|
+
"MPEG1 layer3" => 1152,
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
"MPEG2 layer1" => 384,
|
78
|
+
"MPEG2 layer2" => 1152,
|
79
|
+
"MPEG2 layer3" => 576,
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
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.
|
98
|
+
sync_bits = header.unpack1("B11")
|
99
99
|
|
100
|
-
if sync_bits == "
|
101
|
-
@header = header.
|
100
|
+
if sync_bits == ("1" * 11).b
|
101
|
+
@header = header.unpack1("B*")
|
102
102
|
@position = offset
|
103
103
|
|
104
|
-
parse
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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 =
|
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 ==
|
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(
|
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).
|
37
|
-
@bytes_count = @flags & 2 == 2 ? file_io.read(4).
|
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
|
41
|
+
%w[Xing Info].include? @id
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
data/lib/wahwah/mp3_tag.rb
CHANGED
@@ -42,67 +42,70 @@ module WahWah
|
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
|
-
def parse
|
46
|
-
@id3_tag = parse_id3_tag
|
47
|
-
parse_duration if mpeg_frame_header.valid?
|
48
|
-
end
|
49
45
|
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
def parse
|
47
|
+
@id3_tag = parse_id3_tag
|
48
|
+
parse_duration if mpeg_frame_header.valid?
|
49
|
+
end
|
53
50
|
|
54
|
-
|
55
|
-
|
56
|
-
|
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)
|
57
54
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@bitrate = bytes_count * 8 / @duration / 1000 unless @duration.zero?
|
62
|
-
else
|
63
|
-
@bitrate = mpeg_frame_header.frame_bitrate
|
64
|
-
@duration = (file_size - (@id3_tag&.size || 0)) * 8 / (@bitrate * 1000) unless @bitrate.zero?
|
65
|
-
end
|
66
|
-
end
|
55
|
+
return id3_v2_tag if id3_v2_tag.valid?
|
56
|
+
id3_v1_tag if id3_v1_tag.valid?
|
57
|
+
end
|
67
58
|
|
68
|
-
|
69
|
-
|
70
|
-
@
|
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?
|
71
66
|
end
|
67
|
+
end
|
72
68
|
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
76
73
|
|
77
|
-
|
78
|
-
|
79
|
-
|
74
|
+
def xing_header
|
75
|
+
@xing_header ||= Mp3::XingHeader.new(@file_io, xing_header_offset)
|
76
|
+
end
|
80
77
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
mpeg_frame_side_info_size = mpeg_version == 'MPEG1' ?
|
85
|
-
(channel_mode == 'Single Channel' ? 17 : 32) :
|
86
|
-
(channel_mode == 'Single Channel' ? 9 : 17)
|
78
|
+
def vbri_header
|
79
|
+
@vbri_header ||= Mp3::VbriHeader.new(@file_io, vbri_header_offset)
|
80
|
+
end
|
87
81
|
|
88
|
-
|
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
|
89
89
|
end
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
|
91
|
+
mpeg_frame_header_position + mpeg_frame_header_size + mpeg_frame_side_info_size
|
92
|
+
end
|
94
93
|
|
95
|
-
|
96
|
-
|
94
|
+
def vbri_header_offset
|
95
|
+
mpeg_frame_header_position = mpeg_frame_header.position
|
96
|
+
mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
|
97
97
|
|
98
|
-
|
99
|
-
|
100
|
-
vbri_header.frames_count if vbri_header.valid?
|
101
|
-
end
|
98
|
+
mpeg_frame_header_position + mpeg_frame_header_size + 32
|
99
|
+
end
|
102
100
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
107
110
|
end
|
108
111
|
end
|
data/lib/wahwah/mp4/atom.rb
CHANGED
@@ -5,8 +5,8 @@ module WahWah
|
|
5
5
|
class Atom
|
6
6
|
prepend LazyRead
|
7
7
|
|
8
|
-
VERSIONED_ATOMS = %w
|
9
|
-
FLAGGED_ATOMS = %w
|
8
|
+
VERSIONED_ATOMS = %w[meta stsd]
|
9
|
+
FLAGGED_ATOMS = %w[stsd]
|
10
10
|
HEADER_SIZE = 8
|
11
11
|
HEADER_SIZE_FIELD_SIZE = 4
|
12
12
|
EXTENDED_HEADER_SIZE = 8
|
@@ -22,14 +22,15 @@ module WahWah
|
|
22
22
|
atom = new(file_io)
|
23
23
|
|
24
24
|
next unless atom.valid?
|
25
|
-
file_io.seek(atom.size, IO::SEEK_CUR)
|
25
|
+
file_io.seek(atom.size, IO::SEEK_CUR)
|
26
|
+
next unless atom.type == atom_type
|
26
27
|
|
27
28
|
return atom if atom_path.empty?
|
28
29
|
return atom.find(*atom_path)
|
29
30
|
end
|
30
31
|
|
31
32
|
# Return empty atom if can not found
|
32
|
-
new(StringIO.new(
|
33
|
+
new(StringIO.new(""))
|
33
34
|
end
|
34
35
|
|
35
36
|
# An atom header consists of the following fields:
|
@@ -42,17 +43,17 @@ module WahWah
|
|
42
43
|
# A 32-bit integer that contains the type of the atom.
|
43
44
|
# This can often be usefully treated as a four-character field with a mnemonic value .
|
44
45
|
def initialize
|
45
|
-
@size, @type = @file_io.read(HEADER_SIZE)&.unpack(
|
46
|
+
@size, @type = @file_io.read(HEADER_SIZE)&.unpack("Na4")
|
46
47
|
return unless valid?
|
47
48
|
|
48
49
|
# If the size field of an atom is set to 1, the type field is followed by a 64-bit extended size field,
|
49
50
|
# which contains the actual size of the atom as a 64-bit unsigned integer.
|
50
|
-
@size = @file_io.read(EXTENDED_HEADER_SIZE).
|
51
|
+
@size = @file_io.read(EXTENDED_HEADER_SIZE).unpack1("Q>") - EXTENDED_HEADER_SIZE if @size == 1
|
51
52
|
|
52
53
|
# If the size field of an atom is set to 0, which is allowed only for a top-level atom,
|
53
54
|
# designates the last atom in the file and indicates that the atom extends to the end of the file.
|
54
55
|
@size = @file_io.size if @size == 0
|
55
|
-
@size
|
56
|
+
@size -= HEADER_SIZE
|
56
57
|
end
|
57
58
|
|
58
59
|
def valid?
|
@@ -63,38 +64,38 @@ module WahWah
|
|
63
64
|
child_atom_index = data.index(atom_path.first)
|
64
65
|
|
65
66
|
# Return empty atom if can not found
|
66
|
-
return self.class.new(StringIO.new(
|
67
|
+
return self.class.new(StringIO.new("")) if child_atom_index.nil?
|
67
68
|
|
68
69
|
# Because before atom type field there are 4 bytes of size field,
|
69
70
|
# So the child_atom_index should reduce 4.
|
70
|
-
self.class.find(StringIO.new(data[child_atom_index - HEADER_SIZE_FIELD_SIZE
|
71
|
+
self.class.find(StringIO.new(data[child_atom_index - HEADER_SIZE_FIELD_SIZE..]), *atom_path)
|
71
72
|
end
|
72
73
|
|
73
|
-
|
74
74
|
def children
|
75
75
|
@children ||= parse_children_atoms
|
76
76
|
end
|
77
77
|
|
78
78
|
private
|
79
|
-
def parse_children_atoms
|
80
|
-
children_atoms = []
|
81
|
-
atoms_data = data
|
82
79
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
atoms_data = atoms_data[4..-1] if FLAGGED_ATOMS.include? type # Skip 4 bytes for flag
|
87
|
-
atoms_data_io = StringIO.new(atoms_data)
|
80
|
+
def parse_children_atoms
|
81
|
+
children_atoms = []
|
82
|
+
atoms_data = data
|
88
83
|
|
89
|
-
|
90
|
-
|
91
|
-
|
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)
|
92
89
|
|
93
|
-
|
94
|
-
|
90
|
+
until atoms_data_io.eof?
|
91
|
+
atom = self.class.new(atoms_data_io)
|
92
|
+
children_atoms.push(atom)
|
95
93
|
|
96
|
-
|
94
|
+
atom.skip
|
97
95
|
end
|
96
|
+
|
97
|
+
children_atoms
|
98
|
+
end
|
98
99
|
end
|
99
100
|
end
|
100
101
|
end
|
data/lib/wahwah/mp4_tag.rb
CHANGED
@@ -10,124 +10,128 @@ module WahWah
|
|
10
10
|
"\xA9day".b => :year,
|
11
11
|
"\xA9gen".b => :genre,
|
12
12
|
"\xA9nam".b => :title,
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
"covr".b => :image,
|
14
|
+
"disk".b => :disc,
|
15
|
+
"trkn".b => :track,
|
16
|
+
"aART".b => :albumartist
|
17
17
|
}
|
18
18
|
|
19
19
|
META_ATOM_DECODE_BY_TYPE = {
|
20
|
-
0 => ->
|
21
|
-
1 => ->
|
22
|
-
2 => ->
|
23
|
-
3 => ->
|
24
|
-
|
25
|
-
13 => ->
|
26
|
-
14 => ->
|
27
|
-
|
28
|
-
21 => ->
|
29
|
-
22 => ->
|
30
|
-
23 => ->
|
31
|
-
24 => ->
|
32
|
-
|
33
|
-
65 => ->
|
34
|
-
66 => ->
|
35
|
-
67 => ->
|
36
|
-
74 => ->
|
37
|
-
|
38
|
-
75 => ->
|
39
|
-
76 => ->
|
40
|
-
77 => ->
|
41
|
-
78 => ->
|
20
|
+
0 => ->(data) { data }, # reserved
|
21
|
+
1 => ->(data) { Helper.encode_to_utf8(data) }, # UTF-8
|
22
|
+
2 => ->(data) { Helper.encode_to_utf8(data, "UTF-16BE") }, # UTF-16BE
|
23
|
+
3 => ->(data) { Helper.encode_to_utf8(data, "SJIS") }, # SJIS
|
24
|
+
|
25
|
+
13 => ->(data) { {data: data, mime_type: "image/jpeg", type: :cover} }, # JPEG
|
26
|
+
14 => ->(data) { {data: data, mime_type: "image/png", type: :cover} }, # PNG
|
27
|
+
|
28
|
+
21 => ->(data) { data.unpack1("i>") }, # Big endian signed integer
|
29
|
+
22 => ->(data) { data.unpack1("I>") }, # Big endian unsigned integer
|
30
|
+
23 => ->(data) { data.unpack1("g") }, # Big endian 32-bit floating point value
|
31
|
+
24 => ->(data) { data.unpack1("G") }, # Big endian 64-bit floating point value
|
32
|
+
|
33
|
+
65 => ->(data) { data.unpack1("c") }, # 8-bit signed integer
|
34
|
+
66 => ->(data) { data.unpack1("s>") }, # Big-endian 16-bit signed integer
|
35
|
+
67 => ->(data) { data.unpack1("l>") }, # Big-endian 32-bit signed integer
|
36
|
+
74 => ->(data) { data.unpack1("q>") }, # Big-endian 64-bit signed integer
|
37
|
+
|
38
|
+
75 => ->(data) { data.unpack1("C") }, # 8-bit unsigned integer
|
39
|
+
76 => ->(data) { data.unpack1("S>") }, # Big-endian 16-bit unsigned integer
|
40
|
+
77 => ->(data) { data.unpack1("L>") }, # Big-endian 32-bit unsigned integer
|
41
|
+
78 => ->(data) { data.unpack1("Q>") } # Big-endian 64-bit unsigned integer
|
42
42
|
}
|
43
43
|
|
44
44
|
private
|
45
|
-
def parse
|
46
|
-
movie_atom = Mp4::Atom.find(@file_io, 'moov')
|
47
|
-
return unless movie_atom.valid?
|
48
45
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
@comments.push(encoded_data_value)
|
79
|
-
when :track, :disc
|
80
|
-
count, total_count = encoded_data_value.unpack('x2nn')
|
81
|
-
|
82
|
-
instance_variable_set("@#{attr_name}", count) unless count.zero?
|
83
|
-
instance_variable_set("@#{attr_name}_total", total_count) unless total_count.zero?
|
84
|
-
else
|
85
|
-
instance_variable_set("@#{attr_name}", encoded_data_value)
|
86
|
-
end
|
46
|
+
def parse
|
47
|
+
movie_atom = Mp4::Atom.find(@file_io, "moov")
|
48
|
+
return unless movie_atom.valid?
|
49
|
+
|
50
|
+
parse_meta_list_atom movie_atom.find("udta", "meta", "ilst")
|
51
|
+
parse_mvhd_atom movie_atom.find("mvhd")
|
52
|
+
parse_stsd_atom movie_atom.find("trak", "mdia", "minf", "stbl", "stsd")
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_meta_list_atom(atom)
|
56
|
+
return unless atom.valid?
|
57
|
+
|
58
|
+
# The metadata item list atom holds a list of actual metadata values that are present in the metadata atom.
|
59
|
+
# The metadata items are formatted as a list of items.
|
60
|
+
# The metadata item list atom is of type ‘ilst’ and contains a number of metadata items, each of which is an atom.
|
61
|
+
# each metadata item atom contains a Value Atom, to hold the value of the metadata item
|
62
|
+
atom.children.each do |child_atom|
|
63
|
+
attr_name = META_ATOM_MAPPING[child_atom.type]
|
64
|
+
|
65
|
+
# The value of the metadata item is expressed as immediate data in a value atom.
|
66
|
+
# The value atom starts with two fields: a type indicator, and a locale indicator.
|
67
|
+
# Both the type and locale indicators are four bytes long.
|
68
|
+
# There may be multiple ‘value’ entries, using different type
|
69
|
+
data_atom = child_atom.find("data")
|
70
|
+
next unless data_atom.valid?
|
71
|
+
|
72
|
+
if attr_name == :image
|
73
|
+
@images_data.push(data_atom)
|
74
|
+
next
|
87
75
|
end
|
88
|
-
end
|
89
76
|
|
90
|
-
|
91
|
-
|
92
|
-
|
77
|
+
encoded_data_value = parse_meta_data_atom(data_atom)
|
78
|
+
next if attr_name.nil? || encoded_data_value.nil?
|
79
|
+
|
80
|
+
case attr_name
|
81
|
+
when :comment
|
82
|
+
@comments.push(encoded_data_value)
|
83
|
+
when :track, :disc
|
84
|
+
count, total_count = encoded_data_value.unpack("x2nn")
|
85
|
+
|
86
|
+
instance_variable_set("@#{attr_name}", count) unless count.zero?
|
87
|
+
instance_variable_set("@#{attr_name}_total", total_count) unless total_count.zero?
|
88
|
+
else
|
89
|
+
instance_variable_set("@#{attr_name}", encoded_data_value)
|
90
|
+
end
|
93
91
|
end
|
92
|
+
end
|
94
93
|
|
95
|
-
|
96
|
-
|
94
|
+
def parse_meta_data_atom(atom)
|
95
|
+
data_type, data_value = atom.data.unpack("Nx4a*")
|
96
|
+
META_ATOM_DECODE_BY_TYPE[data_type]&.call(data_value)
|
97
|
+
end
|
97
98
|
|
98
|
-
|
99
|
-
|
99
|
+
def parse_mvhd_atom(atom)
|
100
|
+
return unless atom.valid?
|
100
101
|
|
101
|
-
|
102
|
-
|
102
|
+
atom_data = StringIO.new(atom.data)
|
103
|
+
version = atom_data.read(1).unpack1("c")
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
atom_data.seek(8, IO::SEEK_CUR)
|
105
|
+
# Skip flags
|
106
|
+
atom_data.seek(3, IO::SEEK_CUR)
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
atom_data.seek(16, IO::SEEK_CUR)
|
108
|
+
if version == 0
|
109
|
+
# Skip creation and modification time
|
110
|
+
atom_data.seek(8, IO::SEEK_CUR)
|
112
111
|
|
113
|
-
|
114
|
-
|
112
|
+
time_scale, duration = atom_data.read(8).unpack("l>l>")
|
113
|
+
elsif version == 1
|
114
|
+
# Skip creation and modification time
|
115
|
+
atom_data.seek(16, IO::SEEK_CUR)
|
115
116
|
|
116
|
-
|
117
|
+
time_scale, duration = atom_data.read(12).unpack("l>q>")
|
117
118
|
end
|
118
119
|
|
119
|
-
|
120
|
-
|
120
|
+
@duration = duration / time_scale.to_f
|
121
|
+
end
|
121
122
|
|
122
|
-
|
123
|
-
|
123
|
+
def parse_stsd_atom(atom)
|
124
|
+
return unless atom.valid?
|
124
125
|
|
125
|
-
|
126
|
-
|
127
|
-
end
|
126
|
+
mp4a_atom = atom.find("mp4a")
|
127
|
+
esds_atom = atom.find("esds")
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
@sample_rate = mp4a_atom.data.unpack1("x22I>") if mp4a_atom.valid?
|
130
|
+
@bitrate = esds_atom.data.unpack1("x26I>") / 1000 if esds_atom.valid?
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_image_data(image_data_atom)
|
134
|
+
parse_meta_data_atom(image_data_atom)
|
135
|
+
end
|
132
136
|
end
|
133
137
|
end
|
data/lib/wahwah/ogg/flac_tag.rb
CHANGED
@@ -23,9 +23,9 @@ module WahWah
|
|
23
23
|
# Each such packet will contain a single native FLAC metadata block.
|
24
24
|
# The first of these must be a VORBIS_COMMENT block.
|
25
25
|
|
26
|
-
id, streaminfo_block_data = identification_packet.unpack(
|
26
|
+
id, streaminfo_block_data = identification_packet.unpack("x9A4A*")
|
27
27
|
|
28
|
-
return unless id ==
|
28
|
+
return unless id == "fLaC"
|
29
29
|
streaminfo_block = Flac::Block.new(StringIO.new(streaminfo_block_data))
|
30
30
|
vorbis_comment_block = Flac::Block.new(StringIO.new(comment_packet))
|
31
31
|
|
data/lib/wahwah/ogg/opus_tag.rb
CHANGED
@@ -19,12 +19,12 @@ module WahWah
|
|
19
19
|
# 7) [channel_mapping_family] = read 8 bit as unsigned integer
|
20
20
|
# 8) [channel_mapping_table]
|
21
21
|
@sample_rate = 48000
|
22
|
-
@pre_skip = identification_packet[10..11].
|
22
|
+
@pre_skip = identification_packet[10..11].unpack1("v")
|
23
23
|
|
24
|
-
comment_packet_id, comment_packet_body = [comment_packet[0..7], comment_packet[8
|
24
|
+
comment_packet_id, comment_packet_body = [comment_packet[0..7], comment_packet[8..]]
|
25
25
|
|
26
26
|
# Opus comment packet start with 'OpusTags'
|
27
|
-
return unless comment_packet_id ==
|
27
|
+
return unless comment_packet_id == "OpusTags"
|
28
28
|
|
29
29
|
parse_vorbis_comment(comment_packet_body)
|
30
30
|
end
|