~john.vrbanac/ubuntu-app-reviews/minicast

« back to all changes in this revision

Viewing changes to qmpwidget.cpp

  • Committer: Aaron Lewis
  • Date: 2012-07-09 07:22:35 UTC
  • Revision ID: the.warl0ck.1989@gmail.com-20120709072235-fubggzoo35iykjo4
Re-license

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* BEGIN_COMMON_COPYRIGHT_HEADER
 
2
 * (c)LGPL2+
 
3
 *
 
4
 * Copyright: 2012 Labo A.L
 
5
 * Authors:
 
6
 *   Aaron Lewis <the.warl0ck.1989@gmail.com>
 
7
 *
 
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.
 
12
 *
 
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.
 
17
 
 
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
 
22
 *
 
23
 * END_COMMON_COPYRIGHT_HEADER */
 
24
/*
 
25
 *  qmpwidget - A Qt widget for embedding MPlayer
 
26
 *  Copyright (C) 2010 by Jonas Gehring
 
27
 *
 
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.
 
32
 *
 
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.
 
37
 *
 
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/>.
 
40
 */
 
41
 
 
42
 
 
43
#include <QAbstractSlider>
 
44
#include <QKeyEvent>
 
45
#include <QLocalSocket>
 
46
#include <QPainter>
 
47
#include <QProcess>
 
48
#include <QStringList>
 
49
#include <QTemporaryFile>
 
50
#include <QThread>
 
51
#include <QtDebug>
 
52
 
 
53
#ifdef QT_OPENGL_LIB
 
54
 #include <QGLWidget>
 
55
#endif
 
56
 
 
57
#include "qmpwidget.h"
 
58
 
 
59
//#define QMP_DEBUG_OUTPUT
 
60
 
 
61
#ifdef QMP_USE_YUVPIPE
 
62
 #include "qmpyuvreader.h"
 
63
#endif // QMP_USE_YUVPIPE
 
64
 
 
65
 
 
66
// A plain video widget
 
67
class QMPPlainVideoWidget : public QWidget
 
68
{
 
69
        Q_OBJECT
 
70
 
 
71
        public:
 
72
                QMPPlainVideoWidget(QWidget *parent = 0)
 
73
                        : QWidget(parent)
 
74
                {
 
75
                        setAttribute(Qt::WA_NoSystemBackground);
 
76
                        setMouseTracking(true);
 
77
                }
 
78
 
 
79
                void showUserImage(const QImage &image)
 
80
                {
 
81
                        m_userImage = image;
 
82
                        update();
 
83
                }
 
84
 
 
85
        public slots:
 
86
                void displayImage(const QImage &image)
 
87
                {
 
88
                        m_pixmap = QPixmap::fromImage(image);
 
89
                        update();
 
90
                }
 
91
 
 
92
        protected:
 
93
                void paintEvent(QPaintEvent *event)
 
94
                {
 
95
                        Q_UNUSED(event);
 
96
                        QPainter p(this);
 
97
                        p.setCompositionMode(QPainter::CompositionMode_Source);
 
98
 
 
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);
 
104
                        } else {
 
105
                                p.fillRect(rect(), Qt::black);
 
106
                        }
 
107
                        p.end();
 
108
                }
 
109
 
 
110
        private:
 
111
                QPixmap m_pixmap;
 
112
                QImage m_userImage;
 
113
};
 
114
 
 
115
 
 
116
#ifdef QT_OPENGL_LIB
 
117
 
 
118
// A OpenGL video widget
 
119
class QMPOpenGLVideoWidget : public QGLWidget
 
120
{
 
121
        Q_OBJECT
 
122
 
 
123
        public:
 
124
                QMPOpenGLVideoWidget(QWidget *parent = 0)
 
125
                        : QGLWidget(parent), m_tex(-1)
 
126
                {
 
127
                        setMouseTracking(true);
 
128
                }
 
129
 
 
130
                void showUserImage(const QImage &image)
 
131
                {
 
132
                        m_userImage = image;
 
133
 
 
134
                        makeCurrent();
 
135
                        if (m_tex >= 0) {
 
136
                                deleteTexture(m_tex);
 
137
                        }
 
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);
 
142
                        } else {
 
143
                                glViewport(0, 0, width(), qMax(height(), 1));
 
144
                        }
 
145
                        updateGL();
 
146
                }
 
147
 
 
148
        public slots:
 
149
                void displayImage(const QImage &image)
 
150
                {
 
151
                        if (!m_userImage.isNull())  {
 
152
                                return;
 
153
                        }
 
154
 
 
155
                        makeCurrent();
 
156
                        if (m_tex >= 0) {
 
157
                                deleteTexture(m_tex);
 
158
                        }
 
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);
 
162
                        updateGL();
 
163
                }
 
164
 
 
165
        protected:
 
166
                void initializeGL()
 
167
                {
 
168
                        glEnable(GL_TEXTURE_2D);
 
169
                        glClearColor(0, 0, 0, 0);
 
170
                        glClearDepth(1);
 
171
                }
 
172
 
 
173
                void resizeGL(int w, int h)
 
174
                {
 
175
                        glViewport(0, 0, w, qMax(h, 1));
 
176
                }
 
177
 
 
178
                void paintGL()
 
179
                {
 
180
                        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
181
                        glLoadIdentity();
 
182
                        if (m_tex >= 0) {
 
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());
 
188
                                }
 
189
                                glBegin(GL_QUADS);
 
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);
 
194
                                glEnd();
 
195
                        }
 
196
                }
 
197
 
 
198
        private:
 
199
                QImage m_userImage;
 
200
                int m_tex;
 
201
};
 
202
 
 
203
#endif // QT_OPENGL_LIB
 
