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 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