Чтение ID3 тегов и другой информации из mp3 файлов средствами php

Все ID3 теги (ID3 tag) хранятся в последних 128-ми байтах mp3 файла. Это означает, что их можно читать без специальных компонентов. Распределение информации следующее: 

Byte 1-3 = ID 'TAG' 
Byte 4-33 = Title 
Byte 34-63 = Artist 
Byte 64-93 = Album 
Byte 94-97 = Year 
Byte 98-127 = Comment 
Byte 128 = Genre

Для определения тегов достаточно отформатировать распаковку из бинарной строки в соответствии с выше приведенной последовательностью:

01$f fopen('test.mp3''rb');
02rewind($f);
03fseek($f, -128, SEEK_END);
04$tmp fread($f,128);
05if ($tmp[125] == Chr(0) and $tmp[126] != Chr(0)) {
06// ID3 v1.1
07$format 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a28COMMENT/x1/C1TRACK/C1GENRENO';
08else {
09// ID3 v1
10$format 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a30COMMENT/C1GENRENO';
11}
12 
13$id3tag = unpack($format$tmp);

Результат работы скрипта:
Array ( [TAG] => TAG [NAME] => test [ARTISTS] => oz [ALBUM] => [YEAR] => 2007 [COMMENT] => [GENRENO] => 0 ) 

Иногда бывает необходимо кроме ID3 tag 'ов узнать и другую инфу об mp3 файле (частоту, bitrate, длительность и др.), я взял за основу библиотекуhttp://pear.php.net/package/MP3_ID и написал следующую функцию, которая позволяет это сделать. Я не буду ее комментировать, потому что здесь мало программирования, а в основном знание структуры mp3 файла, кто захочет - разберется.

001function readframe($file) {
002if (! ($f fopen($file'rb')) ) die("Unable to open " $file);
003$res['filesize'] = filesize($file);
004do {
005while (fread($f,1) != Chr(255)) { // Find the first frame       
006if (feof($f))  die"No mpeg frame found") ;
007}
008fseek($fftell($f) - 1); // back up one byte
009 
010$frameoffset ftell($f);
011 
012$r fread($f, 4);
013 
014$bits = sprintf("%'08b%'08b%'08b%'08b", ord($r{0}), ord($r{1}), ord($r{2}), ord($r{3}));
015}
016while (!$bits[8] and !$bits[9] and !$bits[10]); // 1st 8 bits true from the while   
017 
018// Detect VBR header
019if ($bits[11] == 0) {
020if (($bits[24] == 1) && ($bits[25] == 1)) {
021$vbroffset = 9; // MPEG 2.5 Mono
022else {
023$vbroffset = 17; // MPEG 2.5 Stereo
024}
025else if ($bits[12] == 0) {
026if (($bits[24] == 1) && ($bits[25] == 1)) {
027$vbroffset = 9; // MPEG 2 Mono
028else {
029$vbroffset = 17; // MPEG 2 Stereo
030}
031else {
032if (($bits[24] == 1) && ($bits[25] == 1)) {
033$vbroffset = 17; // MPEG 1 Mono
034else {
035$vbroffset = 32; // MPEG 1 Stereo
036}
037}
038 
039fseek($fftell($f) + $vbroffset);
040$r fread($f, 4);
041 
042switch ($r) {
043case 'Xing':
044$res['encoding_type'] = 'VBR';
045case 'VBRI':
046default:
047if ($vbroffset != 32) {
048// VBRI Header is fixed after 32 bytes, so maybe we are looking at the wrong place.
049fseek($fftell($f) + 32 - $vbroffset);
050$r fread($f, 4);
051 
052if ($r != 'VBRI') {
053$res['encoding_type'] = 'CBR';
054break;
055}
056else {
057$res['encoding_type'] = 'CBR';
058break;
059}
060 
061$res['encoding_type'] = 'VBR';
062}
063 
064fclose($f);
065 
066if ($bits[11] == 0) {
067$res['mpeg_ver'] = "2.5";
068$bitrates array(
069'1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
070'2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
071'3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
072);
073else if ($bits[12] == 0) {
074$res['mpeg_ver'] = "2";
075$bitrates array(
076'1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
077'2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
078'3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
079);
080else {
081$res['mpeg_ver'] = "1";
082$bitrates array(
083'1' => array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0),
084'2' => array(0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0),
085'3' => array(0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0),
086);
087}
088 
089$layer array(
090array(0,3),
091array(2,1),
092);
093$res['layer'] = $layer[$bits[13]][$bits[14]];
094 
095if ($bits[15] == 0) {
096// It's backwards, if the bit is not set then it is protected.
097$res['crc'] = true;
098}
099 
100$bitrate = 0;
101if ($bits[16] == 1) $bitrate += 8;
102if ($bits[17] == 1) $bitrate += 4;
103if ($bits[18] == 1) $bitrate += 2;
104if ($bits[19] == 1) $bitrate += 1;
105$res['bitrate'] = $bitrates[$res['layer']][$bitrate];
106 
107$frequency array(
108'1' => array(
109'0' => array(44100, 48000),
110'1' => array(32000, 0),
111),
112'2' => array(
113'0' => array(22050, 24000),
114'1' => array(16000, 0),
115),
116'2.5' => array(
117'0' => array(11025, 12000),
118'1' => array(8000, 0),
119),
120);
121$res['frequency'] = $frequency[$res['mpeg_ver']][$bits[20]][$bits[21]];
122 
123$mode array(
124array('Stereo''Joint Stereo'),
125array('Dual Channel''Mono'),
126);
127$res['mode'] = $mode[$bits[24]][$bits[25]];
128 
129$samplesperframe array(
130'1' => array(
131'1' => 384,
132'2' => 1152,
133'3' => 1152
134),
135'2' => array(
136'1' => 384,
137'2' => 1152,
138'3' => 576
139),
140'2.5' => array(
141'1' => 384,
142'2' => 1152,
143'3' => 576
144),
145);
146$res['samples_per_frame'] = $samplesperframe[$res['mpeg_ver']][$res['layer']];
147 
148if ($res['encoding_type'] != 'VBR') {
149if ($res['bitrate'] == 0) {
150$s = -1;
151else {
152$s = ((8*filesize($file))/1000) / $res['bitrate'];
153}
154$res['length'] = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
155$res['lengthh'] = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
156$res['lengths'] = (int)$s;
157 
158$res['samples'] = ceil($res['lengths'] * $res['frequency']);
159if(0 != $res['samples_per_frame']) {
160$res['frames'] = ceil($res['samples'] / $res['samples_per_frame']);
161else {
162$res['frames'] = 0;
163}
164$res['musicsize'] = ceil($res['lengths'] * $res['bitrate'] * 1000 / 8);
165else {
166$res['samples'] = $res['samples_per_frame'] * $res['frames'];
167$s $res['samples'] / $res['frequency'];
168 
169$res['length'] = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
170$res['lengthh'] = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
171$res['lengths'] = (int)$s;
172 
173$res['bitrate'] = (int)(($res['musicsize'] / $s) * 8 / 1000);
174}
175 
176return $res;
177}

Результат работы скрипта:

Array ( 
	[filesize] => 1369759 
	[encoding_type] => CBR 
	[mpeg_ver] => 1 
	[layer] => 3 
	[bitrate] => 192 
	[frequency] => 44100 
	[mode] => Stereo 
	[samples_per_frame] => 1152 
	[length] => 00:57 
	[lengthh] => 00:00:57 
	[lengths] => 57 
	[samples] => 2513700 
	[frames] => 2183 
	[musicsize] => 1368000 
) 

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=29560