204
 
 
205
 
 
206
// A custom QProcess designed for the MPlayer slave interface
 
207
class QMPProcess : public QProcess
 
208
{
 
209
        Q_OBJECT
 
210
 
 
211
        public:
 
212
                QMPProcess(QObject *parent = 0)
 
213
                        : QProcess(parent), m_state(QMPwidget::NotStartedState), m_mplayerPath("mplayer"),
 
214
                          m_fakeInputconf(NULL)
 
215
#ifdef QMP_USE_YUVPIPE
 
216
                          , m_yuvReader(NULL)
 
217
#endif
 
218
                {
 
219
                        resetValues();
 
220
 
 
221
#ifdef Q_WS_WIN
 
222
                        m_mode = QMPwidget::EmbeddedMode;
 
223
                        m_videoOutput = "directx,directx:noaccel";
 
224
#elif defined(Q_WS_X11)
 
225
                        m_mode = QMPwidget::EmbeddedMode;
 
226
 #ifdef QT_OPENGL_LIB
 
227
                        m_videoOutput = "gl2,gl,xv";
 
228
 #else
 
229
                        m_videoOutput = "xv";
 
230
 #endif
 
231
#elif defined(Q_WS_MAC)
 
232
                        m_mode = QMPwidget::PipeMode;
 
233
 #ifdef QT_OPENGL_LIB
 
234
                        m_videoOutput = "gl,quartz";
 
235
 #else
 
236
                        m_videoOutput = "quartz";
 
237
 #endif
 
238
#endif
 
239
 
 
240
                        m_movieFinishedTimer.setSingleShot(true);
 
241
                        m_movieFinishedTimer.setInterval(100);
 
242
 
 
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()));
 
247
                }
 
248
 
 
249
                ~QMPProcess()
 
250
                {
 
251
#ifdef QMP_USE_YUVPIPE
 
252
                        if (m_yuvReader != NULL) {
 
253
                                m_yuvReader->stop();
 
254
                        }
 
255
#endif
 
256
                        if (m_fakeInputconf != NULL) {
 
257
                                delete m_fakeInputconf;
 
258
                        }
 
259
                }
 
260
 
 
261
                // Starts the MPlayer process in idle mode
 
262
                void start(QWidget *widget, const QStringList &args)
 
263
                {
 
264
                        if (m_mode == QMPwidget::PipeMode) {
 
265
#ifdef QMP_USE_YUVPIPE
 
266
                                m_yuvReader = new QMPYuvReader(this);
 
267
#else
 
268
                                m_mode = QMPwidget::EmbeddedMode;
 
269
#endif
 
270
                        }
 
271
 
 
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;
 
282
                                        }
 
283
                                }
 
284
                        }
 
285
 
 
286
                        QStringList myargs;
 
287
                        myargs += "-slave";
 
288
                        myargs += "-idle";
 
289
                        myargs += "-noquiet";
 
290
                        myargs += "-identify";
 
291
                        myargs += "-nomouseinput";
 
292
                        myargs += "-nokeepaspect";
 
293
                        myargs += "-monitorpixelaspect";
 
294
                        myargs += "1";
 
295
                        if (!useFakeInputconf) {
 
296
                                myargs += "-input";
 
297
                                myargs += "nodefault-bindings:conf=/dev/null";
 
298
                        } else {
 
299
#ifndef Q_WS_WIN
 
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);
 
305
                                        } else {
 
306
                                                delete m_fakeInputconf;
 
307
                                                m_fakeInputconf = NULL;
 
308
                                        }
 
309
                                }
 
310
                                if (m_fakeInputconf != NULL) {
 
311
                                        myargs += "-input";
 
312
                                        myargs += QString("conf=%1").arg(m_fakeInputconf->fileName());
 
313
                                }
 
314
#endif
 
315
                        }
 
316
 
 
317
                        if (m_mode == QMPwidget::EmbeddedMode) {
 
318
                                myargs += "-wid";
 
319
                                myargs += QString::number((int)widget->winId());
 
320
                                if (!m_videoOutput.isEmpty()) {
 
321
                                        myargs += "-vo";
 
322
                                        myargs += m_videoOutput;
 
323
                                }
 
324
                        } else {
 
325
#ifdef QMP_USE_YUVPIPE
 
326
                                myargs += "-vo";
 
327
                                myargs += QString("yuv4mpeg:file=%1").arg(m_yuvReader->m_pipe);
 
328
#endif
 
329
                        }
 
330
 
 
331
                        myargs += args;
 
332
#ifdef QMP_DEBUG_OUTPUT
 
333
                        qDebug() << myargs;
 
334
#endif
 
335
 
 
336
                        QProcess::start(m_mplayerPath, myargs);
 
337
                        changeState(QMPwidget::IdleState);
 
338
 
 
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();
 
343
#endif
 
344
                        }
 
345
                }
 
346
 
 
347
                QString mplayerVersion()
 
348
                {
 
349
                        QProcess p;
 
350
                        p.start(m_mplayerPath, QStringList("-version"));
 
351
                        if (!p.waitForStarted()) {
 
352
                                return QString();
 
353
                        }
 
354
                        if (!p.waitForFinished()) {
 
355
                                return QString();
 
356
                        }
 
357
 
 
358
                        QString output = QString(p.readAll());
 
359
                        QRegExp re("MPlayer ([^ ]*)");
 
360
                        if (re.indexIn(output) > -1) {
 
361
                                return re.cap(1);
 
362
                        }
 
363
                        return output;
 
364
                }
 
365
 
 
366
                QProcess::ProcessState processState() const
 
367
                {
 
368
                        return QProcess::state();
 
369
                }
 
370
 
 
371
                void writeCommand(const QString &command)
 
