depix 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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