depix 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
@@ -21,7 +21,7 @@ DPX metadata gets returned as a Depix::DPX object with nested properties.
21
21
  * <tt>project</tt> (String) Project name
22
22
  * <tt>copyright</tt> (String) Copyright
23
23
  * <tt>encrypt_key</tt> Encryption key
24
- * <tt>reserve</tt> (String)
24
+ * <tt>reserve</tt> (NilClass)
25
25
  * <tt>image</tt> (Depix::ImageInfo) Image information - required:
26
26
  * <tt>orientation</tt> (Integer) Orientation descriptor - required
27
27
  * <tt>number_elements</tt> (Integer) How many elements to scan - required
@@ -43,7 +43,7 @@ DPX metadata gets returned as a Depix::DPX object with nested properties.
43
43
  * <tt>end_of_line_padding</tt> End-of-line padding for this image element
44
44
  * <tt>end_of_image_padding</tt> End-of-line padding for this image element
45
45
  * <tt>description</tt> (String)
46
- * <tt>reserve</tt> (String)
46
+ * <tt>reserve</tt> (NilClass)
47
47
  * <tt>orientation</tt> (Depix::OrientationInfo) Orientation - required:
48
48
  * <tt>x_offset</tt>
49
49
  * <tt>y_offset</tt>
@@ -57,7 +57,7 @@ DPX metadata gets returned as a Depix::DPX object with nested properties.
57
57
  * <tt>serial</tt> (String) Input device serial number
58
58
  * <tt>border</tt> (Array of 4 Integer fields) Border validity: XL, XR, YT, YB:
59
59
  * <tt>aspect_ratio</tt> (Array of 2 fields) Aspect (H:V):
60
- * <tt>reserve</tt> (String)
60
+ * <tt>reserve</tt> (NilClass)
61
61
  * <tt>film</tt> (Depix::FilmInfo) Film industry info - required:
62
62
  * <tt>id</tt> (String) Film mfg. ID code (2 digits from film edge code)
63
63
  * <tt>type</tt> (String) Film type (2 digits from film edge code)
@@ -72,7 +72,7 @@ DPX metadata gets returned as a Depix::DPX object with nested properties.
72
72
  * <tt>shutter_angle</tt> (Float) Shutter angle
73
73
  * <tt>frame_id</tt> (String) Frame identification (keyframe)
74
74
  * <tt>slate</tt> (String) Slate information
75
- * <tt>reserve</tt> (String)
75
+ * <tt>reserve</tt> (NilClass)
76
76
  * <tt>television</tt> (Depix::TelevisionInfo) TV industry info - required:
77
77
  * <tt>time_code</tt> Timecode, formatted as HH:MM:SS:FF in the 4 higher bits of each 8bit group
78
78
  * <tt>user_bits</tt> Timecode UBITs
@@ -90,7 +90,7 @@ DPX metadata gets returned as a Depix::DPX object with nested properties.
90
90
  * <tt>break_point</tt> (Float) Break point (?)
91
91
  * <tt>white_level</tt> (Float) White level
92
92
  * <tt>integration_times</tt> (Float) Integration times (S)
93
- * <tt>reserve</tt> (Float)
93
+ * <tt>reserve</tt> (NilClass)
94
94
  * <tt>user</tt> (Depix::UserInfo) User info - required:
95
95
  * <tt>id</tt> (String) Name of the user data tag
96
96
  * <tt>user_data_ptr</tt>
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # --ruby--
2
+ source :rubygems
3
+
4
+ gem"timecode", "~> 1.0"
5
+ gem "progressbar", "~> 0.9"
6
+ gem "sequencer", "~> 1.0"
7
+ gem "term-ansicolor"
8
+
9
+ group :development do
10
+ gem "jeweler"
11
+ gem "rake"
12
+ gem "cli_test", "~>1.0"
13
+ end
@@ -1,3 +1,17 @@
1
+ === 3.0.0 / 2011-11-29
2
+
3
+ * Use jeweler for gem builds
4
+ * depix-describe renamed with a dash. Sorry folks.
5
+ * Adds depix_fix_headers to fix wonky headers with, for instance, improper null-termination
6
+ * Multiple bug fixes
7
+ * Padded reserved fields not shown anymore
8
+ * More fields that have invalid values resolve to nil now
9
+ * fixed that Synthetics#aspect would return pixel aspect and not image aspect
10
+
11
+ === 2.0.1 / 2011-11-09
12
+
13
+ * Fix the errors in depix-describe
14
+
1
15
  === 2.0.0 / 2011-06-14
2
16
 
