aviglitch 0.1.6 → 0.2.0

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.
@@ -7,33 +7,29 @@ module AviGlitch
7
7
 
8
8
  # AviGlitch::Frames object generated from the +file+.
9
9
  attr_reader :frames
10
- # The input file (copied tempfile).
11
- attr_reader :file
10
+ # The input file
11
+ attr_reader :avi
12
12
 
13
13
  ##
14
14
  # Creates a new instance of AviGlitch::Base, open the file and
15
15
  # make it ready to manipulate.
16
- # It requires +path+ as Pathname.
17
- def initialize path
18
- File.open(path, 'rb') do |f|
19
- # copy as tempfile
20
- @file = Tempfile.new 'aviglitch', binmode: true
21
- f.rewind
22
- while d = f.read(BUFFER_SIZE) do
23
- @file.print d
16
+ # It requires +path+ as Pathname or an instance of AviGlirtch::Avi.
17
+ def initialize path_or_object
18
+ if path_or_object.kind_of?(Avi)
19
+ @avi = path_or_object
20
+ else
21
+ unless AviGlitch::Base.surely_formatted? path_or_object
22
+ raise 'Unsupported file passed.'
24
23
  end
24
+ @avi = Avi.new path_or_object
25
25
  end
26
-
27
- unless AviGlitch::Base.surely_formatted? @file
28
- raise 'Unsupported file passed.'
29
- end
30
- @frames = Frames.new @file
26
+ @frames = Frames.new @avi
31
27
  end
32
28
 
33
29
  ##
34
30
  # Outputs the glitched file to +path+, and close the file.
35
31
  def output path, do_file_close = true
36
- FileUtils.cp @file.path, path
32
+ @avi.output path
37
33
  close if do_file_close
38
34
  self
39
35
  end
@@ -41,7 +37,7 @@ module AviGlitch
41
37
  ##
42
38
  # An explicit file close.
43
39
  def close
44
- @file.close!
40
+ @avi.close
45
41
  end
46
42
 
47
43
  ##
@@ -62,7 +58,7 @@ module AviGlitch
62
58
  def glitch target = :all, &block # :yield: data
63
59
  if block_given?
64
60
  @frames.each do |frame|
65
- if valid_target? target, frame
61
+ if frame.is? target
66
62
  frame.data = yield frame.data
67
63
  end
68
64
  end
@@ -127,55 +123,29 @@ module AviGlitch
127
123
  alias_method :write, :output
128
124
  alias_method :has_keyframes?, :has_keyframe?
129
125
 
130
- def valid_target? target, frame #:nodoc:
131
- return true if target == :all
132
- begin
133
- frame.send "is_#{target.to_s.sub(/frames$/, 'frame')}?"
134
- rescue
135
- false
136
- end
137
- end
138
-
139
- private :valid_target?
140
-
141
126
  class << self
142
127
  ##
143
128
  # Checks if the +file+ is a correctly formetted AVI file.
144
129
  # +file+ can be String or Pathname or IO.
145
130
  def surely_formatted? file, debug = false
146
- answer = true
147
- is_io = file.respond_to?(:seek) # Probably IO.
148
- file = File.open(file, 'rb') unless is_io
131
+ passed = true
149
132
  begin
150
- file.rewind
151
- unless file.read(4) == 'RIFF'
152
- answer = false
153
- warn 'RIFF sign is not found' if debug
154
- end
155
- len = file.read(4).unpack('V').first
156
- unless file.read(4) == 'AVI '
157
- answer = false
158
- warn 'AVI sign is not found' if debug
159
- end
160
- while file.read(4) =~ /^(?:LIST|JUNK)$/ do
161
- s = file.read(4).unpack('V').first
162
- file.pos += s
163
- end
164
- file.pos -= 4
165
- # we require idx1
166
- unless file.read(4) == 'idx1'
167
- answer = false
168
- warn 'idx1 is not found' if debug
133
+ riff = Avi.rifftree file
134
+ {
135
+ 'RIFF-AVI sign': /^RIFF \(\d+\) ’AVI ’$/,
136
+ 'movi': /^\s+LIST \(\d+\) ’movi’$/,
137
+ 'idx1': /^\s+idx1 \(\d+\)$/
138
+ }.each do |m, r|
139
+ unless riff =~ r
140
+ warn "#{m} is not found." if debug
141
+ passed = false
142
+ end
169
143
  end
