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.         add     esi,7
  53.         add     esi,dword[esi]
  54.         add     esi,4
  55.         ; Количество тегов
  56.         mov     ebx,dword[esi]
  57.         add     esi,4
  58.  
  59.         ; Метаданных нет
  60.         or      ebx,ebx
  61.         jz      loc_free
  62. loc_get_tags:
  63.         push    ebx
  64.  
  65.         ; Размер тега
  66.         mov     ecx,dword[esi]
  67.         add     esi,4
  68.  
  69.         ; Первые 4 символа названия тега
  70.         mov     eax,[esi]
  71.         ; Перевести в нижний регистр
  72.         or      eax,20202020h
  73.         mov     ebx,title
  74.         cmp     eax,'titl'
  75.         je      @f
  76.         mov     ebx,artist
  77.         cmp     eax,'arti'
  78.         je      @f
  79.         mov     ebx,album
  80.         cmp     eax,'albu'
  81.         je      @f
  82.  
  83.         ; Пропустить тег
  84.         add     esi,ecx
  85.         jmp     loc_next_tag
  86. @@:
  87.         ; Найти символ-разделитель
  88.         mov     al,'='
  89.         mov     edi,esi
  90.         repne   scasb
  91.         ; Передвинуть указатель на значение тега
  92.         mov     esi,edi
  93.         ; Разделитель не нашелся, битые данные
  94.         or      ecx,ecx
  95.         jz      loc_next_tag
  96.  
  97.         ; UTF-8 -> юникод
  98.         push    ecx
  99.         invoke  MultiByteToWideChar,CP_UTF8,0,edi,ecx,0,0
  100.         invoke  MultiByteToWideChar,CP_UTF8,0,edi,-1,ebx,eax
  101.         pop     ecx
  102.         ; Перейти к следующему тегу
  103.         add     esi,ecx
  104. loc_next_tag:
  105.         pop     ebx
  106.         ; Все теги обработали?
  107.         sub     ebx,1
  108.         jnz     loc_get_tags
  109.  
  110.         invoke  wsprintf,buff,mask,artist,album,title
  111.         add     esp,20
  112.         invoke  MessageBox,0,buff,szTitle,0
  113.  
  114. loc_free:
  115.         ; Освободить память
  116.         invoke  GlobalUnlock,[hMem]
  117.  
  118.         jmp     loc_close
  119.  
  120. loc_skip:
  121.         ; Перейти к следующей странице
  122.         invoke  _llseek,[desc],ebx,FILE_CURRENT
  123.         cmp     eax,-1
  124.         je      loc_close
  125.         jmp     loc_read
  126. loc_close:
  127.         invoke  CloseHandle,[desc]
Метаданные начинаются с сигнатуры 03h,'vorbis', это обязательно надо проверять. Затем идет DWORD с размером строки описания производителя и сама строка производителя. После этого начинается собственно сам блок тегов. Он начинается с DWORD'а, в котором записано количество тегов. Каждый тег имеет структуру: DWORD с длиной строки и строка типа "TITLE=My Song", где TITLE - название тега и после знака "=" его значение, записанное в кодировке UTF-8. Согласно документации, название тега может быть как в верхнем, так и в нижнем регистре, а также вперемешку.

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

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

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


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 182 | Комментариев: 0

Комментарии

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

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

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

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