From f4692059ca0eddc702ea3906162bb534c7bb90bd Mon Sep 17 00:00:00 2001 From: Mohammed Sameer Date: Mon, 27 Aug 2012 03:13:24 +0300 Subject: [PATCH] Implemented renderer for meego textured video interface --- lib/lib.pro | 7 +- lib/qtcamconfig.h | 3 +- lib/qtcamviewfinderrenderermeego.cpp | 319 +++++++++++++++++++++++++++ lib/qtcamviewfinderrenderermeego.h | 57 +++++ 4 files changed, 384 insertions(+), 2 deletions(-) create mode 100644 lib/qtcamviewfinderrenderermeego.cpp create mode 100644 lib/qtcamviewfinderrenderermeego.h diff --git a/lib/lib.pro b/lib/lib.pro index 76a862c..0de447b 100644 --- a/lib/lib.pro +++ b/lib/lib.pro @@ -8,7 +8,7 @@ QT += opengl CONFIG += link_pkgconfig debug PKGCONFIG = gstreamer-0.10 gstreamer-interfaces-0.10 gstreamer-video-0.10 gstreamer-tag-0.10 \ - gstreamer-pbutils-0.10 + gstreamer-pbutils-0.10 meego-gstreamer-interfaces-0.10 HEADERS += qtcamconfig.h qtcamera.h qtcamscanner.h qtcamdevice.h qtcamviewfinder.h \ qtcammode.h qtcamgstreamermessagehandler.h qtcamgstreamermessagelistener.h \ @@ -24,5 +24,10 @@ SOURCES += qtcamconfig.cpp qtcamera.cpp qtcamscanner.cpp qtcamdevice.cpp qtcamvi HEADERS += qtcammode_p.h qtcamdevice_p.h +isEqual(MEEGO_EDITION, harmattan) { +SOURCES += qtcamviewfinderrenderermeego.cpp +HEADERS += qtcamviewfinderrenderermeego.h +} + SOURCES += main.cpp HEADERS += main.h diff --git a/lib/qtcamconfig.h b/lib/qtcamconfig.h index eb04bc9..c6f002c 100644 --- a/lib/qtcamconfig.h +++ b/lib/qtcamconfig.h @@ -10,9 +10,10 @@ #define SCANNER_TYPE_V4L2 "v4l2" #define SCANNER_TYPE_ENUM "enum" +// TODO: kill those //#define RENDERER_TYPE_GL_SINK "glsink" //#define RENDERER_TYPE_X_OVERLAY "xoverlay" -//#define RENDERER_TYPE_MEEGO "meego" +#define RENDERER_TYPE_MEEGO "meego" #define RENDERER_TYPE_GENERIC "generic" class QtCamConfigPrivate; diff --git a/lib/qtcamviewfinderrenderermeego.cpp b/lib/qtcamviewfinderrenderermeego.cpp new file mode 100644 index 0000000..8c5eeed --- /dev/null +++ b/lib/qtcamviewfinderrenderermeego.cpp @@ -0,0 +1,319 @@ +#include "qtcamviewfinderrenderermeego.h" +#include +#include +#include +#include "qtcamconfig.h" +#include +#include +#include +#include +#include + +QT_CAM_VIEWFINDER_RENDERER(RENDERER_TYPE_MEEGO, QtCamViewfinderRendererMeeGo); + +#define GL_TEXTURE_EXTERNAL_OES 0x8060 + +static const QString FRAGMENT_SHADER = "" + "#extension GL_OES_EGL_image_external: enable\n" + "uniform samplerExternalOES texture0;" + "varying lowp vec2 fragTexCoord;" + "void main() {" + " gl_FragColor = texture2D(texture0, fragTexCoord);" + "}" + ""; + +static const QString VERTEX_SHADER = "" + "attribute highp vec4 inputVertex;" + "attribute lowp vec2 textureCoord;" + "uniform highp mat4 matrix;" + "uniform highp mat4 matrixWorld;" + "varying lowp vec2 fragTexCoord;" + "" + "void main() {" + " gl_Position = matrix * matrixWorld * inputVertex;" + " fragTexCoord = textureCoord;" + "}" + ""; + +QtCamViewfinderRendererMeeGo::QtCamViewfinderRendererMeeGo(QtCamConfig *config, + QObject *parent) : + QtCamViewfinderRenderer(config, parent), + m_conf(config), + m_sink(0), + m_frame(-1), + m_id(0), + m_notify(0), + m_needsInit(true), + m_program(0) { + + // Texture coordinates: + // Reversed because of Qt reversed coordinate system + m_texCoords[0] = 0; m_texCoords[1] = 1; + m_texCoords[2] = 1; m_texCoords[3] = 1; + m_texCoords[4] = 1; m_texCoords[5] = 0; + m_texCoords[6] = 0; m_texCoords[7] = 0; + + bzero(&m_vertexCoords, 8); +} + +QtCamViewfinderRendererMeeGo::~QtCamViewfinderRendererMeeGo() { + if (m_sink) { + g_signal_handler_disconnect(m_sink, m_id); + g_signal_handler_disconnect(m_sink, m_notify); + g_object_remove_toggle_ref(G_OBJECT(m_sink), (GToggleNotify)sink_notify, this); + m_sink = 0; + } +} + +void QtCamViewfinderRendererMeeGo::paint(QPainter *painter) { + QMutexLocker locker(&m_frameMutex); + if (m_frame == -1) { + return; + } + + painter->beginNativePainting(); + + if (m_needsInit) { + calculateProjectionMatrix(painter->viewport()); + m_needsInit = false; + } + + if (!m_program) { + // Program will be created if needed and will never be deleted even + // if attaching the shaders fail. + createProgram(); + } + + paintFrame(painter, m_frame); + + painter->endNativePainting(); +} + +void QtCamViewfinderRendererMeeGo::resize(const QSizeF& size) { + if (size == m_size) { + return; + } + + m_size = size; + + m_renderArea = QRectF(); + + calculateCoords(); + + // TODO: this will destroy everything + // but we need a way to reset the viewport and the transformation matrix only. + m_needsInit = true; +} + +void QtCamViewfinderRendererMeeGo::reset() { + // Nothing. +} + +GstElement *QtCamViewfinderRendererMeeGo::sinkElement() { + if (!QGLContext::currentContext()) { + qCritical() << "Cannot create the GStreamer element without an OpenGL context."; + return 0; + } + + if (!m_sink) { + m_sink = gst_element_factory_make(m_conf->viewfinderSink().toAscii().data(), + "QtCamViewfinderRendererMeeGoSink"); + if (!m_sink) { + qCritical() << "Failed to create" << m_conf->viewfinderSink(); + return 0; + } + + g_object_add_toggle_ref(G_OBJECT(m_sink), (GToggleNotify)sink_notify, this); + } + + // Displa can be replaced with a null pointer. + // We all know that the sink used for Harmattan ignores the x-display property ;-) + + Display *d = QX11Info::display(); + g_object_set(G_OBJECT(m_sink), "x-display", d, "use-framebuffer-memory", TRUE, NULL); + + EGLDisplay dpy = eglGetDisplay((EGLNativeDisplayType)d); + EGLContext context = eglGetCurrentContext(); + + g_object_set(G_OBJECT(m_sink), "egl-display", dpy, "egl-context", context, NULL); + + m_id = g_signal_connect(G_OBJECT(m_sink), "frame-ready", G_CALLBACK(frame_ready), this); + + GstPad *pad = gst_element_get_static_pad(m_sink, "sink"); + m_notify = g_signal_connect(G_OBJECT(pad), "notify::caps", + G_CALLBACK(sink_caps_changed), this); + gst_object_unref(pad); + + return m_sink; +} + +void QtCamViewfinderRendererMeeGo::frame_ready(GstElement *sink, int frame, + QtCamViewfinderRendererMeeGo *r) { + Q_UNUSED(sink); + Q_UNUSED(frame); + + r->m_frameMutex.lock(); + r->m_frame = frame; + r->m_frameMutex.unlock(); + + QMetaObject::invokeMethod(r, "updateRequested", Qt::QueuedConnection); +} + +void QtCamViewfinderRendererMeeGo::sink_notify(QtCamViewfinderRendererMeeGo *q, + GObject *object, gboolean is_last_ref) { + + if (is_last_ref) { + g_signal_handler_disconnect(q->m_sink, q->m_id); + g_object_remove_toggle_ref(G_OBJECT(q->m_sink), (GToggleNotify)sink_notify, q); + q->m_sink = 0; + } +} + +void QtCamViewfinderRendererMeeGo::sink_caps_changed(GObject *obj, GParamSpec *pspec, + QtCamViewfinderRendererMeeGo *q) { + Q_UNUSED(obj); + Q_UNUSED(pspec); + + int width, height; + if (obj && gst_video_get_size (GST_PAD(obj), &width, &height)) { + QMetaObject::invokeMethod(q, "setVideoSize", Qt::QueuedConnection, + Q_ARG(QSizeF, QSizeF(width, height))); + } +} + +void QtCamViewfinderRendererMeeGo::calculateProjectionMatrix(const QRectF& rect) { + m_projectionMatrix = QMatrix4x4(); + m_projectionMatrix.ortho(rect); +} + +void QtCamViewfinderRendererMeeGo::createProgram() { + if (m_program) { + delete m_program; + } + + m_program = new QGLShaderProgram(this); + + if (!m_program->addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER)) { + qCritical() << "Failed to add vertex shader"; + return; + } + + if (!m_program->addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER)) { + qCritical() << "Failed to add fragment shader"; + return; + } + + m_program->bindAttributeLocation("inputVertex", 0); + m_program->bindAttributeLocation("textureCoord", 1); + + if (!m_program->link()) { + qCritical() << "Failed to link program!"; + return; + } + + if (!m_program->bind()) { + qCritical() << "Failed to bind program"; + return; + } + + m_program->setUniformValue("texture0", 0); // texture UNIT 0 + m_program->release(); +} + +void QtCamViewfinderRendererMeeGo::paintFrame(QPainter *painter, int frame) { + if (frame == -1) { + return; + } + + MeegoGstVideoTexture *sink = MEEGO_GST_VIDEO_TEXTURE(m_sink); + if (!meego_gst_video_texture_acquire_frame(sink, frame)) { + qDebug() << "Failed to acquire frame"; + return; + } + + m_program->bind(); + + m_program->setUniformValue("matrix", m_projectionMatrix); + m_program->setUniformValue("matrixWorld", QMatrix4x4(painter->combinedTransform())); + + if (!meego_gst_video_texture_bind_frame(sink, GL_TEXTURE_EXTERNAL_OES, frame)) { + qDebug() << "Failed to bind frame"; + m_program->release(); + return; + } + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &m_vertexCoords); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, &m_texCoords); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + if (!meego_gst_video_texture_bind_frame(sink, GL_TEXTURE_EXTERNAL_OES, -1)) { + qDebug() << "Failed to unbind frame"; + } + + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); + + m_program->release(); + + // We are not using fences. + // TODO: make it configurable. + meego_gst_video_texture_release_frame(sink, frame, 0); +} + +void QtCamViewfinderRendererMeeGo::calculateCoords() { + if (!m_size.isValid() || !m_videoSize.isValid()) { + return; + } + + QRectF area = renderArea(); + + qreal leftMargin = area.x(); + qreal topMargin = area.y(); + QSizeF renderSize = area.size(); + + m_vertexCoords[0] = leftMargin; + m_vertexCoords[1] = topMargin + renderSize.height(); + + m_vertexCoords[2] = renderSize.width() + leftMargin; + m_vertexCoords[3] = topMargin + renderSize.height(); + + m_vertexCoords[4] = renderSize.width() + leftMargin; + m_vertexCoords[5] = topMargin; + + m_vertexCoords[6] = leftMargin; + m_vertexCoords[7] = topMargin; +} + +QRectF QtCamViewfinderRendererMeeGo::renderArea() { + if (!m_renderArea.isNull()) { + return m_renderArea; + } + + QSizeF renderSize = m_videoSize; + renderSize.scale(m_size, Qt::KeepAspectRatio); + + qreal leftMargin = (m_size.width() - renderSize.width())/2.0; + qreal topMargin = (m_size.height() - renderSize.height())/2.0; + + m_renderArea = QRectF(QPointF(leftMargin, topMargin), renderSize); + + return m_renderArea; +} + +void QtCamViewfinderRendererMeeGo::setVideoSize(const QSizeF& size) { + if (size == m_videoSize) { + return; + } + + m_videoSize = size; + + m_renderArea = QRectF(); + + calculateCoords(); + + m_needsInit = true; +} diff --git a/lib/qtcamviewfinderrenderermeego.h b/lib/qtcamviewfinderrenderermeego.h new file mode 100644 index 0000000..0ccfbf6 --- /dev/null +++ b/lib/qtcamviewfinderrenderermeego.h @@ -0,0 +1,57 @@ +// -*- c++ -*- + +#ifndef QT_CAM_VIEWFINDER_RENDERER_MEEGO_H +#define QT_CAM_VIEWFINDER_RENDERER_MEEGO_H + +#include "qtcamviewfinderrenderer.h" +#include +#include +#include +#include + +class QGLShaderProgram; + +class QtCamViewfinderRendererMeeGo : public QtCamViewfinderRenderer { + Q_OBJECT + +public: + Q_INVOKABLE QtCamViewfinderRendererMeeGo(QtCamConfig *config, QObject *parent = 0); + + ~QtCamViewfinderRendererMeeGo(); + + virtual void paint(QPainter *painter); + virtual void resize(const QSizeF& size); + virtual void reset(); + virtual GstElement *sinkElement(); + +private slots: + void setVideoSize(const QSizeF& size); + +private: + static void frame_ready(GstElement *sink, int frame, QtCamViewfinderRendererMeeGo *r); + static void sink_notify(QtCamViewfinderRendererMeeGo *q, GObject *object, gboolean is_last_ref); + static void sink_caps_changed(GObject *obj, GParamSpec *pspec, QtCamViewfinderRendererMeeGo *q); + + void calculateProjectionMatrix(const QRectF& rect); + void createProgram(); + void paintFrame(QPainter *painter, int frame); + void calculateCoords(); + QRectF renderArea(); + + QtCamConfig *m_conf; + GstElement *m_sink; + QMutex m_frameMutex; + int m_frame; + unsigned long m_id; + unsigned long m_notify; + bool m_needsInit; + QGLShaderProgram *m_program; + QMatrix4x4 m_projectionMatrix; + GLfloat m_vertexCoords[8]; + GLfloat m_texCoords[8]; + QSizeF m_size; + QSizeF m_videoSize; + QRectF m_renderArea; +}; + +#endif /* QT_CAM_VIEWFINDER_RENDERER_MEEGO_H */ -- 2.25.1