depix 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/DPX_HEADER_STRUCTURE.txt +91 -94
- data/History.txt +5 -0
- data/Manifest.txt +3 -0
- data/README.txt +4 -6
- data/Rakefile +8 -4
- data/bin/depix-describe +15 -6
- data/lib/depix.rb +69 -100
- data/lib/depix/compact_structs.rb +42 -0
- data/lib/depix/dict.rb +343 -0
- data/lib/depix/enums.rb +43 -0
- data/lib/depix/struct_explainer.rb +37 -40
- data/lib/depix/structs.rb +112 -232
- data/test/test_depix.rb +23 -90
- data/test/test_dict.rb +650 -0
- metadata +6 -2
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/structs'
|
2
|
+
|
3
|
+
module Depix
|
4
|
+
CompactInfo = FileInfo.only(
|
5
|
+
:magic,
|
6
|
+
:ditto_key,
|
7
|
+
:filename,
|
8
|
+
:timestamp
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
CompactFilmInfo = FilmInfo.only(
|
13
|
+
:offset,
|
14
|
+
:count,
|
15
|
+
:frame_position,
|
16
|
+
:frame_id,
|
17
|
+
:slate
|
18
|
+
)
|
19
|
+
|
20
|
+
CompactOrientation = OrientationInfo.only(
|
21
|
+
:filename,
|
22
|
+
:timestamp
|
23
|
+
)
|
24
|
+
|
25
|
+
CompactTelevision = TelevisionInfo.only(
|
26
|
+
:time_code,
|
27
|
+
:user_bits,
|
28
|
+
:field_number
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
# A version of the DPX structure that only accounts for the values that change per frame if the ditto_key is set to 1
|
33
|
+
class CompactDPX < Dict
|
34
|
+
inner :file, CompactInfo, :desc => "File information, only frame-transient values"
|
35
|
+
|
36
|
+
inner :image, ImageInfo.filler
|
37
|
+
|
38
|
+
inner :orientation, OrientationInfo, :desc => "Orientation, only frame-transient values"
|
39
|
+
inner :film, CompactFilmInfo, :desc => "Film industry info, only frame-dependent values"
|
40
|
+
inner :television, CompactTelevision, :desc => "TV industry info, only frame-dependent values"
|
41
|
+
end
|
42
|
+
end
|
data/lib/depix/dict.rb
ADDED
@@ -0,0 +1,343 @@
|
|
1
|
+
module Depix
|
2
|
+
|
3
|
+
#:stopdoc:
|
4
|
+
class Field
|
5
|
+
attr_accessor :name, :length, :pattern, :req, :desc, :rtype
|
6
|
+
alias_method :req?, :req
|
7
|
+
|
8
|
+
# Hash init
|
9
|
+
def initialize(opts = {})
|
10
|
+
opts.each_pair {|k, v| send(k.to_s + '=', v) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Emit an unsigned int field
|
14
|
+
def self.emit_u32(o = {})
|
15
|
+
U32Field.new(o)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Emit a short int field
|
19
|
+
def self.emit_u8(o = {})
|
20
|
+
U8Field.new(o)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Emit a double int field
|
24
|
+
def self.emit_u16(o = {})
|
25
|
+
U16Field.new(o)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Emit a char field
|
29
|
+
def self.emit_char(o = {})
|
30
|
+
opts = {:length => 1}.merge(o)
|
31
|
+
CharField.new(opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Emit a float field
|
35
|
+
def self.emit_r32(o = {})
|
36
|
+
R32Field.new(o)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return a cleaned value
|
40
|
+
def clean(v)
|
41
|
+
v
|
42
|
+
end
|
43
|
+
|
44
|
+
def explain
|
45
|
+
[rtype ? ("(%s)" % rtype) : nil, desc, (req? ? "- required" : nil)].compact.join(' ')
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the actual values from the stack. The stack will begin on the element we need,
|
49
|
+
# so the default consumption is shift
|
50
|
+
def consume!(stack)
|
51
|
+
clean(stack.shift)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class U32Field < Field
|
56
|
+
BLANK = 0xFFFFFFFF
|
57
|
+
|
58
|
+
def pattern
|
59
|
+
"N"
|
60
|
+
end
|
61
|
+
|
62
|
+
def length
|
63
|
+
4
|
64
|
+
end
|
65
|
+
|
66
|
+
def clean(value)
|
67
|
+
value == BLANK ? nil : value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class U8Field < Field
|
72
|
+
|
73
|
+
BLANK = 0xFF
|
74
|
+
|
75
|
+
def pattern
|
76
|
+
"c"
|
77
|
+
end
|
78
|
+
|
79
|
+
def length
|
80
|
+
1
|
81
|
+
end
|
82
|
+
|
83
|
+
def rtype
|
84
|
+
Integer
|
85
|
+
end
|
86
|
+
|
87
|
+
def clean(v)
|
88
|
+
v == BLANK ? nil : v
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Filler < Field
|
93
|
+
def pattern
|
94
|
+
"x#{length ? length.to_i : 1}"
|
95
|
+
end
|
96
|
+
|
97
|
+
# Leave the stack alone since we skipped
|
98
|
+
def consume(stack)
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class U16Field < Field
|
104
|
+
BLANK = 0xFFFF
|
105
|
+
|
106
|
+
def pattern
|
107
|
+
"n"
|
108
|
+
end
|
109
|
+
|
110
|
+
def length
|
111
|
+
2
|
112
|
+
end
|
113
|
+
|
114
|
+
def rtype
|
115
|
+
Integer
|
116
|
+
end
|
117
|
+
|
118
|
+
def clean(v)
|
119
|
+
v == BLANK ? nil : v
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class R32Field < Field
|
124
|
+
def pattern
|
125
|
+
"g"
|
126
|
+
end
|
127
|
+
|
128
|
+
def clean(v)
|
129
|
+
v.nan? ? nil : v
|
130
|
+
end
|
131
|
+
|
132
|
+
def length
|
133
|
+
4
|
134
|
+
end
|
135
|
+
|
136
|
+
def rtype
|
137
|
+
Float
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class CharField < Field
|
142
|
+
BLANK = "\0"
|
143
|
+
|
144
|
+
def pattern
|
145
|
+
"A#{(length || 1).to_i}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def clean(v)
|
149
|
+
if v == BLANK
|
150
|
+
nil
|
151
|
+
else
|
152
|
+
cleansed = v.gsub(0xFF.chr, '').gsub(0x00.chr, '')
|
153
|
+
cleansed.empty? ? nil : cleansed
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def rtype
|
158
|
+
String
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Wrapper for an array structure
|
163
|
+
class ArrayField < Field
|
164
|
+
attr_accessor :members
|
165
|
+
undef :length=, :pattern=
|
166
|
+
|
167
|
+
def length
|
168
|
+
members.inject(0){|_, s| _ + s.length }
|
169
|
+
end
|
170
|
+
|
171
|
+
def pattern
|
172
|
+
members.inject(''){|_, s| _ + s.pattern }
|
173
|
+
end
|
174
|
+
|
175
|
+
def consume!(stack)
|
176
|
+
members.map{|m| m.consume!(stack)}
|
177
|
+
end
|
178
|
+
|
179
|
+
def rtype
|
180
|
+
Array
|
181
|
+
end
|
182
|
+
|
183
|
+
def explain
|
184
|
+
return 'Empty array' if (!members || members.empty?)
|
185
|
+
tpl = "(Array of %d %s fields)" % [ members.length, members[0].rtype]
|
186
|
+
r = (req? ? "- required" : nil)
|
187
|
+
[tpl, desc, r].compact.join(' ')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Wrapper for a contained structure
|
192
|
+
class InnerField < Field
|
193
|
+
attr_accessor :cast
|
194
|
+
undef :length=, :pattern=
|
195
|
+
|
196
|
+
def length
|
197
|
+
cast.length
|
198
|
+
end
|
199
|
+
|
200
|
+
def pattern
|
201
|
+
cast.pattern
|
202
|
+
end
|
203
|
+
|
204
|
+
def consume!(stack)
|
205
|
+
cast.consume!(stack)
|
206
|
+
end
|
207
|
+
|
208
|
+
def rtype
|
209
|
+
cast
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Base class for a struct. Could also be implemented as a module actually
|
214
|
+
class Dict
|
215
|
+
DEF_OPTS = { :req => false, :desc => nil }
|
216
|
+
|
217
|
+
class << self
|
218
|
+
|
219
|
+
# Get the array of fields defined in this struct
|
220
|
+
def fields
|
221
|
+
@fields ||= []
|
222
|
+
end
|
223
|
+
|
224
|
+
# Define a 4-byte unsigned integer
|
225
|
+
def u32(name, *extras)
|
226
|
+
count, opts = count_and_opts_from(extras)
|
227
|
+
attr_accessor name
|
228
|
+
fields << Field.emit_u32( {:name => name }.merge(opts) )
|
229
|
+
end
|
230
|
+
|
231
|
+
# Define a double-width unsigned integer
|
232
|
+
def u16(name, *extras)
|
233
|
+
count, opts = count_and_opts_from(extras)
|
234
|
+
attr_accessor name
|
235
|
+
fields << Field.emit_u16( {:name => name }.merge(opts) )
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
# Define a small unsigned integer
|
240
|
+
def u8(name, *extras)
|
241
|
+
count, opts = count_and_opts_from(extras)
|
242
|
+
attr_accessor name
|
243
|
+
fields << Field.emit_u8( {:name => name }.merge(opts) )
|
244
|
+
end
|
245
|
+
|
246
|
+
# Define a real number
|
247
|
+
def r32(name, *extras)
|
248
|
+
count, opts = count_and_opts_from(extras)
|
249
|
+
attr_accessor name
|
250
|
+
fields << Field.emit_r32( {:name => name}.merge(opts) )
|
251
|
+
end
|
252
|
+
|
253
|
+
# Define an array of values
|
254
|
+
def array(name, mapped_to, *extras)
|
255
|
+
count, opts = count_and_opts_from(extras)
|
256
|
+
attr_accessor name
|
257
|
+
|
258
|
+
a = ArrayField.new({:name => name}.merge(opts))
|
259
|
+
a.members = if mapped_to.is_a?(Class) # Array of structs
|
260
|
+
[InnerField.new(:cast => mapped_to)] * count
|
261
|
+
else
|
262
|
+
[Field.send("emit_#{mapped_to}")] * count
|
263
|
+
end
|
264
|
+
fields << a
|
265
|
+
end
|
266
|
+
|
267
|
+
# Define a nested struct
|
268
|
+
def inner(name, mapped_to, *extras)
|
269
|
+
count, opts = count_and_opts_from(extras)
|
270
|
+
attr_accessor name
|
271
|
+
fields << InnerField.new({:name => name, :cast => mapped_to}.merge(opts))
|
272
|
+
end
|
273
|
+
|
274
|
+
# Define a char field
|
275
|
+
def char(name, *extras)
|
276
|
+
count, opts = count_and_opts_from(extras)
|
277
|
+
attr_accessor name
|
278
|
+
fields << Field.emit_char( {:name => name, :length => count}.merge(opts) )
|
279
|
+
end
|
280
|
+
|
281
|
+
# Get the pattern that will be used to unpack this structure and all of it's descendants
|
282
|
+
def pattern
|
283
|
+
fields.map{|f| f.pattern }.join
|
284
|
+
end
|
285
|
+
|
286
|
+
# How many bytes are needed to complete this structure
|
287
|
+
def length
|
288
|
+
fields.inject(0){|_, s| _ + s.length }
|
289
|
+
end
|
290
|
+
|
291
|
+
# Consume a stack of unpacked values, letting each field decide how many to consume
|
292
|
+
def consume!(stack_of_unpacked_values)
|
293
|
+
new_item = new
|
294
|
+
@fields.each do | field |
|
295
|
+
new_item.send("#{field.name}=", field.consume!(stack_of_unpacked_values)) unless field.name.nil?
|
296
|
+
end
|
297
|
+
new_item
|
298
|
+
end
|
299
|
+
|
300
|
+
# Apply this structure to data in the string, returning an instance of this structure with fields completed
|
301
|
+
def apply!(string)
|
302
|
+
consume!(string.unpack(pattern))
|
303
|
+
end
|
304
|
+
|
305
|
+
# Get a class that would parse just the same, preserving only the fields passed in the array. This speeds
|
306
|
+
# up parsing because we only extract and conform the fields that we need
|
307
|
+
def only(*field_names)
|
308
|
+
distillate = fields.inject([]) do | m, f |
|
309
|
+
if field_names.include?(f.name) # preserve
|
310
|
+
m.push(f)
|
311
|
+
else # create filler
|
312
|
+
unless m[-1].is_a?(Filler)
|
313
|
+
m.push(Filler.new(:length => f.length))
|
314
|
+
else
|
315
|
+
m[-1].length += f.length
|
316
|
+
end
|
317
|
+
m
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
anon = Class.new(self)
|
322
|
+
anon.fields.replace(distillate)
|
323
|
+
anon
|
324
|
+
end
|
325
|
+
|
326
|
+
# Get an opaque struct based on this one, that will consume exactly as many bytes as this
|
327
|
+
# structure would occupy, but discard them instead
|
328
|
+
def filler
|
329
|
+
only([])
|
330
|
+
end
|
331
|
+
|
332
|
+
private
|
333
|
+
|
334
|
+
# extract_options! on a diet
|
335
|
+
def count_and_opts_from(args)
|
336
|
+
options, count = (args[-1].is_a?(Hash) ? DEF_OPTS.merge(args.pop) : DEF_OPTS), (args.shift || 1)
|
337
|
+
[count, options]
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
#:startdoc:
|
343
|
+
end
|
data/lib/depix/enums.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Depix
|
2
|
+
COLORIMETRIC = {
|
3
|
+
:UserDefined => 0,
|
4
|
+
:PrintingDensity => 1,
|
5
|
+
:Linear => 2,
|
6
|
+
:Logarithmic => 3,
|
7
|
+
:UnspecifiedVideo => 4,
|
8
|
+
:SMTPE_274M => 5,
|
9
|
+
:ITU_R709 => 6,
|
10
|
+
:ITU_R601_625L => 7,
|
11
|
+
:ITU_R601_525L => 8,
|
12
|
+
:NTSCCompositeVideo => 9,
|
13
|
+
:PALCompositeVideo => 10,
|
14
|
+
:ZDepthLinear => 11,
|
15
|
+
:DepthHomogeneous => 12
|
16
|
+
}
|
17
|
+
|
18
|
+
COMPONENT_TYPE = {
|
19
|
+
:Undefined => 0,
|
20
|
+
:Red => 1,
|
21
|
+
:Green => 2,
|
22
|
+
:Blue => 3,
|
23
|
+
:Alpha => 4,
|
24
|
+
:Luma => 6,
|
25
|
+
:ColorDifferenceCbCr => 7,
|
26
|
+
:Depth => 8,
|
27
|
+
:CompositeVideo => 9,
|
28
|
+
:RGB => 50,
|
29
|
+
:RGBA => 51,
|
30
|
+
:ABGR => 52,
|
31
|
+
:CbYCrY422 => 100,
|
32
|
+
:CbYACrYA4224 => 101,
|
33
|
+
:CbYCr444 => 102,
|
34
|
+
:CbYCrA4444 => 103,
|
35
|
+
:UserDef2Element => 150,
|
36
|
+
:UserDef3Element => 151,
|
37
|
+
:UserDef4Element => 152,
|
38
|
+
:UserDef5Element => 153,
|
39
|
+
:UserDef6Element => 154,
|
40
|
+
:UserDef7Element => 155,
|
41
|
+
:UserDef8Element => 156,
|
42
|
+
}
|
43
|
+
end
|
@@ -1,45 +1,13 @@
|
|
1
1
|
# Generates an RDoc description of the DPX structs from the structs.rb file
|
2
|
-
class
|
2
|
+
class RdocExplainer #:nodoc:
|
3
3
|
attr_accessor :io, :attr_template, :struct_template
|
4
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
5
|
TPL = <<eof
|
37
6
|
= DPX header structure description
|
38
7
|
|
39
|
-
DPX metadata gets returned as a
|
40
|
-
|
41
|
-
|
42
|
-
meta.file.magic # same as meta[:file][:magic]
|
8
|
+
DPX metadata gets returned as a Depix::DPX object with nested properties.
|
9
|
+
|
10
|
+
meta.file.magic # => "SDPX"
|
43
11
|
|
44
12
|
== Metadata structure
|
45
13
|
|
@@ -47,16 +15,45 @@ and method name
|
|
47
15
|
eof
|
48
16
|
|
49
17
|
def initialize
|
50
|
-
|
18
|
+
@padding = ' '
|
51
19
|
@attr_template = "%s* <tt>%s</tt> %s"
|
52
|
-
@struct_template = "%s* <tt>%s</tt>
|
53
|
-
@array_template = "%s* <tt>%s</tt>
|
20
|
+
@struct_template = "%s* <tt>%s</tt> %s:"
|
21
|
+
@array_template = "%s* <tt>%s</tt> %s:"
|
54
22
|
|
55
23
|
end
|
56
24
|
|
57
25
|
def get_rdoc_for(struct)
|
58
26
|
@io = StringIO.new
|
59
|
-
explain_struct(
|
27
|
+
explain_struct(struct)
|
60
28
|
TPL % @io.string
|
61
29
|
end
|
30
|
+
|
31
|
+
include Depix
|
32
|
+
|
33
|
+
def explain_struct(struct, padding = '') #:nodoc:
|
34
|
+
struct.fields.each do | e |
|
35
|
+
if e.is_a?(InnerField)
|
36
|
+
|
37
|
+
@io.puts( @struct_template % [padding, e.name, e.explain])
|
38
|
+
explain_struct(e.rtype, padding + @padding)
|
39
|
+
|
40
|
+
elsif e.is_a?(ArrayField)
|
41
|
+
|
42
|
+
@io.puts( @array_template % [padding, e.name, e.explain])
|
43
|
+
|
44
|
+
inner_struct = e.members[0]
|
45
|
+
|
46
|
+
if inner_struct.is_a?(InnerField)
|
47
|
+
explain_struct(inner_struct.rtype, padding + @padding)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
explain_attr(padding, e)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def explain_attr(padding, e)
|
56
|
+
type_name = e.rtype ? "(#{e.rtype})" : nil
|
57
|
+
@io.puts( @attr_template % [padding, e.name, e.explain])
|
58
|
+
end
|
62
59
|
end
|