aviglitch 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.0.3 / 2010-07-07
2
+
3
+ * Changed AviGlitch::Frames allowing to slice and concatenate frames
4
+ (like Array).
5
+ * Changed datamosh cli to accept multiple files.
6
+ * And some tiny fixes.
7
+
1
8
  == 0.0.2 / 2010-05-17
2
9
 
3
10
  * Removed AviGlitch#new. Use AviGlitch#open instead of #new.
@@ -1,6 +1,6 @@
1
1
  = AviGlitch
2
2
 
3
- * http://github.com/ucnv/aviglitch/tree/master
3
+ * http://github.com/ucnv/aviglitch
4
4
 
5
5
  == Description
6
6
 
@@ -16,7 +16,7 @@ See following urls for details;
16
16
 
17
17
  * Not supports AVI2 files right now.
18
18
  * Not supports files with interleave.
19
- * Parses only container level structure, not parses codecs.
19
+ * Parses only container level structure, doesn't parse codecs.
20
20
 
21
21
  == Synopsis
22
22
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
@@ -7,7 +7,6 @@ require 'aviglitch'
7
7
 
8
8
  output = './out.avi'
9
9
  all = false
10
- force = false
11
10
 
12
11
  opts = OptionParser.new do |opts|
13
12
  opts.banner = "datamosh - AviGlitch's datamoshing video generator."
@@ -17,9 +16,6 @@ opts = OptionParser.new do |opts|
17
16
  "Output the video to OUTPUT (./out.avi by default)") do |f|
18
17
  output = f
19
18
  end
20
- opts.on("-f", "--force", "Overwrite an existing output file") do
21
- force = true
22
- end
23
19
  opts.on("-a", "--all",
24
20
  "Remove all keyframes (It remains a first keyframe by default)") do
25
21
  all = true
@@ -29,23 +25,24 @@ opts = OptionParser.new do |opts|
29
25
  exit
30
26
  end
31
27
  end
28
+
32
29
  input = opts.parse!
33
30
  if input.empty?
34
31
  puts opts
35
32
  exit 1
36
33
  end
37
- if !force && File.exists?(output)
38
- puts "!!! `#{output}' already exists. Use -f option to overwrite."
39
- exit 1
40
- end
41
34
 
42
- a = AviGlitch.open input.first
35
+ a = AviGlitch.open input.shift
43
36
  a.glitch_with_index :keyframe do |frame, i|
44
- if !all && i == 0 # keep the first frame
45
- frame
46
- else
37
+ (!all && i == 0) ? frame : "\000" * frame.size # keep the first frame
38
+ end
39
+ input.each do |file|
40
+ b = AviGlitch.open file
41
+ b.glitch :keyframe do |frame|
47
42
  "\000" * frame.size
48
43
  end
44
+ a.frames.concat b.frames
49
45
  end
50
- a.output(output)
46
+
47
+ a.output output
51
48
 
@@ -29,14 +29,18 @@ require 'aviglitch/frames'
29
29
  #
30
30
  module AviGlitch
31
31
 
32
- VERSION = '0.0.2'
32
+ VERSION = '0.0.3'
33
33
 
34
34
  class << self
35
35
  ##
36
36
  # Returns AviGlitch::Base instance.
37
- # It requires +path+ as String or Pathname.
38
- def AviGlitch.open path
39
- AviGlitch::Base.new(Pathname(path))
37
+ # It requires +path_or_frames+ as String or Pathname, or Frames instance.
38
+ def AviGlitch.open path_or_frames
39
+ if path_or_frames.kind_of?(Frames)
40
+ path_or_frames.to_avi
41
+ else
42
+ AviGlitch::Base.new(Pathname(path_or_frames))
43
+ end
40
44
  end
41
45
  end
42
46
  end
@@ -8,15 +8,13 @@ module AviGlitch
8
8
  # To glitch, and save file. The instance returned through AviGlitch#open.
9
9
  #
10
10
  class Base
11
- SAFE_FRAMES_COUNT = 150000 # :nodoc:
12
-
13
11
  # AviGlitch::Frames object generated from the +file+.
14
12
  attr_reader :frames
15
13
  # The input file (copied tempfile).
16
14
  attr_reader :file
17
15
 
18
16
  ##
19
- # Create a new instance of AviGlitch::Base, open the file and
17
+ # Creates a new instance of AviGlitch::Base, open the file and
20
18
  # make it ready to manipulate.
21
19
  # It requires +path+ as Pathname.
22
20
  def initialize path
@@ -32,16 +30,12 @@ module AviGlitch
32
30
  unless AviGlitch::Base.surely_formatted? @file
33
31
  raise 'Unsupported file passed.'
34
32
  end
35
- unless safe_frames_count? @file
36
- close
37
- exit
38
- end
39
33
  @frames = Frames.new @file
40
34
  # I believe Ruby's GC to close and remove the Tempfile..
41
35
  end
42
36
 
43
37
  ##
44
- # Output the glitched file to +path+, and close the file.
38
+ # Outputs the glitched file to +path+, and close the file.
45
39
  def output path
46
40
  FileUtils.cp @file.path, path
47
41
  close
@@ -54,7 +48,7 @@ module AviGlitch
54
48
  end
55
49
 
56
50
  ##
57
- # Glitch each frame data.
51
+ # Glitches each frame data.
58
52
  # It is a convent method to iterate each frame.
59
53
  #
60
54
  # The argument +target+ takes symbols listed below:
@@ -67,66 +61,53 @@ module AviGlitch
67
61
  # It also requires a block. In the block, you take the frame data
68
62
  # as a String parameter.
69
63
  # To modify the data, simply return a modified data.
