Wednesday, December 26, 2012

Custom QML Component to Integrate Hardware Acceleration in Qt Scene Graph

This is a custom QQuickItem subclass rendering hardware-decoded h264 1080p video on Raspberry Pi. The egl_render component used in some previous posts is good. The QML component can play nicely with the rest of the scene rendered by the Qt Quick renderer in the scene graph.
Code will be available when I'll find the time to clean it up, but is a direct consequence of the previous posts which include the code to implement decoding and rendering.
In the QML test code, I created some simple standard QML animations and also placed an Image element with 0.5 opacity overlapping the video element.
NOTE: New information is available here.

Sunday, December 9, 2012

Decoding and Rendering to Texture H264 with OpenMAX on Raspberry Pi

After accomplishing the target of decoding and rendering compressed image formats onto OpenGL textures directly using OpenMAX, I've looked into how to use the same concept to render h264 stream directly into a texture using the same OpenMAX component on my Raspberry Pi.
As a starting point I took the same sample code I used in this post, and the hello_video sample code by Broadcom in VideoCore.

This is the code I wrote to make it work (PiOmxTextures_2.0.tar.bz2 version 2.0, https://github.com/carlonluca/pi): notice that this is only a collection of notes, which compiles and seems to work fine on the wheezy Raspberry Pi image, it is not a fully re-usable component. I'm still working on that.
The code is pretty messed up and much of it is not fully implemented. Error management is almost inexistent, but still it can be useful to guess how to make things work. The rest is up to you ;-)
This is a video illustrating the sample code running:

To compile the code refer to this article. You will need the Qt 5.0 libraries (version 4.0 might be sufficient) running with the eglfs plugin. Hope this helps!

Friday, December 7, 2012

Decoding and Rendering Compressed Images with OpenMAX on Raspberry Pi

After building Qt 5.0 on Raspberry Pi, I focused the attention to hardware acceleration using OpenMAX. This is quite an interesting subject to study. The most interesting element I found among those available for VideoCore is the egl_render component. This component should be able to render content coming from the output of the decoder component directly into an EGL image, which is bound to a texture.
Of much interest for me is rendering video into an texture, but I thought for the moment I could start just with decoding an image and placing it to an OpenGL texture for rendering. This can be done by using the image_decode component and the egl_render component. Refer to the documentation in the git for some more information.
Unfortunately not much documentation is available for this task, but I found something very interesting that helped me out much:
  1. This source code by Matt Ownby: I reused much of his excellent work.
  2. This project by Qt
  3. The textures example from Qt sources
The code I wrote is provided in a package below. I ran some tests by using the original way of loading textures:

QGLWidget::bindTexture(QPixmap(fileAbsolutePath, GL_TEXTURE_2D, GL_RGBA));

and the method using the egl_render OpenMAX component. These are the results I got by loading 6 jpegs 1920x1080:
    Average time out of 5 runs without OMX: 6750ms;
    Average time out of 5 runs with OMX: 918ms.
Here is the package containing the sources (version 1.0): PiOmxTextures.tar.bz2. The code links to Qt 5.0 and needs 6 images to be specified on the command line to load. You need to move the images to the same directory (no spaces) and to name those images like:

prefix{0, 1, 2, 3, 4, 5}.jpg

To compile you'll need Qt Creator or at least qmake:

cd PiOmxTextures
your_qmake
make


This is a video showing the performance loading 6 1080p jpegs, first the software decoding is used, second and third run are hardware accelerated:
Sorry the quality is really bad, but the performance can be appreciated anyway. Also it seems that for some reason the software implementation failed to load one image, you can see a black texture on the side of the cube. The same is not happening with the hardware implementation. Didn't investigate the reason.
This is not a ready-to-use code, just some notes that might be useful. I've almost never used OpenMAX or OpenGL so if you happen to have any suggestion or observation, feel free to comment! ;-)

Tuesday, November 27, 2012

Making QtWebKit with WebKit2 in Qt 5.0 work on Raspberry Pi

After compiling Qt 5.0 for Raspberry Pi, I wanted to try all the modules. I compiled and tested the simplest modules first, like:
  1. qtbase
  2. qtdeclarative
  3. qtjsbackend
  4. qtquick1
  5. qtscript
  6. qtxmlpatterns
Everything worked quite smoothly, without any change, except the patch to the v8 engine in qtjsbackend module for ARM hardfp (read here).

I then turned my attention to the QtWebKit module: I know that many changes have been applied to this module for in Qt 5.0, so it is very interesting to try it. Therefore, I started to compile it the usual way:

cd your_qt_sources/qtwebkit
your_qt_prefix/bin/qmake
make -jn
make install

Unfortunately, at the moment of writing, it didn't work for me: the module compiled and linked successfully, but crashed almost as soon as the page load procedure started. I therefore had to start searching around and asking Qt guys about this issue, and during this, I was frequently directed here. Turned out this is exactly the issue I encountered: I applied the latest patch (consider reading the entire report and check that a more stable fix is available):

cd your_qt_sources/qtwebkit
patch -p1 < ~/webkit_patch

where webkit_patch is the linked diff. So, now you can compile the module to include the differences and try again. Consider that the patch seems to be near land, so it might not be necessary for you in the future. As a first test, this is the code I tried:

#include
#include

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    QWebView webView;
    webView.setUrl(QUrl("http://www.repubblica.it"));
    webView.show();
    return app.exec();
}


