From 092be7678b67552cb3161fe162242bf8d3aeed2f Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 17 Feb 2010 00:54:13 +0000 Subject: Added old abandoned KDE3 version of kmplayer git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kmplayer@1091557 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/kmplayerpartbase.cpp | 2047 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2047 insertions(+) create mode 100644 src/kmplayerpartbase.cpp (limited to 'src/kmplayerpartbase.cpp') diff --git a/src/kmplayerpartbase.cpp b/src/kmplayerpartbase.cpp new file mode 100644 index 0000000..10a9f87 --- /dev/null +++ b/src/kmplayerpartbase.cpp @@ -0,0 +1,2047 @@ +/** + * Copyright (C) 2002-2003 by Koos Vriezen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +#ifdef KDE_USE_FINAL +#undef Always +#endif + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kmplayerpartbase.h" +#include "kmplayerview.h" +#include "playlistview.h" +#include "viewarea.h" +#include "kmplayercontrolpanel.h" +#include "kmplayerconfig.h" +#include "kmplayerprocess.h" +#include "kmplayer_smil.h" + +namespace KMPlayer { + +class KMPLAYER_NO_EXPORT BookmarkOwner : public KBookmarkOwner { +public: + BookmarkOwner (PartBase *); + KDE_NO_CDTOR_EXPORT virtual ~BookmarkOwner () {} + void openBookmarkURL(const QString& _url); + QString currentTitle() const; + QString currentURL() const; +private: + PartBase * m_player; +}; + +class KMPLAYER_NO_EXPORT BookmarkManager : public KBookmarkManager { +public: + BookmarkManager (const QString &); +}; + +} // namespace + +using namespace KMPlayer; + +KDE_NO_CDTOR_EXPORT BookmarkOwner::BookmarkOwner (PartBase * player) + : m_player (player) {} + +KDE_NO_EXPORT void BookmarkOwner::openBookmarkURL (const QString & url) { + m_player->openURL (KURL (url)); +} + +KDE_NO_EXPORT QString BookmarkOwner::currentTitle () const { + return m_player->source ()->prettyName (); +} + +KDE_NO_EXPORT QString BookmarkOwner::currentURL () const { + return m_player->source ()->url ().url (); +} + +inline BookmarkManager::BookmarkManager(const QString & bmfile) + : KBookmarkManager (bmfile, false) { +} + +//----------------------------------------------------------------------------- + +PartBase::PartBase (QWidget * wparent, const char *wname, + QObject * parent, const char *name, KConfig * config) + : KMediaPlayer::Player (wparent, wname ? wname : "kde_kmplayer_view", parent, name ? name : "kde_kmplayer_part"), + m_config (config), + m_view (new View (wparent, wname ? wname : "kde_kmplayer_view")), + m_settings (new Settings (this, config)), + m_recorder (0L), + m_source (0L), + m_bookmark_menu (0L), + m_record_timer (0), + m_update_tree_timer (0), + m_noresize (false), + m_auto_controls (true), + m_bPosSliderPressed (false), + m_in_update_tree (false) +{ + MPlayer *mplayer = new MPlayer (this, m_settings); + m_players ["mplayer"] = mplayer; + m_process = mplayer; + Xine * xine = new Xine (this, m_settings); + m_players ["xine"] = xine; + m_players ["gstreamer"] = new GStreamer (this, m_settings); + m_recorders ["mencoder"] = new MEncoder (this, m_settings); + m_recorders ["mplayerdumpstream"] = new MPlayerDumpstream(this, m_settings); + m_recorders ["ffmpeg"] = new FFMpeg (this, m_settings); + m_recorders ["xine"] = xine; + m_sources ["urlsource"] = new URLSource (this); + + QString bmfile = locate ("data", "kmplayer/bookmarks.xml"); + QString localbmfile = locateLocal ("data", "kmplayer/bookmarks.xml"); + if (localbmfile != bmfile) { + kdDebug () << "cp " << bmfile << " " << localbmfile << endl; + KProcess p; + p << "/bin/cp" << QFile::encodeName (bmfile) << QFile::encodeName (localbmfile); + p.start (KProcess::Block); + } + m_bookmark_manager = new BookmarkManager (localbmfile); + m_bookmark_owner = new BookmarkOwner (this); +} + +void PartBase::showConfigDialog () { + m_settings->show ("URLPage"); +} + +KDE_NO_EXPORT void PartBase::showPlayListWindow () { + // note, this is also the slot of application's view_playlist action, but + // anyhow, actions don't work for the fullscreen out-of-the-box, so ... + if (m_view->viewArea ()->isFullScreen ()) + fullScreen (); + else if (m_view->viewArea ()->isMinimalMode ()) + ; //done by app: m_view->viewArea ()->minimalMode (); + else + m_view->toggleShowPlaylist (); +} + +KDE_NO_EXPORT void PartBase::addBookMark (const QString & t, const QString & url) { + KBookmarkGroup b = m_bookmark_manager->root (); + b.addBookmark (m_bookmark_manager, t, KURL (url)); + m_bookmark_manager->emitChanged (b); +} + +void PartBase::init (KActionCollection * action_collection) { + KParts::Part::setWidget (m_view); + m_view->init (action_collection); +#ifdef HAVE_NSPR + m_players ["npp"] = new NpPlayer (this, m_settings, m_service); +#endif + connect(m_settings, SIGNAL(configChanged()), this, SLOT(settingsChanged())); + m_settings->readConfig (); + m_settings->applyColorSetting (false); + m_bookmark_menu = new KBookmarkMenu (m_bookmark_manager, m_bookmark_owner, m_view->controlPanel ()->bookmarkMenu (), action_collection, true, true); + connect (m_view, SIGNAL (urlDropped (const KURL::List &)), this, SLOT (openURL (const KURL::List &))); + connectPlaylist (m_view->playList ()); + connectInfoPanel (m_view->infoPanel ()); + new KAction (i18n ("Edit playlist &item"), 0, 0, m_view->playList (), SLOT (editCurrent ()), action_collection, "edit_playlist_item"); +} + +void PartBase::connectPanel (ControlPanel * panel) { + panel->contrastSlider ()->setValue (m_settings->contrast); + panel->brightnessSlider ()->setValue (m_settings->brightness); + panel->hueSlider ()->setValue (m_settings->hue); + panel->saturationSlider ()->setValue (m_settings->saturation); + panel->volumeBar ()->setValue (m_settings->volume); + connect (panel->button (ControlPanel::button_playlist), SIGNAL (clicked ()), this, SLOT (showPlayListWindow ())); + connect (panel->button (ControlPanel::button_back), SIGNAL (clicked ()), this, SLOT (back ())); + connect (panel->button (ControlPanel::button_play), SIGNAL (clicked ()), this, SLOT (play ())); + connect (panel->button (ControlPanel::button_forward), SIGNAL (clicked ()), this, SLOT (forward ())); + connect (panel->button (ControlPanel::button_pause), SIGNAL (clicked ()), this, SLOT (pause ())); + connect (panel->button (ControlPanel::button_stop), SIGNAL (clicked ()), this, SLOT (stop ())); + connect (panel->button (ControlPanel::button_record), SIGNAL (clicked()), this, SLOT (record())); + connect (panel->volumeBar (), SIGNAL (volumeChanged (int)), this, SLOT (volumeChanged (int))); + connect (panel->positionSlider (), SIGNAL (valueChanged (int)), this, SLOT (positionValueChanged (int))); + connect (panel->positionSlider (), SIGNAL (sliderPressed()), this, SLOT (posSliderPressed())); + connect (panel->positionSlider (), SIGNAL (sliderReleased()), this, SLOT (posSliderReleased())); + connect (this, SIGNAL (positioned (int, int)), panel, SLOT (setPlayingProgress (int, int))); + connect (this, SIGNAL (loading(int)), panel, SLOT(setLoadingProgress(int))); + connect (panel->contrastSlider (), SIGNAL (valueChanged(int)), this, SLOT (contrastValueChanged(int))); + connect (panel->brightnessSlider (), SIGNAL (valueChanged(int)), this, SLOT (brightnessValueChanged(int))); + connect (panel->hueSlider (), SIGNAL (valueChanged(int)), this, SLOT (hueValueChanged(int))); + connect (panel->saturationSlider (), SIGNAL (valueChanged(int)), this, SLOT (saturationValueChanged(int))); + connect (this, SIGNAL (languagesUpdated(const QStringList &, const QStringList &)), panel, SLOT (setLanguages (const QStringList &, const QStringList &))); + connect (panel->audioMenu (), SIGNAL (activated (int)), this, SLOT (audioSelected (int))); + connect (panel->subtitleMenu (), SIGNAL (activated (int)), this, SLOT (subtitleSelected (int))); + connect (this, SIGNAL (audioIsSelected (int)), panel, SLOT (selectAudioLanguage (int))); + connect (this, SIGNAL (subtitleIsSelected (int)), panel, SLOT (selectSubtitle (int))); + panel->popupMenu()->connectItem (ControlPanel::menu_fullscreen, this, SLOT (fullScreen ())); + panel->popupMenu ()->connectItem (ControlPanel::menu_config, + this, SLOT (showConfigDialog ())); + panel->popupMenu ()->connectItem (ControlPanel::menu_video, + m_view, SLOT(toggleVideoConsoleWindow())); + panel->popupMenu ()->connectItem (ControlPanel::menu_playlist, + m_view, SLOT (toggleShowPlaylist ())); + connect (this, SIGNAL (statusUpdated (const QString &)), + panel->view (), SLOT (setStatusMessage (const QString &))); + //connect (panel (), SIGNAL (clicked ()), m_settings, SLOT (show ())); +} + +void PartBase::connectPlaylist (PlayListView * playlist) { + connect (playlist, SIGNAL (addBookMark (const QString &, const QString &)), + this, SLOT (addBookMark (const QString &, const QString &))); + connect (playlist, SIGNAL (executed (QListViewItem *)), + this, SLOT (playListItemExecuted (QListViewItem *))); + connect (playlist, SIGNAL (clicked (QListViewItem *)), + this, SLOT (playListItemClicked (QListViewItem *))); + connect (this, SIGNAL (treeChanged (int, NodePtr, NodePtr, bool, bool)), + playlist, SLOT (updateTree (int, NodePtr, NodePtr, bool, bool))); + connect (this, SIGNAL (treeUpdated ()), + playlist, SLOT (triggerUpdate ())); +} + +void PartBase::connectInfoPanel (InfoWindow * infopanel) { + connect (this, SIGNAL (infoUpdated (const QString &)), + infopanel->view (), SLOT (setInfoMessage (const QString &))); +} + +PartBase::~PartBase () { + kdDebug() << "PartBase::~PartBase" << endl; + m_view = (View*) 0; + stop (); + if (m_source) + m_source->deactivate (); + delete m_settings; + delete m_bookmark_menu; + delete m_bookmark_manager; + delete m_bookmark_owner; +} + +void PartBase::settingsChanged () { + if (!m_view) + return; + if (m_settings->showcnfbutton) + m_view->controlPanel()->button (ControlPanel::button_config)->show(); + else + m_view->controlPanel()->button (ControlPanel::button_config)->hide(); + m_view->controlPanel()->enableRecordButtons (m_settings->showrecordbutton); + if (m_settings->showplaylistbutton) + m_view->controlPanel()->button (ControlPanel::button_playlist)->show(); + else + m_view->controlPanel()->button (ControlPanel::button_playlist)->hide(); + if (!m_settings->showbroadcastbutton) + m_view->controlPanel ()->broadcastButton ()->hide (); + keepMovieAspect (m_settings->sizeratio); + m_settings->applyColorSetting (true); +} + +KMediaPlayer::View* PartBase::view () { + return m_view; +} + +extern const char * strGeneralGroup; + +bool PartBase::setProcess (Mrl *mrl) { + // determine backend, start with temp_backends + QString p = temp_backends [m_source->name()]; + bool remember_backend = p.isEmpty (); + bool changed = false; + if (p.isEmpty ()) { + // next try to find mimetype match from kmplayerrc + if (!mrl->mimetype.isEmpty ()) { + m_config->setGroup (mrl->mimetype); + p = m_config->readEntry ("player", "" ); + remember_backend = !(!p.isEmpty () && + m_players.contains (p) && + m_players [p]->supports (m_source->name ())); + } + } + if (p.isEmpty ()) + // try source match from kmplayerrc + p = m_settings->backends [m_source->name()]; + if (p.isEmpty ()) { + // try source match from kmplayerrc by re-reading + m_config->setGroup (strGeneralGroup); + p = m_config->readEntry (m_source->name (), ""); + } + if (p.isEmpty () || + !m_players.contains (p) || + !m_players [p]->supports (m_source->name ())) { + // finally find first supported player + p.truncate (0); + if (!m_process || !m_process->supports (m_source->name ())) { + ProcessMap::const_iterator i, e = m_players.end(); + for (i = m_players.begin(); i != e; ++i) + if (i.data ()->supports (m_source->name ())) { + p = QString (i.data ()->name ()); + break; + } + } else + p = QString (m_process->name ()); + } + if (!p.isEmpty ()) { + if (!m_process || p != m_process->name ()) { + setProcess (p.ascii ()); + updatePlayerMenu (m_view->controlPanel ()); + changed = true; + } + if (remember_backend) + m_settings->backends [m_source->name()] = m_process->name (); + else + temp_backends.remove (m_source->name()); + } + return changed; +} + +void PartBase::setProcess (const char * name) { + Process * process = name ? m_players [name] : 0L; + if (m_process == process) + return; + if (!m_source) + m_source = m_sources ["urlsource"]; + Process * old_process = m_process; + m_process = process; + if (old_process && old_process->state () > Process::NotRunning) + old_process->quit (); + if (!m_process) + return; + m_process->setSource (m_source); + if (m_process->playing ()) { + m_view->controlPanel ()->setPlaying (true); + m_view->controlPanel ()->showPositionSlider (!!m_source->length ()); + m_view->controlPanel ()->enableSeekButtons (m_source->isSeekable ()); + } + emit processChanged (name); +} + +void PartBase::setRecorder (const char * name) { + Process * recorder = name ? m_recorders [name] : 0L; + if (m_recorder == recorder) + return; + if (m_recorder) + m_recorder->quit (); + m_recorder = recorder; +} + +KDE_NO_EXPORT void PartBase::slotPlayerMenu (int id) { + bool playing = m_process->playing (); + const char * srcname = m_source->name (); + QPopupMenu * menu = m_view->controlPanel ()->playerMenu (); + ProcessMap::const_iterator pi = m_players.begin(), e = m_players.end(); + unsigned i = 0; + for (; pi != e && i < menu->count(); ++pi) { + Process * proc = pi.data (); + if (!proc->supports (srcname)) + continue; + int menuid = menu->idAt (i); + menu->setItemChecked (menuid, menuid == id); + if (menuid == id) { + if (proc->name () != QString ("npp")) + m_settings->backends [srcname] = proc->name (); + temp_backends [srcname] = proc->name (); + if (playing && strcmp (m_process->name (), proc->name ())) + m_process->quit (); + setProcess (proc->name ()); + } + ++i; + } + if (playing) + setSource (m_source); // re-activate +} + +void PartBase::updatePlayerMenu (ControlPanel * panel) { + if (!m_view || !m_process) + return; + QPopupMenu * menu = panel->playerMenu (); + menu->clear (); + if (!m_source) + return; + const ProcessMap::const_iterator e = m_players.end(); + int id = 0; // if multiple parts, id's should be the same for all menu's + for (ProcessMap::const_iterator i = m_players.begin(); i != e; ++i) { + Process * p = i.data (); + if (p->supports (m_source->name ())) { + menu->insertItem (p->menuName (), this, SLOT (slotPlayerMenu (int)), 0, id++); + if (i.data() == m_process) + menu->setItemChecked (id-1, true); + } + } +} + +void PartBase::connectSource (Source * old_source, Source * source) { + if (old_source) { + disconnect (old_source, SIGNAL(endOfPlayItems ()), this, SLOT(stop ())); + disconnect (old_source, SIGNAL (dimensionsChanged ()), + this, SLOT (sourceHasChangedAspects ())); + disconnect (old_source, SIGNAL (startPlaying ()), + this, SLOT (playingStarted ())); + disconnect (old_source, SIGNAL (stopPlaying ()), + this, SLOT (playingStopped ())); + } + if (source) { + connect (source, SIGNAL (endOfPlayItems ()), this, SLOT (stop ())); + connect (source, SIGNAL (dimensionsChanged ()), + this, SLOT (sourceHasChangedAspects ())); + connect (source, SIGNAL (startPlaying()), this, SLOT(playingStarted())); + connect (source, SIGNAL (stopPlaying ()), this, SLOT(playingStopped())); + } +} + +void PartBase::setSource (Source * _source) { + Source * old_source = m_source; + if (m_source) { + m_source->deactivate (); + stop (); + if (m_view) { + m_view->reset (); + emit infoUpdated (QString ()); + } + disconnect (m_source, SIGNAL (startRecording ()), + this, SLOT (recordingStarted ())); + disconnect (this, SIGNAL (audioIsSelected (int)), + m_source, SLOT (setAudioLang (int))); + disconnect (this, SIGNAL (subtitleIsSelected (int)), + m_source, SLOT (setSubtitle (int))); + } + if (m_view) { + if (m_auto_controls) + m_view->controlPanel ()->setAutoControls (m_auto_controls); + m_view->controlPanel ()->enableRecordButtons (m_settings->showrecordbutton); + if (!m_settings->showcnfbutton) + m_view->controlPanel()->button(ControlPanel::button_config)->hide(); + if (!m_settings->showplaylistbutton) + m_view->controlPanel()->button(ControlPanel::button_playlist)->hide(); + } + m_source = _source; + connectSource (old_source, m_source); + m_process->setSource (m_source); + connect (m_source, SIGNAL(startRecording()), this,SLOT(recordingStarted())); + connect (this, SIGNAL (audioIsSelected (int)), + m_source, SLOT (setAudioLang (int))); + connect (this, SIGNAL (subtitleIsSelected (int)), + m_source, SLOT (setSubtitle (int))); + m_source->init (); + m_source->setIdentified (false); + if (m_view && m_view->viewer ()) { + updatePlayerMenu (m_view->controlPanel ()); + m_view->viewer ()->setAspect (0.0); + } + if (m_source) QTimer::singleShot (0, m_source, SLOT (activate ())); + updateTree (true, true); + emit sourceChanged (old_source, m_source); +} + +KDE_NO_EXPORT void PartBase::changeURL (const QString & url) { + emit urlChanged (url); +} + +bool PartBase::isSeekable (void) const { + return m_source ? m_source->isSeekable () : false; +} + +bool PartBase::hasLength () const { + return m_source ? m_source->hasLength () : false; +} + +unsigned long PartBase::length () const { + return m_source ? m_source->length () : 0; +} + +bool PartBase::openURL (const KURL & url) { + kdDebug () << "PartBase::openURL " << url.url() << url.isValid () << endl; + if (!m_view) return false; + stop (); + Source * src = (url.isEmpty () ? m_sources ["urlsource"] : (!url.protocol ().compare ("kmplayer") && m_sources.contains (url.host ()) ? m_sources [url.host ()] : m_sources ["urlsource"])); + src->setSubURL (KURL ()); + src->setURL (url); + setSource (src); + return true; +} + +bool PartBase::openURL (const KURL::List & urls) { + if (urls.size () == 1) { + openURL (urls[0]); + } else { + openURL (KURL ()); + NodePtr d = m_source->document (); + if (d) + for (unsigned int i = 0; i < urls.size (); i++) + d->appendChild (new GenericURL (d, KURL::decode_string (urls [i].url ()))); + } + return true; +} + +bool PartBase::closeURL () { + stop (); + if (m_view) { + m_view->viewer ()->setAspect (0.0); + m_view->reset (); + } + return true; +} + +bool PartBase::openFile () { + return false; +} + +void PartBase::keepMovieAspect (bool b) { + if (m_view) { + m_view->setKeepSizeRatio (b); + if (m_source) + m_view->viewer ()->setAspect (b ? m_source->aspect () : 0.0); + } +} + +void PartBase::recordingStarted () { + if (m_settings->replayoption == Settings::ReplayAfter) + m_record_timer = startTimer (1000 * m_settings->replaytime); +} + +void PartBase::recordingStopped () { + killTimer (m_record_timer); + m_record_timer = 0; + Recorder * rec = dynamic_cast (m_recorder); + if (rec) { + if (m_settings->replayoption == Settings::ReplayFinished || + (m_settings->replayoption == Settings::ReplayAfter && !playing ())) + openURL (rec->recordURL ()); + rec->setURL (KURL ()); + } + setRecorder ("mencoder"); //FIXME see PartBase::record() checking playing() +} + +void PartBase::timerEvent (QTimerEvent * e) { + if (e->timerId () == m_record_timer) { + kdDebug () << "record timer event" << (m_recorder->playing () && !playing ()) << endl; + m_record_timer = 0; + if (m_recorder->playing () && !playing ()) { + Recorder * rec = dynamic_cast (m_recorder); + if (rec) { + openURL (rec->recordURL ()); + rec->setURL (KURL ()); + } + } + } else if (e->timerId () == m_update_tree_timer) { + m_update_tree_timer = 0; + updateTree (m_update_tree_full, true); + } + killTimer (e->timerId ()); +} + +void PartBase::playingStarted () { + //m_view->viewer ()->setAspect (m_source->aspect ()); + if (m_view) { + m_view->controlPanel ()->setPlaying (true); + m_view->controlPanel ()->showPositionSlider (!!m_source->length ()); + m_view->controlPanel ()->enableSeekButtons (m_source->isSeekable ()); + if (m_settings->autoadjustvolume && m_process) + m_process->volume(m_view->controlPanel()->volumeBar()->value(),true); + } + emit loading (100); +} + +void PartBase::playingStopped () { + kdDebug () << "playingStopped " << this << endl; + if (m_view) { + m_view->controlPanel ()->setPlaying (false); + m_view->reset (); + } + m_bPosSliderPressed = false; +} + +KDE_NO_EXPORT void PartBase::setPosition (int position, int length) { + if (m_view && !m_bPosSliderPressed) + emit positioned (position, length); +} + +void PartBase::setLoaded (int percentage) { + emit loading (percentage); +} + +unsigned long PartBase::position () const { + return m_source ? 100 * m_source->position () : 0; +} + +void PartBase::pause () { + NodePtr doc = m_source ? m_source->document () : 0L; + if (doc) { + if (doc->state == Node::state_deferred) + doc->undefer (); + else + doc->defer (); + } +} + +void PartBase::back () { + m_source->backward (); +} + +void PartBase::forward () { + m_source->forward (); +} + +KDE_NO_EXPORT void PartBase::playListItemClicked (QListViewItem * item) { + if (!item) + return; + PlayListItem * vi = static_cast (item); + RootPlayListItem * ri = vi->playListView ()->rootItem (item); + if (ri == item && vi->node) { + QString src = ri->source; + //kdDebug() << "playListItemClicked " << src << " " << vi->node->nodeName() << endl; + Source * source = src.isEmpty() ? m_source : m_sources[src.ascii()]; + if (vi->node->isPlayable ()) { + source->jump (vi->node); //may become !isPlayable by lazy loading + if (!vi->node->isPlayable ()) + emit treeChanged (ri->id, vi->node, 0, false, true); + } else if (vi->firstChild ()) + vi->listView ()->setOpen (vi, !vi->isOpen ()); + } else if (!vi->node && !vi->m_attr) + updateTree (); // items already deleted +} + +KDE_NO_EXPORT void PartBase::playListItemExecuted (QListViewItem * item) { + if (m_in_update_tree) return; + if (m_view->editMode ()) return; + PlayListItem * vi = static_cast (item); + RootPlayListItem * ri = vi->playListView ()->rootItem (item); + if (ri == item) + return; // both null or handled by playListItemClicked + if (vi->node) { + QString src = ri->source; + //kdDebug() << "playListItemExecuted " << src << " " << vi->node->nodeName() << endl; + Source * source = src.isEmpty() ? m_source : m_sources[src.ascii()]; + if (vi->node->isPlayable ()) { + source->jump (vi->node); //may become !isPlayable by lazy loading + if (!vi->node->isPlayable ()) + emit treeChanged (ri->id, vi->node, 0, false, true); + } else if (vi->firstChild ()) + vi->listView ()->setOpen (vi, !vi->isOpen ()); + } else if (vi->m_attr) { + if (vi->m_attr->name () == StringPool::attr_src || + vi->m_attr->name () == StringPool::attr_href || + vi->m_attr->name () == StringPool::attr_url || + vi->m_attr->name () == StringPool::attr_value || + vi->m_attr->name () == "data") { + QString src (vi->m_attr->value ()); + if (!src.isEmpty ()) { + PlayListItem * pi = static_cast (item->parent()); + if (pi) { + for (NodePtr e = pi->node; e; e = e->parentNode ()) { + Mrl * mrl = e->mrl (); + if (mrl) + src = KURL (mrl->absolutePath (), src).url (); + } + KURL url (src); + if (url.isValid ()) + openURL (url); + } + } + } + } else + emit treeChanged (ri->id, ri->node, 0L, false, false); + if (m_view) + m_view->viewArea ()->setFocus (); +} + +void PartBase::updateTree (bool full, bool force) { + if (force) { + m_in_update_tree = true; + if (m_update_tree_full) { + if (m_source) + emit treeChanged (0, m_source->root (), m_source->current (), true, false); + } else + emit treeUpdated (); + m_in_update_tree = false; + if (m_update_tree_timer) { + killTimer (m_update_tree_timer); + m_update_tree_timer = 0; + } + } else if (!m_update_tree_timer) { + m_update_tree_timer = startTimer (100); + m_update_tree_full = full; + } else + m_update_tree_full |= full; +} + +void PartBase::updateInfo (const QString & msg) { + emit infoUpdated (msg); +} + +void PartBase::updateStatus (const QString & msg) { + emit statusUpdated (msg); +} + +void PartBase::setLanguages (const QStringList & al, const QStringList & sl) { + emit languagesUpdated (al, sl); +} + +KDE_NO_EXPORT void PartBase::audioSelected (int id) { + emit audioIsSelected (id); +} + +KDE_NO_EXPORT void PartBase::subtitleSelected (int id) { + emit subtitleIsSelected (id); +} + +void PartBase::record () { + if (m_view) m_view->setCursor (QCursor (Qt::WaitCursor)); + if (m_recorder->playing ()) { + m_recorder->stop (); + } else { + m_settings->show ("RecordPage"); + m_view->controlPanel ()->setRecording (false); + } + if (m_view) m_view->setCursor (QCursor (Qt::ArrowCursor)); +} + +void PartBase::play () { + if (!m_process || !m_view) return; + QPushButton * pb = ::qt_cast (sender ()); + if (pb && !pb->isOn ()) { + stop (); + return; + } + if (m_update_tree_timer) { + killTimer (m_update_tree_timer); + m_update_tree_timer = 0; + } + if (m_process->state () == Process::NotRunning) { + PlayListItem * lvi = m_view->playList ()->currentPlayListItem (); + if (lvi) { // make sure it's in the first tree + QListViewItem * pitem = lvi; + while (pitem->parent()) + pitem = pitem->parent(); + if (pitem != m_view->playList ()->firstChild ()) + lvi = 0L; + } + if (!lvi) + lvi = static_cast(m_view->playList()->firstChild()); + if (lvi) + for (NodePtr n = lvi->node; n; n = n->parentNode ()) { + if (n->isPlayable ()) { + m_source->setCurrent (n); + break; + } + } + m_process->ready (m_view->viewer ()); + } else if (m_process->state () == Process::Ready) { + m_source->playCurrent (); + } else + m_process->play (m_source, m_source->current ()); +} + +bool PartBase::playing () const { + return m_process && m_process->state () > Process::Ready; +} + +void PartBase::stop () { + QPushButton * b = m_view ? m_view->controlPanel ()->button (ControlPanel::button_stop) : 0L; + if (b) { + if (!b->isOn ()) + b->toggle (); + m_view->setCursor (QCursor (Qt::WaitCursor)); + } + if (m_process) + m_process->quit (); + if (m_source) + m_source->reset (); + if (m_view) { + m_view->setCursor (QCursor (Qt::ArrowCursor)); + if (b->isOn ()) + b->toggle (); + m_view->controlPanel ()->setPlaying (false); + setLoaded (100); + } +} + +void PartBase::seek (unsigned long msec) { + if (m_process) + m_process->seek (msec/100, true); +} + +void PartBase::adjustVolume (int incdec) { + m_process->volume (incdec, false); +} + +void PartBase::increaseVolume () { + if (m_view) + m_view->controlPanel ()->volumeBar ()->setValue (m_view->controlPanel ()->volumeBar ()->value () + 2); +} + +void PartBase::decreaseVolume () { + if (m_view) + m_view->controlPanel ()->volumeBar ()->setValue (m_view->controlPanel ()->volumeBar ()->value () - 2); +} + +KDE_NO_EXPORT void PartBase::posSliderPressed () { + m_bPosSliderPressed=true; +} + +KDE_NO_EXPORT void PartBase::posSliderReleased () { + m_bPosSliderPressed=false; +#if (QT_VERSION < 0x030200) + const QSlider * posSlider = dynamic_cast (sender ()); +#else + const QSlider * posSlider = ::qt_cast (sender ()); +#endif + if (posSlider) + m_process->seek (posSlider->value(), true); +} + +KDE_NO_EXPORT void PartBase::volumeChanged (int val) { + if (m_process) { + m_settings->volume = val; + m_process->volume (val, true); + } +} + +KDE_NO_EXPORT void PartBase::contrastValueChanged (int val) { + m_settings->contrast = val; + m_process->contrast (val, true); +} + +KDE_NO_EXPORT void PartBase::brightnessValueChanged (int val) { + m_settings->brightness = val; + m_process->brightness (val, true); +} + +KDE_NO_EXPORT void PartBase::hueValueChanged (int val) { + m_settings->hue = val; + m_process->hue (val, true); +} + +KDE_NO_EXPORT void PartBase::saturationValueChanged (int val) { + m_settings->saturation = val; + m_process->saturation (val, true); +} + +KDE_NO_EXPORT void PartBase::sourceHasChangedAspects () { + if (m_view && m_source) { + //kdDebug () << "sourceHasChangedAspects " << m_source->aspect () << endl; + m_view->viewer ()->setAspect (m_source->aspect ()); + m_view->updateLayout (); + } + emit sourceDimensionChanged (); +} + +KDE_NO_EXPORT void PartBase::positionValueChanged (int pos) { + QSlider * slider = ::qt_cast (sender ()); + if (slider && slider->isEnabled ()) + m_process->seek (pos, true); +} + +KDE_NO_EXPORT void PartBase::fullScreen () { + if (m_view) + m_view->fullScreen (); +} + +KDE_NO_EXPORT void PartBase::toggleFullScreen () { + m_view->fullScreen (); +} + +KDE_NO_EXPORT bool PartBase::isPlaying () { + return playing (); +} + +KAboutData* PartBase::createAboutData () { + KMessageBox::error(0L, "createAboutData", "KMPlayer"); + return 0; +} + +//----------------------------------------------------------------------------- + +Source::Source (const QString & name, PartBase * player, const char * n) + : QObject (player, n), + m_name (name), m_player (player), m_identified (false), m_auto_play (true), + m_frequency (0), m_xvport (0), m_xvencoding (-1), m_doc_timer (0) { + init (); +} + +Source::~Source () { + if (m_document) + m_document->document ()->dispose (); + m_document = 0L; + Q_ASSERT (m_current.ptr () == 0L); +} + +void Source::init () { + //setDimensions (320, 240); + m_width = 0; + m_height = 0; + m_aspect = 0.0; + m_length = 0; + m_position = 0; + setLength (m_document, 0); + m_recordcmd.truncate (0); +} + +KDE_NO_EXPORT void Source::setLanguages (const QStringList & alang, const QStringList & slang) { + m_player->setLanguages (alang, slang); +} + +void Source::setDimensions (NodePtr node, int w, int h) { + Mrl * mrl = node ? node->mrl () : 0L; + if (mrl && mrl->view_mode == Mrl::WindowMode) { + mrl->width = w; + mrl->height = h; + float a = h > 0 ? 1.0 * w / h : 0.0; + mrl->aspect = a; + if (m_player->view ()) { + static_cast (m_player->view())->viewer()->setAspect(a); + static_cast (m_player->view ())->updateLayout (); + } + } else if (m_aspect < 0.001 || m_width != w || m_height != h) { + bool ev = (w > 0 && h > 0) || + (h == 0 && m_height > 0) || + (w == 0 && m_width > 0); + m_width = w; + m_height = h; + if (m_aspect < 0.001) + setAspect (node, h > 0 ? 1.0 * w / h : 0.0); + //kdDebug () << "setDimensions " << w << "x" << h << " a:" << m_aspect << endl; + if (ev) + emit dimensionsChanged (); + } +} + +void Source::setAspect (NodePtr node, float a) { + //kdDebug () << "setAspect " << a << endl; + Mrl * mrl = node ? node->mrl () : 0L; + bool changed = false; + if (mrl) { + if (mrl->view_mode == Mrl::WindowMode) + changed |= (fabs (mrl->aspect - a) > 0.001); + mrl->aspect = a; + } + if (!mrl || mrl->view_mode == Mrl::SingleMode) { + changed |= (fabs (m_aspect - a) > 0.001); + m_aspect = a; + } + if (changed) + emit dimensionsChanged (); +} + +void Source::setLength (NodePtr, int len) { + m_length = len; + m_player->setPosition (m_position, m_length); +} + +KDE_NO_EXPORT void Source::setPosition (int pos) { + m_position = pos; + m_player->setPosition (pos, m_length); +} + +KDE_NO_EXPORT void Source::setLoading (int percentage) { + m_player->setLoaded (percentage); +} + +/* +static void printTree (NodePtr root, QString off=QString()) { + if (!root) { + kdDebug() << off << "[null]" << endl; + return; + } + kdDebug() << off << root->nodeName() << " " << (Element*)root << (root->isPlayable() ? root->mrl ()->src : QString ("-")) << endl; + off += QString (" "); + for (NodePtr e = root->firstChild(); e; e = e->nextSibling()) + printTree(e, off); +}*/ + +void Source::setURL (const KURL & url) { + m_url = url; + m_back_request = 0L; + if (m_document && !m_document->hasChildNodes () && + (m_document->mrl()->src.isEmpty () || + m_document->mrl()->src == url.url ())) + // special case, mime is set first by plugin FIXME v + m_document->mrl()->src = url.url (); + else { + if (m_document) + m_document->document ()->dispose (); + m_document = new Document (url.url (), this); + } + if (m_player->process () && m_player->source () == this) + m_player->updateTree (); + //kdDebug() << name() << " setURL " << url << endl; + m_current = m_document; +} + +void Source::setTitle (const QString & title) { + emit titleChanged (title); +} + +KDE_NO_EXPORT void Source::setAudioLang (int id) { + View * v = static_cast (m_player->view()); + if (v && m_player->process ()) + m_player->process ()->setAudioLang (id, v->controlPanel ()->audioMenu ()->text (id)); +} + +KDE_NO_EXPORT void Source::setSubtitle (int id) { + View * v = static_cast (m_player->view()); + if (v && m_player->process ()) + m_player->process ()->setSubtitle (id, v->controlPanel ()->subtitleMenu ()->text (id)); +} + +void Source::reset () { + if (m_document) { + //kdDebug() << "Source::first" << endl; + m_current = NodePtr (); + m_document->reset (); + m_player->updateTree (); + } + init (); +} + +QString Source::currentMrl () { + Mrl * mrl = m_current ? m_current->mrl () : 0L; + kdDebug() << "Source::currentMrl " << (m_current ? m_current->nodeName():"") << " src:" << (mrl ? mrl->absolutePath () : QString ()) << endl; + return mrl ? mrl->absolutePath () : QString (); +} + +void Source::playCurrent () { + QString url = currentMrl (); + m_player->changeURL (url); + m_width = m_height = 0; + m_aspect = 0.0; + if (m_player->view ()) + static_cast (m_player->view ())->playingStop ();//show controls + if (m_document && !m_document->active ()) { + if (!m_current) + m_document->activate (); + else { // ugly code duplicate w/ back_request + for (NodePtr p = m_current->parentNode(); p; p = p->parentNode()) + p->state = Element::state_activated; + m_current->activate (); + } + } else if (!m_current) { + emit endOfPlayItems (); + } else if (m_current->state == Element::state_deferred) { + // m_current->undefer (); + } else if (m_player->process ()->state () == Process::NotRunning) { + m_player->process ()->ready (static_cast (m_player->view ())->viewer ()); + } else if (m_player->process ()) { + Mrl * mrl = m_back_request ? m_back_request->mrl () : m_current->mrl (); + if (mrl->view_mode == Mrl::SingleMode) { + // don't reset the dimensions if we have any + m_width = mrl->width; + m_height = mrl->height; + m_aspect = mrl->aspect; + } + m_back_request = 0L; + m_player->process ()->play (this, mrl->linkNode ()); + } + //kdDebug () << "Source::playCurrent " << (m_current ? m_current->nodeName():" doc act:") << (m_document && !m_document->active ()) << " cur:" << (!m_current) << " cur act:" << (m_current && !m_current->active ()) << endl; + m_player->updateTree (); + emit dimensionsChanged (); +} + +static NodePtr findDepthFirst (NodePtr elm) { + if (!elm) + return NodePtr (); + NodePtr tmp = elm; + for ( ; tmp; tmp = tmp->nextSibling ()) { + if (tmp->isPlayable ()) + return tmp; + NodePtr tmp2 = findDepthFirst (tmp->firstChild ()); + if (tmp2) + return tmp2; + } + return NodePtr (); +} + +bool Source::requestPlayURL (NodePtr mrl) { + //kdDebug() << "Source::requestPlayURL " << mrl->mrl ()->src << endl; + if (m_player->process ()->state () > Process::Ready) { + if (m_player->process ()->mrl () == mrl->mrl ()->linkNode ()) + return true; + m_back_request = mrl; // still playing, schedule it + m_player->process ()->stop (); + } else { + if (mrl->mrl ()->view_mode == Mrl::SingleMode) + m_current = mrl; + else + m_back_request = mrl; + m_player->updateTree (); + QTimer::singleShot (0, this, SLOT (playCurrent ())); + } + m_player->setProcess (mrl->mrl ()); + return true; +} + +bool Source::resolveURL (NodePtr) { + return true; +} + +void Source::setTimeout (int ms) { + //kdDebug () << "Source::setTimeout " << ms << endl; + if (m_doc_timer) + killTimer (m_doc_timer); + m_doc_timer = ms > -1 ? startTimer (ms) : 0; +} + +void Source::timerEvent (QTimerEvent * e) { + if (e->timerId () == m_doc_timer && m_document && m_document->active ()) + m_document->document ()->timer (); // will call setTimeout() + else + killTimer (e->timerId ()); +} + +bool Source::setCurrent (NodePtr mrl) { + m_current = mrl; + return true; +} + +void Source::stateElementChanged (Node * elm, Node::State os, Node::State ns) { + //kdDebug() << "Source::stateElementChanged " << elm->nodeName () << " state:" << (int) elm->state << " cur isPlayable:" << (m_current && m_current->isPlayable ()) << " elm==linkNode:" << (m_current && elm == m_current->mrl ()->linkNode ()) << " p state:" << m_player->process ()->state () << endl; + if (ns == Node::state_deactivated && elm == m_document && !m_back_request) { + emit endOfPlayItems (); // played all items + } else if ((ns == Node::state_deactivated || ns == Node::state_finished) && + m_player->process ()->mrl() && + elm == m_player->process ()->mrl ()->mrl ()->linkNode ()) { + if (m_player->process ()->state () > Process::Ready) + //a SMIL movies stopped by SMIL events rather than movie just ending + m_player->process ()->stop (); + if (m_player->view ()) // move away the video widget + QTimer::singleShot (0, m_player->view (), SLOT (updateLayout ())); + } else if ((ns == Node::state_deferred || + (os == Node::state_deferred && ns > Node::state_deferred)) && + elm == m_document) { + m_player->process ()->pause (); + } else if (ns == Node::state_activated && + elm->isPlayable () && + elm->mrl ()->view_mode == Mrl::SingleMode) { + Node *p = elm->parentNode(); + if (!p || !p->mrl () || p->mrl ()->view_mode == Mrl::SingleMode) + // make sure we don't set current to nested document + m_current = elm; + } + if (elm->expose ()) { + if (ns == Node::state_activated || ns == Node::state_deactivated) + m_player->updateTree (); + else if (ns == Node::state_began || os == Node::state_began) + m_player->updateTree (false); + } +} + +SurfacePtr Source::getSurface (NodePtr n) { + if (m_player->view ()) + return static_cast (m_player->view())->viewArea()->getSurface(n); + return 0L; +} + +void Source::setInfoMessage (const QString & msg) { + m_player->updateInfo (msg); +} + +void Source::bitRates (int & preferred, int & maximal) { + preferred = 1024 * m_player->settings ()->prefbitrate; + maximal= 1024 * m_player->settings ()->maxbitrate; +} + +void Source::insertURL (NodePtr node, const QString & mrl, const QString & title) { + if (!node || !node->mrl ()) // this should always be false + return; + QString cur_url = node->mrl ()->absolutePath (); + KURL url (cur_url, mrl); + kdDebug() << "Source::insertURL " << KURL (cur_url) << " " << url << endl; + if (!url.isValid ()) + kdError () << "try to append non-valid url" << endl; + else if (KURL (cur_url) == url) + kdError () << "try to append url to itself" << endl; + else { + int depth = 0; // cache this? + for (NodePtr e = node; e->parentNode (); e = e->parentNode ()) + ++depth; + if (depth < 40) { + node->appendChild (new GenericURL (m_document, KURL::decode_string (url.url ()), title.isEmpty() ? KURL::decode_string (mrl) : title)); + m_player->updateTree (); + } else + kdError () << "insertURL exceeds depth limit" << endl; + } +} + +void Source::play () { + m_player->updateTree (); + QTimer::singleShot (0, m_player, SLOT (play ())); + //printTree (m_document); +} + +void Source::backward () { + if (m_document->hasChildNodes ()) { + m_back_request = m_current; + if (!m_back_request || m_back_request == m_document) { + m_back_request = m_document->lastChild (); + while (m_back_request->lastChild () && !m_back_request->isPlayable ()) + m_back_request = m_back_request->lastChild (); + if (m_back_request->isPlayable ()) + return; + } + while (m_back_request && m_back_request != m_document) { + if (m_back_request->previousSibling ()) { + m_back_request = m_back_request->previousSibling (); + NodePtr e = findDepthFirst (m_back_request); // lastDepth.. + if (e) { + m_back_request = e; + if (m_player->playing ()) + m_player->process ()->stop (); + else if (m_current) { + m_document->reset (); + m_current = e; + QTimer::singleShot (0, this, SLOT (playCurrent ())); + } + return; + } + } else + m_back_request = m_back_request->parentNode (); + } + m_back_request = 0L; + } else + m_player->process ()->seek (-1 * m_player->settings ()->seektime * 10, false); +} + +void Source::forward () { + if (m_document->hasChildNodes ()) { + if (m_player->playing ()) + m_player->process ()->stop (); + else if (m_current) + m_current->finish (); + } else + m_player->process ()->seek (m_player->settings()->seektime * 10, false); +} + +void Source::jump (NodePtr e) { + if (e->isPlayable ()) { + if (m_player->playing ()) { + m_back_request = e; + m_player->process ()->stop (); + } else { + if (m_current) + m_document->reset (); + m_current = e; + QTimer::singleShot (0, this, SLOT (playCurrent ())); + } + } else + m_player->updateTree (); +} + +NodePtr Source::document () { + if (!m_document) + m_document = new Document (QString (), this); + return m_document; +} + +NodePtr Source::root () { + return document (); +} + +bool Source::processOutput (const QString &) { + return false; +} + +QString Source::filterOptions () { + Settings* m_settings = m_player->settings (); + QString PPargs (""); + if (m_settings->postprocessing) + { + if (m_settings->pp_default) + PPargs = "-vf pp=de"; + else if (m_settings->pp_fast) + PPargs = "-vf pp=fa"; + else if (m_settings->pp_custom) { + PPargs = "-vf pp="; + if (m_settings->pp_custom_hz) { + PPargs += "hb"; + if (m_settings->pp_custom_hz_aq && \ + m_settings->pp_custom_hz_ch) + PPargs += ":ac"; + else if (m_settings->pp_custom_hz_aq) + PPargs += ":a"; + else if (m_settings->pp_custom_hz_ch) + PPargs += ":c"; + PPargs += '/'; + } + if (m_settings->pp_custom_vt) { + PPargs += "vb"; + if (m_settings->pp_custom_vt_aq && \ + m_settings->pp_custom_vt_ch) + PPargs += ":ac"; + else if (m_settings->pp_custom_vt_aq) + PPargs += ":a"; + else if (m_settings->pp_custom_vt_ch) + PPargs += ":c"; + PPargs += '/'; + } + if (m_settings->pp_custom_dr) { + PPargs += "dr"; + if (m_settings->pp_custom_dr_aq && \ + m_settings->pp_custom_dr_ch) + PPargs += ":ac"; + else if (m_settings->pp_custom_dr_aq) + PPargs += ":a"; + else if (m_settings->pp_custom_dr_ch) + PPargs += ":c"; + PPargs += '/'; + } + if (m_settings->pp_custom_al) { + PPargs += "al"; + if (m_settings->pp_custom_al_f) + PPargs += ":f"; + PPargs += '/'; + } + if (m_settings->pp_custom_tn) { + PPargs += "tn"; + /*if (1 <= m_settings->pp_custom_tn_s <= 3){ + PPargs += ":"; + PPargs += m_settings->pp_custom_tn_s; + }*/ //disabled 'cos this is wrong + PPargs += '/'; + } + if (m_settings->pp_lin_blend_int) { + PPargs += "lb"; + PPargs += '/'; + } + if (m_settings->pp_lin_int) { + PPargs += "li"; + PPargs += '/'; + } + if (m_settings->pp_cub_int) { + PPargs += "ci"; + PPargs += '/'; + } + if (m_settings->pp_med_int) { + PPargs += "md"; + PPargs += '/'; + } + if (m_settings->pp_ffmpeg_int) { + PPargs += "fd"; + PPargs += '/'; + } + } + if (PPargs.endsWith("/")) + PPargs.truncate(PPargs.length()-1); + } + return PPargs; +} + +bool Source::hasLength () { + return true; +} + +bool Source::isSeekable () { + return true; +} + +void Source::setIdentified (bool b) { + //kdDebug () << "Source::setIdentified " << m_identified << b <viewer ()) return; + Recorder *rec = dynamic_cast (p); + if (rec && !rec->recordURL ().isEmpty ()) { + kdDebug () << "recordState " << statemap[olds] << " -> " << statemap[news] << endl; + m_player->updateStatus (i18n ("Recorder %1 %2").arg (p->name ()).arg (statemap[news])); + p->viewer ()->view ()->controlPanel ()->setRecording (news > Process::Ready); + if (news == Process::Ready) { + if (olds > Process::Ready) { + p->quit (); + } else { + NodePtr n = current (); + if (!n) + n = document (); + p->play (this, n); + } + } else if (news > Process::Ready) { + emit startRecording (); + } else if (news == Process::NotRunning) + emit stopRecording (); + } else { + p->viewer()->view()->controlPanel()->setPlaying(news > Process::Ready); + kdDebug () << "processState " << statemap[olds] << " -> " << statemap[news] << endl; + m_player->updateStatus (i18n ("Player %1 %2").arg (p->name ()).arg (statemap[news])); + if (!p->mrl () && news > Process::Ready) { + p->stop (); // reschedule for Ready state + } else if (news == Process::Playing) { + if (p->mrl ()->state == Element::state_deferred) + p->mrl ()->undefer (); + p->viewer ()->view ()->playingStart (); + emit startPlaying (); + } else if (news == Process::NotRunning) { + if (hasLength () && position () > length ()) + setLength (m_document, position ()); + setPosition (0); + if (p == m_player->process ()) + emit stopPlaying (); + // else changed process + } else if (news == Process::Ready) { + if (olds > Process::Ready) { + NodePtr node = p->mrl (); // p->mrl is weak, needs check + Mrl * mrl = node ? node->mrl () : 0L; + if (m_back_request && m_back_request->isPlayable ()) { + if (m_back_request->mrl ()->view_mode == Mrl::SingleMode) + // jump in pl + m_current = m_back_request; + else if (mrl) + // overlapping SMIL audio/video + mrl->endOfFile (); + if (m_current->id >= SMIL::id_node_first && + m_current->id < SMIL::id_node_last) { + playCurrent (); // just play back_request + } else { + // sanitize pl having all parents of current activated + m_document->reset (); // deactivate everything + for (NodePtr p = m_current->parentNode(); p; p = p->parentNode()) + p->state = Element::state_activated; + m_current->activate (); // calls requestPlayUrl + } + m_back_request = 0L; + } else if(mrl) + { + mrl->endOfFile (); // set node to finished + } + if (m_player->view() && + (!mrl || mrl->view_mode != Mrl::WindowMode)) + static_cast(m_player->view())->viewArea()->repaint(); + } else + QTimer::singleShot (0, this, SLOT (playCurrent ())); + } else if (news == Process::Buffering) { + if (p->mrl ()->mrl ()->view_mode != Mrl::SingleMode) + p->mrl ()->defer (); // paused the SMIL + } + } +} + +QString Source::plugin (const QString &mime) const { + m_player->config ()->setGroup (mime); + return m_player->config ()->readEntry ("plugin", "" ); +} + +QString Source::prettyName () { + return i18n ("Unknown"); +} + +//----------------------------------------------------------------------------- + +URLSource::URLSource (PartBase * player, const KURL & url) + : Source (i18n ("URL"), player, "urlsource"), activated (false) { + setURL (url); + //kdDebug () << "URLSource::URLSource" << endl; +} + +URLSource::~URLSource () { + //kdDebug () << "URLSource::~URLSource" << endl; +} + +void URLSource::init () { + Source::init (); +} + +void URLSource::dimensions (int & w, int & h) { + if (!m_player->mayResize () && m_player->view ()) { + w = static_cast (m_player->view ())->viewer ()->width (); + h = static_cast (m_player->view ())->viewer ()->height (); + } else + Source::dimensions (w, h); + +} + +bool URLSource::hasLength () { + return !!length (); +} + +KDE_NO_EXPORT void URLSource::activate () { + if (activated) + return; + activated = true; + if (url ().isEmpty () && (!m_document || !m_document->hasChildNodes ())) { + m_player->updateTree (); + return; + } + if (m_auto_play) + play (); +} + +KDE_NO_EXPORT void URLSource::stopResolving () { + if (m_resolve_info) { + for (SharedPtr ri = m_resolve_info; ri; ri = ri->next) + ri->job->kill (); + m_resolve_info = 0L; + m_player->updateStatus (i18n ("Disconnected")); + m_player->setLoaded (100); + } +} + +void URLSource::reset () { + stopResolving (); + Source::reset (); +} + +void URLSource::forward () { + stopResolving (); + Source::forward (); +} + +void URLSource::backward () { + stopResolving (); + Source::backward (); +} + +void URLSource::jump (NodePtr e) { + stopResolving (); + Source::jump (e); +} + +void URLSource::deactivate () { + activated = false; + reset (); + getSurface (0L); +} + +QString URLSource::prettyName () { + if (m_url.isEmpty ()) + return i18n ("URL"); + if (m_url.url ().length () > 50) { + QString newurl = m_url.protocol () + QString ("://"); + if (m_url.hasHost ()) + newurl += m_url.host (); + if (m_url.port ()) + newurl += QString (":%1").arg (m_url.port ()); + QString file = m_url.fileName (); + int len = newurl.length () + file.length (); + KURL path = KURL (m_url.directory ()); + bool modified = false; + while (path.url ().length () + len > 50 && path != path.upURL ()) { + path = path.upURL (); + modified = true; + } + QString dir = path.directory (); + if (!dir.endsWith (QString ("/"))) + dir += '/'; + if (modified) + dir += QString (".../"); + newurl += dir + file; + return i18n ("URL - %1").arg (newurl); + } + return i18n ("URL - %1").arg (m_url.prettyURL ()); +} + +static bool isPlayListMime (const QString & mime) { + QString m (mime); + int plugin_pos = m.find ("-plugin"); + if (plugin_pos > 0) + m.truncate (plugin_pos); + const char * mimestr = m.ascii (); + return mimestr && (!strcmp (mimestr, "audio/mpegurl") || + !strcmp (mimestr, "audio/x-mpegurl") || + !strncmp (mimestr, "video/x-ms", 10) || + !strncmp (mimestr, "audio/x-ms", 10) || + //!strcmp (mimestr, "video/x-ms-wmp") || + //!strcmp (mimestr, "video/x-ms-asf") || + //!strcmp (mimestr, "video/x-ms-wmv") || + //!strcmp (mimestr, "video/x-ms-wvx") || + //!strcmp (mimestr, "video/x-msvideo") || + !strcmp (mimestr, "audio/x-scpls") || + !strcmp (mimestr, "audio/x-pn-realaudio") || + !strcmp (mimestr, "audio/vnd.rn-realaudio") || + !strcmp (mimestr, "audio/m3u") || + !strcmp (mimestr, "audio/x-m3u") || + !strncmp (mimestr, "text/", 5) || + (!strncmp (mimestr, "application/", 12) && + strstr (mimestr + 12,"+xml")) || + !strncasecmp (mimestr, "application/smil", 16) || + !strncasecmp (mimestr, "application/xml", 15) || + //!strcmp (mimestr, "application/rss+xml") || + //!strcmp (mimestr, "application/atom+xml") || + !strcmp (mimestr, "application/x-mplayer2")); +} + +KDE_NO_EXPORT void URLSource::read (NodePtr root, QTextStream & textstream) { + QString line; + do { + line = textstream.readLine (); + } while (!line.isNull () && line.stripWhiteSpace ().isEmpty ()); + if (!line.isNull ()) { + NodePtr cur_elm = root; + if (cur_elm->isPlayable ()) + cur_elm = cur_elm->mrl ()->linkNode (); + if (cur_elm->mrl ()->mimetype == QString ("audio/x-scpls")) { + bool groupfound = false; + int nr = -1; + struct Entry { + QString url, title; + } * entries = 0L; + do { + line = line.stripWhiteSpace (); + if (!line.isEmpty ()) { + if (line.startsWith (QString ("[")) && line.endsWith (QString ("]"))) { + if (!groupfound && line.mid (1, line.length () - 2).stripWhiteSpace () == QString ("playlist")) + groupfound = true; + else + break; + kdDebug () << "Group found: " << line << endl; + } else if (groupfound) { + int eq_pos = line.find (QChar ('=')); + if (eq_pos > 0) { + if (line.lower ().startsWith (QString ("numberofentries"))) { + nr = line.mid (eq_pos + 1).stripWhiteSpace ().toInt (); + kdDebug () << "numberofentries : " << nr << endl; + if (nr > 0 && nr < 1024) + entries = new Entry[nr]; + else + nr = 0; + } else if (nr > 0) { + QString ll = line.lower (); + if (ll.startsWith (QString ("file"))) { + int i = line.mid (4, eq_pos-4).toInt (); + if (i > 0 && i <= nr) + entries[i-1].url = line.mid (eq_pos + 1).stripWhiteSpace (); + } else if (ll.startsWith (QString ("title"))) { + int i = line.mid (5, eq_pos-5).toInt (); + if (i > 0 && i <= nr) + entries[i-1].title = line.mid (eq_pos + 1).stripWhiteSpace (); + } + } + } + } + } + line = textstream.readLine (); + } while (!line.isNull ()); + for (int i = 0; i < nr; i++) + if (!entries[i].url.isEmpty ()) + cur_elm->appendChild (new GenericURL (m_document, KURL::decode_string (entries[i].url), entries[i].title)); + delete [] entries; + } else if (line.stripWhiteSpace ().startsWith (QChar ('<'))) { + readXML (cur_elm, textstream, line); + //cur_elm->normalize (); + if (m_document && m_document->firstChild ()) { + // SMIL documents have set its size of root-layout + Mrl * mrl = m_document->firstChild ()->mrl (); + if (mrl) + Source::setDimensions (m_document->firstChild (), mrl->width, mrl->height); + } + } else if (line.lower () != QString ("[reference]")) do { + QString mrl = line.stripWhiteSpace (); + if (line == QString ("--stop--")) + break; + if (mrl.lower ().startsWith (QString ("asf "))) + mrl = mrl.mid (4).stripWhiteSpace (); + if (!mrl.isEmpty () && !mrl.startsWith (QChar ('#'))) + cur_elm->appendChild (new GenericURL (m_document, mrl)); + line = textstream.readLine (); + } while (!line.isNull ()); /* TODO && m_document.size () < 1024 / * support 1k entries * /);*/ + } +} + +KDE_NO_EXPORT void URLSource::kioData (KIO::Job * job, const QByteArray & d) { + SharedPtr rinfo = m_resolve_info; + while (rinfo && rinfo->job != job) + rinfo = rinfo->next; + if (!rinfo) { + kdWarning () << "Spurious kioData" << endl; + return; + } + int size = rinfo->data.size (); + int newsize = size + d.size (); + if (!size) { // first data + int accuraty = 0; + KMimeType::Ptr mime = KMimeType::findByContent (d, &accuraty); + if (!mime || + !mime->name ().startsWith (QString ("text/")) || + (newsize > 4 && !strncmp (d.data (), "RIFF", 4))) { + newsize = 0; + kdDebug () << "URLSource::kioData: " << mime->name () << accuraty << endl; + } + } + //kdDebug () << "URLSource::kioData: " << newsize << endl; + if (newsize <= 0 || newsize > 200000) { + rinfo->data.resize (0); + rinfo->job->kill (false); + m_player->setLoaded (100); + } else { + rinfo->data.resize (newsize); + memcpy (rinfo->data.data () + size, d.data (), newsize - size); + m_player->setLoaded (++rinfo->progress); + } +} + +KDE_NO_EXPORT void URLSource::kioMimetype (KIO::Job * job, const QString & mimestr) { + SharedPtr rinfo = m_resolve_info; + while (rinfo && rinfo->job != job) + rinfo = rinfo->next; + if (!rinfo) { + kdWarning () << "Spurious kioData" << endl; + return; + } + if (rinfo->resolving_mrl) + rinfo->resolving_mrl->mrl ()->mimetype = mimestr; + if (!rinfo->resolving_mrl || !isPlayListMime (mimestr)) + job->kill (false); +} + +KDE_NO_EXPORT void URLSource::kioResult (KIO::Job * job) { + SharedPtr previnfo, rinfo = m_resolve_info; + while (rinfo && rinfo->job != job) { + previnfo = rinfo; + rinfo = rinfo->next; + } + if (!rinfo) { + kdWarning () << "Spurious kioData" << endl; + return; + } + m_player->updateStatus (""); + m_player->setLoaded (100); + if (previnfo) + previnfo->next = rinfo->next; + else + m_resolve_info = rinfo->next; + QTextStream textstream (rinfo->data, IO_ReadOnly); + if (rinfo->resolving_mrl) { + if (isPlayListMime (rinfo->resolving_mrl->mrl ()->mimetype)) + read (rinfo->resolving_mrl, textstream); + rinfo->resolving_mrl->mrl ()->resolved = true; + rinfo->resolving_mrl->undefer (); + } + static_cast (m_player->view())->controlPanel()->setPlaying (false); +} + +void URLSource::playCurrent () { + Mrl *mrl = m_back_request + ? m_back_request->mrl () + : m_current ? m_current->mrl () : NULL; + if (mrl && mrl->active () && (!mrl->isPlayable () || !mrl->resolved)) + // an async playCurrent() call (eg. backend is up & running), ignore + return; + Source::playCurrent (); +} + +void URLSource::play () { + Source::play (); +} + +bool URLSource::requestPlayURL (NodePtr mrl) { + if (m_document.ptr () != mrl->mrl ()->linkNode ()) { + KURL base = m_document->mrl ()->src; + KURL dest = mrl->mrl ()->linkNode ()->absolutePath (); + // check if some remote playlist tries to open something local, but + // do ignore unknown protocols because there are so many and we only + // want to cache local ones. + if ( +#if 0 + !KProtocolInfo::protocolClass (dest.protocol ()).isEmpty () && +#else + dest.isLocalFile () && +#endif + !kapp->authorizeURLAction ("redirect", base, dest)) { + kdWarning () << "requestPlayURL from document " << base << " to play " << dest << " is not allowed" << endl; + return false; + } + } + return Source::requestPlayURL (mrl); +} + +void URLSource::setURL (const KURL & url) { + Source::setURL (url); + Mrl *mrl = document ()->mrl (); + if (!url.isEmpty () && url.isLocalFile () && mrl->mimetype.isEmpty ()) { + KMimeType::Ptr mimeptr = KMimeType::findByURL (url); + if (mimeptr) + mrl->mimetype = mimeptr->name (); + } +} + +bool URLSource::resolveURL (NodePtr m) { + Mrl * mrl = m->mrl (); + if (!mrl || mrl->src.isEmpty ()) + return true; + int depth = 0; + for (NodePtr e = m->parentNode (); e; e = e->parentNode ()) + ++depth; + if (depth > 40) + return true; + KURL url (mrl->absolutePath ()); + QString mimestr = mrl->mimetype; + if (mimestr == "application/x-shockwave-flash" || + mimestr == "application/futuresplash") + return true; // FIXME + bool maybe_playlist = isPlayListMime (mimestr); + kdDebug () << "resolveURL " << mrl->absolutePath () << " " << mimestr << endl; + if (url.isLocalFile ()) { + QFile file (url.path ()); + if (!file.exists ()) { + kdWarning () << "resolveURL " << url.path() << " not found" << endl; + return true; + } + if (mimestr.isEmpty ()) { + KMimeType::Ptr mimeptr = KMimeType::findByURL (url); + if (mimeptr) { + mrl->mimetype = mimeptr->name (); + maybe_playlist = isPlayListMime (mrl->mimetype); // get new mime + } + } + if (maybe_playlist && file.size () < 2000000 && file.open (IO_ReadOnly)) { + char databuf [512]; + int nr_bytes = file.readBlock (databuf, 512); + if (nr_bytes > 3) { + int accuraty = 0; + KMimeType::Ptr mime = KMimeType::findByContent (QCString (databuf, nr_bytes), &accuraty); + if ((mime && !mime->name().startsWith (QString("text/"))) || + !strncmp (databuf, "RIFF", 4)) { + return true; + } + kdDebug () << "mime: " << (mime ? mime->name (): QString("null")) << endl; + } + file.reset (); + QTextStream textstream (&file); + read (m, textstream); + } + } else if ((maybe_playlist && + url.protocol ().compare (QString ("mms")) && + url.protocol ().compare (QString ("rtsp")) && + url.protocol ().compare (QString ("rtp"))) || + (mimestr.isEmpty () && + (url.protocol ().startsWith (QString ("http")) || + url.protocol () == QString::fromLatin1 ("media") || + url.protocol () == QString::fromLatin1 ("remote")))) { + KIO::Job * job = KIO::get (url, false, false); + job->addMetaData ("PropagateHttpHeader", "true"); + job->addMetaData ("errorPage", "false"); + m_resolve_info = new ResolveInfo (m, job, m_resolve_info); + connect (m_resolve_info->job, SIGNAL(data(KIO::Job*,const QByteArray&)), + this, SLOT (kioData (KIO::Job *, const QByteArray &))); + //connect( m_job, SIGNAL(connected(KIO::Job*)), + // this, SLOT(slotConnected(KIO::Job*))); + connect(m_resolve_info->job, SIGNAL(mimetype(KIO::Job*,const QString&)), + this, SLOT (kioMimetype (KIO::Job *, const QString &))); + connect (m_resolve_info->job, SIGNAL (result (KIO::Job *)), + this, SLOT (kioResult (KIO::Job *))); + static_cast (m_player->view ())->controlPanel ()->setPlaying (true); + m_player->updateStatus (i18n ("Connecting")); + m_player->setLoaded (0); + return false; // wait for result .. + } + return true; +} + +//----------------------------------------------------------------------------- + +namespace KMPlayer { + static KStaticDeleter dataCacheDeleter; + static DataCache * memory_cache; +} + +void DataCache::add (const QString & url, const QByteArray & data) { + QByteArray bytes; + bytes.duplicate (data); + cache_map.insert (url, bytes); + preserve_map.erase (url); + emit preserveRemoved (url); +} + +bool DataCache::get (const QString & url, QByteArray & data) { + DataMap::const_iterator it = cache_map.find (url); + if (it != cache_map.end ()) { + data.duplicate (it.data ()); + return true; + } + return false; +} + +bool DataCache::preserve (const QString & url) { + PreserveMap::const_iterator it = preserve_map.find (url); + if (it == preserve_map.end ()) { + preserve_map.insert (url, true); + return true; + } + return false; +} + +bool DataCache::isPreserved (const QString & url) { + return preserve_map.find (url) != preserve_map.end (); +} + +bool DataCache::unpreserve (const QString & url) { + const PreserveMap::iterator it = preserve_map.find (url); + if (it == preserve_map.end ()) + return false; + preserve_map.erase (it); + emit preserveRemoved (url); + return true; +} + +RemoteObjectPrivate::RemoteObjectPrivate (RemoteObject * r) + : job (0L), remote_object (r), preserve_wait (false) { + if (!memory_cache) + dataCacheDeleter.setObject (memory_cache, new DataCache); +} + +RemoteObjectPrivate::~RemoteObjectPrivate () { + clear (); +} + +KDE_NO_EXPORT bool RemoteObjectPrivate::download (const QString & str) { + url = str; + KURL kurl (str); + if (kurl.isLocalFile ()) { + QFile file (kurl.path ()); + if (file.exists () && file.open (IO_ReadOnly)) { + data = file.readAll (); + file.close (); + } + remote_object->remoteReady (data); + return true; + } + if (memory_cache->get (str, data)) { + //kdDebug () << "download found in cache " << str << endl; + remote_object->remoteReady (data); + return true; + } + if (memory_cache->preserve (str)) { + //kdDebug () << "downloading " << str << endl; + job = KIO::get (kurl, false, false); + connect (job, SIGNAL (data (KIO::Job *, const QByteArray &)), + this, SLOT (slotData (KIO::Job *, const QByteArray &))); + connect (job, SIGNAL (result (KIO::Job *)), + this, SLOT (slotResult (KIO::Job *))); + connect (job, SIGNAL (mimetype (KIO::Job *, const QString &)), + this, SLOT (slotMimetype (KIO::Job *, const QString &))); + } else { + //kdDebug () << "download preserved " << str << endl; + connect (memory_cache, SIGNAL (preserveRemoved (const QString &)), + this, SLOT (cachePreserveRemoved (const QString &))); + preserve_wait = true; + } + return false; +} + +KDE_NO_EXPORT void RemoteObjectPrivate::clear () { + if (job) { + job->kill (); // quiet, no result signal + job = 0L; + memory_cache->unpreserve (url); + } else if (preserve_wait) { + disconnect (memory_cache, SIGNAL (preserveRemoved (const QString &)), + this, SLOT (cachePreserveRemoved (const QString &))); + preserve_wait = false; + } +} + +KDE_NO_EXPORT void RemoteObjectPrivate::slotResult (KIO::Job * kjob) { + if (!kjob->error ()) + memory_cache->add (url, data); + else + data.resize (0); + job = 0L; // signal KIO::Job::result deletes itself + remote_object->remoteReady (data); +} + +KDE_NO_EXPORT +void RemoteObjectPrivate::cachePreserveRemoved (const QString & str) { + if (str == url && !memory_cache->isPreserved (str)) { + preserve_wait = false; + disconnect (memory_cache, SIGNAL (preserveRemoved (const QString &)), + this, SLOT (cachePreserveRemoved (const QString &))); + download (str); + } +} + +KDE_NO_EXPORT +void RemoteObjectPrivate::slotData (KIO::Job*, const QByteArray& qb) { + if (qb.size ()) { + int old_size = data.size (); + data.resize (old_size + qb.size ()); + memcpy (data.data () + old_size, qb.data (), qb.size ()); + } +} + +KDE_NO_EXPORT +void RemoteObjectPrivate::slotMimetype (KIO::Job *, const QString & m) { + mime = m; +} + +KDE_NO_CDTOR_EXPORT RemoteObject::RemoteObject () + : d (new RemoteObjectPrivate (this)) {} + +KDE_NO_CDTOR_EXPORT RemoteObject::~RemoteObject () { + delete d; +} + +/** + * abort previous wget job + */ +KDE_NO_EXPORT void RemoteObject::killWGet () { + d->clear (); // assume data is invalid +} + +/** + * Gets contents from url and puts it in m_data + */ +KDE_NO_EXPORT bool RemoteObject::wget (const QString & url) { + clear (); + return d->download (url); +} + +KDE_NO_EXPORT QString RemoteObject::mimetype () { + if (d->data.size () > 0 && d->mime.isEmpty ()) { + int accuraty; + KMimeType::Ptr mime = KMimeType::findByContent (d->data, &accuraty); + if (mime) + d->mime = mime->name (); + } + return d->mime; +} + +KDE_NO_EXPORT void RemoteObject::clear () { + killWGet (); + d->url.truncate (0); + d->mime.truncate (0); + d->data.resize (0); +} + +KDE_NO_EXPORT bool RemoteObject::downloading () const { + return !!d->job; +} + +//----------------------------------------------------------------------------- + +#include "kmplayerpartbase.moc" +#include "kmplayersource.moc" -- cgit v1.2.3