64
+ # It returns +self+
70
65
  def glitch target = :all, &block # :yield: data
71
- frames.each do |frame|
66
+ @frames.each do |frame|
72
67
  if valid_target? target, frame
73
68
  frame.data = yield frame.data
74
69
  end
75
70
  end
71
+ self
76
72
  end
77
73
 
78
74
  ##
79
75
  # Do glitch with index.
80
76
  def glitch_with_index target = :all, &block # :yield: data, index
81
77
  i = 0
82
- frames.each do |frame|
78
+ @frames.each do |frame|
83
79
  if valid_target? target, frame
84
80
  frame.data = yield(frame.data, i)
85
81
  i += 1
86
82
  end
87
83
  end
84
+ self
88
85
  end
89
86
 
90
- alias :write :output
87
+ alias_method :write, :output
88
+
89
+ ##
90
+ # Swaps the frames with other Frames data.
91
+ def frames= other
92
+ raise TypeError unless other.kind_of?(Frames)
93
+ @frames.clear
94
+ @frames.concat other
95
+ end
91
96
 
92
97
  def valid_target? target, frame # :nodoc:
93
98
  return true if target == :all
94
99
  begin
95
- frame.send "is_#{target.to_s}?"
100
+ frame.send "is_#{target.to_s.sub(/frames$/, 'frame')}?"
96
101
  rescue
97
102
  false
98
103
  end
99
104
  end
100
105
 
101
- def safe_frames_count? io # :nodoc:
102
- r = true
103
- io.pos = 12
104
- while io.read(4) != 'idx1' do
105
- s = io.read(4).unpack('V').first
106
- io.pos += s
107
- end
108
- s = io.read(4).unpack('V').first
109
- fc = s / 16
110
- if fc >= SAFE_FRAMES_COUNT
111
- trap(:INT) do
112
- close
113
- exit
114
- end
115
- m = ["WARNING: The passed file has too many frames (#{fc}).\n",
116
- "It may use a large memory to process. ",
117
- "We recommend to chop the movie to smaller chunks before you glitch.\n",
118
- "Do you want to continue anyway? [yN] "].join('')
119
- a = Readline.readline m
120
- r = a == 'y'
121
- end
122
- r
123
- end
124
-
125
- private_instance_methods [:valid_target?, :is_safe_frames_count?]
106
+ private :valid_target?
126
107
 
127
108
  class << self
128
109
  ##
129
- # Check if the +file+ is a correctly formetted AVI file.
110
+ # Checks if the +file+ is a correctly formetted AVI file.
130
111
  # +file+ can be String or Pathname or IO.
131
112
  def surely_formatted? file, debug = false
132
113
  answer = true
@@ -13,7 +13,7 @@ module AviGlitch
13
13
  attr_reader :id, :flag
14
14
 
15
15
  ##
16
- # Create a new AviGlitch::Frame object.
16
+ # Creates a new AviGlitch::Frame object.
17
17
  #
18
18
  # The arguments are:
19
19
  # [+data+] just data, without meta-data
@@ -35,7 +35,7 @@ module AviGlitch
35
35
 
36
36
  ##
37
37
  # Alias for is_keyframe?
38
- alias :is_iframe? :is_keyframe?
38
+ alias_method :is_iframe?, :is_keyframe?
39
39
 
40
40
  ##
41
41
  # Returns if it is a video frame and also not a key frame.
@@ -45,7 +45,7 @@ module AviGlitch
45
45
 
46
46
  ##
47
47
  # Alias for is_deltaframe?
48
- alias :is_pframe? :is_deltaframe?
48
+ alias_method :is_pframe?, :is_deltaframe?
49
49
 
50
50
  ##
51
51
  # Returns if it is a video frame.
@@ -1,3 +1,5 @@
1
+ require 'stringio'
2
+
1
3
  module AviGlitch
2
4
 
3
5
  # Frames provides the interface to access each frame
@@ -12,12 +14,19 @@ module AviGlitch
12
14
  # frame.data = frame.data.gsub(/\d/, '0')
13
15
  # end
14
16
  #
15
- # In the block passed into iteration method, the parameter is the reference
17
+ # In the block passed into iteration method, the parameter is a reference
16
18
  # of AviGlitch::Frame object.
17
19
  #
18
20
  class Frames
19
21
  include Enumerable
20
22
 
23
+ SAFE_FRAMES_COUNT = 150000 # :nodoc:
24
+ @@warn_if_frames_are_too_large = true # :nodoc:
25
+
26
+ attr_reader :meta
27
+
28
+ ##
29
+ # Creates a new AviGlitch::Frames object.
21
30
  def initialize io
22
31
  io.rewind
23
32
  io.pos = 12 # /^RIFF[\s\S]{4}AVI $/
@@ -38,38 +47,65 @@ module AviGlitch
38
47
  :size => io.read(4).unpack('V').first,
39
48
  }
40
49
  end
50
+ unless safe_frames_count? @meta.size
51
+ @io.close!
52
+ exit
53
+ end
41
54
  io.rewind
42
55
  @io = io
43
56
  end
44
57
 
58
+ ##
59
+ # Enumerates the frames.
45
60
  def each
46
61
  temp = Tempfile.new 'frames'
47
- temp.print 'movi'
62
+ frames_data_as_io(temp, Proc.new)
63
+ overwrite temp
64
+ temp.close!
65
+ end
66
+
67
+ ##
68
+ # Returns the number of frames.
69
+ def size
70
+ @meta.size
71
+ end
72
+
73
+ def frames_data_as_io(io = nil, block = nil) #:nodoc:
74
+ io = Tempfile.new('tmep') if io.nil?
48
75
  @meta = @meta.select do |m|
