среда, 31 июля 2013 г.

Qwt Android


Qwt - кроссплатформенная библиотека для отображения научных данных в графическом виде.

Рабочая среда:
Ubuntu 13.04

Будет продемонстрировано:
- сборка qwt под Android
- использование динамической библиотеки в связке qt5 + Android

Сборка qwt под Android  

Для сборки qwt необходимо собрать библиотеку qt5
Сборка Qt5 Android http://qt-project.org/wiki/Qt5ForAndroid
Либо можно не собирать Qt5, а скачать собранную http://qt-project.org/downloads

После сборки qt5 можно приступить к сборке qwt.
Исходники qwt http://sourceforge.net/projects/qwt/files

Собрать qwt под десктоп просто:
qmake & make -j5
Для сборки библиотеки qwt под Android можно использовать Qt Creator, выбрав соответствующий Kit. В файле qwtconfig.conf нужно закомментировать QwtExamples, QwtDesigner.

Использование динамической библиотеки в связке qt5 + Android 

В Qt Creator создадим новый проект Qt Gui Application. Добавим комплект сборки Android.

В .pro файл нужно добавить библиотеку qwt
INCLUDEPATH += путь_до_хейдоров
DEPENDPATH += путь_до_хейдоров

android {
    QWT_PATH = /путь/до/библиотеки/qwt/под/android
    qwt.path = /libs/armeabi-v7a
    qwt.files = $$QWT_PATH/libqwt.so
    INSTALLS += qwt
    message("android")
} else:unix {
    QWT_PATH = /путь/до/библиотеки/qwt/под/десктоп
    message("unix")
}
LIBS += -L$$QWT_PATH -lqwt

Данная операция добавит библиотеку libqwt.so в файл apk.
Для загрузки библиотеки в память во время старта, нужно добавить ее имя в файл  android/res/values/libs.xml
<array name="bundled_libs">
    <item>qwt</item>
</array>
Причем важна очередность, если одна библиотека использует другую.
 
Все, теперь можно использовать qwt.

вторник, 30 июля 2013 г.

Qml Qwt Android

В данной статье будем запускать библиотеку qwt под Android.
Отображение qwt будем производить с помощью qml.  Для этих цели будет использоваться Qt5.

Рабочая среда:
Ubuntu 13.04

Будет продемонстрировано:
- отображения QWidget в qml и взаимодействия с ним(на примере QwtPlot)

Сборка qwt  под android http://vadim-d.blogspot.com/2013/07/qwt-android.html

Отображения QWidget в qml и взаимодействия с ним

Для отображения простого графика на qwt необходимы два класса QwtPlot и QwtPlotCurve. Добавим эти классы в qml.

Для добавления своих типов в qml существуют классы: QQuickPaintedItem, QQuickItem.
Подробнее http://qt-project.org/doc/qt-5.1/qtquick/qquickitem.html

Поскольку QwtPlot наследуется от QWidget, для типа который будет представлять QwtPlot, необходимо наследоваться от QQuickPaintedItem. В методе void paint(QPainter *painter) происходит рисование QwtPlot.

.h
#ifndef QUICKWIDGET_H
#define QUICKWIDGET_H

#include <QQuickPaintedItem>

class QwtPlot;

class QuickQwtPlot : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QString titleYLeft READ titleYLeft WRITE setAxisYLeft)
    Q_PROPERTY(QString titleYRight READ titleYRight WRITE setAxisYRight)
  Q_PROPERTY(QString titleXBottom READ titleXBottom WRITE setAxisXBottom)
    Q_PROPERTY(QString titleXTop READ titleXTop WRITE setAxisXTop)
    Q_PROPERTY(QString title READ title WRITE setTitle)

public:
    QuickQwtPlot(QQuickItem *parent = 0);
    ~QuickQwtPlot();

    void paint(QPainter *painter);

    QwtPlot *getPlot();

    QString titleXBottom() const;
    QString titleXTop() const;
    QString titleYRight() const;
    QString titleYLeft() const;
    QString title() const;

