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

@ -23,8 +23,9 @@ function substrIfLonger($string, $maxLength) {
function analyze($filename, &$total_duration, &$total_parse_time, $id3v2 = false) {
if (!is_readable($filename)) return;
try {
$audio = new Mp3Info($filename, true);
$audio = new Mp3Info($filename, true);
} catch (Exception $e) {
var_dump($filename.': '.$e->getMessage());
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;

@ -66,7 +66,7 @@ class Mp3Info {
/**
* Audio size in bytes. Note that this value is NOT equals file size.
* @var int|long
* @var int
*/
public $audioSize;
/**
@ -76,7 +76,7 @@ class Mp3Info {
public $_fileName;
/**
* Contains file size
* @var long
* @var int
*/
public $_fileSize;
/**
@ -157,6 +157,7 @@ class Mp3Info {
/**
* $mode is self::META, self::TAGS or their combination.
* @throws \Exception
*/
public function __construct($filename, $parseTags = false) {
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]
* 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]
* @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);
$fp = fopen($filename, "rb");
/** Size of audio data (exclude tags size)
* @var int */
$audioSize = $filesize;
$audioSize = $fileSize;
// parse tags
if (fread($fp, 3) == self::TAG2_SYNC) {
@ -197,7 +203,7 @@ class Mp3Info {
$audioSize -= ($id3v2Size = $size);
}
}
fseek($fp, $filesize - 128);
fseek($fp, $fileSize - 128);
if (fread($fp, 3) == self::TAG1_SYNC) {
if ($mode & self::TAGS) $audioSize -= $this->readId3v1Body($fp);
else $audioSize -= 128;
@ -227,11 +233,28 @@ class Mp3Info {
/**
* Read first frame information.
* @param resource $fp
* @return int Number of frames (if present if first frame)
* @throws \Exception
*/
private function readFirstFrame($fp) {
$pos = ftell($fp);
$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!");
switch ($headerBytes[1] >> 3 & 0b11) {
@ -349,7 +372,9 @@ class Mp3Info {
* b - Extended header
* c - Experimental indicator
* d - Footer present
* @param resource $fp
* @return int Returns length of id3v2 tag.
* @throws \Exception
*/
private function readId3v2Body($fp) {
// read the rest of the id3v2 header
@ -377,11 +402,14 @@ class Mp3Info {
{}
}
$size = substr($data, 8, 32);
// some fucking shit
// getting only 7 of 8 bits of size bytes
$sizes = str_split($size, 8);
array_walk($sizes, function (&$value) { $value = substr($value, 1);});
$size = implode("", $sizes);
$size = bindec($size);
if ($this->id3v2MajorVersion == 2) // parse id3v2.2.0 body
/*throw new \Exception('NEED TO PARSE id3v2.2.0 flags!');*/
{}