ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
getid3_id3v2 Class Reference
+ Inheritance diagram for getid3_id3v2:
+ Collaboration diagram for getid3_id3v2:

Public Member Functions

 Analyze ()
 
 ParseID3v2GenreString ($genrestring)
 
 ParseID3v2Frame (&$parsedFrame)
 
 DeUnsynchronise ($data)
 
 LookupExtendedHeaderRestrictionsTagSizeLimits ($index)
 
 LookupExtendedHeaderRestrictionsTextEncodings ($index)
 
 LookupExtendedHeaderRestrictionsTextFieldSize ($index)
 
 LookupExtendedHeaderRestrictionsImageEncoding ($index)
 
 LookupExtendedHeaderRestrictionsImageSizeSize ($index)
 
 LookupCurrencyUnits ($currencyid)
 
 LookupCurrencyCountry ($currencyid)
 
- 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 LanguageLookup ($languagecode, $casesensitive=false)
 
static ETCOEventLookup ($index)
 
static SYTLContentTypeLookup ($index)
 
static APICPictureTypeLookup ($index, $returnarray=false)
 
static COMRReceivedAsLookup ($index)
 
static RVA2ChannelTypeLookup ($index)
 
static FrameNameLongLookup ($framename)
 
static FrameNameShortLookup ($framename)
 
static TextEncodingTerminatorLookup ($encoding)
 
static TextEncodingNameLookup ($encoding)
 
static IsValidID3v2FrameName ($framename, $id3v2majorversion)
 
static IsANumber ($numberstring, $allowdecimal=false, $allownegative=false)
 
static IsValidDateStampString ($datestamp)
 
static ID3v2HeaderLength ($majorversion)
 
static ID3v22iTunesBrokenFrameName ($frame_name)
 

Data Fields

 $StartingOffset = 0
 

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 19 of file module.tag.id3v2.php.

Member Function Documentation

◆ Analyze()

getid3_id3v2::Analyze ( )

Reimplemented from getid3_handler.

Definition at line 23 of file module.tag.id3v2.php.

23 {
24 $info = &$this->getid3->info;
25
26 // Overall tag structure:
27 // +-----------------------------+
28 // | Header (10 bytes) |
29 // +-----------------------------+
30 // | Extended Header |
31 // | (variable length, OPTIONAL) |
32 // +-----------------------------+
33 // | Frames (variable length) |
34 // +-----------------------------+
35 // | Padding |
36 // | (variable length, OPTIONAL) |
37 // +-----------------------------+
38 // | Footer (10 bytes, OPTIONAL) |
39 // +-----------------------------+
40
41 // Header
42 // ID3v2/file identifier "ID3"
43 // ID3v2 version $04 00
44 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
45 // ID3v2 size 4 * %0xxxxxxx
46
47
48 // shortcuts
49 $info['id3v2']['header'] = true;
50 $thisfile_id3v2 = &$info['id3v2'];
51 $thisfile_id3v2['flags'] = array();
52 $thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
53
54
55 $this->fseek($this->StartingOffset);
56 $header = $this->fread(10);
57 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
58
59 $thisfile_id3v2['majorversion'] = ord($header{3});
60 $thisfile_id3v2['minorversion'] = ord($header{4});
61
62 // shortcut
63 $id3v2_majorversion = &$thisfile_id3v2['majorversion'];
64
65 } else {
66
67 unset($info['id3v2']);
68 return false;
69
70 }
71
72 if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
73
74 $this->error('this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']);
75 return false;
76
77 }
78
79 $id3_flags = ord($header{5});
80 switch ($id3v2_majorversion) {
81 case 2:
82 // %ab000000 in v2.2
83 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
84 $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression
85 break;
86
87 case 3:
88 // %abc00000 in v2.3
89 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
90 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
91 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
92 break;
93
94 case 4:
95 // %abcd0000 in v2.4
96 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
97 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
98 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
99 $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present
100 break;
101 }
102
103 $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
104
105 $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset;
106 $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
107
108
109
110 // create 'encoding' key - used by getid3::HandleAllTags()
111 // in ID3v2 every field can have it's own encoding type
112 // so force everything to UTF-8 so it can be handled consistantly
113 $thisfile_id3v2['encoding'] = 'UTF-8';
114
115
116 // Frames
117
118 // All ID3v2 frames consists of one frame header followed by one or more
119 // fields containing the actual information. The header is always 10
120 // bytes and laid out as follows:
121 //
122 // Frame ID $xx xx xx xx (four characters)
123 // Size 4 * %0xxxxxxx
124 // Flags $xx xx
125
126 $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
127 if (!empty($thisfile_id3v2['exthead']['length'])) {
128 $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4);
129 }
130 if (!empty($thisfile_id3v2_flags['isfooter'])) {
131 $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
132 }
133 if ($sizeofframes > 0) {
134
135 $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable
136
137 // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
138 if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
139 $framedata = $this->DeUnsynchronise($framedata);
140 }
141 // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
142 // of on tag level, making it easier to skip frames, increasing the streamability
143 // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
144 // there exists an unsynchronised frame, while the new unsynchronisation flag in
145 // the frame header [S:4.1.2] indicates unsynchronisation.
146
147
148 //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
149 $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
150
151
152 // Extended Header
153 if (!empty($thisfile_id3v2_flags['exthead'])) {
154 $extended_header_offset = 0;
155
156 if ($id3v2_majorversion == 3) {
157
158 // v2.3 definition:
159 //Extended header size $xx xx xx xx // 32-bit integer
160 //Extended Flags $xx xx
161 // %x0000000 %00000000 // v2.3
162 // x - CRC data present
163 //Size of padding $xx xx xx xx
164
165 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0);
166 $extended_header_offset += 4;
167
168 $thisfile_id3v2['exthead']['flag_bytes'] = 2;
169 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
170 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
171
172 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000);
173
174 $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
175 $extended_header_offset += 4;
176
177 if ($thisfile_id3v2['exthead']['flags']['crc']) {
178 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
179 $extended_header_offset += 4;
180 }
181 $extended_header_offset += $thisfile_id3v2['exthead']['padding_size'];
182
183 } elseif ($id3v2_majorversion == 4) {
184
185 // v2.4 definition:
186 //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer
187 //Number of flag bytes $01
188 //Extended Flags $xx
189 // %0bcd0000 // v2.4
190 // b - Tag is an update
191 // Flag data length $00
192 // c - CRC data present
193 // Flag data length $05
194 // Total frame CRC 5 * %0xxxxxxx
195 // d - Tag restrictions
196 // Flag data length $01
197
198 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true);
199 $extended_header_offset += 4;
200
201 $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1
202 $extended_header_offset += 1;
203
204 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
205 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
206
207 $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40);
208 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20);
209 $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10);
210
211 if ($thisfile_id3v2['exthead']['flags']['update']) {
212 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0
213 $extended_header_offset += 1;
214 }
215
216 if ($thisfile_id3v2['exthead']['flags']['crc']) {
217 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5
218 $extended_header_offset += 1;
219 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false);
220 $extended_header_offset += $ext_header_chunk_length;
221 }
222
223 if ($thisfile_id3v2['exthead']['flags']['restrictions']) {
224 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1
225 $extended_header_offset += 1;
226
227 // %ppqrrstt
228 $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1));
229 $extended_header_offset += 1;
230 $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions
231 $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions
232 $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions
233 $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions
234 $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions
235
236 $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']);
237 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']);
238 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']);
239 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']);
240 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']);
241 }
242
243 if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) {
244 $this->warning('ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')');
245 }
246 }
247
248 $framedataoffset += $extended_header_offset;
249 $framedata = substr($framedata, $extended_header_offset);
250 } // end extended header
251
252
253 while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
254 if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
255 // insufficient room left in ID3v2 header for actual data - must be padding
256 $thisfile_id3v2['padding']['start'] = $framedataoffset;
257 $thisfile_id3v2['padding']['length'] = strlen($framedata);
258 $thisfile_id3v2['padding']['valid'] = true;
259 for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
260 if ($framedata{$i} != "\x00") {
261 $thisfile_id3v2['padding']['valid'] = false;
262 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
263 $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
264 break;
265 }
266 }
267 break; // skip rest of ID3v2 header
268 }
269 if ($id3v2_majorversion == 2) {
270 // Frame ID $xx xx xx (three characters)
271 // Size $xx xx xx (24-bit integer)
272 // Flags $xx xx
273
274 $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header
275 $framedata = substr($framedata, 6); // and leave the rest in $framedata
276 $frame_name = substr($frame_header, 0, 3);
277 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0);
278 $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
279
280 } elseif ($id3v2_majorversion > 2) {
281
282 // Frame ID $xx xx xx xx (four characters)
283 // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
284 // Flags $xx xx
285
286 $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
287 $framedata = substr($framedata, 10); // and leave the rest in $framedata
288
289 $frame_name = substr($frame_header, 0, 4);
290 if ($id3v2_majorversion == 3) {
291 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
292 } else { // ID3v2.4+
293 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value)
294 }
295
296 if ($frame_size < (strlen($framedata) + 4)) {
297 $nextFrameID = substr($framedata, $frame_size, 4);
298 if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) {
299 // next frame is OK
300 } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
301 // MP3ext known broken frames - "ok" for the purposes of this test
302 } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
303 $this->warning('ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3');
304 $id3v2_majorversion = 3;
305 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
306 }
307 }
308
309
310 $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
311 }
312
313 if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
314 // padding encountered
315
316 $thisfile_id3v2['padding']['start'] = $framedataoffset;
317 $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata);
318 $thisfile_id3v2['padding']['valid'] = true;
319
320 $len = strlen($framedata);
321 for ($i = 0; $i < $len; $i++) {
322 if ($framedata{$i} != "\x00") {
323 $thisfile_id3v2['padding']['valid'] = false;
324 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
325 $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
326 break;
327 }
328 }
329 break; // skip rest of ID3v2 header
330 }
331
332 if ($iTunesBrokenFrameNameFixed = self::ID3v22iTunesBrokenFrameName($frame_name)) {
333 $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1", "v7.0.0.70" are known-guilty, probably others too)]. Translated frame name from "'.str_replace("\x00", ' ', $frame_name).'" to "'.$iTunesBrokenFrameNameFixed.'" for parsing.');
334 $frame_name = $iTunesBrokenFrameNameFixed;
335 }
336 if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
337
338 unset($parsedFrame);
339 $parsedFrame['frame_name'] = $frame_name;
340 $parsedFrame['frame_flags_raw'] = $frame_flags;
341 $parsedFrame['data'] = substr($framedata, 0, $frame_size);
342 $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size);
343 $parsedFrame['dataoffset'] = $framedataoffset;
344
345 $this->ParseID3v2Frame($parsedFrame);
346 $thisfile_id3v2[$frame_name][] = $parsedFrame;
347
348 $framedata = substr($framedata, $frame_size);
349
350 } else { // invalid frame length or FrameID
351
352 if ($frame_size <= strlen($framedata)) {
353
354 if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) {
355
356 // next frame is valid, just skip the current frame
357 $framedata = substr($framedata, $frame_size);
358 $this->warning('Next ID3v2 frame is valid, skipping current frame.');
359
360 } else {
361
362 // next frame is invalid too, abort processing
363 //unset($framedata);
364 $framedata = null;
365 $this->error('Next ID3v2 frame is also invalid, aborting processing.');
366
367 }
368
369 } elseif ($frame_size == strlen($framedata)) {
370
371 // this is the last frame, just skip
372 $this->warning('This was the last ID3v2 frame.');
373
374 } else {
375
376 // next frame is invalid too, abort processing
377 //unset($framedata);
378 $framedata = null;
379 $this->warning('Invalid ID3v2 frame size, aborting.');
380
381 }
382 if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) {
383
384 switch ($frame_name) {
385 case "\x00\x00".'MP':
386 case "\x00".'MP3':
387 case ' MP3':
388 case 'MP3e':
389 case "\x00".'MP':
390 case ' MP':
391 case 'MP3':
392 $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]');
393 break;
394
395 default:
396 $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).');
397 break;
398 }
399
400 } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) {
401
402 $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).');
403
404 } else {
405
406 $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).');
407
408 }
409
410 }
411 $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion));
412
413 }
414
415 }
416
417
418 // Footer
419
420 // The footer is a copy of the header, but with a different identifier.
421 // ID3v2 identifier "3DI"
422 // ID3v2 version $04 00
423 // ID3v2 flags %abcd0000
424 // ID3v2 size 4 * %0xxxxxxx
425
426 if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
427 $footer = $this->fread(10);
428 if (substr($footer, 0, 3) == '3DI') {
429 $thisfile_id3v2['footer'] = true;
430 $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
431 $thisfile_id3v2['minorversion_footer'] = ord($footer{4});
432 }
433 if ($thisfile_id3v2['majorversion_footer'] <= 4) {
434 $id3_flags = ord(substr($footer{5}));
435 $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
436 $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
437 $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
438 $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10);
439
440 $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1);
441 }
442 } // end footer
443
444 if (isset($thisfile_id3v2['comments']['genre'])) {
445 $genres = array();
446 foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
447 foreach ($this->ParseID3v2GenreString($value) as $genre) {
448 $genres[] = $genre;
449 }
450 }
451 $thisfile_id3v2['comments']['genre'] = array_unique($genres);
452 unset($key, $value, $genres, $genre);
453 }
454
455 if (isset($thisfile_id3v2['comments']['track'])) {
456 foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
457 if (strstr($value, '/')) {
458 list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
459 }
460 }
461 }
462
463 if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) {
464 $thisfile_id3v2['comments']['year'] = array($matches[1]);
465 }
466
467
468 if (!empty($thisfile_id3v2['TXXX'])) {
469 // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames
470 foreach ($thisfile_id3v2['TXXX'] as $txxx_array) {
471 switch ($txxx_array['description']) {
472 case 'replaygain_track_gain':
473 if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) {
474 $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
475 }
476 break;
477 case 'replaygain_track_peak':
478 if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) {
479 $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']);
480 }
481 break;
482 case 'replaygain_album_gain':
483 if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) {
484 $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
485 }
486 break;
487 }
488 }
489 }
490
491
492 // Set avdataoffset
493 $info['avdataoffset'] = $thisfile_id3v2['headerlength'];
494 if (isset($thisfile_id3v2['footer'])) {
495 $info['avdataoffset'] += 10;
496 }
497
498 return true;
499 }
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1711
fread($bytes)
Definition: getid3.php:1683
warning($text)
Definition: getid3.php:1758
error($text)
Definition: getid3.php:1752
LookupExtendedHeaderRestrictionsImageSizeSize($index)
LookupExtendedHeaderRestrictionsTextFieldSize($index)
LookupExtendedHeaderRestrictionsTagSizeLimits($index)
static IsValidID3v2FrameName($framename, $id3v2majorversion)
LookupExtendedHeaderRestrictionsTextEncodings($index)
static ID3v2HeaderLength($majorversion)
LookupExtendedHeaderRestrictionsImageEncoding($index)
ParseID3v2Frame(&$parsedFrame)
ParseID3v2GenreString($genrestring)
static CastAsInt($floatnum)
Definition: getid3.lib.php:65
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: getid3.lib.php:263
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
$info
Definition: index.php:5