At this point, it should work. As you can see, performance is unacceptable. This is not the way an embedded browser is supposed to work indeed, this is more like classes that can be used for a desktop browser.
Next step was testing WebKit2 with tiling and all that nice stuff. So I tried to create a simple application loading a WebView QML element, like this:

main.cpp:
#include

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQuickView viewer;
    viewer.setSource(QUrl("/home/pi/main.qml"));
    viewer.show();
    return app.exec();
}


main.qml:
import QtQuick 2.0
import QtWebKit 3.0

WebView {
    url: "http://www.cnn.com"
    x: 100
    y: 100  
    width:  1500
    height: 900
}


When I tried this the first time, it closed with an anonymous error on QtWebProcess:

Failed to start " QtWebProcess 14"

After looking into the sources, I found this is simply a failure to find the QtWebProcess binary. It is needed because WebKit2 uses a separate process to draw the content. Placing it in /usr/bin is sufficient:

cd /usr/bin
sudo ln -s your_qt_prefix/bin/QtWebProcess QtWebProcess

Or better use a relative path. Now what you might get is... nothing at all... At least this is what I got. After some seconds a crash occurred and often nothing was drawn.
It took me some time to guess what was happening: it seems that QtWebKit is trying to draw with 32 bit color depth, while instead the rest of Qt is rendering using 16 bit color depth (especially if you're using the eglfs platform plugin) on a RGB565 EGL configuration.
According to the Qt WebKit code, it seems that the entire code is assuming 32 bit color depth is being used, and this is hardcoded instead of negotiated with the main process. As a quick fix, I made the entire EGL configuration to be 32 bit (read this).
By setting this, it is possible to make QtWebKit work in 1080p with 32 bit color depth. If I'll find the time I'll do the needed modifications to have the same configuration with the correct color depth.

Consider that, at the moment of writing, the configuration of the wheezy image does not assign sufficient memory to the GPU.You'll see therefore pages where many tiles are black: that is the result of insufficient GPU memory when trying to allocate for the texture. Just assign some more memory to the GPU editing the /boot/config.txt file of your Pi.

Sample Video



NOTE: For updated information read here.

Monday, November 26, 2012

32 bit color depth Qt on Raspberry Pi

EDIT: I see this post is still consulted very often. The solution below was the only one I found years ago. Now it seems that setting the env variable QT_QPA_EGLFS_FORCE888 to 1 works perfectly.

Some days ago I was asked to test the performance of 32 bit EGL config color depth with Qt on Raspberry Pi using the eglfs platform plugin. I tried to set the framebuffer to 32 bit color depth using the usual command:

fbset -depth 32

Nothing changed unfortunately. I also tried some other changes I was suggested but no way.
This seems due to some strange parameter passed to the q_configFromGLFormat function in the qeglconvenience.cpp source file. What I did to make Qt render 32 bits per pixel is to simply rewrite the function to always return a 32 bit EGL configuration. This is not a fix of the issue nor a good modification, it is just a hack to make it render true color. I hope I'll find the time to analyze the situation more accurately.

This is the simple modification I made:

EGLint configCount = 0;
assert(eglGetConfigs(display, 0, 0, &configCount) == EGL_TRUE);
qDebug("%d EGL configurations were found.", configCount);
EGLConfig* configs = new EGLConfig[configCount];
assert(eglGetConfigs(display, configs, configCount, &configCount) == EGL_TRUE);

// List the configurations.
for (int i = 0; i < configCount; i++) {
   EGLint redSize, blueSize, greenSize, alphaSize;
   //assert(eglGetConfigAttrib(display, *(configs + i), EGL_RED_SIZE, &redSize) == EGL_TRUE);
   //assert(eglGetConfigAttrib(display, *(configs + i), EGL_GREEN_SIZE, &greenSize) == EGL_TRUE);
   //assert(eglGetConfigAttrib(display, *(configs + i), EGL_BLUE_SIZE, &blueSize) == EGL_TRUE);
   //assert(eglGetConfigAttrib(display, *(configs + i), EGL_ALPHA_SIZE, &alphaSize) == EGL_TRUE);
   //qDebug("Config %d: RGBA(%d, %d, %d, %d).", i, redSize, greenSize, blueSize, alphaSize);

}

// Choose the first 32 bit configuration.
EGLConfig config;
int i = 0;
for (i = 0; i < configCount; i++) {
   EGLint redSize, blueSize, greenSize, alphaSize;
   assert(eglGetConfigAttrib(display, *(configs + i), EGL_RED_SIZE, &redSize) == EGL_TRUE);
   assert(eglGetConfigAttrib(display, *(configs + i), EGL_GREEN_SIZE, &greenSize) == EGL_TRUE);
   assert(eglGetConfigAttrib(display, *(configs + i), EGL_BLUE_SIZE, &blueSize) == EGL_TRUE);
   assert(eglGetConfigAttrib(display, *(configs + i), EGL_ALPHA_SIZE, &alphaSize) == EGL_TRUE);
   qDebug("Config %d: RGBA(%d, %d, %d, %d).", i, redSize, greenSize, blueSize, alphaSize);
   if (redSize == 8 && blueSize == 8 && greenSize == 8 && alphaSize == 8) {
      config = *(configs + i);
      break;
   }

}
delete[] configs;
return config;

Also, you might want to choose according to other values of the EGL configurations. Anyway, this is sufficient to achieve the result for the moment.

After this modification, you'll have to rebuild the static library this is included into, and then the eglfs plugin. So:

cd your_qt_sources/qtbase/src/platformsupport
make

this should rebuild libQt5PlatformSupport.a, then rebuild the entire eglfs plugin:

cd your_qt_sources/qtbase/src/plugins/platforms/eglfs
make clean
make -jn

then place the newly built libqeglfs.so in your Pi in the platform plugin directory.

NOTE: for updated information read here.

Sunday, November 18, 2012

Bring up Qt 5.0 on Raspberry Pi

NOTE: For updated information read here.
Ok, so I got my brand new Raspberry Pi! :-) I found a couple of guides on how to compile Qt 5.0 for it, but still I had some troubles doing that, and this guide is to speed up the process for others. Let's start from the beginning...

