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