public slots:
    void heightChanged();
    void widthChanged();
    void setAxisXBottom(QString arg);
    void setAxisXTop(QString arg);
    void setAxisYRight(QString arg);
    void setAxisYLeft(QString arg);
    void setTitle(QString arg);

private:
    QwtPlot *mPlot;
};

#endif // QUICKWIDGET_H
.cpp
#include "QuickQwtPlot.h"

#include <qwt_legend.h>
#include <qwt_plot_canvas.h>
#include "qwt_plot.h"

#include <QDebug>

QuickQwtPlot::QuickQwtPlot(QQuickItem *parent)  : QQuickPaintedItem(parent)
{
    mPlot = new QwtPlot;

    mPlot->setAttribute(Qt::WA_NoSystemBackground);

    connect(this, SIGNAL(heightChanged()), this, SLOT(heightChanged()));
    connect(this, SIGNAL(widthChanged()), this, SLOT(widthChanged()));

    mPlot->insertLegend(new QwtLegend(), QwtPlot::BottomLegend);

    // canvas
    QwtPlotCanvas *canvas = new QwtPlotCanvas();
    canvas->setLineWidth(0);
    canvas->setFrameStyle(QFrame::Box | QFrame::Plain);
    canvas->setBorderRadius(4);
    QPalette canvasPalette(Qt::white);
    canvas->setPalette(canvasPalette);
    mPlot->setCanvas(canvas);

    mPlot->setAutoReplot(true);
}

QuickQwtPlot::~QuickQwtPlot()
{
    delete mPlot;
}

void QuickQwtPlot::paint(QPainter *painter)
{
    painter->setRenderHints(QPainter::Antialiasing, true);
    mPlot->render(painter);
}

QwtPlot *QuickQwtPlot::getPlot()
{
    return mPlot;
}

QString QuickQwtPlot::titleXBottom() const
{
    return mPlot->axisTitle(QwtPlot::xBottom).text();
}

QString QuickQwtPlot::titleXTop() const
{
    return mPlot->axisTitle(QwtPlot::xTop).text();
}

QString QuickQwtPlot::titleYRight() const
{
    return mPlot->axisTitle(QwtPlot::yRight).text();
}

QString QuickQwtPlot::titleYLeft() const
{
    return mPlot->axisTitle(QwtPlot::yLeft).text();
}

void QuickQwtPlot::heightChanged()
{
    mPlot->setFixedHeight(contentsBoundingRect().height());
}

void QuickQwtPlot::widthChanged()
{
    mPlot->setFixedWidth(contentsBoundingRect().width());
}

void QuickQwtPlot::setAxisXBottom(QString arg)
{
    mPlot->setAxisTitle(QwtPlot::xBottom, arg);
}

void QuickQwtPlot::setAxisXTop(QString arg)
{
    mPlot->setAxisTitle(QwtPlot::xTop, arg);
}

void QuickQwtPlot::setAxisYRight(QString arg)
{
    mPlot->setAxisTitle(QwtPlot::yRight, arg);
}

void QuickQwtPlot::setAxisYLeft(QString arg)
{
    mPlot->setAxisTitle(QwtPlot::yLeft, arg);
}

QString QuickQwtPlot::title() const
{
    return mPlot->title().text();
}

void QuickQwtPlot::setTitle(QString arg)
{
    mPlot->setTitle(arg);
}
Можно видеть, что добавлены свойства типа:
Q_PROPERTY(QString titleYLeft READ titleYLeft WRITE setAxisYLeft) 
они необходимы для доступа к свойствам класса QwtPlot из qml. Например для задания названия осей.
Изменение размера нашего типа QuickQwtPlot в qml вызовет слоты heightChanged(), widthChanged(). Из них мы установим соответствующий размер для QwtPlot через contentsBoundingRect().
Для того чтобы QuickQwtPlot был виден в qml, нужно его зарегистрировать с помощью функции qmlRegisterType. qmlRegisterType вызывать в main до создания объекта QQuickView.
qmlRegisterType<QuickQwtPlot>("QuickQwt", 1, 0, "QuickQwtPlot");
Тогда в qml:
import QuickQwt 1.0
QuickQwtPlot {} // QwtPlot
Далее  рассмотрим добавление QwtPlotCurve в qml.
Поскольку класс QwtPlotCurve не производит рисования, то наследоваться нужно от QQuickItem.

