Fix scanning mp3-files with padding before audio data

This commit is contained in:
wapmorgan 2018-03-19 22:47:12 +03:00
parent 9de5d868fe
commit 0ed1ec7caa
2 changed files with 35 additions and 6 deletions

View File

@ -23,8 +23,9 @@ function substrIfLonger($string, $maxLength) {
function analyze($filename, &$total_duration, &$total_parse_time, $id3v2 = false) { function analyze($filename, &$total_duration, &$total_parse_time, $id3v2 = false) {
if (!is_readable($filename)) return; if (!is_readable($filename)) return;
try { try {
$audio = new Mp3Info($filename, true); $audio = new Mp3Info($filename, true);
} catch (Exception $e) { } catch (Exception $e) {
var_dump($filename.': '.$e->getMessage());
return null; return null;
} }
echo sprintf('%15s | %4s | %7s | %0.1fkHz | %-11s | %-10s | %5d | %.5f', substrIfLonger(basename($filename), 15), formatTime($audio->duration), $audio->isVbr ? 'vbr' : ($audio->bitRate / 1000).'kbps', ($audio->sampleRate / 1000), isset($audio->tags1['song']) ? substrIfLonger($audio->tags1['song'], 11) : null, isset($audio->tags1['artist']) ? substrIfLonger($audio->tags1['artist'], 10) : null, isset($audio->tags1['track']) ? substrIfLonger($audio->tags1['track'], 5) : null, $audio->_parsingTime).PHP_EOL; echo sprintf('%15s | %4s | %7s | %0.1fkHz | %-11s | %-10s | %5d | %.5f', substrIfLonger(basename($filename), 15), formatTime($audio->duration), $audio->isVbr ? 'vbr' : ($audio->bitRate / 1000).'kbps', ($audio->sampleRate / 1000), isset($audio->tags1['song']) ? substrIfLonger($audio->tags1['song'], 11) : null, isset($audio->tags1['artist']) ? substrIfLonger($audio->tags1['artist'], 10) : null, isset($audio->tags1['track']) ? substrIfLonger($audio->tags1['track'], 5) : null, $audio->_parsingTime).PHP_EOL;

View File

@ -66,7 +66,7 @@ class Mp3Info {
/** /**
* Audio size in bytes. Note that this value is NOT equals file size. * Audio size in bytes. Note that this value is NOT equals file size.
* @var int|long * @var int
*/ */
public $audioSize; public $audioSize;
/** /**
@ -76,7 +76,7 @@ class Mp3Info {
public $_fileName; public $_fileName;
/** /**
* Contains file size * Contains file size
* @var long * @var int
*/ */
public $_fileSize; public $_fileSize;
/** /**
@ -157,6 +157,7 @@ class Mp3Info {
/** /**
* $mode is self::META, self::TAGS or their combination. * $mode is self::META, self::TAGS or their combination.
* @throws \Exception
*/ */
public function __construct($filename, $parseTags = false) { public function __construct($filename, $parseTags = false) {
if (is_null(self::$_bitRateTable)) self::$_bitRateTable = require dirname(__FILE__).'/../data/bitRateTable.php'; if (is_null(self::$_bitRateTable)) self::$_bitRateTable = require dirname(__FILE__).'/../data/bitRateTable.php';
@ -174,14 +175,19 @@ class Mp3Info {
* ID3V2 TAG - provides a lot of meta data. [optional] * ID3V2 TAG - provides a lot of meta data. [optional]
* MPEG AUDIO FRAMES - contains audio data. A frame consists of a frame header and a frame data. The first frame may contain extra information about mp3 (marked with "Xing" or "Info" string). Rest of frames can contain only audio data. * MPEG AUDIO FRAMES - contains audio data. A frame consists of a frame header and a frame data. The first frame may contain extra information about mp3 (marked with "Xing" or "Info" string). Rest of frames can contain only audio data.
* ID3V1 TAG - provides a few of meta data. [optional] * ID3V1 TAG - provides a few of meta data. [optional]
* @param $filename
* @param $fileSize
* @param $mode
* @return float|int
* @throws \Exception
*/ */
private function parseAudio($filename, $filesize, $mode) { private function parseAudio($filename, $fileSize, $mode) {
$time = microtime(true); $time = microtime(true);
$fp = fopen($filename, "rb"); $fp = fopen($filename, "rb");
/** Size of audio data (exclude tags size) /** Size of audio data (exclude tags size)
* @var int */ * @var int */
$audioSize = $filesize; $audioSize = $fileSize;
// parse tags // parse tags
if (fread($fp, 3) == self::TAG2_SYNC) { if (fread($fp, 3) == self::TAG2_SYNC) {
@ -197,7 +203,7 @@ class Mp3Info {
$audioSize -= ($id3v2Size = $size); $audioSize -= ($id3v2Size = $size);
} }
} }
fseek($fp, $filesize - 128); fseek($fp, $fileSize - 128);
if (fread($fp, 3) == self::TAG1_SYNC) { if (fread($fp, 3) == self::TAG1_SYNC) {
if ($mode & self::TAGS) $audioSize -= $this->readId3v1Body($fp); if ($mode & self::TAGS) $audioSize -= $this->readId3v1Body($fp);
else $audioSize -= 128; else $audioSize -= 128;
@ -227,11 +233,28 @@ class Mp3Info {
/** /**
* Read first frame information. * Read first frame information.
* @param resource $fp
* @return int Number of frames (if present if first frame) * @return int Number of frames (if present if first frame)
* @throws \Exception
*/ */
private function readFirstFrame($fp) { private function readFirstFrame($fp) {
$pos = ftell($fp); $pos = ftell($fp);
$headerBytes = $this->readBytes($fp, 4); $headerBytes = $this->readBytes($fp, 4);
// if bytes are null, search for something else 1024 bytes forward
if (array_unique($headerBytes) === [0]) {
$limit_pos = $pos + 2048;
do {
$pos = ftell($fp);
$bytes = $this->readBytes($fp, 1);
if ($bytes[0] !== 0) {
fseek($fp, $pos);
$headerBytes = $this->readBytes($fp, 4);
break;
}
} while (ftell($fp) < $limit_pos);
}
if (($headerBytes[0] & 0xFF) != 0xFF || (($headerBytes[1] >> 5) & 0b111) != 0b111) throw new \Exception("At ".$pos."(".dechex($pos).") should be the first frame header!"); if (($headerBytes[0] & 0xFF) != 0xFF || (($headerBytes[1] >> 5) & 0b111) != 0b111) throw new \Exception("At ".$pos."(".dechex($pos).") should be the first frame header!");
switch ($headerBytes[1] >> 3 & 0b11) { switch ($headerBytes[1] >> 3 & 0b11) {
@ -349,7 +372,9 @@ class Mp3Info {
* b - Extended header * b - Extended header
* c - Experimental indicator * c - Experimental indicator
* d - Footer present * d - Footer present
* @param resource $fp
* @return int Returns length of id3v2 tag. * @return int Returns length of id3v2 tag.
* @throws \Exception
*/ */
private function readId3v2Body($fp) { private function readId3v2Body($fp) {
// read the rest of the id3v2 header // read the rest of the id3v2 header
@ -377,11 +402,14 @@ class Mp3Info {
{} {}
} }
$size = substr($data, 8, 32); $size = substr($data, 8, 32);
// some fucking shit // some fucking shit
// getting only 7 of 8 bits of size bytes
$sizes = str_split($size, 8); $sizes = str_split($size, 8);
array_walk($sizes, function (&$value) { $value = substr($value, 1);}); array_walk($sizes, function (&$value) { $value = substr($value, 1);});
$size = implode("", $sizes); $size = implode("", $sizes);
$size = bindec($size); $size = bindec($size);
if ($this->id3v2MajorVersion == 2) // parse id3v2.2.0 body if ($this->id3v2MajorVersion == 2) // parse id3v2.2.0 body
/*throw new \Exception('NEED TO PARSE id3v2.2.0 flags!');*/ /*throw new \Exception('NEED TO PARSE id3v2.2.0 flags!');*/
{} {}