aviglitch 0.1.3 → 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,34 +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.open 'aviglitch'
21
- f.rewind
22
- while d = f.read(1024) 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
31
- # I believe Ruby's GC to close and remove the Tempfile..
26
+ @frames = Frames.new @avi
32
27
  end
33
28
 
34
29
  ##
35
30
  # Outputs the glitched file to +path+, and close the file.
36
31
  def output path, do_file_close = true
37
- FileUtils.cp @file.path, path
32
+ @avi.output path
38
33
  close if do_file_close
39
34
  self
40
35
  end
@@ -42,7 +37,7 @@ module AviGlitch
42
37
  ##
43
38
  # An explicit file close.
44
39
  def close
45
- @file.close!
40
+ @avi.close
46
41
  end
47
42
 
48
43
  ##
@@ -59,34 +54,38 @@ module AviGlitch
59
54
  # It also requires a block. In the block, you take the frame data
60
55
  # as a String parameter.
61
56
  # To modify the data, simply return a modified data.
62
- # It returns +self+
57
+ # Without a block it returns Enumerator, with a block it returns +self+.
63
58
  def glitch target = :all, &block # :yield: data
64
- @frames.each do |frame|
65
- if valid_target? target, frame
66
- frame.data = yield frame.data
59
+ if block_given?
60
+ @frames.each do |frame|
61
+ if frame.is? target
62
+ frame.data = yield frame.data
63
+ end
67
64
  end
65
+ self
66
+ else
67
+ self.enum_for :glitch, target
68
68
  end
69
- self
70
69
  end
71
70
 
72
71
  ##
73
72
  # Do glitch with index.
74
73
  def glitch_with_index target = :all, &block # :yield: data, index
75
- i = 0
76
- @frames.each do |frame|
77
- if valid_target? target, frame
78
- frame.data = yield(frame.data, i)
79
- i += 1
74
+ if block_given?
75
+ self.glitch(target).with_index do |x, i|
76
+ yield x, i
80
77
  end
78
+ self
79
+ else
80
+ self.glitch target
81
81
  end
82
- self
83
82
  end
84
83
 
85
84
  ##
86
- # Clears all (or in +range+) keyframes to deltaframes.
87
- # It's an alias for Frames#clear_keyframes!
88
- def clear_keyframes! range = nil
89
- self.frames.clear_keyframes! range
85
+ # Mutates all (or in +range+) keyframes into deltaframes.
86
+ # It's an alias for Frames#mutate_keyframes_into_deltaframes!
87
+ def mutate_keyframes_into_deltaframes! range = nil
88
+ self.frames.mutate_keyframes_into_deltaframes! range
90
89
  self
91
90
  end
92
91
 
@@ -103,6 +102,16 @@ module AviGlitch
103
102
  result
104
103
  end
105
104
 
105
+ ##
106
+ # Removes all keyframes.
107
+ # It is same as +glitch(:keyframes){|f| nil }+
108
+ def remove_all_keyframes!
109
+ self.glitch :keyframe do |f|
110
+ nil
111
+ end
112
+ self
113
+ end
114
+
106
115
  ##
107
116
  # Swaps the frames with other Frames data.
108
117
  def frames= other
@@ -114,57 +123,29 @@ module AviGlitch
114
123
  alias_method :write, :output
115
124
  alias_method :has_keyframes?, :has_keyframe?
116
125
 
117
- def valid_target? target, frame #:nodoc:
118
- return true if target == :all
119
- begin
120
- frame.send "is_#{target.to_s.sub(/frames$/, 'frame')}?"
121
- rescue
122
- false
123
- end
124
- end
125
-
126
- private :valid_target?
127
-
128
126
  class << self
129
127
  ##
130
128
  # Checks if the +file+ is a correctly formetted AVI file.
131
129
  # +file+ can be String or Pathname or IO.
132
130
  def surely_formatted? file, debug = false
