Index: phonon-xine/videowidget.cpp =================================================================== --- phonon-xine/videowidget.cpp (révision 682687) +++ phonon-xine/videowidget.cpp (copie de travail) @@ -67,7 +67,6 @@ void VideoWidget::xineCallback( int &x, int &y, int &width, int &height, double &ratio, int videoWidth, int videoHeight, double videoRatio, bool mayResize ) { - Q_UNUSED( videoRatio ); Q_UNUSED( videoWidth ); Q_UNUSED( videoHeight ); Q_UNUSED( mayResize ); @@ -76,6 +75,7 @@ y = this->y(); width = this->width(); height = this->height(); + m_videoRatio = videoRatio; // square pixels ratio = 1.0; @@ -87,7 +87,8 @@ m_videoPort(0), m_path(0), m_fullScreen(false), - m_empty(true) + m_empty(true), + m_videoRatio(1.0) { // for some reason it can hang if the widget is 0x0 setMinimumSize( 1, 1 ); @@ -175,6 +176,14 @@ m_path = 0; } +void VideoWidget::requestScreenShot() +{ + if (m_path && m_path->mediaObject()) { + XineStream &xs = m_path->mediaObject()->stream(); + xs.requestScreenShot( m_videoRatio ); + } +} + Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const { return m_aspectRatio; Index: phonon-xine/xinestream.h =================================================================== --- phonon-xine/xinestream.h (révision 682687) +++ phonon-xine/xinestream.h (copie de travail) @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,7 @@ bool hasVideo() const; bool isSeekable() const; void setVolume(int vol); + void requestScreenShot(double videoRatio); void addAudioPostList(const AudioPostList &); void removeAudioPostList(const AudioPostList &); @@ -140,6 +142,8 @@ void angleChanged(int); void availableTitlesChanged(int); void titleChanged(int); + + void screenShotReady(const QImage&); protected: bool event(QEvent *ev); @@ -165,6 +169,9 @@ void error(Phonon::ErrorType, const QString &); void internalPause(); void internalPlay(); + void takeScreenShot(); + uchar* yv12ToRgb(uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, int width, int height); + void yuy2Toyv12(uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int width, int height); xine_stream_t *m_stream; xine_event_queue_t *m_event_queue; @@ -179,6 +186,7 @@ QMutex m_portMutex; mutable QMutex m_mutex; mutable QMutex m_streamInfoMutex; + mutable QMutex m_screenshotMutex; mutable QMutex m_updateTimeMutex; mutable QWaitCondition m_waitingForStreamInfo; QWaitCondition m_waitingForEventLoop; @@ -215,6 +223,8 @@ bool m_ticking : 1; bool m_closing : 1; bool m_eventLoopReady : 1; + QImage m_screenshotImage; + double m_screenshotRatio; }; } // namespace Xine Index: phonon-xine/mediaobject.cpp =================================================================== --- phonon-xine/mediaobject.cpp (révision 682687) +++ phonon-xine/mediaobject.cpp (copie de travail) @@ -80,6 +80,7 @@ connect(&m_stream, SIGNAL(prefinishMarkReached(qint32)), SIGNAL(prefinishMarkReached(qint32)), Qt::QueuedConnection); connect(&m_stream, SIGNAL(availableTitlesChanged(int)), SLOT(handleAvailableTitlesChanged(int))); connect(&m_stream, SIGNAL(needNextUrl()), SLOT(needNextUrl())); + connect(&m_stream, SIGNAL(screenShotReady(const QImage&)), SIGNAL(screenShotReady(const QImage&))); } MediaObject::~MediaObject() Index: phonon-xine/videowidget.h =================================================================== --- phonon-xine/videowidget.h (révision 682687) +++ phonon-xine/videowidget.h (copie de travail) @@ -65,6 +65,9 @@ bool isValid() const { return videoPort() != 0; } void setVideoEmpty(bool); + + public Q_SLOTS: + void requestScreenShot(); signals: void videoPortChanged(); @@ -96,6 +99,7 @@ QSize m_sizeHint; int m_videoWidth; int m_videoHeight; + double m_videoRatio; bool m_fullScreen; /** * No video should be shown, all paint events should draw black Index: phonon-xine/xinestream.cpp =================================================================== --- phonon-xine/xinestream.cpp (révision 682687) +++ phonon-xine/xinestream.cpp (copie de travail) @@ -66,7 +66,8 @@ QuitEventLoop = 2018, PauseForBuffering = 2019, // XXX numerically used in bytestream.cpp UnpauseForBuffering = 2020, // XXX numerically used in bytestream.cpp - Error = 2021 + Error = 2021, + GetScreenShot = 2022 }; class ErrorEvent : public QEvent @@ -317,6 +318,16 @@ } // called from main thread +void XineStream::requestScreenShot(double videoRatio) +{ + QMutexLocker locker(&m_screenshotMutex); + m_screenshotImage = QImage(); + m_screenshotRatio = videoRatio, + QCoreApplication::postEvent(this, new QEvent(static_cast(GetScreenShot))); +} + + +// called from main thread bool XineStream::isSeekable() const { if (!m_streamInfoReady) { @@ -335,6 +346,64 @@ } // xine thread +void XineStream::takeScreenShot() +{ + Q_ASSERT(QThread::currentThread() == this); + + if (m_stream) { + QMutexLocker locker(&m_screenshotMutex); + uint8_t *yuv=NULL, *y=NULL, *u=NULL, *v=NULL; + int width, height, ratio, format; + if (!xine_get_current_frame(m_stream, &width, &height, &ratio, &format, NULL)) + goto out; + yuv = new uint8_t[((width+8) * (height+1) * 2)]; + if (yuv == NULL) + //Not enough memory to make screenshot! + goto out; + + xine_get_current_frame(m_stream, &width, &height, &ratio, &format, yuv); + //convert to yv12 if necessary + switch (format) { + case XINE_IMGFMT_YUY2:{ + uint8_t *yuy2 = yuv; + yuv = new uint8_t[(width * height * 2)]; + if (yuv == NULL) + //Not enough memory to make screenshot! + goto out; + y = yuv; + u = yuv + width * height; + v = yuv + width * height * 5 / 4; + yuy2Toyv12 (y, u, v, yuy2, width, height);delete [] yuy2; + break; + } + case XINE_IMGFMT_YV12: + y = yuv; + u = yuv + width * height; + v = yuv + width * height * 5 / 4; + break; + default: + //unsupported format + delete [] yuv; + goto out; + } + //convert to rgb + uchar *rgb32BitData = yv12ToRgb (y, u, v, width, height); + if (rgb32BitData) { + m_screenshotImage = QImage(rgb32BitData, width, height, QImage::Format_RGB32); + if (m_screenshotRatio >= 1.0) + width = (int)((double) width * m_screenshotRatio + 0.5); + else + height = (int)((double) height / m_screenshotRatio + 0.5); + m_screenshotImage = m_screenshotImage.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + delete [] rgb32BitData; + } + delete [] yuv; + } +out: + emit screenShotReady( m_screenshotImage ); +} + +// xine thread void XineStream::getStreamInfo() { Q_ASSERT(QThread::currentThread() == this); @@ -941,6 +1010,10 @@ getStreamInfo(); ev->accept(); return true; + case GetScreenShot: + takeScreenShot(); + ev->accept(); + return true; case UpdateVolume: if (m_stream) { xine_set_param(m_stream, XINE_PARAM_AUDIO_AMP_LEVEL, m_volume); @@ -1618,6 +1691,111 @@ } } +/************************************************************ + * Helpers to convert yuy and yv12 frames to rgb * + * code from gxine modified for 32bit output * + * Copyright (C) 2000-2003 the xine project * + ************************************************************/ +void XineStream::yuy2Toyv12(uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int width, int height) +{ + int i, j, w2; + w2 = width / 2; + + for (i = 0; i < height; i += 2) { + for (j = 0; j < w2; j++) { + // packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] + *(y++) = *(input++); + *(u++) = *(input++); + *(y++) = *(input++); + *(v++) = *(input++); + } + // down sampling + for (j = 0; j < w2; j++) { + // skip every second line for U and V + *(y++) = *(input++); + input++; + *(y++) = *(input++); + input++; + } + } +} + +uchar* XineStream::yv12ToRgb(uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, int width, int height) +{ + // Create rgb data from yv12 +#define clip_8_bit(val) \ +{ \ + if (val < 0) \ + val = 0; \ + else \ + if (val > 255) val = 255; \ +} + int i, j; + int y, u, v; + int r, g, b; + int sub_i_uv; + int sub_j_uv; + int uv_width, uv_height; + uchar *rgb; + + uv_width = width / 2; + uv_height = height / 2; + + rgb = new uchar[(width * height * 4)]; //qt needs a 32bit align + if (!rgb) + return NULL; + + for (i = 0; i < height; ++i) { + // calculate u & v rows + sub_i_uv = ((i * uv_height) / height); + for (j = 0; j < width; ++j) { + // calculate u & v columns + sub_j_uv = ((j * uv_width) / width); + /*************************************************** + * + * Colour conversion from + * http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30 + * + * Thanks to Billy Biggs + * for the pointer and the following conversion. + * + * R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ]) + * G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ]) + * B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ]) + * + * Where in xine the above values are represented as + * + * Y' == image->y + * Cb == image->u + * Cr == image->v + * + ***************************************************/ + y = src_y[(i * width) + j] - 16; + u = src_u[(sub_i_uv * uv_width) + sub_j_uv] - 128; + v = src_v[(sub_i_uv * uv_width) + sub_j_uv] - 128; + r = (int)((1.1644 * (double)y) + (1.5960 * (double)v)); + g = (int)((1.1644 * (double)y) - (0.3918 * (double)u) - (0.8130 * (double)v)); + b = (int)((1.1644 * (double)y) + (2.0172 * (double)u)); + clip_8_bit (r); + clip_8_bit (g); + clip_8_bit (b); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + rgb[(i * width + j) * 4 + 0] = b; + rgb[(i * width + j) * 4 + 1] = g; + rgb[(i * width + j) * 4 + 2] = r; + rgb[(i * width + j) * 4 + 3] = 0; +#else + rgb[(i * width + j) * 4 + 3] = b; + rgb[(i * width + j) * 4 + 2] = g; + rgb[(i * width + j) * 4 + 1] = r; + rgb[(i * width + j) * 4 + 0] = 0; +#endif + } + } + return rgb; +} + + } // namespace Xine } // namespace Phonon Index: phonon-xine/mediaobject.h =================================================================== --- phonon-xine/mediaobject.h (révision 682687) +++ phonon-xine/mediaobject.h (copie de travail) @@ -128,6 +128,8 @@ void chapterChanged(int); void availableAnglesChanged(int); void angleChanged(int); + + void screenShotReady(const QImage&); protected: VideoPath* videoPath() const { return m_videoPath; }