SVN commit 844334 by mkretz: I messed up the branches - here comes the Experimental::SnapshotInterface implementation for trunk M +5 -1 events.h M +4 -0 mediaobject.cpp M +31 -10 videowidget.cpp M +13 -2 videowidget.h M +106 -0 xinestream.cpp --- trunk/KDE/kdebase/runtime/phonon/xine/events.h #844333:844334 @@ -28,6 +28,7 @@ #include #include #include +#include #define QEVENT(type) Event(Event::type) @@ -98,7 +99,9 @@ IsThereAXineEngineForMe, NoThereIsNoXineEngineForYou, HeresYourXineStream, - Cleanup + Cleanup, + RequestSnapshot, + SnapshotReady }; int ref; @@ -115,6 +118,7 @@ return 0; } +EVENT_CLASS1(SnapshotReady, QImage i, image(i), const QImage, image) EVENT_CLASS1(HeresYourXineStream, QExplicitlySharedDataPointer s, stream(s), QExplicitlySharedDataPointer, stream) EVENT_CLASS1(HasVideo, bool v, hasVideo(v), const bool, hasVideo) EVENT_CLASS1(UpdateVolume, int v, volume(v), const int, volume) --- trunk/KDE/kdebase/runtime/phonon/xine/mediaobject.cpp #844333:844334 @@ -667,6 +667,10 @@ // postEvent takes ownership of the event and will delete it when done QCoreApplication::postEvent(m_stream, copyEvent(static_cast(e))); break; + case Event::RequestSnapshot: + // postEvent takes ownership of the event and will delete it when done + QCoreApplication::postEvent(m_stream, new Event(e->type())); + break; case Event::SetParam: // postEvent takes ownership of the event and will delete it when done QCoreApplication::postEvent(m_stream, copyEvent(static_cast(e))); --- trunk/KDE/kdebase/runtime/phonon/xine/videowidget.cpp #844333:844334 @@ -1,19 +1,20 @@ /* This file is part of the KDE project Copyright (C) 2006-2007 Matthias Kretz - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. + This program 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 program is distributed in the hope that it will be useful, + 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 General Public License for more details. + 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 General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. + 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. */ @@ -21,6 +22,7 @@ #include "events.h" #include #include +#include #include #include #include @@ -339,6 +341,18 @@ } } +QImage VideoWidget::snapshot() const +{ + QImage img; + QMutexLocker lock(&m_snapshotLock); + const_cast(this)->upstreamEvent(new Event(Event::RequestSnapshot)); + if (m_snapshotWait.wait(&m_snapshotLock, 1000)) { + img = m_snapshotImage; + m_snapshotImage = QImage(); + } + return img; +} + /* int VideoWidget::overlayCapabilities() const { @@ -557,6 +571,7 @@ p.fillRect(rect(), Qt::black); //#ifndef PHONON_XINE_NO_VIDEOWIDGET } else if (xt->m_videoPort) { + //kDebug(); const QRect &rect = event->rect(); xcb_expose_event_t xcb_event; @@ -629,6 +644,12 @@ } } break; + case Event::SnapshotReady: + m_snapshotLock.lock(); + m_snapshotImage = static_cast(e)->image; + m_snapshotWait.wakeAll(); + m_snapshotLock.unlock(); + break; default: QCoreApplication::sendEvent(this, e); break; --- trunk/KDE/kdebase/runtime/phonon/xine/videowidget.h #844333:844334 @@ -21,6 +21,9 @@ #define PHONON_XINE_VIDEOWIDGET_H #include +#include +#include +#include #include "sinknode.h" #include #include @@ -31,6 +34,8 @@ #include #include +//#include +#include #include "connectnotificationinterface.h" class QMouseEvent; @@ -87,10 +92,10 @@ bool m_isValid; }; -class VideoWidget : public QWidget, public Phonon::VideoWidgetInterface, public Phonon::Xine::SinkNode +class VideoWidget : public QWidget, public Phonon::VideoWidgetInterface, public Phonon::Xine::SinkNode, public Phonon::Experimental::SnapshotInterface { Q_OBJECT - Q_INTERFACES(Phonon::VideoWidgetInterface Phonon::Xine::SinkNode) + Q_INTERFACES(Phonon::VideoWidgetInterface Phonon::Xine::SinkNode Phonon::Experimental::SnapshotInterface) public: VideoWidget(QWidget *parent = 0); ~VideoWidget(); @@ -114,6 +119,8 @@ qreal saturation() const; void setSaturation(qreal); + QImage snapshot() const; + void xineCallback(int &x, int &y, int &width, int &height, double &ratio, int videoWidth, int videoHeight, double videoRatio, bool mayResize); @@ -144,6 +151,10 @@ Phonon::VideoWidget::AspectRatio m_aspectRatio; Phonon::VideoWidget::ScaleMode m_scaleMode; + mutable QMutex m_snapshotLock; + mutable QWaitCondition m_snapshotWait; + mutable QImage m_snapshotImage; + QSize m_sizeHint; /** --- trunk/KDE/kdebase/runtime/phonon/xine/xinestream.cpp #844333:844334 @@ -1027,6 +1027,112 @@ } } return true; + case Event::RequestSnapshot: + ev->accept(); + if (m_stream) { + const int32_t w = xine_get_stream_info(m_stream, XINE_STREAM_INFO_VIDEO_WIDTH); + const int32_t h = xine_get_stream_info(m_stream, XINE_STREAM_INFO_VIDEO_HEIGHT); + kDebug(610) << "taking snapshot of" << w << h; + if (w > 0 && h > 0) { + int width, height, ratio_code, format; + uint8_t img[w * h * 4]; + int success = xine_get_current_frame (m_stream, &width, &height, &ratio_code, + &format, &img[0]); + if (!success) { + return true; + } + Q_ASSERT(w * h * 2 >= width * height); + QImage qimg(width, height, QImage::Format_RGB32); + /* + * Do YCbCr - sRGB conversion according to ITU-R BT.709 with Kb = 0.0722 and Kr = 0.2126 + * + * / \ / \ / \ + * | R | | 76309.0411, 0.0, 117489.0789 | | Y - 16 | + * | | | | | | + * => | G | = | 76309.0411, -13975.46119, -34924.74576 | * | Cb - 128 | * 2^-16 + * | | | | | | + * | B | | 76309.0411, 138438.3634, 0 | | Cr - 128 | + * \ / \ / \ / + */ + switch (format) { + case XINE_IMGFMT_YUY2: // every four consecutive pixels Y0 Cb Y1 Cr + kDebug(610) << "got a YUY2 snapshot"; + Q_ASSERT(width % 2 == 0); + for (int row = 0; row < height; ++row) { + QRgb *line = reinterpret_cast(qimg.scanLine(row)); + const uint8_t *yuyv = &img[2 * width]; + for (int col = 0; col < 2 * width; col += 4) { + const int y0 = (yuyv[col] - 16) * 76309; + const int u = yuyv[col + 1] - 128; + const int y1 = (yuyv[col + 2] - 16) * 76309; + const int v = yuyv[col + 3] - 128; + const int r = 117489 * v + 16384; + const int g = -13975 * u -34925 * v + 16384; + const int b = 138438 * u + 16384; + line[col >> 1] = qRgb( + qBound(0, (y0 + r) >> 16, 255), + qBound(0, (y0 + g) >> 16, 255), + qBound(0, (y0 + b) >> 16, 255)); + line[(col >> 1) + 1] = qRgb( + qBound(0, (y1 + r) >> 16, 255), + qBound(0, (y1 + g) >> 16, 255), + qBound(0, (y1 + b) >> 16, 255)); + } + } + break; + case XINE_IMGFMT_YV12: + kDebug(610) << "got a YV12 snapshot"; + Q_ASSERT(width % 2 == 0); + Q_ASSERT(height % 2 == 0); + { + const int w2 = width >> 1; + const uint8_t *yplane = &img[0]; + const uint8_t *uplane = &img[width * height]; + const uint8_t *vplane = &img[width * height + ((width * height) >> 2)]; + for (int row = 0; row < height; row += 2) { + QRgb *line0 = reinterpret_cast(qimg.scanLine(row)); + QRgb *line1 = reinterpret_cast(qimg.scanLine(row + 1)); + const uint8_t *yline0 = &yplane[row * width]; + const uint8_t *yline1 = &yplane[(row + 1) * width]; + const uint8_t *uline = &uplane[(row >> 1) * w2]; + const uint8_t *vline = &vplane[(row >> 1) * w2]; + for (int col = 0; col < width; col += 2) { + const int y0 = (yline0[col ] - 16) * 76309; + const int y1 = (yline0[col + 1] - 16) * 76309; + const int y2 = (yline1[col ] - 16) * 76309; + const int y3 = (yline1[col + 1] - 16) * 76309; + const int u = (uline[col >> 1] - 128); + const int v = (vline[col >> 1] - 128); + const int r = 117489 * v + 16384; + const int g = -13975 * u -34925 * v + 16384; + const int b = 138438 * u + 16384; + line0[col] = qRgb( + qBound(0, (y0 + r) >> 16, 255), + qBound(0, (y0 + g) >> 16, 255), + qBound(0, (y0 + b) >> 16, 255)); + line0[col + 1] = qRgb( + qBound(0, (y1 + r) >> 16, 255), + qBound(0, (y1 + g) >> 16, 255), + qBound(0, (y1 + b) >> 16, 255)); + line1[col] = qRgb( + qBound(0, (y2 + r) >> 16, 255), + qBound(0, (y2 + g) >> 16, 255), + qBound(0, (y2 + b) >> 16, 255)); + line1[col + 1] = qRgb( + qBound(0, (y3 + r) >> 16, 255), + qBound(0, (y3 + g) >> 16, 255), + qBound(0, (y3 + b) >> 16, 255)); + } + } + } + break; + default: + return true; + } + handleDownstreamEvent(new SnapshotReadyEvent(qimg)); + } + } + return true; case Event::MrlChanged: ev->accept(); {