Links

First link of all is this: http://qt-project.org/wiki/RaspberryPi. The process is already explained there, and there is a summary of what Qt guys already made work on the platform. Still, as I said, I encountered some troubles, so I write down some notes.
Also another relevant source is: http://qt-project.org/wiki/Building_Qt_5_from_Git.

Platform plugin

As you might already know, Qt 5.0 comes with the QPA (Qt Platform Abstraction) mechanism. This means you can choose from the command line what platform driver to use. At the beginning I thought I could use the xcb plugin, but that turned out to be a bad idea: I had some troubles with libxcb being unable to communicate with X11 for some protocol-related incompatibility, like some version mismatching, I don't know (if you do, please share! :-)).
So, I went directly to the interesting part: getting rid of X11 in favor of the eglfs plugin. I'm leaving the wayland integration for a later time.

Setting up the image

You already know all this, anyway I downloaded to last wheezy image from the Pi website, and printed it in my SD card:

sudo dd if=the_wheezy_image.img of=/dev/your_sd_device

I later discovered the space was insufficient, so, I used gparted to enlarge it. You should have that for your distribution: I was using a Kubuntu 12.10 in a VM on Mac OS X.

Setting up the compilation environment

I chose to go with the Linaro toolchain, using hardfp support of course. If you don't know what hardfp support, look for that on the Internet: there is a simple explaination here. I found that toolchain for the Pi here: https://github.com/raspberrypi/tools/tree/master/arm-bcm2708. Download it and place it in a safe place (your_toolchain_path) :-)

The toolchain is not sufficient to compile Qt, you also need some other lib. To make sure to use the same libs that are in the wheezy image, I created an image for the Pi with all the dependencies I needed, downloading with aptitude and then bringing that image back to my compilation environment. By using that sysroot, I'm confident everything will be fine.
Another possible way to go is to simply scp all the libs from the Pi to the compilation system, but that is quite bothering. Also, I tried to use rsync, that is quite more simple to use, I don't have to spend time looking for the installed files any time I install a new package I need for the compilation.
My dream would anyway being able to mount the root of my Pi using NFS. This way, without any work at all I would have all my libs. NFS is actually available for the Pi, but still I was unable to make it work. If you are, please share! ;-)

Ok, this is what I wrote down while setting everything up. To compile the xcb platform driver I had to apt-get these libs on my Pi image.

sudo apt-get install libxcb1-dev libxcb-image0-dev ibxcb-image0-dev ibxcb-xfixes0-dev libxcb-keysyms1-dev libx11-dev

This made me able to compile that plugin.
For satisfying the (optional) dependencies of the qtcore module I also installed:

sudo apt-get install libdbus-dev libudev-dev libssl-dev

to add dbus and udev support.
Also, these are the packages I needed to install on the Pi to have qtmutimedia compiled with gstreamer support:

sudo apt-get install libasound2-dev libgstreamer0.10-dev

Everything should now be fine: using one of the proposed methods get the root filesystem of the Pi into the compilation environment and compilation shall begin!
How you do that is up to you: simple way is to shutdown your Pi, place the SD back in your Linux Box and:

sudo dd if=/dev/your_sd_device of=your_pi_image.img

then mount your image as a loopback device:

sudo mount -o loop,offset=62914560 your_pi_image.img your_sysroot_path

Also pay attention: the offset might change, so use fdisk to find the correct offset in the image file.
Now there is a problem: it seems that in the multi-arch image there are some symlinks which points to files with an absolute path. This is a problem since, typically, you want to place your_sysroot somewhere which is not the root, and therefore the symlink becomes invalid. The page I linked above provides a script to fix these paths: https://gitorious.org/cross-compile-tools/cross-compile-tools/blobs/master/fixQualifiedLibraryPaths. Give it as parameter the location of your sysroot on the compilation machine and it should fix the paths.

You'll also need some instruments on the compilation system: if you're running Kubuntu or Ubuntu, you'll need these packages, otherwise use the instruments provided by your distribution:

sudo apt-get install build-essential perl python git

Also, if you intent to also compile QtWebKit, you'll need additional packages:

sudo apt-get install flex bison gperf libicu-dev libxslt-dev ruby

I hope I didn't forget anything... if I did, please consider adding a comment!
NOTE: If you're compiling in a virtual environment, like I am, beware that QtWebKit compilation will require more than 512MB of RAM! I spent half a day trying to recompile a new toolchain because of "internal compiler error". At least 1GB of RAM should be reserved, but 2GB might be better for QtWebKit.

Getting the source code

This is pretty standard, anyway:

git clone git://gitorious.org/qt/qt5.git qt5
cd qt5
perl init-repository

This will download everything you need.

Configuration and build

