diff options
author | axis <qt-info@nokia.com> | 2009-04-24 11:34:15 (GMT) |
---|---|---|
committer | axis <qt-info@nokia.com> | 2009-04-24 11:34:15 (GMT) |
commit | 8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76 (patch) | |
tree | a17e1a767a89542ab59907462206d7dcf2e504b2 /src/3rdparty/phonon/gstreamer/glrenderer.cpp | |
download | Qt-8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76.zip Qt-8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76.tar.gz Qt-8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76.tar.bz2 |
Long live Qt for S60!
Diffstat (limited to 'src/3rdparty/phonon/gstreamer/glrenderer.cpp')
-rw-r--r-- | src/3rdparty/phonon/gstreamer/glrenderer.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/gstreamer/glrenderer.cpp b/src/3rdparty/phonon/gstreamer/glrenderer.cpp new file mode 100644 index 0000000..6cf3459 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/glrenderer.cpp @@ -0,0 +1,339 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <QtGui/QPainter> +#include <QtGui/QResizeEvent> + +#ifndef QT_NO_OPENGL + +#include "common.h" +#include "message.h" +#include "mediaobject.h" +#include "qwidgetvideosink.h" +#include "glrenderer.h" +#include "qrgb.h" + +#if !defined(QT_OPENGL_ES) + +#include <gst/gst.h> + +// support old OpenGL installations (1.2) +// assume that if TEXTURE0 isn't defined, none are +#ifndef GL_TEXTURE0 +# define GL_TEXTURE0 0x84C0 +# define GL_TEXTURE1 0x84C1 +# define GL_TEXTURE2 0x84C2 +#endif + +QT_BEGIN_NAMESPACE + +static void frameRendered() +{ + static QString displayFps = qgetenv("PHONON_GST_FPS"); + if (displayFps.isEmpty()) + return; + + static int frames = 0; + static QTime lastTime = QTime::currentTime(); + QTime time = QTime::currentTime(); + + int delta = lastTime.msecsTo(time); + if (delta > 2000) { + printf("FPS: %f\n", 1000.0 * frames / qreal(delta)); + lastTime = time; + frames = 0; + } + + ++frames; +} + +namespace Phonon +{ +namespace Gstreamer +{ + +GLRenderer::GLRenderer(VideoWidget* videoWidget) : + AbstractRenderer(videoWidget) + , m_glWindow(0) +{ + videoWidget->backend()->logMessage("Creating OpenGL renderer"); + QGLFormat format = QGLFormat::defaultFormat(); + format.setSwapInterval(1); // Enable vertical sync on draw to avoid tearing + m_glWindow = new GLRenderWidgetImplementation(videoWidget, format); + + if ((m_videoSink = m_glWindow->createVideoSink())) { //if ((m_videoSink = m_glWindow->createVideoSink())) { + gst_object_ref (GST_OBJECT (m_videoSink)); //Take ownership + gst_object_sink (GST_OBJECT (m_videoSink)); + + QWidgetVideoSinkBase* sink = reinterpret_cast<QWidgetVideoSinkBase*>(m_videoSink); + // Let the videosink know which widget to direct frame updates to + sink->renderWidget = videoWidget; + } +} + +GLRenderer::~GLRenderer() +{ + if (m_videoSink) { + gst_object_unref (GST_OBJECT (m_videoSink)); + m_videoSink = 0; + } +} + + +bool GLRenderer::eventFilter(QEvent * event) +{ + if (event->type() == QEvent::User) { + NewFrameEvent *frameEvent= static_cast <NewFrameEvent *>(event); + m_glWindow->setNextFrame(frameEvent->frame, frameEvent->width, frameEvent->height); + return true; + } + else if (event->type() == QEvent::Resize) { + m_glWindow->setGeometry(m_videoWidget->geometry()); + return true; + } + return false; +} + +void GLRenderer::handleMediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()) { + case MediaNodeEvent::SourceChanged: + { + Q_ASSERT(m_glWindow); + m_glWindow->clearFrame(); + break; + } + default: + break; + } +} + +GstElement* GLRenderWidgetImplementation::createVideoSink() +{ + if (hasYUVSupport()) + return GST_ELEMENT(g_object_new(get_type_YUV(), NULL)); + return 0; +} + +void GLRenderWidgetImplementation::setNextFrame(const QByteArray &array, int w, int h) +{ + if (m_videoWidget->root()->state() == Phonon::LoadingState) + return; + + m_frame = QImage(); + + if (hasYUVSupport()) + updateTexture(array, w, h); + else + m_frame = QImage((uchar *)array.constData(), w, h, QImage::Format_RGB32); + + m_array = array; + m_width = w; + m_height = h; + + update(); +} + +void GLRenderWidgetImplementation::clearFrame() +{ + m_frame = QImage(); + m_array = QByteArray(); + update(); +} + +bool GLRenderWidgetImplementation::hasYUVSupport() const +{ + return m_yuvSupport; +} + +static QImage convertFromYUV(const QByteArray &array, int w, int h) +{ + QImage result(w, h, QImage::Format_RGB32); + + // TODO: bilinearly interpolate the U and V channels for better result + + for (int y = 0; y < h; ++y) { + uint *sp = (uint *)result.scanLine(y); + + const uchar *yp = (const uchar *)(array.constData() + y * w); + const uchar *up = (const uchar *)(array.constData() + w * h + (y/2)*(w/2)); + const uchar *vp = (const uchar *)(array.constData() + w * h * 5/4 + (y/2)*(w/2)); + + for (int x = 0; x < w; ++x) { + const int sy = *yp; + const int su = *up; + const int sv = *vp; + + const int R = int(1.164 * (sy - 16) + 1.596 * (sv - 128)); + const int G = int(1.164 * (sy - 16) - 0.813 * (sv - 128) - 0.391 * (su - 128)); + const int B = int(1.164 * (sy - 16) + 2.018 * (su - 128)); + + *sp = qRgb(qBound(0, R, 255), + qBound(0, G, 255), + qBound(0, B, 255)); + + ++yp; + ++sp; + if (x & 1) { + ++up; + ++vp; + } + } + } + return result; +} + +const QImage &GLRenderWidgetImplementation::currentFrame() const +{ + if (m_frame.isNull() && !m_array.isNull()) + m_frame = convertFromYUV(m_array, m_width, m_height); + + return m_frame; +} + +#ifndef GL_FRAGMENT_PROGRAM_ARB +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#endif + +// arbfp1 fragment program for converting yuv to rgb +const char *const yuvToRgb = + "!!ARBfp1.0" + "PARAM c[3] = { { 0.5, 0.0625 }," + "{ 1.164, 0, 1.596, 2.0179999 }," + "{ 1.164, -0.391, -0.81300002 } };" + "TEMP R0;" + "TEMP R1;" + "TEX R0.x, fragment.texcoord[0], texture[2], 2D;" + "ADD R1.z, R0.x, -c[0].x;" + "TEX R1.x, fragment.texcoord[0], texture[0], 2D;" + "TEX R0.x, fragment.texcoord[0], texture[1], 2D;" + "ADD R1.x, R1, -c[0].y;" + "ADD R1.y, R0.x, -c[0].x;" + "DP3 result.color.x, R1, c[1];" + "DP3 result.color.y, R1, c[2];" + "DP3 result.color.z, R1, c[1].xwyw;" + "END"; + +GLRenderWidgetImplementation::GLRenderWidgetImplementation(VideoWidget*videoWidget, const QGLFormat &format) : + QGLWidget(format, videoWidget) + , m_program(0) + , m_yuvSupport(false) + , m_videoWidget(videoWidget) +{ + makeCurrent(); + glGenTextures(3, m_texture); + + glProgramStringARB = (_glProgramStringARB) context()->getProcAddress(QLatin1String("glProgramStringARB")); + glBindProgramARB = (_glBindProgramARB) context()->getProcAddress(QLatin1String("glBindProgramARB")); + glDeleteProgramsARB = (_glDeleteProgramsARB) context()->getProcAddress(QLatin1String("glDeleteProgramsARB")); + glGenProgramsARB = (_glGenProgramsARB) context()->getProcAddress(QLatin1String("glGenProgramsARB")); + glActiveTexture = (_glActiveTexture) context()->getProcAddress(QLatin1String("glActiveTexture")); + + m_hasPrograms = glProgramStringARB && glBindProgramARB && glDeleteProgramsARB && glGenProgramsARB && glActiveTexture; + + if (m_hasPrograms) { + glGenProgramsARB(1, &m_program); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program); + + const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(yuvToRgb); + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + int(strlen(yuvToRgb)), gl_src); + + if (glGetError() != GL_NO_ERROR) { + glDeleteProgramsARB(1, &m_program); + m_hasPrograms = false; + } else { + m_yuvSupport = true; + } + } + + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + setPalette(palette); + setAutoFillBackground(true); + // Videowidget allways have this property to allow hiding the mouse cursor + setMouseTracking(true); +} + +void GLRenderWidgetImplementation::updateTexture(const QByteArray &array, int width, int height) +{ + m_width = width; + m_height = height; + + makeCurrent(); + + int w[3] = { width, width/2, width/2 }; + int h[3] = { height, height/2, height/2 }; + int offs[3] = { 0, width*height, width*height*5/4 }; + + for (int i = 0; i < 3; ++i) { + glBindTexture(GL_TEXTURE_2D, m_texture[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w[i], h[i], 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, array.data() + offs[i]); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } +} + +void GLRenderWidgetImplementation::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + m_drawFrameRect = m_videoWidget->calculateDrawFrameRect(); + if (m_yuvSupport && frameIsSet()) { + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program); + const float tx_array[] = { 0, 0, 1, 0, 1, 1, 0, 1}; + const QRectF r = drawFrameRect(); + + const float v_array[] = { r.left(), r.top(), r.right(), r.top(), r.right(), r.bottom(), r.left(), r.bottom() }; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_texture[0]); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_texture[1]); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_texture[2]); + glActiveTexture(GL_TEXTURE0); + + glVertexPointer(2, GL_FLOAT, 0, v_array); + glTexCoordPointer(2, GL_FLOAT, 0, tx_array); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDrawArrays(GL_QUADS, 0, 4); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_FRAGMENT_PROGRAM_ARB); + } else { + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.drawImage(drawFrameRect(), currentFrame()); + } + + frameRendered(); +} +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // QT_OPENGL_ES +#endif // QT_NO_OPENGL |