format_parser 2.4.3 → 2.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/format_parser/version.rb +1 -1
- data/lib/parsers/iso_base_media_file_format/decoder.rb +7 -2
- data/spec/parsers/iso_base_media_file_format/decoder_spec.rb +115 -24
- data/spec/parsers/mov_parser_spec.rb +67 -18
- data/spec/parsers/mp4_parser_spec.rb +101 -27
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3b2c55550282f0f8d7b04723decd4d0d35f73afc3d987fbaaf14e2274838bab
|
4
|
+
data.tar.gz: 24119780ac672b473a1e01c3cc2bb4dc545aec45d8a5e331a09b37cebece100b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 251a7268c1c829424613f9a811bf59d88061b96efbc44557f6bfcffbbd0d7de5c1430f98b956eeace9bc590b1fbc6ada05eeaa215c526f24fe063c236bb5b8af
|
7
|
+
data.tar.gz: 2e263df394eddd302c1ea8d4f99bcac262e276f251a1cf37abe0475ec0c6ec36fa5a2c6181d88e9a889053b00fe54568d72f1bd083395cd6994f916ff848d3ed
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
## 2.4.4
|
2
|
+
* Prevent infinite loops when parsing ISOBMFF boxes with size = 0 (meaning that the box extends to the end of the file).
|
3
|
+
|
1
4
|
## 2.4.3
|
2
5
|
* Improve resiliency in ISOBMFF parsing to missing mandatory boxes and fields.
|
3
6
|
* Simplify ISOBMFF frame rate calculations.
|
@@ -129,10 +129,15 @@ module FormatParser
|
|
129
129
|
# @return [Box, nil]
|
130
130
|
def parse_box
|
131
131
|
position = @buf.pos
|
132
|
-
|
133
132
|
size = read_int
|
134
133
|
type = read_string(4)
|
135
|
-
|
134
|
+
|
135
|
+
if size == 1
|
136
|
+
size = read_int(n: 8)
|
137
|
+
elsif size == 0
|
138
|
+
size = @buf.size - position
|
139
|
+
end
|
140
|
+
|
136
141
|
body_size = size - (@buf.pos - position)
|
137
142
|
next_box_position = position + size
|
138
143
|
|
@@ -48,12 +48,29 @@ describe FormatParser::ISOBaseMediaFileFormat::Decoder do
|
|
48
48
|
end
|
49
49
|
let(:result) { subject.build_box_tree(0xFF, io) }
|
50
50
|
|
51
|
-
it
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
it
|
56
|
-
|
51
|
+
it 'parses successfully' do
|
52
|
+
expect(result.length).to eq(1)
|
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
|
66
|
+
|
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([])
|
73
|
+
end
|
57
74
|
end
|
58
75
|
|
59
76
|
context 'when parsing a container box' do
|
@@ -66,12 +83,29 @@ describe FormatParser::ISOBaseMediaFileFormat::Decoder do
|
|
66
83
|
end
|
67
84
|
let(:result) { subject.build_box_tree(0xFF, io) }
|
68
85
|
|
69
|
-
it
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
it
|
74
|
-
|
86
|
+
it 'parses successfully' do
|
87
|
+
expect(result.length).to eq(1)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'parses the correct type' do
|
91
|
+
expect(result[0].type).to eq('moov')
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'parses the correct position' do
|
95
|
+
expect(result[0].position).to eq(0)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'parses the correct size' do
|
99
|
+
expect(result[0].size).to eq(0x18)
|
100
|
+
end
|
101
|
+
|
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)
|
108
|
+
end
|
75
109
|
end
|
76
110
|
|
77
111
|
context 'when parsing an empty box' do
|
@@ -83,17 +117,32 @@ describe FormatParser::ISOBaseMediaFileFormat::Decoder do
|
|
83
117
|
end
|
84
118
|
let(:result) { subject.build_box_tree(0xFF, io) }
|
85
119
|
|
86
|
-
it
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
it
|
120
|
+
it 'parses successfully' do
|
121
|
+
expect(result.length).to eq(1)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'parses the correct type' do
|
125
|
+
expect(result[0].type).to eq('nmhd')
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'parses the correct position' do
|
129
|
+
expect(result[0].position).to eq(0)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'parses the correct size' do
|
133
|
+
expect(result[0].size).to eq(0x18)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'parses version and flags' do
|
91
137
|
expect(result[0].fields).to include({
|
92
138
|
version: 1,
|
93
139
|
flags: 'fla'
|
94
140
|
})
|
95
141
|
end
|
96
|
-
|
142
|
+
|
143
|
+
it 'skips children' do
|
144
|
+
expect(result[0].children).to eq([])
|
145
|
+
end
|
97
146
|
end
|
98
147
|
|
99
148
|
context 'when parsing a uuid box' do
|
@@ -106,12 +155,54 @@ describe FormatParser::ISOBaseMediaFileFormat::Decoder do
|
|
106
155
|
end
|
107
156
|
let(:result) { subject.build_box_tree(0xFF, io) }
|
108
157
|
|
109
|
-
it
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
it
|
114
|
-
|
158
|
+
it 'parses successfully' do
|
159
|
+
expect(result.length).to eq(1)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'parses the correct type' do
|
163
|
+
expect(result[0].type).to eq('uuid')
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'parses the correct position' do
|
167
|
+
expect(result[0].position).to eq(0)
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'parses the correct size' do
|
171
|
+
expect(result[0].size).to eq(0x20)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'parses usertype' do
|
175
|
+
expect(result[0].fields).to include({ usertype: usertype })
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'skips children' do
|
179
|
+
expect(result[0].children).to eq([])
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
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) }
|
193
|
+
|
194
|
+
it 'reads the rest of the file' do
|
195
|
+
expect(result.length).to eq(2)
|
196
|
+
expect(io.pos).to eq(0x20)
|
197
|
+
end
|
198
|
+
|
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 ')
|
205
|
+
end
|
115
206
|
end
|
116
207
|
end
|
117
208
|
end
|
@@ -35,15 +35,41 @@ describe FormatParser::MOVParser do
|
|
35
35
|
context "for #{path}" do
|
36
36
|
let(:result) { subject.call(File.open(path, 'rb')) }
|
37
37
|
|
38
|
-
it
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
it
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
it
|
38
|
+
it 'should not be nil' do
|
39
|
+
expect(result).not_to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should have video nature' do
|
43
|
+
expect(result.nature).to eq(:video)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should have MOV video content type' do
|
47
|
+
expect(result.content_type).to eq('video/quicktime')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should have MOV video format' do
|
51
|
+
expect(result.format).to eq(:mov)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should have a non-zero height ' do
|
55
|
+
expect(result.height_px).to be > 0
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should have a non-zero width' do
|
59
|
+
expect(result.width_px).to be > 0
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should have a non-zero duration' do
|
63
|
+
expect(result.media_duration_seconds).to be > 0
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should have a non-nil frame rate' do
|
67
|
+
expect(result.frame_rate).not_to be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should have intrinsics' do
|
71
|
+
expect(result.intrinsics).not_to be_nil
|
72
|
+
end
|
47
73
|
end
|
48
74
|
end
|
49
75
|
|
@@ -51,7 +77,9 @@ describe FormatParser::MOVParser do
|
|
51
77
|
context "for #{path}" do
|
52
78
|
let(:result) { subject.call(File.open(path, 'rb')) }
|
53
79
|
|
54
|
-
it
|
80
|
+
it 'should be nil' do
|
81
|
+
expect(result).to be_nil
|
82
|
+
end
|
55
83
|
end
|
56
84
|
end
|
57
85
|
|
@@ -61,10 +89,21 @@ describe FormatParser::MOVParser do
|
|
61
89
|
subject.call(File.open(path, 'rb'))
|
62
90
|
end
|
63
91
|
|
64
|
-
it
|
65
|
-
|
66
|
-
|
67
|
-
|
92
|
+
it 'should have the correct height' do
|
93
|
+
expect(result.height_px).to eq(360)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should have the correct width' do
|
97
|
+
expect(result.width_px).to eq(640)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should have the correct duration' do
|
101
|
+
expect(result.media_duration_seconds.truncate(2)).to eq(9.36)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should have the correct frame rate' do
|
105
|
+
expect(result.frame_rate).to eq(30)
|
106
|
+
end
|
68
107
|
end
|
69
108
|
|
70
109
|
context "for a scaled MOV video" do
|
@@ -73,8 +112,13 @@ describe FormatParser::MOVParser do
|
|
73
112
|
subject.call(File.open(path, 'rb'))
|
74
113
|
end
|
75
114
|
|
76
|
-
it
|
77
|
-
|
115
|
+
it 'should have the correct height' do
|
116
|
+
expect(result.height_px).to eq(720)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should have the correct width' do
|
120
|
+
expect(result.width_px).to eq(1280)
|
121
|
+
end
|
78
122
|
end
|
79
123
|
|
80
124
|
context "for a rotated MOV video" do
|
@@ -83,8 +127,13 @@ describe FormatParser::MOVParser do
|
|
83
127
|
subject.call(File.open(path, 'rb'))
|
84
128
|
end
|
85
129
|
|
86
|
-
it
|
87
|
-
|
130
|
+
it 'should have the correct height' do
|
131
|
+
expect(result.height_px).to eq(640)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should have the correct width' do
|
135
|
+
expect(result.width_px).to eq(360)
|
136
|
+
end
|
88
137
|
end
|
89
138
|
end
|
90
139
|
end
|
@@ -35,15 +35,41 @@ describe FormatParser::MP4Parser do
|
|
35
35
|
context "for #{path}" do
|
36
36
|
let(:result) { subject.call(File.open(path, 'rb')) }
|
37
37
|
|
38
|
-
it
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
it
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
it
|
38
|
+
it 'should not be nil' do
|
39
|
+
expect(result).not_to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should have video nature' do
|
43
|
+
expect(result.nature).to eq(:video)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should have MP4 video content type' do
|
47
|
+
expect(result.content_type).to eq('video/mp4')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should have MP4 video format' do
|
51
|
+
expect([:mp4, :mv4]).to include(result.format)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should have a non-zero height ' do
|
55
|
+
expect(result.height_px).to be > 0
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should have a non-zero width' do
|
59
|
+
expect(result.width_px).to be > 0
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should have a non-zero duration' do
|
63
|
+
expect(result.media_duration_seconds).to be > 0
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should have a non-nil frame rate' do
|
67
|
+
expect(result.frame_rate).not_to be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should have intrinsics' do
|
71
|
+
expect(result.intrinsics).not_to be_nil
|
72
|
+
end
|
47
73
|
end
|
48
74
|
end
|
49
75
|
|
@@ -51,12 +77,29 @@ describe FormatParser::MP4Parser do
|
|
51
77
|
context "for #{path}" do
|
52
78
|
let(:result) { subject.call(File.open(path, 'rb')) }
|
53
79
|
|
54
|
-
it
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
it
|
59
|
-
|
80
|
+
it 'should not be nil' do
|
81
|
+
expect(result).not_to be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should have audio nature' do
|
85
|
+
expect(result.nature).to eq(:audio)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should have MP4 audio content type' do
|
89
|
+
expect(result.content_type).to eq('audio/mp4')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should have MP4 audio format' do
|
93
|
+
expect([:m4a, :m4b, :m4p, :m4r]).to include(result.format)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should have a non-zero duration' do
|
97
|
+
expect(result.media_duration_seconds).to be > 0
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should have intrinsics' do
|
101
|
+
expect(result.intrinsics).not_to be_nil
|
102
|
+
end
|
60
103
|
end
|
61
104
|
end
|
62
105
|
|
@@ -64,7 +107,9 @@ describe FormatParser::MP4Parser do
|
|
64
107
|
context "for #{path}" do
|
65
108
|
let(:result) { subject.call(File.open(path, 'rb')) }
|
66
109
|
|
67
|
-
it
|
110
|
+
it 'should be nil' do
|
111
|
+
expect(result).to be_nil
|
112
|
+
end
|
68
113
|
end
|
69
114
|
end
|
70
115
|
|
@@ -74,10 +119,21 @@ describe FormatParser::MP4Parser do
|
|
74
119
|
subject.call(File.open(path, 'rb'))
|
75
120
|
end
|
76
121
|
|
77
|
-
it
|
78
|
-
|
79
|
-
|
80
|
-
|
122
|
+
it 'should have the correct height' do
|
123
|
+
expect(result.height_px).to eq(360)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should have the correct width' do
|
127
|
+
expect(result.width_px).to eq(640)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should have the correct duration' do
|
131
|
+
expect(result.media_duration_seconds.truncate(2)).to eq(9.36)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should have the correct frame rate' do
|
135
|
+
expect(result.frame_rate).to eq(30)
|
136
|
+
end
|
81
137
|
end
|
82
138
|
|
83
139
|
context "for a scaled MP4 video" do
|
@@ -86,8 +142,13 @@ describe FormatParser::MP4Parser do
|
|
86
142
|
subject.call(File.open(path, 'rb'))
|
87
143
|
end
|
88
144
|
|
89
|
-
it
|
90
|
-
|
145
|
+
it 'should have the correct height' do
|
146
|
+
expect(result.height_px).to eq(720)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should have the correct width' do
|
150
|
+
expect(result.width_px).to eq(1280)
|
151
|
+
end
|
91
152
|
end
|
92
153
|
|
93
154
|
context "for a rotated MP4 video" do
|
@@ -96,8 +157,13 @@ describe FormatParser::MP4Parser do
|
|
96
157
|
subject.call(File.open(path, 'rb'))
|
97
158
|
end
|
98
159
|
|
99
|
-
it
|
100
|
-
|
160
|
+
it 'should have the correct height' do
|
161
|
+
expect(result.height_px).to eq(640)
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should have the correct width' do
|
165
|
+
expect(result.width_px).to eq(360)
|
166
|
+
end
|
101
167
|
end
|
102
168
|
|
103
169
|
context "for a multi-track MP4 video" do
|
@@ -106,9 +172,17 @@ describe FormatParser::MP4Parser do
|
|
106
172
|
subject.call(File.open(path, 'rb'))
|
107
173
|
end
|
108
174
|
|
109
|
-
it
|
110
|
-
|
111
|
-
|
175
|
+
it 'should have the correct height' do
|
176
|
+
expect(result.height_px).to eq(1280)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should have the correct width' do
|
180
|
+
expect(result.width_px).to eq(1024)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should have the correct frame rate' do
|
184
|
+
expect(result.frame_rate).to eq(24)
|
185
|
+
end
|
112
186
|
end
|
113
187
|
end
|
114
188
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: format_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4.
|
4
|
+
version: 2.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Noah Berman
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-03-
|
12
|
+
date: 2023-03-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: exifr
|