require 'tempfile' require 'fileutils' require 'aviglitch/frame' require 'aviglitch/frames' # AviGlitch provides the ways to glitch AVI formatted video files. # # == Synopsis: # # You can manipulate each frame, like: # # avi = Aviglitch.new '/path/to/your.avi' # avi.frames.each |frame| # if frame.is_keyframe? # frame.data = frame.data.gsub(/\d/, '0') # end # end # avi.write '/path/to/broken.avi' # # Using the method glitch, it can be written like: # # avi = AviGlitch.new '/path/to/your.avi' # avi.glitch(:keyframe) do |data| # data.gsub(/\d/, '0') # end # avi.write '/path/to/broken.avi' # #-- # It does not support AVI2, interleave format. # class AviGlitch VERSION = '0.0.1' # AviGlitch::Frames object generated from the +file+. attr_reader :frames # The input file (copied tempfile). attr_reader :file ## # Create new instance of AviGlitch, open the file and # make it ready to manipulate. # It requires +path+ as String or Pathname. def initialize path File.open(path) do |f| # copy as tempfile @file = Tempfile.open 'aviglitch' f.rewind while d = f.read(1024) do @file.print d end end unless AviGlitch.surely_formatted? @file raise 'Unsupported file passed.' end @frames = Frames.new @file # I believe Ruby's GC to close and remove the Tempfile.. end ## # Output the glitched file to +path+, and close the file. def write path FileUtils.cp @file.path, path @file.close true end ## # Glitch each frame data. # It is a convent method to iterate each frame. # # The argument +target+ takes symbols listed below: # [:keyframe] select video key frames (aka I-frame) # [:deltaframe] select video delta frames (difference frames) # [:videoframe] select both of keyframe and deltaframe # [:audioframe] select audio frames # [:all] select all frames # # It also requires a block. In the block, you take the frame data # as a String parameter. # To modify the data, simply return a modified data. def glitch target = :all, &block # :yield: data frames.each do |frame| if valid_target? target, frame frame.data = yield frame.data end end end ## # Do glitch with index. def glitch_with_index target = :all, &block # :yield: data, index i = 0 frames.each do |frame| if valid_target? target, frame frame.data = yield(frame.data, i) i += 1 end end end alias :output :write def valid_target? target, frame # :nodoc: return true if target == :all begin frame.send "is_#{target.to_s}?" rescue false end end private_instance_methods :valid_target? class << self ## # Check if the +file+ is a correctly formetted AVI file. # +file+ can be String or Pathname or IO. def surely_formatted? file, debug = false answer = true is_io = file.respond_to?(:seek) # Probably IO. file = File.open(file) unless is_io begin file.seek 0, IO::SEEK_END eof = file.pos file.rewind unless file.read(4) == 'RIFF' answer = false warn 'RIFF sign is not found' if debug end len = file.read(4).unpack('V').first unless len + 8 == eof answer = false warn 'Length info is invalid' if debug end unless file.read(4) == 'AVI ' answer = false warn 'AVI sign is not found' if debug end while file.read(4) =~ /^(?:LIST|JUNK)$/ do s = file.read(4).unpack('V').first file.pos += s end file.pos -= 4 # we require idx1 unless file.read(4) == 'idx1' answer = false warn 'idx1 is not found' if debug end s = file.read(4).unpack('V').first file.pos += s rescue => err warn err.message if debug answer = false ensure file.close unless is_io end answer end alias :open :new end end