aviglitch 0.0.2 → 0.0.3

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.
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