49
- @io.pos = @pos_of_movi + m[:offset] + 8
76
+ @io.pos = @pos_of_movi + m[:offset] + 8 # 8 for id and size
50
77
  frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag])
51
- yield frame
52
- unless frame.data.nil? || frame.data.to_s.empty?
53
- m[:offset] = temp.pos
78
+ block.call(frame) if block # accept the variable block as Proc
79
+ yield frame if block_given? # or a given block (or do nothing)
80
+ unless frame.data.nil?
81
+ m[:offset] = io.pos + 4 # 4 for 'movi'
54
82
  m[:size] = frame.data.size
55
- temp.print m[:id]
56
- temp.print [frame.data.size].pack('V')
57
- temp.print frame.data
58
- temp.print "\000" if frame.data.size % 2 == 1
83
+ io.print m[:id]
84
+ io.print [frame.data.size].pack('V')
85
+ io.print frame.data
86
+ io.print "\000" if frame.data.size % 2 == 1
59
87
  true
60
88
  else
61
89
  false
62
90
  end
63
91
  end
92
+ io
93
+ end
64
94
 
95
+ def overwrite data #:nodoc:
96
+ unless safe_frames_count? @meta.size
97
+ @io.close!
98
+ exit
99
+ end
65
100
  # Overwrite the file
66
- @io.pos = @pos_of_movi - 4
67
- @io.print [temp.pos].pack('V')
68
- temp.rewind
69
- while d = temp.read(1024) do
101
+ data.seek 0, IO::SEEK_END
102
+ @io.pos = @pos_of_movi - 4 # 4 for size
103
+ @io.print [data.pos + 4].pack('V') # 4 for 'movi'
104
+ @io.print 'movi'
105
+ data.rewind
106
+ while d = data.read(1024) do
70
107
  @io.print d
71
108
  end
72
- temp.close true
73
109
  @io.print 'idx1'
74
110
  @io.print [@meta.size * 16].pack('V')
75
111
  @meta.each do |m|
@@ -85,13 +121,249 @@ module AviGlitch
85
121
  @io.print [eof - 8].pack('V')
86
122
  ## frame count
87
123
  @io.pos = 48
88
- @io.print [@meta.size].pack('V')
124
+ vid_frames = @meta.select do |m|
125
+ id = m[:id]
126
+ id[2, 2] == 'db' || id[2, 2] == 'dc'
127
+ end
128
+ @io.print [vid_frames.size].pack('V')
89
129
 
130
+ @io.pos
90
131
  end
91
132
 
