Move ID3v2 flags parsing into method
Signed-off-by: Markus Birth <markus@birth-online.de>
This commit is contained in:
+79
-82
@@ -78,6 +78,15 @@ class Mp3Info
|
||||
self::MPEG_2 => [13, 21],
|
||||
self::MPEG_25 => [13, 21],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $_id3v2HeaderFlags = [
|
||||
2 => ['unsynchronisation', 'compression'],
|
||||
3 => ['unsynchronisation', 'extended_header', 'experimental_indicator'],
|
||||
4 => ['unsynchronisation', 'extended_header', 'experimental_indicator', 'footer_present'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int Limit in bytes for seeking a mpeg header in file
|
||||
@@ -190,19 +199,20 @@ class Mp3Info
|
||||
/**
|
||||
* @var int Major version of id3v2 tag (if id3v2 present) (2 or 3 or 4)
|
||||
*/
|
||||
public $id3v2MajorVersion;
|
||||
public $id3v2MajorVersion; // @deprecated
|
||||
public $id3v2Version;
|
||||
|
||||
/**
|
||||
* @var int Minor version of id3v2 tag (if id3v2 present)
|
||||
*/
|
||||
public $id3v2MinorVersion;
|
||||
public $id3v2MinorVersion; // @deprecated
|
||||
public $id3v2Revision;
|
||||
|
||||
/**
|
||||
* @var array List of id3v2 header flags (if id3v2 present)
|
||||
*/
|
||||
public $id3v2Flags = [];
|
||||
public $id3v2HeaderFlags = [];
|
||||
|
||||
/**
|
||||
* @var array List of id3v2 tags flags (if id3v2 present)
|
||||
@@ -307,18 +317,21 @@ class Mp3Info
|
||||
/** @var int Size of audio data (exclude tags size) */
|
||||
$audioSize = $this->fileObj->getFileSize();
|
||||
|
||||
// parse tags
|
||||
// try ID3v2 parsing
|
||||
$audioSize -= ($this->_id3Size = $this->_parseId3v2Header(!($mode & self::TAGS)));
|
||||
|
||||
/*/ parse tags
|
||||
if ($this->fileObj->getBytes(3) == self::TAG2_SYNC) {
|
||||
if ($mode & self::TAGS) {
|
||||
$audioSize -= ($this->_id3Size = $this->readId3v2Body());
|
||||
} else {
|
||||
$this->fileObj->seekForward(2); // 2 bytes of tag version
|
||||
$this->fileObj->seekForward(2); // 2 bytes of tag version+revision
|
||||
$this->fileObj->seekForward(1); // 1 byte of tag flags
|
||||
$size = $this->getSynchsafeSize($this->fileObj->getBytes(4));
|
||||
$size += 10; // add header size
|
||||
$audioSize -= ($this->_id3Size = $size);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
$this->fileObj->seekTo($this->fileObj->getFileSize() - 128);
|
||||
if ($this->fileObj->getBytes(3) == self::TAG1_SYNC) {
|
||||
@@ -486,60 +499,60 @@ class Mp3Info
|
||||
$this->tags1['genre'] = '(' . ord($this->fileObj->getBytes(1)) . ')';
|
||||
return 128;
|
||||
}
|
||||
|
||||
private function _parseId3v2Flags(int $version, int $flags): array
|
||||
{
|
||||
$result = array();
|
||||
if ($version >= 2) {
|
||||
// parse id3v2.2.0 header flags
|
||||
$result['unsynchronisation'] = ($flags & 128 == 128);
|
||||
$result['compression'] = ($flags & 64 == 64);
|
||||
}
|
||||
|
||||
if ($version >= 3) {
|
||||
// id3v2.3 changes second bit from compression to extended_header
|
||||
$result['extended_header'] = &$result['compression'];
|
||||
unset($result['compression']);
|
||||
// parse additional id3v2.3.0 header flags
|
||||
$result['experimental_indicator'] = ($flags & 32 == 32);
|
||||
|
||||
}
|
||||
|
||||
if ($version >= 4) {
|
||||
// parse additional id3v2.4.0 header flags
|
||||
$result['footer_present'] = ($flags & 16 == 16);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads id3v2 tag.
|
||||
* -----------------------------------
|
||||
* Overall tag header structure (10 bytes)
|
||||
* ID3v2/file identifier "ID3" (3 bytes)
|
||||
* ID3v2 version (2 bytes)
|
||||
* ID3v2 flags (1 byte)
|
||||
* ID3v2 size 4 * %0xxxxxxx (4 bytes)
|
||||
* -----------------------------------
|
||||
* id3v2.2.0 tag header (10 bytes)
|
||||
* ID3/file identifier "ID3" (3 bytes)
|
||||
* ID3 version $02 00 (2 bytes)
|
||||
* ID3 flags %xx000000 (1 byte)
|
||||
* ID3 size 4 * %0xxxxxxx (4 bytes)
|
||||
* Flags:
|
||||
* x (bit 7) - unsynchronisation
|
||||
* x (bit 6) - compression
|
||||
* -----------------------------------
|
||||
* id3v2.3.0 tag header (10 bytes)
|
||||
* ID3v2/file identifier "ID3" (3 bytes)
|
||||
* ID3v2 version $03 00 (2 bytes)
|
||||
* ID3v2 flags %abc00000 (1 byte)
|
||||
* ID3v2 size 4 * %0xxxxxxx (4 bytes)
|
||||
* Flags:
|
||||
* a - Unsynchronisation
|
||||
* b - Extended header
|
||||
* c - Experimental indicator
|
||||
* Extended header structure (10 bytes)
|
||||
* Extended header size $xx xx xx xx
|
||||
* Extended Flags $xx xx
|
||||
* Size of padding $xx xx xx xx
|
||||
* Extended flags:
|
||||
* %x0000000 00000000
|
||||
* x - CRC data present
|
||||
* -----------------------------------
|
||||
* id3v2.4.0 tag header (10 bytes)
|
||||
* ID3v2/file identifier "ID3" (3 bytes)
|
||||
* ID3v2 version $04 00 (2 bytes)
|
||||
* ID3v2 flags %abcd0000 (1 byte)
|
||||
* ID3v2 size 4 * %0xxxxxxx (4 bytes)
|
||||
* Flags:
|
||||
* a - Unsynchronisation
|
||||
* b - Extended header
|
||||
* c - Experimental indicator
|
||||
* d - Footer present
|
||||
* @param resource $fp
|
||||
* @return int Returns length of id3v2 tag.
|
||||
* @throws \Exception
|
||||
* Reads ID3v2 header and returns the ID3v2 tag size. 0 if no header was found.
|
||||
*
|
||||
* @param bool $sizeOnly Only parse ID3v2 size
|
||||
*
|
||||
* @return int Size of ID3v2 struct
|
||||
*/
|
||||
private function readId3v2Body()
|
||||
private function _parseId3v2Header(bool $sizeOnly = true): int
|
||||
{
|
||||
// check for "ID3" marker
|
||||
if ($this->fileObj->getBytes(3) != self::TAG2_SYNC) {
|
||||
// No ID3V2 tag found
|
||||
return 0;
|
||||
}
|
||||
|
||||
$headerSize = 10;
|
||||
$footerSize = 0;
|
||||
|
||||
// read the rest of the id3v2 header
|
||||
$raw = $this->fileObj->getBytes(3);
|
||||
$size = $this->getSynchsafeSize($this->fileObj->getBytes(4));
|
||||
if ($sizeOnly) {
|
||||
// just return the size
|
||||
// NOTE: $footerSize unclear at this point, might be inaccurate
|
||||
return $headerSize + $footerSize + $size;
|
||||
}
|
||||
|
||||
// parse version and flags
|
||||
$data = unpack('Cversion/Crevision/Cflags', $raw);
|
||||
$this->id3v2Version = $data['version'];
|
||||
$this->id3v2Revision = $data['revision'];
|
||||
@@ -547,39 +560,21 @@ class Mp3Info
|
||||
$this->id3v2MajorVersion = $data['version'];
|
||||
$this->id3v2MinorVersion = $data['revision'];
|
||||
|
||||
$this->id3v2Flags = array();
|
||||
$flagsList = self::$_id3v2HeaderFlags[$this->id3v2Version];
|
||||
// TODO: CONTINUE HERE - PARSE DIFFERENTLY
|
||||
$this->id3v2Flags = $this->_parseId3v2Flags($this->id3v2Version, $data['flags']);
|
||||
|
||||
if ($this->id3v2Version >= 2) {
|
||||
// parse id3v2.2.0 header flags
|
||||
$this->id3v2Flags['unsynchronisation'] = ($data['flags'] & 128 == 128);
|
||||
$this->id3v2Flags['compression'] = ($data['flags'] & 64 == 64);
|
||||
}
|
||||
|
||||
if ($this->id3v2Version >= 3) {
|
||||
// id3v2.3 changes second bit from compression to extended_header
|
||||
$this->id3v2Flags['extended_header'] = &$this->id3v2Flags['compression'];
|
||||
unset($this->id3v2Flags['compression']);
|
||||
// parse additional id3v2.3.0 header flags
|
||||
$this->id3v2Flags['experimental_indicator'] = ($data['flags'] & 32 == 32);
|
||||
|
||||
if ($this->id3v2Flags['extended_header']) {
|
||||
throw new Exception('NEED TO PARSE EXTENDED HEADER!');
|
||||
}
|
||||
if ($this->id3v2Flags['extended_header']) {
|
||||
throw new Exception('NEED TO PARSE EXTENDED HEADER!');
|
||||
}
|
||||
|
||||
if ($this->id3v2Version >= 4) {
|
||||
// parse additional id3v2.4.0 header flags
|
||||
$this->id3v2Flags['footer_present'] = ($data['flags'] & 16 == 16);
|
||||
|
||||
if ($this->id3v2Flags['footer_present']) {
|
||||
// footer is a copy of header - so can be ignored
|
||||
// (10 Bytes not included in $size)
|
||||
//throw new Exception('NEED TO PARSE id3v2.4 FOOTER!');
|
||||
}
|
||||
if ($this->id3v2Flags['footer_present']) {
|
||||
// footer is a copy of header - so can be ignored
|
||||
// (10 Bytes not included in $size)
|
||||
$footerSize = 10;
|
||||
}
|
||||
|
||||
$size = $this->getSynchsafeSize($this->fileObj->getBytes(4));
|
||||
|
||||
// Now parse the frames
|
||||
if ($this->id3v2Version == 2) {
|
||||
// parse id3v2.2.0 body
|
||||
/*throw new \Exception('NEED TO PARSE id3v2.2.0 flags!');*/
|
||||
@@ -591,7 +586,7 @@ class Mp3Info
|
||||
$this->parseId3v24Body(10 + $size);
|
||||
}
|
||||
|
||||
return 10 + $size; // 10 bytes - header, rest - body
|
||||
return $headerSize + $footerSize + $size;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -600,6 +595,7 @@ class Mp3Info
|
||||
*/
|
||||
protected function parseId3v23Body($lastByte)
|
||||
{
|
||||
// TODO: Change $lastByte into $payloadLength so it works independent of starting location
|
||||
while ($this->fileObj->getFilePos() < $lastByte) {
|
||||
$raw = $this->fileObj->getBytes(10);
|
||||
$frame_id = substr($raw, 0, 4);
|
||||
@@ -798,6 +794,7 @@ class Mp3Info
|
||||
*/
|
||||
protected function parseId3v24Body($lastByte)
|
||||
{
|
||||
// TODO: Change $lastByte into $payloadLength so it works independent of starting location
|
||||
while ($this->fileObj->getFilePos() < $lastByte) {
|
||||
$frame_id = $this->fileObj->getBytes(4);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user