Blog. Just Blog

Работа с метаданными MP3-файлов на Ассемблере

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

Во многих файлах, в частности музыкальных и видео, присутствуют особые блоки данных, так называемые метаданные или теги. Метаданные содержат информацию о названии композиции и альбома, имени исполнителя, жанре, номере трека и т.п. Их используют программные и аппаратные проигрыватели, каталогизаторы и другие средства для работы с мультимедийными файлами. Давайте посмотрим, как можно получить теги из MP3-файла или изменить их средствами самой системы без ручного парсинга внутренней структуры файла.

Начиная с Windows Vista, для работы с метаданными файлов используется интерфейс IPropertyStore. Нам понадобится GUID и описание интерфейса, а также несколько структур и констант, о которых не знает FASM.
  1. struct PROPVARIANT
  2.     vt        dw ?
  3.     wReserved rw 3
  4.     struct
  5.         cbSize    dd ?
  6.         pBlobData dd ?
  7.     ends
  8. ends
  9.  
  10. struct PROPKEY
  11.      fmtid rb 16
  12.      pid   dd ?
  13. ends
  14.  
  15. GPS_READWRITE = 2
  16. VT_BSTR       = 8
  17.  
  18. ; GUID {886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}
  19. IID_IPropertyStore dd 0886D8EEBh
  20.                    dw 08CF2h
  21.                    dw 04446h
  22.                    db 08Dh, 002h, 0CDh, 0BAh, 01Dh, 0BDh, 0CFh, 099h
  23.  
  24. ; IID_IPropertyStore Interface
  25. struct IPropertyStore
  26.     ; IUnknown
  27.     QueryInterface dd ?   ; 000h
  28.     AddRef         dd ?   ; 004h
  29.     Release        dd ?   ; 008h
  30.     ; IPropertyStore
  31.     GetCount       dd ?   ; 00Ch
  32.     GetAt          dd ?   ; 010h
  33.     GetValue       dd ?   ; 014h
  34.     SetValue       dd ?   ; 018h
  35.     Commit         dd ?   ; 01Ch
  36. ends
Список метаданных медиафайлов можно посмотреть в MSDN. Какие-то теги поддерживаются аудиофайлами, какие-то только для видео, а какие-то универсальные. Нас интересуют GUID параметров и их числовые идентификаторы. Мы будем получать из файла теги "Title", "Album title", "Artist" и "Duration", для них эти данные будут следующими:
  1. ; GUID {F29F85E0-4FF9-1068-AB91-08002B27B3D9}
  2. PKEY_Title dd 0F29F85E0h
  3.            dw 04FF9h
  4.            dw 01068h
  5.            db 0ABh, 091h, 008h, 000h, 02Bh, 027h, 0B3h, 0D9h
  6. PKEY_Title_pid = 2
  7.  
  8. ; GUID {56A3372E-CE9C-11D2-9F0E-006097C686F6}
  9. PKEY_Music_AlbumTitle dd 056A3372Eh
  10.                       dw 0CE9Ch
  11.                       dw 011D2h
  12.                       db 09Fh, 00Eh, 000h, 060h, 097h, 0C6h, 086h, 0F6h
  13. PKEY_Music_AlbumTitle_pid = 4
  14.  
  15. ; GUID {56A3372E-CE9C-11D2-9F0E-006097C686F6}
  16. PKEY_Music_Artist dd 056A3372Eh
  17.                   dw 0CE9Ch
  18.                   dw 011D2h
  19.                   db 09Fh, 00Eh, 000h, 060h, 097h, 0C6h, 086h, 0F6h
  20. PKEY_Music_Artist_pid = 2
  21.  
  22. ; GUID {64440490-4C8B-11D1-8B70-080036B11A03}
  23. PKEY_Media_Duration dd 064440490h
  24.                     dw 04C8Bh
  25.                     dw 011D1h
  26.                     db 08Bh, 070h, 008h, 000h, 036h, 0B1h, 01Ah, 003h
  27. PKEY_Media_Duration_pid = 3