References $header, $i, $info, $key, $StartingOffset, getid3_lib\BigEndian2Int(), getid3_lib\CastAsInt(), DeUnsynchronise(), getid3_handler\error(), getid3_handler\fread(), getid3_handler\fseek(), ID3v2HeaderLength(), IsValidID3v2FrameName(), LookupExtendedHeaderRestrictionsImageEncoding(), LookupExtendedHeaderRestrictionsImageSizeSize(), LookupExtendedHeaderRestrictionsTagSizeLimits(), LookupExtendedHeaderRestrictionsTextEncodings(), LookupExtendedHeaderRestrictionsTextFieldSize(), ParseID3v2Frame(), ParseID3v2GenreString(), and getid3_handler\warning().

+ Here is the call graph for this function:

◆ APICPictureTypeLookup()

static getid3_id3v2::APICPictureTypeLookup (   $index,
  $returnarray = false 
)
static

Definition at line 3144 of file module.tag.id3v2.php.

3144 {
3145 static $APICPictureTypeLookup = array(
3146 0x00 => 'Other',
3147 0x01 => '32x32 pixels \'file icon\' (PNG only)',
3148 0x02 => 'Other file icon',
3149 0x03 => 'Cover (front)',
3150 0x04 => 'Cover (back)',
3151 0x05 => 'Leaflet page',
3152 0x06 => 'Media (e.g. label side of CD)',
3153 0x07 => 'Lead artist/lead performer/soloist',
3154 0x08 => 'Artist/performer',
3155 0x09 => 'Conductor',
3156 0x0A => 'Band/Orchestra',
3157 0x0B => 'Composer',
3158 0x0C => 'Lyricist/text writer',
3159 0x0D => 'Recording Location',
3160 0x0E => 'During recording',
3161 0x0F => 'During performance',
3162 0x10 => 'Movie/video screen capture',
3163 0x11 => 'A bright coloured fish',
3164 0x12 => 'Illustration',
3165 0x13 => 'Band/artist logotype',
3166 0x14 => 'Publisher/Studio logotype'
3167 );
3168 if ($returnarray) {
3169 return $APICPictureTypeLookup;
3170 }
3171 return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
3172 }
$index
Definition: metadata.php:60

References $index.

Referenced by ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ COMRReceivedAsLookup()

static getid3_id3v2::COMRReceivedAsLookup (   $index)
static

Definition at line 3174 of file module.tag.id3v2.php.

3174 {
3175 static $COMRReceivedAsLookup = array(
3176 0x00 => 'Other',
3177 0x01 => 'Standard CD album with other songs',
3178 0x02 => 'Compressed audio on CD',
3179 0x03 => 'File over the Internet',
3180 0x04 => 'Stream over the Internet',
3181 0x05 => 'As note sheets',
3182 0x06 => 'As note sheets in a book with other sheets',
3183 0x07 => 'Music on other media',
3184 0x08 => 'Non-musical merchandise'
3185 );
3186
3187 return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
3188 }

References $index.

Referenced by ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ DeUnsynchronise()

getid3_id3v2::DeUnsynchronise (   $data)

Definition at line 2185 of file module.tag.id3v2.php.

2185 {
2186 return str_replace("\xFF\x00", "\xFF", $data);
2187 }
$data
Definition: bench.php:6

References $data.

Referenced by Analyze(), and ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ ETCOEventLookup()

static getid3_id3v2::ETCOEventLookup (   $index)
static

Definition at line 3085 of file module.tag.id3v2.php.

3085 {
3086 if (($index >= 0x17) && ($index <= 0xDF)) {
3087 return 'reserved for future use';
3088 }
3089 if (($index >= 0xE0) && ($index <= 0xEF)) {
3090 return 'not predefined synch 0-F';
3091 }
3092 if (($index >= 0xF0) && ($index <= 0xFC)) {
3093 return 'reserved for future use';
3094 }
3095
3096 static $EventLookup = array(
3097 0x00 => 'padding (has no meaning)',
3098 0x01 => 'end of initial silence',
3099 0x02 => 'intro start',
3100 0x03 => 'main part start',
3101 0x04 => 'outro start',
3102 0x05 => 'outro end',
3103 0x06 => 'verse start',
3104 0x07 => 'refrain start',
3105 0x08 => 'interlude start',
3106 0x09 => 'theme start',
3107 0x0A => 'variation start',
3108 0x0B => 'key change',
3109 0x0C => 'time change',
3110 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
3111 0x0E => 'sustained noise',
3112 0x0F => 'sustained noise end',
3113 0x10 => 'intro end',
3114 0x11 => 'main part end',
3115 0x12 => 'verse end',
3116 0x13 => 'refrain end',
3117 0x14 => 'theme end',
3118 0x15 => 'profanity',
3119 0x16 => 'profanity end',
3120 0xFD => 'audio end (start of silence)',
3121 0xFE => 'audio file ends',
3122 0xFF => 'one more byte of events follows'
3123 );
3124
3125 return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
3126 }

References $index.

Referenced by ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ FrameNameLongLookup()

static getid3_id3v2::FrameNameLongLookup (   $framename)
static

This is not a comment!

AENC        Audio encryption
APIC        Attached picture
ASPI        Audio seek point index
BUF Recommended buffer size
CNT Play counter
COM Comments
COMM        Comments
COMR        Commercial frame
CRA Audio encryption
CRM Encrypted meta frame
ENCR        Encryption method registration
EQU Equalisation
EQU2        Equalisation (2)
EQUA        Equalisation
ETC Event timing codes
ETCO        Event timing codes
GEO General encapsulated object
GEOB        General encapsulated object
GRID        Group identification registration
IPL Involved people list
IPLS        Involved people list
LINK        Linked information
LNK Linked information
MCDI        Music CD identifier
MCI Music CD Identifier
MLL MPEG location lookup table
MLLT        MPEG location lookup table
OWNE        Ownership frame
PCNT        Play counter
PIC Attached picture
POP Popularimeter
POPM        Popularimeter
POSS        Position synchronisation frame
PRIV        Private frame
RBUF        Recommended buffer size
REV Reverb
RVA Relative volume adjustment
RVA2        Relative volume adjustment (2)
RVAD        Relative volume adjustment
RVRB        Reverb
SEEK        Seek frame
SIGN        Signature frame
SLT Synchronised lyric/text
STC Synced tempo codes
SYLT        Synchronised lyric/text
SYTC        Synchronised tempo codes
TAL Album/Movie/Show title
TALB        Album/Movie/Show title
TBP BPM (Beats Per Minute)
TBPM        BPM (beats per minute)
TCM Composer
TCMP        Part of a compilation
TCO Content type
TCOM        Composer
TCON        Content type
TCOP        Copyright message
TCP Part of a compilation
TCR Copyright message
TDA Date
TDAT        Date
TDEN        Encoding time
TDLY        Playlist delay
TDOR        Original release time
TDRC        Recording time
TDRL        Release time
TDTG        Tagging time
TDY Playlist delay
TEN Encoded by
TENC        Encoded by
TEXT        Lyricist/Text writer
TFLT        File type
TFT File type
TIM Time
TIME        Time
TIPL        Involved people list
TIT1        Content group description
TIT2        Title/songname/content description
TIT3        Subtitle/Description refinement
TKE Initial key
TKEY        Initial key
TLA Language(s)
TLAN        Language(s)
TLE Length
TLEN        Length
TMCL        Musician credits list
TMED        Media type
TMOO        Mood
TMT Media type
TOA Original artist(s)/performer(s)
TOAL        Original album/movie/show title
TOF Original filename
TOFN        Original filename
TOL Original Lyricist(s)/text writer(s)
TOLY        Original lyricist(s)/text writer(s)
TOPE        Original artist(s)/performer(s)
TOR Original release year
TORY        Original release year
TOT Original album/Movie/Show title
TOWN        File owner/licensee
TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
TP2 Band/Orchestra/Accompaniment
TP3 Conductor/Performer refinement
TP4 Interpreted, remixed, or otherwise modified by
TPA Part of a set
TPB Publisher
TPE1        Lead performer(s)/Soloist(s)
TPE2        Band/orchestra/accompaniment
TPE3        Conductor/performer refinement
TPE4        Interpreted, remixed, or otherwise modified by
TPOS        Part of a set
TPRO        Produced notice
TPUB        Publisher
TRC ISRC (International Standard Recording Code)
TRCK        Track number/Position in set
TRD Recording dates
TRDA        Recording dates
TRK Track number/Position in set
TRSN        Internet radio station name
TRSO        Internet radio station owner
TS2 Album-Artist sort order
TSA Album sort order
TSC Composer sort order
TSI Size
TSIZ        Size
TSO2        Album-Artist sort order
TSOA        Album sort order
TSOC        Composer sort order
TSOP        Performer sort order
TSOT        Title sort order
TSP Performer sort order
TSRC        ISRC (international standard recording code)
TSS Software/hardware and settings used for encoding
TSSE        Software/Hardware and settings used for encoding
TSST        Set subtitle
TST Title sort order
TT1 Content group description
TT2 Title/Songname/Content description
TT3 Subtitle/Description refinement
TXT Lyricist/text writer
TXX User defined text information frame
TXXX        User defined text information frame
TYE Year
TYER        Year
UFI Unique file identifier
UFID        Unique file identifier
ULT Unsychronised lyric/text transcription
USER        Terms of use
USLT        Unsynchronised lyric/text transcription
WAF Official audio file webpage
WAR Official artist/performer webpage
WAS Official audio source webpage
WCM Commercial information
WCOM        Commercial information
WCOP        Copyright/Legal information
WCP Copyright/Legal information
WOAF        Official audio file webpage
WOAR        Official artist/performer webpage
WOAS        Official audio source webpage
WORS        Official Internet radio station homepage
WPAY        Payment
WPB Publishers official webpage
WPUB        Publishers official webpage
WXX User defined URL link frame
WXXX        User defined URL link frame
TFEA        Featured Artist
TSTU        Recording Studio
rgad        Replay Gain Adjustment

Definition at line 3206 of file module.tag.id3v2.php.

3206 {
3207
3208 $begin = __LINE__;
3209
3382 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
3383
3384 // Last three:
3385 // from Helium2 [www.helium2.com]
3386 // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
3387 }
static EmbeddedLookup($key, $begin, $end, $file, $name)

References getid3_lib\EmbeddedLookup().

Referenced by ParseID3v2Frame().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ FrameNameShortLookup()

static getid3_id3v2::FrameNameShortLookup (   $framename)
static

This is not a comment!

AENC        audio_encryption
APIC        attached_picture
ASPI        audio_seek_point_index
BUF recommended_buffer_size
CNT play_counter
COM comment
COMM        comment
COMR        commercial_frame
CRA audio_encryption
CRM encrypted_meta_frame
ENCR        encryption_method_registration
EQU equalisation
EQU2        equalisation
EQUA        equalisation
ETC event_timing_codes
ETCO        event_timing_codes
GEO general_encapsulated_object
GEOB        general_encapsulated_object
GRID        group_identification_registration
IPL involved_people_list
IPLS        involved_people_list
LINK        linked_information
LNK linked_information
MCDI        music_cd_identifier
MCI music_cd_identifier
MLL mpeg_location_lookup_table
MLLT        mpeg_location_lookup_table
OWNE        ownership_frame
PCNT        play_counter
PIC attached_picture
POP popularimeter
POPM        popularimeter
POSS        position_synchronisation_frame
PRIV        private_frame
RBUF        recommended_buffer_size
REV reverb
RVA relative_volume_adjustment
RVA2        relative_volume_adjustment
RVAD        relative_volume_adjustment
RVRB        reverb
SEEK        seek_frame
SIGN        signature_frame
SLT synchronised_lyric
STC synced_tempo_codes
SYLT        synchronised_lyric
SYTC        synchronised_tempo_codes
TAL album
TALB        album
TBP bpm
TBPM        bpm
TCM composer
TCMP        part_of_a_compilation
TCO genre
TCOM        composer
TCON        genre
TCOP        copyright_message
TCP part_of_a_compilation
TCR copyright_message
TDA date
TDAT        date
TDEN        encoding_time
TDLY        playlist_delay
TDOR        original_release_time
TDRC        recording_time
TDRL        release_time
TDTG        tagging_time
TDY playlist_delay
TEN encoded_by
TENC        encoded_by
TEXT        lyricist
TFLT        file_type
TFT file_type
TIM time
TIME        time
TIPL        involved_people_list
TIT1        content_group_description
TIT2        title
TIT3        subtitle
TKE initial_key
TKEY        initial_key
TLA language
TLAN        language
TLE length
TLEN        length
TMCL        musician_credits_list
TMED        media_type
TMOO        mood
TMT media_type
TOA original_artist
TOAL        original_album
TOF original_filename
TOFN        original_filename
TOL original_lyricist
TOLY        original_lyricist
TOPE        original_artist
TOR original_year
TORY        original_year
TOT original_album
TOWN        file_owner
TP1 artist
TP2 band
TP3 conductor
TP4 remixer
TPA part_of_a_set
TPB publisher
TPE1        artist
TPE2        band
TPE3        conductor
TPE4        remixer
TPOS        part_of_a_set
TPRO        produced_notice
TPUB        publisher
TRC isrc
TRCK        track_number
TRD recording_dates
TRDA        recording_dates
TRK track_number
TRSN        internet_radio_station_name
TRSO        internet_radio_station_owner
TS2 album_artist_sort_order
TSA album_sort_order
TSC composer_sort_order
TSI size
TSIZ        size
TSO2        album_artist_sort_order
TSOA        album_sort_order
TSOC        composer_sort_order
TSOP        performer_sort_order
TSOT        title_sort_order
TSP performer_sort_order
TSRC        isrc
TSS encoder_settings
TSSE        encoder_settings
TSST        set_subtitle
TST title_sort_order
TT1 content_group_description
TT2 title
TT3 subtitle
TXT lyricist
TXX text
TXXX        text
TYE year
TYER        year
UFI unique_file_identifier
UFID        unique_file_identifier
ULT unsychronised_lyric
USER        terms_of_use
USLT        unsynchronised_lyric
WAF url_file
WAR url_artist
WAS url_source
WCM commercial_information
WCOM        commercial_information
WCOP        copyright
WCP copyright
WOAF        url_file
WOAR        url_artist
WOAS        url_source
WORS        url_station
WPAY        url_payment
WPB url_publisher
WPUB        url_publisher
WXX url_user
WXXX        url_user
TFEA        featured_artist
TSTU        recording_studio
rgad        replay_gain_adjustment

