format_parser 1.0.0 → 1.1.0

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: e49394510e191185e3be1f3560ca182a103b9446225a1163294f2db6a912ae76
4
+ data.tar.gz: 4f52d761857020a0957711528ad4d10ee885c74745897fa14a5e69292abf0cc2
5
5
  SHA512:
6
- metadata.gz: f6d059459bcc4813a657bbe6b4eebdbd667d7fb15497ea1cfad2e92e355a68388fd558cc28af4e383f8d909e4a4735d4a485e34d26f1bef7b02e2420650ee0a9
7
- data.tar.gz: 990f6e2490a1c38b60e1ce0fcc8f9b3e0b5471ac429a04277171e59a61b0d60d2998834792a7ea124fe474f94446f411620539018ee15bfe176a4d767e1d5919
6
+ metadata.gz: 829bd488d021cb0db5ce2b718aa661bdf3d4f86a55e55808c8279367e955d35b2bdccdd2bdf805ff736cef6d49f8c0f425c1ec65c0d7f07ada2db684581d4c1f
7
+ data.tar.gz: ed76f04c157a865de03df0d614d0d353b53e1e9c9b0a164ac6b2a6612a358643ae210b00fd92b91fec6cefd8c8c71e6ff559e63df3343b13ce161175425dbd3a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 1.1.0
2
+ * Add support for `frame_rate` in moov_parser
3
+
1
4
  ## 1.0.0
2
5
  * Dropping support for Ruby 2.2.X, 2.3.X and 2.4.X
3
6
  * MP3: Fix negative length reads in edge cases by bumping `id3tag` version to `v0.14.2`
data/Gemfile CHANGED
@@ -1,4 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'ruby-debug-ide'
4
+ gem 'debase'
5
+ gem 'solargraph', group: :development
6
+
3
7
  # Gem dependencies specified in the gemspec
4
8
  gemspec
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
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,24 @@ 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
+
114
133
  def parse_mdhd_atom(io, _)
115
134
  version = read_byte_value(io)
116
135
  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
 
@@ -64,13 +64,15 @@ class FormatParser::MOOVParser
64
64
  intrinsics: atom_tree,
65
65
  )
66
66
  else
67
+ frame_rate = parse_sample_atom(decoder, atom_tree)&.truncate(2)
67
68
  FormatParser::Video.new(
68
69
  format: format_from_moov_type(file_type),
69
70
  width_px: width,
70
71
  height_px: height,
72
+ frame_rate: frame_rate,
71
73
  media_duration_seconds: media_duration_s,
72
74
  content_type: MP4_MIXED_MIME_TYPE,
73
- intrinsics: atom_tree,
75
+ intrinsics: atom_tree
74
76
  )
75
77
  end
76
78
  end
@@ -115,5 +117,35 @@ 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_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).first[: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
+
118
150
  FormatParser.register_parser new, natures: :video, formats: FTYP_MAP.values, priority: 1
119
151
  end
data/lib/video.rb CHANGED
@@ -8,6 +8,8 @@ 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
 
@@ -106,6 +106,7 @@ describe FormatParser::MOOVParser do
106
106
  expect(result.format).to eq(:mov)
107
107
  expect(result.width_px).to eq(160)
108
108
  expect(result.height_px).to eq(90)
109
+ expect(result.frame_rate).to eq(14.98)
109
110
  end
110
111
 
111
112
  it 'provides filename hints' do
@@ -122,6 +123,7 @@ describe FormatParser::MOOVParser do
122
123
  expect(result.format).to eq(:mov)
123
124
  expect(result.width_px).to eq(640)
124
125
  expect(result.height_px).to eq(360)
126
+ expect(result.frame_rate).to eq(30)
125
127
  end
126
128
 
127
129
  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.1.0
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-01 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