22 {
23 $info = &$this->getid3->info;
24
25 $info[
'fileformat'] =
'ogg';
26
27
28 if (isset(
$info[
'id3v2'])) {
29 $this->
warning(
'Illegal ID3v2 tag present.');
30 }
31 if (isset(
$info[
'id3v1'])) {
32 $this->
warning(
'Illegal ID3v1 tag present.');
33 }
34 if (isset(
$info[
'ape'])) {
35 $this->
warning(
'Illegal APE tag present.');
36 }
37
38
39
40
42
44 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
45
46 if ($this->
ftell() >= $this->getid3->fread_buffer_size()) {
47 $this->
error(
'Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().
' bytes (this might not be an Ogg-Vorbis file?)');
48 unset(
$info[
'fileformat']);
50 return false;
51 }
52
53 $filedata = $this->
fread($oggpageinfo[
'page_length']);
54 $filedataoffset = 0;
55
56 if (substr($filedata, 0, 4) == 'fLaC') {
57
58 $info[
'audio'][
'dataformat'] =
'flac';
59 $info[
'audio'][
'bitrate_mode'] =
'vbr';
60 $info[
'audio'][
'lossless'] =
true;
61
62 } elseif (substr($filedata, 1, 6) == 'vorbis') {
63
65
66 } elseif (substr($filedata, 0, 8) == 'OpusHead') {
67
69 return false;
70 }
71
72 } elseif (substr($filedata, 0, 8) == 'Speex ') {
73
74
75
76 $info[
'audio'][
'dataformat'] =
'speex';
77 $info[
'mime_type'] =
'audio/speex';
78 $info[
'audio'][
'bitrate_mode'] =
'abr';
79 $info[
'audio'][
'lossless'] =
false;
80
81 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_string'] = substr($filedata, $filedataoffset, 8);
82 $filedataoffset += 8;
83 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_version'] = substr($filedata, $filedataoffset, 20);
84 $filedataoffset += 20;
86 $filedataoffset += 4;
88 $filedataoffset += 4;
90 $filedataoffset += 4;
92 $filedataoffset += 4;
94 $filedataoffset += 4;
96 $filedataoffset += 4;
98 $filedataoffset += 4;
100 $filedataoffset += 4;
102 $filedataoffset += 4;
104 $filedataoffset += 4;
106 $filedataoffset += 4;
108 $filedataoffset += 4;
110 $filedataoffset += 4;
111
112 $info[
'speex'][
'speex_version'] = trim(
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_version']);
113 $info[
'speex'][
'sample_rate'] =
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'rate'];
114 $info[
'speex'][
'channels'] =
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'nb_channels'];
115 $info[
'speex'][
'vbr'] = (bool)
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'vbr'];
117
118 $info[
'audio'][
'sample_rate'] =
$info[
'speex'][
'sample_rate'];
119 $info[
'audio'][
'channels'] =
$info[
'speex'][
'channels'];
120 if (
$info[
'speex'][
'vbr']) {
121 $info[
'audio'][
'bitrate_mode'] =
'vbr';
122 }
123
124 } elseif (substr($filedata, 0, 7) == "\x80".'theora') {
125
126
127
128 $info[
'ogg'][
'pageheader'][
'theora'][
'theora_magic'] = substr($filedata, $filedataoffset, 7);
129 $filedataoffset += 7;
131 $filedataoffset += 1;
133 $filedataoffset += 1;
135 $filedataoffset += 1;
137 $filedataoffset += 2;
139 $filedataoffset += 2;
141 $filedataoffset += 3;
143 $filedataoffset += 3;
145 $filedataoffset += 1;
147 $filedataoffset += 1;
149 $filedataoffset += 4;
151 $filedataoffset += 4;
153 $filedataoffset += 3;
155 $filedataoffset += 3;
157 $filedataoffset += 1;
159 $filedataoffset += 3;
161 $filedataoffset += 2;
162
163 $info[
'ogg'][
'pageheader'][
'theora'][
'quality'] = (
$info[
'ogg'][
'pageheader'][
'theora'][
'flags'] & 0xFC00) >> 10;
164 $info[
'ogg'][
'pageheader'][
'theora'][
'kfg_shift'] = (
$info[
'ogg'][
'pageheader'][
'theora'][
'flags'] & 0x03E0) >> 5;
165 $info[
'ogg'][
'pageheader'][
'theora'][
'pixel_format_id'] = (
$info[
'ogg'][
'pageheader'][
'theora'][
'flags'] & 0x0018) >> 3;
166 $info[
'ogg'][
'pageheader'][
'theora'][
'reserved'] = (
$info[
'ogg'][
'pageheader'][
'theora'][
'flags'] & 0x0007) >> 0;
169
170 $info[
'video'][
'dataformat'] =
'theora';
171 $info[
'mime_type'] =
'video/ogg';
172
173
174 $info[
'video'][
'resolution_x'] =
$info[
'ogg'][
'pageheader'][
'theora'][
'resolution_x'];
175 $info[
'video'][
'resolution_y'] =
$info[
'ogg'][
'pageheader'][
'theora'][
'resolution_y'];
176 if (
$info[
'ogg'][
'pageheader'][
'theora'][
'frame_rate_denominator'] > 0) {
177 $info[
'video'][
'frame_rate'] = (float)
$info[
'ogg'][
'pageheader'][
'theora'][
'frame_rate_numerator'] /
$info[
'ogg'][
'pageheader'][
'theora'][
'frame_rate_denominator'];
178 }
179 if (
$info[
'ogg'][
'pageheader'][
'theora'][
'pixel_aspect_denominator'] > 0) {
180 $info[
'video'][
'pixel_aspect_ratio'] = (float)
$info[
'ogg'][
'pageheader'][
'theora'][
'pixel_aspect_numerator'] /
$info[
'ogg'][
'pageheader'][
'theora'][
'pixel_aspect_denominator'];
181 }
182$this->
warning(
'Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().
'] -- bitrate, playtime and all audio data are currently unavailable');
183
184
185 } elseif (substr($filedata, 0, 8) == "fishead\x00") {
186
187
188
189 $filedataoffset += 8;
191 $filedataoffset += 2;
193 $filedataoffset += 2;
195 $filedataoffset += 8;
197 $filedataoffset += 8;
199 $filedataoffset += 8;
201 $filedataoffset += 8;
203 $filedataoffset += 20;
204
205 $info[
'ogg'][
'skeleton'][
'fishead'][
'version'] =
$info[
'ogg'][
'skeleton'][
'fishead'][
'raw'][
'version_major'].
'.'.
$info[
'ogg'][
'skeleton'][
'fishead'][
'raw'][
'version_minor'];
206 $info[
'ogg'][
'skeleton'][
'fishead'][
'presentationtime'] =
$info[
'ogg'][
'skeleton'][
'fishead'][
'raw'][
'presentationtime_numerator'] /
$info[
'ogg'][
'skeleton'][
'fishead'][
'raw'][
'presentationtime_denominator'];
207 $info[
'ogg'][
'skeleton'][
'fishead'][
'basetime'] =
$info[
'ogg'][
'skeleton'][
'fishead'][
'raw'][
'basetime_numerator'] /
$info[
'ogg'][
'skeleton'][
'fishead'][
'raw'][
'basetime_denominator'];
208 $info[
'ogg'][
'skeleton'][
'fishead'][
'utc'] =
$info[
'ogg'][
'skeleton'][
'fishead'][
'raw'][
'utc'];
209
210
211 $counter = 0;
212 do {
214 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno'].
'.'.$counter++] = $oggpageinfo;
215 $filedata = $this->
fread($oggpageinfo[
'page_length']);
216 $this->
fseek($oggpageinfo[
'page_end_offset']);
217
218 if (substr($filedata, 0, 8) == "fisbone\x00") {
219
220 $filedataoffset = 8;
222 $filedataoffset += 4;
224 $filedataoffset += 4;
226 $filedataoffset += 4;
228 $filedataoffset += 8;
230 $filedataoffset += 8;
232 $filedataoffset += 8;
234 $filedataoffset += 4;
236 $filedataoffset += 1;
237 $info[
'ogg'][
'skeleton'][
'fisbone'][
'raw'][
'padding'] = substr($filedata, $filedataoffset, 3);
238 $filedataoffset += 3;
239
240 } elseif (substr($filedata, 1, 6) == 'theora') {
241
242 $info[
'video'][
'dataformat'] =
'theora1';
243 $this->
error(
'Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().
']');
244
245
246 } elseif (substr($filedata, 1, 6) == 'vorbis') {
247
249
250 } else {
251 $this->
error(
'unexpected');
252
253 }
254
255 } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00"));
256
257 $this->
fseek($oggpageinfo[
'page_start_offset']);
258
259 $this->
error(
'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().
']');
260
261
262 } else {
263
264 $this->
error(
'Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).
'"');
266 unset(
$info[
'mime_type']);
267 return false;
268
269 }
270
271
273 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
274
275 switch (
$info[
'audio'][
'dataformat']) {
276 case 'vorbis':
277 $filedata = $this->
fread(
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_length']);
279 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'stream_type'] = substr($filedata, 1, 6);
280
282 break;
283
284 case 'flac':
286 if (!$flac->parseMETAdata()) {
287 $this->
error(
'Failed to parse FLAC headers');
288 return false;
289 }
290 unset($flac);
291 break;
292
293 case 'speex':
294 $this->
fseek(
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_length'], SEEK_CUR);
296 break;
297
298 case 'opus':
299 $filedata = $this->
fread(
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_length']);
300 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'stream_type'] = substr($filedata, 0, 8);
301 if(substr($filedata, 0, 8) != 'OpusTags') {
302 $this->
error(
'Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).
'"');
303 return false;
304 }
305
307 break;
308
309 }
310
311
313
314 $this->
warning(
'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).
'GB)');
315
316 } else {
317
318 $this->
fseek(max(
$info[
'avdataend'] - $this->getid3->fread_buffer_size(), 0));
319 $LastChunkOfOgg = strrev($this->
fread($this->getid3->fread_buffer_size()));
320 if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
321 $this->
fseek(
$info[
'avdataend'] - ($LastOggSpostion + strlen(
'SggO')));
324 $info[
'ogg'][
'samples'] =
$info[
'ogg'][
'pageheader'][
'eos'][
'pcm_abs_position'];
325 if (
$info[
'ogg'][
'samples'] == 0) {
326 $this->
error(
'Corrupt Ogg file: eos.number of samples == zero');
327 return false;
328 }
329 if (!empty(
$info[
'audio'][
'sample_rate'])) {
330 $info[
'ogg'][
'bitrate_average'] = ((
$info[
'avdataend'] -
$info[
'avdataoffset']) * 8) / (
$info[
'ogg'][
'samples'] /
$info[
'audio'][
'sample_rate']);
331 }
332 }
333
334 }
335
336 if (!empty(
$info[
'ogg'][
'bitrate_average'])) {
337 $info[
'audio'][
'bitrate'] =
$info[
'ogg'][
'bitrate_average'];
338 } elseif (!empty(
$info[
'ogg'][
'bitrate_nominal'])) {
339 $info[
'audio'][
'bitrate'] =
$info[
'ogg'][
'bitrate_nominal'];
340 } elseif (!empty(
$info[
'ogg'][
'bitrate_min']) && !empty(
$info[
'ogg'][
'bitrate_max'])) {
341 $info[
'audio'][
'bitrate'] = (
$info[
'ogg'][
'bitrate_min'] +
$info[
'ogg'][
'bitrate_max']) / 2;
342 }
343 if (isset(
$info[
'audio'][
'bitrate']) && !isset(
$info[
'playtime_seconds'])) {
344 if (
$info[
'audio'][
'bitrate'] == 0) {
345 $this->
error(
'Corrupt Ogg file: bitrate_audio == zero');
346 return false;
347 }
348 $info[
'playtime_seconds'] = (float) (((
$info[
'avdataend'] -
$info[
'avdataoffset']) * 8) /
$info[
'audio'][
'bitrate']);
349 }
350
351 if (isset(
$info[
'ogg'][
'vendor'])) {
352 $info[
'audio'][
'encoder'] = preg_replace(
'/^Encoded with /',
'',
$info[
'ogg'][
'vendor']);
353
354
355 if (
$info[
'audio'][
'dataformat'] ==
'vorbis') {
356
357
358 if (preg_match(
'/^Xiph.Org/',
$info[
'audio'][
'encoder'])) {
359
360 if (
$info[
'audio'][
'bitrate_mode'] ==
'abr') {
361
362
363 $info[
'audio'][
'encoder_options'] =
'-b '.round(
$info[
'ogg'][
'bitrate_nominal'] / 1000);
364
365 } elseif ((
$info[
'audio'][
'bitrate_mode'] ==
'vbr') && (
$info[
'audio'][
'channels'] == 2) && (
$info[
'audio'][
'sample_rate'] >= 44100) && (
$info[
'audio'][
'sample_rate'] <= 48000)) {
366
367 $info[
'audio'][
'encoder_options'] =
'-q '.$this->get_quality_from_nominal_bitrate(
$info[
'ogg'][
'bitrate_nominal']);
368
369 }
370 }
371
372 if (empty(
$info[
'audio'][
'encoder_options']) && !empty(
$info[
'ogg'][
'bitrate_nominal'])) {
373 $info[
'audio'][
'encoder_options'] =
'Nominal bitrate: '.intval(round(
$info[
'ogg'][
'bitrate_nominal'] / 1000)).
'kbps';
374 }
375 }
376 }
377
378 return true;
379 }
@tutorial http://flac.sourceforge.net/format.html
fseek($bytes, $whence=SEEK_SET)
static LittleEndian2Int($byteword, $signed=false)
static intValueSupported($num)
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
static TheoraPixelFormat($pixelformat_id)
ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
static SpeexBandModeLookup($mode)
ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
static TheoraColorSpace($colorspace_id)