92
- def size
93
- @meta.size
133
+ ##
134
+ # Removes all frames and returns self.
135
+ def clear
136
+ @meta = []
137
+ overwrite StringIO.new
138
+ self
139
+ end
140
+
141
+ ##
142
+ # Appends the frames in the other Frames into the tail of self.
143
+ # It is destructive like Array does.
144
+ def concat other_frames
145
+ raise TypeError unless other_frames.kind_of?(Frames)
146
+ # data
147
+ this_data = Tempfile.new 'this'
148
+ self.frames_data_as_io this_data
149
+ other_data = Tempfile.new 'other'
150
+ other_frames.frames_data_as_io other_data
151
+ this_data.seek 0, IO::SEEK_END
152
+ this_size = this_data.pos
153
+ other_data.rewind
154
+ while d = other_data.read(1024) do
155
+ this_data.print d
156
+ end
157
+ other_data.close!
158
+ # meta
159
+ other_meta = other_frames.meta.collect do |m|
160
+ x = m.dup
161
+ x[:offset] += this_size
162
+ x
163
+ end
164
+ @meta.concat other_meta
165
+ # close
166
+ overwrite this_data
167
+ this_data.close!
168
+ end
169
+
170
+ ##
171
+ # Returns a concatenation of the two Frames as a new Frames instance.
172
+ def + other_frames
173
+ r = self.to_avi
174
+ r.frames.concat other_frames
175
+ r.frames
176
+ end
177
+
178
+ ##
179
+ # Returns the new Frames as a +times+ times repeated concatenation
180
+ # of the original Frames.
181
+ def * times
182
+ result = self.slice 0, 0
183
+ frames = self.slice 0..-1
184
+ times.times do
185
+ result.concat frames
186
+ end
187
+ result
188
+ end
189
+
190
+ ##
191
+ # Returns the Frame object at the given index or
192
+ # returns new Frames object that sliced with the given index and length
193
+ # or with the Range.
194
+ # Just like Array.
195
+ def slice *args
196
+ b, l = get_beginning_and_length *args
197
+ if l.nil?
198
+ self.at b
199
+ else
200
+ e = b + l - 1
201
+ r = self.to_avi
202
+ r.frames.each_with_index do |f, i|
203
+ unless i >= b && i <= e
204
+ f.data = nil
205
+ end
206
+ end
207
+ r.frames
208
+ end
209
+ end
210
+
211
+ ##
212
+ # Alias for slice
213
+ alias_method :[], :slice
214
+
215
+ ##
216
+ # Removes frame(s) at the given index or the range (same as slice).
217
+ # Returns the new Frames contains removed frames.
218
+ def slice! *args
219
+ b, l = get_beginning_and_length *args
220
+ head, sliced, tail = ()
221
+ sliced = l.nil? ? self.slice(b) : self.slice(b, l)
222
+ head = self.slice(0, b)
223
+ l = 1 if l.nil?
224
+ tail = self.slice((b + l)..-1)
225
+ self.clear
226
+ self.concat head + tail
227
+ sliced
228
+ end
229
+
230
+ ##
231
+ # Removes frame(s) at the given index or the range (same as []).
232
+ # Inserts the given Frame or Frames's contents into the removed index.
233
+ def []= *args, value
234
+ b, l = get_beginning_and_length *args
235
+ ll = l.nil? ? 1 : l
236
+ head = self.slice(0, b)
237
+ rest = self.slice((b + ll)..-1)
238
+ if l.nil? || value.kind_of?(Frame)
239
+ head.push value
240
+ elsif value.kind_of?(Frames)
241
+ head.concat value
242
+ else
243
+ raise TypeError
244
+ end
245
+ new_frames = head + rest
246
+
247
+ self.clear
248
+ self.concat new_frames
249
+ end
250
+
251
+ ##
252
+ # Returns one Frame object at the given index.
253
+ def at n
254
+ m = @meta[n]
255
+ return nil if m.nil?
256
+ @io.pos = @pos_of_movi + m[:offset] + 8
257
+ frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag])
258
+ @io.rewind
259
+ frame
260
+ end
261
+
262
+ ##
263
+ # Returns the first Frame object.
264
+ def first
265
+ self.slice(0)
266
+ end
267
+
268
+ ##
269
+ # Returns the last Frame object.
270
+ def last
271
+ self.slice(self.size - 1)
272
+ end
273
+
274
+ ##
275
+ # Appends the given Frame into the tail of self.
276
+ def push frame
277
+ raise TypeError unless frame.kind_of? Frame
278
+ # data
279
+ this_data = Tempfile.new 'this'
280
+ self.frames_data_as_io this_data
281
+ this_data.seek 0, IO::SEEK_END
282
+ this_size = this_data.pos
283
+ this_data.print frame.id
284
+ this_data.print [frame.data.size].pack('V')
285
+ this_data.print frame.data
286
+ this_data.print "\000" if frame.data.size % 2 == 1
287
+ # meta
288
+ @meta << {
289
+ :id => frame.id,
290
+ :flag => frame.flag,
291
+ :offset => this_size + 4, # 4 for 'movi'
292
+ :size => frame.data.size,
293
+ }
294
+ # close
295
+ overwrite this_data
296
+ this_data.close!
297
+ self
298
+ end
299
+
300
+ ##
301
+ # Alias for push
302
+ alias_method :<<, :push
303
+
304
+ ##
305
+ # Insert the given Frame objects into the given index.
306
+ def insert n, *args
307
+ new_frames = self.slice(0, n)
308
+ args.each do |f|
309
+ new_frames.push f
310
+ end
311
+ new_frames.concat self.slice(n..-1)
312
+
313
+ self.clear
314
+ self.concat new_frames
315
+ self
316
+ end
317
+
318
+ ##
319
+ # Deletes one Frame at the given index.
320
+ def delete_at n
321
+ self.slice! n
322
+ end
323
+
324
+ ##
325
+ # Returns true if +other+'s frames are same as self's frames.
326
+ def == other
327
+ @meta == other.meta
328
+ end
329
+
330
+ ##
331
+ # Generate new AviGlitch::Base instance using self.
332
+ def to_avi
333
+ AviGlitch.open @io.path
334
+ end
335
+
336
+ def get_beginning_and_length *args #:nodoc:
337
+ b, l = args
338
+ if args.first.kind_of? Range
339
+ r = args.first
340
+ b = r.begin
341
+ e = r.end >= 0 ? r.end : @meta.size + r.end
342
+ l = e - b + 1
343
+ end
344
+ b = b >= 0 ? b : @meta.size + b
345
+ [b, l]
346
+ end
347
+
348
+ def safe_frames_count? count # :nodoc:
349
+ r = true
350
+ if @@warn_if_frames_are_too_large && count >= SAFE_FRAMES_COUNT
351
+ trap(:INT) do
352
+ @io.close!
353
+ exit
354
+ end
355
+ m = ["WARNING: The avi data has too many frames (#{count}).\n",
356
+ "It may use a large memory to process. ",
357
+ "We recommend to chop the movie to smaller chunks before you glitch.\n",
358
+ "Do you want to continue anyway? [yN] "].join('')
359
+ a = Readline.readline m
360
+ r = a == 'y'
361
+ @@warn_if_frames_are_too_large = !r
362
+ end
363
+ r
94
364
  end
95
365
 
366
+ protected :frames_data_as_io, :meta
367
+ private :overwrite, :get_beginning_and_length
96
368
  end
97
369
  end
@@ -16,25 +16,18 @@ describe AviGlitch do
16
16
  FileUtils.rmdir OUTPUT_DIR
17
17
  end
18
18
 
19
- it 'raise an error against unsupported files' do
19
+ it 'should raise an error against unsupported files' do
20
20
  lambda {
21
21
  avi = AviGlitch.open __FILE__
22
22
  }.should raise_error
23
23
  end
24
24
 
25
- it 'returns AviGlitch::Base object through the method #open' do
25
+ it 'should return AviGlitch::Base object through the method #open' do
26
26
  avi = AviGlitch.open @in
27
27
  avi.should be_kind_of AviGlitch::Base
28
28
  end
29
29
 
30
- it 'saves the same file when nothing is changed' do
31
- avi = AviGlitch.open @in
32
- avi.frames.each do |f|
33
- ;
34
- end
35
- avi.output @out
36
- FileUtils.cmp(@in, @out).should be true
37
-
30
+ it 'should save the same file when nothing is changed' do
38
31
  avi = AviGlitch.open @in
39
32
  avi.glitch do |d|
40
33
  d
@@ -43,19 +36,6 @@ describe AviGlitch do
43
36
  FileUtils.cmp(@in, @out).should be true
44
37
  end
45
38
 