133
- answer = true
134
- is_io = file.respond_to?(:seek) # Probably IO.
135
- file = File.open(file, 'rb') unless is_io
131
+ passed = true
136
132
  begin
137
- file.seek 0, IO::SEEK_END
138
- eof = file.pos
139
- file.rewind
140
- unless file.read(4) == 'RIFF'
141
- answer = false
142
- warn 'RIFF sign is not found' if debug
143
- end
144
- len = file.read(4).unpack('V').first
145
- unless file.read(4) == 'AVI '
146
- answer = false
147
- warn 'AVI sign is not found' if debug
148
- end
149
- while file.read(4) =~ /^(?:LIST|JUNK)$/ do
150
- s = file.read(4).unpack('V').first
151
- file.pos += s
152
- end
153
- file.pos -= 4
154
- # we require idx1
155
- unless file.read(4) == 'idx1'
156
- answer = false
157
- 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
158
143
  end
159
- s = file.read(4).unpack('V').first
160
- file.pos += s
161
- rescue => err
162
- warn err.message if debug
163
- answer = false
164
- ensure
165
- file.close unless is_io
144
+ rescue => e
145
+ warn e.message if debug
146
+ passed = false
166
147
  end
167
- answer
148
+ passed
168
149
  end
169
150
  end
170
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,124 +18,78 @@ module AviGlitch
18
18
  class Frames
19
19
  include Enumerable
20
20
 
21
- SAFE_FRAMES_COUNT = 150000 #:nodoc:
22
- @@warn_if_frames_are_too_large = true #:nodoc:
23
-
24
- attr_reader :meta
21
+ attr_reader :avi
25
22
 
26
23
  ##
27
24
  # Creates a new AviGlitch::Frames object.
28
- def initialize io
29
- io.rewind
30
- io.pos = 12 # /^RIFF[\s\S]{4}AVI $/
31
- while io.read(4) =~ /^(?:LIST|JUNK)$/ do
32
- s = io.read(4).unpack('V').first
33
- @pos_of_movi = io.pos - 4 if io.read(4) == 'movi'
34
- io.pos += s - 4
35
- end
36
- @pos_of_idx1 = io.pos - 4 # here must be idx1
37
- s = io.read(4).unpack('V').first + io.pos
38
- @meta = []
39
- while chunk_id = io.read(4) do
40
- break if io.pos >= s
41
- @meta << {
42
- :id => chunk_id,
43
- :flag => io.read(4).unpack('V').first,
44
- :offset => io.read(4).unpack('V').first,
45
- :size => io.read(4).unpack('V').first,
46
- }
47
- end
48
- fix_offsets_if_needed io
49
- unless safe_frames_count? @meta.size
50
- io.close!
51
- exit
52
- end
53
- io.rewind
54
- @io = io
25
+ def initialize avi
26
+ @avi = avi
55
27
  end
56
28
 
57
29
  ##
58
30
  # Enumerates the frames.
59
- def each
60
- temp = Tempfile.new 'frames'
61
- frames_data_as_io(temp, Proc.new)
62
- overwrite temp
63
- temp.close!
31
+ # It returns Enumerator if a block is not given.
32
+ def each &block
33
+ if block_given?
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
57
+ else
58
+ self.enum_for :each
59
+ end
64
60
  end
65
61
 
66
62
  ##
67
63
  # Returns the number of frames.
68
64
  def size
69
- @meta.size
65
+ @avi.indices.size
70
66
  end
71
67
 
