format_parser 1.0.0 → 1.2.1
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 +9 -0
- data/lib/format_parser/version.rb +1 -1
- data/lib/parsers/moov_parser/decoder.rb +36 -0
- data/lib/parsers/moov_parser.rb +52 -4
- data/lib/video.rb +4 -0
- data/spec/parsers/moov_parser_spec.rb +4 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9127e98631d7e02c8972809990709a409778faafcc2fed8df4a01cba417a3637
|
4
|
+
data.tar.gz: 6705fd3d1fab6789b8d61e438191e3ba0a2db6a9f15747998647d489abcdb9a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95a1dd99683f3ebc60f0ccf9ed5a5715c9299979233b13712fe71f56f87a6feeb6cf0102cc256d5c0f0b3346b4a5c9b889709621c6afeb664640b881d703de7d
|
7
|
+
data.tar.gz: df71c70633550a9b99ed38c0e53049f39d58e4c6d1ade33882317e326da74bb3f3e1665b931205c4aed59482cc1bb2f9c227db429987259eab45e6e48ee036e7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 1.2.1
|
2
|
+
* Resolve bug when `stts` atom is `nil`
|
3
|
+
|
4
|
+
## 1.2.0
|
5
|
+
* Add support for `codecs` in moov_parser for video metadata
|
6
|
+
|
7
|
+
## 1.1.0
|
8
|
+
* Add support for `frame_rate` in moov_parser
|
9
|
+
|
1
10
|
## 1.0.0
|
2
11
|
* Dropping support for Ruby 2.2.X, 2.3.X and 2.4.X
|
3
12
|
* MP3: Fix negative length reads in edge cases by bumping `id3tag` version to `v0.14.2`
|
@@ -72,6 +72,7 @@ class FormatParser::MOOVParser::Decoder
|
|
72
72
|
|
73
73
|
trak_atoms.find do |trak_atom|
|
74
74
|
hdlr_atom = find_first_atom_by_path([trak_atom], 'trak', 'mdia', 'hdlr')
|
75
|
+
next if hdlr_atom.nil?
|
75
76
|
hdlr_atom.atom_fields[:component_type] == 'mhlr' && hdlr_atom.atom_fields[:component_subtype] == 'vide'
|
76
77
|
end
|
77
78
|
end
|
@@ -111,6 +112,41 @@ class FormatParser::MOOVParser::Decoder
|
|
111
112
|
}
|
112
113
|
end
|
113
114
|
|
115
|
+
def parse_stts_atom(io, _)
|
116
|
+
version = read_byte_value(io)
|
117
|
+
is_v1 = version == 1
|
118
|
+
stts = {
|
119
|
+
version: version,
|
120
|
+
flags: read_bytes(io, 3),
|
121
|
+
number_of_entries: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
122
|
+
entries: []
|
123
|
+
}
|
124
|
+
stts[:number_of_entries].times {
|
125
|
+
stts[:entries] << {
|
126
|
+
sample_count: read_32bit_uint(io),
|
127
|
+
sample_duration: read_32bit_uint(io)
|
128
|
+
}
|
129
|
+
}
|
130
|
+
stts
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_stsd_atom(io, _)
|
134
|
+
version = read_byte_value(io)
|
135
|
+
is_v1 = version == 1
|
136
|
+
stsd = {
|
137
|
+
version: version,
|
138
|
+
flags: read_bytes(io, 3),
|
139
|
+
number_of_entries: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
140
|
+
codecs: []
|
141
|
+
}
|
142
|
+
stsd[:number_of_entries].times {
|
143
|
+
codec_length = read_32bit_uint(io)
|
144
|
+
stsd[:codecs] << read_bytes(io, 4)
|
145
|
+
io.seek(io.pos + codec_length - 8) # 8 bytes is the header length containing the codec length and the codec name that we just did read
|
146
|
+
}
|
147
|
+
stsd
|
148
|
+
end
|
149
|
+
|
114
150
|
def parse_mdhd_atom(io, _)
|
115
151
|
version = read_byte_value(io)
|
116
152
|
is_v1 = version == 1
|
data/lib/parsers/moov_parser.rb
CHANGED
@@ -48,9 +48,9 @@ class FormatParser::MOOVParser
|
|
48
48
|
width, height = parse_dimensions(decoder, atom_tree)
|
49
49
|
|
50
50
|
# Try to find the "topmost" duration (respecting edits)
|
51
|
-
if
|
52
|
-
timescale =
|
53
|
-
duration =
|
51
|
+
if mvhd = decoder.find_first_atom_by_path(atom_tree, 'moov', 'mvhd')
|
52
|
+
timescale = mvhd.field_value(:tscale)
|
53
|
+
duration = mvhd.field_value(:duration)
|
54
54
|
media_duration_s = duration / timescale.to_f
|
55
55
|
end
|
56
56
|
|
@@ -68,9 +68,11 @@ class FormatParser::MOOVParser
|
|
68
68
|
format: format_from_moov_type(file_type),
|
69
69
|
width_px: width,
|
70
70
|
height_px: height,
|
71
|
+
frame_rate: parse_time_to_sample_atom(decoder, atom_tree)&.truncate(2),
|
71
72
|
media_duration_seconds: media_duration_s,
|
72
73
|
content_type: MP4_MIXED_MIME_TYPE,
|
73
|
-
|
74
|
+
codecs: parse_sample_description_atom(decoder, atom_tree),
|
75
|
+
intrinsics: atom_tree
|
74
76
|
)
|
75
77
|
end
|
76
78
|
end
|
@@ -115,5 +117,51 @@ class FormatParser::MOOVParser
|
|
115
117
|
maybe_atom_size >= minimum_ftyp_atom_size && maybe_ftyp_atom_signature == 'ftyp'
|
116
118
|
end
|
117
119
|
|
120
|
+
# Sample information is found in the 'time-to-sample' stts atom.
|
121
|
+
# The media atom mdhd is needed too in order to get the movie timescale
|
122
|
+
def parse_time_to_sample_atom(decoder, atom_tree)
|
123
|
+
video_trak_atom = decoder.find_video_trak_atom(atom_tree)
|
124
|
+
|
125
|
+
stts = if video_trak_atom
|
126
|
+
decoder.find_first_atom_by_path([video_trak_atom], 'trak', 'mdia', 'minf', 'stbl', 'stts')
|
127
|
+
else
|
128
|
+
decoder.find_first_atom_by_path(atom_tree, 'moov', 'trak', 'mdia', 'minf', 'stbl', 'stts')
|
129
|
+
end
|
130
|
+
|
131
|
+
mdhd = if video_trak_atom
|
132
|
+
decoder.find_first_atom_by_path([video_trak_atom], 'trak', 'mdia', 'mdhd')
|
133
|
+
else
|
134
|
+
decoder.find_first_atom_by_path(atom_tree, 'moov', 'trak', 'mdia', 'mdhd')
|
135
|
+
end
|
136
|
+
|
137
|
+
if stts && mdhd
|
138
|
+
timescale = mdhd.atom_fields[:tscale]
|
139
|
+
sample_duration = stts.field_value(:entries).dig(0, :sample_duration)
|
140
|
+
if timescale.nil? || timescale == 0 || sample_duration.nil? || sample_duration == 0
|
141
|
+
nil
|
142
|
+
else
|
143
|
+
timescale.to_f / sample_duration
|
144
|
+
end
|
145
|
+
else
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_sample_description_atom(decoder, atom_tree)
|
151
|
+
video_trak_atom = decoder.find_video_trak_atom(atom_tree)
|
152
|
+
|
153
|
+
stsd = if video_trak_atom
|
154
|
+
decoder.find_first_atom_by_path([video_trak_atom], 'trak', 'mdia', 'minf', 'stbl', 'stsd')
|
155
|
+
else
|
156
|
+
decoder.find_first_atom_by_path(atom_tree, 'moov', 'trak', 'mdia', 'minf', 'stbl', 'stsd')
|
157
|
+
end
|
158
|
+
|
159
|
+
if stsd
|
160
|
+
stsd.field_value(:codecs)
|
161
|
+
else
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
118
166
|
FormatParser.register_parser new, natures: :video, formats: FTYP_MAP.values, priority: 1
|
119
167
|
end
|
data/lib/video.rb
CHANGED
@@ -8,9 +8,13 @@ module FormatParser
|
|
8
8
|
|
9
9
|
attr_accessor :height_px
|
10
10
|
|
11
|
+
attr_accessor :frame_rate
|
12
|
+
|
11
13
|
# Type of the file (e.g :mp3)
|
12
14
|
attr_accessor :format
|
13
15
|
|
16
|
+
attr_accessor :codecs
|
17
|
+
|
14
18
|
# Duration of the media object (be it audio or video) in seconds,
|
15
19
|
# as a Float
|
16
20
|
attr_accessor :media_duration_seconds
|
@@ -94,6 +94,7 @@ describe FormatParser::MOOVParser do
|
|
94
94
|
expect(result.format).to eq(:mov)
|
95
95
|
expect(result.width_px).to eq(1920)
|
96
96
|
expect(result.height_px).to eq(1080)
|
97
|
+
expect(result.codecs).to eq(['apcn'])
|
97
98
|
end
|
98
99
|
|
99
100
|
it 'parses an MP4 video file and provides the necessary metadata' do
|
@@ -106,6 +107,8 @@ describe FormatParser::MOOVParser do
|
|
106
107
|
expect(result.format).to eq(:mov)
|
107
108
|
expect(result.width_px).to eq(160)
|
108
109
|
expect(result.height_px).to eq(90)
|
110
|
+
expect(result.frame_rate).to eq(14.98)
|
111
|
+
expect(result.codecs).to eq(['avc1'])
|
109
112
|
end
|
110
113
|
|
111
114
|
it 'provides filename hints' do
|
@@ -122,6 +125,7 @@ describe FormatParser::MOOVParser do
|
|
122
125
|
expect(result.format).to eq(:mov)
|
123
126
|
expect(result.width_px).to eq(640)
|
124
127
|
expect(result.height_px).to eq(360)
|
128
|
+
expect(result.frame_rate).to eq(30)
|
125
129
|
end
|
126
130
|
|
127
131
|
it 'does not raise error when a meta atom has size 0' do
|
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: 1.
|
4
|
+
version: 1.2.1
|
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: 2022-
|
12
|
+
date: 2022-04-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ks
|
@@ -315,7 +315,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
315
315
|
- !ruby/object:Gem::Version
|
316
316
|
version: '0'
|
317
317
|
requirements: []
|
318
|
-
rubygems_version: 3.
|
318
|
+
rubygems_version: 3.3.4
|
319
319
|
signing_key:
|
320
320
|
specification_version: 4
|
321
321
|
summary: A library for efficient parsing of file metadata
|