format_parser 0.2.0 → 0.3.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/.travis.yml +1 -0
  4. data/README.md +14 -11
  5. data/format_parser.gemspec +11 -10
  6. data/lib/care.rb +9 -17
  7. data/lib/format_parser.rb +11 -13
  8. data/lib/format_parser/version.rb +1 -1
  9. data/lib/io_constraint.rb +3 -3
  10. data/lib/io_utils.rb +4 -10
  11. data/lib/parsers/aiff_parser.rb +9 -10
  12. data/lib/parsers/dpx_parser.rb +42 -42
  13. data/lib/parsers/dsl.rb +2 -2
  14. data/lib/parsers/exif_parser.rb +3 -8
  15. data/lib/parsers/fdx_parser.rb +3 -3
  16. data/lib/parsers/gif_parser.rb +3 -5
  17. data/lib/parsers/jpeg_parser.rb +4 -8
  18. data/lib/parsers/moov_parser.rb +8 -6
  19. data/lib/parsers/moov_parser/decoder.rb +105 -122
  20. data/lib/parsers/mp3_parser.rb +36 -46
  21. data/lib/parsers/mp3_parser/id3_v1.rb +7 -13
  22. data/lib/parsers/mp3_parser/id3_v2.rb +6 -6
  23. data/lib/parsers/png_parser.rb +5 -12
  24. data/lib/parsers/psd_parser.rb +2 -2
  25. data/lib/parsers/tiff_parser.rb +10 -12
  26. data/lib/parsers/wav_parser.rb +3 -3
  27. data/lib/read_limiter.rb +3 -7
  28. data/lib/remote_io.rb +3 -6
  29. data/spec/care_spec.rb +10 -10
  30. data/spec/file_information_spec.rb +1 -3
  31. data/spec/format_parser_spec.rb +6 -6
  32. data/spec/io_utils_spec.rb +7 -7
  33. data/spec/parsers/exif_parser_spec.rb +2 -3
  34. data/spec/parsers/gif_parser_spec.rb +1 -1
  35. data/spec/parsers/jpeg_parser_spec.rb +0 -1
  36. data/spec/parsers/moov_parser_spec.rb +2 -3
  37. data/spec/parsers/png_parser_spec.rb +1 -1
  38. data/spec/parsers/tiff_parser_spec.rb +0 -1
  39. data/spec/parsers/wav_parser_spec.rb +3 -3
  40. data/spec/read_limiter_spec.rb +0 -1
  41. data/spec/remote_fetching_spec.rb +34 -20
  42. data/spec/remote_io_spec.rb +20 -21
  43. data/spec/spec_helper.rb +2 -2
  44. metadata +19 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aac884390c22699e4c3508ef5ecd363d2f1d2b5a
4
- data.tar.gz: da10c68c25b0ba46929282a1bcd918e9bf4513b1
3
+ metadata.gz: 606211b4e5b24b26244fdc1a869e9e3c3a1960ea
4
+ data.tar.gz: c8da075299f9373ababeaffe28454c94329adf2b
5
5
  SHA512:
6
- metadata.gz: 74efa5fe85532815e9475e711af8cfc6a2a4478c1832479ec073f7fdecdf4ed2997a61d2c5e068fe6013cd8fb9fbea7c16eaf1bc109485dde7d469b4cec62342
7
- data.tar.gz: 2a8da42f8d4f49dff0b7c76393d5c9385ef109ada91d539235d5afbef98689e5f99ca4a5b9a99843c109d2665ea1c4843192d3cf9c25d4ccb3117c98c25fb455
6
+ metadata.gz: ff3f7310ba2ff1b414b03b066bbddc42ee80fd448e51dfccb11d2bfe4a9d088d4b92b4e4c9cfcbf39173a9d69c627125b112cc4e7902d66080b113751f9a3b2e
7
+ data.tar.gz: 2e6146596b92f490641d41d48e71d51486900edcd7aa25a060345e7ee7c6d6272220a7a019d047b67296d9e9f7a062b3af431817b624b8c1c11891c422eaf14c
@@ -0,0 +1,2 @@
1
+ inherit_gem:
2
+ wetransfer_style: ruby/default.yml
@@ -8,4 +8,5 @@ matrix:
8
8
  allow_failures:
9
9
  - rvm: jruby-9.0
10
10
  script:
11
+ - bundle exec rubocop
11
12
  - bundle exec rspec
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # format_parser
2
2
 
3
+
3
4
  is a Ruby library for prying open video, image, document, and audio files.
4
5
  It includes a number of parser modules that try to recover metadata useful for post-processing and layout while reading the absolute
5
6
  minimum amount of data possible.
@@ -7,35 +8,37 @@ minimum amount of data possible.
7
8
  `format_parser` is inspired by [imagesize,](https://rubygems.org/gem/imagesize) [fastimage](https://github.com/sdsykes/fastimage)
8
9
  and [dimensions,](https://github.com/sstephenson/dimensions) borrowing from them where appropriate.
9
10
 
11
+ [![Gem Version](https://badge.fury.io/rb/format_parser.svg)](https://badge.fury.io/rb/format_parser) [![Build Status](https://travis-ci.org/WeTransfer/format_parser.svg?branch=master)](https://travis-ci.org/WeTransfer/format_parser)
12
+
10
13
  ## Currently supported filetypes:
11
14
 
12
15
  `TIFF, PSD, PNG, MP3, JPEG, GIF, DPX, AIFF, WAV, FDX, MOV, MP4`
13
16
 
14
- ...with more on the way!
17
+ ...with [more](https://github.com/WeTransfer/format_parser/issues?q=is%3Aissue+is%3Aopen+label%3Aformats) on the way!
15
18
 
16
19
  ## Basic usage
17
20
 
18
- Pass an IO object that responds to `read` and `seek` to `FormatParser` and an array of matches will be returned.
21
+ Pass an IO object that responds to `read` and `seek` to `FormatParser` and the first confirmed match will be returned.
19
22
 
20
23
  ```ruby
21
- matches = FormatParser.parse(File.open("myimage.jpg", "rb"))
22
- matches.first.nature #=> :image
23
- matches.first.format #=> :jpg
24
- matches.first.width_px #=> 320
25
- matches.first.height_px #=> 240
26
- matches.first.orientation #=> :top_left
24
+ match = FormatParser.parse(File.open("myimage.jpg", "rb"))
25
+ match.nature #=> :image
26
+ match.format #=> :jpg
27
+ match.width_px #=> 320
28
+ match.height_px #=> 240
29
+ match.orientation #=> :top_left
27
30
  ```
28
31
 
29
- If you would rather receive only one result, call the gem as follows:
32
+ If you would rather receive all potential results from the gem, call the gem as follows:
30
33
 
31
34
  ```ruby
32
- FormatParser.parse(File.open("myimage.jpg", "rb"), returns: :one)
35
+ FormatParser.parse(File.open("myimage.jpg", "rb"), results: :all)
33
36
  ```
34
37
 
35
38
  You can also optimize the metadata extraction by providing hints to the gem:
36
39
 
37
40
  ```ruby
38
- FormatParser.parse(File.open("myimage", "rb"), natures: [:video, :image], formats: [:jpg, :png, :mp4])
41
+ FormatParser.parse(File.open("myimage", "rb"), natures: [:video, :image], formats: [:jpg, :png, :mp4], results: :all)
39
42
  ```
40
43
 
41
44
  ## Creating your own parsers
@@ -1,42 +1,42 @@
1
- # coding: utf-8
1
+
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'format_parser/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "format_parser"
7
+ spec.name = 'format_parser'
8
8
  spec.version = FormatParser::VERSION
9
9
  spec.authors = ['Noah Berman', 'Julik Tarkhanov']
10
10
  spec.email = ['noah@noahberman.org', 'me@julik.nl']
11
11
  spec.licenses = ['MIT']
12
- spec.summary = "A library for efficient parsing of file metadata"
12
+ spec.summary = 'A library for efficient parsing of file metadata'
13
13
  spec.description = "A Ruby library for prying open files you can convert to a previewable format, such as video, image and audio files. It includes
14
14
  a number of parser modules that try to recover metadata useful for post-processing and layout while reading the absolute
15
15
  minimum amount of data possible."
16
- spec.homepage = "https://github.com/WeTransfer/format_parser"
17
- spec.license = "MIT"
16
+ spec.homepage = 'https://github.com/WeTransfer/format_parser'
17
+ spec.license = 'MIT'
18
18
  # Alert people to a change in the gem's interface, will remove in a subsequent version
19
19
  spec.post_install_message = %q{
20
20
  -----------------------------------------------------------------------------
21
- | ATTENTION: format_parser v0.2.0 introduces changes to the gem's interface.|
21
+ | ALERT: format_parser **v0.3.0** introduces changes to the gem's interface.|
22
22
  | See https://github.com/WeTransfer/format_parser#basic-usage |
23
23
  | for up-to-date usage instructions. Thank you for using format_parser! :) |
24
24
  -----------------------------------------------------------------------------
25
25
  }
26
26
  # to allow pushing to a single host or delete this section to allow pushing to any host.
27
27
  if spec.respond_to?(:metadata)
28
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
28
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
29
29
  else
30
- raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
30
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
31
31
  end
32
32
 
33
33
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
34
34
  # Make sure large fixture files are not packaged with the gem every time
35
35
  f.match(%r{^spec/fixtures/})
36
36
  end
37
- spec.bindir = "exe"
37
+ spec.bindir = 'exe'
38
38
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
39
- spec.require_paths = ["lib"]
39
+ spec.require_paths = ['lib']
40
40
 
41
41
  spec.add_dependency 'ks', '~> 0.0.1'
42
42
  spec.add_dependency 'exifr', '~> 1.0'
@@ -47,4 +47,5 @@ Gem::Specification.new do |spec|
47
47
  spec.add_development_dependency 'simplecov', '~> 0.15'
48
48
  spec.add_development_dependency 'pry', '~> 0.11'
49
49
  spec.add_development_dependency 'yard', '~> 0.9'
50
+ spec.add_development_dependency 'wetransfer_style', '0.4.0'
50
51
  end
@@ -7,8 +7,9 @@ class Care
7
7
  DEFAULT_PAGE_SIZE = 16 * 1024
8
8
 
9
9
  class IOWrapper
10
- def initialize(io, cache=Cache.new(DEFAULT_PAGE_SIZE))
11
- @io, @cache = io, cache
10
+ def initialize(io, cache = Cache.new(DEFAULT_PAGE_SIZE))
11
+ @io = io
12
+ @cache = cache
12
13
  @pos = 0
13
14
  end
14
15
 
@@ -26,8 +27,9 @@ class Care
26
27
 
27
28
  def read(n_bytes)
28
29
  return '' if n_bytes == 0 # As hardcoded for all Ruby IO objects
30
+ raise ArgumentError, "negative length #{n_bytes} given" if n_bytes < 0 # also as per Ruby IO objects
29
31
  read = @cache.byteslice(@io, @pos, n_bytes)
30
- return nil unless read && !read.empty?
32
+ return unless read && !read.empty?
31
33
  @pos += read.bytesize
32
34
  read
33
35
  end
@@ -40,10 +42,6 @@ class Care
40
42
  clear
41
43
  @io.close if @io.respond_to?(:close)
42
44
  end
43
-
44
- def size
45
- @io.size
46
- end
47
45
  end
48
46
 
49
47
  # Stores cached pages of data from the given IO as strings.
@@ -51,7 +49,7 @@ class Care
51
49
  class Cache
52
50
  def initialize(page_size = DEFAULT_PAGE_SIZE)
53
51
  @page_size = page_size.to_i
54
- raise ArgumentError, "The page size must be a positive Integer" unless @page_size > 0
52
+ raise ArgumentError, 'The page size must be a positive Integer' unless @page_size > 0
55
53
  @pages = {}
56
54
  @lowest_known_empty_page = nil
57
55
  end
@@ -72,7 +70,7 @@ class Care
72
70
  first_page = at / @page_size
73
71
  last_page = (at + n_bytes) / @page_size
74
72
 
75
- relevant_pages = (first_page..last_page).map{|i| hydrate_page(io, i) }
73
+ relevant_pages = (first_page..last_page).map { |i| hydrate_page(io, i) }
76
74
 
77
75
  # Create one string combining all the pages which are relevant for
78
76
  # us - it is much easier to address that string instead of piecing
@@ -96,11 +94,7 @@ class Care
96
94
 
97
95
  # Returning an empty string from read() is very confusing for the caller,
98
96
  # and no builtins do this - if we are at EOF we should return nil
99
- if slice && !slice.empty?
100
- slice
101
- else
102
- nil
103
- end
97
+ slice if slice && !slice.empty?
104
98
  end
105
99
 
106
100
  def clear
@@ -110,9 +104,7 @@ class Care
110
104
  def hydrate_page(io, page_i)
111
105
  # Avoid trying to read the page if we know there is no content to fill it
112
106
  # in the underlying IO
113
- if @lowest_known_empty_page && page_i >= @lowest_known_empty_page
114
- return nil
115
- end
107
+ return if @lowest_known_empty_page && page_i >= @lowest_known_empty_page
116
108
 
117
109
  @pages[page_i] ||= read_page(io, page_i)
118
110
  end
@@ -1,5 +1,3 @@
1
- require 'thread'
2
-
3
1
  module FormatParser
4
2
  require_relative 'image'
5
3
  require_relative 'audio'
@@ -28,7 +26,7 @@ module FormatParser
28
26
  end
29
27
  end
30
28
 
31
- def self.parse_http(url)
29
+ def self.parse_http(url, **kwargs)
32
30
  remote_io = RemoteIO.new(url)
33
31
  cached_io = Care::IOWrapper.new(remote_io)
34
32
 
@@ -36,25 +34,27 @@ module FormatParser
36
34
  # by all parsers anyway. Additionally, when using RemoteIO we need
37
35
  # to explicitly obtain the size of the resource, which is only available
38
36
  # after having performed at least one successful GET - at least on S3
39
- cached_io.read(1); cached_io.seek(0)
37
+ cached_io.read(1)
38
+ cached_io.seek(0)
40
39
 
41
- parse(cached_io)
40
+ parse(cached_io, **kwargs)
42
41
  end
43
42
 
44
- def self.parse(io, natures: @natures.to_a, formats: @formats.to_a, returns: :all)
43
+ # Return all by default
44
+ def self.parse(io, natures: @natures.to_a, formats: @formats.to_a, results: :first)
45
45
  # If the cache is preconfigured do not apply an extra layer. It is going
46
46
  # to be preconfigured when using parse_http.
47
47
  io = Care::IOWrapper.new(io) unless io.is_a?(Care::IOWrapper)
48
48
 
49
49
  # How many results has the user asked for? Used to determinate whether an array
50
50
  # is returned or not.
51
- amount = case returns
51
+ amount = case results
52
52
  when :all
53
53
  @parsers.count
54
- when :one
54
+ when :first
55
55
  1
56
56
  else
57
- throw ArgumentError.new(":returns does not match any supported mode (:all, :one)")
57
+ throw ArgumentError.new(':results does not match any supported mode (:all, :first)')
58
58
  end
59
59
 
60
60
  # Always instantiate parsers fresh for each input, since they might
@@ -64,7 +64,7 @@ module FormatParser
64
64
  # We need to rewind for each parser, anew
65
65
  io.seek(0)
66
66
  # Limit how many operations the parser can perform
67
- limited_io = ReadLimiter.new(io, max_bytes: 512*1024, max_reads: 64*1024, max_seeks: 64*1024)
67
+ limited_io = ReadLimiter.new(io, max_bytes: 512 * 1024, max_reads: 64 * 1024, max_seeks: 64 * 1024)
68
68
  begin
69
69
  parser.call(limited_io)
70
70
  rescue IOUtils::InvalidRead
@@ -82,14 +82,12 @@ module FormatParser
82
82
  results.to_a
83
83
  end
84
84
 
85
- private
86
-
87
85
  def self.parsers_for(natures, formats)
88
86
  # returns lazy enumerator for only computing the minimum amount of work (see :returns keyword argument)
89
87
  @parsers.map(&:new).select do |parser|
90
88
  # Do a given parser contain any nature and/or format asked by the user?
91
89
  (natures & parser.natures).size > 0 && (formats & parser.formats).size > 0
92
- end.lazy
90
+ end.lazy
93
91
  end
94
92
 
95
93
  Dir.glob(__dir__ + '/parsers/*.rb').sort.each do |parser_file|
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -19,15 +19,15 @@ class FormatParser::IOConstraint
19
19
  def initialize(io)
20
20
  @io = io
21
21
  end
22
-
22
+
23
23
  def read(n_bytes)
24
24
  @io.read(n_bytes)
25
25
  end
26
-
26
+
27
27
  def seek(absolute_offset)
28
28
  @io.seek(absolute_offset)
29
29
  end
30
-
30
+
31
31
  def size
32
32
  @io.size
33
33
  end
@@ -3,12 +3,10 @@ module FormatParser::IOUtils
3
3
  end
4
4
 
5
5
  def safe_read(io, n)
6
- if n.nil?
7
- raise ArgumentError, "Unbounded reads are not supported"
8
- end
6
+ raise ArgumentError, 'Unbounded reads are not supported' if n.nil?
9
7
  buf = io.read(n)
10
8
 
11
- if !buf
9
+ unless buf
12
10
  raise InvalidRead, "We wanted to read #{n} bytes from the IO, but the IO is at EOF"
13
11
  end
14
12
  if buf.bytesize != n
@@ -19,15 +17,11 @@ module FormatParser::IOUtils
19
17
  end
20
18
 
21
19
  def safe_skip(io, n)
22
- if n.nil?
23
- raise ArgumentError, "Unbounded skips are not supported"
24
- end
20
+ raise ArgumentError, 'Unbounded skips are not supported' if n.nil?
25
21
 
26
22
  return if n == 0
27
23
 
28
- if n < 0
29
- raise InvalidRead, "Negative skips are not supported"
30
- end
24
+ raise InvalidRead, 'Negative skips are not supported' if n < 0
31
25
 
32
26
  if io.respond_to?(:pos)
33
27
  io.seek(io.pos + n)
@@ -25,11 +25,11 @@ class FormatParser::AIFFParser
25
25
  def call(io)
26
26
  io = FormatParser::IOConstraint.new(io)
27
27
  form_chunk_type, chunk_size = safe_read(io, 8).unpack('a4N')
28
- return unless form_chunk_type == "FORM" && chunk_size > 4
28
+ return unless form_chunk_type == 'FORM' && chunk_size > 4
29
29
 
30
30
  fmt_chunk_type = safe_read(io, 4)
31
-
32
- return unless fmt_chunk_type == "AIFF"
31
+
32
+ return unless fmt_chunk_type == 'AIFF'
33
33
 
34
34
  # There might be COMT chunks, for example in Logic exports
35
35
  loop do
@@ -49,16 +49,15 @@ class FormatParser::AIFFParser
49
49
  safe_skip(io, chunk_size)
50
50
  next
51
51
  else # This most likely not an AIFF
52
- return nil
52
+ return
53
53
  end
54
54
  end
55
55
  end
56
56
 
57
57
  def unpack_comm_chunk(io)
58
58
  # Parse the COMM chunk
59
- channels, sample_frames, sample_size, sample_rate_extended = safe_read(io, 2 + 4 + 2 + 10).unpack('nNna10')
59
+ channels, sample_frames, _sample_size, sample_rate_extended = safe_read(io, 2 + 4 + 2 + 10).unpack('nNna10')
60
60
  sample_rate = unpack_extended_float(sample_rate_extended)
61
- bytes_per_sample = (sample_size - 1) / 8 + 1
62
61
 
63
62
  return unless sample_frames > 0
64
63
 
@@ -71,18 +70,18 @@ class FormatParser::AIFFParser
71
70
  num_audio_channels: channels,
72
71
  audio_sample_rate_hz: sample_rate.to_i,
73
72
  media_duration_frames: sample_frames,
74
- media_duration_seconds: duration_in_seconds,
73
+ media_duration_seconds: duration_in_seconds
75
74
  )
76
75
  end
77
-
76
+
78
77
  def unpack_extended_float(ten_bytes_string)
79
78
  extended = ten_bytes_string.unpack('B80')[0]
80
79
 
81
80
  sign = extended[0, 1]
82
81
  exponent = extended[1, 15].to_i(2) - ((1 << 14) - 1)
83
82
  fraction = extended[16, 64].to_i(2)
84
-
85
- ((sign == '1') ? -1.0 : 1.0) * (fraction.to_f / ((1 << 63) - 1)) * (2 ** exponent)
83
+
84
+ (sign == '1' ? -1.0 : 1.0) * (fraction.to_f / ((1 << 63) - 1)) * (2**exponent)
86
85
  end
87
86
 
88
87
  FormatParser.register_parser_constructor self
@@ -6,7 +6,7 @@ class FormatParser::DPXParser
6
6
  formats :dpx
7
7
 
8
8
  FILE_INFO = [
9
- # :x4, # magic bytes SDPX, we read them anyway so not in the pattern
9
+ # :x4, # magic bytes SDPX, we read them anyway so not in the pattern
10
10
  :x4, # u32 :image_offset, :desc => 'Offset to image data in bytes', :req => true
11
11
  :x8, # char :version, 8, :desc => 'Version of header format', :req => true
12
12
  :x4, # u32 :file_size, :desc => "Total image size in bytes", :req => true
@@ -20,48 +20,48 @@ class FormatParser::DPXParser
20
20
  :x200, # char :project, 200, :desc => 'Project name'
21
21
  :x200, # char :copyright, 200, :desc => 'Copyright'
22
22
  :x4, # u32 :encrypt_key, :desc => 'Encryption key'
23
- :x104, # blanking :reserve, 104
23
+ :x104, # blanking :reserve, 104
24
24
  ].join
25
25
 
26
26
  FILM_INFO = [
27
- :x2, # char :id, 2, :desc => 'Film mfg. ID code (2 digits from film edge code)'
28
- :x2, # char :type, 2, :desc => 'Film type (2 digits from film edge code)'
29
- :x2, # char :offset, 2, :desc => 'Offset in perfs (2 digits from film edge code)'
30
- :x6, # char :prefix, 6, :desc => 'Prefix (6 digits from film edge code'
31
- :x4, # char :count, 4, :desc => 'Count (4 digits from film edge code)'
32
- :x32,# char :format, 32, :desc => 'Format (e.g. Academy)'
33
- :x4, # u32 :frame_position, :desc => 'Frame position in sequence'
34
- :x4, # u32 :sequence_extent, :desc => 'Sequence length'
35
- :x4, # u32 :held_count, :desc => 'For how many frames the frame is held'
36
- :x4, # r32 :frame_rate, :desc => 'Frame rate'
37
- :x4, # r32 :shutter_angle, :desc => 'Shutter angle'
38
- :x4, # char :frame_id, 32, :desc => 'Frame identification (keyframe)'
39
- :x4, # char :slate, 100, :desc => 'Slate information'
40
- :x4, # blanking :reserve, 56
27
+ :x2, # char :id, 2, :desc => 'Film mfg. ID code (2 digits from film edge code)'
28
+ :x2, # char :type, 2, :desc => 'Film type (2 digits from film edge code)'
29
+ :x2, # char :offset, 2, :desc => 'Offset in perfs (2 digits from film edge code)'
30
+ :x6, # char :prefix, 6, :desc => 'Prefix (6 digits from film edge code'
31
+ :x4, # char :count, 4, :desc => 'Count (4 digits from film edge code)'
32
+ :x32, # char :format, 32, :desc => 'Format (e.g. Academy)'
33
+ :x4, # u32 :frame_position, :desc => 'Frame position in sequence'
34
+ :x4, # u32 :sequence_extent, :desc => 'Sequence length'
35
+ :x4, # u32 :held_count, :desc => 'For how many frames the frame is held'
36
+ :x4, # r32 :frame_rate, :desc => 'Frame rate'
37
+ :x4, # r32 :shutter_angle, :desc => 'Shutter angle'
38
+ :x4, # char :frame_id, 32, :desc => 'Frame identification (keyframe)'
39
+ :x4, # char :slate, 100, :desc => 'Slate information'
40
+ :x4, # blanking :reserve, 56
41
41
  ].join
42
42
 
43
43
  IMAGE_ELEMENT = [
44
- :x4, # u32 :data_sign, :desc => 'Data sign (0=unsigned, 1=signed). Core is unsigned', :req => true
44
+ :x4, # u32 :data_sign, :desc => 'Data sign (0=unsigned, 1=signed). Core is unsigned', :req => true
45
45
  #
46
- :x4, # u32 :low_data, :desc => 'Reference low data code value'
47
- :x4, # r32 :low_quantity, :desc => 'Reference low quantity represented'
48
- :x4, # u32 :high_data, :desc => 'Reference high data code value (1023 for 10bit per channel)'
49
- :x4, # r32 :high_quantity, :desc => 'Reference high quantity represented'
46
+ :x4, # u32 :low_data, :desc => 'Reference low data code value'
47
+ :x4, # r32 :low_quantity, :desc => 'Reference low quantity represented'
48
+ :x4, # u32 :high_data, :desc => 'Reference high data code value (1023 for 10bit per channel)'
49
+ :x4, # r32 :high_quantity, :desc => 'Reference high quantity represented'
50
50
  #
51
- :x1, # u8 :descriptor, :desc => 'Descriptor for this image element (ie Video or Film), by enum', :req => true
51
+ :x1, # u8 :descriptor, :desc => 'Descriptor for this image element (ie Video or Film), by enum', :req => true
52
52
  # TODO - colirimetry information might be handy to recover,
53
53
  # as well as "bit size per element" (how many bits _per component_ we have) -
54
54
  # this will be different for, say, 8-bit DPX files versus 10-bit etc.
55
- :x1, # u8 :transfer, :desc => 'Transfer function (ie Linear), by enum', :req => true
56
- :x1, # u8 :colorimetric, :desc => 'Colorimetric (ie YcbCr), by enum', :req => true
57
- :x1, # u8 :bit_size, :desc => 'Bit size for element (ie 10)', :req => true
55
+ :x1, # u8 :transfer, :desc => 'Transfer function (ie Linear), by enum', :req => true
56
+ :x1, # u8 :colorimetric, :desc => 'Colorimetric (ie YcbCr), by enum', :req => true
57
+ :x1, # u8 :bit_size, :desc => 'Bit size for element (ie 10)', :req => true
58
58
  #
59
- :x2, # u16 :packing, :desc => 'Packing (0=Packed into 32-bit words, 1=Filled to 32-bit words))', :req => true
60
- :x2, # u16 :encoding, :desc => "Encoding (0=None, 1=RLE)", :req => true
61
- :x4, # u32 :data_offset, :desc => 'Offset to data for this image element', :req => true
62
- :x4, # u32 :end_of_line_padding, :desc => "End-of-line padding for this image element"
63
- :x4, # u32 :end_of_image_padding, :desc => "End-of-line padding for this image element"
64
- :x32,# char :description, 32
59
+ :x2, # u16 :packing, :desc => 'Packing (0=Packed into 32-bit words, 1=Filled to 32-bit words))', :req => true
60
+ :x2, # u16 :encoding, :desc => "Encoding (0=None, 1=RLE)", :req => true
61
+ :x4, # u32 :data_offset, :desc => 'Offset to data for this image element', :req => true
62
+ :x4, # u32 :end_of_line_padding, :desc => "End-of-line padding for this image element"
63
+ :x4, # u32 :end_of_image_padding, :desc => "End-of-line padding for this image element"
64
+ :x32, # char :description, 32
65
65
  ].join
66
66
 
67
67
  IMAGE_INFO = [
@@ -93,7 +93,7 @@ class FormatParser::DPXParser
93
93
  :x4,
94
94
  :x4,
95
95
 
96
- # TODO - the aspect ratio might be handy to recover since it
96
+ # TODO: - the aspect ratio might be handy to recover since it
97
97
  # will be used in DPX files in, say, anamorphic (non-square pixels)
98
98
  :x4, # array :aspect_ratio , :u32, 2, :desc => "Aspect (H:V)"
99
99
  :x4,
@@ -107,16 +107,16 @@ class FormatParser::DPXParser
107
107
  ORIENTATION_INFO,
108
108
  ].join
109
109
 
110
- DPX_INFO_LE = DPX_INFO.tr("n", "v").tr("N", "V")
110
+ DPX_INFO_LE = DPX_INFO.tr('n', 'v').tr('N', 'V')
111
111
 
112
112
  SIZEOF = ->(pattern) {
113
113
  bytes_per_element = {
114
- "v" => 2, # 16bit uints
115
- "n" => 2,
116
- "V" => 4, # 32bit uints
117
- "N" => 4,
118
- "C" => 1,
119
- "x" => 1,
114
+ 'v' => 2, # 16bit uints
115
+ 'n' => 2,
116
+ 'V' => 4, # 32bit uints
117
+ 'N' => 4,
118
+ 'C' => 1,
119
+ 'x' => 1,
120
120
  }
121
121
  pattern.scan(/[^\d]\d+/).map do |pattern|
122
122
  unpack_code = pattern[0]
@@ -133,15 +133,15 @@ class FormatParser::DPXParser
133
133
  io = FormatParser::IOConstraint.new(io)
134
134
  magic = io.read(4)
135
135
 
136
- return nil unless [BE_MAGIC, LE_MAGIC].include?(magic)
136
+ return unless [BE_MAGIC, LE_MAGIC].include?(magic)
137
137
 
138
138
  unpack_pattern = DPX_INFO
139
139
  unpack_pattern = DPX_INFO_LE if magic == LE_MAGIC
140
- num_elements, pixels_per_line, num_lines, *_ = safe_read(io, HEADER_SIZE).unpack(unpack_pattern)
140
+ _num_elements, pixels_per_line, num_lines, *_ = safe_read(io, HEADER_SIZE).unpack(unpack_pattern)
141
141
  FormatParser::Image.new(
142
142
  format: :dpx,
143
143
  width_px: pixels_per_line,
144
- height_px: num_lines,
144
+ height_px: num_lines
145
145
  )
146
146
  end
147
147