ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
getid3_ogg Class Reference
+ Inheritance diagram for getid3_ogg:
+ Collaboration diagram for getid3_ogg:

Public Member Functions

 getid3_ogg (&$fd, &$ThisFileInfo)
 
 ParseOggPageHeader (&$fd)
 
 ParseVorbisCommentsFilepointer (&$fd, &$ThisFileInfo)
 
 SpeexBandModeLookup ($mode)
 
 OggPageSegmentLength ($OggInfoArray, $SegmentNumber=1)
 
 get_quality_from_nominal_bitrate ($nominal_bitrate)
 
 Analyze ()
 
 ParseVorbisPageHeader (&$filedata, &$filedataoffset, &$oggpageinfo)
 
 ParseOpusPageHeader (&$filedata, &$filedataoffset, &$oggpageinfo)
 
 ParseOggPageHeader ()
 
 ParseVorbisComments ()
 
- Public Member Functions inherited from getid3_handler
 __construct (getID3 $getid3, $call_module=null)
 
 Analyze ()
 
 AnalyzeString ($string)
 
 setStringMode ($string)
 
 saveAttachment ($name, $offset, $length, $image_mime=null)
 

Static Public Member Functions

static SpeexBandModeLookup ($mode)
 
static OggPageSegmentLength ($OggInfoArray, $SegmentNumber=1)
 
static get_quality_from_nominal_bitrate ($nominal_bitrate)
 
static TheoraColorSpace ($colorspace_id)
 
static TheoraPixelFormat ($pixelformat_id)
 

Additional Inherited Members

- Protected Member Functions inherited from getid3_handler
 ftell ()
 
 fread ($bytes)
 
 fseek ($bytes, $whence=SEEK_SET)
 
 feof ()
 
 isDependencyFor ($module)
 
 error ($text)
 
 warning ($text)
 
 notice ($text)
 
- Protected Attributes inherited from getid3_handler
 $getid3
 
 $data_string_flag = false
 
 $data_string = ''
 
 $data_string_position = 0
 
 $data_string_length = 0
 

Detailed Description

Definition at line 18 of file module.audio.ogg.php.

Member Function Documentation

◆ Analyze()

getid3_ogg::Analyze ( )

Definition at line 22 of file module.audio.ogg.php.

References $info, getid3_lib\BigEndian2Int(), getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), getid3_lib\intValueSupported(), getid3_lib\LittleEndian2Int(), ParseOggPageHeader(), ParseOpusPageHeader(), ParseVorbisComments(), ParseVorbisPageHeader(), and SpeexBandModeLookup().

