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,43 @@
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
+
11
+ See followig urls for details;
12
+ * vimeo http://www.vimeo.com/groups/artifacts
13
+ * wikipedia http://en.wikipedia.org/wiki/Compression_artifact
14
+
15
+ == Features/Problems
16
+
17
+ * Not supports AVI2 files right now.
18
+ * Not supports files with interleave.
19
+ * Parses only container level structure, not parses codecs.
20
+
21
+ == Synopsis
22
+
23
+ require 'aviglitch'
24
+
25
+ avi = AviGlitch.new('/path/to/your.avi')
26
+ avi.glitch(:keyframe) do |data|
27
+ data.gsub(/\d/, '0')
28
+ end
29
+ avi.write('/path/to/broken.avi')
30
+
31
+ This library also includes a command tool named +datamosh+.
32
+ It creates the keyframes removed video.
33
+
34
+ $ datamosh /path/to/your.avi /path/to/broken.avi
35
+
36
+ == Installation
37
+
38
+ gem sources -a http://gems.github.com
39
+ sudo gem install ucnv-aviglitch
40
+
41
+ == Copyright
42
+
43
+ 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/datamosh 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,170 @@
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 a 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
+ close
66
+ end
67
+
68
+ ##
69
+ # An explicit file close.
70
+ def close
71
+ @file.close!
72
+ end
73
+
74
+ ##
75
+ # Glitch each frame data.
76
+ # It is a convent method to iterate each frame.
77
+ #
78
+ # The argument +target+ takes symbols listed below:
79
+ # [<tt>:keyframe</tt> or <tt>:iframe</tt>] select video key frames (aka I-frame)
80
+ # [<tt>:deltaframe</tt> or <tt>:pframe</tt>] select video delta frames (difference frames)
81
+ # [<tt>:videoframe</tt>] select both of keyframe and deltaframe
82
+ # [<tt>:audioframe</tt>] select audio frames
83
+ # [<tt>:all</tt>] select all frames
84
+ #
85
+ # It also requires a block. In the block, you take the frame data
86
+ # as a String parameter.
87
+ # To modify the data, simply return a modified data.
88
+ def glitch target = :all, &block # :yield: data
89
+ frames.each do |frame|
90
+ if valid_target? target, frame
91
+ frame.data = yield frame.data
92
+ end
93
+ end
94
+ end
95
+
96
+ ##
97
+ # Do glitch with index.
98
+ def glitch_with_index target = :all, &block # :yield: data, index
99
+ i = 0
100
+ frames.each do |frame|
101
+ if valid_target? target, frame
102
+ frame.data = yield(frame.data, i)
103
+ i += 1
104
+ end
105
+ end
106
+ end
107
+
108
+ alias :output :write
109
+
110
+ def valid_target? target, frame # :nodoc:
111
+ return true if target == :all
112
+ begin
113
+ frame.send "is_#{target.to_s}?"
114
+ rescue
115
+ false
116
+ end
117
+ end
118
+
119
+ private_instance_methods :valid_target?
120
+ class << self
121
+
122
+ ##
123
+ # Check if the +file+ is a correctly formetted AVI file.
124
+ # +file+ can be String or Pathname or IO.
125
+ def surely_formatted? file, debug = false
126
+ answer = true
127
+ is_io = file.respond_to?(:seek) # Probably IO.
128
+ file = File.open(file) unless is_io
129
+ begin
130
+ file.seek 0, IO::SEEK_END
131
+ eof = file.pos
132
+ file.rewind
133
+ unless file.read(4) == 'RIFF'
134
+ answer = false
135
+ warn 'RIFF sign is not found' if debug
136
+ end
137
+ len = file.read(4).unpack('V').first
138
+ unless len + 8 == eof
139
+ answer = false
140
+ warn 'Length info is invalid' if debug
141
+ end
142
+ unless file.read(4) == 'AVI '
143
+ answer = false
144
+ warn 'AVI sign is not found' if debug
145
+ end
146
+ while file.read(4) =~ /^(?:LIST|JUNK)$/ do
147
+ s = file.read(4).unpack('V').first
148
+ file.pos += s
149
+ end
150
+ file.pos -= 4
151
+ # we require idx1
152
+ unless file.read(4) == 'idx1'
153
+ answer = false
154
+ warn 'idx1 is not found' if debug
155
+ end
156
+ s = file.read(4).unpack('V').first
157
+ file.pos += s
158
+ rescue => err
159
+ warn err.message if debug
160
+ answer = false
161
+ ensure
162
+ file.close unless is_io
163
+ end
164
+ answer
165
+ end
166
+
167
+ alias :open :new
168
+
169
+ end
170
+ end
@@ -0,0 +1,64 @@
1
+ class AviGlitch
2
+
3
+ # Frame is the struct of the frame data and meta-data.
4
+ # You can access this class through AviGlitch::Frames.
5
+ # To modify the binary data, operate the +data+ property.
6
+ class Frame
7
+
8
+ AVIIF_LIST = 0x00000001
9
+ AVIIF_KEYFRAME = 0x00000010
10
+ AVIIF_NO_TIME = 0x00000100
11
+
12
+ attr_accessor :data
13
+ attr_reader :id, :flag
14
+
15
+ ##
16
+ # Create a new AviGlitch::Frame object.
17
+ #
18
+ # The arguments are:
19
+ # [+data+] just data, without meta-data
20
+ # [+id+] id for the stream number and content type code
21
+ # (like "00dc")
22
+ # [+flag+] flag that describes the chunk type (taken from idx1)
23
+ #
24
+ def initialize data, id, flag
25
+ @data = data
26
+ @id = id
27
+ @flag = flag
28
+ end
29
+
30
+ ##
31
+ # Returns if it is a video frame and also a key frame.
32
+ def is_keyframe?
33
+ is_videoframe? && @flag & AVIIF_KEYFRAME != 0
34
+ end
35
+
36
+ ##
37
+ # Alias for is_keyframe?
38
+ alias :is_iframe? :is_keyframe?
39
+
40
+ ##
41
+ # Returns if it is a video frame and also not a key frame.
42
+ def is_deltaframe?
43
+ is_videoframe? && @flag & AVIIF_KEYFRAME == 0
44
+ end
45
+
46
+ ##
47
+ # Alias for is_deltaframe?
48
+ alias :is_pframe? :is_deltaframe?
49
+
50
+ ##
51
+ # Returns if it is a video frame.
52
+ def is_videoframe?
53
+ @id[2, 2] == 'db' || @id[2, 2] == 'dc'
54
+ end
55
+
56
+ ##
57
+ # Returns if it is an audio frame.
58
+ def is_audioframe?
59
+ @id[2, 2] == 'wb'
60
+ end
61
+
62
+ end
63
+ end
64
+
@@ -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 + '*').to_s)
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,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aviglitch
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - ucnv
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2009-08-02 00:00:00 +09:00
18
+ default_executable: datamosh
19
+ dependencies: []
20
+
21
+ description:
22
+ email: ucnvvv@gmail.com
23
+ executables:
24
+ - datamosh
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - ChangeLog
29
+ - LICENSE
30
+ - README.rdoc
31
+ files:
32
+ - ChangeLog
33
+ - README.rdoc
34
+ - Rakefile
35
+ - VERSION
36
+ - bin/datamosh
37
+ - lib/aviglitch.rb
38
+ - lib/aviglitch/frame.rb
39
+ - lib/aviglitch/frames.rb
40
+ - spec/aviglitch_spec.rb
41
+ - spec/files/sample.avi
42
+ - spec/spec_helper.rb
43
+ - LICENSE
44
+ has_rdoc: true
45
+ homepage: http://github.com/ucnv/aviglitch
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.3.6
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: A Ruby library to destroy your AVI files.
74
+ test_files:
75
+ - spec/aviglitch_spec.rb
76
+ - spec/spec_helper.rb