Definition at line 3390 of file module.tag.id3v2.php.

3390 {
3391
3392 $begin = __LINE__;
3393
3566 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
3567 }

References getid3_lib\EmbeddedLookup().

Referenced by ParseID3v2Frame().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ID3v22iTunesBrokenFrameName()

static getid3_id3v2::ID3v22iTunesBrokenFrameName (   $frame_name)
static

Definition at line 3656 of file module.tag.id3v2.php.

3656 {
3657 // iTunes (multiple versions) has been known to write ID3v2.3 style frames
3658 // but use ID3v2.2 frame names, right-padded using either [space] or [null]
3659 // to make them fit in the 4-byte frame name space of the ID3v2.3 frame.
3660 // This function will detect and translate the corrupt frame name into ID3v2.3 standard.
3661 static $ID3v22_iTunes_BrokenFrames = array(
3662 'BUF' => 'RBUF', // Recommended buffer size
3663 'CNT' => 'PCNT', // Play counter
3664 'COM' => 'COMM', // Comments
3665 'CRA' => 'AENC', // Audio encryption
3666 'EQU' => 'EQUA', // Equalisation
3667 'ETC' => 'ETCO', // Event timing codes
3668 'GEO' => 'GEOB', // General encapsulated object
3669 'IPL' => 'IPLS', // Involved people list
3670 'LNK' => 'LINK', // Linked information
3671 'MCI' => 'MCDI', // Music CD identifier
3672 'MLL' => 'MLLT', // MPEG location lookup table
3673 'PIC' => 'APIC', // Attached picture
3674 'POP' => 'POPM', // Popularimeter
3675 'REV' => 'RVRB', // Reverb
3676 'RVA' => 'RVAD', // Relative volume adjustment
3677 'SLT' => 'SYLT', // Synchronised lyric/text
3678 'STC' => 'SYTC', // Synchronised tempo codes
3679 'TAL' => 'TALB', // Album/Movie/Show title
3680 'TBP' => 'TBPM', // BPM (beats per minute)
3681 'TCM' => 'TCOM', // Composer
3682 'TCO' => 'TCON', // Content type
3683 'TCP' => 'TCMP', // Part of a compilation
3684 'TCR' => 'TCOP', // Copyright message
3685 'TDA' => 'TDAT', // Date
3686 'TDY' => 'TDLY', // Playlist delay
3687 'TEN' => 'TENC', // Encoded by
3688 'TFT' => 'TFLT', // File type
3689 'TIM' => 'TIME', // Time
3690 'TKE' => 'TKEY', // Initial key
3691 'TLA' => 'TLAN', // Language(s)
3692 'TLE' => 'TLEN', // Length
3693 'TMT' => 'TMED', // Media type
3694 'TOA' => 'TOPE', // Original artist(s)/performer(s)
3695 'TOF' => 'TOFN', // Original filename
3696 'TOL' => 'TOLY', // Original lyricist(s)/text writer(s)
3697 'TOR' => 'TORY', // Original release year
3698 'TOT' => 'TOAL', // Original album/movie/show title
3699 'TP1' => 'TPE1', // Lead performer(s)/Soloist(s)
3700 'TP2' => 'TPE2', // Band/orchestra/accompaniment
3701 'TP3' => 'TPE3', // Conductor/performer refinement
3702 'TP4' => 'TPE4', // Interpreted, remixed, or otherwise modified by
3703 'TPA' => 'TPOS', // Part of a set
3704 'TPB' => 'TPUB', // Publisher
3705 'TRC' => 'TSRC', // ISRC (international standard recording code)
3706 'TRD' => 'TRDA', // Recording dates
3707 'TRK' => 'TRCK', // Track number/Position in set
3708 'TS2' => 'TSO2', // Album-Artist sort order
3709 'TSA' => 'TSOA', // Album sort order
3710 'TSC' => 'TSOC', // Composer sort order
3711 'TSI' => 'TSIZ', // Size
3712 'TSP' => 'TSOP', // Performer sort order
3713 'TSS' => 'TSSE', // Software/Hardware and settings used for encoding
3714 'TST' => 'TSOT', // Title sort order
3715 'TT1' => 'TIT1', // Content group description
3716 'TT2' => 'TIT2', // Title/songname/content description
3717 'TT3' => 'TIT3', // Subtitle/Description refinement
3718 'TXT' => 'TEXT', // Lyricist/Text writer
3719 'TXX' => 'TXXX', // User defined text information frame
3720 'TYE' => 'TYER', // Year
3721 'UFI' => 'UFID', // Unique file identifier
3722 'ULT' => 'USLT', // Unsynchronised lyric/text transcription
3723 'WAF' => 'WOAF', // Official audio file webpage
3724 'WAR' => 'WOAR', // Official artist/performer webpage
3725 'WAS' => 'WOAS', // Official audio source webpage
3726 'WCM' => 'WCOM', // Commercial information
3727 'WCP' => 'WCOP', // Copyright/Legal information
3728 'WPB' => 'WPUB', // Publishers official webpage
3729 'WXX' => 'WXXX', // User defined URL link frame
3730 );
3731 if (strlen($frame_name) == 4) {
3732 if ((substr($frame_name, 3, 1) == ' ') || (substr($frame_name, 3, 1) == "\x00")) {
3733 if (isset($ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)])) {
3734 return $ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)];
3735 }
3736 }
3737 }
3738 return false;
3739 }

◆ ID3v2HeaderLength()

static getid3_id3v2::ID3v2HeaderLength (   $majorversion)
static

Definition at line 3652 of file module.tag.id3v2.php.

3652 {
3653 return (($majorversion == 2) ? 6 : 10);
3654 }

Referenced by Analyze(), and getid3_write_id3v2\GenerateID3v2Tag().

+ Here is the caller graph for this function:

◆ IsANumber()

static getid3_id3v2::IsANumber (   $numberstring,
  $allowdecimal = false,
  $allownegative = false 
)
static

Definition at line 3609 of file module.tag.id3v2.php.

3609 {
3610 for ($i = 0; $i < strlen($numberstring); $i++) {
3611 if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
3612 if (($numberstring{$i} == '.') && $allowdecimal) {
3613 // allowed
3614 } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
3615 // allowed
3616 } else {
3617 return false;
3618 }
3619 }
3620 }
3621 return true;
3622 }

References $i.

◆ IsValidDateStampString()

static getid3_id3v2::IsValidDateStampString (   $datestamp)
static

Definition at line 3624 of file module.tag.id3v2.php.

3624 {
3625 if (strlen($datestamp) != 8) {
3626 return false;
3627 }
3628 if (!self::IsANumber($datestamp, false)) {
3629 return false;
3630 }
3631 $year = substr($datestamp, 0, 4);
3632 $month = substr($datestamp, 4, 2);
3633 $day = substr($datestamp, 6, 2);
3634 if (($year == 0) || ($month == 0) || ($day == 0)) {
3635 return false;
3636 }
3637 if ($month > 12) {
3638 return false;
3639 }
3640 if ($day > 31) {
3641 return false;
3642 }
3643 if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
3644 return false;
3645 }
3646 if (($day > 29) && ($month == 2)) {
3647 return false;
3648 }
3649 return true;
3650 }

Referenced by ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ IsValidID3v2FrameName()

static getid3_id3v2::IsValidID3v2FrameName (   $framename,
  $id3v2majorversion 
)
static

Definition at line 3595 of file module.tag.id3v2.php.

3595 {
3596 switch ($id3v2majorversion) {
3597 case 2:
3598 return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
3599 break;
3600
3601 case 3:
3602 case 4:
3603 return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
3604 break;
3605 }
3606 return false;
3607 }

Referenced by Analyze(), getid3_write_id3v2\GenerateID3v2FrameData(), and getid3_write_id3v2\GenerateID3v2Tag().

+ Here is the caller graph for this function:

◆ LanguageLookup()

static getid3_id3v2::LanguageLookup (   $languagecode,
  $casesensitive = false 
)
static

This is not a comment!