22  {
23  $info = &$this->getid3->info;
24 
25  $info['fileformat'] = 'ogg';
26 
27  // Warn about illegal tags - only vorbiscomments are allowed
28  if (isset($info['id3v2'])) {
29  $info['warning'][] = 'Illegal ID3v2 tag present.';
30  }
31  if (isset($info['id3v1'])) {
32  $info['warning'][] = 'Illegal ID3v1 tag present.';
33  }
34  if (isset($info['ape'])) {
35  $info['warning'][] = 'Illegal APE tag present.';
36  }
37 
38 
39  // Page 1 - Stream Header
40 
41  $this->fseek($info['avdataoffset']);
42 
43  $oggpageinfo = $this->ParseOggPageHeader();
44  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
45 
46  if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
47  $info['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']);
49  unset($info['ogg']);
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 
64  $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
65 
66  } elseif (substr($filedata, 0, 8) == 'OpusHead') {
67 
68  if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) {
69  return false;
70  }
71 
72  } elseif (substr($filedata, 0, 8) == 'Speex ') {
73 
74  // http://www.speex.org/manual/node10.html
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); // hard-coded to 'Speex '
82  $filedataoffset += 8;
83  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
84  $filedataoffset += 20;
85  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
86  $filedataoffset += 4;
87  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
88  $filedataoffset += 4;
89  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
90  $filedataoffset += 4;
91  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
92  $filedataoffset += 4;
93  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
94  $filedataoffset += 4;
95  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
96  $filedataoffset += 4;
97  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
98  $filedataoffset += 4;
99  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
100  $filedataoffset += 4;
101  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
102  $filedataoffset += 4;
103  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
104  $filedataoffset += 4;
105  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
106  $filedataoffset += 4;
107  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
108  $filedataoffset += 4;
109  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $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'];
116  $info['speex']['band_type'] = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
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  // http://www.theora.org/doc/Theora.pdf (section 6.2)
127 
128  $info['ogg']['pageheader']['theora']['theora_magic'] = substr($filedata, $filedataoffset, 7); // hard-coded to "\x80.'theora'
129  $filedataoffset += 7;
130  $info['ogg']['pageheader']['theora']['version_major'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
131  $filedataoffset += 1;
132  $info['ogg']['pageheader']['theora']['version_minor'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
133  $filedataoffset += 1;
134  $info['ogg']['pageheader']['theora']['version_revision'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
135  $filedataoffset += 1;
136  $info['ogg']['pageheader']['theora']['frame_width_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
137  $filedataoffset += 2;
138  $info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
139  $filedataoffset += 2;
140  $info['ogg']['pageheader']['theora']['resolution_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
141  $filedataoffset += 3;
142  $info['ogg']['pageheader']['theora']['resolution_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
143  $filedataoffset += 3;
144  $info['ogg']['pageheader']['theora']['picture_offset_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
145  $filedataoffset += 1;
146  $info['ogg']['pageheader']['theora']['picture_offset_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
147  $filedataoffset += 1;
148  $info['ogg']['pageheader']['theora']['frame_rate_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
149  $filedataoffset += 4;
150  $info['ogg']['pageheader']['theora']['frame_rate_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
151  $filedataoffset += 4;
152  $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
153  $filedataoffset += 3;
154  $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
155  $filedataoffset += 3;
156  $info['ogg']['pageheader']['theora']['color_space_id'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
157  $filedataoffset += 1;
158  $info['ogg']['pageheader']['theora']['nominal_bitrate'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
159  $filedataoffset += 3;
160  $info['ogg']['pageheader']['theora']['flags'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
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; // should be 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']);
169 
170  $info['video']['dataformat'] = 'theora';
171  $info['mime_type'] = 'video/ogg';
172  //$info['audio']['bitrate_mode'] = 'abr';
173  //$info['audio']['lossless'] = false;
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 $info['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  // Ogg Skeleton version 3.0 Format Specification
188  // http://xiph.org/ogg/doc/skeleton.html
189  $filedataoffset += 8;
190  $info['ogg']['skeleton']['fishead']['raw']['version_major'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
191  $filedataoffset += 2;
192  $info['ogg']['skeleton']['fishead']['raw']['version_minor'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
193  $filedataoffset += 2;
194  $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
195  $filedataoffset += 8;
196  $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
197  $filedataoffset += 8;
198  $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
199  $filedataoffset += 8;
200  $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
201  $filedataoffset += 8;
202  $info['ogg']['skeleton']['fishead']['raw']['utc'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20));
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 {
213  $oggpageinfo = $this->ParseOggPageHeader();
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;
221  $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
222  $filedataoffset += 4;
223  $info['ogg']['skeleton']['fisbone']['raw']['serial_number'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
224  $filedataoffset += 4;
225  $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
226  $filedataoffset += 4;
227  $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
228  $filedataoffset += 8;
229  $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
230  $filedataoffset += 8;
231  $info['ogg']['skeleton']['fisbone']['raw']['basegranule'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
232  $filedataoffset += 8;
233  $info['ogg']['skeleton']['fisbone']['raw']['preroll'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
234  $filedataoffset += 4;
235  $info['ogg']['skeleton']['fisbone']['raw']['granuleshift'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
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  $info['error'][] = 'Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']';
244  //break;
245 
246  } elseif (substr($filedata, 1, 6) == 'vorbis') {
247 
248  $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
249 
250  } else {
251  $info['error'][] = 'unexpected';
252  //break;
253  }
254  //} while ($oggpageinfo['page_seqno'] == 0);
255  } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00"));
256 
257  $this->fseek($oggpageinfo['page_start_offset']);
258 
259  $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']';
260  //return false;
261 
262  } else {
263 
264  $info['error'][] = 'Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
265  unset($info['ogg']);
266  unset($info['mime_type']);
267  return false;
268 
269  }
270 
271  // Page 2 - Comment Header
272  $oggpageinfo = $this->ParseOggPageHeader();
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']);
278  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
279  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
280 
281  $this->ParseVorbisComments();
282  break;
283 
284  case 'flac':
285  $flac = new getid3_flac($this->getid3);
286  if (!$flac->parseMETAdata()) {
287  $info['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);
295  $this->ParseVorbisComments();
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); // hard-coded to 'OpusTags'
301  if(substr($filedata, 0, 8) != 'OpusTags') {
302  $info['error'][] = 'Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"';
303  return false;
304  }
305 
306  $this->ParseVorbisComments();
307  break;
308 
309  }
310 
311  // Last Page - Number of Samples
312  if (!getid3_lib::intValueSupported($info['avdataend'])) {
313 
314  $info['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')));
322  $info['avdataend'] = $this->ftell();
323  $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
324  $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
325  if ($info['ogg']['samples'] == 0) {
326  $info['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  $info['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  // Vorbis only
355  if ($info['audio']['dataformat'] == 'vorbis') {
356 
357  // Vorbis 1.0 starts with Xiph.Org
358  if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
359 
360  if ($info['audio']['bitrate_mode'] == 'abr') {
361 
362  // Set -b 128 on abr files
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  // Set -q N on vbr files
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  }
ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
static intValueSupported($num)
Definition: getid3.lib.php:80
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
$info
Definition: example_052.php:80
fread($bytes)
Definition: getid3.php:1685
BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: getid3.lib.php:234
ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
http://flac.sourceforge.net/format.html
SpeexBandModeLookup($mode)
+ Here is the call graph for this function:

◆ get_quality_from_nominal_bitrate() [1/2]

getid3_ogg::get_quality_from_nominal_bitrate (   $nominal_bitrate)

Definition at line 518 of file module.audio.ogg.php.

518  {
519 
520  // decrease precision
521  $nominal_bitrate = $nominal_bitrate / 1000;
522 
523  if ($nominal_bitrate < 128) {
524  // q-1 to q4
525  $qval = ($nominal_bitrate - 64) / 16;
526  } elseif ($nominal_bitrate < 256) {
527  // q4 to q8
528  $qval = $nominal_bitrate / 32;
529  } elseif ($nominal_bitrate < 320) {
530  // q8 to q9
531  $qval = ($nominal_bitrate + 256) / 64;
532  } else {
533  // q9 to q10
534  $qval = ($nominal_bitrate + 1300) / 180;
535  }
536  //return $qval; // 5.031324
537  //return intval($qval); // 5
538  return round($qval, 1); // 5 or 4.9
539  }

◆ get_quality_from_nominal_bitrate() [2/2]

static getid3_ogg::get_quality_from_nominal_bitrate (   $nominal_bitrate)
static

Definition at line 793 of file module.audio.ogg.php.

793  {
794 
795  // decrease precision
796  $nominal_bitrate = $nominal_bitrate / 1000;
797 
798  if ($nominal_bitrate < 128) {
799  // q-1 to q4
800  $qval = ($nominal_bitrate - 64) / 16;
801  } elseif ($nominal_bitrate < 256) {
802  // q4 to q8
803  $qval = $nominal_bitrate / 32;
804  } elseif ($nominal_bitrate < 320) {
805  // q8 to q9
806  $qval = ($nominal_bitrate + 256) / 64;
807  } else {
808  // q9 to q10
809  $qval = ($nominal_bitrate + 1300) / 180;
810  }
811  //return $qval; // 5.031324
812  //return intval($qval); // 5
813  return round($qval, 1); // 5 or 4.9
814  }

◆ getid3_ogg()

getid3_ogg::getid3_ogg ( $fd,
$ThisFileInfo 
)

Definition at line 21 of file module.audio.ogg.php.

References getid3_flac\FLACparseMETAdata(), getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), GETID3_FREAD_BUFFER_SIZE, getid3_lib\LittleEndian2Int(), ParseOggPageHeader(), ParseVorbisCommentsFilepointer(), and SpeexBandModeLookup().

21  {
22 
23  $ThisFileInfo['fileformat'] = 'ogg';
24 
25  // Warn about illegal tags - only vorbiscomments are allowed
26  if (isset($ThisFileInfo['id3v2'])) {
27  $ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.';
28  }
29  if (isset($ThisFileInfo['id3v1'])) {
30  $ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.';
31  }
32  if (isset($ThisFileInfo['ape'])) {
33  $ThisFileInfo['warning'][] = 'Illegal APE tag present.';
34  }
35 
36 
37  // Page 1 - Stream Header
38 
39  fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
40 
41  $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
42  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
43 
44  if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) {
45  $ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)';
46  unset($ThisFileInfo['fileformat']);
47  unset($ThisFileInfo['ogg']);
48  return false;
49  }
50 
51  $filedata = fread($fd, $oggpageinfo['page_length']);
52  $filedataoffset = 0;
53 
54  if (substr($filedata, 0, 4) == 'fLaC') {
55 
56  $ThisFileInfo['audio']['dataformat'] = 'flac';
57  $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
58  $ThisFileInfo['audio']['lossless'] = true;
59 
60  } elseif (substr($filedata, 1, 6) == 'vorbis') {
61 
62  $ThisFileInfo['audio']['dataformat'] = 'vorbis';
63  $ThisFileInfo['audio']['lossless'] = false;
64 
65  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
66  $filedataoffset += 1;
67  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
68  $filedataoffset += 6;
69  $ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
70  $filedataoffset += 4;
71  $ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
72  $filedataoffset += 1;
73  $ThisFileInfo['audio']['channels'] = $ThisFileInfo['ogg']['numberofchannels'];
74  $ThisFileInfo['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
75  $filedataoffset += 4;
76  if ($ThisFileInfo['ogg']['samplerate'] == 0) {
77  $ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero';
78  return false;
79  }
80  $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['ogg']['samplerate'];
81  $ThisFileInfo['ogg']['samples'] = 0; // filled in later
82  $ThisFileInfo['ogg']['bitrate_average'] = 0; // filled in later
83  $ThisFileInfo['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
84  $filedataoffset += 4;
85  $ThisFileInfo['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
86  $filedataoffset += 4;
87  $ThisFileInfo['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
88  $filedataoffset += 4;
89  $ThisFileInfo['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
90  $ThisFileInfo['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
91  $ThisFileInfo['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
92 
93  $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
94  if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) {
95  unset($ThisFileInfo['ogg']['bitrate_max']);
96  $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
97  }
98  if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
99  unset($ThisFileInfo['ogg']['bitrate_nominal']);
100  }
101  if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) {
102  unset($ThisFileInfo['ogg']['bitrate_min']);
103  $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
104  }
105 
106  } elseif (substr($filedata, 0, 8) == 'Speex ') {
107 
108  // http://www.speex.org/manual/node10.html
109 
110  $ThisFileInfo['audio']['dataformat'] = 'speex';
111  $ThisFileInfo['mime_type'] = 'audio/speex';
112  $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
113  $ThisFileInfo['audio']['lossless'] = false;
114 
115  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex '
116  $filedataoffset += 8;
117  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
118  $filedataoffset += 20;
119  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
120  $filedataoffset += 4;
121  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
122  $filedataoffset += 4;
123  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
124  $filedataoffset += 4;
125  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
126  $filedataoffset += 4;
127  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
128  $filedataoffset += 4;
129  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
130  $filedataoffset += 4;
131  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
132  $filedataoffset += 4;
133  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
134  $filedataoffset += 4;
135  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
136  $filedataoffset += 4;
137  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
138  $filedataoffset += 4;
139  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
140  $filedataoffset += 4;
141  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
142  $filedataoffset += 4;
143  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
144  $filedataoffset += 4;
145 
146  $ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
147  $ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
148  $ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
149  $ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
150  $ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
151 
152  $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate'];
153  $ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels'];
154  if ($ThisFileInfo['speex']['vbr']) {
155  $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
156  }
157 
158  } else {
159 
160  $ThisFileInfo['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found neither';
161  unset($ThisFileInfo['ogg']);
162  unset($ThisFileInfo['mime_type']);
163  return false;
164 
165  }
166 
167 
168  // Page 2 - Comment Header
169 
170  $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
171  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
172 
173  switch ($ThisFileInfo['audio']['dataformat']) {
174 
175  case 'vorbis':
176  $filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
177  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
178  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
179 
180  getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
181  break;
182 
183  case 'flac':
184  if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) {
185  $ThisFileInfo['error'][] = 'Failed to parse FLAC headers';
186  return false;
187  }
188  break;
189 
190  case 'speex':
191  fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
192  getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
193  break;
194 
195  }
196 
197 
198 
199  // Last Page - Number of Samples
200 
201  fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET);
202  $LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE));
203  if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
204  fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET);
205  $ThisFileInfo['avdataend'] = ftell($fd);
206  $ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd);
207  $ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position'];
208  if ($ThisFileInfo['ogg']['samples'] == 0) {
209  $ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
210  return false;
211  }
212  $ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']);
213  }
214 
215  if (!empty($ThisFileInfo['ogg']['bitrate_average'])) {
216  $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average'];
217  } elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
218  $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal'];
219  } elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) {
220  $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2;
221  }
222  if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) {
223  if ($ThisFileInfo['audio']['bitrate'] == 0) {
224  $ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
225  return false;
226  }
227  $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']);
228  }
229 
230  if (isset($ThisFileInfo['ogg']['vendor'])) {
231  $ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']);
232 
233  // Vorbis only
234  if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') {
235 
236  // Vorbis 1.0 starts with Xiph.Org
237  if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) {
238 
239  if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') {
240 
241  // Set -b 128 on abr files
242  $ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000);
243 
244  } elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) {
245  // Set -q N on vbr files
246  $ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']);
247 
248  }
249  }
250 
251  if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
252  $ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps';
253  }
254  }
255  }
256 
257  return true;
258  }
ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo)
const GETID3_FREAD_BUFFER_SIZE
Definition: getid3.php:14
FLACparseMETAdata(&$fd, &$ThisFileInfo)
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
fread($bytes)
Definition: getid3.php:1685
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
SpeexBandModeLookup($mode)
+ Here is the call graph for this function:

◆ OggPageSegmentLength() [1/2]

getid3_ogg::OggPageSegmentLength (   $OggInfoArray,
  $SegmentNumber = 1 
)

Definition at line 504 of file module.audio.ogg.php.

Referenced by ParseVorbisComments(), and ParseVorbisCommentsFilepointer().

504  {
505  for ($i = 0; $i < $SegmentNumber; $i++) {
506  $segmentlength = 0;
507  foreach ($OggInfoArray['segment_table'] as $key => $value) {
508  $segmentlength += $value;
509  if ($value < 255) {
510  break;
511  }
512  }
513  }
514  return $segmentlength;
515  }
+ Here is the caller graph for this function:

◆ OggPageSegmentLength() [2/2]

static getid3_ogg::OggPageSegmentLength (   $OggInfoArray,
  $SegmentNumber = 1 
)
static

Definition at line 779 of file module.audio.ogg.php.

779  {
780  for ($i = 0; $i < $SegmentNumber; $i++) {
781  $segmentlength = 0;
782  foreach ($OggInfoArray['segment_table'] as $key => $value) {
783  $segmentlength += $value;
784  if ($value < 255) {
785  break;
786  }
787  }
788  }
789  return $segmentlength;
790  }

◆ ParseOggPageHeader() [1/2]

getid3_ogg::ParseOggPageHeader ( $fd)

Definition at line 261 of file module.audio.ogg.php.

References getid3_handler\feof(), getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), GETID3_FREAD_BUFFER_SIZE, and getid3_lib\LittleEndian2Int().

261  {
262  // http://xiph.org/ogg/vorbis/doc/framing.html
263  $oggheader['page_start_offset'] = ftell($fd); // where we started from in the file
264 
265  $filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
266  $filedataoffset = 0;
267  while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
268  if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) {
269  // should be found before here
270  return false;
271  }
272  if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
273  if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) {
274  // get some more data, unless eof, in which case fail
275  return false;
276  }
277  }
278  }
279  $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
280 
281  $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
282  $filedataoffset += 1;
283  $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
284  $filedataoffset += 1;
285  $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
286  $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
287  $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
288 
289  $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
290  $filedataoffset += 8;
291  $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
292  $filedataoffset += 4;
293  $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
294  $filedataoffset += 4;
295  $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
296  $filedataoffset += 4;
297  $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
298  $filedataoffset += 1;
299  $oggheader['page_length'] = 0;
300  for ($i = 0; $i < $oggheader['page_segments']; $i++) {
301  $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
302  $filedataoffset += 1;
303  $oggheader['page_length'] += $oggheader['segment_table'][$i];
304  }
305  $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
306  $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
307  fseek($fd, $oggheader['header_end_offset'], SEEK_SET);
308 
309  return $oggheader;
310  }
const GETID3_FREAD_BUFFER_SIZE
Definition: getid3.php:14
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
fread($bytes)
Definition: getid3.php:1685
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
+ Here is the call graph for this function:

◆ ParseOggPageHeader() [2/2]

getid3_ogg::ParseOggPageHeader ( )

Definition at line 480 of file module.audio.ogg.php.

References getid3_handler\feof(), getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), and getid3_lib\LittleEndian2Int().

Referenced by Analyze(), getid3_ogg(), ParseVorbisComments(), and ParseVorbisCommentsFilepointer().

480  {
481  // http://xiph.org/ogg/vorbis/doc/framing.html
482  $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
483 
484  $filedata = $this->fread($this->getid3->fread_buffer_size());
485  $filedataoffset = 0;
486  while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
487  if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
488  // should be found before here
489  return false;
490  }
491  if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
492  if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) {
493  // get some more data, unless eof, in which case fail
494  return false;
495  }
496  }
497  }
498  $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
499 
500  $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
501  $filedataoffset += 1;
502  $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
503  $filedataoffset += 1;
504  $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
505  $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
506  $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
507 
508  $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
509  $filedataoffset += 8;
510  $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
511  $filedataoffset += 4;
512  $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
513  $filedataoffset += 4;
514  $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
515  $filedataoffset += 4;
516  $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
517  $filedataoffset += 1;
518  $oggheader['page_length'] = 0;
519  for ($i = 0; $i < $oggheader['page_segments']; $i++) {
520  $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
521  $filedataoffset += 1;
522  $oggheader['page_length'] += $oggheader['segment_table'][$i];
523  }
524  $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
525  $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
526  $this->fseek($oggheader['header_end_offset']);
527 
528  return $oggheader;
529  }
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
fread($bytes)
Definition: getid3.php:1685
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseOpusPageHeader()

