23 $info = &$this->getid3->info;
25 $info[
'fileformat'] =
'ogg';
28 if (isset(
$info[
'id3v2'])) {
29 $this->
warning(
'Illegal ID3v2 tag present.');
31 if (isset(
$info[
'id3v1'])) {
32 $this->
warning(
'Illegal ID3v1 tag present.');
34 if (isset(
$info[
'ape'])) {
35 $this->
warning(
'Illegal APE tag present.');
44 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
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']);
53 $filedata = $this->
fread($oggpageinfo[
'page_length']);
56 if (substr($filedata, 0, 4) ==
'fLaC') {
58 $info[
'audio'][
'dataformat'] =
'flac';
59 $info[
'audio'][
'bitrate_mode'] =
'vbr';
60 $info[
'audio'][
'lossless'] =
true;
62 } elseif (substr($filedata, 1, 6) ==
'vorbis') {
66 } elseif (substr($filedata, 0, 8) ==
'OpusHead') {
72 } elseif (substr($filedata, 0, 8) ==
'Speex ') {
76 $info[
'audio'][
'dataformat'] =
'speex';
77 $info[
'mime_type'] =
'audio/speex';
78 $info[
'audio'][
'bitrate_mode'] =
'abr';
79 $info[
'audio'][
'lossless'] =
false;
81 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_string'] = substr($filedata, $filedataoffset, 8);
83 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_version'] = substr($filedata, $filedataoffset, 20);
84 $filedataoffset += 20;
100 $filedataoffset += 4;
102 $filedataoffset += 4;
104 $filedataoffset += 4;
106 $filedataoffset += 4;
108 $filedataoffset += 4;
110 $filedataoffset += 4;
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'];
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';
124 } elseif (substr($filedata, 0, 7) ==
"\x80".
'theora') {
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;
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;
167 $info[
'ogg'][
'pageheader'][
'theora'][
'color_space'] = self::TheoraColorSpace(
$info[
'ogg'][
'pageheader'][
'theora'][
'color_space_id']);
168 $info[
'ogg'][
'pageheader'][
'theora'][
'pixel_format'] = self::TheoraPixelFormat(
$info[
'ogg'][
'pageheader'][
'theora'][
'pixel_format_id']);
170 $info[
'video'][
'dataformat'] =
'theora';
171 $info[
'mime_type'] =
'video/ogg';
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'];
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'];
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');
185 } elseif (substr($filedata, 0, 8) ==
"fishead\x00") {
189 $filedataoffset += 8;
191 $filedataoffset += 2;
193 $filedataoffset += 2;
195 $filedataoffset += 8;
197 $filedataoffset += 8;
199 $filedataoffset += 8;
201 $filedataoffset += 8;
203 $filedataoffset += 20;
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'];
214 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno'].
'.'.$counter++] = $oggpageinfo;
215 $filedata = $this->
fread($oggpageinfo[
'page_length']);
216 $this->
fseek($oggpageinfo[
'page_end_offset']);
218 if (substr($filedata, 0, 8) ==
"fisbone\x00") {
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;
240 } elseif (substr($filedata, 1, 6) ==
'theora') {
242 $info[
'video'][
'dataformat'] =
'theora1';
243 $this->
error(
'Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().
']');
246 } elseif (substr($filedata, 1, 6) ==
'vorbis') {
251 $this->
error(
'unexpected');
255 }
while (($oggpageinfo[
'page_seqno'] == 0) && (substr($filedata, 0, 8) !=
"fisbone\x00"));
257 $this->
fseek($oggpageinfo[
'page_start_offset']);
259 $this->
error(
'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().
']');
264 $this->
error(
'Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).
'"');
266 unset(
$info[
'mime_type']);
273 $info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
275 switch (
$info[
'audio'][
'dataformat']) {
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);
286 if (!$flac->parseMETAdata()) {
287 $this->
error(
'Failed to parse FLAC headers');
294 $this->
fseek(
$info[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_length'], SEEK_CUR);
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).
'"');
314 $this->
warning(
'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).
'GB)');
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');
329 if (!empty(
$info[
'audio'][
'sample_rate'])) {
330 $info[
'ogg'][
'bitrate_average'] = ((
$info[
'avdataend'] -
$info[
'avdataoffset']) * 8) / (
$info[
'ogg'][
'samples'] /
$info[
'audio'][
'sample_rate']);
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;
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');
348 $info[
'playtime_seconds'] = (float) (((
$info[
'avdataend'] -
$info[
'avdataoffset']) * 8) /
$info[
'audio'][
'bitrate']);
351 if (isset(
$info[
'ogg'][
'vendor'])) {
352 $info[
'audio'][
'encoder'] = preg_replace(
'/^Encoded with /',
'',
$info[
'ogg'][
'vendor']);
355 if (
$info[
'audio'][
'dataformat'] ==
'vorbis') {
358 if (preg_match(
'/^Xiph.Org/',
$info[
'audio'][
'encoder'])) {
360 if (
$info[
'audio'][
'bitrate_mode'] ==
'abr') {
363 $info[
'audio'][
'encoder_options'] =
'-b '.round(
$info[
'ogg'][
'bitrate_nominal'] / 1000);
365 } elseif ((
$info[
'audio'][
'bitrate_mode'] ==
'vbr') && (
$info[
'audio'][
'channels'] == 2) && (
$info[
'audio'][
'sample_rate'] >= 44100) && (
$info[
'audio'][
'sample_rate'] <= 48000)) {
367 $info[
'audio'][
'encoder_options'] =
'-q '.$this->get_quality_from_nominal_bitrate(
$info[
'ogg'][
'bitrate_nominal']);
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';
ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
static intValueSupported($num)
static LittleEndian2Int($byteword, $signed=false)
ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
fseek($bytes, $whence=SEEK_SET)
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
static SpeexBandModeLookup($mode)
http://flac.sourceforge.net/format.html