format_parser 2.4.3 → 2.4.5
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 +6 -0
- data/lib/format_parser/version.rb +1 -1
- data/lib/parsers/iso_base_media_file_format/decoder.rb +8 -3
- 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: 7daa912b01159c41fc44b099188bac919984d4e0d75000acc7053e221b719f06
|
4
|
+
data.tar.gz: d081b9d2e1bbe5458b1ca57a5d1140c4cf1ad289a2d1af8557093c201b6969b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e19114d58216267ea215e91f6ec0d616eb58459d0a052f755decdb7fc3f7b7d653f6ab17a79d12e09039df538d2c7361cdee44e015802378489ca186e333fbc
|
7
|
+
data.tar.gz: c1a3b6fe0c05ec3002e1f823f14d06d95eec0f96f411190970fa954080827f08ca14ecb50b638edd23155c72f76b14da7ba305770adffa120b329a39c7557033
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 2.4.5
|
2
|
+
* Disable `udta` ISOBMFF box parsing, since their contents are not guaranteed to be consistent with the spec.
|
3
|
+
|
4
|
+
## 2.4.4
|
5
|
+
* Prevent infinite loops when parsing ISOBMFF boxes with size = 0 (meaning that the box extends to the end of the file).
|
6
|
+
|
1
7
|
## 2.4.3
|
2
8
|
* Improve resiliency in ISOBMFF parsing to missing mandatory boxes and fields.
|
3
9
|
* Simplify ISOBMFF frame rate calculations.
|
@@ -116,7 +116,7 @@ module FormatParser
|
|
116
116
|
'trak' => :container,
|
117
117
|
# 'trex' => :trex,
|
118
118
|
# 'tsel' => :tsel,
|
119
|
-
'udta' => :container,
|
119
|
+
# 'udta' => :container,
|
120
120
|
# 'url ' => :dref_url,
|
121
121
|
# 'urn ' => :dref_urn,
|
122
122
|
'uuid' => :uuid,
|
@@ -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.5
|
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-04-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: exifr
|