.h
#ifndef QUICKCURVE_H
#define QUICKCURVE_H

#include "qwt_plot_curve.h"

#include <QQuickItem>
#include <QMetaType>

class QuickCurve : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QVariantList attach READ attach WRITE setAttach)
    Q_PROPERTY(QString title READ title WRITE setTitle)
    Q_PROPERTY(QColor color READ color WRITE setColor)
    Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth)
    Q_PROPERTY(QwtPlotCurve::CurveStyle style READ style WRITE setStyle)
    Q_PROPERTY(QwtPlotCurve::CurveStyle NoCurve READ NoCurve)
    Q_PROPERTY(QwtPlotCurve::CurveStyle Lines READ Lines)
    Q_PROPERTY(QwtPlotCurve::CurveStyle Sticks READ Sticks)
    Q_PROPERTY(QwtPlotCurve::CurveStyle Steps READ Steps)
    Q_PROPERTY(QwtPlotCurve::CurveStyle Dots READ Dots)

public:
    explicit QuickCurve(QQuickItem *parent = 0);
    ~QuickCurve();

    QVariantList attach() const;
    QString title() const;
    QColor color() const;
    int lineWidth() const;

    QwtPlotCurve::CurveStyle NoCurve() const;
    QwtPlotCurve::CurveStyle style() const;
    QwtPlotCurve::CurveStyle Lines() const;
    QwtPlotCurve::CurveStyle Sticks() const;
    QwtPlotCurve::CurveStyle Steps() const;
    QwtPlotCurve::CurveStyle Dots() const;

signals:

public slots:
    void setAttach(QVariantList arg);
    void setTitle(QString arg);
    void setColor(QColor arg);
    void setLineWidth(int arg);
    void setStyle(QwtPlotCurve::CurveStyle arg);

protected:
    void replote();
    QwtPlotCurve *getCurve() const;

private:
    QwtPlotCurve *mCurve;
    QVariantList mPlots;
};

Q_DECLARE_METATYPE(QwtPlotCurve::CurveStyle)

#endif // QUICKCURVE_H
Как видно, класс в основном реализует проброс свойств класса QwtPlotCurve из c++ в qml с помощью Q_PROPERTY. Свойства добавлены не все, лишь те, что  были необходимы. В объявлении содержится макрос Q_DECLARE_METATYPE. Он необходим для добавления всех функций с которыми будет работать QVariant. Также нужно зарегистрировать в main собственные типы которые используются в  Q_PROPERTY. Производится это через функцию qRegisterMetaType<>().
Например:
qRegisterMetaType<QwtPlotCurve::CurveStyle>();

.cpp
#include "QuickCurve.h"
#include "QuickQwtPlot.h"

#include <QDebug>

QuickCurve::QuickCurve(QQuickItem *parent) :
    QQuickItem(parent)
{
    mCurve = new QwtPlotCurve;
    mCurve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
}

QuickCurve::~QuickCurve()
{
    delete mCurve;
}

QVariantList QuickCurve::attach() const
{
    return mPlots;
}

QString QuickCurve::title() const
{
    return mCurve->title().text();
}

QColor QuickCurve::color() const
{
    return mCurve->pen().color();
}

int QuickCurve::lineWidth() const
{
    return mCurve->pen().width();
}

QwtPlotCurve::CurveStyle QuickCurve::NoCurve() const
{
    return QwtPlotCurve::NoCurve;
}

QwtPlotCurve::CurveStyle QuickCurve::style() const
{
    return mCurve->style();
}

QwtPlotCurve::CurveStyle QuickCurve::Lines() const
{
    return QwtPlotCurve::Lines;
}

QwtPlotCurve::CurveStyle QuickCurve::Sticks() const
{
    return QwtPlotCurve::Sticks;
}

QwtPlotCurve::CurveStyle QuickCurve::Steps() const
{
    return QwtPlotCurve::Steps;
}

QwtPlotCurve::CurveStyle QuickCurve::Dots() const
{
    return QwtPlotCurve::Dots;
}