getid3_ogg::ParseOpusPageHeader ( $filedata,
$filedataoffset,
$oggpageinfo 
)
Todo:
find a usable way to detect abr (vbr that is padded to be abr)

Definition at line 430 of file module.audio.ogg.php.

References $info, and getid3_lib\LittleEndian2Int().

Referenced by Analyze().

430  {
431  $info = &$this->getid3->info;
432  $info['audio']['dataformat'] = 'opus';
433  $info['mime_type'] = 'audio/ogg; codecs=opus';
434 
436  $info['audio']['bitrate_mode'] = 'vbr';
437 
438  $info['audio']['lossless'] = false;
439 
440  $info['ogg']['pageheader']['opus']['opus_magic'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'OpusHead'
441  $filedataoffset += 8;
442  $info['ogg']['pageheader']['opus']['version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
443  $filedataoffset += 1;
444 
445  if ($info['ogg']['pageheader']['opus']['version'] < 1 || $info['ogg']['pageheader']['opus']['version'] > 15) {
446  $info['error'][] = 'Unknown opus version number (only accepting 1-15)';
447  return false;
448  }
449 
450  $info['ogg']['pageheader']['opus']['out_channel_count'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
451  $filedataoffset += 1;
452 
453  if ($info['ogg']['pageheader']['opus']['out_channel_count'] == 0) {
454  $info['error'][] = 'Invalid channel count in opus header (must not be zero)';
455  return false;
456  }
457 
458  $info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
459  $filedataoffset += 2;
460 
461  $info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
462  $filedataoffset += 4;
463 
464  //$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
465  //$filedataoffset += 2;
466 
467  //$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
468  //$filedataoffset += 1;
469 
470  $info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version'];
471  $info['opus']['sample_rate'] = $info['ogg']['pageheader']['opus']['sample_rate'];
472  $info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
473 
474  $info['audio']['channels'] = $info['opus']['out_channel_count'];
475  $info['audio']['sample_rate'] = $info['opus']['sample_rate'];
476  return true;
477  }
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
$info
Definition: example_052.php:80
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseVorbisComments()

getid3_ogg::ParseVorbisComments ( )
Todo:
use 'coverartmime' where available

Definition at line 532 of file module.audio.ogg.php.

References $data, getid3_handler\$getid3, $info, getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), getid3_lib\GetDataImageSize(), getid3_lib\LittleEndian2Int(), getid3_handler\notice(), OggPageSegmentLength(), ParseOggPageHeader(), and getid3_handler\warning().

Referenced by Analyze().

532  {
533  $info = &$this->getid3->info;
534 
535  $OriginalOffset = $this->ftell();
536  $commentdataoffset = 0;
537  $VorbisCommentPage = 1;
538 
539  switch ($info['audio']['dataformat']) {
540  case 'vorbis':
541  case 'speex':
542  case 'opus':
543  $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
544  $this->fseek($CommentStartOffset);
545  $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
546  $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
547 
548  if ($info['audio']['dataformat'] == 'vorbis') {
549  $commentdataoffset += (strlen('vorbis') + 1);
550  }
551  else if ($info['audio']['dataformat'] == 'opus') {
552  $commentdataoffset += strlen('OpusTags');
553  }
554 
555  break;
556 
557  case 'flac':
558  $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4;
559  $this->fseek($CommentStartOffset);
560  $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
561  break;
562 
563  default:
564  return false;
565  break;
566  }
567 
568  $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
569  $commentdataoffset += 4;
570 
571  $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
572  $commentdataoffset += $VendorSize;
573 
574  $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
575  $commentdataoffset += 4;
576  $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
577 
578  $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
579  $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
580  for ($i = 0; $i < $CommentsCount; $i++) {
581 
582  if ($i >= 10000) {
583  // https://github.com/owncloud/music/issues/212#issuecomment-43082336
584  $info['warning'][] = 'Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments';
585  break;
586  }
587 
588  $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
589 
590  if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
591  if ($oggpageinfo = $this->ParseOggPageHeader()) {
592  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
593 
594  $VorbisCommentPage++;
595 
596  // First, save what we haven't read yet
597  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
598 
599  // Then take that data off the end
600  $commentdata = substr($commentdata, 0, $commentdataoffset);
601 
602  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
603  $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
604  $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
605 
606  // Finally, stick the unused data back on the end
607  $commentdata .= $AsYetUnusedData;
608 
609  //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
610  $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1));
611  }
612 
613  }
614  $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
615 
616  // replace avdataoffset with position just after the last vorbiscomment
617  $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4;
618 
619  $commentdataoffset += 4;
620  while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) {
621  if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) {
622  $info['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments';
623  break 2;
624  }
625 
626  $VorbisCommentPage++;
627 
628  $oggpageinfo = $this->ParseOggPageHeader();
629  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
630 
631  // First, save what we haven't read yet
632  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
633 
634  // Then take that data off the end
635  $commentdata = substr($commentdata, 0, $commentdataoffset);
636 
637  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
638  $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
639  $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
640 
641  // Finally, stick the unused data back on the end
642  $commentdata .= $AsYetUnusedData;
643 
644  //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
645  if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
646  $info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
647  break;
648  }
649  $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
650  if ($readlength <= 0) {
651  $info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
652  break;
653  }
654  $commentdata .= $this->fread($readlength);
655 
656  //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
657  }
658  $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset;
659  $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']);
660  $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size'];
661 
662  if (!$commentstring) {
663 
664  // no comment?
665  $info['warning'][] = 'Blank Ogg comment ['.$i.']';
666 
667  } elseif (strstr($commentstring, '=')) {
668 
669  $commentexploded = explode('=', $commentstring, 2);
670  $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]);
671  $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
672 
673  if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') {
674 
675  // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
676  // The unencoded format is that of the FLAC picture block. The fields are stored in big endian order as in FLAC, picture data is stored according to the relevant standard.
677  // http://flac.sourceforge.net/format.html#metadata_block_picture
678  $flac = new getid3_flac($this->getid3);
679  $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']));
680  $flac->parsePICTURE();
681  $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0];
682  unset($flac);
683 
684  } elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') {
685 
686  $data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
687  $this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure');
689  $imageinfo = getid3_lib::GetDataImageSize($data);
690  if ($imageinfo === false || !isset($imageinfo['mime'])) {
691  $this->warning('COVERART vorbiscomment tag contains invalid image');
692  continue;
693  }
694 
695  $ogg = new self($this->getid3);
696  $ogg->setStringMode($data);
697  $info['ogg']['comments']['picture'][] = array(
698  'image_mime' => $imageinfo['mime'],
699  'datalength' => strlen($data),
700  'picturetype' => 'cover art',
701  'image_height' => $imageinfo['height'],
702  'image_width' => $imageinfo['width'],
703  'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
704  );
705  unset($ogg);
706 
707  } else {
708 
709  $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
710 
711  }
712 
713  } else {
714 
715  $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
716 
717  }
718  unset($ThisFileInfo_ogg_comments_raw[$i]);
719  }
720  unset($ThisFileInfo_ogg_comments_raw);
721 
722 
723  // Replay Gain Adjustment
724  // http://privatewww.essex.ac.uk/~djmrob/replaygain/
725  if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) {
726  foreach ($info['ogg']['comments'] as $index => $commentvalue) {
727  switch ($index) {
728  case 'rg_audiophile':
729  case 'replaygain_album_gain':
730  $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
731  unset($info['ogg']['comments'][$index]);
732  break;
733 
734  case 'rg_radio':
735  case 'replaygain_track_gain':
736  $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
737  unset($info['ogg']['comments'][$index]);
738  break;
739 
740  case 'replaygain_album_peak':
741  $info['replay_gain']['album']['peak'] = (double) $commentvalue[0];
742  unset($info['ogg']['comments'][$index]);
743  break;
744 
745  case 'rg_peak':
746  case 'replaygain_track_peak':
747  $info['replay_gain']['track']['peak'] = (double) $commentvalue[0];
748  unset($info['ogg']['comments'][$index]);
749  break;
750 
751  case 'replaygain_reference_loudness':
752  $info['replay_gain']['reference_volume'] = (double) $commentvalue[0];
753  unset($info['ogg']['comments'][$index]);
754  break;
755 
756  default:
757  // do nothing
758  break;
759  }
760  }
761  }
762 
763  $this->fseek($OriginalOffset);
764 
765  return true;
766  }
notice($text)
Definition: getid3.php:1748
OggPageSegmentLength($OggInfoArray, $SegmentNumber=1)
warning($text)
Definition: getid3.php:1744
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
GetDataImageSize($imgData)
$info
Definition: example_052.php:80
$data
fread($bytes)
Definition: getid3.php:1685
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
http://flac.sourceforge.net/format.html
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseVorbisCommentsFilepointer()

getid3_ogg::ParseVorbisCommentsFilepointer ( $fd,
$ThisFileInfo 
)

Definition at line 313 of file module.audio.ogg.php.

References getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), getid3_lib\GetDataImageSize(), getid3_lib\image_type_to_mime_type(), getid3_lib\LittleEndian2Int(), OggPageSegmentLength(), and ParseOggPageHeader().

Referenced by getid3_flac\FLACparseMETAdata(), and getid3_ogg().

313  {
314 
315  $OriginalOffset = ftell($fd);
316  $CommentStartOffset = $OriginalOffset;
317  $commentdataoffset = 0;
318  $VorbisCommentPage = 1;
319 
320  switch ($ThisFileInfo['audio']['dataformat']) {
321  case 'vorbis':
322  $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
323  fseek($fd, $CommentStartOffset, SEEK_SET);
324  $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
325  $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
326 
327  $commentdataoffset += (strlen('vorbis') + 1);
328  break;
329 
330  case 'flac':
331  fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
332  $commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']);
333  break;
334 
335  case 'speex':
336  $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
337  fseek($fd, $CommentStartOffset, SEEK_SET);
338  $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
339  $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
340  break;
341 
342  default:
343  return false;
344  break;
345  }
346 
347  $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
348  $commentdataoffset += 4;
349 
350  $ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
351  $commentdataoffset += $VendorSize;
352 
353  $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
354  $commentdataoffset += 4;
355  $ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
356 
357  $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
358  for ($i = 0; $i < $CommentsCount; $i++) {
359 
360  $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
361 
362  if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
363  $VorbisCommentPage++;
364 
365  $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
366  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
367 
368  // First, save what we haven't read yet
369  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
370 
371  // Then take that data off the end
372  $commentdata = substr($commentdata, 0, $commentdataoffset);
373 
374  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
375  $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
376  $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
377 
378  // Finally, stick the unused data back on the end
379  $commentdata .= $AsYetUnusedData;
380 
381  //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
382  $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
383 
384  }
385  $ThisFileInfo['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
386 
387  // replace avdataoffset with position just after the last vorbiscomment
388  $ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4;
389 
390  $commentdataoffset += 4;
391  while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) {
392  if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) {
393  $ThisFileInfo['error'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments';
394  break 2;
395  }
396 
397  $VorbisCommentPage++;
398 
399  $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
400  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
401 
402  // First, save what we haven't read yet
403  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
404 
405  // Then take that data off the end
406  $commentdata = substr($commentdata, 0, $commentdataoffset);
407 
408  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
409  $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
410  $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
411 
412  // Finally, stick the unused data back on the end
413  $commentdata .= $AsYetUnusedData;
414 
415  //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
416  $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
417 
418  //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
419  }
420  $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']);
421  $commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size'];
422 
423  if (!$commentstring) {
424 
425  // no comment?
426  $ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']';
427 
428  } elseif (strstr($commentstring, '=')) {
429 
430  $commentexploded = explode('=', $commentstring, 2);
431  $ThisFileInfo['ogg']['comments_raw'][$i]['key'] = strtoupper($commentexploded[0]);
432  $ThisFileInfo['ogg']['comments_raw'][$i]['value'] = @$commentexploded[1];
433  $ThisFileInfo['ogg']['comments_raw'][$i]['data'] = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']);
434 
435  $ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value'];
436 
437  $imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data']);
438  $ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::image_type_to_mime_type($imagechunkcheck[2]);
439  if (!$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] || ($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) {
440  unset($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime']);
441  unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']);
442  }
443 
444  } else {
445 
446  $ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
447 
448  }
449  }
450 
451 
452  // Replay Gain Adjustment
453  // http://privatewww.essex.ac.uk/~djmrob/replaygain/
454  if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) {
455  foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) {
456  switch ($index) {
457  case 'rg_audiophile':
458  case 'replaygain_album_gain':
459  $ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
460  unset($ThisFileInfo['ogg']['comments'][$index]);
461  break;
462 
463  case 'rg_radio':
464  case 'replaygain_track_gain':
465  $ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
466  unset($ThisFileInfo['ogg']['comments'][$index]);
467  break;
468 
469  case 'replaygain_album_peak':
470  $ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0];
471  unset($ThisFileInfo['ogg']['comments'][$index]);
472  break;
473 
474  case 'rg_peak':
475  case 'replaygain_track_peak':
476  $ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0];
477  unset($ThisFileInfo['ogg']['comments'][$index]);
478  break;
479 
480 
481  default:
482  // do nothing
483  break;
484  }
485  }
486  }
487 
488  fseek($fd, $OriginalOffset, SEEK_SET);
489 
490  return true;
491  }
OggPageSegmentLength($OggInfoArray, $SegmentNumber=1)
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
GetDataImageSize($imgData)
fread($bytes)
Definition: getid3.php:1685
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
image_type_to_mime_type($imagetypeid)
Definition: getid3.lib.php:419
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseVorbisPageHeader()