Принцип работы с метаданными видео- и аудиофайлов примерно следующий. Сперва при помощи функции SHGetPropertyStoreFromParsingName получаем ссылку на интерфейс IPropertyStore для выбранного медиафайла. Обратите внимание, что путь к файлу обязательно должен быть полным, а строка с этим путем должна быть в юникоде без вариантов. Сама функция также поддерживается только в Windows Vista и более новых системах.
  1.         invoke  CoInitialize,0
  2.  
  3.         ; Получить полный путь к файлу
  4.         invoke  GetFullPathName,sample,MAX_PATH,fname,NULL
  5.         ; Получить интерфейс IPropertyStore
  6.         invoke  SHGetPropertyStoreFromParsingName,fname,\
  7.                 NULL,GPS_READWRITE,IID_IPropertyStore,pStore
Для получения нужного тега структура PROPKEY заполняется данными GUID и идентификатором этого тега, затем вызывается метод GetValue интерфейса IPropertyStore. Я специально подобрал теги разных типов: мультистроковый, обычный строковый и целочисленный, чтобы показать, как обрабатывать их результат.
  1.         ; Исполнитель
  2.         mov     edi,prop.fmtid
  3.         mov     esi,PKEY_Music_Artist
  4.         movsd
  5.         movsd
  6.         movsd
  7.         movsd
  8.         mov     [prop.pid],PKEY_Music_Artist_pid
  9.  
  10.         mov     eax,[pStore]
  11.         mov     eax,[eax]
  12.         stdcall [eax+IPropertyStore.GetValue],[pStore],prop,variant
  13.         ; [variant.cbSize] - количество строк в мультистроковом параметре
  14.         ; или 0, если тег не заполнен
  15.         ; [variant.pBlobData] - указатель на указатель на строку с названием
  16.         ; исполнителя, но только если значение [variant.cbSize] больше 0
  17.         cmp     [variant.cbSize],0
  18.         je      @f
  19.         mov     eax,[variant.pBlobData]
  20.         mov     eax,[eax]
  21.         ; EAX = указатель на строку с названием исполнителя
  22. @@:
  23.         ; Название альбома
  24.         mov     edi,prop.fmtid
  25.         mov     esi,PKEY_Music_AlbumTitle
  26.         movsd
  27.         movsd
  28.         movsd
  29.         movsd
  30.         mov     [prop.pid],PKEY_Music_AlbumTitle_pid
  31.  
  32.         mov     eax,[pStore]
  33.         mov     eax,[eax]
  34.         stdcall [eax+IPropertyStore.GetValue],[pStore],prop,variant
  35.         ; [variant.cbSize] - указатель на строку названия альбома
  36.         ; или 0, если тег не заполнен
  37.  
  38.         ; Название трека
  39.         mov     edi,prop.fmtid
  40.         mov     esi,PKEY_Title
  41.         movsd
  42.         movsd
  43.         movsd
  44.         movsd
  45.         mov     [prop.pid],PKEY_Title_pid
  46.  
  47.         mov     eax,[pStore]
  48.         mov     eax,[eax]
  49.         stdcall [eax+IPropertyStore.GetValue],[pStore],prop,variant
  50.         ; [variant.cbSize] - указатель на строку названия композиции 
  51.         ; или 0, если тег не заполнен
  52.  
  53.         ; Длительность
  54.         mov     edi,prop.fmtid
  55.         mov     esi,PKEY_Media_Duration
  56.         movsd
  57.         movsd
  58.         movsd
  59.         movsd
  60.         mov     [prop.pid],PKEY_Media_Duration_pid
  61.  
  62.         mov     eax,[pStore]
  63.         mov     eax,[eax]
  64.         stdcall [eax+IPropertyStore.GetValue],[pStore],prop,variant
  65.  
  66.         mov     eax,[variant.cbSize]
  67.         ; Длительность композиции возвращается в 100-наносекундных значениях
  68.         ; ее надо преобразовать в обычные секунды
  69.         xor     edx,edx
  70.         mov     ecx,10000000
  71.         div     ecx
  72.         ; EAX = длительность композиции в секундах
