depix 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,10 +34,10 @@ and method name
34
34
  * <tt>low_quantity</tt> Float
35
35
  * <tt>high_data</tt> Integer
36
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
37
+ * <tt>descriptor</tt> Integer
38
+ * <tt>transfer</tt> Integer
39
+ * <tt>colorimetric</tt> Integer
40
+ * <tt>bit_size</tt> Integer
41
41
  * <tt>packing</tt> Integer
42
42
  * <tt>encoding</tt> Integer
43
43
  * <tt>data_offset</tt> Integer
@@ -79,10 +79,10 @@ and method name
79
79
  * <tt>television</tt> hash of
80
80
  * <tt>time_code</tt> Integer
81
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
82
+ * <tt>interlace</tt> Integer
83
+ * <tt>field_number</tt> Integer
84
+ * <tt>video_signal</tt> Integer
85
+ * <tt>padding</tt> Integer
86
86
  * <tt>horizontal_sample_rate</tt> Float
87
87
  * <tt>vertical_sample_rate</tt> Float
88
88
  * <tt>frame_rate</tt> Float
data/History.txt CHANGED
@@ -1,3 +1,6 @@
1
+ === 1.0.1 / 2008-12-18
2
+ * small doc and usability improvements
3
+
1
4
  === 1.0.0 / 2008-12-18
2
5
 
3
6
  * 1 major enhancement
data/README.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  = depix
2
2
 
3
- * http://rubyforge.org/julik/depix
3
+ * http://wiretap.rubyforge.org/depix
4
4
 
5
5
  == DESCRIPTION:
6
6
 
@@ -8,7 +8,7 @@ Read DPX file metadata
8
8
 
9
9
  == SYNOPSIS:
10
10
 
11
- meta = Depix::Reader.new.from_file(dpx_file_path)
11
+ meta = Depix::Reader.from_file(dpx_file_path)
12
12
  puts meta.television.time_code #=> 10:00:00:02
13
13
 
14
14
  The data returned is described in the DPX_HEADER_STRUCTURE[link:files/DPX_HEADER_STRUCTURE_txt.html]. The structs
@@ -21,7 +21,11 @@ The gem also contains an executable called depix-desribe which can be used from
21
21
  == NOTES:
22
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
24
+ the whole DPX file (which usually is around 8mb per frame) to know the details.
25
+
26
+ In the future there will be a possibility to modify and commit the headers, but it's not a priority at this time.
27
+
28
+ Autodesk IFFS systems write the reel name for the file to the orientation.device field
25
29
 
26
30
  == REQUIREMENTS:
27
31
 
data/Rakefile CHANGED
@@ -6,6 +6,7 @@ Hoe.new('depix', Depix::VERSION) do |p|
6
6
  p.developer('Julik Tarkhanov', 'me@julik.nl')
7
7
  p.rubyforge_name = 'wiretap'
8
8
  p.extra_deps.reject! {|e| e[0] == 'hoe' }
9
+ p.remote_rdoc_dir = 'depix'
9
10
  end
10
11
 
11
12
  task :describe_structs do
data/bin/depix-describe CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/depix'
4
- ARGV.flatten.each do | file |
4
+ if ARGV.flatten.map do | file |
5
5
  data = Depix::Reader.describe_file(file)
6
6
  puts "Describing DPX #{file}. Empty elements are omitted."
7
7
  puts data
8
+ end.empty?
9
+ STDERR.puts "Usage: depix-describe somefile.dpx anotherfile.dpx"
8
10
  end
data/lib/depix.rb CHANGED
@@ -5,8 +5,12 @@ require 'timecode'
5
5
  require File.dirname(__FILE__) + '/depix/structs'
6
6
 
7
7
  module Depix
8
- VERSION = '1.0.0'
9
- BLANK_4, BLANK_2 = 0xFFFFFFFF, 0xFFFF
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:
10
14
 
11
15
  # Methodic hash - stolen from Camping
12
16
  class H < Hash