170
- s = file.read(4).unpack('V').first
171
- file.pos += s
172
- rescue => err
173
- warn err.message if debug
174
- answer = false
175
- ensure
176
- file.close unless is_io
144
+ rescue => e
145
+ warn e.message if debug
146
+ passed = false
177
147
  end
178
- answer
148
+ passed
179
149
  end
180
150
  end
181
151
  end
@@ -58,6 +58,26 @@ module AviGlitch
58
58
  @id[2, 2] == 'wb'
59
59
  end
60
60
 
61
+ ##
62
+ # Returns if it is a frame in +frame_type+.
63
+ def is? frame_type
64
+ return true if frame_type == :all
65
+ detection = "is_#{frame_type.to_s.sub(/frames$/, 'frame')}?"
66
+ begin
67
+ self.send detection
68
+ rescue NoMethodError => e
69
+ false
70
+ end
71
+ end
72
+
73
+ ##
74
+ # Compares its content.
75
+ def == other
76
+ self.id == other.id &&
77
+ self.flag == other.flag &&
78
+ self.data == other.data
79
+ end
80
+
61
81
  end
62
82
  end
63
83
 
@@ -18,45 +18,12 @@ module AviGlitch
18
18
  class Frames
19
19
  include Enumerable
20
20
 
21
- # :stopdoc:
22
-
23
- ##
24
- SAFE_FRAMES_COUNT = 150000
25
- @@warn_if_frames_are_too_large = true
26
-
27
- # :startdoc:
28
-
29
- attr_reader :meta
21
+ attr_reader :avi
30
22
 
31
23
  ##
32
24
  # Creates a new AviGlitch::Frames object.
33
- def initialize io
34
- io.rewind
35
- io.pos = 12 # /^RIFF[\s\S]{4}AVI $/
36
- while io.read(4) =~ /^(?:LIST|JUNK)$/ do
37
- s = io.read(4).unpack('V').first
38
- @pos_of_movi = io.pos - 4 if io.read(4) == 'movi'
39
- io.pos += s - 4
40
- end
41
- @pos_of_idx1 = io.pos - 4 # here must be idx1
42
- s = io.read(4).unpack('V').first + io.pos
43
- @meta = []
44
- while chunk_id = io.read(4) do
45
- break if io.pos >= s
46
- @meta << {
47
- :id => chunk_id,
48
- :flag => io.read(4).unpack('V').first,
49
- :offset => io.read(4).unpack('V').first,
50
- :size => io.read(4).unpack('V').first,
51
- }
52
- end
53
- fix_offsets_if_needed io
54
- unless safe_frames_count? @meta.size
55
- io.close!
56
- exit
57
- end
58
- io.rewind
59
- @io = io
25
+ def initialize avi
26
+ @avi = avi
60
27
  end
61
28
 
62
29
  ##
@@ -64,10 +31,29 @@ module AviGlitch
64
31
  # It returns Enumerator if a block is not given.
65
32
  def each &block
66
33
  if block_given?
67
- temp = Tempfile.new 'frames', binmode: true
68
- frames_data_as_io(temp, block)
69
- overwrite temp
70
- temp.close!
34
+ Tempfile.open('temp', binmode: true) do |newmovi|
35
+ @avi.process_movi do |indices, movi|
36
+ newindices = indices.select do |m|
37
+ movi.pos = m[:offset] + 8 # 8 for id and size
38
+ frame = Frame.new(movi.read(m[:size]), m[:id], m[:flag])
39
+ block.call frame
40
+ unless frame.data.nil?
41
+ m[:offset] = newmovi.pos
42
+ m[:size] = frame.data.size
43
+ m[:flag] = frame.flag
44
+ m[:id] = frame.id
45
+ newmovi.print m[:id]
46
+ newmovi.print [frame.data.size].pack('V')
47
+ newmovi.print frame.data
48
+ newmovi.print "\0" if frame.data.size % 2 == 1
49
+ true
50
+ else
51
+ false
52
+ end
53
+ end
54
+ [newindices, newmovi]
55
+ end
56
+ end
71
57
  else