372
                {
 
373
#ifdef QMP_DEBUG_OUTPUT
 
374
                        qDebug("in: \"%s\"", qPrintable(command));
 
375
#endif
 
376
                        QProcess::write(command.toLocal8Bit()+"\n");
 
377
                }
 
378
 
 
379
                void quit()
 
380
                {
 
381
                        writeCommand("quit");
 
382
                        QProcess::waitForFinished(100);
 
383
                        if (QProcess::state() == QProcess::Running) {
 
384
                                QProcess::kill();
 
385
                        }
 
386
                        QProcess::waitForFinished(-1);
 
387
                }
 
388
 
 
389
                void pause()
 
390
                {
 
391
                        writeCommand("pause");
 
392
                }
 
393
 
 
394
                void stop()
 
395
                {
 
396
                        writeCommand("stop");
 
397
                }
 
398
 
 
399
        signals:
 
400
                void stateChanged(int state);
 
401
                void streamPositionChanged(double position);
 
402
                void error(const QString &reason);
 
403
 
 
404
                void readStandardOutput(const QString &line);
 
405
                void readStandardError(const QString &line);
 
406
 
 
407
        private slots:
 
408
                void readStdout()
 
409
                {
 
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]));
 
415
#endif
 
416
                                parseLine(lines[i]);
 
417
                                emit readStandardOutput(lines[i]);
 
418
                        }
 
419
                }
 
420
 
 
421
                void readStderr()
 
422
                {
 
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]));
 
428
#endif
 
429
                                parseLine(lines[i]);
 
430
                                emit readStandardError(lines[i]);
 
431
                        }
 
432
                }
 
433
 
 
434
                void finished()
 
435
                {
 
436
                        // Called if the *process* has finished
 
437
                        changeState(QMPwidget::NotStartedState);
 
438
                }
 
439
 
 
440
                void movieFinished()
 
441
                {
 
442
                        if (m_state == QMPwidget::PlayingState) {
 
443
                                changeState(QMPwidget::IdleState);
 
444
                        }
 
445
                }
 
446
 
 
447
        private:
 
448
                // Parses a line of MPlayer output
 
449
                void parseLine(const QString &line)
 
450
                {
 
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);
 
469
                                }
 
470
                                parsePosition(line);
 
471
                        } else if (line.startsWith("Exiting...")) {
 
472
                                changeState(QMPwidget::NotStartedState);
 
473
                        }
 
474
                }
 
475
 
 
476
                // Parses MPlayer's media identification output
 
477
                void parseMediaInfo(const QString &line)
 
478
                {
 
479
                        QStringList info = line.split("=");
 
480
                        if (info.count() < 2) {
 
481
                                return;
 
482
                        }
 
483
 
 
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();
 
494
 
 
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();
 
503
 
 
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();
 
508
 
 
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]);
 
513
                        }
 
514
                }
 
515
 
 
516
                // Parsas MPlayer's position output
 
517
                void parsePosition(const QString &line)
 
518
                {
 
519
            static QRegExp rx("[ :]");
 
520
                        QStringList info = line.split(rx, QString::SkipEmptyParts);
 
521
 
 
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();
 
526
 
 
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();
 
531
                                        }
 
532
                                }
 
533
            }
 
534
 
 
535
                        if (oldpos != m_streamPosition) {
 
536
                                emit streamPositionChanged(m_streamPosition);
 
537
                        }
 
538
                }
 
539
 
 
540
                // Changes the current state, possibly emitting multiple signals
 
541
                void changeState(QMPwidget::State state, const QString &comment = QString())
 
542
                {
 
543
#ifdef QMP_USE_YUVPIPE
 
544
                        if (m_yuvReader != NULL && (state == QMPwidget::ErrorState || state == QMPwidget::NotStartedState)) {
 
545
                                m_yuvReader->stop();
 
546
                                m_yuvReader->deleteLater();
 
547
                        }
 
548
#endif
 
549
 
 
550
                        if (m_state == state) {
 
551
                                return;
 
552
                        }
 
553
 
 
554
                        if (m_state == QMPwidget::PlayingState) {
 
555
                                m_movieFinishedTimer.stop();
 
556
                        }
 
557
 
 
558
                        m_state = state;
 
559
                        emit stateChanged(m_state);
 
560
 
 
561
                        switch (m_state) {
 
562
                                case QMPwidget::NotStartedState:
 
563
                                        resetValues();
 
564
                                        break;
 
565
 
 
566
                                case QMPwidget::ErrorState:
 
567
                                        emit error(comment);
 
568
                                        resetValues();
 
569
                                        break;
 
570
 
 
571
                                default: break;
 
572
                        }
 
573
                }
 
574
 
 
575
                // Resets the media info and position values
 
576
                void resetValues()
 
577
                {
 
578
                        m_mediaInfo = QMPwidget::MediaInfo();
 
579
                        m_streamPosition = -1;
 
580
                }
 
581
 
 
582
                // Writes a dummy input configuration to the given device
 
583
                void writeFakeInputconf(QIODevice *device)
 
584
                {
 
585
                        // Query list of supported keys
 
586
                        QProcess p;
 
587
                        p.start(m_mplayerPath, QStringList("-input") += "keylist");
 
588
                        if (!p.waitForStarted()) {
 
589
                                return;
 
590
                        }
 
591
                        if (!p.waitForFinished()) {
 
592
                                return;
 
593
                        }
 
594
                        QStringList keys = QString(p.readAll()).split("\n", QString::SkipEmptyParts);
 
595
 
 
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;
 
601
                        }
 
602
                }
 
603
 
 
604
        public:
 
605
                QMPwidget::State m_state;
 
606
 
 
607
                QString m_mplayerPath;
 
608
                QString m_videoOutput;
 
609
                QString m_pipe;
 