@@ -17,11 +21,37 @@ module Depix
17
21
  # => :macadamian
18
22
  #
19
23
  def method_missing(m,*a)
20
- m.to_s=~/=$/?self[$`]=a[0]:a==[]? (self.key?(m.to_sym) ? self[m.to_sym] : super ) : super
24
+ m.to_s=~/=$/ ? (self[$`] = a[0]) : (a == [] ? (self.key?(m.to_s) ? self[m.to_s] : super ) : super)
21
25
  end
22
26
  undef id, type
23
27
  end
24
28
 
29
+ # Offers convenience access to a few common attributes bypassing the piecemeal structs
30
+ module Synthetics
31
+ def keycode
32
+ [film.id, film.type, film.offset, film.prefix, film.count].compact.join(' ')
33
+ end
34
+
35
+ def flame_reel
36
+ orientation.device.to_s.scan(/^(\w+)/).to_s
37
+ end
38
+
39
+ def time_code
40
+ Timecode.from_uint(television.time_code, film.fps)
41
+ end
42
+
43
+ # Get the name of the transfer function (Linear, Logarithmic, ...)
44
+ def colorimetric
45
+ Structs::COLORIMETRIC.invert[image.image_elements[0].colorimetric]
46
+ end
47
+
48
+ # Get the name of the compnent type (RGB, YCbCr, ...)
49
+ def component_type
50
+ Structs::COMPONENT_TYPE.invert[image.image_elements[0].descriptor]
51
+ end
52
+
53
+ end
54
+
25
55
  # Reads the metadata
26
56
  class Reader
27
57
 
@@ -36,12 +66,14 @@ module Depix
36
66
  new.from_string(str)
37
67
  end
38
68
 
69
+ # Returns a printable report on all the headers present in the string
39
70
  def describe_string(str)
40
71
  reader = new
41
72
  result = reader.deep_parse(str, Structs::DPX_INFO)
42
73
  reader.inform(result)
43
74
  end
44
-
75
+
76
+ # Returns a printable report on all the headers present in the file at the path passed
45
77
  def describe_file(path)
46
78
  header = File.open(path, 'r') { |f| f.read(Structs::TEMPLATE_LENGTH) }
47
79
  describe_string(header)
@@ -81,9 +113,12 @@ module Depix
81
113
  end
82
114
 
83
115
  def wrap(result)
84
- self.class.nestify(Structs::TEMPLATE_KEYS, result)
116
+ eich = self.class.nestify(Structs::TEMPLATE_KEYS, result)
117
+ class << eich; include Synthetics; end
118
+ eich
85
119
  end
86
120
 
121
+ # FIXME - currently no array handling
87
122
  def self.nestify(keys, values)
88
123
  auto_hash = H.new do |h,k|
89
124
  h[k] = H.new(&h.default_proc)
@@ -104,20 +139,9 @@ module Depix
104
139
  end
105
140
 
106
141
  def unpad(string) # :nodoc:
107
- string.gsub(0xFF.chr, '').gsub(0xFF.chr, '')
142
+ string.gsub("\000", '').gsub(0xFF.chr, '')
108
143
  end
109
144
 
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
145
+ #:startdoc:
122
146
  end
123
147
  end
data/lib/depix/structs.rb CHANGED
@@ -4,10 +4,6 @@ module Depix
4
4
  # Which is a reformulation of http://www.cineon.com/ff_draft.php
5
5
  #
6
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
7
  module Structs
12
8
 
