/* * KDE Xv interface * * Copyright (C) 2001 George Staikos (staikos@kde.org) * * 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif #include "kxv.h" #include #include #include #include #ifdef HAVE_XSHM extern "C" { #include #include } #endif #ifdef HAVE_LIBXV #include #include #endif #ifdef HAVE_LIBXVMC #include #include #endif KXv::KXv() { xv_adaptors = 0; _devs.setAutoDelete(true); } KXv::~KXv() { kdDebug() << "KXv::~KXv: Close Xv connection." << endl; _devs.clear(); #ifdef HAVE_LIBXV if (xv_adaptors > 0) XvFreeAdaptorInfo((XvAdaptorInfo *)xv_adaptor_info); #endif } KXvDeviceList& KXv::devices() { return _devs; } bool KXv::haveXv() { #ifndef HAVE_LIBXV return false; #else unsigned int tmp; if (Success != XvQueryExtension(tqt_xdisplay(), &tmp, &tmp, &tmp, &tmp, &tmp)) return false; return true; #endif } KXv* KXv::connect(Drawable d) { KXv *xvptr; xvptr = new KXv; if (!xvptr->init(d)) { kdDebug() << "KXv::connect: Xv init failed." << endl; delete xvptr; return NULL; } kdDebug() << "KXv::connect: Xv init completed." << endl; return xvptr; } bool KXv::init(Drawable d) { #ifndef HAVE_LIBXV return false; #else if (Success != XvQueryExtension(tqt_xdisplay(), &xv_version, &xv_release, &xv_request, &xv_event, &xv_error)) { kdWarning() << "KXv::init: Xv extension not available." << endl; return false; } #ifdef HAVE_LIBXVMC // Causes crashes for some people. // if (Success == XvMCQueryExtension(tqt_xdisplay(),0,0)) { // kdDebug() << "Found XvMC!" << endl; // } #endif if (Success != XvQueryAdaptors(tqt_xdisplay(), d, &xv_adaptors, (XvAdaptorInfo **)&xv_adaptor_info)) { // Note technically fatal... what to do? kdWarning() << "KXv::init: XvQueryAdaptors failed." << endl; } XvAdaptorInfo *ai = (XvAdaptorInfo *)xv_adaptor_info; for (unsigned int i = 0; i < xv_adaptors; i++) { KXvDevice *xvd = new KXvDevice; xvd->xv_type = ai[i].type; xvd->xv_port = ai[i].base_id; xvd->xv_name = ai[i].name; xvd->xv_adaptor = i; xvd->xv_nvisualformats = ai[i].num_formats; xvd->xv_visualformats = ai[i].formats; if (ai[i].type & XvInputMask && ai[i].type & XvVideoMask ) { kdDebug() << "KXv::init: Xv VideoMask port " << ai[i].base_id << " was found." << " Device is: " << ai[i].name << "." << endl; } if (ai[i].type & XvInputMask && ai[i].type & XvImageMask ) { kdDebug() << "KXv::init: Xv ImageMask port " << ai[i].base_id << " was found." << " Device is: " << ai[i].name << "." << endl; } if (xvd->init()) { _devs.append(xvd); } else { delete xvd; } } return true; #endif } bool KXvDevice::grabStill(TQImage* /*pix*/, int /*dw*/, int /*dh*/) { #ifndef HAVE_LIBXV return false; #else return false; #endif } int KXvDevice::displayImage(TQWidget *widget, const unsigned char *const data, int w, int h, int dw, int dh) { if (!widget) return -1; return displayImage(widget->winId(), data, w, h, 0, 0, w, h, dw, dh); } int KXvDevice::displayImage(TQWidget *widget, const unsigned char *const data, int w, int h, int x, int y, int sw, int sh, int dw, int dh) { if (!widget) return -1; return displayImage(widget->winId(), data, w, h, x, y, sw, sh, dw, dh); } int KXvDevice::displayImage(Window win, const unsigned char *const data, int w, int h, int dw, int dh) { return displayImage(win, data, w, h, 0, 0, w, h, dw, dh); } int KXvDevice::displayImage(Window win, const unsigned char *const data, int w, int h, int x, int y, int sw, int sh, int dw, int dh) { #ifndef HAVE_LIBXV return -1; #else Q_ASSERT(xv_port != -1); // Must be a video capable device! if (!(xv_type & XvImageMask) || !(xv_type & XvInputMask)) { kdWarning() << "KXvDevice::displayImage: This is not a video capable device." << endl; return -1; } if (xv_image_w != w || xv_image_h != h || !xv_image) rebuildImage(w, h, _shm); if (!xv_image) return -1; if (win != xv_last_win && xv_gc) { XFreeGC(tqt_xdisplay(), xv_gc); xv_gc = 0; } if (!xv_gc) { xv_last_win = win; xv_gc = XCreateGC(tqt_xdisplay(), win, 0, NULL); } int rc = 0; Q_ASSERT(xv_image); if (!_shm) { static_cast(xv_image)->data = (char *)const_cast(data); rc = XvPutImage(tqt_xdisplay(), xv_port, win, xv_gc, static_cast(xv_image), x, y, sw, sh, 0, 0, dw, dh); } else { #ifdef HAVE_XSHM memcpy(static_cast(xv_image)->data, data, static_cast(xv_image)->data_size); rc = XvShmPutImage(tqt_xdisplay(), xv_port, win, xv_gc, static_cast(xv_image), x, y, sw, sh, 0, 0, dw, dh, 0); #endif } XSync(tqt_xdisplay(), False); return rc; #endif } bool KXvDevice::startVideo(TQWidget *w, int dw, int dh) { if (!w) return false; return startVideo(w->winId(), dw, dh); } bool KXvDevice::startVideo(Window w, int dw, int dh) { #ifndef HAVE_LIBXV return false; #else int sx = 0, sy = 0, dx = 0, dy = 0, sw = dw, sh = dh; // Must be a video capable device! if (!(xv_type & XvVideoMask) || !(xv_type & XvInputMask)) { kdWarning() << "KXvDevice::startVideo: This is not a video capable device." << endl; return false; } if (videoStarted) stopVideo(); if (xv_port == -1) { kdWarning() << "KXvDevice::startVideo: No xv_port." << endl; return false; } if (w != xv_last_win && xv_gc) { XFreeGC(tqt_xdisplay(), xv_gc); xv_gc = 0; } if (!xv_gc) { xv_last_win = w; xv_gc = XCreateGC(tqt_xdisplay(), w, 0, NULL); } if (-1 != xv_encoding) { sw = ((XvEncodingInfo *)xv_encoding_info)[xv_encoding].width; sh = ((XvEncodingInfo *)xv_encoding_info)[xv_encoding].height; } // xawtv does this here: // ng_ratio_fixup(&dw, &dh, &dx, &dy); kdDebug() << "XvPutVideo: " << tqt_xdisplay() << " " << xv_port << " " << w << " " << xv_gc << " " << sx << " " << sy << " " << sw << " " << sh << " " << dx << " " << dy << " " << dw << " " << dh << endl; XvPutVideo(tqt_xdisplay(), xv_port, w, xv_gc, sx, sy, sw, sh, dx, dy, dw, dh); videoStarted = true; videoWindow = w; return true; #endif } bool KXvDevice::stopVideo() { #ifndef HAVE_LIBXV return false; #else if (!videoStarted) return true; if (xv_port == -1) { kdWarning() << "KXvDevice::stopVideo: No xv_port." << endl; return false; } XvStopVideo(tqt_xdisplay(), xv_port, videoWindow); videoStarted = false; return true; #endif } KXvDevice::KXvDevice() { xv_encoding_info = NULL; xv_formatvalues = NULL; xv_attr = NULL; xv_port = -1; xv_encoding = -1; xv_name = TQString(); xv_type = -1; xv_adaptor = -1; _shm = false; #ifdef HAVE_LIBXV xv_imageformat = 0x32595559; // FIXME (YUY2) #ifdef HAVE_XSHM if (!XShmQueryExtension(tqt_xdisplay())) { _haveShm = false; } else { _shm = true; _haveShm = true; } xv_shminfo = new XShmSegmentInfo; #else xv_shminfo = 0; #endif #endif xv_gc = 0; xv_last_win = 0; videoStarted = false; _attrs.setAutoDelete(true); xv_image = 0; xv_image_w = 320; xv_image_h = 200; } KXvDevice::~KXvDevice() { #ifdef HAVE_LIBXV _attrs.clear(); if (videoStarted) stopVideo(); if (xv_encoding_info) XvFreeEncodingInfo((XvEncodingInfo *)xv_encoding_info); XFree(xv_formatvalues); XFree(xv_attr); #ifdef HAVE_XSHM delete (XShmSegmentInfo*)xv_shminfo; #endif destroyImage(); #endif if (xv_gc) XFreeGC(tqt_xdisplay(), xv_gc); #ifdef HAVE_LIBXV if (xv_port != -1) XvUngrabPort(tqt_xdisplay(), xv_port, CurrentTime); #endif } bool KXvDevice::init() { #ifndef HAVE_LIBXV return false; #else assert(xv_port != -1); // make sure we were prepped by KXv already. if (XvGrabPort(tqt_xdisplay(), xv_port, CurrentTime)) { kdWarning() << "KXvDevice::init(): Unable to grab Xv port." << endl; return false; } if (Success != XvQueryEncodings(tqt_xdisplay(), xv_port, &xv_encodings, (XvEncodingInfo **)&xv_encoding_info)) { kdWarning() << "KXvDevice::init: Xv QueryEncodings failed. Dropping Xv support for this device." << endl; return false; } // Package the encodings up nicely for (unsigned int i = 0; i < xv_encodings; i++) { //kdDebug() << "Added encoding: " << ((XvEncodingInfo *)xv_encoding_info)[i].name << endl; _encodingList << ((XvEncodingInfo *)xv_encoding_info)[i].name; } xv_attr = XvQueryPortAttributes(tqt_xdisplay(), xv_port, &xv_encoding_attributes); XvAttribute *xvattr = (XvAttribute *)xv_attr; kdDebug() << "Attributes for port " << xv_port << endl; for (int i = 0; i < xv_encoding_attributes; i++) { assert(xvattr); kdDebug() << " -> " << xvattr[i].name << ((xvattr[i].flags & XvGettable) ? " get" : "") << ((xvattr[i].flags & XvSettable) ? " set" : "") << " Range: " << xvattr[i].min_value << " -> " << xvattr[i].max_value << endl; KXvDeviceAttribute *xvda = new KXvDeviceAttribute; xvda->name = xvattr[i].name; xvda->min = xvattr[i].min_value; xvda->max = xvattr[i].max_value; xvda->flags = xvattr[i].flags; _attrs.append(xvda); } XvImageFormatValues *fo; fo = XvListImageFormats(tqt_xdisplay(), xv_port, &xv_formats); xv_formatvalues = (void *)fo; kdDebug() << "Image formats for port " << xv_port << endl; for (int i = 0; i < xv_formats; i++) { assert(fo); TQString imout; imout.sprintf(" 0x%x (%c%c%c%c) %s", fo[i].id, fo[i].id & 0xff, (fo[i].id >> 8) & 0xff, (fo[i].id >> 16) & 0xff, (fo[i].id >> 24) & 0xff, ((fo[i].format == XvPacked) ? "Packed" : "Planar")); kdDebug() << imout << endl; } kdDebug() << "Disabling double buffering." << endl; setAttribute("XV_DOUBLE_BUFFER", 0); return true; #endif } bool KXvDevice::supportsWidget(TQWidget *w) { #ifndef HAVE_LIBXV return false; #else for (int i = 0; i < xv_nvisualformats; i++) { if (static_cast(xv_visualformats)[i].visual_id == static_cast(w->x11Visual())->visualid) { return true; } } return false; #endif } bool KXvDevice::isVideoSource() { #ifndef HAVE_LIBXV return false; #else if (xv_type & XvVideoMask && xv_type & XvInputMask) return true; return false; #endif } bool KXvDevice::isImageBackend() { #ifndef HAVE_LIBXV return false; #else if (xv_type & XvImageMask && xv_type & XvInputMask) return true; return false; #endif } const KXvDeviceAttributes& KXvDevice::attributes() { return _attrs; } bool KXvDevice::getAttributeRange(const TQString& attribute, int *min, int *max) { for (KXvDeviceAttribute *at = _attrs.first(); at != NULL; at = _attrs.next()) { if (at->name == attribute) { if (min) *min = at->min; if (max) *max = at->max; return true; } } return false; } bool KXvDevice::getAttribute(const TQString& attribute, int *val) { #ifndef HAVE_LIBXV return false; #else for (KXvDeviceAttribute *at = _attrs.first(); at != NULL; at = _attrs.next()) { if (at->name == attribute) { if (val) XvGetPortAttribute(tqt_xdisplay(), xv_port, at->atom(), val); return true; } } return false; #endif } bool KXvDevice::setAttribute(const TQString& attribute, int val) { #ifndef HAVE_LIBXV return false; #else for (KXvDeviceAttribute *at = _attrs.first(); at != NULL; at = _attrs.next()) { if (at->name == attribute) { XvSetPortAttribute(tqt_xdisplay(), xv_port, at->atom(), val); XSync(tqt_xdisplay(), False); return true; } } return false; #endif } const TQString& KXvDevice::name() const { return xv_name; } int KXvDevice::port() const { return xv_port; } const TQStringList& KXvDevice::encodings() const { return _encodingList; } bool KXvDevice::encoding(TQString& encoding) { #ifndef HAVE_LIBXV return false; #else XvEncodingID enc; for (KXvDeviceAttribute *at = _attrs.first(); at != 0L; at = _attrs.next()) { if (at->name == "XV_ENCODING") { XvGetPortAttribute(tqt_xdisplay(), xv_port, at->atom(), (int*)&enc); kdDebug() << "KXvDevice: encoding: " << enc << endl; encoding = enc; return true; } } return false; #endif } bool KXvDevice::setEncoding(const TQString& e) { #ifdef HAVE_LIBXV for (unsigned int i = 0; i < xv_encodings; i++) { if (e == ((XvEncodingInfo *)xv_encoding_info)[i].name) { xv_encoding = i; return setAttribute("XV_ENCODING", ((XvEncodingInfo *)xv_encoding_info)[i].encoding_id); } } #endif return false; } bool KXvDevice::videoPlaying() const { return videoStarted; } bool KXvDevice::useShm(bool on) { #ifndef HAVE_XSHM if (on) { return false; } #endif if (!_haveShm) { return false; } if (_shm != on) rebuildImage(xv_image_w, xv_image_h, on); if (_haveShm) // This can change in rebuildImage() _shm = on; return _shm; } bool KXvDevice::usingShm() const { return _shm; } #include void KXvDevice::rebuildImage(int w, int h, bool shm) { if (xv_image) { destroyImage(); } #ifdef HAVE_LIBXV if (!shm) { xv_image = (void*)XvCreateImage(tqt_xdisplay(), xv_port, xv_imageformat, 0, w, h); if (!xv_image) { kdWarning() << "KXvDevice::rebuildImage: XvCreateImage failed." << endl; } } else { #ifdef HAVE_XSHM memset(xv_shminfo, 0, sizeof(XShmSegmentInfo)); xv_image = (void*)XvShmCreateImage(tqt_xdisplay(), xv_port, xv_imageformat, 0, w, h, static_cast(xv_shminfo)); if (!xv_image) { kdWarning() << "KXvDevice::rebuildImage: Error using SHM with Xv! Disabling SHM..." << endl; _haveShm = false; _shm = false; xv_image = (void*)XvCreateImage(tqt_xdisplay(), xv_port, xv_imageformat, 0, w, h); if (!xv_image) { kdWarning() << "KXvDevice::rebuildImage: XvCreateImage failed." << endl; } } else { static_cast(xv_shminfo)->shmid = shmget(IPC_PRIVATE, static_cast(xv_image)->data_size, IPC_CREAT | 0600); static_cast(xv_shminfo)->shmaddr = (char*)shmat(static_cast(xv_shminfo)->shmid, 0, 0); static_cast(xv_shminfo)->readOnly = True; static_cast(xv_image)->data = static_cast(xv_shminfo)->shmaddr; XShmAttach(tqt_xdisplay(), static_cast(xv_shminfo)); XSync(tqt_xdisplay(), False); shmctl(static_cast(xv_shminfo)->shmid, IPC_RMID, 0); } #endif } Q_ASSERT(xv_image != 0); xv_image_w = w; xv_image_h = h; #endif } void KXvDevice::destroyImage() { #ifdef HAVE_LIBXV if (!_shm) { if (xv_image) { static_cast(xv_image)->data = 0; } } else { if (xv_image) { #ifdef HAVE_XSHM shmdt(static_cast(xv_shminfo)->shmaddr); #endif } } XFree(xv_image); xv_image = 0; #endif } Atom KXvDeviceAttribute::atom() { return XInternAtom(tqt_xdisplay(), name.latin1(), False); }