ogginfo-rb 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. data/lib/ogginfo-rb.rb +185 -0
  2. metadata +53 -0
data/lib/ogginfo-rb.rb ADDED
@@ -0,0 +1,185 @@
1
+ # Useful sites:
2
+ # http://en.wikipedia.org/wiki/Ogg#Page_structure
3
+ # http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-600004.2
4
+
5
+ require 'stringio'
6
+
7
+ module Ogg
8
+ class Info
9
+ attr_reader :comments
10
+ attr_reader :duration, :sample_rate, :nominal_bitrate, :channels, :bitrate
11
+
12
+ def initialize path
13
+ file = File.new(path.to_str, 'rb')
14
+
15
+ # Identification header
16
+ id_packet = Vorbis::IdentificationHeaderPacket.new(Page.find_page(file))
17
+ @sample_rate = id_packet.audio_sample_rate
18
+ @nominal_bitrate = id_packet.bitrate_nominal
19
+ @channels = id_packet.audio_channels
20
+
21
+ # Comment header
22
+ comment_packet = Vorbis::CommentHeaderPacket.new(id_packet.next_page)
23
+ @comments = comment_packet.comments
24
+
25
+ pos_after_headers = file.pos
26
+
27
+ begin
28
+ @duration = calculate_duration file
29
+ rescue Exception
30
+ @duration = 0
31
+ end
32
+
33
+ begin
34
+ @bitrate = (file.stat.size - pos_after_headers).to_f * 8 / @duration
35
+ rescue Exception
36
+ @bitrate = 0
37
+ end
38
+
39
+ file.close
40
+ end
41
+
42
+ def self.open(path)
43
+ info = Ogg::Info.new(path)
44
+ yield info if block_given?
45
+ return info
46
+ end
47
+
48
+ private
49
+ def calculate_duration file
50
+ # This seek is a dirty performance hack (saves reading whole file)
51
+ file.seek(-5000, IO::SEEK_END)
52
+ # Find last page
53
+ pg = page = Page.find_page(file)
54
+ while pg
55
+ page = pg
56
+ pg = pg.next_page
57
+ end
58
+ # Duration is last granule position divided by sample right
59
+ return page.granule_position.to_f / @sample_rate
60
+ end
61
+ end
62
+
63
+ class Page
64
+ CAPTURE = 'OggS'
65
+
66
+ attr_reader :data
67
+ attr_reader :segment_table, :granule_position, :page_type
68
+
69
+ def initialize file
70
+ @file = file
71
+
72
+ # Header
73
+ capture = file.read(CAPTURE.size)
74
+ puts "Capture is not #{CAPTURE.inspect}" unless capture == CAPTURE
75
+
76
+ version = file.read(1).unpack('C').first
77
+ @page_type = file.read(1).unpack('C').first
78
+ @granule_position = file.read(8).unpack('Q').first
79
+ bitstream_serial_number = file.read(4).unpack('L').first
80
+ page_sequence_number = file.read(4).unpack('L').first
81
+ checksum = file.read(4).unpack('L').first
82
+ page_segments = file.read(1).unpack('C').first
83
+ @segment_table = file.read(page_segments).unpack('C*')
84
+
85
+ # Content
86
+ @data = StringIO.new '', 'w+'
87
+ @segment_table.each do |n|
88
+ @data.write file.read(n)
89
+ end
90
+ @data.close_write
91
+ @data.rewind
92
+ end
93
+
94
+ def continues?
95
+ return @segment_table.last == 255
96
+ end
97
+
98
+ def next_page
99
+ return Page.find_page(@file)
100
+ end
101
+
102
+ def self.find_page file
103
+ buffer = file.read(CAPTURE.size)
104
+ while buffer != CAPTURE
105
+ return if file.eof?
106
+ buffer = buffer[1..-1] << file.read(1)
107
+ end
108
+ file.pos -= CAPTURE.size
109
+ return Page.new(file)
110
+ end
111
+ end
112
+
113
+ class Packet
114
+ attr_reader :data
115
+
116
+ def initialize page
117
+ @data = StringIO.new('', 'w+')
118
+ @data.write(page.data.read)
119
+ while page.continues?
120
+ page = page.next_page
121
+ @data.write(page.data.read)
122
+ end
123
+ @last_page = page
124
+ @data.close_write
125
+ @data.rewind
126
+ end
127
+
128
+ def next_page
129
+ return @last_page.next_page
130
+ end
131
+ end
132
+
133
+ module Vorbis
134
+ attr_reader :packet_type
135
+
136
+ class HeaderPacket < Ogg::Packet
137
+ def initialize page
138
+ super
139
+
140
+ @packet_type = @data.read(1).unpack('C').first
141
+ vorbis_check = @data.read(6)
142
+ unless vorbis_check == 'vorbis'
143
+ $stderr.puts "Warning: 'vorbis' string not found in header"
144
+ end
145
+ end
146
+ end
147
+
148
+ class IdentificationHeaderPacket < HeaderPacket
149
+ attr_reader :vorbis_version, :audio_channels, :audio_sample_rate
150
+ attr_reader :bitrate_maximum, :bitrate_nominal, :bitrate_minimum
151
+
152
+ def initialize page
153
+ super
154
+
155
+ @vorbis_version = @data.read(4).unpack('L').first
156
+ @audio_channels = @data.read(1).unpack('C').first
157
+ @audio_sample_rate = @data.read(4).unpack('L').first
158
+ @bitrate_maximum = @data.read(4).unpack('l').first
159
+ @bitrate_nominal = @data.read(4).unpack('l').first
160
+ @bitrate_minimum = @data.read(4).unpack('l').first
161
+ blocksizes = @data.read(1).unpack('C').first
162
+ end
163
+ end
164
+
165
+ class CommentHeaderPacket < HeaderPacket
166
+ attr_reader :comments, :vendor_string
167
+
168
+ def initialize page
169
+ super
170
+
171
+ vendor_length = @data.read(4).unpack('L').first
172
+ @vendor_string = @data.read(vendor_length)
173
+ user_comment_list_length = @data.read(4).unpack('L').first
174
+ @comments = {}
175
+
176
+ user_comment_list_length.times do
177
+ length = @data.read(4).unpack('L').first
178
+ comment = @data.read(length)
179
+ key, value = comment.split('=', 2)
180
+ (@comments[key.downcase] ||= []) << value
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ogginfo-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Aiden Nibali
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-09 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: dismal.denizen@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/ogginfo-rb.rb
26
+ has_rdoc: false
27
+ homepage: http://rubyforge.org/projects/ogginfo-rb/
28
+ post_install_message:
29
+ rdoc_options: []
30
+
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: "0"
38
+ version:
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ requirements: []
46
+
47
+ rubyforge_project: ogginfo-rb
48
+ rubygems_version: 1.3.1
49
+ signing_key:
50
+ specification_version: 2
51
+ summary: A library for accessing metadata in Ogg files
52
+ test_files: []
53
+