Afficher une vidéo avec Qt et MPlayer (utilisation de QProcess)
Par
Denys Bulant (Tutoriels Qt)
Comment visualiser une vidéo dans Qt ?
Comment intégrer MPlayer et Qt ?
Qt, dans son souci de framework généraliste, n'implémente pas toujours tout ce dont on peut avoir besoin, surtout lorsque cela n'est pas d'usage courant. Les vidéos font partie de ce qui n'était pas couvert par Qt (maintenant supporté depuis la 4.4 par le biais de Phonon). Cependant, un grand nombre de librairies et autres backends existent. Nous allons voir comment utiliser l'un d'entre eux : MPlayer.
I. Introduction
II. Généralités sur MPlayer en tant que backend...
III. Exemple complet
IV. Si vous avez des remarques...
V. Annexes
I. Introduction
Qt, dans son souci de framework généraliste, n'implémente pas toujours tout ce dont on peut avoir besoin, surtout lorsque cela n'est pas d'usage courant. Les vidéos font partie de ce qui n'est pas couvert par Qt. Cependant, un grand nombre de librairies et autres backends existent. Nous allons voir comment utiliser l'un d'entre eux : MPlayer.
Version de Qt
: Toutes versions (exemple fait avec C++/Qt4 et une version pyQt disponible).
II. Généralités sur MPlayer en tant que backend...
La particularité de MPlayer est de ne pas s'intégrer par le biais d'une api, mais par un process externe. Les applications désirant l'utiliser en tant que tel, doivent communiquer avec lui par le biais de son flux d'entrée. Les flux de sorties (standard/erreur) peuvent bien sûr être analysés pour récupérer diverses infos sur la vidéo ainsi que les réponses à des requêtes pouvant être formulées par votre application.
Voici ce dont vous aurez besoin :
Pour utiliser MPlayer comme backend, il y a 2 arguments à lui passer impérativement :
- L'id du widget à utiliser pour le rendu par le biais de "-wid" ;
- "-slave" qui permet de le mettre en mode esclave, d'où le nom ;-)
| Exemple de code lançant MPlayer comme backend : |
QStringList args;
args << "-slave";
#ifdef Q_WS_WIN
args << "-wid" << QString::number(reinterpret_cast<unsigned int>(renderTarget->winId()));
#else
args << "-wid" << QString::number(renderTarget->winId());
#endif
|
À savoir aussi que sur Windows, je n'ai trouvé que
directx
comme driver de sortie :/
Cela ne concerne pas les autres OS ; lors de mon test sur Linux, je n'ai eu aucun driver à spécifier et cela fonctionne parfaitement.
Apparemment, les seuls drivers compatibles pour un embarquement de MPlayer sous Linux sont
xv
,
x11
et
gl
.
 | Sous Linux, il faut faire attention à avoir un widget de taille raisonnable pour contenir la vidéo. En effet, contrairement à Windows, un resize à la volée n'est pas possible :/
