function ParseID3v2Frame(&$parsedFrame, &$ThisFileInfo) { // shortcuts $id3v2_majorversion = $ThisFileInfo['id3v2']['majorversion']; $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); if (empty($parsedFrame['framenamelong'])) { unset($parsedFrame['framenamelong']); } $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); if (empty($parsedFrame['framenameshort'])) { unset($parsedFrame['framenameshort']); } if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard if ($id3v2_majorversion == 3) { // Frame Header Flags // %abc00000 %ijk00000 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x80); // i - Compression $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40); // j - Encryption $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x20); // k - Grouping identity } elseif ($id3v2_majorversion == 4) { // Frame Header Flags // %0abc0000 %0h00kmnp $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40); // h - Grouping identity $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8); // k - Compression $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4); // m - Encryption $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2); // n - Unsynchronisation $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1); // p - Data length indicator // Frame-level de-unsynchronisation - ID3v2.4 if ($parsedFrame['flags']['Unsynchronisation']) { $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); } } // Frame-level de-compression if ($parsedFrame['flags']['compression']) { $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); if (!function_exists('gzuncompress')) { $ThisFileInfo['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "' . $parsedFrame['frame_name'] . '"'; } elseif ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { $parsedFrame['data'] = $decompresseddata; } else { $ThisFileInfo['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "' . $parsedFrame['frame_name'] . '"'; } } } if (isset($parsedFrame['datalength']) && $parsedFrame['datalength'] == 0) { $warning = 'Frame "' . $parsedFrame['frame_name'] . '" at offset ' . $parsedFrame['dataoffset'] . ' has no data portion'; switch ($parsedFrame['frame_name']) { case 'WCOM': $warning .= ' (this is known to happen with files tagged by RioPort)'; break; default: break; } $ThisFileInfo['warning'][] = $warning; } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'UFID' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'UFI') { // 4.1 UFI Unique file identifier // There may be more than one 'UFID' frame in a tag, // but only one with the same 'Owner identifier'. // <Header for 'Unique file identifier', ID: 'UFID'> // Owner identifier <text string> $00 // Identifier <up to 64 bytes binary data> $frame_terminatorpos = strpos($parsedFrame['data'], ""); $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); $parsedFrame['ownerid'] = $frame_idstring; $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen("")); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'TXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'TXX') { // 4.2.2 TXX User defined text information frame // There may be more than one 'TXXX' frame in each tag, // but only one with the same description. // <Header for 'User defined text information frame', ID: 'TXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // Value <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['description'] = $frame_description; $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data'])); } unset($parsedFrame['data']); } elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame // There may only be one text information frame of its kind in an tag. // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', // excluding 'TXXX' described in 4.2.6.> // Text encoding $xx // Information <text string(s) according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { // remove possible terminating \x00 (put by encoding id or software bug) $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); if ($string[strlen($string) - 1] == "") { $string = substr($string, 0, strlen($string) - 1); } $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; unset($string); } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'WXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'WXX') { // 4.3.2 WXX User defined URL link frame // There may be more than one 'WXXX' frame in each tag, // but only one with the same description // <Header for 'User defined URL link frame', ID: 'WXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // URL <text string> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } if ($frame_terminatorpos) { // there are null bytes after the data - this is not according to spec // only use data up to first null byte $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); } else { // no null bytes following data, just use all data $frame_urldata = (string) $parsedFrame['data']; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['url'] = $frame_urldata; $parsedFrame['description'] = $frame_description; if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']); } unset($parsedFrame['data']); } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames // There may only be one URL link frame of its kind in a tag, // except when stated otherwise in the frame description // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' // described in 4.3.2.> // URL <text string> $parsedFrame['url'] = trim($parsedFrame['data']); if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'IPLS' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'IPL') { // 4.4 IPL Involved people list (ID3v2.2 only) // There may only be one 'IPL' frame in each tag // <Header for 'User defined URL link frame', ID: 'IPL'> // Text encoding $xx // People list strings <textstrings> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MCDI' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MCI') { // 4.5 MCI Music CD identifier // There may only be one 'MCDI' frame in each tag // <Header for 'Music CD identifier', ID: 'MCDI'> // CD TOC <binary data> if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ETCO' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ETC') { // 4.6 ETC Event timing codes // There may only be one 'ETCO' frame in each tag // <Header for 'Event timing codes', ID: 'ETCO'> // Time stamp format $xx // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Followed by a list of key events in the following format: // Type of event $xx // Time stamp $xx (xx ...) // The 'Time stamp' is set to zero if directly at the beginning of the sound // or after the previous event. All events MUST be sorted in chronological order. $frame_offset = 0; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); while ($frame_offset < strlen($parsedFrame['data'])) { $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MLLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MLL') { // 4.7 MLL MPEG location lookup table // There may only be one 'MLLT' frame in each tag // <Header for 'Location lookup table', ID: 'MLLT'> // MPEG frames between reference $xx xx // Bytes between reference $xx xx xx // Milliseconds between reference $xx xx xx // Bits for bytes deviation $xx // Bits for milliseconds dev. $xx // Then for every reference the following data is included; // Deviation in bytes %xxx.... // Deviation in milliseconds %xxx.... $frame_offset = 0; $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); $parsedFrame['data'] = substr($parsedFrame['data'], 10); while ($frame_offset < strlen($parsedFrame['data'])) { $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); } $reference_counter = 0; while (strlen($deviationbitstream) > 0) { $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); $reference_counter++; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYTC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'STC') { // 4.8 STC Synchronised tempo codes // There may only be one 'SYTC' frame in each tag // <Header for 'Synchronised tempo codes', ID: 'SYTC'> // Time stamp format $xx // Tempo data <binary data> // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file $frame_offset = 0; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $timestamp_counter = 0; while ($frame_offset < strlen($parsedFrame['data'])) { $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); } $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $timestamp_counter++; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ULT') { // 4.9 ULT Unsynchronised lyric/text transcription // There may be more than one 'Unsynchronised lyrics/text transcription' frame // in each tag, but only one with the same language and content descriptor. // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'> // Text encoding $xx // Language $xx xx xx // Content descriptor <text string according to encoding> $00 (00) // Lyrics/text <full text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['data'] = $parsedFrame['data']; $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['description'] = $frame_description; if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'SLT') { // 4.10 SLT Synchronised lyric/text // There may be more than one 'SYLT' frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Synchronised lyrics/text', ID: 'SYLT'> // Text encoding $xx // Language $xx xx xx // Time stamp format $xx // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Content type $xx // Content descriptor <text string according to encoding> $00 (00) // Terminated text to be synced (typically a syllable) // Sync identifier (terminator to above string) $00 (00) // Time stamp $xx (xx ...) $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $timestampindex = 0; $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); while (strlen($frame_remainingdata)) { $frame_offset = 0; $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); if ($frame_terminatorpos === false) { $frame_remainingdata = ''; } else { if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) { // timestamp probably omitted for first data item } else { $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); $frame_remainingdata = substr($frame_remainingdata, 4); } $timestampindex++; } } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'COM') { // 4.11 COM Comments // There may be more than one comment frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Comment', ID: 'COMM'> // Text encoding $xx // Language $xx xx xx // Short content descrip. <text string according to encoding> $00 (00) // The actual text <full text string according to encoding> if (strlen($parsedFrame['data']) < 5) { $ThisFileInfo['warning'][] = 'Invalid data (too short) for "' . $parsedFrame['frame_name'] . '" frame at offset ' . $parsedFrame['dataoffset']; } else { $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['description'] = $frame_description; $parsedFrame['data'] = $frame_text; if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); } } } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'RVA2') { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) // There may be more than one 'RVA2' frame in each tag, // but only one with the same identification string // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'> // Identification <text string> $00 // The 'identification' string is used to identify the situation and/or // device where this adjustment should apply. The following is then // repeated for every channel: // Type of channel $xx // Volume adjustment $xx xx // Bits representing peak $xx // Peak volume $xx (xx ...) $frame_terminatorpos = strpos($parsedFrame['data'], ""); $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("")); $parsedFrame['description'] = $frame_idstring; while (strlen($frame_remainingdata)) { $frame_offset = 0; $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); $parsedFrame[$frame_channeltypeid]['channeltypeid'] = $frame_channeltypeid; $parsedFrame[$frame_channeltypeid]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); $parsedFrame[$frame_channeltypeid]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed $frame_offset += 2; $parsedFrame[$frame_channeltypeid]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); $frame_bytespeakvolume = ceil($parsedFrame[$frame_channeltypeid]['bitspeakvolume'] / 8); $parsedFrame[$frame_channeltypeid]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'RVAD' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'RVA') { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) // There may only be one 'RVA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'RVA'> // ID3v2.2 => Increment/decrement %000000ba // ID3v2.3 => Increment/decrement %00fedcba // Bits used for volume descr. $xx // Relative volume change, right $xx xx (xx ...) // a // Relative volume change, left $xx xx (xx ...) // b // Peak volume right $xx xx (xx ...) // Peak volume left $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, right back $xx xx (xx ...) // c // Relative volume change, left back $xx xx (xx ...) // d // Peak volume right back $xx xx (xx ...) // Peak volume left back $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, center $xx xx (xx ...) // e // Peak volume center $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, bass $xx xx (xx ...) // f // Peak volume bass $xx xx (xx ...) $frame_offset = 0; $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['right'] === false) { $parsedFrame['volumechange']['right'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['left'] === false) { $parsedFrame['volumechange']['left'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; if ($id3v2_majorversion == 3) { $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); if (strlen($parsedFrame['data']) > 0) { $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['rightrear'] === false) { $parsedFrame['volumechange']['rightrear'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['leftrear'] === false) { $parsedFrame['volumechange']['leftrear'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); if (strlen($parsedFrame['data']) > 0) { $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['center'] === false) { $parsedFrame['volumechange']['center'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); if (strlen($parsedFrame['data']) > 0) { $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['bass'] === false) { $parsedFrame['volumechange']['bass'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'EQU2') { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) // There may be more than one 'EQU2' frame in each tag, // but only one with the same identification string // <Header of 'Equalisation (2)', ID: 'EQU2'> // Interpolation method $xx // $00 Band // $01 Linear // Identification <text string> $00 // The following is then repeated for every adjustment point // Frequency $xx xx // Volume adjustment $xx xx $frame_offset = 0; $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $parsedFrame['description'] = $frame_idstring; $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("")); while (strlen($frame_remainingdata)) { $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); $frame_remainingdata = substr($frame_remainingdata, 4); } $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'EQUA' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'EQU') { // 4.13 EQU Equalisation (ID3v2.2 only) // There may only be one 'EQUA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'EQU'> // Adjustment bits $xx // This is followed by 2 bytes + ('adjustment bits' rounded up to the // nearest byte) for every equalisation band in the following format, // giving a frequency range of 0 - 32767Hz: // Increment/decrement %x (MSB of the Frequency) // Frequency (lower 15 bits) // Adjustment $xx (xx ...) $frame_offset = 0; $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); while (strlen($frame_remainingdata) > 0) { $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); if ($parsedFrame[$frame_frequency]['incdec'] === false) { $parsedFrame[$frame_frequency]['adjustment'] *= -1; } $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RVRB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'REV') { // 4.14 REV Reverb // There may only be one 'RVRB' frame in each tag. // <Header for 'Reverb', ID: 'RVRB'> // Reverb left (ms) $xx xx // Reverb right (ms) $xx xx // Reverb bounces, left $xx // Reverb bounces, right $xx // Reverb feedback, left to left $xx // Reverb feedback, left to right $xx // Reverb feedback, right to right $xx // Reverb feedback, right to left $xx // Premix left to right $xx // Premix right to left $xx $frame_offset = 0; $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'APIC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'PIC') { // 4.15 PIC Attached picture // There may be several pictures attached to one file, // each in their individual 'APIC' frame, but only one // with the same content descriptor // <Header for 'Attached picture', ID: 'APIC'> // Text encoding $xx // ID3v2.3+ => MIME type <text string> $00 // ID3v2.2 => Image format $xx xx xx // Picture type $xx // Description <text string according to encoding> $00 (00) // Picture data <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); if (strtolower($frame_imagetype) == 'ima') { // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); if ($frame_imagetype == 'JPEG') { $frame_imagetype = 'JPG'; } $frame_offset = $frame_terminatorpos + strlen(""); } else { $frame_offset += 3; } } if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) { $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(""); } $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); if ($id3v2_majorversion == 2) { $parsedFrame['imagetype'] = $frame_imagetype; } else { $parsedFrame['mime'] = $frame_mimetype; } $parsedFrame['picturetypeid'] = $frame_picturetype; $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); $parsedFrame['description'] = $frame_description; $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data']); if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) { $parsedFrame['image_mime'] = 'image/' . getid3_lib::ImageTypesLookup($imagechunkcheck[2]); if ($imagechunkcheck[0]) { $parsedFrame['image_width'] = $imagechunkcheck[0]; } if ($imagechunkcheck[1]) { $parsedFrame['image_height'] = $imagechunkcheck[1]; } $parsedFrame['image_bytes'] = strlen($parsedFrame['data']); } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GEOB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'GEO') { // 4.16 GEO General encapsulated object // There may be more than one 'GEOB' frame in each tag, // but only one with the same content descriptor // <Header for 'General encapsulated object', ID: 'GEOB'> // Text encoding $xx // MIME type <text string> $00 // Filename <text string according to encoding> $00 (00) // Content description <text string according to encoding> $00 (00) // Encapsulated object <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_filename) === 0) { $frame_filename = ''; } $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['mime'] = $frame_mimetype; $parsedFrame['filename'] = $frame_filename; $parsedFrame['description'] = $frame_description; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PCNT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CNT') { // 4.17 CNT Play counter // There may only be one 'PCNT' frame in each tag. // When the counter reaches all one's, one byte is inserted in // front of the counter thus making the counter eight bits bigger // <Header for 'Play counter', ID: 'PCNT'> // Counter $xx xx xx xx (xx ...) $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POPM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'POP') { // 4.18 POP Popularimeter // There may be more than one 'POPM' frame in each tag, // but only one with the same email address // <Header for 'Popularimeter', ID: 'POPM'> // Email to user <text string> $00 // Rating $xx // Counter $xx xx xx xx (xx ...) $frame_offset = 0; $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_emailaddress) === 0) { $frame_emailaddress = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); $parsedFrame['email'] = $frame_emailaddress; $parsedFrame['rating'] = $frame_rating; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RBUF' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'BUF') { // 4.19 BUF Recommended buffer size // There may only be one 'RBUF' frame in each tag // <Header for 'Recommended buffer size', ID: 'RBUF'> // Buffer size $xx xx xx // Embedded info flag %0000000x // Offset to next tag $xx xx xx xx $frame_offset = 0; $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); $frame_offset += 3; $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRM') { // 4.20 Encrypted meta frame (ID3v2.2 only) // There may be more than one 'CRM' frame in a tag, // but only one with the same 'owner identifier' // <Header for 'Encrypted meta frame', ID: 'CRM'> // Owner identifier <textstring> $00 (00) // Content/explanation <textstring> $00 (00) // Encrypted datablock <binary data> $frame_offset = 0; $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['description'] = $frame_description; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'AENC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRA') { // 4.21 CRA Audio encryption // There may be more than one 'AENC' frames in a tag, // but only one with the same 'Owner identifier' // <Header for 'Audio encryption', ID: 'AENC'> // Owner identifier <text string> $00 // Preview start $xx xx // Preview length $xx xx // Encryption info <binary data> $frame_offset = 0; $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid == ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'LINK' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'LNK') { // 4.22 LNK Linked information // There may be more than one 'LINK' frame in a tag, // but only one with the same contents // <Header for 'Linked information', ID: 'LINK'> // ID3v2.3+ => Frame identifier $xx xx xx xx // ID3v2.2 => Frame identifier $xx xx xx // URL <text string> $00 // ID and additional data <text string(s)> $frame_offset = 0; if ($id3v2_majorversion == 2) { $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; } else { $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); $frame_offset += 4; } $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_url) === 0) { $frame_url = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['url'] = $frame_url; $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POSS') { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) // There may only be one 'POSS' frame in each tag // <Head for 'Position synchronisation', ID: 'POSS'> // Time stamp format $xx // Position $xx (xx ...) $frame_offset = 0; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USER') { // 4.22 USER Terms of use (ID3v2.3+ only) // There may be more than one 'Terms of use' frame in a tag, // but only one with the same 'Language' // <Header for 'Terms of use frame', ID: 'USER'> // Text encoding $xx // Language $xx xx xx // The actual text <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'OWNE') { // 4.23 OWNE Ownership frame (ID3v2.3+ only) // There may only be one 'OWNE' frame in a tag // <Header for 'Ownership frame', ID: 'OWNE'> // Text encoding $xx // Price paid <text string> $00 // Date of purch. <text string> // Seller <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) { $parsedFrame['purchasedateunix'] = mktime(0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); } $frame_offset += 8; $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMR') { // 4.24 COMR Commercial frame (ID3v2.3+ only) // There may be more than one 'commercial frame' in a tag, // but no two may be identical // <Header for 'Commercial frame', ID: 'COMR'> // Text encoding $xx // Price string <text string> $00 // Valid until <text string> // Contact URL <text string> $00 // Received as $xx // Name of seller <text string according to encoding> $00 (00) // Description <text string according to encoding> $00 (00) // Picture MIME type <string> $00 // Seller logo <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_rawpricearray = explode('/', $frame_pricestring); foreach ($frame_rawpricearray as $key => $val) { $frame_currencyid = substr($val, 0, 3); $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); } $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); $frame_offset += 8; $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_sellername) === 0) { $frame_sellername = ''; } $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['pricevaliduntil'] = $frame_datestring; $parsedFrame['contacturl'] = $frame_contacturl; $parsedFrame['receivedasid'] = $frame_receivedasid; $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); $parsedFrame['sellername'] = $frame_sellername; $parsedFrame['description'] = $frame_description; $parsedFrame['mime'] = $frame_mimetype; $parsedFrame['logo'] = $frame_sellerlogo; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ENCR') { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) // There may be several 'ENCR' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Encryption method registration', ID: 'ENCR'> // Owner identifier <text string> $00 // Method symbol $xx // Encryption data <binary data> $frame_offset = 0; $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GRID') { // 4.26 GRID Group identification registration (ID3v2.3+ only) // There may be several 'GRID' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Group ID registration', ID: 'GRID'> // Owner identifier <text string> $00 // Group symbol $xx // Group dependent data <binary data> $frame_offset = 0; $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PRIV') { // 4.27 PRIV Private frame (ID3v2.3+ only) // The tag may contain more than one 'PRIV' frame // but only with different contents // <Header for 'Private frame', ID: 'PRIV'> // Owner identifier <text string> $00 // The private data <binary data> $frame_offset = 0; $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SIGN') { // 4.28 SIGN Signature frame (ID3v2.4+ only) // There may be more than one 'signature frame' in a tag, // but no two may be identical // <Header for 'Signature frame', ID: 'SIGN'> // Group symbol $xx // Signature <binary data> $frame_offset = 0; $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SEEK') { // 4.29 SEEK Seek frame (ID3v2.4+ only) // There may only be one 'seek frame' in a tag // <Header for 'Seek frame', ID: 'SEEK'> // Minimum offset to next tag $xx xx xx xx $frame_offset = 0; $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'ASPI') { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) // There may only be one 'audio seek point index' frame in a tag // <Header for 'Seek Point Index', ID: 'ASPI'> // Indexed data start (S) $xx xx xx xx // Indexed data length (L) $xx xx xx xx // Number of index points (N) $xx xx // Bits per index point (b) $xx // Then for every index point the following data is included: // Fraction at index (Fi) $xx (xx) $frame_offset = 0; $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); for ($i = 0; $i < $frame_indexpoints; $i++) { $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); $frame_offset += $frame_bytesperpoint; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RGAD') { // Replay Gain Adjustment // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html // There may only be one 'RGAD' frame in a tag // <Header for 'Replay Gain Adjustment', ID: 'RGAD'> // Peak Amplitude $xx $xx $xx $xx // Radio Replay Gain Adjustment %aaabbbcd %dddddddd // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd // a - name code // b - originator code // c - sign bit // d - replay gain adjustment $frame_offset = 0; $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3)); $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3)); $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1)); $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9)); $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3)); $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3)); $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1)); $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9)); $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); $ThisFileInfo['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; $ThisFileInfo['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; $ThisFileInfo['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; $ThisFileInfo['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; $ThisFileInfo['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; unset($parsedFrame['data']); } return true; }
function getid3_dts(&$fd, &$ThisFileInfo) { // Specs taken from "DTS Coherent Acoustics;Core and Extensions, ETSI TS 102 114 V1.2.1 (2002-12)" // (http://pda.etsi.org/pda/queryform.asp) // With thanks to Gambit <*****@*****.**> http://mac.sourceforge.net/atl/ $ThisFileInfo['fileformat'] = 'dts'; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $DTSheader = fread($fd, 16); $ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader, 0, 4)); if ($ThisFileInfo['dts']['raw']['magic'] != 0x7ffe8001) { $ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "0x' . str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT) . '"'; unset($ThisFileInfo['fileformat']); unset($ThisFileInfo['dts']); return false; } $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12)); $bsOffset = 0; $ThisFileInfo['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5; $ThisFileInfo['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7)); $bsOffset += 7; $ThisFileInfo['dts']['raw']['frame_byte_size'] = bindec(substr($fhBS, $bsOffset, 14)); $bsOffset += 14; $ThisFileInfo['dts']['raw']['channel_arrangement'] = bindec(substr($fhBS, $bsOffset, 6)); $bsOffset += 6; $ThisFileInfo['dts']['raw']['sample_frequency'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; $ThisFileInfo['dts']['raw']['bitrate'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5; $ThisFileInfo['dts']['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['raw']['extension_audio'] = bindec(substr($fhBS, $bsOffset, 3)); $bsOffset += 3; $ThisFileInfo['dts']['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['raw']['lfe_effects'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; $ThisFileInfo['dts']['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; if ($ThisFileInfo['dts']['flags']['crc_present']) { $ThisFileInfo['dts']['raw']['crc16'] = bindec(substr($fhBS, $bsOffset, 16)); $bsOffset += 16; } $ThisFileInfo['dts']['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; $ThisFileInfo['dts']['raw']['copy_history'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; $ThisFileInfo['dts']['raw']['bits_per_sample'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; $ThisFileInfo['dts']['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; $ThisFileInfo['dts']['raw']['dialog_normalization'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; $ThisFileInfo['dts']['bitrate'] = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']); $ThisFileInfo['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']); $ThisFileInfo['dts']['sample_rate'] = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']); $ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']); $ThisFileInfo['dts']['flags']['lossless'] = $ThisFileInfo['dts']['raw']['bitrate'] == 31 ? true : false; $ThisFileInfo['dts']['bitrate_mode'] = $ThisFileInfo['dts']['raw']['bitrate'] == 30 ? 'vbr' : 'cbr'; $ThisFileInfo['dts']['channels'] = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']); $ThisFileInfo['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']); $ThisFileInfo['audio']['dataformat'] = 'dts'; $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['dts']['flags']['lossless']; $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['dts']['bitrate_mode']; $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['dts']['bits_per_sample']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['dts']['sample_rate']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['dts']['channels']; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['dts']['bitrate']; if (isset($ThisFileInfo['avdataend'])) { $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8); } return true; }
public static function FixedPoint2_30($raw_data) { $binary_string = getid3_lib::BigEndian2Bin($raw_data); return bindec(substr($binary_string, 0, 2)) + (double) (bindec(substr($binary_string, 2, 30)) / 1073741824); // pow(2, 30) = 1073741824 }
public function Analyze() { $getid3 = $this->getid3; // http://www.atsc.org/standards/a_52a.pdf $getid3->info['fileformat'] = 'ac3'; $getid3->info['audio']['dataformat'] = 'ac3'; $getid3->info['audio']['bitrate_mode'] = 'cbr'; $getid3->info['audio']['lossless'] = false; $getid3->info['ac3']['raw']['bsi'] = array(); $info_ac3 =& $getid3->info['ac3']; $info_ac3_raw =& $info_ac3['raw']; $info_ac3_raw_bsi =& $info_ac3_raw['bsi']; // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256 // new audio samples per channel. A synchronization information (SI) header at the beginning // of each frame contains information needed to acquire and maintain synchronization. A // bit stream information (BSI) header follows SI, and contains parameters describing the coded // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the // end of each frame is an error check field that includes a CRC word for error detection. An // additional CRC word is located in the SI header, the use of which, by a decoder, is optional. // // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC $this->fseek($getid3->info['avdataoffset'], SEEK_SET); $ac3_header['syncinfo'] = $this->fread(5); $info_ac3_raw['synchinfo']['synchword'] = substr($ac3_header['syncinfo'], 0, 2); if ($info_ac3_raw['synchinfo']['synchword'] != "\vw") { throw new getid3_exception('Expecting "\\x0B\\x77" at offset ' . $getid3->info['avdataoffset'] . ', found \\x' . strtoupper(dechex($ac3_header['syncinfo'][0])) . '\\x' . strtoupper(dechex($ac3_header['syncinfo'][1])) . ' instead'); } // syncinfo() { // syncword 16 // crc1 16 // fscod 2 // frmsizecod 6 // } /* end of syncinfo */ $info_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($ac3_header['syncinfo'], 2, 2)); $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($ac3_header['syncinfo'], 4, 1)); $info_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xc0) >> 6; $info_ac3_raw['synchinfo']['frmsizecod'] = $ac3_synchinfo_fscod_frmsizecod & 0x3f; $info_ac3['sample_rate'] = getid3_ac3::AC3sampleRateCodeLookup($info_ac3_raw['synchinfo']['fscod']); if ($info_ac3_raw['synchinfo']['fscod'] <= 3) { $getid3->info['audio']['sample_rate'] = $info_ac3['sample_rate']; } $info_ac3['frame_length'] = getid3_ac3::AC3frameSizeLookup($info_ac3_raw['synchinfo']['frmsizecod'], $info_ac3_raw['synchinfo']['fscod']); $info_ac3['bitrate'] = getid3_ac3::AC3bitrateLookup($info_ac3_raw['synchinfo']['frmsizecod']); $getid3->info['audio']['bitrate'] = $info_ac3['bitrate']; $ac3_header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15)); $info_ac3_raw_bsi['bsid'] = bindec(substr($ac3_header['bsi'], 0, 5)); if ($info_ac3_raw_bsi['bsid'] > 8) { // Decoders which can decode version 8 will thus be able to decode version numbers less than 8. // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used. // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. throw new getid3_exception('Bit stream identification is version ' . $info_ac3_raw_bsi['bsid'] . ', but getID3() only understands up to version 8'); } $info_ac3_raw_bsi['bsmod'] = bindec(substr($ac3_header['bsi'], 5, 3)); $info_ac3_raw_bsi['acmod'] = bindec(substr($ac3_header['bsi'], 8, 3)); $info_ac3['service_type'] = getid3_ac3::AC3serviceTypeLookup($info_ac3_raw_bsi['bsmod'], $info_ac3_raw_bsi['acmod']); $ac3_coding_mode = getid3_ac3::AC3audioCodingModeLookup($info_ac3_raw_bsi['acmod']); foreach ($ac3_coding_mode as $key => $value) { $info_ac3[$key] = $value; } switch ($info_ac3_raw_bsi['acmod']) { case 0: case 1: $getid3->info['audio']['channelmode'] = 'mono'; break; case 3: case 4: $getid3->info['audio']['channelmode'] = 'stereo'; break; default: $getid3->info['audio']['channelmode'] = 'surround'; break; } $getid3->info['audio']['channels'] = $info_ac3['num_channels']; $offset = 11; if ($info_ac3_raw_bsi['acmod'] & 0x1) { // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. $info_ac3_raw_bsi['cmixlev'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $info_ac3['center_mix_level'] = getid3_ac3::AC3centerMixLevelLookup($info_ac3_raw_bsi['cmixlev']); $offset += 2; } if ($info_ac3_raw_bsi['acmod'] & 0x4) { // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream. $info_ac3_raw_bsi['surmixlev'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $info_ac3['surround_mix_level'] = getid3_ac3::AC3surroundMixLevelLookup($info_ac3_raw_bsi['surmixlev']); $offset += 2; } if ($info_ac3_raw_bsi['acmod'] == 0x2) { // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround. $info_ac3_raw_bsi['dsurmod'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $info_ac3['dolby_surround_mode'] = getid3_ac3::AC3dolbySurroundModeLookup($info_ac3_raw_bsi['dsurmod']); $offset += 2; } $info_ac3_raw_bsi['lfeon'] = $ac3_header['bsi'][$offset++] == '1'; $info_ac3['lfe_enabled'] = $info_ac3_raw_bsi['lfeon']; if ($info_ac3_raw_bsi['lfeon']) { $getid3->info['audio']['channels'] .= '.1'; } $info_ac3['channels_enabled'] = getid3_ac3::AC3channelsEnabledLookup($info_ac3_raw_bsi['acmod'], $info_ac3_raw_bsi['lfeon']); // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. $info_ac3_raw_bsi['dialnorm'] = bindec(substr($ac3_header['bsi'], $offset, 5)); $offset += 5; $info_ac3['dialogue_normalization'] = '-' . $info_ac3_raw_bsi['dialnorm'] . 'dB'; $info_ac3_raw_bsi['compre_flag'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['compre_flag']) { $info_ac3_raw_bsi['compr'] = bindec(substr($ac3_header['bsi'], $offset, 8)); $offset += 8; $info_ac3['heavy_compression'] = getid3_ac3::AC3heavyCompression($info_ac3_raw_bsi['compr']); } $info_ac3_raw_bsi['langcode_flag'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['langcode_flag']) { $info_ac3_raw_bsi['langcod'] = bindec(substr($ac3_header['bsi'], $offset, 8)); $offset += 8; } $info_ac3_raw_bsi['audprodie'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['audprodie']) { $info_ac3_raw_bsi['mixlevel'] = bindec(substr($ac3_header['bsi'], $offset, 5)); $offset += 5; $info_ac3_raw_bsi['roomtyp'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $offset += 2; $info_ac3['mixing_level'] = 80 + $info_ac3_raw_bsi['mixlevel'] . 'dB'; $info_ac3['room_type'] = getid3_ac3::AC3roomTypeLookup($info_ac3_raw_bsi['roomtyp']); } if ($info_ac3_raw_bsi['acmod'] == 0x0) { // If acmod is 0, then two completely independent program channels (dual mono) // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case, // a number of additional items are present in BSI or audblk to fully describe Ch2. // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. $info_ac3_raw_bsi['dialnorm2'] = bindec(substr($ac3_header['bsi'], $offset, 5)); $offset += 5; $info_ac3['dialogue_normalization2'] = '-' . $info_ac3_raw_bsi['dialnorm2'] . 'dB'; $info_ac3_raw_bsi['compre_flag2'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['compre_flag2']) { $info_ac3_raw_bsi['compr2'] = bindec(substr($ac3_header['bsi'], $offset, 8)); $offset += 8; $info_ac3['heavy_compression2'] = getid3_ac3::AC3heavyCompression($info_ac3_raw_bsi['compr2']); } $info_ac3_raw_bsi['langcode_flag2'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['langcode_flag2']) { $info_ac3_raw_bsi['langcod2'] = bindec(substr($ac3_header['bsi'], $offset, 8)); $offset += 8; } $info_ac3_raw_bsi['audprodie2'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['audprodie2']) { $info_ac3_raw_bsi['mixlevel2'] = bindec(substr($ac3_header['bsi'], $offset, 5)); $offset += 5; $info_ac3_raw_bsi['roomtyp2'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $offset += 2; $info_ac3['mixing_level2'] = 80 + $info_ac3_raw_bsi['mixlevel2'] . 'dB'; $info_ac3['room_type2'] = getid3_ac3::AC3roomTypeLookup($info_ac3_raw_bsi['roomtyp2']); } } $info_ac3_raw_bsi['copyright'] = $ac3_header['bsi'][$offset++] == '1'; $info_ac3_raw_bsi['original'] = $ac3_header['bsi'][$offset++] == '1'; $info_ac3_raw_bsi['timecode1_flag'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['timecode1_flag']) { $info_ac3_raw_bsi['timecode1'] = bindec(substr($ac3_header['bsi'], $offset, 14)); $offset += 14; } $info_ac3_raw_bsi['timecode2_flag'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['timecode2_flag']) { $info_ac3_raw_bsi['timecode2'] = bindec(substr($ac3_header['bsi'], $offset, 14)); $offset += 14; } $info_ac3_raw_bsi['addbsi_flag'] = $ac3_header['bsi'][$offset++] == '1'; if ($info_ac3_raw_bsi['addbsi_flag']) { $info_ac3_raw_bsi['addbsi_length'] = bindec(substr($ac3_header['bsi'], $offset, 6)); $offset += 6; $ac3_header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($info_ac3_raw_bsi['addbsi_length'])); $info_ac3_raw_bsi['addbsi_data'] = substr($ac3_header['bsi'], 119, $info_ac3_raw_bsi['addbsi_length'] * 8); } return true; }
private function FLACparseSTREAMINFO($meta_data_block_data) { $getid3 = $this->getid3; getid3_lib::ReadSequence('BigEndian2Int', $getid3->info['flac']['STREAMINFO'], $meta_data_block_data, 0, array('min_block_size' => 2, 'max_block_size' => 2, 'min_frame_size' => 3, 'max_frame_size' => 3)); $sample_rate_channels_sample_bits_stream_samples = getid3_lib::BigEndian2Bin(substr($meta_data_block_data, 10, 8)); $getid3->info['flac']['STREAMINFO']['sample_rate'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 0, 20)); $getid3->info['flac']['STREAMINFO']['channels'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 20, 3)) + 1; $getid3->info['flac']['STREAMINFO']['bits_per_sample'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 23, 5)) + 1; $getid3->info['flac']['STREAMINFO']['samples_stream'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 28, 36)); // bindec() returns float in case of int overrun $getid3->info['flac']['STREAMINFO']['audio_signature'] = substr($meta_data_block_data, 18, 16); if (!empty($getid3->info['flac']['STREAMINFO']['sample_rate'])) { $getid3->info['audio']['bitrate_mode'] = 'vbr'; $getid3->info['audio']['sample_rate'] = $getid3->info['flac']['STREAMINFO']['sample_rate']; $getid3->info['audio']['channels'] = $getid3->info['flac']['STREAMINFO']['channels']; $getid3->info['audio']['bits_per_sample'] = $getid3->info['flac']['STREAMINFO']['bits_per_sample']; $getid3->info['playtime_seconds'] = $getid3->info['flac']['STREAMINFO']['samples_stream'] / $getid3->info['flac']['STREAMINFO']['sample_rate']; $getid3->info['audio']['bitrate'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / $getid3->info['playtime_seconds']; } else { throw new getid3_exception('Corrupt METAdata block: STREAMINFO'); } unset($getid3->info['flac']['STREAMINFO']['raw']); return true; }
function getAACADIFheaderFilepointer() { $info =& $this->getid3->info; $info['fileformat'] = 'aac'; $info['audio']['dataformat'] = 'aac'; $info['audio']['lossless'] = false; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $AACheader = fread($this->getid3->fp, 1024); $offset = 0; if (substr($AACheader, 0, 4) == 'ADIF') { // http://faac.sourceforge.net/wiki/index.php?page=ADIF // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf // adif_header() { // adif_id 32 // copyright_id_present 1 // if( copyright_id_present ) // copyright_id 72 // original_copy 1 // home 1 // bitstream_type 1 // bitrate 23 // num_program_config_elements 4 // for (i = 0; i < num_program_config_elements + 1; i++ ) { // if( bitstream_type == '0' ) // adif_buffer_fullness 20 // program_config_element() // } // } $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader); $bitoffset = 0; $info['aac']['header_type'] = 'ADIF'; $bitoffset += 32; $info['aac']['header']['mpeg_version'] = 4; $info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; if ($info['aac']['header']['copyright']) { $info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); $bitoffset += 72; } $info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; $info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; $info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; if ($info['aac']['header']['is_vbr']) { $info['audio']['bitrate_mode'] = 'vbr'; $info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); $bitoffset += 23; } else { $info['audio']['bitrate_mode'] = 'cbr'; $info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); $bitoffset += 23; $info['audio']['bitrate'] = $info['aac']['header']['bitrate']; } if ($info['audio']['bitrate'] == 0) { $info['error'][] = 'Corrupt AAC file: bitrate_audio == zero'; return false; } $info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) { // http://www.audiocoding.com/wiki/index.php?page=program_config_element // buffer_fullness 20 // element_instance_tag 4 // object_type 2 // sampling_frequency_index 4 // num_front_channel_elements 4 // num_side_channel_elements 4 // num_back_channel_elements 4 // num_lfe_channel_elements 2 // num_assoc_data_elements 3 // num_valid_cc_elements 4 // mono_mixdown_present 1 // mono_mixdown_element_number 4 if mono_mixdown_present == 1 // stereo_mixdown_present 1 // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1 // matrix_mixdown_idx_present 1 // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1 // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1 // for (i = 0; i < num_front_channel_elements; i++) { // front_element_is_cpe[i] 1 // front_element_tag_select[i] 4 // } // for (i = 0; i < num_side_channel_elements; i++) { // side_element_is_cpe[i] 1 // side_element_tag_select[i] 4 // } // for (i = 0; i < num_back_channel_elements; i++) { // back_element_is_cpe[i] 1 // back_element_tag_select[i] 4 // } // for (i = 0; i < num_lfe_channel_elements; i++) { // lfe_element_tag_select[i] 4 // } // for (i = 0; i < num_assoc_data_elements; i++) { // assoc_data_element_tag_select[i] 4 // } // for (i = 0; i < num_valid_cc_elements; i++) { // cc_element_is_ind_sw[i] 1 // valid_cc_element_tag_select[i] 4 // } // byte_alignment() VAR // comment_field_bytes 8 // for (i = 0; i < comment_field_bytes; i++) { // comment_field_data[i] 8 // } if (!$info['aac']['header']['is_vbr']) { $info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20)); $bitoffset += 20; } $info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; $info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; $info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); $bitoffset += 3; $info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) { $info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } $info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) { $info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } $info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) { $info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; $info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; } for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) { $info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) { $info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) { $info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) { $info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) { $info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) { $info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } $bitoffset = ceil($bitoffset / 8) * 8; $info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8)); $bitoffset += 8; $info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'])); $bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']; $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']); $info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']); $info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency']; $info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]); if ($info['aac']['program_configs'][$i]['comment_field']) { $info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field']; } } $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']; $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile']; return true; } else { unset($info['fileformat']); unset($info['aac']); $info['error'][] = 'AAC-ADIF synch not found at offset ' . $info['avdataoffset'] . ' (expected "ADIF", found "' . substr($AACheader, 0, 4) . '" instead)'; return false; } }
public static function BigEndian2Float($byte_word) { // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic // http://www.psc.edu/general/software/packages/ieee/ieee.html // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html $bit_word = getid3_lib::BigEndian2Bin($byte_word); if (!$bit_word) { return 0; } $sign_bit = $bit_word[0]; switch (strlen($byte_word) * 8) { case 32: $exponent_bits = 8; $fraction_bits = 23; break; case 64: $exponent_bits = 11; $fraction_bits = 52; break; case 80: // 80-bit Apple SANE format // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ $exponent_string = substr($bit_word, 1, 15); $is_normalized = intval($bit_word[16]); $fraction_string = substr($bit_word, 17, 63); $exponent = pow(2, getid3_lib::Bin2Dec($exponent_string) - 16383); $fraction = $is_normalized + getid3_lib::DecimalBinary2Float($fraction_string); $float_value = $exponent * $fraction; if ($sign_bit == '1') { $float_value *= -1; } return $float_value; break; default: return false; break; } $exponent_string = substr($bit_word, 1, $exponent_bits); $fraction_string = substr($bit_word, $exponent_bits + 1, $fraction_bits); $exponent = bindec($exponent_string); $fraction = bindec($fraction_string); if ($exponent == pow(2, $exponent_bits) - 1 && $fraction != 0) { // Not a Number $float_value = false; } elseif ($exponent == pow(2, $exponent_bits) - 1 && $fraction == 0) { if ($sign_bit == '1') { $float_value = '-infinity'; } else { $float_value = '+infinity'; } } elseif ($exponent == 0 && $fraction == 0) { if ($sign_bit == '1') { $float_value = -0; } else { $float_value = 0; } $float_value = $sign_bit ? 0 : -0; } elseif ($exponent == 0 && $fraction != 0) { // These are 'unnormalized' values $float_value = pow(2, -1 * (pow(2, $exponent_bits - 1) - 2)) * getid3_lib::DecimalBinary2Float($fraction_string); if ($sign_bit == '1') { $float_value *= -1; } } elseif ($exponent != 0) { $float_value = pow(2, $exponent - (pow(2, $exponent_bits - 1) - 1)) * (1 + getid3_lib::DecimalBinary2Float($fraction_string)); if ($sign_bit == '1') { $float_value *= -1; } } return (double) $float_value; }
public function Analyze() { $info =& $this->getid3->info; // Specs taken from "DTS Coherent Acoustics;Core and Extensions, ETSI TS 102 114 V1.2.1 (2002-12)" // (http://pda.etsi.org/pda/queryform.asp) // With thanks to Gambit <*****@*****.**> http://mac.sourceforge.net/atl/ $info['fileformat'] = 'dts'; $this->fseek($info['avdataoffset']); $DTSheader = $this->fread(16); if (strpos($DTSheader, self::syncword) === 0) { $info['dts']['raw']['magic'] = self::syncword; $offset = 4; } else { if (!$this->isDependencyFor('matroska')) { unset($info['fileformat']); return $this->error('Expecting "' . getid3_lib::PrintHexBytes(self::syncword) . '" at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes(substr($DTSheader, 0, 4)) . '"'); } $offset = 0; } $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, $offset, 12)); $bsOffset = 0; $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, $bsOffset, 5); $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, $bsOffset, 7); $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, $bsOffset, 14); $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, $bsOffset, 6); $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, $bsOffset, 4); $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, $bsOffset, 5); $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, $bsOffset, 3); $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, $bsOffset, 2); $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); if ($info['dts']['flags']['crc_present']) { $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, $bsOffset, 16); } $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, $bsOffset, 4); $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, $bsOffset, 2); $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, $bsOffset, 2); $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, $bsOffset, 4); $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']); $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']); $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']); $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']); $info['dts']['flags']['lossless'] = $info['dts']['raw']['bitrate'] == 31 ? true : false; $info['dts']['bitrate_mode'] = $info['dts']['raw']['bitrate'] == 30 ? 'vbr' : 'cbr'; $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']); $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']); $info['audio']['dataformat'] = 'dts'; $info['audio']['lossless'] = $info['dts']['flags']['lossless']; $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode']; $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample']; $info['audio']['sample_rate'] = $info['dts']['sample_rate']; $info['audio']['channels'] = $info['dts']['channels']; $info['audio']['bitrate'] = $info['dts']['bitrate']; if (isset($info['avdataend'])) { $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8); } return true; }
public function ParseID3v2Frame(&$parsedFrame) { // shortcuts $info =& $this->getid3->info; $id3v2_majorversion = $info['id3v2']['majorversion']; $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); if (empty($parsedFrame['framenamelong'])) { unset($parsedFrame['framenamelong']); } $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); if (empty($parsedFrame['framenameshort'])) { unset($parsedFrame['framenameshort']); } if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard if ($id3v2_majorversion == 3) { // Frame Header Flags // %abc00000 %ijk00000 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x80); // i - Compression $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40); // j - Encryption $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x20); // k - Grouping identity } elseif ($id3v2_majorversion == 4) { // Frame Header Flags // %0abc0000 %0h00kmnp $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40); // h - Grouping identity $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8); // k - Compression $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4); // m - Encryption $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2); // n - Unsynchronisation $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1); // p - Data length indicator // Frame-level de-unsynchronisation - ID3v2.4 if ($parsedFrame['flags']['Unsynchronisation']) { $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); } if ($parsedFrame['flags']['DataLengthIndicator']) { $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1); $parsedFrame['data'] = substr($parsedFrame['data'], 4); } } // Frame-level de-compression if ($parsedFrame['flags']['compression']) { $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); if (!function_exists('gzuncompress')) { $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "' . $parsedFrame['frame_name'] . '"'; } else { if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) { $parsedFrame['data'] = $decompresseddata; unset($decompresseddata); } else { $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "' . $parsedFrame['frame_name'] . '"'; } } } } if (!empty($parsedFrame['flags']['DataLengthIndicator'])) { if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) { $info['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'; } } if (isset($parsedFrame['datalength']) && $parsedFrame['datalength'] == 0) { $warning = 'Frame "' . $parsedFrame['frame_name'] . '" at offset ' . $parsedFrame['dataoffset'] . ' has no data portion'; switch ($parsedFrame['frame_name']) { case 'WCOM': $warning .= ' (this is known to happen with files tagged by RioPort)'; break; default: break; } $info['warning'][] = $warning; } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'UFID' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'UFI') { // 4.1 UFI Unique file identifier // There may be more than one 'UFID' frame in a tag, // but only one with the same 'Owner identifier'. // <Header for 'Unique file identifier', ID: 'UFID'> // Owner identifier <text string> $00 // Identifier <up to 64 bytes binary data> $exploded = explode("", $parsedFrame['data'], 2); $parsedFrame['ownerid'] = isset($exploded[0]) ? $exploded[0] : ''; $parsedFrame['data'] = isset($exploded[1]) ? $exploded[1] : ''; } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'TXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'TXX') { // 4.2.2 TXX User defined text information frame // There may be more than one 'TXXX' frame in each tag, // but only one with the same description. // <Header for 'User defined text information frame', ID: 'TXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // Value <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description)); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $commentkey = $parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0); if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); } else { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); } } //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain } elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame // There may only be one text information frame of its kind in an tag. // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', // excluding 'TXXX' described in 4.2.6.> // Text encoding $xx // Information <text string(s) according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / // This of course breaks when an artist name contains slash character, e.g. "AC/DC" // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user switch ($parsedFrame['encoding']) { case 'UTF-16': case 'UTF-16BE': case 'UTF-16LE': $wordsize = 2; break; case 'ISO-8859-1': case 'UTF-8': default: $wordsize = 1; break; } $Txxx_elements = array(); $Txxx_elements_start_offset = 0; for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) { if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("", $wordsize)) { $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); $Txxx_elements_start_offset = $i + $wordsize; } } $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); foreach ($Txxx_elements as $Txxx_element) { $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element); if (!empty($string)) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; } } unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset); } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'WXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'WXX') { // 4.3.2 WXX User defined URL link frame // There may be more than one 'WXXX' frame in each tag, // but only one with the same description // <Header for 'User defined URL link frame', ID: 'WXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // URL <text string> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } if ($frame_terminatorpos) { // there are null bytes after the data - this is not according to spec // only use data up to first null byte $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); } else { // no null bytes following data, just use all data $frame_urldata = (string) $parsedFrame['data']; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['url'] = $frame_urldata; $parsedFrame['description'] = $frame_description; if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']); } unset($parsedFrame['data']); } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames // There may only be one URL link frame of its kind in a tag, // except when stated otherwise in the frame description // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' // described in 4.3.2.> // URL <text string> $parsedFrame['url'] = trim($parsedFrame['data']); if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'IPLS' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'IPL') { // 4.4 IPL Involved people list (ID3v2.2 only) // http://id3.org/id3v2.3.0#sec4.4 // There may only be one 'IPL' frame in each tag // <Header for 'User defined URL link frame', ID: 'IPL'> // Text encoding $xx // People list strings <textstrings> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset); // http://www.getid3.org/phpBB3/viewtopic.php?t=1369 // "this tag typically contains null terminated strings, which are associated in pairs" // "there are users that use the tag incorrectly" $IPLS_parts = array(); if (strpos($parsedFrame['data_raw'], "") !== false) { $IPLS_parts_unsorted = array(); if (strlen($parsedFrame['data_raw']) % 2 == 0 && (substr($parsedFrame['data_raw'], 0, 2) == "ÿþ" || substr($parsedFrame['data_raw'], 0, 2) == "þÿ")) { // 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 $thisILPS = ''; for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) { $twobytes = substr($parsedFrame['data_raw'], $i, 2); if ($twobytes === "") { $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); $thisILPS = ''; } else { $thisILPS .= $twobytes; } } if (strlen($thisILPS) > 2) { // 2-byte BOM $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); } } else { // ISO-8859-1 or UTF-8 or other single-byte-null character set $IPLS_parts_unsorted = explode("", $parsedFrame['data_raw']); } if (count($IPLS_parts_unsorted) == 1) { // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson" foreach ($IPLS_parts_unsorted as $key => $value) { $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value); $position = ''; foreach ($IPLS_parts_sorted as $person) { $IPLS_parts[] = array('position' => $position, 'person' => $person); } } } elseif (count($IPLS_parts_unsorted) % 2 == 0) { $position = ''; $person = ''; foreach ($IPLS_parts_unsorted as $key => $value) { if ($key % 2 == 0) { $position = $value; } else { $person = $value; $IPLS_parts[] = array('position' => $position, 'person' => $person); $position = ''; $person = ''; } } } else { foreach ($IPLS_parts_unsorted as $key => $value) { $IPLS_parts[] = array($value); } } } else { $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']); } $parsedFrame['data'] = $IPLS_parts; if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MCDI' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MCI') { // 4.5 MCI Music CD identifier // There may only be one 'MCDI' frame in each tag // <Header for 'Music CD identifier', ID: 'MCDI'> // CD TOC <binary data> if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ETCO' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ETC') { // 4.6 ETC Event timing codes // There may only be one 'ETCO' frame in each tag // <Header for 'Event timing codes', ID: 'ETCO'> // Time stamp format $xx // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Followed by a list of key events in the following format: // Type of event $xx // Time stamp $xx (xx ...) // The 'Time stamp' is set to zero if directly at the beginning of the sound // or after the previous event. All events MUST be sorted in chronological order. $frame_offset = 0; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); while ($frame_offset < strlen($parsedFrame['data'])) { $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MLLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MLL') { // 4.7 MLL MPEG location lookup table // There may only be one 'MLLT' frame in each tag // <Header for 'Location lookup table', ID: 'MLLT'> // MPEG frames between reference $xx xx // Bytes between reference $xx xx xx // Milliseconds between reference $xx xx xx // Bits for bytes deviation $xx // Bits for milliseconds dev. $xx // Then for every reference the following data is included; // Deviation in bytes %xxx.... // Deviation in milliseconds %xxx.... $frame_offset = 0; $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); $parsedFrame['data'] = substr($parsedFrame['data'], 10); while ($frame_offset < strlen($parsedFrame['data'])) { $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); } $reference_counter = 0; while (strlen($deviationbitstream) > 0) { $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); $reference_counter++; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYTC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'STC') { // 4.8 STC Synchronised tempo codes // There may only be one 'SYTC' frame in each tag // <Header for 'Synchronised tempo codes', ID: 'SYTC'> // Time stamp format $xx // Tempo data <binary data> // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file $frame_offset = 0; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $timestamp_counter = 0; while ($frame_offset < strlen($parsedFrame['data'])) { $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); } $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $timestamp_counter++; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ULT') { // 4.9 ULT Unsynchronised lyric/text transcription // There may be more than one 'Unsynchronised lyrics/text transcription' frame // in each tag, but only one with the same language and content descriptor. // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'> // Text encoding $xx // Language $xx xx xx // Content descriptor <text string according to encoding> $00 (00) // Lyrics/text <full text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['data'] = $parsedFrame['data']; $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['description'] = $frame_description; if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'SLT') { // 4.10 SLT Synchronised lyric/text // There may be more than one 'SYLT' frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Synchronised lyrics/text', ID: 'SYLT'> // Text encoding $xx // Language $xx xx xx // Time stamp format $xx // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Content type $xx // Content descriptor <text string according to encoding> $00 (00) // Terminated text to be synced (typically a syllable) // Sync identifier (terminator to above string) $00 (00) // Time stamp $xx (xx ...) $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $timestampindex = 0; $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); while (strlen($frame_remainingdata)) { $frame_offset = 0; $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator); if ($frame_terminatorpos === false) { $frame_remainingdata = ''; } else { if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator)); if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) { // timestamp probably omitted for first data item } else { $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); $frame_remainingdata = substr($frame_remainingdata, 4); } $timestampindex++; } } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'COM') { // 4.11 COM Comments // There may be more than one comment frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Comment', ID: 'COMM'> // Text encoding $xx // Language $xx xx xx // Short content descrip. <text string according to encoding> $00 (00) // The actual text <full text string according to encoding> if (strlen($parsedFrame['data']) < 5) { $info['warning'][] = 'Invalid data (too short) for "' . $parsedFrame['frame_name'] . '" frame at offset ' . $parsedFrame['dataoffset']; } else { $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['description'] = $frame_description; $parsedFrame['data'] = $frame_text; if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $commentkey = $parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0); if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } else { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } } } } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'RVA2') { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) // There may be more than one 'RVA2' frame in each tag, // but only one with the same identification string // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'> // Identification <text string> $00 // The 'identification' string is used to identify the situation and/or // device where this adjustment should apply. The following is then // repeated for every channel: // Type of channel $xx // Volume adjustment $xx xx // Bits representing peak $xx // Peak volume $xx (xx ...) $frame_terminatorpos = strpos($parsedFrame['data'], ""); $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("")); $parsedFrame['description'] = $frame_idstring; $RVA2channelcounter = 0; while (strlen($frame_remainingdata) >= 5) { $frame_offset = 0; $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid; $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed $frame_offset += 2; $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); if ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1 || $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4) { $info['warning'][] = 'ID3v2::RVA2 frame[' . $RVA2channelcounter . '] contains invalid ' . $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] . '-byte bits-representing-peak value'; break; } $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8); $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); $RVA2channelcounter++; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'RVAD' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'RVA') { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) // There may only be one 'RVA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'RVA'> // ID3v2.2 => Increment/decrement %000000ba // ID3v2.3 => Increment/decrement %00fedcba // Bits used for volume descr. $xx // Relative volume change, right $xx xx (xx ...) // a // Relative volume change, left $xx xx (xx ...) // b // Peak volume right $xx xx (xx ...) // Peak volume left $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, right back $xx xx (xx ...) // c // Relative volume change, left back $xx xx (xx ...) // d // Peak volume right back $xx xx (xx ...) // Peak volume left back $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, center $xx xx (xx ...) // e // Peak volume center $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, bass $xx xx (xx ...) // f // Peak volume bass $xx xx (xx ...) $frame_offset = 0; $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['right'] === false) { $parsedFrame['volumechange']['right'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['left'] === false) { $parsedFrame['volumechange']['left'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; if ($id3v2_majorversion == 3) { $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); if (strlen($parsedFrame['data']) > 0) { $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['rightrear'] === false) { $parsedFrame['volumechange']['rightrear'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['leftrear'] === false) { $parsedFrame['volumechange']['leftrear'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); if (strlen($parsedFrame['data']) > 0) { $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['center'] === false) { $parsedFrame['volumechange']['center'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); if (strlen($parsedFrame['data']) > 0) { $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); if ($parsedFrame['incdec']['bass'] === false) { $parsedFrame['volumechange']['bass'] *= -1; } $frame_offset += $frame_bytesvolume; $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'EQU2') { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) // There may be more than one 'EQU2' frame in each tag, // but only one with the same identification string // <Header of 'Equalisation (2)', ID: 'EQU2'> // Interpolation method $xx // $00 Band // $01 Linear // Identification <text string> $00 // The following is then repeated for every adjustment point // Frequency $xx xx // Volume adjustment $xx xx $frame_offset = 0; $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $parsedFrame['description'] = $frame_idstring; $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("")); while (strlen($frame_remainingdata)) { $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); $frame_remainingdata = substr($frame_remainingdata, 4); } $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'EQUA' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'EQU') { // 4.13 EQU Equalisation (ID3v2.2 only) // There may only be one 'EQUA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'EQU'> // Adjustment bits $xx // This is followed by 2 bytes + ('adjustment bits' rounded up to the // nearest byte) for every equalisation band in the following format, // giving a frequency range of 0 - 32767Hz: // Increment/decrement %x (MSB of the Frequency) // Frequency (lower 15 bits) // Adjustment $xx (xx ...) $frame_offset = 0; $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); while (strlen($frame_remainingdata) > 0) { $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); if ($parsedFrame[$frame_frequency]['incdec'] === false) { $parsedFrame[$frame_frequency]['adjustment'] *= -1; } $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RVRB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'REV') { // 4.14 REV Reverb // There may only be one 'RVRB' frame in each tag. // <Header for 'Reverb', ID: 'RVRB'> // Reverb left (ms) $xx xx // Reverb right (ms) $xx xx // Reverb bounces, left $xx // Reverb bounces, right $xx // Reverb feedback, left to left $xx // Reverb feedback, left to right $xx // Reverb feedback, right to right $xx // Reverb feedback, right to left $xx // Premix left to right $xx // Premix right to left $xx $frame_offset = 0; $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'APIC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'PIC') { // 4.15 PIC Attached picture // There may be several pictures attached to one file, // each in their individual 'APIC' frame, but only one // with the same content descriptor // <Header for 'Attached picture', ID: 'APIC'> // Text encoding $xx // ID3v2.3+ => MIME type <text string> $00 // ID3v2.2 => Image format $xx xx xx // Picture type $xx // Description <text string according to encoding> $00 (00) // Picture data <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); if (strtolower($frame_imagetype) == 'ima') { // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); if ($frame_imagetype == 'JPEG') { $frame_imagetype = 'JPG'; } $frame_offset = $frame_terminatorpos + strlen(""); } else { $frame_offset += 3; } } if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) { $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(""); } $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($frame_offset >= $parsedFrame['datalength']) { $info['warning'][] = 'data portion of APIC frame is missing at offset ' . ($parsedFrame['dataoffset'] + 8 + $frame_offset); } else { $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); if ($id3v2_majorversion == 2) { $parsedFrame['imagetype'] = $frame_imagetype; } else { $parsedFrame['mime'] = $frame_mimetype; } $parsedFrame['picturetypeid'] = $frame_picturetype; $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); $parsedFrame['description'] = $frame_description; $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); $parsedFrame['datalength'] = strlen($parsedFrame['data']); $parsedFrame['image_mime'] = ''; $imageinfo = array(); $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo); if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) { $parsedFrame['image_mime'] = 'image/' . getid3_lib::ImageTypesLookup($imagechunkcheck[2]); if ($imagechunkcheck[0]) { $parsedFrame['image_width'] = $imagechunkcheck[0]; } if ($imagechunkcheck[1]) { $parsedFrame['image_height'] = $imagechunkcheck[1]; } } do { if ($this->getid3->option_save_attachments === false) { // skip entirely unset($parsedFrame['data']); break; } if ($this->getid3->option_save_attachments === true) { // great /* } elseif (is_int($this->getid3->option_save_attachments)) { if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) { // too big, skip $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)'; unset($parsedFrame['data']); break; } */ } elseif (is_string($this->getid3->option_save_attachments)) { $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); if (!is_dir($dir) || !is_writable($dir)) { // cannot write, skip $info['warning'][] = 'attachment at ' . $frame_offset . ' cannot be saved to "' . $dir . '" (not writable)'; unset($parsedFrame['data']); break; } } // if we get this far, must be OK if (is_string($this->getid3->option_save_attachments)) { $destination_filename = $dir . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $frame_offset; if (!file_exists($destination_filename) || is_writable($destination_filename)) { file_put_contents($destination_filename, $parsedFrame['data']); } else { $info['warning'][] = 'attachment at ' . $frame_offset . ' cannot be saved to "' . $destination_filename . '" (not writable)'; } $parsedFrame['data_filename'] = $destination_filename; unset($parsedFrame['data']); } else { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!isset($info['id3v2']['comments']['picture'])) { $info['id3v2']['comments']['picture'] = array(); } $comments_picture_data = array(); foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) { if (isset($parsedFrame[$picture_key])) { $comments_picture_data[$picture_key] = $parsedFrame[$picture_key]; } } $info['id3v2']['comments']['picture'][] = $comments_picture_data; unset($comments_picture_data); } } } while (false); } } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GEOB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'GEO') { // 4.16 GEO General encapsulated object // There may be more than one 'GEOB' frame in each tag, // but only one with the same content descriptor // <Header for 'General encapsulated object', ID: 'GEOB'> // Text encoding $xx // MIME type <text string> $00 // Filename <text string according to encoding> $00 (00) // Content description <text string according to encoding> $00 (00) // Encapsulated object <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_filename) === 0) { $frame_filename = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['mime'] = $frame_mimetype; $parsedFrame['filename'] = $frame_filename; $parsedFrame['description'] = $frame_description; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PCNT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CNT') { // 4.17 CNT Play counter // There may only be one 'PCNT' frame in each tag. // When the counter reaches all one's, one byte is inserted in // front of the counter thus making the counter eight bits bigger // <Header for 'Play counter', ID: 'PCNT'> // Counter $xx xx xx xx (xx ...) $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POPM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'POP') { // 4.18 POP Popularimeter // There may be more than one 'POPM' frame in each tag, // but only one with the same email address // <Header for 'Popularimeter', ID: 'POPM'> // Email to user <text string> $00 // Rating $xx // Counter $xx xx xx xx (xx ...) $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_emailaddress) === 0) { $frame_emailaddress = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); $parsedFrame['email'] = $frame_emailaddress; $parsedFrame['rating'] = $frame_rating; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RBUF' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'BUF') { // 4.19 BUF Recommended buffer size // There may only be one 'RBUF' frame in each tag // <Header for 'Recommended buffer size', ID: 'RBUF'> // Buffer size $xx xx xx // Embedded info flag %0000000x // Offset to next tag $xx xx xx xx $frame_offset = 0; $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); $frame_offset += 3; $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); unset($parsedFrame['data']); } elseif ($id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRM') { // 4.20 Encrypted meta frame (ID3v2.2 only) // There may be more than one 'CRM' frame in a tag, // but only one with the same 'owner identifier' // <Header for 'Encrypted meta frame', ID: 'CRM'> // Owner identifier <textstring> $00 (00) // Content/explanation <textstring> $00 (00) // Encrypted datablock <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['description'] = $frame_description; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'AENC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRA') { // 4.21 CRA Audio encryption // There may be more than one 'AENC' frames in a tag, // but only one with the same 'Owner identifier' // <Header for 'Audio encryption', ID: 'AENC'> // Owner identifier <text string> $00 // Preview start $xx xx // Preview length $xx xx // Encryption info <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid == ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'LINK' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'LNK') { // 4.22 LNK Linked information // There may be more than one 'LINK' frame in a tag, // but only one with the same contents // <Header for 'Linked information', ID: 'LINK'> // ID3v2.3+ => Frame identifier $xx xx xx xx // ID3v2.2 => Frame identifier $xx xx xx // URL <text string> $00 // ID and additional data <text string(s)> $frame_offset = 0; if ($id3v2_majorversion == 2) { $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; } else { $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); $frame_offset += 4; } $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_url) === 0) { $frame_url = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['url'] = $frame_url; $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POSS') { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) // There may only be one 'POSS' frame in each tag // <Head for 'Position synchronisation', ID: 'POSS'> // Time stamp format $xx // Position $xx (xx ...) $frame_offset = 0; $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USER') { // 4.22 USER Terms of use (ID3v2.3+ only) // There may be more than one 'Terms of use' frame in a tag, // but only one with the same 'Language' // <Header for 'Terms of use frame', ID: 'USER'> // Text encoding $xx // Language $xx xx xx // The actual text <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'OWNE') { // 4.23 OWNE Ownership frame (ID3v2.3+ only) // There may only be one 'OWNE' frame in a tag // <Header for 'Ownership frame', ID: 'OWNE'> // Text encoding $xx // Price paid <text string> $00 // Date of purch. <text string> // Seller <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) { $parsedFrame['purchasedateunix'] = mktime(0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); } $frame_offset += 8; $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMR') { // 4.24 COMR Commercial frame (ID3v2.3+ only) // There may be more than one 'commercial frame' in a tag, // but no two may be identical // <Header for 'Commercial frame', ID: 'COMR'> // Text encoding $xx // Price string <text string> $00 // Valid until <text string> // Contact URL <text string> $00 // Received as $xx // Name of seller <text string according to encoding> $00 (00) // Description <text string according to encoding> $00 (00) // Picture MIME type <string> $00 // Seller logo <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) { $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding'; $frame_textencoding_terminator = ""; } $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_rawpricearray = explode('/', $frame_pricestring); foreach ($frame_rawpricearray as $key => $val) { $frame_currencyid = substr($val, 0, 3); $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); } $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); $frame_offset += 8; $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_sellername) === 0) { $frame_sellername = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(""); $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['pricevaliduntil'] = $frame_datestring; $parsedFrame['contacturl'] = $frame_contacturl; $parsedFrame['receivedasid'] = $frame_receivedasid; $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); $parsedFrame['sellername'] = $frame_sellername; $parsedFrame['description'] = $frame_description; $parsedFrame['mime'] = $frame_mimetype; $parsedFrame['logo'] = $frame_sellerlogo; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ENCR') { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) // There may be several 'ENCR' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Encryption method registration', ID: 'ENCR'> // Owner identifier <text string> $00 // Method symbol $xx // Encryption data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GRID') { // 4.26 GRID Group identification registration (ID3v2.3+ only) // There may be several 'GRID' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Group ID registration', ID: 'GRID'> // Owner identifier <text string> $00 // Group symbol $xx // Group dependent data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PRIV') { // 4.27 PRIV Private frame (ID3v2.3+ only) // The tag may contain more than one 'PRIV' frame // but only with different contents // <Header for 'Private frame', ID: 'PRIV'> // Owner identifier <text string> $00 // The private data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(""); $parsedFrame['ownerid'] = $frame_ownerid; $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SIGN') { // 4.28 SIGN Signature frame (ID3v2.4+ only) // There may be more than one 'signature frame' in a tag, // but no two may be identical // <Header for 'Signature frame', ID: 'SIGN'> // Group symbol $xx // Signature <binary data> $frame_offset = 0; $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SEEK') { // 4.29 SEEK Seek frame (ID3v2.4+ only) // There may only be one 'seek frame' in a tag // <Header for 'Seek frame', ID: 'SEEK'> // Minimum offset to next tag $xx xx xx xx $frame_offset = 0; $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'ASPI') { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) // There may only be one 'audio seek point index' frame in a tag // <Header for 'Seek Point Index', ID: 'ASPI'> // Indexed data start (S) $xx xx xx xx // Indexed data length (L) $xx xx xx xx // Number of index points (N) $xx xx // Bits per index point (b) $xx // Then for every index point the following data is included: // Fraction at index (Fi) $xx (xx) $frame_offset = 0; $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) { $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); $frame_offset += $frame_bytesperpoint; } unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RGAD') { // Replay Gain Adjustment // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html // There may only be one 'RGAD' frame in a tag // <Header for 'Replay Gain Adjustment', ID: 'RGAD'> // Peak Amplitude $xx $xx $xx $xx // Radio Replay Gain Adjustment %aaabbbcd %dddddddd // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd // a - name code // b - originator code // c - sign bit // d - replay gain adjustment $frame_offset = 0; $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3)); $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3)); $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1)); $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9)); $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3)); $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3)); $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1)); $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9)); $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; unset($parsedFrame['data']); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'CHAP') { // CHAP Chapters frame (ID3v2.3+ only) // http://id3.org/id3v2-chapters-1.0 // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes) // Element ID <text string> $00 // Start time $xx xx xx xx // End time $xx xx xx xx // Start offset $xx xx xx xx // End offset $xx xx xx xx // <Optional embedded sub-frames> $frame_offset = 0; @(list($parsedFrame['element_id']) = explode("", $parsedFrame['data'], 2)); $frame_offset += strlen($parsedFrame['element_id'] . ""); $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; if (substr($parsedFrame['data'], $frame_offset, 4) != "ÿÿÿÿ") { // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized." $parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); } $frame_offset += 4; if (substr($parsedFrame['data'], $frame_offset, 4) != "ÿÿÿÿ") { // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized." $parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); } $frame_offset += 4; if ($frame_offset < strlen($parsedFrame['data'])) { $parsedFrame['subframes'] = array(); while ($frame_offset < strlen($parsedFrame['data'])) { // <Optional embedded sub-frames> $subframe = array(); $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4); $frame_offset += 4; $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; if ($subframe['size'] > strlen($parsedFrame['data']) - $frame_offset) { $info['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)'; break; } $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); $frame_offset += $subframe['size']; $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1)); $subframe['text'] = substr($subframe_rawdata, 1); $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']); $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text'])); switch (substr($encoding_converted_text, 0, 2)) { case "ÿþ": case "þÿ": switch (strtoupper($info['id3v2']['encoding'])) { case 'ISO-8859-1': case 'UTF-8': $encoding_converted_text = substr($encoding_converted_text, 2); // remove unwanted byte-order-marks break; default: // ignore break; } break; default: // do not remove BOM break; } if ($subframe['name'] == 'TIT2' || $subframe['name'] == 'TIT3') { if ($subframe['name'] == 'TIT2') { $parsedFrame['chapter_name'] = $encoding_converted_text; } elseif ($subframe['name'] == 'TIT3') { $parsedFrame['chapter_description'] = $encoding_converted_text; } $parsedFrame['subframes'][] = $subframe; } else { $info['warning'][] = 'ID3v2.CHAP subframe "' . $subframe['name'] . '" not handled (only TIT2 and TIT3)'; } } unset($subframe_rawdata, $subframe, $encoding_converted_text); } $id3v2_chapter_entry = array(); foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) { if (isset($parsedFrame[$id3v2_chapter_key])) { $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key]; } } if (!isset($info['id3v2']['chapters'])) { $info['id3v2']['chapters'] = array(); } $info['id3v2']['chapters'][] = $id3v2_chapter_entry; unset($id3v2_chapter_entry, $id3v2_chapter_key); } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'CTOC') { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only) // http://id3.org/id3v2-chapters-1.0 // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes) // Element ID <text string> $00 // CTOC flags %xx // Entry count $xx // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */ // <Optional embedded sub-frames> $frame_offset = 0; @(list($parsedFrame['element_id']) = explode("", $parsedFrame['data'], 2)); $frame_offset += strlen($parsedFrame['element_id'] . ""); $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1)); $frame_offset += 1; $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1)); $frame_offset += 1; $terminator_position = null; for ($i = 0; $i < $parsedFrame['entry_count']; $i++) { $terminator_position = strpos($parsedFrame['data'], "", $frame_offset); $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset); $frame_offset = $terminator_position + 1; } $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x1); $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x3); unset($ctoc_flags_raw, $terminator_position); if ($frame_offset < strlen($parsedFrame['data'])) { $parsedFrame['subframes'] = array(); while ($frame_offset < strlen($parsedFrame['data'])) { // <Optional embedded sub-frames> $subframe = array(); $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4); $frame_offset += 4; $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); $frame_offset += 4; $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); $frame_offset += 2; if ($subframe['size'] > strlen($parsedFrame['data']) - $frame_offset) { $info['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)'; break; } $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); $frame_offset += $subframe['size']; $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1)); $subframe['text'] = substr($subframe_rawdata, 1); $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']); $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text'])); switch (substr($encoding_converted_text, 0, 2)) { case "ÿþ": case "þÿ": switch (strtoupper($info['id3v2']['encoding'])) { case 'ISO-8859-1': case 'UTF-8': $encoding_converted_text = substr($encoding_converted_text, 2); // remove unwanted byte-order-marks break; default: // ignore break; } break; default: // do not remove BOM break; } if ($subframe['name'] == 'TIT2' || $subframe['name'] == 'TIT3') { if ($subframe['name'] == 'TIT2') { $parsedFrame['toc_name'] = $encoding_converted_text; } elseif ($subframe['name'] == 'TIT3') { $parsedFrame['toc_description'] = $encoding_converted_text; } $parsedFrame['subframes'][] = $subframe; } else { $info['warning'][] = 'ID3v2.CTOC subframe "' . $subframe['name'] . '" not handled (only TIT2 and TIT3)'; } } unset($subframe_rawdata, $subframe, $encoding_converted_text); } } return true; }
function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) { $offset = 0; $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); $offset += 2; $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); $offset += 2; $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); $offset += 3; $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); $offset += 3; $SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8)); $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20)); $ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1; $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1; $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36)); $offset += 8; $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16); $offset += 16; if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) { $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels']; $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; } else { $ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO'; return false; } return true; }
public function Analyze() { $getid3 = $this->getid3; $getid3->info['mpeg']['video']['raw'] = array(); $info_mpeg_video =& $getid3->info['mpeg']['video']; $info_mpeg_video_raw =& $info_mpeg_video['raw']; $getid3->info['video'] = array(); $info_video =& $getid3->info['video']; $getid3->include_module('audio.mp3'); if ($getid3->info['avdataend'] <= $getid3->info['avdataoffset']) { throw new getid3_exception('"avdataend" (' . $getid3->info['avdataend'] . ') is unexpectedly less-than-or-equal-to "avdataoffset" (' . $getid3->info['avdataoffset'] . ')'); } $getid3->info['fileformat'] = 'mpeg'; fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $mpeg_stream_data = fread($getid3->fp, min(100000, $getid3->info['avdataend'] - $getid3->info['avdataoffset'])); $mpeg_stream_data_length = strlen($mpeg_stream_data); $video_chunk_offset = 0; while (substr($mpeg_stream_data, $video_chunk_offset++, 4) !== getid3_mpeg::VIDEO_SEQUENCE_HEADER) { if ($video_chunk_offset >= $mpeg_stream_data_length) { throw new getid3_exception('Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?'); } } // Start code 32 bits // horizontal frame size 12 bits // vertical frame size 12 bits // pixel aspect ratio 4 bits // frame rate 4 bits // bitrate 18 bits // marker bit 1 bit // VBV buffer size 10 bits // constrained parameter flag 1 bit // intra quant. matrix flag 1 bit // intra quant. matrix values 512 bits (present if matrix flag == 1) // non-intra quant. matrix flag 1 bit // non-intra quant. matrix values 512 bits (present if matrix flag == 1) $info_video['dataformat'] = 'mpeg'; $video_chunk_offset += strlen(getid3_mpeg::VIDEO_SEQUENCE_HEADER) - 1; $frame_size_dword = getid3_lib::BigEndian2Int(substr($mpeg_stream_data, $video_chunk_offset, 3)); $video_chunk_offset += 3; $aspect_ratio_frame_rate_dword = getid3_lib::BigEndian2Int(substr($mpeg_stream_data, $video_chunk_offset, 1)); $video_chunk_offset += 1; $assorted_information = getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 4)); $video_chunk_offset += 4; $info_mpeg_video_raw['framesize_horizontal'] = ($frame_size_dword & 0xfff000) >> 12; // 12 bits for horizontal frame size $info_mpeg_video_raw['framesize_vertical'] = $frame_size_dword & 0xfff; // 12 bits for vertical frame size $info_mpeg_video_raw['pixel_aspect_ratio'] = ($aspect_ratio_frame_rate_dword & 0xf0) >> 4; $info_mpeg_video_raw['frame_rate'] = $aspect_ratio_frame_rate_dword & 0xf; $info_mpeg_video['framesize_horizontal'] = $info_mpeg_video_raw['framesize_horizontal']; $info_mpeg_video['framesize_vertical'] = $info_mpeg_video_raw['framesize_vertical']; $info_mpeg_video['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($info_mpeg_video_raw['pixel_aspect_ratio']); $info_mpeg_video['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($info_mpeg_video_raw['pixel_aspect_ratio']); $info_mpeg_video['frame_rate'] = $this->MPEGvideoFramerateLookup($info_mpeg_video_raw['frame_rate']); $info_mpeg_video_raw['bitrate'] = bindec(substr($assorted_information, 0, 18)); $info_mpeg_video_raw['marker_bit'] = (bool) bindec($assorted_information[18]); $info_mpeg_video_raw['vbv_buffer_size'] = bindec(substr($assorted_information, 19, 10)); $info_mpeg_video_raw['constrained_param_flag'] = (bool) bindec($assorted_information[29]); $info_mpeg_video_raw['intra_quant_flag'] = (bool) bindec($assorted_information[30]); if ($info_mpeg_video_raw['intra_quant_flag']) { // read 512 bits $info_mpeg_video_raw['intra_quant'] = getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 64)); $video_chunk_offset += 64; $info_mpeg_video_raw['non_intra_quant_flag'] = (bool) bindec($info_mpeg_video_raw['intra_quant'][511]); $info_mpeg_video_raw['intra_quant'] = bindec($assorted_information[31]) . substr(getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 64)), 0, 511); if ($info_mpeg_video_raw['non_intra_quant_flag']) { $info_mpeg_video_raw['non_intra_quant'] = substr($mpeg_stream_data, $video_chunk_offset, 64); $video_chunk_offset += 64; } } else { $info_mpeg_video_raw['non_intra_quant_flag'] = (bool) bindec($assorted_information[31]); if ($info_mpeg_video_raw['non_intra_quant_flag']) { $info_mpeg_video_raw['non_intra_quant'] = substr($mpeg_stream_data, $video_chunk_offset, 64); $video_chunk_offset += 64; } } if ($info_mpeg_video_raw['bitrate'] == 0x3ffff) { // 18 set bits $getid3->warning('This version of getID3() cannot determine average bitrate of VBR MPEG video files'); $info_mpeg_video['bitrate_mode'] = 'vbr'; } else { $info_mpeg_video['bitrate'] = $info_mpeg_video_raw['bitrate'] * 400; $info_mpeg_video['bitrate_mode'] = 'cbr'; $info_video['bitrate'] = $info_mpeg_video['bitrate']; } $info_video['resolution_x'] = $info_mpeg_video['framesize_horizontal']; $info_video['resolution_y'] = $info_mpeg_video['framesize_vertical']; $info_video['frame_rate'] = $info_mpeg_video['frame_rate']; $info_video['bitrate_mode'] = $info_mpeg_video['bitrate_mode']; $info_video['pixel_aspect_ratio'] = $info_mpeg_video['pixel_aspect_ratio']; $info_video['lossless'] = false; $info_video['bits_per_sample'] = 24; //0x000001B3 begins the sequence_header of every MPEG video stream. //But in MPEG-2, this header must immediately be followed by an //extension_start_code (0x000001B5) with a sequence_extension ID (1). //(This extension contains all the additional MPEG-2 stuff.) //MPEG-1 doesn't have this extension, so that's a sure way to tell the //difference between MPEG-1 and MPEG-2 video streams. $info_video['codec'] = substr($mpeg_stream_data, $video_chunk_offset, 4) == getid3_mpeg::VIDEO_EXTENSION_START ? 'MPEG-2' : 'MPEG-1'; $audio_chunk_offset = 0; while (true) { while (substr($mpeg_stream_data, $audio_chunk_offset++, 4) !== getid3_mpeg::AUDIO_START) { if ($audio_chunk_offset >= $mpeg_stream_data_length) { break 2; } } for ($i = 0; $i <= 7; $i++) { // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after // I have no idea why or what the difference is, so this is a stupid hack. // If anybody has any better idea of what's going on, please let me know - info@getid3.org // make copy of info $dummy = $getid3->info; // clone getid3 - better safe than sorry $clone = clone $this->getid3; // check $mp3 = new getid3_mp3($clone); if ($mp3->decodeMPEGaudioHeader($getid3->fp, $audio_chunk_offset + 3 + 8 + $i, $dummy, false)) { $getid3->info = $dummy; $getid3->info['audio']['bitrate_mode'] = 'cbr'; $getid3->info['audio']['lossless'] = false; break 2; } // destroy copy unset($dummy); } } // Temporary hack to account for interleaving overhead: if (!empty($info_video['bitrate']) && !empty($getid3->info['audio']['bitrate'])) { $getid3->info['playtime_seconds'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / ($info_video['bitrate'] + $getid3->info['audio']['bitrate']); // Interleaved MPEG audio/video files have a certain amount of overhead that varies // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter // Use interpolated lookup tables to approximately guess how much is overhead, because // playtime is calculated as filesize / total-bitrate $getid3->info['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($info_video['bitrate'], $getid3->info['audio']['bitrate']); //switch ($info_video['bitrate']) { // case('5000000'): // $multiplier = 0.93292642112380355828048824319889; // break; // case('5500000'): // $multiplier = 0.93582895375200989965359777343219; // break; // case('6000000'): // $multiplier = 0.93796247714820932532911373859139; // break; // case('7000000'): // $multiplier = 0.9413264083635103463010117778776; // break; // default: // $multiplier = 1; // break; //} //$getid3->info['playtime_seconds'] *= $multiplier; //$getid3->warning('Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.'); if ($info_video['bitrate'] < 50000) { $getid3->warning('Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.'); } } return true; }
public function Analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'mpeg'; $this->fseek($info['avdataoffset']); $MPEGstreamData = $this->fread($this->getid3->option_fread_buffer_size); $MPEGstreamBaseOffset = 0; // how far are we from the beginning of the file data ($info['avdataoffset']) $MPEGstreamDataOffset = 0; // how far are we from the beginning of the buffer data (~32kB) $StartCodeValue = false; $prevStartCodeValue = false; $GOPcounter = -1; $FramesByGOP = array(); $ParsedAVchannels = array(); do { //echo $MPEGstreamDataOffset.' vs '.(strlen($MPEGstreamData) - 1024).'<Br>'; if ($MPEGstreamDataOffset > strlen($MPEGstreamData) - 16384) { // buffer running low, get more data //echo 'reading more data<br>'; $MPEGstreamData .= $this->fread($this->getid3->option_fread_buffer_size); if (strlen($MPEGstreamData) > $this->getid3->option_fread_buffer_size) { $MPEGstreamData = substr($MPEGstreamData, $MPEGstreamDataOffset); $MPEGstreamBaseOffset += $MPEGstreamDataOffset; $MPEGstreamDataOffset = 0; } } if (($StartCodeOffset = strpos($MPEGstreamData, self::START_CODE_BASE, $MPEGstreamDataOffset)) === false) { //echo 'no more start codes found.<br>'; break; } else { $MPEGstreamDataOffset = $StartCodeOffset; $prevStartCodeValue = $StartCodeValue; $StartCodeValue = ord(substr($MPEGstreamData, $StartCodeOffset + 3, 1)); //echo 'Found "'.strtoupper(dechex($StartCodeValue)).'" at offset '.($MPEGstreamBaseOffset + $StartCodeOffset).' ($MPEGstreamDataOffset = '.$MPEGstreamDataOffset.')<br>'; } $MPEGstreamDataOffset += 4; switch ($StartCodeValue) { case 0x0: // picture_start_code if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') { $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4)); $bitstreamoffset = 0; $PictureHeader = array(); $PictureHeader['temporal_reference'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10-bit unsigned integer associated with each input picture. It is incremented by one, modulo 1024, for each input frame. When a frame is coded as two fields the temporal reference in the picture header of both fields is the same. Following a group start header the temporal reference of the earliest picture (in display order) shall be reset to zero. $PictureHeader['picture_coding_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_coding_type $PictureHeader['vbv_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 16); // 16 bits for vbv_delay //... etc $FramesByGOP[$GOPcounter][] = $PictureHeader; } break; case 0xb3: // sequence_header_code /* Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations. Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation. Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries. */ $info['video']['codec'] = 'MPEG-1'; // will be updated if extension_start_code found $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8)); $bitstreamoffset = 0; $info['mpeg']['video']['raw']['horizontal_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for horizontal frame size. Note: horizontal_size_extension, if present, will add 2 most-significant bits to this value $info['mpeg']['video']['raw']['vertical_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for vertical frame size. Note: vertical_size_extension, if present, will add 2 most-significant bits to this value $info['mpeg']['video']['raw']['aspect_ratio_information'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for aspect_ratio_information $info['mpeg']['video']['raw']['frame_rate_code'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for Frame Rate id code $info['mpeg']['video']['raw']['bitrate'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 18); // 18 bits for bit_rate_value (18 set bits = VBR, otherwise bitrate = this value * 400) $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation. $info['mpeg']['video']['raw']['vbv_buffer_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10 bits vbv_buffer_size_value $info['mpeg']['video']['raw']['constrained_param_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: constrained_param_flag $info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: load_intra_quantiser_matrix if ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix']) { $bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12, 64)); for ($i = 0; $i < 64; $i++) { $info['mpeg']['video']['raw']['intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); } } $info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); if ($info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix']) { $bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12 + ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] ? 64 : 0), 64)); for ($i = 0; $i < 64; $i++) { $info['mpeg']['video']['raw']['non_intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); } } $info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information']); $info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information']); $info['mpeg']['video']['frame_rate'] = self::videoFramerateLookup($info['mpeg']['video']['raw']['frame_rate_code']); if ($info['mpeg']['video']['raw']['bitrate'] == 0x3ffff) { // 18 set bits = VBR //$this->warning('This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files'); $info['mpeg']['video']['bitrate_mode'] = 'vbr'; } else { $info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400; $info['mpeg']['video']['bitrate_mode'] = 'cbr'; $info['video']['bitrate'] = $info['mpeg']['video']['bitrate']; } $info['video']['resolution_x'] = $info['mpeg']['video']['raw']['horizontal_size_value']; $info['video']['resolution_y'] = $info['mpeg']['video']['raw']['vertical_size_value']; $info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate']; $info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode']; $info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio']; $info['video']['lossless'] = false; $info['video']['bits_per_sample'] = 24; break; case 0xb5: // extension_start_code $info['video']['codec'] = 'MPEG-2'; $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8)); // 48 bits for Sequence Extension ID; 61 bits for Sequence Display Extension ID; 59 bits for Sequence Scalable Extension ID $bitstreamoffset = 0; $info['mpeg']['video']['raw']['extension_start_code_identifier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for extension_start_code_identifier //echo $info['mpeg']['video']['raw']['extension_start_code_identifier'].'<br>'; switch ($info['mpeg']['video']['raw']['extension_start_code_identifier']) { case 1: // 0001 Sequence Extension ID $info['mpeg']['video']['raw']['profile_and_level_indication'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for profile_and_level_indication $info['mpeg']['video']['raw']['progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_sequence $info['mpeg']['video']['raw']['chroma_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for chroma_format $info['mpeg']['video']['raw']['horizontal_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for horizontal_size_extension $info['mpeg']['video']['raw']['vertical_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for vertical_size_extension $info['mpeg']['video']['raw']['bit_rate_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for bit_rate_extension $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation. $info['mpeg']['video']['raw']['vbv_buffer_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for vbv_buffer_size_extension $info['mpeg']['video']['raw']['low_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: low_delay $info['mpeg']['video']['raw']['frame_rate_extension_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for frame_rate_extension_n $info['mpeg']['video']['raw']['frame_rate_extension_d'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for frame_rate_extension_d $info['video']['resolution_x'] = $info['mpeg']['video']['raw']['horizontal_size_extension'] << 12 | $info['mpeg']['video']['raw']['horizontal_size_value']; $info['video']['resolution_y'] = $info['mpeg']['video']['raw']['vertical_size_extension'] << 12 | $info['mpeg']['video']['raw']['vertical_size_value']; $info['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence']; $info['mpeg']['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence']; $info['mpeg']['video']['chroma_format'] = self::chromaFormatTextLookup($info['mpeg']['video']['raw']['chroma_format']); break; case 2: // 0010 Sequence Display Extension ID $info['mpeg']['video']['raw']['video_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for video_format $info['mpeg']['video']['raw']['colour_description'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: colour_description if ($info['mpeg']['video']['raw']['colour_description']) { $info['mpeg']['video']['raw']['colour_primaries'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for colour_primaries $info['mpeg']['video']['raw']['transfer_characteristics'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for transfer_characteristics $info['mpeg']['video']['raw']['matrix_coefficients'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for matrix_coefficients } $info['mpeg']['video']['raw']['display_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_horizontal_size $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation. $info['mpeg']['video']['raw']['display_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_vertical_size $info['mpeg']['video']['video_format'] = self::videoFormatTextLookup($info['mpeg']['video']['raw']['video_format']); break; case 3: // 0011 Quant Matrix Extension ID break; case 5: // 0101 Sequence Scalable Extension ID $info['mpeg']['video']['raw']['scalable_mode'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for scalable_mode $info['mpeg']['video']['raw']['layer_id'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for layer_id if ($info['mpeg']['video']['raw']['scalable_mode'] == 1) { // "spatial scalability" $info['mpeg']['video']['raw']['lower_layer_prediction_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_horizontal_size $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation. $info['mpeg']['video']['raw']['lower_layer_prediction_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_vertical_size $info['mpeg']['video']['raw']['horizontal_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_m $info['mpeg']['video']['raw']['horizontal_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_n $info['mpeg']['video']['raw']['vertical_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_m $info['mpeg']['video']['raw']['vertical_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_n } elseif ($info['mpeg']['video']['raw']['scalable_mode'] == 3) { // "temporal scalability" $info['mpeg']['video']['raw']['picture_mux_enable'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: picture_mux_enable if ($info['mpeg']['video']['raw']['picture_mux_enable']) { $info['mpeg']['video']['raw']['mux_to_progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: mux_to_progressive_sequence } $info['mpeg']['video']['raw']['picture_mux_order'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_order $info['mpeg']['video']['raw']['picture_mux_factor'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_factor } $info['mpeg']['video']['scalable_mode'] = self::scalableModeTextLookup($info['mpeg']['video']['raw']['scalable_mode']); break; case 7: // 0111 Picture Display Extension ID break; case 8: // 1000 Picture Coding Extension ID $info['mpeg']['video']['raw']['f_code_00'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][0] (forward horizontal) $info['mpeg']['video']['raw']['f_code_01'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][1] (forward vertical) $info['mpeg']['video']['raw']['f_code_10'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][0] (backward horizontal) $info['mpeg']['video']['raw']['f_code_11'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][1] (backward vertical) $info['mpeg']['video']['raw']['intra_dc_precision'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for intra_dc_precision $info['mpeg']['video']['raw']['picture_structure'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for picture_structure $info['mpeg']['video']['raw']['top_field_first'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: top_field_first $info['mpeg']['video']['raw']['frame_pred_frame_dct'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: frame_pred_frame_dct $info['mpeg']['video']['raw']['concealment_motion_vectors'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: concealment_motion_vectors $info['mpeg']['video']['raw']['q_scale_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: q_scale_type $info['mpeg']['video']['raw']['intra_vlc_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: intra_vlc_format $info['mpeg']['video']['raw']['alternate_scan'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: alternate_scan $info['mpeg']['video']['raw']['repeat_first_field'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: repeat_first_field $info['mpeg']['video']['raw']['chroma_420_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: chroma_420_type $info['mpeg']['video']['raw']['progressive_frame'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_frame $info['mpeg']['video']['raw']['composite_display_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: composite_display_flag if ($info['mpeg']['video']['raw']['composite_display_flag']) { $info['mpeg']['video']['raw']['v_axis'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: v_axis $info['mpeg']['video']['raw']['field_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for field_sequence $info['mpeg']['video']['raw']['sub_carrier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: sub_carrier $info['mpeg']['video']['raw']['burst_amplitude'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 7); // 7 bits for burst_amplitude $info['mpeg']['video']['raw']['sub_carrier_phase'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for sub_carrier_phase } $info['mpeg']['video']['intra_dc_precision_bits'] = $info['mpeg']['video']['raw']['intra_dc_precision'] + 8; $info['mpeg']['video']['picture_structure'] = self::pictureStructureTextLookup($info['mpeg']['video']['raw']['picture_structure']); break; case 9: // 1001 Picture Spatial Scalable Extension ID break; case 10: // 1010 Picture Temporal Scalable Extension ID break; default: $this->warning('Unexpected $info[mpeg][video][raw][extension_start_code_identifier] value of ' . $info['mpeg']['video']['raw']['extension_start_code_identifier']); break; } break; case 0xb8: // group_of_pictures_header $GOPcounter++; if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') { $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4)); // 27 bits needed for group_of_pictures_header $bitstreamoffset = 0; $GOPheader = array(); $GOPheader['byte_offset'] = $MPEGstreamBaseOffset + $StartCodeOffset; $GOPheader['drop_frame_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: drop_frame_flag $GOPheader['time_code_hours'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for time_code_hours $GOPheader['time_code_minutes'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_minutes $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation. $GOPheader['time_code_seconds'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_seconds $GOPheader['time_code_pictures'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_pictures $GOPheader['closed_gop'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: closed_gop $GOPheader['broken_link'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: broken_link $time_code_separator = $GOPheader['drop_frame_flag'] ? ';' : ':'; // While non-drop time code is displayed with colons separating the digit pairs—"HH:MM:SS:FF"—drop frame is usually represented with a semi-colon (;) or period (.) as the divider between all the digit pairs—"HH;MM;SS;FF", "HH.MM.SS.FF" $GOPheader['time_code'] = sprintf('%02d' . $time_code_separator . '%02d' . $time_code_separator . '%02d' . $time_code_separator . '%02d', $GOPheader['time_code_hours'], $GOPheader['time_code_minutes'], $GOPheader['time_code_seconds'], $GOPheader['time_code_pictures']); $info['mpeg']['group_of_pictures'][] = $GOPheader; } break; case 0xc0: // audio stream // audio stream case 0xc1: // audio stream // audio stream case 0xc2: // audio stream // audio stream case 0xc3: // audio stream // audio stream case 0xc4: // audio stream // audio stream case 0xc5: // audio stream // audio stream case 0xc6: // audio stream // audio stream case 0xc7: // audio stream // audio stream case 0xc8: // audio stream // audio stream case 0xc9: // audio stream // audio stream case 0xca: // audio stream // audio stream case 0xcb: // audio stream // audio stream case 0xcc: // audio stream // audio stream case 0xcd: // audio stream // audio stream case 0xce: // audio stream // audio stream case 0xcf: // audio stream // audio stream case 0xd0: // audio stream // audio stream case 0xd1: // audio stream // audio stream case 0xd2: // audio stream // audio stream case 0xd3: // audio stream // audio stream case 0xd4: // audio stream // audio stream case 0xd5: // audio stream // audio stream case 0xd6: // audio stream // audio stream case 0xd7: // audio stream // audio stream case 0xd8: // audio stream // audio stream case 0xd9: // audio stream // audio stream case 0xda: // audio stream // audio stream case 0xdb: // audio stream // audio stream case 0xdc: // audio stream // audio stream case 0xdd: // audio stream // audio stream case 0xde: // audio stream // audio stream case 0xdf: // audio stream //case 0xE0: // video stream //case 0xE1: // video stream //case 0xE2: // video stream //case 0xE3: // video stream //case 0xE4: // video stream //case 0xE5: // video stream //case 0xE6: // video stream //case 0xE7: // video stream //case 0xE8: // video stream //case 0xE9: // video stream //case 0xEA: // video stream //case 0xEB: // video stream //case 0xEC: // video stream //case 0xED: // video stream //case 0xEE: // video stream //case 0xEF: // video stream if (isset($ParsedAVchannels[$StartCodeValue])) { break; } $ParsedAVchannels[$StartCodeValue] = $StartCodeValue; // http://en.wikipedia.org/wiki/Packetized_elementary_stream // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html /* $PackedElementaryStream = array(); if ($StartCodeValue >= 0xE0) { $PackedElementaryStream['stream_type'] = 'video'; $PackedElementaryStream['stream_id'] = $StartCodeValue - 0xE0; } else { $PackedElementaryStream['stream_type'] = 'audio'; $PackedElementaryStream['stream_id'] = $StartCodeValue - 0xC0; } $PackedElementaryStream['packet_length'] = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $StartCodeOffset + 4, 2)); $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 6, 3)); // more may be needed below $bitstreamoffset = 0; $PackedElementaryStream['marker_bits'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for marker_bits -- should be "10" = 2 echo 'marker_bits = '.$PackedElementaryStream['marker_bits'].'<br>'; $PackedElementaryStream['scrambling_control'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for scrambling_control -- 00 implies not scrambled $PackedElementaryStream['priority'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: priority $PackedElementaryStream['data_alignment_indicator'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: data_alignment_indicator -- 1 indicates that the PES packet header is immediately followed by the video start code or audio syncword $PackedElementaryStream['copyright'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: copyright -- 1 implies copyrighted $PackedElementaryStream['original_or_copy'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: original_or_copy -- 1 implies original $PackedElementaryStream['pts_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: pts_flag -- Presentation Time Stamp $PackedElementaryStream['dts_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: dts_flag -- Decode Time Stamp $PackedElementaryStream['escr_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: escr_flag -- Elementary Stream Clock Reference $PackedElementaryStream['es_rate_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: es_rate_flag -- Elementary Stream [data] Rate $PackedElementaryStream['dsm_trick_mode_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: dsm_trick_mode_flag -- DSM trick mode - not used by DVD $PackedElementaryStream['additional_copy_info_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: additional_copy_info_flag $PackedElementaryStream['crc_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: crc_flag $PackedElementaryStream['extension_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: extension_flag $PackedElementaryStream['pes_remain_header_length'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 1 bit flag: priority $additional_header_bytes = 0; $additional_header_bytes += ($PackedElementaryStream['pts_flag'] ? 5 : 0); $additional_header_bytes += ($PackedElementaryStream['dts_flag'] ? 5 : 0); $additional_header_bytes += ($PackedElementaryStream['escr_flag'] ? 6 : 0); $additional_header_bytes += ($PackedElementaryStream['es_rate_flag'] ? 3 : 0); $additional_header_bytes += ($PackedElementaryStream['additional_copy_info_flag'] ? 1 : 0); $additional_header_bytes += ($PackedElementaryStream['crc_flag'] ? 2 : 0); $additional_header_bytes += ($PackedElementaryStream['extension_flag'] ? 1 : 0); $PackedElementaryStream['additional_header_bytes'] = $additional_header_bytes; $bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 9, $additional_header_bytes)); $info['mpeg']['packed_elementary_streams'][$PackedElementaryStream['stream_type']][$PackedElementaryStream['stream_id']][] = $PackedElementaryStream; */ $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info = $info; $getid3_mp3 = new getid3_mp3($getid3_temp); for ($i = 0; $i <= 7; $i++) { // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after // I have no idea why or what the difference is, so this is a stupid hack. // If anybody has any better idea of what's going on, please let me know - info@getid3.org $getid3_temp->info = $info; // only overwrite real data if valid header found //echo 'audio at? '.($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i).'<br>'; if ($getid3_mp3->decodeMPEGaudioHeader($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i, $getid3_temp->info, false)) { //echo 'yes!<br>'; $info = $getid3_temp->info; $info['audio']['bitrate_mode'] = 'cbr'; $info['audio']['lossless'] = false; break; } } unset($getid3_temp, $getid3_mp3); break; case 0xbc: // Program Stream Map // Program Stream Map case 0xbd: // Private stream 1 (non MPEG audio, subpictures) // Private stream 1 (non MPEG audio, subpictures) case 0xbe: // Padding stream // Padding stream case 0xbf: // Private stream 2 (navigation data) // Private stream 2 (navigation data) case 0xf0: // ECM stream // ECM stream case 0xf1: // EMM stream // EMM stream case 0xf2: // DSM-CC stream // DSM-CC stream case 0xf3: // ISO/IEC_13522_stream // ISO/IEC_13522_stream case 0xf4: // ITU-I Rec. H.222.1 type A // ITU-I Rec. H.222.1 type A case 0xf5: // ITU-I Rec. H.222.1 type B // ITU-I Rec. H.222.1 type B case 0xf6: // ITU-I Rec. H.222.1 type C // ITU-I Rec. H.222.1 type C case 0xf7: // ITU-I Rec. H.222.1 type D // ITU-I Rec. H.222.1 type D case 0xf8: // ITU-I Rec. H.222.1 type E // ITU-I Rec. H.222.1 type E case 0xf9: // ancilliary stream // ancilliary stream case 0xfa: // ISO/IEC 14496-1 SL-packtized stream // ISO/IEC 14496-1 SL-packtized stream case 0xfb: // ISO/IEC 14496-1 FlexMux stream // ISO/IEC 14496-1 FlexMux stream case 0xfc: // metadata stream // metadata stream case 0xfd: // extended stream ID // extended stream ID case 0xfe: // reserved data stream // reserved data stream case 0xff: // program stream directory // ignore break; default: // ignore break; } } while (true); // // Temporary hack to account for interleaving overhead: // if (!empty($info['video']['bitrate']) && !empty($info['audio']['bitrate'])) { // $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['video']['bitrate'] + $info['audio']['bitrate']); // // // Interleaved MPEG audio/video files have a certain amount of overhead that varies // // by both video and audio bitrates, and not in any sensible, linear/logarithmic pattern // // Use interpolated lookup tables to approximately guess how much is overhead, because // // playtime is calculated as filesize / total-bitrate // $info['playtime_seconds'] *= self::systemNonOverheadPercentage($info['video']['bitrate'], $info['audio']['bitrate']); // // //switch ($info['video']['bitrate']) { // // case('5000000'): // // $multiplier = 0.93292642112380355828048824319889; // // break; // // case('5500000'): // // $multiplier = 0.93582895375200989965359777343219; // // break; // // case('6000000'): // // $multiplier = 0.93796247714820932532911373859139; // // break; // // case('7000000'): // // $multiplier = 0.9413264083635103463010117778776; // // break; // // default: // // $multiplier = 1; // // break; // //} // //$info['playtime_seconds'] *= $multiplier; // //$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.'; // if ($info['video']['bitrate'] < 50000) { // $this->warning('Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.'); // } // } // /* $time_prev = 0; $byte_prev = 0; $vbr_bitrates = array(); foreach ($info['mpeg']['group_of_pictures'] as $gopkey => $gopdata) { $time_this = ($gopdata['time_code_hours'] * 3600) + ($gopdata['time_code_minutes'] * 60) + $gopdata['time_code_seconds'] + ($gopdata['time_code_seconds'] / 30); $byte_this = $gopdata['byte_offset']; if ($gopkey > 0) { if ($time_this > $time_prev) { $bytedelta = $byte_this - $byte_prev; $timedelta = $time_this - $time_prev; $this_bitrate = ($bytedelta * 8) / $timedelta; echo $gopkey.': ('.number_format($time_prev, 2).'-'.number_format($time_this, 2).') '.number_format($bytedelta).' bytes over '.number_format($timedelta, 3).' seconds = '.number_format($this_bitrate / 1000, 2).'kbps<br>'; $time_prev = $time_this; $byte_prev = $byte_this; $vbr_bitrates[] = $this_bitrate; } } } echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($vbr_bitrates), 1).'<br>'; */ //echo '<pre>'.print_r($FramesByGOP, true).'</pre>'; if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') { $last_GOP_id = max(array_keys($FramesByGOP)); $frames_in_last_GOP = count($FramesByGOP[$last_GOP_id]); $gopdata =& $info['mpeg']['group_of_pictures'][$last_GOP_id]; $info['playtime_seconds'] = $gopdata['time_code_hours'] * 3600 + $gopdata['time_code_minutes'] * 60 + $gopdata['time_code_seconds'] + ($gopdata['time_code_pictures'] + $frames_in_last_GOP + 1) / $info['mpeg']['video']['frame_rate']; if (!isset($info['video']['bitrate'])) { $overall_bitrate = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; $info['video']['bitrate'] = $overall_bitrate - (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0); } unset($info['mpeg']['group_of_pictures']); } return true; }
public function Analyze() { $getid3 = $this->getid3; $getid3->info['dts'] = array(); $info_dts =& $getid3->info['dts']; $getid3->info['fileformat'] = 'dts'; fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $header = fread($getid3->fp, 16); $fhBS = getid3_lib::BigEndian2Bin(substr($header, 4, 12)); $bs_offset = 0; $info_dts['raw']['frame_type'] = bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['raw']['deficit_samples'] = bindec(substr($fhBS, $bs_offset, 5)); $bs_offset += 5; $info_dts['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bs_offset, 7)); $bs_offset += 7; $info_dts['raw']['frame_byte_size'] = bindec(substr($fhBS, $bs_offset, 14)); $bs_offset += 14; $info_dts['raw']['channel_arrangement'] = bindec(substr($fhBS, $bs_offset, 6)); $bs_offset += 6; $info_dts['raw']['sample_frequency'] = bindec(substr($fhBS, $bs_offset, 4)); $bs_offset += 4; $info_dts['raw']['bitrate'] = bindec(substr($fhBS, $bs_offset, 5)); $bs_offset += 5; $info_dts['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['raw']['extension_audio'] = bindec(substr($fhBS, $bs_offset, 3)); $bs_offset += 3; $info_dts['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['raw']['lfe_effects'] = bindec(substr($fhBS, $bs_offset, 2)); $bs_offset += 2; $info_dts['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; if ($info_dts['flags']['crc_present']) { $info_dts['raw']['crc16'] = bindec(substr($fhBS, $bs_offset, 16)); $bs_offset += 16; } $info_dts['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bs_offset, 4)); $bs_offset += 4; $info_dts['raw']['copy_history'] = bindec(substr($fhBS, $bs_offset, 2)); $bs_offset += 2; $info_dts['raw']['bits_per_sample'] = bindec(substr($fhBS, $bs_offset, 2)); $bs_offset += 2; $info_dts['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $info_dts['raw']['dialog_normalization'] = bindec(substr($fhBS, $bs_offset, 4)); $bs_offset += 4; $info_dts['bitrate'] = $this->DTSbitrateLookup($info_dts['raw']['bitrate']); $info_dts['bits_per_sample'] = $this->DTSbitPerSampleLookup($info_dts['raw']['bits_per_sample']); $info_dts['sample_rate'] = $this->DTSsampleRateLookup($info_dts['raw']['sample_frequency']); $info_dts['dialog_normalization'] = $this->DTSdialogNormalization($info_dts['raw']['dialog_normalization'], $info_dts['raw']['encoder_soft_version']); $info_dts['flags']['lossless'] = $info_dts['raw']['bitrate'] == 31 ? true : false; $info_dts['bitrate_mode'] = $info_dts['raw']['bitrate'] == 30 ? 'vbr' : 'cbr'; $info_dts['channels'] = $this->DTSnumChannelsLookup($info_dts['raw']['channel_arrangement']); $info_dts['channel_arrangement'] = $this->DTSchannelArrangementLookup($info_dts['raw']['channel_arrangement']); $getid3->info['audio']['dataformat'] = 'dts'; $getid3->info['audio']['lossless'] = $info_dts['flags']['lossless']; $getid3->info['audio']['bitrate_mode'] = $info_dts['bitrate_mode']; $getid3->info['audio']['bits_per_sample'] = $info_dts['bits_per_sample']; $getid3->info['audio']['sample_rate'] = $info_dts['sample_rate']; $getid3->info['audio']['channels'] = $info_dts['channels']; $getid3->info['audio']['bitrate'] = $info_dts['bitrate']; $getid3->info['playtime_seconds'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) / ($info_dts['bitrate'] / 8); return true; }
private function parseSTREAMINFO($BlockData) { $info =& $this->getid3->info; $info['flac']['STREAMINFO'] = array(); $streaminfo =& $info['flac']['STREAMINFO']; $streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2)); $streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2)); $streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3)); $streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3)); $SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8)); $streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20)); $streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1; $streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1; $streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36)); $streaminfo['audio_signature'] = substr($BlockData, 18, 16); if (!empty($streaminfo['sample_rate'])) { $info['audio']['bitrate_mode'] = 'vbr'; $info['audio']['sample_rate'] = $streaminfo['sample_rate']; $info['audio']['channels'] = $streaminfo['channels']; $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample']; $info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate']; if ($info['playtime_seconds'] > 0) { if (!$this->isDependencyFor('matroska')) { $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; } else { $this->warning('Cannot determine audio bitrate because total stream size is unknown'); } } } else { return $this->error('Corrupt METAdata block: STREAMINFO'); } return true; }
public function Analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'dts'; $this->fseek($info['avdataoffset']); $DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes // check syncword $sync = substr($DTSheader, 0, 4); if (($encoding = array_search($sync, self::$syncwords)) !== false) { $info['dts']['raw']['magic'] = $sync; $this->readBinDataOffset = 32; } elseif ($this->isDependencyFor('matroska')) { // Matroska contains DTS without syncword encoded as raw big-endian format $encoding = 0; $this->readBinDataOffset = 0; } else { unset($info['fileformat']); return $this->error('Expecting "' . implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)) . '" at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes($sync) . '"'); } // decode header $fhBS = ''; for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) { switch ($encoding) { case 0: // raw big-endian $fhBS .= getid3_lib::BigEndian2Bin(substr($DTSheader, $word_offset, 2)); break; case 1: // raw little-endian $fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))); break; case 2: // 14-bit big-endian $fhBS .= substr(getid3_lib::BigEndian2Bin(substr($DTSheader, $word_offset, 2)), 2, 14); break; case 3: // 14-bit little-endian $fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14); break; } } $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1); $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5); $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7); $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14); $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6); $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4); $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5); $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3); $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2); $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1); if ($info['dts']['flags']['crc_present']) { $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16); } $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4); $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2); $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2); $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1); $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4); $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']); $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']); $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']); $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']); $info['dts']['flags']['lossless'] = $info['dts']['raw']['bitrate'] == 31 ? true : false; $info['dts']['bitrate_mode'] = $info['dts']['raw']['bitrate'] == 30 ? 'vbr' : 'cbr'; $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']); $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']); $info['audio']['dataformat'] = 'dts'; $info['audio']['lossless'] = $info['dts']['flags']['lossless']; $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode']; $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample']; $info['audio']['sample_rate'] = $info['dts']['sample_rate']; $info['audio']['channels'] = $info['dts']['channels']; $info['audio']['bitrate'] = $info['dts']['bitrate']; if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) { $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8); if ($encoding == 2 || $encoding == 3) { // 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate $info['playtime_seconds'] *= 14 / 16; } } return true; }
static function FixedPoint2_30($rawdata) { $binarystring = getid3_lib::BigEndian2Bin($rawdata); return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (double) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / 1073741824); }
public function Analyze() { $getid3 = $this->getid3; // http://faac.sourceforge.net/wiki/index.php?page=ADIF // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf // adif_header() { // adif_id 32 // copyright_id_present 1 // if( copyright_id_present ) // copyright_id 72 // original_copy 1 // home 1 // bitstream_type 1 // bitrate 23 // num_program_config_elements 4 // for (i = 0; i < num_program_config_elements + 1; i++ ) { // if( bitstream_type == '0' ) // adif_buffer_fullness 20 // program_config_element() // } // } $getid3->info['fileformat'] = 'aac'; $getid3->info['audio']['dataformat'] = 'aac'; $getid3->info['audio']['lossless'] = false; $getid3->info['aac']['header'] = array(); $info_aac =& $getid3->info['aac']; $info_aac_header =& $info_aac['header']; fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $aac_header_bitstream = getid3_lib::BigEndian2Bin(fread($getid3->fp, 1024)); $info_aac['header_type'] = 'ADIF'; $info_aac_header['mpeg_version'] = 4; $bit_offset = 32; $info_aac_header['copyright'] = $aac_header_bitstream[$bit_offset++] == '1'; if ($info_aac_header['copyright']) { $info_aac_header['copyright_id'] = getid3_aac_adif::Bin2String(substr($aac_header_bitstream, $bit_offset, 72)); $bit_offset += 72; } $info_aac_header['original_copy'] = $aac_header_bitstream[$bit_offset++] == '1'; $info_aac_header['home'] = $aac_header_bitstream[$bit_offset++] == '1'; $info_aac_header['is_vbr'] = $aac_header_bitstream[$bit_offset++] == '1'; if ($info_aac_header['is_vbr']) { $getid3->info['audio']['bitrate_mode'] = 'vbr'; $info_aac_header['bitrate_max'] = bindec(substr($aac_header_bitstream, $bit_offset, 23)); $bit_offset += 23; } else { $getid3->info['audio']['bitrate_mode'] = 'cbr'; $info_aac_header['bitrate'] = bindec(substr($aac_header_bitstream, $bit_offset, 23)); $bit_offset += 23; $getid3->info['audio']['bitrate'] = $info_aac_header['bitrate']; } $info_aac_header['num_program_configs'] = 1 + bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; for ($i = 0; $i < $info_aac_header['num_program_configs']; $i++) { // http://www.audiocoding.com/wiki/index.php?page=program_config_element // buffer_fullness 20 // element_instance_tag 4 // object_type 2 // sampling_frequency_index 4 // num_front_channel_elements 4 // num_side_channel_elements 4 // num_back_channel_elements 4 // num_lfe_channel_elements 2 // num_assoc_data_elements 3 // num_valid_cc_elements 4 // mono_mixdown_present 1 // mono_mixdown_element_number 4 if mono_mixdown_present == 1 // stereo_mixdown_present 1 // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1 // matrix_mixdown_idx_present 1 // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1 // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1 // for (i = 0; i < num_front_channel_elements; i++) { // front_element_is_cpe[i] 1 // front_element_tag_select[i] 4 // } // for (i = 0; i < num_side_channel_elements; i++) { // side_element_is_cpe[i] 1 // side_element_tag_select[i] 4 // } // for (i = 0; i < num_back_channel_elements; i++) { // back_element_is_cpe[i] 1 // back_element_tag_select[i] 4 // } // for (i = 0; i < num_lfe_channel_elements; i++) { // lfe_element_tag_select[i] 4 // } // for (i = 0; i < num_assoc_data_elements; i++) { // assoc_data_element_tag_select[i] 4 // } // for (i = 0; i < num_valid_cc_elements; i++) { // cc_element_is_ind_sw[i] 1 // valid_cc_element_tag_select[i] 4 // } // byte_alignment() VAR // comment_field_bytes 8 // for (i = 0; i < comment_field_bytes; i++) { // comment_field_data[i] 8 // } $info_aac['program_configs'][$i] = array(); $info_aac_program_configs_i =& $info_aac['program_configs'][$i]; if (!$info_aac_header['is_vbr']) { $info_aac_program_configs_i['buffer_fullness'] = bindec(substr($aac_header_bitstream, $bit_offset, 20)); $bit_offset += 20; } $info_aac_program_configs_i['element_instance_tag'] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $info_aac_program_configs_i['object_type'] = bindec(substr($aac_header_bitstream, $bit_offset + 4, 2)); $info_aac_program_configs_i['sampling_frequency_index'] = bindec(substr($aac_header_bitstream, $bit_offset + 6, 4)); $info_aac_program_configs_i['num_front_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 10, 4)); $info_aac_program_configs_i['num_side_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 14, 4)); $info_aac_program_configs_i['num_back_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 18, 4)); $info_aac_program_configs_i['num_lfe_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 22, 2)); $info_aac_program_configs_i['num_assoc_data_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 24, 3)); $info_aac_program_configs_i['num_valid_cc_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 27, 4)); $bit_offset += 31; $info_aac_program_configs_i['mono_mixdown_present'] = $aac_header_bitstream[$bit_offset++] == 1; if ($info_aac_program_configs_i['mono_mixdown_present']) { $info_aac_program_configs_i['mono_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } $info_aac_program_configs_i['stereo_mixdown_present'] = $aac_header_bitstream[$bit_offset++] == 1; if ($info_aac_program_configs_i['stereo_mixdown_present']) { $info_aac_program_configs_i['stereo_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } $info_aac_program_configs_i['matrix_mixdown_idx_present'] = $aac_header_bitstream[$bit_offset++] == 1; if ($info_aac_program_configs_i['matrix_mixdown_idx_present']) { $info_aac_program_configs_i['matrix_mixdown_idx'] = bindec(substr($aac_header_bitstream, $bit_offset, 2)); $bit_offset += 2; $info_aac_program_configs_i['pseudo_surround_enable'] = $aac_header_bitstream[$bit_offset++] == 1; } for ($j = 0; $j < $info_aac_program_configs_i['num_front_channel_elements']; $j++) { $info_aac_program_configs_i['front_element_is_cpe'][$j] = $aac_header_bitstream[$bit_offset++] == 1; $info_aac_program_configs_i['front_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } for ($j = 0; $j < $info_aac_program_configs_i['num_side_channel_elements']; $j++) { $info_aac_program_configs_i['side_element_is_cpe'][$j] = $aac_header_bitstream[$bit_offset++] == 1; $info_aac_program_configs_i['side_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } for ($j = 0; $j < $info_aac_program_configs_i['num_back_channel_elements']; $j++) { $info_aac_program_configs_i['back_element_is_cpe'][$j] = $aac_header_bitstream[$bit_offset++] == 1; $info_aac_program_configs_i['back_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } for ($j = 0; $j < $info_aac_program_configs_i['num_lfe_channel_elements']; $j++) { $info_aac_program_configs_i['lfe_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } for ($j = 0; $j < $info_aac_program_configs_i['num_assoc_data_elements']; $j++) { $info_aac_program_configs_i['assoc_data_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } for ($j = 0; $j < $info_aac_program_configs_i['num_valid_cc_elements']; $j++) { $info_aac_program_configs_i['cc_element_is_ind_sw'][$j] = $aac_header_bitstream[$bit_offset++] == 1; $info_aac_program_configs_i['valid_cc_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); $bit_offset += 4; } $bit_offset = ceil($bit_offset / 8) * 8; $info_aac_program_configs_i['comment_field_bytes'] = bindec(substr($aac_header_bitstream, $bit_offset, 8)); $bit_offset += 8; $info_aac_program_configs_i['comment_field'] = getid3_aac_adif::Bin2String(substr($aac_header_bitstream, $bit_offset, 8 * $info_aac_program_configs_i['comment_field_bytes'])); $bit_offset += 8 * $info_aac_program_configs_i['comment_field_bytes']; $info_aac_header['profile_text'] = getid3_aac_adif::AACprofileLookup($info_aac_program_configs_i['object_type'], $info_aac_header['mpeg_version']); $info_aac_program_configs_i['sampling_frequency'] = $getid3->info['audio']['sample_rate'] = getid3_aac_adif::AACsampleRateLookup($info_aac_program_configs_i['sampling_frequency_index']); $getid3->info['audio']['channels'] = getid3_aac_adif::AACchannelCountCalculate($info_aac_program_configs_i); if ($info_aac_program_configs_i['comment_field']) { $info_aac['comments'][] = $info_aac_program_configs_i['comment_field']; } } $getid3->info['playtime_seconds'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / $getid3->info['audio']['bitrate']; $getid3->info['audio']['encoder_options'] = $info_aac['header_type'] . ' ' . $info_aac_header['profile_text']; return true; }