aviglitch 0.0.1

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