В предыдущей статье описывалась процедура настройки компьютера Mac mini, полностью готового к разработке мультимедийного приложения. В этой статье рассказывается о том, как сделать первый шаг на пути к разработке медиа-проигрывателя. Проигрыватель будет пока поддерживать только формат JPEG, но его функции легко расширить для поддержки других медиаформатов.
Прежде чем перейти к делу, я хотел бы поблагодарить всех, кто прислал свои отзывы о предыдущей статье. Благодаря вам я принял решение добавить в конце этого цикла как минимум одну статью, посвященную безопасности встраиваемых Linux™-устройств.
Требования к программе просмотра изображения
Начинаем разработку программы. Обычному устройству просмотра изображений требуются как минимум три функции:
- Отображение цветных растровых изображений на экране.
- Вывод списка графических файлов на жестком диске или подключенном внешнем носителе (во многих коммерческих устройствах для хранения данных используется внутренняя или внешняя флэш-память).
- Декодирование графических файлов.
Разумеется, желательно обеспечить и другие функции. В частности, крайне важна возможность масштабирования изображений под размер экрана. Кроме того, неплохо было бы реализовать красивые переходные эффекты при смене изображений.
Поддержка графики оборудованием
Начнем с поддержки графики. Оборудование Mac mini и программное обеспечение, установленные по инструкциям предыдущей статьи, дают вам несколько способов управления изображением на экране на уровне пикселов. Стандартный подход — запустить X-сервер, который у вас уже должен быть установлен, если вы в точности следовали инструкциям из предыдущей статьи. Позднее мы рассмотрим применение X, но пока будем считать этот вариант слишком сложным — не то чтобы ужасно сложным, но все же есть смысл рассмотреть сначала более простые пути.
Вместо системы X намного проще использовать напрямую драйвер фреймбуфера в ядре. Это дает удобный прямой доступ к видеопамяти. Ядро инициализирует дисплей в полноадресуемом режиме (APA); вам нужно только запросить у драйвера характеристики дисплея и внести адрес фреймбуфера в адресное пространство вашего процесса.
Наиболее часто приводятся следующие недостатки фреймбуфера:
- Значительная зависимость от устройства: приложение должно уметь преобразовывать данные из любого цветового представления в формат устройства фреймбуфера. Драйвер фреймбуфера может лишь сообщить требуемый формат данных, но не может за вас выполнить их преобразование.
- Более низкая производительность: большинство современных видеоплат обладают такими возможностями, как 3D-ускорение, аппаратная заливка, аппаратная отрисовка линий и т. д., которые не используются при работе с фреймбуфером. Если существует драйвер для X Window, то, как правило, он поддерживает все такие функции ускорения, и приложения автоматически пользуются их преимуществами. Однако во многих встраиваемых платформах аппаратное ускорение отсутствует, поэтому X в таких случаях только снижает производительность.
- Сложности для разработчика: из предыдущего пункта следует, что разработчику приложения придется писать либо портировать код работы с палитрой и отрисовки графических примитивов — шрифтов, многоугольников, и т. п.
В случае со встраиваемыми устройствами первый пункт имеет чисто номинальное значение, ведь по определению при разработке встраиваемого устройства вы уже ограничиваетесь каким-либо оборудованием, и ваша программа будет работать только с ним. О независимости от устройства стоит волноваться только в том случае, если вы планируете портировать код вашего приложения на другую платформу с иным форматом видеопамяти.
Второй пункт также зачастую не касается встраиваемых устройств. Основная причина, по которой я хочу рассказать вам о работе с фреймбуфером прежде, чем погрузиться в мир готового кода графических интерфейсов, заключается в том, что фреймбуфер — это, образно говоря, «наименьший общий знаменатель», и большинство встраиваемых Linux-устройств с графическим интерфейсом работают именно с фреймбуфером (даже если они используют X-сервер — довольно часто встречаются гибридные интерфейсы, о которых я тоже расскажу в этом цикле статей).
Доступ к фреймбуферу чаще всего получают через устройства /dev/fb* (в большинстве случаев —
/dev/fb0). Переключение режимов может осуществляться через интерфейс ioctl, либо из командной строки при помощи утилиты fbset. Для нее, в свою очередь, требуется конфигурационный файл /etc/fb.modes, где указываются частоты и глубина цвета для различных видеорежимов.
Можно указать видеорежим, используемый при запуске PowerPC®-системы, добавив соответствующий параметр ядра в файл настроек yaboot. Если вы привыкли работать на системах x86, то вам будет приятно узнать, что загрузчик yaboot практически идентичен загрузчику LILO в плане настройки и использования; по умолчанию файл настроек расположен в /etc/yaboot.conf, и когда вы внесете в него изменения, вы можете применить их, запустив утилиту ybin. Чтобы явно задать конкретный видеорежим на этапе загрузки, просто добавьте параметр append="video=ВИДЕОРЕЖИМ" в конец команды
yaboot, запускающей ядро, и запустите ybin, чтобы применить сделанные изменения (дальше будет подробнее рассказано о редактировании этих параметров). Например, файл yaboot.conf для Mac mini может выглядеть следующим образом:
Листинг 1. Пример файла yaboot.conf
boot=/dev/hda6
init-message="Нажмите TAB для получения списка вариантов или подождите..."
partition=8
timeout=30
install=/usr/lib/yaboot/yaboot
magicboot=/usr/lib/yaboot/ofboot
delay=10
enablecdboot
image=/boot/vmlinux-2.6.10-1.ydl.1
label=linux
read-only
initrd=/boot/initrd-2.6.10-1.ydl.1.img
root=/dev/hda8
append="video=radeonfb:1024x768-16"
|
(Если вы настраиваете iMac, то нужно указать видеодрайвер atyfb, так как в этом компьютере используется старый видеочип Mach64. Для задания корректных частот на iMac следует указывать видеорежим следующим образом: atyfb:vmode:17,cmode:16 — см. ниже).
Параметры в строке "video=" можно указывать в двух форматах: либо video=driver:hresxyres-depth
(где hres — физическое разрешение по горизонтали, yres — разрешение по вертикали и depth — код глубины цвета), либо video=driver:vmode:V,cmode:C
(где V
— номер видеорежима, а C - код глубины цвета). Обратите внимание, что коды vmode не несут какой-либо смысловой нагрузки, а просто обозначают номер режима из устаревшей спецификации Apple на содержимое PRAM. Эти коды перечислены в ядре Linux в файле drivers/video/macmodes.h.
Ко всему вышесказанному следует добавить, что, изменяя загрузочные видеопараметры, можно столкнуться с загадочными проблемами. Некоторые виды оборудования в компьютерах Mac (в данном случае я говорю об упомянутой в прошлой статье резервной системе на основе iMac, см. Ресурсы) упорно отказываются принимать другие видеопараметры. Такое поведение, с одной стороны, частично объясняется различными странными ограничениями встроенных мониторов моноблоков Apple, а с другой — неизвестными проблемами в коде расчета фазовой синхронизации в ядре.
Кстати, если Linux не сможет найти подходящий видеорежим на вашем компьютере, вы можете использовать параметр "video=ofonly" (ofonly означает «только режим Open Firmware»). Это похоже на режимы VESA BIOS в компьютерах на базе процессоров
Intel®.
Если вы хотите оставить загрузочные видеопараметры такими, как они были определены автоматически, то изменять видеорежим можно по своему усмотрению командой fbset. Сначала добавьте следующие строки в файл /etc/fb.modes:
Листинг 2. Определение режимов фреймбуфера
mode "1024x768-universal"
geometry 1024 768 1024 768 16
timings 12735 160 32 28 1 96 3
hsync high
vsync high
endmode
|
Сохранив этот файл, вы сможете переключаться в режим 1024x768 с 16-битной глубиной цвета с помощью команды fbset 1024x768-universal.
Кстати, если вам интересно, почему я взял такие параметры, то причина в том, что и встроенный монитор в iMac, и ЖК-монитор, который я подключаю к Mac mini, с трудом работают с нестандартными частотами синхронизации. Видеорежимы, определенные в ядре по умолчанию, в частности, не работали на iMac; оба компьютера запускались только в режиме 1024x768 с 8-битным цветом. Поэтому при помощи команды fbset
(без аргументов) я узнал параметры этого режима, изменил глубину цвета на 16 бит на пиксел и тем самым получил неплохой видеорежим, работающий на обоих компьютерах.
Далее предполагается, что компьютер уже переведен в совместимый видеорежим (16 бит на пиксел), при этом разрешение не имеет значения. Чтобы компьютер автоматически переходил в нужный видеорежим при загрузке, добавьте следующие строки в файл /etc/rc.d/rc.local сразу перед строкой "touch...":
fbset 1024x768-universal
setterm -blank 0
Вторая строка просто отключает автоматическую очистку экрана. Без этой строки через пять минут бездействия с экрана автоматически будет исчезать вся информация.
Теперь, когда видеорежим настроен (ну... почти настроен — в конце статьи вы поймете, что я имею в виду), давайте посмотрим, что нужно сделать, чтобы выводить на экран данные. Все, что нужно — выяснить, где находится видеопамять и каков ее формат. Эту информацию можно получить парой ioctl-вызовов, которые возвратят видимую область, глубину цвета, виртуальный размер экрана и другие сведения. См. файл graphics.c в архиве с исходным кодом, ссылка на который приведена в конце этой статьи. Вкратце, шаги следующие:
- Открыть устройство фреймбуфера —
/dev/fb0. - Получить информацию о "фиксированном" и "изменяющемся" экранах (
fb_fix_screeninfoиfb_var_screeninfo), включая физические и виртуальные размеры, глубину цвета, физический адрес видеопамяти. - Вызвать
mmap(2), чтобы добавить память фреймбуфера в адресное пространство данного процесса. - Выделить достаточное количество основной памяти для закадрового буфера («черновика»).
- Отключить курсор текстового режима.
Что касается последнего пункта, то если вы заглянете в конец функции GR_InitGraphics, вы увидите следующую уловку:
Листинг 3. Инициализация фреймбуфера
// Отключаем курсор
handle = open(FB_TTY, O_RDWR);
if (0 < handle) {
write(handle, "\033[?1c", 5);
close(handle);
}
|
Если вы где-либо в системе не предусмотрите такой код (примечание: вы можете просто использовать команду типа echo -e '\033[?1c'
в загрузочных файлах), то поверх вашего изображения будет мигать курсор.
Я назвал этот код «уловкой» потому, что он основан на том неочевидном факте, что используемое вами устройство фреймбуфера (fb0) связано с устройством консоли tty0, а это не всегда так (и кстати, ничто не мешает запустить эту программу из другого виртуального терминала!). Однако в нашем случае это допущение всегда будет верным, потому что наш проект является встраиваемым устройством, где вы можете управлять загрузкой программы и средой ее выполнения.
Что касается файлов graphics.c и
graphics.h, обратите внимание, что я определил единицу измерения для пикселов как typedef PIXEL и, помимо этого, определил макрос
RGB2PIX, преобразующий RGB-триаду 8:8:8 в «родной» формат экранной памяти (в данном случае — 16-битное беззнаковое целое типа unsigned
short). Этим предоставляется базовый уровень абстракции от устройства на уровне компиляции, что будет полезно при портировании кода на другую платформу.
Графическая подсистема теперь работает и встает задача поиска файлов на жестком диске. Этот функционал обеспечивается кодом в файле
filescan.c, в первую очередь, функцией FL_Scan(). Эта функция ищет поддерживаемые типы файлов в каталоге /web и добавляет их в плоский массив динамически выделяемых структур. На данный момент каждая такая структура содержит только имя файла, но далее вы добавите к каждому изображению атрибуты, задаваемые пользователем (длительность показа и т. д.). Обратите внимание, что я написал отдельную функцию, которая определяет возможность воспроизведения файла с заданным именем на основе его расширения (без учета регистра символов) и назначает этому файлу один из перечисленных типов. Сейчас это может показаться излишним, но пригодится впоследствии, когда будет введена поддержка других типов медиафайлов.
Основной цикл программы следующий:
- Запустить функцию FL_Scan() для поиска изображений. Если изображений не найдено, то подождать 10 секунд и повторить поиск.
- Проиграть слайдшоу из всех найденных изображений; между изображениями делать паузу в 10 секунд.
- Повторять бесконечно.
Теперь о декодировании JPEG. Для простоты я применил библиотеку JPEG, которая входит в состав Yellow Dog Linux. Библиотека основана на стандартном образце кода IJG JPEG 6b и проста в применении. Обычно не следует без изменений использовать эту библиотеку в профессиональной встраиваемой системе, так как она активно использует динамическое управление памятью (malloc/free). Тем не менее, поскольку наш проект не является профессиональной встраиваемой системой, я посчитал оправданным применение стандартной библиотеки (а также добавил собственную реализацию динамического выделения памяти).
На менее мощных компьютерах может потребоваться более внимательный подход к вопросам памяти. Мне приходилось работать над различными коммерческими цифровыми мультимедийными устройствами. Я использовал тот же самый код от Independent JPEG Group, но написал свою собственную небольшую подпрограмму управления памятью, которая выделяла память декомпрессора в отдельный пул, сбрасывающийся при каждом вызове кодека. Все это реализовывалось на довольно необычной системе со своей собственной закрытой ОС, которая работала на микроконтроллере архитектуры PA-RISC с 384 килобайтами памяти под все — стеки, буферы, глобальные переменные и т. д. В Mac mini ограничений намного меньше!
Сведения о вызове декомпрессора JPEG содержатся в файле codecs.c. Не обойдется без некоторого количества возни — в основном, нужно создать точки возврата setjmp для всех ошибок, возникающих глубоко внутри библиотеки JPEG. Основные шаги хорошо задокументированы в примере программы на C, ссылку на которую вы найдете в разделе Ресурсы. Обратите внимание, что чтение одной строки за раз официально считается нежелательным из-за неэффективности. Суть JPEG-кодирования такова, что оригинальный файл декодируется блоками по 8 строк (точнее, блоками 8x8), поэтому при каждом вызове jpeg_read_scanlines следующая строка либо копируется из буфера предварительного декодирования, либо целиком считываются следующие восемь строк.
При помощи профилировщика я выяснил, что почти все время декодирования и отрисовки тратится либо на JPEG-вычисления (чего избежать нельзя), либо на внешний код, написанный вами, который выполняет масштабирование, размывание цветов (dithering) и другие функции. Поэтому в обычных условиях неважно, одна или восемь строк считываются за раз — это лишь незначительно влияет на производительность.
Теперь запишите пару JPEG-файлов в каталог /web и запустите программу ibmslides. Если все настроено корректно, то изображения будут отображаться одно за другим (без масштабирования и центрирования). Если вы подключитесь по FTP и запишете в каталог еще несколько изображений, то они появятся при следующем проходе слайдшоу. Все отлично... или нет? Нет! На iMac цвета отображаются абсолютно неправильно.
Тут целая история, полная отчаяния и анализа кода, но я лишь вкратце расскажу о моих экспериментах. В старых версиях iMac устройство фреймбуфера сообщает, что оно работает в стандартном режиме 5:6:5 с глубиной цвета 16 бит на пиксел. Но это совершенно не так — на самом деле оно работает в очень странном и нестандартном режиме. Старший бит каждого слова используется в каких-то необычных целях; пикселы, где этот бит установлен, показываются черными. Другие компоненты цвета тоже проявляют странное поведение: красный, зеленый и синий занимают правые биты, но интенсивность цвета не имеет линейной зависимости от того, что записывается в эти биты. Создается ощущение, что ЦАП в чипе Mach64 все еще работает в палитровом видеорежиме (с использованием CLUT), а не в режиме direct color (с непосредственным представлением цвета).
Если с первого раза у вас не получилось...
Что тогда делать? Можно засучить рукава и мужественно патчить ядро, а можно пойти более хитрым путем и инициализировать видеоподсистему как-то иначе. Мы будем делать это при помощи XFree86 — сервера оконной системы X11.
На самом деле я, честное слово, хотел и по другим причинам перейти от простого использования фреймбуфера к гибриду X + фреймбуфер, а сложности с цветами только ускорили этот переход.
Сейчас я просто покажу вам волшебное решение, не особенно объясняя его. В архиве с исходным кодом вы найдете графический файл blank.xbm. Это пустой файл растрового изображения, созданный при помощи утилиты bitmap(1), и с его помощью мы избавимся от экранного курсора.
Сначала откройте файл /etc/X11/XF86Config и внесите следующие изменения в раздел Screen:
Листинг 4. Файл XF86Config
Section "Screen"
Identifier "Screen0"
Device "Card0"
Monitor "Monitor0"
DefaultDepth 16
SubSection "Display"
Depth 16
Modes "1024x768"
EndSubSection
EndSection
|
Теперь скопируйте файл blank.xbm в каталог /etc (или любое другое удобное место) и выполните следующие команды (предполагается, что программа ibmslides находится в текущем каталоге):
Листинг 5. Запуск X
X & sleep 10 ; DISPLAY=:0 ; export DISPLAY ; twm & xsetroot -cursor
/etc/blank.xbm /etc/blank.xbm ; xset -dpms s off ; ./ibmslides
|
Вот и всё — идеальные изображения!
Теперь у вас есть полностью работающее PowerPC-устройство с Linux, на котором запущены различные демоны, и вы обладаете базовым пониманием использования фреймбуфера в графических приложениях для Linux. В следующей статье я объясню, как это работает и какие процессы при этом происходят, и подробно расскажу вам, как добавить поддержку масштабирования и простого скриптового языка.
Раздел загрузок для данной статьи обновляется. Попытайтесь повторить загрузку позднее.
- Примите участие в обсуждении материала на форуме.
-
Оригинал статьи (EN)
- Читайте остальные статьи этого цикла .
- Прочтите практическое руководство (HOWTO) по программированию фреймбуфера (EN). К сожалению, оно довольно старое и сконцентрировано на архитектуре x86, но из него можно почерпнуть некоторое количество полезной информации для новичков.
- Лучший справочный ресурс — это само ядро Linux. Последнюю версию ядра всегда можно загрузить на
домашней странице ядра Linux.
- Вы можете изучить стандартный пример использования кода IJG.(EN)
- Информацию о стандарте JPEG и других сопутствующих стандартах (JBIG, JPEG2000) можно найти на домашней странице комитета ISO по JPEG.(EN)
-
CenterStage
— еще один проект по превращению Mac в мультимедийное устройство.(EN)
- Статья о батареях Mac mini обсуждалась на slashdot.(EN)
- Хотите узнать, сколько изображений вы сможете хранить на диске? Обязательно почитайте про компромисс между размером и качеством JPEG-изображений.(EN)
-
Команда IBM Image Library
Applications провела немало работы в области захвата, хранения и показа изображений. Для нашего проекта, возможно, не нужны такие сложности, но на их сайте можно найти немало полезной информации. (EN)
Льюин Эдвардс (Lewin A.R.W. Edwards) работает в компании, входящей в список 50 крупнейших компаний мира. Он занимается разработкой беспроводных устройств охраны и пожарной безопасности. До этого он в течение пяти лет разрабатывал сетевые устройства на базе архитектур x86, ARM и PA-RISC в компании Digi-Frame Inc. Обладает обширным опытом в программном обеспечении по шифрованию и безопасности; автор двух книг о разработке встраиваемых устройств. С ним можно связаться по адресу sysadm@zws.com.