Blog. Just Blog

Парсинг метаданных OGG-файлов на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Парсинг метаданных OGG-файлов на Ассемблере
Парсинг метаданных OGG-файлов на Ассемблере

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

Сперва немного теории. OGG-файл состоит из так называемых страниц, каждая из которых состоит из заголовка и данных, в зависимости от типа секции. Для удобства дальнейшей работы я оформил заголовок в виде структуры:
  1. struct OGG_PAGE_HEADER
  2.         dMagic    dd ?  ; Capture pattern 'OggS'
  3.         bVersion  db ?  ; Version
  4.         bType     db ?  ; Header type
  5.         qGranule  dq ?  ; Granule position
  6.         dSerial   dd ?  ; Bitstream serial number
  7.         dPage     dd ?  ; Page sequence number
  8.         dCRC      dd ?  ; Checksum
  9.         bSegments db ?  ; Page segments
  10. ends
Более подробное описание полей заголовка можете прочитать здесь. После заголовка следует таблица сегментов и сами данные. Полный размер страницы вычисляется следующим образом. К размеру заголовка прибавляется размер таблицы сегментов, он равен значению поля bSegments и не превышает 255 байт. Каждый байт таблицы равен размеру сегмента, надо просуммировать эти значения и добавить к длине заголовка с таблицей. Таким образом, максимальный размер данных для одной страницы не может превышать 65535 байт. Также опытным путем установлено, что метаданные хранятся во второй по счету странице (поле dPage=1, отсчет страниц начинается с 0) и могут продолжаться в следующих страницах если, например, в них сохранена обложка альбома или какая-то объемная портянка текста.

При парсинге поочередно читаем из файла заголовки каждой страницы, если поле dPage имеет нужное значение, то определяем размер данных, резервируем под них память и считываем. Иначе просто перемещаем указатель в файле на окончание текущей страницы.
  1.         invoke  CreateFile,sample,GENERIC_READ,FILE_SHARE_READ,\
  2.                 0,OPEN_EXISTING,0,0
  3.         mov     [desc],eax
  4. loc_read:
  5.         ; Прочитать заголовок страницы
  6.         invoke  _lread,[desc],buff,sizeof.OGG_PAGE_HEADER
  7.  
  8.         ; Проверить корректность страницы
  9.         mov     esi,buff
  10.         cmp     [esi+OGG_PAGE_HEADER.dMagic],'OggS'
  11.         jne     loc_close
  12.  
  13.         ; Количество сегментов в странице
  14.         movzx   ebx,[esi+OGG_PAGE_HEADER.bSegments]
  15.  
  16.         ; Прочитать таблицу размеров сегментов
  17.         invoke  _lread,[desc],tTable,ebx
  18.  
  19.         ; Подсчитать длину всех сегментов
  20.         mov     ecx,ebx
  21.         mov     esi,tTable
  22.         xor     ebx,ebx
  23. @@:
  24.         lodsb
  25.         movzx   eax,al
  26.         add     ebx,eax
  27.         loop    @b
  28.  
  29.         ; Метаданные записаны в страницу 1
  30.         mov     esi,buff
  31.         cmp     [esi+OGG_PAGE_HEADER.dPage],1
  32.         jne     loc_skip
  33.  
  34.         ; Выделить память под метаданные
  35.         invoke  GlobalAlloc,GMEM_ZEROINIT,ebx
  36.         mov     [hMem],eax
  37.         invoke  GlobalLock,[hMem]
  38.         mov     [pMem],eax
  39.  
  40.         ; Прочитать метаданные
  41.         invoke  _lread,[desc],[pMem],ebx
  42.  
  43.         ; Сигнатура блока метаданных
  44.         mov     esi,[pMem]
  45.         cmp     byte [esi],3
  46.         jne     loc_free
  47.         cmp     dword [esi+1],'vorb'
  48.         jne     loc_free
  49.         cmp     word [esi+5],'is'
  50.         jne     loc_free
  51.  
  52.         ; Пропустить сигнатуру
  53.         add     esi,7
  54.         ; Длина строки названия кодировщика
  55.         add     esi,dword[esi]
  56.         add     esi,4
  57.         ; Количество тегов
  58.         mov     ebx,dword[esi]
  59.         add     esi,4
  60.  
  61.         ; Метаданных нет
  62.         or      ebx,ebx
  63.         jz      loc_free
  64. loc_get_tags:
  65.         push    ebx
  66.  
  67.         ; Размер тега
  68.         mov     ecx,dword[esi]
  69.         add     esi,4
  70.  
  71.         ; Первые 4 символа названия тега
  72.         mov     eax,[esi]
  73.         ; Перевести в нижний регистр
  74.         or      eax,20202020h
  75.         mov     ebx,title
  76.         cmp     eax,'titl'
  77.         je      @f
  78.         mov     ebx,artist
  79.         cmp     eax,'arti'
  80.         je      @f
  81.         mov     ebx,album
  82.         cmp     eax,'albu'
  83.         je      @f
  84.  
  85.         ; Пропустить тег
  86.         add     esi,ecx
  87.         jmp     loc_next_tag
  88. @@:
  89.         ; Найти символ-разделитель
  90.         mov     al,'='
  91.         mov     edi,esi
  92.         repne   scasb
  93.         ; Передвинуть указатель на значение тега
  94.         mov     esi,edi
  95.         ; Разделитель не нашелся, битые данные
  96.         or      ecx,ecx
  97.         jz      loc_next_tag
  98.  
  99.         ; UTF-8 -> юникод
  100.         push    ecx
  101.         invoke  MultiByteToWideChar,CP_UTF8,0,edi,ecx,0,0
  102.         invoke  MultiByteToWideChar,CP_UTF8,0,edi,-1,ebx,eax
  103.         pop     ecx
  104.         ; Перейти к следующему тегу
  105.         add     esi,ecx
  106. loc_next_tag:
  107.         pop     ebx
  108.         ; Все теги обработали?
  109.         sub     ebx,1
  110.         jnz     loc_get_tags
  111.  
  112.         ...
  113.         ; artist -> наименование исполнителя
  114.         ; album -> название альбома
  115.         ; title -> название трека
  116.         ...
  117.  
  118. loc_free:
  119.         ; Освободить память
  120.         invoke  GlobalUnlock,[hMem]
  121.  
  122.         jmp     loc_close
  123.  
  124. loc_skip:
  125.         ; Перейти к следующей странице
  126.         invoke  _llseek,[desc],ebx,FILE_CURRENT
  127.         cmp     eax,-1
  128.         je      loc_close
  129.         jmp     loc_read
  130. loc_close:
  131.         invoke  CloseHandle,[desc]
Метаданные начинаются с сигнатуры 03h,'vorbis', это обязательно надо проверять. Затем идет DWORD с размером строки описания производителя и сама строка производителя. После этого начинается собственно сам блок тегов. Он начинается с DWORD'а, в котором записано количество тегов. Каждый тег имеет структуру: DWORD с длиной строки и строка типа "TITLE=My Song", где TITLE - название тега и после знака "=" его значение, записанное в кодировке UTF-8. Согласно документации, название тега может быть как в верхнем, так и в нижнем регистре, а также вперемешку.

В приложении пример программы с исходным текстом, которая парсит метаданные из OGG-файла и выводит их.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

OGG.Metadata.Demo.zip (401,737 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 513 | Комментариев: 0

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0076 сек.) / Память: 4.5 Mb
Наверх