610
                QMPwidget::Mode m_mode;
 
611
 
 
612
                QMPwidget::MediaInfo m_mediaInfo;
 
613
                double m_streamPosition; // This is the video position
 
614
                QTimer m_movieFinishedTimer;
 
615
 
 
616
                QString m_currentTag;
 
617
 
 
618
                QTemporaryFile *m_fakeInputconf;
 
619
 
 
620
#ifdef QMP_USE_YUVPIPE
 
621
                QPointer<QMPYuvReader> m_yuvReader;
 
622
#endif
 
623
};
 
624
 
 
625
 
 
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)
 
630
{
 
631
 
 
632
}
 
633
 
 
634
 
 
635
/*!
 
636
 * \brief Constructor
 
637
 *
 
638
 * \param parent Parent widget
 
639
 */
 
640
QMPwidget::QMPwidget(QWidget *parent)
 
641
        : QWidget(parent)
 
642
{
 
643
        setFocusPolicy(Qt::StrongFocus);
 
644
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 
645
 
 
646
#ifdef QT_OPENGL_LIB
 
647
        m_widget = new QMPOpenGLVideoWidget(this);
 
648
#else
 
649
        m_widget = new QMPPlainVideoWidget(this);
 
650
#endif
 
651
 
 
652
        QPalette p = palette();
 
653
        p.setColor(QPalette::Window, Qt::black);
 
654
        setPalette(p);
 
655
 
 
656
        m_seekTimer.setInterval(50);
 
657
        m_seekTimer.setSingleShot(true);
 
658
        connect(&m_seekTimer, SIGNAL(timeout()), this, SLOT(delayedSeek()));
 
659
 
 
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 &)));
 
666
}
 
667
 
 
668
/*!
 
669
 * \brief Destructor
 
670
 * \details
 
671
 * This function will ask the MPlayer process to quit and block until it has really
 
672
 * finished.
 
673
 */
 
674
QMPwidget::~QMPwidget()
 
675
{
 
676
        if (m_process->processState() == QProcess::Running) {
 
677
                m_process->quit();
 
678
        }
 
679
        delete m_process;
 
680
}
 
681
 
 
682
/*!
 
683
 * \brief Returns the current MPlayer process state
 
684
 *
 
685
 * \returns The process state
 
686
 */
 
687
QMPwidget::State QMPwidget::state() const
 
688
{
 
689
        return m_process->m_state;
 
690
}
 
691
 
 
692
/*!
 
693
 * \brief Returns the current media info object 
 
694
 * \details
 
695
 * Please check QMPwidget::MediaInfo::ok to make sure the media
 
696
 * information has been fully parsed.
 
697
 *
 
698
 * \returns The media info object
 
699
 */
 
700
QMPwidget::MediaInfo QMPwidget::mediaInfo() const
 
701
{
 
702
        return m_process->m_mediaInfo;
 
703
}
 
704
 
 
705
/*!
 
706
 * \brief Returns the current playback position
 
707
 *
 
708
 * \returns The current playback position in seconds
 
709
 * \sa seek()
 
710
 */
 
711
double QMPwidget::tell() const
 
712
{
 
713
        return m_process->m_streamPosition;
 
714
}
 
715
 
 
716
/*!
 
717
 * \brief Returns the MPlayer process
 
718
 *
 
719
 * \returns The MPlayer process
 
720
 */
 
721
QProcess *QMPwidget::process() const
 
722
{
 
723
        return m_process;
 
724
}
 
725
 
 
726
/*!
 
727
 * \brief Sets the video playback mode
 
728
 * \details
 
729
 * Please see \ref playbackmodes for a discussion of the available modes.
 
730
 *
 
731
 * \param mode The video playback mode
 
732
 * \sa mode()
 
733
 */
 
734
void QMPwidget::setMode(Mode mode)
 
735
{
 
736
#ifdef QMP_USE_YUVPIPE
 
737
        m_process->m_mode = mode;
 
738
#else
 
739
        Q_UNUSED(mode)
 
740
#endif
 
741
}
 
742
 
 
743
/*!
 
744
 * \brief Returns the current video playback mode
 
745
 *
 
746
 * \returns The current video playback mode
 
747
 * \sa setMode()
 
748
 */
 
749
QMPwidget::Mode QMPwidget::mode() const
 
750
{
 
751
        return m_process->m_mode;
 
752
}
 
753
 
 
754
/*!
 
755
 * \brief Sets the video output mode
 
756
 * \details
 
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.
 
760
 *
 
761
 * Per default, this string will have the following values:
 
762
 * <table>
 
763
 *  <tr><th>System</th><th>Configuration</th><th>Value</th></tr>
 
764
 *  <tr>
 
765
 *   <td>Windows</td>
 
766
 *   <td></td>
 
767
 *   <td>\p "directx,directx:noaccel"</td>
 
768
 *  </tr>
 
769
 *  <tr>
 
770
 *   <td>X11</td>
 
771
 *   <td>Compiled without OpenGL support</td>
 
772
 *   <td>\p "xv"</td>
 
773
 *  </tr>
 
774
 *  <tr>
 
775
 *   <td>X11</td>
 
776
 *   <td>Compiled with OpenGL support</td>
 
777
 *   <td>\p "gl2,gl,xv"</td>
 
778
 *  </tr>
 
779
 *  <tr>
 
780
 *   <td>Mac OS X</td>
 
781
 *   <td>Compiled without OpenGL support</td>
 
782
 *   <td>\p "quartz"</td>
 
783
 *  </tr>
 
784
 *  <tr>
 
785
 *   <td>Mac OS X</td>
 
786
 *   <td>Compiled with OpenGL support</td>
 
787
 *   <td>\p "gl,quartz"</td>
 
788
 *  </tr>
 
789
 * </table>
 
790
 *
 
791
 *
 
792
 * \param output The video output mode string
 
793
 * \sa videoOutput()
 
794
 */
 
