[This page contain russian (DOS-866 codepage) description of OGM files] [HTML version avaible here: http://shounen.ru/docs/ogm/ogm.shtml] Описание формата OGM Что есть в этом документе: Формат OGM-файла, принципы объединения нескольких элементарных потоков в один физический файл, структура заголовков аудио, видео и текстовых потоков. Чего в этом документе нет: Структры данных внутри аудио, видео и текстовых потоков. Вступление. Некоторое время назад, на смену формата mp3 был создан формат OGG Vorbis. Для хранения звукового потока vorbis был разработан стандарт OGG Bitstream, который в дальнейшем применили для хранения видео. 1. Порядок бит и байт. В кратце: LSb+little endian. Порядок бит в байте: 01234567 (другими словами, последний, правый бит имеет макисмальное значение) Порядок байт при записи многобайтовых чисел: 0123 (0xAB 0xCD 0xEF 0x12 в файле означают число 0x12EFCDAB) 2. Основные концепции. 1) Логический поток - данные одного типа (например, видео-поток, первый аудио-поток, второй аудио-поток) 2) Пакет - порция данных логического потока (его структура целиком и полностью лежит на совести кодера/декодера). 3) Сегмент - кусок данных длиной в 255 байт (и менее) длиной. Сегмент с нулевой длиной так же допустим. У сегмента так же нет ни заголовка ни каких-либо данных помимо содержимого (content'а). 4) Страница - несколько сегментов с заголовком страницы. 5) Физический поток - объединение нескольких логических потоков. OGM-файл и представляет из себя этот самый физический поток. 3. Методы объединения страниц нескольких логических потоков внутри одного физического потока: Chaining (объединение по цепочке): #############$$$$$$$$$$$$$@@@@@@@@@@@ ####поток1###$$$поток2$$$$@@@поток3@@ #############$$$$$$$$$$$$$@@@@@@@@@@@ Grouping (чередование): #$@#$$@@#$@# #$@#$$@@#$@# #$@#$$@@#$@#... (В случае AVI-файла это называют interleave) У каждого логического потока есть признаки НАЧАЛА ПОТОКА (Begin of Stream, BoS) и КОНЦА ПОТОКА (End of Stream, EoS). Эти признаки обозначаются флагами в заголовке страницы. (BoS - в первой странице логического потока, EoS - в последней). Соответственно, в физическом потоке может быть несколько BoS и EoS. (в случае одного видео и двух аудио, например, три BoS и три EoS). В случае нескольких логических потоков внутри физического, все страницы с BoS (т.е. "начала логических потоков") должны быть расположены до первой страницы без флага BoS. Порядок чередования страниц из разных логических потоков может быть произвольным, гарантируется только, что для каждого логического потока страницы расположены в хронологическом порядке (т.е. N-ая страница будет расположена в файле всегда позже, чем N-1). В случае смешанного типа объединения (т.е. и Chaining'a и Grouping'а) все EoS (последние) страницы первой группы должны появиться до первой (BoS) страницы второй группы. 4. Структуры пакета, сегмента и страницы: Структура пакета, как было уже сказано выше, целиком и полностью определяется декодером. Пакет режется в несколько сегментов. Сегмент так же не имеет собственной структруры. По длинне он должен быть не более 255 байт (в частности, сегмент может быть нулевой длины, т.е. не содержать в себе информации). Сегменты объединяются в страницы, к ним добавляется заголовок страницы. (не более 255 сегментов в странице) Примечания: пакет всегда начинается с начала страницы. пакет может быть в размере больше, чем максимально допустимый размер страницы, в этом случае он продолжается в следующей страницы, а в заголовке такой страницы (которая содержит продолжение пакета) выставляется соответствующий флаг (continued packet flag) 5. Заголовок страницы (page_header): offset size type value or/and comments, name 0x0 4 FourCC capture_pattern, 'OggS', 0x4f 0x67 0x67 0x53 0x4 1 ? должна быть 0, версия структуры заголовка 0x5 1 bitfield header_type_flag, 0x1 - страница содержит продолжение пакета 0x2 - BoS (Begin of Stream, начало потока) 0x4 - EoS (End of Stream, конец потока) 0x6 8 int64 absolute_granule_position, номер последнего сэмпла/фрейма целиком закодированного в этом пакете (сэмплы, закодированные в пакете, который не умещается в эту страницу, а продолжаются дальше, не засчитываются). Число -1 означает что в указанной странице нет целиком закодированных сэмплов. N.B. little-endian формат числа (как у Интела) 0xE 4 int32 page_serial_number, номер логического потока в указанном физическом потоке. Должно быть уникальным в рамках одного физического потока для всех логических. 0x12 4 int32 Page_sequence_no, порядковый номер страницы в ЛОГИЧЕСКОМ потоке. 0x16 4 uint32 Page_checksum, CRC код страницы 0x1A 1 byte page_segments, число сегментов в странице, диапазон значений [0-255] (0 и 255 допустимы) 0x1B * byte[] segment_table, таблица размеров сегментов в странице. Количество записей в таблице определяется page_segment, в случае нулевого значения, segment_table отсутствует. ...................... содержимое сегмента Замечания: Страница может быть одновременной первой и последней в потоке (BoS и EoS установленны одновременно) Размер данных страницы может быть нулевой, в этом случае страница просто пропускается. Максимальный размер данных в странице составляет 255*255=65025 байт. Если последний сегмент в странице имеет размер 255 байт, то после него, в segment_table заносятся данные о "нулевом" сегменте (сегменте с длиной 0). Выглядит это так (например, данные у нас занимают 765 байт): число сегментов:4 таблица сегментов: 0xFF 0xFF 0xFF 0x0 Исключение - сегмент с размером данных 65025 байт, т.к. нулевой сегмент просто некуда дописывать. Если последний сегмент отличен от 0xFF, то нулевой после него не добавляется. 6. Определение размера сегмента и данных: размер данных в сегменте: сумма всех значений в segment_table. Количество значений указано в page_segments. размер сегмента: 0x1B+page_segments+сумма всех значений segment_table. (это значение всегда меньше 64к). 7. Стуктура заголовка видео-потока. offset size description 0x0 char тип пакета, для заголовка должен быть 0x1 0x1 char[8] streamtype, для видео должен быть 'video ' последние три символа - 0x0 (не пробелы) 0x9 char[4] используемый кодек (FourCC) 0xD int32 размер структуры 0x11 int64 интервал времени между "юнитами", в 1/10000000 секунды (time_unit) 0x19 int64 кадров в "юнит". (samples_per_unit) fps=10000000*samples_per_unit/time_unit 0x21 int32 default_len (честно, не знаю что такое, обычно равно 1) 0x25 int32 buffersize - размер необходимого буфера для декодирования 0x29 int32 bit_per_sample, глубина цвета (битов на пиксел) N.B. В оригинальной спецификации указано, что эта величина - int16, но практика показывает, что всё-таки 32. 0x2D int32 width, ширина картинки (в пикселах) 0x31 int32 Heigth, высота картинки (в пикселах) 8. Стурктура заголовка аудио-потока (для vorbis-audio). 0x0 byte тип пакета, для заголовка должен быть 0x1 0x1 char[6] для vorbis-audio должен быть 'vorbis' 0x7 int32 vorbis version, в настоящий момент =0. 0xB byte число каналов 0xC int32 частота дискретизации сигнала 0x10 int32 минимальный битрейт (обычно 0) 0x14 int32 средний битрейт 0x18 int32 макисмальный битрейт (обычно 0) 0x1C byte размеры блоков для декодирования (экспонента двойки) старшие 4 бита - для blocksize_0 младшие 4 бита - для blocksize_1 0x1D byte младший бит - framing_flag 9. Стуктура заголовка текстового потока. 0x0 byte тип пакета, для заголовка должен быть 0x1 0x1 char[8] тип потока, для текста должен быть "text " (последние четыре символы не пробелы, а 0) 0x9 char [4] subtype, для текста = 0. 0xD int32 размер структуры 0x11 int64 длительность отсчёта (для текста 1мс, 0x2710) 0x19 int64 всегда 1 0x21 int32 default_len, =1. 0x25 int32 увы, назначения не знаю (обычно или 96 или 60) 0x29 4 x int32 unknown, usually 0. 10. Комментарии Каждый поток может иметь комментарии. Они обычно находятся во второй или третьей странице потока. N.B. в комментариях строки не являются классическими C string'ами (ASCIIZ), и НЕ заканчиваются нулевым байтом. Строка состоит из размера (4байта) и самой строки (что-то вроде строк в Pascal). Структура коментария: 0x0 byte тип пакета, для комментария должен быть 0x3 0x1 int32 vendor_vector_size 0x5 byte[] название софта, создавшего файл ??? int32 количество комментариев (user_comment_list_length) .................. комментарии Каждый комментарий состоит из 4байтного поля размера строки и самой строки. Внутри каждая строка выглядит примерно так: field_name=field_value При этом название field_name регистронезависимое, и состоит только из ASCII7. Точнее (цитирую спецификацию): The field name is case-insensitive and may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. ASCII 0x41 through 0x5A inclusive (characters A-Z) is to be considered equivalent to ASCII 0x61 through 0x7A inclusive (characters a-z). значение поля (field_value) представяет из себя UTF-8 строку без заключительного 0. В случае, если комментарии отсутствуют (user_comment_list_length=0), то, соответствено, списка комментариев нет. В конце после списка комментариев (или даже в случае отсутствия такого списка) следует байт 0x1 (framing_bit), его отстутствие - признак повреждённого фрейма. Названия полей могут быть произвольные, могут повторяться (т.е. например, три строки вида AUTHOR=...). Примерный список возможных названий полей (список не полный) LANGUAGE TITLE VERSION ALBUM TRACKNUMBER ARTIST PERFORMER DESCRIPTION GENRE DATE LOCATION 11. Пример кода, работающего с OGM файлом. Код находится в файле ogmnav.c (http://shounen.ru/docs/ogm/ogmnav.c) Замечания к коду: 1) Код не является оптимальным по скорости, и написан так, чтобы в максимально-удобной форме показать принципы навигации между пакетами. Две простейшие оптимизации, которые можно применить - это буфферизация чтения и более аккуратный пропуск ненужных страниц. (при помощи fseek, а не чтения "в никуда"). 2) Относительно использования массива unsigned char вместо структуры, описывающей заголовок страницы. Причины использования очевидны. Во-первых, компилятор далеко не всегда распологает структуру в памяти так, как она записана в декларации. Так, вполне может быть, что компилятор, с целью оптимизации, выравняет все переменные на границы машинных слов. (т.е. внутри структуры появятся "дырки" и fread этой структуры из памяти даст некорректный результат). Второе - код с массивом будет одинаково хорошо работать как на машине с litte-endian архитектурой (Intel), так и на big-endian (Mac, Alpha). 3) В программе не проверяется CRC каждой страницы Более "живой" пример находится в модуле ogm.c из состава AVInfo (http://shounen.ru/soft/avinfo/), специфика модуля такова, что ему передаются два парметра - хэндл уже открытого на чтение файла и численный параметр, отвечающий за степень детализации собираемой информации. 12. Ссылки на ресурсы http://vorbis.org/ - авторы спецификации контейнера http://tobias.everwicked.com/ - автор DirectShow фильтра для воспроизведения, собственно, автор идеи засунуть видео в OGM. (!Документация содержит ошибки!) http://mplayerhq.hu - довольно популярный плеер, не использует VfW, в частности, играет OGM. 13. Заключение, копирайты. (с) George Shuklin,2004 gs@shounen.ru, http://shounen.ru/ . Использование статьи для разработчиков, в т.ч. для написания коммерческого софта абсолютно свободное. Публикация на web-сайтах и в бумажных изданиях возможна при сохранении явно указанного авторства и ссылок, приведённых в статье (при условии их действительности на момент публикации). Если вы заметили в статье грамматическую, синтаксическую, логическую ошибку, опечатку, неточность, имеете уточнённые данные по значению полей, отмеченных вопросительными знаками, хотите добавить линк в список ресурсов, пишите на указанный адрес.