ホームページ  >  記事  >  バックエンド開発  >  PHPはMP3のメディアタグ情報を取得します

PHPはMP3のメディアタグ情報を取得します

*文
*文オリジナル
2017-12-26 16:09:071737ブラウズ

PHP如何获取MP3的媒体标签信息?本文主要介绍了PHP获取音频文件的相关信息的相关资料。希望对大家有所帮助。

项目需求:现在有一个音频文件上传的功能,在上传后PHP需要获取这个音频文件的相关信息,例如:时长等,由于这个文件是放在买的空间上的,没有像ffmpeg这样的扩展来处理,那么PHP能不能获取到这些信息?

下面是之前在项目中用到的一个用PHP进行音频文件头部信息的读取与写入操作的实现,主要针对 WMA 和 MP3 两种格式,供参考。

<?php
// AudioExif.class.php
// 用PHP进行音频文件头部信息的读取与写入
// 目前只支持 WMA 和 MP3 两种格式, 只支持常用的几个头部信息
//
// 写入信息支持: Title(名称), Artist(艺术家), Copyright(版权), Description (描述)
// Year(年代), Genre (流派), AlbumTitle (专辑标题)
// 其中 mp3 和 wma 略有不同, 具体返回的信息还可能更多, 但只有以上信息可以被写入
// mp3 还支持 Track (曲目编号写入)
// 对于 MP3 文件支持 ID3v1也支持ID3v2, 读取时优先 v2, 写入时总是会写入v1, 必要时写入v2
//
// 用法说明: (由于 wma 使用 Unicode 存取, 故还需要 mb_convert_encoding() 扩展
// 返回数据及写入数据均为 ANSI 编码, 即存什么就显示什么 (中文_GB2312)
//
// require (&#39;AudioExif.class.php&#39;);
// $AE = new AudioExif;
// $file = &#39;/path/to/test.mp3&#39;;
//
// 1. 检查文件是否完整 (only for wma, mp3始终返回 true)
//
// $AE->CheckSize($file);
//
// 2. 读取信息, 返回值由信息组成的数组, 键名解释参见上方
//
// print_r($AE->GetInfo($file));
//
// 3. 写入信息, 第二参数是一个哈希数组, 键->值, 支持的参见上方的, mp3也支持 Track
// 要求第一参数的文件路径可由本程序写入
// $pa = array(&#39;Title&#39; => &#39;新标题&#39;, &#39;AlbumTitle&#39; => &#39;新的专辑名称&#39;);
// $AE->SetInfo($file, $pa);
//
// 其它: 该插件花了不少时间搜集查找 wma及mp3 的文件格式说明文档与网页, 希望对大家有用.
// 其实网上已经有不少类似的程序, 但对 wma 实在太少了, 只能在 win 平台下通过 M$ 的
// API 来操作, 而 MP3 也很少有可以在 unix/linux 命令行操作的, 所以特意写了这个模块
//
// 如果发现 bug 或提交 patch, 或加以改进使它更加健壮, 请告诉我.
// (关于 ID3和Wma的文件格式及结构 在网上应该都可以找到参考资料)
//
if (!extension_loaded(&#39;mbstring&#39;)){
trigger_error(&#39;PHP Extension module `mbstring` is required for AudioExif&#39;, E_USER_WARNING);
return true;
}
// the Main Class
class AudioExif{
// public vars
var $_wma = false;
var $_mp3 = false;
// Construct
function AudioExif() {
// nothing to do
}
// check the filesize
function CheckSize($file) {
$handler = &$this->_get_handler($file);
if (!$handler) return false;
return $handler->check_size($file);
}
// get the infomations
function GetInfo($file) {
$handler = &$this->_get_handler($file);
if (!$handler) return false;
return $handler->get_info($file);
}
// write the infomations
function SetInfo($file, $pa) {
if (!is_writable($file)) {
trigger_error(&#39;AudioExif: file `&#39; . $file . &#39;` can not been overwritten&#39;, E_USER_WARNING);
return false;
}
$handler = &$this->_get_handler($file);
if (!$handler) return false;
return $handler->set_info($file, $pa);
}
// private methods
function &_get_handler($file) {
$ext = strtolower(strrchr($file, &#39;.&#39;));
$ret = false;
if ($ext == &#39;.mp3&#39;) {
// MP3
$ret = &$this->_mp3;
if (!$ret) $ret = new _Mp3Exif();

}
else if ($ext == &#39;.wma&#39;)
{ // wma
$ret = &$this->_wma;
if (!$ret) $ret = new _WmaExif();
}
else
{ // unknown
trigger_error(&#39;AudioExif not supported `&#39; . $ext . &#39;` file.&#39;, E_USER_WARNING);
}
return $ret;
}
}

// DBCS => gb2312
function dbcs_gbk($str)
{
// strip the last "\0\0"
$str = substr($str, 0, -2);
return mb_convert_encoding($str, &#39;GBK&#39;, &#39;UCS-2LE&#39;);
}

// gb2312 => DBCS
function gbk_dbcs($str)
{
$str = mb_convert_encoding($str, &#39;UCS-2LE&#39;, &#39;GBK&#39;);
$str .= "\0\0";
return $str;
}

// file exif
class _AudioExif
{
var $fd;
var $head;
var $head_off;
var $head_buf;

// init the file handler
function _file_init($fpath, $write = false)
{
$mode = ($write ? &#39;rb+&#39; : &#39;rb&#39;);
$this->fd = @fopen($fpath, $mode);
if (!$this->fd)
{
trigger_error(&#39;AudioExif: `&#39; . $fpath . &#39;` can not be opened with mode `&#39; . $mode . &#39;`&#39;, E_USER_WARNING);
return false;
}
$this->head = false;
$this->head_off = 0;
$this->head_buf = &#39;&#39;;
return true;
}

// read buffer from the head_buf & move the off pointer
function _read_head_buf($len)
{
if ($len <= 0) return NULL;
$buf = substr($this->head_buf, $this->head_off, $len);
$this->head_off += strlen($buf);
return $buf;
}

// read one short value
function _read_head_short()
{
$ord1 = ord(substr($this->head_buf, $this->head_off, 1));
$ord2 = ord(substr($this->head_buf, $this->head_off+1, 1));
$this->head_off += 2;
return ($ord1 + ($ord2<<8));
}

// save the file head
function _file_save($head, $olen, $nlen = 0)
{
if ($nlen == 0) $nlen = strlen($head);
if ($nlen == $olen)
{
// shorter
flock($this->fd, LOCK_EX);
fseek($this->fd, 0, SEEK_SET);
fwrite($this->fd, $head, $nlen);
flock($this->fd, LOCK_UN);
}
else
{
// longer, buffer required
$stat = fstat($this->fd);
$fsize = $stat[&#39;size&#39;];

// buf required (4096?) 应该不会 nlen - olen > 4096 吧
$woff = 0;
$roff = $olen;

// read first buffer
flock($this->fd, LOCK_EX);
fseek($this->fd, $roff, SEEK_SET);
$buf = fread($this->fd, 4096);

// seek to start
fseek($this->fd, $woff, SEEK_SET);
fwrite($this->fd, $head, $nlen);
$woff += $nlen;

// seek to woff & write the data
do
{
$buf2 = $buf;
$roff += 4096;
if ($roff < $fsize)
{
fseek($this->fd, $roff, SEEK_SET);
$buf = fread($this->fd, 4096);
}

// save last buffer
$len2 = strlen($buf2);
fseek($this->fd, $woff, SEEK_SET);
fwrite($this->fd, $buf2, $len2);
$woff += $len2;
}
while ($roff < $fsize);
ftruncate($this->fd, $woff);
flock($this->fd, LOCK_UN);
}
}

// close the file
function _file_deinit()
{
if ($this->fd)
{
fclose($this->fd);
$this->fd = false;
}
}
}

// wma class
class _WmaExif extends _AudioExif
{
var $items1 = array(&#39;Title&#39;, &#39;Artist&#39;, &#39;Copyright&#39;, &#39;Description&#39;, &#39;Reserved&#39;);
var $items2 = array(&#39;Year&#39;, &#39;Genre&#39;, &#39;AlbumTitle&#39;);

// check file size (length) maybe invalid file
function check_size($file)
{
$ret = false;
if (!$this->_file_init($file)) return true;
if ($this->_init_header())
{
$buf = fread($this->fd, 24);
$tmp = unpack(&#39;H32id/Vlen/H8unused&#39;, $buf);
if ($tmp[&#39;id&#39;] == &#39;3626b2758e66cf11a6d900aa0062ce6c&#39;)
{
$stat = fstat($this->fd);
$ret = ($stat[&#39;size&#39;] == ($this->head[&#39;len&#39;] + $tmp[&#39;len&#39;]));
}
}
$this->_file_deinit();
return $ret;
}

// set info (save the infos)
function set_info($file, $pa)
{
// check the pa
settype($pa, &#39;array&#39;);
if (!$this->_file_init($file, true)) return false;
if (!$this->_init_header())
{
$this->_file_deinit();
return false;
}

// parse the old header & generate the new header
$head_body = &#39;&#39;;
$st_found = $ex_found = false;
$head_num = $this->head[&#39;num&#39;];
while (($tmp = $this->_get_head_frame()) && ($head_num > 0))
{
$head_num--;
if ($tmp[&#39;id&#39;] == &#39;3326b2758e66cf11a6d900aa0062ce6c&#39;)
{ // Standard Info
// 1-4
$st_found = true;
$st_body1 = $st_body2 = &#39;&#39;;
$lenx = unpack(&#39;v5&#39;, $this->_read_head_buf(10));
$tmp[&#39;len&#39;] -= 34; // 10 + 24
for ($i = 0; $i < count($this->items1); $i++)
{
$l = $lenx[$i+1];
$k = $this->items1[$i];
$tmp[&#39;len&#39;] -= $l;

$data = $this->_read_head_buf($l);
if (isset($pa[$k])) $data = gbk_dbcs($pa[$k]);

$st_body2 .= $data;
$st_body1 .= pack(&#39;v&#39;, strlen($data));
}
// left length
if ($tmp[&#39;len&#39;] > 0) $st_body2 .= $this->_read_head_buf($tmp[&#39;len&#39;]);

// save to head_body
$head_body .= pack(&#39;H32VH8&#39;, $tmp[&#39;id&#39;], strlen($st_body1 . $st_body2)+24, $tmp[&#39;unused&#39;]);
$head_body .= $st_body1 . $st_body2;

$st_body2 .= $data;
}

// save to head_body
$head_body .= pack(&#39;H32Va4&#39;, &#39;3326b2758e66cf11a6d900aa0062ce6c&#39;, strlen($st_body1 . $st_body2)+24, &#39;&#39;);
$head_body .= $st_body1 . $st_body2;
$this->head[&#39;num&#39;]++;
}
// ex not found?
if (!$ex_found)
{
$inum = 0;
$et_body = &#39;&#39;;
foreach ($this->items2 as $k)
{
$nbuf = gbk_dbcs(&#39;WM/&#39; . $k);
$vbuf = (isset($pa[$k]) ? gbk_dbcs($pa[$k]) : "");
$et_body .= pack(&#39;v&#39;, strlen($nbuf)) . $nbuf . pack(&#39;vv&#39;, 0, strlen($vbuf)) . $vbuf;
$inum++;
}
$head_body .= pack(&#39;H32Va4v&#39;, &#39;40a4d0d207e3d21197f000a0c95ea850&#39;, strlen($et_body)+26, &#39;&#39;, $inum);
$head_body .= $et_body;
$this->head[&#39;num&#39;]++;
}

// after save
$new_len = strlen($head_body) + 30;
$old_len = $this->head[&#39;len&#39;];
if ($new_len < $old_len)
{
$head_body .= str_repeat("\0", $old_len - $new_len);
$new_len = $old_len;
}
$tmp = $this->head;
$head_buf = pack(&#39;H32VVVH4&#39;, $tmp[&#39;id&#39;], $new_len, $tmp[&#39;len2&#39;], $tmp[&#39;num&#39;], $tmp[&#39;unused&#39;]);
$head_buf .= $head_body;
$this->_file_save($head_buf, $old_len, $new_len);

// close the file & return
$this->_file_deinit();
return true;
}

// get info
function get_info($file)
{
$ret = array();
if (!$this->_file_init($file)) return false;
if (!$this->_init_header())
{
$this->_file_deinit();
return false;
}

// get the data from head_buf
$head_num = $this->head[&#39;num&#39;]; // num of head_frame
while (($tmp = $this->_get_head_frame()) && $head_num > 0)
{
$head_num--;
if ($tmp[&#39;id&#39;] == &#39;3326b2758e66cf11a6d900aa0062ce6c&#39;)
{ // Standard Info
$lenx = unpack(&#39;v*&#39;, $this->_read_head_buf(10));
for ($i = 1; $i <= count($this->items1); $i++)
{
$k = $this->items1[$i-1];
$ret[$k] = dbcs_gbk($this->_read_head_buf($lenx[$i]));
}
}
else if ($tmp[&#39;id&#39;] == &#39;40a4d0d207e3d21197f000a0c95ea850&#39;)
{ // Extended Info
$inum = $this->_read_head_short();
$tmp[&#39;len&#39;] -= 26;
while ($inum > 0 && $tmp[&#39;len&#39;] > 0)
{
// attribute name
$nlen = $this->_read_head_short();
$nbuf = $this->_read_head_buf($nlen);

// the flag & value length
$flag = $this->_read_head_short();
$vlen = $this->_read_head_short();
$vbuf = $this->_read_head_buf($vlen);

// update the XX

$tmp[&#39;len&#39;] -= (6 + $nlen + $vlen);
$inum--;

$name = dbcs_gbk($nbuf);
$k = substr($name, 3);
if (in_array($k, $this->items2))
{ // all is string value (refer to falg for other tags)
$ret[$k] = dbcs_gbk($vbuf);
}
}
}

else
{ // skip only
if ($tmp[&#39;len&#39;] > 24) $this->head_off += ($tmp[&#39;len&#39;] - 24);
}
}
$this->_file_deinit();
return $ret;
}

// get the header?
function _init_header()
{
fseek($this->fd, 0, SEEK_SET);
$buf = fread($this->fd, 30);
if (strlen($buf) != 30) return false;
$tmp = unpack(&#39;H32id/Vlen/Vlen2/Vnum/H4unused&#39;, $buf);
if ($tmp[&#39;id&#39;] != &#39;3026b2758e66cf11a6d900aa0062ce6c&#39;)
return false;

$this->head_buf = fread($this->fd, $tmp[&#39;len&#39;] - 30);
$this->head = $tmp;
return true;
}

// _get_head_frame()
function _get_head_frame()
{
$buf = $this->_read_head_buf(24);
if (strlen($buf) != 24) return false;
$tmp = unpack(&#39;H32id/Vlen/H8unused&#39;, $buf);
return $tmp;
}
}

// mp3 class (if not IDv2 then select IDv1)
class _Mp3Exif extends _AudioExif
{
var $head1;
var $genres = array(&#39;Blues&#39;,&#39;Classic Rock&#39;,&#39;Country&#39;,&#39;Dance&#39;,&#39;Disco&#39;,&#39;Funk&#39;,&#39;Grunge&#39;,&#39;Hip-Hop&#39;,&#39;Jazz&#39;,&#39;Metal&#39;,&#39;New Age&#39;,&#39;Oldies&#39;,&#39;Other&#39;,&#39;Pop&#39;,&#39;R&B&#39;,&#39;Rap&#39;,&#39;Reggae&#39;,&#39;Rock&#39;,&#39;Techno&#39;,&#39;Industrial&#39;,&#39;Alternative&#39;,&#39;Ska&#39;,&#39;Death Metal&#39;,&#39;Pranks&#39;,&#39;Soundtrack&#39;,&#39;Euro-Techno&#39;,&#39;Ambient&#39;,&#39;Trip-Hop&#39;,&#39;Vocal&#39;,&#39;Jazz+Funk&#39;,&#39;Fusion&#39;,&#39;Trance&#39;,&#39;Classical&#39;,&#39;Instrumental&#39;,&#39;Acid&#39;,&#39;House&#39;,&#39;Game&#39;,&#39;Sound Clip&#39;,&#39;Gospel&#39;,&#39;Noise&#39;,&#39;AlternRock&#39;,&#39;Bass&#39;,&#39;Soul&#39;,&#39;Punk&#39;,&#39;Space&#39;,&#39;Meditative&#39;,&#39;Instrumental Pop&#39;,&#39;Instrumental Rock&#39;,&#39;Ethnic&#39;,&#39;Gothic&#39;,&#39;Darkwave&#39;,&#39;Techno-Industrial&#39;,&#39;Electronic&#39;,&#39;Pop-Folk&#39;,&#39;Eurodance&#39;,&#39;Dream&#39;,&#39;Southern Rock&#39;,&#39;Comedy&#39;,&#39;Cult&#39;,&#39;Gangsta&#39;,&#39;Top 40&#39;,&#39;Christian Rap&#39;,&#39;Pop/Funk&#39;,&#39;Jungle&#39;,&#39;Native American&#39;,&#39;Cabaret&#39;,&#39;New Wave&#39;,&#39;Psychadelic&#39;,&#39;Rave&#39;,&#39;Showtunes&#39;,&#39;Trailer&#39;,&#39;Lo-Fi&#39;,&#39;Tribal&#39;,&#39;Acid Punk&#39;,&#39;Acid Jazz&#39;,&#39;Polka&#39;,&#39;Retro&#39;,&#39;Musical&#39;,&#39;Rock & Roll&#39;,&#39;Hard Rock&#39;,&#39;Unknown&#39;);

// MP3 always return true
function check_size($file)
{
return true;
}

// get info
function get_info($file)
{
if (!$this->_file_init($file)) return false;
$ret = false;
if ($this->_init_header())
{
$ret = ($this->head ? $this->_get_v2_info() : $this->_get_v1_info());
$ret[&#39;meta&#39;] = $this->_get_meta_info();
}
$this->_file_deinit();
return $ret;
}

// set info
function set_info($file, $pa)
{
if (!$this->_file_init($file, true)) return false;
if ($this->_init_header())
{
// always save v1 info
$this->_set_v1_info($pa);
// set v2 first if need
$this->_set_v2_info($pa);
}
$this->_file_deinit();
return true;
}

// get the header information[v1+v2], call after file_init
function _init_header()
{
$this->head1 = false;
$this->head = false;

// try to get ID3v1 first
fseek($this->fd, -128, SEEK_END);
$buf = fread($this->fd, 128);

if (strlen($buf) == 128 && substr($buf, 0, 3) == &#39;TAG&#39;)
{
$tmp = unpack(&#39;a3id/a30Title/a30Artist/a30AlbumTitle/a4Year/a28Description/CReserved/CTrack/CGenre&#39;, $buf);
$this->head1 = $tmp;
}

// try to get ID3v2
fseek($this->fd, 0, SEEK_SET);
$buf = fread($this->fd, 10);
if (strlen($buf) == 10 && substr($buf, 0, 3) == &#39;ID3&#39;)
{
$tmp = unpack(&#39;a3id/Cver/Crev/Cflag/C4size&#39;, $buf);
$tmp[&#39;size&#39;] = ($tmp[&#39;size1&#39;]<<21)|($tmp[&#39;size2&#39;]<<14)|($tmp[&#39;size3&#39;]<<7)|$tmp[&#39;size4&#39;];
unset($tmp[&#39;size1&#39;], $tmp[&#39;size2&#39;], $tmp[&#39;size3&#39;], $tmp[&#39;size4&#39;]);

$this->head = $tmp;
$this->head_buf = fread($this->fd, $tmp[&#39;size&#39;]);
}
return ($this->head1 || $this->head);
}

// get v1 info
function _get_v1_info()
{
$ret = array();
$tmpa = array(&#39;Title&#39;, &#39;Artist&#39;, &#39;Copyright&#39;, &#39;Description&#39;, &#39;Year&#39;, &#39;AlbumTitle&#39;);
foreach ($tmpa as $tmp)
{
$ret[$tmp] = $this->head1[$tmp];
if ($pos = strpos($ret[$tmp], "\0"))
$ret[$tmp] = substr($ret[$tmp], 0, $pos);
}

// count the Genre, [Track]
if ($this->head1[&#39;Reserved&#39;] == 0) $ret[&#39;Track&#39;] = $this->head1[&#39;Track&#39;];
else $ret[&#39;Description&#39;] .= chr($ret[&#39;Reserved&#39;]) . chr($ret[&#39;Track&#39;]);

// Genre_idx
$g = $this->head1[&#39;Genre&#39;];
if (!isset($this->genres[$g])) $ret[&#39;Genre&#39;] = &#39;Unknown&#39;;
else $ret[&#39;Genre&#39;] = $this->genres[$g];

// return the value
$ret[&#39;ID3v1&#39;] = &#39;yes&#39;;
return $ret;
}

// get v2 info
function _get_v2_info()
{
$ret = array();
$items = array( &#39;TCOP&#39;=>&#39;Copyright&#39;, &#39;TPE1&#39;=>&#39;Artist&#39;, &#39;TIT2&#39;=>&#39;Title&#39;, &#39;TRCK&#39;=> &#39;Track&#39;,
&#39;TCON&#39;=>&#39;Genre&#39;, &#39;COMM&#39;=>&#39;Description&#39;, &#39;TYER&#39;=>&#39;Year&#39;, &#39;TALB&#39;=>&#39;AlbumTitle&#39;);
while (true)
{
$buf = $this->_read_head_buf(10);
if (strlen($buf) != 10) break;
$tmp = unpack(&#39;a4fid/Nsize/nflag&#39;, $buf);
if ($tmp[&#39;size&#39;] == 0) break;
$tmp[&#39;dat&#39;] = $this->_read_head_buf($tmp[&#39;size&#39;]);

// 0x6000 (11000000 00000000)
if ($tmp[&#39;flag&#39;] & 0x6000) continue;

// mapping the data
if ($k = $items[$tmp[&#39;fid&#39;]])
{ // If first char is "\0", just skip
if (substr($tmp[&#39;dat&#39;], 0, 1) == "\0") $tmp[&#39;dat&#39;] = substr($tmp[&#39;dat&#39;], 1);
$ret[$k] = $tmp[&#39;dat&#39;];
}
}

// reset the genre
if ($g = $ret[&#39;Genre&#39;])
{
if (substr($g,0,1) == &#39;(&#39; && substr($g,-1,1) == &#39;)&#39;) $g = substr($g, 1, -1);
if (is_numeric($g))
{
$g = intval($g);
$ret[&#39;Genre&#39;] = (isset($this->genres[$g]) ? $this->genres[$g] : &#39;Unknown&#39;);
}
}

$ret[&#39;ID3v1&#39;] = &#39;no&#39;;
return $ret;
}
// get meta info of MP3
function _get_meta_info()
{
// seek to the lead buf: 0xff
$off = 0;
if ($this->head) $off = $this->head[&#39;size&#39;] + 10;
fseek($this->fd, $off, SEEK_SET);
while (!feof($this->fd))
{
$skip = ord(fread($this->fd, 1));
if ($skip == 0xff) break;
}
if ($skip != 0xff) return false;
$buf = fread($this->fd, 3);
if (strlen($buf) != 3) return false;
$tmp = unpack(&#39;C3&#39;, $buf);
if (($tmp[1] & 0xf0) != 0xf0) return false;
// get the meta info
$meta = array();
// get mpeg version
$meta[&#39;mpeg&#39;] = ($tmp[1] & 0x08 ? 1 : 2);
$meta[&#39;layer&#39;] = ($tmp[1] & 0x04) ? (($tmp[1] & 0x02) ? 1 : 2) : (($tmp[1] & 0x02) ? 3 : 0);
$meta[&#39;epro&#39;] = ($tmp[1] & 0x01) ? &#39;no&#39; : &#39;yes&#39;;
// bit rates
$bit_rates = array(
1 => array(
1 => array(0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),
2 => array(0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0),
3 => array(0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0)
),
2 => array(
1 => array(0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0),
2 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0),
3 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0)
)
);
$i = $meta[&#39;mpeg&#39;];
$j = $meta[&#39;layer&#39;];
$k = ($tmp[2]>>4);
$meta[&#39;bitrate&#39;] = $bit_rates[$i][$j][$k];
// sample rates <采样率>
$sam_rates = array(1=>array(44100,48000,32000,0), 2=>array(22050,24000,16000,0));
$meta[&#39;samrate&#39;] = $sam_rates[$i][$k];
$meta["padding"] = ($tmp[2] & 0x02) ? &#39;on&#39; : &#39;off&#39;;
$meta["private"] = ($tmp[2] & 0x01) ? &#39;on&#39; : &#39;off&#39;;
// mode & mode_ext
$k = ($tmp[3]>>6);
$channel_modes = array(&#39;stereo&#39;, &#39;joint stereo&#39;, &#39;dual channel&#39;, &#39;single channel&#39;);
$meta[&#39;mode&#39;] = $channel_modes[$k];
$k = (($tmp[3]>>4) & 0x03);
$extend_modes = array(&#39;MPG_MD_LR_LR&#39;, &#39;MPG_MD_LR_I&#39;, &#39;MPG_MD_MS_LR&#39;, &#39;MPG_MD_MS_I&#39;);
$meta[&#39;ext_mode&#39;] = $extend_modes[$k];
$meta[&#39;copyright&#39;] = ($tmp[3] & 0x08) ? &#39;yes&#39; : &#39;no&#39;;
$meta[&#39;original&#39;] = ($tmp[3] & 0x04) ? &#39;yes&#39; : &#39;no&#39;;
$emphasis = array(&#39;none&#39;, &#39;50/15 microsecs&#39;, &#39;rreserved&#39;, &#39;CCITT J 17&#39;);
$k = ($tmp[3] & 0x03);
$meta[&#39;emphasis&#39;] = $emphasis[$k];
return $meta;
}
// set v1 info
function _set_v1_info($pa)
{
// ID3v1 (simpled)
$off = -128;
if (!($tmp = $this->head1))
{
$off = 0;
$tmp[&#39;id&#39;] = &#39;TAG&#39;;
$tmp[&#39;Title&#39;] = $tmp[&#39;Artist&#39;] = $tmp[&#39;AlbumTitle&#39;] = $tmp[&#39;Year&#39;] = $tmp[&#39;Description&#39;] = &#39;&#39;;
$tmp[&#39;Reserved&#39;] = $tmp[&#39;Track&#39;] = $tmp[&#39;Genre&#39;] = 0;
}

// basic items
$items = array(&#39;Title&#39;, &#39;Artist&#39;, &#39;Copyright&#39;, &#39;Description&#39;, &#39;Year&#39;, &#39;AlbumTitle&#39;);
foreach ($items as $k)
{
if (isset($pa[$k])) $tmp[$k] = $pa[$k];
}
// genre index
if (isset($pa[&#39;Genre&#39;]))
{
$g = 0;
foreach ($this->genres as $gtmp)
{
if (!strcasecmp($gtmp, $pa[&#39;Genre&#39;]))
break;
$g++;
}
$tmp[&#39;Genre&#39;] = $g;
}
if (isset($pa[&#39;Track&#39;])) $tmp[&#39;Track&#39;] = intval($pa[&#39;Track&#39;]);
// pack the data
$buf = pack(&#39;a3a30a30a30a4a28CCC&#39;, $tmp[&#39;id&#39;], $tmp[&#39;Title&#39;], $tmp[&#39;Artist&#39;], $tmp[&#39;AlbumTitle&#39;],
$tmp[&#39;Year&#39;], $tmp[&#39;Description&#39;], 0, $tmp[&#39;Track&#39;], $tmp[&#39;Genre&#39;]);
flock($this->fd, LOCK_EX);
fseek($this->fd, $off, SEEK_END);
fwrite($this->fd, $buf, 128);
flock($this->fd, LOCK_UN);
}
// set v2 info
function _set_v2_info($pa)
{
if (!$this->head)
{ // insert ID3
return; // 没有就算了
/**
$tmp = array(&#39;id&#39;=>&#39;ID3&#39;,&#39;ver&#39;=>3,&#39;rev&#39;=>0,&#39;flag&#39;=>0);
$tmp[&#39;size&#39;] = -10; // +10 => 0
$this->head = $tmp;
$this->head_buf = &#39;&#39;;
$this->head_off = 0;
**/
}
$items = array( &#39;TCOP&#39;=>&#39;Copyright&#39;, &#39;TPE1&#39;=>&#39;Artist&#39;, &#39;TIT2&#39;=>&#39;Title&#39;, &#39;TRAC&#39;=>&#39;Track&#39;,
&#39;TCON&#39;=>&#39;Genre&#39;, &#39;COMM&#39;=>&#39;Description&#39;, &#39;TYER&#39;=>&#39;Year&#39;, &#39;TALB&#39;=>&#39;AlbumTitle&#39;);
$head_body = &#39;&#39;;
while (true)
{
$buf = $this->_read_head_buf(10);
if (strlen($buf) != 10) break;
$tmp = unpack(&#39;a4fid/Nsize/nflag&#39;, $buf);
if ($tmp[&#39;size&#39;] == 0) break;
$data = $this->_read_head_buf($tmp[&#39;size&#39;]);
if (($k = $items[$tmp[&#39;fid&#39;]]) && isset($pa[$k]))
{
// the data should prefix by "\0" [replace]
$data = "\0" . $pa[$k];
unset($pa[$k]);
}
$head_body .= pack(&#39;a4Nn&#39;, $tmp[&#39;fid&#39;], strlen($data), $tmp[&#39;flag&#39;]) . $data;
}
// reverse the items & set the new tags
$items = array_flip($items);
foreach ($pa as $k => $v)
{
if ($fid = $items[$k])
{
$head_body .= pack(&#39;a4Nn&#39;, $fid, strlen($v) + 1, 0) . "\0" . $v;
}
}
// new length
$new_len = strlen($head_body) + 10;
$old_len = $this->head[&#39;size&#39;] + 10;
if ($new_len < $old_len)
{
$head_body .= str_repeat("\0", $old_len - $new_len);
$new_len = $old_len;
}
// count the size1,2,3,4, no include the header
// 较为变态的算法... :p (28bytes integer)
$size = array();
$nlen = $new_len - 10;
for ($i = 4; $i > 0; $i--)
{
$size[$i] = ($nlen & 0x7f);
$nlen >>= 7;
}
$tmp = $this->head;
//echo "old_len : $old_len new_len: $new_len\n";
$head_buf = pack(&#39;a3CCCCCCC&#39;, $tmp[&#39;id&#39;], $tmp[&#39;ver&#39;], $tmp[&#39;rev&#39;], $tmp[&#39;flag&#39;],
$size[1], $size[2], $size[3], $size[4]);
$head_buf .= $head_body;
$this->_file_save($head_buf, $old_len, $new_len);
}

相关推荐:

PHP Streams流详解

php 断点续传下载功能

php 文件分割与合并(断点续传)

以上がPHPはMP3のメディアタグ情報を取得しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。