четверг, 25 августа 2011 г.

Bash 32bit unsigned dec to hex

Конвертируем десятичные числа в шестнадцатеричные в bash и видим, что числа больше 2 миллиардов становятся отрицательными. Тут нужно немного простой математической магии:
 VAL=3123456789  
 printf "%#x%x" $(( ${VAL} / 65536 )) $(( ${VAL} % 65536 ))   

понедельник, 2 мая 2011 г.

gcc Тёмная магия линкера

Последние пару дней упорно бился над проблемой потерянного символа. Обстоятельства его потери были не столь уж и мистическими, причину я понял сразу. А вот какое заклятье использовать дабы её искоренить, я понял только после детального изучения манов и кучи экспериментов.

Итак имеется статическая библиотека А и несколько плюсовых исходников. Юный маг хочет собрать из этого всего динамическую библиотеку. Разумеется это ему удаётся сделать одной левой, но... В полученной библиотеке отсутствует один из символов имеющихся в скатической библиотеке, который нигде более в рамках имеющегося кода не вызывается.

Ситуация выглядит логично. Просто линкер обнаружил unreferenced symbol и для оптимизации размера сгенерённого файла и ускорения зугрузки библиотеки не стал включать мёртвый код. Ну откуда ему знать, что это точка входа в приложение, которая будет позвана из Java кода через JNI. И собственно потерять можно всё что угодно, но не этот символ.

На помощь пришёл невнятно описанный в манах ключ -u. Он явно говрорит линкеру считать символ имя которого переданно аргументом этому ключу как undefined символ и провести поиск этого символа в библиотеках использумемых при линковке. Это заклинание помогло спасти бедный символ от жестогкого скальпеля линкера.

В процессе исследования было найдено ещё одно интересное чародейство, позволяюще ускорять время загрузки бинамической библиотеки путём исключения из таблицы символов внутренних функций и переменных библиотеки. Если нужно чтобы таблица символов содержала только публичный интерфейс библиотеки, то публичные функции, классы и переменные нужно промаркировать с помощью __attribute__((visibility("default"))):
 __attribute__((visibility("default")))  
 void foo();  
 class __attribute__((visibility("default"))) MyClass {  
 };  
 __attribute__((visibility("default")))  
 int globalNum = 5;  

После чего при компиляции нужно использовать флаг -fvisibility=hidden.

Суть сей магии достаточно проста. По умолчанию gcc считает все символы публичными и при линковке динамической библиотеки перечисляет абсолютно всё в таблице символов. Если что-то или кто-то используется только библиотечными потрохами и наружу смотреть не должно, то его можно явно промаркировать с помощью __attribute__((visibility("hidden"))) и оно не попадёт в таблицу символов. Но, как правило, публичный интерфейс намного меньше приватного и реже меняется, поэтому куда логичней перечислять символы которые должны быть видны. Тут на помощь приходит флаг -fvisibility=hidden который указывает, что все символы, для которых не указанно обратное, нужно считать спрятанными.

Зесь есть абсолютно полная аналогия с подходом в Windows и их __declspec(dllexport). К счастью тут не наблюдается ничего подобного маразматичному __declspec(dllimport). Символ промаркерованный с помощью __attribute__((visibility("default"))) в публичном хедере не нужно маркировать как-то иначе при компиляции приложения использующего данную динамическую библиотеку.

четверг, 4 марта 2010 г.

Тёмная магия f:facet

Сегодня убил один день на то чтобы разместить одну строчку на странице! Да я обажаю всех создателей JSF и Facelets и желаю им медленной и мучительной смерти!

Оказывается, если поместить несколько элементов (обязательно JFS элементов) внутри <f:facet name="header">, то только последний из них будет отображён. Посему нужно использовать <f:subview> чтобы обойти сей косяк.

среда, 7 октября 2009 г.

Qt4 эмуляция сокета с помощью QBuffer в юнит тестах