XXX unknown
xxx unknown
aar Afar
abk Abkhazian
ace Achinese
ach Acoli
ada Adangme
afa Afro-Asiatic (Other)
afh Afrihili
afr Afrikaans
aka Akan
akk Akkadian
alb Albanian
ale Aleut
alg Algonquian Languages
amh Amharic
ang English, Old (ca. 450-1100)
apa Apache Languages
ara Arabic
arc Aramaic
arm Armenian
arn Araucanian
arp Arapaho
art Artificial (Other)
arw Arawak
asm Assamese
ath Athapascan Languages
ava Avaric
ave Avestan
awa Awadhi
aym Aymara
aze Azerbaijani
bad Banda
bai Bamileke Languages
bak Bashkir
bal Baluchi
bam Bambara
ban Balinese
baq Basque
bas Basa
bat Baltic (Other)
bej Beja
bel Byelorussian
bem Bemba
ben Bengali
ber Berber (Other)
bho Bhojpuri
bih Bihari
bik Bikol
bin Bini
bis Bislama
bla Siksika
bnt Bantu (Other)
bod Tibetan
bra Braj
bre Breton
bua Buriat
bug Buginese
bul Bulgarian
bur Burmese
cad Caddo
cai Central American Indian (Other)
car Carib
cat Catalan
cau Caucasian (Other)
ceb Cebuano
cel Celtic (Other)
ces Czech
cha Chamorro
chb Chibcha
che Chechen
chg Chagatai
chi Chinese
chm Mari
chn Chinook jargon
cho Choctaw
chr Cherokee
chu Church Slavic
chv Chuvash
chy Cheyenne
cop Coptic
cor Cornish
cos Corsican
cpe Creoles and Pidgins, English-based (Other)
cpf Creoles and Pidgins, French-based (Other)
cpp Creoles and Pidgins, Portuguese-based (Other)
cre Cree
crp Creoles and Pidgins (Other)
cus Cushitic (Other)
cym Welsh
cze Czech
dak Dakota
dan Danish
del Delaware
deu German
din Dinka
div Divehi
doi Dogri
dra Dravidian (Other)
dua Duala
dum Dutch, Middle (ca. 1050-1350)
dut Dutch
dyu Dyula
dzo Dzongkha
efi Efik
egy Egyptian (Ancient)
eka Ekajuk
ell Greek, Modern (1453-)
elx Elamite
eng English
enm English, Middle (ca. 1100-1500)
epo Esperanto
esk Eskimo (Other)
esl Spanish
est Estonian
eus Basque
ewe Ewe
ewo Ewondo
fan Fang
fao Faroese
fas Persian
fat Fanti
fij Fijian
fin Finnish
fiu Finno-Ugrian (Other)
fon Fon
fra French
fre French
frm French, Middle (ca. 1400-1600)
fro French, Old (842- ca. 1400)
fry Frisian
ful Fulah
gaa Ga
gae Gaelic (Scots)
gai Irish
gay Gayo
gdh Gaelic (Scots)
gem Germanic (Other)
geo Georgian
ger German
gez Geez
gil Gilbertese
glg Gallegan
gmh German, Middle High (ca. 1050-1500)
goh German, Old High (ca. 750-1050)
gon Gondi
got Gothic
grb Grebo
grc Greek, Ancient (to 1453)
gre Greek, Modern (1453-)
grn Guarani
guj Gujarati
hai Haida
hau Hausa
haw Hawaiian
heb Hebrew
her Herero
hil Hiligaynon
him Himachali
hin Hindi
hmo Hiri Motu
hun Hungarian
hup Hupa
hye Armenian
iba Iban
ibo Igbo
ice Icelandic
ijo Ijo
iku Inuktitut
ilo Iloko
ina Interlingua (International Auxiliary language Association)
inc Indic (Other)
ind Indonesian
ine Indo-European (Other)
ine Interlingue
ipk Inupiak
ira Iranian (Other)
iri Irish
iro Iroquoian uages
isl Icelandic
ita Italian
jav Javanese
jaw Javanese
jpn Japanese
jpr Judeo-Persian
jrb Judeo-Arabic
kaa Kara-Kalpak
kab Kabyle
kac Kachin
kal Greenlandic
kam Kamba
kan Kannada
kar Karen
kas Kashmiri
kat Georgian
kau Kanuri
kaw Kawi
kaz Kazakh
kha Khasi
khi Khoisan (Other)
khm Khmer
kho Khotanese
kik Kikuyu
kin Kinyarwanda
kir Kirghiz
kok Konkani
kom Komi
kon Kongo
kor Korean
kpe Kpelle
kro Kru
kru Kurukh
kua Kuanyama
kum Kumyk
kur Kurdish
kus Kusaie
kut Kutenai
lad Ladino
lah Lahnda
lam Lamba
lao Lao
lat Latin
lav Latvian
lez Lezghian
lin Lingala
lit Lithuanian
lol Mongo
loz Lozi
ltz Letzeburgesch
lub Luba-Katanga
lug Ganda
lui Luiseno
lun Lunda
luo Luo (Kenya and Tanzania)
mac Macedonian
mad Madurese
mag Magahi
mah Marshall
mai Maithili
mak Macedonian
mak Makasar
mal Malayalam
man Mandingo
mao Maori
map Austronesian (Other)
mar Marathi
mas Masai
max Manx
may Malay
men Mende
mga Irish, Middle (900 - 1200)
mic Micmac
min Minangkabau
mis Miscellaneous (Other)
mkh Mon-Kmer (Other)
mlg Malagasy
mlt Maltese
mni Manipuri
mno Manobo Languages
moh Mohawk
mol Moldavian
mon Mongolian
mos Mossi
mri Maori
msa Malay
mul Multiple Languages
mun Munda Languages
mus Creek
mwr Marwari
mya Burmese
myn Mayan Languages
nah Aztec
nai North American Indian (Other)
nau Nauru
nav Navajo
nbl Ndebele, South
nde Ndebele, North
ndo Ndongo
nep Nepali
new Newari
nic Niger-Kordofanian (Other)
niu Niuean
nla Dutch
nno Norwegian (Nynorsk)
non Norse, Old
nor Norwegian
nso Sotho, Northern
nub Nubian Languages
nya Nyanja
nym Nyamwezi
nyn Nyankole
nyo Nyoro
nzi Nzima
oci Langue d'Oc (post 1500)
oji Ojibwa
ori Oriya
orm Oromo
osa Osage
oss Ossetic
ota Turkish, Ottoman (1500 - 1928)
oto Otomian Languages
paa Papuan-Australian (Other)
pag Pangasinan
pal Pahlavi
pam Pampanga
pan Panjabi
pap Papiamento
pau Palauan
peo Persian, Old (ca 600 - 400 B.C.)
per Persian
phn Phoenician
pli Pali
pol Polish
pon Ponape
por Portuguese
pra Prakrit uages
pro Provencal, Old (to 1500)
pus Pushto
que Quechua
raj Rajasthani
rar Rarotongan
roa Romance (Other)
roh Rhaeto-Romance
rom Romany
ron Romanian
rum Romanian
run Rundi
rus Russian
sad Sandawe
sag Sango
sah Yakut
sai South American Indian (Other)
sal Salishan Languages
sam Samaritan Aramaic
san Sanskrit
sco Scots
scr Serbo-Croatian
sel Selkup
sem Semitic (Other)
sga Irish, Old (to 900)
shn Shan
sid Sidamo
sin Singhalese
sio Siouan Languages
sit Sino-Tibetan (Other)
sla Slavic (Other)
slk Slovak
slo Slovak
slv Slovenian
smi Sami Languages
smo Samoan
sna Shona
snd Sindhi
sog Sogdian
som Somali
son Songhai
sot Sotho, Southern
spa Spanish
sqi Albanian
srd Sardinian
srr Serer
ssa Nilo-Saharan (Other)
ssw Siswant
ssw Swazi
suk Sukuma
sun Sudanese
sus Susu
sux Sumerian
sve Swedish
swa Swahili
swe Swedish
syr Syriac
tah Tahitian
tam Tamil
tat Tatar
tel Telugu
tem Timne
ter Tereno
tgk Tajik
tgl Tagalog
tha Thai
tib Tibetan
tig Tigre
tir Tigrinya
tiv Tivi
tli Tlingit
tmh Tamashek
tog Tonga (Nyasa)
ton Tonga (Tonga Islands)
tru Truk
tsi Tsimshian
tsn Tswana
tso Tsonga
tuk Turkmen
tum Tumbuka
tur Turkish
tut Altaic (Other)
twi Twi
tyv Tuvinian
uga Ugaritic
uig Uighur
ukr Ukrainian
umb Umbundu
und Undetermined
urd Urdu
uzb Uzbek
vai Vai
ven Venda
vie Vietnamese
vol Volapük
vot Votic
wak Wakashan Languages
wal Walamo
war Waray
was Washo
wel Welsh
wen Sorbian Languages
wol Wolof
xho Xhosa
yao Yao
yap Yap
yid Yiddish
yor Yoruba
zap Zapotec
zen Zenaga
zha Zhuang
zho Chinese
zul Zulu
zun Zuni

Definition at line 2629 of file module.tag.id3v2.php.

2629 {
2630
2631 if (!$casesensitive) {
2632 $languagecode = strtolower($languagecode);
2633 }
2634
2635 // http://www.id3.org/id3v2.4.0-structure.txt
2636 // [4. ID3v2 frame overview]
2637 // The three byte language field, present in several frames, is used to
2638 // describe the language of the frame's content, according to ISO-639-2
2639 // [ISO-639-2]. The language should be represented in lower case. If the
2640 // language is not known the string "XXX" should be used.
2641
2642
2643 // ISO 639-2 - http://www.id3.org/iso639-2.html
2644
2645 $begin = __LINE__;
2646
3081 return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
3082 }

References getid3_lib\EmbeddedLookup().

Referenced by getid3_write_id3v2\GenerateID3v2FrameData(), getid3_write_id3v2\ID3v2IsValidPriceString(), ParseID3v2Frame(), and getid3_quicktime\QuicktimeLanguageLookup().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ LookupCurrencyCountry()

getid3_id3v2::LookupCurrencyCountry (   $currencyid)

This is not a comment!

AED United Arab Emirates
AFA Afghanistan
ALL Albania
AMD Armenia
ANG Netherlands Antilles
AOA Angola
ARS Argentina
ATS Austria
AUD Australia
AWG Aruba
AZM Azerbaijan
BAM Bosnia and Herzegovina
BBD Barbados
BDT Bangladesh
BEF Belgium
BGL Bulgaria
BHD Bahrain
BIF Burundi
BMD Bermuda
BND Brunei Darussalam
BOB Bolivia
BRL Brazil
BSD Bahamas
BTN Bhutan
BWP Botswana
BYR Belarus
BZD Belize
CAD Canada
CDF Congo/Kinshasa
CHF Switzerland
CLP Chile
CNY China
COP Colombia
CRC Costa Rica
CUP Cuba
CVE Cape Verde
CYP Cyprus
CZK Czech Republic
DEM Germany
DJF Djibouti
DKK Denmark
DOP Dominican Republic
DZD Algeria
EEK Estonia
EGP Egypt
ERN Eritrea
ESP Spain
ETB Ethiopia
EUR Euro Member Countries
FIM Finland
FJD Fiji
FKP Falkland Islands (Malvinas)
FRF France
GBP United Kingdom
GEL Georgia
GGP Guernsey
GHC Ghana
GIP Gibraltar
GMD Gambia
GNF Guinea
GRD Greece
GTQ Guatemala
GYD Guyana
HKD Hong Kong
HNL Honduras
HRK Croatia
HTG Haiti
HUF Hungary
IDR Indonesia
IEP Ireland (Eire)
ILS Israel
IMP Isle of Man
INR India
IQD Iraq
IRR Iran
ISK Iceland
ITL Italy
JEP Jersey
JMD Jamaica
JOD Jordan
JPY Japan
KES Kenya
KGS Kyrgyzstan
KHR Cambodia
KMF Comoros
KPW Korea
KWD Kuwait
KYD Cayman Islands
KZT Kazakstan
LAK Laos
LBP Lebanon
LKR Sri Lanka
LRD Liberia
LSL Lesotho
LTL Lithuania
LUF Luxembourg
LVL Latvia
LYD Libya
MAD Morocco
MDL Moldova
MGF Madagascar
MKD Macedonia
MMK Myanmar (Burma)
MNT Mongolia
MOP Macau
MRO Mauritania
MTL Malta
MUR Mauritius
MVR Maldives (Maldive Islands)
MWK Malawi
MXN Mexico
MYR Malaysia
MZM Mozambique
NAD Namibia
NGN Nigeria
NIO Nicaragua
NLG Netherlands (Holland)
NOK Norway
NPR Nepal
NZD New Zealand
OMR Oman
PAB Panama
PEN Peru
PGK Papua New Guinea
PHP Philippines
PKR Pakistan
PLN Poland
PTE Portugal
PYG Paraguay
QAR Qatar
ROL Romania
RUR Russia
RWF Rwanda
SAR Saudi Arabia
SBD Solomon Islands
SCR Seychelles
SDD Sudan
SEK Sweden
SGD Singapore
SHP Saint Helena
SIT Slovenia
SKK Slovakia
SLL Sierra Leone
SOS Somalia
SPL Seborga
SRG Suriname
STD São Tome and Principe
SVC El Salvador
SYP Syria
SZL Swaziland
THB Thailand
TJR Tajikistan
TMM Turkmenistan
TND Tunisia
TOP Tonga
TRL Turkey
TTD Trinidad and Tobago
TVD Tuvalu
TWD Taiwan
TZS Tanzania
UAH Ukraine
UGX Uganda
USD United States of America
UYU Uruguay
UZS Uzbekistan
VAL Vatican City
VEB Venezuela
VND Viet Nam
VUV Vanuatu
WST Samoa
XAF Communauté Financière Africaine
XAG Silver
XAU Gold
XCD East Caribbean
XDR International Monetary Fund
XPD Palladium
XPF Comptoirs Français du Pacifique
XPT Platinum
YER Yemen
YUM Yugoslavia
ZAR South Africa
ZMK Zambia
ZWD Zimbabwe

Definition at line 2432 of file module.tag.id3v2.php.

2432 {
2433
2434 $begin = __LINE__;
2435
2624 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
2625 }

References getid3_lib\EmbeddedLookup().

+ Here is the call graph for this function:

◆ LookupCurrencyUnits()

getid3_id3v2::LookupCurrencyUnits (   $currencyid)

This is not a comment!

AED Dirhams
AFA Afghanis
ALL Leke
AMD Drams
ANG Guilders
AOA Kwanza
ARS Pesos
ATS Schillings
AUD Dollars
AWG Guilders
AZM Manats
BAM Convertible Marka
BBD Dollars
BDT Taka
BEF Francs
BGL Leva
BHD Dinars
BIF Francs
BMD Dollars
BND Dollars
BOB Bolivianos
BRL Brazil Real
BSD Dollars
BTN Ngultrum
BWP Pulas
BYR Rubles
BZD Dollars
CAD Dollars
CDF Congolese Francs
CHF Francs
CLP Pesos
CNY Yuan Renminbi
COP Pesos
CRC Colones
CUP Pesos
CVE Escudos
CYP Pounds
CZK Koruny
DEM Deutsche Marks
DJF Francs
DKK Kroner
DOP Pesos
DZD Algeria Dinars
EEK Krooni
EGP Pounds
ERN Nakfa
ESP Pesetas
ETB Birr
EUR Euro
FIM Markkaa
FJD Dollars
FKP Pounds
FRF Francs
GBP Pounds
GEL Lari
GGP Pounds
GHC Cedis
GIP Pounds
GMD Dalasi
GNF Francs
GRD Drachmae
GTQ Quetzales
GYD Dollars
HKD Dollars
HNL Lempiras
HRK Kuna
HTG Gourdes
HUF Forints
IDR Rupiahs
IEP Pounds
ILS New Shekels
IMP Pounds
INR Rupees
IQD Dinars
IRR Rials
ISK Kronur
ITL Lire
JEP Pounds
JMD Dollars
JOD Dinars
JPY Yen
KES Shillings
KGS Soms
KHR Riels
KMF Francs
KPW Won
KWD Dinars
KYD Dollars
KZT Tenge
LAK Kips
LBP Pounds
LKR Rupees
LRD Dollars
LSL Maloti
LTL Litai
LUF Francs
LVL Lati
LYD Dinars
MAD Dirhams
MDL Lei
MGF Malagasy Francs
MKD Denars
MMK Kyats
MNT Tugriks
MOP Patacas
MRO Ouguiyas
MTL Liri
MUR Rupees
MVR Rufiyaa
MWK Kwachas
MXN Pesos
MYR Ringgits
MZM Meticais
NAD Dollars
NGN Nairas
NIO Gold Cordobas
NLG Guilders
NOK Krone
NPR Nepal Rupees
NZD Dollars
OMR Rials
PAB Balboa
PEN Nuevos Soles
PGK Kina
PHP Pesos
PKR Rupees
PLN Zlotych
PTE Escudos
PYG Guarani
QAR Rials
ROL Lei
RUR Rubles
RWF Rwanda Francs
SAR Riyals
SBD Dollars
SCR Rupees
SDD Dinars
SEK Kronor
SGD Dollars
SHP Pounds
SIT Tolars
SKK Koruny
SLL Leones
SOS Shillings
SPL Luigini
SRG Guilders
STD Dobras
SVC Colones
SYP Pounds
SZL Emalangeni
THB Baht
TJR Rubles
TMM Manats
TND Dinars
TOP Pa'anga
TRL Liras
TTD Dollars
TVD Tuvalu Dollars
TWD New Dollars
TZS Shillings
UAH Hryvnia
UGX Shillings
USD Dollars
UYU Pesos
UZS Sums
VAL Lire
VEB Bolivares
VND Dong
VUV Vatu
WST Tala
XAF Francs
XAG Ounces
XAU Ounces
XCD Dollars
XDR Special Drawing Rights
XPD Ounces
XPF Francs
XPT Ounces
YER Rials
YUM New Dinars
ZAR Rand
ZMK Kwacha
ZWD Zimbabwe Dollars

Definition at line 2235 of file module.tag.id3v2.php.

