depix 1.0.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.
@@ -0,0 +1,100 @@
1
+ = DPX header structure description
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]
7
+
8
+ == Metadata structure
9
+
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> String
38
+ * <tt>transfer</tt> String
39
+ * <tt>colorimetric</tt> String
40
+ * <tt>bit_size</tt> String
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> String
83
+ * <tt>field_number</tt> String
84
+ * <tt>video_signal</tt> String
85
+ * <tt>padding</tt> String
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
100
+
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2008-12-18
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,14 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ DPX_HEADER_STRUCTURE.txt
5
+ Rakefile
6
+ bin/depix-describe
7
+ lib/depix.rb
8
+ lib/depix/struct_explainer.rb
9
+ lib/depix/structs.rb
10
+ test/test_depix.rb
11
+ test/samples/E012_P001_L000002_lin.0001.dpx
12
+ test/samples/E012_P001_L000002_lin.0002.dpx
13
+ test/samples/E012_P001_L000002_log.0001.dpx
14
+ test/samples/E012_P001_L000002_log.0002.dpx
@@ -0,0 +1,57 @@
1
+ = depix
2
+
3
+ * http://rubyforge.org/julik/depix
4
+
5
+ == DESCRIPTION:
6
+
7
+ Read DPX file metadata
8
+
9
+ == SYNOPSIS:
10
+
11
+ meta = Depix::Reader.new.from_file(dpx_file_path)
12
+ puts meta.television.time_code #=> 10:00:00:02
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)
16
+
17
+ The gem also contains an executable called depix-desribe which can be used from the command line
18
+
19
+ $book depix-describe 001_PTAPE_001.001.dpx
20
+
21
+ == NOTES:
22
+
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
+ == REQUIREMENTS:
27
+
28
+ * timecode gem (sudo gem install timecode)
29
+
30
+ == INSTALL:
31
+
32
+ * sudo gem install depix
33
+
34
+ == LICENSE:
35
+
36
+ (The MIT License)
37
+
38
+ Copyright (c) 2008 Julik Tarkhanov
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining
41
+ a copy of this software and associated documentation files (the
42
+ 'Software'), to deal in the Software without restriction, including
43
+ without limitation the rights to use, copy, modify, merge, publish,
44
+ distribute, sublicense, and/or sell copies of the Software, and to
45
+ permit persons to whom the Software is furnished to do so, subject to
46
+ the following conditions:
47
+
48
+ The above copyright notice and this permission notice shall be
49
+ included in all copies or substantial portions of the Software.
50
+
51
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
52
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
53
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
54
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
55
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
56
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
57
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require './lib/depix.rb'
4
+
5
+ Hoe.new('depix', Depix::VERSION) do |p|
6
+ p.developer('Julik Tarkhanov', 'me@julik.nl')
7
+ p.rubyforge_name = 'wiretap'
8
+ p.extra_deps.reject! {|e| e[0] == 'hoe' }
9
+ end
10
+
11
+ task :describe_structs do
12
+ require File.dirname(__FILE__) + '/lib/depix/struct_explainer'
13
+ File.open('DPX_HEADER_STRUCTURE.txt', 'w') {|f| f << RdocExplainer.new.get_rdoc_for(Depix::Structs::DPX_INFO) }
14
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/depix'
4
+ ARGV.flatten.each do | file |
5
+ data = Depix::Reader.describe_file(file)
6
+ puts "Describing DPX #{file}. Empty elements are omitted."
7
+ puts data
8
+ end
@@ -0,0 +1,123 @@
1
+ require 'stringio'
2
+ require 'rubygems'
3
+ require 'timecode'
4
+
5
+ require File.dirname(__FILE__) + '/depix/structs'
6
+
7
+ module Depix
8
+ VERSION = '1.0.0'
9
+ BLANK_4, BLANK_2 = 0xFFFFFFFF, 0xFFFF
10
+
11
+ # Methodic hash - stolen from Camping
12
+ class H < Hash
13
+ # Gets or sets keys in the hash.
14
+ #
15
+ # @cookies.my_favorite = :macadamian
16
+ # @cookies.my_favorite
17
+ # => :macadamian
18
+ #
19
+ def method_missing(m,*a)
20
+ m.to_s=~/=$/?self[$`]=a[0]:a==[]? (self.key?(m.to_sym) ? self[m.to_sym] : super ) : super
21
+ end
22
+ undef id, type
23
+ end
24
+
25
+ # Reads the metadata
26
+ class Reader
27
+
28
+ class << self
29
+ # Read the header from file (no worries, only the needed number of bytes will be read into memory). Returns a H with the metadata.
30
+ def from_file(path)
31
+ new.from_file(path)
32
+ end
33
+
34
+ # Read the metadata from an in-memory string. Returns a H with the metadata.
35
+ def from_string(str)
36
+ new.from_string(str)
37
+ end
38
+
39
+ def describe_string(str)
40
+ reader = new
41
+ result = reader.deep_parse(str, Structs::DPX_INFO)
42
+ reader.inform(result)
43
+ end
44
+
45
+ def describe_file(path)
46
+ header = File.open(path, 'r') { |f| f.read(Structs::TEMPLATE_LENGTH) }
47
+ describe_string(header)
48
+ end
49
+ end
50
+
51
+ #:stopdoc:
52
+ def from_file(path)
53
+ header = File.open(path, 'r') { |f| f.read(Structs::TEMPLATE_LENGTH) }
54
+ from_string(header)
55
+ end
56
+
57
+ def from_string(str) #:nodoc:
58
+ wrap(deep_parse(str, Structs::DPX_INFO))
59
+ end
60
+
61
+ def deep_parse(data, structure)
62
+ magic = data[0..3]
63
+ template = (magic == "SDPX") ? Structs::TEMPLATE_BE : Structs::TEMPLATE_LE
64
+
65
+ result = data.unpack(template).map do |e|
66
+ case e
67
+ when String
68
+ clean = unpad(e)
69
+ clean.empty? ? nil : clean
70
+ when Integer
71
+ (e == BLANK_2 || e == BLANK_4) ? nil : e
72
+ when Float
73
+ e.nan? ? nil : e
74
+ end
75
+ end
76
+ result
77
+ end
78
+
79
+ def inform(result)
80
+ Structs::TEMPLATE_KEYS.zip(result).map{|k, v| "#{k}:#{v}" unless v.nil? }.compact.join("\n")
81
+ end
82
+
83
+ def wrap(result)
84
+ self.class.nestify(Structs::TEMPLATE_KEYS, result)
85
+ end
86
+
87
+ def self.nestify(keys, values)
88
+ auto_hash = H.new do |h,k|
89
+ h[k] = H.new(&h.default_proc)
90
+ end
91
+
92
+ keys.each_with_index do |path, idx |
93
+ value = values[idx]
94
+
95
+ sub, elems = auto_hash, path.split('.')
96
+ while elems.any?
97
+ dir = elems.shift
98
+ dir = dir.to_i if dir =~ /^(\d+)$/
99
+ elems.any? ? (sub = sub[dir]) : (sub[dir] = value)
100
+ end
101
+ end
102
+
103
+ auto_hash
104
+ end
105
+
106
+ def unpad(string) # :nodoc:
107
+ string.gsub(0xFF.chr, '').gsub(0xFF.chr, '')
108
+ end
109
+
110
+ TIME_FIELDS = 7 # :nodoc:
111
+
112
+ def uint_to_tc(timestamp) # :nodoc:
113
+ shift = 4 * TIME_FIELDS;
114
+ tc_elements = (0..TIME_FIELDS).map do
115
+ part = ((timestamp >> shift) & 0x0F)
116
+ shift -= 4
117
+ part
118
+ end.join.scan(/(\d{2})/).flatten.map{|e| e.to_i}
119
+
120
+ Timecode.at(*tc_elements)
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,62 @@
1
+ # Generates an RDoc description of the DPX structs from the structs.rb file
2
+ class Formatter #:nodoc:
3
+ attr_accessor :io, :attr_template, :struct_template
4
+
5
+ def initialize
6
+ @io = STDOUT
7
+ @attr_template = "%s%s (%s)"
8
+ @struct_template = "%s%s (which is a hash with the following elements):"
9
+ @array_template = "%s%s (array , %d members):"
10
+ @padding = ' '
11
+ end
12
+
13
+ def explain_struct(struct, padding = '') #:nodoc:
14
+ struct.each do | e |
15
+ key, cast, len = e
16
+ if cast.is_a?(Depix::Structs::Struct)
17
+ @io.puts( @struct_template % [padding, key, len])
18
+ explain_struct(cast, padding + @padding)
19
+ elsif cast.is_a?(Array) # Repeats
20
+ @io.puts( @array_template % [padding, key, cast.size])
21
+ inner_struct = cast[0]
22
+ ikey, icast, ilen = inner_struct
23
+ if icast.is_a?(Depix::Structs::Struct)
24
+ explain_struct(icast, padding + @padding)
25
+ else
26
+ @io.puts( @attr_template % [padding, '', icast, ilen])
27
+ end
28
+ else
29
+ @io.puts( @attr_template % [padding, key, cast, len])
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ class RdocExplainer < Formatter #:nodoc:
36
+ TPL = <<eof
37
+ = DPX header structure description
38
+
39
+ DPX metadata gets returned as a hash containing other nested hashes. You can address hash keys by symbol, string
40
+ and method name
41
+
42
+ meta.file.magic # same as meta[:file][:magic]
43
+
44
+ == Metadata structure
45
+
46
+ %s
47
+ eof
48
+
49
+ def initialize
50
+ super
51
+ @attr_template = "%s* <tt>%s</tt> %s"
52
+ @struct_template = "%s* <tt>%s</tt> hash of"
53
+ @array_template = "%s* <tt>%s</tt> (array , %d members):"
54
+
55
+ end
56
+
57
+ def get_rdoc_for(struct)
58
+ @io = StringIO.new
59
+ explain_struct(Depix::Structs::DPX_INFO)
60
+ TPL % @io.string
61
+ end
62
+ end
@@ -0,0 +1,259 @@
1
+ module Depix
2
+ # Basically a copy of http://trac.imagemagick.org/browser/ImageMagick/trunk/coders/dpx.c
3
+ #
4
+ # Which is a reformulation of http://www.cineon.com/ff_draft.php
5
+ #
6
+ # Which is a preamble to some SMPTE crap that you have to buy for 14 bucks. Or download from http://www.cinesite.com/static/scanning/techdocs/dpx_spec.pdf
7
+ #
8
+ # It's very fragile - in the world of C, everything is fixed length. If Tolstoy wanted to write
9
+ # "War and Peace" in C he would need to know the number of letters ahead. It has good and bad
10
+ # qualities - the good ones being computers go faster like that. The rest are bad parts.
11
+ module Structs
12
+
13
+ COLORIMETRIC = {
14
+ :UserDefined => 0,
15
+ :PrintingDensity => 1,
16
+ :Linear => 2,
17
+ :Logarithmic => 3,
18
+ :UnspecifiedVideo => 4,
19
+ :SMTPE_274M => 5,
20
+ :ITU_R709 => 6,
21
+ :ITU_R601_625L => 7,
22
+ :ITU_R601_525L => 8,
23
+ :NTSCCompositeVideo => 9,
24
+ :PALCompositeVideo => 10,
25
+ :ZDepthLinear => 11,
26
+ :DepthHomogeneous => 12
27
+ }
28
+
29
+ COMPONENT_TYPE = {
30
+ :Undefined => 0,
31
+ :Red => 1,
32
+ :Green => 2,
33
+ :Blue => 3,
34
+ :Alpha => 4,
35
+ :Luma => 6,
36
+ :ColorDifferenceCbCr => 7,
37
+ :Depth => 8,
38
+ :CompositeVideo => 9,
39
+ :RGB => 50,
40
+ :RGBA => 51,
41
+ :ABGR => 52,
42
+ :CbYCrY422 => 100,
43
+ :CbYACrYA4224 => 101,
44
+ :CbYCr444 => 102,
45
+ :CbYCrA4444 => 103,
46
+ :UserDef2Element => 150,
47
+ :UserDef3Element => 151,
48
+ :UserDef4Element => 152,
49
+ :UserDef5Element => 153,
50
+ :UserDef6Element => 154,
51
+ :UserDef7Element => 155,
52
+ :UserDef8Element => 156,
53
+ }
54
+
55
+ #:stopdoc:
56
+
57
+ # To avoid fucking up with sizes afterwards
58
+ UINT, FLOAT, USHORT, UCHAR = 4, 4, 2, 1
59
+
60
+ def self.struct_size(struct_const) #:nodoc:
61
+ struct_const.inject(0){| s, e | s + e[2]}
62
+ end
63
+
64
+ # Used to distinguish structs from repeated values
65
+ class Struct < Array; end
66
+
67
+
68
+ FILE_INFO = Struct[
69
+ [:magic, String, 4],
70
+ [:image_offset, Integer, UINT],
71
+
72
+ [:version, String, 8],
73
+
74
+ [:file_size, Integer, UINT],
75
+ [:ditto_key, Integer, UINT],
76
+ [:generic_size, Integer, UINT],
77
+ [:industry_size, Integer, UINT],
78
+ [:user_size, Integer, UINT],
79
+
80
+ [:filename, String, 100],
81
+ [:timestamp, String, 24],
82
+ [:creator, String, 100],
83
+ [:project, String, 200],
84
+ [:copyright, String, 200],
85
+
86
+ [:encrypt_key, Integer, UINT],
87
+ [:reserve, String, 104],
88
+ ]
89
+
90
+ FILM_INFO = Struct[
91
+ [:id, String, 2],
92
+ [:type, String, 2],
93
+ [:offset, String, 2],
94
+ [:prefix, String, 6],
95
+ [:count, String, 4],
96
+ [:format, String, 32],
97
+
98
+ [:frame_position, Integer, UINT],
99
+ [:sequence_extent, Integer, UINT],
100
+ [:held_count, Integer, UINT],
101
+
102
+ [:frame_rate, Float, FLOAT],
103
+ [:shutter_angle, Float, FLOAT],
104
+
105
+ [:frame_id, String, 32],
106
+ [:slate, String, 100],
107
+ [:reserve, String, 56],
108
+ ]
109
+
110
+
111
+ IMAGE_ELEMENT = Struct[
112
+ [:data_sign, Integer, UINT],
113
+ [:low_data, Integer, UINT],
114
+ [:low_quantity, Float, FLOAT],
115
+ [:high_data, Integer, UINT],
116
+ [:high_quantity, Float, FLOAT],
117
+
118
+ # TODO: Autoreplace with enum values. Note: with these we will likely be addressing the enums
119
+ [:descriptor, String, UCHAR],
120
+ [:transfer, String, UCHAR],
121
+ [:colorimetric, String, UCHAR],
122
+ [:bit_size, String, UCHAR],
123
+
124
+ [:packing, Integer, USHORT],
125
+ [:encoding, Integer, USHORT],
126
+ [:data_offset, Integer, UINT],
127
+ [:end_of_line_padding, Integer, UINT],
128
+ [:end_of_image_padding, Integer, UINT],
129
+ [:description, String, 32],
130
+ ]
131
+
132
+ IMAGE_ELEMENTS = (0..7).map{|e| [e, IMAGE_ELEMENT, struct_size(IMAGE_ELEMENT)] }
133
+
134
+ IMAGE_INFO = Struct[
135
+ [:orientation, Integer, USHORT],
136
+ [:number_elements, Integer, USHORT],
137
+
138
+ [:pixels_per_line, Integer, UINT],
139
+ [:lines_per_element, Integer, UINT],
140
+
141
+ [:image_elements, IMAGE_ELEMENTS, struct_size(IMAGE_ELEMENTS) ],
142
+
143
+ [:reserve, String, 52],
144
+ ]
145
+
146
+ BORDER = (0..3).map{|s| [s, Integer, USHORT] }
147
+
148
+ ASPECT_RATIO = [
149
+ [0, Integer, UINT],
150
+ [1, Integer, UINT],
151
+ ]
152
+
153
+ ORIENTATION_INFO = Struct[
154
+
155
+ [:x_offset, Integer, UINT],
156
+ [:y_offset, Integer, UINT],
157
+
158
+ [:x_center, Float, FLOAT],
159
+ [:y_center, Float, FLOAT],
160
+
161
+ [:x_size, Integer, UINT],
162
+ [:y_size, Integer, UINT],
163
+
164
+ [:filename, String, 100],
165
+ [:timestamp, String, 24],
166
+ [:device, String, 32],
167
+ [:serial, String, 32],
168
+
169
+ [:border, BORDER, struct_size(BORDER)],
170
+ [:aspect_ratio, ASPECT_RATIO, struct_size(ASPECT_RATIO)],
171
+
172
+ [:reserve, String, 28],
173
+ ]
174
+
175
+ TELEVISION_INFO = Struct[
176
+ [:time_code, Integer, UINT],
177
+ [:user_bits, Integer, UINT],
178
+
179
+ [:interlace, String, UCHAR],
180
+ [:field_number, String, UCHAR],
181
+ [:video_signal, String, UCHAR],
182
+ [:padding, String, UCHAR],
183
+
184
+ [:horizontal_sample_rate, Float, FLOAT],
185
+ [:vertical_sample_rate, Float, FLOAT],
186
+ [:frame_rate, Float, FLOAT],
187
+ [:time_offset, Float, FLOAT],
188
+ [:gamma, Float, FLOAT],
189
+ [:black_level, Float, FLOAT],
190
+ [:black_gain, Float, FLOAT],
191
+ [:break_point, Float, FLOAT],
192
+ [:white_level, Float, FLOAT],
193
+ [:integration_times, Float, FLOAT],
194
+ [:reserve, String, 76],
195
+ ]
196
+
197
+ USER_INFO = Struct[
198
+ [:id, String, 32],
199
+ [:user_data, Integer, UINT],
200
+ ]
201
+
202
+ DPX_INFO = Struct[
203
+ [:file, FILE_INFO, struct_size(FILE_INFO)],
204
+ [:image, IMAGE_INFO, struct_size(IMAGE_INFO)],
205
+ [:orientation, ORIENTATION_INFO, struct_size(ORIENTATION_INFO)],
206
+ [:film, FILM_INFO, struct_size(FILM_INFO)],
207
+ [:television, TELEVISION_INFO, struct_size(TELEVISION_INFO)],
208
+ [:user, USER_INFO, struct_size(USER_INFO)],
209
+ ]
210
+
211
+ # Converts the nexted structs to one template that can be fed to Ruby pack/unpack. This yields
212
+ # some impressive performance improvements (about 1.4 times faster) over reading fields bytewise
213
+ def self.struct_to_template(struct, big_endian)
214
+ keys, template = [], ''
215
+ struct.each do | elem |
216
+ key, cast, size = elem
217
+ pattern = case true
218
+ when cast.is_a?(Struct) # Nested structs
219
+ inner_keys, inner_template = struct_to_template(cast, big_endian)
220
+ # Use a dot as a divider. We will detect it later on and merge into nested hashes
221
+ keys += inner_keys.map{|k| [key, k].join('.') }
222
+ inner_template
223
+ when cast.is_a?(Array) # Repeat values
224
+ inner_keys, inner_template = struct_to_template(cast, big_endian)
225
+ # Use a dot as a divider. We will detect it later on and merge into nested hashes
226
+ keys += inner_keys.map{|k| [key, k].join('.') }
227
+ inner_template
228
+ when cast == Integer || cast == Timecode
229
+ keys << key.to_s
230
+ integer_template(size, big_endian)
231
+ when cast == String
232
+ keys << key.to_s
233
+ "A#{size}"
234
+ when cast == Float
235
+ keys << key.to_s
236
+ big_endian ? "g" : "f"
237
+ end
238
+
239
+ template << pattern
240
+ end
241
+ [keys, template]
242
+ end
243
+
244
+ def self.integer_template(size, big_endian) #:nodoc:
245
+ if size == 2
246
+ big_endian ? "n" : "v"
247
+ elsif size == 4
248
+ big_endian ? "N" : "V"
249
+ end
250
+ end
251
+
252
+ # Shortcuts used to speed up parsing
253
+ TEMPLATE_KEYS, TEMPLATE_BE = struct_to_template(DPX_INFO, true)
254
+ TEMPLATE_LE = struct_to_template(DPX_INFO, false)
255
+ TEMPLATE_LENGTH = struct_size(DPX_INFO)
256
+
257
+ #:startdoc:
258
+ end
259
+ end
@@ -0,0 +1,87 @@
1
+ require File.dirname(__FILE__) + '/../lib/depix'
2
+ require 'test/unit'
3
+
4
+ class StructsTest < Test::Unit::TestCase
5
+ def test_struct_size
6
+ int = [[:some, Integer, 13]]
7
+ assert_equal 13, Depix::Structs.struct_size(int)
8
+
9
+ two_ints = [[:some, String, 13], [:some, String, 13]]
10
+ assert_equal 26, Depix::Structs.struct_size(two_ints)
11
+
12
+ nested_struct = [[:some, String, 10], [:some, two_ints, Depix::Structs.struct_size(two_ints)]]
13
+ assert_equal 36, Depix::Structs.struct_size(nested_struct)
14
+ end
15
+
16
+ def test_integer_template
17
+ assert_equal "N", Depix::Structs.integer_template(4, true)
18
+ assert_equal "V", Depix::Structs.integer_template(4, false)
19
+
20
+ assert_equal "n", Depix::Structs.integer_template(2, true)
21
+ assert_equal "v", Depix::Structs.integer_template(2, false)
22
+ end
23
+
24
+ def test_struct_to_template
25
+ one_int = [[:some, Integer, 4]]
26
+ assert_equal [["some"], "N"], Depix::Structs.struct_to_template(one_int, true)
27
+ assert_equal [["some"], "V"], Depix::Structs.struct_to_template(one_int, false)
28
+
29
+ float = [[:afloat, Float, 4]]
30
+ assert_equal [["afloat"], "g"], Depix::Structs.struct_to_template(float, true)
31
+ assert_equal [["afloat"], "f"], Depix::Structs.struct_to_template(float, false)
32
+
33
+ two_ints = [[:some, Integer, 4], [:another, Integer, 4]]
34
+ assert_equal [["some", "another"], "NN"], Depix::Structs.struct_to_template(two_ints, true)
35
+ assert_equal [["some", "another"], "VV"], Depix::Structs.struct_to_template(two_ints, false)
36
+
37
+ two_ints = [[:some, Integer, 4], [:another, Integer, 2]]
38
+ assert_equal [["some", "another"], "Nn"], Depix::Structs.struct_to_template(two_ints, true)
39
+
40
+ two_ints = [[:some, Integer, 4], [:another, Integer, 2], [:str, String, 5]]
41
+ assert_equal [["some", "another", "str"], "NnA5"], Depix::Structs.struct_to_template(two_ints, true)
42
+
43
+ int_and_nest = [
44
+ [:some, Integer, 4],
45
+ [:another, [[:inner, String, 3]], 3]
46
+ ]
47
+ assert_equal [["some", "another.inner"], "NA3"], Depix::Structs.struct_to_template(int_and_nest, true)
48
+ end
49
+
50
+ def test_template_length
51
+ assert_equal 2084, Depix::Structs::TEMPLATE_LENGTH
52
+ end
53
+
54
+ def test_dpx_info_present
55
+ assert_nothing_raised { Depix::Structs.const_get(:DPX_INFO) }
56
+ end
57
+ end
58
+
59
+ class ReaderTest < Test::Unit::TestCase
60
+ def test_nestify
61
+ k, v = ["foo", "bar"], [1, 2]
62
+ assert_equal( {"foo"=>1, "bar"=> 2}, Depix::Reader.nestify(k,v))
63
+
64
+ k, v = ["foo", "bar.baz"], [1, 2]
65
+ assert_equal( {"foo"=>1, "bar"=> {"baz"=> 2}}, Depix::Reader.nestify(k,v))
66
+
67
+ k, v = ["foo", "bar.baz", "bar.bam"], [1, 2, 3]
68
+ assert_equal( {"foo"=>1, "bar"=> {"baz"=> 2, "bam" => 3}}, Depix::Reader.nestify(k,v))
69
+
70
+ k, v = ["foo", "bar.baz.boo", "bar.baz.doo"], [1, 2, 3]
71
+ assert_equal( {"foo"=>1, "bar"=> {"baz"=> {"boo" => 2, "doo" => 3}}}, Depix::Reader.nestify(k,v))
72
+
73
+ k, v = ["foo", "bar.0", "bar.1"], [1, 2, 3]
74
+ assert_equal( {"foo"=>1, "bar"=>{0=>2, 1=>3}}, Depix::Reader.nestify(k,v))
75
+
76
+ k, v = ["foo", "bar.0.baz", "bar.0.dam"], [1, 2, 3]
77
+ assert_equal( {"foo"=>1, "bar"=>{0=>{"dam"=>3, "baz"=>2}}}, Depix::Reader.nestify(k,v))
78
+ end
79
+
80
+ def test_parse
81
+ file = 'samples/E012_P001_L000002_lin.0001.dpx'
82
+ parsed = Depix::Reader.from_file(file)
83
+ assert_equal 'SDPX', parsed.file.magic
84
+ assert_equal 320, image.pixels_per_line
85
+ assert_equal 240, image.lines_per_element
86
+ end
87
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: depix
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Julik Tarkhanov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-19 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.2
24
+ version:
25
+ description: Read DPX file metadata
26
+ email:
27
+ - me@julik.nl
28
+ executables:
29
+ - depix-describe
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.txt
36
+ - DPX_HEADER_STRUCTURE.txt
37
+ files:
38
+ - History.txt
39
+ - Manifest.txt
40
+ - README.txt
41
+ - DPX_HEADER_STRUCTURE.txt
42
+ - Rakefile
43
+ - bin/depix-describe
44
+ - lib/depix.rb
45
+ - lib/depix/struct_explainer.rb
46
+ - lib/depix/structs.rb
47
+ - test/test_depix.rb
48
+ - test/samples/E012_P001_L000002_lin.0001.dpx
49
+ - test/samples/E012_P001_L000002_lin.0002.dpx
50
+ - test/samples/E012_P001_L000002_log.0001.dpx
51
+ - test/samples/E012_P001_L000002_log.0002.dpx
52
+ has_rdoc: true
53
+ homepage: http://rubyforge.org/julik/depix
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --main
57
+ - README.txt
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project: wiretap
75
+ rubygems_version: 1.3.1
76
+ signing_key:
77
+ specification_version: 2
78
+ summary: Read DPX file metadata
79
+ test_files:
80
+ - test/test_depix.rb