13
9
  COLORIMETRIC = {
@@ -55,7 +51,7 @@ module Depix
55
51
  #:stopdoc:
56
52
 
57
53
  # To avoid fucking up with sizes afterwards
58
- UINT, FLOAT, USHORT, UCHAR = 4, 4, 2, 1
54
+ U32, R32, U16, U8, UCHAR = 4, 4, 2, 1, 1
59
55
 
60
56
  def self.struct_size(struct_const) #:nodoc:
61
57
  struct_const.inject(0){| s, e | s + e[2]}
@@ -67,15 +63,15 @@ module Depix
67
63
 
68
64
  FILE_INFO = Struct[
69
65
  [:magic, String, 4],
70
- [:image_offset, Integer, UINT],
66
+ [:image_offset, Integer, U32],
71
67
 
72
68
  [:version, String, 8],
73
69
 
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],
70
+ [:file_size, Integer, U32],
71
+ [:ditto_key, Integer, U32],
72
+ [:generic_size, Integer, U32],
73
+ [:industry_size, Integer, U32],
74
+ [:user_size, Integer, U32],
79
75
 
80
76
  [:filename, String, 100],
81
77
  [:timestamp, String, 24],
@@ -83,7 +79,7 @@ module Depix
83
79
  [:project, String, 200],
84
80
  [:copyright, String, 200],
85
81
 
86
- [:encrypt_key, Integer, UINT],
82
+ [:encrypt_key, Integer, U32],
87
83
  [:reserve, String, 104],
88
84
  ]
89
85
 
@@ -95,12 +91,12 @@ module Depix
95
91
  [:count, String, 4],
96
92
  [:format, String, 32],
97
93
 
98
- [:frame_position, Integer, UINT],
99
- [:sequence_extent, Integer, UINT],
100
- [:held_count, Integer, UINT],
94
+ [:frame_position, Integer, U32],
95
+ [:sequence_extent, Integer, U32],
96
+ [:held_count, Integer, U32],
101
97
 
102
- [:frame_rate, Float, FLOAT],
103
- [:shutter_angle, Float, FLOAT],
98
+ [:frame_rate, Float, R32],
99
+ [:shutter_angle, Float, R32],
104
100
 
105
101
  [:frame_id, String, 32],
106
102
  [:slate, String, 100],
@@ -109,57 +105,57 @@ module Depix
109
105
 
110
106
 
111
107
  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],
108
+ [:data_sign, Integer, U32],
109
+ [:low_data, Integer, U32],
110
+ [:low_quantity, Float, R32],
111
+ [:high_data, Integer, U32],
112
+ [:high_quantity, Float, R32],
117
113
 
118
114
  # 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],
115
+ [:descriptor, Integer, U8],
116
+ [:transfer, Integer, U8],
117
+ [:colorimetric, Integer, U8],
118
+ [:bit_size, Integer, U8],
123
119
 
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],
120
+ [:packing, Integer, U16],
121
+ [:encoding, Integer, U16],
122
+ [:data_offset, Integer, U32],
123
+ [:end_of_line_padding, Integer, U32],
124
+ [:end_of_image_padding, Integer, U32],
129
125
  [:description, String, 32],
130
126
  ]
131
127
 
132
128
  IMAGE_ELEMENTS = (0..7).map{|e| [e, IMAGE_ELEMENT, struct_size(IMAGE_ELEMENT)] }
133
129
 
134
130
  IMAGE_INFO = Struct[
135
- [:orientation, Integer, USHORT],
136
- [:number_elements, Integer, USHORT],
131
+ [:orientation, Integer, U16],
132
+ [:number_elements, Integer, U16],
137
133
 
138
- [:pixels_per_line, Integer, UINT],
139
- [:lines_per_element, Integer, UINT],
134
+ [:pixels_per_line, Integer, U32],
135
+ [:lines_per_element, Integer, U32],
140
136
 
141
137
  [:image_elements, IMAGE_ELEMENTS, struct_size(IMAGE_ELEMENTS) ],
142
138
 
143
139
  [:reserve, String, 52],
144
140
  ]
145
141
 
146
- BORDER = (0..3).map{|s| [s, Integer, USHORT] }
142
+ BORDER = (0..3).map{|s| [s, Integer, U16] }
147
143
 
148
144
  ASPECT_RATIO = [
149
- [0, Integer, UINT],
150
- [1, Integer, UINT],
145
+ [0, Integer, U32],
146
+ [1, Integer, U32],
151
147
  ]
152
148
 
