Add support for ID3v2.3 unsynchronisation

git-svn-id: http://php-reader.googlecode.com/svn/branches/zend@155 51a70ab9-7547-0410-9469-37e369ee0574
This commit is contained in:
svollbehr
2009-05-31 08:07:58 +00:00
parent 44b19c9e37
commit 752c03858a
2 changed files with 51 additions and 22 deletions

View File

@@ -290,6 +290,10 @@ abstract class Zend_Media_Id3_Frame extends Zend_Media_Id3_Object
$data = $buffer->toString(); $data = $buffer->toString();
$size = $this->_size = strlen($data); $size = $this->_size = strlen($data);
// ID3v2.4.0 supports frame level unsynchronisation. The corresponding
// option is set to true when any of the frames use the
// unsynchronisation scheme. The usage is denoted by
// Zend_Media_Id3_Header flag that is set accordingly upon file write.
if ($this->getOption('version', 4) >= 4) { if ($this->getOption('version', 4) >= 4) {
$data = $this->_encodeUnsynchronisation($data); $data = $this->_encodeUnsynchronisation($data);
if (($dataLength = strlen($data)) != $size) { if (($dataLength = strlen($data)) != $size) {

View File

@@ -43,7 +43,6 @@ require_once 'Zend/Media/Id3/Header.php';
* unique and predefined identifier which allows software to skip unknown * unique and predefined identifier which allows software to skip unknown
* frames. * frames.
* *
* @todo Unsynchronisation not supported for ID3v2.3 tag
* @category Zend * @category Zend
* @package Zend_Media * @package Zend_Media
* @subpackage ID3 * @subpackage ID3
@@ -134,6 +133,9 @@ final class Zend_Media_Id3v2 extends Zend_Media_Id3_Object
} }
$this->_header = new Zend_Media_Id3_Header($this->_reader, $options); $this->_header = new Zend_Media_Id3_Header($this->_reader, $options);
$tagSize = $this->_header->getSize();
if ($this->_header->getVersion() < 3 || if ($this->_header->getVersion() < 3 ||
$this->_header->getVersion() > 4) { $this->_header->getVersion() > 4) {
require_once 'Zend/Media/Id3/Exception.php'; require_once 'Zend/Media/Id3/Exception.php';
@@ -142,9 +144,10 @@ final class Zend_Media_Id3v2 extends Zend_Media_Id3_Object
} }
if ($this->_header->getVersion() < 4 && if ($this->_header->getVersion() < 4 &&
$this->_header->hasFlag(Zend_Media_Id3_Header::UNSYNCHRONISATION)) { $this->_header->hasFlag(Zend_Media_Id3_Header::UNSYNCHRONISATION)) {
require_once 'Zend/Media/Id3/Exception.php'; $data = $this->_reader->read($this->_header->getSize());
throw new Zend_Media_Id3_Exception $this->_reader = new Zend_Io_StringReader
('Unsynchronisation not yet supported for this version of ID3v2 tag'); ($this->_decodeUnsynchronisation($data));
$tagSize = $this->_reader->getSize();
} }
$this->clearOption('unsyncronisation'); $this->clearOption('unsyncronisation');
if ($this->_header->hasFlag(Zend_Media_Id3_Header::UNSYNCHRONISATION)) { if ($this->_header->hasFlag(Zend_Media_Id3_Header::UNSYNCHRONISATION)) {
@@ -164,7 +167,7 @@ final class Zend_Media_Id3v2 extends Zend_Media_Id3_Object
$offset = $this->_reader->getOffset(); $offset = $this->_reader->getOffset();
// Jump off the loop if we reached the end of the tag // Jump off the loop if we reached the end of the tag
if ($offset - $startOffset - 10 >= $this->_header->getSize() - if ($offset - $startOffset - 10 >= $tagSize -
($this->hasFooter() ? 10 : 0) - 10 /* header */) { ($this->hasFooter() ? 10 : 0) - 10 /* header */) {
break; break;
} }
@@ -517,16 +520,34 @@ final class Zend_Media_Id3v2 extends Zend_Media_Id3_Object
$frame->write($buffer); $frame->write($buffer);
} }
} }
$data = $buffer->toString(); $frameData = $buffer->toString();
$datalen = strlen($data); $frameDataLength = strlen($frameData);
$padlen = 0; $paddingLength = 0;
// ID3v2.4.0 supports frame level unsynchronisation. The corresponding
// option is set to true when any of the frames use the
// unsynchronisation scheme.
if ($this->getOption('unsyncronisation', false) === true) { if ($this->getOption('unsyncronisation', false) === true) {
$this->_header->setFlags $this->_header->setFlags
($this->_header->getFlags() | ($this->_header->getFlags() |
Zend_Media_Id3_Header::UNSYNCHRONISATION); Zend_Media_Id3_Header::UNSYNCHRONISATION);
} }
// ID3v2.3.0 supports only tag level unsynchronisation
if ($this->getOption('version', 4) < 4) {
$frameData = $this->_encodeUnsynchronisation($frameData);
if (($len = strlen($frameData)) != $frameDataLength) {
$frameDataLength = $len;
$this->_header->setFlags
($this->_header->getFlags() |
Zend_Media_Id3_Header::UNSYNCHRONISATION);
} else {
$this->_header->setFlags
($this->_header->getFlags() &
~Zend_Media_Id3_Header::UNSYNCHRONISATION);
}
}
// The tag padding is calculated as follows. If the tag can be written // The tag padding is calculated as follows. If the tag can be written
// in the space of the previous tag, the remaining space is used for // in the space of the previous tag, the remaining space is used for
// padding. If there is no previous tag or the new tag is bigger than // padding. If there is no previous tag or the new tag is bigger than
@@ -534,45 +555,49 @@ final class Zend_Media_Id3v2 extends Zend_Media_Id3_Object
// 4096 bytes. // 4096 bytes.
if ($this->hasFooter() === false) { if ($this->hasFooter() === false) {
if ($this->_reader !== null && if ($this->_reader !== null &&
$datalen < $this->_header->getSize()) { $frameDataLength < $this->_header->getSize()) {
$padlen = $this->_header->getSize() - $datalen; $paddingLength = $this->_header->getSize() - $frameDataLength;
} else { } else {
$padlen = 4096; $paddingLength = 4096;
} }
} }
/* ID3v2.4.0 CRC calculated w/ padding */ /* ID3v2.4.0 CRC calculated w/ padding */
if ($this->getOption('version', 4) >= 4) { if ($this->getOption('version', 4) >= 4) {
$data = str_pad($data, $datalen + $padlen, "\0"); $frameData =
str_pad($frameData, $frameDataLength += $paddingLength, "\0");
} }
$extendedHeaderData = '';
$extendedHeaderDataLength = 0;
if ($this->hasExtendedHeader()) { if ($this->hasExtendedHeader()) {
$this->_extendedHeader->setPadding($padlen); $this->_extendedHeader->setPadding($paddingLength);
if ($this->_extendedHeader->hasFlag if ($this->_extendedHeader->hasFlag
(Zend_Media_Id3_ExtendedHeader::CRC32)) { (Zend_Media_Id3_ExtendedHeader::CRC32)) {
$crc = crc32($data); $crc = crc32($frameData);
if ($crc & 0x80000000) { if ($crc & 0x80000000) {
$crc = -(($crc ^ 0xffffffff) + 1); $crc = -(($crc ^ 0xffffffff) + 1);
} }
$this->_extendedHeader->setCrc($crc); $this->_extendedHeader->setCrc($crc);
} }
$buffer = new Zend_Io_StringWriter();
$this->_extendedHeader->write($buffer);
$extendedHeaderData = $buffer->toString();
$extendedHeaderDataLength = strlen($extendedHeaderData);
} }
/* ID3v2.3.0 CRC calculated w/o padding */ /* ID3v2.3.0 CRC calculated w/o padding */
if ($this->getOption('version', 4) < 4) { if ($this->getOption('version', 4) < 4) {
$data = str_pad($data, $datalen + $padlen, "\0"); $frameData =
str_pad($frameData, $frameDataLength += $paddingLength, "\0");
} }
$this->_header->setSize(strlen($data)); $this->_header->setSize($extendedHeaderDataLength + $frameDataLength);
$writer->write('ID3'); $writer->write('ID3');
$this->_header->write($writer); $this->_header->write($writer);
if ($this->hasExtendedHeader()) { $writer->write($extendedHeaderData);
$this->_extendedHeader->write($writer); $writer->write($frameData);
}
$writer->write($data);
if ($this->hasFooter()) { if ($this->hasFooter()) {
$writer->write('3DI'); $writer->write('3DI');
$this->_footer->write($writer); $this->_footer->write($writer);