Можно немного сократить код, если сразу хранить параметры в нужном формате и обращаться сразу к ним, не копируя в структуру PROPKEY.
  1. ; GUID {56A3372E-CE9C-11D2-9F0E-006097C686F6}
  2. PKEY_Music_Artist dd 056A3372Eh
  3.                   dw 0CE9Ch
  4.                   dw 011D2h
  5.                   db 09Fh, 00Eh, 000h, 060h, 097h, 0C6h, 086h, 0F6h
  6. PKEY_Music_Artist_pid dd 2
  7.         ...
  8.         ...
  9.         mov     eax,[pStore]
  10.         mov     eax,[eax]
  11.         stdcall [eax+IPropertyStore.GetValue],[pStore],PKEY_Music_Artist,variant
При изменении тегов сперва заполняется структура PROPKEY или берутся уже заполненные данные, тут все по аналогии с чтением тегов. Затем настраивается структура PROPVARIANT с новыми значениями строк или чисел, в зависимости от того, какой тип имеет изменяемый тег. Когда все подготовлено, вызывается метод SetValue. В самом конце, когда внесены все изменения во все необходимые теги, они фиксируются в файле при помощи метода Commit.
  1.         ; Заменить тег
  2.         mov     edi,prop.fmtid
  3.         mov     esi,PKEY_Music_AlbumTitle
  4.         movsd
  5.         movsd
  6.         movsd
  7.         movsd
  8.         mov     [prop.pid],PKEY_Music_AlbumTitle_pid
  9.  
  10.         ; Заполнить структуру для обычной строки
  11.         mov     [variant.vt],VT_BSTR
  12.         mov     [variant.cbSize],szNew
  13.         mov     [variant.pBlobData],0
  14.  
  15.         ; Установить новое значение тега
  16.         mov     eax,[pStore]
  17.         mov     eax,[eax]
  18.         stdcall [eax+IPropertyStore.SetValue],[pStore],prop,variant
  19.  
  20.         ; Сохранить изменения
  21.         mov     eax,[pStore]
  22.         mov     eax,[eax]
  23.         stdcall [eax+IPropertyStore.Commit],[pStore]
Тут надо понимать, что не все значения можно изменить. Например, продолжительность композиции и битрейт являются постоянными и могут быть только прочитаны, а вот теги типа названия альбома или имени исполнителя поменять очень даже можно.

После получения или изменения тегов не забываем освобождать задействованные ресурсы:
  1.         ; Прибраться за собой
  2.         mov     eax,[pStore]
  3.         mov     eax,[eax]
  4.         stdcall [eax+IPropertyStore.Release],[pStore]
  5.  
  6.         invoke  CoUninitialize
В приложении примеры программ с исходными текстами, одна из которых читает метаданные из музыкального файла, а вторая записывает их в файл.

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

MP3.Metadata.Demo.zip (314,139 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (17.03.2022 в 13:07):
Как выяснилось еще, в некоторых случаях функция возвращает неправильное значение длительности композиции. Готовлю статью про ручной парсинг внутренней структуры MP3.
ManHunter (02.02.2022 в 15:09):
Как выяснилось, функция некорректно работает в том случае, если в значении тега присутствует символ "/", например, группа "To/Die/For" возвращается как "To". Причина кроется где-то в глубине системы.
Вот статья про ручной парсинг метаданных: https://www.manhunter.ru/assem...emblere.html
ManHunter (24.11.2021 в 13:20):
Эпоха потребления, увы.
Grey (24.11.2021 в 13:18):
140 просмотров и тишина..., блин да это шедевр.
Кэп, по моему, ты давно переплюнул уроки Iczelion'а. Этот раздел в твоем блоке просто бриллиант!!!
Спасибо тебе человеческое за твою огромную многолетнюю кропотливую работу!
Grey (23.11.2021 в 08:56):
Аплодисменты!

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

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

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