logo


home map contact


Если вы видите это сообщение - значит вы используете браузер Internet Explorer. Негативное отношение к этому браузеру сложилось не только у владельца данного ресурса, но и у подавляющего большинства людей, разбирающихся в web технологиях. Попробуйте установить один из браузеров по ссылке ниже, возможно вам он тоже понравится больше!

Opera, the fastest and most secure web browser     Spread Firefox Affiliate Button

чтение 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


Для определения тегов достаточно отформатировать распаковку из бинарной строки в соответствии с выше приведенной последовательностью:
$f = fopen('test.mp3', 'rb');
rewind($f);
fseek($f, -128, SEEK_END);
$tmp = fread($f,128);
if ($tmp[125] == Chr(0) and $tmp[126] != Chr(0)) {
    // ID3 v1.1
    $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a28COMMENT/x1/C1TRACK/C1GENRENO';
} else {
    // ID3 v1
    $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a30COMMENT/C1GENRENO';
}

$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 файла, кто захочет - разберется.
function readframe($file) {
	if (! ($f = fopen($file, 'rb')) ) die("Unable to open " . $file);
    $res['filesize'] = filesize($file);
    do {
        while (fread($f,1) != Chr(255)) { // Find the first frame        
        	if (feof($f))  die( "No mpeg frame found") ;
        }
        fseek($f, ftell($f) - 1); // back up one byte

        $frameoffset = ftell($f);

        $r = fread($f, 4);
        
        $bits = sprintf("%'08b%'08b%'08b%'08b", ord($r{0}), ord($r{1}), ord($r{2}), ord($r{3}));
    } 
	while (!$bits[8] and !$bits[9] and !$bits[10]); // 1st 8 bits true from the while    

    // Detect VBR header
    if ($bits[11] == 0) {
        if (($bits[24] == 1) && ($bits[25] == 1)) {
            $vbroffset = 9; // MPEG 2.5 Mono
        } else {
            $vbroffset = 17; // MPEG 2.5 Stereo
        }
    } else if ($bits[12] == 0) {
        if (($bits[24] == 1) && ($bits[25] == 1)) {
            $vbroffset = 9; // MPEG 2 Mono
        } else {
            $vbroffset = 17; // MPEG 2 Stereo
        }
    } else {
        if (($bits[24] == 1) && ($bits[25] == 1)) {
            $vbroffset = 17; // MPEG 1 Mono
        } else {
            $vbroffset = 32; // MPEG 1 Stereo
        }
    }

    fseek($f, ftell($f) + $vbroffset);
    $r = fread($f, 4);

    switch ($r) {
        case 'Xing':
            $res['encoding_type'] = 'VBR';
        case 'VBRI':
        default:
            if ($vbroffset != 32) {
                // VBRI Header is fixed after 32 bytes, so maybe we are looking at the wrong place.
                fseek($f, ftell($f) + 32 - $vbroffset);
                $r = fread($f, 4);

                if ($r != 'VBRI') {
                    $res['encoding_type'] = 'CBR';
                    break;
                }
            } else {
                $res['encoding_type'] = 'CBR';
                break;
            }

            $res['encoding_type'] = 'VBR';
    }

    fclose($f);

    if ($bits[11] == 0) {
        $res['mpeg_ver'] = "2.5";
        $bitrates = 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),
                 );
    } else if ($bits[12] == 0) {
        $res['mpeg_ver'] = "2";
        $bitrates = 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),
                 );
    } else {
        $res['mpeg_ver'] = "1";
        $bitrates = 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),
                 );
    }
    
    $layer = array(
        array(0,3),
        array(2,1),
              );
    $res['layer'] = $layer[$bits[13]][$bits[14]];
    
    if ($bits[15] == 0) {
        // It's backwards, if the bit is not set then it is protected.
        $res['crc'] = true;
    }

    $bitrate = 0;
    if ($bits[16] == 1) $bitrate += 8;
    if ($bits[17] == 1) $bitrate += 4;
    if ($bits[18] == 1) $bitrate += 2;
    if ($bits[19] == 1) $bitrate += 1;
    $res['bitrate'] = $bitrates[$res['layer']][$bitrate];

    $frequency = array(
        '1' => array(
            '0' => array(44100, 48000),
            '1' => array(32000, 0),
                ),
        '2' => array(
            '0' => array(22050, 24000),
            '1' => array(16000, 0),
                ),
        '2.5' => array(
            '0' => array(11025, 12000),
            '1' => array(8000, 0),
                  ),
          );
    $res['frequency'] = $frequency[$res['mpeg_ver']][$bits[20]][$bits[21]];

    $mode = array(
        array('Stereo', 'Joint Stereo'),
        array('Dual Channel', 'Mono'),
             );
    $res['mode'] = $mode[$bits[24]][$bits[25]];
    
    $samplesperframe = array(
        '1' => array(
            '1' => 384,
            '2' => 1152,
            '3' => 1152
        ),
        '2' => array(
            '1' => 384,
            '2' => 1152,
            '3' => 576
        ),
        '2.5' => array(
            '1' => 384,
            '2' => 1152,
            '3' => 576
        ),
    );
    $res['samples_per_frame'] = $samplesperframe[$res['mpeg_ver']][$res['layer']];

    if ($res['encoding_type'] != 'VBR') {
        if ($res['bitrate'] == 0) {
            $s = -1;
        } else {
            $s = ((8*filesize($file))/1000) / $res['bitrate'];
        }
        $res['length'] = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengthh'] = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengths'] = (int)$s;

        $res['samples'] = ceil($res['lengths'] * $res['frequency']);
        if(0 != $res['samples_per_frame']) {
            $res['frames'] = ceil($res['samples'] / $res['samples_per_frame']);
        } else {
            $res['frames'] = 0;
        }
        $res['musicsize'] = ceil($res['lengths'] * $res['bitrate'] * 1000 / 8);
    } else {
        $res['samples'] = $res['samples_per_frame'] * $res['frames'];
        $s = $res['samples'] / $res['frequency'];

        $res['length'] = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengthh'] = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengths'] = (int)$s;

        $res['bitrate'] = (int)(($res['musicsize'] / $s) * 8 / 1000);
    }
    
    return $res;
} 
Результат работы скрипта:
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 
) 




