run:R W Run
54.21 KB
2026-03-11 16:18:51
R W Run
79.05 KB
2026-03-11 16:18:51
R W Run
1.36 KB
2026-03-11 16:18:51
R W Run
133.61 KB
2026-03-11 16:18:51
R W Run
26.5 KB
2026-03-11 16:18:51
R W Run
104.64 KB
2026-03-11 16:18:51
R W Run
164.97 KB
2026-03-11 16:18:51
R W Run
136.53 KB
2026-03-11 16:18:51
R W Run
38.46 KB
2026-03-11 16:18:51
R W Run
10.63 KB
2026-03-11 16:18:51
R W Run
19.23 KB
2026-03-11 16:18:51
R W Run
104.5 KB
2026-03-11 16:18:51
R W Run
42.74 KB
2026-03-11 16:18:51
R W Run
18.63 KB
2026-03-11 16:18:51
R W Run
14.7 KB
2026-03-11 16:18:51
R W Run
151.2 KB
2026-03-11 16:18:51
R W Run
11.75 KB
2026-03-11 16:18:51
R W Run
25.71 KB
2026-03-11 16:18:51
R W Run
error_log
📄module.audio.ogg.php
1<?php
2
3/////////////////////////////////////////////////////////////////
4/// getID3() by James Heinrich <info@getid3.org> //
5// available at https://github.com/JamesHeinrich/getID3 //
6// or https://www.getid3.org //
7// or http://getid3.sourceforge.net //
8// see readme.txt for more details //
9/////////////////////////////////////////////////////////////////
10// //
11// module.audio.ogg.php //
12// module for analyzing Ogg Vorbis, OggFLAC and Speex files //
13// dependencies: module.audio.flac.php //
14// ///
15/////////////////////////////////////////////////////////////////
16
17if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
18 exit;
19}
20getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
21
22class getid3_ogg extends getid3_handler
23{
24 /**
25 * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html
26 *
27 * @return bool
28 */
29 public function Analyze() {
30 $info = &$this->getid3->info;
31
32 $info['fileformat'] = 'ogg';
33
34 // Warn about illegal tags - only vorbiscomments are allowed
35 if (isset($info['id3v2'])) {
36 $this->warning('Illegal ID3v2 tag present.');
37 }
38 if (isset($info['id3v1'])) {
39 $this->warning('Illegal ID3v1 tag present.');
40 }
41 if (isset($info['ape'])) {
42 $this->warning('Illegal APE tag present.');
43 }
44
45
46 // Page 1 - Stream Header
47
48 $this->fseek($info['avdataoffset']);
49
50 $oggpageinfo = $this->ParseOggPageHeader();
51 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
52
53 if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
54 $this->error('Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)');
55 unset($info['fileformat']);
56 unset($info['ogg']);
57 return false;
58 }
59
60 $filedata = $this->fread($oggpageinfo['page_length']);
61 $filedataoffset = 0;
62
63 if (substr($filedata, 0, 4) == 'fLaC') {
64
65 $info['audio']['dataformat'] = 'flac';
66 $info['audio']['bitrate_mode'] = 'vbr';
67 $info['audio']['lossless'] = true;
68
69 } elseif (substr($filedata, 1, 6) == 'vorbis') {
70
71 $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
72
73 } elseif (substr($filedata, 0, 8) == 'OpusHead') {
74
75 if ($this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) === false) {
76 return false;
77 }
78
79 } elseif (substr($filedata, 0, 8) == 'Speex ') {
80
81 // http://www.speex.org/manual/node10.html
82
83 $info['audio']['dataformat'] = 'speex';
84 $info['mime_type'] = 'audio/speex';
85 $info['audio']['bitrate_mode'] = 'abr';
86 $info['audio']['lossless'] = false;
87
88 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex '
89 $filedataoffset += 8;
90 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
91 $filedataoffset += 20;
92 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
93 $filedataoffset += 4;
94 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
95 $filedataoffset += 4;
96 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
97 $filedataoffset += 4;
98 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
99 $filedataoffset += 4;
100 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
101 $filedataoffset += 4;
102 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
103 $filedataoffset += 4;
104 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
105 $filedataoffset += 4;
106 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
107 $filedataoffset += 4;
108 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
109 $filedataoffset += 4;
110 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
111 $filedataoffset += 4;
112 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
113 $filedataoffset += 4;
114 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
115 $filedataoffset += 4;
116 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
117 $filedataoffset += 4;
118
119 $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
120 $info['speex']['sample_rate'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
121 $info['speex']['channels'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
122 $info['speex']['vbr'] = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
123 $info['speex']['band_type'] = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
124
125 $info['audio']['sample_rate'] = $info['speex']['sample_rate'];
126 $info['audio']['channels'] = $info['speex']['channels'];
127 if ($info['speex']['vbr']) {
128 $info['audio']['bitrate_mode'] = 'vbr';
129 }
130
131 } elseif (substr($filedata, 0, 7) == "\x80".'theora') {
132
133 // http://www.theora.org/doc/Theora.pdf (section 6.2)
134
135 $info['ogg']['pageheader']['theora']['theora_magic'] = substr($filedata, $filedataoffset, 7); // hard-coded to "\x80.'theora'
136 $filedataoffset += 7;
137 $info['ogg']['pageheader']['theora']['version_major'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
138 $filedataoffset += 1;
139 $info['ogg']['pageheader']['theora']['version_minor'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
140 $filedataoffset += 1;
141 $info['ogg']['pageheader']['theora']['version_revision'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
142 $filedataoffset += 1;
143 $info['ogg']['pageheader']['theora']['frame_width_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
144 $filedataoffset += 2;
145 $info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
146 $filedataoffset += 2;
147 $info['ogg']['pageheader']['theora']['resolution_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
148 $filedataoffset += 3;
149 $info['ogg']['pageheader']['theora']['resolution_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
150 $filedataoffset += 3;
151 $info['ogg']['pageheader']['theora']['picture_offset_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
152 $filedataoffset += 1;
153 $info['ogg']['pageheader']['theora']['picture_offset_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
154 $filedataoffset += 1;
155 $info['ogg']['pageheader']['theora']['frame_rate_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
156 $filedataoffset += 4;
157 $info['ogg']['pageheader']['theora']['frame_rate_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
158 $filedataoffset += 4;
159 $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
160 $filedataoffset += 3;
161 $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
162 $filedataoffset += 3;
163 $info['ogg']['pageheader']['theora']['color_space_id'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
164 $filedataoffset += 1;
165 $info['ogg']['pageheader']['theora']['nominal_bitrate'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
166 $filedataoffset += 3;
167 $info['ogg']['pageheader']['theora']['flags'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
168 $filedataoffset += 2;
169
170 $info['ogg']['pageheader']['theora']['quality'] = ($info['ogg']['pageheader']['theora']['flags'] & 0xFC00) >> 10;
171 $info['ogg']['pageheader']['theora']['kfg_shift'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x03E0) >> 5;
172 $info['ogg']['pageheader']['theora']['pixel_format_id'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0018) >> 3;
173 $info['ogg']['pageheader']['theora']['reserved'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0007) >> 0; // should be 0
174 $info['ogg']['pageheader']['theora']['color_space'] = self::TheoraColorSpace($info['ogg']['pageheader']['theora']['color_space_id']);
175 $info['ogg']['pageheader']['theora']['pixel_format'] = self::TheoraPixelFormat($info['ogg']['pageheader']['theora']['pixel_format_id']);
176
177 $info['video']['dataformat'] = 'theora';
178 $info['mime_type'] = 'video/ogg';
179 //$info['audio']['bitrate_mode'] = 'abr';
180 //$info['audio']['lossless'] = false;
181 $info['video']['resolution_x'] = $info['ogg']['pageheader']['theora']['resolution_x'];
182 $info['video']['resolution_y'] = $info['ogg']['pageheader']['theora']['resolution_y'];
183 if ($info['ogg']['pageheader']['theora']['frame_rate_denominator'] > 0) {
184 $info['video']['frame_rate'] = (float) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
185 }
186 if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
187 $info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
188 }
189$this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable');
190
191
192 } elseif (substr($filedata, 0, 8) == "fishead\x00") {
193
194 // Ogg Skeleton version 3.0 Format Specification
195 // http://xiph.org/ogg/doc/skeleton.html
196 $filedataoffset += 8;
197 $info['ogg']['skeleton']['fishead']['raw']['version_major'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
198 $filedataoffset += 2;
199 $info['ogg']['skeleton']['fishead']['raw']['version_minor'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
200 $filedataoffset += 2;
201 $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
202 $filedataoffset += 8;
203 $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
204 $filedataoffset += 8;
205 $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
206 $filedataoffset += 8;
207 $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
208 $filedataoffset += 8;
209 $info['ogg']['skeleton']['fishead']['raw']['utc'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20));
210 $filedataoffset += 20;
211
212 $info['ogg']['skeleton']['fishead']['version'] = $info['ogg']['skeleton']['fishead']['raw']['version_major'].'.'.$info['ogg']['skeleton']['fishead']['raw']['version_minor'];
213 $info['ogg']['skeleton']['fishead']['presentationtime'] = getid3_lib::SafeDiv($info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'], $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator']);
214 $info['ogg']['skeleton']['fishead']['basetime'] = getid3_lib::SafeDiv($info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'], $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator']);
215 $info['ogg']['skeleton']['fishead']['utc'] = $info['ogg']['skeleton']['fishead']['raw']['utc'];
216
217
218 $counter = 0;
219 do {
220 $oggpageinfo = $this->ParseOggPageHeader();
221 $info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo;
222 $filedata = $this->fread($oggpageinfo['page_length']);
223 $this->fseek($oggpageinfo['page_end_offset']);
224
225 if (substr($filedata, 0, 8) == "fisbone\x00") {
226
227 $filedataoffset = 8;
228 $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
229 $filedataoffset += 4;
230 $info['ogg']['skeleton']['fisbone']['raw']['serial_number'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
231 $filedataoffset += 4;
232 $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
233 $filedataoffset += 4;
234 $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
235 $filedataoffset += 8;
236 $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
237 $filedataoffset += 8;
238 $info['ogg']['skeleton']['fisbone']['raw']['basegranule'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
239 $filedataoffset += 8;
240 $info['ogg']['skeleton']['fisbone']['raw']['preroll'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
241 $filedataoffset += 4;
242 $info['ogg']['skeleton']['fisbone']['raw']['granuleshift'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
243 $filedataoffset += 1;
244 $info['ogg']['skeleton']['fisbone']['raw']['padding'] = substr($filedata, $filedataoffset, 3);
245 $filedataoffset += 3;
246
247 } elseif (substr($filedata, 1, 6) == 'theora') {
248
249 $info['video']['dataformat'] = 'theora1';
250 $this->error('Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']');
251 //break;
252
253 } elseif (substr($filedata, 1, 6) == 'vorbis') {
254
255 $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
256
257 } else {
258 $this->error('unexpected');
259 //break;
260 }
261 //} while ($oggpageinfo['page_seqno'] == 0);
262 } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00"));
263
264 $this->fseek($oggpageinfo['page_start_offset']);
265
266 $this->error('Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']');
267 //return false;
268
269 } elseif (substr($filedata, 0, 5) == "\x7F".'FLAC') {
270 // https://xiph.org/flac/ogg_mapping.html
271
272 $info['audio']['dataformat'] = 'flac';
273 $info['audio']['bitrate_mode'] = 'vbr';
274 $info['audio']['lossless'] = true;
275
276 $info['ogg']['flac']['header']['version_major'] = ord(substr($filedata, 5, 1));
277 $info['ogg']['flac']['header']['version_minor'] = ord(substr($filedata, 6, 1));
278 $info['ogg']['flac']['header']['header_packets'] = getid3_lib::BigEndian2Int(substr($filedata, 7, 2)) + 1; // "A two-byte, big-endian binary number signifying the number of header (non-audio) packets, not including this one. This number may be zero (0x0000) to signify 'unknown' but be aware that some decoders may not be able to handle such streams."
279 $info['ogg']['flac']['header']['magic'] = substr($filedata, 9, 4);
280 if ($info['ogg']['flac']['header']['magic'] != 'fLaC') {
281 $this->error('Ogg-FLAC expecting "fLaC", found "'.$info['ogg']['flac']['header']['magic'].'" ('.trim(getid3_lib::PrintHexBytes($info['ogg']['flac']['header']['magic'])).')');
282 return false;
283 }
284 $info['ogg']['flac']['header']['STREAMINFO_bytes'] = getid3_lib::BigEndian2Int(substr($filedata, 13, 4));
285 $info['flac']['STREAMINFO'] = getid3_flac::parseSTREAMINFOdata(substr($filedata, 17, 34));
286 if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
287 $info['audio']['bitrate_mode'] = 'vbr';
288 $info['audio']['sample_rate'] = $info['flac']['STREAMINFO']['sample_rate'];
289 $info['audio']['channels'] = $info['flac']['STREAMINFO']['channels'];
290 $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
291 $info['playtime_seconds'] = getid3_lib::SafeDiv($info['flac']['STREAMINFO']['samples_stream'], $info['flac']['STREAMINFO']['sample_rate']);
292 }
293
294 } else {
295
296 $this->error('Expecting one of "vorbis", "Speex", "OpusHead", "vorbis", "fishhead", "theora", "fLaC" identifier strings, found "'.substr($filedata, 0, 8).'"');
297 unset($info['ogg']);
298 unset($info['mime_type']);
299 return false;
300
301 }
302
303 // Page 2 - Comment Header
304 $oggpageinfo = $this->ParseOggPageHeader();
305 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
306
307 switch ($info['audio']['dataformat']) {
308 case 'vorbis':
309 $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
310 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
311 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
312
313 $this->ParseVorbisComments();
314 break;
315
316 case 'flac':
317 $flac = new getid3_flac($this->getid3);
318 if (!$flac->parseMETAdata()) {
319 $this->error('Failed to parse FLAC headers');
320 return false;
321 }
322 unset($flac);
323 break;
324
325 case 'speex':
326 $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
327 $this->ParseVorbisComments();
328 break;
329
330 case 'opus':
331 $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
332 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 0, 8); // hard-coded to 'OpusTags'
333 if(substr($filedata, 0, 8) != 'OpusTags') {
334 $this->error('Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"');
335 return false;
336 }
337
338 $this->ParseVorbisComments();
339 break;
340
341 }
342
343 // Last Page - Number of Samples
344 if (!getid3_lib::intValueSupported($info['avdataend'])) {
345
346 $this->warning('Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)');
347
348 } else {
349
350 $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0));
351 $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size()));
352 if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
353 if (substr($LastChunkOfOgg, 13, 8) === "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF") {
354 // https://github.com/JamesHeinrich/getID3/issues/450
355 // "Sometimes, Opus encoders (WhatsApp voice registrations and others) add a special last header with a granule duration of 0xFFFFFFFFFFFFFF.
356 // This value indicates "this is the end," but must be ignored; otherwise, it makes calculations wrong."
357 $LastOggSpostion = strpos($LastChunkOfOgg, 'SggO', $LastOggSpostion + 1);
358 }
359 $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO')));
360 $info['avdataend'] = $this->ftell();
361 $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
362 $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
363 if ($info['ogg']['samples'] == 0) {
364 $this->error('Corrupt Ogg file: eos.number of samples == zero');
365 return false;
366 }
367 if (!empty($info['audio']['sample_rate'])) {
368 $info['ogg']['bitrate_average'] = (($info['avdataend'] - $info['avdataoffset']) * 8) * $info['audio']['sample_rate'] / $info['ogg']['samples'];
369 }
370 }
371
372 }
373
374 if (!empty($info['ogg']['bitrate_average'])) {
375 $info['audio']['bitrate'] = $info['ogg']['bitrate_average'];
376 } elseif (!empty($info['ogg']['bitrate_nominal'])) {
377 $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal'];
378 } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) {
379 $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2;
380 }
381 if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
382 if ($info['audio']['bitrate'] == 0) {
383 $this->error('Corrupt Ogg file: bitrate_audio == zero');
384 return false;
385 }
386 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']);
387 }
388
389 if (isset($info['ogg']['vendor'])) {
390 $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']);
391
392 // Vorbis only
393 if ($info['audio']['dataformat'] == 'vorbis') {
394
395 // Vorbis 1.0 starts with Xiph.Org
396 if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
397
398 if ($info['audio']['bitrate_mode'] == 'abr') {
399
400 // Set -b 128 on abr files
401 $info['audio']['encoder_options'] = '-b '.round($info['ogg']['bitrate_nominal'] / 1000);
402
403 } elseif (($info['audio']['bitrate_mode'] == 'vbr') && ($info['audio']['channels'] == 2) && ($info['audio']['sample_rate'] >= 44100) && ($info['audio']['sample_rate'] <= 48000)) {
404 // Set -q N on vbr files
405 $info['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']);
406
407 }
408 }
409
410 if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) {
411 $info['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($info['ogg']['bitrate_nominal'] / 1000)).'kbps';
412 }
413 }
414 }
415
416 return true;
417 }
418
419 /**
420 * @param string $filedata
421 * @param int $filedataoffset
422 * @param array $oggpageinfo
423 *
424 * @return bool
425 */
426 public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
427 $info = &$this->getid3->info;
428 $info['audio']['dataformat'] = 'vorbis';
429 $info['audio']['lossless'] = false;
430
431 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
432 $filedataoffset += 1;
433 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
434 $filedataoffset += 6;
435 $info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
436 $filedataoffset += 4;
437 $info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
438 $filedataoffset += 1;
439 $info['audio']['channels'] = $info['ogg']['numberofchannels'];
440 $info['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
441 $filedataoffset += 4;
442 if ($info['ogg']['samplerate'] == 0) {
443 $this->error('Corrupt Ogg file: sample rate == zero');
444 return false;
445 }
446 $info['audio']['sample_rate'] = $info['ogg']['samplerate'];
447 $info['ogg']['samples'] = 0; // filled in later
448 $info['ogg']['bitrate_average'] = 0; // filled in later
449 $info['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
450 $filedataoffset += 4;
451 $info['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
452 $filedataoffset += 4;
453 $info['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
454 $filedataoffset += 4;
455 $info['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
456 $info['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
457 $info['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
458
459 $info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
460 if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) {
461 unset($info['ogg']['bitrate_max']);
462 $info['audio']['bitrate_mode'] = 'abr';
463 }
464 if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
465 unset($info['ogg']['bitrate_nominal']);
466 }
467 if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) {
468 unset($info['ogg']['bitrate_min']);
469 $info['audio']['bitrate_mode'] = 'abr';
470 }
471 return true;
472 }
473
474 /**
475 * @link http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
476 *
477 * @param string $filedata
478 * @param int $filedataoffset
479 * @param array $oggpageinfo
480 *
481 * @return bool
482 */
483 public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
484 $info = &$this->getid3->info;
485 $info['audio']['dataformat'] = 'opus';
486 $info['mime_type'] = 'audio/ogg; codecs=opus';
487
488 /** @todo find a usable way to detect abr (vbr that is padded to be abr) */
489 $info['audio']['bitrate_mode'] = 'vbr';
490
491 $info['audio']['lossless'] = false;
492
493 $info['ogg']['pageheader']['opus']['opus_magic'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'OpusHead'
494 $filedataoffset += 8;
495 $info['ogg']['pageheader']['opus']['version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
496 $filedataoffset += 1;
497
498 if ($info['ogg']['pageheader']['opus']['version'] < 1 || $info['ogg']['pageheader']['opus']['version'] > 15) {
499 $this->error('Unknown opus version number (only accepting 1-15)');
500 return false;
501 }
502
503 $info['ogg']['pageheader']['opus']['out_channel_count'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
504 $filedataoffset += 1;
505
506 if ($info['ogg']['pageheader']['opus']['out_channel_count'] == 0) {
507 $this->error('Invalid channel count in opus header (must not be zero)');
508 return false;
509 }
510
511 $info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
512 $filedataoffset += 2;
513
514 $info['ogg']['pageheader']['opus']['input_sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
515 $filedataoffset += 4;
516
517 //$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
518 //$filedataoffset += 2;
519
520 //$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
521 //$filedataoffset += 1;
522
523 $info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version'];
524 $info['opus']['sample_rate_input'] = $info['ogg']['pageheader']['opus']['input_sample_rate'];
525 $info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
526
527 $info['audio']['channels'] = $info['opus']['out_channel_count'];
528 $info['audio']['sample_rate_input'] = $info['opus']['sample_rate_input'];
529 $info['audio']['sample_rate'] = 48000; // "All Opus audio is coded at 48 kHz, and should also be decoded at 48 kHz for playback (unless the target hardware does not support this sampling rate). However, this field may be used to resample the audio back to the original sampling rate, for example, when saving the output to a file." -- https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/structOpusHead.html
530 return true;
531 }
532
533 /**
534 * @return array|false
535 */
536 public function ParseOggPageHeader() {
537 // http://xiph.org/ogg/vorbis/doc/framing.html
538 $oggheader = array();
539 $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
540
541 $filedata = $this->fread($this->getid3->fread_buffer_size());
542 $filedataoffset = 0;
543 while (substr($filedata, $filedataoffset++, 4) != 'OggS') {
544 if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
545 // should be found before here
546 return false;
547 }
548 if (($filedataoffset + 28) > strlen($filedata)) {
549 if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === '')) {
550 // get some more data, unless eof, in which case fail
551 return false;
552 }
553 }
554 }
555 $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
556
557 $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
558 $filedataoffset += 1;
559 $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
560 $filedataoffset += 1;
561 $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
562 $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
563 $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
564
565 $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
566 $filedataoffset += 8;
567 $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
568 $filedataoffset += 4;
569 $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
570 $filedataoffset += 4;
571 $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
572 $filedataoffset += 4;
573 $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
574 $filedataoffset += 1;
575 $oggheader['page_length'] = 0;
576 for ($i = 0; $i < $oggheader['page_segments']; $i++) {
577 $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
578 $filedataoffset += 1;
579 $oggheader['page_length'] += $oggheader['segment_table'][$i];
580 }
581 $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
582 $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
583 $this->fseek($oggheader['header_end_offset']);
584
585 return $oggheader;
586 }
587
588 /**
589 * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
590 *
591 * @return bool
592 */
593 public function ParseVorbisComments() {
594 $info = &$this->getid3->info;
595
596 $OriginalOffset = $this->ftell();
597 $commentdata = null;
598 $commentdataoffset = 0;
599 $VorbisCommentPage = 1;
600 $CommentStartOffset = 0;
601
602 switch ($info['audio']['dataformat']) {
603 case 'vorbis':
604 case 'speex':
605 case 'opus':
606 $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
607 $this->fseek($CommentStartOffset);
608 $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
609 $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
610
611 if ($info['audio']['dataformat'] == 'vorbis') {
612 $commentdataoffset += (strlen('vorbis') + 1);
613 }
614 else if ($info['audio']['dataformat'] == 'opus') {
615 $commentdataoffset += strlen('OpusTags');
616 }
617
618 break;
619
620 case 'flac':
621 $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4;
622 $this->fseek($CommentStartOffset);
623 $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
624 break;
625
626 default:
627 return false;
628 }
629
630 $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
631 $commentdataoffset += 4;
632
633 $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
634 $commentdataoffset += $VendorSize;
635
636 $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
637 $commentdataoffset += 4;
638 $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
639
640 $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
641 $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
642 for ($i = 0; $i < $CommentsCount; $i++) {
643
644 if ($i >= 10000) {
645 // https://github.com/owncloud/music/issues/212#issuecomment-43082336
646 $this->warning('Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments');
647 break;
648 }
649
650 $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
651
652 if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
653 if ($oggpageinfo = $this->ParseOggPageHeader()) {
654 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
655
656 $VorbisCommentPage++;
657
658 // First, save what we haven't read yet
659 $AsYetUnusedData = substr($commentdata, $commentdataoffset);
660
661 // Then take that data off the end
662 $commentdata = substr($commentdata, 0, $commentdataoffset);
663
664 // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
665 $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
666 $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
667
668 // Finally, stick the unused data back on the end
669 $commentdata .= $AsYetUnusedData;
670
671 //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
672 $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1));
673 }
674
675 }
676 $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
677
678 // replace avdataoffset with position just after the last vorbiscomment
679 $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4;
680
681 $commentdataoffset += 4;
682 while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) {
683 if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) {
684 $this->warning('Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments');
685 break 2;
686 }
687
688 $VorbisCommentPage++;
689
690 if ($oggpageinfo = $this->ParseOggPageHeader()) {
691 $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
692
693 // First, save what we haven't read yet
694 $AsYetUnusedData = substr($commentdata, $commentdataoffset);
695
696 // Then take that data off the end
697 $commentdata = substr($commentdata, 0, $commentdataoffset);
698
699 // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
700 $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
701 $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
702
703 // Finally, stick the unused data back on the end
704 $commentdata .= $AsYetUnusedData;
705
706 //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
707 if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
708 $this->warning('undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
709 break;
710 }
711 $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
712 if ($readlength <= 0) {
713 $this->warning('invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
714 break;
715 }
716 $commentdata .= $this->fread($readlength);
717
718 //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
719 } else {
720 $this->warning('failed to ParseOggPageHeader() at offset '.$this->ftell());
721 break;
722 }
723 }
724 $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset;
725 $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']);
726 $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size'];
727
728 if (!$commentstring) {
729
730 // no comment?
731 $this->warning('Blank Ogg comment ['.$i.']');
732
733 } elseif (strstr($commentstring, '=')) {
734
735 $commentexploded = explode('=', $commentstring, 2);
736 $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]);
737 $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
738
739 if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') {
740
741 // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
742 // 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.
743 // http://flac.sourceforge.net/format.html#metadata_block_picture
744 $flac = new getid3_flac($this->getid3);
745 $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']));
746 $flac->parsePICTURE();
747 $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0];
748 unset($flac);
749
750 } elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') {
751
752 $data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
753 $this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure');
754 /** @todo use 'coverartmime' where available */
755 $imageinfo = getid3_lib::GetDataImageSize($data);
756 if ($imageinfo === false || !isset($imageinfo['mime'])) {
757 $this->warning('COVERART vorbiscomment tag contains invalid image');
758 continue;
759 }
760
761 $ogg = new self($this->getid3);
762 $ogg->setStringMode($data);
763 $info['ogg']['comments']['picture'][] = array(
764 'image_mime' => $imageinfo['mime'],
765 'datalength' => strlen($data),
766 'picturetype' => 'cover art',
767 'image_height' => $imageinfo['height'],
768 'image_width' => $imageinfo['width'],
769 'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
770 );
771 unset($ogg);
772
773 } else {
774
775 $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
776
777 }
778
779 } else {
780
781 $this->warning('[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring);
782
783 }
784 unset($ThisFileInfo_ogg_comments_raw[$i]);
785 }
786 unset($ThisFileInfo_ogg_comments_raw);
787
788
789 // Replay Gain Adjustment
790 // http://privatewww.essex.ac.uk/~djmrob/replaygain/
791 if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) {
792 foreach ($info['ogg']['comments'] as $index => $commentvalue) {
793 switch ($index) {
794 case 'rg_audiophile':
795 case 'replaygain_album_gain':
796 $info['replay_gain']['album']['adjustment'] = (float) $commentvalue[0];
797 unset($info['ogg']['comments'][$index]);
798 break;
799
800 case 'rg_radio':
801 case 'replaygain_track_gain':
802 $info['replay_gain']['track']['adjustment'] = (float) $commentvalue[0];
803 unset($info['ogg']['comments'][$index]);
804 break;
805
806 case 'replaygain_album_peak':
807 $info['replay_gain']['album']['peak'] = (float) $commentvalue[0];
808 unset($info['ogg']['comments'][$index]);
809 break;
810
811 case 'rg_peak':
812 case 'replaygain_track_peak':
813 $info['replay_gain']['track']['peak'] = (float) $commentvalue[0];
814 unset($info['ogg']['comments'][$index]);
815 break;
816
817 case 'replaygain_reference_loudness':
818 $info['replay_gain']['reference_volume'] = (float) $commentvalue[0];
819 unset($info['ogg']['comments'][$index]);
820 break;
821
822 default:
823 // do nothing
824 break;
825 }
826 }
827 }
828
829 $this->fseek($OriginalOffset);
830
831 return true;
832 }
833
834 /**
835 * @param int $mode
836 *
837 * @return string|null
838 */
839 public static function SpeexBandModeLookup($mode) {
840 static $SpeexBandModeLookup = array();
841 if (empty($SpeexBandModeLookup)) {
842 $SpeexBandModeLookup[0] = 'narrow';
843 $SpeexBandModeLookup[1] = 'wide';
844 $SpeexBandModeLookup[2] = 'ultra-wide';
845 }
846 return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
847 }
848
849 /**
850 * @param array $OggInfoArray
851 * @param int $SegmentNumber
852 *
853 * @return int
854 */
855 public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
856 $segmentlength = 0;
857 for ($i = 0; $i < $SegmentNumber; $i++) {
858 $segmentlength = 0;
859 foreach ($OggInfoArray['segment_table'] as $key => $value) {
860 $segmentlength += $value;
861 if ($value < 255) {
862 break;
863 }
864 }
865 }
866 return $segmentlength;
867 }
868
869 /**
870 * @param int $nominal_bitrate
871 *
872 * @return float
873 */
874 public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
875
876 // decrease precision
877 $nominal_bitrate = $nominal_bitrate / 1000;
878
879 if ($nominal_bitrate < 128) {
880 // q-1 to q4
881 $qval = ($nominal_bitrate - 64) / 16;
882 } elseif ($nominal_bitrate < 256) {
883 // q4 to q8
884 $qval = $nominal_bitrate / 32;
885 } elseif ($nominal_bitrate < 320) {
886 // q8 to q9
887 $qval = ($nominal_bitrate + 256) / 64;
888 } else {
889 // q9 to q10
890 $qval = ($nominal_bitrate + 1300) / 180;
891 }
892 //return $qval; // 5.031324
893 //return intval($qval); // 5
894 return round($qval, 1); // 5 or 4.9
895 }
896
897 /**
898 * @param int $colorspace_id
899 *
900 * @return string|null
901 */
902 public static function TheoraColorSpace($colorspace_id) {
903 // http://www.theora.org/doc/Theora.pdf (table 6.3)
904 static $TheoraColorSpaceLookup = array();
905 if (empty($TheoraColorSpaceLookup)) {
906 $TheoraColorSpaceLookup[0] = 'Undefined';
907 $TheoraColorSpaceLookup[1] = 'Rec. 470M';
908 $TheoraColorSpaceLookup[2] = 'Rec. 470BG';
909 $TheoraColorSpaceLookup[3] = 'Reserved';
910 }
911 return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
912 }
913
914 /**
915 * @param int $pixelformat_id
916 *
917 * @return string|null
918 */
919 public static function TheoraPixelFormat($pixelformat_id) {
920 // http://www.theora.org/doc/Theora.pdf (table 6.4)
921 static $TheoraPixelFormatLookup = array();
922 if (empty($TheoraPixelFormatLookup)) {
923 $TheoraPixelFormatLookup[0] = '4:2:0';
924 $TheoraPixelFormatLookup[1] = 'Reserved';
925 $TheoraPixelFormatLookup[2] = '4:2:2';
926 $TheoraPixelFormatLookup[3] = '4:4:4';
927 }
928 return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null);
929 }
930
931}
932
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1