72
58
  self.enum_for :each
73
59
  end
@@ -76,84 +62,34 @@ module AviGlitch
76
62
  ##
77
63
  # Returns the number of frames.
78
64
  def size
79
- @meta.size
65
+ @avi.indices.size
80
66
  end
81
67
 
82
68
  ##
83
69
  # Returns the number of the specific +frame_type+.
84
70
  def size_of frame_type
85
- detection = "is_#{frame_type.to_s.sub(/frames$/, 'frame')}?"
86
- @meta.select { |m|
87
- Frame.new(nil, m[:id], m[:flag]).send detection
71
+ @avi.indices.select { |m|
72
+ Frame.new(nil, m[:id], m[:flag]).is? frame_type
88
73
  }.size
89
74
  end
90
75
 
91
- def frames_data_as_io io = nil, block = nil #:nodoc:
92
- io = Tempfile.new('tmep', binmode: true) if io.nil?
93
- @meta = @meta.select do |m|
94
- @io.pos = @pos_of_movi + m[:offset] + 8 # 8 for id and size
95
- frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag])
96
- block.call(frame) if block # accept the variable block as Proc
97
- yield frame if block_given? # or a given block (or do nothing)
98
- unless frame.data.nil?
99
- m[:offset] = io.pos + 4 # 4 for 'movi'
100
- m[:size] = frame.data.size
101
- m[:flag] = frame.flag
102
- m[:id] = frame.id
103
- io.print m[:id]
104
- io.print [frame.data.size].pack('V')
105
- io.print frame.data
106
- io.print "\000" if frame.data.size % 2 == 1
107
- true
108
- else
109
- false
110
- end
111
- end
112
- io
113
- end
114
-
115
- def overwrite data #:nodoc:
116
- unless safe_frames_count? @meta.size
117
- @io.close!
118
- exit
119
- end
120
- # Overwrite the file
121
- @io.pos = @pos_of_movi - 4 # 4 for size
122
- @io.print [data.pos + 4].pack('V') # 4 for 'movi'
123
- @io.print 'movi'
124
- data.rewind
125
- while d = data.read(BUFFER_SIZE) do
126
- @io.print d
127
- end
128
- @io.print 'idx1'
129
- @io.print [@meta.size * 16].pack('V')
130
- idx = @meta.collect { |m|
131
- m[:id] + [m[:flag], m[:offset], m[:size]].pack('V3')
132
- }.join
133
- @io.print idx
134
- eof = @io.pos
135
- @io.truncate eof
136
-
137
- # Fix info
138
- ## file size
139
- @io.pos = 4
140
- @io.print [eof - 8].pack('V')
141
- ## frame count
142
- @io.pos = 48
143
- vid_frames = @meta.select do |m|
144
- id = m[:id]
145
- id[2, 2] == 'db' || id[2, 2] == 'dc'
76
+ ##
77
+ # Returns the data size of total frames.
78
+ def data_size
79
+ size = 0
80
+ @avi.process_movi do |indices, movi|
81
+ size = movi.size
82
+ [indices, movi]
146
83
  end
147
- @io.print [vid_frames.size].pack('V')
148
-
149
- @io.pos
84
+ size
150
85
  end
151
86
 
152
87
  ##
153
88
  # Removes all frames and returns self.
154
89
  def clear
155
- @meta = []
156
- overwrite StringIO.new
90
+ @avi.process_movi do |indices, movi|
91
+ [[], StringIO.new]
92
+ end
157
93
  self
158
94
  end
159
95
 
@@ -162,27 +98,24 @@ module AviGlitch
162
98
  # It is destructive like Array does.
163
99
  def concat other_frames
164
100
  raise TypeError unless other_frames.kind_of?(Frames)
