summaryrefslogtreecommitdiffstats
path: root/kopete/libkopete/avdevice/qvideostream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/libkopete/avdevice/qvideostream.cpp')
-rw-r--r--kopete/libkopete/avdevice/qvideostream.cpp731
1 files changed, 731 insertions, 0 deletions
diff --git a/kopete/libkopete/avdevice/qvideostream.cpp b/kopete/libkopete/avdevice/qvideostream.cpp
new file mode 100644
index 00000000..cd7aafc2
--- /dev/null
+++ b/kopete/libkopete/avdevice/qvideostream.cpp
@@ -0,0 +1,731 @@
+/*
+ *
+ * Copyright (C) 2002 George Staikos <[email protected]>
+ * 2004 Dirk Ziegelmeier <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "qvideostream.h"
+#include <qevent.h>
+#include <qimage.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include "kxv.h"
+
+#include <sys/types.h>
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XSHM
+extern "C" {
+#include <sys/shm.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+}
+#endif
+
+#ifdef HAVE_GL
+class QVideoStreamGLWidget : public QGLWidget
+{
+public:
+ QVideoStreamGLWidget(QWidget* parent = 0, const char* name = 0);
+ virtual ~QVideoStreamGLWidget();
+
+ void setInputSize(const QSize& sz);
+ void display(const unsigned char *const img, int x, int y, int sw, int sh);
+
+private:
+ virtual void resizeGL(int w, int h);
+ void initializeGL();
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+ void calc(QPoint& p, QPoint& v);
+
+
+ QSize _inputSize;
+ GLuint _tex;
+ int _tw, _th;
+ QWidget* _w;
+ int _maxGL;
+ QSize _sz;
+ bool _glfun;
+ QPoint _ul, _ur, _ll, _lr;
+ QPoint _vul, _vur, _vll, _vlr;
+ QTimer* _glfunTimer;
+};
+#endif
+
+class QVideoStreamPrivate
+{
+public:
+ QVideoStreamPrivate();
+ ~QVideoStreamPrivate();
+ KXv *xvHandle;
+ KXvDevice *xvdev;
+ XImage *xim;
+ GC gc;
+#ifdef HAVE_GL
+ QVideoStreamGLWidget* glwidget;
+#endif
+#ifdef HAVE_XSHM
+ XShmSegmentInfo shmh;
+#endif
+};
+
+QVideoStreamPrivate::QVideoStreamPrivate()
+{
+ xvHandle = 0;
+ xim = 0;
+}
+
+QVideoStreamPrivate::~QVideoStreamPrivate()
+{
+ delete xvHandle;
+}
+
+QVideoStream::QVideoStream(QWidget *widget, const char* name)
+ : QObject(widget, name),
+ d(new QVideoStreamPrivate),
+ _w(widget),
+ _methods(METHOD_NONE),
+ _method(METHOD_NONE),
+ _format(FORMAT_NONE),
+ _init(false)
+{
+ int dummy;
+ unsigned int dummy2;
+ findDisplayProperties(_xFormat, dummy, dummy2, dummy);
+
+ _methods = (VideoMethod)(_methods | METHOD_X11);
+
+#ifdef HAVE_XSHM
+ if (XShmQueryExtension(_w->x11Display())) {
+ _methods = (VideoMethod)(_methods | METHOD_XSHM);
+ }
+#endif
+
+ if (KXv::haveXv()) {
+ _methods = (VideoMethod)(_methods | METHOD_XV);
+#ifdef HAVE_XSHM
+ _methods = (VideoMethod)(_methods | METHOD_XVSHM);
+#endif
+ }
+
+#ifdef HAVE_GL
+ if (QGLFormat::hasOpenGL()) {
+ _methods = (VideoMethod)(_methods | METHOD_GL);
+ }
+#endif
+
+ d->gc = XCreateGC(_w->x11Display(), _w->winId(), 0, NULL);
+}
+
+QVideoStream::~QVideoStream()
+{
+ deInit();
+ XFreeGC(_w->x11Display(), d->gc);
+ delete d;
+}
+
+void QVideoStream::deInit()
+{
+ if (!_init)
+ return;
+
+ _init = false;
+ _format = FORMAT_NONE;
+
+ Q_ASSERT(_methods & _method);
+ if (!(_methods & _method))
+ return;
+
+ switch (_method) {
+ case METHOD_XSHM:
+#ifdef HAVE_XSHM
+ XShmDetach(_w->x11Display(), &(d->shmh));
+ XDestroyImage(d->xim);
+ d->xim = 0;
+ shmdt(d->shmh.shmaddr);
+#endif
+ break;
+ case METHOD_X11:
+ delete[] d->xim->data;
+ d->xim->data = 0;
+ XDestroyImage(d->xim);
+ d->xim = 0;
+ break;
+ case METHOD_XVSHM:
+ case METHOD_XV:
+ delete d->xvHandle;
+ d->xvHandle = 0;
+ break;
+ case METHOD_GL:
+#ifdef HAVE_GL
+ delete d->glwidget;
+#endif
+ break;
+ default:
+ Q_ASSERT(0);
+ return;
+ }
+}
+
+void QVideoStream::init()
+{
+ Q_ASSERT(_methods & _method);
+ if (!(_methods & _method))
+ return;
+
+ switch (_method) {
+ case METHOD_XSHM:
+ {
+#ifdef HAVE_XSHM
+ if ( !_inputSize.isValid() ) {
+ kdWarning() << "QVideoStream::init() (XSHM): Unable to initialize due to invalid input size." << endl;
+ return;
+ }
+
+ memset(&(d->shmh), 0, sizeof(XShmSegmentInfo));
+ d->xim = XShmCreateImage(_w->x11Display(),
+ (Visual*)_w->x11Visual(),
+ _w->x11Depth(),
+ ZPixmap, 0, &(d->shmh),
+ _inputSize.width(),
+ _inputSize.height());
+ d->shmh.shmid = shmget(IPC_PRIVATE,
+ d->xim->bytes_per_line*d->xim->height,
+ IPC_CREAT|0600);
+ d->shmh.shmaddr = (char *)shmat(d->shmh.shmid, 0, 0);
+ d->xim->data = (char*)d->shmh.shmaddr;
+ d->shmh.readOnly = False;
+ Status s = XShmAttach(_w->x11Display(), &(d->shmh));
+ if (s) {
+ XSync(_w->x11Display(), False);
+ shmctl(d->shmh.shmid, IPC_RMID, 0);
+ _format = _xFormat;
+ _init = true;
+ } else {
+ kdWarning() << "XShmAttach failed!" << endl;
+ XDestroyImage(d->xim);
+ d->xim = 0;
+ shmdt(d->shmh.shmaddr);
+ }
+#endif
+ }
+ break;
+ case METHOD_X11:
+ if ( !_inputSize.isValid() ) {
+ kdWarning() << "QVideoStream::init() (X11): Unable to initialize due to invalid input size." << endl;
+ return;
+ }
+
+ d->xim = XCreateImage(_w->x11Display(),
+ (Visual*)_w->x11Visual(),
+ _w->x11Depth(),
+ ZPixmap, 0, 0,
+ _inputSize.width(),
+ _inputSize.height(),
+ 32, 0);
+
+ d->xim->data = new char[d->xim->bytes_per_line*_inputSize.height()];
+ _format = _xFormat;
+ _init = true;
+ break;
+ case METHOD_XVSHM:
+ case METHOD_XV:
+ {
+ if (d->xvHandle)
+ delete d->xvHandle;
+
+ d->xvHandle = KXv::connect(_w->winId());
+ KXvDeviceList& xvdl(d->xvHandle->devices());
+ KXvDevice *xvdev = NULL;
+
+ for (xvdev = xvdl.first(); xvdev; xvdev = xvdl.next()) {
+ if (xvdev->isImageBackend() &&
+ xvdev->supportsWidget(_w)) {
+ d->xvdev = xvdev;
+ d->xvdev->useShm(_method == METHOD_XVSHM);
+ _format = FORMAT_YUYV;
+ _init = true;
+ break;
+ }
+ }
+
+ if (!_init) {
+ delete d->xvHandle;
+ d->xvHandle = 0;
+ }
+ }
+ break;
+ case METHOD_GL:
+#ifdef HAVE_GL
+ d->glwidget = new QVideoStreamGLWidget(_w, "QVideoStreamGLWidget");
+ d->glwidget->resize(_w->width(), _w->height());
+ d->glwidget->show();
+ _format = FORMAT_BGR24;
+ _init = true;
+#endif
+ break;
+ default:
+ Q_ASSERT(0);
+ return;
+ }
+}
+
+bool QVideoStream::haveMethod(VideoMethod method) const
+{
+ return _methods & method;
+}
+
+QVideo::VideoMethod QVideoStream::method() const
+{
+ return _method;
+}
+
+QVideo::VideoMethod QVideoStream::setMethod(VideoMethod method)
+{
+ if (_methods & method) {
+ deInit();
+ _method = method;
+ init();
+ }
+
+ return _method;
+}
+
+QSize QVideoStream::maxSize() const
+{
+ return _size;
+}
+
+int QVideoStream::maxWidth() const
+{
+ return _size.width();
+}
+
+int QVideoStream::maxHeight() const
+{
+ return _size.height();
+}
+
+QSize QVideoStream::size() const
+{
+ return _size;
+}
+
+int QVideoStream::width() const
+{
+ return _size.width();
+}
+
+int QVideoStream::height() const
+{
+ return _size.height();
+}
+
+QSize QVideoStream::setSize(const QSize& sz)
+{
+ _size = sz;
+ return _size;
+}
+
+int QVideoStream::setWidth(int width)
+{
+ if (width < 0)
+ width = 0;
+ if (width > maxWidth())
+ width = maxWidth();
+ _size.setWidth(width);
+ return _size.width();
+}
+
+int QVideoStream::setHeight(int height)
+{
+ if (height < 0)
+ height = 0;
+ if (height > maxHeight())
+ height = maxHeight();
+ _size.setHeight(height);
+ return _size.height();
+}
+
+QSize QVideoStream::inputSize() const
+{
+ return _inputSize;
+}
+
+int QVideoStream::inputWidth() const
+{
+ return _inputSize.width();
+}
+
+int QVideoStream::inputHeight() const
+{
+ return _inputSize.height();
+}
+
+QSize QVideoStream::setInputSize(const QSize& sz)
+{
+ if (sz == _inputSize)
+ return _inputSize;
+ _inputSize = sz;
+ if (_method & (METHOD_XSHM | METHOD_X11)) {
+ deInit();
+ init();
+ }
+#ifdef HAVE_GL
+ if (_method & METHOD_GL) {
+ d->glwidget->setInputSize(_inputSize);
+ }
+#endif
+ return _inputSize;
+}
+
+int QVideoStream::setInputWidth(int width)
+{
+ if (width == _inputSize.width())
+ return _inputSize.width();
+ _inputSize.setWidth(width);
+ if (_method & (METHOD_XSHM | METHOD_X11)) {
+ deInit();
+ init();
+ }
+#ifdef HAVE_GL
+ if (_method & METHOD_GL) {
+ d->glwidget->setInputSize(_inputSize);
+ }
+#endif
+ return _inputSize.width();
+}
+
+int QVideoStream::setInputHeight(int height)
+{
+ if (height == _inputSize.height())
+ return _inputSize.height();
+ _inputSize.setHeight(height);
+ if (_method & (METHOD_XSHM | METHOD_X11)) {
+ deInit();
+ init();
+ }
+#ifdef HAVE_GL
+ if (_method & METHOD_GL) {
+ d->glwidget->setInputSize(_inputSize);
+ }
+#endif
+ return _inputSize.height();
+}
+
+bool QVideoStream::supportsFormat(VideoMethod method, ImageFormat format)
+{
+ return (bool)(formatsForMethod(method) & format);
+}
+
+QVideo::ImageFormat QVideoStream::formatsForMethod(VideoMethod method)
+{
+ switch(method) {
+ case METHOD_XSHM:
+ case METHOD_X11:
+ return _xFormat;
+ case METHOD_XV:
+ case METHOD_XVSHM:
+ return FORMAT_YUYV;
+ case METHOD_GL:
+ return FORMAT_BGR24;
+ default:
+ return FORMAT_NONE;
+ }
+}
+
+QVideo::ImageFormat QVideoStream::format() const
+{
+ return _format;
+}
+
+bool QVideoStream::setFormat(ImageFormat format)
+{
+ if(supportsFormat(_method, format)) {
+ _format = format;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int QVideoStream::displayFrame(const unsigned char *const img)
+{
+ return displayFrame(img, 0, 0, _inputSize.width(), _inputSize.height());
+}
+
+int QVideoStream::displayFrame(const unsigned char *const img, int x, int y, int sw, int sh)
+{
+ Q_ASSERT(_init);
+ if (!_init)
+ return -1;
+
+ Q_ASSERT(_methods & _method);
+ if (!(_methods & _method))
+ return -1;
+
+ switch (_method) {
+ case METHOD_XV:
+ case METHOD_XVSHM:
+ return d->xvdev->displayImage(_w, img,
+ _inputSize.width(), _inputSize.height(), x, y, sw, sh,
+ _size.width(), _size.height());
+ break;
+ case METHOD_XSHM:
+#ifdef HAVE_XSHM
+ memcpy(d->xim->data,img,d->xim->bytes_per_line*d->xim->height);
+ XShmPutImage(_w->x11Display(), _w->winId(), d->gc, d->xim,
+ x, y,
+ 0, 0,
+ sw, sh,
+ 0);
+ XSync(_w->x11Display(), False);
+ break;
+#else
+ return -1;
+#endif
+ case METHOD_X11:
+ memcpy(d->xim->data, img, d->xim->bytes_per_line*d->xim->height);
+ XPutImage(_w->x11Display(), _w->winId(), d->gc, d->xim,
+ x, y,
+ 0, 0,
+ sw, sh);
+ XSync(_w->x11Display(), False);
+ break;
+ case METHOD_GL:
+#ifdef HAVE_GL
+ d->glwidget->display(img, x, y, sw, sh);
+#endif
+ break;
+ default:
+ Q_ASSERT(0);
+ return -1;
+ }
+
+ return 0;
+}
+
+QVideoStream& QVideoStream::operator<<(const unsigned char *const img)
+{
+ displayFrame(img);
+ return *this;
+}
+
+// ---------------------------------------------------------------------------------------
+#ifdef HAVE_GL
+
+QVideoStreamGLWidget::QVideoStreamGLWidget(QWidget* parent, const char* name)
+ : QGLWidget(QGLFormat(QGL::DoubleBuffer | QGL::Rgba | QGL::DirectRendering), parent, name),
+ _tex(0),
+ _w(parent),
+ _glfun(false)
+{
+ kdDebug() << "QVideoStreamGLWidget::QVideoStreamGLWidget()" << endl;
+
+ connect(_w, SIGNAL(resized(int, int)),
+ this, SLOT(resize(int, int)));
+
+ topLevelWidget()->installEventFilter(this);
+ _glfunTimer = new QTimer();
+}
+
+QVideoStreamGLWidget::~QVideoStreamGLWidget()
+{
+ kdDebug() << "QVideoStreamGLWidget::~QVideoStreamGLWidget()" << endl;
+ delete _glfunTimer;
+
+ makeCurrent();
+ if(_tex != 0) {
+ glDeleteTextures(1, &_tex);
+ }
+}
+
+bool QVideoStreamGLWidget::eventFilter(QObject*, QEvent* e)
+{
+ // For some reason, KeyPress does not work (yields 2), QEvent::KeyPress is unknown... What the f...????
+ // I am too lazy to scan the header files for the reason.
+ if(e->type() == 6) {
+ QKeyEvent* ke = static_cast<QKeyEvent*>(e);
+ if(ke->key() == Qt::Key_Pause) {
+ _glfunTimer->start(500, true);
+ } else if (_glfunTimer->isActive() && (ke->key() == Qt::Key_Escape)) {
+ _glfun = !_glfun;
+ }
+ }
+ return false;
+}
+
+void QVideoStreamGLWidget::initializeGL()
+{
+ kdDebug() << "QVideoStreamGLWidget::initializeGL()" << endl;
+ setAutoBufferSwap(false);
+
+ QGLFormat f = format();
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxGL);
+ kdDebug() << "OpenGL capabilities (* = required):" << endl;
+ kdDebug() << " Valid context*: " << isValid() << endl;
+ kdDebug() << " DoubleBuffer*: " << f.doubleBuffer() << endl;
+ kdDebug() << " Depth: " << f.depth() << endl;
+ kdDebug() << " RGBA*: " << f.rgba() << endl;
+ kdDebug() << " Alpha: " << f.alpha() << endl;
+ kdDebug() << " Accum: " << f.accum() << endl;
+ kdDebug() << " Stencil: " << f.stencil() << endl;
+ kdDebug() << " Stereo: " << f.stereo() << endl;
+ kdDebug() << " DirectRendering*: " << f.directRendering() << endl;
+ kdDebug() << " Overlay: " << f.hasOverlay() << endl;
+ kdDebug() << " Plane: " << f.plane() << endl;
+ kdDebug() << " MAX_TEXTURE_SIZE: " << _maxGL << endl;
+
+ qglClearColor(Qt::black);
+ glShadeModel(GL_FLAT);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ _vul = QPoint( 4, 10);
+ _vur = QPoint(-8, 4);
+ _vll = QPoint(10, -4);
+ _vlr = QPoint(-8, -10);
+}
+
+void QVideoStreamGLWidget::resizeGL(int w, int h)
+{
+ _sz = QSize(w, h);
+
+ glViewport(0, 0, w, h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0.0, w, 0.0, h, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ _ul = QPoint(0, 0);
+ _ur = QPoint(w, 0);
+ _ll = QPoint(0, h);
+ _lr = QPoint(w, h);
+}
+
+void QVideoStreamGLWidget::setInputSize(const QSize& sz)
+{
+ makeCurrent();
+
+ _inputSize = sz;
+ int iw = _inputSize.width();
+ int ih = _inputSize.height();
+
+ if ( (iw > _maxGL) || (ih > _maxGL) ) {
+ kdWarning() << "QVideoStreamGLWidget::setInputSize(): Texture too large! maxGL: " << _maxGL << endl;
+ return;
+ }
+
+ // textures have power-of-two x,y dimensions
+ int i;
+ for (i = 0; iw >= (1 << i); i++)
+ ;
+ _tw = (1 << i);
+ for (i = 0; ih >= (1 << i); i++)
+ ;
+ _th = (1 << i);
+
+ // Generate texture
+ if(_tex != 0) {
+ glDeleteTextures(1, &_tex);
+ }
+ glGenTextures(1, &_tex);
+ glBindTexture(GL_TEXTURE_2D, _tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ // Blank texture
+ char* dummy = new char[_tw*_th*4];
+ memset(dummy, 128, _tw*_th*4);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _tw, _th, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, dummy);
+ delete[] dummy;
+}
+
+void QVideoStreamGLWidget::display(const unsigned char *const img, int x, int y, int sw, int sh)
+{
+ makeCurrent();
+
+ // FIXME: Endianess - also support GL_RGB
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _inputSize.width(), _inputSize.height(),
+ GL_BGR, GL_UNSIGNED_BYTE, img);
+
+ // upper right coords
+ float ur_x = (float)(x + sw) / _tw;
+ float ur_y = (float)(y + sh) / _th;
+
+ // lower left coords
+ float ll_x = (float)(x) / _tw;
+ float ll_y = (float)(y) / _th;
+
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ glBindTexture(GL_TEXTURE_2D, _tex);
+ if (!_glfun) {
+ glBegin(GL_QUADS);
+ glTexCoord2f(ll_x, ur_y); glVertex2i(0, 0 );
+ glTexCoord2f(ll_x, ll_y); glVertex2i(0, _sz.height());
+ glTexCoord2f(ur_x, ll_y); glVertex2i(_sz.width(), _sz.height());
+ glTexCoord2f(ur_x, ur_y); glVertex2i(_sz.width(), 0 );
+ glEnd();
+ } else {
+ calc(_ul, _vul);
+ calc(_ur, _vur);
+ calc(_ll, _vll);
+ calc(_lr, _vlr);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, y); glVertex2i(_ul.x(), _ul.y());
+ glTexCoord2f(0, 0); glVertex2i(_ll.x(), _ll.y());
+ glTexCoord2f(x, 0); glVertex2i(_lr.x(), _lr.y());
+ glTexCoord2f(x, y); glVertex2i(_ur.x(), _ur.y());
+ glEnd();
+ }
+ swapBuffers();
+ glDisable(GL_TEXTURE_2D);
+}
+
+void QVideoStreamGLWidget::calc(QPoint& p, QPoint& v)
+{
+ p += v;
+
+ if(p.x() < 0) {
+ p.setX(-p.x());
+ v.setX(-v.x());
+ }
+ if(p.y() < 0) {
+ p.setY(-p.y());
+ v.setY(-v.y());
+ }
+ if(p.x() > _sz.width()) {
+ p.setX(_sz.width() - (p.x() - _sz.width()));
+ v.setX(-v.x());
+ }
+ if(p.y() > _sz.height()) {
+ p.setY(_sz.height() - (p.y() - _sz.height()));
+ v.setY(-v.y());
+ }
+}
+#endif
+
+#include "qvideostream.moc"