3
17
  * Depix::Binary is now public, and it's a nice library. Check it out.
@@ -27,17 +27,29 @@ have the same name as the specified fields.
27
27
 
28
28
  The gem also contains an executable called depix-desribe which can be used from the command line
29
29
 
30
- $book depix-describe 001_PTAPE_001.001.dpx
30
+ $depix_describe 001_PTAPE_001.001.dpx
31
31
 
32
32
  for a long description or
33
33
 
34
- $book depix-describe -s 001_PTAPE_001.001.dpx
34
+ $depix_describe -s 001_PTAPE_001.001.dpx
35
+
36
+ for a short description.
37
+
38
+ If you have a file that does not import into some application you could run "fix headers" on it to comb
39
+ out invalid data (or data some systems do not approve of). To do so, run depix_fix_headers. Note that the files
40
+ will be modified in-place
41
+
42
+ $depix_fix_headers 001_PTAPE_001.001.dpx
43
+
44
+ or for a whole sequence - just supply the -s flag and pass one file
45
+
46
+ $depix_fix_headers -s 001_PTAPE_001.001.dpx
35
47
 
36
- for a short description
37
48
 
38
49
  == NOTES:
39
50
 
40
51
  Autodesk IFFS systems write the reel name for the file to the orientation.device field, some scanners write it into user data.
52
+ Currently unpacking slots which contain invalid reals and ints will yield the maximum possible value for the type
41
53
 
42
54
  == REQUIREMENTS:
43
55
 
data/Rakefile CHANGED
@@ -1,20 +1,34 @@
1
1
  require 'rubygems'
2
- require 'hoe'
3
2
  require './lib/depix'
3
+ require 'jeweler'
4
4
 
5
- Hoe.spec('depix') do |p|
6
- p.version = Depix::VERSION
7
- p.developer('Julik Tarkhanov', 'me@julik.nl')
8
- p.rubyforge_name = 'guerilla-di'
9
- p.extra_deps << ['timecode']
10
- p.remote_rdoc_dir = 'depix'
11
- p.readme_file = 'README.rdoc'
12
- p.extra_rdoc_files = FileList['*.rdoc']
13
-
14
- p.clean_globs = %w( **/.DS_Store )
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.version = Depix::VERSION
7
+ gem.name = "depix"
8
+ gem.summary = "Read and write DPX file headers"
9
+ gem.description = "Allos you to edit headers and read their contents parsed into Ruby objects"
10
+ gem.email = "me@julik.nl"
11
+ gem.homepage = "http://guerilla-di.org/depix"
12
+ gem.authors = ["Julik Tarkhanov"]
13
+ gem.extra_rdoc_files << "DEVELOPER_DOCS.rdoc"
14
+ gem.license = 'MIT'
15
+ gem.executables = ["depix_describe", "depix_fix_headers"]
16
+ gem.extra_rdoc_files = FileList['*.rdoc']
15
17
  end
16
18
 
17
- task :describe_structs do
18
- require File.dirname(__FILE__) + '/lib/depix/struct_explainer'
19
- File.open('DPX_HEADER_STRUCTURE.rdoc', 'w') {|f| f << RdocExplainer.new.get_rdoc_for(Depix::DPX) }
19
+ Jeweler::RubygemsDotOrgTasks.new
20
+
21
+ require 'rake/testtask'
22
+ desc "Run all tests"
23
+ Rake::TestTask.new("test") do |t|
24
+ t.libs << "test"
25
+ t.pattern = 'test/**/test_*.rb'
26
+ t.verbose = true
27
+ end
28
+
29
+ task :default => [ :test ]
30
+
31
+ task :document_structs do
32
+ require './lib/depix/binary/descriptor'
33
+ File.open('DPX_HEADER_STRUCTURE.rdoc', 'w') {|f| f << Depix::Binary::RdocGenerator.new.get_rdoc_for(Depix::DPX) }
20
34
  end
@@ -5,7 +5,7 @@ require 'optparse'
5
5
 
6
6
  options = {}
7
7
  OptionParser.new do |opts|
8
- opts.banner = "Usage: depix-describe somefile.dpx anotherfile.dpx [options]"
8
+ opts.banner = "Usage: depix_describe somefile.dpx anotherfile.dpx [options]"
9
9
 
10
10
  opts.on("-c", "--compact", "Compact output (only fields that change per frame)") do |v|
11
11
  options[:compact] = true
@@ -18,15 +18,16 @@ OptionParser.new do |opts|
18
18
  end.parse!
19
19
 
20
20
  ARGV.each do | file |
