1 #include "qtcamviewfinderrenderermeego.h"
3 #include <gst/video/video.h>
5 #include "qtcamconfig.h"
9 #include <QGLShaderProgram>
10 #include <gst/interfaces/meegovideotexture.h>
12 QT_CAM_VIEWFINDER_RENDERER(RENDERER_TYPE_MEEGO, QtCamViewfinderRendererMeeGo);
14 #define GL_TEXTURE_EXTERNAL_OES 0x8060
16 static const QString FRAGMENT_SHADER = ""
17 "#extension GL_OES_EGL_image_external: enable\n"
18 "uniform samplerExternalOES texture0;"
19 "varying lowp vec2 fragTexCoord;"
21 " gl_FragColor = texture2D(texture0, fragTexCoord);"
25 static const QString VERTEX_SHADER = ""
26 "attribute highp vec4 inputVertex;"
27 "attribute lowp vec2 textureCoord;"
28 "uniform highp mat4 matrix;"
29 "uniform highp mat4 matrixWorld;"
30 "varying lowp vec2 fragTexCoord;"
33 " gl_Position = matrix * matrixWorld * inputVertex;"
34 " fragTexCoord = textureCoord;"
38 QtCamViewfinderRendererMeeGo::QtCamViewfinderRendererMeeGo(QtCamConfig *config,
40 QtCamViewfinderRenderer(config, parent),
49 // Texture coordinates:
50 // Reversed because of Qt reversed coordinate system
51 m_texCoords[0] = 0; m_texCoords[1] = 1;
52 m_texCoords[2] = 1; m_texCoords[3] = 1;
53 m_texCoords[4] = 1; m_texCoords[5] = 0;
54 m_texCoords[6] = 0; m_texCoords[7] = 0;
56 bzero(&m_vertexCoords, 8);
59 QtCamViewfinderRendererMeeGo::~QtCamViewfinderRendererMeeGo() {
61 g_signal_handler_disconnect(m_sink, m_id);
62 g_signal_handler_disconnect(m_sink, m_notify);
63 g_object_remove_toggle_ref(G_OBJECT(m_sink), (GToggleNotify)sink_notify, this);
68 void QtCamViewfinderRendererMeeGo::paint(QPainter *painter) {
69 QMutexLocker locker(&m_frameMutex);
74 painter->beginNativePainting();
77 calculateProjectionMatrix(painter->viewport());
82 // Program will be created if needed and will never be deleted even
83 // if attaching the shaders fail.
87 paintFrame(painter, m_frame);
89 painter->endNativePainting();
92 void QtCamViewfinderRendererMeeGo::resize(const QSizeF& size) {
99 m_renderArea = QRectF();
103 // TODO: this will destroy everything
104 // but we need a way to reset the viewport and the transformation matrix only.
108 void QtCamViewfinderRendererMeeGo::reset() {
112 GstElement *QtCamViewfinderRendererMeeGo::sinkElement() {
113 if (!QGLContext::currentContext()) {
114 qCritical() << "Cannot create the GStreamer element without an OpenGL context.";
119 m_sink = gst_element_factory_make(m_conf->viewfinderSink().toAscii().data(),
120 "QtCamViewfinderRendererMeeGoSink");
122 qCritical() << "Failed to create" << m_conf->viewfinderSink();
126 g_object_add_toggle_ref(G_OBJECT(m_sink), (GToggleNotify)sink_notify, this);
129 // Displa can be replaced with a null pointer.
130 // We all know that the sink used for Harmattan ignores the x-display property ;-)
132 Display *d = QX11Info::display();
133 g_object_set(G_OBJECT(m_sink), "x-display", d, "use-framebuffer-memory", TRUE, NULL);
135 EGLDisplay dpy = eglGetDisplay((EGLNativeDisplayType)d);
136 EGLContext context = eglGetCurrentContext();
138 g_object_set(G_OBJECT(m_sink), "egl-display", dpy, "egl-context", context, NULL);
140 m_id = g_signal_connect(G_OBJECT(m_sink), "frame-ready", G_CALLBACK(frame_ready), this);
142 GstPad *pad = gst_element_get_static_pad(m_sink, "sink");
143 m_notify = g_signal_connect(G_OBJECT(pad), "notify::caps",
144 G_CALLBACK(sink_caps_changed), this);
145 gst_object_unref(pad);
150 void QtCamViewfinderRendererMeeGo::frame_ready(GstElement *sink, int frame,
151 QtCamViewfinderRendererMeeGo *r) {
155 r->m_frameMutex.lock();
157 r->m_frameMutex.unlock();
159 QMetaObject::invokeMethod(r, "updateRequested", Qt::QueuedConnection);
162 void QtCamViewfinderRendererMeeGo::sink_notify(QtCamViewfinderRendererMeeGo *q,
163 GObject *object, gboolean is_last_ref) {
166 g_signal_handler_disconnect(q->m_sink, q->m_id);
167 g_object_remove_toggle_ref(G_OBJECT(q->m_sink), (GToggleNotify)sink_notify, q);
172 void QtCamViewfinderRendererMeeGo::sink_caps_changed(GObject *obj, GParamSpec *pspec,
173 QtCamViewfinderRendererMeeGo *q) {
178 if (obj && gst_video_get_size (GST_PAD(obj), &width, &height)) {
179 QMetaObject::invokeMethod(q, "setVideoSize", Qt::QueuedConnection,
180 Q_ARG(QSizeF, QSizeF(width, height)));
184 void QtCamViewfinderRendererMeeGo::calculateProjectionMatrix(const QRectF& rect) {
185 m_projectionMatrix = QMatrix4x4();
186 m_projectionMatrix.ortho(rect);
189 void QtCamViewfinderRendererMeeGo::createProgram() {
194 m_program = new QGLShaderProgram(this);
196 if (!m_program->addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER)) {
197 qCritical() << "Failed to add vertex shader";
201 if (!m_program->addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER)) {
202 qCritical() << "Failed to add fragment shader";
206 m_program->bindAttributeLocation("inputVertex", 0);
207 m_program->bindAttributeLocation("textureCoord", 1);
209 if (!m_program->link()) {
210 qCritical() << "Failed to link program!";
214 if (!m_program->bind()) {
215 qCritical() << "Failed to bind program";
219 m_program->setUniformValue("texture0", 0); // texture UNIT 0
220 m_program->release();
223 void QtCamViewfinderRendererMeeGo::paintFrame(QPainter *painter, int frame) {
228 // TODO: sometimes there is an assertion.
230 MeegoGstVideoTexture *sink = MEEGO_GST_VIDEO_TEXTURE(m_sink);
231 if (!meego_gst_video_texture_acquire_frame(sink, frame)) {
232 qDebug() << "Failed to acquire frame";
238 m_program->setUniformValue("matrix", m_projectionMatrix);
239 m_program->setUniformValue("matrixWorld", QMatrix4x4(painter->combinedTransform()));
241 if (!meego_gst_video_texture_bind_frame(sink, GL_TEXTURE_EXTERNAL_OES, frame)) {
242 qDebug() << "Failed to bind frame";
243 m_program->release();
247 glEnableVertexAttribArray(0);
248 glEnableVertexAttribArray(1);
250 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &m_vertexCoords);
251 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, &m_texCoords);
253 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
255 if (!meego_gst_video_texture_bind_frame(sink, GL_TEXTURE_EXTERNAL_OES, -1)) {
256 qDebug() << "Failed to unbind frame";
259 glDisableVertexAttribArray(1);
260 glDisableVertexAttribArray(0);
262 m_program->release();
264 // We are not using fences.
265 // TODO: make it configurable.
266 meego_gst_video_texture_release_frame(sink, frame, 0);
269 void QtCamViewfinderRendererMeeGo::calculateCoords() {
270 if (!m_size.isValid() || !m_videoSize.isValid()) {
274 QRectF area = renderArea();
276 qreal leftMargin = area.x();
277 qreal topMargin = area.y();
278 QSizeF renderSize = area.size();
280 m_vertexCoords[0] = leftMargin;
281 m_vertexCoords[1] = topMargin + renderSize.height();
283 m_vertexCoords[2] = renderSize.width() + leftMargin;
284 m_vertexCoords[3] = topMargin + renderSize.height();
286 m_vertexCoords[4] = renderSize.width() + leftMargin;
287 m_vertexCoords[5] = topMargin;
289 m_vertexCoords[6] = leftMargin;
290 m_vertexCoords[7] = topMargin;
293 QRectF QtCamViewfinderRendererMeeGo::renderArea() {
294 if (!m_renderArea.isNull()) {
298 QSizeF renderSize = m_videoSize;
299 renderSize.scale(m_size, Qt::KeepAspectRatio);
301 qreal leftMargin = (m_size.width() - renderSize.width())/2.0;
302 qreal topMargin = (m_size.height() - renderSize.height())/2.0;
304 m_renderArea = QRectF(QPointF(leftMargin, topMargin), renderSize);
309 void QtCamViewfinderRendererMeeGo::setVideoSize(const QSizeF& size) {
310 if (size == m_videoSize) {
316 m_renderArea = QRectF();