depix 1.0.0

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