getid3_ogg::ParseVorbisPageHeader ( $filedata,
$filedataoffset,
$oggpageinfo 
)

Definition at line 381 of file module.audio.ogg.php.

References $info, and getid3_lib\LittleEndian2Int().

Referenced by Analyze().

381  {
382  $info = &$this->getid3->info;
383  $info['audio']['dataformat'] = 'vorbis';
384  $info['audio']['lossless'] = false;
385 
386  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
387  $filedataoffset += 1;
388  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
389  $filedataoffset += 6;
390  $info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
391  $filedataoffset += 4;
392  $info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
393  $filedataoffset += 1;
394  $info['audio']['channels'] = $info['ogg']['numberofchannels'];
395  $info['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
396  $filedataoffset += 4;
397  if ($info['ogg']['samplerate'] == 0) {
398  $info['error'][] = 'Corrupt Ogg file: sample rate == zero';
399  return false;
400  }
401  $info['audio']['sample_rate'] = $info['ogg']['samplerate'];
402  $info['ogg']['samples'] = 0; // filled in later
403  $info['ogg']['bitrate_average'] = 0; // filled in later
404  $info['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
405  $filedataoffset += 4;
406  $info['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
407  $filedataoffset += 4;
408  $info['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
409  $filedataoffset += 4;
410  $info['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
411  $info['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
412  $info['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
413 
414  $info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
415  if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) {
416  unset($info['ogg']['bitrate_max']);
417  $info['audio']['bitrate_mode'] = 'abr';
418  }
419  if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
420  unset($info['ogg']['bitrate_nominal']);
421  }
422  if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) {
423  unset($info['ogg']['bitrate_min']);
424  $info['audio']['bitrate_mode'] = 'abr';
425  }
426  return true;
427  }
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
$info
Definition: example_052.php:80
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ SpeexBandModeLookup() [1/2]

getid3_ogg::SpeexBandModeLookup (   $mode)

Definition at line 493 of file module.audio.ogg.php.

Referenced by Analyze(), and getid3_ogg().

493  {
494  static $SpeexBandModeLookup = array();
495  if (empty($SpeexBandModeLookup)) {
496  $SpeexBandModeLookup[0] = 'narrow';
497  $SpeexBandModeLookup[1] = 'wide';
498  $SpeexBandModeLookup[2] = 'ultra-wide';
499  }
500  return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
501  }
+ Here is the caller graph for this function:

◆ SpeexBandModeLookup() [2/2]

static getid3_ogg::SpeexBandModeLookup (   $mode)
static

Definition at line 768 of file module.audio.ogg.php.

768  {
769  static $SpeexBandModeLookup = array();
770  if (empty($SpeexBandModeLookup)) {
771  $SpeexBandModeLookup[0] = 'narrow';
772  $SpeexBandModeLookup[1] = 'wide';
773  $SpeexBandModeLookup[2] = 'ultra-wide';
774  }
775  return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
776  }

◆ TheoraColorSpace()

static getid3_ogg::TheoraColorSpace (   $colorspace_id)
static

Definition at line 816 of file module.audio.ogg.php.

816  {
817  // http://www.theora.org/doc/Theora.pdf (table 6.3)
818  static $TheoraColorSpaceLookup = array();
819  if (empty($TheoraColorSpaceLookup)) {
820  $TheoraColorSpaceLookup[0] = 'Undefined';
821  $TheoraColorSpaceLookup[1] = 'Rec. 470M';
822  $TheoraColorSpaceLookup[2] = 'Rec. 470BG';
823  $TheoraColorSpaceLookup[3] = 'Reserved';
824  }
825  return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
826  }

◆ TheoraPixelFormat()

static getid3_ogg::TheoraPixelFormat (   $pixelformat_id)
static

Definition at line 828 of file module.audio.ogg.php.

828  {
829  // http://www.theora.org/doc/Theora.pdf (table 6.4)
830  static $TheoraPixelFormatLookup = array();
831  if (empty($TheoraPixelFormatLookup)) {
832  $TheoraPixelFormatLookup[0] = '4:2:0';
833  $TheoraPixelFormatLookup[1] = 'Reserved';
834  $TheoraPixelFormatLookup[2] = '4:2:2';
835  $TheoraPixelFormatLookup[3] = '4:4:4';
836  }
837  return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null);
838  }

The documentation for this class was generated from the following file: