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.
- 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
|