ucnv-aviglitch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 / 2009-08-01
2
+
3
+ * initial release
4
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 ucnv
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,34 @@
1
+ = AviGlitch
2
+
3
+ * http://github.com/ucnv/aviglitch/tree/master
4
+
5
+ == Description:
6
+
7
+ AviGlitch destroys your AVI files.
8
+
9
+ I can't explain why they're going to destroy their own data.
10
+ See wikipedia http://en.wikipedia.org/wiki/Compression_artifact
11
+
12
+ == Synopsis:
13
+
14
+ require 'aviglitch'
15
+
16
+ avi = AviGlitch.new '/path/to/your.avi'
17
+ avi.glitch :keyframe do |data|
18
+ data.gsub(/\d/, '0')
19
+ end
20
+ avi.write '/path/to/broken.avi'
21
+
22
+ This library also contains a command tool named +datamoshing+.
23
+ It creates the keyframes removed video.
24
+
25
+ $ datamoshing /path/to/your.avi /path/to/broken.avi
26
+
27
+ == Installation:
28
+
29
+ gem sources -a http://gems.github.com
30
+ sudo gem install ucnv-aviglitch
31
+
32
+ == Copyright:
33
+
34
+ Copyright (c) 2009 ucnv. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "aviglitch"
8
+ gem.summary = "A Ruby library to destroy your AVI files."
9
+ gem.email = "ucnvvv@gmail.com"
10
+ gem.homepage = "http://github.com/ucnv/aviglitch"
11
+ gem.authors = ["ucnv"]
12
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
13
+ gem.files = %w(README.rdoc ChangeLog Rakefile VERSION) +
14
+ Dir.glob("{bin,spec,lib}/**/*")
15
+
16
+ end
17
+
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION.yml')
40
+ config = YAML.load(File.read('VERSION.yml'))
41
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
42
+ else
43
+ version = ""
44
+ end
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "aviglitch #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
51
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/datamoshing ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # Generate datamoshing
3
+
4
+ require 'rubygems'
5
+ require 'aviglitch'
6
+
7
+ if ARGV.size < 1 || ARGV.first == '--help'
8
+ puts <<-BANNER.gsub(/^\s+/, '')
9
+ Usage: #{File.basename $0} INPUT [OUTPUT]
10
+ Generate a datamoshing video from INPUT to OUTPUT (./out.avi by default).
11
+ BANNER
12
+ exit 0
13
+ end
14
+
15
+ input, output = ARGV
16
+
17
+ a = AviGlitch.new input
18
+ a.glitch_with_index :keyframe do |frame, i|
19
+ if i == 0 # keep the first frame
20
+ frame
21
+ else
22
+ "\000" * frame.size
23
+ end
24
+ end
25
+ a.write(output || 'out.avi')
data/lib/aviglitch.rb ADDED
@@ -0,0 +1,164 @@
1
+ require 'tempfile'
2
+ require 'fileutils'
3
+ require 'aviglitch/frame'
4
+ require 'aviglitch/frames'
5
+
6
+ # AviGlitch provides the ways to glitch AVI formatted video files.
7
+ #
8
+ # == Synopsis:
9
+ #
10
+ # You can manipulate each frame, like:
11
+ #
12
+ # avi = Aviglitch.new '/path/to/your.avi'
13
+ # avi.frames.each |frame|
14
+ # if frame.is_keyframe?
15
+ # frame.data = frame.data.gsub(/\d/, '0')
16
+ # end
17
+ # end
18
+ # avi.write '/path/to/broken.avi'
19
+ #
20
+ # Using the method glitch, it can be written like:
21
+ #
22
+ # avi = AviGlitch.new '/path/to/your.avi'
23
+ # avi.glitch(:keyframe) do |data|
24
+ # data.gsub(/\d/, '0')
25
+ # end
26
+ # avi.write '/path/to/broken.avi'
27
+ #
28
+ #--
29
+ # It does not support AVI2, interleave format.
30
+ #
31
+ class AviGlitch
32
+
33
+ VERSION = '0.0.1'
34
+
35
+ # AviGlitch::Frames object generated from the +file+.
36
+ attr_reader :frames
37
+ # The input file (copied tempfile).
38
+ attr_reader :file
39
+
40
+ ##
41
+ # Create new instance of AviGlitch, open the file and
42
+ # make it ready to manipulate.
43
+ # It requires +path+ as String or Pathname.
44
+ def initialize path
45
+ File.open(path) do |f|
46
+ # copy as tempfile
47
+ @file = Tempfile.open 'aviglitch'
48
+ f.rewind
49
+ while d = f.read(1024) do
50
+ @file.print d
51
+ end
52
+ end
53
+
54
+ unless AviGlitch.surely_formatted? @file
55
+ raise 'Unsupported file passed.'
56
+ end
57
+ @frames = Frames.new @file
58
+ # I believe Ruby's GC to close and remove the Tempfile..
59
+ end
60
+
61
+ ##
62
+ # Output the glitched file to +path+, and close the file.
63
+ def write path
64
+ FileUtils.cp @file.path, path
65
+ @file.close true
66
+ end
67
+
68
+ ##
69
+ # Glitch each frame data.
70
+ # It is a convent method to iterate each frame.
71
+ #
72
+ # The argument +target+ takes symbols listed below:
73
+ # [<tt>:keyframe</tt>] select video key frames (aka I-frame)
74
+ # [<tt>:deltaframe</tt>] select video delta frames (difference frames)
75
+ # [<tt>:videoframe</tt>] select both of keyframe and deltaframe
76
+ # [<tt>:audioframe</tt>] select audio frames
77
+ # [<tt>:all</tt>] select all frames
78
+ #
79
+ # It also requires a block. In the block, you take the frame data
80
+ # as a String parameter.
81
+ # To modify the data, simply return a modified data.
82
+ def glitch target = :all, &block # :yield: data
83
+ frames.each do |frame|
84
+ if valid_target? target, frame
85
+ frame.data = yield frame.data
86
+ end
87
+ end
88
+ end
89
+
90
+ ##
91
+ # Do glitch with index.
92
+ def glitch_with_index target = :all, &block # :yield: data, index
93
+ i = 0
94
+ frames.each do |frame|
95
+ if valid_target? target, frame
96
+ frame.data = yield(frame.data, i)
97
+ i += 1
98
+ end
99
+ end
100
+ end
101
+
102
+ alias :output :write
103
+
104
+ def valid_target? target, frame # :nodoc:
105
+ return true if target == :all
106
+ begin
107
+ frame.send "is_#{target.to_s}?"
108
+ rescue
109
+ false
110
+ end
111
+ end
112
+
113
+ private_instance_methods :valid_target?
114
+ class << self
115
+
116
+ ##
117
+ # Check if the +file+ is a correctly formetted AVI file.
118
+ # +file+ can be String or Pathname or IO.
119
+ def surely_formatted? file, debug = false
120
+ answer = true
121
+ is_io = file.respond_to?(:seek) # Probably IO.
122
+ file = File.open(file) unless is_io
123
+ begin
124
+ file.seek 0, IO::SEEK_END
125
+ eof = file.pos
126
+ file.rewind
127
+ unless file.read(4) == 'RIFF'
128
+ answer = false
129
+ warn 'RIFF sign is not found' if debug
130
+ end
131
+ len = file.read(4).unpack('V').first
132
+ unless len + 8 == eof
133
+ answer = false
134
+ warn 'Length info is invalid' if debug
135
+ end
136
+ unless file.read(4) == 'AVI '
137
+ answer = false
138
+ warn 'AVI sign is not found' if debug
139
+ end
140
+ while file.read(4) =~ /^(?:LIST|JUNK)$/ do
141
+ s = file.read(4).unpack('V').first
142
+ file.pos += s
143
+ end
144
+ file.pos -= 4
145
+ # we require idx1
146
+ unless file.read(4) == 'idx1'
147
+ answer = false
148
+ warn 'idx1 is not found' if debug
149
+ end
150
+ s = file.read(4).unpack('V').first
151
+ file.pos += s
152
+ rescue => err
153
+ warn err.message if debug
154
+ answer = false
155
+ ensure
156
+ file.close unless is_io
157
+ end
158
+ answer
159
+ end
160
+
161
+ alias :open :new
162
+
163
+ end
164
+ end
@@ -0,0 +1,57 @@
1
+ class AviGlitch
2
+
3
+ #
4
+ # Frame is the struct of the frame data and meta-data.
5
+ # You can access this class through AviGlitch::Frames.
6
+ # To modify the binary data, operate the +data+ property.
7
+ class Frame
8
+
9
+ AVIIF_LIST = 0x00000001
10
+ AVIIF_KEYFRAME = 0x00000010
11
+ AVIIF_NO_TIME = 0x00000100
12
+
13
+ attr_accessor :data
14
+ attr_reader :id, :flag
15
+
16
+ ##
17
+ # Create new AviGlitch::Frame object.
18
+ #
19
+ # The arguments are:
20
+ # [+data+] just data, without meta-data
21
+ # [+id+] id for the stream number and content type code
22
+ # (like "00dc")
23
+ # [+flag+] flag that describes the chunk type (taken from idx1)
24
+ #
25
+ def initialize data, id, flag
26
+ @data = data
27
+ @id = id
28
+ @flag = flag
29
+ end
30
+
31
+ ##
32
+ # Returns if it is a video frame and also a key frame.
33
+ def is_keyframe?
34
+ is_videoframe? && @flag & AVIIF_KEYFRAME != 0
35
+ end
36
+
37
+ ##
38
+ # Returns if it is a video frame and also not a key frame.
39
+ def is_deltaframe?
40
+ is_videoframe? && @flag & AVIIF_KEYFRAME == 0
41
+ end
42
+
43
+ ##
44
+ # Returns if it is a video frame.
45
+ def is_videoframe?
46
+ @id[2, 2] == 'db' || @id[2, 2] == 'dc'
47
+ end
48
+
49
+ ##
50
+ # Returns if it is an audio frame.
51
+ def is_audioframe?
52
+ @id[2, 2] == 'wb'
53
+ end
54
+
55
+ end
56
+ end
57
+
@@ -0,0 +1,97 @@
1
+ class AviGlitch
2
+
3
+ # Frames provides the interface to access each frame
4
+ # in the AVI file.
5
+ # It is implemented as Enumerable. You can access this object
6
+ # through AviGlitch#frames, for example:
7
+ #
8
+ # avi = AviGlitch.new '/path/to/your.avi'
9
+ # frames = avi.frames
10
+ # frames.each do |frame|
11
+ # ## frame is a reference of a AviGlitch::Frame object
12
+ # frame.data = frame.data.gsub(/\d/, '0')
13
+ # end
14
+ #
15
+ # In the block passed into iteration method, the parameter is the reference
16
+ # of AviGlitch::Frame object.
17
+ #
18
+ class Frames
19
+ include Enumerable
20
+
21
+ def initialize io
22
+ io.rewind
23
+ io.pos = 12 # /^RIFF[\s\S]{4}AVI $/
24
+ while io.read(4) =~ /^(?:LIST|JUNK)$/ do
25
+ s = io.read(4).unpack('V').first
26
+ @pos_of_movi = io.pos - 4 if io.read(4) == 'movi'
27
+ io.pos += s - 4
28
+ end
29
+ @pos_of_idx1 = io.pos - 4 # here must be idx1
30
+ s = io.read(4).unpack('V').first + io.pos
31
+ @meta = []
32
+ while chunk_id = io.read(4) do
33
+ break if io.pos >= s
34
+ @meta << {
35
+ :id => chunk_id,
36
+ :flag => io.read(4).unpack('V').first,
37
+ :offset => io.read(4).unpack('V').first,
38
+ :size => io.read(4).unpack('V').first,
39
+ }
40
+ end
41
+ io.rewind
42
+ @io = io
43
+ end
44
+
45
+ def each
46
+ temp = Tempfile.new 'frames'
47
+ temp.print 'movi'
48
+ @meta = @meta.select do |m|
49
+ @io.pos = @pos_of_movi + m[:offset] + 8
50
+ 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
54
+ 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
59
+ true
60
+ else
61
+ false
62
+ end
63
+ end
64
+
65
+ # 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
70
+ @io.print d
71
+ end
72
+ temp.close true
73
+ @io.print 'idx1'
74
+ @io.print [@meta.size * 16].pack('V')
75
+ @meta.each do |m|
76
+ @io.print m[:id]
77
+ @io.print [m[:flag], m[:offset], m[:size]].pack('V3')
78
+ end
79
+ eof = @io.pos
80
+ @io.truncate eof
81
+
82
+ # Fix info
83
+ ## file size
84
+ @io.pos = 4
85
+ @io.print [eof - 8].pack('V')
86
+ ## frame count
87
+ @io.pos = 48
88
+ @io.print [@meta.size].pack('V')
89
+
90
+ end
91
+
92
+ def size
93
+ @meta.size
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,109 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe AviGlitch 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
+ end
10
+
11
+ after :each do
12
+ FileUtils.rm Dir.glob(OUTPUT_DIR + '*')
13
+ end
14
+
15
+ after :all do
16
+ FileUtils.rmdir OUTPUT_DIR
17
+ end
18
+
19
+ it 'raise an error against unsupported files' do
20
+ lambda {
21
+ avi = AviGlitch.new __FILE__
22
+ }.should raise_error
23
+ end
24
+
25
+ it 'saves the same file when nothing is changed' do
26
+ avi = AviGlitch.new @in
27
+ avi.frames.each do |f|
28
+ ;
29
+ end
30
+ avi.write @out
31
+ FileUtils.cmp(@in, @out).should be true
32
+
33
+ avi = AviGlitch.new @in
34
+ avi.glitch do |d|
35
+ d
36
+ end
37
+ avi.write @out
38
+ FileUtils.cmp(@in, @out).should be true
39
+ end
40
+
41
+ it 'can manipulate each frame' do
42
+ avi = AviGlitch.new @in
43
+ f = avi.frames
44
+ f.should be_kind_of Enumerable
45
+ avi.frames.each do |f|
46
+ if f.is_keyframe?
47
+ f.data = f.data.gsub(/\d/, '0')
48
+ end
49
+ end
50
+ avi.write @out
51
+ AviGlitch.surely_formatted?(@out, true).should be true
52
+ end
53
+
54
+ it 'can glitch each keyframe' do
55
+ avi = AviGlitch.new @in
56
+ n = 0
57
+ avi.glitch :keyframe do |kf|
58
+ n += 1
59
+ kf.slice(10..kf.size)
60
+ end
61
+ avi.write @out
62
+ i_size = File.stat(@in).size
63
+ o_size = File.stat(@out).size
64
+ o_size.should == i_size - (10 * n)
65
+ AviGlitch.surely_formatted?(@out, true).should be true
66
+ end
67
+
68
+ it 'can glitch each keyframe with index' do
69
+ avi = AviGlitch.new @in
70
+ avi.glitch_with_index :keyframe do |kf, idx|
71
+ if idx < 25
72
+ kf.slice(10..kf.size)
73
+ else
74
+ kf
75
+ end
76
+ end
77
+ avi.write @out
78
+ i_size = File.stat(@in).size
79
+ o_size = File.stat(@out).size
80
+ o_size.should == i_size - (10 * 25)
81
+ AviGlitch.surely_formatted?(@out, true).should be true
82
+ end
83
+
84
+ it 'can remove a frame with returning nil' do
85
+ avi = AviGlitch.new @in
86
+ in_frame_size = avi.frames.size
87
+ rem_count = 0
88
+ avi.glitch :keyframe do |kf|
89
+ rem_count += 1
90
+ nil
91
+ end
92
+ avi.write @out
93
+ AviGlitch.surely_formatted?(@out, true).should be true
94
+
95
+ # frames length in the output file is correct
96
+ avi = AviGlitch.new @out
97
+ out_frame_size = avi.frames.size
98
+ out_frame_size.should == in_frame_size - rem_count
99
+ end
100
+
101
+ it 'has some alias methods' do
102
+ lambda {
103
+ avi = AviGlitch.open @in
104
+ avi.output @out
105
+ }.should_not raise_error
106
+ AviGlitch.surely_formatted?(@out, true).should be true
107
+ end
108
+
109
+ end
Binary file
@@ -0,0 +1,14 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'aviglitch'
6
+ require 'pathname'
7
+ require 'fileutils'
8
+
9
+ FILES_DIR = Pathname.new(File.dirname(__FILE__)).realpath + 'files'
10
+ OUTPUT_DIR = FILES_DIR + 'output'
11
+
12
+ Spec::Runner.configure do |config|
13
+
14
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ucnv-aviglitch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - ucnv
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-02 00:00:00 -07:00
13
+ default_executable: datamoshing
14
+ dependencies: []
15
+
16
+ description:
17
+ email: ucnvvv@gmail.com
18
+ executables:
19
+ - datamoshing
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - ChangeLog
24
+ - LICENSE
25
+ - README.rdoc
26
+ files:
27
+ - ChangeLog
28
+ - README.rdoc
29
+ - Rakefile
30
+ - VERSION
31
+ - bin/datamoshing
32
+ - lib/aviglitch.rb
33
+ - lib/aviglitch/frame.rb
34
+ - lib/aviglitch/frames.rb
35
+ - spec/aviglitch_spec.rb
36
+ - spec/files/sample.avi
37
+ - spec/spec_helper.rb
38
+ - LICENSE
39
+ has_rdoc: false
40
+ homepage: http://github.com/ucnv/aviglitch
41
+ licenses:
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --charset=UTF-8
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.3.5
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: A Ruby library to destroy your AVI files.
66
+ test_files:
67
+ - spec/aviglitch_spec.rb
68
+ - spec/spec_helper.rb