46
- it 'can manipulate each frame' do
47
- avi = AviGlitch.open @in
48
- f = avi.frames
49
- f.should be_kind_of Enumerable
50
- avi.frames.each do |f|
51
- if f.is_keyframe?
52
- f.data = f.data.gsub(/\d/, '0')
53
- end
54
- end
55
- avi.output @out
56
- AviGlitch::Base.surely_formatted?(@out, true).should be true
57
- end
58
-
59
39
  it 'can glitch each keyframe' do
60
40
  avi = AviGlitch.open @in
61
41
  n = 0
@@ -86,28 +66,81 @@ describe AviGlitch do
86
66
  AviGlitch::Base.surely_formatted?(@out, true).should be true
87
67
  end
88
68
 
89
- it 'can remove a frame with returning nil' do
69
+ it 'should have some alias methods' do
70
+ lambda {
71
+ avi = AviGlitch.open @in
72
+ avi.write @out
73
+ }.should_not raise_error
74
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
75
+ end
76
+
77
+ it 'can glitch with :*frames instead of :*frame' do
90
78
  avi = AviGlitch.open @in
91
- in_frame_size = avi.frames.size
92
- rem_count = 0
93
- avi.glitch :keyframe do |kf|
94
- rem_count += 1
95
- nil
79
+ count = 0
80
+ avi.glitch :keyframes do |kf|
81
+ count += 1
82
+ kf
96
83
  end
84
+ avi.close
85
+ count.should > 0
86
+ end
87
+
88
+ it 'should close file when output' do
89
+ avi = AviGlitch.open @in
97
90
  avi.output @out
98
- AviGlitch::Base.surely_formatted?(@out, true).should be true
91
+ lambda {
92
+ avi.glitch do |f|
93
+ f
94
+ end
95
+ }.should raise_error(IOError)
96
+ end
97
+
98
+ it 'can explicit close file' do
99
+ avi = AviGlitch.open @in
100
+ avi.close
101
+ lambda {
102
+ avi.glitch do |f|
103
+ f
104
+ end
105
+ }.should raise_error(IOError)
106
+ end
99
107
 
100
- # frames length in the output file is correct
101
- avi = AviGlitch.open @out
102
- out_frame_size = avi.frames.size
103
- out_frame_size.should == in_frame_size - rem_count
108
+ it 'should offer one liner style coding' do
109
+ lambda {
110
+ AviGlitch.open(@in).glitch(:keyframe){|d| '0' * d.size}.output(@out)
111
+ }.should_not raise_error
112
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
104
113
  end
105
114
 
106
- it 'has some alias methods' do
115
+ it 'should not raise error in multiple glitches' do
107
116
  lambda {
108
117
  avi = AviGlitch.open @in
109
- avi.write @out
118
+ avi.glitch(:keyframe) do |d|
119
+ d.gsub(/\d/, '')
120
+ end
121
+ avi.glitch(:keyframe) do |d|
122
+ nil
123
+ end
124
+ avi.glitch(:audioframe) do |d|
125
+ d * 2
126
+ end
127
+ avi.output @out
110
128
  }.should_not raise_error
111
129
  AviGlitch::Base.surely_formatted?(@out, true).should be true
112
130
  end
131
+
132
+ it 'can work with another frames instance' do
133
+ a = AviGlitch.open @in
134
+ a.glitch :keyframe do |d|
135
+ nil
136
+ end
137
+ a.output(@out.to_s + 'x.avi')
138
+ b = AviGlitch.open @in
139
+ c = AviGlitch.open(@out.to_s + 'x.avi')
140
+ b.frames = c.frames
141
+ b.output @out
142
+
143
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
144
+ end
145
+
113
146
  end
