Внутри MP3. А как оно всё устроено?



    Однажды мне понадобилось решить простенькую (как мне тогда казалось) задачу – в PHP-скрипте узнать длительность mp3-файла. Я слышал о ID3 тегах и сразу подумал, что информация о длительности хранится либо в тегах, либо в заголовках mp3-файла. Поверхностные поиски в интернете показали что за пару-тройку минут решить эту задачу не получится. Поскольку от природы я довольно любопытен а время не поджимало — решил не использовать сторонние инструменты а разобраться в одном из самых популярных форматов самостоятельно.

    Если Вам интересно, что там внутри – добро пожаловать под кат (трафик).


    В данной статье мы не будем подробно останавливаться на извлечении ID3v2 тегов – это можно вынести в отдельную статью, так как там есть различные нюансы. А так же на фрагментах заголовков, которые практически не используются в настоящее время (например, часть Emphasis заголовка mp3-фрейма). Так же мы не рассматриваем структуру самих аудиоданных — тех самых, которые слышим из колонок.

    ID3 теги


    ID3 (от англ. Identify a MP3) — формат метаданных, наиболее часто используемый в звуковых файлах в формате MP3. ID3 подпись содержит данные о названии трека, альбома, имени исполнителя и т. д., которые используются мультимедиапроигрывателями и другими программами, а также аппаратными проигрывателями, для отображения информации о файле и автоматического упорядочивания аудиоколлекции.

    Wikipedia

    Существует две абсолютно разных версии ID3-данных: ID3v1 и ID3v2.

    ID3v1 – имеет фиксированный размер в 128 байт, которые дописываются в конец mp3-файла. Там можно хранить: название трека, исполнитель, альбом, год, комментарий, номер трека (для версии 1.1) и жанр.



    Довольно быстро всем стало понятно, что 128 байт – очень уж небольшое место для хранения таких данных. И поэтому, со временем, появилась и успешно используется вторая версия данных – ID3v2.
    В отличии от первой версии, теги v2 имеют переменную длину и размещаются в начале файла, что позволяет поддерживать потоковое воспроизведение. (Формат ID3v2.4 позволяет так же хранить данные и в конце файла).
    Данные ID3v2 состоят из заголовка и последующих фреймов ID3v2. Например, в версии ID3v2.3 существует более 70 типов фреймов.


    • маркер всегда равен ‘ID3’
    • В данный момент имеются три версии ID3v2.2, ID3v2.3 и ID3v2.4
      Версия v2.2 считается устаревшей.
      v2.3 – самая популярная версия.
      v2.4 – набирает популярность. Одно из отличий от v2.3 в том, что позволяет использовать кодировку UTF-8 (а не только UTF-16)
    • Флаги. В настоящее время используются только три (5,6,7) бита:
      bin: %abc00000
      a ‘unsynchronisation’ – используется только с MPEG-2 и MPEG-2.5 форматами.
      b ‘Extended header’ – указывает на наличие расширенного заголовка
      с ‘Experimental indicator’ – эксперементальный индикатор
    • Длина. Особенность указания длины данных ID3v2 в том, что в каждом байте 7-й бит не используется и всегда установлен в 0.

    Рассмотрим пример:



    В данном случае вместе с заголовком ID3v2 (10 байт) – данные ID3v2 занимают 1024 байта.

    После ID3v2-заголовка идут собственно теги. Подробный разбор чтения тегов ID3v2, как сказано выше, я решил не включать в эту статью.

    Теперь у нас есть информация о наличии и длине тегов ID3 и мы можем приступать в разбору mp3-фрейма и понять-таки – где же хранится длительность. А заодно понять и всё остальное.

    MP3-фрейм



    Весь mp3-файл состоит из фреймов, которые можно извлекать только последовательно. Фрейм содержит в себе заголовок и аудио-данные. Поскольку мы не ставим себе целью написать прошивку для магнитофона – нас интересует именно заголовок фрейма.

    О нем подробнее (куча таблиц и сухой информации)



    Размер заголовка – 4 байта.



    Описание:
    • [0-10] Маркер – 11 бит, заполненные единицами (Frame sync)
    • [11-12] Индекс версии MPEG (Audio version ID)
    • [13-14] Индекс версии Layer (Layer index)

      Кстати, MP3 – это MPEG-1 Layer III
    • [15] Бит защиты (Protection bit)
      1 – нет защиты
      0 – заголовок защищен 16-бит. CRC (следует за заголовком)
    • [16-19] Индекс битрейта (Bitrate index)

      В таблице хранятся значения битрейта в килобит/сек. Однако в данном формате подразумевается, что 1 килобит = 1000 бит, а не 1024. Таким образом 96 Кбит/сек = 96000 бит/сек.
    • [20-21] Индекс частоты дискретизации (Sampling rate index)
    • [22] Бит смещения (Padding bit)
      Если он установлен, то данные смещаются на 1 байт. Это важно для расчета размера фрейма.
    • [23] Бит private (только для информации)
    • [24-25] Режим канала (Channel mode)
    • [26-27] Расширение режима канала. (Mode extension) Используется только с Joint stereo
    • [28] Копирайт (Copyright bit) – только для информации
    • [29] Оригинал (Original bit) – только для информации.
    • [30-31] Акцент (Emphasis) – в данный момент практически не используется.

    Режимы сжатия данных или какой бывает битрейт



    Существует 3 режима сжатия данных:

    CBR (constant bitrate) – постоянный битрейт. Не меняется на всем протяжении трека.

    VBR (variable bitrate) – переменный битрейт. При этом сжатии битрейт постоянно меняется на протяжении трека.

    ABR (average bitrate) – усредненный битрейт. Это понятие используется только при кодировании файла. На «выходе» получается файл с VBR.

    CBR


    Если файл закодирован с постоянным битрейтом – то мы уже можем наконец-то! получить длительность нашего трека по следующей формуле:
    Длительность = Размер аудиоданных / Битрейт (в битах!) * 8

    Например, файл имеет размер 350670 байт. Есть ID3v1 теги (128 байт) и ID3v2 теги (1024 байта). Битрейт = 96. Следовательно размер аудиоданных равен 350670 – 128 – 1024 = 349518 байт.
    Длительность = 349518 / 96000 * 8 = 29,1265 = 29 секунд

    VBR


    Необходимо пояснить – как определить режим сжатия. Всё просто. Если файл сжат с VBR – то добавляется VBR-заголовок. По его наличию мы и можем понять, что используется переменный битрейт.
    Есть два вида заголовков: Xing и VBRI.
    Xing размещается со смещением от начала первого mp3-фрейма в позиции, согласно таблице:



    Например: у нас ID3v2 тег занимает 1024 байта. Если наш mp3-файл имеет режим канала «Стерео» — то заголовок VBR Xing будет начинаться со смещения 1024 + 32 = 1056 байт.

    Заголовок VBRI всегда размещается со смещением +32 байта от начала первого mp3-фрейма.

    Первые четыре байта в обоих заголовках содержат маркер ‘Xing’ или ‘Info’ для Xing. И ‘VBRI’ для VBRI.

    Эти VBR заголовки имеют переменную длину и содержат различную информацию о кодировании файла. Подробнее о структуре заголовков VBR (и не только) можно почитать, например, тут.

    Я же расскажу только о том, что нас интересует в данный момент. А именно – количество фреймов (Number of Frames). Это число длиной 4 байта.
    В заголовке Xing оно содержится по смещению +8 байт от начала заголовка. В VBRI +14 байт от начала заголовка.

    Используя таблицу Сэмплов на фрейм (Sampler Per Frame) мы можем получить длительность mp3-файла, закодированного с переменным битрейтом.



    Длительность = Количество фреймов * Сэмплов на фрейм / Частоту дискретизации


    Например: из заголовка VBRI получили количество фреймов 1118, сэмплов на фрейм = 1152. Частота дискретизации = 44100.
    Длительность = 1118 * 1152 / 44100 = 29.204 = 29 секунд.


    На этом на сегодня всё. Если был кому-то полезен — спасибо.

    Для тех, кто захочет немедленно поковырять внутренности mp3 — Тут лежат скрипт на php, которые я писал для себя одновременно с данной статьей и четыре небольших mp3-файла для теста.

    Ссылки


    id3.org — Читаем о ID3
    id3.org — и кое-что о mp3 frame
    Довольно подробно о mp3 frame
    getID3: Неплохая библиотека для получения информации о mp3. (PHP)
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 55
    • +3
      Спасибо, интересно!
      • –2
        Шикарно!
        • –10
          Крайне удивлен прочитаным. Я думал все на много проще.
          * Ушел ковырять mp3шки
          • +1
            — Ушел ковырять mp3шки
            Все уже расковырено… до нас
          • +2
            действительно, времени у вас было много
            ;-)
            • +11
              кстати, phpcode.us/ffmpeg-php/
              ffmpeg_movie::getDuration() Возвращает продолжительность аудио или видео файла в секундах.
              • 0
                Я почему-то тоже во время ресеарча информации по той же задаче наткнулся на ffmpeg. Чем автору вариант такой не подошёл? Да и классы я видел тоже есть для работы с тэгами, даже для записи id3v2.
                • +1
                  Мне больше понравился getID3. А копать формат я стал в познавательных целях. Ну может быть еще и то, что я не нашел нормальной документации на русском сыграло свою роль.
            • –4
              — Ушел ковырять mp3шки
              Все уже расковырено… до нас
              • –2
                упс, не туда написал
              • +9
                На Хабре блог «Алгоритмы» последние время очень радует. Почти всё попадает в Избранное.
                • +1
                  А где бы почитать про структуру самих аудиоданных?
                  • +5
                    Есть в планах заняться этим вопросом плотнее. Если кто-либо не напишет ранее — это впролне может быть темой следующей статьи.
                    • 0
                      С нетерпением ждём!
                      • +1
                        Пишите, это интересно.
                        • 0
                          Напишите, там даже для layer3 не очень сложно.
                        • +1
                          Стандрат, в котором это описано называется ISO/IEC 11172-3 (Information technology — Coding of moving pictures and associated audio for digital storage media at up to about 1,5 Mbit/s — Part 3: Audio)
                        • +1
                          Весьма полезно. Предлагаю объявить это как «часть 1», а потом написать «часть 2» — собственно про аудиоданные.
                          • 0
                            Я как-то писал читалку данных из mp3 для одного проекта и тоже разбирался во всем этом. Но потом пришло понимание, что не стоит заниматься велосипедостроением, и я взял готовый модуль для perl. Но узнать о стоении mp3 файла было крайне интересно.
                            • –1
                              Как раз пользовался на php getID3 — неплохая библиотека, не без своих глюков конечно.
                              Прикольно что поддерживает расширение возможностей с помощью дополнительных «расширений» по разные форматы файлов. Можно тонко настроить под себя
                              • 0
                                Я тоже написал свою программу для чтения заголовков AVI, а потом нашел getID3 — лучшее, что есть на PHP для парсинга мета-данных различных мультимедиа форматов.
                                • 0
                                  Бесспорно, getID3 — отличная вещь. Я, кстати, о ней изначально упомянул в ссылках.
                                  Но желание самому «залезть внуть» видимо неистребимо )
                                  • 0
                                    вы упомянули про «Неплохая библиотека для получения информации о mp3.»
                                    что это getID3 не узнаешь, если по ссылке не пройдешь, вот вам все про нее и говорят

                                    а статья отличная, спасибо
                              • +1
                                Очень интересно, спасибо.

                                Кому интересно и кто хочет углубить свои знания в этой теме: в нашем с форумом программистов журнала тоже есть интересная статья про устройство mp3 файла.
                                Вот ссылка на онлайн просмоторщик номера: www.procoder.info/index.php?option=com_flippingbook&view=book&id=6
                                Спасибо за внимание.
                                • +1
                                  Я не понимаю почему меня заминусовали?

                                  Я предложил информацию, которая может быть интересна тем, кто хочет подробнее ознакомится с темой.
                                  • +1
                                    На хабре не любят внешние ссылки с саморекламой. Вот вы и поплатились.
                                    • +1
                                      Лично я ссылки на _некоммерческие_ проекты рекламой не считаю, даже наоборот это источник дополнительных знаний.
                                      • 0
                                        Я с вами полностью согласен. На самом деле ведь могу ошибаться. Всё вышесказанное лишь догадка, предположение и основано на личных наблюдениях. А тут кто его знает в чём причина…
                                        • +2
                                          Причина одна и она давно известна. Стадность пользователей Хабрахабра.
                                        • 0
                                          Текст уж больно на рекламу похож, если бы просто написали ссылку и подпись «Ещё по теме», думаю эффект был бы диаметрально противоположный ;)
                                  • 0
                                    Мега-полезная статья для меня! Плюсую.
                                    • 0
                                      Спасибо, интересно (:
                                      • 0
                                        интересно, какой мудак минус поставил (:
                                      • –4
                                        Спасибо, позновательно.
                                        • 0
                                          Использовал для определения длины mp3-трека на сайте простенький класс. Всего 340 строчек кода в нём и есть всё необходимое. Длина определяется одной строчкой кода.
                                          Сейчас к сожалению ссылок найти не могу на него, но если будет интересно, дам ссылку на сайт, где всё это дело применялось.
                                          • 0
                                            340 строчек… немало. В моем примере (ссылка в конце статьи) php-скрипт занимает 190 строк.
                                            Он получает всю необходимую информацию о mp3 файле, включая ID3v1-теги и заголовок ID3v2-тегов.

                                            Учитывая что я не ставил цель написать максимально короткий скрипт — думаю что можно порядком сократить.
                                            • 0
                                              Полностью не просмотрел вашу работу, но на первый взгляд тот скрипт, что использовался мною, чем-то более функционален. Без один, он просто писался под другие цели :) За статью спасибо, при необходимости вернусь к ней снова и испробую на деле.
                                          • 0
                                            Сразу лабораторные по теории кодирования информации вспоминаются… Эх золотое было время…
                                            • +1
                                              Emphasis это не «акцент», это специфическое предыскажение для некоторых систем шумоподавления типа Dolby, например. Соответственно, наличие этого признака должно было включать или выключать экспандер в тракте. Сегодня не используется.
                                              • 0
                                                Согласен с Вами. Описание этого поля как «Акцент» я где-то увидел в свое время и запомнил именно так — показалось удобно.
                                                В любом случае это поле не является значимым в наше время.
                                              • +1
                                                > Однако в данном формате подразумевается, что 1 килобит = 1000 бит, а не 1024

                                                В любом формате 1 килобит = 1000 бит
                                                  • 0
                                                    Спасибо за информацию. Буду иметь ввиду. Но все же считаю, что об этом было необходимо упомянуть, так как кто-либо мог об этом не знать и использовать 1024. А искать потом ошибку — очень не просто как мне кажется
                                                    • 0
                                                      Да об этом стоит упоминать — ибо многие еще не привыкли к этой системе, в результате возникает путаница. Я до сих пор говоря килобайт подразумеваю 1024 байта, хотя это не верно — старая плохая привычка =)
                                                • 0
                                                  вот это респект…
                                                  первая длинная статья, которую прочитал с интересом и до конца… )))
                                                  Спасибо, взял на заметку (в избранное)
                                                  • 0
                                                    Хабр — торт.
                                                    • 0
                                                      Вы пересказали спецификацию. А в чем прикол?
                                                      • 0
                                                        Я попытался собрать информацию о информативной структуре mp3-файла в одном месте, на русском языке, с русскими таблицами и примерами. Лично мне такая информация очень помогла бы в своё время. И поэтому, когда я это собрал, я подумал — что неплохо бы поделиться. Как-то так.
                                                        • 0
                                                          Понял, ключевое слово — русский.
                                                          • 0
                                                            Далеко не все отлично знают английский язык. (я в этих «не все» :)
                                                      • 0
                                                        Вот именно этой информации можно нарыть без проблем, кто хоть раз озадачивался этой проблемой.
                                                        Я в поисках инфы для построения waveforms. Пример.
                                                        Для mp3 решение еще не нашел.
                                                        • 0
                                                          Позволю себе исправить одну небольшую неточность в подсчете длины при постоянном битрейте (CBR).
                                                          Например, файл имеет размер 350670 байт. Есть ID3v1 теги (128 байт) и ID3v2 теги (1024 байта). Битрейт = 96. Следовательно размер аудиоданных равен 350670 – 128 – 1024 = 349518 байт.
                                                          Длительность = 349518 / 96000 * 8 = 29,1265 = 29 секунд

                                                          Мы должны учитывать размеры заголовков фреймов. Если у нас N фреймов аудиоданных, то нужно будет еще отнять от размера файла размер заголовков всех фреймов (8*N), т.е. итоговая формула:

                                                          length = (filesize - 128 - 1024 - 8*N) / (bitrate * 8).

                                                          Поправка небольшая, но все же ;)
                                                          • 0
                                                            Гм, оказывается, во-первых, длина заголовка фрейма 4 байта (в статье была опечатка), а, во-вторых, согласно экспериментальным данным, отнимать все же размер заголовков не надо (почему, это уже другой вопрос — возможно, там битрейт учитывает заголовки)…
                                                            • 0
                                                              Опечатку исправил, спасибо.
                                                              Статья о структуре самих аудио-данных еще готовится. Возможно там и будет видно, как заголовки фреймов вписываются в общую картину
                                                          • 0
                                                            Скажите, а есть ли ограничение хранения звуков в mp3 по частоте? скажем 20000Hz может там быть?

                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.