153
149
  ORIENTATION_INFO = Struct[
154
150
 
155
- [:x_offset, Integer, UINT],
156
- [:y_offset, Integer, UINT],
151
+ [:x_offset, Integer, U32],
152
+ [:y_offset, Integer, U32],
157
153
 
158
- [:x_center, Float, FLOAT],
159
- [:y_center, Float, FLOAT],
154
+ [:x_center, Float, R32],
155
+ [:y_center, Float, R32],
160
156
 
161
- [:x_size, Integer, UINT],
162
- [:y_size, Integer, UINT],
157
+ [:x_size, Integer, U32],
158
+ [:y_size, Integer, U32],
163
159
 
164
160
  [:filename, String, 100],
165
161
  [:timestamp, String, 24],
@@ -173,30 +169,30 @@ module Depix
173
169
  ]
174
170
 
175
171
  TELEVISION_INFO = Struct[
176
- [:time_code, Integer, UINT],
177
- [:user_bits, Integer, UINT],
172
+ [:time_code, Integer, U32],
173
+ [:user_bits, Integer, U32],
178
174
 
179
- [:interlace, String, UCHAR],
180
- [:field_number, String, UCHAR],
181
- [:video_signal, String, UCHAR],
182
- [:padding, String, UCHAR],
175
+ [:interlace, Integer, U8],
176
+ [:field_number, Integer, U8],
177
+ [:video_signal, Integer, U8],
178
+ [:padding, Integer, U8],
183
179
 
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],
180
+ [:horizontal_sample_rate, Float, R32],
181
+ [:vertical_sample_rate, Float, R32],
182
+ [:frame_rate, Float, R32],
183
+ [:time_offset, Float, R32],
184
+ [:gamma, Float, R32],
185
+ [:black_level, Float, R32],
186
+ [:black_gain, Float, R32],
187
+ [:break_point, Float, R32],
188
+ [:white_level, Float, R32],
189
+ [:integration_times, Float, R32],
194
190
  [:reserve, String, 76],
195
191
  ]
196
192
 
197
193
  USER_INFO = Struct[
198
194
  [:id, String, 32],
199
- [:user_data, Integer, UINT],
195
+ [:user_data, Integer, U32],
200
196
  ]
201
197
 