@@ -0,0 +1,61 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe AviGlitch, 'datamosh cli' do
4
+
5
+ before :all do
6
+ FileUtils.mkdir OUTPUT_DIR unless File.exist? OUTPUT_DIR
7
+ @in = FILES_DIR + 'sample.avi'
8
+ @out = OUTPUT_DIR + 'out.avi'
9
+ datamosh = Pathname.new(
10
+ File.join(File.dirname(__FILE__), '..', 'bin/datamosh')
11
+ ).realpath
12
+ @cmd = "ruby %s -o %s " % [datamosh, @out]
13
+ end
14
+
15
+ after :each do
16
+ FileUtils.rm Dir.glob((OUTPUT_DIR + '*').to_s)
17
+ end
18
+
19
+ after :all do
20
+ FileUtils.rmdir OUTPUT_DIR
21
+ end
22
+
23
+ it 'should correctly process files' do
24
+ system [@cmd, @in].join(' ')
25
+ o = AviGlitch.open @out
26
+ o.frames.each_with_index do |f, i|
27
+ if f.is_keyframe? && i == 0
28
+ f.data.should_not match /^\000+$/
29
+ elsif f.is_keyframe?
30
+ f.data.should match /^\000+$/
31
+ end
32
+ end
33
+ o.close
34
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
35
+
36
+ system [@cmd, '-a', @in].join(' ')
37
+ o = AviGlitch.open @out
38
+ o.frames.each do |f|
39
+ if f.is_keyframe?
40
+ f.data.should match /^\000+$/
41
+ end
42
+ end
43
+ o.close
44
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
45
+
46
+ system [@cmd, @in, @in, @in].join(' ')
47
+ a = AviGlitch.open @in
48
+ o = AviGlitch.open @out
49
+ o.frames.size.should == a.frames.size * 3
50
+ o.frames.each_with_index do |f, i|
51
+ if f.is_keyframe? && i == 0
52
+ f.data.should_not match /^\000+$/
53
+ elsif f.is_keyframe?
54
+ f.data.should match /^\000+$/
55
+ end
56
+ end
57
+ o.close
58
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
59
+ end
60
+
61
+ end
@@ -0,0 +1,450 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe AviGlitch::Frames do
4
+
5
+ before :all do
6
+ AviGlitch::Frames.class_eval do
7
+ define_method(:get_real_id_with) do |frame|
8
+ pos = @io.pos
9
+ @io.pos -= frame.data.size
10
+ @io.pos -= 8
11
+ id = @io.read 4
12
+ @io.pos = pos
13
+ id
14
+ end
15
+ end
16
+
17
+ FileUtils.mkdir OUTPUT_DIR unless File.exist? OUTPUT_DIR
18
+ @in = FILES_DIR + 'sample.avi'
19
+ @out = OUTPUT_DIR + 'out.avi'
20
+ end
21
+
22
+ after :each do
23
+ FileUtils.rm Dir.glob((OUTPUT_DIR + '*').to_s)
24
+ end
25
+
26
+ after :all do
27
+ FileUtils.rmdir OUTPUT_DIR
28
+ end
29
+
30
+ it 'should save the same file when nothing is changed' do
31
+ avi = AviGlitch.open @in
32
+ avi.frames.each do |f|
33
+ ;
34
+ end
35
+ avi.output @out
36
+ FileUtils.cmp(@in, @out).should be true
37
+ end
38
+
39
+ it 'can manipulate each frame' do
40
+ avi = AviGlitch.open @in
41
+ f = avi.frames
42
+ f.should be_kind_of Enumerable
43
+ avi.frames.each do |f|
44
+ if f.is_keyframe?
45
+ f.data = f.data.gsub(/\d/, '0')
46
+ end
47
+ end
48
+ avi.output @out
49
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
50
+ end
51
+
52
+ it 'should remove a frame when returning nil' do
53
+ avi = AviGlitch.open @in
54
+ in_frame_size = avi.frames.size
55
+ rem_count = 0
56
+ avi.glitch :keyframe do |kf|
57
+ rem_count += 1
58
+ nil
59
+ end
60
+ avi.output @out
61
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
62
+
63
+ # frames length in the output file is correct
64
+ avi = AviGlitch.open @out
65
+ out_frame_size = avi.frames.size
66
+ out_frame_size.should == in_frame_size - rem_count
67
+ end
68
+
69
+ it 'should read correct positions in #each' do
70
+ avi = AviGlitch.open @in
71
+ frames = avi.frames
72
+ frames.each do |f|
73
+ real_id = frames.get_real_id_with f
74
+ real_id.should == f.id
75
+ end
76
+ avi.close
77
+ end
78
+
79
+ it 'should promise the read frame data is not nil' do
80
+ avi = AviGlitch.open @in
81
+ frames = avi.frames
82
+ frames.each do |f|
83
+ f.data.should_not == nil
84
+ end
85
+ avi.close
86
+ end
87
+
88
+ it 'should hide the inner variables' do
89
+ avi = AviGlitch.open @in
90
+ frames = avi.frames
91
+ lambda { frames.meta }.should raise_error(NoMethodError)
92
+ lambda { frames.io }.should raise_error(NoMethodError)
93
+ lambda { frames.frames_data_as_io }.should raise_error(NoMethodError)
94
+ avi.close
95
+ end
96
+
97
+ it 'should save video frames count in header' do
98
+ avi = AviGlitch.open @in
99
+ c = 0
100
+ avi.frames.each do |f|
101
+ c += 1 if f.is_videoframe?
102
+ end
103
+ avi.output @out
104
+ open(@out) do |f|
105
+ f.pos = 48
106
+ f.read(4).unpack('V').first.should == c
107
+ end
108
+ end
109
+
110
+ it 'should evaluate the equality with owned contents' do
111
+ a = AviGlitch.open @in
112
+ b = AviGlitch.open @in
113
+ a.frames.should == b.frames
114
+ end
115
+
116
+ it 'can generate AviGlitch::Base instance' do
117
+ a = AviGlitch.open @in
118
+ b = a.frames.slice(0, 10)
119
+ c = b.to_avi
120
+ c.should be_kind_of AviGlitch::Base
121
+ c.output @out
122
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
123
+ end
124
+
125
+ it 'can concat with other Frames instance with #concat, destructively' do
126
+ a = AviGlitch.open @in
127
+ b = AviGlitch.open @in
128
+ asize = a.frames.size
129
+ bsize = b.frames.size
130
+ lambda {
131
+ a.frames.concat([1,2,3])
132
+ }.should raise_error(TypeError)
133
+ a.frames.concat(b.frames)
134
+ a.frames.size.should == asize + bsize
135
+ b.frames.size.should == bsize
136
+ a.output @out
137
+ b.close
138
+
139
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
140
+ end
141
+
142
+ it 'can concat with other Frames instance with +' do
143
+ a = AviGlitch.open @in
144
+ b = AviGlitch.open @in
145
+ asize = a.frames.size
146
+ bsize = b.frames.size
147
+ c = a.frames + b.frames
148
+ a.frames.size.should == asize
149
+ b.frames.size.should == bsize
150
+ c.should be_kind_of AviGlitch::Frames
151
+ c.size.should == asize + bsize
152
+ a.close
153
+ b.close
154
+ d = AviGlitch.open c
155
+ d.output @out
156
+
157
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
158
+ end
159
+
160
+ it 'can slice frames using start pos and length' do
161
+ avi = AviGlitch.open @in
162
+ a = avi.frames
163
+ asize = a.size
164
+ c = (a.size / 3).floor
165
+ b = a.slice(1, c)
166
+ b.should be_kind_of AviGlitch::Frames
167
+ b.size.should == c
168
+ lambda {
169
+ b.each {|x| x }
170
+ }.should_not raise_error
171
+
172
+ a.size.should == asize # make sure a is not destroyed
173
+ lambda {
174
+ a.each {|x| x }
175
+ }.should_not raise_error
176
+
177
+ avi.frames.concat b
178
+ avi.output @out
179
+
180
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
181
+ end
182
+
183
+ it 'can slice frames using Range' do
184
+ avi = AviGlitch.open @in
185
+ a = avi.frames
186
+ asize = a.size
187
+ c = (a.size / 3).floor
188
+ spos = 3
189
+ range = spos..(spos + c)
190
+ b = a.slice(range)
191
+ b.should be_kind_of AviGlitch::Frames
192
+ b.size.should == c + 1
193
+ lambda {
194
+ b.each {|x| x }
195
+ }.should_not raise_error
196
+
197
+ range = spos..-1
198
+ d = a.slice(range)
199
+ d.should be_kind_of AviGlitch::Frames
200
+ d.size.should == asize - spos
201
+ lambda {
202
+ d.each {|x| x }
203
+ }.should_not raise_error
204
+
205
+ x = -5
206
+ range = spos..x
207
+ e = a.slice(range)
208
+ e.should be_kind_of AviGlitch::Frames
209
+ e.size.should == asize - spos + x + 1
210
+ lambda {
211
+ e.each {|x| x }
212
+ }.should_not raise_error
213
+
214
+ end
215
+
216
+ it 'can concat repeatedly the same sliced frames' do
217
+ a = AviGlitch.open @in
218
+ b = a.frames.slice(0, 5)
219
+ c = a.frames.slice(0, 10)
220
+ 10.times do
221
+ b.concat(c)
222
+ end
223
+ b.size.should == 5 + (10 * 10)
224
+ end
225
+
226
+ it 'can get one single frame using slice(n)' do
227
+ a = AviGlitch.open @in
228
+ pos = 10
229
+ b = nil
230
+ a.frames.each_with_index do |f, i|
231
+ b = f if i == pos
232
+ end
233
+ c = a.frames.slice(pos)
234
+ c.should be_kind_of AviGlitch::Frame
235
+ c.data.should == b.data
236
+ end
237
+
238
+ it 'can get one single frame using at(n)' do
239
+ a = AviGlitch.open @in
240
+ pos = 10
241
+ b = nil
242
+ a.frames.each_with_index do |f, i|
243
+ b = f if i == pos
244
+ end
245
+ c = a.frames.at(pos)
246
+ c.should be_kind_of AviGlitch::Frame
247
+ c.data.should == b.data
248
+ end
249
+
250
+ it 'can get a first frame ussing first, a last frame using last' do
251
+ a = AviGlitch.open @in
252
+ b0 = c0 = nil
253
+ a.frames.each_with_index do |f, i|
254
+ b0 = f if i == 0
255
+ c0 = f if i == a.frames.size - 1
256
+ end
257
+ b1 = a.frames.first
258
+ c1 = a.frames.last
259
+
260
+ b1.data.should == b0.data
261
+ c1.data.should == c0.data
262
+ end
263
+
264
+ it 'can add a frame at last using push' do
265
+ a = AviGlitch.open @in
266
+ s = a.frames.size
267
+ b = a.frames[10]
268
+ lambda {
269
+ a.frames.push 100
270
+ }.should raise_error(TypeError)
271
+ c = a.frames + a.frames.slice(10, 1)
272
+
273
+ x = a.frames.push b
274
+ a.frames.size.should == s + 1
275
+ x.should == a.frames
276
+ a.frames.should == c
277
+ a.frames.last.data.should == c.last.data
278
+ x = a.frames.push b
279
+ a.frames.size.should == s + 2
280
+ x.should == a.frames
281
+
282
+ a.output @out
283
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
284
+ end
285
+
286
+ it 'can add a frame at last using <<' do
287
+ a = AviGlitch.open @in
288
+ s = a.frames.size
289
+ b = a.frames[10]
290
+
291
+ x = a.frames << b
292
+ a.frames.size.should == s + 1
293
+ x.should == a.frames
294
+
295
+ a.output @out
296
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
297
+ end
298
+
299
+ it 'can delete all frames using clear' do
300
+ a = AviGlitch.open @in
301
+ a.frames.clear
302
+ a.frames.size.should == 0
303
+ end
304
+
305
+ it 'can delete one frame using delete_at' do
306
+ a = AviGlitch.open @in
307
+ l = a.frames.size
308
+ b = a.frames[10]
309
+ c = a.frames[11]
310
+ x = a.frames.delete_at 10
311
+
312
+ x.data.should == b.data
313
+ a.frames[10].data.should == c.data
314
+ a.frames.size.should == l - 1
315
+
316
+ a.output @out
317
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
318
+ end
319
+
320
+ it 'can insert one frame into other frames using insert' do
321
+ a = AviGlitch.open @in
322
+ l = a.frames.size
323
+ b = a.frames[10]
324
+ x = a.frames.insert 5, b
325
+
326
+ x.should == a.frames
327
+ a.frames[5].data.should == b.data
328
+ a.frames[11].data.should == b.data
329
+ a.frames.size.should == l + 1
330
+
331
+ a.output @out
332
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
333
+ end
334
+
335
+ it 'can slice frames destructively using slice!' do
336
+ a = AviGlitch.open @in
337
+ l = a.frames.size
338
+
339
+ b = a.frames.slice!(10)
340
+ b.should be_kind_of AviGlitch::Frame
341
+ a.frames.size.should == l - 1
342
+
343
+ c = a.frames.slice!(0, 10)
344
+ c.should be_kind_of AviGlitch::Frames
345
+ a.frames.size.should == l - 1 - 10
346
+
347
+ d = a.frames.slice!(0..9)
348
+ d.should be_kind_of AviGlitch::Frames
349
+ a.frames.size.should == l - 1 - 10 - 10
350
+ end
351
+
352
+ it 'can swap frame(s) using []=' do
353
+ a = AviGlitch.open @in
354
+ l = a.frames.size
355
+ lambda {
356
+ a.frames[10] = "xxx"
357
+ }.should raise_error(TypeError)
358
+
359
+ b = a.frames[20]
360
+ a.frames[10] = b
361
+ a.frames.size.should == l
362
+ a.frames[10].data.should == b.data
363
+
364
+ a.output @out
365
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
366
+
367
+ a = AviGlitch.open @in
368
+ pl = 5
369
+ pp = 3
370
+ b = a.frames[20, pl]
371
+ a.frames[10..(10 + pp)] = b
372
+ a.frames.size.should == l - pp + pl - 1
373
+ pp.times do |i|
374
+ a.frames[10 + i].data.should == b[i].data
375
+ end
376
+
377
+ lambda {
378
+ a.frames[10] = a.frames.slice(100, 1)
379
+ }.should raise_error(TypeError)
380
+
381
+ a.output @out
382
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
383
+ end
384
+
385
+ it 'can repeat frames using *' do
386
+ a = AviGlitch.open @in
387
+
388
+ r = 20
389
+ b = a.frames.slice(10, 10)
390
+ c = b * r
391
+ c.size.should == 10 * r
392
+
393
+ c.to_avi.output @out
394
+ AviGlitch::Base.surely_formatted?(@out, true).should be true
395
+ end
396
+
397
+ it 'should manipulate frames like array does' do
398
+ avi = AviGlitch.open @in
399
+ a = avi.frames
400
+ x = Array.new a.size
401
+
402
+ fa = a.slice(0, 100)
403
+ fx = x.slice(0, 100)
404
+ fa.size.should == fx.size
405
+
406
+ fa = a.slice(100..-1)
407
+ fx = x.slice(100..-1)
408
+ fa.size.should == fx.size
409
+
410
+ fa = a.slice(100..-10)
411
+ fx = x.slice(100..-10)
412
+ fa.size.should == fx.size
413
+
414
+ fa = a.slice(-200, 10)
415
+ fx = x.slice(-200, 10)
416
+ fa.size.should == fx.size
417
+
418
+ a[100] = a.at 200
419
+ x[100] = x.at 200
420
+ a.size.should == x.size
421
+
422
+ a[100..150] = a.slice(100, 100)
423
+ x[100..150] = x.slice(100, 100)
424
+ a.size.should == x.size
425
+ end
426
+
427
+ it 'should have the method alias to slice as []' do
428
+ a = AviGlitch.open @in
429
+
430
+ b = a.frames[10]
431
+ b.should be_kind_of AviGlitch::Frame
432
+
433
+ c = a.frames[0, 10]
434
+ c.should be_kind_of AviGlitch::Frames
435
+ c.size.should == 10
436
+
437
+ d = a.frames[0..9]
438
+ d.should be_kind_of AviGlitch::Frames
439
+ d.size.should == 10
440
+ end
441
+
442
+ it 'should return nil when getting a frame at out-of-range index' do
443
+ a = AviGlitch.open @in
444
+
445
+ x = a.frames.at(a.frames.size + 1)
446
+ x.should be_nil
447
+ end
448
+
449
+
450
+ end
@@ -1 +1,2 @@
1
- --color
1
+ -c
2
+ -fs
metadata CHANGED
@@ -1,12 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aviglitch
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 2
9
- version: 0.0.2
4
+ version: 0.0.3
10
5
  platform: ruby
