format_parser 2.3.0 → 2.4.4
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/CHANGELOG.md +21 -0
- data/README.md +13 -6
- data/format_parser.gemspec +1 -0
- data/lib/format_parser/version.rb +1 -1
- data/lib/io_utils.rb +18 -33
- data/lib/parsers/cr3_parser/decoder.rb +2 -2
- data/lib/parsers/cr3_parser.rb +13 -11
- data/lib/parsers/heif_parser.rb +46 -46
- data/lib/parsers/iso_base_media_file_format/box.rb +80 -0
- data/lib/parsers/iso_base_media_file_format/decoder.rb +348 -377
- data/lib/parsers/iso_base_media_file_format/utils.rb +89 -0
- data/lib/parsers/mov_parser/decoder.rb +53 -0
- data/lib/parsers/mov_parser.rb +48 -0
- data/lib/parsers/mp4_parser.rb +80 -0
- data/lib/parsers/pdf_parser.rb +5 -2
- data/lib/parsers/webp_parser.rb +2 -2
- data/spec/format_parser_spec.rb +1 -1
- data/spec/parsers/cr3_parser_spec.rb +3 -3
- data/spec/parsers/iso_base_media_file_format/box_spec.rb +399 -0
- data/spec/parsers/iso_base_media_file_format/decoder_spec.rb +117 -151
- data/spec/parsers/iso_base_media_file_format/utils_spec.rb +632 -0
- data/spec/parsers/mov_parser_spec.rb +139 -0
- data/spec/parsers/mp4_parser_spec.rb +188 -0
- data/spec/parsers/pdf_parser_spec.rb +37 -23
- metadata +25 -5
- data/lib/parsers/moov_parser/decoder.rb +0 -353
- data/lib/parsers/moov_parser.rb +0 -165
- data/spec/parsers/moov_parser_spec.rb +0 -144
@@ -1,14 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe FormatParser::ISOBaseMediaFileFormat::Decoder do
|
4
|
-
|
5
|
-
context '
|
6
|
-
|
7
|
-
|
4
|
+
describe '#build_box_tree' do
|
5
|
+
context 'when IO not provided' do
|
6
|
+
context 'when IO not previously provided' do
|
7
|
+
it 'raises an error' do
|
8
|
+
expect { subject.build_box_tree(0x0) }.to raise_error(/IO missing - supply a valid IO object/)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when IO previously provided' do
|
13
|
+
let(:io) { StringIO.new('') }
|
14
|
+
it 'does not raise an error' do
|
15
|
+
expect(subject.build_box_tree(0x0, io)).to eq([])
|
16
|
+
expect(subject.build_box_tree(0x0)).to eq([])
|
17
|
+
end
|
8
18
|
end
|
9
19
|
end
|
10
20
|
|
11
|
-
context
|
21
|
+
context 'when max_read smaller than IO length' do
|
12
22
|
let(:io) do
|
13
23
|
# moov
|
14
24
|
# moov
|
@@ -17,41 +27,53 @@ describe FormatParser::ISOBaseMediaFileFormat::Decoder do
|
|
17
27
|
end
|
18
28
|
|
19
29
|
it 'stops building the tree' do
|
20
|
-
expect(subject.
|
30
|
+
expect(subject.build_box_tree(0x0, io).length).to eq(0)
|
21
31
|
expect(io.pos).to eq(0)
|
22
32
|
|
23
|
-
expect(subject.
|
33
|
+
expect(subject.build_box_tree(0x8, io).length).to eq(1)
|
24
34
|
expect(io.pos).to eq(0x8)
|
25
35
|
io.seek(0)
|
26
36
|
|
27
|
-
expect(subject.
|
37
|
+
expect(subject.build_box_tree(0x10, io).length).to eq(2)
|
28
38
|
expect(io.pos).to eq(0x10)
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
|
-
context 'when parsing
|
42
|
+
context 'when parsing unknown box' do
|
33
43
|
let(:io) do
|
34
44
|
# foo
|
35
45
|
# |-> moov
|
36
46
|
input = [0x14].pack('N') + 'foo ' + [0x1].pack('N') + [0x8].pack('N') + 'moov'
|
37
47
|
StringIO.new(input)
|
38
48
|
end
|
49
|
+
let(:result) { subject.build_box_tree(0xFF, io) }
|
39
50
|
|
40
|
-
it 'parses
|
41
|
-
result = subject.build_atom_tree(0xFF, io)
|
51
|
+
it 'parses successfully' do
|
42
52
|
expect(result.length).to eq(1)
|
43
|
-
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'parses the correct type' do
|
56
|
+
expect(result[0].type).to eq('foo ')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'parses the correct position' do
|
60
|
+
expect(result[0].position).to eq(0)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'parses the correct size' do
|
64
|
+
expect(result[0].size).to eq(0x14)
|
65
|
+
end
|
44
66
|
|
45
|
-
|
46
|
-
expect(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
expect(
|
67
|
+
it 'skips additional fields' do
|
68
|
+
expect(result[0].fields).to eq({})
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'skips children' do
|
72
|
+
expect(result[0].children).to eq([])
|
51
73
|
end
|
52
74
|
end
|
53
75
|
|
54
|
-
context 'when parsing a container
|
76
|
+
context 'when parsing a container box' do
|
55
77
|
let(:io) do
|
56
78
|
# moov
|
57
79
|
# |-> foo
|
@@ -59,183 +81,127 @@ describe FormatParser::ISOBaseMediaFileFormat::Decoder do
|
|
59
81
|
input = [0x18].pack('N') + 'moov' + [0x8].pack('N') + 'foo ' + [0x8].pack('N') + 'bar '
|
60
82
|
StringIO.new(input)
|
61
83
|
end
|
84
|
+
let(:result) { subject.build_box_tree(0xFF, io) }
|
62
85
|
|
63
|
-
it 'parses
|
64
|
-
result = subject.build_atom_tree(0xFF, io)
|
86
|
+
it 'parses successfully' do
|
65
87
|
expect(result.length).to eq(1)
|
66
|
-
|
88
|
+
end
|
67
89
|
|
68
|
-
|
69
|
-
expect(
|
70
|
-
expect(moov_atom.position).to eq(0)
|
71
|
-
expect(moov_atom.size).to eq(0x18)
|
72
|
-
expect(moov_atom.fields).to eq({})
|
73
|
-
expect(moov_atom.children.length).to eq(2)
|
90
|
+
it 'parses the correct type' do
|
91
|
+
expect(result[0].type).to eq('moov')
|
74
92
|
end
|
75
|
-
end
|
76
93
|
|
77
|
-
|
78
|
-
|
79
|
-
# nmhd
|
80
|
-
# |-> foo
|
81
|
-
input = [0x18].pack('N') + 'nmhd' + [0x1].pack('c') + 'fla' + [0x2].pack('N') + [0x8].pack('N') + 'foo '
|
82
|
-
StringIO.new(input)
|
94
|
+
it 'parses the correct position' do
|
95
|
+
expect(result[0].position).to eq(0)
|
83
96
|
end
|
84
97
|
|
85
|
-
it 'parses
|
86
|
-
result
|
87
|
-
|
88
|
-
expect(io.pos).to eq(0x18)
|
98
|
+
it 'parses the correct size' do
|
99
|
+
expect(result[0].size).to eq(0x18)
|
100
|
+
end
|
89
101
|
|
90
|
-
|
91
|
-
expect(
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
flags: 'fla'
|
97
|
-
})
|
98
|
-
expect(nmhd_atom.children).to eq([])
|
102
|
+
it 'skips additional fields' do
|
103
|
+
expect(result[0].fields).to eq({})
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'parses children' do
|
107
|
+
expect(result[0].children.length).to eq(2)
|
99
108
|
end
|
100
109
|
end
|
101
110
|
|
102
|
-
context 'when parsing
|
103
|
-
let(:usertype) { '90f7c66ec2db476b977461e796f0dd4b' }
|
111
|
+
context 'when parsing an empty box' do
|
104
112
|
let(:io) do
|
105
|
-
|
113
|
+
# nmhd
|
114
|
+
# |-> foo
|
115
|
+
input = [0x18].pack('N') + 'nmhd' + [0x1].pack('c') + 'fla' + [0x2].pack('N') + [0x8].pack('N') + 'foo '
|
106
116
|
StringIO.new(input)
|
107
117
|
end
|
118
|
+
let(:result) { subject.build_box_tree(0xFF, io) }
|
108
119
|
|
109
|
-
it 'parses
|
110
|
-
# uuid
|
111
|
-
# |-> foo
|
112
|
-
result = subject.build_atom_tree(0xFF, io)
|
120
|
+
it 'parses successfully' do
|
113
121
|
expect(result.length).to eq(1)
|
114
|
-
expect(io.pos).to eq(0x20)
|
115
|
-
|
116
|
-
nmhd_atom = result[0]
|
117
|
-
expect(nmhd_atom.type).to eq('uuid')
|
118
|
-
expect(nmhd_atom.position).to eq(0)
|
119
|
-
expect(nmhd_atom.size).to eq(0x20)
|
120
|
-
expect(nmhd_atom.fields).to include({
|
121
|
-
usertype: usertype,
|
122
|
-
})
|
123
|
-
expect(nmhd_atom.children).to eq([])
|
124
122
|
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
123
|
|
129
|
-
|
130
|
-
|
131
|
-
context 'without fields and/or children' do
|
132
|
-
subject { described_class.new('foo', 0, 0) }
|
133
|
-
|
134
|
-
it 'sets them as an empty array/hash' do
|
135
|
-
expect(subject.type).to eq('foo')
|
136
|
-
expect(subject.position).to eq(0)
|
137
|
-
expect(subject.size).to eq(0)
|
138
|
-
expect(subject.fields).to eq({})
|
139
|
-
expect(subject.children).to eq([])
|
124
|
+
it 'parses the correct type' do
|
125
|
+
expect(result[0].type).to eq('nmhd')
|
140
126
|
end
|
141
|
-
end
|
142
127
|
|
143
|
-
|
144
|
-
|
145
|
-
|
128
|
+
it 'parses the correct position' do
|
129
|
+
expect(result[0].position).to eq(0)
|
130
|
+
end
|
146
131
|
|
147
|
-
|
132
|
+
it 'parses the correct size' do
|
133
|
+
expect(result[0].size).to eq(0x18)
|
134
|
+
end
|
148
135
|
|
149
|
-
it '
|
150
|
-
expect(
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
expect(subject.children).to eq(children)
|
136
|
+
it 'parses version and flags' do
|
137
|
+
expect(result[0].fields).to include({
|
138
|
+
version: 1,
|
139
|
+
flags: 'fla'
|
140
|
+
})
|
155
141
|
end
|
156
|
-
end
|
157
|
-
end
|
158
142
|
|
159
|
-
|
160
|
-
|
161
|
-
subject { described_class.new('root', 0, 0) }
|
162
|
-
it 'returns nil' do
|
163
|
-
expect(subject.find_first_descendent(%w[root foo])).to be_nil
|
143
|
+
it 'skips children' do
|
144
|
+
expect(result[0].children).to eq([])
|
164
145
|
end
|
165
146
|
end
|
166
147
|
|
167
|
-
context '
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
])
|
148
|
+
context 'when parsing a uuid box' do
|
149
|
+
let(:usertype) { '90f7c66ec2db476b977461e796f0dd4b' }
|
150
|
+
let(:io) do
|
151
|
+
# uuid
|
152
|
+
# |-> foo
|
153
|
+
input = [0x20].pack('N') + 'uuid' + [usertype].pack('H*') + [0x8].pack('N') + 'foo '
|
154
|
+
StringIO.new(input)
|
175
155
|
end
|
156
|
+
let(:result) { subject.build_box_tree(0xFF, io) }
|
176
157
|
|
177
|
-
it '
|
178
|
-
expect(
|
158
|
+
it 'parses successfully' do
|
159
|
+
expect(result.length).to eq(1)
|
179
160
|
end
|
180
|
-
end
|
181
|
-
|
182
|
-
context 'with multiple descendents of the given type(s)' do
|
183
|
-
let(:descendent) { described_class.new('bar', 0, 0) }
|
184
161
|
|
185
|
-
|
186
|
-
|
187
|
-
described_class.new('foo', 0, 0, nil, [
|
188
|
-
descendent
|
189
|
-
]),
|
190
|
-
described_class.new('bar', 0, 0),
|
191
|
-
])
|
162
|
+
it 'parses the correct type' do
|
163
|
+
expect(result[0].type).to eq('uuid')
|
192
164
|
end
|
193
165
|
|
194
|
-
it '
|
195
|
-
expect(
|
166
|
+
it 'parses the correct position' do
|
167
|
+
expect(result[0].position).to eq(0)
|
196
168
|
end
|
197
|
-
end
|
198
|
-
end
|
199
169
|
|
200
|
-
|
201
|
-
|
202
|
-
subject { described_class.new('root', 0, 0) }
|
203
|
-
it 'returns an empty array' do
|
204
|
-
expect(subject.select_descendents(%w[root foo])).to eq([])
|
170
|
+
it 'parses the correct size' do
|
171
|
+
expect(result[0].size).to eq(0x20)
|
205
172
|
end
|
206
|
-
end
|
207
173
|
|
208
|
-
|
209
|
-
|
210
|
-
described_class.new('root', 0, 0, nil, [
|
211
|
-
described_class.new('foo', 0, 0),
|
212
|
-
described_class.new('bar', 0, 0, nil, [
|
213
|
-
described_class.new('baz', 0, 0)
|
214
|
-
])
|
215
|
-
])
|
174
|
+
it 'parses usertype' do
|
175
|
+
expect(result[0].fields).to include({ usertype: usertype })
|
216
176
|
end
|
217
177
|
|
218
|
-
it '
|
219
|
-
expect(
|
178
|
+
it 'skips children' do
|
179
|
+
expect(result[0].children).to eq([])
|
220
180
|
end
|
221
181
|
end
|
222
182
|
|
223
|
-
context '
|
224
|
-
let(:
|
225
|
-
|
226
|
-
|
183
|
+
context 'when parsing a box with 0 size' do
|
184
|
+
let(:io) do
|
185
|
+
# foo
|
186
|
+
# moov
|
187
|
+
# |-> bar
|
188
|
+
# |-> baz
|
189
|
+
input = [0x8].pack('N') + 'foo ' + [0x0].pack('N') + 'moov' + [0x8].pack('N') + 'bar ' + [0x8].pack('N') + 'baz '
|
190
|
+
StringIO.new(input)
|
191
|
+
end
|
192
|
+
let(:result) { subject.build_box_tree(0xFF, io) }
|
227
193
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
descendent_1
|
232
|
-
]),
|
233
|
-
descendent_2,
|
234
|
-
])
|
194
|
+
it 'reads the rest of the file' do
|
195
|
+
expect(result.length).to eq(2)
|
196
|
+
expect(io.pos).to eq(0x20)
|
235
197
|
end
|
236
198
|
|
237
|
-
it '
|
238
|
-
expect(
|
199
|
+
it 'parses correctly' do
|
200
|
+
expect(result[0].type).to eq('foo ')
|
201
|
+
expect(result[1].type).to eq('moov')
|
202
|
+
expect(result[1].children.length).to eq(2)
|
203
|
+
expect(result[1].children[0].type).to eq('bar ')
|
204
|
+
expect(result[1].children[1].type).to eq('baz ')
|
239
205
|
end
|
240
206
|
end
|
241
207
|
end
|