You'll need to configure, building and installing the qtbase module first of all. This is the configure line I used:

./configure -prefix your_qt_prefix -release -device linux-rasp-pi-g++ -make libs -device-option CROSS_COMPILE=your_toolchain_path/bin/arm-linux-gnueabihf- -device-option DISTRO=wheezy -sysroot your_sysroot_path -qpa xcb -opensource -confirm-license -no-pch -make examples -nomake tests

I didn't select tests because it failed compilation asking for libboost, which I had no time to provide. If you want, packages for wheezy are available. In case any error occurs in the configuration process, use the -v parameter to try to guess what went wrong
If your configuration succeeds, you should see a smiling table like this:

Result is:
Building on:   linux-g++
Building for:  devices/linux-rasp-pi-g++
Architecture:  arm, features:
Host architecture: unknown, features:
Platform notes:

            - Also available for Linux: linux-kcc linux-icc linux-cxx
       
qmake vars .......... qmake switches .........
Build ..................  libs
Configuration ..........  cross_compile qpa largefile precompile_header neon pcre  minimal-config small-config medium-config large-config full-config no-pkg-config evdev xcb-poll-for-queued-event linuxfb c++11 egl eglfs opengl opengles2 shared qpa reduce_exports reduce_relocations clock-gettime clock-monotonic mremap getaddrinfo ipv6ifname getifaddrs inotify png freetype system-zlib nis iconv openssl xcb rpath icu concurrent audio-backend v8 release
Debug .................. no
C++11 support .......... yes
pkg-config ............. yes
QtDBus module .......... yes
QtConcurrent code ...... yes
QtGui module ........... yes
QtWidgets module ....... yes
JavaScriptCore JIT ..... To be decided by JavaScriptCore
QML debugging .......... yes
PCH support ............ yes
iWMMXt support ......... no
NEON support ........... yes
IPv6 ifname support .... yes
getaddrinfo support .... yes
getifaddrs support ..... yes
Accessibility .......... no
NIS support ............ yes
CUPS support ........... no
Iconv support .......... yes
Glib support ........... no
GStreamer support ...... no
PulseAudio support ..... no
Large File support ..... yes
GIF support ............ plugin
JPEG support ........... plugin (qt)
PNG support ............ yes (qt)
zlib support ........... system
Session management ..... auto
libudev support ........ yes
OpenGL support ......... yes (OpenGL ES 2.x)
OpenVG support ......... no
XShape support ......... auto
XVideo support ......... auto
XSync support .......... auto
Xinerama support ....... runtime
Xcursor support ........ runtime
Xfixes support ......... runtime
Xrandr support ......... runtime
Xi support ............. runtime
Xi2 support ............ no
MIT-SHM support ........ auto
FontConfig support ..... no
XKB Support ............ auto
immodule support ....... yes
GTK theme support ...... no
SQLite support ......... plugin (qt)
OpenSSL support ........ yes (run-time)
Alsa support ........... no
libICU support ......... yes
PCRE support ........... qt
Xcb support ............ yes
Xrender support ........ no
EGLFS support .......... yes
DirectFB support ....... no
LinuxFB support ........ yes
KMS support ............ no


This means you're pretty ok now, you can start compilation using something like:

make -j(number_of_cpus + 1)

This is the time where you might want to have a coffee.
If everything builds correctly, you're at a good point, so install:

sudo make install

and be happy now! You should see on your system your_qt_prefix now exists and contains at least the bin directory with the qmake executable. Also, in your_sysroot_path/your_qt_prefix/ you should see the compiled libs, the imports, the plugins etc...
You are done!

Compile some other exciting module

Just qtbase alone is pretty interesting, but you can now try to compile other modules! All the modules can be compiled the same way:

cd your_qt_module
your_qt_prefix/bin/qmake
make -j(number_of_cpus + 1)
sudo make install

Some of the modules should compile without bloodsheds, while for some others I'll reserve some other post. Just consider that qjsbackend seems to need a patch at the moment of writing, so, before building it:

cd qtjsbackend
git fetch https://codereview.qt-project.org/p/qt/qtjsbackend refs/changes/56/27256/4 && git cherry-pick FETCH_HEAD

This should make it work correctly.
Consider that qtmultimedia, qtwayland and qtwebkit might need more work. I'll write about QtWebKit when I'll find the time to.
Also, I tried to run Qt with 32bit color depth, and it seems again the algorithm of choice of the EGL configuration still chooses 16bit. I solved by modifying the sources, I'll write about this as soon as I'll have the time to.

Running the examples

Now, you can write some test app or try to run some of the examples on the Pi. You'll have to copy your Qt libraries back to your Pi, or create a new image to place on the SD card. To create the new image just dd your_pi_image.img back on the SD card and boot the Pi with that new filesystem. Now check your_qt_prefix to confirm that the Qt libs have been placed there. Now, in your_pi_prefix/examples you should see many possible application to test. To have a look at the performance, I suggest trying some of the Qt3d or qtquick examples on the eglfs platform plugin:

LD_LIBRARY_PATH=your_qt_prefix/lib ./some_example -platform eglfs

Sample video

This is some QML code I wrote in 10 minutes using a PathView just to show somehow the performance. Very gooooood! This is Qt 5.0 running 1080p 32 bit color depth QtQuick2 with the eglfs plugin!

Friday, August 17, 2012

Programming using the Android NDK with Qt Creator