795
void QMPwidget::setVideoOutput(const QString &output)
 
796
{
 
797
        m_process->m_videoOutput = output;
 
798
}
 
799
 
 
800
/*!
 
801
 * \brief Returns the current video output mode
 
802
 *
 
803
 * \returns The current video output mode
 
804
 * \sa setVideoOutput()
 
805
 */
 
806
QString QMPwidget::videoOutput() const
 
807
{
 
808
        return m_process->m_videoOutput;
 
809
}
 
810
 
 
811
/*!
 
812
 * \brief Sets the path to the MPlayer executable
 
813
 * \details
 
814
 * Per default, it is assumed the MPlayer executable is
 
815
 * available in the current OS path. Therefore, this value is
 
816
 * set to "mplayer".
 
817
 *
 
818
 * \param path Path to the MPlayer executable
 
819
 * \sa mplayerPath()
 
820
 */
 
821
void QMPwidget::setMPlayerPath(const QString &path)
 
822
{
 
823
        m_process->m_mplayerPath = path;
 
824
}
 
825
 
 
826
/*!
 
827
 * \brief Returns the current path to the MPlayer executable
 
828
 *
 
829
 * \returns The path to the MPlayer executable
 
830
 * \sa setMPlayerPath()
 
831
 */
 
832
QString QMPwidget::mplayerPath() const
 
833
{
 
834
        return m_process->m_mplayerPath;
 
835
}
 
836
 
 
837
/*!
 
838
 * \brief Returns the version string of the MPlayer executable
 
839
 * \details
 
840
 * If the mplayer  
 
841
 *
 
842
 *
 
843
 * \returns The version string of the MPlayer executable
 
844
 */
 
845
QString QMPwidget::mplayerVersion()
 
846
{
 
847
        return m_process->mplayerVersion();
 
848
}
 
849
 
 
850
/*!
 
851
 * \brief Sets a seeking slider for this widget
 
852
 */
 
853
void QMPwidget::setSeekSlider(QAbstractSlider *slider)
 
854
{
 
855
        if (m_seekSlider) {
 
856
                m_seekSlider->disconnect(this);
 
857
                disconnect(m_seekSlider);
 
858
        }
 
859
 
 
860
        if (m_process->m_mediaInfo.ok) {
 
861
                slider->setRange(0, m_process->m_mediaInfo.length);
 
862
        }
 
863
        if (m_process->m_mediaInfo.ok) {
 
864
                slider->setEnabled(m_process->m_mediaInfo.seekable);
 
865
        }
 
866
 
 
867
        connect(slider, SIGNAL(valueChanged(int)), this, SLOT(seek(int)));
 
868
        m_seekSlider = slider;
 
869
}
 
870
 
 
871
/*!
 
872
 * \brief Sets a volume slider for this widget
 
873
 */
 
874
void QMPwidget::setVolumeSlider(QAbstractSlider *slider)
 
875
{
 
876
        if (m_volumeSlider) {
 
877
                m_volumeSlider->disconnect(this);
 
878
                disconnect(m_volumeSlider);
 
879
        }
 
880
 
 
881
 
 
882
        slider->setRange(0, 100);
 
883
        slider->setValue(100); // TODO
 
884
 
 
885
        connect(slider, SIGNAL(valueChanged(int)), this, SLOT(setVolume(int)));
 
886
        m_volumeSlider = slider;
 
887
}
 
888
 
 
889
/*!
 
890
 * \brief Shows a custom image
 
891
 * \details
 
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
 
894
 * with a null image.
 
895
 *
 
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.
 
898
 *
 
899
 * \param image Custom image
 
900
 */
 
901
void QMPwidget::showImage(const QImage &image)
 
902
{
 
903
#ifdef QT_OPENGL_LIB
 
904
        qobject_cast<QMPOpenGLVideoWidget *>(m_widget)->showUserImage(image);
 
905
#else
 
906
        qobject_cast<QMPPlainVideoWidget*>(m_widget)->showUserImage(image);
 
907
#endif
 
908
}
 
909
 
 
910
/*!
 
911
 * \brief Returns a suitable size hint for this widget
 
912
 * \details
 
913
 * This function is used internally by Qt.
 
914
 */
 
915
QSize QMPwidget::sizeHint() const
 
916
{
 
917
        if (m_process->m_mediaInfo.ok && !m_process->m_mediaInfo.size.isNull()) {
 
918
                return m_process->m_mediaInfo.size;
 
919
        }
 
920
        return QWidget::sizeHint();
 
921
}
 
922
 
 
923
/*!
 
924
 * \brief Starts the MPlayer process with the given arguments
 
925
 * \details
 
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().
 
928
 *
 
929
 * \param args MPlayer command line arguments
 
930
 */
 
931
void QMPwidget::start(const QStringList &args)
 
932
{
 
933
        if (m_process->processState() == QProcess::Running) {
 
934
                m_process->quit();
 
935
        }
 
936
        m_process->start(m_widget, args);
 
937
}
 
938
 
 
939
/*!
 
940
 * \brief Loads a file or url and starts playback
 
941
 *
 
942
 * \param url File patho or url
 
943
 */
 
944
void QMPwidget::load(const QString &url)
 
945
{
 
946
        Q_ASSERT_X(m_process->state() != QProcess::NotRunning, "QMPwidget::load()", "MPlayer process not started yet");
 
947
 
 
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");
 
954
 
 
955
        writeCommand(QString("loadfile '%1'").arg(url));
 
956
}
 