21
+ puts "\n"
21
22
  puts "Describing DPX #{file}. Empty elements are omitted."
22
23
  puts "===================================================\n"
23
24
  begin
24
25
  if options[:synthetics]
25
- puts Depix.describe_brief(file)
26
+ puts Depix::Describe.new.describe_brief(file)
26
27
  elsif options[:compact]
27
- puts Depix.describe_file(file, true)
28
+ puts Depix::Describe.new.describe(file, true)
28
29
  else
29
- puts Depix.describe_file(file)
30
+ puts Depix::Describe.new.describe(file)
30
31
  end
31
32
  rescue Depix::InvalidHeader
32
33
  puts " - Invalid header data"
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/depix'
4
+ require "rubygems"
5
+ require 'optparse'
6
+ require "progressbar"
7
+ require "sequencer"
8
+
9
+ options = {}
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: depix_fix_headers somefile.dpx anotherfile.dpx" + "\n" +
12
+ "For sequences: depix_fix_headers -s one_file_from_the_sequence.dpx"
13
+
14
+ opts.on("-s", "--sequence", "Detect a sequence instead (pass one file from the sequence)") do |v|
15
+ $sequence = true
16
+ end
17
+ end.parse!
18
+
19
+ files = if $sequence
20
+ paths = []
21
+ Sequencer.from_single_file(ARGV.shift).each_path do | p |
22
+ paths.push(p)
23
+ end
24
+ paths
25
+ else
26
+ ARGV
27
+ end
28
+
29
+ raise "No files provided" unless files.any?
30
+
31
+ header = "Fixing headers in %d files" % files.length
32
+ pbar = ProgressBar.new(header, files.length)
33
+ pbar.format = "%-#{header.length}s %3d%% %s %s"
34
+
35
+ files.each do | file |
36
+ pbar.inc
37
+ Depix::Editor.new(file).commit!
38
+ end
39
+ puts ""
@@ -0,0 +1,93 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "depix"
8
+ s.version = "3.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Julik Tarkhanov"]
12
+ s.date = "2011-11-29"
13
+ s.description = "Allos you to edit headers and read their contents parsed into Ruby objects"
14
+ s.email = "me@julik.nl"
15
+ s.executables = ["depix_describe", "depix_fix_headers"]
16
+ s.extra_rdoc_files = [
17
+ "DPX_HEADER_STRUCTURE.rdoc",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".gemtest",
22
+ ".travis.yml",
23
+ "DPX_HEADER_STRUCTURE.rdoc",
24
+ "Gemfile",
25
+ "History.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "bin/depix_describe",
29
+ "bin/depix_fix_headers",
30
+ "depix.gemspec",
31
+ "lib/depix.rb",
32
+ "lib/depix/binary/fields.rb",
33
+ "lib/depix/binary/rdoc_generator.rb",
34
+ "lib/depix/binary/structure.rb",
35
+ "lib/depix/compact_structs.rb",
36
+ "lib/depix/describe.rb",
37
+ "lib/depix/editor.rb",
38
+ "lib/depix/enums.rb",
39
+ "lib/depix/reader.rb",
40
+ "lib/depix/structs.rb",
41
+ "lib/depix/synthetics.rb",
42
+ "test/samples/026_FROM_HERO_TAPE_5-3-1_MOV.0029.dpx",
43
+ "test/samples/E012_P001_L000002_lin.0001.dpx",
44
+ "test/samples/E012_P001_L000002_lin.0002.dpx",
45
+ "test/samples/E012_P001_L000002_log.0001.dpx",
46
+ "test/samples/E012_P001_L000002_log.0002.dpx",
47
+ "test/samples/from_nuke_no_TC_meta.dpx",
48
+ "test/samples/gluetools_file_header.dpx",
49
+ "test/samples/little_endian.dpx",
50
+ "test/samples/northlight_tc_mode_mismatch.dpx",
51
+ "test/samples/scratch.dpx",
52
+ "test/test_binary.rb",
53
+ "test/test_depix.rb",
54
+ "test/test_describe.rb",
55
+ "test/test_fix_headers.rb"
56
+ ]
57
+ s.homepage = "http://guerilla-di.org/depix"
58
+ s.licenses = ["MIT"]
59
+ s.require_paths = ["lib"]
60
+ s.rubygems_version = "1.8.11"
61
+ s.summary = "Read and write DPX file headers"
62
+
63
+ if s.respond_to? :specification_version then
64
+ s.specification_version = 3
65
+
66
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
67
+ s.add_runtime_dependency(%q<timecode>, ["~> 1.0"])
68
+ s.add_runtime_dependency(%q<progressbar>, ["~> 0.9"])
69
+ s.add_runtime_dependency(%q<sequencer>, ["~> 1.0"])
70
+ s.add_runtime_dependency(%q<term-ansicolor>, [">= 0"])
71
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
72
+ s.add_development_dependency(%q<rake>, [">= 0"])
73
+ s.add_development_dependency(%q<cli_test>, ["~> 1.0"])
74
+ else
75
+ s.add_dependency(%q<timecode>, ["~> 1.0"])
76
+ s.add_dependency(%q<progressbar>, ["~> 0.9"])
77
+ s.add_dependency(%q<sequencer>, ["~> 1.0"])
78
+ s.add_dependency(%q<term-ansicolor>, [">= 0"])
79
+ s.add_dependency(%q<jeweler>, [">= 0"])
80
+ s.add_dependency(%q<rake>, [">= 0"])
81
+ s.add_dependency(%q<cli_test>, ["~> 1.0"])
82
+ end
83
+ else
84
+ s.add_dependency(%q<timecode>, ["~> 1.0"])
85
+ s.add_dependency(%q<progressbar>, ["~> 0.9"])
86
+ s.add_dependency(%q<sequencer>, ["~> 1.0"])
87
+ s.add_dependency(%q<term-ansicolor>, [">= 0"])
88
+ s.add_dependency(%q<jeweler>, [">= 0"])
89
+ s.add_dependency(%q<rake>, [">= 0"])
90
+ s.add_dependency(%q<cli_test>, ["~> 1.0"])
91
+ end
92
+ end
93
+
@@ -12,9 +12,10 @@ require File.expand_path(File.dirname(__FILE__)) + '/depix/synthetics'
12
12
  require File.expand_path(File.dirname(__FILE__)) + '/depix/reader'