I recently had to implement libraries for the Android OS using the Android NDK, or implementing JNI bindings for others to leverage C++ libraries from the Java layer. I commonly did this using simple toold like kate or similar text editors. To be sincere, it was not a completely satisfying approach.
During one of my projects, I had to port a Qt application to Android, using the excellent necessitas SDK. This excellent SDK made it possible to implement for Android using C++, the NDK and Qt Creator as the preferred IDE. I started from here to think I could use Qt Creator to more generally program in C++/JNI to implement libraries for Android, with or without the Qt libraries. This is how I did it.

Download the necessary tools

  1. Download the Qt libraries (I won't use the libraries, I only need the environment).
  2. Download Qt Creator.
  3. Download the Android NDK for your platform.

Add the Android platform specification

I took the platform specification used in the necessitas project and modified it a bit. Look for the directory containing the platform specifications (<qt_libs>/mkspecs), and place in there the platform specification file and the qmake configuration file. You'll have to define at least the ANDROID_NDK_ROOT to the root of the NDK you downloaded.

Setup Qt Creator

In Qt Creator you can specify a new toolchain to use: go to the preferences, "Build & Run", "Toolchains", "Add" and choose "GCC". Then, select the gcc compile from the Android NDK (I see it under <ndk_root>/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-g++).
You'll have to create a new build configuration in the project properties: add a new specification with the default build specification and name it differently. Also, under "Build Steps", add "-spec android-g++" to the additional arguments. This will tell Qt Creator to build according to the Android specifications we added before. Also, select the Android toolchain in the "General" section.

Additional setup

Consider that I also had to remove QMAKE_INCDIR_QT in some cases because it was adding /usr/include to the include paths, thus making the build fail. You may want to add that to the qmake.conf file.

Creating native executables for Android using qmake and Qt Creator

It is sometime useful to creato native executables for Android. You can use this technique to do it very simply. For instance, this is a test I created in a minute (you can use Qt Creator or the command line directly):

nano TestProject.pro

QT              -= core gui
TEMPLATE        = app
SOURCES         = main.cpp
QMAKE_INCDIR_QT =

Now write a simple "Hello Android!" application:

nano main.cpp

#include <stdio.h>

int main(int argc, char** argv)
{
   printf("Hello Android!\n");
   return 0;
}

Now create the Makefile using qmake and the android platform specification:

qmake -spec android-g++
make

You'll get a TestProject execuable compiled for arm platform using the Android bionic C libraries. You can try uploading to an Android emulator:

luca-macbook:test_project luca$ adb remount
remount succeeded
luca-macbook:test_project luca$ adb push TestProject /data
173 KB/s (34639 bytes in 0.195s)
luca-macbook:test_project luca$ adb shell
# cd /data
# ./TestProject
Hello Android!
# rm TestProject

I used this approach on both Mac OS X and Linux. Never tried on Windows.

Saturday, August 11, 2012

Compiling SQLCipher for Windows

SQLCipher is a great extension to the SQLite library. It can be used to encrypt the database when using the SQLite API. It can be compiled for the major platforms like Linux, Mac OS X, iOS, Android and Microsoft Windows.
It is very simple to compile SQLCipher, except for the Windows case, where it can take some more time to get a working library. I spent some hours to have a static library, and I suppose it might be useful to write it down to accelerate the process for anybody who needs it.

In this thread you can find some information on how to compile SQLCipher for Windows. Instead, I preferred to take a different way: I didn't bother compiling OpenSSL (which is needed to compile SQLCipher), and used cygwin to buil the SQLCipher library linking to OpenSSL.

Download SQLCipher sources

First of all download the SQLCipher sources, with a zip archive or from the git. Uncompress the sources somewhere (I'll refer to this path using <sqlcipher_sources>).

Download the OpenSSL library

You can either compile OpenSSL using the Microsoft compiler or mingw or you can download the library from this website. I'll refer to the path containing the libraries with <openssl_binaries>.

Download and setup cygwin

I used cygwin to compile SQLCipher. Download it from the website. When requested, install at least these packages:
  1. make
  2. nano (in case you need to edit)
  3. tcl: Tool Command Language
  4. gcc-mingw-core
  5. mingw-gcc-core
This should be sufficient.

Compile SQLCipher from the cygwin command line

I used MinGW to compile the sources. First of all set the correct C compiler that make will use to compile the sources:

export CC=i686-pc-mingw32-gcc

Now go to the SQLCipher directory:

cd sqlcipher_sources

and lunch the configure script. We will need to lunch it this way:

./configure --disable-tcl CFLAGS="-DSQLITE_HAS_CODEC -Iopenssl_binaries/include" LDFLAGS="openssl_binaries/lib/libeay32.lib /usr/lib/gcc/i686-pc-mingw32/4.5.2/libgcc.a"

This will create the Makefile. Now, you can use the make command to build the library.
At a certain point, I got this message:

libtool: link: i686-pc-mingw32-gcc -DSQLITE_HAS_CODEC -I/cygdrive/c/OpenSSL-Win32/include -DSQLITE_OS_WIN=1 -I. -I./src -I./ext/rtree -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite -DNDEBUG -DSQLITE_THREADSAFE=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 /cygdrive/c/OpenSSL-Win32/lib/libeay32.lib -DHAVE_READLINE=0 -o .libs/sqlite3.exe ./src/shell.c /usr/lib/gcc/i686-pc-mingw32/4.5.2/libgcc.a ./.libs/libsqlite3.a -lpthread -L/usr/local/lib
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0xb04): undefined reference to `_EVP_get_cipherbyname'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0xb0d): undefined reference to `_OPENSSL_add_all_algorithms_noconf'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0xbeb): undefined reference to `_RAND_bytes'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x110f): undefined reference to `_EVP_get_cipherbyname'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1123): undefined reference to `_EVP_CIPHER_key_length'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x113a): undefined reference to `_EVP_CIPHER_iv_length'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1151): undefined reference to `_EVP_CIPHER_block_size'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x115f): undefined reference to `_EVP_sha1'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1167): undefined reference to `_EVP_MD_size'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1748): undefined reference to `_HMAC_CTX_init'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x174d): undefined reference to `_EVP_sha1'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1784): undefined reference to `_HMAC_Init_ex'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x17a3): undefined reference to `_HMAC_Update'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x17c3): undefined reference to `_HMAC_Update'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x17e3): undefined reference to `_HMAC_Final'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x17f3): undefined reference to `_HMAC_CTX_cleanup'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1a1e): undefined reference to `_EVP_CipherInit'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1a34): undefined reference to `_EVP_CIPHER_CTX_set_padding'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1a65): undefined reference to `_EVP_CipherInit'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1a8f): undefined reference to `_EVP_CipherUpdate'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1ab7): undefined reference to `_EVP_CipherFinal'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1acb): undefined reference to `_EVP_CIPHER_CTX_cleanup'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1c23): undefined reference to `_PKCS5_PBKDF2_HMAC_SHA1'
./.libs/libsqlite3.a(sqlite3.o):sqlite3.c:(.text+0x1cf1): undefined reference to `_PKCS5_PBKDF2_HMAC_SHA1'
collect2: ld returned 1 exit status
Makefile:512: recipe for target `sqlite3.exe' failed
make: *** [sqlite3.exe] Error 1