957
 
 
958
/*!
 
959
 * \brief Resumes playback
 
960
 */
 
961
void QMPwidget::play()
 
962
{
 
963
        if (m_process->m_state == PausedState) {
 
964
                m_process->pause();
 
965
        }
 
966
}
 
967
 
 
968
/*!
 
969
 * \brief Pauses playback
 
970
 */
 
971
void QMPwidget::pause()
 
972
{
 
973
        if (m_process->m_state == PlayingState) {
 
974
                m_process->pause();
 
975
        }
 
976
}
 
977
 
 
978
/*!
 
979
 * \brief Stops playback
 
980
 */
 
981
void QMPwidget::stop()
 
982
{
 
983
        m_process->stop();
 
984
}
 
985
 
 
986
/*!
 
987
 * \brief Media playback seeking
 
988
 *
 
989
 * \param offset Seeking offset in seconds
 
990
 * \param whence Seeking mode
 
991
 * \returns \p true If the seeking mode is valid
 
992
 * \sa tell()
 
993
 */
 
994
bool QMPwidget::seek(int offset, int whence)
 
995
{
 
996
        return seek(double(offset), whence);
 
997
}
 
998
 
 
999
/*!
 
1000
 * \brief Media playback seeking
 
1001
 *
 
1002
 * \param offset Seeking offset in seconds
 
1003
 * \param whence Seeking mode
 
1004
 * \returns \p true If the seeking mode is valid
 
1005
 * \sa tell()
 
1006
 */
 
1007
bool QMPwidget::seek(double offset, int whence)
 
1008
{
 
1009
        m_seekTimer.stop(); // Cancel all current seek requests
 
1010
 
 
1011
        switch (whence) {
 
1012
                case RelativeSeek:
 
1013
                case PercentageSeek:
 
1014
                case AbsoluteSeek:
 
1015
                        break;
 
1016
                default:
 
1017
                        return false;
 
1018
        }
 
1019
 
 
1020
        // Schedule seek request
 
1021
        m_seekCommand = QString("seek %1 %2").arg(offset).arg(whence);
 
1022
        m_seekTimer.start();
 
1023
        return true;
 
1024
}
 
1025
 
 
1026
/*!
 
1027
 * \brief Toggles full-screen mode
 
1028
 */
 
1029
void QMPwidget::toggleFullScreen()
 
1030
{
 
1031
        if (!isFullScreen()) {
 
1032
                m_windowFlags = windowFlags() & (Qt::Window);
 
1033
                m_geometry = geometry();
 
1034
                setWindowFlags((windowFlags() | Qt::Window));
 
1035
                // From Phonon::VideoWidget
 
1036
#ifdef Q_WS_X11
 
1037
                show();
 
1038
                raise();
 
1039
                setWindowState(windowState() | Qt::WindowFullScreen);
 
1040
#else
 
1041
                setWindowState(windowState() | Qt::WindowFullScreen);
 
1042
                show();
 
1043
#endif
 
1044
        } else {
 
1045
                setWindowFlags((windowFlags() ^ (Qt::Window)) | m_windowFlags);
 
1046
                setWindowState(windowState() & ~Qt::WindowFullScreen);
 
1047
                setGeometry(m_geometry);
 
1048
                show();
 
1049
        }
 
1050
}
 
1051
 
 
1052
/*!
 
1053
 * \brief Sends a command to the MPlayer process
 
1054
 * \details
 
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.
 
1059
 *
 
1060
 * For a complete list of commands for MPlayer's slave mode, see
 
1061
 * http://www.mplayerhq.hu/DOCS/tech/slave.txt .
 
1062
 *
 
1063
 * \param command The command line. A newline character will be added internally.
 
1064
 */
 
1065
void QMPwidget::writeCommand(const QString &command)
 
1066
{
 
1067
        m_process->writeCommand(command);
 
1068
}
 
1069
 
 
1070
/*!
 
1071
 * \brief Mouse double click event handler
 
1072
 * \details
 
1073
 * This implementation will toggle full screen and accept the event
 
1074
 *
 
1075
 * \param event Mouse event
 
1076
 */
 
1077
void QMPwidget::mouseDoubleClickEvent(QMouseEvent *event)
 
1078
{
 
1079
        toggleFullScreen();
 
1080
        event->accept();
 
1081
}
 
1082
 
 
1083
/*!
 
1084
 * \brief Keyboard press event handler
 
1085
 * \details
 
1086
 * This implementation tries to resemble the classic MPlayer interface. For a
 
1087
 * full list of supported key codes, see \ref shortcuts.
 
1088
 *
 
1089
 * \param event Key event
 
1090
 */
 
1091
void QMPwidget::keyPressEvent(QKeyEvent *event)
 
1092
{
 
1093
        bool accept = true;
 
1094
        switch (event->key()) {
 
1095
                case Qt::Key_P:
 
1096
                case Qt::Key_Space:
 
1097
                        if (state() == PlayingState) {
 
1098
                                pause();
 
1099
                        } else if (state() == PausedState) {
 
1100
                                play();
 
1101
                        }
 
1102
                        break;
 
1103
 
 
1104
                case Qt::Key_F:
 
1105
                        toggleFullScreen();
 
1106
                        break;
 
1107
 
 
1108
                case Qt::Key_Q:
 
1109
                case Qt::Key_Escape:
 
1110
                        stop();
 
1111
                        break;
 
1112
 
 
1113
                case Qt::Key_Minus:
 
1114
                        writeCommand("audio_delay -0.1");
 
1115
                        break;
 
1116
                case Qt::Key_Plus:
 
1117
                        writeCommand("audio_delay 0.1");
 
1118
                        break;
 
1119
 
 
1120
                case Qt::Key_Left:
 
1121
                        seek(-10, RelativeSeek);
 
1122
                        break;
 
1123
                case Qt::Key_Right:
 
1124
                        seek(10, RelativeSeek);
 
1125
                        break;
 
1126
                case Qt::Key_Down:
 
1127
                        seek(-60, RelativeSeek);
 
1128
                        break;
 
1129
                case Qt::Key_Up:
 
1130
                        seek(60, RelativeSeek);
 
1131
                        break;
 
1132
                case Qt::Key_PageDown:
 
1133
                        seek(-600, RelativeSeek);
 
1134
                        break;
 
1135
                case Qt::Key_PageUp:
 
1136
                        seek(600, RelativeSeek);
 
1137
                        break;
 
1138
 
 
1139
                case Qt::Key_Asterisk:
 
1140
                        writeCommand("volume 10");
 
1141
                        break;
 
1142
                case Qt::Key_Slash:
 
1143
                        writeCommand("volume -10");
 
1144
                        break;
 
1145
 
 
1146
                case Qt::Key_X:
 
1147
                        writeCommand("sub_delay 0.1");
 
1148
                        break;
 
1149
                case Qt::Key_Z:
 
1150
                        writeCommand("sub_delay -0.1");
 
1151
                        break;
 
1152
 
 
1153
                default:
 
1154
                        accept = false;
 
1155
                        break;
 
1156
        }
 
1157
 
 
1158
        event->setAccepted(accept);
 
1159
}
 
1160
 
 
1161
/*!
 
1162
 * \brief Resize event handler
 
1163
 * \details
 
1164
 * If you reimplement this function, you need to call this handler, too.
 
1165
 *
 
1166
 * \param event Resize event
 
1167
 */
 
1168
void QMPwidget::resizeEvent(QResizeEvent *event)
 
1169
{
 
1170
        Q_UNUSED(event);
 
1171
        updateWidgetSize();
 
1172
}
 
1173
 
 
1174
void QMPwidget::updateWidgetSize()
 
1175
{
 
1176
        if (!m_process->m_mediaInfo.size.isNull()) {
 
1177
                QSize mediaSize = m_process->m_mediaInfo.size;
 
1178
                QSize widgetSize = size();
 
1179
 
 
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);
 
1184
        } else {
 
1185
                m_widget->setGeometry(QRect(QPoint(0, 0), size()));
 
1186
        }
 
1187
}
 
1188
 
 
1189
void QMPwidget::delayedSeek()
 
1190
{
 
1191
        if (!m_seekCommand.isEmpty()) {
 
1192
                writeCommand(m_seekCommand);
 
1193
                m_seekCommand = QString();
 
1194
        }
 
1195
}
 
1196
 
 
1197
void QMPwidget::setVolume(int volume)
 
1198
{
 
1199
        writeCommand(QString("volume %1 1").arg(volume));
 
1200
}
 
1201
 
 
1202
void QMPwidget::mpStateChanged(int state)
 
1203
{
 
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);
 
1207
        }
 
1208
 
 
1209
        updateWidgetSize();
 
1210
        emit stateChanged(state);
 
1211
}
 
1212
 
 
1213
void QMPwidget::mpStreamPositionChanged(double position)
 
1214
{
 
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)));
 
1219
        }
 
1220
}
 
1221
 
 
1222
void QMPwidget::mpVolumeChanged(int volume)
 
1223
{
 
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)));
 
1228
        }
 
1229
}
 
1230
 
 
1231
 
 
1232
#include "qmpwidget.moc"
 
1233
 
 
1234
 
 
1235
/* Documentation follows */
 
1236
 
 
1237
/*!
 
1238
 * \class QMPwidget
 
1239
 * \brief A Qt widget for embedding MPlayer
 
1240
 * \details
 
1241
 *
 
1242
 * \section Overview
 
1243
 *
 
1244
 * \subsection comm MPlayer communication
 
1245
 *
 
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.
 
1251
 *
 
1252
 * \subsection controls Graphical controls
 
1253
 *
 
1254
 * You can connect sliders for seeking and volume adjustment to an instance of
 
1255
 * this class. Please use setSeekSlider() and setVolumeSlider(), respectively.
 
1256
 *
 
1257
 * \section example Usage example
 
1258
 *
 
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.
 
1262
\code
 
1263
#include <QApplication>
 
1264
#include "qmpwidget.h"
 
1265
 
 
1266
// Program entry point
 
1267
int main(int argc, char **argv)
 
1268
{
 
1269
        QApplication app(argc, argv);
 
1270
 
 
1271
        QMPwidget widget;
 
1272
        widget.show();
 
1273
        widget.start(QStringList("http://tinyurl.com/2vs2kg5"));
 
1274
 
 
1275
        return app.exec();
 
1276
}
 
1277
\endcode
 
1278
 *
 
1279
 *
 
1280
 * For further information about this project, please refer to the
 
1281
 * <a href="index.html">main page</a>.
 
1282
 */
 
