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 ( )

Reimplemented from getid3_handler.

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

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 }
@tutorial http://flac.sourceforge.net/format.html
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
fread($bytes)
Definition: getid3.php:1685
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
static intValueSupported($num)
Definition: getid3.lib.php:80
BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: getid3.lib.php:234
SpeexBandModeLookup($mode)
static TheoraPixelFormat($pixelformat_id)
ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo)
static TheoraColorSpace($colorspace_id)
$info
Definition: example_052.php:80

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

+ 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.

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
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);
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 }
FLACparseMETAdata(&$fd, &$ThisFileInfo)
ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo)
const GETID3_FREAD_BUFFER_SIZE
Definition: getid3.php:14

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

+ 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.

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 }

Referenced by ParseVorbisComments(), and ParseVorbisCommentsFilepointer().

+ 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.

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 }

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

+ Here is the call graph for this function:

◆ ParseOggPageHeader() [2/2]

getid3_ogg::ParseOggPageHeader ( )

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

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 }

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().

+ 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.

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 }

References $info, and getid3_lib\LittleEndian2Int().

Referenced by Analyze().

+ 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.

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');
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
warning($text)
Definition: getid3.php:1744
GetDataImageSize($imgData)
OggPageSegmentLength($OggInfoArray, $SegmentNumber=1)
$data

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().

+ 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.

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 }
image_type_to_mime_type($imagetypeid)
Definition: getid3.lib.php:419

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().

+ 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.

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 }

References $info, and getid3_lib\LittleEndian2Int().

Referenced by Analyze().

+ 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.

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 }

Referenced by Analyze(), and getid3_ogg().

+ 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 }

Referenced by Analyze().

+ Here is the caller graph for this function:

◆ 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 }

Referenced by Analyze().

+ Here is the caller graph for this function:

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