File indexing completed on 2024-04-21 04:53:50

0001 /*
0002     This file is part of the KMPlayer application
0003     SPDX-FileCopyrightText: 2003 Koos Vriezen <koos.vriezen@xs4all.nl>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include <QLayout>
0009 #include <QLabel>
0010 #include <QTimer>
0011 #include <QPushButton>
0012 #include <QCheckBox>
0013 #include <QTableWidget>
0014 #include <QStringList>
0015 #include <QComboBox>
0016 #include <QLineEdit>
0017 #include <QGroupBox>
0018 #include <QWhatsThis>
0019 #include <QTabWidget>
0020 #include <QMessageBox>
0021 #include <QHeaderView>
0022 #include <QMenu>
0023 #include <QStandardPaths>
0024 #include <QFontMetrics>
0025 
0026 #include <kwidgetsaddons_version.h>
0027 #include <KLocalizedString>
0028 #include <KMessageBox>
0029 #include <KLineEdit>
0030 #include <KUrlRequester>
0031 #include <KComboBox>
0032 #include <KConfig>
0033 #include <KConfigGroup>
0034 
0035 #include "kmplayerapp_log.h"
0036 #include "kmplayerpartbase.h"
0037 #include "kmplayerprocess.h"
0038 #include "kmplayerconfig.h"
0039 #include "kmplayertvsource.h"
0040 #include "playmodel.h"
0041 #include "playlistview.h"
0042 #include "viewarea.h"
0043 #include "kmplayer.h"
0044 #include "kmplayercontrolpanel.h"
0045 
0046 static const char * strTV = "TV";
0047 static const char * strTVDriver = "Driver";
0048 
0049 
0050 TVDevicePage::TVDevicePage (QWidget *parent, KMPlayer::NodePtr dev)
0051 : QFrame (parent), device_doc (dev) {
0052     setObjectName("PageTVDevice");
0053     TVDevice * device = KMPlayer::convertNode <TVDevice> (device_doc);
0054     QLabel* deviceLabel = new QLabel(i18n("Video device:") + device->src);
0055     QLabel* audioLabel = new QLabel(i18n("Audio device:"));
0056     audiodevice = new KUrlRequester (QUrl::fromUserInput (device->getAttribute ("audio")));
0057     QLabel* nameLabel = new QLabel(i18n("Name:"));
0058     name = new QLineEdit(device->title);
0059     QLabel *sizewidthLabel = new QLabel(i18n("Width:"));
0060     sizewidth = new QLineEdit(device->getAttribute(KMPlayer::Ids::attr_width));
0061     QLabel* sizeheightLabel = new QLabel (i18n ("Height:"));
0062     sizeheight = new QLineEdit(device->getAttribute(KMPlayer::Ids::attr_height));
0063     noplayback = new QCheckBox(i18n("Do not immediately play"));
0064     noplayback->setChecked (!device->getAttribute ("playback").toInt ());
0065     noplayback->setWhatsThis(i18n("Only start playing after clicking the play button"));
0066     inputsTab = new QTabWidget;
0067     for (KMPlayer::Node *ip = device->firstChild (); ip; ip = ip->nextSibling ()) {
0068         if (ip->id != id_node_tv_input)
0069             continue;
0070         TVInput * input = KMPlayer::convertNode <TVInput> (ip);
0071         QWidget* widget = new QWidget;
0072         QHBoxLayout* tablayout = new QHBoxLayout;
0073         if (!input->getAttribute ("tuner").isEmpty ()) {
0074             QHBoxLayout* horzlayout = new QHBoxLayout;
0075             QVBoxLayout* vertlayout = new QVBoxLayout;
0076             horzlayout->addWidget(new QLabel(i18n("Norm:")));
0077             QComboBox* norms = new QComboBox;
0078             norms->setObjectName("PageTVNorm");
0079             norms->addItem(QString("NTSC"));
0080             norms->addItem(QString("PAL"));
0081             norms->addItem(QString("SECAM"));
0082             norms->setCurrentIndex(norms->findText(input->getAttribute ("norm")));
0083             horzlayout->addWidget (norms);
0084             vertlayout->addLayout (horzlayout);
0085             vertlayout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
0086             QTableWidget* table = new QTableWidget(90, 2);
0087             table->setObjectName("PageTVChannels");
0088             table->setContentsMargins(0, 0, 0, 0);
0089             QFontMetrics metrics (table->font ());
0090             QStringList labels = QStringList() << i18n("Channel") << i18n("Frequency (MHz)");
0091             table->setHorizontalHeaderLabels(labels);
0092             int index = 0;
0093             int first_column_width = QFontMetrics(table->horizontalHeader()->font()).boundingRect(labels[0]).width() + 20;
0094             for (KMPlayer::Node *c=input->firstChild();c;c=c->nextSibling()) {
0095                 if (c->id != id_node_tv_channel)
0096                     continue;
0097                 int strwid = metrics.boundingRect (c->mrl ()->title).width ();
0098                 if (strwid > first_column_width)
0099                     first_column_width = strwid + 4;
0100                 table->setItem(index, 0, new QTableWidgetItem(c->mrl()->title));
0101                 table->setItem(index++, 1, new QTableWidgetItem(KMPlayer::convertNode<TVChannel>(c)->getAttribute("frequency")));
0102             }
0103             table->setColumnWidth (0, first_column_width);
0104             table->horizontalHeader()->setStretchLastSection(true);
0105             tablayout->addWidget (table);
0106             tablayout->addLayout (vertlayout);
0107         }
0108         widget->setLayout(tablayout);
0109         inputsTab->addTab (widget, input->mrl ()->title);
0110     }
0111     QPushButton* delButton = new QPushButton(i18n("Delete"));
0112     connect (delButton, &QPushButton::clicked, this, &TVDevicePage::slotDelete);
0113     QGridLayout* gridlayout = new QGridLayout;
0114     gridlayout->addWidget (audioLabel, 0, 0);
0115     gridlayout->addWidget (audiodevice, 0, 0, 1, 3);
0116     gridlayout->addWidget (nameLabel, 1, 0);
0117     gridlayout->addWidget (name, 1, 1, 1, 3);
0118     gridlayout->addWidget (sizewidthLabel, 2, 0);
0119     gridlayout->addWidget (sizewidth, 2, 1);
0120     gridlayout->addWidget (sizeheightLabel, 2, 2);
0121     gridlayout->addWidget (sizeheight, 2, 3);
0122     QVBoxLayout* layout = new QVBoxLayout;
0123     layout->addWidget(deviceLabel);
0124     layout->addLayout(gridlayout);
0125     layout->addWidget (inputsTab);
0126     layout->addSpacing (5);
0127     layout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Minimum));
0128     QHBoxLayout *buttonlayout = new QHBoxLayout ();
0129     buttonlayout->addWidget (noplayback);
0130     buttonlayout->addItem (new QSpacerItem (0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
0131     buttonlayout->addWidget (delButton);
0132     layout->addLayout (buttonlayout);
0133     setLayout(layout);
0134 }
0135 
0136 void TVDevicePage::slotDelete () {
0137 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0138     if (KMessageBox::warningTwoActions (this,
0139 #else
0140     if (KMessageBox::warningYesNo (this,
0141 #endif
0142                                    i18n ("You are about to delete this device from the Source menu.\nContinue?"),
0143                                    i18n ("Confirm"),
0144                                    KStandardGuiItem::del(), KStandardGuiItem::cancel())
0145 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0146         == KMessageBox::PrimaryAction)
0147 #else
0148         == KMessageBox::Yes)
0149 #endif
0150         Q_EMIT deleted (this);
0151 }
0152 
0153 //-----------------------------------------------------------------------------
0154 
0155 KMPlayerPrefSourcePageTV::KMPlayerPrefSourcePageTV (QWidget *parent, KMPlayerTVSource * tvsource)
0156 : QFrame (parent), m_tvsource (tvsource) {
0157     notebook = new QTabWidget;
0158     notebook->setTabPosition (QTabWidget::South);
0159     QWidget * general = new QWidget (notebook);
0160     QLabel* driverLabel = new QLabel(i18n("Driver:"));
0161     driver = new QLineEdit;
0162     driver->setWhatsThis(i18n("dummy, v4l or bsdbt848"));
0163     QLabel *deviceLabel = new QLabel(i18n("Device:"));
0164     device = new KUrlRequester(QUrl::fromLocalFile("/dev/video"));
0165     device->setWhatsThis(i18n("Path to your video device, eg. /dev/video0"));
0166     scan = new QPushButton(i18n("Scan..."));
0167     QGridLayout *gridlayout = new QGridLayout;
0168     gridlayout->addWidget (driverLabel, 0, 0);
0169     gridlayout->addWidget (driver, 0, 1);
0170     gridlayout->addWidget (deviceLabel, 1, 0);
0171     gridlayout->addWidget (device, 1, 1);
0172     QHBoxLayout *buttonlayout = new QHBoxLayout;
0173     buttonlayout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Minimum));
0174     buttonlayout->addWidget (scan);
0175     QVBoxLayout *layout = new QVBoxLayout;
0176     layout->addLayout(gridlayout);
0177     layout->addLayout (buttonlayout);
0178     layout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
0179     general->setLayout(layout);
0180     notebook->addTab(general, i18n("General"));
0181     QVBoxLayout* mainlayout = new QVBoxLayout;
0182     mainlayout->addWidget(notebook);
0183     setLayout(mainlayout);
0184 }
0185 
0186 void KMPlayerPrefSourcePageTV::showEvent (QShowEvent *) {
0187     m_tvsource->readXML ();
0188 }
0189 
0190 //-----------------------------------------------------------------------------
0191 
0192 TVNode::TVNode (KMPlayer::NodePtr &d, const QString & s, const char * t, short id, const QString & n) : KMPlayer::GenericMrl (d, s, n, t) {
0193     this->id = id;
0194     editable = true;
0195 }
0196 
0197 void TVNode::setNodeName (const QString & nn) {
0198     title = nn;
0199     setAttribute (KMPlayer::Ids::attr_name, nn);
0200 }
0201 
0202 //-----------------------------------------------------------------------------
0203 
0204 TVChannel::TVChannel (KMPlayer::NodePtr & d, const QString & n, double freq) : TVNode (d, QString ("tv://"), "channel", id_node_tv_channel, n) {
0205     setAttribute (KMPlayer::Ids::attr_name, n);
0206     setAttribute ("frequency", QString::number (freq, 'f', 2));
0207 }
0208 
0209 TVChannel::TVChannel (KMPlayer::NodePtr & d) : TVNode (d, QString ("tv://"), "channel", id_node_tv_channel) {
0210 }
0211 
0212 void TVChannel::closed () {
0213     title = getAttribute (KMPlayer::Ids::attr_name);
0214     Mrl::closed ();
0215 }
0216 
0217 //-----------------------------------------------------------------------------
0218 
0219 TVInput::TVInput (KMPlayer::NodePtr & d, const QString & n, int id)
0220  : TVNode (d, QString ("tv://"), "input", id_node_tv_input, n) {
0221     setAttribute (KMPlayer::Ids::attr_name, n);
0222     setAttribute (KMPlayer::Ids::attr_id, QString::number (id));
0223 }
0224 
0225 TVInput::TVInput (KMPlayer::NodePtr & d) : TVNode (d, QString ("tv://"), "input", id_node_tv_input) {
0226 }
0227 
0228 KMPlayer::Node *TVInput::childFromTag (const QString & tag) {
0229     // qCDebug(LOG_KMPLAYER_APP) << nodeName () << " childFromTag " << tag;
0230     if (tag == QString::fromLatin1 ("channel")) {
0231         return new TVChannel (m_doc);
0232     } else
0233         return nullptr;
0234 }
0235 
0236 void TVInput::closed () {
0237     //title = getAttribute (KMPlayer::Ids::attr_name);
0238     Mrl::closed ();
0239 }
0240 
0241 void TVInput::setNodeName (const QString & name) {
0242     Node *p = parentNode ();
0243     QString nm (name);
0244     if (p && p->id == id_node_tv_device) {
0245         int pos = name.indexOf (QString (" - ") + p->mrl ()->title);
0246         if (pos > -1)
0247             nm.truncate (pos);
0248     }
0249     title = nm + QString (" - ") + title;
0250     TVNode::setNodeName (nm);
0251 }
0252 
0253 //-----------------------------------------------------------------------------
0254 
0255 TVDevice::TVDevice (KMPlayer::NodePtr & doc, const QString & d) : TVNode (doc, d, "device", id_node_tv_device), zombie (false) {
0256     setAttribute ("path", d);
0257 }
0258 
0259 TVDevice::TVDevice (KMPlayer::NodePtr & doc)
0260     : TVNode (doc, i18n ("tv device"), "device", id_node_tv_device), zombie (false) {
0261 }
0262 
0263 TVDevice::~TVDevice () {
0264     if (device_page)
0265         device_page->deleteLater ();
0266 }
0267 
0268 KMPlayer::Node *TVDevice::childFromTag (const QString & tag) {
0269     // qCDebug(LOG_KMPLAYER_APP) << nodeName () << " childFromTag " << tag;
0270     if (tag == QString::fromLatin1 ("input"))
0271         return new TVInput (m_doc);
0272     return nullptr;
0273 }
0274 
0275 void TVDevice::closed () {
0276     updateNodeName ();
0277     Mrl::closed ();
0278 }
0279 
0280 void TVDevice::message (KMPlayer::MessageType msg, void *data) {
0281     if (KMPlayer::MsgChildFinished == msg)
0282         finish ();
0283     else
0284         TVNode::message (msg, data);
0285 }
0286 
0287 void *TVDevice::role (KMPlayer::RoleType msg, void *content)
0288 {
0289     if (KMPlayer::RolePlaylist == msg)
0290         return nullptr;
0291     return TVNode::role (msg, content);
0292 }
0293 
0294 void TVDevice::setNodeName (const QString & name) {
0295     TVNode::setNodeName (name);
0296     updateNodeName ();
0297 }
0298 
0299 void TVDevice::updateNodeName () {
0300     title = getAttribute (KMPlayer::Ids::attr_name);
0301     src = getAttribute ("path");
0302     for (KMPlayer::Node *c = firstChild (); c; c = c->nextSibling ())
0303         if (c->id == id_node_tv_input) {
0304             TVInput * i = static_cast <TVInput *> (c);
0305             i->title = i->getAttribute (KMPlayer::Ids::attr_name) +
0306                 QString (" - ") + title;
0307         }
0308 }
0309 
0310 void TVDevice::updateDevicePage () {
0311     if (!device_page)
0312         return;
0313     title = device_page->name->text ();
0314     setAttribute (KMPlayer::Ids::attr_name, title);
0315     setAttribute ("audio", device_page->audiodevice->lineEdit()->text ());
0316     setAttribute ("playback", device_page->noplayback->isChecked() ? "0" : "1");
0317     setAttribute (KMPlayer::Ids::attr_width, device_page->sizewidth->text ());
0318     setAttribute (KMPlayer::Ids::attr_height, device_page->sizeheight->text ());
0319     int i = 0;
0320     for (KMPlayer::Node *ip = firstChild(); ip; ip=ip->nextSibling(),++i) {
0321         if (ip->id != id_node_tv_input)
0322             continue;
0323         TVInput * input = KMPlayer::convertNode <TVInput> (ip);
0324         bool ok;
0325         if (input->getAttribute ("tuner").toInt (&ok) && ok) {
0326             QWidget* widget = device_page->inputsTab->widget(i);
0327             QTableWidget* table = static_cast<QTableWidget*>(widget->findChild<QTableWidget*>("PageTVChannels"));
0328             if (table) {
0329                 input->clearChildren ();
0330                 for (int j = 0; j<table->rowCount() && table->item (j, 1); ++j) {
0331                     input->appendChild (new TVChannel (m_doc, table->item (j, 0)->text (), table->item (j, 1)->text ().toDouble ()));
0332                 }
0333             }
0334             QComboBox* norms = static_cast<QComboBox*>(widget->findChild<QComboBox*>("PageTVNorm"));
0335             if (norms) {
0336                 input->setAttribute ("norm", norms->currentText ());
0337             }
0338         }
0339     }
0340 }
0341 
0342 //-----------------------------------------------------------------------------
0343 
0344 TVDocument::TVDocument (KMPlayerTVSource * source)
0345     : FileDocument (id_node_tv_document, "tv://", source), m_source (source) {
0346     title = i18n ("Television");
0347     bookmarkable = false;
0348 }
0349 
0350 KMPlayer::Node *TVDocument::childFromTag (const QString & tag) {
0351     // qCDebug(LOG_KMPLAYER_APP) << nodeName () << " childFromTag " << tag;
0352     if (tag == QString::fromLatin1 ("device"))
0353         return new TVDevice (m_doc);
0354     return FileDocument::childFromTag (tag);
0355 }
0356 
0357 void TVDocument::message (KMPlayer::MessageType msg, void *data) {
0358     if (KMPlayer::MsgChildFinished == msg)
0359         finish ();
0360     else
0361         FileDocument::message (msg, data);
0362 }
0363 
0364 void TVDocument::defer () {
0365     if (!resolved) {
0366         resolved = true;
0367         readFromFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/tv.xml");
0368     }
0369 }
0370 
0371 //-----------------------------------------------------------------------------
0372 
0373 KMPlayerTVSource::KMPlayerTVSource(KMPlayerApp* a)
0374     : KMPlayer::Source (i18n ("TV"), a->player(), "tvsource"), m_app(a), m_configpage(nullptr), scanner(nullptr), config_read(false) {
0375     m_url = QUrl("tv://");
0376     m_document = new TVDocument (this);
0377     m_player->settings ()->addPage (this);
0378     tree_id = m_player->playModel()->addTree (m_document, "tvsource", "video-television", KMPlayer::PlayModel::TreeEdit | KMPlayer::PlayModel::Moveable | KMPlayer::PlayModel::Deleteable);
0379 }
0380 
0381 KMPlayerTVSource::~KMPlayerTVSource () {
0382     static_cast <TVDocument *> (m_document.ptr ())->sync
0383         (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/tv.xml");
0384 }
0385 
0386 void KMPlayerTVSource::activate () {
0387     m_identified = true;
0388     //if (m_player->settings ()->showbroadcastbutton)
0389     //    m_app->view()->controlPanel()->broadcastButton ()->show ();
0390     if (m_cur_tvdevice && !m_current) {
0391         for (KMPlayer::Node *i = m_cur_tvdevice->firstChild(); i && !m_current; i=i->nextSibling())
0392             if (i->id == id_node_tv_input) {
0393                 TVInput * input = KMPlayer::convertNode <TVInput> (i);
0394                 bool ok;
0395                 m_cur_tvinput = i;
0396                 if (input->getAttribute ("tuner").toInt (&ok) && ok) {
0397                     for (KMPlayer::Node *c = i->firstChild (); c; c = c->nextSibling ())
0398                         if (c->id == id_node_tv_channel) {
0399                             setCurrent (c->mrl ());
0400                             break;
0401                         }
0402                 } else
0403                     m_current = i;
0404             }
0405     } else if (!m_cur_tvdevice)
0406         KMPlayer::Source::reset ();
0407     if (m_cur_tvdevice) {
0408         QString playback = static_cast <KMPlayer::Element *> (m_cur_tvdevice.ptr ())->getAttribute (QString::fromLatin1 ("playback"));
0409         if (playback.isEmpty () || playback.toInt ())
0410             QTimer::singleShot (0, m_player, &KMPlayer::PartBase::play);
0411     }
0412 }
0413 /* TODO: playback by
0414  * ffmpeg -vd /dev/video0 -r 25 -s 768x576 -f rawvideo - |mplayer -nocache -ao arts -rawvideo on:w=768:h=576:fps=25 -quiet -
0415  */
0416 
0417 void KMPlayerTVSource::deactivate () {
0418     //if (m_player->view () && !m_app->view ()->controlPanel()->broadcastButton ()->isOn ())
0419     //    m_app->view ()->controlPanel()->broadcastButton ()->hide ();
0420     reset ();
0421 }
0422 
0423 void KMPlayerTVSource::play (KMPlayer::Mrl *mrl) {
0424     if (mrl && mrl->id == id_node_tv_document) {
0425         readXML ();
0426     } else {
0427         m_current = mrl;
0428         for (KMPlayer::Node *e = mrl; e; e = e->parentNode ()) {
0429             if (e->id == id_node_tv_device) {
0430                 m_cur_tvdevice = e;
0431                 break;
0432             } else if (e->id == id_node_tv_input)
0433                 m_cur_tvinput = e;
0434         }
0435         if (m_player->source () != this)
0436             m_player->setSource (this);
0437         else
0438             KMPlayer::Source::play (mrl);
0439         /*else if (m_player->process ()->playing ()) {
0440             //m_back_request = m_current;
0441             m_player->process ()->stop ();
0442         } else {
0443             buildArguments ();
0444             if (m_app->broadcasting ())
0445                 QTimer::singleShot (0, m_app->broadcastConfig (), SLOT (startFeed ()));
0446             else
0447                 KMPlayer::Source::play (mrl);
0448         }*/
0449     }
0450 }
0451 
0452 KMPlayer::NodePtr KMPlayerTVSource::root () {
0453     return m_cur_tvinput;
0454 }
0455 
0456 void KMPlayerTVSource::setCurrent (KMPlayer::Mrl *mrl) {
0457     TVChannel * channel = nullptr;
0458     TVInput * input = nullptr;
0459     m_current = mrl;
0460     KMPlayer::NodePtr elm = m_current;
0461     if (elm && elm->id == id_node_tv_channel) {
0462         channel = KMPlayer::convertNode <TVChannel> (elm);
0463         elm = elm->parentNode ();
0464     }
0465     if (elm && elm->id == id_node_tv_input)
0466         input = KMPlayer::convertNode <TVInput> (elm);
0467     if (!(channel || (input && input->getAttribute ("tuner").isEmpty ())))
0468         return;
0469     m_cur_tvinput = input;
0470     m_cur_tvdevice = input->parentNode ();
0471     m_player->playModel()->updateTree(0, m_cur_tvinput, m_current, true, false);
0472     if (m_cur_tvdevice->id != id_node_tv_device) {
0473         return;
0474     }
0475     TVDevice * tvdevice = KMPlayer::convertNode <TVDevice> (m_cur_tvdevice);
0476     m_identified = true;
0477     m_audiodevice = tvdevice->getAttribute ("audio");
0478     m_videodevice = tvdevice->src;
0479     m_videonorm = input->getAttribute ("norm");
0480     m_tuner = input->getAttribute (KMPlayer::Ids::attr_name);
0481     QString xvport = tvdevice->getAttribute ("xvport");
0482     if (!xvport.isEmpty ())
0483         m_xvport = xvport.toInt ();
0484     QString xvenc = input->getAttribute ("xvenc");
0485     if (!xvenc.isEmpty ())
0486         m_xvencoding = xvenc.toInt ();
0487     QString command = QString::asprintf ("device=%s:input=%s",
0488             tvdevice->src.toLatin1 ().data (),
0489             input->getAttribute (KMPlayer::Ids::attr_id).toLatin1 ().data ());
0490     if (channel) {
0491         QString freq = channel->getAttribute ("frequency");
0492         m_frequency = (int)(1000 * freq.toDouble ());
0493         command += QString (":freq=%1").arg (freq);
0494     } else
0495         m_frequency = 0;
0496     if (!m_videonorm.isEmpty ())
0497         command += QString (":norm=%1").arg (m_videonorm);
0498     m_app->setCaption (i18n ("TV: ") + (channel ? channel->mrl ()->title : input->mrl ()->title), false);
0499     setDimensions (m_cur_tvdevice,
0500             tvdevice->getAttribute (KMPlayer::Ids::attr_width).toInt (),
0501             tvdevice->getAttribute (KMPlayer::Ids::attr_height).toInt ());
0502     m_options = QString::asprintf ("-tv noaudio:driver=%s:%s:width=%d:height=%d -slave -nocache -quiet", tvdriver.toLatin1 ().data (), command.toLatin1 ().data (), width (), height ());
0503     m_recordcmd = QString::asprintf ("-tv %s:driver=%s:%s:width=%d:height=%d", m_audiodevice.isEmpty () ? "noaudio" : QString(QLatin1String ("forceaudio:adevice=") + m_audiodevice).toLatin1 ().data(), tvdriver.toLatin1 ().data (), command.toLatin1 ().data (), width (), height ());
0504 }
0505 
0506 void KMPlayerTVSource::menuClicked (int id) {
0507     KMPlayer::Node *elm = m_document->firstChild ();
0508     for (; id > 0; --id,  elm = elm->nextSibling ())
0509         ;
0510     m_cur_tvdevice = elm;
0511     m_cur_tvinput = elm->firstChild (); // FIXME
0512     m_current = nullptr;
0513     m_player->setSource (this);
0514 }
0515 
0516 QString KMPlayerTVSource::filterOptions () {
0517     if (! m_player->settings ()->disableppauto)
0518         return KMPlayer::Source::filterOptions ();
0519     return QString ("-vf pp=lb");
0520 }
0521 
0522 bool KMPlayerTVSource::hasLength () {
0523     return false;
0524 }
0525 
0526 bool KMPlayerTVSource::isSeekable () {
0527     return true;
0528 }
0529 
0530 QString KMPlayerTVSource::prettyName () {
0531     QString name (i18n ("TV"));
0532     //if (m_tvsource)
0533     //    name += ' ' + m_tvsource->title;
0534     return name;
0535 }
0536 
0537 void KMPlayerTVSource::write (KSharedConfigPtr m_config) {
0538     if (!config_read) return;
0539     KConfigGroup (m_config, strTV).writeEntry (strTVDriver, tvdriver);
0540     static_cast <TVDocument *> (m_document.ptr ())->writeToFile
0541         (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/tv.xml");
0542     qCDebug(LOG_KMPLAYER_APP) << "KMPlayerTVSource::write XML";
0543 }
0544 
0545 void KMPlayerTVSource::readXML () {
0546     if (config_read) return;
0547     config_read = true;
0548     qCDebug(LOG_KMPLAYER_APP) << "KMPlayerTVSource::readXML";
0549     m_document->defer ();
0550     m_player->playModel()->updateTree (tree_id, m_document, nullptr, false, false);
0551     sync (false);
0552 }
0553 
0554 void KMPlayerTVSource::read (KSharedConfigPtr m_config) {
0555     tvdriver = KConfigGroup (m_config, strTV).readEntry (
0556             strTVDriver, QString ("v4l2"));
0557 }
0558 
0559 void KMPlayerTVSource::sync (bool fromUI) {
0560     if (!m_configpage) return;
0561     if (m_document && m_document->hasChildNodes ())
0562         m_app->showBroadcastConfig ();
0563     else
0564         m_app->hideBroadcastConfig ();
0565     if (fromUI) {
0566         tvdriver = m_configpage->driver->text ();
0567         for (KMPlayer::Node *d=m_document->firstChild();d; d=d->nextSibling())
0568             if (d->id == id_node_tv_device)
0569                 static_cast <TVDevice *> (d)->updateDevicePage ();
0570         m_player->playModel()->updateTree(tree_id, m_document, nullptr, false, false);
0571     } else {
0572         m_configpage->driver->setText (tvdriver);
0573         for (KMPlayer::Node *dp = m_document->firstChild (); dp; dp = dp->nextSibling ())
0574             if (dp->id == id_node_tv_device)
0575                 addTVDevicePage (KMPlayer::convertNode <TVDevice> (dp));
0576     }
0577 }
0578 
0579 void KMPlayerTVSource::prefLocation (QString & item, QString & icon, QString & tab) {
0580     item = i18n ("Source");
0581     icon = QString ("source");
0582     tab = i18n ("TV");
0583 }
0584 
0585 QFrame * KMPlayerTVSource::prefPage (QWidget * parent) {
0586     if (!m_configpage) {
0587         m_configpage = new KMPlayerPrefSourcePageTV (parent, this);
0588         scanner = new TVDeviceScannerSource (this);
0589         connect (m_configpage->scan, &QPushButton::clicked, this, &KMPlayerTVSource::slotScan);
0590     }
0591     return m_configpage;
0592 }
0593 
0594 static bool hasTVDevice (KMPlayer::NodePtr doc, const QString & devstr) {
0595     for (KMPlayer::Node *e = doc->firstChild (); e; e = e->nextSibling ())
0596         if (e->id == id_node_tv_device &&
0597                 static_cast <TVDevice *> (e)->src == devstr)
0598             return true;
0599     return false;
0600 }
0601 
0602 void KMPlayerTVSource::slotScan () {
0603     QString devstr = m_configpage->device->lineEdit()->text ();
0604     if (!hasTVDevice(m_document, devstr)) {
0605         scanner->scan (devstr, m_configpage->driver->text());
0606         connect (scanner, &TVDeviceScannerSource::scanFinished,
0607                 this, &KMPlayerTVSource::slotScanFinished);
0608     } else
0609         KMessageBox::error (m_configpage, i18n ("Device already present."),
0610                 i18n ("Error"));
0611 }
0612 
0613 void KMPlayerTVSource::slotScanFinished (TVDevice * tvdevice) {
0614     disconnect (scanner, &TVDeviceScannerSource::scanFinished,
0615                 this, &KMPlayerTVSource::slotScanFinished);
0616     if (tvdevice) {
0617         tvdevice->zombie = false;
0618         addTVDevicePage (tvdevice, true);
0619         m_player->playModel()->updateTree(tree_id, m_document, nullptr, false, false);
0620     } else
0621         KMessageBox::error(m_configpage,i18n("No device found."),i18n("Error"));
0622 }
0623 
0624 void KMPlayerTVSource::addTVDevicePage(TVDevice *dev, bool show) {
0625     if (dev->device_page)
0626         dev->device_page->deleteLater ();
0627     dev->device_page = new TVDevicePage (m_configpage->notebook, dev);
0628     m_configpage->notebook->addTab(dev->device_page, dev->title);
0629     connect (dev->device_page, &TVDevicePage::deleted,
0630              this, &KMPlayerTVSource::slotDeviceDeleted);
0631     if (show)
0632         m_configpage->notebook->setCurrentIndex(m_configpage->notebook->count()-1);
0633 }
0634 
0635 void KMPlayerTVSource::slotDeviceDeleted (TVDevicePage *devpage) {
0636     m_document->removeChild (devpage->device_doc);
0637     m_configpage->notebook->setCurrentIndex(0);
0638     m_player->playModel()->updateTree (tree_id, m_document, nullptr, false, false);
0639 }
0640 
0641 //-----------------------------------------------------------------------------
0642 
0643 TVDeviceScannerSource::TVDeviceScannerSource (KMPlayerTVSource * src)
0644  : KMPlayer::Source (i18n ("TVScanner"), src->player (), "tvscanner"),
0645    m_tvsource (src),
0646    m_tvdevice (nullptr),
0647    m_process (nullptr),
0648    m_viewer (nullptr) {
0649 }
0650 
0651 void TVDeviceScannerSource::init () {
0652 }
0653 
0654 bool TVDeviceScannerSource::processOutput (const QString & line) {
0655     if (m_nameRegExp.indexIn(line) > -1) {
0656         m_tvdevice->title = m_nameRegExp.cap (1);
0657         m_tvdevice->setAttribute(KMPlayer::Ids::attr_name,m_tvdevice->title);
0658         qCDebug(LOG_KMPLAYER_APP) << "Name " << m_tvdevice->title;
0659     } else if (m_sizesRegExp.indexIn(line) > -1) {
0660         m_tvdevice->setAttribute (KMPlayer::Ids::attr_width,
0661                 m_sizesRegExp.cap(1));
0662         m_tvdevice->setAttribute (KMPlayer::Ids::attr_height,
0663                 m_sizesRegExp.cap(2));
0664         m_tvdevice->setAttribute ("minwidth", m_sizesRegExp.cap (1));
0665         m_tvdevice->setAttribute ("minheight", m_sizesRegExp.cap (2));
0666         m_tvdevice->setAttribute ("maxwidth", m_sizesRegExp.cap (3));
0667         m_tvdevice->setAttribute ("maxheight", m_sizesRegExp.cap (4));
0668     } else if (m_inputRegExp.indexIn(line) > -1) {
0669         KMPlayer::NodePtr doc = m_tvsource->document ();
0670         TVInput * input = new TVInput (doc, m_inputRegExp.cap(2).trimmed(),
0671                                        m_inputRegExp.cap (1).toInt ());
0672         if (m_inputRegExp.cap (3).toInt () == 1)
0673             input->setAttribute ("tuner", "1");
0674         m_tvdevice->appendChild (input);
0675         qCDebug(LOG_KMPLAYER_APP) << "Input " << input->mrl ()->title;
0676     } else if (m_inputRegExpV4l2.indexIn(line) > -1) {
0677         KMPlayer::NodePtr doc = m_tvsource->document ();
0678         QStringList sl = m_inputRegExpV4l2.cap(1).split (QChar (';'));
0679         const QStringList::iterator e = sl.end ();
0680         for (QStringList::iterator i = sl.begin (); i != e; ++i) {
0681             int pos = (*i).indexOf (QChar ('='));
0682             if (pos > 0) {
0683                 int id = (*i).left (pos).trimmed ().toInt ();
0684                 TVInput *input = new TVInput(doc,(*i).mid(pos+1).trimmed(), id);
0685                 if (!id && m_caps.indexOf ("tuner") > -1)
0686                     input->setAttribute ("tuner", "1");
0687                 m_tvdevice->appendChild (input);
0688             }
0689         }
0690     } else {
0691         int pos = line.indexOf ("Capabilites:");
0692         if (pos > 0)
0693             m_caps = line.mid (pos + 12);
0694         return false;
0695     }
0696     return true;
0697 }
0698 
0699 QString TVDeviceScannerSource::filterOptions () {
0700     return QString ("");
0701 }
0702 
0703 bool TVDeviceScannerSource::hasLength () {
0704     return false;
0705 }
0706 
0707 bool TVDeviceScannerSource::isSeekable () {
0708     return false;
0709 }
0710 
0711 bool TVDeviceScannerSource::scan (const QString & dev, const QString & dri) {
0712     if (m_tvdevice)
0713         return false;
0714     setUrl ("tv://");
0715     KMPlayer::NodePtr doc = m_tvsource->document ();
0716     m_tvdevice = new TVDevice (doc, dev);
0717     m_tvsource->document ()->appendChild (m_tvdevice);
0718     m_tvdevice->zombie = true; // not for real yet
0719     m_driver = dri;
0720     m_old_source = m_tvsource->player ()->source ();
0721     m_tvsource->player ()->setSource (this);
0722     m_identified = true;
0723     play (m_tvdevice);
0724     return true;
0725 }
0726 
0727 void TVDeviceScannerSource::activate () {
0728     m_nameRegExp.setPattern ("Selected device:\\s*([^\\s].*)");
0729     m_sizesRegExp.setPattern ("Supported sizes:\\s*([0-9]+)x([0-9]+) => ([0-9]+)x([0-9]+)");
0730     m_inputRegExp.setPattern ("\\s*([0-9]+):\\s*([^:]+):[^\\(]*\\(tuner:([01]),\\s*norm:([^\\)]+)\\)");
0731     m_inputRegExpV4l2.setPattern ("inputs:((?:\\s*[0-9]+\\s*=\\s*[^;]+;)+)");
0732 }
0733 
0734 void TVDeviceScannerSource::deactivate () {
0735     qCDebug(LOG_KMPLAYER_APP) << "TVDeviceScannerSource::deactivate";
0736     if (m_tvdevice) {
0737         if (m_tvdevice->parentNode ())
0738             m_tvdevice->parentNode ()->removeChild (m_tvdevice);
0739         m_tvdevice = nullptr;
0740         delete m_process;
0741         Q_EMIT scanFinished (m_tvdevice);
0742     }
0743 }
0744 
0745 void TVDeviceScannerSource::play (KMPlayer::Mrl *) {
0746     if (!m_tvdevice)
0747         return;
0748     m_options = QString::asprintf ("tv:// -tv driver=%s:device=%s -identify -frames 0", m_driver.toLatin1 ().data (), m_tvdevice->src.toLatin1 ().data ());
0749     m_tvsource->player ()->stop ();
0750     KMPlayer::Node *n = new KMPlayer::SourceDocument (this, QString ());
0751     setDocument (n, n);
0752     m_process = m_player->mediaManager()->processInfos()["mplayer"]->create (m_player, this);
0753     m_viewer = m_player->viewWidget ()->viewArea ()->createVideoWidget ();
0754     m_process->ready ();
0755 }
0756 
0757 void TVDeviceScannerSource::scanningFinished () {
0758     TVDevice * dev = nullptr;
0759     delete m_process;
0760     qCDebug(LOG_KMPLAYER_APP) << "scanning done " << m_tvdevice->hasChildNodes ();
0761     if (!m_tvdevice->hasChildNodes ()) {
0762         m_tvsource->document ()->removeChild (m_tvdevice);
0763     } else {
0764         dev = m_tvdevice;
0765         if (width () > 0 && height () > 0) {
0766             m_tvdevice->setAttribute (KMPlayer::Ids::attr_width,
0767                     QString::number (width ()));
0768             m_tvdevice->setAttribute (KMPlayer::Ids::attr_height,
0769                     QString::number (height ()));
0770         }
0771     }
0772     m_tvdevice = nullptr;
0773     m_player->setSource (m_old_source);
0774     Q_EMIT scanFinished (dev);
0775 }
0776 
0777 void TVDeviceScannerSource::stateChange (KMPlayer::IProcess *,
0778                    KMPlayer::IProcess::State os, KMPlayer::IProcess::State ns) {
0779     if (KMPlayer::IProcess::Ready == ns) {
0780         if (os > KMPlayer::IProcess::Ready)
0781             QTimer::singleShot (0, this, &TVDeviceScannerSource::scanningFinished);
0782         else if (m_process && os < KMPlayer::IProcess::Ready)
0783             m_process->play ();
0784     }
0785 }
0786 
0787 void TVDeviceScannerSource::processDestroyed (KMPlayer::IProcess *) {
0788     m_process = nullptr;
0789     KMPlayer::View *view = m_player->viewWidget ();
0790     if (view)
0791         view->viewArea ()->destroyVideoWidget (m_viewer);
0792     m_viewer = nullptr;
0793 }
0794 
0795 KMPlayer::IViewer *TVDeviceScannerSource::viewer () {
0796     return m_viewer;
0797 }
0798 
0799 KMPlayer::Mrl *TVDeviceScannerSource::getMrl () {
0800     return document ()->mrl ();
0801 }
0802 
0803 #include "moc_kmplayertvsource.cpp"