1283
 
 
1284
/*!
 
1285
 * \enum QMPwidget::State
 
1286
 * \brief MPlayer state
 
1287
 * \details
 
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)
 
1291
 *
 
1292
 * <table>
 
1293
 *  <tr><th>Constant</th><th>Value</th><th>Description</th></tr>
 
1294
 *  <tr>
 
1295
 *   <td>\p QMPwidget::NotStartedState</td>
 
1296
 *   <td>\p -1</td>
 
1297
 *   <td>The Mplayer process has not been started yet or has already terminated.</td>
 
1298
 *  </tr>
 
1299
 *  <tr>
 
1300
 *   <td>\p QMPwidget::IdleState</td>
 
1301
 *   <td>\p 0</td>
 
1302
 *   <td>The MPlayer process has been started, but is idle and waiting for commands.</td>
 
1303
 *  </tr>
 
1304
 *  <tr>
 
1305
 *   <td>\p QMPwidget::LoadingState</td>
 
1306
 *   <td>\p 1</td>
 
1307
 *   <td>The media file is being loaded, but playback has not been started yet.</td>
 
1308
 *  </tr>
 
1309
 *  <tr>
 
1310
 *   <td>\p QMPwidget::StoppedState</td>
 
1311
 *   <td>\p 2</td>
 
1312
 *   <td>This constant is deprecated and is not being used</td>
 
1313
 *  </tr>
 
1314
 *  <tr>
 
1315
 *   <td>\p QMPwidget::PlayingState</td>
 
1316
 *   <td>\p 3</td>
 
1317
 *   <td></td>
 
1318
 *  </tr>
 
1319
 *  <tr>
 
1320
 *   <td>\p QMPwidget::BufferingState</td>
 
1321
 *   <td>\p 4</td>
 
1322
 *   <td></td>
 
1323
 *  </tr>
 
1324
 *  <tr>
 
1325
 *   <td>\p QMPwidget::PausedState</td>
 
1326
 *   <td>\p 5</td>
 
1327
 *   <td></td>
 
1328
 *  </tr>
 
1329
 *  <tr>
 
1330
 *   <td>\p QMPwidget::ErrorState</td>
 
1331
 *   <td>\p 6</td>
 
1332
 *   <td></td>
 
1333
 *  </tr>
 
1334
 * </table>
 
1335
 */
 
1336
 
 
1337
/*!
 
1338
 * \enum QMPwidget::Mode
 
1339
 * \brief Video playback modes
 
1340
 * \details
 
1341
 * This enumeration describes valid modes for video playback. Please see \ref playbackmodes for a
 
1342
 * detailed description of both modes.
 
1343
 *
 
1344
 * <table>
 
1345
 *  <tr><th>Constant</th><th>Value</th><th>Description</th></tr>
 
1346
 *  <tr>
 
1347
 *   <td>\p QMPwidget::EmbeddedMode</td>
 
1348
 *   <td>\p 0</td>
 
1349
 *   <td>MPlayer will render directly into a Qt widget.</td>
 
1350
 *  </tr>
 
1351
 *  <tr>
 
1352
 *   <td>\p QMPwidget::PipedMode</td>
 
1353
 *   <td>\p 1</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>
 
1356
 *  </tr>
 
1357
 * </table>
 
1358
 */
 
1359
 
 
1360
/*!
 
1361
 * \enum QMPwidget::SeekMode
 
1362
 * \brief Seeking modes
 
1363
 * \details
 
1364
 * This enumeration describes valid modes for seeking the media stream.
 
1365
 *
 
1366
 * <table>
 
1367
 *  <tr><th>Constant</th><th>Value</th><th>Description</th></tr>
 
1368
 *  <tr>
 
1369
 *   <td>\p QMPwidget::RelativeSeek</td>
 
1370
 *   <td>\p 0</td>
 
1371
 *   <td>Relative seek in seconds</td>
 
1372
 *  </tr>
 
1373
 *  <tr>
 
1374
 *   <td>\p QMPwidget::PercantageSeek</td>
 
1375
 *   <td>\p 1</td>
 
1376
 *   <td>Seek to a position given by a percentage of the whole movie duration</td>
 
1377
 *  </tr>
 
1378
 *  <tr>
 
1379
 *   <td>\p QMPwidget::AbsoluteSeek</td>
 
1380
 *   <td>\p 2</td>
 
1381
 *   <td>Seek to a position given by an absolute time</td>
 
1382
 *  </tr>
 
1383
 * </table>
 
1384
 */
 
1385
 
 
1386
/*!
 
1387
 * \fn void QMPwidget::stateChanged(int state)
 
1388
 * \brief Emitted if the state has changed
 
1389
 * \details
 
1390
 * This signal is emitted when the state of the MPlayer process changes.
 
1391
 *
 
1392
 * \param state The new state
 
1393
 */
 
1394
 
 
1395
/*!
 
1396
 * \fn void QMPwidget::error(const QString &reason)
 
1397
 * \brief Emitted if the state has changed to QMPwidget::ErrorState
 
1398
 * \details
 
1399
 * This signal is emitted when the state of the MPlayer process changes to QMPwidget::ErrorState.
 
1400
 *
 
1401
 * \param reason Textual error description (may be empty)
 
1402
 */
 
1403
 
 
1404
/*!
 
1405
 * \fn void QMPwidget::readStandardOutput(const QString &line)
 
1406
 * \brief Signal for reading MPlayer's standard output
 
1407
 * \details
 
1408
 * This signal is emitted when MPlayer wrote a line of text to its standard output channel.
 
1409
 */
 
1410
 
 
1411
/*!
 
1412
 * \fn void QMPwidget::readStandardError(const QString &line)
 
1413
 * \brief Signal for reading MPlayer's standard error
 
1414
 * \details
 
1415
 * This signal is emitted when MPlayer wrote a line of text to its standard error channel.
 
1416
 */
 
1417
/* BEGIN_COMMON_COPYRIGHT_HEADER
 
1418
 * (c)LGPL2+
 
1419
 *
 
1420
 * Copyright: 2012 Labo A.L
 
1421
 * Authors:
 
1422
 *   Aaron Lewis <the.warl0ck.1989@gmail.com>
 
1423
 *
 
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.
 
1428
 *
 
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.
 
1433
 
 
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
 
1438
 *
 
1439
 * END_COMMON_COPYRIGHT_HEADER */
1
1440
/*
2
1441
 *  qmpwidget - A Qt widget for embedding MPlayer
3
1442
 *  Copyright (C) 2010 by Jonas Gehring