summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Everton <dan@iocaine.org>2007-12-09 03:16:26 +0000
committerDan Everton <dan@iocaine.org>2007-12-09 03:16:26 +0000
commit6fafb6cd989b449793c4ae065f5a97cab4b95230 (patch)
tree52aa957a3741b9648b6b6a07c0c4f1c0f8dc6832
parent44d8097dfa460e8c7135d1afe20ae5439ce4a141 (diff)
downloadrockbox-6fafb6cd989b449793c4ae065f5a97cab4b95230.tar.gz
rockbox-6fafb6cd989b449793c4ae065f5a97cab4b95230.zip
Unify the Ogg Vorbis and Ogg Speex metadata parsers.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15897 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/metadata.c4
-rw-r--r--apps/metadata/metadata_parsers.h3
-rw-r--r--apps/metadata/ogg.c265
3 files changed, 57 insertions, 215 deletions
diff --git a/apps/metadata.c b/apps/metadata.c
index 556350bfb6..3abbd74c35 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -134,7 +134,7 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
134 break; 134 break;
135 135
136 case AFMT_OGG_VORBIS: 136 case AFMT_OGG_VORBIS:
137 if (!get_vorbis_metadata(fd, id3))/*detects and handles Ogg/Speex files*/ 137 if (!get_ogg_metadata(fd, id3))/*detects and handles Ogg/Speex files*/
138 { 138 {
139 return false; 139 return false;
140 } 140 }
@@ -142,7 +142,7 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
142 break; 142 break;
143 143
144 case AFMT_SPEEX: 144 case AFMT_SPEEX:
145 if (!get_speex_metadata(fd, id3)) 145 if (!get_ogg_metadata(fd, id3))
146 { 146 {
147 return false; 147 return false;
148 } 148 }
diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h
index 5fbdf6f756..c3265f8a43 100644
--- a/apps/metadata/metadata_parsers.h
+++ b/apps/metadata/metadata_parsers.h
@@ -26,8 +26,7 @@ bool get_monkeys_metadata(int fd, struct mp3entry* id3);
26bool get_musepack_metadata(int fd, struct mp3entry *id3); 26bool get_musepack_metadata(int fd, struct mp3entry *id3);
27bool get_sid_metadata(int fd, struct mp3entry* id3); 27bool get_sid_metadata(int fd, struct mp3entry* id3);
28bool get_spc_metadata(int fd, struct mp3entry* id3); 28bool get_spc_metadata(int fd, struct mp3entry* id3);
29bool get_speex_metadata(int fd, struct mp3entry* id3); 29bool get_ogg_metadata(int fd, struct mp3entry* id3);
30bool get_vorbis_metadata(int fd, struct mp3entry* id3);
31bool get_wave_metadata(int fd, struct mp3entry* id3); 30bool get_wave_metadata(int fd, struct mp3entry* id3);
32bool get_wavpack_metadata(int fd, struct mp3entry* id3); 31bool get_wavpack_metadata(int fd, struct mp3entry* id3);
33bool get_a52_metadata(int fd, struct mp3entry* id3); 32bool get_a52_metadata(int fd, struct mp3entry* id3);
diff --git a/apps/metadata/ogg.c b/apps/metadata/ogg.c
index a109694e9d..9b604a11a4 100644
--- a/apps/metadata/ogg.c
+++ b/apps/metadata/ogg.c
@@ -27,247 +27,89 @@
27#include "metadata_common.h" 27#include "metadata_common.h"
28#include "logf.h" 28#include "logf.h"
29 29
30/* A simple parser to read vital metadata from an Ogg Speex file. Returns 30/* A simple parser to read vital metadata from an Ogg Vorbis file.
31 * false if metadata needed by the Speex codec couldn't be read. 31 * Can also handle parsing Ogg Speex files for metadata. Returns
32 * false if metadata needed by the codec couldn't be read.
32 */ 33 */
33 34bool get_ogg_metadata(int fd, struct mp3entry* id3)
34bool get_speex_metadata(int fd, struct mp3entry* id3)
35{ 35{
36 /* An Ogg File is split into pages, each starting with the string 36 /* An Ogg File is split into pages, each starting with the string
37 * "OggS". Each page has a timestamp (in PCM samples) referred to as 37 * "OggS". Each page has a timestamp (in PCM samples) referred to as
38 * the "granule position". 38 * the "granule position".
39 * 39 *
40 * An Ogg Vorbis has the following structure:
41 * 1) Identification header (containing samplerate, numchannels, etc)
42 * 2) Comment header - containing the Vorbis Comments
43 * 3) Setup header - containing codec setup information
44 * 4) Many audio packets...
45 *
40 * An Ogg Speex has the following structure: 46 * An Ogg Speex has the following structure:
41 * 1) Identification header (containing samplerate, numchannels, etc) 47 * 1) Identification header (containing samplerate, numchannels, etc)
42 Described in this page: (http://www.speex.org/manual2/node7.html) 48 * Described in this page: (http://www.speex.org/manual2/node7.html)
43 * 2) Comment header - containing the Vorbis Comments 49 * 2) Comment header - containing the Vorbis Comments
44 * 3) Many audio packets... 50 * 3) Many audio packets.
45 */ 51 */
46 52
47 /* Use the path name of the id3 structure as a temporary buffer. */ 53 /* Use the path name of the id3 structure as a temporary buffer. */
48 unsigned char* buf = (unsigned char*)id3->path; 54 unsigned char* buf = (unsigned char *)id3->path;
49 long comment_size; 55 long comment_size;
50 long remaining = 0; 56 long remaining = 0;
51 long last_serial = 0; 57 long last_serial = 0;
52 long serial, r; 58 long serial, r;
53 int segments; 59 int segments, header_size;
54 int i; 60 int i;
55 bool eof = false; 61 bool eof = false;
56 62
57 if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 58) < 33)) 63 /* 92 bytes is enough for both Vorbis and Speex headers */
64 if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 92) < 92))
58 { 65 {
59 return false; 66 return false;
60 } 67 }
61 68
62 if ((memcmp(buf, "OggS", 4) != 0) || (memcmp(&buf[28], "Speex", 5) != 0)) 69 /* All Ogg streams start with OggS */
70 if (memcmp(buf, "OggS", 4) != 0)
63 { 71 {
64 return false; 72 return false;
65 } 73 }
66 74
67 /* We need to ensure the serial number from this page is the same as the 75 /* Check for format magic and then get metadata */
68 * one from the last page (since we only support a single bitstream). 76 if (memcmp(&buf[29], "vorbis", 6) == 0)
69 */
70 serial = get_long_le(&buf[14]);
71 if ((lseek(fd, 33, SEEK_SET) < 0)||(read(fd, buf, 58) < 4))
72 {
73 return false;
74 }
75
76 id3->frequency = get_slong(&buf[31]);
77 last_serial = get_long_le(&buf[27]);/*temporary, header size*/
78 id3->bitrate = get_long_le(&buf[47]);
79 id3->vbr = get_long_le(&buf[55]);
80 id3->filesize = filesize(fd);
81 /* Comments are in second Ogg page */
82 if (lseek(fd, 28+last_serial/*(temporary for header size)*/, SEEK_SET) < 0)
83 { 77 {
84 return false; 78 id3->codectype = AFMT_OGG_VORBIS;
85 } 79 id3->frequency = get_long_le(&buf[40]);
80 id3->vbr = true;
86 81
87 /* Minimum header length for Ogg pages is 27. */ 82 /* Comments are in second Ogg page (byte 58 onwards for Vorbis) */
88 if (read(fd, buf, 27) < 27) 83 if (lseek(fd, 58, SEEK_SET) < 0)
89 {
90 return false;
91 }
92
93 if (memcmp(buf, "OggS", 4) !=0 )
94 {
95 return false;
96 }
97
98 segments = buf[26];
99 /* read in segment table */
100 if (read(fd, buf, segments) < segments)
101 {
102 return false;
103 }
104
105 /* The second packet in a vorbis stream is the comment packet. It *may*
106 * extend beyond the second page, but usually does not. Here we find the
107 * length of the comment packet (or the rest of the page if the comment
108 * packet extends to the third page).
109 */
110 for (i = 0; i < segments; i++)
111 {
112 remaining += buf[i];
113 /* The last segment of a packet is always < 255 bytes */
114 if (buf[i] < 255)
115 { 84 {
116 break; 85 return false;
117 } 86 }
118 } 87 }
119 88 else if (memcmp(&buf[28], "Speex ", 8) == 0)
120 comment_size = remaining;
121
122 /* Failure to read the tags isn't fatal. */
123 read_vorbis_tags(fd, id3, remaining);
124
125 /* We now need to search for the last page in the file - identified by
126 * by ('O','g','g','S',0) and retrieve totalsamples.
127 */
128
129 /* A page is always < 64 kB */
130 if (lseek(fd, -(MIN(64 * 1024, id3->filesize)), SEEK_END) < 0)
131 { 89 {
132 return false; 90 id3->codectype = AFMT_SPEEX;
133 } 91 id3->frequency = get_slong(&buf[64]);
134 92 id3->vbr = get_long_le(&buf[88]);
135 remaining = 0;
136 93
137 while (!eof) 94 header_size = get_long_le(&buf[60]);
138 {
139 r = read(fd, &buf[remaining], MAX_PATH - remaining);
140
141 if (r <= 0)
142 {
143 eof = true;
144 }
145 else
146 {
147 remaining += r;
148 }
149
150 /* Inefficient (but simple) search */
151 i = 0;
152
153 while (i < (remaining - 3))
154 {
155 if ((buf[i] == 'O') && (memcmp(&buf[i], "OggS", 4) == 0))
156 {
157 if (i < (remaining - 17))
158 {
159 /* Note that this only reads the low 32 bits of a
160 * 64 bit value.
161 */
162 id3->samples = get_long_le(&buf[i + 6]);
163 last_serial = get_long_le(&buf[i + 14]);
164
165 /* If this page is very small the beginning of the next
166 * header could be in buffer. Jump near end of this header
167 * and continue */
168 i += 27;
169 }
170 else
171 {
172 break;
173 }
174 }
175 else
176 {
177 i++;
178 }
179 }
180 95
181 if (i < remaining) 96 /* Comments are in second Ogg page (byte 108 onwards for Speex) */
97 if (lseek(fd, 28 + header_size, SEEK_SET) < 0)
182 { 98 {
183 /* Move the remaining bytes to start of buffer. 99 return false;
184 * Reuse var 'segments' as it is no longer needed */
185 segments = 0;
186 while (i < remaining)
187 {
188 buf[segments++] = buf[i++];
189 }
190 remaining = segments;
191 }
192 else
193 {
194 /* Discard the rest of the buffer */
195 remaining = 0;
196 } 100 }
197 } 101 }
198 102 else
199 /* This file has mutiple vorbis bitstreams (or is corrupt). */
200 /* FIXME we should display an error here. */
201 if (serial != last_serial)
202 {
203 logf("serialno mismatch");
204 logf("%ld", serial);
205 logf("%ld", last_serial);
206 return false;
207 }
208
209 id3->length = (id3->samples / id3->frequency) * 1000;
210 id3->bitrate = (((int64_t) id3->filesize - comment_size) * 8) / id3->length;
211 return true;
212}
213
214/* A simple parser to read vital metadata from an Ogg Vorbis file.
215 * Calls get_speex_metadata if a speex file is identified. Returns
216 * false if metadata needed by the Vorbis codec couldn't be read.
217 */
218bool get_vorbis_metadata(int fd, struct mp3entry* id3)
219{
220 /* An Ogg File is split into pages, each starting with the string
221 * "OggS". Each page has a timestamp (in PCM samples) referred to as
222 * the "granule position".
223 *
224 * An Ogg Vorbis has the following structure:
225 * 1) Identification header (containing samplerate, numchannels, etc)
226 * 2) Comment header - containing the Vorbis Comments
227 * 3) Setup header - containing codec setup information
228 * 4) Many audio packets...
229 */
230
231 /* Use the path name of the id3 structure as a temporary buffer. */
232 unsigned char* buf = (unsigned char *)id3->path;
233 long comment_size;
234 long remaining = 0;
235 long last_serial = 0;
236 long serial, r;
237 int segments;
238 int i;
239 bool eof = false;
240
241 if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 58) < 4))
242 { 103 {
243 return false; 104 return false;
244 } 105 }
245 106
246 if ((memcmp(buf, "OggS", 4) != 0) || (memcmp(&buf[29], "vorbis", 6) != 0)) 107 id3->filesize = filesize(fd);
247 {
248 if ((memcmp(buf, "OggS", 4) != 0) || (memcmp(&buf[28], "Speex", 5) != 0))
249 {
250 return false;
251 }
252 else
253 {
254 id3->codectype = AFMT_SPEEX;
255 return get_speex_metadata(fd, id3);
256 }
257 }
258 108
259 /* We need to ensure the serial number from this page is the same as the 109 /* We need to ensure the serial number from this page is the same as the
260 * one from the last page (since we only support a single bitstream). 110 * one from the last page (since we only support a single bitstream).
261 */ 111 */
262 serial = get_long_le(&buf[14]); 112 serial = get_long_le(&buf[14]);
263 id3->frequency = get_long_le(&buf[40]);
264 id3->filesize = filesize(fd);
265
266 /* Comments are in second Ogg page */
267 if (lseek(fd, 58, SEEK_SET) < 0)
268 {
269 return false;
270 }
271 113
272 /* Minimum header length for Ogg pages is 27. */ 114 /* Minimum header length for Ogg pages is 27. */
273 if (read(fd, buf, 27) < 27) 115 if (read(fd, buf, 27) < 27)
@@ -304,21 +146,24 @@ bool get_vorbis_metadata(int fd, struct mp3entry* id3)
304 } 146 }
305 } 147 }
306 148
307 /* Now read in packet header (type and id string) */
308 if (read(fd, buf, 7) < 7)
309 {
310 return false;
311 }
312
313 comment_size = remaining; 149 comment_size = remaining;
314 remaining -= 7; 150
151 if (id3->codectype == AFMT_OGG_VORBIS) {
152 /* Now read in packet header (type and id string) */
153 if (read(fd, buf, 7) < 7)
154 {
155 return false;
156 }
315 157
316 /* The first byte of a packet is the packet type; comment packets are 158 remaining -= 7;
317 * type 3. 159
318 */ 160 /* The first byte of a packet is the packet type; comment packets are
319 if ((buf[0] != 3) || (memcmp(buf + 1, "vorbis", 6) !=0)) 161 * type 3.
320 { 162 */
321 return false; 163 if (buf[0] != 3)
164 {
165 return false;
166 }
322 } 167 }
323 168
324 /* Failure to read the tags isn't fatal. */ 169 /* Failure to read the tags isn't fatal. */
@@ -409,7 +254,6 @@ bool get_vorbis_metadata(int fd, struct mp3entry* id3)
409 } 254 }
410 255
411 id3->length = ((int64_t) id3->samples * 1000) / id3->frequency; 256 id3->length = ((int64_t) id3->samples * 1000) / id3->frequency;
412
413 if (id3->length <= 0) 257 if (id3->length <= 0)
414 { 258 {
415 logf("ogg length invalid!"); 259 logf("ogg length invalid!");
@@ -417,7 +261,6 @@ bool get_vorbis_metadata(int fd, struct mp3entry* id3)
417 } 261 }
418 262
419 id3->bitrate = (((int64_t) id3->filesize - comment_size) * 8) / id3->length; 263 id3->bitrate = (((int64_t) id3->filesize - comment_size) * 8) / id3->length;
420 id3->vbr = true;
421 264
422 return true; 265 return true;
423} 266}