1
/* This file is part of the KDE project
2
Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>
4
This program is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Library General Public
6
License as published by the Free Software Foundation; either
7
version 2 of the License, or (at your option) any later version.
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
Boston, MA 02110-1301, USA.
20
#include "videowidget.h"
23
#include <QtCore/QExplicitlySharedDataPointer>
24
#include <QtCore/QMutexLocker>
25
#include <QtCore/QSharedData>
29
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
32
//#endif // PHONON_XINE_NO_VIDEOWIDGET
35
#include "xineengine.h"
36
#include "mediaobject.h"
38
#include <QApplication>
39
#include <QDesktopWidget>
40
#include <QMouseEvent>
41
#include <QVBoxLayout>
42
#include "keepreference.h"
49
static XcbConnection *s_instance = 0;
51
QExplicitlySharedDataPointer<XcbConnection> XcbConnection::instance()
53
debug() << Q_FUNC_INFO;
58
return QExplicitlySharedDataPointer<XcbConnection>(s_instance);
61
XcbConnection::~XcbConnection()
63
debug() << Q_FUNC_INFO;
66
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
67
xcb_disconnect(m_xcbConnection);
69
//#endif // PHONON_XINE_NO_VIDEOWIDGET
72
XcbConnection::XcbConnection()
75
debug() << Q_FUNC_INFO;
76
Q_ASSERT(!s_instance);
78
int preferredScreen = 0;
79
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
80
m_xcbConnection = xcb_connect(NULL, &preferredScreen);//DisplayString(x11Info().display()), NULL);
81
if (m_xcbConnection) {
82
xcb_screen_iterator_t screenIt = xcb_setup_roots_iterator(xcb_get_setup(m_xcbConnection));
83
while ((screenIt.rem > 1) && (preferredScreen > 0)) {
84
xcb_screen_next(&screenIt);
87
m_screen = screenIt.data;
89
//#endif // PHONON_XINE_NO_VIDEOWIDGET
92
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
93
static void dest_size_cb(void *user_data, int video_width, int video_height, double video_pixel_aspect,
94
int *dest_width, int *dest_height, double *dest_pixel_aspect)
96
Phonon::Xine::VideoWidgetXT *xt = static_cast<VideoWidgetXT *>(user_data);
97
if (!xt->videoWidget()) {
100
*dest_pixel_aspect = 1.0;
105
xt->videoWidget()->xineCallback(win_x, win_y, *dest_width, *dest_height, *dest_pixel_aspect,
106
video_width, video_height, video_pixel_aspect, false);
109
static void frame_output_cb(void *user_data, int video_width, int video_height,
110
double video_pixel_aspect, int *dest_x, int *dest_y,
111
int *dest_width, int *dest_height,
112
double *dest_pixel_aspect, int *win_x, int *win_y)
114
Phonon::Xine::VideoWidgetXT *xt = static_cast<VideoWidgetXT *>(user_data);
115
if (!xt->videoWidget()) {
120
*dest_pixel_aspect = 1.0;
124
xt->videoWidget()->xineCallback(*win_x, *win_y, *dest_width, *dest_height, *dest_pixel_aspect,
125
video_width, video_height, video_pixel_aspect, true);
130
//#endif // PHONON_XINE_NO_VIDEOWIDGET
132
void VideoWidget::xineCallback(int &x, int &y, int &width, int &height, double &ratio,
133
int videoWidth, int videoHeight, double videoRatio, bool mayResize)
135
Q_UNUSED(videoRatio);
136
Q_UNUSED(videoWidth);
137
Q_UNUSED(videoHeight);
142
width = this->width();
143
height = this->height();
149
VideoWidgetXT::VideoWidgetXT(VideoWidget *w)
150
: SinkNodeXT("VideoWidget"),
151
m_xcbConnection(0), //XcbConnection::instance()),
152
m_videoPort(0), m_videoWidget(w), m_isValid(false)
154
memset(&m_visual, 0, sizeof(m_visual));
156
m_xine = Backend::xine();
159
void VideoWidgetXT::createVideoPort()
161
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
162
Q_ASSERT(!m_videoPort);
163
int preferredScreen = 0;
164
m_xcbConnection = xcb_connect(NULL, &preferredScreen);//DisplayString(x11Info().display()), NULL);
165
debug() << Q_FUNC_INFO << "xcb_connect" << m_xcbConnection;
166
if (m_xcbConnection && m_xine) {
167
xcb_screen_iterator_t screenIt = xcb_setup_roots_iterator(xcb_get_setup(m_xcbConnection));
168
while ((screenIt.rem > 1) && (preferredScreen > 0)) {
169
xcb_screen_next(&screenIt);
172
m_visual.connection = m_xcbConnection;
173
//Q_ASSERT(m_xcbConnection->screen());
174
//m_visual.screen = m_xcbConnection->screen();
175
m_visual.screen = screenIt.data;
176
m_visual.window = m_videoWidget->winId();
177
m_visual.user_data = static_cast<void *>(this);
178
m_visual.dest_size_cb = Phonon::Xine::dest_size_cb;
179
m_visual.frame_output_cb = Phonon::Xine::frame_output_cb;
181
// make sure all Qt<->X communication is done, else xine_open_video_driver will crash
182
QApplication::syncX();
184
Q_ASSERT(m_videoWidget->testAttribute(Qt::WA_WState_Created));
185
m_videoPort = xine_open_video_driver(m_xine, "auto", XINE_VISUAL_TYPE_XCB, static_cast<void *>(&m_visual));
187
//#endif // PHONON_XINE_NO_VIDEOWIDGET
188
m_videoPort = xine_open_video_driver(m_xine, "auto", XINE_VISUAL_TYPE_NONE, 0);
189
qWarning() << "No xine video output plugin using libxcb for threadsafe access to the X server found. No video for you.";
190
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
193
//#endif // PHONON_XINE_NO_VIDEOWIDGET
196
VideoWidget::VideoWidget(QWidget *parent)
198
SinkNode(new VideoWidgetXT(this)),
199
m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto),
200
m_scaleMode(Phonon::VideoWidget::FitInView),
207
// for some reason it can hang if the widget is 0x0
208
setMinimumSize(1, 1);
210
QPalette palette = this->palette();
211
palette.setColor(backgroundRole(), Qt::black);
214
// when resizing fill with black (backgroundRole color) the rest is done by paintEvent
215
setAttribute(Qt::WA_OpaquePaintEvent, true);
217
// disable Qt composition management as Xine draws onto the widget directly using X calls
218
setAttribute(Qt::WA_PaintOnScreen, true);
221
xt->createVideoPort();
223
// required for dvdnav
224
setMouseTracking(true);
227
VideoWidget::~VideoWidget()
229
debug() << Q_FUNC_INFO;
231
xt->m_videoWidget = 0;
232
if (xt->m_videoPort) {
233
xine_port_send_gui_data(xt->m_videoPort, XINE_GUI_SEND_WILL_DESTROY_DRAWABLE, 0);
237
VideoWidgetXT::~VideoWidgetXT()
239
debug() << Q_FUNC_INFO;
240
if (m_videoPort && m_xine) {
241
xine_close_video_driver(m_xine, m_videoPort);
243
if (m_xcbConnection) {
244
debug() << Q_FUNC_INFO << "xcb_disconnect" << m_xcbConnection;
245
xcb_disconnect(m_xcbConnection);
250
Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const
252
return m_aspectRatio;
255
void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio)
257
m_aspectRatio = aspectRatio;
258
switch (m_aspectRatio) {
259
case Phonon::VideoWidget::AspectRatioWidget:
260
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_SQUARE));
262
case Phonon::VideoWidget::AspectRatioAuto:
263
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_AUTO));
265
case Phonon::VideoWidget::AspectRatio4_3:
266
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_4_3));
268
case Phonon::VideoWidget::AspectRatio16_9:
269
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_ANAMORPHIC));
275
Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const
280
void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode mode)
286
qreal VideoWidget::brightness() const
291
static const qreal ONE = 1.0;
292
void VideoWidget::setBrightness(qreal newBrightness)
294
newBrightness = qBound(-ONE, newBrightness, ONE);
295
if (m_brightness != newBrightness) {
296
m_brightness = newBrightness;
297
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_BRIGHTNESS, static_cast<int>(0x7fff * (m_brightness + ONE))));
301
qreal VideoWidget::contrast() const
306
void VideoWidget::setContrast(qreal newContrast)
308
newContrast = qBound(-ONE, newContrast, ONE);
309
if (m_contrast != newContrast) {
310
m_contrast = newContrast;
311
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_CONTRAST, static_cast<int>(0x7fff * (m_contrast + ONE))));
315
qreal VideoWidget::hue() const
320
void VideoWidget::setHue(qreal newHue)
322
newHue = qBound(-ONE, newHue, ONE);
323
if (m_hue != newHue) {
325
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_HUE, static_cast<int>(0x7fff * (m_hue + ONE))));
329
qreal VideoWidget::saturation() const
334
void VideoWidget::setSaturation(qreal newSaturation)
336
newSaturation = qBound(-ONE, newSaturation, ONE);
337
if (m_saturation != newSaturation) {
338
m_saturation = newSaturation;
339
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_SATURATION, static_cast<int>(0x7fff * (m_saturation + ONE))));
343
QImage VideoWidget::snapshot() const
346
QMutexLocker lock(&m_snapshotLock);
347
const_cast<VideoWidget *>(this)->upstreamEvent(new RequestSnapshotEvent(img, &m_snapshotWait) );
348
if (m_snapshotWait.wait(&m_snapshotLock, 1000)) {
355
int VideoWidget::overlayCapabilities() const
357
return Phonon::Experimental::OverlayApi::OverlayOpaque;
360
bool VideoWidget::createOverlay(QWidget *widget, int type)
362
if ((overlay != 0) || (type != Phonon::Experimental::OverlayApi::OverlayOpaque))
366
QLayout *layout = new QVBoxLayout(this);
367
layout->setMargin(0);
371
layout()->addWidget(widget);
377
void VideoWidget::childEvent(QChildEvent *event)
379
if (event->removed() && (event->child() == overlay))
381
QWidget::childEvent(event);
385
void VideoWidget::updateZoom()
387
if (m_aspectRatio == Phonon::VideoWidget::AspectRatioWidget) {
388
const QSize s = size();
389
QSize imageSize = m_sizeHint;
390
imageSize.scale(s, Qt::KeepAspectRatio);
391
if (imageSize.width() < s.width()) {
392
const int zoom = s.width() * 100 / imageSize.width();
393
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_X, zoom));
394
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_Y, 100));
396
const int zoom = s.height() * 100 / imageSize.height();
397
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_X, 100));
398
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_Y, zoom));
400
} else if (m_scaleMode == Phonon::VideoWidget::ScaleAndCrop) {
401
const QSize s = size();
402
QSize imageSize = m_sizeHint;
403
// the image size is in square pixels
404
// first transform it to the current aspect ratio
405
debug() << Q_FUNC_INFO << imageSize;
406
switch (m_aspectRatio) {
407
case Phonon::VideoWidget::AspectRatioAuto:
408
// FIXME: how can we find out the ratio xine decided on? the event?
410
case Phonon::VideoWidget::AspectRatio4_3:
411
imageSize.setWidth(imageSize.height() * 4 / 3);
413
case Phonon::VideoWidget::AspectRatio16_9:
414
imageSize.setWidth(imageSize.height() * 16 / 9);
417
// correct ratio already
420
debug() << Q_FUNC_INFO << imageSize;
421
imageSize.scale(s, Qt::KeepAspectRatioByExpanding);
422
debug() << Q_FUNC_INFO << imageSize << s;
424
if (imageSize.width() > s.width()) {
425
zoom = imageSize.width() * 100 / s.width();
427
zoom = imageSize.height() * 100 / s.height();
429
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_X, zoom));
430
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_Y, zoom));
432
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_X, 100));
433
upstreamEvent(new SetParamEvent(XINE_PARAM_VO_ZOOM_Y, 100));
437
void VideoWidget::resizeEvent(QResizeEvent *ev)
440
QWidget::resizeEvent(ev);
443
bool VideoWidget::event(QEvent *ev)
445
switch (ev->type()) {
446
case Event::NavButtonIn:
447
debug() << Q_FUNC_INFO << "NavButtonIn";
448
setCursor(QCursor(Qt::PointingHandCursor));
451
case Event::NavButtonOut:
452
debug() << Q_FUNC_INFO << "NavButtonOut";
456
case Event::FrameFormatChange:
459
FrameFormatChangeEvent *e = static_cast<FrameFormatChangeEvent *>(ev);
460
debug() << Q_FUNC_INFO << "FrameFormatChangeEvent " << e->size;
461
m_sizeHint = e->size;
466
return QWidget::event(ev);
470
void VideoWidget::mouseMoveEvent(QMouseEvent *mev)
474
x11_rectangle_t rect;
475
xine_event_t *event = new xine_event_t;
476
xine_input_data_t *input = new xine_input_data_t;
483
xine_port_send_gui_data(xt->m_videoPort, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void *) &rect);
485
event->type = XINE_EVENT_INPUT_MOUSE_MOVE;
487
event->data_length = sizeof(*input);
491
//debug() << Q_FUNC_INFO << "upstreamEvent(EventSendEvent(move " << rect.x << rect.y;
492
upstreamEvent(new EventSendEvent(event));
494
QWidget::mouseMoveEvent(mev);
497
void VideoWidget::mousePressEvent(QMouseEvent *mev)
502
switch (mev->button()) {
506
case Qt::MouseButtonMask:
508
case Qt::RightButton: // 3
510
case Qt::MidButton: // 2
512
case Qt::LeftButton: // 1
514
x11_rectangle_t rect;
515
xine_event_t *event = new xine_event_t;
516
xine_input_data_t *input = new xine_input_data_t;
523
xine_port_send_gui_data(xt->m_videoPort, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void *) &rect);
525
event->type = XINE_EVENT_INPUT_MOUSE_BUTTON;
527
event->data_length = sizeof(*input);
528
input->button = button;
531
//debug() << Q_FUNC_INFO << "upstreamEvent(EventSendEvent(button " << rect.x << rect.y;
532
upstreamEvent(new EventSendEvent(event));
535
QWidget::mousePressEvent(mev);
538
bool VideoWidget::isValid() const
541
//K_XT(const VideoWidget);
542
//return xt->m_isValid;
545
xine_video_port_t *VideoWidgetXT::videoPort() const
550
void VideoWidgetXT::rewireTo(SourceNodeXT *source)
552
if (!source->videoOutputPort()) {
555
xine_post_wire_video_port(source->videoOutputPort(), videoPort());
558
void VideoWidget::paintEvent(QPaintEvent *event)
562
//debug() << Q_FUNC_INFO << "m_empty = " << m_empty;
563
if (m_empty || !source()) {// || m_path->mediaObject()->state() == Phonon::LoadingState) {
565
p.fillRect(rect(), Qt::black);
566
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
567
} else if (xt->m_videoPort) {
568
//debug() << Q_FUNC_INFO;
569
const QRect &rect = event->rect();
571
xcb_expose_event_t xcb_event;
572
memset(&xcb_event, 0, sizeof(xcb_event));
574
xcb_event.window = winId();
575
xcb_event.x = rect.x();
576
xcb_event.y = rect.y();
577
xcb_event.width = rect.width();
578
xcb_event.height = rect.height();
581
xine_port_send_gui_data(xt->m_videoPort, XINE_GUI_SEND_EXPOSE_EVENT, &xcb_event);
582
//#endif // PHONON_XINE_NO_VIDEOWIDGET
585
p.fillRect(rect(), Qt::black);
587
QWidget::paintEvent(event);
590
void VideoWidget::showEvent(QShowEvent *)
593
//xine_port_send_gui_data(xt->m_videoPort, XINE_GUI_SEND_VIDEOWIN_VISIBLE, static_cast<void *>(1));
596
void VideoWidget::hideEvent(QHideEvent *)
599
//xine_port_send_gui_data(xt->m_videoPort, XINE_GUI_SEND_VIDEOWIN_VISIBLE, static_cast<void *>(0));
602
void VideoWidget::changeEvent(QEvent *event)
606
if (event->type() == QEvent::ParentAboutToChange)
608
debug() << Q_FUNC_INFO << "ParentAboutToChange";
610
else if (event->type() == QEvent::ParentChange)
612
debug() << Q_FUNC_INFO << "ParentChange" << winId();
613
//#ifndef PHONON_XINE_NO_VIDEOWIDGET
614
if (xt->m_visual.window != winId()) {
615
xt->m_visual.window = winId();
616
if (xt->m_videoPort) {
617
// make sure all Qt<->X communication is done, else winId() might not be known at the
619
QApplication::syncX();
620
xine_port_send_gui_data(xt->m_videoPort, XINE_GUI_SEND_DRAWABLE_CHANGED, reinterpret_cast<void *>(xt->m_visual.window));
621
debug() << Q_FUNC_INFO << "XINE_GUI_SEND_DRAWABLE_CHANGED done.";
624
//#endif // PHONON_XINE_NO_VIDEOWIDGET
628
void VideoWidget::downstreamEvent(Event *e)
632
case Event::HasVideo:
634
HasVideoEvent *ev = static_cast<HasVideoEvent *>(e);
635
m_empty = !ev->hasVideo;
642
QCoreApplication::sendEvent(this, e);
645
SinkNode::downstreamEvent(e);
648
void VideoWidget::aboutToChangeXineEngine()
650
debug() << Q_FUNC_INFO;
652
if (xt->m_videoPort) {
653
VideoWidgetXT *xt2 = new VideoWidgetXT(this);
654
xt2->m_xine = xt->m_xine;
655
xt2->m_videoPort = xt->m_videoPort;
656
xt2->m_xcbConnection = xt->m_xcbConnection;
658
xt->m_xcbConnection = 0;
659
KeepReference<> *keep = new KeepReference<>;
660
keep->addObject(xt2);
665
void VideoWidget::xineEngineChanged()
667
debug() << Q_FUNC_INFO;
670
Q_ASSERT(!xt->m_videoPort);
671
xt->createVideoPort();
675
}} //namespace Phonon::Xine
677
#include "videowidget.moc"