I'm sure there is a more elegant way of solving this, but simply correcting the command which failed was sufficient for me (the linking order is the cause; any static library or object that depends on other library should be placed before it in the command line, so libeay32.lib should be placed after libsqlite3.a):

i686-pc-mingw32-gcc -DSQLITE_HAS_CODEC -I/cygdrive/c/OpenSSL-Win32/include -DSQLITE_OS_WIN=1 -I. -I./src -I./ext/rtree -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite -DNDEBUG -DSQLITE_THREADSAFE=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DHAVE_READLINE=0 -o .libs/sqlite3.exe ./src/shell.c /usr/lib/gcc/i686-pc-mingw32/4.5.2/libgcc.a ./.libs/libsqlite3.a -lpthread -L/usr/local/lib /cygdrive/c/OpenSSL-Win32/lib/libeay32.lib

You should find the static library in sqlcipher_sources.lib directory, together with the sqlite3 executable.

Building a project linking to libsqlcipher.a

When building a project using the libsqlcipher, I had to link all the necessary libraries in the correct order:
  1. libeay32.lib
  2. libsqlite3.a
  3. libgcc.a
For example, in a Qt project I added this to link to the library:

win32 {
message("Compiling for Windows!")
LIBS += \
    $$_PRO_FILE_PWD_/os_specific_windows/3rdparty/lib/libeay32.lib \
    $$_PRO_FILE_PWD_/os_specific_windows/3rdparty/lib/libsqlite3.a \
    $$_PRO_FILE_PWD_/os_specific_windows/3rdparty/lib/libgcc.a

INCLUDEPATH += 3rdparty/include
}

Windows 7 64bit

After being asked, out of curiosity, I also tried to compile for Windows 7 64bit, and I followed the exact same procedure (I just downloaded and installed the 64bit version of all the libs and cygwin packages). The error during make is:

libtool: link: x86_64-w64-mingw32-gcc -shared .libs/sqlite3.o /usr/lib/gcc/x86_64-w64-mingw32/4.5.3/libgcc.a -lpthread -o .libs/cygsqlite3-0.dll -Wl,--enable-auto-image-base -Xlinker --out-implib -Xlinker .libs/libsqlite3.dll.a
Creating library file: .libs/libsqlite3.dll.a .libs/sqlite3.o:sqlite3.c:(.text+0xc73): undefined reference to `EVP_get_cipherbyname'
.libs/sqlite3.o:sqlite3.c:(.text+0xc7d): undefined reference to `OPENSSL_add_all_algorithms_noconf'
.libs/sqlite3.o:sqlite3.c:(.text+0xd7f): undefined reference to `RAND_bytes'
.libs/sqlite3.o:sqlite3.c:(.text+0x133a): undefined reference to `EVP_get_cipherbyname'
.libs/sqlite3.o:sqlite3.c:(.text+0x1352): undefined reference to `EVP_CIPHER_key_length'
.libs/sqlite3.o:sqlite3.c:(.text+0x136c): undefined reference to `EVP_CIPHER_iv_length'
.libs/sqlite3.o:sqlite3.c:(.text+0x1386): undefined reference to `EVP_CIPHER_block_size'
.libs/sqlite3.o:sqlite3.c:(.text+0x1395): undefined reference to `EVP_sha1'
.libs/sqlite3.o:sqlite3.c:(.text+0x139d): undefined reference to `EVP_MD_size'
.libs/sqlite3.o:sqlite3.c:(.text+0x1a37): undefined reference to `HMAC_CTX_init'
.libs/sqlite3.o:sqlite3.c:(.text+0x1a3c): undefined reference to `EVP_sha1'
.libs/sqlite3.o:sqlite3.c:(.text+0x1a76): undefined reference to `HMAC_Init_ex'
.libs/sqlite3.o:sqlite3.c:(.text+0x1a96): undefined reference to `HMAC_Update'
.libs/sqlite3.o:sqlite3.c:(.text+0x1ab3): undefined reference to `HMAC_Update'
.libs/sqlite3.o:sqlite3.c:(.text+0x1ad0): undefined reference to `HMAC_Final'
.libs/sqlite3.o:sqlite3.c:(.text+0x1ae2): undefined reference to `HMAC_CTX_cleanup'
.libs/sqlite3.o:sqlite3.c:(.text+0x1d38): undefined reference to `EVP_CipherInit'
.libs/sqlite3.o:sqlite3.c:(.text+0x1d4d): undefined reference to `EVP_CIPHER_CTX_set_padding'
.libs/sqlite3.o:sqlite3.c:(.text+0x1d7b): undefined reference to `EVP_CipherInit'
.libs/sqlite3.o:sqlite3.c:(.text+0x1da4): undefined reference to `EVP_CipherUpdate'
.libs/sqlite3.o:sqlite3.c:(.text+0x1dce): undefined reference to `EVP_CipherFinal'
.libs/sqlite3.o:sqlite3.c:(.text+0x1de4): undefined reference to `EVP_CIPHER_CTX_cleanup'
.libs/sqlite3.o:sqlite3.c:(.text+0x1f50): undefined reference to `PKCS5_PBKDF2_HMAC_SHA1'
.libs/sqlite3.o:sqlite3.c:(.text+0x2025): undefined reference to `PKCS5_PBKDF2_HMAC_SHA1'
/usr/lib/gcc/x86_64-w64-mingw32/4.5.3/../../../../x86_64-w64-mingw32/bin/ld: .libs/sqlite3.o: bad reloc address 0x1c0 in section `.data'
collect2: ld returned 1 exit status
Makefile:501: recipe for target `libsqlite3.la' failed
make: *** [libsqlite3.la] Error 1


So it seems that also the library now is failing to compile: maybe something has changed in some tool or in the Makefile, I don't know.
Luckily, sqlite3 uses amalgamation; it is therefore possible to compile manually quite simply. I built the library this way:

x86_64-w64-mingw32-ar cru .libs/libsqlite3.a /usr/lib/gcc/x86_64-w64-mingw32/4.5.3/libgcc.a sqlite3.o
x86_64-w64-mingw32-ranlib .libs/libsqlite3.a


since the object file sqlite3.o was already compiled by make, and the sqlcipher executable simply like this:

x86_64-w64-mingw32-gcc -DSQLITE_HAS_CODEC -I/cygdrive/c/OpenSSL-Win64/include -DSQLITE_OS_WIN=1 -I. -I./src -I./ext/rtree -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite -DNDEBUG -DSQLITE_THREADSAFE=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DHAVE_READLINE=0 -o .libs/sqlite3.exe ./src/shell.c /usr/lib/gcc/x86_64-w64-mingw32/4.5.3/libgcc.a .libs/libsqlite3.a /cygdrive/c/OpenSSL-Win64/lib/libeay32.lib

I tried to create a simple encrypted database and seemed to work.
Hope this helps! 

Wednesday, May 2, 2012

Embedded cross-platform development for Android, iOS and Linux Embedded

I recently came across a new problem: cross-platform development of libraries on iOS, Android and Linux Embedded devices. What I wanted was a cross-platform way of writing a library letting me perform as much non-GUI work as possible.

The only reasonable solution I could think of was developing the library entirely in C/C++ language. This can be quite long, but simple to recompile for all these platforms. Linux Embedded should commonly support entire C/C++ toolchains (like Codesourcery toolchains), iOS supports standard C/C++ quite well and can be used very simply from Objective-C using Objective-C++ and Android can make use of C/C++ libraries through a JNI interface (not as much quick as the others, but possible).

Ok, then C/C++ is my choice. Anyway, would it possible to increase my productivity by using some kind of general purpose C/C++ library instead of relying only on the standard library? There are many possible solutions to this, one of which is the Neptune library.

During the last months anyway, I've come to be very close to the Qt libraries. I started to love the completeness, the flexibility and the comfortable API Qt provides.

I've been looking with interest for months to the new development of the Qt porting to Android (thanks to BogDan Vatra) and iOS but I never had the chance to try. Anyway, those ports make use of the QPA build of Qt, which started with the Lighthouse project to provide Qt portability on the GUI level to other platforms. This is not exactly what I needed: I'm not interested at the moment on the GUI development. What I need is simpler. Anyway, I found this very useful to start working.

The environment I'm currently working with allows me to create a common Qt Makefile to be parsed by qmake to generate a Makefile for any platform I need: for Android the toolchain provided by the NDK and for iOS the toolchain provided by Apple.

After this introduction, I explain the steps I followed to setup the environment.

Setting up for Android development

First thing to do is to download the Android NDK from the official website. Uncompress it somewhere.

Download and compile the Android port of Qt: it might be good to compile a specification file for the build, it is quite simple, but the Android's toolchain is not a complete toolchain. This implies that some modifications to the Qt sources are needed. I started to do it, but for the moment simply downloading from the git the last sources from Necessitas is good enough.

