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.
- checksums.yaml +7 -0
- data/.github/workflows/ruby.yml +35 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/ChangeLog.md +67 -0
- data/Gemfile +7 -0
- data/LICENSE +1 -1
- data/README.md +49 -0
- data/Rakefile +6 -43
- data/aviglitch.gemspec +27 -0
- data/bin/datamosh +16 -3
- data/lib/aviglitch/avi.rb +550 -0
- data/lib/aviglitch/base.rb +57 -76
- data/lib/aviglitch/frame.rb +20 -0
- data/lib/aviglitch/frames.rb +181 -180
- data/lib/aviglitch.rb +6 -9
- data/spec/avi2_spec.rb +40 -0
- data/spec/aviglitch_spec.rb +37 -18
- data/spec/datamosh_spec.rb +4 -14
- data/spec/frames_spec.rb +114 -28
- data/spec/spec_helper.rb +45 -1
- metadata +67 -31
- data/ChangeLog +0 -40
- data/README.rdoc +0 -42
- data/VERSION +0 -1
- data/lib/aviglitch/tempfile.rb +0 -8
- data/spec/files/sample.avi +0 -0
data/lib/aviglitch.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
require 'tempfile'
|
2
|
-
require 'fileutils'
|
3
|
-
require 'readline'
|
4
2
|
require 'pathname'
|
5
3
|
require 'stringio'
|
4
|
+
require 'aviglitch/avi'
|
6
5
|
require 'aviglitch/base'
|
7
6
|
require 'aviglitch/frame'
|
8
7
|
require 'aviglitch/frames'
|
9
|
-
require 'aviglitch/tempfile'
|
10
8
|
|
11
9
|
# AviGlitch provides the ways to glitch AVI formatted video files.
|
12
10
|
#
|
@@ -15,7 +13,7 @@ require 'aviglitch/tempfile'
|
|
15
13
|
# You can manipulate each frame, like:
|
16
14
|
#
|
17
15
|
# avi = AviGlitch.open '/path/to/your.avi'
|
18
|
-
# avi.frames.each |frame|
|
16
|
+
# avi.frames.each do |frame|
|
19
17
|
# if frame.is_keyframe?
|
20
18
|
# frame.data = frame.data.gsub(/\d/, '0')
|
21
19
|
# end
|
@@ -30,18 +28,17 @@ require 'aviglitch/tempfile'
|
|
30
28
|
# end
|
31
29
|
# avi.output '/path/to/broken.avi'
|
32
30
|
#
|
33
|
-
#--
|
34
|
-
# It does not support AVI2, interleave format.
|
35
|
-
#
|
36
31
|
module AviGlitch
|
37
32
|
|
38
|
-
VERSION = '0.
|
33
|
+
VERSION = '0.2.0'
|
34
|
+
|
35
|
+
BUFFER_SIZE = 2 ** 24
|
39
36
|
|
40
37
|
class << self
|
41
38
|
##
|
42
39
|
# Returns AviGlitch::Base instance.
|
43
40
|
# It requires +path_or_frames+ as String or Pathname, or Frames instance.
|
44
|
-
def
|
41
|
+
def open path_or_frames
|
45
42
|
if path_or_frames.kind_of?(Frames)
|
46
43
|
path_or_frames.to_avi
|
47
44
|
else
|
data/spec/avi2_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe AviGlitch, 'AVI2.0' do
|
4
|
+
|
5
|
+
it 'should save same file when nothing has changed' do
|
6
|
+
avi = AviGlitch.open @in2
|
7
|
+
avi.glitch do |d|
|
8
|
+
d
|
9
|
+
end
|
10
|
+
avi.output @out
|
11
|
+
FileUtils.cmp(@in2, @out).should be true
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should be AVI1.0 when its size has reduced less than 1GB' do
|
15
|
+
a = AviGlitch.open @in2
|
16
|
+
size = 0
|
17
|
+
a.glitch do |d|
|
18
|
+
size += d.size
|
19
|
+
size < 1024 ** 3 ? d : nil
|
20
|
+
end
|
21
|
+
a.output @out
|
22
|
+
b = AviGlitch.open @out
|
23
|
+
b.avi.was_avi2?.should be false
|
24
|
+
b.close
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should be AVI2.0 when its size has increased over 1GB' do
|
28
|
+
a = AviGlitch.open @in
|
29
|
+
n = Math.log(1024.0 ** 3 / a.frames.data_size.to_f, 2).ceil
|
30
|
+
f = a.frames[0..-1]
|
31
|
+
n.times do
|
32
|
+
fx = f[0..-1]
|
33
|
+
f.concat fx
|
34
|
+
end
|
35
|
+
f.to_avi.output @out
|
36
|
+
b = AviGlitch.open @out
|
37
|
+
b.avi.was_avi2?.should be true
|
38
|
+
b.close
|
39
|
+
end
|
40
|
+
end
|
data/spec/aviglitch_spec.rb
CHANGED
@@ -2,24 +2,10 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
2
|
|
3
3
|
describe AviGlitch do
|
4
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
|
-
end
|
10
|
-
|
11
|
-
after :each do
|
12
|
-
FileUtils.rm Dir.glob((OUTPUT_DIR + '*').to_s)
|
13
|
-
end
|
14
|
-
|
15
|
-
after :all do
|
16
|
-
FileUtils.rmdir OUTPUT_DIR
|
17
|
-
end
|
18
|
-
|
19
5
|
it 'should raise an error against unsupported files' do
|
20
6
|
lambda {
|
21
7
|
avi = AviGlitch.open __FILE__
|
22
|
-
}.should raise_error
|
8
|
+
}.should raise_error(RuntimeError)
|
23
9
|
end
|
24
10
|
|
25
11
|
it 'should return AviGlitch::Base object through the method #open' do
|
@@ -52,13 +38,24 @@ describe AviGlitch do
|
|
52
38
|
|
53
39
|
it 'can glitch each keyframe with index' do
|
54
40
|
avi = AviGlitch.open @in
|
41
|
+
|
42
|
+
a_size = 0
|
43
|
+
avi.glitch :keyframe do |f|
|
44
|
+
a_size += 1
|
45
|
+
f
|
46
|
+
end
|
47
|
+
|
48
|
+
b_size = 0
|
55
49
|
avi.glitch_with_index :keyframe do |kf, idx|
|
50
|
+
b_size += 1
|
56
51
|
if idx < 25
|
57
52
|
kf.slice(10..kf.size)
|
58
53
|
else
|
59
54
|
kf
|
60
55
|
end
|
61
56
|
end
|
57
|
+
expect(a_size).to be == b_size
|
58
|
+
|
62
59
|
avi.output @out
|
63
60
|
i_size = File.stat(@in).size
|
64
61
|
o_size = File.stat(@out).size
|
@@ -143,9 +140,9 @@ describe AviGlitch do
|
|
143
140
|
AviGlitch::Base.surely_formatted?(@out, true).should be true
|
144
141
|
end
|
145
142
|
|
146
|
-
it 'should
|
143
|
+
it 'should mutate keyframes into deltaframes' do
|
147
144
|
a = AviGlitch.open @in
|
148
|
-
a.
|
145
|
+
a.mutate_keyframes_into_deltaframes!
|
149
146
|
a.output @out
|
150
147
|
a = AviGlitch.open @out
|
151
148
|
a.frames.each do |f|
|
@@ -153,7 +150,7 @@ describe AviGlitch do
|
|
153
150
|
end
|
154
151
|
|
155
152
|
a = AviGlitch.open @in
|
156
|
-
a.
|
153
|
+
a.mutate_keyframes_into_deltaframes! 0..50
|
157
154
|
a.output @out
|
158
155
|
a = AviGlitch.open @out
|
159
156
|
a.frames.each_with_index do |f, i|
|
@@ -172,4 +169,26 @@ describe AviGlitch do
|
|
172
169
|
a.has_keyframe?.should be false
|
173
170
|
end
|
174
171
|
|
172
|
+
it 'should #remove_all_keyframes!' do
|
173
|
+
a = AviGlitch.open @in
|
174
|
+
a.has_keyframe?.should be true
|
175
|
+
a.remove_all_keyframes!
|
176
|
+
a.has_keyframe?.should be false
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should count same number of specific frames' do
|
180
|
+
a = AviGlitch.open @in
|
181
|
+
dc1 = 0
|
182
|
+
dc2 = 0
|
183
|
+
a.frames.each do |f|
|
184
|
+
dc1 += 1 if f.is_deltaframe?
|
185
|
+
end
|
186
|
+
a.glitch(:deltaframe) do |d|
|
187
|
+
dc2 += 1
|
188
|
+
d
|
189
|
+
end
|
190
|
+
|
191
|
+
expect(dc1).to eq(dc2)
|
192
|
+
end
|
193
|
+
|
175
194
|
end
|
data/spec/datamosh_spec.rb
CHANGED
@@ -3,23 +3,12 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
3
|
describe AviGlitch, 'datamosh cli' do
|
4
4
|
|
5
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
6
|
here = File.dirname(__FILE__)
|
10
7
|
lib = Pathname.new(File.join(here, '..', 'lib')).realpath
|
11
8
|
datamosh = Pathname.new(File.join(here, '..', 'bin/datamosh')).realpath
|
12
9
|
@cmd = "ruby -I%s %s -o %s " % [lib, datamosh, @out]
|
13
10
|
end
|
14
11
|
|
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
12
|
it 'should correctly process files' do
|
24
13
|
a = AviGlitch.open @in
|
25
14
|
keys = a.frames.inject(0) do |c, f|
|
@@ -27,12 +16,13 @@ describe AviGlitch, 'datamosh cli' do
|
|
27
16
|
c
|
28
17
|
end
|
29
18
|
total = a.frames.size
|
19
|
+
first_keyframe = a.frames.index(a.frames.first_of(:keyframe))
|
30
20
|
a.close
|
31
21
|
|
32
22
|
system [@cmd, @in].join(' ')
|
33
23
|
o = AviGlitch.open @out
|
34
24
|
o.frames.size.should == total
|
35
|
-
o.frames.
|
25
|
+
o.frames[first_keyframe].is_keyframe?.should be true
|
36
26
|
o.has_keyframe?.should be true
|
37
27
|
o.close
|
38
28
|
AviGlitch::Base.surely_formatted?(@out, true).should be true
|
@@ -40,7 +30,7 @@ describe AviGlitch, 'datamosh cli' do
|
|
40
30
|
system [@cmd, '-a', @in].join(' ')
|
41
31
|
o = AviGlitch.open @out
|
42
32
|
o.frames.size.should == total
|
43
|
-
o.frames.
|
33
|
+
o.frames[first_keyframe].is_keyframe?.should be false
|
44
34
|
o.has_keyframe?.should be false
|
45
35
|
o.close
|
46
36
|
AviGlitch::Base.surely_formatted?(@out, true).should be true
|
@@ -48,7 +38,7 @@ describe AviGlitch, 'datamosh cli' do
|
|
48
38
|
system [@cmd, @in, @in, @in].join(' ')
|
49
39
|
o = AviGlitch.open @out
|
50
40
|
o.frames.size.should == total * 3
|
51
|
-
o.frames.
|
41
|
+
o.frames[first_keyframe].is_keyframe?.should be true
|
52
42
|
o.close
|
53
43
|
AviGlitch::Base.surely_formatted?(@out, true).should be true
|
54
44
|
|
data/spec/frames_spec.rb
CHANGED
@@ -5,26 +5,21 @@ describe AviGlitch::Frames do
|
|
5
5
|
before :all do
|
6
6
|
AviGlitch::Frames.class_eval do
|
7
7
|
define_method(:get_real_id_with) do |frame|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
movi = @avi.get_movi
|
9
|
+
pos = movi.pos
|
10
|
+
movi.pos -= frame.data.size
|
11
|
+
movi.pos -= 8
|
12
|
+
id = movi.read 4
|
13
|
+
movi.pos = pos
|
13
14
|
id
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
18
|
+
AviGlitch::Avi.class_eval do
|
19
|
+
define_method(:get_movi) do
|
20
|
+
@movi
|
21
|
+
end
|
22
|
+
end
|
28
23
|
end
|
29
24
|
|
30
25
|
it 'should save the same file when nothing is changed' do
|
@@ -85,15 +80,6 @@ describe AviGlitch::Frames do
|
|
85
80
|
avi.close
|
86
81
|
end
|
87
82
|
|
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
83
|
it 'should save video frames count in header' do
|
98
84
|
avi = AviGlitch.open @in
|
99
85
|
c = 0
|
@@ -460,9 +446,9 @@ describe AviGlitch::Frames do
|
|
460
446
|
end
|
461
447
|
end
|
462
448
|
|
463
|
-
it 'should
|
449
|
+
it 'should mutate keyframes into deltaframe' do
|
464
450
|
a = AviGlitch.open @in
|
465
|
-
a.frames.
|
451
|
+
a.frames.mutate_keyframes_into_deltaframes!
|
466
452
|
a.output @out
|
467
453
|
a = AviGlitch.open @out
|
468
454
|
a.frames.each do |f|
|
@@ -470,7 +456,7 @@ describe AviGlitch::Frames do
|
|
470
456
|
end
|
471
457
|
|
472
458
|
a = AviGlitch.open @in
|
473
|
-
a.frames.
|
459
|
+
a.frames.mutate_keyframes_into_deltaframes! 0..50
|
474
460
|
a.output @out
|
475
461
|
a = AviGlitch.open @out
|
476
462
|
a.frames.each_with_index do |f, i|
|
@@ -480,4 +466,104 @@ describe AviGlitch::Frames do
|
|
480
466
|
end
|
481
467
|
end
|
482
468
|
|
469
|
+
it 'should return Enumerator with #each' do
|
470
|
+
a = AviGlitch.open @in
|
471
|
+
enum = a.frames.each
|
472
|
+
enum.each do |f, i|
|
473
|
+
if f.is_keyframe?
|
474
|
+
f.data = f.data.gsub(/\d/, '')
|
475
|
+
end
|
476
|
+
end
|
477
|
+
a.output @out
|
478
|
+
AviGlitch::Base.surely_formatted?(@out, true).should be true
|
479
|
+
expect(File.size(@out)).to be < File.size(@in)
|
480
|
+
end
|
481
|
+
|
482
|
+
it 'should use Enumerator as an external iterator',
|
483
|
+
:skip => Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('1.9.0') || RUBY_PLATFORM == 'java' do
|
484
|
+
a = AviGlitch.open @in
|
485
|
+
e = a.frames.each
|
486
|
+
expect {
|
487
|
+
while f = e.next do
|
488
|
+
expect(f).to be_a(AviGlitch::Frame)
|
489
|
+
if f.is_keyframe?
|
490
|
+
f.data = f.data.gsub(/\d/, '')
|
491
|
+
end
|
492
|
+
end
|
493
|
+
}.to raise_error(StopIteration)
|
494
|
+
a.output @out
|
495
|
+
AviGlitch::Base.surely_formatted?(@out, true).should be true
|
496
|
+
expect(File.size(@out)).to be < File.size(@in)
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'should count the size of specific frames' do
|
500
|
+
a = AviGlitch.open @in
|
501
|
+
f = a.frames
|
502
|
+
|
503
|
+
kc1 = f.size_of :keyframes
|
504
|
+
kc2 = f.size_of :keyframe
|
505
|
+
kc3 = f.size_of :iframes
|
506
|
+
kc4 = f.size_of :iframe
|
507
|
+
|
508
|
+
dc1 = f.size_of :deltaframes
|
509
|
+
dc2 = f.size_of :deltaframe
|
510
|
+
dc3 = f.size_of :pframes
|
511
|
+
dc4 = f.size_of :pframe
|
512
|
+
|
513
|
+
vc1 = f.size_of :videoframes
|
514
|
+
vc2 = f.size_of :videoframe
|
515
|
+
|
516
|
+
ac1 = f.size_of :audioframes
|
517
|
+
ac2 = f.size_of :audioframe
|
518
|
+
|
519
|
+
kc = dc = vc = ac = 0
|
520
|
+
a.frames.each do |x|
|
521
|
+
vc += x.is_videoframe? ? 1 : 0
|
522
|
+
kc += x.is_keyframe? ? 1 : 0
|
523
|
+
dc += x.is_deltaframe? ? 1 : 0
|
524
|
+
ac += x.is_audioframe? ? 1 : 0
|
525
|
+
end
|
526
|
+
|
527
|
+
a.close
|
528
|
+
|
529
|
+
expect(kc1).to eq(kc)
|
530
|
+
expect(kc2).to eq(kc)
|
531
|
+
expect(kc3).to eq(kc)
|
532
|
+
expect(kc4).to eq(kc)
|
533
|
+
|
534
|
+
expect(dc1).to eq(dc)
|
535
|
+
expect(dc2).to eq(dc)
|
536
|
+
expect(dc3).to eq(dc)
|
537
|
+
expect(dc4).to eq(dc)
|
538
|
+
|
539
|
+
expect(vc1).to eq(vc)
|
540
|
+
expect(vc2).to eq(vc)
|
541
|
+
|
542
|
+
expect(ac1).to eq(ac)
|
543
|
+
expect(ac2).to eq(ac)
|
544
|
+
end
|
545
|
+
|
546
|
+
it 'should pick the first / last frame with a method' do
|
547
|
+
a = AviGlitch.open @in
|
548
|
+
fkidx = -1
|
549
|
+
lkidx = -1
|
550
|
+
faidx = -1
|
551
|
+
laidx = -1
|
552
|
+
a.frames.each_with_index do |f, i|
|
553
|
+
if f.is_keyframe?
|
554
|
+
fkidx = i if fkidx == -1
|
555
|
+
lkidx = i
|
556
|
+
end
|
557
|
+
if f.is_audioframe?
|
558
|
+
faidx = i if faidx == -1
|
559
|
+
laidx = i
|
560
|
+
end
|
561
|
+
end
|
562
|
+
a.frames.index(a.frames.first_of(:keyframe)).should eq(fkidx)
|
563
|
+
a.frames.rindex(a.frames.last_of(:keyframe)).should eq(lkidx)
|
564
|
+
a.frames.index(a.frames.first_of(:audioframe)).should eq(faidx)
|
565
|
+
a.frames.rindex(a.frames.last_of(:audioframe)).should eq(laidx)
|
566
|
+
a.close
|
567
|
+
end
|
568
|
+
|
483
569
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,10 +2,54 @@ require 'rspec'
|
|
2
2
|
require 'aviglitch'
|
3
3
|
require 'pathname'
|
4
4
|
require 'fileutils'
|
5
|
+
require 'net/http'
|
5
6
|
|
6
7
|
FILES_DIR = Pathname.new(File.dirname(__FILE__)).realpath + 'files'
|
7
8
|
OUTPUT_DIR = FILES_DIR + 'output'
|
8
9
|
|
9
10
|
RSpec.configure do |config|
|
10
|
-
|
11
|
+
config.filter_run_excluding :skip => true
|
12
|
+
config.expect_with :rspec do |c|
|
13
|
+
c.syntax = [:should, :expect]
|
14
|
+
end
|
15
|
+
|
16
|
+
config.before(:all) do
|
17
|
+
FileUtils.mkdir FILES_DIR unless File.exist? FILES_DIR
|
18
|
+
FileUtils.mkdir OUTPUT_DIR unless File.exist? OUTPUT_DIR
|
19
|
+
@in = FILES_DIR + 'sample1.avi'
|
20
|
+
@in2 = FILES_DIR + 'sample2.avi'
|
21
|
+
@out = OUTPUT_DIR + 'out.avi'
|
22
|
+
[
|
23
|
+
[@in2, 'http://a.ucnv.org/sample2.avi'], [@in, 'http://a.ucnv.org/sample1.avi']
|
24
|
+
].each do |file, url|
|
25
|
+
unless File.exist? file
|
26
|
+
if file == @in2
|
27
|
+
puts 'At first test it needs to download a file over 1GB. It will take a while.'
|
28
|
+
end
|
29
|
+
puts 'Downloading ' + url
|
30
|
+
$stdout.sync = true
|
31
|
+
u = URI.parse url
|
32
|
+
Net::HTTP.start(u.host, u.port) do |http|
|
33
|
+
res = http.request_head u.path
|
34
|
+
max = res['content-length'].to_i
|
35
|
+
len = 0
|
36
|
+
bl = 75
|
37
|
+
File.open(file, 'w') do |file|
|
38
|
+
http.get(u.path) do |chunk|
|
39
|
+
file.write chunk
|
40
|
+
len += chunk.length
|
41
|
+
pct = '%3.1f' % (100.0 * len / max)
|
42
|
+
bar = ('#' * (bl * len / max)).ljust(bl)
|
43
|
+
print "\r#{bar} #{'%5s' % pct}%" unless ENV['CI']
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
puts
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
config.after(:each) do
|
53
|
+
FileUtils.rm Dir.glob((OUTPUT_DIR + '*').to_s)
|
54
|
+
end
|
11
55
|
end
|