2235 {
2236
2237 $begin = __LINE__;
2238
2428 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
2429 }

References getid3_lib\EmbeddedLookup().

Referenced by ParseID3v2Frame().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsImageEncoding()

getid3_id3v2::LookupExtendedHeaderRestrictionsImageEncoding (   $index)

Definition at line 2217 of file module.tag.id3v2.php.

2217 {
2218 static $LookupExtendedHeaderRestrictionsImageEncoding = array(
2219 0x00 => 'No restrictions',
2220 0x01 => 'Images are encoded only with PNG or JPEG',
2221 );
2222 return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
2223 }

References $index.

Referenced by Analyze().

+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsImageSizeSize()

getid3_id3v2::LookupExtendedHeaderRestrictionsImageSizeSize (   $index)

Definition at line 2225 of file module.tag.id3v2.php.

2225 {
2226 static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
2227 0x00 => 'No restrictions',
2228 0x01 => 'All images are 256x256 pixels or smaller',
2229 0x02 => 'All images are 64x64 pixels or smaller',
2230 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise',
2231 );
2232 return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
2233 }

References $index.

Referenced by Analyze().

+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTagSizeLimits()

getid3_id3v2::LookupExtendedHeaderRestrictionsTagSizeLimits (   $index)

Definition at line 2189 of file module.tag.id3v2.php.

2189 {
2190 static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
2191 0x00 => 'No more than 128 frames and 1 MB total tag size',
2192 0x01 => 'No more than 64 frames and 128 KB total tag size',
2193 0x02 => 'No more than 32 frames and 40 KB total tag size',
2194 0x03 => 'No more than 32 frames and 4 KB total tag size',
2195 );
2196 return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
2197 }

References $index.

Referenced by Analyze().

+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTextEncodings()

getid3_id3v2::LookupExtendedHeaderRestrictionsTextEncodings (   $index)

Definition at line 2199 of file module.tag.id3v2.php.

2199 {
2200 static $LookupExtendedHeaderRestrictionsTextEncodings = array(
2201 0x00 => 'No restrictions',
2202 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
2203 );
2204 return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
2205 }

References $index.

Referenced by Analyze().

+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTextFieldSize()

getid3_id3v2::LookupExtendedHeaderRestrictionsTextFieldSize (   $index)

Definition at line 2207 of file module.tag.id3v2.php.

2207 {
2208 static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
2209 0x00 => 'No restrictions',
2210 0x01 => 'No string is longer than 1024 characters',
2211 0x02 => 'No string is longer than 128 characters',
2212 0x03 => 'No string is longer than 30 characters',
2213 );
2214 return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
2215 }

References $index.

Referenced by Analyze().

+ Here is the caller graph for this function:

◆ ParseID3v2Frame()

getid3_id3v2::ParseID3v2Frame ( $parsedFrame)

Definition at line 544 of file module.tag.id3v2.php.

