1
/* BEGIN_COMMON_COPYRIGHT_HEADER
4
* Copyright: 2012 Labo A.L
6
* Aaron Lewis <the.warl0ck.1989@gmail.com>
8
* This program or library is free software; you can redistribute it
9
* and/or modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2.1 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General
19
* Public License along with this library; if not, write to the
20
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21
* Boston, MA 02110-1301 USA
23
* END_COMMON_COPYRIGHT_HEADER */
25
* qmpwidget - A Qt widget for embedding MPlayer
26
* Copyright (C) 2010 by Jonas Gehring
28
* This program is free software: you can redistribute it and/or modify
29
* it under the terms of the GNU General Public License as published by
30
* the Free Software Foundation, either version 3 of the License, or
31
* (at your option) any later version.
33
* This program is distributed in the hope that it will be useful,
34
* but WITHOUT ANY WARRANTY; without even the implied warranty of
35
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36
* GNU General Public License for more details.
38
* You should have received a copy of the GNU General Public License
39
* along with this program. If not, see <http://www.gnu.org/licenses/>.
43
#include <QAbstractSlider>
45
#include <QLocalSocket>
48
#include <QStringList>
49
#include <QTemporaryFile>
57
#include "qmpwidget.h"
59
//#define QMP_DEBUG_OUTPUT
61
#ifdef QMP_USE_YUVPIPE
62
#include "qmpyuvreader.h"
63
#endif // QMP_USE_YUVPIPE
66
// A plain video widget
67
class QMPPlainVideoWidget : public QWidget
72
QMPPlainVideoWidget(QWidget *parent = 0)
75
setAttribute(Qt::WA_NoSystemBackground);
76
setMouseTracking(true);
79
void showUserImage(const QImage &image)
86
void displayImage(const QImage &image)
88
m_pixmap = QPixmap::fromImage(image);
93
void paintEvent(QPaintEvent *event)
97
p.setCompositionMode(QPainter::CompositionMode_Source);
99
if (!m_userImage.isNull()) {
100
p.fillRect(rect(), Qt::black);
101
p.drawImage(rect().center() - m_userImage.rect().center(), m_userImage);
102
} else if (!m_pixmap.isNull()) {
103
p.drawPixmap(rect(), m_pixmap);
105
p.fillRect(rect(), Qt::black);
118
// A OpenGL video widget
119
class QMPOpenGLVideoWidget : public QGLWidget
124
QMPOpenGLVideoWidget(QWidget *parent = 0)
125
: QGLWidget(parent), m_tex(-1)
127
setMouseTracking(true);
130
void showUserImage(const QImage &image)
136
deleteTexture(m_tex);
138
if (!m_userImage.isNull()) {
139
m_tex = bindTexture(image);
140
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
141
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
143
glViewport(0, 0, width(), qMax(height(), 1));
149
void displayImage(const QImage &image)
151
if (!m_userImage.isNull()) {
157
deleteTexture(m_tex);
159
m_tex = bindTexture(image);
160
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
161
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
168
glEnable(GL_TEXTURE_2D);
169
glClearColor(0, 0, 0, 0);
173
void resizeGL(int w, int h)
175
glViewport(0, 0, w, qMax(h, 1));
180
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
183
glBindTexture(GL_TEXTURE_2D, m_tex);
184
if (!m_userImage.isNull()) {
185
QRect r = m_userImage.rect();
186
r.moveTopLeft(rect().center() - m_userImage.rect().center());
187
glViewport(r.x(), r.y(), r.width(), r.height());
190
glTexCoord2f(0, 0); glVertex2f(-1, -1);
191
glTexCoord2f(1, 0); glVertex2f( 1, -1);
192
glTexCoord2f(1, 1); glVertex2f( 1, 1);
193
glTexCoord2f(0, 1); glVertex2f(-1, 1);
203
#endif // QT_OPENGL_LIB
206
// A custom QProcess designed for the MPlayer slave interface
207
class QMPProcess : public QProcess
212
QMPProcess(QObject *parent = 0)
213
: QProcess(parent), m_state(QMPwidget::NotStartedState), m_mplayerPath("mplayer"),
214
m_fakeInputconf(NULL)
215
#ifdef QMP_USE_YUVPIPE
222
m_mode = QMPwidget::EmbeddedMode;
223
m_videoOutput = "directx,directx:noaccel";
224
#elif defined(Q_WS_X11)
225
m_mode = QMPwidget::EmbeddedMode;
227
m_videoOutput = "gl2,gl,xv";
229
m_videoOutput = "xv";
231
#elif defined(Q_WS_MAC)
232
m_mode = QMPwidget::PipeMode;
234
m_videoOutput = "gl,quartz";
236
m_videoOutput = "quartz";
240
m_movieFinishedTimer.setSingleShot(true);
241
m_movieFinishedTimer.setInterval(100);
243
connect(this, SIGNAL(readyReadStandardOutput()), this, SLOT(readStdout()));
244
connect(this, SIGNAL(readyReadStandardError()), this, SLOT(readStderr()));
245
connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished()));
246
connect(&m_movieFinishedTimer, SIGNAL(timeout()), this, SLOT(movieFinished()));
251
#ifdef QMP_USE_YUVPIPE
252
if (m_yuvReader != NULL) {
256
if (m_fakeInputconf != NULL) {
257
delete m_fakeInputconf;
261
// Starts the MPlayer process in idle mode
262
void start(QWidget *widget, const QStringList &args)
264
if (m_mode == QMPwidget::PipeMode) {
265
#ifdef QMP_USE_YUVPIPE
266
m_yuvReader = new QMPYuvReader(this);
268
m_mode = QMPwidget::EmbeddedMode;
272
// Figure out the mplayer version in order to check if
273
// "-input nodefault-bindings" is available
274
bool useFakeInputconf = true;
275
QString version = mplayerVersion();
276
if (version.contains("SVN")) { // Check revision
277
QRegExp re("SVN-r([0-9]*)");
278
if (re.indexIn(version) > -1) {
279
int revision = re.cap(1).toInt();
280
if (revision >= 28878) {
281
useFakeInputconf = false;
289
myargs += "-noquiet";
290
myargs += "-identify";
291
myargs += "-nomouseinput";
292
myargs += "-nokeepaspect";
293
myargs += "-monitorpixelaspect";
295
if (!useFakeInputconf) {
297
myargs += "nodefault-bindings:conf=/dev/null";
300
// Ugly hack for older versions of mplayer (used in kmplayer and other)
301
if (m_fakeInputconf == NULL) {
302
m_fakeInputconf = new QTemporaryFile();
303
if (m_fakeInputconf->open()) {
304
writeFakeInputconf(m_fakeInputconf);
306
delete m_fakeInputconf;
307
m_fakeInputconf = NULL;
310
if (m_fakeInputconf != NULL) {
312
myargs += QString("conf=%1").arg(m_fakeInputconf->fileName());
317
if (m_mode == QMPwidget::EmbeddedMode) {
319
myargs += QString::number((int)widget->winId());
320
if (!m_videoOutput.isEmpty()) {
322
myargs += m_videoOutput;
325
#ifdef QMP_USE_YUVPIPE
327
myargs += QString("yuv4mpeg:file=%1").arg(m_yuvReader->m_pipe);
332
#ifdef QMP_DEBUG_OUTPUT
336
QProcess::start(m_mplayerPath, myargs);
337
changeState(QMPwidget::IdleState);
339
if (m_mode == QMPwidget::PipeMode) {
340
#ifdef QMP_USE_YUVPIPE
341
connect(m_yuvReader, SIGNAL(imageReady(const QImage &)), widget, SLOT(displayImage(const QImage &)));
342
m_yuvReader->start();
347
QString mplayerVersion()
350
p.start(m_mplayerPath, QStringList("-version"));
351
if (!p.waitForStarted()) {
354
if (!p.waitForFinished()) {
358
QString output = QString(p.readAll());
359
QRegExp re("MPlayer ([^ ]*)");
360
if (re.indexIn(output) > -1) {
366
QProcess::ProcessState processState() const
368
return QProcess::state();
371
void writeCommand(const QString &command)
373
#ifdef QMP_DEBUG_OUTPUT
374
qDebug("in: \"%s\"", qPrintable(command));
376
QProcess::write(command.toLocal8Bit()+"\n");
381
writeCommand("quit");
382
QProcess::waitForFinished(100);
383
if (QProcess::state() == QProcess::Running) {
386
QProcess::waitForFinished(-1);
391
writeCommand("pause");
396
writeCommand("stop");
400
void stateChanged(int state);
401
void streamPositionChanged(double position);
402
void error(const QString &reason);
404
void readStandardOutput(const QString &line);
405
void readStandardError(const QString &line);
410
QStringList lines = QString::fromLocal8Bit(readAllStandardOutput()).split("\n", QString::SkipEmptyParts);
411
for (int i = 0; i < lines.count(); i++) {
412
lines[i].remove("\r");
413
#ifdef QMP_DEBUG_OUTPUT
414
qDebug("out: \"%s\"", qPrintable(lines[i]));
417
emit readStandardOutput(lines[i]);
423
QStringList lines = QString::fromLocal8Bit(readAllStandardError()).split("\n", QString::SkipEmptyParts);
424
for (int i = 0; i < lines.count(); i++) {
425
lines[i].remove("\r");
426
#ifdef QMP_DEBUG_OUTPUT
427
qDebug("err: \"%s\"", qPrintable(lines[i]));
430
emit readStandardError(lines[i]);
436
// Called if the *process* has finished
437
changeState(QMPwidget::NotStartedState);
442
if (m_state == QMPwidget::PlayingState) {
443
changeState(QMPwidget::IdleState);
448
// Parses a line of MPlayer output
449
void parseLine(const QString &line)
451
if (line.startsWith("Playing ")) {
452
changeState(QMPwidget::LoadingState);
453
} else if (line.startsWith("Cache fill:")) {
454
changeState(QMPwidget::BufferingState);
455
} else if (line.startsWith("Starting playback...")) {
456
m_mediaInfo.ok = true; // No more info here
457
changeState(QMPwidget::PlayingState);
458
} else if (line.startsWith("File not found: ")) {
459
changeState(QMPwidget::ErrorState);
460
} else if (line.endsWith("ID_PAUSED")) {
461
changeState(QMPwidget::PausedState);
462
} else if (line.startsWith("ID_")) {
463
parseMediaInfo(line);
464
} else if (line.startsWith("No stream found")) {
465
changeState(QMPwidget::ErrorState, line);
466
} else if (line.startsWith("A:") || line.startsWith("V:")) {
467
if (m_state != QMPwidget::PlayingState) {
468
changeState(QMPwidget::PlayingState);
471
} else if (line.startsWith("Exiting...")) {
472
changeState(QMPwidget::NotStartedState);
476
// Parses MPlayer's media identification output
477
void parseMediaInfo(const QString &line)
479
QStringList info = line.split("=");
480
if (info.count() < 2) {
484
if (info[0] == "ID_VIDEO_FORMAT") {
485
m_mediaInfo.videoFormat = info[1];
486
} else if (info[0] == "ID_VIDEO_BITRATE") {
487
m_mediaInfo.videoBitrate = info[1].toInt();
488
} else if (info[0] == "ID_VIDEO_WIDTH") {
489
m_mediaInfo.size.setWidth(info[1].toInt());
490
} else if (info[0] == "ID_VIDEO_HEIGHT") {
491
m_mediaInfo.size.setHeight(info[1].toInt());
492
} else if (info[0] == "ID_VIDEO_FPS") {
493
m_mediaInfo.framesPerSecond = info[1].toDouble();
495
} else if (info[0] == "ID_AUDIO_FORMAT") {
496
m_mediaInfo.audioFormat = info[1];
497
} else if (info[0] == "ID_AUDIO_BITRATE") {
498
m_mediaInfo.audioBitrate = info[1].toInt();
499
} else if (info[0] == "ID_AUDIO_RATE") {
500
m_mediaInfo.sampleRate = info[1].toInt();
501
} else if (info[0] == "ID_AUDIO_NCH") {
502
m_mediaInfo.numChannels = info[1].toInt();
504
} else if (info[0] == "ID_LENGTH") {
505
m_mediaInfo.length = info[1].toDouble();
506
} else if (info[0] == "ID_SEEKABLE") {
507
m_mediaInfo.seekable = (bool)info[1].toInt();
509
} else if (info[0].startsWith("ID_CLIP_INFO_NAME")) {
510
m_currentTag = info[1];
511
} else if (info[0].startsWith("ID_CLIP_INFO_VALUE") && !m_currentTag.isEmpty()) {
512
m_mediaInfo.tags.insert(m_currentTag, info[1]);
516
// Parsas MPlayer's position output
517
void parsePosition(const QString &line)
519
static QRegExp rx("[ :]");
520
QStringList info = line.split(rx, QString::SkipEmptyParts);
522
double oldpos = m_streamPosition;
523
for (int i = 0; i < info.count(); i++) {
524
if ( ( info[i] == "A" || info[i] == "V" ) && info.count() > i) {
525
m_streamPosition = info[i+1].toDouble();
527
// If the movie is near its end, start a timer that will check whether
528
// the movie has really finished.
529
if (qAbs(m_streamPosition - m_mediaInfo.length) < 1) {
530
m_movieFinishedTimer.start();
535
if (oldpos != m_streamPosition) {
536
emit streamPositionChanged(m_streamPosition);
540
// Changes the current state, possibly emitting multiple signals
541
void changeState(QMPwidget::State state, const QString &comment = QString())
543
#ifdef QMP_USE_YUVPIPE
544
if (m_yuvReader != NULL && (state == QMPwidget::ErrorState || state == QMPwidget::NotStartedState)) {
546
m_yuvReader->deleteLater();
550
if (m_state == state) {
554
if (m_state == QMPwidget::PlayingState) {
555
m_movieFinishedTimer.stop();
559
emit stateChanged(m_state);
562
case QMPwidget::NotStartedState:
566
case QMPwidget::ErrorState:
575
// Resets the media info and position values
578
m_mediaInfo = QMPwidget::MediaInfo();
579
m_streamPosition = -1;
582
// Writes a dummy input configuration to the given device
583
void writeFakeInputconf(QIODevice *device)
585
// Query list of supported keys
587
p.start(m_mplayerPath, QStringList("-input") += "keylist");
588
if (!p.waitForStarted()) {
591
if (!p.waitForFinished()) {
594
QStringList keys = QString(p.readAll()).split("\n", QString::SkipEmptyParts);
596
// Write dummy command for each key
597
QTextStream out(device);
598
for (int i = 0; i < keys.count(); i++) {
599
keys[i].remove("\r");
600
out << keys[i] << " " << "ignored" << endl;
605
QMPwidget::State m_state;
607
QString m_mplayerPath;
608
QString m_videoOutput;
610
QMPwidget::Mode m_mode;
612
QMPwidget::MediaInfo m_mediaInfo;
613
double m_streamPosition; // This is the video position
614
QTimer m_movieFinishedTimer;
616
QString m_currentTag;
618
QTemporaryFile *m_fakeInputconf;
620
#ifdef QMP_USE_YUVPIPE
621
QPointer<QMPYuvReader> m_yuvReader;
626
// Initialize the media info structure
627
QMPwidget::MediaInfo::MediaInfo()
628
: videoBitrate(0), framesPerSecond(0), sampleRate(0), numChannels(0),
629
ok(false), length(0), seekable(false)
638
* \param parent Parent widget
640
QMPwidget::QMPwidget(QWidget *parent)
643
setFocusPolicy(Qt::StrongFocus);
644
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
647
m_widget = new QMPOpenGLVideoWidget(this);
649
m_widget = new QMPPlainVideoWidget(this);
652
QPalette p = palette();
653
p.setColor(QPalette::Window, Qt::black);
656
m_seekTimer.setInterval(50);
657
m_seekTimer.setSingleShot(true);
658
connect(&m_seekTimer, SIGNAL(timeout()), this, SLOT(delayedSeek()));
660
m_process = new QMPProcess(this);
661
connect(m_process, SIGNAL(stateChanged(int)), this, SLOT(mpStateChanged(int)));
662
connect(m_process, SIGNAL(streamPositionChanged(double)), this, SLOT(mpStreamPositionChanged(double)));
663
connect(m_process, SIGNAL(error(const QString &)), this, SIGNAL(error(const QString &)));
664
connect(m_process, SIGNAL(readStandardOutput(const QString &)), this, SIGNAL(readStandardOutput(const QString &)));
665
connect(m_process, SIGNAL(readStandardError(const QString &)), this, SIGNAL(readStandardError(const QString &)));
671
* This function will ask the MPlayer process to quit and block until it has really
674
QMPwidget::~QMPwidget()
676
if (m_process->processState() == QProcess::Running) {
683
* \brief Returns the current MPlayer process state
685
* \returns The process state
687
QMPwidget::State QMPwidget::state() const
689
return m_process->m_state;
693
* \brief Returns the current media info object
695
* Please check QMPwidget::MediaInfo::ok to make sure the media
696
* information has been fully parsed.
698
* \returns The media info object
700
QMPwidget::MediaInfo QMPwidget::mediaInfo() const
702
return m_process->m_mediaInfo;
706
* \brief Returns the current playback position
708
* \returns The current playback position in seconds
711
double QMPwidget::tell() const
713
return m_process->m_streamPosition;
717
* \brief Returns the MPlayer process
719
* \returns The MPlayer process
721
QProcess *QMPwidget::process() const
727
* \brief Sets the video playback mode
729
* Please see \ref playbackmodes for a discussion of the available modes.
731
* \param mode The video playback mode
734
void QMPwidget::setMode(Mode mode)
736
#ifdef QMP_USE_YUVPIPE
737
m_process->m_mode = mode;
744
* \brief Returns the current video playback mode
746
* \returns The current video playback mode
749
QMPwidget::Mode QMPwidget::mode() const
751
return m_process->m_mode;
755
* \brief Sets the video output mode
757
* The video output mode string will be passed to MPlayer using its \p -vo option.
758
* Please see http://www.mplayerhq.hu/DOCS/HTML/en/video.html for an overview of
759
* available video output modes.
761
* Per default, this string will have the following values:
763
* <tr><th>System</th><th>Configuration</th><th>Value</th></tr>
767
* <td>\p "directx,directx:noaccel"</td>
771
* <td>Compiled without OpenGL support</td>
776
* <td>Compiled with OpenGL support</td>
777
* <td>\p "gl2,gl,xv"</td>
781
* <td>Compiled without OpenGL support</td>
782
* <td>\p "quartz"</td>
786
* <td>Compiled with OpenGL support</td>
787
* <td>\p "gl,quartz"</td>
792
* \param output The video output mode string
795
void QMPwidget::setVideoOutput(const QString &output)
797
m_process->m_videoOutput = output;
801
* \brief Returns the current video output mode
803
* \returns The current video output mode
804
* \sa setVideoOutput()
806
QString QMPwidget::videoOutput() const
808
return m_process->m_videoOutput;
812
* \brief Sets the path to the MPlayer executable
814
* Per default, it is assumed the MPlayer executable is
815
* available in the current OS path. Therefore, this value is
818
* \param path Path to the MPlayer executable
821
void QMPwidget::setMPlayerPath(const QString &path)
823
m_process->m_mplayerPath = path;
827
* \brief Returns the current path to the MPlayer executable
829
* \returns The path to the MPlayer executable
830
* \sa setMPlayerPath()
832
QString QMPwidget::mplayerPath() const
834
return m_process->m_mplayerPath;
838
* \brief Returns the version string of the MPlayer executable
843
* \returns The version string of the MPlayer executable
845
QString QMPwidget::mplayerVersion()
847
return m_process->mplayerVersion();
851
* \brief Sets a seeking slider for this widget
853
void QMPwidget::setSeekSlider(QAbstractSlider *slider)
856
m_seekSlider->disconnect(this);
857
disconnect(m_seekSlider);
860
if (m_process->m_mediaInfo.ok) {
861
slider->setRange(0, m_process->m_mediaInfo.length);
863
if (m_process->m_mediaInfo.ok) {
864
slider->setEnabled(m_process->m_mediaInfo.seekable);
867
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(seek(int)));
868
m_seekSlider = slider;
872
* \brief Sets a volume slider for this widget
874
void QMPwidget::setVolumeSlider(QAbstractSlider *slider)
876
if (m_volumeSlider) {
877
m_volumeSlider->disconnect(this);
878
disconnect(m_volumeSlider);
882
slider->setRange(0, 100);
883
slider->setValue(100); // TODO
885
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(setVolume(int)));
886
m_volumeSlider = slider;
890
* \brief Shows a custom image
892
* This function sets a custom image that will be shown instead of the MPlayer
893
* video output. In order to show MPlayer's output again, call this function
896
* \note If the current playback mode is not set to \p PipeMode, this function
897
* will have no effect if MPlayer draws to the widget.
899
* \param image Custom image
901
void QMPwidget::showImage(const QImage &image)
904
qobject_cast<QMPOpenGLVideoWidget *>(m_widget)->showUserImage(image);
906
qobject_cast<QMPPlainVideoWidget*>(m_widget)->showUserImage(image);
911
* \brief Returns a suitable size hint for this widget
913
* This function is used internally by Qt.
915
QSize QMPwidget::sizeHint() const
917
if (m_process->m_mediaInfo.ok && !m_process->m_mediaInfo.size.isNull()) {
918
return m_process->m_mediaInfo.size;
920
return QWidget::sizeHint();
924
* \brief Starts the MPlayer process with the given arguments
926
* If there's another process running, it will be terminated first. MPlayer
927
* will be run in idle mode and is avaiting your commands, e.g. via load().
929
* \param args MPlayer command line arguments
931
void QMPwidget::start(const QStringList &args)
933
if (m_process->processState() == QProcess::Running) {
936
m_process->start(m_widget, args);
940
* \brief Loads a file or url and starts playback
942
* \param url File patho or url
944
void QMPwidget::load(const QString &url)
946
Q_ASSERT_X(m_process->state() != QProcess::NotRunning, "QMPwidget::load()", "MPlayer process not started yet");
948
// From the MPlayer slave interface documentation:
949
// "Try using something like [the following] to switch to the next file.
950
// It avoids audio playback starting to play the old file for a short time
951
// before switching to the new one.
952
writeCommand("pausing_keep_force pt_step 1");
953
writeCommand("get_property pause");
955
writeCommand(QString("loadfile '%1'").arg(url));
959
* \brief Resumes playback
961
void QMPwidget::play()
963
if (m_process->m_state == PausedState) {
969
* \brief Pauses playback
971
void QMPwidget::pause()
973
if (m_process->m_state == PlayingState) {
979
* \brief Stops playback
981
void QMPwidget::stop()
987
* \brief Media playback seeking
989
* \param offset Seeking offset in seconds
990
* \param whence Seeking mode
991
* \returns \p true If the seeking mode is valid
994
bool QMPwidget::seek(int offset, int whence)
996
return seek(double(offset), whence);
1000
* \brief Media playback seeking
1002
* \param offset Seeking offset in seconds
1003
* \param whence Seeking mode
1004
* \returns \p true If the seeking mode is valid
1007
bool QMPwidget::seek(double offset, int whence)
1009
m_seekTimer.stop(); // Cancel all current seek requests
1013
case PercentageSeek:
1020
// Schedule seek request
1021
m_seekCommand = QString("seek %1 %2").arg(offset).arg(whence);
1022
m_seekTimer.start();
1027
* \brief Toggles full-screen mode
1029
void QMPwidget::toggleFullScreen()
1031
if (!isFullScreen()) {
1032
m_windowFlags = windowFlags() & (Qt::Window);
1033
m_geometry = geometry();
1034
setWindowFlags((windowFlags() | Qt::Window));
1035
// From Phonon::VideoWidget
1039
setWindowState(windowState() | Qt::WindowFullScreen);
1041
setWindowState(windowState() | Qt::WindowFullScreen);
1045
setWindowFlags((windowFlags() ^ (Qt::Window)) | m_windowFlags);
1046
setWindowState(windowState() & ~Qt::WindowFullScreen);
1047
setGeometry(m_geometry);
1053
* \brief Sends a command to the MPlayer process
1055
* Since MPlayer is being run in slave mode, it reads commands from the standard
1056
* input. It is assumed that the interface provided by this class might not be
1057
* sufficient for some situations, so you can use this functions to directly
1058
* control the MPlayer process.
1060
* For a complete list of commands for MPlayer's slave mode, see
1061
* http://www.mplayerhq.hu/DOCS/tech/slave.txt .
1063
* \param command The command line. A newline character will be added internally.
1065
void QMPwidget::writeCommand(const QString &command)
1067
m_process->writeCommand(command);
1071
* \brief Mouse double click event handler
1073
* This implementation will toggle full screen and accept the event
1075
* \param event Mouse event
1077
void QMPwidget::mouseDoubleClickEvent(QMouseEvent *event)
1084
* \brief Keyboard press event handler
1086
* This implementation tries to resemble the classic MPlayer interface. For a
1087
* full list of supported key codes, see \ref shortcuts.
1089
* \param event Key event
1091
void QMPwidget::keyPressEvent(QKeyEvent *event)
1094
switch (event->key()) {
1097
if (state() == PlayingState) {
1099
} else if (state() == PausedState) {
1109
case Qt::Key_Escape:
1114
writeCommand("audio_delay -0.1");
1117
writeCommand("audio_delay 0.1");
1121
seek(-10, RelativeSeek);
1124
seek(10, RelativeSeek);
1127
seek(-60, RelativeSeek);
1130
seek(60, RelativeSeek);
1132
case Qt::Key_PageDown:
1133
seek(-600, RelativeSeek);
1135
case Qt::Key_PageUp:
1136
seek(600, RelativeSeek);
1139
case Qt::Key_Asterisk:
1140
writeCommand("volume 10");
1143
writeCommand("volume -10");
1147
writeCommand("sub_delay 0.1");
1150
writeCommand("sub_delay -0.1");
1158
event->setAccepted(accept);
1162
* \brief Resize event handler
1164
* If you reimplement this function, you need to call this handler, too.
1166
* \param event Resize event
1168
void QMPwidget::resizeEvent(QResizeEvent *event)
1174
void QMPwidget::updateWidgetSize()
1176
if (!m_process->m_mediaInfo.size.isNull()) {
1177
QSize mediaSize = m_process->m_mediaInfo.size;
1178
QSize widgetSize = size();
1180
double factor = qMin(double(widgetSize.width()) / mediaSize.width(), double(widgetSize.height()) / mediaSize.height());
1181
QRect wrect(0, 0, int(factor * mediaSize.width() + 0.5), int(factor * mediaSize.height()));
1182
wrect.moveTopLeft(rect().center() - wrect.center());
1183
m_widget->setGeometry(wrect);
1185
m_widget->setGeometry(QRect(QPoint(0, 0), size()));
1189
void QMPwidget::delayedSeek()
1191
if (!m_seekCommand.isEmpty()) {
1192
writeCommand(m_seekCommand);
1193
m_seekCommand = QString();
1197
void QMPwidget::setVolume(int volume)
1199
writeCommand(QString("volume %1 1").arg(volume));
1202
void QMPwidget::mpStateChanged(int state)
1204
if (m_seekSlider != NULL && state == PlayingState && m_process->m_mediaInfo.ok) {
1205
m_seekSlider->setRange(0, m_process->m_mediaInfo.length);
1206
m_seekSlider->setEnabled(m_process->m_mediaInfo.seekable);
1210
emit stateChanged(state);
1213
void QMPwidget::mpStreamPositionChanged(double position)
1215
if (m_seekSlider != NULL && m_seekCommand.isEmpty() && m_seekSlider->value() != qRound(position)) {
1216
m_seekSlider->disconnect(this);
1217
m_seekSlider->setValue(qRound(position));
1218
connect(m_seekSlider, SIGNAL(valueChanged(int)), this, SLOT(seek(int)));
1222
void QMPwidget::mpVolumeChanged(int volume)
1224
if (m_volumeSlider != NULL) {
1225
m_volumeSlider->disconnect(this);
1226
m_volumeSlider->setValue(volume);
1227
connect(m_seekSlider, SIGNAL(valueChanged(int)), this, SLOT(setVolume(int)));
1232
#include "qmpwidget.moc"
1235
/* Documentation follows */
1239
* \brief A Qt widget for embedding MPlayer
1244
* \subsection comm MPlayer communication
1246
* If you want to communicate with MPlayer through its
1247
* <a href="http://www.mplayerhq.hu/DOCS/tech/slave.txt">slave mode protocol</a>,
1248
* you can use the writeCommand() slot. If MPlayer writes to its standard output
1249
* or standard error channel, the signals readStandardOutput() and
1250
* readStandardError() will be emitted.
1252
* \subsection controls Graphical controls
1254
* You can connect sliders for seeking and volume adjustment to an instance of
1255
* this class. Please use setSeekSlider() and setVolumeSlider(), respectively.
1257
* \section example Usage example
1259
* A minimal example using this widget to play a low-res version of
1260
* <a href="http://www.bigbuckbunny.org/">Big Buck Bunny</a> might look as follows.
1261
* Please note that the actual movie URL has been shortened for the sake of clarity.
1263
#include <QApplication>
1264
#include "qmpwidget.h"
1266
// Program entry point
1267
int main(int argc, char **argv)
1269
QApplication app(argc, argv);
1273
widget.start(QStringList("http://tinyurl.com/2vs2kg5"));
1280
* For further information about this project, please refer to the
1281
* <a href="index.html">main page</a>.
1285
* \enum QMPwidget::State
1286
* \brief MPlayer state
1288
* This enumeration is somewhat identical to <a href="http://doc.trolltech.com/phonon.html#State-enum">
1289
* Phonon's State enumeration</a>, except that it has an additional
1290
* member which is used when the MPlayer process has not been started yet (NotStartedState)
1293
* <tr><th>Constant</th><th>Value</th><th>Description</th></tr>
1295
* <td>\p QMPwidget::NotStartedState</td>
1297
* <td>The Mplayer process has not been started yet or has already terminated.</td>
1300
* <td>\p QMPwidget::IdleState</td>
1302
* <td>The MPlayer process has been started, but is idle and waiting for commands.</td>
1305
* <td>\p QMPwidget::LoadingState</td>
1307
* <td>The media file is being loaded, but playback has not been started yet.</td>
1310
* <td>\p QMPwidget::StoppedState</td>
1312
* <td>This constant is deprecated and is not being used</td>
1315
* <td>\p QMPwidget::PlayingState</td>
1320
* <td>\p QMPwidget::BufferingState</td>
1325
* <td>\p QMPwidget::PausedState</td>
1330
* <td>\p QMPwidget::ErrorState</td>
1338
* \enum QMPwidget::Mode
1339
* \brief Video playback modes
1341
* This enumeration describes valid modes for video playback. Please see \ref playbackmodes for a
1342
* detailed description of both modes.
1345
* <tr><th>Constant</th><th>Value</th><th>Description</th></tr>
1347
* <td>\p QMPwidget::EmbeddedMode</td>
1349
* <td>MPlayer will render directly into a Qt widget.</td>
1352
* <td>\p QMPwidget::PipedMode</td>
1354
* <td>MPlayer will write the video data into a FIFO which will be parsed in a seperate thread.\n
1355
The frames will be rendered by QMPwidget.</td>
1361
* \enum QMPwidget::SeekMode
1362
* \brief Seeking modes
1364
* This enumeration describes valid modes for seeking the media stream.
1367
* <tr><th>Constant</th><th>Value</th><th>Description</th></tr>
1369
* <td>\p QMPwidget::RelativeSeek</td>
1371
* <td>Relative seek in seconds</td>
1374
* <td>\p QMPwidget::PercantageSeek</td>
1376
* <td>Seek to a position given by a percentage of the whole movie duration</td>
1379
* <td>\p QMPwidget::AbsoluteSeek</td>
1381
* <td>Seek to a position given by an absolute time</td>
1387
* \fn void QMPwidget::stateChanged(int state)
1388
* \brief Emitted if the state has changed
1390
* This signal is emitted when the state of the MPlayer process changes.
1392
* \param state The new state
1396
* \fn void QMPwidget::error(const QString &reason)
1397
* \brief Emitted if the state has changed to QMPwidget::ErrorState
1399
* This signal is emitted when the state of the MPlayer process changes to QMPwidget::ErrorState.
1401
* \param reason Textual error description (may be empty)
1405
* \fn void QMPwidget::readStandardOutput(const QString &line)
1406
* \brief Signal for reading MPlayer's standard output
1408
* This signal is emitted when MPlayer wrote a line of text to its standard output channel.
1412
* \fn void QMPwidget::readStandardError(const QString &line)
1413
* \brief Signal for reading MPlayer's standard error
1415
* This signal is emitted when MPlayer wrote a line of text to its standard error channel.
1417
/* BEGIN_COMMON_COPYRIGHT_HEADER
1420
* Copyright: 2012 Labo A.L
1422
* Aaron Lewis <the.warl0ck.1989@gmail.com>
1424
* This program or library is free software; you can redistribute it
1425
* and/or modify it under the terms of the GNU Lesser General Public
1426
* License as published by the Free Software Foundation; either
1427
* version 2.1 of the License, or (at your option) any later version.
1429
* This library is distributed in the hope that it will be useful,
1430
* but WITHOUT ANY WARRANTY; without even the implied warranty of
1431
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1432
* Lesser General Public License for more details.
1434
* You should have received a copy of the GNU Lesser General
1435
* Public License along with this library; if not, write to the
1436
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1437
* Boston, MA 02110-1301 USA
1439
* END_COMMON_COPYRIGHT_HEADER */
2
1441
* qmpwidget - A Qt widget for embedding MPlayer
3
1442
* Copyright (C) 2010 by Jonas Gehring