当前位置:首页 > PHP教程 > php应用 > 列表

PHP获取音频文件的相关信息

发布:smiling 来源: PHP粉丝网  添加日期:2021-06-02 10:09:27 浏览: 评论:0 

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

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

  1. <?php 
  2. // AudioExif.class.php 
  3. // 用PHP进行音频文件头部信息的读取与写入 
  4. // 目前只支持 WMA 和 MP3 两种格式, 只支持常用的几个头部信息 
  5. // 
  6. // 写入信息支持: Title(名称), Artist(艺术家), Copyright(版权), Description (描述) 
  7. // Year(年代), Genre (流派), AlbumTitle (专辑标题) 
  8. // 其中 mp3 和 wma 略有不同, 具体返回的信息还可能更多, 但只有以上信息可以被写入 
  9. // mp3 还支持 Track (曲目编号写入) 
  10. // 对于 MP3 文件支持 ID3v1也支持ID3v2, 读取时优先 v2, 写入时总是会写入v1, 必要时写入v2 
  11. // 
  12. // 用法说明: (由于 wma 使用 Unicode 存取, 故还需要 mb_convert_encoding() 扩展 
  13. // 返回数据及写入数据均为 ANSI 编码, 即存什么就显示什么 (中文_GB2312) 
  14. // 
  15. // require ('AudioExif.class.php'); 
  16. // $AE = new AudioExif; 
  17. // $file = '/path/to/test.mp3'; 
  18. // 
  19. // 1. 检查文件是否完整 (only for wma, mp3始终返回 true) 
  20. // 
  21. // $AE->CheckSize($file); 
  22. // 
  23. // 2. 读取信息, 返回值由信息组成的数组, 键名解释参见上方 
  24. // 
  25. // print_r($AE->GetInfo($file)); 
  26. // 
  27. // 3. 写入信息, 第二参数是一个哈希数组, 键->值, 支持的参见上方的, mp3也支持 Track 
  28. // 要求第一参数的文件路径可由本程序写入 
  29. // $pa = array('Title' => '新标题', 'AlbumTitle' => '新的专辑名称'); 
  30. // $AE->SetInfo($file, $pa); 
  31. // 
  32. // 其它: 该插件花了不少时间搜集查找 wma及mp3 的文件格式说明文档与网页, 希望对大家有用. 
  33. // 其实网上已经有不少类似的程序, 但对 wma 实在太少了, 只能在 win 平台下通过 M$ 的 
  34. // API 来操作, 而 MP3 也很少有可以在 unix/linux 命令行操作的, 所以特意写了这个模块 
  35. // 
  36. // 如果发现 bug 或提交 patch, 或加以改进使它更加健壮, 请告诉我. 
  37. // (关于 ID3和Wma的文件格式及结构 在网上应该都可以找到参考资料) 
  38. // 
  39. if (!extension_loaded('mbstring')){ 
  40. trigger_error('PHP Extension module `mbstring` is required for AudioExif', E_USER_WARNING); 
  41. return true; 
  42. // the Main Class 
  43. class AudioExif{ 
  44. // public vars 
  45. var $_wma = false; 
  46. var $_mp3 = false; 
  47. // Construct 
  48. function AudioExif() { 
  49. // nothing to do 
  50. // check the filesize 
  51. function CheckSize($file) { 
  52. $handler = &$this->_get_handler($file); 
  53. if (!$handlerreturn false; 
  54. return $handler->check_size($file); 
  55. // get the infomations 
  56. function GetInfo($file) { 
  57. $handler = &$this->_get_handler($file); 
  58. if (!$handlerreturn false; 
  59. return $handler->get_info($file); 
  60. // write the infomations 
  61. function SetInfo($file$pa) { 
  62. if (!is_writable($file)) { 
  63. trigger_error('AudioExif: file `' . $file . '` can not been overwritten', E_USER_WARNING); 
  64. return false; 
  65. $handler = &$this->_get_handler($file); 
  66. if (!$handlerreturn false; 
  67. return $handler->set_info($file$pa); 
  68. // private methods 
  69. function &_get_handler($file) { 
  70. $ext = strtolower(strrchr($file'.')); 
  71. $ret = false; 
  72. if ($ext == '.mp3') { 
  73. // MP3 
  74. $ret = &$this->_mp3; 
  75. if (!$ret$ret = new _Mp3Exif(); 
  76.  
  77. else if ($ext == '.wma'
  78. // wma 
  79. $ret = &$this->_wma; 
  80. if (!$ret$ret = new _WmaExif(); 
  81. else 
  82. // unknown 
  83. trigger_error('AudioExif not supported `' . $ext . '` file.', E_USER_WARNING); 
  84. return $ret
  85.  
  86. // DBCS => gb2312 
  87. function dbcs_gbk($str
  88. // strip the last "\0\0" 
  89. $str = substr($str, 0, -2); 
  90. return mb_convert_encoding($str'GBK''UCS-2LE'); 
  91.  
  92. // gb2312 => DBCS 
  93. function gbk_dbcs($str
  94. $str = mb_convert_encoding($str'UCS-2LE''GBK'); 
  95. $str .= "\0\0"
  96. return $str
  97.  
  98. // file exif 
  99. class _AudioExif 
  100. var $fd
  101. var $head
  102. var $head_off
  103. var $head_buf
  104.  
  105. // init the file handler 
  106. function _file_init($fpath$write = false) 
  107. $mode = ($write ? 'rb+' : 'rb'); 
  108. $this->fd = @fopen($fpath$mode); 
  109. if (!$this->fd) 
  110. trigger_error('AudioExif: `' . $fpath . '` can not be opened with mode `' . $mode . '`', E_USER_WARNING); 
  111. return false; 
  112. $this->head = false; 
  113. $this->head_off = 0; 
  114. $this->head_buf = ''
  115. return true; 
  116.  
  117. // read buffer from the head_buf & move the off pointer 
  118. function _read_head_buf($len
  119. if ($len <= 0) return NULL; 
  120. $buf = substr($this->head_buf, $this->head_off, $len); 
  121. $this->head_off += strlen($buf); 
  122. return $buf
  123.  
  124. // read one short value 
  125. function _read_head_short() 
  126. $ord1 = ord(substr($this->head_buf, $this->head_off, 1)); 
  127. $ord2 = ord(substr($this->head_buf, $this->head_off+1, 1)); 
  128. $this->head_off += 2; 
  129. return ($ord1 + ($ord2<<8)); 
  130.  
  131. // save the file head 
  132. function _file_save($head$olen$nlen = 0) 
  133. if ($nlen == 0) $nlen = strlen($head); 
  134. if ($nlen == $olen
  135. // shorter 
  136. flock($this->fd, LOCK_EX); 
  137. fseek($this->fd, 0, SEEK_SET); 
  138. fwrite($this->fd, $head$nlen); 
  139. flock($this->fd, LOCK_UN); 
  140. else 
  141. // longer, buffer required 
  142. $stat = fstat($this->fd); 
  143. $fsize = $stat['size']; 
  144.  
  145. // buf required (4096?) 应该不会 nlen - olen > 4096 吧 
  146. $woff = 0; 
  147. $roff = $olen
  148.  
  149. // read first buffer 
  150. flock($this->fd, LOCK_EX); 
  151. fseek($this->fd, $roff, SEEK_SET); 
  152. $buf = fread($this->fd, 4096); 
  153.  
  154. // seek to start 
  155. fseek($this->fd, $woff, SEEK_SET); 
  156. fwrite($this->fd, $head$nlen); 
  157. $woff += $nlen
  158.  
  159. // seek to woff & write the data 
  160. do 
  161. $buf2 = $buf
  162. $roff += 4096; 
  163. if ($roff < $fsize
  164. fseek($this->fd, $roff, SEEK_SET); 
  165. $buf = fread($this->fd, 4096); 
  166.  
  167. // save last buffer 
  168. $len2 = strlen($buf2); 
  169. fseek($this->fd, $woff, SEEK_SET); 
  170. fwrite($this->fd, $buf2$len2); 
  171. $woff += $len2
  172. while ($roff < $fsize); 
  173. ftruncate($this->fd, $woff); 
  174. flock($this->fd, LOCK_UN); 
  175.  
  176. // close the file 
  177. function _file_deinit() 
  178. if ($this->fd) 
  179. fclose($this->fd); 
  180. $this->fd = false; 
  181.  
  182. // wma class 
  183. class _WmaExif extends _AudioExif 
  184. var $items1 = array('Title''Artist''Copyright''Description''Reserved'); 
  185. var $items2 = array('Year''Genre''AlbumTitle'); 
  186.  
  187. // check file size (length) maybe invalid file 
  188. function check_size($file
  189. $ret = false; 
  190. if (!$this->_file_init($file)) return true; 
  191. if ($this->_init_header()) 
  192. $buf = fread($this->fd, 24); 
  193. $tmp = unpack('H32id/Vlen/H8unused'$buf); 
  194. if ($tmp['id'] == '3626b2758e66cf11a6d900aa0062ce6c'
  195. $stat = fstat($this->fd); 
  196. $ret = ($stat['size'] == ($this->head['len'] + $tmp['len'])); 
  197. $this->_file_deinit(); 
  198. return $ret
  199.  
  200. // set info (save the infos) 
  201. function set_info($file$pa
  202. // check the pa 
  203. settype($pa'array'); 
  204. if (!$this->_file_init($file, true)) return false; 
  205. if (!$this->_init_header()) 
  206. $this->_file_deinit(); 
  207. return false; 
  208.  
  209. // parse the old header & generate the new header 
  210. $head_body = ''
  211. $st_found = $ex_found = false; 
  212. $head_num = $this->head['num']; 
  213. while (($tmp = $this->_get_head_frame()) && ($head_num > 0)) 
  214. $head_num--; 
  215. if ($tmp['id'] == '3326b2758e66cf11a6d900aa0062ce6c'
  216. // Standard Info 
  217. // 1-4 
  218. $st_found = true; 
  219. $st_body1 = $st_body2 = ''
  220. $lenx = unpack('v5'$this->_read_head_buf(10)); 
  221. $tmp['len'] -= 34; // 10 + 24 
  222. for ($i = 0; $i < count($this->items1); $i++) 
  223. $l = $lenx[$i+1]; 
  224. $k = $this->items1[$i]; 
  225. $tmp['len'] -= $l
  226.  
  227. $data = $this->_read_head_buf($l); 
  228. if (isset($pa[$k])) $data = gbk_dbcs($pa[$k]); 
  229.  
  230. $st_body2 .= $data
  231. $st_body1 .= pack('v'strlen($data)); 
  232. // left length 
  233. if ($tmp['len'] > 0) $st_body2 .= $this->_read_head_buf($tmp['len']); 
  234.  
  235. // save to head_body 
  236. $head_body .= pack('H32VH8'$tmp['id'], strlen($st_body1 . $st_body2)+24, $tmp['unused']); 
  237. $head_body .= $st_body1 . $st_body2
  238.  
  239. $st_body2 .= $data
  240.  
  241. // save to head_body 
  242. $head_body .= pack('H32Va4''3326b2758e66cf11a6d900aa0062ce6c'strlen($st_body1 . $st_body2)+24, ''); 
  243. $head_body .= $st_body1 . $st_body2
  244. $this->head['num']++; 
  245. // ex not found? 
  246. if (!$ex_found
  247. $inum = 0; 
  248. $et_body = ''
  249. foreach ($this->items2 as $k
  250. $nbuf = gbk_dbcs('WM/' . $k); 
  251. $vbuf = (isset($pa[$k]) ? gbk_dbcs($pa[$k]) : ""); 
  252. $et_body .= pack('v'strlen($nbuf)) . $nbuf . pack('vv', 0, strlen($vbuf)) . $vbuf
  253. $inum++; 
  254. $head_body .= pack('H32Va4v''40a4d0d207e3d21197f000a0c95ea850'strlen($et_body)+26, ''$inum); 
  255. $head_body .= $et_body
  256. $this->head['num']++; 
  257.  
  258. // after save 
  259. $new_len = strlen($head_body) + 30; 
  260. $old_len = $this->head['len']; 
  261. if ($new_len < $old_len
  262. $head_body .= str_repeat("\0"$old_len - $new_len); 
  263. $new_len = $old_len
  264. $tmp = $this->head; 
  265. $head_buf = pack('H32VVVH4'$tmp['id'], $new_len$tmp['len2'], $tmp['num'], $tmp['unused']); 
  266. $head_buf .= $head_body
  267. $this->_file_save($head_buf$old_len$new_len); 
  268.  
  269. // close the file & return 
  270. $this->_file_deinit(); 
  271. return true; 
  272.  
  273. // get info 
  274. function get_info($file
  275. $ret = array(); 
  276. if (!$this->_file_init($file)) return false; 
  277. if (!$this->_init_header()) 
  278. $this->_file_deinit(); 
  279. return false; 
  280.  
  281. // get the data from head_buf 
  282. $head_num = $this->head['num']; // num of head_frame 
  283. while (($tmp = $this->_get_head_frame()) && $head_num > 0) 
  284. $head_num--; 
  285. if ($tmp['id'] == '3326b2758e66cf11a6d900aa0062ce6c'
  286. // Standard Info 
  287. $lenx = unpack('v*'$this->_read_head_buf(10)); 
  288. for ($i = 1; $i <= count($this->items1); $i++) 
  289. $k = $this->items1[$i-1]; 
  290. $ret[$k] = dbcs_gbk($this->_read_head_buf($lenx[$i])); 
  291. else if ($tmp['id'] == '40a4d0d207e3d21197f000a0c95ea850'
  292. // Extended Info 
  293. $inum = $this->_read_head_short(); 
  294. $tmp['len'] -= 26; 
  295. while ($inum > 0 && $tmp['len'] > 0) 
  296. // attribute name 
  297. $nlen = $this->_read_head_short(); 
  298. $nbuf = $this->_read_head_buf($nlen); 
  299.  
  300. // the flag & value length 
  301. $flag = $this->_read_head_short(); 
  302. $vlen = $this->_read_head_short(); 
  303. $vbuf = $this->_read_head_buf($vlen); 
  304.  
  305. // update the XX 
  306.  
  307. $tmp['len'] -= (6 + $nlen + $vlen); 
  308. $inum--; 
  309.  
  310. $name = dbcs_gbk($nbuf); 
  311. $k = substr($name, 3); 
  312. if (in_array($k$this->items2)) 
  313. // all is string value (refer to falg for other tags) 
  314. $ret[$k] = dbcs_gbk($vbuf); 
  315.  
  316. else 
  317. // skip only 
  318. if ($tmp['len'] > 24) $this->head_off += ($tmp['len'] - 24); 
  319. $this->_file_deinit(); 
  320. return $ret
  321.  
  322. // get the header? 
  323. function _init_header() 
  324. fseek($this->fd, 0, SEEK_SET); 
  325. $buf = fread($this->fd, 30); 
  326. if (strlen($buf) != 30) return false; 
  327. $tmp = unpack('H32id/Vlen/Vlen2/Vnum/H4unused'$buf); 
  328. if ($tmp['id'] != '3026b2758e66cf11a6d900aa0062ce6c'
  329. return false; 
  330.  
  331. $this->head_buf = fread($this->fd, $tmp['len'] - 30); 
  332. $this->head = $tmp
  333. return true; 
  334.  
  335. // _get_head_frame() 
  336. function _get_head_frame() 
  337. $buf = $this->_read_head_buf(24); 
  338. if (strlen($buf) != 24) return false; 
  339. $tmp = unpack('H32id/Vlen/H8unused'$buf); 
  340. return $tmp
  341.  
  342. // mp3 class (if not IDv2 then select IDv1) 
  343. class _Mp3Exif extends _AudioExif 
  344. var $head1
  345. var $genres = array('Blues','Classic Rock','Country','Dance','Disco','Funk','Grunge','Hip-Hop','Jazz','Metal','New Age','Oldies','Other','Pop','R&B','Rap','Reggae','Rock','Techno','Industrial','Alternative','Ska','Death Metal','Pranks','Soundtrack','Euro-Techno','Ambient','Trip-Hop','Vocal','Jazz+Funk','Fusion','Trance','Classical','Instrumental','Acid','House','Game','Sound Clip','Gospel','Noise','AlternRock','Bass','Soul','Punk','Space','Meditative','Instrumental Pop','Instrumental Rock','Ethnic','Gothic','Darkwave','Techno-Industrial','Electronic','Pop-Folk','Eurodance','Dream','Southern Rock','Comedy','Cult','Gangsta','Top 40','Christian Rap','Pop/Funk','Jungle','Native American','Cabaret','New Wave','Psychadelic','Rave','Showtunes','Trailer','Lo-Fi','Tribal','Acid Punk','Acid Jazz','Polka','Retro','Musical','Rock & Roll','Hard Rock','Unknown'); 
  346.  
  347. // MP3 always return true 
  348. function check_size($file
  349. return true; 
  350.  
  351. // get info 
  352. function get_info($file
  353. if (!$this->_file_init($file)) return false; 
  354. $ret = false; 
  355. if ($this->_init_header()) 
  356. $ret = ($this->head ? $this->_get_v2_info() : $this->_get_v1_info()); 
  357. $ret['meta'] = $this->_get_meta_info(); 
  358. $this->_file_deinit(); 
  359. return $ret
  360.  
  361. // set info 
  362. function set_info($file$pa
  363. if (!$this->_file_init($file, true)) return false; 
  364. if ($this->_init_header()) 
  365. // always save v1 info 
  366. $this->_set_v1_info($pa); 
  367. // set v2 first if need 
  368. $this->_set_v2_info($pa); 
  369. $this->_file_deinit(); 
  370. return true; 
  371.  
  372. // get the header information[v1+v2], call after file_init 
  373. function _init_header() 
  374. $this->head1 = false; 
  375. $this->head = false; 
  376.  
  377. // try to get ID3v1 first 
  378. fseek($this->fd, -128, SEEK_END); 
  379. $buf = fread($this->fd, 128); 
  380.  
  381. if (strlen($buf) == 128 && substr($buf, 0, 3) == 'TAG'
  382. $tmp = unpack('a3id/a30Title/a30Artist/a30AlbumTitle/a4Year/a28Description/CReserved/CTrack/CGenre'$buf); 
  383. $this->head1 = $tmp
  384.  
  385. // try to get ID3v2 
  386. fseek($this->fd, 0, SEEK_SET); 
  387. $buf = fread($this->fd, 10); 
  388. if (strlen($buf) == 10 && substr($buf, 0, 3) == 'ID3'
  389. $tmp = unpack('a3id/Cver/Crev/Cflag/C4size'$buf); 
  390. $tmp['size'] = ($tmp['size1']<<21)|($tmp['size2']<<14)|($tmp['size3']<<7)|$tmp['size4']; 
  391. unset($tmp['size1'], $tmp['size2'], $tmp['size3'], $tmp['size4']); 
  392.  
  393. $this->head = $tmp
  394. $this->head_buf = fread($this->fd, $tmp['size']); 
  395. return ($this->head1 || $this->head); 
  396.  
  397. // get v1 info 
  398. function _get_v1_info() 
  399. $ret = array(); 
  400. $tmpa = array('Title''Artist''Copyright''Description''Year''AlbumTitle'); 
  401. foreach ($tmpa as $tmp
  402. $ret[$tmp] = $this->head1[$tmp]; 
  403. if ($pos = strpos($ret[$tmp], "\0")) 
  404. $ret[$tmp] = substr($ret[$tmp], 0, $pos); 
  405.  
  406. // count the Genre, [Track] 
  407. if ($this->head1['Reserved'] == 0) $ret['Track'] = $this->head1['Track']; 
  408. else $ret['Description'] .= chr($ret['Reserved']) . chr($ret['Track']); 
  409.  
  410. // Genre_idx 
  411. $g = $this->head1['Genre']; 
  412. if (!isset($this->genres[$g])) $ret['Genre'] = 'Unknown'
  413. else $ret['Genre'] = $this->genres[$g]; 
  414.  
  415. // return the value 
  416. $ret['ID3v1'] = 'yes'
  417. return $ret
  418.  
  419. // get v2 info 
  420. function _get_v2_info() 
  421. $ret = array(); 
  422. $items = array'TCOP'=>'Copyright''TPE1'=>'Artist''TIT2'=>'Title''TRCK'=> 'Track'
  423. 'TCON'=>'Genre''COMM'=>'Description''TYER'=>'Year''TALB'=>'AlbumTitle'); 
  424. while (true) 
  425. $buf = $this->_read_head_buf(10); 
  426. if (strlen($buf) != 10) break
  427. $tmp = unpack('a4fid/Nsize/nflag'$buf); 
  428. if ($tmp['size'] == 0) break
  429. $tmp['dat'] = $this->_read_head_buf($tmp['size']); 
  430.  
  431. // 0x6000 (11000000 00000000) 
  432. if ($tmp['flag'] & 0x6000) continue
  433.  
  434. // mapping the data 
  435. if ($k = $items[$tmp['fid']]) 
  436. // If first char is "\0", just skip 
  437. if (substr($tmp['dat'], 0, 1) == "\0"$tmp['dat'] = substr($tmp['dat'], 1); 
  438. $ret[$k] = $tmp['dat']; 
  439.  
  440. // reset the genre 
  441. if ($g = $ret['Genre']) 
  442. if (substr($g,0,1) == '(' && substr($g,-1,1) == ')'$g = substr($g, 1, -1); 
  443. if (is_numeric($g)) 
  444. $g = intval($g); 
  445. $ret['Genre'] = (isset($this->genres[$g]) ? $this->genres[$g] : 'Unknown'); 
  446.  
  447. $ret['ID3v1'] = 'no'
  448. return $ret
  449. // get meta info of MP3 
  450. function _get_meta_info() 
  451. // seek to the lead buf: 0xff 
  452. $off = 0; 
  453. if ($this->head) $off = $this->head['size'] + 10; 
  454. fseek($this->fd, $off, SEEK_SET); 
  455. while (!feof($this->fd)) 
  456. $skip = ord(fread($this->fd, 1)); 
  457. if ($skip == 0xff) break
  458. if ($skip != 0xff) return false; 
  459. $buf = fread($this->fd, 3); 
  460. if (strlen($buf) != 3) return false; 
  461. $tmp = unpack('C3'$buf); 
  462. if (($tmp[1] & 0xf0) != 0xf0) return false; 
  463. // get the meta info 
  464. $meta = array(); 
  465. // get mpeg version 
  466. $meta['mpeg'] = ($tmp[1] & 0x08 ? 1 : 2); 
  467. $meta['layer'] = ($tmp[1] & 0x04) ? (($tmp[1] & 0x02) ? 1 : 2) : (($tmp[1] & 0x02) ? 3 : 0); 
  468. $meta['epro'] = ($tmp[1] & 0x01) ? 'no' : 'yes'
  469. // bit rates 
  470. $bit_rates = array
  471. 1 => array
  472. 1 => array(0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0), 
  473. 2 => array(0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0), 
  474. 3 => array(0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0) 
  475. ), 
  476. 2 => array
  477. 1 => array(0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0), 
  478. 2 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0), 
  479. 3 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0) 
  480. ); 
  481. $i = $meta['mpeg']; 
  482. $j = $meta['layer']; 
  483. $k = ($tmp[2]>>4); 
  484. $meta['bitrate'] = $bit_rates[$i][$j][$k]; 
  485. // sample rates <采样率> 
  486. $sam_rates = array(1=>array(44100,48000,32000,0), 2=>array(22050,24000,16000,0)); 
  487. $meta['samrate'] = $sam_rates[$i][$k]; 
  488. $meta["padding"] = ($tmp[2] & 0x02) ? 'on' : 'off'
  489. $meta["private"] = ($tmp[2] & 0x01) ? 'on' : 'off'
  490. // mode & mode_ext 
  491. $k = ($tmp[3]>>6); 
  492. $channel_modes = array('stereo''joint stereo''dual channel''single channel'); 
  493. $meta['mode'] = $channel_modes[$k]; 
  494. $k = (($tmp[3]>>4) & 0x03); 
  495. $extend_modes = array('MPG_MD_LR_LR''MPG_MD_LR_I''MPG_MD_MS_LR''MPG_MD_MS_I'); 
  496. $meta['ext_mode'] = $extend_modes[$k]; 
  497. $meta['copyright'] = ($tmp[3] & 0x08) ? 'yes' : 'no'
  498. $meta['original'] = ($tmp[3] & 0x04) ? 'yes' : 'no'
  499. $emphasis = array('none''50/15 microsecs''rreserved''CCITT J 17'); 
  500. $k = ($tmp[3] & 0x03); 
  501. $meta['emphasis'] = $emphasis[$k]; 
  502. return $meta
  503. // set v1 info 
  504. function _set_v1_info($pa
  505. // ID3v1 (simpled) 
  506. $off = -128; 
  507. if (!($tmp = $this->head1)) 
  508. $off = 0; 
  509. $tmp['id'] = 'TAG'
  510. $tmp['Title'] = $tmp['Artist'] = $tmp['AlbumTitle'] = $tmp['Year'] = $tmp['Description'] = ''
  511. $tmp['Reserved'] = $tmp['Track'] = $tmp['Genre'] = 0; 
  512.  
  513. // basic items 
  514. $items = array('Title''Artist''Copyright''Description''Year''AlbumTitle'); 
  515. foreach ($items as $k
  516. if (isset($pa[$k])) $tmp[$k] = $pa[$k]; 
  517. // genre index 
  518. if (isset($pa['Genre'])) 
  519. $g = 0; 
  520. foreach ($this->genres as $gtmp
  521. if (!strcasecmp($gtmp$pa['Genre'])) 
  522. break
  523. $g++; 
  524. $tmp['Genre'] = $g
  525. if (isset($pa['Track'])) $tmp['Track'] = intval($pa['Track']); 
  526. // pack the data 
  527. $buf = pack('a3a30a30a30a4a28CCC'$tmp['id'], $tmp['Title'], $tmp['Artist'], $tmp['AlbumTitle'], 
  528. $tmp['Year'], $tmp['Description'], 0, $tmp['Track'], $tmp['Genre']); 
  529. flock($this->fd, LOCK_EX); 
  530. fseek($this->fd, $off, SEEK_END); 
  531. fwrite($this->fd, $buf, 128); 
  532. flock($this->fd, LOCK_UN); 
  533. // set v2 info 
  534. function _set_v2_info($pa
  535. if (!$this->head) 
  536. // insert ID3 
  537. return// 没有就算了 
  538. /** 
  539. $tmp = array('id'=>'ID3','ver'=>3,'rev'=>0,'flag'=>0); 
  540. $tmp['size'] = -10; // +10 => 0 
  541. $this->head = $tmp; 
  542. $this->head_buf = ''; 
  543. $this->head_off = 0; 
  544. **/ 
  545. $items = array'TCOP'=>'Copyright''TPE1'=>'Artist''TIT2'=>'Title''TRAC'=>'Track'
  546. 'TCON'=>'Genre''COMM'=>'Description''TYER'=>'Year''TALB'=>'AlbumTitle'); 
  547. $head_body = ''
  548. while (true) 
  549. $buf = $this->_read_head_buf(10); 
  550. if (strlen($buf) != 10) break
  551. $tmp = unpack('a4fid/Nsize/nflag'$buf); 
  552. if ($tmp['size'] == 0) break
  553. $data = $this->_read_head_buf($tmp['size']); 
  554. if (($k = $items[$tmp['fid']]) && isset($pa[$k])) 
  555. // the data should prefix by "\0" [replace] 
  556. $data = "\0" . $pa[$k]; 
  557. unset($pa[$k]); 
  558. $head_body .= pack('a4Nn'$tmp['fid'], strlen($data), $tmp['flag']) . $data
  559. // reverse the items & set the new tags 
  560. $items = array_flip($items); 
  561. foreach ($pa as $k => $v
  562. if ($fid = $items[$k]) 
  563. $head_body .= pack('a4Nn'$fidstrlen($v) + 1, 0) . "\0" . $v
  564. // new length 
  565. $new_len = strlen($head_body) + 10; 
  566. $old_len = $this->head['size'] + 10; 
  567. if ($new_len < $old_len
  568. $head_body .= str_repeat("\0"$old_len - $new_len); 
  569. $new_len = $old_len
  570. // count the size1,2,3,4, no include the header 
  571. // 较为变态的算法... :p (28bytes integer) 
  572. $size = array(); 
  573. $nlen = $new_len - 10; 
  574. for ($i = 4; $i > 0; $i--) 
  575. $size[$i] = ($nlen & 0x7f); 
  576. $nlen >>= 7; 
  577. $tmp = $this->head; 
  578. //echo "old_len : $old_len new_len: $new_len\n"; 
  579. $head_buf = pack('a3CCCCCCC'$tmp['id'], $tmp['ver'], $tmp['rev'], $tmp['flag'], 
  580. $size[1], $size[2], $size[3], $size[4]); 
  581. $head_buf .= $head_body
  582. $this->_file_save($head_buf$old_len$new_len); 
  583. }

Tags: PHP获取音频文件

分享到: