1 /* This file is part of the KMPlayer application
2    Copyright (C) 2003 Koos Vriezen <koos.vriezen@xs4all.nl>
3 
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18 */
19 
20 #include <qlayout.h>
21 #include <qlabel.h>
22 #include <qtimer.h>
23 #include <qpushbutton.h>
24 #include <qcheckbox.h>
25 #include <QTableWidget>
26 #include <qstringlist.h>
27 #include <qcombobox.h>
28 #include <qlineedit.h>
29 #include <qgroupbox.h>
30 #include <qwhatsthis.h>
31 #include <qtabwidget.h>
32 #include <qmessagebox.h>
33 #include <QHeaderView>
34 #include <QMenu>
35 #include <QStandardPaths>
36 #include <qfontmetrics.h>
37 
38 #include <klocale.h>
39 #include <kdebug.h>
40 #include <kmessagebox.h>
41 #include <klineedit.h>
42 #include <kurlrequester.h>
43 #include <kcombobox.h>
44 #include <kconfig.h>
45 #include <kconfiggroup.h>
46 
47 #include "kmplayerpartbase.h"
48 #include "kmplayerprocess.h"
49 #include "kmplayerconfig.h"
50 #include "kmplayertvsource.h"
51 #include "playmodel.h"
52 #include "playlistview.h"
53 #include "viewarea.h"
54 #include "kmplayer.h"
55 #include "kmplayercontrolpanel.h"
56 
57 static const char * strTV = "TV";
58 static const char * strTVDriver = "Driver";
59 
60 
TVDevicePage(QWidget * parent,KMPlayer::NodePtr dev)61 KDE_NO_CDTOR_EXPORT TVDevicePage::TVDevicePage (QWidget *parent, KMPlayer::NodePtr dev)
62 : QFrame (parent), device_doc (dev) {
63     setObjectName("PageTVDevice");
64     TVDevice * device = KMPlayer::convertNode <TVDevice> (device_doc);
65     QLabel* deviceLabel = new QLabel(i18n("Video device:") + device->src);
66     QLabel* audioLabel = new QLabel(i18n("Audio device:"));
67     audiodevice = new KUrlRequester (KUrl (device->getAttribute ("audio")));
68     QLabel* nameLabel = new QLabel(i18n("Name:"));
69     name = new QLineEdit(device->title);
70     QLabel *sizewidthLabel = new QLabel(i18n("Width:"));
71     sizewidth = new QLineEdit(device->getAttribute(KMPlayer::Ids::attr_width));
72     QLabel* sizeheightLabel = new QLabel (i18n ("Height:"));
73     sizeheight = new QLineEdit(device->getAttribute(KMPlayer::Ids::attr_height));
74     noplayback = new QCheckBox(i18n("Do not immediately play"));
75     noplayback->setChecked (!device->getAttribute ("playback").toInt ());
76     noplayback->setWhatsThis(i18n("Only start playing after clicking the play button"));
77     inputsTab = new QTabWidget;
78     for (KMPlayer::Node *ip = device->firstChild (); ip; ip = ip->nextSibling ()) {
79         if (ip->id != id_node_tv_input)
80             continue;
81         TVInput * input = KMPlayer::convertNode <TVInput> (ip);
82         QWidget* widget = new QWidget;
83         QHBoxLayout* tablayout = new QHBoxLayout;
84         if (!input->getAttribute ("tuner").isEmpty ()) {
85             QHBoxLayout* horzlayout = new QHBoxLayout;
86             QVBoxLayout* vertlayout = new QVBoxLayout;
87             horzlayout->addWidget(new QLabel(i18n("Norm:")));
88             QComboBox* norms = new QComboBox;
89             norms->setObjectName("PageTVNorm");
90             norms->addItem(QString("NTSC"));
91             norms->addItem(QString("PAL"));
92             norms->addItem(QString("SECAM"));
93             norms->setCurrentIndex(norms->findText(input->getAttribute ("norm")));
94             horzlayout->addWidget (norms);
95             vertlayout->addLayout (horzlayout);
96             vertlayout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
97             QTableWidget* table = new QTableWidget(90, 2);
98             table->setObjectName("PageTVChannels");
99             table->setContentsMargins(0, 0, 0, 0);
100             QFontMetrics metrics (table->font ());
101             QStringList labels = QStringList() << i18n("Channel") << i18n("Frequency (MHz)");
102             table->setHorizontalHeaderLabels(labels);
103             int index = 0;
104             int first_column_width = QFontMetrics(table->horizontalHeader()->font()).boundingRect(labels[0]).width() + 20;
105             for (KMPlayer::Node *c=input->firstChild();c;c=c->nextSibling()) {
106                 if (c->id != id_node_tv_channel)
107                     continue;
108                 int strwid = metrics.boundingRect (c->mrl ()->title).width ();
109                 if (strwid > first_column_width)
110                     first_column_width = strwid + 4;
111                 table->setItem(index, 0, new QTableWidgetItem(c->mrl()->title));
112                 table->setItem(index++, 1, new QTableWidgetItem(KMPlayer::convertNode<TVChannel>(c)->getAttribute("frequency")));
113             }
114             table->setColumnWidth (0, first_column_width);
115             table->horizontalHeader()->setStretchLastSection(true);
116             tablayout->addWidget (table);
117             tablayout->addLayout (vertlayout);
118         }
119         widget->setLayout(tablayout);
120         inputsTab->addTab (widget, input->mrl ()->title);
121     }
122     QPushButton* delButton = new QPushButton(i18n("Delete"));
123     connect (delButton, SIGNAL (clicked ()), this, SLOT (slotDelete ()));
124     QGridLayout* gridlayout = new QGridLayout;
125     gridlayout->addWidget (audioLabel, 0, 0);
126     gridlayout->addWidget (audiodevice, 0, 0, 1, 3);
127     gridlayout->addWidget (nameLabel, 1, 0);
128     gridlayout->addWidget (name, 1, 1, 1, 3);
129     gridlayout->addWidget (sizewidthLabel, 2, 0);
130     gridlayout->addWidget (sizewidth, 2, 1);
131     gridlayout->addWidget (sizeheightLabel, 2, 2);
132     gridlayout->addWidget (sizeheight, 2, 3);
133     QVBoxLayout* layout = new QVBoxLayout;
134     layout->addWidget(deviceLabel);
135     layout->addLayout(gridlayout);
136     layout->addWidget (inputsTab);
137     layout->addSpacing (5);
138     layout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Minimum));
139     QHBoxLayout *buttonlayout = new QHBoxLayout ();
140     buttonlayout->addWidget (noplayback);
141     buttonlayout->addItem (new QSpacerItem (0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
142     buttonlayout->addWidget (delButton);
143     layout->addLayout (buttonlayout);
144     setLayout(layout);
145 }
146 
slotDelete()147 KDE_NO_EXPORT void TVDevicePage::slotDelete () {
148     if (KMessageBox::warningYesNo (this, i18n ("You are about to remove this device from the Source menu.\nContinue?"), i18n ("Confirm")) == KMessageBox::Yes)
149         emit deleted (this);
150 }
151 
152 //-----------------------------------------------------------------------------
153 
KMPlayerPrefSourcePageTV(QWidget * parent,KMPlayerTVSource * tvsource)154 KDE_NO_CDTOR_EXPORT KMPlayerPrefSourcePageTV::KMPlayerPrefSourcePageTV (QWidget *parent, KMPlayerTVSource * tvsource)
155 : QFrame (parent), m_tvsource (tvsource) {
156     notebook = new QTabWidget;
157     notebook->setTabPosition (QTabWidget::South);
158     QWidget * general = new QWidget (notebook);
159     QLabel* driverLabel = new QLabel(i18n("Driver:"));
160     driver = new QLineEdit;
161     driver->setWhatsThis(i18n("dummy, v4l or bsdbt848"));
162     QLabel *deviceLabel = new QLabel(i18n("Device:"));
163     device = new KUrlRequester(KUrl("/dev/video"));
164     device->setWhatsThis(i18n("Path to your video device, eg. /dev/video0"));
165     scan = new QPushButton(i18n("Scan..."));
166     QGridLayout *gridlayout = new QGridLayout;
167     gridlayout->addWidget (driverLabel, 0, 0);
168     gridlayout->addWidget (driver, 0, 1);
169     gridlayout->addWidget (deviceLabel, 1, 0);
170     gridlayout->addWidget (device, 1, 1);
171     QHBoxLayout *buttonlayout = new QHBoxLayout;
172     buttonlayout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Minimum));
173     buttonlayout->addWidget (scan);
174     QVBoxLayout *layout = new QVBoxLayout;
175     layout->addLayout(gridlayout);
176     layout->addLayout (buttonlayout);
177     layout->addItem (new QSpacerItem (0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
178     general->setLayout(layout);
179     notebook->addTab(general, i18n("General"));
180     QVBoxLayout* mainlayout = new QVBoxLayout;
181     mainlayout->addWidget(notebook);
182     setLayout(mainlayout);
183 }
184 
showEvent(QShowEvent *)185 KDE_NO_EXPORT void KMPlayerPrefSourcePageTV::showEvent (QShowEvent *) {
186     m_tvsource->readXML ();
187 }
188 
189 //-----------------------------------------------------------------------------
190 
TVNode(KMPlayer::NodePtr & d,const QString & s,const char * t,short id,const QString & n)191 KDE_NO_CDTOR_EXPORT TVNode::TVNode (KMPlayer::NodePtr &d, const QString & s, const char * t, short id, const QString & n) : KMPlayer::GenericMrl (d, s, n, t) {
192     this->id = id;
193     editable = true;
194 }
195 
setNodeName(const QString & nn)196 KDE_NO_EXPORT void TVNode::setNodeName (const QString & nn) {
197     title = nn;
198     setAttribute (KMPlayer::Ids::attr_name, nn);
199 }
200 
201 //-----------------------------------------------------------------------------
202 
TVChannel(KMPlayer::NodePtr & d,const QString & n,double freq)203 KDE_NO_CDTOR_EXPORT TVChannel::TVChannel (KMPlayer::NodePtr & d, const QString & n, double freq) : TVNode (d, QString ("tv://"), "channel", id_node_tv_channel, n) {
204     setAttribute (KMPlayer::Ids::attr_name, n);
205     setAttribute ("frequency", QString::number (freq, 'f', 2));
206 }
207 
TVChannel(KMPlayer::NodePtr & d)208 KDE_NO_CDTOR_EXPORT TVChannel::TVChannel (KMPlayer::NodePtr & d) : TVNode (d, QString ("tv://"), "channel", id_node_tv_channel) {
209 }
210 
closed()211 KDE_NO_EXPORT void TVChannel::closed () {
212     title = getAttribute (KMPlayer::Ids::attr_name);
213     Mrl::closed ();
214 }
215 
216 //-----------------------------------------------------------------------------
217 
TVInput(KMPlayer::NodePtr & d,const QString & n,int id)218 TVInput::TVInput (KMPlayer::NodePtr & d, const QString & n, int id)
219  : TVNode (d, QString ("tv://"), "input", id_node_tv_input, n) {
220     setAttribute (KMPlayer::Ids::attr_name, n);
221     setAttribute (KMPlayer::Ids::attr_id, QString::number (id));
222 }
223 
TVInput(KMPlayer::NodePtr & d)224 KDE_NO_CDTOR_EXPORT TVInput::TVInput (KMPlayer::NodePtr & d) : TVNode (d, QString ("tv://"), "input", id_node_tv_input) {
225 }
226 
childFromTag(const QString & tag)227 KDE_NO_EXPORT KMPlayer::Node *TVInput::childFromTag (const QString & tag) {
228     // kDebug () << nodeName () << " childFromTag " << tag;
229     if (tag == QString::fromLatin1 ("channel")) {
230         return new TVChannel (m_doc);
231     } else
232         return 0L;
233 }
234 
closed()235 KDE_NO_EXPORT void TVInput::closed () {
236     //title = getAttribute (KMPlayer::Ids::attr_name);
237     Mrl::closed ();
238 }
239 
setNodeName(const QString & name)240 KDE_NO_EXPORT void TVInput::setNodeName (const QString & name) {
241     Node *p = parentNode ();
242     QString nm (name);
243     if (p && p->id == id_node_tv_device) {
244         int pos = name.indexOf (QString (" - ") + p->mrl ()->title);
245         if (pos > -1)
246             nm.truncate (pos);
247     }
248     title = nm + QString (" - ") + title;
249     TVNode::setNodeName (nm);
250 }
251 
252 //-----------------------------------------------------------------------------
253 
TVDevice(KMPlayer::NodePtr & doc,const QString & d)254 KDE_NO_CDTOR_EXPORT TVDevice::TVDevice (KMPlayer::NodePtr & doc, const QString & d) : TVNode (doc, d, "device", id_node_tv_device), zombie (false) {
255     setAttribute ("path", d);
256 }
257 
TVDevice(KMPlayer::NodePtr & doc)258 KDE_NO_CDTOR_EXPORT TVDevice::TVDevice (KMPlayer::NodePtr & doc)
259     : TVNode (doc, i18n ("tv device"), "device", id_node_tv_device), zombie (false) {
260 }
261 
~TVDevice()262 KDE_NO_CDTOR_EXPORT TVDevice::~TVDevice () {
263     if (device_page)
264         device_page->deleteLater ();
265 }
266 
childFromTag(const QString & tag)267 KDE_NO_EXPORT KMPlayer::Node *TVDevice::childFromTag (const QString & tag) {
268     // kDebug () << nodeName () << " childFromTag " << tag;
269     if (tag == QString::fromLatin1 ("input"))
270         return new TVInput (m_doc);
271     return 0L;
272 }
273 
closed()274 KDE_NO_EXPORT void TVDevice::closed () {
275     updateNodeName ();
276     Mrl::closed ();
277 }
278 
message(KMPlayer::MessageType msg,void * data)279 KDE_NO_EXPORT void TVDevice::message (KMPlayer::MessageType msg, void *data) {
280     if (KMPlayer::MsgChildFinished == msg)
281         finish ();
282     else
283         TVNode::message (msg, data);
284 }
285 
role(KMPlayer::RoleType msg,void * content)286 void *TVDevice::role (KMPlayer::RoleType msg, void *content)
287 {
288     if (KMPlayer::RolePlaylist == msg)
289         return NULL;
290     return TVNode::role (msg, content);
291 }
292 
setNodeName(const QString & name)293 KDE_NO_EXPORT void TVDevice::setNodeName (const QString & name) {
294     TVNode::setNodeName (name);
295     updateNodeName ();
296 }
297 
updateNodeName()298 KDE_NO_EXPORT void TVDevice::updateNodeName () {
299     title = getAttribute (KMPlayer::Ids::attr_name);
300     src = getAttribute ("path");
301     for (KMPlayer::Node *c = firstChild (); c; c = c->nextSibling ())
302         if (c->id == id_node_tv_input) {
303             TVInput * i = static_cast <TVInput *> (c);
304             i->title = i->getAttribute (KMPlayer::Ids::attr_name) +
305                 QString (" - ") + title;
306         }
307 }
308 
updateDevicePage()309 KDE_NO_EXPORT void TVDevice::updateDevicePage () {
310     if (!device_page)
311         return;
312     title = device_page->name->text ();
313     setAttribute (KMPlayer::Ids::attr_name, title);
314     setAttribute ("audio", device_page->audiodevice->lineEdit()->text ());
315     setAttribute ("playback", device_page->noplayback->isChecked() ? "0" : "1");
316     setAttribute (KMPlayer::Ids::attr_width, device_page->sizewidth->text ());
317     setAttribute (KMPlayer::Ids::attr_height, device_page->sizeheight->text ());
318     int i = 0;
319     for (KMPlayer::Node *ip = firstChild(); ip; ip=ip->nextSibling(),++i) {
320         if (ip->id != id_node_tv_input)
321             continue;
322         TVInput * input = KMPlayer::convertNode <TVInput> (ip);
323         bool ok;
324         if (input->getAttribute ("tuner").toInt (&ok) && ok) {
325             QWidget* widget = device_page->inputsTab->widget(i);
326             QTableWidget* table = static_cast<QTableWidget*>(widget->findChild<QTableWidget*>("PageTVChannels"));
327             if (table) {
328                 input->clearChildren ();
329                 for (int j = 0; j<table->rowCount() && table->item (j, 1); ++j) {
330                     input->appendChild (new TVChannel (m_doc, table->item (j, 0)->text (), table->item (j, 1)->text ().toDouble ()));
331                 }
332             }
333             QComboBox* norms = static_cast<QComboBox*>(widget->findChild<QComboBox*>("PageTVNorm"));
334             if (norms) {
335                 input->setAttribute ("norm", norms->currentText ());
336             }
337         }
338     }
339 }
340 
341 //-----------------------------------------------------------------------------
342 
343 KDE_NO_CDTOR_EXPORT
TVDocument(KMPlayerTVSource * source)344 TVDocument::TVDocument (KMPlayerTVSource * source)
345     : FileDocument (id_node_tv_document, "tv://", source), m_source (source) {
346     title = i18n ("Television");
347     bookmarkable = false;
348 }
349 
childFromTag(const QString & tag)350 KDE_NO_EXPORT KMPlayer::Node *TVDocument::childFromTag (const QString & tag) {
351     // kDebug () << nodeName () << " childFromTag " << tag;
352     if (tag == QString::fromLatin1 ("device"))
353         return new TVDevice (m_doc);
354     return FileDocument::childFromTag (tag);
355 }
356 
message(KMPlayer::MessageType msg,void * data)357 KDE_NO_EXPORT void TVDocument::message (KMPlayer::MessageType msg, void *data) {
358     if (KMPlayer::MsgChildFinished == msg)
359         finish ();
360     else
361         FileDocument::message (msg, data);
362 }
363 
defer()364 KDE_NO_EXPORT void TVDocument::defer () {
365     if (!resolved) {
366         resolved = true;
367         readFromFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/tv.xml");
368     }
369 }
370 
371 //-----------------------------------------------------------------------------
372 
KMPlayerTVSource(KMPlayerApp * a)373 KDE_NO_CDTOR_EXPORT KMPlayerTVSource::KMPlayerTVSource(KMPlayerApp* a)
374     : KMPlayer::Source (i18n ("TV"), a->player(), "tvsource"), m_app(a), m_configpage(0L), scanner(0L), config_read(false) {
375     m_url = "tv://";
376     m_document = new TVDocument (this);
377     m_player->settings ()->addPage (this);
378     tree_id = m_player->playModel()->addTree (m_document, "tvsource", "video-television", KMPlayer::PlayModel::TreeEdit | KMPlayer::PlayModel::Moveable | KMPlayer::PlayModel::Deleteable);
379 }
380 
~KMPlayerTVSource()381 KDE_NO_CDTOR_EXPORT KMPlayerTVSource::~KMPlayerTVSource () {
382     static_cast <TVDocument *> (m_document.ptr ())->sync
383         (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/tv.xml");
384 }
385 
activate()386 KDE_NO_EXPORT void KMPlayerTVSource::activate () {
387     m_identified = true;
388     //if (m_player->settings ()->showbroadcastbutton)
389     //    m_app->view()->controlPanel()->broadcastButton ()->show ();
390     if (m_cur_tvdevice && !m_current) {
391         for (KMPlayer::Node *i = m_cur_tvdevice->firstChild(); i && !m_current; i=i->nextSibling())
392             if (i->id == id_node_tv_input) {
393                 TVInput * input = KMPlayer::convertNode <TVInput> (i);
394                 bool ok;
395                 m_cur_tvinput = i;
396                 if (input->getAttribute ("tuner").toInt (&ok) && ok) {
397                     for (KMPlayer::Node *c = i->firstChild (); c; c = c->nextSibling ())
398                         if (c->id == id_node_tv_channel) {
399                             setCurrent (c->mrl ());
400                             break;
401                         }
402                 } else
403                     m_current = i;
404             }
405     } else if (!m_cur_tvdevice)
406         KMPlayer::Source::reset ();
407     if (m_cur_tvdevice) {
408         QString playback = static_cast <KMPlayer::Element *> (m_cur_tvdevice.ptr ())->getAttribute (QString::fromLatin1 ("playback"));
409         if (playback.isEmpty () || playback.toInt ())
410             QTimer::singleShot (0, m_player, SLOT (play ()));
411     }
412 }
413 /* TODO: playback by
414  * ffmpeg -vd /dev/video0 -r 25 -s 768x576 -f rawvideo - |mplayer -nocache -ao arts -rawvideo on:w=768:h=576:fps=25 -quiet -
415  */
416 
deactivate()417 KDE_NO_EXPORT void KMPlayerTVSource::deactivate () {
418     //if (m_player->view () && !m_app->view ()->controlPanel()->broadcastButton ()->isOn ())
419     //    m_app->view ()->controlPanel()->broadcastButton ()->hide ();
420     reset ();
421 }
422 
play(KMPlayer::Mrl * mrl)423 void KMPlayerTVSource::play (KMPlayer::Mrl *mrl) {
424     if (mrl && mrl->id == id_node_tv_document) {
425         readXML ();
426     } else {
427         m_current = mrl;
428         for (KMPlayer::Node *e = mrl; e; e = e->parentNode ()) {
429             if (e->id == id_node_tv_device) {
430                 m_cur_tvdevice = e;
431                 break;
432             } else if (e->id == id_node_tv_input)
433                 m_cur_tvinput = e;
434         }
435         if (m_player->source () != this)
436             m_player->setSource (this);
437         else
438             KMPlayer::Source::play (mrl);
439         /*else if (m_player->process ()->playing ()) {
440             //m_back_request = m_current;
441             m_player->process ()->stop ();
442         } else {
443             buildArguments ();
444             if (m_app->broadcasting ())
445                 QTimer::singleShot (0, m_app->broadcastConfig (), SLOT (startFeed ()));
446             else
447                 KMPlayer::Source::play (mrl);
448         }*/
449     }
450 }
451 
root()452 KDE_NO_EXPORT KMPlayer::NodePtr KMPlayerTVSource::root () {
453     return m_cur_tvinput;
454 }
455 
setCurrent(KMPlayer::Mrl * mrl)456 KDE_NO_EXPORT void KMPlayerTVSource::setCurrent (KMPlayer::Mrl *mrl) {
457     TVChannel * channel = 0L;
458     TVInput * input = 0L;
459     m_current = mrl;
460     KMPlayer::NodePtr elm = m_current;
461     if (elm && elm->id == id_node_tv_channel) {
462         channel = KMPlayer::convertNode <TVChannel> (elm);
463         elm = elm->parentNode ();
464     }
465     if (elm && elm->id == id_node_tv_input)
466         input = KMPlayer::convertNode <TVInput> (elm);
467     if (!(channel || (input && input->getAttribute ("tuner").isEmpty ())))
468         return;
469     m_cur_tvinput = input;
470     m_cur_tvdevice = input->parentNode ();
471     m_player->playModel()->updateTree(0, m_cur_tvinput, m_current, true, false);
472     if (m_cur_tvdevice->id != id_node_tv_device) {
473         return;
474     }
475     TVDevice * tvdevice = KMPlayer::convertNode <TVDevice> (m_cur_tvdevice);
476     m_identified = true;
477     m_audiodevice = tvdevice->getAttribute ("audio");
478     m_videodevice = tvdevice->src;
479     m_videonorm = input->getAttribute ("norm");
480     m_tuner = input->getAttribute (KMPlayer::Ids::attr_name);
481     QString xvport = tvdevice->getAttribute ("xvport");
482     if (!xvport.isEmpty ())
483         m_xvport = xvport.toInt ();
484     QString xvenc = input->getAttribute ("xvenc");
485     if (!xvenc.isEmpty ())
486         m_xvencoding = xvenc.toInt ();
487     QString command;
488     command.sprintf ("device=%s:input=%s",
489             tvdevice->src.toAscii ().data (),
490             input->getAttribute (KMPlayer::Ids::attr_id).toAscii ().data ());
491     if (channel) {
492         QString freq = channel->getAttribute ("frequency");
493         m_frequency = (int)(1000 * freq.toDouble ());
494         command += QString (":freq=%1").arg (freq);
495     } else
496         m_frequency = 0;
497     if (!m_videonorm.isEmpty ())
498         command += QString (":norm=%1").arg (m_videonorm);
499     m_app->setCaption (i18n ("TV: ") + (channel ? channel->mrl ()->title : input->mrl ()->title), false);
500     setDimensions (m_cur_tvdevice,
501             tvdevice->getAttribute (KMPlayer::Ids::attr_width).toInt (),
502             tvdevice->getAttribute (KMPlayer::Ids::attr_height).toInt ());
503     m_options.sprintf ("-tv noaudio:driver=%s:%s:width=%d:height=%d -slave -nocache -quiet", tvdriver.toAscii ().data (), command.toAscii ().data (), width (), height ());
504     m_recordcmd.sprintf ("-tv %s:driver=%s:%s:width=%d:height=%d", m_audiodevice.isEmpty () ? "noaudio" : QString(QLatin1String ("forceaudio:adevice=") + m_audiodevice).toAscii ().data(), tvdriver.toAscii ().data (), command.toAscii ().data (), width (), height ());
505 }
506 
menuClicked(int id)507 KDE_NO_EXPORT void KMPlayerTVSource::menuClicked (int id) {
508     KMPlayer::Node *elm = m_document->firstChild ();
509     for (; id > 0; --id,  elm = elm->nextSibling ())
510         ;
511     m_cur_tvdevice = elm;
512     m_cur_tvinput = elm->firstChild (); // FIXME
513     m_current = 0L;
514     m_player->setSource (this);
515 }
516 
filterOptions()517 KDE_NO_EXPORT QString KMPlayerTVSource::filterOptions () {
518     if (! m_player->settings ()->disableppauto)
519         return KMPlayer::Source::filterOptions ();
520     return QString ("-vf pp=lb");
521 }
522 
hasLength()523 KDE_NO_EXPORT bool KMPlayerTVSource::hasLength () {
524     return false;
525 }
526 
isSeekable()527 KDE_NO_EXPORT bool KMPlayerTVSource::isSeekable () {
528     return true;
529 }
530 
prettyName()531 KDE_NO_EXPORT QString KMPlayerTVSource::prettyName () {
532     QString name (i18n ("TV"));
533     //if (m_tvsource)
534     //    name += ' ' + m_tvsource->title;
535     return name;
536 }
537 
write(KSharedConfigPtr m_config)538 KDE_NO_EXPORT void KMPlayerTVSource::write (KSharedConfigPtr m_config) {
539     if (!config_read) return;
540     KConfigGroup (m_config, strTV).writeEntry (strTVDriver, tvdriver);
541     static_cast <TVDocument *> (m_document.ptr ())->writeToFile
542         (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/tv.xml");
543     kDebug () << "KMPlayerTVSource::write XML";
544 }
545 
readXML()546 KDE_NO_EXPORT void KMPlayerTVSource::readXML () {
547     if (config_read) return;
548     config_read = true;
549     kDebug () << "KMPlayerTVSource::readXML";
550     m_document->defer ();
551     m_player->playModel()->updateTree (tree_id, m_document, 0, false, false);
552     sync (false);
553 }
554 
read(KSharedConfigPtr m_config)555 KDE_NO_EXPORT void KMPlayerTVSource::read (KSharedConfigPtr m_config) {
556     tvdriver = KConfigGroup (m_config, strTV).readEntry (
557             strTVDriver, QString ("v4l2"));
558 }
559 
sync(bool fromUI)560 KDE_NO_EXPORT void KMPlayerTVSource::sync (bool fromUI) {
561     if (!m_configpage) return;
562     if (m_document && m_document->hasChildNodes ())
563         m_app->showBroadcastConfig ();
564     else
565         m_app->hideBroadcastConfig ();
566     if (fromUI) {
567         tvdriver = m_configpage->driver->text ();
568         for (KMPlayer::Node *d=m_document->firstChild();d; d=d->nextSibling())
569             if (d->id == id_node_tv_device)
570                 static_cast <TVDevice *> (d)->updateDevicePage ();
571         m_player->playModel()->updateTree(tree_id, m_document, 0, false, false);
572     } else {
573         m_configpage->driver->setText (tvdriver);
574         for (KMPlayer::Node *dp = m_document->firstChild (); dp; dp = dp->nextSibling ())
575             if (dp->id == id_node_tv_device)
576                 addTVDevicePage (KMPlayer::convertNode <TVDevice> (dp));
577     }
578 }
579 
prefLocation(QString & item,QString & icon,QString & tab)580 KDE_NO_EXPORT void KMPlayerTVSource::prefLocation (QString & item, QString & icon, QString & tab) {
581     item = i18n ("Source");
582     icon = QString ("source");
583     tab = i18n ("TV");
584 }
585 
prefPage(QWidget * parent)586 KDE_NO_EXPORT QFrame * KMPlayerTVSource::prefPage (QWidget * parent) {
587     if (!m_configpage) {
588         m_configpage = new KMPlayerPrefSourcePageTV (parent, this);
589         scanner = new TVDeviceScannerSource (this);
590         connect (m_configpage->scan, SIGNAL(clicked()), this, SLOT(slotScan()));
591     }
592     return m_configpage;
593 }
594 
hasTVDevice(KMPlayer::NodePtr doc,const QString & devstr)595 static bool hasTVDevice (KMPlayer::NodePtr doc, const QString & devstr) {
596     for (KMPlayer::Node *e = doc->firstChild (); e; e = e->nextSibling ())
597         if (e->id == id_node_tv_device &&
598                 static_cast <TVDevice *> (e)->src == devstr)
599             return true;
600     return false;
601 }
602 
slotScan()603 KDE_NO_EXPORT void KMPlayerTVSource::slotScan () {
604     QString devstr = m_configpage->device->lineEdit()->text ();
605     if (!hasTVDevice(m_document, devstr)) {
606         scanner->scan (devstr, m_configpage->driver->text());
607         connect (scanner, SIGNAL (scanFinished (TVDevice *)),
608                 this, SLOT (slotScanFinished (TVDevice *)));
609     } else
610         KMessageBox::error (m_configpage, i18n ("Device already present."),
611                 i18n ("Error"));
612 }
613 
slotScanFinished(TVDevice * tvdevice)614 KDE_NO_EXPORT void KMPlayerTVSource::slotScanFinished (TVDevice * tvdevice) {
615     disconnect (scanner, SIGNAL (scanFinished (TVDevice *)),
616                 this, SLOT (slotScanFinished (TVDevice *)));
617     if (tvdevice) {
618         tvdevice->zombie = false;
619         addTVDevicePage (tvdevice, true);
620         m_player->playModel()->updateTree(tree_id, m_document, 0, false, false);
621     } else
622         KMessageBox::error(m_configpage,i18n("No device found."),i18n("Error"));
623 }
624 
addTVDevicePage(TVDevice * dev,bool show)625 KDE_NO_EXPORT void KMPlayerTVSource::addTVDevicePage(TVDevice *dev, bool show) {
626     if (dev->device_page)
627         dev->device_page->deleteLater ();
628     dev->device_page = new TVDevicePage (m_configpage->notebook, dev);
629     m_configpage->notebook->addTab(dev->device_page, dev->title);
630     connect (dev->device_page, SIGNAL (deleted (TVDevicePage *)),
631              this, SLOT (slotDeviceDeleted (TVDevicePage *)));
632     if (show)
633         m_configpage->notebook->setCurrentIndex(m_configpage->notebook->count()-1);
634 }
635 
slotDeviceDeleted(TVDevicePage * devpage)636 KDE_NO_EXPORT void KMPlayerTVSource::slotDeviceDeleted (TVDevicePage *devpage) {
637     m_document->removeChild (devpage->device_doc);
638     m_configpage->notebook->setCurrentIndex(0);
639     m_player->playModel()->updateTree (tree_id, m_document, 0, false, false);
640 }
641 
642 //-----------------------------------------------------------------------------
643 
TVDeviceScannerSource(KMPlayerTVSource * src)644 KDE_NO_CDTOR_EXPORT TVDeviceScannerSource::TVDeviceScannerSource (KMPlayerTVSource * src)
645  : KMPlayer::Source (i18n ("TVScanner"), src->player (), "tvscanner"),
646    m_tvsource (src),
647    m_tvdevice (0L),
648    m_process (NULL),
649    m_viewer (NULL) {
650 }
651 
init()652 KDE_NO_EXPORT void TVDeviceScannerSource::init () {
653 }
654 
processOutput(const QString & line)655 KDE_NO_EXPORT bool TVDeviceScannerSource::processOutput (const QString & line) {
656     if (m_nameRegExp.indexIn(line) > -1) {
657         m_tvdevice->title = m_nameRegExp.cap (1);
658         m_tvdevice->setAttribute(KMPlayer::Ids::attr_name,m_tvdevice->title);
659         kDebug() << "Name " << m_tvdevice->title;
660     } else if (m_sizesRegExp.indexIn(line) > -1) {
661         m_tvdevice->setAttribute (KMPlayer::Ids::attr_width,
662                 m_sizesRegExp.cap(1));
663         m_tvdevice->setAttribute (KMPlayer::Ids::attr_height,
664                 m_sizesRegExp.cap(2));
665         m_tvdevice->setAttribute ("minwidth", m_sizesRegExp.cap (1));
666         m_tvdevice->setAttribute ("minheight", m_sizesRegExp.cap (2));
667         m_tvdevice->setAttribute ("maxwidth", m_sizesRegExp.cap (3));
668         m_tvdevice->setAttribute ("maxheight", m_sizesRegExp.cap (4));
669     } else if (m_inputRegExp.indexIn(line) > -1) {
670         KMPlayer::NodePtr doc = m_tvsource->document ();
671         TVInput * input = new TVInput (doc, m_inputRegExp.cap(2).trimmed(),
672                                        m_inputRegExp.cap (1).toInt ());
673         if (m_inputRegExp.cap (3).toInt () == 1)
674             input->setAttribute ("tuner", "1");
675         m_tvdevice->appendChild (input);
676         kDebug() << "Input " << input->mrl ()->title;
677     } else if (m_inputRegExpV4l2.indexIn(line) > -1) {
678         KMPlayer::NodePtr doc = m_tvsource->document ();
679         QStringList sl = m_inputRegExpV4l2.cap(1).split (QChar (';'));
680         const QStringList::iterator e = sl.end ();
681         for (QStringList::iterator i = sl.begin (); i != e; ++i) {
682             int pos = (*i).indexOf (QChar ('='));
683             if (pos > 0) {
684                 int id = (*i).left (pos).trimmed ().toInt ();
685                 TVInput *input = new TVInput(doc,(*i).mid(pos+1).trimmed(), id);
686                 if (!id && m_caps.indexOf ("tuner") > -1)
687                     input->setAttribute ("tuner", "1");
688                 m_tvdevice->appendChild (input);
689             }
690         }
691     } else {
692         int pos = line.indexOf ("Capabilites:");
693         if (pos > 0)
694             m_caps = line.mid (pos + 12);
695         return false;
696     }
697     return true;
698 }
699 
filterOptions()700 KDE_NO_EXPORT QString TVDeviceScannerSource::filterOptions () {
701     return QString ("");
702 }
703 
hasLength()704 KDE_NO_EXPORT bool TVDeviceScannerSource::hasLength () {
705     return false;
706 }
707 
isSeekable()708 KDE_NO_EXPORT bool TVDeviceScannerSource::isSeekable () {
709     return false;
710 }
711 
scan(const QString & dev,const QString & dri)712 KDE_NO_EXPORT bool TVDeviceScannerSource::scan (const QString & dev, const QString & dri) {
713     if (m_tvdevice)
714         return false;
715     setUrl ("tv://");
716     KMPlayer::NodePtr doc = m_tvsource->document ();
717     m_tvdevice = new TVDevice (doc, dev);
718     m_tvsource->document ()->appendChild (m_tvdevice);
719     m_tvdevice->zombie = true; // not for real yet
720     m_driver = dri;
721     m_old_source = m_tvsource->player ()->source ();
722     m_tvsource->player ()->setSource (this);
723     m_identified = true;
724     play (m_tvdevice);
725     return true;
726 }
727 
activate()728 KDE_NO_EXPORT void TVDeviceScannerSource::activate () {
729     m_nameRegExp.setPattern ("Selected device:\\s*([^\\s].*)");
730     m_sizesRegExp.setPattern ("Supported sizes:\\s*([0-9]+)x([0-9]+) => ([0-9]+)x([0-9]+)");
731     m_inputRegExp.setPattern ("\\s*([0-9]+):\\s*([^:]+):[^\\(]*\\(tuner:([01]),\\s*norm:([^\\)]+)\\)");
732     m_inputRegExpV4l2.setPattern ("inputs:((?:\\s*[0-9]+\\s*=\\s*[^;]+;)+)");
733 }
734 
deactivate()735 KDE_NO_EXPORT void TVDeviceScannerSource::deactivate () {
736     kDebug () << "TVDeviceScannerSource::deactivate";
737     if (m_tvdevice) {
738         if (m_tvdevice->parentNode ())
739             m_tvdevice->parentNode ()->removeChild (m_tvdevice);
740         m_tvdevice = 0L;
741         delete m_process;
742         emit scanFinished (m_tvdevice);
743     }
744 }
745 
play(KMPlayer::Mrl *)746 KDE_NO_EXPORT void TVDeviceScannerSource::play (KMPlayer::Mrl *) {
747     if (!m_tvdevice)
748         return;
749     m_options.sprintf ("tv:// -tv driver=%s:device=%s -identify -frames 0", m_driver.toAscii ().data (), m_tvdevice->src.toAscii ().data ());
750     m_tvsource->player ()->stop ();
751     KMPlayer::Node *n = new KMPlayer::SourceDocument (this, QString ());
752     setDocument (n, n);
753     m_process = m_player->mediaManager()->processInfos()["mplayer"]->create (m_player, this);
754     m_viewer = m_player->viewWidget ()->viewArea ()->createVideoWidget ();
755     m_process->ready ();
756 }
757 
scanningFinished()758 KDE_NO_EXPORT void TVDeviceScannerSource::scanningFinished () {
759     TVDevice * dev = 0L;
760     delete m_process;
761     kDebug () << "scanning done " << m_tvdevice->hasChildNodes ();
762     if (!m_tvdevice->hasChildNodes ()) {
763         m_tvsource->document ()->removeChild (m_tvdevice);
764     } else {
765         dev = m_tvdevice;
766         if (width () > 0 && height () > 0) {
767             m_tvdevice->setAttribute (KMPlayer::Ids::attr_width,
768                     QString::number (width ()));
769             m_tvdevice->setAttribute (KMPlayer::Ids::attr_height,
770                     QString::number (height ()));
771         }
772     }
773     m_tvdevice = 0L;
774     m_player->setSource (m_old_source);
775     emit scanFinished (dev);
776 }
777 
stateChange(KMPlayer::IProcess *,KMPlayer::IProcess::State os,KMPlayer::IProcess::State ns)778 void TVDeviceScannerSource::stateChange (KMPlayer::IProcess *,
779                    KMPlayer::IProcess::State os, KMPlayer::IProcess::State ns) {
780     if (KMPlayer::IProcess::Ready == ns) {
781         if (os > KMPlayer::IProcess::Ready)
782             QTimer::singleShot (0, this, SLOT (scanningFinished()));
783         else if (m_process && os < KMPlayer::IProcess::Ready)
784             m_process->play ();
785     }
786 }
787 
processDestroyed(KMPlayer::IProcess *)788 void TVDeviceScannerSource::processDestroyed (KMPlayer::IProcess *) {
789     m_process = NULL;
790     KMPlayer::View *view = m_player->viewWidget ();
791     if (view)
792         view->viewArea ()->destroyVideoWidget (m_viewer);
793     m_viewer = NULL;
794 }
795 
viewer()796 KMPlayer::IViewer *TVDeviceScannerSource::viewer () {
797     return m_viewer;
798 }
799 
getMrl()800 KMPlayer::Mrl *TVDeviceScannerSource::getMrl () {
801     return document ()->mrl ();
802 }
803 
804 
805 #include "kmplayertvsource.moc"
806