Rubyでflvからmp3を抜き取ってみた

余計な機能がついてるのは別のライブラリの一部として作ったため

require 'stringio'

StringIO.class_eval do
  def read_format(size, format)
    bin = read(size)
    bin = "\000" + bin if size == 3
    bin.unpack(format).first
  end
end

module Video
  class WrongFormatError < StandardError; end
  class Flv
    attr_accessor :body, :title
    def initialize
      @header = {}
    end
    
    def save(path)
      File.open(path, 'wb') do |f|
        f.write @body
      end
    end

    def mp3
      mp3 = MP3.new
      mp3.body = slice_mp3_part
      mp3
    end

    def slice_mp3_part
      io = StringIO.new(@body.slice(9..-1))
      binary = ''
      loop do
        part = {}
        [[:previous_tag_size, 4, 'N'],
         [:type, 1],
         [:body_length, 3, 'N'],
         [:timestamp, 3,   'N'],
         [:timestamp_extended, 1, 'c*'],
         [:steram_id, 3, 'c*']].each do |name, size, format|
          if io.eof?
            return binary
          end
          value = (format) ? io.read_format(size, format) : io.read(size)
          part[name] = value
        end

        if part[:type] == [0x08].pack('c')
          io.pos += 1
          binary += io.read(part[:body_length] - 1)
        else
          io.pos += part[:body_length]
        end
      end
    end

    class MP3
      attr_accessor :body
      def save(path)
        File.open(path, 'wb') do |f|
          f.write(@body)
        end
      end
    end
    
  end
end

使い方は

require 'video'
video = Video::Flv.new
File.open(path_to_flv) do |f|
  video.body = f.read
end
video.mp3.save(path_to_save)

みたいな感じ。
使い方のほうは検証してないのでアシカラズ