Комментарии:



Hermes     25.03.08 в 11:35

Большое спасибо! очень интересно , коротко и главное всё по делу!





Dok     22.04.08 в 11:44

Автор молоток! Спасибо!





melok    11.11.08 14:49

Cтатья отличная, потому прошу разрешить ее размещения в разделе \"Библиотека->программирование->PHP\" на моем сайте http://melok.com.ua/




Baxa    15.12.08 04:38

Причем тут ссылка на объект? 46 строка: $this->encoding_type = \'VBR\';




Alek Veritov    17.12.08 11:00

да, ссылка не причем, исправил




Alex    24.03.09 15:54

Огромный респект, сотит конечно учесть, что в файле может и не быть тегов , а инфу по длительности все таки можно вытащить ... хотя легко доделывается




Darkhan    12.02.10 12:52

Все сделано хорошо, удобно, но он у меня выводит неправильную длину песни и битрейт. Когда длина 3:50 и битрейт 256, он выводит 07:42 и 128 kbit




Stas    07.05.10 20:58

Спасибо, Автору. Коротко и понятно. Только у меня почему-то длительность не правильно выводит. Из трех файлов только у одного вывел реальную длительность. У второго так вообще отрицательная получилась. Из-за чего может быть?




tolikman    23.06.10 16:03

Всем здравствуйте! Очень интересная статья - коротко, ясно и полезно! Тока научиться бы читать еще ID3v2.x теги. Мне очень помогла! НО! Я кажется нашел ошибку во втором листинге!
строка 166: $res['samples'] = $res['samples_per_frame'] * $res['frames'];
Значение $res['frames'] еще не определено до этой строки! Скорее всего необходимо искать все фреймы в файле!
Спасибо автору.




Zorg    16.07.10 15:21

Вы кудесник и как я вижу тоже любите простоту и лаконичность. Великий респект за ресурс.




олександр    27.05.11 18:38

У меня сайт на дле, можете подсказать куда этот скрипт ставить надо?




waip    21.06.11 10:02

олександр: смотря что вы хотите с ним сделать. мое мнение что вам лучше свой двиг написать и походу поймете что и куда нужно вписывать.




waip    21.06.11 10:03

Сори за флуд автору спосяб. =)




Darkhan    06.10.11 23:12

Почему никто не говорит про ошибки функции?
Скрипт вообще негодный почти всегда ложную инфу выводит...

Почему так? кто знает?




Айдин    17.01.12 09:19

КрасавчиК!!! Как раз столкнулся с такой проблемой!




Айдин    26.01.12 06:46

В некоторых случаях не правильно определяет продолжительность, битрейт. -00:00:01 или в 2,3,4 раза больше чем истинная продолжительность mp3-шки




Андрей Шеляков    30.04.12 14:38

Автору реальный респект=)




Андрей Шеляков    30.04.12 14:54

Ещёб статью о том как записать значение тега




bifurcator    07.05.12 09:36

В целом работает, но в некоторых случаях выдает отрицательные значения продолжительности.
Автор крут, но функция требует доработки.






Если вам помогла или просто понравилась эта статья вы можете отблагодарить автора кликнув по рекламе. Спасибо!





комментировать:
прежде чем писать комментарий убедитесь, пожалуйста, что он не попадает в нижеследующие категории:
  • не стоит писать "я чайник, помогите ..." или "я начинающий, объясните ..." - уважайте себя! может вы и новичок, но ведь не тупой же! сами вполне способны во всем разобраться, тем более, что материал здесь изложен весьма доступно

  • не пишите вопросы типа "как мне сделать такую же менюху как наверху?", "куда вставлять этот код?", "как устроен интернет?" и т.д. - уважайте время автора!

  • комментарии вида "все, что здесь написано - бред" будут оставлены только если будут подписаны "я такой-то, разработчик сайта такого-то с посещаемостью 1000 хостов в день" и т.п. Если вы не согласны с чем-то - обоснуйте, напишите как правильно, а писать простые ругательства не надо, это не забор

прямые оскорбления кого бы то ни было будут удалятся!
здоровая критика приветствуется!



от кого:  

security picture