В одной своей разработке, я решил реализовать работу с сетью не напрямую через QTcpSocket, а через общий интерфейс для всех устройств ввода вывода имеющийся в Qt4, QIODevice. И в первую очередь решил сэкономить время при написании юнит тестов, используя вместо честных сокетов QBuffer, имплементацию интерфейса QIODevice, пишущую и читающую данные из буфера в оперативной памяти. Но не тут то было.

Первое, что я выяснил, что сигнал QIODevice::readyRead() выбрасывается из EventLoop'а даже в случае такого простого класса, как буфер. Так что первое что пришлось сделать это понять как во время выполнения теста, не надолго отлучиться из тестовой функции в EventLoop. Первая идея завести свой QEventLoop прямо в тесте запустить, его не забыв предварительно задать условия его остановки, например так:
QTimer::singleShot(500,myELoop,SLOT(quit()))

За пол секунды EventLoop заведомо сделает всё необходимое, и тест можно будет завершать. Но выглядит это решение откровенно говоря не очень. Покопавшись в документации, я нашёл более элегантное решение:
void QTest::qWait(int ms)

приостанавливает выполнение текущего теста на заданное количество миллисекунд отдавая управление EventLoop'у. Причём экспериментальным путём было выяснено, что все необходимые операции цикл обработки событий успевает проделать за одну миллисекунду. Подозреваю, что получив управление EventLoop не возвращается раньше, чем сгенерирует все сигналы, хотя это я не проверял.

Для эмуляции сокета я решил создать два объекта QBuffer с общим буфером, но фокус не прошёл, так как класс QBuffer не генерирует сигнал readyRead если содержимое буфера изменилось без помощи его методов. По всей видимости просто нету надёжного механизма сделать это, так как QByteArray используемый в качестве буфера данных не имеет никаких механизмов оповещения об изменении содержащихся в нём данных. После внимательного перечитывание документации по Qt4 и нескольких экспериментов я пришёл к следующему решению. Создаём два объекта QBuffer после чего пишем в один из них данные и выполняем "передачу данных по сети вручную":
 quint64 pos = dev2.pos();  
 dev2.write( dev1.buffer() );  
 dev2.seek(pos);  
 QTest::qWait(1);  

Тут стоит отметить, что тот факт, что сигнал readyRead() генерируется в цикле обработке событий, а не непосредственно в функции write() есть очень хорошо. Ведь если бы он генерировался сразу, то его обработчик был бы вызван раньше, чем я верну указатель на текущую позицию в устройстве в прежнее значение и обработчик счёл бы, что на самом деле новых данных не прибыло. Вышеприведённый код имеет смысл обернуть в некую функцию, чтобы не страдать копипастом понарпасну.

В общем на изобретение такого метода ушло около двух часов, но результат того стоил. Мне не нужно создавать QTcpServer и слушать какой-нибудь порт, который по абстрактным соображениям вроде никто другой слушать не должен. Я могу эмулировать сетевое соединение используя пару буферов в оперативной памяти.

воскресенье, 7 декабря 2008 г.

Полноценный ffmpeg в Ubuntu

В связи с тем, что в некоторых странах (Америка, Австралия) можно патентовать алгоритмы, поддержка некоторых форматов в ffmpeg в Ubuntu искуственно отключена. В частности пострадал формат mp3. Бинарный пакет из репозитория может декодировать mp3, но не может его создавать. Дабы обойти сие ограничение, необходимо пересобрать пакет вручную.

Итак, для начала потребуются кое какие доп пакеты:
 sudo aptitude install build-essential fakeroot  
Достаём исходный код ffmpeg:
 apt-get source ffmpeg  
Будет выкачан и распакован архив с исходным кодом, на него будут наложены специфичные для дистрибутива патчи, и будут добавлены скрипты сборки пакета. Далее необходимо перейти в директорию с исходными кодами ffmpeg-debian... название директории может зависеть от версии пакета.

Теперь необходимо разрешить сборку дополнительных кодеков. Для этого нужно добавить в переменную DEB_BUILD_OPTIONS ключ externalcodecs (это актуально для 8.10, меня терзают смутные сомнения, что раньше нужно было использовать ключ risky):
 export DEB_BUILD_OPTIONS+=externalcodecs  
