ogginfo-rb 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+