Web/PHP
mp3파일 정보 얻기
aucd29
2013. 9. 26. 21:38
(7/16 8:19pm 소스 수정 했습니다.)
최근 올라왔던
흑심품은연필 님의 'mp3 파일의 정보를 알아내는 방법',
loverise 님 의 'mp3 파일정보 전부 긁어내기',
그리고 제목에 'id3' 로 검색하면 나오는 전영복 님의 'MP3 ID3v1 Tag 정보보기'
세 글을 짜집기하여-_- 거기에 최종적으로 ID3v2 태그 정보 뽑아내는 루틴을 추가하여
클래스로 만들었습니다.
허락없이 마음대로 짜집기 한 점 세 분께 사과드리구요 ^^;;;
소스는 맨 아래에 있고, 음.. 간단히 id3 태그 구조에 대하여 설명을..
http://www.id3.org/id3v1.1_blocks.gif
ID3v1.1 태그 구조입니다.
v1 (http://www.id3.org/id3v1_blocks.gif) 과 달라진 점이라면,
comment 태그가 1바이트 줄어들고,
그 1바이트에 track number 를 쓸 수 있게 했다는 점입니다.
ID3v1 태그는 mp3 파일의 제일 끝에 위치해 있고, 그 크기는 128 바이트 고정입니다.
제일 처음에 'TAG' 라는 문자열로 ID3v1 태그가 시작됨을 알리구요. (3 bytes)
Title - 30 bytes
Artist - 30 bytes
Album - 30 bytes
Year - 4 bytes
Comment - 29 bytes
Track - 1 byte
genre - 1 byte
로 이루어져 있습니다.
ID3v2 태그는 mp3 파일 제일 처음에 위치합니다.
역시 ID3v1 태그처럼 'ID3' 라는 문자열로서 ID3v2 태그가 시작됨을 알립니다.
ID3v2 태그의 구조를 보면,
+-----------------------------+
| Header (10 bytes) |
+-----------------------------+
| Extended Header |
| (variable length, OPTIONAL) |
+-----------------------------+
| Frames (variable length) |
+-----------------------------+
| Padding |
| (variable length, OPTIONAL) |
+-----------------------------+
| Footer (10 bytes, OPTIONAL) |
+-----------------------------+
와 같이 이루어져 있습니다.
Header 부분을 보면,
ID3 - 3 bytes
version - 2 bytes
flags - 1 byte
size - 4 bytes
와 같이 되어있구요. 여기서 size 로부터 ID3v2 태그 전체의 사이즈를 구할 수 있습니다.
Extended Header, Padding, Footer 는 말그대로 option 인 고로 넘어갑니다.
사실은 뭔 소린지 읽어봐도 잘 모르겠습니다. ㅠ_ㅠ
중요한 건 Frames 입니다. 이곳에 ID3v1 태그에서의 title, artist, ... 와 같은 정보가
들어있죠.
하나의 frame 은
frame ID - 4 bytes
frame size - 4 bytes
frame flags - 2bytes
frame body - frame size 로 부터 구할 수 있는 프레임 사이즈 bytes
로 구성됩니다.
frame ID 는 해당 프레임의 종류를 나타내 주는 4 바이트 문자열입니다.
http://www.id3.org/id3v2.3.0.html#sec4 에 현 ID3v2 버전에서 지원되는 frame ID
종류가 나열되어 있습니다. (무쟈게 많죠-_-?)
이중에 우리가 가장 많이 쓰는 mp3 플레이어인 Winamp 에서 쓰이는 ID 는
아래 소스에 $frame_id 에 있는 것입니다.
그 외에 윈앰프에서 보면 Composer, Org.Artist, Copyright, URL,
Encoded By 등의 항목이 있지만 이 부분이 채워져 있는 mp3 를 한번도
본 적이 없는 관계로 제 소스에는 없습니다.
필요한 경우 위 ID 리스트에서 찾아서 추가해주시면 됩니다. ^^
결국, mp3 파일 하나의 구조를 전체적으로 보면 다음 그림과 같이 됩니다.
그림이 허접해도 이해를..^^;;
http://www.flyforu.com/temp/mp3.gif
아무쪼록 도움이 되길 바랍니다. ^^;
<?
class mp3vars
{
var $mpeg = array(
'00' => 'MPEG v2.5',
'01' => 'reserved',
'10' => 'MPEG v2.0',
'11' => 'MPEG v1.0'
);
var $layer = array(
'00' => 'reserved',
'01' => 'Layer III',
'10' => 'Layer II',
'11' => 'Layer I'
);
var $bitrate = array(
'0000' => '00',
'0001' => '32',
'0010' => '40',
'0011' => '48',
'0100' => '56',
'0101' => '64',
'0110' => '80',
'0111' => '96',
'1000' => '112',
'1001' => '128',
'1010' => '160',
'1011' => '192',
'1100' => '224',
'1101' => '256',
'1110' => '320',
'1111' => '0'
);
var $sampling = array(
'00' => '44100',
'01' => '48000',
'10' => '32000',
'11' => 'reserved'
);
/*
var $crc = array(
'0' => 'Yes',
'1' => 'No'
);
var $padding = array(
'0' => 'Frame is padded',
'1' => 'Frame is not padded'
);
var $channel = array(
'00' => 'Stereo',
'01' => 'Joint Stereo',
'10' => 'Dual',
'11' => 'Mono'
);
var $copyright = array(
'0' => 'No',
'1' => 'Yes'
);
var $original = array(
'0' => 'Copy',
'1' => 'Original'
);
var $emphasis = array(
'00' => 'None',
'01' => '50/15',
'10' => 'reserved',
'11' => 'CCIT J.17'
);
*/
var $frame_id = array(
'TPE1' => 'artist',
'TIT2' => 'title',
'TALB' => 'album',
'TRCK' => 'track',
'COMM' => 'comment',
'TCON' => 'genre',
'TYER' => 'year'
);
function gettag($filename)
{
$this->tag = array();
$fp = fopen($filename, 'r');
$tag_header = fread($fp, 10);
if (substr($tag_header, 0, 3) != 'ID3')
{
$id3v2 = 0;
}
else
{
$id3v2 = 1;
// 태그 버전 읽어오기
$tver = substr($tag_header, 3, 2);
$this->tag['id3ver'] = '2.'.ord($tver[0]).'.'.ord($tver[1]);
// flag읽어오기
$flag = substr($tag_header, 5, 1);
// 태그 사이즈 읽어오기
$tsize = substr($tag_header, 6, 4);
for ($i = 0; $i < 4; $i++)
{
$tgsize[$i] = sprintf("%07d", decbin(ord($tsize[$i])));
$tagsize .= $tgsize[$i];
}
$tagsize = bindec($tagsize) + 10;
// 태그 프레임 읽어오기
for($i = 10; $i < $tagsize; $i++) // 태그 헤더에 해당하는 10 바이트 다음부터 태그 사이즈까지
{
fseek($fp, $i);
$id = fread($fp, 4); // 4바이트 씩 읽어와서
if ($id[0] >= 'A' && $id[0] <= 'Z' && array_key_exists($id, $this->frame_id))
{
$size = fread($fp, 4);
$frame_size = '';
for ($j = 0; $j < 4; $j++)
{
$frsize[$j] = sprintf("%07d", decbin(ord($size[$j])));
$frame_size .= $frsize[$j];
}
$frame_size = bindec($frame_size);
$flag = fread($fp, 2);
$key = $this->frame_id[$id];
$this->tag[$key] = fread($fp, $frame_size);
if ($key == 'genre')
$this->tag[$key] = preg_replace("/\([0-9]*\)/", "", $this->tag[$key]);
elseif ($key == 'comment')
$this->tag[$key] = substr($this->tag[$key], 4);
$i += 9 + $frame_size;
}
}
}
fseek($fp, $tagsize); // 읽어온 태그 사이즈를 건너뛰어서
$contents = fread($fp, 4); // mp3의 헤드를 읽어옵니다.
for($i = 0; $i < 4; $i++)
{
$bin[$i] = sprintf("%08d", decbin(ord($contents[$i]))); // 아스키 값으로 불러와서 이진수로 바꾸고 다시 8자리 이진수로 바꾼뒤에
}
$z = implode('', $bin); // 합쳐주면 32자리의 헤드가 완성됩니다
// 처음 11자리 A부분
//$a = "$z[0]$z[1]$z[2]$z[3]$z[4]$z[5]$z[6]$z[7]$z[8]$z[9]z[10]";
// 2자리 B부분 MPEG정보
$b = "$z[11]$z[12]";
$this->tag['mpeg'] = $this->mpeg[$b];
// 2자리 C부분 Layer정보
$c = "$z[13]$z[14]";
$this->tag['layer'] = $this->layer[$c];
/*
$d = "$z[15]"; //1자리 D부분 CRC정보
$this->tag['crc'] = $this->crc[$d];
*/
// 4자리 E부분 비트레이트, 음질이라고도 하죠
$e = "$z[16]$z[17]$z[18]$z[19]";
$this->tag['bitrate'] = $this->bitrate[$e];
// 2자리 F부분 샘플링
$f = "$z[20]$z[21]";
$this->tag['sampling'] = $this->sampling[$f];
/*
$g = "$z[22]"; //2자리 G부분 프레임 패딩
$this->tag['padding'] = $this->padding[$g];
$h = "$z[23]"; //1자리 H부분
$i = "$z[24]$z[25]"; //2자리 I부분 스테레오정보
$this->tag['channel'] = $this->channel[$i];
$j = "$z[26]$z[27]"; //2자리 J부분
$k = "$z[28]"; //2자리 K부분 카리라이터 여부
$this->tag['copyright'] = $this->copyright[$k];
$l = "$z[29]"; //2자리 L부분 원본 여부
$this->tag['original'] = $this->original[$l];
$m = "$z[30]$z[31]"; //2자리 M부분 Emphasis
$this->tag['emphasis'] = $this->emphasis[$m];
*/
// 노래 플레이타임
$this->tag['time'] = (int)(filesize($filename) / ( $this->tag['bitrate'] * 1000 / 8 ));
// ID3v2 태그가 없으면 v1 태그 정보를 가져옴
if (!$id3v2)
{
fseek($fp, filesize($filename) - 128);
$mp3tag = fread($fp, 128);
fclose($fp);
if (substr($mp3tag, 0, 3) != 'TAG')
{
echo "ID3 Tag 가 없습니다<br>\n";
return array();
}
else
{
$this->tag['id3ver'] = '1.1';
$mp3tag = substr($mp3tag, 3);
$id3v1 = array('title'=>30, 'artist'=>30, 'album'=>30, 'year'=>4, 'comment'=>29, 'track'=>1, 'genre'=>1);
$start = 0;
foreach($id3v1 as $key => $value)
{
$id3v1val = substr($mp3tag, $start, $value);
if ($key == 'track' || $key == 'genre')
$id3v1val = ord($id3v1val);
$this->tag[$key] = $id3v1val;
$start += $value;
}
return $this->tag;
}
}
return $this->tag;
}
}
$mp3 = new mp3vars;
$tag = $mp3->gettag("t.mp3");
foreach($tag as $key => $value)
{
echo "$key => $value <br>";
}
?>
최근 올라왔던
흑심품은연필 님의 'mp3 파일의 정보를 알아내는 방법',
loverise 님 의 'mp3 파일정보 전부 긁어내기',
그리고 제목에 'id3' 로 검색하면 나오는 전영복 님의 'MP3 ID3v1 Tag 정보보기'
세 글을 짜집기하여-_- 거기에 최종적으로 ID3v2 태그 정보 뽑아내는 루틴을 추가하여
클래스로 만들었습니다.
허락없이 마음대로 짜집기 한 점 세 분께 사과드리구요 ^^;;;
소스는 맨 아래에 있고, 음.. 간단히 id3 태그 구조에 대하여 설명을..
http://www.id3.org/id3v1.1_blocks.gif
ID3v1.1 태그 구조입니다.
v1 (http://www.id3.org/id3v1_blocks.gif) 과 달라진 점이라면,
comment 태그가 1바이트 줄어들고,
그 1바이트에 track number 를 쓸 수 있게 했다는 점입니다.
ID3v1 태그는 mp3 파일의 제일 끝에 위치해 있고, 그 크기는 128 바이트 고정입니다.
제일 처음에 'TAG' 라는 문자열로 ID3v1 태그가 시작됨을 알리구요. (3 bytes)
Title - 30 bytes
Artist - 30 bytes
Album - 30 bytes
Year - 4 bytes
Comment - 29 bytes
Track - 1 byte
genre - 1 byte
로 이루어져 있습니다.
ID3v2 태그는 mp3 파일 제일 처음에 위치합니다.
역시 ID3v1 태그처럼 'ID3' 라는 문자열로서 ID3v2 태그가 시작됨을 알립니다.
ID3v2 태그의 구조를 보면,
+-----------------------------+
| Header (10 bytes) |
+-----------------------------+
| Extended Header |
| (variable length, OPTIONAL) |
+-----------------------------+
| Frames (variable length) |
+-----------------------------+
| Padding |
| (variable length, OPTIONAL) |
+-----------------------------+
| Footer (10 bytes, OPTIONAL) |
+-----------------------------+
와 같이 이루어져 있습니다.
Header 부분을 보면,
ID3 - 3 bytes
version - 2 bytes
flags - 1 byte
size - 4 bytes
와 같이 되어있구요. 여기서 size 로부터 ID3v2 태그 전체의 사이즈를 구할 수 있습니다.
Extended Header, Padding, Footer 는 말그대로 option 인 고로 넘어갑니다.
사실은 뭔 소린지 읽어봐도 잘 모르겠습니다. ㅠ_ㅠ
중요한 건 Frames 입니다. 이곳에 ID3v1 태그에서의 title, artist, ... 와 같은 정보가
들어있죠.
하나의 frame 은
frame ID - 4 bytes
frame size - 4 bytes
frame flags - 2bytes
frame body - frame size 로 부터 구할 수 있는 프레임 사이즈 bytes
로 구성됩니다.
frame ID 는 해당 프레임의 종류를 나타내 주는 4 바이트 문자열입니다.
http://www.id3.org/id3v2.3.0.html#sec4 에 현 ID3v2 버전에서 지원되는 frame ID
종류가 나열되어 있습니다. (무쟈게 많죠-_-?)
이중에 우리가 가장 많이 쓰는 mp3 플레이어인 Winamp 에서 쓰이는 ID 는
아래 소스에 $frame_id 에 있는 것입니다.
그 외에 윈앰프에서 보면 Composer, Org.Artist, Copyright, URL,
Encoded By 등의 항목이 있지만 이 부분이 채워져 있는 mp3 를 한번도
본 적이 없는 관계로 제 소스에는 없습니다.
필요한 경우 위 ID 리스트에서 찾아서 추가해주시면 됩니다. ^^
결국, mp3 파일 하나의 구조를 전체적으로 보면 다음 그림과 같이 됩니다.
그림이 허접해도 이해를..^^;;
http://www.flyforu.com/temp/mp3.gif
아무쪼록 도움이 되길 바랍니다. ^^;
<?
class mp3vars
{
var $mpeg = array(
'00' => 'MPEG v2.5',
'01' => 'reserved',
'10' => 'MPEG v2.0',
'11' => 'MPEG v1.0'
);
var $layer = array(
'00' => 'reserved',
'01' => 'Layer III',
'10' => 'Layer II',
'11' => 'Layer I'
);
var $bitrate = array(
'0000' => '00',
'0001' => '32',
'0010' => '40',
'0011' => '48',
'0100' => '56',
'0101' => '64',
'0110' => '80',
'0111' => '96',
'1000' => '112',
'1001' => '128',
'1010' => '160',
'1011' => '192',
'1100' => '224',
'1101' => '256',
'1110' => '320',
'1111' => '0'
);
var $sampling = array(
'00' => '44100',
'01' => '48000',
'10' => '32000',
'11' => 'reserved'
);
/*
var $crc = array(
'0' => 'Yes',
'1' => 'No'
);
var $padding = array(
'0' => 'Frame is padded',
'1' => 'Frame is not padded'
);
var $channel = array(
'00' => 'Stereo',
'01' => 'Joint Stereo',
'10' => 'Dual',
'11' => 'Mono'
);
var $copyright = array(
'0' => 'No',
'1' => 'Yes'
);
var $original = array(
'0' => 'Copy',
'1' => 'Original'
);
var $emphasis = array(
'00' => 'None',
'01' => '50/15',
'10' => 'reserved',
'11' => 'CCIT J.17'
);
*/
var $frame_id = array(
'TPE1' => 'artist',
'TIT2' => 'title',
'TALB' => 'album',
'TRCK' => 'track',
'COMM' => 'comment',
'TCON' => 'genre',
'TYER' => 'year'
);
function gettag($filename)
{
$this->tag = array();
$fp = fopen($filename, 'r');
$tag_header = fread($fp, 10);
if (substr($tag_header, 0, 3) != 'ID3')
{
$id3v2 = 0;
}
else
{
$id3v2 = 1;
// 태그 버전 읽어오기
$tver = substr($tag_header, 3, 2);
$this->tag['id3ver'] = '2.'.ord($tver[0]).'.'.ord($tver[1]);
// flag읽어오기
$flag = substr($tag_header, 5, 1);
// 태그 사이즈 읽어오기
$tsize = substr($tag_header, 6, 4);
for ($i = 0; $i < 4; $i++)
{
$tgsize[$i] = sprintf("%07d", decbin(ord($tsize[$i])));
$tagsize .= $tgsize[$i];
}
$tagsize = bindec($tagsize) + 10;
// 태그 프레임 읽어오기
for($i = 10; $i < $tagsize; $i++) // 태그 헤더에 해당하는 10 바이트 다음부터 태그 사이즈까지
{
fseek($fp, $i);
$id = fread($fp, 4); // 4바이트 씩 읽어와서
if ($id[0] >= 'A' && $id[0] <= 'Z' && array_key_exists($id, $this->frame_id))
{
$size = fread($fp, 4);
$frame_size = '';
for ($j = 0; $j < 4; $j++)
{
$frsize[$j] = sprintf("%07d", decbin(ord($size[$j])));
$frame_size .= $frsize[$j];
}
$frame_size = bindec($frame_size);
$flag = fread($fp, 2);
$key = $this->frame_id[$id];
$this->tag[$key] = fread($fp, $frame_size);
if ($key == 'genre')
$this->tag[$key] = preg_replace("/\([0-9]*\)/", "", $this->tag[$key]);
elseif ($key == 'comment')
$this->tag[$key] = substr($this->tag[$key], 4);
$i += 9 + $frame_size;
}
}
}
fseek($fp, $tagsize); // 읽어온 태그 사이즈를 건너뛰어서
$contents = fread($fp, 4); // mp3의 헤드를 읽어옵니다.
for($i = 0; $i < 4; $i++)
{
$bin[$i] = sprintf("%08d", decbin(ord($contents[$i]))); // 아스키 값으로 불러와서 이진수로 바꾸고 다시 8자리 이진수로 바꾼뒤에
}
$z = implode('', $bin); // 합쳐주면 32자리의 헤드가 완성됩니다
// 처음 11자리 A부분
//$a = "$z[0]$z[1]$z[2]$z[3]$z[4]$z[5]$z[6]$z[7]$z[8]$z[9]z[10]";
// 2자리 B부분 MPEG정보
$b = "$z[11]$z[12]";
$this->tag['mpeg'] = $this->mpeg[$b];
// 2자리 C부분 Layer정보
$c = "$z[13]$z[14]";
$this->tag['layer'] = $this->layer[$c];
/*
$d = "$z[15]"; //1자리 D부분 CRC정보
$this->tag['crc'] = $this->crc[$d];
*/
// 4자리 E부분 비트레이트, 음질이라고도 하죠
$e = "$z[16]$z[17]$z[18]$z[19]";
$this->tag['bitrate'] = $this->bitrate[$e];
// 2자리 F부분 샘플링
$f = "$z[20]$z[21]";
$this->tag['sampling'] = $this->sampling[$f];
/*
$g = "$z[22]"; //2자리 G부분 프레임 패딩
$this->tag['padding'] = $this->padding[$g];
$h = "$z[23]"; //1자리 H부분
$i = "$z[24]$z[25]"; //2자리 I부분 스테레오정보
$this->tag['channel'] = $this->channel[$i];
$j = "$z[26]$z[27]"; //2자리 J부분
$k = "$z[28]"; //2자리 K부분 카리라이터 여부
$this->tag['copyright'] = $this->copyright[$k];
$l = "$z[29]"; //2자리 L부분 원본 여부
$this->tag['original'] = $this->original[$l];
$m = "$z[30]$z[31]"; //2자리 M부분 Emphasis
$this->tag['emphasis'] = $this->emphasis[$m];
*/
// 노래 플레이타임
$this->tag['time'] = (int)(filesize($filename) / ( $this->tag['bitrate'] * 1000 / 8 ));
// ID3v2 태그가 없으면 v1 태그 정보를 가져옴
if (!$id3v2)
{
fseek($fp, filesize($filename) - 128);
$mp3tag = fread($fp, 128);
fclose($fp);
if (substr($mp3tag, 0, 3) != 'TAG')
{
echo "ID3 Tag 가 없습니다<br>\n";
return array();
}
else
{
$this->tag['id3ver'] = '1.1';
$mp3tag = substr($mp3tag, 3);
$id3v1 = array('title'=>30, 'artist'=>30, 'album'=>30, 'year'=>4, 'comment'=>29, 'track'=>1, 'genre'=>1);
$start = 0;
foreach($id3v1 as $key => $value)
{
$id3v1val = substr($mp3tag, $start, $value);
if ($key == 'track' || $key == 'genre')
$id3v1val = ord($id3v1val);
$this->tag[$key] = $id3v1val;
$start += $value;
}
return $this->tag;
}
}
return $this->tag;
}
}
$mp3 = new mp3vars;
$tag = $mp3->gettag("t.mp3");
foreach($tag as $key => $value)
{
echo "$key => $value <br>";
}
?>