72
- def frames_data_as_io io = nil, block = nil #:nodoc:
73
- io = Tempfile.new('tmep') if io.nil?
74
- @meta = @meta.select do |m|
75
- @io.pos = @pos_of_movi + m[:offset] + 8 # 8 for id and size
76
- frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag])
77
- block.call(frame) if block # accept the variable block as Proc
78
- yield frame if block_given? # or a given block (or do nothing)
79
- unless frame.data.nil?
80
- m[:offset] = io.pos + 4 # 4 for 'movi'
81
- m[:size] = frame.data.size
82
- m[:flag] = frame.flag
83
- m[:id] = frame.id
84
- io.print m[:id]
85
- io.print [frame.data.size].pack('V')
86
- io.print frame.data
87
- io.print "\000" if frame.data.size % 2 == 1
88
- true
89
- else
90
- false
91
- end
92
- end
93
- io
68
+ ##
69
+ # Returns the number of the specific +frame_type+.
70
+ def size_of frame_type
71
+ @avi.indices.select { |m|
72
+ Frame.new(nil, m[:id], m[:flag]).is? frame_type
73
+ }.size
94
74
  end
95
75
 
96
- def overwrite data #:nodoc:
97
- unless safe_frames_count? @meta.size
98
- @io.close!
99
- exit
100
- end
101
- # Overwrite the file
102
- data.seek 0, IO::SEEK_END
103
- @io.pos = @pos_of_movi - 4 # 4 for size
104
- @io.print [data.pos + 4].pack('V') # 4 for 'movi'
105
- @io.print 'movi'
106
- data.rewind
107
- while d = data.read(1024) do
108
- @io.print d
109
- end
110
- @io.print 'idx1'
111
- @io.print [@meta.size * 16].pack('V')
112
- @meta.each do |m|
113
- @io.print m[:id]
114
- @io.print [m[:flag], m[:offset], m[:size]].pack('V3')
115
- end
116
- eof = @io.pos
117
- @io.truncate eof
118
-
119
- # Fix info
120
- ## file size
121
- @io.pos = 4
122
- @io.print [eof - 8].pack('V')
123
- ## frame count
124
- @io.pos = 48
125
- vid_frames = @meta.select do |m|
126
- id = m[:id]
127
- 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]
128
83
  end
129
- @io.print [vid_frames.size].pack('V')
130
-
131
- @io.pos
84
+ size
132
85
  end
133
86
 
134
87
  ##
135
88
  # Removes all frames and returns self.
136
89
  def clear
137
- @meta = []
138
- overwrite StringIO.new
90
+ @avi.process_movi do |indices, movi|
91
+ [[], StringIO.new]
92
+ end
139
93
  self
140
94
  end
141
95
 
@@ -144,28 +98,25 @@ module AviGlitch
144
98
  # It is destructive like Array does.
145
99
  def concat other_frames
146
100
  raise TypeError unless other_frames.kind_of?(Frames)
147
- # data
148
- this_data = Tempfile.new 'this'
149
- self.frames_data_as_io this_data
150
- other_data = Tempfile.new 'other'
151
- other_frames.frames_data_as_io other_data
152
- this_data.seek 0, IO::SEEK_END
153
- this_size = this_data.pos
154
- other_data.rewind
155
- while d = other_data.read(1024) do
156
- this_data.print d
157
- end
158
- other_data.close!
159
- # meta
160
- other_meta = other_frames.meta.collect do |m|
161
- x = m.dup
162
- x[:offset] += this_size
163
- 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]
164
117
  end
165
- @meta.concat other_meta
166
- # close
167
- overwrite this_data
168
- this_data.close!
118
+
119
+ self
169
120
  end
170
121
 
171
122
  ##
@@ -253,11 +204,16 @@ module AviGlitch
253
204
  ##
254
205
  # Returns one Frame object at the given index.
255
206
  def at n
256
- m = @meta[n]
257
- return nil if m.nil?
258
- @io.pos = @pos_of_movi + m[:offset] + 8
259
- frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag])
260
- @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
261
217
  frame
262
218
  end
263
219
 
@@ -273,29 +229,101 @@ module AviGlitch
273
229
  self.slice(self.size - 1)
274
230
  end
275
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
+
276
308
  ##
277
309
  # Appends the given Frame into the tail of self.
278
310
  def push frame
279
311
  raise TypeError unless frame.kind_of? Frame
280
- # data
281
- this_data = Tempfile.new 'this'
282
- self.frames_data_as_io this_data
283
- this_data.seek 0, IO::SEEK_END
284
- this_size = this_data.pos
285
- this_data.print frame.id
286
- this_data.print [frame.data.size].pack('V')
287
- this_data.print frame.data
288
- this_data.print "\000" if frame.data.size % 2 == 1
289
- # meta
290
- @meta << {
291
- :id => frame.id,
292
- :flag => frame.flag,
293
- :offset => this_size + 4, # 4 for 'movi'
294
- :size => frame.data.size,
295
- }
296
- # close
297
- overwrite this_data
298
- 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
299
327
  self
300
328
  end
301
329
 
@@ -324,30 +352,31 @@ module AviGlitch
324
352
  end
325
353
 
326
354
  ##
327
- # Modify keyframes to deltaframes at given range, or all.
328
- def clear_keyframes! range = nil
355
+ # Mutates keyframes into deltaframes at given range, or all.
356
+ def mutate_keyframes_into_deltaframes! range = nil
329
357
  range = 0..self.size if range.nil?
330
358
  self.each_with_index do |frame, i|
331
359
  if range.include? i
332
360
  frame.flag = 0 if frame.is_keyframe?
333
361
  end
334
362
  end
363
+ self
335
364
  end
336
365
 
337
366
  ##
338
367
  # Returns true if +other+'s frames are same as self's frames.
339
368
  def == other
340
- @meta == other.meta
369
+ @avi == other.avi
341
370
  end
342
371
 
343
372
  ##
344
373
  # Generates new AviGlitch::Base instance using self.
345
374
  def to_avi
346
- AviGlitch.open @io.path
375
+ AviGlitch::Base.new @avi.clone
347
376
  end
348
377
 
349
- def inspect # :nodec:
350
- "#<#{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}>"
351
380
  end
352
381
 
353
382
  def get_beginning_and_length *args #:nodoc:
@@ -355,46 +384,18 @@ module AviGlitch
355
384
  if args.first.kind_of? Range
356
385
  r = args.first
357
386
  b = r.begin
358
- e = r.end >= 0 ? r.end : @meta.size + r.end
387
+ e = r.end >= 0 ? r.end : self.size + r.end
359
388
  l = e - b + 1
360
389
  end
361
- b = b >= 0 ? b : @meta.size + b
390
+ b = b >= 0 ? b : self.size + b
362
391
  [b, l]
363
392
  end
364
393
 
365
394
  def safe_frames_count? count #:nodoc:
366
- r = true
367
- if @@warn_if_frames_are_too_large && count >= SAFE_FRAMES_COUNT
368
- trap(:INT) do
369
- @io.close!
370
- exit
371
- end
372
- m = ["WARNING: The avi data has too many frames (#{count}).\n",
373
- "It may use a large memory to process. ",
374
- "We recommend to chop the movie to smaller chunks before you glitch.\n",
375
- "Do you want to continue anyway? [yN] "].join('')
376
- a = Readline.readline m
377
- r = a == 'y'
378
- @@warn_if_frames_are_too_large = !r
379
- end
380
- r
381
- end
382
-
383
- def fix_offsets_if_needed io #:nodoc:
384
- # rarely data offsets begin from 0 of the file
385
- return if @meta.empty?
386
- pos = io.pos
387
- m = @meta.first
388
- io.pos = @pos_of_movi + m[:offset]
389
- unless io.read(4) == m[:id]
390
- @meta.each do |x|
391
- x[:offset] -= @pos_of_movi
392
- end
393
- end
394
- io.pos = pos
395
+ warn "[DEPRECATION] `safe_frames_count?` is deprecated."
396
+ true
395
397
  end
396
398
 
397
- protected :frames_data_as_io, :meta
398
- private :overwrite, :get_beginning_and_length, :fix_offsets_if_needed
399
+ private :get_beginning_and_length
399
400
  end
400
401
  end