lib/aviglitch/frames.rb in aviglitch-0.1.6 vs lib/aviglitch/frames.rb in aviglitch-0.2.0

- old
+ new

@@ -16,175 +16,108 @@ # of AviGlitch::Frame object. # class Frames include Enumerable - # :stopdoc: + attr_reader :avi ## - SAFE_FRAMES_COUNT = 150000 - @@warn_if_frames_are_too_large = true - - # :startdoc: - - attr_reader :meta - - ## # Creates a new AviGlitch::Frames object. - def initialize io - io.rewind - io.pos = 12 # /^RIFF[\s\S]{4}AVI $/ - while io.read(4) =~ /^(?:LIST|JUNK)$/ do - s = io.read(4).unpack('V').first - @pos_of_movi = io.pos - 4 if io.read(4) == 'movi' - io.pos += s - 4 - end - @pos_of_idx1 = io.pos - 4 # here must be idx1 - s = io.read(4).unpack('V').first + io.pos - @meta = [] - while chunk_id = io.read(4) do - break if io.pos >= s - @meta << { - :id => chunk_id, - :flag => io.read(4).unpack('V').first, - :offset => io.read(4).unpack('V').first, - :size => io.read(4).unpack('V').first, - } - end - fix_offsets_if_needed io - unless safe_frames_count? @meta.size - io.close! - exit - end - io.rewind - @io = io + def initialize avi + @avi = avi end ## # Enumerates the frames. # It returns Enumerator if a block is not given. def each &block if block_given? - temp = Tempfile.new 'frames', binmode: true - frames_data_as_io(temp, block) - overwrite temp - temp.close! + Tempfile.open('temp', binmode: true) do |newmovi| + @avi.process_movi do |indices, movi| + newindices = indices.select do |m| + movi.pos = m[:offset] + 8 # 8 for id and size + frame = Frame.new(movi.read(m[:size]), m[:id], m[:flag]) + block.call frame + unless frame.data.nil? + m[:offset] = newmovi.pos + m[:size] = frame.data.size + m[:flag] = frame.flag + m[:id] = frame.id + newmovi.print m[:id] + newmovi.print [frame.data.size].pack('V') + newmovi.print frame.data + newmovi.print "\0" if frame.data.size % 2 == 1 + true + else + false + end + end + [newindices, newmovi] + end + end else self.enum_for :each end end ## # Returns the number of frames. def size - @meta.size + @avi.indices.size end ## # Returns the number of the specific +frame_type+. def size_of frame_type - detection = "is_#{frame_type.to_s.sub(/frames$/, 'frame')}?" - @meta.select { |m| - Frame.new(nil, m[:id], m[:flag]).send detection + @avi.indices.select { |m| + Frame.new(nil, m[:id], m[:flag]).is? frame_type }.size end - def frames_data_as_io io = nil, block = nil #:nodoc: - io = Tempfile.new('tmep', binmode: true) if io.nil? - @meta = @meta.select do |m| - @io.pos = @pos_of_movi + m[:offset] + 8 # 8 for id and size - frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag]) - block.call(frame) if block # accept the variable block as Proc - yield frame if block_given? # or a given block (or do nothing) - unless frame.data.nil? - m[:offset] = io.pos + 4 # 4 for 'movi' - m[:size] = frame.data.size - m[:flag] = frame.flag - m[:id] = frame.id - io.print m[:id] - io.print [frame.data.size].pack('V') - io.print frame.data - io.print "\000" if frame.data.size % 2 == 1 - true - else - false - end + ## + # Returns the data size of total frames. + def data_size + size = 0 + @avi.process_movi do |indices, movi| + size = movi.size + [indices, movi] end - io + size end - def overwrite data #:nodoc: - unless safe_frames_count? @meta.size - @io.close! - exit - end - # Overwrite the file - @io.pos = @pos_of_movi - 4 # 4 for size - @io.print [data.pos + 4].pack('V') # 4 for 'movi' - @io.print 'movi' - data.rewind - while d = data.read(BUFFER_SIZE) do - @io.print d - end - @io.print 'idx1' - @io.print [@meta.size * 16].pack('V') - idx = @meta.collect { |m| - m[:id] + [m[:flag], m[:offset], m[:size]].pack('V3') - }.join - @io.print idx - eof = @io.pos - @io.truncate eof - - # Fix info - ## file size - @io.pos = 4 - @io.print [eof - 8].pack('V') - ## frame count - @io.pos = 48 - vid_frames = @meta.select do |m| - id = m[:id] - id[2, 2] == 'db' || id[2, 2] == 'dc' - end - @io.print [vid_frames.size].pack('V') - - @io.pos - end - ## # Removes all frames and returns self. def clear - @meta = [] - overwrite StringIO.new + @avi.process_movi do |indices, movi| + [[], StringIO.new] + end self end ## # Appends the frames in the other Frames into the tail of self. # It is destructive like Array does. def concat other_frames raise TypeError unless other_frames.kind_of?(Frames) - # data - this_data = Tempfile.new 'this', binmode: true - self.frames_data_as_io this_data - other_data = Tempfile.new 'other', binmode: true - other_frames.frames_data_as_io other_data - this_size = this_data.size - other_data.rewind - while d = other_data.read(BUFFER_SIZE) do - this_data.print d + @avi.process_movi do |this_indices, this_movi| + this_size = this_movi.size + this_movi.pos = this_size + other_frames.avi.process_movi do |other_indices, other_movi| + while d = other_movi.read(BUFFER_SIZE) do + this_movi.print d + end + other_meta = other_indices.collect do |m| + x = m.dup + x[:offset] += this_size + x + end + this_indices.concat other_meta + [other_indices, other_movi] + end + [this_indices, this_movi] end - other_data.close! - # meta - other_meta = other_frames.meta.collect do |m| - x = m.dup - x[:offset] += this_size - x - end - @meta.concat other_meta - # close - overwrite this_data - this_data.close! + self end ## # Returns a concatenation of the two Frames as a new Frames instance. @@ -269,15 +202,20 @@ end ## # Returns one Frame object at the given index. def at n - m = @meta[n] - return nil if m.nil? - @io.pos = @pos_of_movi + m[:offset] + 8 - frame = Frame.new(@io.read(m[:size]), m[:id], m[:flag]) - @io.rewind + frame = nil + @avi.process_movi do |indices, movi| + m = indices[n] + unless m.nil? + movi.pos = m[:offset] + 8 + frame = Frame.new(movi.read(m[:size]), m[:id], m[:flag]) + movi.rewind + end + [indices, movi] + end frame end ## # Returns the first Frame object. @@ -290,31 +228,104 @@ def last self.slice(self.size - 1) end ## + # Returns the first Frame object in +frame_type+. + def first_of frame_type + frame = nil + @avi.process_movi do |indices, movi| + indices.each do |m| + movi.pos = m[:offset] + 8 + f = Frame.new(movi.read(m[:size]), m[:id], m[:flag]) + if f.is?(frame_type) + frame = f + break + end + end + [indices, movi] + end + frame + end + + ## + # Returns the last Frame object in +frame_type+. + def last_of frame_type + frame = nil + @avi.process_movi do |indices, movi| + indices.reverse.each do |m| + movi.pos = m[:offset] + 8 + f = Frame.new(movi.read(m[:size]), m[:id], m[:flag]) + if f.is?(frame_type) + frame = f + break + end + end + [indices, movi] + end + frame + end + + ## + # Returns an index of the first found +frame+. + def index frame + n = -1 + @avi.process_movi do |indices, movi| + indices.each_with_index do |m, i| + movi.pos = m[:offset] + 8 + f = Frame.new(movi.read(m[:size]), m[:id], m[:flag]) + if f == frame + n = i + break + end + end + [indices, movi] + end + n + end + + ## + # Alias for index + alias_method :find_index, :index + + ## + # Returns an index of the first found +frame+, starting from the last. + def rindex frame + n = -1 + @avi.process_movi do |indices, movi| + indices.reverse.each_with_index do |m, i| + movi.pos = m[:offset] + 8 + f = Frame.new(movi.read(m[:size]), m[:id], m[:flag]) + if f == frame + n = indices.size - 1 - i + break + end + end + [indices, movi] + end + n + end + + ## # Appends the given Frame into the tail of self. def push frame raise TypeError unless frame.kind_of? Frame - # data - this_data = Tempfile.new 'this', binmode: true - self.frames_data_as_io this_data - this_size = this_data.size - this_data.print frame.id - this_data.print [frame.data.size].pack('V') - this_data.print frame.data - this_data.print "\000" if frame.data.size % 2 == 1 - # meta - @meta << { - :id => frame.id, - :flag => frame.flag, - :offset => this_size + 4, # 4 for 'movi' - :size => frame.data.size, - } - # close - overwrite this_data - this_data.close! + @avi.process_movi do |indices, movi| + this_size = movi.size + movi.pos = this_size + movi.print frame.id + movi.print [frame.data.size].pack('V') + movi.print frame.data + movi.print "\0" if frame.data.size % 2 == 1 + indices << { + :id => frame.id, + :flag => frame.flag, + :offset => this_size, + :size => frame.data.size, + } + [indices, movi] + end self end ## # Alias for push @@ -353,66 +364,38 @@ end ## # Returns true if +other+'s frames are same as self's frames. def == other - @meta == other.meta + @avi == other.avi end ## # Generates new AviGlitch::Base instance using self. def to_avi - AviGlitch.open @io.path + AviGlitch::Base.new @avi.clone end - def inspect # :nodec: - "#<#{self.class.name}:#{sprintf("0x%x", object_id)} @io=#{@io.inspect} size=#{self.size}>" + def inspect #:nodoc: + "#<#{self.class.name}:#{sprintf("0x%x", object_id)} size=#{self.size}>" end def get_beginning_and_length *args #:nodoc: b, l = args if args.first.kind_of? Range r = args.first b = r.begin - e = r.end >= 0 ? r.end : @meta.size + r.end + e = r.end >= 0 ? r.end : self.size + r.end l = e - b + 1 end - b = b >= 0 ? b : @meta.size + b + b = b >= 0 ? b : self.size + b [b, l] end def safe_frames_count? count #:nodoc: - r = true - if @@warn_if_frames_are_too_large && count >= SAFE_FRAMES_COUNT - trap(:INT) do - @io.close! - exit - end - m = ["WARNING: The avi data has too many frames (#{count}).\n", - "It may use a large memory to process. ", - "We recommend to chop the movie to smaller chunks before you glitch.\n", - "Do you want to continue anyway? [yN] "].join('') - a = Readline.readline m - r = a == 'y' - @@warn_if_frames_are_too_large = !r - end - r + warn "[DEPRECATION] `safe_frames_count?` is deprecated." + true end - def fix_offsets_if_needed io #:nodoc: - # rarely data offsets begin from 0 of the file - return if @meta.empty? - pos = io.pos - m = @meta.first - io.pos = @pos_of_movi + m[:offset] - unless io.read(4) == m[:id] - @meta.each do |x| - x[:offset] -= @pos_of_movi - end - end - io.pos = pos - end - - protected :frames_data_as_io, :meta - private :overwrite, :get_beginning_and_length, :fix_offsets_if_needed + private :get_beginning_and_length end end