После этого нужно установить библиотеки необходимые для сборки пакета:
 sudo aptitude build-dep ffmpeg  
Теперь можно собрать и установить deb-пакеты:
 dpkg-buildpackage -b  
 cd ../  
 sudo dpkg -i *.deb  
Теперь можно использовать 100% возможностей ffmpeg

Video watermark

Сняв что-нибудь интересное на видекамеру и потратив пару часиков на редактирование этого самого чего-то интересного, хочется выложить результат куда-нибудь в сеть. Разумеется при этом ещё хочется как-то отметиться, что автор именно я и никто другой. Можно добавить титры, но их с неплохой вероятностью могут вырезать за ненадобностью.

Но есть куда более ндёжный способ. Можно добавить в видео водяной знак. Он отчётливо виден, но не акцентирует на себе внимания, и его достаточно проблемно устранить. Настолько проблемно, что из любительского видео его точно вырезать никто не станет.

Итак, изначально у нас есть полностью отредактированное видео в файле final.dv, мы хотим получить видео с водяным знаком, которое можно раздовать друзьям for_share.dv. Я продпочитаю работать с сырым DV, но, на самом деле, формат не имеет значения, главное, чтобы его понимал ffmpeg. Собственно добавлять водяной знак будет именно он.

Для начала создадим шаблон водяного знака. Это должен быть рисунок с однородным цветом фона, лучше всего если это будет рисунок в оттенках серого. Сам шаблон водяного знака это всё то, что отличается от цвета фона. Я создаю шаблон следующим образом:
 convert -size 1024x576 xc:#000000 -font Bookman-DemiItalic -pointsize 50 -fill "#909090" -draw "text 810,560 'VestniK'" -gaussian 2,10 -shade 180x30 watermark.gif  

для видео в формате 16:9. Если требуется соотношение сторон 4:3, то нужно поменять размер.

Терерь можно подписать исходное видео:
 ffmpeg -i final.dv -vhook '/usr/lib/vhook/watermark.so -f watermark.gif -t 0x7F7F7F' -target dv for_share.dv  


Ещё одна мелочь по поводу видео. На видео хостингах как правило закаченное видео пережимается в flv со звуком в mp3. Если заливать видео в этом формате, то оно пережиматься не будет. Так можно получить больший контроль над качеством видео. Готовый рецепт по сохранению видео для публикации в интернете выглядит следующим образом:
 ffmpeg -i for_share.dv -f flv -vcodec flv -qscale 15 -maxrate 512k -bufsize 1M -s 480x270 -padtop 30 -padbottom 60 -acodec mp3 -ab 128k -ar 44100 for_internet.flv  

Помимо всего прочего здесь я подгоняю видео под соотношение сторон 4:3, так как у меня видокамера снимает видео с соотношением сторон 16:9. Если у вас видео имеет соотношение сторон 4:3, то команда для конвертации видео упростится:
 ffmpeg -i for_share.dv -f flv -vcodec flv -qscale 15 -maxrate 512k -bufsize 1M -s 480x360 -acodec mp3 -ab 128k -ar 44100 for_internet.flv  
.

среда, 29 октября 2008 г.

QtXml DOM создание некорректного документа

В Qt4 есть библиотека классов для работы с XML. И там есть набор классов для работы с DOM. Только что обнаружил там весьма неприятную фичу. Дело в том, что QDomDocument является наследником QDomNode и следовательно к нему можно добавлять любое количество child нод с помощью функции QDomNode::appendChild(). При этом добавлении не проверяется, валидности этой операции с точки зрения структуры XML документа и можно создать например такой документ:
 <!DOCTYPE MyDocType>  
 <root1>  
   <tag>text<tag>  
 </root1>  
 <root2>  
   <tag>text<tag>  
 </root2>  

В котором содержится более чем один корневой элемент.
Что примечательно, если попытаться распарсить такой XML с помощью функции QDomDocument::setContent(), то будет выданна ошибка.
Как будет время покопаюсь в багрепортах и списках рассылки Qt4. Я эту фичу обнаружил в Qt 4.4.