/** * 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 TQString& _url); TQString currentTitle() const; TQString currentURL() const; private: PartBase * m_player; }; class KMPLAYER_NO_EXPORT BookmarkManager : public KBookmarkManager { public: BookmarkManager (const TQString &); }; } // namespace using namespace KMPlayer; KDE_NO_CDTOR_EXPORT BookmarkOwner::BookmarkOwner (PartBase * player) : m_player (player) {} KDE_NO_EXPORT void BookmarkOwner::openBookmarkURL (const TQString & url) { m_player->openURL (KURL (url)); } KDE_NO_EXPORT TQString BookmarkOwner::currentTitle () const { return m_player->source ()->prettyName (); } KDE_NO_EXPORT TQString BookmarkOwner::currentURL () const { return m_player->source ()->url ().url (); } inline BookmarkManager::BookmarkManager(const TQString & bmfile) : KBookmarkManager (bmfile, false) { } //----------------------------------------------------------------------------- PartBase::PartBase (TQWidget * wparent, const char *wname, TQObject * 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); TQString bmfile = locate ("data", "kmplayer/bookmarks.xml"); TQString localbmfile = locateLocal ("data", "kmplayer/bookmarks.xml"); if (localbmfile != bmfile) { kdDebug () << "cp " << bmfile << " " << localbmfile << endl; KProcess p; p << "/bin/cp" << TQString(TQFile::encodeName (bmfile)) << TQString(TQFile::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 TQString & t, const TQString & 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, TQT_SIGNAL(configChanged()), this, TQT_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, TQT_SIGNAL (urlDropped (const KURL::List &)), this, TQT_SLOT (openURL (const KURL::List &))); connectPlaylist (m_view->playList ()); connectInfoPanel (m_view->infoPanel ()); new KAction (i18n ("Edit playlist &item"), 0, 0, TQT_TQOBJECT(m_view->playList ()), TQT_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), TQT_SIGNAL (clicked ()), this, TQT_SLOT (showPlayListWindow ())); connect (panel->button (ControlPanel::button_back), TQT_SIGNAL (clicked ()), this, TQT_SLOT (back ())); connect (panel->button (ControlPanel::button_play), TQT_SIGNAL (clicked ()), this, TQT_SLOT (play ())); connect (panel->button (ControlPanel::button_forward), TQT_SIGNAL (clicked ()), this, TQT_SLOT (forward ())); connect (panel->button (ControlPanel::button_pause), TQT_SIGNAL (clicked ()), this, TQT_SLOT (pause ())); connect (panel->button (ControlPanel::button_stop), TQT_SIGNAL (clicked ()), this, TQT_SLOT (stop ())); connect (panel->button (ControlPanel::button_record), TQT_SIGNAL (clicked()), this, TQT_SLOT (record())); connect (panel->volumeBar (), TQT_SIGNAL (volumeChanged (int)), this, TQT_SLOT (volumeChanged (int))); connect (panel->positionSlider (), TQT_SIGNAL (valueChanged (int)), this, TQT_SLOT (positionValueChanged (int))); connect (panel->positionSlider (), TQT_SIGNAL (sliderPressed()), this, TQT_SLOT (posSliderPressed())); connect (panel->positionSlider (), TQT_SIGNAL (sliderReleased()), this, TQT_SLOT (posSliderReleased())); connect (this, TQT_SIGNAL (positioned (int, int)), panel, TQT_SLOT (setPlayingProgress (int, int))); connect (this, TQT_SIGNAL (loading(int)), panel, TQT_SLOT(setLoadingProgress(int))); connect (panel->contrastSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (contrastValueChanged(int))); connect (panel->brightnessSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (brightnessValueChanged(int))); connect (panel->hueSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (hueValueChanged(int))); connect (panel->saturationSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (saturationValueChanged(int))); connect (this, TQT_SIGNAL (languagesUpdated(const TQStringList &, const TQStringList &)), panel, TQT_SLOT (setLanguages (const TQStringList &, const TQStringList &))); connect (panel->audioMenu (), TQT_SIGNAL (activated (int)), this, TQT_SLOT (audioSelected (int))); connect (panel->subtitleMenu (), TQT_SIGNAL (activated (int)), this, TQT_SLOT (subtitleSelected (int))); connect (this, TQT_SIGNAL (audioIsSelected (int)), panel, TQT_SLOT (selectAudioLanguage (int))); connect (this, TQT_SIGNAL (subtitleIsSelected (int)), panel, TQT_SLOT (selectSubtitle (int))); panel->popupMenu()->connectItem (ControlPanel::menu_fullscreen, this, TQT_SLOT (fullScreen ())); panel->popupMenu ()->connectItem (ControlPanel::menu_config, this, TQT_SLOT (showConfigDialog ())); panel->popupMenu ()->connectItem (ControlPanel::menu_video, m_view, TQT_SLOT(toggleVideoConsoleWindow())); panel->popupMenu ()->connectItem (ControlPanel::menu_playlist, m_view, TQT_SLOT (toggleShowPlaylist ())); connect (this, TQT_SIGNAL (statusUpdated (const TQString &)), panel->view (), TQT_SLOT (setStatusMessage (const TQString &))); //connect (panel (), TQT_SIGNAL (clicked ()), m_settings, TQT_SLOT (show ())); } void PartBase::connectPlaylist (PlayListView * playlist) { connect (playlist, TQT_SIGNAL (addBookMark (const TQString &, const TQString &)), this, TQT_SLOT (addBookMark (const TQString &, const TQString &))); connect (playlist, TQT_SIGNAL (executed (TQListViewItem *)), this, TQT_SLOT (playListItemExecuted (TQListViewItem *))); connect (playlist, TQT_SIGNAL (clicked (TQListViewItem *)), this, TQT_SLOT (playListItemClicked (TQListViewItem *))); connect (this, TQT_SIGNAL (treeChanged (int, NodePtr, NodePtr, bool, bool)), playlist, TQT_SLOT (updateTree (int, NodePtr, NodePtr, bool, bool))); connect (this, TQT_SIGNAL (treeUpdated ()), playlist, TQT_SLOT (triggerUpdate ())); } void PartBase::connectInfoPanel (InfoWindow * infopanel) { connect (this, TQT_SIGNAL (infoUpdated (const TQString &)), infopanel->view (), TQT_SLOT (setInfoMessage (const TQString &))); } 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 TQString 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 = TQString (i.data ()->name ()); break; } } else p = TQString (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 (); TQPopupMenu * 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 () != TQString ("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; TQPopupMenu * 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, TQT_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, TQT_SIGNAL(endOfPlayItems ()), this, TQT_SLOT(stop ())); disconnect (old_source, TQT_SIGNAL (dimensionsChanged ()), this, TQT_SLOT (sourceHasChangedAspects ())); disconnect (old_source, TQT_SIGNAL (startPlaying ()), this, TQT_SLOT (playingStarted ())); disconnect (old_source, TQT_SIGNAL (stopPlaying ()), this, TQT_SLOT (playingStopped ())); } if (source) { connect (source, TQT_SIGNAL (endOfPlayItems ()), this, TQT_SLOT (stop ())); connect (source, TQT_SIGNAL (dimensionsChanged ()), this, TQT_SLOT (sourceHasChangedAspects ())); connect (source, TQT_SIGNAL (startPlaying()), this, TQT_SLOT(playingStarted())); connect (source, TQT_SIGNAL (stopPlaying ()), this, TQT_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 (TQString ()); } disconnect (m_source, TQT_SIGNAL (startRecording ()), this, TQT_SLOT (recordingStarted ())); disconnect (this, TQT_SIGNAL (audioIsSelected (int)), m_source, TQT_SLOT (setAudioLang (int))); disconnect (this, TQT_SIGNAL (subtitleIsSelected (int)), m_source, TQT_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, TQT_SIGNAL(startRecording()), this,TQT_SLOT(recordingStarted())); connect (this, TQT_SIGNAL (audioIsSelected (int)), m_source, TQT_SLOT (setAudioLang (int))); connect (this, TQT_SIGNAL (subtitleIsSelected (int)), m_source, TQT_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) TQTimer::singleShot (0, m_source, TQT_SLOT (activate ())); updateTree (true, true); emit sourceChanged (old_source, m_source); } KDE_NO_EXPORT void PartBase::changeURL (const TQString & 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 (TQTimerEvent * 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 (TQListViewItem * item) { if (!item) return; PlayListItem * vi = static_cast (item); RootPlayListItem * ri = vi->playListView ()->rootItem (item); if (ri == item && vi->node) { TQString 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 (TQListViewItem * 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) { TQString 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") { TQString 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 TQString & msg) { emit infoUpdated (msg); } void PartBase::updateStatus (const TQString & msg) { emit statusUpdated (msg); } void PartBase::setLanguages (const TQStringList & al, const TQStringList & 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 (TQCursor (TQt::WaitCursor)); if (m_recorder->playing ()) { m_recorder->stop (); } else { m_settings->show ("RecordPage"); m_view->controlPanel ()->setRecording (false); } if (m_view) m_view->setCursor (TQCursor (TQt::ArrowCursor)); } void PartBase::play () { if (!m_process || !m_view) return; TQPushButton * pb = ::tqqt_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 TQListViewItem * 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 () { TQPushButton * b = m_view ? m_view->controlPanel ()->button (ControlPanel::button_stop) : 0L; if (b) { if (!b->isOn ()) b->toggle (); m_view->setCursor (TQCursor (TQt::WaitCursor)); } if (m_process) m_process->quit (); if (m_source) m_source->reset (); if (m_view) { m_view->setCursor (TQCursor (TQt::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; const TQSlider * posSlider = ::tqqt_cast (sender ()); 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) { TQSlider * slider = ::tqqt_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 TQString & name, PartBase * player, const char * n) : TQObject (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 TQStringList & alang, const TQStringList & 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, TQString off=TQString()) { if (!root) { kdDebug() << off << "[null]" << endl; return; } kdDebug() << off << root->nodeName() << " " << (Element*)root << (root->isPlayable() ? root->mrl ()->src : TQString ("-")) << endl; off += TQString (" "); 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 TQString & 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 (); } TQString Source::currentMrl () { Mrl * mrl = m_current ? m_current->mrl () : 0L; kdDebug() << "Source::currentMrl " << (m_current ? m_current->nodeName():"") << " src:" << (mrl ? mrl->absolutePath () : TQString ()) << endl; return mrl ? mrl->absolutePath () : TQString (); } void Source::playCurrent () { TQString 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 (); TQTimer::singleShot (0, this, TQT_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 (TQTimerEvent * 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 TQTimer::singleShot (0, m_player->view (), TQT_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 TQString & 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 TQString & mrl, const TQString & title) { if (!node || !node->mrl ()) // this should always be false return; TQString 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 (); TQTimer::singleShot (0, m_player, TQT_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; TQTimer::singleShot (0, this, TQT_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; TQTimer::singleShot (0, this, TQT_SLOT (playCurrent ())); } } else m_player->updateTree (); } NodePtr Source::document () { if (!m_document) m_document = new Document (TQString (), this); return m_document; } NodePtr Source::root () { return document (); } bool Source::processOutput (const TQString &) { return false; } TQString Source::filterOptions () { Settings* m_settings = m_player->settings (); TQString 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 TQTimer::singleShot (0, this, TQT_SLOT (playCurrent ())); } else if (news == Process::Buffering) { if (p->mrl ()->mrl ()->view_mode != Mrl::SingleMode) p->mrl ()->defer (); // paused the SMIL } } } TQString Source::plugin (const TQString &mime) const { m_player->config ()->setGroup (mime); return m_player->config ()->readEntry ("plugin", "" ); } TQString 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); } TQString URLSource::prettyName () { if (m_url.isEmpty ()) return i18n ("URL"); if (m_url.url ().length () > 50) { TQString newurl = m_url.protocol () + TQString ("://"); if (m_url.hasHost ()) newurl += m_url.host (); if (m_url.port ()) newurl += TQString (":%1").arg (m_url.port ()); TQString 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; } TQString dir = path.directory (); if (!dir.endsWith (TQString ("/"))) dir += '/'; if (modified) dir += TQString (".../"); newurl += dir + file; return i18n ("URL - %1").arg (newurl); } return i18n ("URL - %1").arg (m_url.prettyURL ()); } static bool isPlayListMime (const TQString & mime) { TQString 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, TQTextStream & textstream) { TQString 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 == TQString ("audio/x-scpls")) { bool groupfound = false; int nr = -1; struct Entry { TQString url, title; } * entries = 0L; do { line = line.stripWhiteSpace (); if (!line.isEmpty ()) { if (line.startsWith (TQString ("[")) && line.endsWith (TQString ("]"))) { if (!groupfound && line.mid (1, line.length () - 2).stripWhiteSpace () == TQString ("playlist")) groupfound = true; else break; kdDebug () << "Group found: " << line << endl; } else if (groupfound) { int eq_pos = line.find (TQChar ('=')); if (eq_pos > 0) { if (line.lower ().startsWith (TQString ("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) { TQString ll = line.lower (); if (ll.startsWith (TQString ("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 (TQString ("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 (TQChar ('<'))) { 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 () != TQString ("[reference]")) do { TQString mrl = line.stripWhiteSpace (); if (line == TQString ("--stop--")) break; if (mrl.lower ().startsWith (TQString ("asf "))) mrl = mrl.mid (4).stripWhiteSpace (); if (!mrl.isEmpty () && !mrl.startsWith (TQChar ('#'))) 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 TQByteArray & 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 (TQString ("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 TQString & 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; TQTextStream 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 ()); TQString 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 ()) { TQFile 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 (TQCString (databuf, nr_bytes), &accuraty); if ((mime && !mime->name().startsWith (TQString("text/"))) || !strncmp (databuf, "RIFF", 4)) { return true; } kdDebug () << "mime: " << (mime ? mime->name (): TQString("null")) << endl; } file.reset (); TQTextStream textstream (&file); read (m, textstream); } } else if ((maybe_playlist && url.protocol ().compare (TQString ("mms")) && url.protocol ().compare (TQString ("rtsp")) && url.protocol ().compare (TQString ("rtp"))) || (mimestr.isEmpty () && (url.protocol ().startsWith (TQString ("http")) || url.protocol () == TQString::fromLatin1 ("media") || url.protocol () == TQString::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, TQT_SIGNAL(data(KIO::Job*,const TQByteArray&)), this, TQT_SLOT (kioData (KIO::Job *, const TQByteArray &))); //connect( m_job, TQT_SIGNAL(connected(KIO::Job*)), // this, TQT_SLOT(slotConnected(KIO::Job*))); connect(m_resolve_info->job, TQT_SIGNAL(mimetype(KIO::Job*,const TQString&)), this, TQT_SLOT (kioMimetype (KIO::Job *, const TQString &))); connect (m_resolve_info->job, TQT_SIGNAL (result (KIO::Job *)), this, TQT_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 TQString & url, const TQByteArray & data) { TQByteArray bytes; bytes.duplicate (data); cache_map.insert (url, bytes); preserve_map.erase (url); emit preserveRemoved (url); } bool DataCache::get (const TQString & url, TQByteArray & 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 TQString & 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 TQString & url) { return preserve_map.find (url) != preserve_map.end (); } bool DataCache::unpreserve (const TQString & 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 TQString & str) { url = str; KURL kurl (str); if (kurl.isLocalFile ()) { TQFile 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, TQT_SIGNAL (data (KIO::Job *, const TQByteArray &)), this, TQT_SLOT (slotData (KIO::Job *, const TQByteArray &))); connect (job, TQT_SIGNAL (result (KIO::Job *)), this, TQT_SLOT (slotResult (KIO::Job *))); connect (job, TQT_SIGNAL (mimetype (KIO::Job *, const TQString &)), this, TQT_SLOT (slotMimetype (KIO::Job *, const TQString &))); } else { //kdDebug () << "download preserved " << str << endl; connect (memory_cache, TQT_SIGNAL (preserveRemoved (const TQString &)), this, TQT_SLOT (cachePreserveRemoved (const TQString &))); 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, TQT_SIGNAL (preserveRemoved (const TQString &)), this, TQT_SLOT (cachePreserveRemoved (const TQString &))); 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 TQString & str) { if (str == url && !memory_cache->isPreserved (str)) { preserve_wait = false; disconnect (memory_cache, TQT_SIGNAL (preserveRemoved (const TQString &)), this, TQT_SLOT (cachePreserveRemoved (const TQString &))); download (str); } } KDE_NO_EXPORT void RemoteObjectPrivate::slotData (KIO::Job*, const TQByteArray& 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 TQString & 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 TQString & url) { clear (); return d->download (url); } KDE_NO_EXPORT TQString 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"