544 {
545
546 // shortcuts
547 $info = &$this->getid3->info;
548 $id3v2_majorversion = $info['id3v2']['majorversion'];
549
550 $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
551 if (empty($parsedFrame['framenamelong'])) {
552 unset($parsedFrame['framenamelong']);
553 }
554 $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
555 if (empty($parsedFrame['framenameshort'])) {
556 unset($parsedFrame['framenameshort']);
557 }
558
559 if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard
560 if ($id3v2_majorversion == 3) {
561 // Frame Header Flags
562 // %abc00000 %ijk00000
563 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation
564 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation
565 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only
566 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression
567 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption
568 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity
569
570 } elseif ($id3v2_majorversion == 4) {
571 // Frame Header Flags
572 // %0abc0000 %0h00kmnp
573 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation
574 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation
575 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only
576 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity
577 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression
578 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption
579 $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation
580 $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator
581
582 // Frame-level de-unsynchronisation - ID3v2.4
583 if ($parsedFrame['flags']['Unsynchronisation']) {
584 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
585 }
586
587 if ($parsedFrame['flags']['DataLengthIndicator']) {
588 $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
589 $parsedFrame['data'] = substr($parsedFrame['data'], 4);
590 }
591 }
592
593 // Frame-level de-compression
594 if ($parsedFrame['flags']['compression']) {
595 $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
596 if (!function_exists('gzuncompress')) {
597 $this->warning('gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"');
598 } else {
599 if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
600 //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
601 $parsedFrame['data'] = $decompresseddata;
602 unset($decompresseddata);
603 } else {
604 $this->warning('gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"');
605 }
606 }
607 }
608 }
609
610 if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
611 if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
612 $this->warning('ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data');
613 }
614 }
615
616 if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) {
617
618 $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion';
619 switch ($parsedFrame['frame_name']) {
620 case 'WCOM':
621 $warning .= ' (this is known to happen with files tagged by RioPort)';
622 break;
623
624 default:
625 break;
626 }
627 $this->warning($warning);
628
629 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier
630 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier
631 // There may be more than one 'UFID' frame in a tag,
632 // but only one with the same 'Owner identifier'.
633 // <Header for 'Unique file identifier', ID: 'UFID'>
634 // Owner identifier <text string> $00
635 // Identifier <up to 64 bytes binary data>
636 $exploded = explode("\x00", $parsedFrame['data'], 2);
637 $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : '');
638 $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : '');
639
640 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame
641 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame
642 // There may be more than one 'TXXX' frame in each tag,
643 // but only one with the same description.
644 // <Header for 'User defined text information frame', ID: 'TXXX'>
645 // Text encoding $xx
646 // Description <text string according to encoding> $00 (00)
647 // Value <text string according to encoding>
648
649 $frame_offset = 0;
650 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
651 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
652 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
653 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
654 $frame_textencoding_terminator = "\x00";
655 }
656 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
657 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
658 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
659 }
660 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
661 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
662 // if description only contains a BOM or terminator then make it blank
663 $frame_description = '';
664 }
665 $parsedFrame['encodingid'] = $frame_textencoding;
666 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
667
668 $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
669 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
670 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
671 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
672 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
673 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
674 } else {
675 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
676 }
677 }
678 //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
679
680
681 } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
682 // There may only be one text information frame of its kind in an tag.
683 // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
684 // excluding 'TXXX' described in 4.2.6.>
685 // Text encoding $xx
686 // Information <text string(s) according to encoding>
687
688 $frame_offset = 0;
689 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
690 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
691 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
692 }
693
694 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
695
696 $parsedFrame['encodingid'] = $frame_textencoding;
697 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
698
699 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
700 // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
701 // This of course breaks when an artist name contains slash character, e.g. "AC/DC"
702 // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense
703 // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user
704 switch ($parsedFrame['encoding']) {
705 case 'UTF-16':
706 case 'UTF-16BE':
707 case 'UTF-16LE':
708 $wordsize = 2;
709 break;
710 case 'ISO-8859-1':
711 case 'UTF-8':
712 default:
713 $wordsize = 1;
714 break;
715 }
716 $Txxx_elements = array();
717 $Txxx_elements_start_offset = 0;
718 for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) {
719 if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) {
720 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
721 $Txxx_elements_start_offset = $i + $wordsize;
722 }
723 }
724 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
725 foreach ($Txxx_elements as $Txxx_element) {
726 $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element);
727 if (!empty($string)) {
728 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
729 }
730 }
731 unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset);
732 }
733
734 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
735 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame
736 // There may be more than one 'WXXX' frame in each tag,
737 // but only one with the same description
738 // <Header for 'User defined URL link frame', ID: 'WXXX'>
739 // Text encoding $xx
740 // Description <text string according to encoding> $00 (00)
741 // URL <text string>
742
743 $frame_offset = 0;
744 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
745 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
746 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
747 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
748 $frame_textencoding_terminator = "\x00";
749 }
750 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
751 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
752 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
753 }
754 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
755 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
756 // if description only contains a BOM or terminator then make it blank
757 $frame_description = '';
758 }
759 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
760
761 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
762 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
763 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
764 }
765 if ($frame_terminatorpos) {
766 // there are null bytes after the data - this is not according to spec
767 // only use data up to first null byte
768 $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
769 } else {
770 // no null bytes following data, just use all data
771 $frame_urldata = (string) $parsedFrame['data'];
772 }
773
774 $parsedFrame['encodingid'] = $frame_textencoding;
775 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
776
777 $parsedFrame['url'] = $frame_urldata;
778 $parsedFrame['description'] = $frame_description;
779 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
780 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
781 }
782 unset($parsedFrame['data']);
783
784
785 } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
786 // There may only be one URL link frame of its kind in a tag,
787 // except when stated otherwise in the frame description
788 // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
789 // described in 4.3.2.>
790 // URL <text string>
791
792 $parsedFrame['url'] = trim($parsedFrame['data']);
793 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
794 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
795 }
796 unset($parsedFrame['data']);
797
798
799 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only)
800 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only)
801 // http://id3.org/id3v2.3.0#sec4.4
802 // There may only be one 'IPL' frame in each tag
803 // <Header for 'User defined URL link frame', ID: 'IPL'>
804 // Text encoding $xx
805 // People list strings <textstrings>
806
807 $frame_offset = 0;
808 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
809 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
810 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
811 }
812 $parsedFrame['encodingid'] = $frame_textencoding;
813 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
814 $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset);
815
816 // http://www.getid3.org/phpBB3/viewtopic.php?t=1369
817 // "this tag typically contains null terminated strings, which are associated in pairs"
818 // "there are users that use the tag incorrectly"
819 $IPLS_parts = array();
820 if (strpos($parsedFrame['data_raw'], "\x00") !== false) {
821 $IPLS_parts_unsorted = array();
822 if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) {
823 // UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding
824 $thisILPS = '';
825 for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) {
826 $twobytes = substr($parsedFrame['data_raw'], $i, 2);
827 if ($twobytes === "\x00\x00") {
828 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
829 $thisILPS = '';
830 } else {
831 $thisILPS .= $twobytes;
832 }
833 }
834 if (strlen($thisILPS) > 2) { // 2-byte BOM
835 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
836 }
837 } else {
838 // ISO-8859-1 or UTF-8 or other single-byte-null character set
839 $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']);
840 }
841 if (count($IPLS_parts_unsorted) == 1) {
842 // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson"
843 foreach ($IPLS_parts_unsorted as $key => $value) {
844 $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value);
845 $position = '';
846 foreach ($IPLS_parts_sorted as $person) {
847 $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
848 }
849 }
850 } elseif ((count($IPLS_parts_unsorted) % 2) == 0) {
851 $position = '';
852 $person = '';
853 foreach ($IPLS_parts_unsorted as $key => $value) {
854 if (($key % 2) == 0) {
855 $position = $value;
856 } else {
857 $person = $value;
858 $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
859 $position = '';
860 $person = '';
861 }
862 }
863 } else {
864 foreach ($IPLS_parts_unsorted as $key => $value) {
865 $IPLS_parts[] = array($value);
866 }
867 }
868
869 } else {
870 $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']);
871 }
872 $parsedFrame['data'] = $IPLS_parts;
873
874 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
875 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
876 }
877
878
879 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier
880 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier
881 // There may only be one 'MCDI' frame in each tag
882 // <Header for 'Music CD identifier', ID: 'MCDI'>
883 // CD TOC <binary data>
884
885 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
886 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
887 }
888
889
890 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes
891 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes
892 // There may only be one 'ETCO' frame in each tag
893 // <Header for 'Event timing codes', ID: 'ETCO'>
894 // Time stamp format $xx
895 // Where time stamp format is:
896 // $01 (32-bit value) MPEG frames from beginning of file
897 // $02 (32-bit value) milliseconds from beginning of file
898 // Followed by a list of key events in the following format:
899 // Type of event $xx
900 // Time stamp $xx (xx ...)
901 // The 'Time stamp' is set to zero if directly at the beginning of the sound
902 // or after the previous event. All events MUST be sorted in chronological order.
903
904 $frame_offset = 0;
905 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
906
907 while ($frame_offset < strlen($parsedFrame['data'])) {
908 $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
909 $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
910 $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
911 $frame_offset += 4;
912 }
913 unset($parsedFrame['data']);
914
915
916 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table
917 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table
918 // There may only be one 'MLLT' frame in each tag
919 // <Header for 'Location lookup table', ID: 'MLLT'>
920 // MPEG frames between reference $xx xx
921 // Bytes between reference $xx xx xx
922 // Milliseconds between reference $xx xx xx
923 // Bits for bytes deviation $xx
924 // Bits for milliseconds dev. $xx
925 // Then for every reference the following data is included;
926 // Deviation in bytes %xxx....
927 // Deviation in milliseconds %xxx....
928
929 $frame_offset = 0;
930 $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
931 $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
932 $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
933 $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
934 $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
935 $parsedFrame['data'] = substr($parsedFrame['data'], 10);
936 while ($frame_offset < strlen($parsedFrame['data'])) {
937 $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
938 }
939 $reference_counter = 0;
940 while (strlen($deviationbitstream) > 0) {
941 $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
942 $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
943 $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
944 $reference_counter++;
945 }
946 unset($parsedFrame['data']);
947
948
949 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes
950 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes
951 // There may only be one 'SYTC' frame in each tag
952 // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
953 // Time stamp format $xx
954 // Tempo data <binary data>
955 // Where time stamp format is:
956 // $01 (32-bit value) MPEG frames from beginning of file
957 // $02 (32-bit value) milliseconds from beginning of file
958
959 $frame_offset = 0;
960 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
961 $timestamp_counter = 0;
962 while ($frame_offset < strlen($parsedFrame['data'])) {
963 $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
964 if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
965 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
966 }
967 $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
968 $frame_offset += 4;
969 $timestamp_counter++;
970 }
971 unset($parsedFrame['data']);
972
973
974 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription
975 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription
976 // There may be more than one 'Unsynchronised lyrics/text transcription' frame
977 // in each tag, but only one with the same language and content descriptor.
978 // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
979 // Text encoding $xx
980 // Language $xx xx xx
981 // Content descriptor <text string according to encoding> $00 (00)
982 // Lyrics/text <full text string according to encoding>
983
984 $frame_offset = 0;
985 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
986 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
987 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
988 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
989 $frame_textencoding_terminator = "\x00";
990 }
991 $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
992 $frame_offset += 3;
993 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
994 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
995 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
996 }
997 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
998 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
999 // if description only contains a BOM or terminator then make it blank
1000 $frame_description = '';
1001 }
1002 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
1003
1004 $parsedFrame['encodingid'] = $frame_textencoding;
1005 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1006
1007 $parsedFrame['language'] = $frame_language;
1008 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1009 $parsedFrame['description'] = $frame_description;
1010 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1011 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1012 }
1013 unset($parsedFrame['data']);
1014
1015
1016 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text
1017 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text
1018 // There may be more than one 'SYLT' frame in each tag,
1019 // but only one with the same language and content descriptor.
1020 // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
1021 // Text encoding $xx
1022 // Language $xx xx xx
1023 // Time stamp format $xx
1024 // $01 (32-bit value) MPEG frames from beginning of file
1025 // $02 (32-bit value) milliseconds from beginning of file
1026 // Content type $xx
1027 // Content descriptor <text string according to encoding> $00 (00)
1028 // Terminated text to be synced (typically a syllable)
1029 // Sync identifier (terminator to above string) $00 (00)
1030 // Time stamp $xx (xx ...)
1031
1032 $frame_offset = 0;
1033 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1034 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1035 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1036 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1037 $frame_textencoding_terminator = "\x00";
1038 }
1039 $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1040 $frame_offset += 3;
1041 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1042 $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1043 $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
1044 $parsedFrame['encodingid'] = $frame_textencoding;
1045 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1046
1047 $parsedFrame['language'] = $frame_language;
1048 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1049
1050 $timestampindex = 0;
1051 $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
1052 while (strlen($frame_remainingdata)) {
1053 $frame_offset = 0;
1054 $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator);
1055 if ($frame_terminatorpos === false) {
1056 $frame_remainingdata = '';
1057 } else {
1058 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1059 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1060 }
1061 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
1062
1063 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
1064 if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
1065 // timestamp probably omitted for first data item
1066 } else {
1067 $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
1068 $frame_remainingdata = substr($frame_remainingdata, 4);
1069 }
1070 $timestampindex++;
1071 }
1072 }
1073 unset($parsedFrame['data']);
1074
1075
1076 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments
1077 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments
1078 // There may be more than one comment frame in each tag,
1079 // but only one with the same language and content descriptor.
1080 // <Header for 'Comment', ID: 'COMM'>
1081 // Text encoding $xx
1082 // Language $xx xx xx
1083 // Short content descrip. <text string according to encoding> $00 (00)
1084 // The actual text <full text string according to encoding>
1085
1086 if (strlen($parsedFrame['data']) < 5) {
1087
1088 $this->warning('Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']);
1089
1090 } else {
1091
1092 $frame_offset = 0;
1093 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1094 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1095 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1096 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1097 $frame_textencoding_terminator = "\x00";
1098 }
1099 $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1100 $frame_offset += 3;
1101 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1102 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1103 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1104 }
1105 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1106 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1107 // if description only contains a BOM or terminator then make it blank
1108 $frame_description = '';
1109 }
1110 $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
1111
1112 $parsedFrame['encodingid'] = $frame_textencoding;
1113 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1114
1115 $parsedFrame['language'] = $frame_language;
1116 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1117 $parsedFrame['description'] = $frame_description;
1118 $parsedFrame['data'] = $frame_text;
1119 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1120 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
1121 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
1122 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1123 } else {
1124 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1125 }
1126 }
1127
1128 }
1129
1130 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
1131 // There may be more than one 'RVA2' frame in each tag,
1132 // but only one with the same identification string
1133 // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
1134 // Identification <text string> $00
1135 // The 'identification' string is used to identify the situation and/or
1136 // device where this adjustment should apply. The following is then
1137 // repeated for every channel:
1138 // Type of channel $xx
1139 // Volume adjustment $xx xx
1140 // Bits representing peak $xx
1141 // Peak volume $xx (xx ...)
1142
1143 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
1144 $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
1145 if (ord($frame_idstring) === 0) {
1146 $frame_idstring = '';
1147 }
1148 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
1149 $parsedFrame['description'] = $frame_idstring;
1150 $RVA2channelcounter = 0;
1151 while (strlen($frame_remainingdata) >= 5) {
1152 $frame_offset = 0;
1153 $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
1154 $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
1155 $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
1156 $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed
1157 $frame_offset += 2;
1158 $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
1159 if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) {
1160 $this->warning('ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value');
1161 break;
1162 }
1163 $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
1164 $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
1165 $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
1166 $RVA2channelcounter++;
1167 }
1168 unset($parsedFrame['data']);
1169
1170
1171 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
1172 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only)
1173 // There may only be one 'RVA' frame in each tag
1174 // <Header for 'Relative volume adjustment', ID: 'RVA'>
1175 // ID3v2.2 => Increment/decrement %000000ba
1176 // ID3v2.3 => Increment/decrement %00fedcba
1177 // Bits used for volume descr. $xx
1178 // Relative volume change, right $xx xx (xx ...) // a
1179 // Relative volume change, left $xx xx (xx ...) // b
1180 // Peak volume right $xx xx (xx ...)
1181 // Peak volume left $xx xx (xx ...)
1182 // ID3v2.3 only, optional (not present in ID3v2.2):
1183 // Relative volume change, right back $xx xx (xx ...) // c
1184 // Relative volume change, left back $xx xx (xx ...) // d
1185 // Peak volume right back $xx xx (xx ...)
1186 // Peak volume left back $xx xx (xx ...)
1187 // ID3v2.3 only, optional (not present in ID3v2.2):
1188 // Relative volume change, center $xx xx (xx ...) // e
1189 // Peak volume center $xx xx (xx ...)
1190 // ID3v2.3 only, optional (not present in ID3v2.2):
1191 // Relative volume change, bass $xx xx (xx ...) // f
1192 // Peak volume bass $xx xx (xx ...)
1193
1194 $frame_offset = 0;
1195 $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
1196 $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
1197 $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
1198 $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1199 $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
1200 $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1201 if ($parsedFrame['incdec']['right'] === false) {
1202 $parsedFrame['volumechange']['right'] *= -1;
1203 }
1204 $frame_offset += $frame_bytesvolume;
1205 $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1206 if ($parsedFrame['incdec']['left'] === false) {
1207 $parsedFrame['volumechange']['left'] *= -1;
1208 }
1209 $frame_offset += $frame_bytesvolume;
1210 $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1211 $frame_offset += $frame_bytesvolume;
1212 $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1213 $frame_offset += $frame_bytesvolume;
1214 if ($id3v2_majorversion == 3) {
1215 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1216 if (strlen($parsedFrame['data']) > 0) {
1217 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
1218 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
1219 $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1220 if ($parsedFrame['incdec']['rightrear'] === false) {
1221 $parsedFrame['volumechange']['rightrear'] *= -1;
1222 }
1223 $frame_offset += $frame_bytesvolume;
1224 $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1225 if ($parsedFrame['incdec']['leftrear'] === false) {
1226 $parsedFrame['volumechange']['leftrear'] *= -1;
1227 }
1228 $frame_offset += $frame_bytesvolume;
1229 $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1230 $frame_offset += $frame_bytesvolume;
1231 $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1232 $frame_offset += $frame_bytesvolume;
1233 }
1234 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1235 if (strlen($parsedFrame['data']) > 0) {
1236 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
1237 $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1238 if ($parsedFrame['incdec']['center'] === false) {
1239 $parsedFrame['volumechange']['center'] *= -1;
1240 }
1241 $frame_offset += $frame_bytesvolume;
1242 $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1243 $frame_offset += $frame_bytesvolume;
1244 }
1245 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1246 if (strlen($parsedFrame['data']) > 0) {
1247 $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
1248 $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1249 if ($parsedFrame['incdec']['bass'] === false) {
1250 $parsedFrame['volumechange']['bass'] *= -1;
1251 }
1252 $frame_offset += $frame_bytesvolume;
1253 $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1254 $frame_offset += $frame_bytesvolume;
1255 }
1256 }
1257 unset($parsedFrame['data']);
1258
1259
1260 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
1261 // There may be more than one 'EQU2' frame in each tag,
1262 // but only one with the same identification string
1263 // <Header of 'Equalisation (2)', ID: 'EQU2'>
1264 // Interpolation method $xx
1265 // $00 Band
1266 // $01 Linear
1267 // Identification <text string> $00
1268 // The following is then repeated for every adjustment point
1269 // Frequency $xx xx
1270 // Volume adjustment $xx xx
1271
1272 $frame_offset = 0;
1273 $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1274 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1275 $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1276 if (ord($frame_idstring) === 0) {
1277 $frame_idstring = '';
1278 }
1279 $parsedFrame['description'] = $frame_idstring;
1280 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
1281 while (strlen($frame_remainingdata)) {
1282 $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
1283 $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
1284 $frame_remainingdata = substr($frame_remainingdata, 4);
1285 }
1286 $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
1287 unset($parsedFrame['data']);
1288
1289
1290 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only)
1291 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only)
1292 // There may only be one 'EQUA' frame in each tag
1293 // <Header for 'Relative volume adjustment', ID: 'EQU'>
1294 // Adjustment bits $xx
1295 // This is followed by 2 bytes + ('adjustment bits' rounded up to the
1296 // nearest byte) for every equalisation band in the following format,
1297 // giving a frequency range of 0 - 32767Hz:
1298 // Increment/decrement %x (MSB of the Frequency)
1299 // Frequency (lower 15 bits)
1300 // Adjustment $xx (xx ...)
1301
1302 $frame_offset = 0;
1303 $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
1304 $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
1305
1306 $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
1307 while (strlen($frame_remainingdata) > 0) {
1308 $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
1309 $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
1310 $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
1311 $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
1312 $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
1313 if ($parsedFrame[$frame_frequency]['incdec'] === false) {
1314 $parsedFrame[$frame_frequency]['adjustment'] *= -1;
1315 }
1316 $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
1317 }
1318 unset($parsedFrame['data']);
1319
1320
1321 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb
1322 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb
1323 // There may only be one 'RVRB' frame in each tag.
1324 // <Header for 'Reverb', ID: 'RVRB'>
1325 // Reverb left (ms) $xx xx
1326 // Reverb right (ms) $xx xx
1327 // Reverb bounces, left $xx
1328 // Reverb bounces, right $xx
1329 // Reverb feedback, left to left $xx
1330 // Reverb feedback, left to right $xx
1331 // Reverb feedback, right to right $xx
1332 // Reverb feedback, right to left $xx
1333 // Premix left to right $xx
1334 // Premix right to left $xx
1335
1336 $frame_offset = 0;
1337 $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1338 $frame_offset += 2;
1339 $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1340 $frame_offset += 2;
1341 $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1342 $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1343 $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1344 $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1345 $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1346 $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1347 $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1348 $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1349 unset($parsedFrame['data']);
1350
1351
1352 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture
1353 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture
1354 // There may be several pictures attached to one file,
1355 // each in their individual 'APIC' frame, but only one
1356 // with the same content descriptor
1357 // <Header for 'Attached picture', ID: 'APIC'>
1358 // Text encoding $xx
1359 // ID3v2.3+ => MIME type <text string> $00
1360 // ID3v2.2 => Image format $xx xx xx
1361 // Picture type $xx
1362 // Description <text string according to encoding> $00 (00)
1363 // Picture data <binary data>
1364
1365 $frame_offset = 0;
1366 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1367 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1368 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1369 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1370 $frame_textencoding_terminator = "\x00";
1371 }
1372
1373 if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
1374 $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
1375 if (strtolower($frame_imagetype) == 'ima') {
1376 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
1377 // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net)
1378 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1379 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1380 if (ord($frame_mimetype) === 0) {
1381 $frame_mimetype = '';
1382 }
1383 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
1384 if ($frame_imagetype == 'JPEG') {
1385 $frame_imagetype = 'JPG';
1386 }
1387 $frame_offset = $frame_terminatorpos + strlen("\x00");
1388 } else {
1389 $frame_offset += 3;
1390 }
1391 }
1392 if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
1393 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1394 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1395 if (ord($frame_mimetype) === 0) {
1396 $frame_mimetype = '';
1397 }
1398 $frame_offset = $frame_terminatorpos + strlen("\x00");
1399 }
1400
1401 $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1402
1403 if ($frame_offset >= $parsedFrame['datalength']) {
1404 $this->warning('data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset));
1405 } else {
1406 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1407 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1408 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1409 }
1410 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1411 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1412 // if description only contains a BOM or terminator then make it blank
1413 $frame_description = '';
1414 }
1415 $parsedFrame['encodingid'] = $frame_textencoding;
1416 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1417
1418 if ($id3v2_majorversion == 2) {
1419 $parsedFrame['imagetype'] = $frame_imagetype;
1420 } else {
1421 $parsedFrame['mime'] = $frame_mimetype;
1422 }
1423 $parsedFrame['picturetypeid'] = $frame_picturetype;
1424 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
1425 $parsedFrame['description'] = $frame_description;
1426 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
1427 $parsedFrame['datalength'] = strlen($parsedFrame['data']);
1428
1429 $parsedFrame['image_mime'] = '';
1430 $imageinfo = array();
1431 if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
1432 if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
1433 $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
1434 if ($imagechunkcheck[0]) {
1435 $parsedFrame['image_width'] = $imagechunkcheck[0];
1436 }
1437 if ($imagechunkcheck[1]) {
1438 $parsedFrame['image_height'] = $imagechunkcheck[1];
1439 }
1440 }
1441 }
1442
1443 do {
1444 if ($this->getid3->option_save_attachments === false) {
1445 // skip entirely
1446 unset($parsedFrame['data']);
1447 break;
1448 }
1449 if ($this->getid3->option_save_attachments === true) {
1450 // great
1451/*
1452 } elseif (is_int($this->getid3->option_save_attachments)) {
1453 if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) {
1454 // too big, skip
1455 $this->warning('attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)');
1456 unset($parsedFrame['data']);
1457 break;
1458 }
1459*/
1460 } elseif (is_string($this->getid3->option_save_attachments)) {
1461 $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
1462 if (!is_dir($dir) || !is_writable($dir)) {
1463 // cannot write, skip
1464 $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)');
1465 unset($parsedFrame['data']);
1466 break;
1467 }
1468 }
1469 // if we get this far, must be OK
1470 if (is_string($this->getid3->option_save_attachments)) {
1471 $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset;
1472 if (!file_exists($destination_filename) || is_writable($destination_filename)) {
1473 file_put_contents($destination_filename, $parsedFrame['data']);
1474 } else {
1475 $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)');
1476 }
1477 $parsedFrame['data_filename'] = $destination_filename;
1478 unset($parsedFrame['data']);
1479 } else {
1480 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1481 if (!isset($info['id3v2']['comments']['picture'])) {
1482 $info['id3v2']['comments']['picture'] = array();
1483 }
1484 $comments_picture_data = array();
1485 foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
1486 if (isset($parsedFrame[$picture_key])) {
1487 $comments_picture_data[$picture_key] = $parsedFrame[$picture_key];
1488 }
1489 }
1490 $info['id3v2']['comments']['picture'][] = $comments_picture_data;
1491 unset($comments_picture_data);
1492 }
1493 }
1494 } while (false);
1495 }
1496
1497 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object
1498 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object
1499 // There may be more than one 'GEOB' frame in each tag,
1500 // but only one with the same content descriptor
1501 // <Header for 'General encapsulated object', ID: 'GEOB'>
1502 // Text encoding $xx
1503 // MIME type <text string> $00
1504 // Filename <text string according to encoding> $00 (00)
1505 // Content description <text string according to encoding> $00 (00)
1506 // Encapsulated object <binary data>
1507
1508 $frame_offset = 0;
1509 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1510 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1511 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1512 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1513 $frame_textencoding_terminator = "\x00";
1514 }
1515 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1516 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1517 if (ord($frame_mimetype) === 0) {
1518 $frame_mimetype = '';
1519 }
1520 $frame_offset = $frame_terminatorpos + strlen("\x00");
1521
1522 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1523 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1524 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1525 }
1526 $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1527 if (ord($frame_filename) === 0) {
1528 $frame_filename = '';
1529 }
1530 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1531
1532 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1533 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1534 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1535 }
1536 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1537 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1538 // if description only contains a BOM or terminator then make it blank
1539 $frame_description = '';
1540 }
1541 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1542
1543 $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
1544 $parsedFrame['encodingid'] = $frame_textencoding;
1545 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1546
1547 $parsedFrame['mime'] = $frame_mimetype;
1548 $parsedFrame['filename'] = $frame_filename;
1549 $parsedFrame['description'] = $frame_description;
1550 unset($parsedFrame['data']);
1551
1552
1553 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter
1554 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter
1555 // There may only be one 'PCNT' frame in each tag.
1556 // When the counter reaches all one's, one byte is inserted in
1557 // front of the counter thus making the counter eight bits bigger
1558 // <Header for 'Play counter', ID: 'PCNT'>
1559 // Counter $xx xx xx xx (xx ...)
1560
1561 $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
1562
1563
1564 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter
1565 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter
1566 // There may be more than one 'POPM' frame in each tag,
1567 // but only one with the same email address
1568 // <Header for 'Popularimeter', ID: 'POPM'>
1569 // Email to user <text string> $00
1570 // Rating $xx
1571 // Counter $xx xx xx xx (xx ...)
1572
1573 $frame_offset = 0;
1574 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1575 $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1576 if (ord($frame_emailaddress) === 0) {
1577 $frame_emailaddress = '';
1578 }
1579 $frame_offset = $frame_terminatorpos + strlen("\x00");
1580 $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1581 $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
1582 $parsedFrame['email'] = $frame_emailaddress;
1583 $parsedFrame['rating'] = $frame_rating;
1584 unset($parsedFrame['data']);
1585
1586
1587 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size
1588 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size
1589 // There may only be one 'RBUF' frame in each tag
1590 // <Header for 'Recommended buffer size', ID: 'RBUF'>
1591 // Buffer size $xx xx xx
1592 // Embedded info flag %0000000x
1593 // Offset to next tag $xx xx xx xx
1594
1595 $frame_offset = 0;
1596 $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
1597 $frame_offset += 3;
1598
1599 $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
1600 $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
1601 $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1602 unset($parsedFrame['data']);
1603
1604
1605 } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only)
1606 // There may be more than one 'CRM' frame in a tag,
1607 // but only one with the same 'owner identifier'
1608 // <Header for 'Encrypted meta frame', ID: 'CRM'>
1609 // Owner identifier <textstring> $00 (00)
1610 // Content/explanation <textstring> $00 (00)
1611 // Encrypted datablock <binary data>
1612
1613 $frame_offset = 0;
1614 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1615 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1616 $frame_offset = $frame_terminatorpos + strlen("\x00");
1617
1618 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1619 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1620 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1621 // if description only contains a BOM or terminator then make it blank
1622 $frame_description = '';
1623 }
1624 $frame_offset = $frame_terminatorpos + strlen("\x00");
1625
1626 $parsedFrame['ownerid'] = $frame_ownerid;
1627 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1628 $parsedFrame['description'] = $frame_description;
1629 unset($parsedFrame['data']);
1630
1631
1632 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption
1633 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption
1634 // There may be more than one 'AENC' frames in a tag,
1635 // but only one with the same 'Owner identifier'
1636 // <Header for 'Audio encryption', ID: 'AENC'>
1637 // Owner identifier <text string> $00
1638 // Preview start $xx xx
1639 // Preview length $xx xx
1640 // Encryption info <binary data>
1641
1642 $frame_offset = 0;
1643 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1644 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1645 if (ord($frame_ownerid) === 0) {
1646 $frame_ownerid = '';
1647 }
1648 $frame_offset = $frame_terminatorpos + strlen("\x00");
1649 $parsedFrame['ownerid'] = $frame_ownerid;
1650 $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1651 $frame_offset += 2;
1652 $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1653 $frame_offset += 2;
1654 $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
1655 unset($parsedFrame['data']);
1656
1657
1658 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
1659 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
1660 // There may be more than one 'LINK' frame in a tag,
1661 // but only one with the same contents
1662 // <Header for 'Linked information', ID: 'LINK'>
1663 // ID3v2.3+ => Frame identifier $xx xx xx xx
1664 // ID3v2.2 => Frame identifier $xx xx xx
1665 // URL <text string> $00
1666 // ID and additional data <text string(s)>
1667
1668 $frame_offset = 0;
1669 if ($id3v2_majorversion == 2) {
1670 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
1671 $frame_offset += 3;
1672 } else {
1673 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
1674 $frame_offset += 4;
1675 }
1676
1677 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1678 $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1679 if (ord($frame_url) === 0) {
1680 $frame_url = '';
1681 }
1682 $frame_offset = $frame_terminatorpos + strlen("\x00");
1683 $parsedFrame['url'] = $frame_url;
1684
1685 $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
1686 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
1687 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']);
1688 }
1689 unset($parsedFrame['data']);
1690
1691
1692 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
1693 // There may only be one 'POSS' frame in each tag
1694 // <Head for 'Position synchronisation', ID: 'POSS'>
1695 // Time stamp format $xx
1696 // Position $xx (xx ...)
1697
1698 $frame_offset = 0;
1699 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1700 $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
1701 unset($parsedFrame['data']);
1702
1703
1704 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only)
1705 // There may be more than one 'Terms of use' frame in a tag,
1706 // but only one with the same 'Language'
1707 // <Header for 'Terms of use frame', ID: 'USER'>
1708 // Text encoding $xx
1709 // Language $xx xx xx
1710 // The actual text <text string according to encoding>
1711
1712 $frame_offset = 0;
1713 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1714 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1715 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1716 }
1717 $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1718 $frame_offset += 3;
1719 $parsedFrame['language'] = $frame_language;
1720 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1721 $parsedFrame['encodingid'] = $frame_textencoding;
1722 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1723
1724 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1725 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1726 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1727 }
1728 unset($parsedFrame['data']);
1729
1730
1731 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only)
1732 // There may only be one 'OWNE' frame in a tag
1733 // <Header for 'Ownership frame', ID: 'OWNE'>
1734 // Text encoding $xx
1735 // Price paid <text string> $00
1736 // Date of purch. <text string>
1737 // Seller <text string according to encoding>
1738
1739 $frame_offset = 0;
1740 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1741 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1742 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1743 }
1744 $parsedFrame['encodingid'] = $frame_textencoding;
1745 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1746
1747 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1748 $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1749 $frame_offset = $frame_terminatorpos + strlen("\x00");
1750
1751 $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
1752 $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
1753 $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
1754
1755 $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
1756 if ($this->IsValidDateStampString($parsedFrame['purchasedate'])) {
1757 $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
1758 }
1759 $frame_offset += 8;
1760
1761 $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
1762 unset($parsedFrame['data']);
1763
1764
1765 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only)
1766 // There may be more than one 'commercial frame' in a tag,
1767 // but no two may be identical
1768 // <Header for 'Commercial frame', ID: 'COMR'>
1769 // Text encoding $xx
1770 // Price string <text string> $00
1771 // Valid until <text string>
1772 // Contact URL <text string> $00
1773 // Received as $xx
1774 // Name of seller <text string according to encoding> $00 (00)
1775 // Description <text string according to encoding> $00 (00)
1776 // Picture MIME type <string> $00
1777 // Seller logo <binary data>
1778
1779 $frame_offset = 0;
1780 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1781 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1782 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1783 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1784 $frame_textencoding_terminator = "\x00";
1785 }
1786
1787 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1788 $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1789 $frame_offset = $frame_terminatorpos + strlen("\x00");
1790 $frame_rawpricearray = explode('/', $frame_pricestring);
1791 foreach ($frame_rawpricearray as $key => $val) {
1792 $frame_currencyid = substr($val, 0, 3);
1793 $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
1794 $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
1795 }
1796
1797 $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
1798 $frame_offset += 8;
1799
1800 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1801 $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1802 $frame_offset = $frame_terminatorpos + strlen("\x00");
1803
1804 $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1805
1806 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1807 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1808 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1809 }
1810 $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1811 if (ord($frame_sellername) === 0) {
1812 $frame_sellername = '';
1813 }
1814 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1815
1816 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1817 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1818 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1819 }
1820 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1821 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1822 // if description only contains a BOM or terminator then make it blank
1823 $frame_description = '';
1824 }
1825 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1826
1827 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1828 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1829 $frame_offset = $frame_terminatorpos + strlen("\x00");
1830
1831 $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
1832
1833 $parsedFrame['encodingid'] = $frame_textencoding;
1834 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1835
1836 $parsedFrame['pricevaliduntil'] = $frame_datestring;
1837 $parsedFrame['contacturl'] = $frame_contacturl;
1838 $parsedFrame['receivedasid'] = $frame_receivedasid;
1839 $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
1840 $parsedFrame['sellername'] = $frame_sellername;
1841 $parsedFrame['description'] = $frame_description;
1842 $parsedFrame['mime'] = $frame_mimetype;
1843 $parsedFrame['logo'] = $frame_sellerlogo;
1844 unset($parsedFrame['data']);
1845
1846
1847 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only)
1848 // There may be several 'ENCR' frames in a tag,
1849 // but only one containing the same symbol
1850 // and only one containing the same owner identifier
1851 // <Header for 'Encryption method registration', ID: 'ENCR'>
1852 // Owner identifier <text string> $00
1853 // Method symbol $xx
1854 // Encryption data <binary data>
1855
1856 $frame_offset = 0;
1857 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1858 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1859 if (ord($frame_ownerid) === 0) {
1860 $frame_ownerid = '';
1861 }
1862 $frame_offset = $frame_terminatorpos + strlen("\x00");
1863
1864 $parsedFrame['ownerid'] = $frame_ownerid;
1865 $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1866 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1867
1868
1869 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only)
1870
1871 // There may be several 'GRID' frames in a tag,
1872 // but only one containing the same symbol
1873 // and only one containing the same owner identifier
1874 // <Header for 'Group ID registration', ID: 'GRID'>
1875 // Owner identifier <text string> $00
1876 // Group symbol $xx
1877 // Group dependent data <binary data>
1878
1879 $frame_offset = 0;
1880 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1881 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1882 if (ord($frame_ownerid) === 0) {
1883 $frame_ownerid = '';
1884 }
1885 $frame_offset = $frame_terminatorpos + strlen("\x00");
1886
1887 $parsedFrame['ownerid'] = $frame_ownerid;
1888 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1889 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1890
1891
1892 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only)
1893 // The tag may contain more than one 'PRIV' frame
1894 // but only with different contents
1895 // <Header for 'Private frame', ID: 'PRIV'>
1896 // Owner identifier <text string> $00
1897 // The private data <binary data>
1898
1899 $frame_offset = 0;
1900 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1901 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1902 if (ord($frame_ownerid) === 0) {
1903 $frame_ownerid = '';
1904 }
1905 $frame_offset = $frame_terminatorpos + strlen("\x00");
1906
1907 $parsedFrame['ownerid'] = $frame_ownerid;
1908 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1909
1910
1911 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only)
1912 // There may be more than one 'signature frame' in a tag,
1913 // but no two may be identical
1914 // <Header for 'Signature frame', ID: 'SIGN'>
1915 // Group symbol $xx
1916 // Signature <binary data>
1917
1918 $frame_offset = 0;
1919 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1920 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1921
1922
1923 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only)
1924 // There may only be one 'seek frame' in a tag
1925 // <Header for 'Seek frame', ID: 'SEEK'>
1926 // Minimum offset to next tag $xx xx xx xx
1927
1928 $frame_offset = 0;
1929 $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1930
1931
1932 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only)
1933 // There may only be one 'audio seek point index' frame in a tag
1934 // <Header for 'Seek Point Index', ID: 'ASPI'>
1935 // Indexed data start (S) $xx xx xx xx
1936 // Indexed data length (L) $xx xx xx xx
1937 // Number of index points (N) $xx xx
1938 // Bits per index point (b) $xx
1939 // Then for every index point the following data is included:
1940 // Fraction at index (Fi) $xx (xx)
1941
1942 $frame_offset = 0;
1943 $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1944 $frame_offset += 4;
1945 $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1946 $frame_offset += 4;
1947 $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1948 $frame_offset += 2;
1949 $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1950 $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
1951 for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) {
1952 $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
1953 $frame_offset += $frame_bytesperpoint;
1954 }
1955 unset($parsedFrame['data']);
1956
1957 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment
1958 // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
1959 // There may only be one 'RGAD' frame in a tag
1960 // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
1961 // Peak Amplitude $xx $xx $xx $xx
1962 // Radio Replay Gain Adjustment %aaabbbcd %dddddddd
1963 // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd
1964 // a - name code
1965 // b - originator code
1966 // c - sign bit
1967 // d - replay gain adjustment
1968
1969 $frame_offset = 0;
1970 $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
1971 $frame_offset += 4;
1972 $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
1973 $frame_offset += 2;
1974 $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
1975 $frame_offset += 2;
1976 $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
1977 $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
1978 $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
1979 $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
1980 $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
1981 $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
1982 $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
1983 $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
1984 $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
1985 $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
1986 $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
1987 $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
1988 $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
1989 $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
1990
1991 $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
1992 $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
1993 $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
1994 $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
1995 $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
1996
1997 unset($parsedFrame['data']);
1998
1999 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CHAP')) { // CHAP Chapters frame (ID3v2.3+ only)
2000 // http://id3.org/id3v2-chapters-1.0
2001 // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes)
2002 // Element ID <text string> $00
2003 // Start time $xx xx xx xx
2004 // End time $xx xx xx xx
2005 // Start offset $xx xx xx xx
2006 // End offset $xx xx xx xx
2007 // <Optional embedded sub-frames>
2008
2009 $frame_offset = 0;
2010 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
2011 $frame_offset += strlen($parsedFrame['element_id']."\x00");
2012 $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2013 $frame_offset += 4;
2014 $parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2015 $frame_offset += 4;
2016 if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
2017 // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
2018 $parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2019 }
2020 $frame_offset += 4;
2021 if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
2022 // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
2023 $parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2024 }
2025 $frame_offset += 4;
2026
2027 if ($frame_offset < strlen($parsedFrame['data'])) {
2028 $parsedFrame['subframes'] = array();
2029 while ($frame_offset < strlen($parsedFrame['data'])) {
2030 // <Optional embedded sub-frames>
2031 $subframe = array();
2032 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
2033 $frame_offset += 4;
2034 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2035 $frame_offset += 4;
2036 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
2037 $frame_offset += 2;
2038 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
2039 $this->warning('CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)');
2040 break;
2041 }
2042 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
2043 $frame_offset += $subframe['size'];
2044
2045 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
2046 $subframe['text'] = substr($subframe_rawdata, 1);
2047 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
2048 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
2049 switch (substr($encoding_converted_text, 0, 2)) {
2050 case "\xFF\xFE":
2051 case "\xFE\xFF":
2052 switch (strtoupper($info['id3v2']['encoding'])) {
2053 case 'ISO-8859-1':
2054 case 'UTF-8':
2055 $encoding_converted_text = substr($encoding_converted_text, 2);
2056 // remove unwanted byte-order-marks
2057 break;
2058 default:
2059 // ignore
2060 break;
2061 }
2062 break;
2063 default:
2064 // do not remove BOM
2065 break;
2066 }
2067
2068 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
2069 if ($subframe['name'] == 'TIT2') {
2070 $parsedFrame['chapter_name'] = $encoding_converted_text;
2071 } elseif ($subframe['name'] == 'TIT3') {
2072 $parsedFrame['chapter_description'] = $encoding_converted_text;
2073 }
2074 $parsedFrame['subframes'][] = $subframe;
2075 } else {
2076 $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
2077 }
2078 }
2079 unset($subframe_rawdata, $subframe, $encoding_converted_text);
2080 }
2081
2082 $id3v2_chapter_entry = array();
2083 foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
2084 if (isset($parsedFrame[$id3v2_chapter_key])) {
2085 $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
2086 }
2087 }
2088 if (!isset($info['id3v2']['chapters'])) {
2089 $info['id3v2']['chapters'] = array();
2090 }
2091 $info['id3v2']['chapters'][] = $id3v2_chapter_entry;
2092 unset($id3v2_chapter_entry, $id3v2_chapter_key);
2093
2094
2095 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
2096 // http://id3.org/id3v2-chapters-1.0
2097 // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes)
2098 // Element ID <text string> $00
2099 // CTOC flags %xx
2100 // Entry count $xx
2101 // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */
2102 // <Optional embedded sub-frames>
2103
2104 $frame_offset = 0;
2105 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
2106 $frame_offset += strlen($parsedFrame['element_id']."\x00");
2107 $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
2108 $frame_offset += 1;
2109 $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
2110 $frame_offset += 1;
2111
2112 $terminator_position = null;
2113 for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
2114 $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);
2115 $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
2116 $frame_offset = $terminator_position + 1;
2117 }
2118
2119 $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01);
2120 $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);
2121
2122 unset($ctoc_flags_raw, $terminator_position);
2123
2124 if ($frame_offset < strlen($parsedFrame['data'])) {
2125 $parsedFrame['subframes'] = array();
2126 while ($frame_offset < strlen($parsedFrame['data'])) {
2127 // <Optional embedded sub-frames>
2128 $subframe = array();
2129 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
2130 $frame_offset += 4;
2131 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2132 $frame_offset += 4;
2133 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
2134 $frame_offset += 2;
2135 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
2136 $this->warning('CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)');
2137 break;
2138 }
2139 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
2140 $frame_offset += $subframe['size'];
2141
2142 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
2143 $subframe['text'] = substr($subframe_rawdata, 1);
2144 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
2145 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
2146 switch (substr($encoding_converted_text, 0, 2)) {
2147 case "\xFF\xFE":
2148 case "\xFE\xFF":
2149 switch (strtoupper($info['id3v2']['encoding'])) {
2150 case 'ISO-8859-1':
2151 case 'UTF-8':
2152 $encoding_converted_text = substr($encoding_converted_text, 2);
2153 // remove unwanted byte-order-marks
2154 break;
2155 default:
2156 // ignore
2157 break;
2158 }
2159 break;
2160 default:
2161 // do not remove BOM
2162 break;
2163 }
2164
2165 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
2166 if ($subframe['name'] == 'TIT2') {
2167 $parsedFrame['toc_name'] = $encoding_converted_text;
2168 } elseif ($subframe['name'] == 'TIT3') {
2169 $parsedFrame['toc_description'] = $encoding_converted_text;
2170 }
2171 $parsedFrame['subframes'][] = $subframe;
2172 } else {
2173 $this->warning('ID3v2.CTOC subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
2174 }
2175 }
2176 unset($subframe_rawdata, $subframe, $encoding_converted_text);
2177 }
2178
2179 }
2180
2181 return true;
2182 }
$warning
Definition: X509warning.php:13
static COMRReceivedAsLookup($index)
static IsValidDateStampString($datestamp)
static TextEncodingNameLookup($encoding)
static FrameNameShortLookup($framename)
static APICPictureTypeLookup($index, $returnarray=false)
static TextEncodingTerminatorLookup($encoding)
static ETCOEventLookup($index)
static LanguageLookup($languagecode, $casesensitive=false)
LookupCurrencyUnits($currencyid)
static RVA2ChannelTypeLookup($index)
static FrameNameLongLookup($framename)
static SYTLContentTypeLookup($index)
static RGADadjustmentLookup($rawadjustment, $signbit)
static iconv_fallback($in_charset, $out_charset, $string)
Definition: getid3.lib.php:957
static BigEndian2Bin($byteword)
Definition: getid3.lib.php:297
static GetDataImageSize($imgData, &$imageinfo=array())
static iconv_fallback_iso88591_utf8($string, $bom=false)
Definition: getid3.lib.php:699
static RGADoriginatorLookup($originatorcode)
static Bin2Dec($binstring, $signed=false)
Definition: getid3.lib.php:342
static RGADnameLookup($namecode)
static BigEndian2Float($byteword)
Definition: getid3.lib.php:185
static Dec2Bin($number)
Definition: getid3.lib.php:328