202
198
  DPX_INFO = Struct[
@@ -242,7 +238,9 @@ module Depix
242
238
  end
243
239
 
244
240
  def self.integer_template(size, big_endian) #:nodoc:
245
- if size == 2
241
+ if size == 1
242
+ "c"
243
+ elsif size == 2
246
244
  big_endian ? "n" : "v"
247
245
  elsif size == 4
248
246
  big_endian ? "N" : "V"
data/test/test_depix.rb CHANGED
@@ -56,6 +56,16 @@ class StructsTest < Test::Unit::TestCase
56
56
  end
57
57
  end
58
58
 
59
+ class EichTest < Test::Unit::TestCase
60
+ def test_eich
61
+ eich = Depix::H.new
62
+ assert_nothing_raised { assert_not_nil eich.foo = 1 }
63
+ assert_nothing_raised { eich.foo }
64
+ assert_equal ['foo'], eich.keys
65
+ assert_equal 1, eich.foo
66
+ end
67
+ end
68
+
59
69
  class ReaderTest < Test::Unit::TestCase
60
70
  def test_nestify
61
71
  k, v = ["foo", "bar"], [1, 2]
@@ -77,11 +87,65 @@ class ReaderTest < Test::Unit::TestCase
77
87
  assert_equal( {"foo"=>1, "bar"=>{0=>{"dam"=>3, "baz"=>2}}}, Depix::Reader.nestify(k,v))
78
88
  end
79
89
 
80
- def test_parse
90
+ def test_parsed_properly
91
+ file = 'samples/E012_P001_L000002_lin.0001.dpx'
92
+ parsed = Depix::Reader.from_file(file)
93
+ assert_equal "SDPX", parsed.file.magic
94
+ assert_equal 8192, parsed.file.image_offset
95
+ assert_equal "V1.0", parsed.file.version
96
+ assert_equal 319488, parsed.file.file_size
97
+ assert_equal 1, parsed.file.ditto_key
98
+ assert_equal 1664, parsed.file.generic_size
99
+ assert_equal 384, parsed.file.industry_size
100
+ assert_equal 6144, parsed.file.user_size
101
+ assert_equal "E012_P001_L000002_lin.0001.dpx", parsed.file.filename
102
+ assert_equal "2008:12:19:01:18:37:CEST", parsed.file.timestamp
103
+ assert_equal "UTODESK", parsed.file.creator
104
+ assert_equal 0, parsed.image.orientation
105
+ assert_equal 1, parsed.image.number_elements
106
+ assert_equal 320, parsed.image.pixels_per_line
107
+ assert_equal 240, parsed.image.lines_per_element
108
+ assert_equal 0, parsed.image.image_elements[0].data_sign
109
+ assert_equal 0, parsed.image.image_elements[0].low_data
110
+ assert_equal 0.0, parsed.image.image_elements[0].low_quantity
111
+ assert_equal 1023, parsed.image.image_elements[0].high_data
112
+ assert_in_delta 2.04699993133545, parsed.image.image_elements[0].high_quantity, 1.0 ** -10
113
+
114
+ assert_equal 50, parsed.image.image_elements[0].descriptor # RGB :-)
115
+ assert_equal 2, parsed.image.image_elements[0].transfer
116
+ assert_equal 2, parsed.image.image_elements[0].colorimetric
117
+ assert_equal 10, parsed.image.image_elements[0].bit_size
118
+ assert_equal 1, parsed.image.image_elements[0].packing
119
+ assert_equal 0, parsed.image.image_elements[0].encoding
120
+ assert_equal 8192, parsed.image.image_elements[0].data_offset
121
+ assert_equal 0, parsed.image.image_elements[0].end_of_line_padding
122
+ assert_equal 0, parsed.image.image_elements[0].end_of_image_padding
123
+ assert_equal "IMAGE DESCRIPTION DATA P", parsed.image.image_elements[0].description
124
+ # assert_equal "E012x�", parsed.orientation.device - this is where Flame writes the reel
125
+ assert_equal 853, parsed.orientation.aspect_ratio[0]
126
+ assert_equal 640, parsed.orientation.aspect_ratio[1]
127
+
128
+ assert_equal '75', parsed.film.id
129
+ assert_equal '00', parsed.film.type
130
+ assert_equal '19', parsed.film.offset
131
+ assert_equal '740612', parsed.film.prefix
132
+ assert_equal '9841', parsed.film.count
133
+ assert_equal 1, parsed.film.frame_position
134
+ assert_equal 2, parsed.film.sequence_extent
135
+ assert_equal 1, parsed.film.held_count
136
+ assert_equal 25.0, parsed.film.frame_rate
137
+ assert_equal 18157848, parsed.television.time_code
138
+ assert_equal 0, parsed.television.user_bits
139
+ end
140
+
141
+ def test_syntethics
142
+ assert_nothing_raised { Depix::Synthetics }
143
+
81
144
  file = 'samples/E012_P001_L000002_lin.0001.dpx'
82
145
  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
146
+ assert_equal "75 00 19 740612 9841", parsed.keycode
147
+ assert_equal :RGB, parsed.component_type
148
+ assert_equal :Linear, parsed.colorimetric
149
+ assert_equal "E012", parsed.flame_reel
86
150
  end
87
151
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: depix
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
@@ -50,7 +50,7 @@ files:
50
50
  - test/samples/E012_P001_L000002_log.0001.dpx
51
51
  - test/samples/E012_P001_L000002_log.0002.dpx
52
52
  has_rdoc: true
53
- homepage: http://rubyforge.org/julik/depix
53
+ homepage: http://wiretap.rubyforge.org/depix
54
54
  post_install_message:
55
55
  rdoc_options:
56
56
  - --main