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"