References $i, $info, $key, $warning, APICPictureTypeLookup(), getid3_lib\BigEndian2Bin(), getid3_lib\BigEndian2Float(), getid3_lib\BigEndian2Int(), getid3_lib\Bin2Dec(), COMRReceivedAsLookup(), getid3_lib\Dec2Bin(), DeUnsynchronise(), ETCOEventLookup(), FrameNameLongLookup(), FrameNameShortLookup(), getid3_lib\GetDataImageSize(), getid3_lib\iconv_fallback(), getid3_lib\iconv_fallback_iso88591_utf8(), IsValidDateStampString(), LanguageLookup(), LookupCurrencyUnits(), getid3_lib\RGADadjustmentLookup(), getid3_lib\RGADnameLookup(), getid3_lib\RGADoriginatorLookup(), RVA2ChannelTypeLookup(), SYTLContentTypeLookup(), TextEncodingNameLookup(), TextEncodingTerminatorLookup(), and getid3_handler\warning().

Referenced by Analyze().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseID3v2GenreString()

getid3_id3v2::ParseID3v2GenreString (   $genrestring)

Definition at line 502 of file module.tag.id3v2.php.

502 {
503 // Parse genres into arrays of genreName and genreID
504 // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
505 // ID3v2.4.x: '21' $00 'Eurodisco' $00
506 $clean_genres = array();
507
508 // hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags
509 if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) {
510 // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
511 // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
512 if (preg_match('#/#', $genrestring)) {
513 $genrestring = str_replace('/', "\x00", $genrestring);
514 $genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring);
515 $genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring);
516 }
517
518 // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
519 if (preg_match('#;#', $genrestring)) {
520 $genrestring = str_replace(';', "\x00", $genrestring);
521 }
522 }
523
524
525 if (strpos($genrestring, "\x00") === false) {
526 $genrestring = preg_replace('#\‍(([0-9]{1,3})\‍)#', '$1'."\x00", $genrestring);
527 }
528
529 $genre_elements = explode("\x00", $genrestring);
530 foreach ($genre_elements as $element) {
531 $element = trim($element);
532 if ($element) {
533 if (preg_match('#^[0-9]{1,3}#', $element)) {
534 $clean_genres[] = getid3_id3v1::LookupGenreName($element);
535 } else {
536 $clean_genres[] = str_replace('((', '(', $element);
537 }
538 }
539 }
540 return $clean_genres;
541 }
static LookupGenreName($genreid, $allowSCMPXextended=true)

