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