Class: Ogg::Decoder

Inherits:
Object
  • Object
show all
Defined in:
lib/ogg.rb

Overview

Ogg::Decoder is used to decode Ogg bitstreams. The easiest way of properly parsing an Ogg file is to read consecutive packets with the read_packet method. For example:

require 'ogg'

open("file.ogg", "rb") do |file|
  dec = Ogg::Decoder.new(file)
  packet = dec.read_packet
  # Do something with the packet...
end

The terms “page” and “packet” have special meanings when dealing with Ogg. A packet is a section of data which is encoded in the Ogg container. A page is a section of the Ogg container used as a means of storing packets. Since packets are what contain the “juicy bits” of the file, Ogg::Decoder provides sufficient abstraction to make handling of individual pages unnecessary. However, if you do need to read pages, that functionality is available via the read_page method.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io, opts = {}) ⇒ Decoder

Create a new Decoder from an IO which should be open for binary reading.



40
41
42
43
44
45
46
47
48
49
# File 'lib/ogg.rb', line 40

def initialize io, opts={}
  @io = io
  @packets = []
  
  opts = {
    :verify_checksum => false,
  }.merge!(Hash[opts.map {|k, v| [k.to_s.downcase.to_sym, v]}])
  
  @verify_checksum = opts[:verify_checksum]
end

Instance Attribute Details

#verify_checksumObject

This property determines whether the integrity of pages should be checked using CRCs (a lot slower but more robust).



37
38
39
# File 'lib/ogg.rb', line 37

def verify_checksum
  @verify_checksum
end

Instance Method Details

#read_last_pageObject

Seek to and read the last page in the bitstream.

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ogg.rb', line 86

def read_last_page
  raise 'Last page can only be read from a file stream' unless @io.is_a? File
			buffer_size = 1024
			pos = @io.stat.size - buffer_size
			while pos > 0
@io.seek pos, IO::SEEK_SET
sio = StringIO.new @io.read(buffer_size)

dec = Decoder.new(sio)
sub_pos = nil

# Find last page in buffer
    loop do
      begin
        sub_pos = dec.seek_to_page
        sio.pos += 1
      rescue
        break
      end
    end

    if sub_pos
      @io.seek(pos + sub_pos, IO::SEEK_SET)
      page = read_page
      return page
    end
    
pos -= buffer_size * 2 - ('OggS'.size - 1)
			end
			
			# This means that the Ogg file contains no pages
			raise MalformedFileError
end

#read_packetObject

Seek to and read the next packet in the bitstream. Returns a string containing the packet’s binary data or nil if there are no packets left.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/ogg.rb', line 123

def read_packet
  return @packets.pop unless @packets.empty?
  
  while @packets.empty?
    page = read_page
    raise EOFError.new("End of file reached") if page.nil?
    input = StringIO.new(page.data)
    
    page.segment_table.each do |seg|
      @partial ||= ""
      
      @partial << input.read(seg)
      if seg != 255
        @packets.insert(0, @partial)
        @partial = nil
      end
    end
  end
  
  return @packets.pop
end

#read_pageObject

Seek to and read the next page from the bitstream. Returns a Page or nil if there are no pages left.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/ogg.rb', line 70

def read_page
  page = nil
  while not @io.eof?
    begin
      seek_to_page
      page = Page.read @io
      page = nil if @verify_checksum and not page.correct_checksum?
      break
    rescue Exception => ex
      # False alarm, keep looking...
    end
  end
  return page
end

#seek_to_page(capture = 'OggS') ⇒ Object

Moves the file cursor forward to the next potential page. You probably wish to use the read_page method, which does some validation and actually returns the parsed page.

Raises:

  • (EOFError)


54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ogg.rb', line 54

def seek_to_page(capture='OggS')
  buffer = @io.read(capture.size)
  page = nil
  while not @io.eof?
    if buffer == capture
      @io.pos -= capture.size
      return @io.pos
    end
    (buffer = buffer[1..-1] << @io.read(1)) rescue Exception
  end
  
  raise EOFError
end