void QuickCurve::setAttach(QVariantList arg)
{
    foreach (QVariant item, arg) {
        if (item.canConvert<QuickQwtPlot *>()) {
            qDebug() << "attach plot";
            QuickQwtPlot *plot = item.value<QuickQwtPlot *>();
            mCurve->attach(plot->getPlot());
            mPlots.append(item);
        }
    }
}

QwtPlotCurve *QuickCurve::getCurve() const
{
    return mCurve;
}

void QuickCurve::setStyle(QwtPlotCurve::CurveStyle arg)
{
    mCurve->setStyle(arg);
}

void QuickCurve::replote()
{
    foreach (QVariant item, mPlots) {
        if (item.canConvert<QuickQwtPlot *>()) {
            QuickQwtPlot *plot = item.value<QuickQwtPlot *>();
            plot->update();
        }
    }
}

void QuickCurve::setTitle(QString arg)
{
    mCurve->setTitle(arg);
}

void QuickCurve::setColor(QColor arg)
{
    mCurve->setPen(arg, mCurve->pen().width());
}

void QuickCurve::setLineWidth(int arg)
{
    mCurve->setPen(mCurve->pen().color(), arg);
}
Переменная mPlots типа QVariantList содержит все экземпляра QwtPlot, которые были добавлены через attach. С одной кривой можно связать несколько полотен QwtPlot. Функции void replote() и QwtPlotCurve *getCurve() const видны как protected. Было задумано наследоваться от класса QwtPlotCurve и уже в нем задавать данные для кривой. Например:

.h
#ifndef QUICKCURVELIGHT_H
#define QUICKCURVELIGHT_H

#include <QLightSensor>

#include "QuickCurve.h"

class QuickCurveLight : public QuickCurve
{
    Q_OBJECT
public:
    explicit QuickCurveLight(QQuickItem *parent = 0);
   
signals:
   
public slots:
    void timerEvent(QTimerEvent *event);

private:
    void updatePlygon(QPolygonF *polygon, double value, quint32 size = 100);

    QPolygonF mPointsLight;
    QLightSensor mLightSensor;
};

#endif // QUICKCURVELIGHT_H
.cpp
#include "QuickCurveLight.h"

QuickCurveLight::QuickCurveLight(QQuickItem *parent) :
    QuickCurve(parent)
{
    startTimer(500);
    mLightSensor.start();
}

void QuickCurveLight::timerEvent(QTimerEvent *event)
{
    if (mLightSensor.isActive() && !mLightSensor.isBusy()) {
        updatePlygon(&mPointsLight, mLightSensor.reading()->lux());
        getCurve()->setSamples(mPointsLight);
    }
    replote();
    QQuickItem::timerEvent(event);
}

void QuickCurveLight::updatePlygon(QPolygonF *polygon, double value, quint32 size)
{
    if (static_cast<quint32>(polygon->size()) > size) {
        polygon->pop_front();

        const int h = 1;
        for (quint32 i = 0; i < size; ++i)
            (*polygon)[i].rx() = polygon->at(i).x() - h;
    }
    polygon->push_back(QPointF(polygon->size(), value));
}

Отображение графика в qml:
import QtQuick 2.0
import QuickQwt 1.0

Rectangle {
    color: "lightblue"
    focus: true

    QuickQwtPlot {
        id: plot
        anchors.fill: parent

        title: "Light sensor"
        titleYLeft: "lx"
    }

    QuickCurveLight {
        title: "Light"
        color: "yellow"
        lineWidth: 5
        style: Lines

        attach: [plot]
    }

    MouseArea {
        anchors.fill: parent;

        onClicked: Qt.quit()
    }
}

воскресенье, 2 июня 2013 г.

Собираем ffmpeg под Android

Рабочее окружение

  1. Ubuntu 13.04 x86_64
  2. Eclipse
  3. android-ndk-r8e
  4. arm-linux-androideabi

Сборка

1. Скачиваем скрипт для сборки ffmpeg
git clone git://github.com/yixia/FFmpeg-Android.git
2. Переходим FFmpeg-Android.