165
- # data
166
- this_data = Tempfile.new 'this', binmode: true
167
- self.frames_data_as_io this_data
168
- other_data = Tempfile.new 'other', binmode: true
169
- other_frames.frames_data_as_io other_data
170
- this_size = this_data.size
171
- other_data.rewind
172
- while d = other_data.read(BUFFER_SIZE) do
173
- this_data.print d
174
- end
175
- other_data.close!
176
- # meta
177
- other_meta = other_frames.meta.collect do |m|
178
- x = m.dup
179
- x[:offset] += this_size
180
- x
101
+ @avi.process_movi do |this_indices, this_movi|
102
+ this_size = this_movi.size
103
+ this_movi.pos = this_size
104
+ other_frames.avi.process_movi do |other_indices, other_movi|
105
+ while d = other_movi.read(BUFFER_SIZE) do
106
+ this_movi.print d
107
+ end
108
+ other_meta = other_indices.collect do |m|
109
+ x = m.dup
110
+ x[:offset] += this_size
111
+ x
112
+ end
113
+ this_indices.concat other_meta
114
+ [other_indices, other_movi]
115
+ end
116
+ [this_indices, this_movi]
181
117
  end
182
- @meta.concat other_meta
183
- # close
184
- overwrite this_data
185
- this_data.close!
118
+
186
119
  self
187
120
  end
188
121
 
@@ -271,11 +204,16 @@ module AviGlitch
271
204
  ##
272
205
  # Returns one Frame object at the given index.
273
206
  def at n
274
- m = @meta[n]
275
- return nil if m.nil?
276
- @io.pos = @pos_of_movi + m[:offset] + 8
277
- frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag])
278
- @io.rewind
207
+ frame = nil
208
+ @avi.process_movi do |indices, movi|
209
+ m = indices[n]
210
+ unless m.nil?
211
+ movi.pos = m[:offset] + 8
212
+ frame = Frame.new(movi.read(m[:size]), m[:id], m[:flag])
213
+ movi.rewind
214
+ end
215
+ [indices, movi]
216
+ end
279
217
  frame
280
218
  end
281
219
 
@@ -291,28 +229,101 @@ module AviGlitch
291
229
  self.slice(self.size - 1)
292
230
  end
293
231
 
232
+ ##
233
+ # Returns the first Frame object in +frame_type+.
234
+ def first_of frame_type
235
+ frame = nil
236
+ @avi.process_movi do |indices, movi|
237
+ indices.each do |m|
238
+ movi.pos = m[:offset] + 8
239
+ f = Frame.new(movi.read(m[:size]), m[:id], m[:flag])
240
+ if f.is?(frame_type)
241
+ frame = f
242
+ break
243
+ end
244
+ end
245
+ [indices, movi]
246
+ end
247
+ frame
248
+ end
249
+
250
+ ##
251
+ # Returns the last Frame object in +frame_type+.
252
+ def last_of frame_type
253
+ frame = nil
254
+ @avi.process_movi do |indices, movi|
255
+ indices.reverse.each do |m|
256
+ movi.pos = m[:offset] + 8
257
+ f = Frame.new(movi.read(m[:size]), m[:id], m[:flag])
258
+ if f.is?(frame_type)
259
+ frame = f
260
+ break
261
+ end
262
+ end
263
+ [indices, movi]
264
+ end
265
+ frame
266
+ end
267
+
268
+ ##
269
+ # Returns an index of the first found +frame+.
270
+ def index frame
271
+ n = -1
272
+ @avi.process_movi do |indices, movi|
273
+ indices.each_with_index do |m, i|
274
+ movi.pos = m[:offset] + 8
275
+ f = Frame.new(movi.read(m[:size]), m[:id], m[:flag])
276
+ if f == frame
277
+ n = i
278
+ break
279
+ end
280
+ end
281
+ [indices, movi]
282
+ end
283
+ n
284
+ end
285
+
286
+ ##
287
+ # Alias for index
288
+ alias_method :find_index, :index
289
+
290
+ ##
291
+ # Returns an index of the first found +frame+, starting from the last.
292
+ def rindex frame
293
+ n = -1
294
+ @avi.process_movi do |indices, movi|
295
+ indices.reverse.each_with_index do |m, i|
296
+ movi.pos = m[:offset] + 8
297
+ f = Frame.new(movi.read(m[:size]), m[:id], m[:flag])
298
+ if f == frame
299
+ n = indices.size - 1 - i
300
+ break
301
+ end
302
+ end
303
+ [indices, movi]
304
+ end
305
+ n
306
+ end
307
+
294
308
  ##
295
309
  # Appends the given Frame into the tail of self.
