Public feed
#601165
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 <phonon/phononnamespace.h>
#include <xine.h>
#include <QWaitCondition>
+#include <QImage>
#include <sys/time.h>
#include <time.h>
@@ -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<QEvent::Type>(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 <vektor@dumbterm.net>
+ * 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; }