摘要:該文介紹了MP3文件及ID3標簽基本結構以及基于PHP語言對MP3文件ID3標簽信息的讀取的幾種方法。
關鍵詞:MP3;PHP;ID3
中圖分類號:TP317.4文獻標識碼:A文章編號:1009-3044(2008)35-2184-04
Use PHP to Read MP3 Files ID3 Tag Information
SHEN Yu-bao1,2
(1. Hefei University of Technology, Hefei 230009, China; 2. Hefei Nursery Teachers Training School, Hefei 230011, China)
Abstract: In this paper, introduce the basic structure of MP3 files and ID3 tags, as well as several ways of reading MP3 files ID3 tag information by php.
Key words: MP3; PHP; ID3
1 MP3文件
1.1 MP3文件簡介
MP3全稱是動態影像專家壓縮標準音頻層面3(Moving Picture Experts Group Audio Layer III)。是當今較流行的一種數字音頻編碼和有損壓縮格式,它設計用來大幅度地降低音頻數據量,它是在1991年由位于德國埃爾朗根的研究組織Fraunhofer-Gesellschaft的一組工程師發明和標準化的。
MP3就是一種音頻壓縮技術,由于這種壓縮方式的全稱叫MPEG Audio Layer3,所以人們把它簡稱為MP3。MP3是利用 MPEG Audio Layer 3 的技術,將音樂以 1:10 甚至 1:12 的壓縮率,壓縮成容量較小的文件,換句話說,能夠在音質丟失很小的情況下把文件壓縮到更小的程度。而且還非常好的保持了原來的音質。正是因為MP3體積小,音質高的特點使得MP3格式幾乎成為網上音樂的代名詞。每分鐘音樂的MP3格式只有1MB左右大小,這樣每首歌的大小只有3-4兆字節。使用MP3播放器對MP3文件進行實時的解壓縮(解碼),這樣,高品質的MP3音樂就播放出來了。MP3文件格式也是當今互聯網上最常見的音頻文件格式。
2 MP3文件ID3標簽信息及其格式
2.1 ID3簡介
ID3標簽是MP3音樂檔案中的歌曲附加信息,它能夠在MP3中附加曲子的演出者、作者以及其它類別資訊,方便眾多樂曲的管理。缺少ID3標簽并不會影響 MP3的播放,但若沒有的話,管理音樂文件也會相當的麻煩。
ID3,一般是位于一個MP3文件的開頭或末尾的若干字節內,附加了關于該MP3的歌手,標題,專輯名稱,年代,風格等信息,該信息就被稱為ID3信息,ID3信息分為兩個版本,v1和v2版。
其中:v1版的ID3在MP3文件的末尾128字節,以TAG三個字符開頭,后面跟上歌曲信息。
v2版一般位于MP3的開頭,可以存儲歌詞,該專輯的圖片等大容量的信息。
此外,ID3也就是ID3Tags,其中的“Tag”在英文中名詞的一個意思是“標簽”,所以“ID3Tags”也就是“MP3文件曲目標簽”的意思。有些地方以“ID3”這樣的簡稱來代表“ID3Tags”,二者本質是一樣的。
2.2 ID3格式
ID3V1比較簡單,它是存放在MP3文件的末尾,用16進制的編輯器打開一個MP3文件,查看其末尾的128個順序存放字節,數據結構定義如下:
char Header[3];/*標簽頭必須是\"TAG\"否則認為沒有標簽*/
char Title[30];/*標題*/
char Artist[30]; /*作者*/
char Album[30];/*專集*/
char Year[4];/*出品年代*/
char Comment[30];/*備注*/
char Genre;/*類型*/
ID3V1的各項信息都是順序存放,沒有任何標識將其分開,比如標題信息不足30個字節,則使用'\\0'補足,否則將造成信息錯誤。
ID3V2到現在一共有4個版本,但流行的播放軟件一般只支持第3版,既ID3v2.3。由于ID3V1記錄在MP3文件的末尾,ID3V2就只好記錄在MP3文件的首部了。也正是由于這個原因,對ID3V2的操作比ID3V1要慢。而且ID3V2結構比ID3V1的結構要復雜得多,但比前者全面且可以伸縮和擴展。
例如ID3V2.3:每個ID3V2.3的標簽都一個標簽頭和若干個標簽幀或一個擴展標簽頭組成。關于曲目的信息如標題、作者等都存放在不同的標簽幀中,擴展標簽頭和標簽幀并不是必要的,但每個標簽至少要有一個標簽幀。標簽頭和標簽幀一起順序存放在MP3文件的首部。這里由于篇幅限制,詳細介紹可以參見http://www.id3.org網站。
3 使用PHP讀取MP3文件ID3標簽信息
下面只針對MP3的ID3v1進行了討論由于在現實應用中,本例以讀取id3v1為例,php最大優勢就是開源,同樣對于MP3文件ID3標簽信息讀取,我們同樣可以采取開源。
3.1 利用pear庫中MP3_ID類讀取ID3標簽信息
PEAR(PHP Extension and Application Repository)由Stig S. Bakken于2000年在PHP開發者會議(PHP Developers' Meeting, PDM) 上提出,目的是實做可以重復使用的函式庫來提供PHP社群使用。在今天,有PEAR這個由全世界眾多頂尖的PHP高手所共同生產的智慧結晶,可以讓我們輕松又有效率地撰寫代碼,并且克服許多撰寫上的困難。在許多方面如:圖片處理類(IMAGE)、XML類(XML)、數學類(MATH)等許多方面有著成熟的代碼,在讀取MP3ID3標簽信息有一個Id.php類,具體使用代碼如下:
require_once(\"MP3/Id.php\"); //引用pear類中Id.php開源代碼
$file = \"beijing.MP3\";//讀取MP3文件名稱
$id3 = new MP3_Id(); //新建類
$id3->read($file);//讀取id3v1
print_r($id3);//輸出id3v1結果
?>
執行結果如圖1所示:
圖1
3.2 利用getid3讀取ID3標簽信息
getid3是一套腳本從各種多媒體文件中讀寫有用的信息,它支持多種格式,如id3v1, id3v2, ogg, mpc, avi, Lyrics3等,支持媒體格式且支持目錄的讀取。
主頁為:http://www.getid3.org/ 可以免費下載使用。讀取代碼如下:
require_once('../getid3/getid3/getid3.php');
$filename=\"beijing.MP3\";
$getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($filename);
getid3_lib::CopyTagsToComments($ThisFileInfo);
print_r($ThisFileInfo);
?>
執行結果部分如圖2所示:
圖2
3.3 編寫PHP代碼讀取ID3信息
$genres = Array('Blues','Classic Rock','Country','Dance','Disco','Funk',
'Grunge','Hip-Hop','Jazz' ......)//設置類型數組部分代碼略
$genreids = Array(\"Blues\" => 0,\"Classic Rock\" => 1,\"Country\" => 2,\"Dance\" => 3,
\"Disco\" => 4, ......)//設置類型數組部分代碼略
$version=Array(\"00\"=>2.5, \"10\"=>2, \"11\"=>1);//id3版本
$layer=Array(\"01\"=>3, \"10\"=>2, \"11\"=>1);//音頻采用壓縮技術
$crc=Array(\"Yes\", \"No\");
$bitrate[\"0001\"]=Array(32,32,32,32,8,8);
$bitrate[\"0010\"]=Array(64,48,40,48,16,16);
$bitrate[\"0011\"]=Array(96,56,48,56,24,24);
$bitrate[\"0100\"]=Array(128,64,56,64,32,32);
$bitrate[\"0101\"]=Array(160,80,64,80,40,40);
$bitrate[\"0110\"]=Array(192,96,80,96,48,48);
$bitrate[\"0111\"]=Array(224,112,96,112,56,56);
$bitrate[\"1000\"]=Array(256,128,112,128,64,64);
$bitrate[\"1001\"]=Array(288,160,128,144,80,80);
$bitrate[\"1010\"]=Array(320,192,160,160,96,96);
$bitrate[\"1011\"]=Array(352,224,192,176,112,112);
$bitrate[\"1100\"]=Array(384,256,224,192,128,128);
$bitrate[\"1101\"]=Array(416,320,256,224,144,144);
$bitrate[\"1110\"]=Array(448,384,320,256,160,160);
$bitindex=Array(\"1111\"=>\"0\",\"1110\"=>\"1\",\"1101\"=>\"2\",
\"1011\"=>\"3\",\"1010\"=>\"4\",\"1001\"=>\"5\",\"0011\"=>\"3\",\"0010\"=>4,\"0001\"=>\"5\");
$freq[\"00\"]=Array(\"11\"=>44100,\"10\"=>22050,\"00\"=>11025);
$freq[\"01\"]=Array(\"11\"=>48000,\"10\"=>24000,\"00\"=>12000);
$freq[\"10\"]=Array(\"11\"=>32000,\"10\"=>16000,\"00\"=>8000);
$mode=Array(\"00\"=>\"Stereo\",\"01\"=>\"Jointstereo\",\"10\"=>\"Dual channel\",\"11\"=>\"Mono\");
$copy=Array(\"No\",\"Yes\");
function strip_1s( $str ) {
$res = explode( chr(0), $str );
return chop( $res[0] );}
function MP3_id($file) {
global $version, $layer, $crc, $bitrate, $bitindex, $freq, $mode, $copy, $genres;//設置全局變量
if(!$f=@fopen($file, \"r\")) { return -1; break; } else {
$tmp=fread($f,4);
if($tmp==\"RIFF\") {
$idtag[\"ftype\"]=\"Wave\";
fseek($f, 0);
$tmp=fread($f,128);
$x=StrPos($tmp, \"data\");
fseek($f, $x+8);
$tmp=fread($f,4);}
for($y=0;$y<4;$y++) {
$x=decbin(ord($tmp[$y]));
for($i=0;$i<(8-StrLen($x));$i++) {$x.=\"0\";}
$bajt.=$x;}
if(substr($bajt,1,11)!=\"11111111111\") {
fseek($f, 4);
$tmp=fread($f,2048);
for($i=0;$i<2048;$i++){
if(ord($tmp[$i])==255 substr(decbin(ord($tmp[$i+1])),0,3)==\"111\") {
$tmp=substr($tmp, $i,4);
$bajt=\"\";
for($y=0;$y<4;$y++) {
$x=decbin(ord($tmp[$y]));
for($i=0;$i<(8-StrLen($x));$i++) {$x.=\"0\";}
$bajt.=$x;}
break;}}}
if($bajt==\"\") {
return -1;
break;}
$len=filesize($file);//取得文件大小
$idtag[\"version\"]=$version[substr($bajt,11,2)]; //取得ID標簽版本
$idtag[\"layer\"]=$layer[substr($bajt,13,2)];
$idtag[\"crc\"]=$crc[$bajt[15]];
$idtag[\"bitrate\"]=$bitrate[substr($bajt,16,4)][$bitindex[substr($bajt,11,4)]];
$idtag[\"frequency\"]=$freq[substr($bajt,20,2)][substr($bajt,11,2)];
$idtag[\"padding\"]=$copy[$bajt[22]];
$idtag[\"mode\"]=$mode[substr($bajt,24,2)];
$idtag[\"copyright\"]=$copy[$bajt[28]];
$idtag[\"original\"]=$copy[$bajt[29]];
//取得文件存儲大小
if($idtag[\"layer\"]==1) { $fsize=(12*($idtag[\"bitrate\"]*1000)/$idtag[\"frequency\"]+$idtag[\"padding\"])*4; }
else { $fsize=144*(($idtag[\"bitrate\"]*1000)/$idtag[\"frequency\"]+$idtag[\"padding\"]);}
$idtag[\"lenght_sec\"] = @round($len/Round($fsize)/38.37);
$idtag[\"length\"]=(Round($fsize))? @date(\"i:s\",round($len/Round($fsize)/38.37)) : 0;
if(!$len) $len=filesize($file);
fseek($f, $len-128);
$tag = fread($f, 128);
if(Substr($tag,0,3)==\"TAG\") {
$idtag[\"file\"]=$file;
$idtag[\"tag\"]=-1;
$idtag[\"title\"]=strip_1s( Substr($tag,3,30) );
$idtag[\"artist\"]=strip_1s( Substr($tag,33,30) );
$idtag[\"album\"]=strip_1s( Substr($tag,63,30) );
$idtag[\"year\"]=strip_1s( Substr($tag,93,4) );
$idtag[\"comment\"]=strip_1s( Substr($tag,97,30) );
if (strlen( $idtag[\"comment\"]) < 29 ) {
if (Ord(Substr($tag,125,1)) == chr(0)) // If char 125 is 1 then track (maybe) is present
$idtag[\"track\"]=Ord(Substr($tag,126,1));
else $idtag[\"track\"]=0;
} else { // If the comment is 29 or 30 chars long, there's no way to put track #
$idtag[\"track\"]=0;}
$idtag[\"genreid\"]=Ord(Substr($tag,127,1));
$idtag[\"genre\"]=$genres[$idtag[\"genreid\"]];
$idtag[\"filesize\"]=$len;
} else {
$idtag[\"tag\"]=0;}
if(!$idtag[\"title\"]) {
$idtag[\"title\"]=Str_replace(\"\\\\\",\"/\", $file);
$idtag[\"title\"]=substr($idtag[\"title\"],strrpos($idtag[\"title\"],\"/\")+1, 255);}
fclose($f); //關閉文件
return $idtag;//返回參數
}}
print_r(MP3_id(\"beijing.MP3\")); //輸出MP3_id信息
?>
執行結果如圖3所示:
4 總結
以上程序代碼在Apache2.0 + Windows XP + PHP5環境下測試通過,利用pear庫中MP3_ID類和getid3開源不僅僅可以讀取ID3標簽信息、也可以對其信息進行寫入,getid3可以進行目錄的讀取,同時也支持多種音頻文件格式,甚至可以自動讀取數據寫入到MYSQL數據庫中,其應用有很大挖掘潛力,但是其對中文編碼的支持存在一定的缺陷,希望本文能夠對音頻點播等應用方面具有一定積極的意義。
參考文獻:
[1] Davis M E,Pblilips J A. Learning PHP MySQL[M]. 北京:中國電力出版社,2007.