format_parser 1.0.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04e71e127d550feb0a5aaa54a27cb68c1671e034a5d309c520e4b81ea385e865
4
- data.tar.gz: 4617b5ea41651af01635d16dfba8de00f9765d1f3a092c7ec56170dd0d607495
3
+ metadata.gz: 9127e98631d7e02c8972809990709a409778faafcc2fed8df4a01cba417a3637
4
+ data.tar.gz: 6705fd3d1fab6789b8d61e438191e3ba0a2db6a9f15747998647d489abcdb9a2
5
5
  SHA512:
6
- metadata.gz: f6d059459bcc4813a657bbe6b4eebdbd667d7fb15497ea1cfad2e92e355a68388fd558cc28af4e383f8d909e4a4735d4a485e34d26f1bef7b02e2420650ee0a9
7
- data.tar.gz: 990f6e2490a1c38b60e1ce0fcc8f9b3e0b5471ac429a04277171e59a61b0d60d2998834792a7ea124fe474f94446f411620539018ee15bfe176a4d767e1d5919
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`
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '1.0.0'
2
+ VERSION = '1.2.1'
3
3
  end
@@ -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
@@ -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 mdhd = decoder.find_first_atom_by_path(atom_tree, 'moov', 'mvhd')
52
- timescale = mdhd.field_value(:tscale)
53
- duration = mdhd.field_value(: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
- intrinsics: atom_tree,
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.0.0
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-01-11 00:00:00.000000000 Z
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.1.6
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