File indexing completed on 2024-04-28 16:08:05

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 2003 Koos Vriezen <koos.vriezen@xs4all.nl>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 #include <math.h>
0021 #include "config-kmplayer.h"
0022 #include <unistd.h>
0023 #include <qstring.h>
0024 #include <qfile.h>
0025 #include <qfileinfo.h>
0026 #include <qtimer.h>
0027 #include <qlayout.h>
0028 #include <qtablewidget.h>
0029 #include <qlineedit.h>
0030 #include <qslider.h>
0031 #include <qcombobox.h>
0032 #include <qcheckbox.h>
0033 #include <qspinbox.h>
0034 #include <qlabel.h>
0035 #include <qfontmetrics.h>
0036 #include <qwhatsthis.h>
0037 #include <QList>
0038 #include <QtDBus/QtDBus>
0039 #include <QtCore/QDir>
0040 #include <QtCore/QUrl>
0041 #include <QHeaderView>
0042 #include <QNetworkCookie>
0043 
0044 #include <kdebug.h>
0045 #include <kprotocolmanager.h>
0046 #include <kmessagebox.h>
0047 #include <klocalizedstring.h>
0048 #include <kstandarddirs.h>
0049 #include <kshell.h>
0050 #include <kio/job.h>
0051 #include <kio/accessmanager.h>
0052 
0053 #include "kmplayer_def.h"
0054 #include "kmplayerconfig.h"
0055 #include "kmplayerview.h"
0056 #include "kmplayercontrolpanel.h"
0057 #include "kmplayerprocess.h"
0058 #include "kmplayerpartbase.h"
0059 #include "masteradaptor.h"
0060 #include "streammasteradaptor.h"
0061 #ifdef KMPLAYER_WITH_NPP
0062 # include "callbackadaptor.h"
0063 # include "streamadaptor.h"
0064 #endif
0065 
0066 using namespace KMPlayer;
0067 
0068 ProcessInfo::ProcessInfo (const char *nm, const QString &lbl,
0069         const char **supported, MediaManager* mgr, PreferencesPage *prefs)
0070  : name (nm),
0071    label (lbl),
0072    supported_sources (supported),
0073    manager (mgr),
0074    config_page (prefs) {
0075     if (config_page)
0076         manager->player ()->settings ()->addPage (config_page);
0077 }
0078 
0079 ProcessInfo::~ProcessInfo () {
0080     delete config_page;
0081 }
0082 
0083 bool ProcessInfo::supports (const char *source) const {
0084     for (const char ** s = supported_sources; s[0]; ++s) {
0085         if (!strcmp (s[0], source))
0086             return true;
0087     }
0088     return false;
0089 }
0090 
0091 //------------------------%<----------------------------------------------------
0092 
0093 static QString getPath (const KUrl & url) {
0094     QString p = QUrl::fromPercentEncoding (url.url ().toAscii ());
0095     if (p.startsWith (QString ("file:/"))) {
0096         int i = 0;
0097         p = p.mid (5);
0098         for (; i < p.size () && p[i] == QChar ('/'); ++i)
0099             ;
0100         //kDebug () << "getPath " << p.mid (i-1);
0101         if (i > 0)
0102             return p.mid (i-1);
0103         return QString (QChar ('/') + p);
0104     }
0105     return p;
0106 }
0107 
0108 static QString encodeFileOrUrl (const KUrl &url)
0109 {
0110     return url.isEmpty ()
0111         ? QString ()
0112         : QString::fromLocal8Bit (QFile::encodeName (
0113                     url.isLocalFile ()
0114                     ? url.toLocalFile ()
0115                     : QUrl::fromPercentEncoding (url.url ().toLocal8Bit ())));
0116 }
0117 
0118 static QString encodeFileOrUrl (const QString &str)
0119 {
0120     if (!str.startsWith (QString ("dvd:")) &&
0121             !str.startsWith (QString ("vcd:")) &&
0122             !str.startsWith (QString ("tv:")) &&
0123             !str.startsWith (QString ("cdda:")))
0124         return encodeFileOrUrl (KUrl (str));
0125     return str;
0126 }
0127 
0128 static void setupProcess (QProcess **process)
0129 {
0130     delete *process;
0131     *process = new QProcess;
0132     QStringList env = (*process)->systemEnvironment ();
0133     const QStringList::iterator e = env.end ();
0134     for (QStringList::iterator i = env.begin (); i != e; ++i)
0135         if ((*i).startsWith ("SESSION_MANAGER")) {
0136             env.erase (i);
0137             break;
0138         }
0139     (*process)->setEnvironment (env);
0140 }
0141 
0142 static void killProcess (QProcess *process, QWidget *widget) {
0143     if (!process || !process->pid ())
0144         return;
0145     process->terminate ();
0146     if (!process->waitForFinished (1000)) {
0147         process->kill ();
0148         if (!process->waitForFinished (1000) && widget)
0149             KMessageBox::error (widget,
0150                     i18n ("Failed to end player process."), i18n ("Error"));
0151     }
0152 }
0153 
0154 static void outputToView (View *view, const QByteArray &ba)
0155 {
0156     if (view && ba.size ())
0157         view->addText (QString::fromLocal8Bit (ba.constData ()));
0158 }
0159 
0160 Process::Process (QObject *parent, ProcessInfo *pinfo, Settings *settings)
0161  : QObject (parent),
0162    IProcess (pinfo),
0163    m_source (0L),
0164    m_settings (settings),
0165    m_old_state (IProcess::NotRunning),
0166    m_process (0L),
0167    m_job(0L),
0168    m_process_state (QProcess::NotRunning)
0169 {}
0170 
0171 Process::~Process () {
0172     quit ();
0173     delete m_process;
0174     if (user)
0175         user->processDestroyed (this);
0176 }
0177 
0178 void Process::init () {
0179 }
0180 
0181 void Process::initProcess () {
0182     setupProcess (&m_process);
0183     m_process_state = QProcess::NotRunning;
0184     connect (m_process, SIGNAL (stateChanged (QProcess::ProcessState)),
0185             this, SLOT (processStateChanged (QProcess::ProcessState)));
0186     if (m_source) m_source->setPosition (0);
0187 }
0188 
0189 WId Process::widget () {
0190     return view () && user && user->viewer ()
0191         ? user->viewer ()->windowHandle ()
0192         : 0;
0193 }
0194 
0195 Mrl *Process::mrl () const {
0196     if (user)
0197         return user->getMrl ();
0198     return NULL;
0199 }
0200 
0201 static bool processRunning (QProcess *process) {
0202     return process && process->state () > QProcess::NotRunning;
0203 }
0204 
0205 bool Process::running () const {
0206     return processRunning (m_process);
0207 }
0208 
0209 void Process::setAudioLang (int) {}
0210 
0211 void Process::setSubtitle (int) {}
0212 
0213 void Process::pause () {
0214 }
0215 
0216 void Process::unpause () {
0217 }
0218 
0219 bool Process::seek (int /*pos*/, bool /*absolute*/) {
0220     return false;
0221 }
0222 
0223 void Process::volume (int /*pos*/, bool /*absolute*/) {
0224 }
0225 
0226 bool Process::saturation (int /*pos*/, bool /*absolute*/) {
0227     return false;
0228 }
0229 
0230 bool Process::hue (int /*pos*/, bool /*absolute*/) {
0231     return false;
0232 }
0233 
0234 bool Process::contrast (int /*pos*/, bool /*absolute*/) {
0235     return false;
0236 }
0237 
0238 bool Process::brightness (int /*pos*/, bool /*absolute*/) {
0239     return false;
0240 }
0241 
0242 bool Process::grabPicture (const QString &/*file*/, int /*pos*/) {
0243     m_old_state = m_state = Buffering;
0244     setState (Ready);
0245     return false;
0246 }
0247 
0248 void Process::stop () {
0249 }
0250 
0251 void Process::quit () {
0252     killProcess (m_process, view ());
0253     setState (IProcess::NotRunning);
0254 }
0255 
0256 void Process::setState (IProcess::State newstate) {
0257     if (m_state != newstate) {
0258         bool need_timer = m_old_state == m_state;
0259         m_old_state = m_state;
0260         m_state = newstate;
0261         if (need_timer)
0262             QTimer::singleShot (0, this, SLOT (rescheduledStateChanged ()));
0263     }
0264 }
0265 
0266 KDE_NO_EXPORT void Process::rescheduledStateChanged () {
0267     IProcess::State old_state = m_old_state;
0268     m_old_state = m_state;
0269     if (user) {
0270         user->stateChange (this, old_state, m_state);
0271     } else {
0272         if (m_state > IProcess::Ready)
0273             kError() << "Process running, mrl disappeared" << endl;
0274         delete this;
0275     }
0276 }
0277 
0278 bool Process::play () {
0279     Mrl *m = mrl ();
0280     if (!m)
0281         return false;
0282     bool nonstdurl = m->src.startsWith ("tv:/") ||
0283         m->src.startsWith ("dvd:") ||
0284         m->src.startsWith ("cdda:") ||
0285         m->src.startsWith ("vcd:");
0286     QString url = nonstdurl ? m->src : m->absolutePath ();
0287     bool changed = m_url != url;
0288     m_url = url;
0289     if (user) // FIXME: remove check
0290         user->starting (this);
0291     if (!changed ||
0292             KUrl (m_url).isLocalFile () ||
0293             nonstdurl ||
0294             (m_source && m_source->avoidRedirects ()))
0295         return deMediafiedPlay ();
0296     m_job = KIO::stat (m_url, KIO::HideProgressInfo);
0297     connect (m_job, SIGNAL (result (KJob *)), this, SLOT (result (KJob *)));
0298     return true;
0299 }
0300 
0301 bool Process::deMediafiedPlay () {
0302     return false;
0303 }
0304 
0305 void Process::result (KJob * job) {
0306     KIO::UDSEntry entry = static_cast <KIO::StatJob *> (job)->statResult ();
0307     QString url = entry.stringValue (KIO::UDSEntry::UDS_LOCAL_PATH);
0308     if (!url.isEmpty ())
0309         m_url = url;
0310     m_job = 0L;
0311     deMediafiedPlay ();
0312 }
0313 
0314 void Process::terminateJobs () {
0315     if (m_job) {
0316         m_job->kill ();
0317         m_job = 0L;
0318     }
0319 }
0320 
0321 bool Process::ready () {
0322     setState (IProcess::Ready);
0323     return true;
0324 }
0325 
0326 void Process::processStateChanged (QProcess::ProcessState nstate)
0327 {
0328     if (QProcess::Starting == m_process_state) {
0329         if (QProcess::NotRunning == nstate)
0330             setState (IProcess::NotRunning);
0331         else if (state () == IProcess::Ready)
0332             setState (IProcess::Buffering);
0333         m_process_state = nstate;
0334     }
0335 }
0336 
0337 void Process::startProcess (const QString &program, const QStringList &args)
0338 {
0339     m_process_state = QProcess::Starting;
0340     m_process->start (program, args);
0341 }
0342 
0343 View *Process::view () const {
0344     return m_source ? m_source->player ()->viewWidget () : NULL;
0345 }
0346 
0347 //-----------------------------------------------------------------------------
0348 
0349 RecordDocument::RecordDocument (const QString &url, const QString &rurl,
0350         const QString &rec, Source *src)
0351  : SourceDocument (src, url),
0352    record_file (rurl),
0353    recorder (rec) {
0354     id = id_node_record_document;
0355 }
0356 
0357 void RecordDocument::begin () {
0358     if (!media_info) {
0359         media_info = new MediaInfo (this, MediaManager::AudioVideo);
0360         media_info->create ();
0361     }
0362     media_info->media->play ();
0363 }
0364 
0365 void RecordDocument::message (MessageType msg, void *content) {
0366     switch (msg) {
0367     case MsgMediaFinished:
0368         deactivate ();
0369         break;
0370     default:
0371         SourceDocument::message (msg, content);
0372     }
0373 }
0374 
0375 void RecordDocument::deactivate () {
0376     state = state_deactivated;
0377     ((MediaManager *) role (RoleMediaManager))->player ()->recorderStopped ();
0378     Document::deactivate ();
0379 }
0380 
0381 static RecordDocument *recordDocument (ProcessUser *user) {
0382     Mrl *mrl = user ? user->getMrl () : NULL;
0383     return mrl && id_node_record_document == mrl->id
0384         ? static_cast <RecordDocument *> (mrl) : NULL;
0385 }
0386 
0387 //-----------------------------------------------------------------------------
0388 
0389 static bool proxyForURL (const KUrl &url, QString &proxy) {
0390     KProtocolManager::slaveProtocol (url, proxy);
0391     return !proxy.isNull ();
0392 }
0393 
0394 //-----------------------------------------------------------------------------
0395 
0396 KDE_NO_CDTOR_EXPORT MPlayerBase::MPlayerBase (QObject *parent, ProcessInfo *pinfo, Settings * settings)
0397     : Process (parent, pinfo, settings),
0398       m_needs_restarted (false) {
0399     m_process = new QProcess;
0400 }
0401 
0402 KDE_NO_CDTOR_EXPORT MPlayerBase::~MPlayerBase () {
0403 }
0404 
0405 KDE_NO_EXPORT void MPlayerBase::initProcess () {
0406     Process::initProcess ();
0407     const KUrl &url (m_source->url ());
0408     if (!url.isEmpty ()) {
0409         QString proxy_url;
0410         if (KProtocolManager::useProxy () && proxyForURL (url, proxy_url)) {
0411             QStringList env = m_process->environment ();
0412             env << (QString ("http_proxy=") + proxy_url);
0413             m_process->setEnvironment (env);
0414         }
0415     }
0416     connect (m_process, SIGNAL (bytesWritten (qint64)),
0417             this, SLOT (dataWritten (qint64)));
0418     connect (m_process, SIGNAL (finished (int, QProcess::ExitStatus)),
0419             this, SLOT (processStopped (int, QProcess::ExitStatus)));
0420 }
0421 
0422 bool MPlayerBase::removeQueued (const char *cmd) {
0423     for (QList<QByteArray>::iterator i = commands.begin ();
0424             i != commands.end ();
0425             ++i)
0426         if (!strncmp ((*i).data (), cmd, strlen (cmd))) {
0427             commands.erase (i);
0428             return true;
0429         }
0430     return false;
0431 }
0432 
0433 KDE_NO_EXPORT bool MPlayerBase::sendCommand (const QString & cmd) {
0434     if (running ()) {
0435         commands.push_front (QString (cmd + '\n').toAscii ());
0436         fprintf (stderr, "eval %s", commands.last ().constData ());
0437         if (commands.size () < 2)
0438             m_process->write (commands.last ());
0439         return true;
0440     }
0441     return false;
0442 }
0443 
0444 KDE_NO_EXPORT void MPlayerBase::stop () {
0445     terminateJobs ();
0446 }
0447 
0448 KDE_NO_EXPORT void MPlayerBase::quit () {
0449     if (running ()) {
0450         kDebug() << "MPlayerBase::quit";
0451         stop ();
0452         disconnect (m_process, SIGNAL (finished (int, QProcess::ExitStatus)),
0453                 this, SLOT (processStopped (int, QProcess::ExitStatus)));
0454         m_process->waitForFinished (2000);
0455         if (running ())
0456             Process::quit ();
0457         commands.clear ();
0458         m_needs_restarted = false;
0459         processStopped ();
0460     }
0461     Process::quit ();
0462 }
0463 
0464 KDE_NO_EXPORT void MPlayerBase::dataWritten (qint64) {
0465     if (!commands.size ()) return;
0466     kDebug() << "eval done " << commands.last ().data ();
0467     commands.pop_back ();
0468     if (commands.size ())
0469         m_process->write (commands.last ());
0470 }
0471 
0472 KDE_NO_EXPORT void MPlayerBase::processStopped () {
0473     setState (IProcess::Ready);
0474 }
0475 
0476 KDE_NO_EXPORT void MPlayerBase::processStopped (int, QProcess::ExitStatus) {
0477     kDebug() << "process stopped" << endl;
0478     commands.clear ();
0479     processStopped ();
0480 }
0481 
0482 //-----------------------------------------------------------------------------
0483 
0484 static const char *mplayer_supports [] = {
0485     "dvdsource", "exitsource", "introsource", "pipesource", "tvscanner", "tvsource", "urlsource", "vcdsource", "audiocdsource", 0L
0486 };
0487 
0488 MPlayerProcessInfo::MPlayerProcessInfo (MediaManager *mgr)
0489  : ProcessInfo ("mplayer", i18n ("&MPlayer"), mplayer_supports,
0490          mgr, new MPlayerPreferencesPage ()) {}
0491 
0492 IProcess *MPlayerProcessInfo::create (PartBase *part, ProcessUser *usr) {
0493     MPlayer *m = new MPlayer (part, this, part->settings ());
0494     m->setSource (part->source ());
0495     m->user = usr;
0496     part->processCreated (m);
0497     return m;
0498 }
0499 
0500 KDE_NO_CDTOR_EXPORT
0501 MPlayer::MPlayer (QObject *parent, ProcessInfo *pinfo, Settings *settings)
0502  : MPlayerBase (parent, pinfo, settings),
0503    m_widget (0L),
0504    m_transition_state (NotRunning),
0505    aid (-1), sid (-1)
0506 {}
0507 
0508 KDE_NO_CDTOR_EXPORT MPlayer::~MPlayer () {
0509     if (m_widget && !m_widget->parent ())
0510         delete m_widget;
0511 }
0512 
0513 KDE_NO_EXPORT void MPlayer::init () {
0514 }
0515 
0516 KDE_NO_EXPORT bool MPlayer::ready () {
0517     Process::ready ();
0518     if (user && user->viewer ())
0519         user->viewer ()->useIndirectWidget (true);
0520     return false;
0521 }
0522 
0523 KDE_NO_EXPORT bool MPlayer::deMediafiedPlay () {
0524     if (running ())
0525         return sendCommand (QString ("gui_play"));
0526 
0527     m_transition_state = NotRunning;
0528     if (!m_needs_restarted && running ())
0529         quit (); // rescheduling of setState will reset state just-in-time
0530 
0531     initProcess ();
0532     connect (m_process, SIGNAL (readyReadStandardOutput ()),
0533             this, SLOT (processOutput ()));
0534     connect (m_process, SIGNAL (readyReadStandardError ()),
0535             this, SLOT (processOutput ()));
0536 
0537     m_process_output = QString ();
0538     m_source->setPosition (0);
0539     if (!m_needs_restarted) {
0540         if (m_source->identified ()) {
0541             aid = m_source->audioLangId ();
0542             sid = m_source->subTitleId ();
0543         } else {
0544             aid = sid = -1;
0545         }
0546     } else {
0547         m_needs_restarted = false;
0548     }
0549     alanglist = NULL;
0550     slanglist = NULL;
0551     slanglist_end = NULL;
0552     alanglist_end = NULL;
0553     m_request_seek = -1;
0554     m_tmpURL.truncate (0);
0555 
0556     QStringList args;
0557     //m_view->consoleOutput ()->clear ();
0558     MPlayerPreferencesPage *cfg_page = static_cast <MPlayerPreferencesPage *>(process_info->config_page);
0559     QString exe = cfg_page->mplayer_path;
0560     if (exe.isEmpty ())
0561         exe = "mplayer";
0562 
0563     args << "-wid" << QString::number (widget ());
0564     args << "-slave";
0565 
0566     QString strVideoDriver = QString (m_settings->videodrivers[m_settings->videodriver].driver);
0567     if (!strVideoDriver.isEmpty ()) {
0568         args << "-vo" << strVideoDriver.toLower();
0569         if (view () && view ()->keepSizeRatio () &&
0570                 strVideoDriver.toLower() == QString::fromLatin1 ("x11"))
0571             args << "-zoom";
0572     }
0573 
0574     QString strAudioDriver = QString (m_settings->audiodrivers[m_settings->audiodriver].driver);
0575     if (!strAudioDriver.isEmpty ())
0576         args << "-ao" << strAudioDriver.toLower();
0577 
0578     if (m_settings->framedrop)
0579         args << "-framedrop";
0580 
0581     if (cfg_page->additionalarguments.length () > 0)
0582         args << KShell::splitArgs (cfg_page->additionalarguments);
0583 
0584     // postproc thingies
0585     args << KShell::splitArgs (m_source->filterOptions ());
0586 
0587     if (m_settings->autoadjustcolors) {
0588         args << "-contrast" << QString::number (m_settings->contrast);
0589         args << "-brightness" <<QString::number(m_settings->brightness);
0590         args << "-hue" << QString::number (m_settings->hue);
0591         args << "-saturation" <<QString::number(m_settings->saturation);
0592     }
0593 
0594     if (aid > -1)
0595         args << "-aid" << QString::number (aid);
0596 
0597     if (sid > -1)
0598         args << "-sid" << QString::number (sid);
0599 
0600     for (Node *n = mrl (); n; n = n->parentNode ()) {
0601         if (n->id != id_node_group_node && n->id != id_node_playlist_item)
0602             break;
0603         QString plops = static_cast<Element *>(n)->getAttribute ("mplayeropts");
0604         if (!plops.isNull ()) {
0605             QStringList sl = plops.split (QChar (' '));
0606             for (int i = 0; i < sl.size (); ++i)
0607                 args << sl[i];
0608             break;
0609         }
0610     }
0611 
0612     args << KShell::splitArgs (m_source->options ());
0613 
0614     KUrl url (m_url);
0615     if (!url.isEmpty ()) {
0616         if (m_source->url ().isLocalFile ())
0617             m_process->setWorkingDirectory
0618                 (QFileInfo (m_source->url ().path ()).absolutePath ());
0619         if (url.isLocalFile ()) {
0620             m_url = url.toLocalFile ();
0621             if (cfg_page->alwaysbuildindex &&
0622                     (m_url.toLower ().endsWith (".avi") ||
0623                      m_url.toLower ().endsWith (".divx")))
0624                 args << "-idx";
0625         } else {
0626             int cache = cfg_page->cachesize;
0627             if (cache > 3 && !url.url ().startsWith (QString ("dvd")) &&
0628                     !url.url ().startsWith (QString ("vcd")) &&
0629                     !m_url.startsWith (QString ("tv://")))
0630                 args << "-cache" << QString::number (cache);
0631             if (m_url.startsWith (QString ("cdda:/")) &&
0632                     !m_url.startsWith (QString ("cdda://")))
0633                 m_url = QString ("cdda://") + m_url.mid (6);
0634         }
0635         if (url.protocol () != QString ("stdin"))
0636             args << encodeFileOrUrl (m_url);
0637     }
0638     Mrl *m = mrl ();
0639     if (m && m->repeat > 0)
0640         args << "-loop" << QString::number (m->repeat);
0641     else if (m_settings->loop)
0642         args << "-loop" << 0;
0643     args << "-identify";
0644     const QString surl = encodeFileOrUrl (m_source->subUrl ());
0645     if (!surl.isEmpty ())
0646         args << "-sub" << surl;
0647     qDebug ("mplayer %s\n", args.join (" ").toLocal8Bit ().constData ());
0648 
0649     startProcess (exe, args);
0650 
0651     old_volume = view () ? view ()->controlPanel ()->volumeBar ()->value () : 0;
0652 
0653     return true;
0654 }
0655 
0656 KDE_NO_EXPORT void MPlayer::stop () {
0657     terminateJobs ();
0658     if (!m_source || !running ())
0659         return;
0660     sendCommand (QString ("quit"));
0661     MPlayerBase::stop ();
0662 }
0663 
0664 KDE_NO_EXPORT void MPlayer::pause () {
0665     if (Paused != m_transition_state) {
0666         m_transition_state = Paused;
0667         if (!removeQueued ("pause"))
0668             sendCommand (QString ("pause"));
0669     }
0670 }
0671 
0672 KDE_NO_EXPORT void MPlayer::unpause () {
0673     if (m_transition_state == Paused
0674             || (Paused == m_state
0675                 && m_transition_state != Playing)) {
0676         m_transition_state = Playing;
0677         if (!removeQueued ("pause"))
0678             sendCommand (QString ("pause"));
0679     }
0680 }
0681 
0682 KDE_NO_EXPORT bool MPlayer::seek (int pos, bool absolute) {
0683     if (!m_source || !m_source->hasLength () ||
0684             (absolute && m_source->position () == pos))
0685         return false;
0686     if (m_request_seek >= 0 && commands.size () > 1) {
0687         QList<QByteArray>::iterator i = commands.begin ();
0688         for (++i; i != commands.end (); ++i)
0689             if (!strncmp ((*i).data (), "seek", 4)) {
0690                 i = commands.erase (i);
0691                 m_request_seek = -1;
0692                 break;
0693             }
0694     }
0695     if (m_request_seek >= 0) {
0696         //m_request_seek = pos;
0697         return false;
0698     }
0699     m_request_seek = pos;
0700     QString cmd;
0701     cmd.sprintf ("seek %d %d", pos/10, absolute ? 2 : 0);
0702     if (!absolute)
0703         pos = m_source->position () + pos;
0704     m_source->setPosition (pos);
0705     return sendCommand (cmd);
0706 }
0707 
0708 KDE_NO_EXPORT void MPlayer::volume (int incdec, bool absolute) {
0709     if (absolute)
0710         incdec -= old_volume;
0711     if (incdec == 0)
0712         return;
0713     old_volume += incdec;
0714     sendCommand (QString ("volume ") + QString::number (incdec));
0715 }
0716 
0717 KDE_NO_EXPORT bool MPlayer::saturation (int val, bool absolute) {
0718     QString cmd;
0719     cmd.sprintf ("saturation %d %d", val, absolute ? 1 : 0);
0720     return sendCommand (cmd);
0721 }
0722 
0723 KDE_NO_EXPORT bool MPlayer::hue (int val, bool absolute) {
0724     QString cmd;
0725     cmd.sprintf ("hue %d %d", val, absolute ? 1 : 0);
0726     return sendCommand (cmd);
0727 }
0728 
0729 KDE_NO_EXPORT bool MPlayer::contrast (int val, bool /*absolute*/) {
0730     QString cmd;
0731     cmd.sprintf ("contrast %d 1", val);
0732     return sendCommand (cmd);
0733 }
0734 
0735 KDE_NO_EXPORT bool MPlayer::brightness (int val, bool /*absolute*/) {
0736     QString cmd;
0737     cmd.sprintf ("brightness %d 1", val);
0738     return sendCommand (cmd);
0739 }
0740 
0741 KDE_NO_EXPORT bool MPlayer::grabPicture (const QString &file, int pos) {
0742     Mrl *m = mrl ();
0743     if (m_state > Ready || !m || m->src.isEmpty ())
0744         return false; //FIXME
0745     initProcess ();
0746     m_old_state = m_state = Buffering;
0747     unlink (file.toAscii ().constData ());
0748     QByteArray ba = file.toLocal8Bit ();
0749     ba.append ("XXXXXX");
0750     if (mkdtemp ((char *) ba.constData ())) {
0751         m_grab_dir = QString::fromLocal8Bit (ba.constData ());
0752         QString exe ("mplayer");
0753         QStringList args;
0754         QString jpgopts ("jpeg:outdir=");
0755         jpgopts += KShell::quoteArg (m_grab_dir);
0756         args << "-vo" << jpgopts;
0757         args << "-frames" << "1" << "-nosound" << "-quiet";
0758         if (pos > 0)
0759             args << "-ss" << QString::number (pos);
0760         args << encodeFileOrUrl (m->src);
0761         kDebug () << args.join (" ");
0762         m_process->start (exe, args);
0763         if (m_process->waitForStarted ()) {
0764             m_grab_file = file;
0765             setState (Playing);
0766             return true;
0767         } else {
0768             rmdir (ba.constData ());
0769             m_grab_dir.truncate (0);
0770         }
0771     } else {
0772         kError () << "mkdtemp failure";
0773     }
0774     setState (Ready);
0775     return false;
0776 }
0777 
0778 KDE_NO_EXPORT void MPlayer::processOutput () {
0779     const QByteArray ba = m_process->readAllStandardOutput ();
0780     const char *str = ba.constData ();
0781     int slen = ba.size ();
0782     if (!mrl () || slen <= 0) return;
0783     View *v = view ();
0784 
0785     bool ok;
0786     QRegExp *patterns = static_cast<MPlayerPreferencesPage *>(process_info->config_page)->m_patterns;
0787     QRegExp & m_refURLRegExp = patterns[MPlayerPreferencesPage::pat_refurl];
0788     QRegExp & m_refRegExp = patterns[MPlayerPreferencesPage::pat_ref];
0789     do {
0790         int len = strcspn (str, "\r\n");
0791         QString out = m_process_output + QString::fromLocal8Bit (str, len);
0792         m_process_output = QString ();
0793         str += len;
0794         slen -= len;
0795         if (slen <= 0) {
0796             m_process_output = out;
0797             break;
0798         }
0799         bool process_stats = false;
0800         if (str[0] == '\r') {
0801             if (slen > 1 && str[1] == '\n') {
0802                 str++;
0803                 slen--;
0804             } else
0805                 process_stats = true;
0806         }
0807         str++;
0808         slen--;
0809 
0810         if (process_stats) {
0811             QRegExp & m_posRegExp = patterns[MPlayerPreferencesPage::pat_pos];
0812             QRegExp & m_cacheRegExp = patterns[MPlayerPreferencesPage::pat_cache];
0813             if (m_posRegExp.indexIn (out) > -1) {
0814                 if (m_source->hasLength ()) {
0815                     int pos = int (10.0 * m_posRegExp.cap (1).toFloat ());
0816                     m_source->setPosition (pos);
0817                     m_request_seek = -1;
0818                 }
0819                 if (Playing == m_transition_state) {
0820                     m_transition_state = NotRunning;
0821                     setState (Playing);
0822                 }
0823             } else if (m_cacheRegExp.indexIn (out) > -1) {
0824                 m_source->setLoading (int (m_cacheRegExp.cap(1).toDouble()));
0825             }
0826         } else if (out.startsWith ("ID_LENGTH")) {
0827             int pos = out.indexOf ('=');
0828             if (pos > 0) {
0829                 int l = (int) out.mid (pos + 1).toDouble (&ok);
0830                 if (ok && l >= 0) {
0831                     m_source->setLength (mrl (), 10 * l);
0832                 }
0833             }
0834         } else if (out.startsWith ("ID_PAUSED")) {
0835             if (Paused == m_transition_state) {
0836                 m_transition_state = NotRunning;
0837                 setState (Paused);
0838             }
0839         } else if (m_refURLRegExp.indexIn(out) > -1) {
0840             kDebug () << "Reference mrl " << m_refURLRegExp.cap (1);
0841             if (!m_tmpURL.isEmpty () &&
0842                     (m_url.endsWith (m_tmpURL) || m_tmpURL.endsWith (m_url)))
0843                 m_source->insertURL (mrl (), m_tmpURL);;
0844             KUrl tmp (m_refURLRegExp.cap (1));
0845             m_tmpURL = tmp.isLocalFile () ? tmp.toLocalFile () : tmp.url ();
0846             if (m_source->url () == m_tmpURL ||
0847                     m_url.endsWith (m_tmpURL) || m_tmpURL.endsWith (m_url))
0848                 m_tmpURL.truncate (0);
0849         } else if (m_refRegExp.indexIn (out) > -1) {
0850             kDebug () << "Reference File ";
0851             m_tmpURL.truncate (0);
0852         } else if (out.startsWith ("ID_VIDEO_WIDTH")) {
0853             int pos = out.indexOf ('=');
0854             if (pos > 0) {
0855                 int w = out.mid (pos + 1).toInt ();
0856                 m_source->setDimensions (mrl (), w, m_source->height ());
0857             }
0858         } else if (out.startsWith ("ID_VIDEO_HEIGHT")) {
0859             int pos = out.indexOf ('=');
0860             if (pos > 0) {
0861                 int h = out.mid (pos + 1).toInt ();
0862                 m_source->setDimensions (mrl (), m_source->width (), h);
0863             }
0864         } else if (out.startsWith ("ID_VIDEO_ASPECT")) {
0865             int pos = out.indexOf ('=');
0866             if (pos > 0) {
0867                 bool ok;
0868                 QString val = out.mid (pos + 1);
0869                 float a = val.toFloat (&ok);
0870                 if (!ok) {
0871                     val.replace (',', '.');
0872                     a = val.toFloat (&ok);
0873                 }
0874                 if (ok && a > 0.001)
0875                     m_source->setAspect (mrl (), a);
0876             }
0877         } else if (out.startsWith ("ID_AID_")) {
0878             int pos = out.indexOf ('_', 7);
0879             if (pos > 0) {
0880                 int id = out.mid (7, pos - 7).toInt ();
0881                 pos = out.indexOf ('=', pos);
0882                 if (pos > 0) {
0883                     if (!alanglist_end) {
0884                         alanglist = new Source::LangInfo (id, out.mid (pos + 1));
0885                         alanglist_end = alanglist;
0886                     } else {
0887                         alanglist_end->next = new Source::LangInfo (id, out.mid(pos+1));
0888                         alanglist_end = alanglist_end->next;
0889                     }
0890                     kDebug () << "lang " << id << " " << alanglist_end->name;
0891                 }
0892             }
0893         } else if (out.startsWith ("ID_SID_")) {
0894             int pos = out.indexOf ('_', 7);
0895             if (pos > 0) {
0896                 int id = out.mid (7, pos - 7).toInt ();
0897                 pos = out.indexOf ('=', pos);
0898                 if (pos > 0) {
0899                     if (!slanglist_end) {
0900                         slanglist = new Source::LangInfo (id, out.mid (pos + 1));
0901                         slanglist_end = slanglist;
0902                     } else {
0903                         slanglist_end->next = new Source::LangInfo (id, out.mid(pos+1));
0904                         slanglist_end = slanglist_end->next;
0905                     }
0906                     kDebug () << "sid " << id << " " << slanglist_end->name;
0907                 }
0908             }
0909         } else if (out.startsWith ("ICY Info")) {
0910             int p = out.indexOf ("StreamTitle=", 8);
0911             if (p > -1) {
0912                 p += 12;
0913                 int e = out.indexOf (';', p);
0914                 if (e > -1)
0915                     e -= p;
0916                 QString inf = out.mid (p, e);
0917                 mrl ()->document ()->message (MsgInfoString, &inf);
0918             }
0919         } else if (v) {
0920             QRegExp & m_startRegExp = patterns[MPlayerPreferencesPage::pat_start];
0921             QRegExp & m_sizeRegExp = patterns[MPlayerPreferencesPage::pat_size];
0922             v->addText (out, true);
0923             if (!m_source->processOutput (out)) {
0924                 // int movie_width = m_source->width ();
0925                 if (/*movie_width <= 0 &&*/ m_sizeRegExp.indexIn (out) > -1) {
0926                     int movie_width = m_sizeRegExp.cap (1).toInt (&ok);
0927                     int movie_height = ok ? m_sizeRegExp.cap (2).toInt (&ok) : 0;
0928                     if (ok && movie_width > 0 && movie_height > 0) {
0929                         m_source->setDimensions(mrl(),movie_width,movie_height);
0930                         m_source->setAspect (mrl(), 1.0*movie_width/movie_height);
0931                     }
0932                 } else if (m_startRegExp.indexIn (out) > -1) {
0933                     if (!m_tmpURL.isEmpty () && m_url != m_tmpURL) {
0934                         m_source->insertURL (mrl (), m_tmpURL);;
0935                         m_tmpURL.truncate (0);
0936                     }
0937                     m_source->setIdentified ();
0938                     m_source->setLanguages (alanglist, slanglist);
0939                     m_source->setLoading (100);
0940                     setState (IProcess::Playing);
0941                     m_source->setPosition (0);
0942                 }
0943             }
0944         }
0945     } while (slen > 0);
0946 }
0947 
0948 KDE_NO_EXPORT void MPlayer::processStopped () {
0949     if (mrl ()) {
0950         QString url;
0951         if (!m_grab_dir.isEmpty ()) {
0952             QDir dir (m_grab_dir);
0953             QStringList files = dir.entryList ();
0954             bool renamed = false;
0955             for (int i = 0; i < files.size (); ++i) {
0956                 kDebug() << files[i];
0957                 if (files[i] == "." || files[i] == "..")
0958                     continue;
0959                 if (!renamed) {
0960                     kDebug() << "rename " << dir.filePath (files[i]) << "->" << m_grab_file;
0961                     renamed = true;
0962                     ::rename (dir.filePath (files[i]).toLocal8Bit().constData(),
0963                             m_grab_file.toLocal8Bit ().constData ());
0964                 } else {
0965                     kDebug() << "rm " << files[i];
0966                     dir.remove (files[i]);
0967                 }
0968             }
0969             QString dirname = dir.dirName ();
0970             dir.cdUp ();
0971             kDebug() << m_grab_dir << " " << files.size () << " rmdir " << dirname;
0972             dir.rmdir (dirname);
0973         }
0974         if (m_source && m_needs_restarted) {
0975             commands.clear ();
0976             int pos = m_source->position ();
0977             play ();
0978             seek (pos, true);
0979             return;
0980         }
0981     }
0982     setState (IProcess::Ready);
0983 }
0984 
0985 void MPlayer::setAudioLang (int id) {
0986     aid = id;
0987     m_needs_restarted = true;
0988     sendCommand (QString ("quit"));
0989 }
0990 
0991 void MPlayer::setSubtitle (int id) {
0992     sid = id;
0993     m_needs_restarted = true;
0994     sendCommand (QString ("quit"));
0995 }
0996 
0997 //-----------------------------------------------------------------------------
0998 
0999 extern const char * strMPlayerGroup;
1000 static const char * strMPlayerPatternGroup = "MPlayer Output Matching";
1001 static const char * strMPlayerPath = "MPlayer Path";
1002 static const char * strAddArgs = "Additional Arguments";
1003 static const char * strCacheSize = "Cache Size for Streaming";
1004 static const char * strAlwaysBuildIndex = "Always build index";
1005 static const int non_patterns = 4;
1006 
1007 static struct MPlayerPattern {
1008     KLocalizedString caption;
1009     const char * name;
1010     const char * pattern;
1011 } _mplayer_patterns [] = {
1012     { ki18n ("Size pattern"), "Movie Size", "VO:.*[^0-9]([0-9]+)x([0-9]+)" },
1013     { ki18n ("Cache pattern"), "Cache Fill", "Cache fill:[^0-9]*([0-9\\.]+)%" },
1014     { ki18n ("Position pattern"), "Movie Position", "[AV]:\\s*([0-9\\.]+)" },
1015     { ki18n ("Index pattern"), "Index Pattern", "Generating Index: +([0-9]+)%" },
1016     { ki18n ("Reference URL pattern"), "Reference URL Pattern", "Playing\\s+(.*[^\\.])\\.?\\s*$" },
1017     { ki18n ("Reference pattern"), "Reference Pattern", "Reference Media file" },
1018     { ki18n ("Start pattern"), "Start Playing", "Start[^ ]* play" },
1019     { ki18n ("VCD track pattern"), "VCD Tracks", "track ([0-9]+):" },
1020     { ki18n ("Audio CD tracks pattern"), "CDROM Tracks", "[Aa]udio CD[^0-9]+([0-9]+)[^0-9]tracks" }
1021 };
1022 
1023 namespace KMPlayer {
1024 
1025 class KMPLAYER_NO_EXPORT MPlayerPreferencesFrame : public QFrame {
1026 public:
1027     MPlayerPreferencesFrame (QWidget * parent);
1028     QTableWidget * table;
1029 };
1030 
1031 } // namespace
1032 
1033 KDE_NO_CDTOR_EXPORT MPlayerPreferencesFrame::MPlayerPreferencesFrame (QWidget * parent)
1034  : QFrame (parent) {
1035     QVBoxLayout * layout = new QVBoxLayout (this);
1036     table = new QTableWidget (int (MPlayerPreferencesPage::pat_last)+non_patterns, 2, this);
1037     table->verticalHeader ()->setVisible (false);
1038     table->horizontalHeader ()->setVisible (false);
1039     table->setContentsMargins (0, 0, 0, 0);
1040     table->setItem (0, 0, new QTableWidgetItem (i18n ("MPlayer command:")));
1041     table->setItem (0, 1, new QTableWidgetItem ());
1042     table->setItem (1, 0, new QTableWidgetItem (i18n ("Additional command line arguments:")));
1043     table->setItem (1, 1, new QTableWidgetItem ());
1044     table->setItem (2, 0, new QTableWidgetItem (QString("%1 (%2)").arg (i18n ("Cache size:")).arg (i18n ("kB")))); // FIXME for new translations
1045     QSpinBox* spin = new QSpinBox(table->viewport());
1046     spin->setMaximum(32767);
1047     spin->setSingleStep(32);
1048     table->setCellWidget (2, 1, spin);
1049     table->setItem (3, 0, new QTableWidgetItem (i18n ("Build new index when possible")));
1050     table->setCellWidget (3, 1, new QCheckBox (table->viewport()));
1051     table->cellWidget (3, 1)->setWhatsThis(i18n ("Allows seeking in indexed files (AVIs)"));
1052     for (int i = 0; i < int (MPlayerPreferencesPage::pat_last); i++) {
1053         table->setItem (i+non_patterns, 0, new QTableWidgetItem (_mplayer_patterns[i].caption.toString()));
1054         table->setItem (i+non_patterns, 1, new QTableWidgetItem ());
1055     }
1056     for (int i = 0; i < non_patterns + int (MPlayerPreferencesPage::pat_last); i++) {
1057         QTableWidgetItem *item = table->itemAt (i, 0);
1058         item->setFlags (item->flags () ^ Qt::ItemIsEditable);
1059     }
1060     table->horizontalHeader ()->setResizeMode (QHeaderView::ResizeToContents);
1061     table->horizontalHeader ()->setStretchLastSection (true);
1062     table->resizeRowsToContents ();
1063     layout->addWidget (table);
1064 }
1065 
1066 KDE_NO_CDTOR_EXPORT MPlayerPreferencesPage::MPlayerPreferencesPage ()
1067  : m_configframe (0L) {
1068 }
1069 
1070 KDE_NO_EXPORT void MPlayerPreferencesPage::write (KSharedConfigPtr config) {
1071     KConfigGroup patterns_cfg (config, strMPlayerPatternGroup);
1072     for (int i = 0; i < int (pat_last); i++)
1073         patterns_cfg.writeEntry
1074             (_mplayer_patterns[i].name, m_patterns[i].pattern ());
1075     KConfigGroup mplayer_cfg (config, strMPlayerGroup);
1076     mplayer_cfg.writeEntry (strMPlayerPath, mplayer_path);
1077     mplayer_cfg.writeEntry (strAddArgs, additionalarguments);
1078     mplayer_cfg.writeEntry (strCacheSize, cachesize);
1079     mplayer_cfg.writeEntry (strAlwaysBuildIndex, alwaysbuildindex);
1080 }
1081 
1082 KDE_NO_EXPORT void MPlayerPreferencesPage::read (KSharedConfigPtr config) {
1083     KConfigGroup patterns_cfg (config, strMPlayerPatternGroup);
1084     for (int i = 0; i < int (pat_last); i++)
1085         m_patterns[i].setPattern (patterns_cfg.readEntry
1086                 (_mplayer_patterns[i].name, _mplayer_patterns[i].pattern));
1087     KConfigGroup mplayer_cfg (config, strMPlayerGroup);
1088     mplayer_path = mplayer_cfg.readEntry (strMPlayerPath, QString ("mplayer"));
1089     additionalarguments = mplayer_cfg.readEntry (strAddArgs, QString ());
1090     cachesize = mplayer_cfg.readEntry (strCacheSize, 384);
1091     alwaysbuildindex = mplayer_cfg.readEntry (strAlwaysBuildIndex, false);
1092 }
1093 
1094 KDE_NO_EXPORT void MPlayerPreferencesPage::sync (bool fromUI) {
1095     QTableWidget * table = m_configframe->table;
1096     QSpinBox * cacheSize = static_cast<QSpinBox *>(table->cellWidget (2, 1));
1097     QCheckBox * buildIndex = static_cast<QCheckBox *>(table->cellWidget (3, 1));
1098     if (fromUI) {
1099         mplayer_path = table->item (0, 1)->text ();
1100         additionalarguments = table->item (1, 1)->text ();
1101         for (int i = 0; i < int (pat_last); i++)
1102             m_patterns[i].setPattern (table->item (i+non_patterns, 1)->text ());
1103         cachesize = cacheSize->value();
1104         alwaysbuildindex = buildIndex->isChecked ();
1105     } else {
1106         table->item (0, 1)->setText (mplayer_path);
1107         table->item (1, 1)->setText (additionalarguments);
1108         for (int i = 0; i < int (pat_last); i++)
1109             table->item (i+non_patterns, 1)->setText (m_patterns[i].pattern ());
1110         if (cachesize > 0)
1111             cacheSize->setValue(cachesize);
1112         buildIndex->setChecked (alwaysbuildindex);
1113     }
1114 }
1115 
1116 KDE_NO_EXPORT void MPlayerPreferencesPage::prefLocation (QString & item, QString & icon, QString & tab) {
1117     item = i18n ("General Options");
1118     icon = QString ("kmplayer");
1119     tab = i18n ("MPlayer");
1120 }
1121 
1122 KDE_NO_EXPORT QFrame * MPlayerPreferencesPage::prefPage (QWidget * parent) {
1123     m_configframe = new MPlayerPreferencesFrame (parent);
1124     return m_configframe;
1125 }
1126 
1127 //-----------------------------------------------------------------------------
1128 
1129 static const char * mencoder_supports [] = {
1130     "dvdsource", "pipesource", "tvscanner", "tvsource", "urlsource",
1131     "vcdsource", "audiocdsource", NULL
1132 };
1133 
1134 MEncoderProcessInfo::MEncoderProcessInfo (MediaManager *mgr)
1135  : ProcessInfo ("mencoder", i18n ("M&Encoder"), mencoder_supports,
1136          mgr, NULL) {}
1137 
1138 IProcess *MEncoderProcessInfo::create (PartBase *part, ProcessUser *usr) {
1139     MEncoder *m = new MEncoder (part, this, part->settings ());
1140     m->setSource (part->source ());
1141     m->user = usr;
1142     part->processCreated (m);
1143     return m;
1144 }
1145 
1146 KDE_NO_CDTOR_EXPORT
1147 MEncoder::MEncoder (QObject * parent, ProcessInfo *pinfo, Settings * settings)
1148  : MPlayerBase (parent, pinfo, settings) {}
1149 
1150 KDE_NO_CDTOR_EXPORT MEncoder::~MEncoder () {
1151 }
1152 
1153 KDE_NO_EXPORT void MEncoder::init () {
1154 }
1155 
1156 bool MEncoder::deMediafiedPlay () {
1157     stop ();
1158     RecordDocument *rd = recordDocument (user);
1159     if (!rd)
1160         return false;
1161     initProcess ();
1162     QString exe ("mencoder");
1163     QString margs = m_settings->mencoderarguments;
1164     if (m_settings->recordcopy)
1165         margs = QString ("-oac copy -ovc copy");
1166     QStringList args = KShell::splitArgs (margs);
1167     if (m_source)
1168         args << KShell::splitArgs (m_source->recordCmd ());
1169     // FIXME if (m_player->source () == source) // ugly
1170     //    m_player->stop ();
1171     QString myurl = encodeFileOrUrl (m_url);
1172     if (!myurl.isEmpty ())
1173         args << myurl;
1174     args << "-o" << encodeFileOrUrl (rd->record_file);
1175     startProcess (exe, args);
1176     qDebug ("mencoder %s\n", args.join (" ").toLocal8Bit ().constData ());
1177     if (m_process->waitForStarted ()) {
1178         setState (Playing);
1179         return true;
1180     }
1181     stop ();
1182     return false;
1183 }
1184 
1185 KDE_NO_EXPORT void MEncoder::stop () {
1186     terminateJobs ();
1187     if (running ()) {
1188         kDebug () << "MEncoder::stop ()";
1189         Process::quit ();
1190         MPlayerBase::stop ();
1191     }
1192 }
1193 
1194 //-----------------------------------------------------------------------------
1195 
1196 static const char * mplayerdump_supports [] = {
1197     "dvdsource", "pipesource", "tvscanner", "tvsource", "urlsource", "vcdsource", "audiocdsource", 0L
1198 };
1199 
1200 MPlayerDumpProcessInfo::MPlayerDumpProcessInfo (MediaManager *mgr)
1201  : ProcessInfo ("mplayerdumpstream", i18n ("&MPlayerDumpstream"),
1202          mplayerdump_supports, mgr, NULL) {}
1203 
1204 IProcess *MPlayerDumpProcessInfo::create (PartBase *p, ProcessUser *usr) {
1205     MPlayerDumpstream *m = new MPlayerDumpstream (p, this, p->settings ());
1206     m->setSource (p->source ());
1207     m->user = usr;
1208     p->processCreated (m);
1209     return m;
1210 }
1211 
1212 KDE_NO_CDTOR_EXPORT
1213 MPlayerDumpstream::MPlayerDumpstream (QObject *p, ProcessInfo *pi, Settings *s)
1214  : MPlayerBase (p, pi, s) {}
1215 
1216 KDE_NO_CDTOR_EXPORT MPlayerDumpstream::~MPlayerDumpstream () {
1217 }
1218 
1219 KDE_NO_EXPORT void MPlayerDumpstream::init () {
1220 }
1221 
1222 bool MPlayerDumpstream::deMediafiedPlay () {
1223     stop ();
1224     RecordDocument *rd = recordDocument (user);
1225     if (!rd)
1226         return false;
1227     initProcess ();
1228     QString exe ("mplayer");
1229     QStringList args;
1230     args << KShell::splitArgs (m_source->recordCmd ());
1231     // FIXME if (m_player->source () == source) // ugly
1232     //    m_player->stop ();
1233     QString myurl = encodeFileOrUrl (m_url);
1234     if (!myurl.isEmpty ())
1235         args << myurl;
1236     args << "-dumpstream" << "-dumpfile" << encodeFileOrUrl (rd->record_file);
1237     qDebug ("mplayer %s\n", args.join (" ").toLocal8Bit ().constData ());
1238     startProcess (exe, args);
1239     if (m_process->waitForStarted ()) {
1240         setState (Playing);
1241         return true;
1242     }
1243     stop ();
1244     return false;
1245 }
1246 
1247 KDE_NO_EXPORT void MPlayerDumpstream::stop () {
1248     terminateJobs ();
1249     if (!m_source || !running ())
1250         return;
1251     kDebug () << "MPlayerDumpstream::stop";
1252     if (running ())
1253         Process::quit ();
1254     MPlayerBase::stop ();
1255 }
1256 
1257 //-----------------------------------------------------------------------------
1258 
1259 MasterProcessInfo::MasterProcessInfo (const char *nm, const QString &lbl,
1260             const char **supported, MediaManager *mgr, PreferencesPage *pp)
1261  : ProcessInfo (nm, lbl, supported, mgr, pp),
1262    m_slave (NULL) {}
1263 
1264 MasterProcessInfo::~MasterProcessInfo () {
1265     stopSlave ();
1266 }
1267 
1268 void MasterProcessInfo::initSlave () {
1269     if (m_path.isEmpty ()) {
1270         static int count = 0;
1271         m_path = QString ("/master_%1").arg (count++);
1272         (void) new MasterAdaptor (this);
1273         QDBusConnection::sessionBus().registerObject (m_path, this);
1274         m_service = QDBusConnection::sessionBus().baseService ();
1275     }
1276     setupProcess (&m_slave);
1277     connect (m_slave, SIGNAL (finished (int, QProcess::ExitStatus)),
1278             this, SLOT (slaveStopped (int, QProcess::ExitStatus)));
1279     connect (m_slave, SIGNAL (readyReadStandardOutput ()),
1280             this, SLOT (slaveOutput ()));
1281     connect (m_slave, SIGNAL (readyReadStandardError ()),
1282             this, SLOT (slaveOutput ()));
1283 }
1284 
1285 void MasterProcessInfo::quitProcesses () {
1286     stopSlave ();
1287 }
1288 
1289 void MasterProcessInfo::stopSlave () {
1290     if (!m_slave_service.isEmpty ()) {
1291         QDBusMessage msg = QDBusMessage::createMethodCall (
1292                 m_slave_service, QString ("/%1").arg (ProcessInfo::name),
1293                 "org.kde.kmplayer.Slave", "quit");
1294         //msg << m_url << mime << plugin << param_len;
1295         msg.setDelayedReply (false);
1296         QDBusConnection::sessionBus().send (msg);
1297     }
1298     if (processRunning (m_slave)) {
1299         m_slave->waitForFinished (1000);
1300         killProcess (m_slave, manager->player ()->view ());
1301     }
1302 }
1303 
1304 void MasterProcessInfo::running (const QString &srv) {
1305     kDebug() << "MasterProcessInfo::running " << srv;
1306     m_slave_service = srv;
1307     MediaManager::ProcessList &pl = manager->processes ();
1308     const MediaManager::ProcessList::iterator e = pl.end ();
1309     for (MediaManager::ProcessList::iterator i = pl.begin (); i != e; ++i)
1310         if (this == (*i)->process_info)
1311             static_cast <Process *> (*i)->setState (IProcess::Ready);
1312 }
1313 
1314 void MasterProcessInfo::slaveStopped (int, QProcess::ExitStatus) {
1315     m_slave_service.truncate (0);
1316     MediaManager::ProcessList &pl = manager->processes ();
1317     const MediaManager::ProcessList::iterator e = pl.end ();
1318     for (MediaManager::ProcessList::iterator i = pl.begin (); i != e; ++i)
1319         if (this == (*i)->process_info)
1320             static_cast <Process *> (*i)->setState (IProcess::NotRunning);
1321 }
1322 
1323 void MasterProcessInfo::slaveOutput () {
1324     outputToView(manager->player()->viewWidget(), m_slave->readAllStandardOutput());
1325     outputToView(manager->player()->viewWidget(), m_slave->readAllStandardError ());
1326 }
1327 
1328 MasterProcess::MasterProcess (QObject *parent, ProcessInfo *pinfo, Settings *settings)
1329  : Process (parent, pinfo, settings) {}
1330 
1331 MasterProcess::~MasterProcess () {
1332 }
1333 
1334 void MasterProcess::init () {
1335 }
1336 
1337 bool MasterProcess::deMediafiedPlay () {
1338     WindowId wid = user->viewer ()->windowHandle ();
1339     m_slave_path = QString ("/stream_%1").arg (wid);
1340     MasterProcessInfo *mpi = static_cast <MasterProcessInfo *>(process_info);
1341     kDebug() << "MasterProcess::deMediafiedPlay " << m_url << " " << wid;
1342 
1343     (void) new StreamMasterAdaptor (this);
1344     QDBusConnection::sessionBus().registerObject (
1345             QString ("%1/stream_%2").arg (mpi->m_path).arg (wid), this);
1346 
1347     QDBusMessage msg = QDBusMessage::createMethodCall (
1348             mpi->m_slave_service, QString ("/%1").arg (process_info->name),
1349                 "org.kde.kmplayer.Slave", "newStream");
1350     if (!m_url.startsWith ("dvd:") ||
1351             !m_url.startsWith ("vcd:") ||
1352             !m_url.startsWith ("cdda:")) {
1353         KUrl url (m_url);
1354         if (url.isLocalFile ())
1355             m_url = getPath (url);
1356     }
1357     msg << m_url << (qulonglong)wid;
1358     msg.setDelayedReply (false);
1359     QDBusConnection::sessionBus().send (msg);
1360     setState (IProcess::Buffering);
1361     return true;
1362 }
1363 
1364 bool MasterProcess::running () const {
1365     MasterProcessInfo *mpi = static_cast <MasterProcessInfo *>(process_info);
1366     return processRunning (mpi->m_slave);
1367 }
1368 
1369 void MasterProcess::loading (int perc) {
1370     process_info->manager->player ()->setLoaded (perc);
1371 }
1372 
1373 void MasterProcess::streamInfo (uint64_t length, double aspect) {
1374     kDebug() << length;
1375     m_source->setLength (mrl (), length);
1376     m_source->setAspect (mrl (), aspect);
1377 }
1378 
1379 void MasterProcess::streamMetaInfo (QString info) {
1380     m_source->document ()->message (MsgInfoString, &info);
1381 }
1382 
1383 void MasterProcess::playing () {
1384     process_info->manager->player ()->setLoaded (100);
1385     setState (IProcess::Playing);
1386 }
1387 
1388 void MasterProcess::progress (uint64_t pos) {
1389     m_source->setPosition (pos);
1390 }
1391 
1392 void MasterProcess::pause () {
1393     if (IProcess::Playing == m_state) {
1394         MasterProcessInfo *mpi = static_cast<MasterProcessInfo *>(process_info);
1395         QDBusMessage msg = QDBusMessage::createMethodCall (
1396                 mpi->m_slave_service,
1397                 m_slave_path,
1398                 "org.kde.kmplayer.StreamSlave",
1399                 "pause");
1400         msg.setDelayedReply (false);
1401         QDBusConnection::sessionBus().send (msg);
1402     }
1403 }
1404 
1405 void MasterProcess::unpause () {
1406     pause ();
1407 }
1408 
1409 bool MasterProcess::seek (int pos, bool) {
1410     if (IProcess::Playing == m_state) {
1411         MasterProcessInfo *mpi = static_cast<MasterProcessInfo *>(process_info);
1412         QDBusMessage msg = QDBusMessage::createMethodCall (
1413                 mpi->m_slave_service,
1414                 m_slave_path,
1415                 "org.kde.kmplayer.StreamSlave",
1416                 "seek");
1417         msg << (qulonglong) pos << true;
1418         msg.setDelayedReply (false);
1419         QDBusConnection::sessionBus().send (msg);
1420         return true;
1421     }
1422     return false;
1423 }
1424 
1425 KDE_NO_EXPORT void MasterProcess::volume (int incdec, bool) {
1426     if (IProcess::Playing == m_state) {
1427         MasterProcessInfo *mpi = static_cast<MasterProcessInfo *>(process_info);
1428         QDBusMessage msg = QDBusMessage::createMethodCall (
1429                 mpi->m_slave_service,
1430                 m_slave_path,
1431                 "org.kde.kmplayer.StreamSlave",
1432                 "volume");
1433         msg << incdec;
1434         msg.setDelayedReply (false);
1435         QDBusConnection::sessionBus().send (msg);
1436     }
1437 }
1438 
1439 void MasterProcess::eof () {
1440     setState (IProcess::Ready);
1441 }
1442 
1443 void MasterProcess::stop () {
1444     if (m_state > IProcess::Ready) {
1445         MasterProcessInfo *mpi = static_cast<MasterProcessInfo *>(process_info);
1446         QDBusMessage msg = QDBusMessage::createMethodCall (
1447                 mpi->m_slave_service,
1448                 m_slave_path,
1449                 "org.kde.kmplayer.StreamSlave",
1450                 "stop");
1451         msg.setDelayedReply (false);
1452         QDBusConnection::sessionBus().send (msg);
1453     }
1454 }
1455 
1456 //-------------------------%<--------------------------------------------------
1457 
1458 static const char *phonon_supports [] = {
1459     "urlsource", "dvdsource", "vcdsource", "audiocdsource", 0L
1460 };
1461 
1462 PhononProcessInfo::PhononProcessInfo (MediaManager *mgr)
1463   : MasterProcessInfo ("phonon", i18n ("&Phonon"), phonon_supports, mgr, NULL)
1464 {}
1465 
1466 IProcess *PhononProcessInfo::create (PartBase *part, ProcessUser *usr) {
1467     if (!processRunning (m_slave))
1468         startSlave ();
1469     Phonon *p = new Phonon (part, this, part->settings ());
1470     p->setSource (part->source ());
1471     p->user = usr;
1472     part->processCreated (p);
1473     return p;
1474 }
1475 
1476 bool PhononProcessInfo::startSlave () {
1477     initSlave ();
1478     QString exe ("kphononplayer");
1479     QStringList args;
1480     args << "-cb" << (m_service + m_path);
1481     qDebug ("kphononplayer %s", args.join (" ").toLocal8Bit ().constData ());
1482     m_slave->start (exe, args);
1483     return true;
1484 }
1485 
1486 Phonon::Phonon (QObject *parent, ProcessInfo *pinfo, Settings *settings)
1487  : MasterProcess (parent, pinfo, settings) {}
1488 
1489 bool Phonon::ready () {
1490     if (user && user->viewer ())
1491         user->viewer ()->useIndirectWidget (false);
1492     kDebug() << "Phonon::ready " << state () << endl;
1493     PhononProcessInfo *ppi = static_cast <PhononProcessInfo *>(process_info);
1494     if (running ()) {
1495         if (!ppi->m_slave_service.isEmpty ())
1496             setState (IProcess::Ready);
1497         return true;
1498     } else {
1499         return ppi->startSlave ();
1500     }
1501 }
1502 
1503 //-----------------------------------------------------------------------------
1504 
1505 KDE_NO_CDTOR_EXPORT ConfigDocument::ConfigDocument ()
1506     : Document (QString ()) {}
1507 
1508 KDE_NO_CDTOR_EXPORT ConfigDocument::~ConfigDocument () {
1509     kDebug () << "~ConfigDocument";
1510 }
1511 
1512 namespace KMPlayer {
1513     /*
1514      * Element for ConfigDocument
1515      */
1516     struct KMPLAYER_NO_EXPORT SomeNode : public ConfigNode {
1517         KDE_NO_CDTOR_EXPORT SomeNode (NodePtr & d, const QString & t)
1518             : ConfigNode (d, t) {}
1519         KDE_NO_CDTOR_EXPORT ~SomeNode () {}
1520         Node *childFromTag (const QString & t);
1521     };
1522 } // namespace
1523 
1524 KDE_NO_CDTOR_EXPORT ConfigNode::ConfigNode (NodePtr & d, const QString & t)
1525     : DarkNode (d, t.toUtf8 ()), w (0L) {}
1526 
1527 Node *ConfigDocument::childFromTag (const QString & tag) {
1528     if (tag.toLower () == QString ("document"))
1529         return new ConfigNode (m_doc, tag);
1530     return 0L;
1531 }
1532 
1533 Node *ConfigNode::childFromTag (const QString & t) {
1534     return new TypeNode (m_doc, t);
1535 }
1536 
1537 KDE_NO_CDTOR_EXPORT TypeNode::TypeNode (NodePtr & d, const QString & t)
1538  : ConfigNode (d, t), tag (t) {}
1539 
1540 Node *TypeNode::childFromTag (const QString & tag) {
1541     return new SomeNode (m_doc, tag);
1542 }
1543 
1544 Node *SomeNode::childFromTag (const QString & t) {
1545     return new SomeNode (m_doc, t);
1546 }
1547 
1548 QWidget * TypeNode::createWidget (QWidget * parent) {
1549     QByteArray ba = getAttribute (Ids::attr_type).toAscii ();
1550     const char *ctype = ba.constData ();
1551     QString value = getAttribute (Ids::attr_value);
1552     if (!strcmp (ctype, "range")) {
1553         QSlider* slider = new QSlider (parent);
1554         slider->setMinimum(getAttribute (QString ("START")).toInt ());
1555         slider->setMaximum(getAttribute (Ids::attr_end).toInt ());
1556         slider->setPageStep(1);
1557         slider->setOrientation(Qt::Horizontal);
1558         slider->setValue(value.toInt ());
1559         w = slider;
1560     } else if (!strcmp (ctype, "num") || !strcmp (ctype,  "string")) {
1561         w = new QLineEdit (value, parent);
1562     } else if (!strcmp (ctype, "bool")) {
1563         QCheckBox * checkbox = new QCheckBox (parent);
1564         checkbox->setChecked (value.toInt ());
1565         w = checkbox;
1566     } else if (!strcmp (ctype, "enum")) {
1567         QComboBox * combo = new QComboBox (parent);
1568         for (Node *e = firstChild (); e; e = e->nextSibling ())
1569             if (e->isElementNode () && !strcmp (e->nodeName (), "item"))
1570                 combo->addItem (static_cast <Element *> (e)->getAttribute (Ids::attr_value));
1571         combo->setCurrentIndex (value.toInt ());
1572         w = combo;
1573     } else if (!strcmp (ctype, "tree")) {
1574     } else
1575         kDebug() << "Unknown type:" << ctype;
1576     return w;
1577 }
1578 
1579 void TypeNode::changedXML (QTextStream & out) {
1580     if (!w) return;
1581     QByteArray ba = getAttribute (Ids::attr_type).toAscii ();
1582     const char *ctype = ba.constData ();
1583     QString value = getAttribute (Ids::attr_value);
1584     QString newvalue;
1585     if (!strcmp (ctype, "range")) {
1586         newvalue = QString::number (static_cast <QSlider *> (w)->value ());
1587     } else if (!strcmp (ctype, "num") || !strcmp (ctype,  "string")) {
1588         newvalue = static_cast <QLineEdit *> (w)->text ();
1589     } else if (!strcmp (ctype, "bool")) {
1590         newvalue = QString::number (static_cast <QCheckBox *> (w)->isChecked());
1591     } else if (!strcmp (ctype, "enum")) {
1592         newvalue = QString::number (static_cast<QComboBox *>(w)->currentIndex());
1593     } else if (!strcmp (ctype, "tree")) {
1594     } else
1595         kDebug() << "Unknown type:" << ctype;
1596     if (value != newvalue) {
1597         value = newvalue;
1598         setAttribute (Ids::attr_value, newvalue);
1599         out << outerXML ();
1600     }
1601 }
1602 
1603 //-----------------------------------------------------------------------------
1604 
1605 static const char * ffmpeg_supports [] = {
1606     "tvsource", "urlsource", 0L
1607 };
1608 
1609 FFMpegProcessInfo::FFMpegProcessInfo (MediaManager *mgr)
1610  : ProcessInfo ("ffmpeg", i18n ("&FFMpeg"), ffmpeg_supports, mgr, NULL) {}
1611 
1612 IProcess *FFMpegProcessInfo::create (PartBase *p, ProcessUser *usr) {
1613     FFMpeg *m = new FFMpeg (p, this, p->settings ());
1614     m->setSource (p->source ());
1615     m->user = usr;
1616     p->processCreated (m);
1617     return m;
1618 }
1619 
1620 FFMpeg::FFMpeg (QObject *parent, ProcessInfo *pinfo, Settings * settings)
1621  : Process (parent, pinfo, settings) {
1622 }
1623 
1624 KDE_NO_CDTOR_EXPORT FFMpeg::~FFMpeg () {
1625 }
1626 
1627 KDE_NO_EXPORT void FFMpeg::init () {
1628 }
1629 
1630 bool FFMpeg::deMediafiedPlay () {
1631     RecordDocument *rd = recordDocument (user);
1632     if (!rd)
1633         return false;
1634     initProcess ();
1635     connect (m_process, SIGNAL (finished (int, QProcess::ExitStatus)),
1636             this, SLOT (processStopped (int, QProcess::ExitStatus)));
1637     QString outurl = encodeFileOrUrl (rd->record_file);
1638     if (outurl.startsWith (QChar ('/')))
1639         QFile (outurl).remove ();
1640     QString exe ("ffmpeg ");
1641     QStringList args;
1642     if (!m_source->videoDevice ().isEmpty () ||
1643         !m_source->audioDevice ().isEmpty ()) {
1644         if (!m_source->videoDevice ().isEmpty ())
1645             args << "-vd" << m_source->videoDevice ();
1646         else
1647             args << "-vn";
1648         if (!m_source->audioDevice ().isEmpty ())
1649             args << "-ad" << m_source->audioDevice ();
1650         else
1651             args << "-an";
1652         QProcess process;
1653         QString ctl_exe ("v4lctl");
1654         QStringList ctl_args;
1655         if (!m_source->videoNorm ().isEmpty ()) {
1656             ctl_args << "-c" << m_source->videoDevice () << "setnorm" << m_source->videoNorm ();
1657             process.start (ctl_exe, ctl_args);
1658             process.waitForFinished (5000);
1659             args << "-tvstd" << m_source->videoNorm ();
1660         }
1661         if (m_source->frequency () > 0) {
1662             ctl_args.clear ();
1663             ctl_args << "-c" << m_source->videoDevice () << "setfreq" << QString::number (m_source->frequency ());
1664             process.start (ctl_exe, ctl_args);
1665             process.waitForFinished (5000);
1666         }
1667     } else {
1668         args << "-i" << encodeFileOrUrl (m_url);
1669     }
1670     args << KShell::splitArgs (m_settings->ffmpegarguments);
1671     args << outurl;
1672     qDebug ("ffmpeg %s\n", args.join (" ").toLocal8Bit().constData ());
1673     // FIXME if (m_player->source () == source) // ugly
1674     //    m_player->stop ();
1675     m_process->start (exe, args);
1676     if (m_process->waitForStarted ()) {
1677         setState (Playing);
1678         return true;
1679     }
1680     stop ();
1681     return false;
1682 }
1683 
1684 KDE_NO_EXPORT void FFMpeg::stop () {
1685     terminateJobs ();
1686     if (!running ())
1687         return;
1688     kDebug () << "FFMpeg::stop";
1689     m_process->write ("q");
1690 }
1691 
1692 KDE_NO_EXPORT void FFMpeg::quit () {
1693     stop ();
1694     if (!running ())
1695         return;
1696     if (m_process->waitForFinished (2000))
1697         Process::quit ();
1698 }
1699 
1700 KDE_NO_EXPORT void FFMpeg::processStopped (int, QProcess::ExitStatus) {
1701     setState (IProcess::NotRunning);
1702 }
1703 
1704 //-----------------------------------------------------------------------------
1705 
1706 static const char *npp_supports [] = { "urlsource", 0L };
1707 
1708 NppProcessInfo::NppProcessInfo (MediaManager *mgr)
1709  : ProcessInfo ("npp", i18n ("&Ice Ape"), npp_supports, mgr, NULL) {}
1710 
1711 IProcess *NppProcessInfo::create (PartBase *p, ProcessUser *usr) {
1712     NpPlayer *n = new NpPlayer (p, this, p->settings());
1713     n->setSource (p->source ());
1714     n->user = usr;
1715     p->processCreated (n);
1716     return n;
1717 }
1718 
1719 #ifdef KMPLAYER_WITH_NPP
1720 
1721 KDE_NO_CDTOR_EXPORT NpStream::NpStream (NpPlayer *p, uint32_t sid, const QString &u, const QByteArray &ps)
1722  : QObject (p),
1723    url (u),
1724    post (ps),
1725    job (0L), bytes (0),
1726    stream_id (sid),
1727    content_length (0),
1728    finish_reason (NoReason),
1729    received_data (false) {
1730     data_arrival.tv_sec = 0;
1731     (void) new StreamAdaptor (this);
1732     QString objpath = QString ("%1/stream_%2").arg (p->objectPath ()).arg (sid);
1733     QDBusConnection::sessionBus().registerObject (objpath, this);
1734 }
1735 
1736 KDE_NO_CDTOR_EXPORT NpStream::~NpStream () {
1737     close ();
1738 }
1739 
1740 KDE_NO_EXPORT void NpStream::open () {
1741     kDebug () << "NpStream " << stream_id << " open " << url;
1742     if (url.startsWith ("javascript:")) {
1743         NpPlayer *npp = static_cast <NpPlayer *> (parent ());
1744         QString result = npp->evaluate (url.mid (11), false);
1745         if (!result.isEmpty ()) {
1746             QByteArray cr = result.toLocal8Bit ();
1747             int len = strlen (cr.constData ());
1748             pending_buf.resize (len + 1);
1749             memcpy (pending_buf.data (), cr.constData (), len);
1750             pending_buf.data ()[len] = 0;
1751             gettimeofday (&data_arrival, 0L);
1752         }
1753         kDebug () << "result is " << pending_buf.constData ();
1754         finish_reason = BecauseDone;
1755         emit stateChanged ();
1756     } else {
1757         if (!post.size ()) {
1758             job = KIO::get (KUrl (url), KIO::NoReload, KIO::HideProgressInfo);
1759             job->addMetaData ("PropagateHttpHeader", "true");
1760         } else {
1761             QStringList name;
1762             QStringList value;
1763             QString buf;
1764             int data_pos = -1;
1765             for (int i = 0; i < post.size () && data_pos < 0; ++i) {
1766                 char c = post.at (i);
1767                 switch (c) {
1768                 case ':':
1769                     if (name.size () == value.size ()) {
1770                         name << buf;
1771                         buf.truncate (0);
1772                     } else
1773                         buf += QChar (':');
1774                     break;
1775                 case '\r':
1776                     break;
1777                 case '\n':
1778                     if (name.size () == value.size ()) {
1779                         if (buf.isEmpty ()) {
1780                             data_pos = i + 1;
1781                         } else {
1782                             name << buf;
1783                             value << QString ("");
1784                         }
1785                     } else {
1786                         value << buf;
1787                     }
1788                     buf.truncate (0);
1789                     break;
1790                 default:
1791                     buf += QChar (c);
1792                 }
1793             }
1794             job = KIO::http_post (KUrl (url), post.mid (data_pos), KIO::HideProgressInfo);
1795             for (int i = 0; i < name.size (); ++i)
1796                 job->addMetaData (name[i].trimmed (), value[i].trimmed ());
1797         }
1798         job->addMetaData ("errorPage", "false");
1799         connect (job, SIGNAL (data (KIO::Job *, const QByteArray &)),
1800                 this, SLOT (slotData (KIO::Job *, const QByteArray &)));
1801         connect (job, SIGNAL (result (KJob *)),
1802                 this, SLOT (slotResult (KJob *)));
1803         connect (job, SIGNAL(redirection(KIO::Job*, const QUrl&)),
1804                 this, SLOT (redirection (KIO::Job *, const QUrl &)));
1805         connect (job, SIGNAL (mimetype (KIO::Job *, const QString &)),
1806                 SLOT (slotMimetype (KIO::Job *, const QString &)));
1807         connect (job, SIGNAL (totalSize (KJob *, qulonglong)),
1808                 SLOT (slotTotalSize (KJob *, qulonglong)));
1809     }
1810 }
1811 
1812 KDE_NO_EXPORT void NpStream::close () {
1813     if (job) {
1814         job->kill (); // quiet, no result signal
1815         job = 0L;
1816         finish_reason = BecauseStopped;
1817         // don't emit stateChanged(), because always triggered from NpPlayer
1818     }
1819 }
1820 
1821 KDE_NO_EXPORT void NpStream::destroy () {
1822     pending_buf.clear ();
1823     static_cast <NpPlayer *> (parent ())->destroyStream (stream_id);
1824 }
1825 
1826 KDE_NO_EXPORT void NpStream::slotResult (KJob *jb) {
1827     kDebug() << "slotResult " << stream_id << " " << bytes << " err:" << jb->error ();
1828     finish_reason = jb->error () ? BecauseError : BecauseDone;
1829     job = 0L; // signal KIO::Job::result deletes itself
1830     emit stateChanged ();
1831 }
1832 
1833 KDE_NO_EXPORT void NpStream::slotData (KIO::Job*, const QByteArray& qb) {
1834     if (job) {
1835         int sz = pending_buf.size ();
1836         if (sz) {
1837             pending_buf.resize (sz + qb.size ());
1838             memcpy (pending_buf.data () + sz, qb.constData (), qb.size ());
1839         } else {
1840             pending_buf = qb;
1841         }
1842         if (sz + qb.size () > 64000 &&
1843                 !job->isSuspended () && !job->suspend ())
1844             kError () << "suspend not supported" << endl;
1845         if (!sz)
1846             gettimeofday (&data_arrival, 0L);
1847         if (!received_data) {
1848             received_data = true;
1849             http_headers = job->queryMetaData ("HTTP-Headers");
1850             if (!http_headers.isEmpty() && !http_headers.endsWith (QChar ('\n')))
1851                 http_headers += QChar ('\n');
1852         }
1853         if (sz + qb.size ())
1854             emit stateChanged ();
1855     }
1856 }
1857 
1858 KDE_NO_EXPORT void NpStream::redirection (KIO::Job*, const QUrl& kurl) {
1859     url = kurl.url ();
1860     emit redirected (stream_id, kurl);
1861 }
1862 
1863 void NpStream::slotMimetype (KIO::Job *, const QString &mime) {
1864     mimetype = mime.indexOf("adobe.flash") > -1 ? "application/x-shockwave-flash" : mime;
1865 }
1866 
1867 void NpStream::slotTotalSize (KJob *, qulonglong sz) {
1868     content_length = sz;
1869 }
1870 
1871 KDE_NO_CDTOR_EXPORT
1872 NpPlayer::NpPlayer (QObject *parent, ProcessInfo *pinfo, Settings *settings)
1873  : Process (parent, pinfo, settings),
1874    write_in_progress (false),
1875    in_process_stream (false) {
1876 }
1877 
1878 KDE_NO_CDTOR_EXPORT NpPlayer::~NpPlayer () {
1879 }
1880 
1881 KDE_NO_EXPORT void NpPlayer::init () {
1882 }
1883 
1884 KDE_NO_EXPORT void NpPlayer::initProcess () {
1885     setupProcess (&m_process);
1886     m_process_state = QProcess::NotRunning;
1887     connect (m_process, SIGNAL (finished (int, QProcess::ExitStatus)),
1888             this, SLOT (processStopped (int, QProcess::ExitStatus)));
1889     connect (m_process, SIGNAL (readyReadStandardError ()),
1890             this, SLOT (processOutput ()));
1891     connect (m_process, SIGNAL (bytesWritten (qint64)),
1892             this, SLOT (wroteStdin (qint64)));
1893     if (iface.isEmpty ()) {
1894         iface = QString ("org.kde.kmplayer.callback");
1895         static int count = 0;
1896         path = QString ("/npplayer%1").arg (count++);
1897         (void) new CallbackAdaptor (this);
1898         QDBusConnection::sessionBus().registerObject (path, this);
1899         filter = QString ("type='method_call',interface='org.kde.kmplayer.callback'");
1900         service = QDBusConnection::sessionBus().baseService ();
1901         //service = QString (dbus_bus_get_unique_name (conn));
1902         kDebug() << "using service " << service << " interface " << iface << " filter:" << filter;
1903     }
1904 }
1905 
1906 KDE_NO_EXPORT bool NpPlayer::deMediafiedPlay () {
1907     kDebug() << "NpPlayer::play '" << m_url << "' state " << m_state;
1908     // if we change from XPLAIN to XEMBED, the DestroyNotify may come later
1909     if (!view ())
1910         return false;
1911     if (!m_url.isEmpty () && m_url != "about:empty") {
1912         QDBusMessage msg = QDBusMessage::createMethodCall (
1913                 remote_service, "/plugin", "org.kde.kmplayer.backend", "play");
1914         msg << m_url;
1915         msg.setDelayedReply (false);
1916         QDBusConnection::sessionBus().send (msg);
1917         setState (IProcess::Buffering);
1918     }
1919     return true;
1920 }
1921 
1922 KDE_NO_EXPORT bool NpPlayer::ready () {
1923     Mrl *node = mrl ();
1924     if (!node || !user || !user->viewer ())
1925         return false;
1926 
1927     user->viewer ()->useIndirectWidget (false);
1928     user->viewer ()->setMonitoring (IViewer::MonitorNothing);
1929 
1930     if (state () == IProcess::Ready)
1931         return true;
1932 
1933     initProcess ();
1934     QString exe ("knpplayer");
1935     QStringList args;
1936     args << "-cb" << (service + path);
1937     args << "-wid" << QString::number (user->viewer ()->windowHandle ());
1938     startProcess (exe, args);
1939     if (m_process->waitForStarted (5000)) {
1940         QString s;
1941         for (int i = 0; i < 2 && remote_service.isEmpty (); ++i) {
1942             if (!m_process->waitForReadyRead (5000))
1943                 return false;
1944             const QByteArray ba = m_process->readAllStandardOutput ();
1945             s += QString::fromAscii (ba.data (), ba.size ());
1946             int nl = s.indexOf (QChar ('\n'));
1947             if (nl > 0) {
1948                 int p = s.indexOf ("NPP_DBUS_SRV=");
1949                 if (p > -1)
1950                     remote_service = s.mid (p + 13, nl - p - 13);
1951             }
1952         }
1953     }
1954     connect (m_process, SIGNAL (readyReadStandardOutput ()),
1955             this, SLOT (processOutput ()));
1956     if (!remote_service.isEmpty ()) {
1957         QString mime = "text/plain";
1958         QString plugin;
1959         Element *elm = node;
1960         if (elm->id == id_node_html_object) {
1961             // this sucks to have to do this here ..
1962             for (Node *n = elm->firstChild (); n; n = n->nextSibling ())
1963                 if (n->id == KMPlayer::id_node_html_embed) {
1964                     elm = static_cast <Element *> (n);
1965                     break;
1966                 }
1967         }
1968         for (Node *n = mrl (); n; n = n->parentNode ()) {
1969             Mrl *mrl = n->mrl ();
1970             if (mrl && !mrl->mimetype.isEmpty ()) {
1971                 plugin = m_source->plugin (mrl->mimetype);
1972                 kDebug() << "search plugin " << mrl->mimetype << "->" << plugin;
1973                 if (!plugin.isEmpty ()) {
1974                     mime = mrl->mimetype;
1975                     if ( mime.indexOf("adobe.flash") > -1 )
1976                         mime = "application/x-shockwave-flash";
1977                     break;
1978                 }
1979             }
1980         }
1981         if (!plugin.isEmpty ()) {
1982             QDBusMessage msg = QDBusMessage::createMethodCall (
1983                     remote_service, "/plugin", "org.kde.kmplayer.backend", "setup");
1984             msg << mime << plugin;
1985             QMap <QString, QVariant> urlargs;
1986             for (AttributePtr a = elm->attributes ().first (); a; a = a->nextSibling ())
1987                 urlargs.insert (a->name ().toString (), a->value ());
1988             msg << urlargs;
1989             msg.setDelayedReply (false);
1990             QDBusConnection::sessionBus().call (msg, QDBus::BlockWithGui);
1991             setState (IProcess::Ready);
1992             return true;
1993         }
1994     }
1995     m_old_state = m_state = Ready;
1996     stop ();
1997 
1998     return false;
1999 }
2000 
2001 KDE_NO_EXPORT void NpPlayer::running (const QString &srv) {
2002     remote_service = srv;
2003     kDebug () << "NpPlayer::running " << srv;
2004     setState (Ready);
2005 }
2006 
2007 void NpPlayer::plugged () {
2008     view ()->videoStart ();
2009 }
2010 
2011 static int getStreamId (const QString &path) {
2012     int p = path.lastIndexOf (QChar ('_'));
2013     if (p < 0) {
2014         kError() << "wrong object path " << path << endl;
2015         return -1;
2016     }
2017     bool ok;
2018     qint32 sid = path.mid (p+1).toInt (&ok);
2019     if (!ok) {
2020         kError() << "wrong object path suffix " << path.mid (p+1) << endl;
2021         return -1;
2022     }
2023     return sid;
2024 }
2025 
2026 KDE_NO_EXPORT void NpPlayer::request_stream (const QString &path, const QString &url, const QString &target, const QByteArray &post) {
2027     QString uri (url);
2028     kDebug () << "NpPlayer::request " << path << " '" << url << "' " << " tg:" << target << "post" << post.size ();
2029     bool js = url.startsWith ("javascript:");
2030     if (!js) {
2031         QString base = process_info->manager->player ()->docBase ().url ();
2032         uri = KUrl (base.isEmpty () ? m_url : base, url).url ();
2033     }
2034     kDebug () << "NpPlayer::request " << path << " '" << uri << "'" << m_url << "->" << url;
2035     qint32 sid = getStreamId (path);
2036     if ((int)sid >= 0) {
2037         if (!target.isEmpty ()) {
2038             kDebug () << "new page request " << target;
2039             if (js) {
2040                 QString result = evaluate (url.mid (11), false);
2041                 kDebug() << "result is " << result;
2042                 if (result == "undefined")
2043                     uri = QString ();
2044                 else
2045                     uri = KUrl (m_url, result).url (); // probably wrong ..
2046             }
2047             KUrl kurl(uri);
2048             if (kurl.isValid ())
2049                 process_info->manager->player ()->openUrl (kurl, target, QString ());
2050             sendFinish (sid, 0, NpStream::BecauseDone);
2051         } else {
2052             NpStream * ns = new NpStream (this, sid, uri, post);
2053             connect (ns, SIGNAL (stateChanged ()), this, SLOT (streamStateChanged ()));
2054             streams[sid] = ns;
2055             if (url != uri)
2056                 streamRedirected (sid, uri);
2057             if (!write_in_progress)
2058                 processStreams ();
2059         }
2060     }
2061 }
2062 
2063 KDE_NO_EXPORT QString NpPlayer::evaluate (const QString &script, bool store) {
2064     QString result ("undefined");
2065     emit evaluate (script, store, result);
2066     //kDebug () << "evaluate " << script << " => " << result;
2067     return result;
2068 }
2069 
2070 KDE_NO_EXPORT void NpPlayer::dimension (int w, int h) {
2071     source ()->setAspect (mrl (), 1.0 * w/ h);
2072 }
2073 
2074 KDE_NO_EXPORT void NpPlayer::destroyStream (uint32_t sid) {
2075     if (streams.contains (sid)) {
2076         NpStream *ns = streams[sid];
2077         ns->close ();
2078         if (!write_in_progress)
2079             processStreams ();
2080     } else {
2081         kWarning () << "Object " << sid << " not found";
2082     }
2083     if (!sid)
2084         emit loaded ();
2085 }
2086 
2087 KDE_NO_EXPORT
2088 void NpPlayer::sendFinish (quint32 sid, quint32 bytes, NpStream::Reason because) {
2089     kDebug() << "NpPlayer::sendFinish " << sid << " bytes:" << bytes;
2090     if (running ()) {
2091         uint32_t reason = (int) because;
2092         QString objpath = QString ("/stream_%1").arg (sid);
2093         QDBusMessage msg = QDBusMessage::createMethodCall (
2094                 remote_service, objpath, "org.kde.kmplayer.backend", "eof");
2095         msg << bytes << reason;
2096         msg.setDelayedReply (false);
2097         QDBusConnection::sessionBus().send (msg);
2098     }
2099     if (!sid)
2100         emit loaded ();
2101 }
2102 
2103 KDE_NO_EXPORT void NpPlayer::terminateJobs () {
2104     Process::terminateJobs ();
2105     const StreamMap::iterator e = streams.end ();
2106     for (StreamMap::iterator i = streams.begin (); i != e; ++i)
2107         delete i.value ();
2108     streams.clear ();
2109 }
2110 
2111 KDE_NO_EXPORT void NpPlayer::stop () {
2112     terminateJobs ();
2113     if (!running ())
2114         return;
2115     kDebug () << "NpPlayer::stop ";
2116     QDBusMessage msg = QDBusMessage::createMethodCall (
2117             remote_service, "/plugin", "org.kde.kmplayer.backend", "quit");
2118     msg.setDelayedReply (false);
2119     QDBusConnection::sessionBus().send (msg);
2120 }
2121 
2122 KDE_NO_EXPORT void NpPlayer::quit () {
2123     if (running () && !m_process->waitForFinished (2000))
2124         Process::quit ();
2125 }
2126 
2127 KDE_NO_EXPORT void NpPlayer::processOutput () {
2128     if (!remote_service.isEmpty ())
2129         outputToView (view (), m_process->readAllStandardOutput ());
2130     outputToView (view (), m_process->readAllStandardError ());
2131 }
2132 
2133 KDE_NO_EXPORT void NpPlayer::processStopped (int, QProcess::ExitStatus) {
2134     terminateJobs ();
2135     if (m_source)
2136         m_source->document ()->message (MsgInfoString, NULL);
2137     setState (IProcess::NotRunning);
2138 }
2139 
2140 KDE_NO_EXPORT void NpPlayer::streamStateChanged () {
2141     setState (IProcess::Playing); // hmm, this doesn't really fit in current states
2142     if (!write_in_progress)
2143         processStreams ();
2144 }
2145 
2146 KDE_NO_EXPORT void NpPlayer::streamRedirected(uint32_t sid, const QUrl& u) {
2147     if (running ()) {
2148         kDebug() << "redirected " << sid << " to " << u.url();
2149         QString objpath = QString ("/stream_%1").arg (sid);
2150         QDBusMessage msg = QDBusMessage::createMethodCall (
2151                 remote_service, objpath, "org.kde.kmplayer.backend", "redirected");
2152         msg << u.url ();
2153         msg.setDelayedReply (false);
2154         QDBusConnection::sessionBus().send (msg);
2155     }
2156 }
2157 
2158 KDE_NO_EXPORT
2159 void NpPlayer::requestGet (const uint32_t id, const QString &prop, QString *res) {
2160     if (!remote_service.isEmpty ()) {
2161         QDBusMessage msg = QDBusMessage::createMethodCall (
2162                 remote_service, "/plugin", "org.kde.kmplayer.backend", "get");
2163         msg << id << prop;
2164         QDBusMessage rmsg = QDBusConnection::sessionBus().call (msg, QDBus::BlockWithGui);
2165         if (rmsg.type () == QDBusMessage::ReplyMessage) {
2166             //kDebug() << "get " << prop << rmsg.arguments ().size ();
2167             if (rmsg.arguments ().size ()) {
2168                 QString s = rmsg.arguments ().first ().toString ();
2169                 if (s != "error")
2170                     *res = s;
2171             }
2172         } else {
2173             kError() << "get" << prop << rmsg.type () << rmsg.errorMessage ();
2174         }
2175     }
2176 }
2177 
2178 KDE_NO_EXPORT void NpPlayer::requestCall (const uint32_t id, const QString &func,
2179         const QStringList &args, QString *res) {
2180     QDBusMessage msg = QDBusMessage::createMethodCall (
2181             remote_service, "/plugin", "org.kde.kmplayer.backend", "call");
2182     msg << id << func << args;
2183     QDBusMessage rmsg = QDBusConnection::sessionBus().call (msg, QDBus::BlockWithGui);
2184     //kDebug() << "call " << func << rmsg.arguments ().size ();
2185     if (rmsg.arguments ().size ()) {
2186         QString s = rmsg.arguments ().first ().toString ();
2187         if (s != "error")
2188             *res = s;
2189     }
2190 }
2191 
2192 KDE_NO_EXPORT void NpPlayer::processStreams () {
2193     NpStream *stream = 0L;
2194     qint32 stream_id;
2195     timeval tv = { 0x7fffffff, 0 };
2196     const StreamMap::iterator e = streams.end ();
2197     int active_count = 0;
2198 
2199     if (in_process_stream || write_in_progress) {
2200         kDebug() << "wrong call" << kBacktrace();
2201         return;
2202     }
2203     in_process_stream = true;
2204 
2205     //kDebug() << "NpPlayer::processStreams " << streams.size ();
2206     for (StreamMap::iterator i = streams.begin (); i != e;) {
2207         NpStream *ns = i.value ();
2208         if (ns->job) {
2209             active_count++;
2210         } else if (active_count < 5 &&
2211                 ns->finish_reason == NpStream::NoReason) {
2212             write_in_progress = true; // javascript: urls emit stateChange
2213             ns->open ();
2214             write_in_progress = false;
2215             if (ns->job) {
2216                 connect(ns, SIGNAL(redirected(uint32_t, const QUrl&)),
2217                         this, SLOT(streamRedirected(uint32_t, const QUrl&)));
2218                 active_count++;
2219             }
2220         }
2221         if (ns->finish_reason == NpStream::BecauseStopped ||
2222                 ns->finish_reason == NpStream::BecauseError ||
2223                 (ns->finish_reason == NpStream::BecauseDone &&
2224                  ns->pending_buf.size () == 0)) {
2225             sendFinish (i.key(), ns->bytes, ns->finish_reason);
2226             i = streams.erase (i);
2227             delete ns;
2228         } else {
2229             if (ns->pending_buf.size () > 0 &&
2230                     (ns->data_arrival.tv_sec < tv.tv_sec ||
2231                      (ns->data_arrival.tv_sec == tv.tv_sec &&
2232                       ns->data_arrival.tv_usec < tv.tv_usec))) {
2233                 tv = ns->data_arrival;
2234                 stream = ns;
2235                 stream_id = i.key();
2236             }
2237             ++i;
2238         }
2239     }
2240     //kDebug() << "NpPlayer::processStreams " << stream;
2241     if (stream) {
2242         if (stream->finish_reason != NpStream::BecauseStopped &&
2243                 stream->finish_reason != NpStream::BecauseError &&
2244                 !stream->bytes &&
2245                 (!stream->mimetype.isEmpty() || stream->content_length)) {
2246             QString objpath = QString ("/stream_%1").arg (stream->stream_id);
2247             QDBusMessage msg = QDBusMessage::createMethodCall (
2248                     remote_service, objpath, "org.kde.kmplayer.backend", "streamInfo");
2249             msg << stream->mimetype
2250                 << stream->content_length
2251                 << stream->http_headers;
2252             msg.setDelayedReply (false);
2253             QDBusConnection::sessionBus().send (msg);
2254         }
2255         const int header_len = 2 * sizeof (qint32);
2256         qint32 chunk = stream->pending_buf.size ();
2257         send_buf.resize (chunk + header_len);
2258         memcpy (send_buf.data (), &stream_id, sizeof (qint32));
2259         memcpy (send_buf.data() + sizeof (qint32), &chunk, sizeof (qint32));
2260         memcpy (send_buf.data ()+header_len,
2261                 stream->pending_buf.constData (), chunk);
2262         stream->pending_buf = QByteArray ();
2263         /*fprintf (stderr, " => %d %d\n", (long)stream_id, chunk);*/
2264         stream->bytes += chunk;
2265         write_in_progress = true;
2266         m_process->write (send_buf);
2267         if (stream->finish_reason == NpStream::NoReason)
2268             stream->job->resume ();
2269     }
2270     in_process_stream = false;
2271 }
2272 
2273 KDE_NO_EXPORT void NpPlayer::wroteStdin (qint64) {
2274     if (!m_process->bytesToWrite ()) {
2275         write_in_progress = false;
2276         if (running ())
2277             processStreams ();
2278     }
2279 }
2280 
2281 QString NpPlayer::cookie (const QString &url)
2282 {
2283     QString s;
2284     View *v = view ();
2285     if (v) {
2286         KIO::Integration::CookieJar jar (v);
2287         jar.setWindowId (v->topLevelWidget()->winId ());
2288         QList<QNetworkCookie> c = jar.cookiesForUrl (url);
2289         QList<QNetworkCookie>::const_iterator e = c.constEnd ();
2290         for (QList<QNetworkCookie>::const_iterator i = c.constBegin (); i != e; ++i)
2291             s += (s.isEmpty() ? "" : ";") + QString::fromUtf8 ((*i).toRawForm());
2292     }
2293     return s;
2294 }
2295 
2296 #else
2297 
2298 KDE_NO_CDTOR_EXPORT NpStream::NpStream (NpPlayer *p, uint32_t sid, const QString &u, const QByteArray &/*ps*/)
2299     : QObject (p) {}
2300 
2301 KDE_NO_CDTOR_EXPORT NpStream::~NpStream () {}
2302 void NpStream::slotResult (KJob*) {}
2303 void NpStream::slotData (KIO::Job*, const QByteArray&) {}
2304 void NpStream::redirection(KIO::Job*, const QUrl&) {}
2305 void NpStream::slotMimetype (KIO::Job *, const QString &) {}
2306 void NpStream::slotTotalSize (KJob *, KIO::filesize_t) {}
2307 
2308 KDE_NO_CDTOR_EXPORT
2309 NpPlayer::NpPlayer (QObject *parent, ProcessInfo *pinfo, Settings *settings)
2310  : Process (parent, pinfo, settings) {}
2311 KDE_NO_CDTOR_EXPORT NpPlayer::~NpPlayer () {}
2312 KDE_NO_EXPORT void NpPlayer::init () {}
2313 KDE_NO_EXPORT bool NpPlayer::deMediafiedPlay () { return false; }
2314 KDE_NO_EXPORT void NpPlayer::initProcess () {}
2315 KDE_NO_EXPORT void NpPlayer::stop () {}
2316 KDE_NO_EXPORT void NpPlayer::quit () { }
2317 KDE_NO_EXPORT bool NpPlayer::ready () { return false; }
2318 KDE_NO_EXPORT void NpPlayer::requestGet (const uint32_t, const QString &, QString *) {}
2319 KDE_NO_EXPORT void NpPlayer::requestCall (const uint32_t, const QString &, const QStringList &, QString *) {}
2320 KDE_NO_EXPORT void NpPlayer::processOutput () {}
2321 KDE_NO_EXPORT void NpPlayer::processStopped (int, QProcess::ExitStatus) {}
2322 KDE_NO_EXPORT void NpPlayer::wroteStdin (qint64) {}
2323 KDE_NO_EXPORT void NpPlayer::streamStateChanged () {}
2324 KDE_NO_EXPORT void NpPlayer::streamRedirected(uint32_t, const QUrl&) {}
2325 KDE_NO_EXPORT void NpPlayer::terminateJobs () {}
2326 
2327 #endif
2328 
2329 #include "kmplayerprocess.moc"