/** * Removes the ID3v2 tag altogether. * * @param string $filename The path to the file. */ public static function remove($filename) { if ($filename instanceof Zend_Io_Reader) { $reader =& $filename; } else { $reader = new Zend_Io_FileReader($filename, 'r+b'); } $fileSize = $reader->getSize(); if ($reader->read(3) == 'ID3') { $header = new Zend_Media_Id3_Header($reader); $tagSize = 10 + $header->getSize(); } else { return; } $fd = $reader->getFileDescriptor(); for ($i = 0; $tagSize + $i * 1024 < $fileSize; $i++) { fseek($fd, $tagSize + $i * 1024); $buffer = fread($fd, 1024); fseek($fd, $i * 1024); $bytes = fwrite($fd, $buffer, 1024); } ftruncate($fd, $fileSize - $tagSize); }
/** * Writes the tag data. * * @param Zend_Io_Writer $writer The writer object. * @return void */ private function _writeData($writer) { $this->clearOption('unsyncronisation'); $buffer = new Zend_Io_StringWriter(); foreach ($this->_frames as $frames) { foreach ($frames as $frame) { $frame->write($buffer); } } $frameData = $buffer->toString(); $frameDataLength = strlen($frameData); $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) { $this->_header->setFlags($this->_header->getFlags() | 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 // 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 // the space taken by the previous tag, the padding is a constant // 4096 bytes. if ($this->hasFooter() === false) { if ($this->_reader !== null && $frameDataLength < $this->_header->getSize()) { $paddingLength = $this->_header->getSize() - $frameDataLength; } else { $paddingLength = 4096; } } /* ID3v2.4.0 CRC calculated w/ padding */ if ($this->getOption('version', 4) >= 4) { $frameData = str_pad($frameData, $frameDataLength += $paddingLength, ""); } $extendedHeaderData = ''; $extendedHeaderDataLength = 0; if ($this->hasExtendedHeader()) { $this->_extendedHeader->setPadding($paddingLength); if ($this->_extendedHeader->hasFlag(Zend_Media_Id3_ExtendedHeader::CRC32)) { $crc = crc32($frameData); if ($crc & 2147483648.0) { $crc = -(($crc ^ 4294967295.0) + 1); } $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 */ if ($this->getOption('version', 4) < 4) { $frameData = str_pad($frameData, $frameDataLength += $paddingLength, ""); } $this->_header->setSize($extendedHeaderDataLength + $frameDataLength); $writer->write('ID3'); $this->_header->write($writer); $writer->write($extendedHeaderData); $writer->write($frameData); if ($this->hasFooter()) { $writer->write('3DI'); $this->_footer->write($writer); } }
/** * Constructs the Zend_Media_Mpeg_ABS class with given file and options. * * The following options are currently recognized: * o readmode -- Can be one of full or lazy and determines when the read * of frames and their data happens. When in full mode the data is read * automatically during the instantiation of the frame and all the * frames are read during the instantiation of this class. While this * allows faster validation and data fetching, it is unnecessary in * terms of determining just the play duration of the file. Defaults to * lazy. * * o estimatePrecision -- Only applicaple with lazy read mode to determine * the precision of play duration estimate. This precision is equal to * how many frames are read before fixing the average bitrate that is * used to calculate the play duration estimate of the whole file. Each * frame adds about 0.1-0.2ms to the processing of the file. Defaults to * 1000. * * When in lazy data reading mode it is first checked whether a VBR header * is found in a file. If so, the play duration is calculated based no its * data and no further frames are read from the file. If no VBR header is * found, frames up to estimatePrecision are read to calculate an average * bitrate. * * Hence, only zero or <var>estimatePrecision</var> number of frames are * read in lazy data reading mode. The rest of the frames are read * automatically when directly referenced, ie the data is read when it is * needed. * * @param string|resource|Zend_Io_Reader $filename The path to the file, * file descriptor of an opened file, or a {@link Zend_Io_Reader} instance. * @param Array $options The options array. */ public function __construct($filename, $options = array()) { if ($filename instanceof Zend_Io_Reader) { $this->_reader =& $filename; } else { try { $this->_reader = new Zend_Io_FileReader($filename); } catch (Zend_Io_Exception $e) { $this->_reader = null; throw new Zend_Media_Mpeg_Exception($e->getMessage()); } } $this->setOptions($options); $offset = $this->_reader->getOffset(); $this->_bytes = $this->_reader->getSize(); /* Skip ID3v1 tag */ $this->_reader->setOffset(-128); if ($this->_reader->read(3) == 'TAG') { $this->_bytes -= 128; } $this->_reader->setOffset($offset); /* Skip ID3v2 tags (some files errorneusly contain multiple tags) */ while ($this->_reader->readString8(3) == 'ID3') { $header = new Zend_Media_Id3_Header($this->_reader); $this->_reader->skip($header->getSize() + ($header->hasFlag(Zend_Media_Id3_Header::FOOTER) ? 10 : 0)); $offset = $this->_reader->getOffset(); } $this->_reader->setOffset($offset); /* Check whether the ABS is contained within a RIFF chunk */ $offset = $this->_reader->getOffset(); if ($this->_reader->readString8(4) == 'RIFF') { $riffSize = $this->_reader->readUInt32LE(); $riffType = $this->_reader->read(4); // WAVE while ($this->_reader->getOffset() < $offset + 8 + $riffSize - 1) { $chunkId = $this->_reader->read(4); $chunkSize = $this->_reader->readUInt32LE(); if ($chunkId == 'fmt ') { if ($this->_reader->readInt16LE() != 85) { // 85: MPEG-1 Layer 3 Codec throw new Zend_Media_Mpeg_Exception('File does not contain a valid MPEG Audio Bit Stream (Contains RIFF with no MPEG ABS)'); } else { $this->_reader->skip($chunkSize - 2); } } else { if ($chunkId == 'data') { $offset = $this->_reader->getOffset(); break; } else { $this->_reader->skip($chunkSize); } } } } else { $this->_reader->setOffset($offset); } /* Check for VBR headers */ $this->_frames[] = $firstFrame = new Zend_Media_Mpeg_Abs_Frame($this->_reader, $options); $offset = $this->_reader->getOffset(); $this->_reader->setOffset($firstFrame->getOffset() + 4 + self::$sidesizes[$firstFrame->getFrequencyType()][$firstFrame->getMode()]); if (($xing = $this->_reader->readString8(4)) == 'Xing' || $xing == 'Info') { $this->_xingHeader = new Zend_Media_Mpeg_Abs_XingHeader($this->_reader, $options); if ($this->_reader->readString8(4) == 'LAME') { $this->_lameHeader = new Zend_Media_Mpeg_Abs_LameHeader($this->_reader, $options); } // A header frame is not counted as an audio frame array_pop($this->_frames); } $this->_reader->setOffset($firstFrame->getOffset() + 4 + 32); if ($this->_reader->readString8(4) == 'VBRI') { $this->_vbriHeader = new Zend_Media_Mpeg_Abs_VbriHeader($this->_reader, $options); // A header frame is not counted as an audio frame array_pop($this->_frames); } $this->_reader->setOffset($offset); // Ensure we always have read at least one frame if (empty($this->_frames)) { $this->_readFrames(1); } /* Read necessary frames */ if ($this->getOption('readmode', 'lazy') == 'lazy') { if ((($header = $this->_xingHeader) !== null || ($header = $this->_vbriHeader) !== null) && $header->getFrames() != 0) { $this->_estimatedPlayDuration = $header->getFrames() * $firstFrame->getSamples() / $firstFrame->getSamplingFrequency(); if ($this->_lameHeader !== null) { $this->_estimatedBitrate = $this->_lameHeader->getBitrate(); if ($this->_estimatedBitrate == 255) { $this->_estimatedBitrate = round($this->_lameHeader->getMusicLength() / ($header->getFrames() * $firstFrame->getSamples() / $firstFrame->getSamplingFrequency()) / 1000 * 8); } } else { $this->_estimatedBitrate = ($this->_bytes - $firstFrame->getOffset()) / $this->_estimatedPlayDuration / 1000 * 8; } } else { $this->_readFrames($this->getOption('estimatePrecision', 1000)); $this->_estimatedBitrate = $this->_cumulativeBitrate / count($this->_frames); $this->_estimatedPlayDuration = ($this->_bytes - $firstFrame->getOffset()) / ($this->_estimatedBitrate * 1000 / 8); } } else { $this->_readFrames(); $this->_estimatedBitrate = $this->_cumulativeBitrate / count($this->_frames); $this->_estimatedPlayDuration = $this->_cumulativePlayDuration; } }
/** * Constructs the Zend_Media_Mpeg_ABS class with given file and options. * * The following options are currently recognized: * o readmode -- Can be one of full or lazy and determines when the read * of frames and their data happens. When in full mode the data is read * automatically during the instantiation of the frame and all the * frames are read during the instantiation of this class. While this * allows faster validation and data fetching, it is unnecessary in * terms of determining just the play duration of the file. Defaults to * lazy. * * o estimatePrecision -- Only applicaple with lazy read mode to determine * the precision of play duration estimate. This precision is equal to * how many frames are read before fixing the average bitrate that is * used to calculate the play duration estimate of the whole file. Each * frame adds about 0.1-0.2ms to the processing of the file. Defaults to * 1000. * * When in lazy data reading mode it is first checked whether a VBR header * is found in a file. If so, the play duration is calculated based no its * data and no further frames are read from the file. If no VBR header is * found, frames up to estimatePrecision are read to calculate an average * bitrate. * * Hence, only zero or <var>estimatePrecision</var> number of frames are * read in lazy data reading mode. The rest of the frames are read * automatically when directly referenced, ie the data is read when it is * needed. * * @param string|resource|Zend_Io_Reader $filename The path to the file, * file descriptor of an opened file, or a {@link Zend_Io_Reader} instance. * @param Array $options The options array. */ public function __construct($filename, $options = array()) { if ($filename instanceof Zend_Io_Reader) { $this->_reader =& $filename; } else { require_once 'Zend/Io/FileReader.php'; try { $this->_reader = new Zend_Io_FileReader($filename); } catch (Zend_Io_Exception $e) { $this->_reader = null; require_once 'Zend/Media/Mpeg/Exception.php'; throw new Zend_Media_Mpeg_Exception($e->getMessage()); } } $this->setOptions($options); $offset = $this->_reader->getOffset(); $this->_bytes = $this->_reader->getSize(); /* Skip ID3v1 tag */ $this->_reader->setOffset(-128); if ($this->_reader->read(3) == 'TAG') { $this->_bytes -= 128; } $this->_reader->setOffset($offset); /* Skip ID3v2 tag */ if ($this->_reader->readString8(3) == 'ID3') { require_once 'Zend/Media/Id3/Header.php'; $header = new Zend_Media_Id3_Header($this->_reader); $this->_reader->skip($header->getSize() + ($header->hasFlag(Zend_Media_Id3_Header::FOOTER) ? 10 : 0)); } else { $this->_reader->setOffset($offset); } /* Check for VBR headers */ $offset = $this->_reader->getOffset(); $this->_frames[] = $firstFrame = new Zend_Media_Mpeg_Abs_Frame($this->_reader, $options); $postoffset = $this->_reader->getOffset(); $this->_reader->setOffset($offset + 4 + self::$sidesizes[$firstFrame->getFrequencyType()][$firstFrame->getMode()]); if (($xing = $this->_reader->readString8(4)) == 'Xing' || $xing == 'Info') { require_once 'Zend/Media/Mpeg/Abs/XingHeader.php'; $this->_xingHeader = new Zend_Media_Mpeg_Abs_XingHeader($this->_reader, $options); if ($this->_reader->readString8(4) == 'LAME') { require_once 'Zend/Media/Mpeg/Abs/LameHeader.php'; $this->_lameHeader = new Zend_Media_Mpeg_Abs_LameHeader($this->_reader, $options); } // A header frame is not counted as an audio frame array_pop($this->_frames); } $this->_reader->setOffset($offset + 4 + 32); if ($this->_reader->readString8(4) == 'VBRI') { require_once 'Zend/Media/Mpeg/Abs/VbriHeader.php'; $this->_vbriHeader = new Zend_Media_Mpeg_Abs_VbriHeader($this->_reader, $options); // A header frame is not counted as an audio frame array_pop($this->_frames); } $this->_reader->setOffset($postoffset); // Ensure we always have read at least one frame if (empty($this->_frames)) { $this->_readFrames(1); } /* Read necessary frames */ if ($this->getOption('readmode', 'lazy') == 'lazy') { if (($header = $this->_xingHeader) !== null || ($header = $this->_vbriHeader) !== null) { $this->_estimatedPlayDuration = $header->getFrames() * $firstFrame->getSamples() / $firstFrame->getSamplingFrequency(); if ($this->_lameHeader !== null) { $this->_estimatedBitrate = $this->_lameHeader->getBitrate(); if ($this->_estimatedBitrate == 255) { $this->_estimatedBitrate = round($this->_lameHeader->getMusicLength() / ($header->getFrames() * $firstFrame->getSamples() / $firstFrame->getSamplingFrequency()) / 1000 * 8); } } else { $this->_estimatedBitrate = ($this->_bytes - $offset) / $this->_estimatedPlayDuration / 1000 * 8; } } else { $this->_readFrames($this->getOption('estimatePrecision', 1000)); $this->_estimatedBitrate = $this->_cumulativeBitrate / count($this->_frames); $this->_estimatedPlayDuration = ($this->_bytes - $offset) / ($this->_estimatedBitrate * 1000 / 8); } } else { $this->_readFrames(); $this->_estimatedBitrate = $this->_cumulativeBitrate / count($this->_frames); $this->_estimatedPlayDuration = $this->_cumulativePlayDuration; } }