Пользователь
0,0
рейтинг
6 сентября 2010 в 15:53

Разработка → Внутри 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)
Андрей Привалов @negasus
карма
108,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (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 может там быть?

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