|
Ensuite, le mode esclave permet de commander MPlayer pour :
- Obtenir des infos (ex. : get_video_resolution/get_time_length/...) ;
- Donner un ordre (ex. : play/pause/quit/...).
Tout ce qui lui est transmis doit être terminé par un retour chariot. Si vous demandez une information, elle est renvoyée sur la sortie standard avec un début de ligne propre à chaque commande (ex. :
get_video_resolution
renvoie
ANS_VIDEO_RESOLUTION='resX x resY'
).
III. Exemple complet
Voici un programme proposant les fonctionnalités suivantes :
- Lecture d'une vidéo ;
- Arrêt ;
- Avancer/reculer en déplaçant le slider ;
-
Afficher le log de tout ce qui sort sur les flux
stdout
et
stderr
de mplayer.
Vous le trouverez en annexe de ce tuto. Seuls les points essentiels sont repris ici par souci de clarté. Pour vos tests, pensez à changer les valeurs de
mPlayerPath
et
movieFile
, en haut du fichier.
Ce n'est pas un frontend complet, mais cela vous permettra de voir fonctionner Qt 4 avec MPlayer en utilisant
QProcess
:)
Comme dit dans l'introduction, une version
pyQt
a été réalisée par
alteo_gange
. Elle est disponible sur
ce forum
. À noter qu'une version multi-plateforme a été postée par
egaudrain
; le code est dispo à
ce post
.
class PlayerWidget: public QWidget
{
Q_OBJECT
public:
PlayerWidget(QWidget *parent =0)
:QWidget(parent), isPlaying(false)
{
[...]
renderTarget = new QWidget;
renderTarget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
renderTarget->setAttribute(Qt::WA_PaintOnScreen);
renderTarget->setMinimumSize(320, 240);
[...]
mplayerProcess = new QProcess(this);
[...]
connect(mplayerProcess, SIGNAL(readyReadStandardOutput()),
this, SLOT(catchOutput()));
connect(mplayerProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(mplayerEnded(int, QProcess::ExitStatus)));
[...]
}
protected:
virtual void closeEvent(QCloseEvent *e)
{
[...]
}
private:
bool startMPlayer()
{
[...]
QStringList args;
args << "-slave";
args << "-quiet";
#ifdef Q_WS_WIN
args << "-wid" << QString::number(reinterpret_cast<qlonglong>(renderTarget->winId()));
args << "-vo" << "directx:noaccel";
#else
args << "-wid" << QString::number(renderTarget->winId());
log->append("Video output driver may not be necessary for your platform. \
Check: http:
at the VIDEO OUTPUT DRIVERS section.");
#endif
args << movieFile;
qDebug(args.join(" ").toUtf8());
mplayerProcess->setProcessChannelMode(QProcess::MergedChannels);
mplayerProcess->start(mplayerPath, args);
if(!mplayerProcess->waitForStarted(3000))
{
qDebug("allez, cherche le bug :o");
return false;
}
mplayerProcess->write("get_video_resolution\n");
mplayerProcess->write("get_time_length\n");
[...]
}
bool stopMPlayer()
{
[...]
mplayerProcess->write("quit\n");
if(!mplayerProcess->waitForFinished(3000))
{
qDebug("ZOMG, ça plante :(");
return false;
}
return true;
}
private slots:
void catchOutput()
{
while(mplayerProcess->canReadLine())
{
QByteArray buffer(mplayerProcess->readLine());
log->append(QString(buffer));
if(buffer.startsWith("ANS_VIDEO_RESOLUTION"))
{
buffer.remove(0, 21);
buffer.replace(QByteArray("'"), QByteArray(""));
buffer.replace(QByteArray(" "), QByteArray(""));
buffer.replace(QByteArray("\n"), QByteArray(""));
buffer.replace(QByteArray("\r"), QByteArray(""));
int sepIndex = buffer.indexOf('x');
int resX = buffer.left(sepIndex).toInt();
int resY = buffer.mid(sepIndex+1).toInt();
renderTarget->setMinimumSize(resX, resY);
}
if(buffer.startsWith("ANS_LENGTH"))
{
buffer.remove(0, 11);
buffer.replace(QByteArray("'"), QByteArray(""));
buffer.replace(QByteArray(" "), QByteArray(""));
buffer.replace(QByteArray("\n"), QByteArray(""));
buffer.replace(QByteArray("\r"), QByteArray(""));
float maxTime = buffer.toFloat();
timeLine->setMaximum(static_cast<int>(maxTime+1));
}
if(buffer.startsWith("ANS_TIME_POSITION"))
{
buffer.remove(0, 18);
buffer.replace(QByteArray("'"), QByteArray(""));
buffer.replace(QByteArray(" "), QByteArray(""));
buffer.replace(QByteArray("\n"), QByteArray(""));
buffer.replace(QByteArray("\r"), QByteArray(""));
float currTime = buffer.toFloat();
timeLine->setValue(static_cast<int>(currTime+1));
}
}
}
void pollCurrentTime()
{
mplayerProcess->write("get_time_pos\n");
}
void timeLineChanged(int pos)
{
mplayerProcess->write(QString("seek " + QString::number(pos) + " 2\n").toUtf8());
}
void switchPlayState()
{
[...]
}
void mplayerEnded(int exitCode, QProcess::ExitStatus exitStatus)
{
[...]
}
private:
[...]
QWidget *renderTarget;
QProcess *mplayerProcess;
[...]
};
|
IV. Si vous avez des remarques...
Si vous avez besoin d'un éclaircissement, vous pouvez le faire ici :
Forums Qt
.
V. Annexes


Copyright © 2008 Denys Bulant. Aucune reproduction, même partielle, ne peut être faite
de ce site et de l'ensemble de son contenu : textes, documents, images, etc
sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E
de dommages et intérêts.
Cette page est déposée à la
SACD.