11
6
  authors:
12
7
  - ucnv
@@ -14,7 +9,7 @@ autorequire:
14
9
  bindir: bin
15
10
  cert_chain: []
16
11
 
17
- date: 2010-05-17 00:00:00 +09:00
12
+ date: 2010-07-07 00:00:00 +09:00
18
13
  default_executable: datamosh
19
14
  dependencies: []
20
15
 
@@ -39,7 +34,9 @@ files:
39
34
  - lib/aviglitch/frame.rb
40
35
  - lib/aviglitch/frames.rb
41
36
  - spec/aviglitch_spec.rb
37
+ - spec/datamosh_spec.rb
42
38
  - spec/files/sample.avi
39
+ - spec/frames_spec.rb
43
40
  - spec/spec.opts
44
41
  - spec/spec_helper.rb
45
42
  - LICENSE
@@ -56,23 +53,23 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
53
  requirements:
57
54
  - - ">="
58
55
  - !ruby/object:Gem::Version
59
- segments:
60
- - 0
61
56
  version: "0"
57
+ version:
62
58
  required_rubygems_version: !ruby/object:Gem::Requirement
63
59
  requirements:
64
60
  - - ">="
65
61
  - !ruby/object:Gem::Version
66
- segments:
67
- - 0
68
62
  version: "0"
63
+ version:
69
64
  requirements: []
70
65
 
71
66
  rubyforge_project:
72
- rubygems_version: 1.3.6
67
+ rubygems_version: 1.3.5
73
68
  signing_key:
74
69
  specification_version: 3
75
70
  summary: A Ruby library to destroy your AVI files.
76
71
  test_files:
77
72
  - spec/aviglitch_spec.rb
73
+ - spec/datamosh_spec.rb
74
+ - spec/frames_spec.rb
78
75
  - spec/spec_helper.rb