depix 1.0.1 → 1.0.2

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.
@@ -1,100 +1,97 @@
1
1
  = DPX header structure description
2
2
 
3
- DPX metadata gets returned as a hash containing other nested hashes. You can address hash keys by symbol, string
4
- and method name
5
-
6
- meta.file.magic # same as meta[:file][:magic]
3
+ DPX metadata gets returned as a Depix::DPX object with nested properties.
4
+
5
+ meta.file.magic # => "SDPX"
7
6
 
8
7
  == Metadata structure
9
8
 
10
- * <tt>file</tt> hash of
11
- * <tt>magic</tt> String
12
- * <tt>image_offset</tt> Integer
13
- * <tt>version</tt> String
14
- * <tt>file_size</tt> Integer
15
- * <tt>ditto_key</tt> Integer
16
- * <tt>generic_size</tt> Integer
17
- * <tt>industry_size</tt> Integer
18
- * <tt>user_size</tt> Integer
19
- * <tt>filename</tt> String
20
- * <tt>timestamp</tt> String
21
- * <tt>creator</tt> String
22
- * <tt>project</tt> String
23
- * <tt>copyright</tt> String
24
- * <tt>encrypt_key</tt> Integer
25
- * <tt>reserve</tt> String
26
- * <tt>image</tt> hash of
27
- * <tt>orientation</tt> Integer
28
- * <tt>number_elements</tt> Integer
29
- * <tt>pixels_per_line</tt> Integer
30
- * <tt>lines_per_element</tt> Integer
31
- * <tt>image_elements</tt> (array , 8 members):
32
- * <tt>data_sign</tt> Integer
33
- * <tt>low_data</tt> Integer
34
- * <tt>low_quantity</tt> Float
35
- * <tt>high_data</tt> Integer
36
- * <tt>high_quantity</tt> Float
37
- * <tt>descriptor</tt> Integer
38
- * <tt>transfer</tt> Integer
39
- * <tt>colorimetric</tt> Integer
40
- * <tt>bit_size</tt> Integer
41
- * <tt>packing</tt> Integer
42
- * <tt>encoding</tt> Integer
43
- * <tt>data_offset</tt> Integer
44
- * <tt>end_of_line_padding</tt> Integer
45
- * <tt>end_of_image_padding</tt> Integer
46
- * <tt>description</tt> String
47
- * <tt>reserve</tt> String
48
- * <tt>orientation</tt> hash of
49
- * <tt>x_offset</tt> Integer
50
- * <tt>y_offset</tt> Integer
51
- * <tt>x_center</tt> Float
52
- * <tt>y_center</tt> Float
53
- * <tt>x_size</tt> Integer
54
- * <tt>y_size</tt> Integer
55
- * <tt>filename</tt> String
56
- * <tt>timestamp</tt> String
57
- * <tt>device</tt> String
58
- * <tt>serial</tt> String
59
- * <tt>border</tt> (array , 4 members):
60
- * <tt></tt> Integer
61
- * <tt>aspect_ratio</tt> (array , 2 members):
62
- * <tt></tt> Integer
63
- * <tt>reserve</tt> String
64
- * <tt>film</tt> hash of
65
- * <tt>id</tt> String
66
- * <tt>type</tt> String
67
- * <tt>offset</tt> String
68
- * <tt>prefix</tt> String
69
- * <tt>count</tt> String
70
- * <tt>format</tt> String
71
- * <tt>frame_position</tt> Integer
72
- * <tt>sequence_extent</tt> Integer
73
- * <tt>held_count</tt> Integer
74
- * <tt>frame_rate</tt> Float
75
- * <tt>shutter_angle</tt> Float
76
- * <tt>frame_id</tt> String
77
- * <tt>slate</tt> String
78
- * <tt>reserve</tt> String
79
- * <tt>television</tt> hash of
80
- * <tt>time_code</tt> Integer
81
- * <tt>user_bits</tt> Integer
82
- * <tt>interlace</tt> Integer
83
- * <tt>field_number</tt> Integer
84
- * <tt>video_signal</tt> Integer
85
- * <tt>padding</tt> Integer
86
- * <tt>horizontal_sample_rate</tt> Float
87
- * <tt>vertical_sample_rate</tt> Float
88
- * <tt>frame_rate</tt> Float
89
- * <tt>time_offset</tt> Float
90
- * <tt>gamma</tt> Float
91
- * <tt>black_level</tt> Float
92
- * <tt>black_gain</tt> Float
93
- * <tt>break_point</tt> Float
94
- * <tt>white_level</tt> Float
95
- * <tt>integration_times</tt> Float
96
- * <tt>reserve</tt> String
97
- * <tt>user</tt> hash of
98
- * <tt>id</tt> String
99
- * <tt>user_data</tt> Integer
9
+ * <tt>file</tt> (Depix::FileInfo) File information:
10
+ * <tt>magic</tt> (String) Endianness (SDPX is big endian) - required
11
+ * <tt>image_offset</tt> Offset to image data in bytes - required
12
+ * <tt>version</tt> (String) Version of header format - required
13
+ * <tt>file_size</tt> Total image size in bytes - required
14
+ * <tt>ditto_key</tt> Whether the basic headers stay the same through the sequence (1 means they do)
15
+ * <tt>generic_size</tt> Generic header length
16
+ * <tt>industry_size</tt> Industry header length
17
+ * <tt>user_size</tt> User header length
18
+ * <tt>filename</tt> (String) Original filename
19
+ * <tt>timestamp</tt> (String) Creation 15
20
+ * <tt>creator</tt> (String) Creator application
21
+ * <tt>roject</tt> (String) Project name
22
+ * <tt>copyright</tt> (String) Copyright
23
+ * <tt>encrypt_key</tt> Encryption key
24
+ * <tt>reserve</tt> (String)
25
+ * <tt>image</tt> (Depix::ImageInfo) Image information:
26
+ * <tt>orientation</tt> (Integer) Orientation descriptor - required
27
+ * <tt>number_elements</tt> (Integer) How many elements to scan - required
28
+ * <tt>pixels_per_line</tt> Pixels per horizontal line - required
29
+ * <tt>lines_per_element</tt> Line count - required
30
+ * <tt>image_elements</tt> (Array of 8 Depix::ImageElement fields) Image elements:
31
+ * <tt>data_sign</tt> Data sign (0=unsigned, 1=signed). Core is unsigned - required
32
+ * <tt>low_data</tt> Reference low data code value
33
+ * <tt>low_quantity</tt> (Float) Reference low quantity represented
34
+ * <tt>high_data</tt> Reference high data code value (1023 for 10bit per channel)
35
+ * <tt>high_quantity</tt> (Float) Reference high quantity represented
36
+ * <tt>descriptor</tt> (Integer) Descriptor for this image element (ie Video or Film), by enum - required
37
+ * <tt>transfer</tt> (Integer) Transfer function (ie Linear), by enum - required
38
+ * <tt>colorimetric</tt> (Integer) Colorimetric (ie YcbCr), by enum - required
39
+ * <tt>bit_size</tt> (Integer) Bit size for element (ie 10) - required
40
+ * <tt>packing</tt> (Integer) Packing (0=Packed into 32-bit words, 1=Filled to 32-bit words)) - required
41
+ * <tt>encoding</tt> (Integer) Encoding (0=None, 1=RLE) - required
42
+ * <tt>data_offset</tt> Offset to data for this image element - required
43
+ * <tt>end_of_line_padding</tt> End-of-line padding for this image element
44
+ * <tt>end_of_image_padding</tt> End-of-line padding for this image element
45
+ * <tt>description</tt> (String)
46
+ * <tt>reserve</tt> (String)
47
+ * <tt>orientation</tt> (Depix::OrientationInfo) Orientation:
48
+ * <tt>x_offset</tt>
49
+ * <tt>y_offset</tt>
50
+ * <tt>x_center</tt> (Float)
51
+ * <tt>y_center</tt> (Float)
52
+ * <tt>x_size</tt> Original X size
53
+ * <tt>y_size</tt> Original Y size
54
+ * <tt>filename</tt> (String) Source image filename
55
+ * <tt>timestamp</tt> (String) Source image/tape timestamp
56
+ * <tt>device</tt> (String) Input device or tape
57
+ * <tt>serial</tt> (String) Input device serial number
58
+ * <tt>border</tt> (Array of 4 Integer fields) Border validity: XL, XR, YT, YB:
59
+ * <tt>aspect_ratio</tt> (Array of 2 fields) Aspect (H:V):
60
+ * <tt>reserve</tt> (String)
61
+ * <tt>film</tt> (Depix::FilmInfo) Film industry info:
62
+ * <tt>id</tt> (String) Film mfg. ID code (2 digits from film edge code)
63
+ * <tt>type</tt> (String) Film type (2 digits from film edge code)
64
+ * <tt>offset</tt> (String) Offset in perfs (2 digits from film edge code)
65
+ * <tt>prefix</tt> (String) Prefix (6 digits from film edge code
66
+ * <tt>count</tt> (String) Count (4 digits from film edge code)
67
+ * <tt>format</tt> (String) Format (e.g. Academy)
68
+ * <tt>frame_position</tt> Frame position in sequence
69
+ * <tt>sequence_extent</tt> Sequence length
70
+ * <tt>held_count</tt> For how many frames the frame is held
71
+ * <tt>frame_rate</tt> (Float) Frame rate
72
+ * <tt>shutter_angle</tt> (Float) Shutter angle
73
+ * <tt>frame_id</tt> (String) Frame identification (keyframe)
74
+ * <tt>slate</tt> (String) Slate information
75
+ * <tt>reserve</tt> (String)
76
+ * <tt>television</tt> (Depix::TelevisionInfo) TV industry info:
77
+ * <tt>time_code</tt> Timecode, formatted as HH:MM:SS:FF in the 4 higher bits of each 8bit group
78
+ * <tt>user_bits</tt> Timecode UBITs
79
+ * <tt>interlace</tt> (Integer) Interlace (0 = noninterlaced; 1 = 2:1 interlace
80
+ * <tt>field_number</tt> (Integer) Field number
81
+ * <tt>video_signal</tt> (Integer) Video signal (by enum)
82
+ * <tt>padding</tt> (Integer) Zero (for byte alignment)
83
+ * <tt>horizontal_sample_rate</tt> (Float) Horizontal sampling Hz
84
+ * <tt>vertical_sample_rate</tt> (Float) Vertical sampling Hz
85
+ * <tt>frame_rate</tt> (Float) Frame rate
86
+ * <tt>time_offset</tt> (Float) From sync pulse to first pixel
87
+ * <tt>gamma</tt> (Float) Gamma
88
+ * <tt>black_level</tt> (Float) Black pedestal code value
89
+ * <tt>black_gain</tt> (Float) Black gain code value
90
+ * <tt>break_point</tt> (Float) Break point (?)
91
+ * <tt>white_level</tt> (Float) White level
92
+ * <tt>integration_times</tt> (Float) Integration times (S)
93
+ * <tt>reserve</tt> (Float)
94
+ * <tt>user</tt> (Depix::UserInfo) User info:
95
+ * <tt>id</tt> (String) Name of the user data tag
96
+ * <tt>user_data_ptr</tt>
100
97
 
data/History.txt CHANGED
@@ -1,4 +1,9 @@
1
+ === 1.0.2 / 2008-12-19
2
+
3
+ * refactor struct parsing completely
4
+
1
5
  === 1.0.1 / 2008-12-18
6
+
2
7
  * small doc and usability improvements
3
8
 
4
9
  === 1.0.0 / 2008-12-18
data/Manifest.txt CHANGED
@@ -7,6 +7,9 @@ bin/depix-describe
7
7
  lib/depix.rb
8
8
  lib/depix/struct_explainer.rb
9
9
  lib/depix/structs.rb
10
+ lib/depix/compact_structs.rb
11
+ lib/depix/enums.rb
12
+ lib/depix/dict.rb
10
13
  test/test_depix.rb
11
14
  test/samples/E012_P001_L000002_lin.0001.dpx
12
15
  test/samples/E012_P001_L000002_lin.0002.dpx
data/README.txt CHANGED
@@ -9,10 +9,11 @@ Read DPX file metadata
9
9
  == SYNOPSIS:
10
10
 
11
11
  meta = Depix::Reader.from_file(dpx_file_path)
12
- puts meta.television.time_code #=> 10:00:00:02
12
+ puts meta.time_code #=> 10:00:00:02
13
13
 
14
- The data returned is described in the DPX_HEADER_STRUCTURE[link:files/DPX_HEADER_STRUCTURE_txt.html]. The structs
15
- used for actual parsing are in the Depix::Structs module (but in a much less readable form, obviously)
14
+ The data returned is described in the DPX_HEADER_STRUCTURE[link:files/DPX_HEADER_STRUCTURE_txt.html]. It's
15
+ a vanilla Ruby object with no extra methods except for the readers that have the same name as the specified
16
+ fields
16
17
 
17
18
  The gem also contains an executable called depix-desribe which can be used from the command line
18
19
 
@@ -20,9 +21,6 @@ The gem also contains an executable called depix-desribe which can be used from
20
21
 
21
22
  == NOTES:
22
23
 
23
- The reader tries to be efficient - fast Ruby unpacking is used, some shortcuts are taken. Also don't worry - we do not need to read
24
- the whole DPX file (which usually is around 8mb per frame) to know the details.
25
-
26
24
  In the future there will be a possibility to modify and commit the headers, but it's not a priority at this time.
27
25
 
28
26
  Autodesk IFFS systems write the reel name for the file to the orientation.device field
data/Rakefile CHANGED
@@ -1,15 +1,19 @@
1
1
  require 'rubygems'
2
2
  require 'hoe'
3
- require './lib/depix.rb'
3
+ require './lib/depix'
4
4
 
5
- Hoe.new('depix', Depix::VERSION) do |p|
5
+ Class.new(Hoe) do
6
+ def extra_deps
7
+ super.reject {|e| e[0] == 'hoe' }
8
+ end
9
+ end.new('depix', Depix::VERSION) do |p|
6
10
  p.developer('Julik Tarkhanov', 'me@julik.nl')
7
11
  p.rubyforge_name = 'wiretap'
8
- p.extra_deps.reject! {|e| e[0] == 'hoe' }
12
+ p.extra_deps << 'timecode'
9
13
  p.remote_rdoc_dir = 'depix'
10
14
  end
11
15
 
12
16
  task :describe_structs do
13
17
  require File.dirname(__FILE__) + '/lib/depix/struct_explainer'
14
- File.open('DPX_HEADER_STRUCTURE.txt', 'w') {|f| f << RdocExplainer.new.get_rdoc_for(Depix::Structs::DPX_INFO) }
18
+ File.open('DPX_HEADER_STRUCTURE.txt', 'w') {|f| f << RdocExplainer.new.get_rdoc_for(Depix::DPX) }
15
19
  end
data/bin/depix-describe CHANGED
@@ -1,10 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/depix'
4
- if ARGV.flatten.map do | file |
5
- data = Depix::Reader.describe_file(file)
6
- puts "Describing DPX #{file}. Empty elements are omitted."
7
- puts data
8
- end.empty?
9
- STDERR.puts "Usage: depix-describe somefile.dpx anotherfile.dpx"
4
+ require 'optparse'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: depix-describe somefile.dpx anotherfile.dpx [options]"
9
+
10
+ opts.on("-c", "--compact", "Compact output (only fields that change per frame)") do |v|
11
+ options[:compact] = true
12
+ end
13
+ end.parse!
14
+
15
+ ARGV.each do | file |
16
+ puts "Describing DPX #{file}. Empty elements are omitted.\n\n"
17
+ puts "===================================================\n\n"
18
+ puts Depix.describe_file(file, options[:compact])
10
19
  end
data/lib/depix.rb CHANGED
@@ -2,29 +2,13 @@ require 'stringio'
2
2
  require 'rubygems'
3
3
  require 'timecode'
4
4
 
5
+ require File.dirname(__FILE__) + '/depix/dict'
5
6
  require File.dirname(__FILE__) + '/depix/structs'
7
+ require File.dirname(__FILE__) + '/depix/compact_structs'
8
+ require File.dirname(__FILE__) + '/depix/enums'
6
9
 
7
10
  module Depix
8
- VERSION = '1.0.1'
9
-
10
- BLANK_2 = 0xFFFF #:nodoc:
11
- BLANK_4 = 0xFFFFFFFF #:nodoc:
12
- BLANK_F = 0xFFFFFFFF #:nodoc:
13
- BLANK_CHAR = 0xFF #:nodoc:
14
-
15
- # Methodic hash - stolen from Camping
16
- class H < Hash
17
- # Gets or sets keys in the hash.
18
- #
19
- # @cookies.my_favorite = :macadamian
20
- # @cookies.my_favorite
21
- # => :macadamian
22
- #
23
- def method_missing(m,*a)
24
- m.to_s=~/=$/ ? (self[$`] = a[0]) : (a == [] ? (self.key?(m.to_s) ? self[m.to_s] : super ) : super)
25
- end
26
- undef id, type
27
- end
11
+ VERSION = '1.0.2'
28
12
 
29
13
  # Offers convenience access to a few common attributes bypassing the piecemeal structs
30
14
  module Synthetics
@@ -37,111 +21,96 @@ module Depix
37
21
  end
38
22
 
39
23
  def time_code
40
- Timecode.from_uint(television.time_code, film.fps)
24
+ Timecode.from_uint(television.time_code) #, film.frame_rate)
41
25
  end
42
26
 
43
27
  # Get the name of the transfer function (Linear, Logarithmic, ...)
44
28
  def colorimetric
45
- Structs::COLORIMETRIC.invert[image.image_elements[0].colorimetric]
29
+ COLORIMETRIC.invert[image.image_elements[0].colorimetric]
46
30
  end
47
31
 
48
32
  # Get the name of the compnent type (RGB, YCbCr, ...)
49
33
  def component_type
50
- Structs::COMPONENT_TYPE.invert[image.image_elements[0].descriptor]
34
+ COMPONENT_TYPE.invert[image.image_elements[0].descriptor]
51
35
  end
52
-
36
+
37
+ # Is this DPX file little-endian? This would be an exception, but still useful
38
+ def le?
39
+ file.magic == 'XPDS'
40
+ end
41
+ end
42
+
43
+ class DPX < Dict
44
+ include Synthetics
45
+ end
46
+
47
+ # Return a DPX object describing a file at path.
48
+ # The second argument specifies whether you need a compact or a full description
49
+ def self.from_file(path, compact = false)
50
+ Reader.new.from_file(path, compact)
51
+ end
52
+
53
+ # Return a DPX object describing headers embedded at the start of the string.
54
+ # The second argument specifies whether you need a compact or a full description
55
+ def self.from_string(string, compact = false)
56
+ Reader.new.parse(string, compact)
57
+ end
58
+
59
+ # Retrurn a formatted description of the DPX file at path. Empty values are omitted.
60
+ def self.describe_file(path, compact = false)
61
+ Reader.new.describe_file(path, compact)
53
62
  end
54
63
 
55
- # Reads the metadata
56
64
  class Reader
57
65
 
58
- class << self
59
- # Read the header from file (no worries, only the needed number of bytes will be read into memory). Returns a H with the metadata.
60
- def from_file(path)
61
- new.from_file(path)
62
- end
63
-
64
- # Read the metadata from an in-memory string. Returns a H with the metadata.
65
- def from_string(str)
66
- new.from_string(str)
67
- end
68
-
69
- # Returns a printable report on all the headers present in the string
70
- def describe_string(str)
71
- reader = new
72
- result = reader.deep_parse(str, Structs::DPX_INFO)
73
- reader.inform(result)
74
- end
75
-
76
- # Returns a printable report on all the headers present in the file at the path passed
77
- def describe_file(path)
78
- header = File.open(path, 'r') { |f| f.read(Structs::TEMPLATE_LENGTH) }
79
- describe_string(header)
80
- end
66
+ # Returns a printable report on all the headers present in the file at the path passed
67
+ def describe_file(path, compact = false)
68
+ header = File.open(path, 'r') { |f| f.read(DPX.length) }
69
+ describe_struct(parse(header, false))
81
70
  end
82
71
 
83
- #:stopdoc:
84
- def from_file(path)
85
- header = File.open(path, 'r') { |f| f.read(Structs::TEMPLATE_LENGTH) }
86
- from_string(header)
72
+ def from_file(path, compact)
73
+ header = File.open(path, 'r') { |f| f.read(DPX.length) }
74
+ parse(header, compact)
87
75
  end
88
76
 
89
- def from_string(str) #:nodoc:
90
- wrap(deep_parse(str, Structs::DPX_INFO))
91
- end
92
-
93
- def deep_parse(data, structure)
77
+ # The hear of Depix
78
+ def parse(data, compact)
94
79
  magic = data[0..3]
95
- template = (magic == "SDPX") ? Structs::TEMPLATE_BE : Structs::TEMPLATE_LE
80
+ struct = compact ? CompactDPX : DPX
96
81
 
97
- result = data.unpack(template).map do |e|
98
- case e
99
- when String
100
- clean = unpad(e)
101
- clean.empty? ? nil : clean
102
- when Integer
103
- (e == BLANK_2 || e == BLANK_4) ? nil : e
104
- when Float
105
- e.nan? ? nil : e
106
- end
107
- end
108
- result
82
+ template = (magic == "SDPX") ? struct.pattern : make_le(struct.pattern)
83
+ struct.consume!(data.unpack(template))
109
84
  end
110
85
 
111
- def inform(result)
112
- Structs::TEMPLATE_KEYS.zip(result).map{|k, v| "#{k}:#{v}" unless v.nil? }.compact.join("\n")
113
- end
114
-
115
- def wrap(result)
116
- eich = self.class.nestify(Structs::TEMPLATE_KEYS, result)
117
- class << eich; include Synthetics; end
118
- eich
119
- end
120
-
121
- # FIXME - currently no array handling
122
- def self.nestify(keys, values)
123
- auto_hash = H.new do |h,k|
124
- h[k] = H.new(&h.default_proc)
125
- end
126
-
127
- keys.each_with_index do |path, idx |
128
- value = values[idx]
129
-
130
- sub, elems = auto_hash, path.split('.')
131
- while elems.any?
132
- dir = elems.shift
133
- dir = dir.to_i if dir =~ /^(\d+)$/
134
- elems.any? ? (sub = sub[dir]) : (sub[dir] = value)
86
+ # Describe a filled DPX structure
87
+ def describe_struct(result, pad_offset = 0)
88
+ result.class.fields.inject([]) do | info, field |
89
+ value = result.send(field.name)
90
+ parts = []
91
+ if value
92
+ parts << field.desc if field.desc
93
+ parts << if field.is_a?(InnerField)
94
+ describe_struct(value, pad_offset + 1)
95
+ elsif field.is_a?(ArrayField)
96
+ # Exception for image elements
97
+ value = result.image_elements[0...result.number_elements] if field.name == :image_elements
98
+ value.map { | v | v.is_a?(Dict) ? describe_struct(v, pad_offset + 2) : v }
99
+ else
100
+ value
101
+ end
135
102
  end
136
- end
137
-
138
- auto_hash
103
+ if parts.any?
104
+ info << parts.join(' ')
105
+ end
106
+ info
107
+ end.map{|e| (' ' * pad_offset) + e }.join("\n")
139
108
  end
140
109
 
141
- def unpad(string) # :nodoc:
142
- string.gsub("\000", '').gsub(0xFF.chr, '')
110
+ # Convert an unpack pattern to LE
111
+ def make_le(pattern)
112
+ pattern.gsub(/n/, "v").gsub(/N/, "V").gsub(/g/, "f")
143
113
  end
144
114
 
145
- #:startdoc:
146
115
  end
147
116
  end