13
13
  require File.expand_path(File.dirname(__FILE__)) + '/depix/editor'
14
14
 
15
+ require File.expand_path(File.dirname(__FILE__)) + '/depix/describe'
15
16
 
16
17
  module Depix
17
- VERSION = '2.0.0'
18
+ VERSION = '3.0.0'
18
19
 
19
20
  class InvalidHeader < RuntimeError; end
20
21
 
@@ -31,15 +32,4 @@ module Depix
31
32
  def self.from_string(string, compact = false)
32
33
  Reader.new.parse(string, compact)
33
34
  end
34
-
35
- # Retrurn a formatted description of the DPX file at path. Empty values are omitted.
36
- def self.describe_file(path, compact = false)
37
- Reader.new.describe_file(path, compact)
38
- end
39
-
40
- # Return a formatted description of the DPX file at path, showing only synthetic attributes
41
- def self.describe_brief(path)
42
- Reader.new.describe_synthetics_of_struct(from_file(path))
43
- end
44
-
45
35
  end
@@ -1,15 +1,15 @@
1
- module Depix
2
- module Binary
3
- module Fields
1
+ # coding: ASCII-8BIT
2
+ module Depix; module Binary; module Fields
4
3
 
5
4
  # Base class for a padded field in a struct
6
5
  class Field
7
- attr_accessor :name, # Field name
8
- :length, # Field length in bytes, including any possible padding
9
- :pattern, # The unpack pattern that defines the field
10
- :req, # Is the field required?
11
- :desc, # Field description
12
- :rtype # To which Ruby type this has to be cast (and which type is accepted as value)
6
+ attr_accessor :name # Field name
7
+ attr_accessor :length # Field length in bytes, including any possible padding
8
+ attr_accessor :pattern # The unpack pattern that defines the field
9
+ attr_accessor :req # Is the field required?
10
+ attr_accessor :desc # Field description
11
+ attr_accessor :rtype # To which Ruby type this has to be cast (and which type is accepted as value)
12
+
13
13
  alias_method :req?, :req
14
14
 
15
15
  # Hash init
@@ -53,6 +53,11 @@ module Depix
53
53
  [value].pack(pattern)
54
54
  end
55
55
  end
56
+
57
+ private
58
+ def blanking?(blob)
59
+ blob.nil? || blob.empty? || blob == (0xFF.chr * length)
60
+ end
56
61
  end
57
62
 
58
63
  # unit32 field
@@ -84,7 +89,7 @@ module Depix
84
89
  # uint8 field
85
90
  class U8Field < Field
86
91
  undef :length=, :pattern=
87
-
92
+
88
93
  BLANK = 0xFF
89
94
 
90
95
  def pattern
@@ -144,26 +149,36 @@ module Depix
144
149
  end
