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