References getid3_id3v1\LookupGenreName().

Referenced by Analyze().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ RVA2ChannelTypeLookup()

static getid3_id3v2::RVA2ChannelTypeLookup (   $index)
static

Definition at line 3190 of file module.tag.id3v2.php.

3190 {
3191 static $RVA2ChannelTypeLookup = array(
3192 0x00 => 'Other',
3193 0x01 => 'Master volume',
3194 0x02 => 'Front right',
3195 0x03 => 'Front left',
3196 0x04 => 'Back right',
3197 0x05 => 'Back left',
3198 0x06 => 'Front centre',
3199 0x07 => 'Back centre',
3200 0x08 => 'Subwoofer'
3201 );
3202
3203 return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
3204 }

References $index.

Referenced by ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ SYTLContentTypeLookup()

static getid3_id3v2::SYTLContentTypeLookup (   $index)
static

Definition at line 3128 of file module.tag.id3v2.php.

3128 {
3129 static $SYTLContentTypeLookup = array(
3130 0x00 => 'other',
3131 0x01 => 'lyrics',
3132 0x02 => 'text transcription',
3133 0x03 => 'movement/part name', // (e.g. 'Adagio')
3134 0x04 => 'events', // (e.g. 'Don Quijote enters the stage')
3135 0x05 => 'chord', // (e.g. 'Bb F Fsus')
3136 0x06 => 'trivia/\'pop up\' information',
3137 0x07 => 'URLs to webpages',
3138 0x08 => 'URLs to images'
3139 );
3140
3141 return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
3142 }

References $index.

Referenced by ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ TextEncodingNameLookup()

static getid3_id3v2::TextEncodingNameLookup (   $encoding)
static

Definition at line 3582 of file module.tag.id3v2.php.

3582 {
3583 // http://www.id3.org/id3v2.4.0-structure.txt
3584 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3585 static $TextEncodingNameLookup = array(
3586 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00.
3587 1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
3588 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3589 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00.
3590 255 => 'UTF-16BE'
3591 );
3592 return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
3593 }

Referenced by ParseID3v2Frame().

+ Here is the caller graph for this function:

◆ TextEncodingTerminatorLookup()

static getid3_id3v2::TextEncodingTerminatorLookup (   $encoding)
static

Definition at line 3569 of file module.tag.id3v2.php.

3569 {
3570 // http://www.id3.org/id3v2.4.0-structure.txt
3571 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3572 static $TextEncodingTerminatorLookup = array(
3573 0 => "\x00", // $00 ISO-8859-1. Terminated with $00.
3574 1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
3575 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3576 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
3577 255 => "\x00\x00"
3578 );
3579 return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00");
3580 }

Referenced by getid3_write_id3v2\GenerateID3v2FrameData(), and ParseID3v2Frame().

+ Here is the caller graph for this function:

Field Documentation

◆ $StartingOffset

getid3_id3v2::$StartingOffset = 0

Definition at line 21 of file module.tag.id3v2.php.

Referenced by Analyze().


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