296
310
  def push frame
297
311
  raise TypeError unless frame.kind_of? Frame
298
- # data
299
- this_data = Tempfile.new 'this', binmode: true
300
- self.frames_data_as_io this_data
301
- this_size = this_data.size
302
- this_data.print frame.id
303
- this_data.print [frame.data.size].pack('V')
304
- this_data.print frame.data
305
- this_data.print "\000" if frame.data.size % 2 == 1
306
- # meta
307
- @meta << {
308
- :id => frame.id,
309
- :flag => frame.flag,
310
- :offset => this_size + 4, # 4 for 'movi'
311
- :size => frame.data.size,
312
- }
313
- # close
314
- overwrite this_data
315
- this_data.close!
312
+ @avi.process_movi do |indices, movi|
313
+ this_size = movi.size
314
+ movi.pos = this_size
315
+ movi.print frame.id
316
+ movi.print [frame.data.size].pack('V')
317
+ movi.print frame.data
318
+ movi.print "\0" if frame.data.size % 2 == 1
319
+ indices << {
320
+ :id => frame.id,
321
+ :flag => frame.flag,
322
+ :offset => this_size,
323
+ :size => frame.data.size,
324
+ }
325
+ [indices, movi]
326
+ end
316
327
  self
317
328
  end
318
329
 
@@ -355,17 +366,17 @@ module AviGlitch
355
366
  ##
356
367
  # Returns true if +other+'s frames are same as self's frames.
357
368
  def == other
358
- @meta == other.meta
369
+ @avi == other.avi
359
370
  end
360
371
 
361
372
  ##
362
373
  # Generates new AviGlitch::Base instance using self.
363
374
  def to_avi
364
- AviGlitch.open @io.path
375
+ AviGlitch::Base.new @avi.clone
365
376
  end
366
377
 
367
- def inspect # :nodec:
368
- "#<#{self.class.name}:#{sprintf("0x%x", object_id)} @io=#{@io.inspect} size=#{self.size}>"
378
+ def inspect #:nodoc:
379
+ "#<#{self.class.name}:#{sprintf("0x%x", object_id)} size=#{self.size}>"
369
380
  end
370
381
 
371
382
  def get_beginning_and_length *args #:nodoc:
@@ -373,46 +384,18 @@ module AviGlitch
373
384
  if args.first.kind_of? Range
374
385
  r = args.first
375
386
  b = r.begin
376
- e = r.end >= 0 ? r.end : @meta.size + r.end
387
+ e = r.end >= 0 ? r.end : self.size + r.end
377
388
  l = e - b + 1
378
389
  end
379
- b = b >= 0 ? b : @meta.size + b
390
+ b = b >= 0 ? b : self.size + b
380
391
  [b, l]
381
392
  end
382
393
 
383
394
  def safe_frames_count? count #:nodoc:
384
- r = true
385
- if @@warn_if_frames_are_too_large && count >= SAFE_FRAMES_COUNT
386
- trap(:INT) do
387
- @io.close!
388
- exit
389
- end
390
- m = ["WARNING: The avi data has too many frames (#{count}).\n",
391
- "It may use a large memory to process. ",
392
- "We recommend to chop the movie to smaller chunks before you glitch.\n",
393
- "Do you want to continue anyway? [yN] "].join('')
394
- a = Readline.readline m
395
- r = a == 'y'
396
- @@warn_if_frames_are_too_large = !r
397
- end
398
- r
399
- end
400
-
401
- def fix_offsets_if_needed io #:nodoc:
402
- # rarely data offsets begin from 0 of the file
403
- return if @meta.empty?
404
- pos = io.pos
405
- m = @meta.first
406
- io.pos = @pos_of_movi + m[:offset]
407
- unless io.read(4) == m[:id]
408
- @meta.each do |x|
409
- x[:offset] -= @pos_of_movi
410
- end
411
- end
412
- io.pos = pos
395
+ warn "[DEPRECATION] `safe_frames_count?` is deprecated."
396
+ true
413
397
  end
414
398
 
415
- protected :frames_data_as_io, :meta
416
- private :overwrite, :get_beginning_and_length, :fix_offsets_if_needed
399
+ private :get_beginning_and_length
417
400
  end
418
401
  end