145
150
 
146
151
  def clean(v)
147
- v == BLANK ? nil : v
152
+ (v == BLANK || v == -1) ? nil : v
148
153
  end
149
154
 
150
155
  def validate!(value)
151
156
  super(value)
152
157
  raise "#{name} value #{value} out of bounds for 16bit unsigned int" if (value < 0 || value >= BLANK)
153
158
  end
159
+
154
160
  end
155
161
 
156
- # real32 field
162
+ # real32 field. Now, there is some lap dancing to be done here.
163
+ # The DPX files in the wild define nonexistant data as a charfield with all
164
+ # it's bits set (four times 0xFF byte). This is easy to verify with a string
165
+ # but practically useless once the charfield has been unpacked into a float.
166
+ # Therefore we first unpack the value as a charfield, then we check whether it's all
167
+ # blanking, and if it is we return nil. If it's not "all bits set" though what we will do
168
+ # is try to decode it again using the real float unpack pattern. The same dance
169
+ # happens reciprocally when repacking the data.
157
170
  class R32Field < Field
158
171
  undef :length=, :pattern=
159
- BLANK = 0xFFFFFFFF
172
+ BLANK = (0xFF.chr * 4)
173
+ PATTERN_BE = "g"
174
+ PATTERN_LE = "n"
160
175
 
161
176
  def pattern
162
177
  "g"
163
178
  end
164
179
 
165
180
  def clean(v)
166
- v.nan? ? nil : v
181
+ (v.nil? || v.nan?) ? nil : v
167
182
  end
168
183
 
169
184
  def length
@@ -173,34 +188,35 @@ module Depix
173
188
  def rtype
174
189
  Float
175
190
  end
191
+
192
+ # The packing of NaN
193
+ # [12] pry(main)> value = 0 / 0.0
194
+ # => NaN
195
+ # [13] pry(main)> [value].pack("g")
196
+ # => "\xFF\xC0\x00\x00"
197
+ # [14] pry(main)> [value].pack("g")
198
+ def pack(value)
199
+ (value.nil? || value.nan? ) ? BLANK : [value].pack("g")
200
+ end
176
201
  end
177
202
 
178
203
  # null-terminated string field with fixed padding
179
204
  class CharField < Field
180
- BLANK = "\0"
181
205
  undef :pattern=
182
206
 
183
- BLANKING_VALUES = [0x00.chr, 0xFF.chr]
184
- BLANKING_PATTERNS = BLANKING_VALUES.inject([]) do | p, char |
185
- p << /^([#{char}]+)/ << /([#{char}]+)$/mu
186
- end
187
-
188
207
  def initialize(opts = {})
189
208
  super({:length => 1}.merge(opts))
190
209
  end
191
210
 
192
211
  def pattern
193
- "A#{(length || 1).to_i}"
212
+ "Z#{length}"
194
213
  end
195
214
 
196
215
  def clean(v)
197
- if v == BLANK
198
- nil
199
- else
200
- # Truncate everything from the null byte up
201
- upto_nulb = v.split(0x00.chr).shift
202
- (upto_nulb.nil? || upto_nulb.empty?) ? nil : upto_nulb
203
- end
216
+ # Use the pack->unpack trick to remove null-termination
217
+ v = pack(v.to_s).unpack(pattern)[0]
218
+ # Blanked fields are 0xFF all the way
219
+ blanking?(v) ? nil : v
204
220
  end
205
221
 
206
222
  def rtype
@@ -213,7 +229,35 @@ module Depix
213
229
  end
214
230
 
215
231
  def pack(value)
216
- value.ljust(length, "\000") rescue ("\000" * length)
232
+ unless blanking?(value)
233
+ [value].pack(pattern)
234
+ else
235
+ 0xFF.chr * length
236
+ end
237
+ end
238
+ end
239
+
240
+ # For reserved fields and blanking
241
+ class BlankingField < CharField
242
+
243
+ def validate!(value)
244
+ raise "The value of this field should be nil" unless value.nil?
245
+ end
246
+
247
+ def pattern
248
+ "Z#{length}"
249
+ end
250
+
251
+ def clean(v)
252
+ nil
253
+ end
254
+
255
+ def rtype
256
+ NilClass
257
+ end
258
+
259
+ def pack(value)
260
+ 0xFF.chr * length
217
261
  end
218
262
  end
219
263
 
@@ -297,6 +341,4 @@ module Depix
297
341
  cast.pack(value)
298
342
  end
299
343
  end
300
- end
301
- end
302
- end
344
+ end; end; end