В скрипте FFmpeg-Android.sh 
Добавим размещение ANDROID_NDK
ANDROID_NDK=~/android-ndk
К списку параметров make-standalone-toolchain.sh добавить параметр
--system=linux-x86_64
Где linux-x86_64 разрядность системы.

Изменим строку
export CC="ccache arm-linux-androideabi-gcc"
на
export CC="arm-linux-androideabi-gcc"
 В цикле
for version in neon armv7 vfp armv6; do
можно убрать неиспользуемые платформы. Например
 for version in armv7; do
Запустить FFmpeg-Android.sh. Скрипт сольет и соберет ffmpeg. Результат компиляции будет в build/ffmpeg.

Использование ffmpeg под Android

1. Создаем новый проект в Eclipse

2. Добавим поддержку Native. Правой кнопкой по проекту, Android Tools-Add Native Support

3. Добавим вызов нашей библиотеки из MainActivity

public class MainActivity extends Activity {

    private static native int logFileInfo(String filename);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        logFileInfo("/sdcard/Download/Flipped.2010.HDRip_mvo.x264.RG.tru.mkv");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    static {
        // load first libffmpeg.so
        System.loadLibrary("ffmpeg");
        System.loadLibrary("FfmpegAndroid");
    }
}

Адрес видео файла необходимо исправить на свой.

4. Cоздадим заголовочный файл функции logFileInfo для c++.

Для этого переходим в папку из которой в терминале вызываем команду 
javah полный.путь.до.класса
Например
javah com.example.ffmpegandroid.MainActivity
Полученный файл переместим в папку jni.
5. Изменим Android.mk. Содержимое моего Android.mk:
LOCAL_PATH := $(call my-dir)

FFMPEG=/home/vadim/FFmpeg-Android/build/ffmpeg/armv7

include $(CLEAR_VARS)

LOCAL_MODULE := prebuild-ffmpeg
LOCAL_SRC_FILES := $(FFMPEG)/libffmpeg.so

include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := $(FFMPEG)/include
LOCAL_CFLAGS += -D__STDC_CONSTANT_MACROS
LOCAL_LDLIBS += -llog -ljnigraphics -lz -lm $(FFMPEG)/libffmpeg.so
LOCAL_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=neon
LOCAL_MODULE    := FfmpegAndroid
LOCAL_SRC_FILES := FfmpegAndroid.cpp

include $(BUILD_SHARED_LIBRARY)
 6. В c++ будем вызывать ffmpeg. Хейдеры ffmpeg нужно обернуть в extern "C".
#include <jni.h>

#include "com_example_ffmpegandroid_MainActivity.h"

#include <android/log.h>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}

#define LOG_TAG "mylib"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

jint JNICALL Java_com_example_ffmpegandroid_MainActivity_logFileInfo(
        JNIEnv *env, jclass, jstring filename) {
    // init ffmpeg
    av_register_all();
    avcodec_register_all();

    LOGI("Hello from ffmpeg");

    const char *str = env->GetStringUTFChars(filename, 0);
    AVFormatContext *pFormatCtx = avformat_alloc_context();

    const int res = avformat_open_input(&pFormatCtx, str, 0, 0);
    LOGI("result %d", res);

    if (res != 0) {
        LOGE("Can't open file '%s'\n", str);
        return 1;
    } else if (res >= 0) {
        LOGI("File was opened\n");
        LOGI("File '%s', Codec %s", pFormatCtx->filename,
                pFormatCtx->iformat->name);
        LOGI("Duration %d", pFormatCtx->duration);
    }

    return 0;
}
Если все верно, то LogCat должен вводить примерно следующее:
I/mylib(24494): Hello from ffmpeg
I/mylib(24494): result 0
I/mylib(24494): File was opened
I/mylib(24494): File '/sdcard/Download/Flipped.2010.HDRip_mvo.x264.RG.tru.mkv', Codec matroska,webm
I/mylib(24494): Duration 1694753600

Проект в Eclipse
http://yadi.sk/d/nZzMNzj85R7IG
Скрипт сборки ffmpeg
http://yadi.sk/d/ichI1kzj5R7IA