Now you have both the Android toolchain and the Qt sources compiled for Android. You can select the new Qt platform and compile directly from Qt Creator. This is damn good, cause it makes it quite simple to implement the JNI interface also, which I've never found a way to write comfortably from Eclipse.

Once the library is compiled, you can place it into an Android project as instructed in the manual of the Android NDK and all is done. You have C/C++ sources with Qt support ready to be used in an Android project. You can also implement the JNI interface and include the sources only when building for android using the qmake scopes.

Setting up for iOS development

It might seem a paradox, but I found it more difficult to compile for iOS than for Android... What you can do is use the QPA specification for iOS that can be found in <Qt sources>/mkspecs/qpa/macx-iphone*. By using qmake from the command line you can create a Makefile for iOS and then compile it on a Mac:

qmake -spec qpa/macx-iphonesimulator-g++


you can also directly include Objective-C and Objective-C++ sources directly into the Qt project file by using the variable:

OBJECTIVE_SOURCES += source_list


Just remember that only static libraries are allowed on iOS, so include something like:


ios {
CONFIG += static
}

 

to compile statically when on iOS.

When I tried this for the first time it didn't work. It seems that for some reason there is a difference between my XCode setup and the one used as a reference for the platform specification. What I had to do is to fix the qmake.conf specification for the iOS platform so that I compiles correctly. I found this article very useful to see what needed to be changed.
In particular, for recent versions of Xcode, I had to change the variable QMAKE_IOS_DEV_PATH (in mkspecs/qpa/macx-iphonesimulator-g++/qmake.conf) from:

QMAKE_IOS_DEV_PATH = /Developer/Platforms/iPhoneSimulator.platform/Developer


to:

QMAKE_IOS_DEV_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer

and the QMAKE_IOS_SDK_VERSION (in mkspecs/qpa/common/g++-base-macx-iphone.conf) to 5.1.

Using Qt libraries on Android and iOS

There is one other point when implementing libraries using Qt: many Qt features require an event loop to be running and that an instance of QCoreApplication to be instantiated somehow. To do this, it might be sufficient to run the event loop and instantiate QCoreApplication in a thread of the library: in the init function, just spawn a new thread and exec the event loop.
This is an example of what I tried: first I created a Java class like this:

package com.lukeshq.android.test.qt;

/**
 * Author Luca Carlon
 * Date: 05.10.2012
 */

public class QtTest {
   // Static initializer.
   static {
      System.loadLibrary("QtCore");
      System.loadLibrary("AndroidTest");
   }

   public QtTest() {
      new Thread(new Runnable() {
         @Override
         public void run() {
            while (true) {
               try {
                  Thread.sleep(1000);
               }
               catch (InterruptedException e) {
                  e.printStackTrace();
               }
               mySlot();
            }
         }
      }).start();
   }

   // Native interface.
   public native boolean mySlot();
}

then, in my library, I created a simple object like this:

class MyQObject : public QObject
{
   Q_OBJECT
public:
   explicit MyQObject(QObject* parent = 0);

public slots:
   void mySlot();
};

and this is the implementation of the bindings:

/*----------------------------------------------------------------------
|    includes
+---------------------------------------------------------------------*/
#include <QString>
#include <QtConcurrentRun>
#include <QCoreApplication>
#include <QTimer>
#include <jni.h>
#include <android/log.h>
#include "myqobject.h"

/*----------------------------------------------------------------------
|    declarations
+---------------------------------------------------------------------*/
MyQObject* myQObject;

/*----------------------------------------------------------------------
|    runQCoreApplication
+---------------------------------------------------------------------*/
void runQCoreApplication()
{
   // Instantiate QCoreApplication.
   int i = 1;
   char* c[1] = {"MyLib"};
   QCoreApplication a(i, c);

   // Start the QTimer.
   __android_log_print(ANDROID_LOG_INFO, "LibTag", "Starting QTimer!");
   QTimer* t = new QTimer();
   myQObject = new MyQObject();
   QObject::connect(t, SIGNAL(timeout()), myQObject, SLOT(mySlot()));
   t->setInterval(1000);
   t->setSingleShot(false);
   t->start();

   // Start the event loop.
   a.exec();
}

/*----------------------------------------------------------------------
|    JNI_OnLoad
+---------------------------------------------------------------------*/
extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved)
{
   Q_UNUSED(vm);
   Q_UNUSED(reserved);

   // Test QString.
   QString myString = QString("My QString!");
   __android_log_print(ANDROID_LOG_INFO, "LibTag", "%s", qPrintable(myString));

   // Start the QCoreApplication event loop.
   QtConcurrent::run(&runQCoreApplication);

   return JNI_VERSION_1_6;

}

/*----------------------------------------------------------------------
|    JNI_OnUnload
+---------------------------------------------------------------------*/
extern "C" JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM* vm, void* reserved)
{
   Q_UNUSED(vm);
   Q_UNUSED(reserved);

   __android_log_print(ANDROID_LOG_INFO, "LibTag", "Unloading!");
}

/*----------------------------------------------------------------------
|    Java_com_lukeshq_android_test_qt_QtTest_myslot
+---------------------------------------------------------------------*/
extern "C" JNIEXPORT void JNICALL
Java_com_lukeshq_android_test_qt_QtTest_mySlot(JNIEnv* env, jobject thiz)
{
   Q_UNUSED(env);
   Q_UNUSED(thiz);

   QMetaObject::invokeMethod(myQObject, "mySlot");
}