diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 00:59:09 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 00:59:09 +0000 |
commit | 7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b (patch) | |
tree | b5e07812e82f00c780d2c035dca102b8e364f447 /kaffeine/src/player-parts | |
download | kaffeine-7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b.tar.gz kaffeine-7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b.zip |
Added old abandoned KDE3 version of Kaffeine
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kaffeine@1088031 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kaffeine/src/player-parts')
54 files changed, 13885 insertions, 0 deletions
diff --git a/kaffeine/src/player-parts/Makefile.am b/kaffeine/src/player-parts/Makefile.am new file mode 100644 index 0000000..81311d2 --- /dev/null +++ b/kaffeine/src/player-parts/Makefile.am @@ -0,0 +1,5 @@ +if with_gstreamer + GST_SUBDIR = gstreamer-part +endif + +SUBDIRS = kaffeine-part xine-part $(GST_SUBDIR) diff --git a/kaffeine/src/player-parts/README b/kaffeine/src/player-parts/README new file mode 100644 index 0000000..53aa664 --- /dev/null +++ b/kaffeine/src/player-parts/README @@ -0,0 +1,17 @@ +This directory contains the player parts (aka backends) for kaffeine. + +It consists of the following subdirectories: + +- kaffeine-part + The base for all player parts. Every player part is derived from the + "KaffeinePart" class and reimplements functions. Additionally kaffeine-part + provides functions for embedding the player part e.g. into browsers. + +- dummy-part + An empty part which you can use to create a new player part. + +- xine-part + This is the default (and recommended) player part for kaffeine using xine-lib. + +- gstreamer-part + This is an experimental player part using GStreamer. diff --git a/kaffeine/src/player-parts/dummy-part/Makefile.am b/kaffeine/src/player-parts/dummy-part/Makefile.am new file mode 100644 index 0000000..106d07a --- /dev/null +++ b/kaffeine/src/player-parts/dummy-part/Makefile.am @@ -0,0 +1,20 @@ + +kde_module_LTLIBRARIES = libdummypart.la + +INCLUDES = -I$(top_srcdir)/kaffeine/src/player-parts/ $(all_includes) + +METASOURCES = AUTO + +noinst_HEADERS = dummy_part.h + +libdummypart_la_SOURCES = dummy_part.cpp +libdummypart_la_LIBADD = $(LIB_KPARTS) $(LIB_KFILE) ../libkaffeinepart.la +libdummypart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -avoid-version -no-undefined + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = dummy_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/dummypart +partrc_DATA = dummy_part.rc
\ No newline at end of file diff --git a/kaffeine/src/player-parts/dummy-part/README b/kaffeine/src/player-parts/dummy-part/README new file mode 100644 index 0000000..a569c62 --- /dev/null +++ b/kaffeine/src/player-parts/dummy-part/README @@ -0,0 +1,6 @@ + +************** +* DUMMY PART * +************** + +You can use this files as templates for your own player-part.
\ No newline at end of file diff --git a/kaffeine/src/player-parts/dummy-part/dummy_part.cpp b/kaffeine/src/player-parts/dummy-part/dummy_part.cpp new file mode 100644 index 0000000..7c52cf7 --- /dev/null +++ b/kaffeine/src/player-parts/dummy-part/dummy_part.cpp @@ -0,0 +1,120 @@ +/* + * dummy_part.cpp + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kparts/genericfactory.h> +#include <kaction.h> + +#include "dummy_part.h" + +typedef KParts::GenericFactory<DummyPart> DummyPartFactory; +K_EXPORT_COMPONENT_FACTORY (libdummypart, DummyPartFactory); + + +DummyPart::DummyPart(QWidget* parentWidget, const char* widgetName, QObject* parent, const char* name, const QStringList& /*args*/) +: KaffeinePart(parent, name ? name : "DummyPart") +{ + // we need an instance + setInstance(DummyPartFactory::instance()); + + // m_player = new Player(this); + // m_player->setFocusPolicy(QWidget::ClickFocus); + // setWidget(m_player); + + setXMLFile("dummy_part.rc"); + initActions(); +} + + +DummyPart::~DummyPart() +{ +} + +bool DummyPart::isPlaying() +{ + return false; +} + +uint DummyPart::volume() const +{ + return 0; +} + +uint DummyPart::position() const +{ + return 0; +} + +bool DummyPart::closeURL() +{ + return true; +} + +KAboutData *DummyPart::createAboutData() +{ + KAboutData* aboutData = new KAboutData( "dummypart", I18N_NOOP("DummyPart"), + "0.1", "Description...", + KAboutData::License_GPL, + "(c) 2004, Jürgen Kofler.", 0, "http://kaffeine.sourceforge.net"); + aboutData->addAuthor("Jürgen Kofler.",0, "kaffeine@gmx.net"); + + return aboutData; +} + +bool DummyPart::openURL(const MRL& mrl) +{ + // m_player->setLocation(mrl.url()); +} + +void DummyPart::slotPlay() +{ + // m_player->play(); +} + +void DummyPart::slotTogglePause() +{ + +} + +void DummyPart::slotSetVolume(uint) +{ + +} + +void DummyPart::slotSetPosition(uint) +{ + +} + +void DummyPart::slotStop() +{ + // m_player->stop(); +} + +void DummyPart::slotMute() +{ + +} + +void DummyPart::initActions() +{ + new KAction(i18n("Play"), "player_play", 0, this, SLOT(slotPlay()), actionCollection(), "player_play"); + new KAction(i18n("Pause"), "player_pause", Key_Space, this, SLOT(slotTogglePause()), actionCollection(), "player_pause"); + new KAction(i18n("Stop"), "player_stop", Key_Backspace, this, SLOT(slotStop()), actionCollection(), "player_stop"); +} diff --git a/kaffeine/src/player-parts/dummy-part/dummy_part.desktop b/kaffeine/src/player-parts/dummy-part/dummy_part.desktop new file mode 100644 index 0000000..de51e00 --- /dev/null +++ b/kaffeine/src/player-parts/dummy-part/dummy_part.desktop @@ -0,0 +1,23 @@ +[Desktop Entry] +Encoding=UTF-8 +Icon=gear +Name=DummyPart +Name[bn]=ডামি-পার্ট +Name[ca]=Part inútil +Name[da]=Tom program-part +Name[et]=Libakomponent +Name[fr]=Partie factice +Name[ja]=ダミーパート +Name[nb]=Tullemodul +Name[nn]=Eksempel-del +Name[se]=Fillenoassi +Name[sv]=Tom programdel +Name[tg]=Қисми амсила +Name[tr]=Kukla Kısım +Name[uk]=Порожній компонент +Name[xx]=xxDummyPartxx +Name[zh_CN]=伪部件 +MimeType=audio/x-mp3 +ServiceTypes=KaffeinePart +Type=Service +X-KDE-Library=libdummypart diff --git a/kaffeine/src/player-parts/dummy-part/dummy_part.h b/kaffeine/src/player-parts/dummy-part/dummy_part.h new file mode 100644 index 0000000..8f9a568 --- /dev/null +++ b/kaffeine/src/player-parts/dummy-part/dummy_part.h @@ -0,0 +1,72 @@ +/* + * dummy_part.h + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef DUMMYPART_H +#define DUMMYPART_H + +#include <kparts/factory.h> + +#include "kaffeinepart.h" + +/** + * DummyPart - use this as template for own player parts + * @author Jürgen Kofler <kaffeine@gmx.net> + * + */ + + +class DummyPart : public KaffeinePart +{ + Q_OBJECT +public: + DummyPart(QWidget*, const char*, QObject*, const char*, const QStringList&); + virtual ~DummyPart(); + + /* + *Reimplement from KaffeinePart + */ + bool isPlaying(); + uint volume() const; /* percent */ + uint position() const; /* percent */ + + bool closeURL(); + static KAboutData* createAboutData(); + +public slots: + /* + * Reimplement from KaffeinePart + */ + bool openURL(const MRL& mrl); + void slotPlay(); + void slotTogglePause(); + void slotSetVolume(uint); /* percent */ + void slotSetPosition(uint); /* percent */ + void slotStop(); + void slotMute(); + +private: + void initActions(); + +private: + // Player* m_play; + +}; + +#endif /* DUMMYPART_H */ diff --git a/kaffeine/src/player-parts/dummy-part/dummy_part.rc b/kaffeine/src/player-parts/dummy-part/dummy_part.rc new file mode 100644 index 0000000..d58b2aa --- /dev/null +++ b/kaffeine/src/player-parts/dummy-part/dummy_part.rc @@ -0,0 +1,10 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="dummy_part" version="1"> +<MenuBar> + <Menu name="player"><text>&Player</text> + <Action name="player_play"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + </Menu> +</MenuBar> +</kpartgui>
\ No newline at end of file diff --git a/kaffeine/src/player-parts/gstreamer-part/Makefile.am b/kaffeine/src/player-parts/gstreamer-part/Makefile.am new file mode 100644 index 0000000..3d058be --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/Makefile.am @@ -0,0 +1,22 @@ +kde_module_LTLIBRARIES = libgstreamerpart.la + +INCLUDES = -I$(top_srcdir)/kaffeine/src/player-parts/kaffeine-part -I$(top_srcdir)/kaffeine/src/ $(all_includes) $(CFLAGS_GSTREAMER) + +METASOURCES = AUTO + +noinst_HEADERS = gstreamer_part.h timer.h video.h videosettings.h gstreamerconfig.h + +libgstreamerpart_la_SOURCES = gstreamer_part.cpp video.cpp timer.cpp videosettings.cpp gstreamerconfig.cpp +libgstreamerpart_la_LIBADD = $(LIB_KPARTS) $(LIB_KFILE) ../kaffeine-part/libkaffeinepart.la +libgstreamerpart_la_LDFLAGS = -module $(KDE_PLUGIN) $(LIB_GSTREAMER) -lgstinterfaces-0.10 $(all_libraries) -avoid-version -no-undefined + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = gstreamer_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/gstreamerpart +partrc_DATA = gstreamer_part.rc + +icondir = $(kde_icondir)/hicolor/16x16/apps/ +icon_DATA = gstreamer.png diff --git a/kaffeine/src/player-parts/gstreamer-part/gstreamer.png b/kaffeine/src/player-parts/gstreamer-part/gstreamer.png Binary files differnew file mode 100644 index 0000000..1a73f7b --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/gstreamer.png diff --git a/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.cpp b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.cpp new file mode 100644 index 0000000..22e747a --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.cpp @@ -0,0 +1,1082 @@ +/* + * gstreamer_part.cpp + * + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * based on kiss by Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + + +#include <kparts/genericfactory.h> +#include <kaction.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <kxmlguifactory.h> +#include <ktoolbar.h> +#include <kio/netaccess.h> +#include <kmimetype.h> +#include <kpopupmenu.h> + +#include <qtimer.h> +#include <qslider.h> +#include <qfile.h> +#include <qtooltip.h> + +#include "gstreamer_part.h" +#include "gstreamer_part.moc" +#include "playlistimport.h" + +#define TIMER_EVENT_PLAYBACK_FINISHED 100 +#define TIMER_EVENT_ERROR 102 +#define TIMER_EVENT_NEW_STATE 103 +#define TIMER_EVENT_FOUND_TAG 104 + + + +typedef KParts::GenericFactory<GStreamerPart> GStreamerPartFactory; +K_EXPORT_COMPONENT_FACTORY (libgstreamerpart, GStreamerPartFactory); + + +GStreamerPart::GStreamerPart( QWidget* parentWidget, const char* /*widgetName*/, QObject* parent, const char* name, + const QStringList& /*args*/ ) + : KaffeinePart( parent, name ? name : "GStreamerPart" ), + m_play(NULL), m_videosink(NULL), m_audiosink(NULL), m_visual(NULL), m_videoSettings(NULL), + m_gstConfig(NULL), m_mute(false), m_logoPath(QString::null), m_posToolbar(NULL) +{ + // we need an instance + setInstance( GStreamerPartFactory::instance() ); + parentWidget->setPaletteBackgroundColor( QColor(0,0,0) ); //black + + bus = NULL; + + loadConfig(); + + if ( !initGStreamer() ) + { + kdError() << "GStreamerPart: Initializing of GStreamer failed!" << endl; + emit canceled( i18n("GStreamer initializing failed!") ); + return; + } + + m_status = GST_STATE_NULL; + + kdDebug() << "GStreamerPart: Creating video window" << endl; + m_video = new VideoWindow( parentWidget, m_videosink ); + connect( m_video, SIGNAL(signalNewFrameSize(const QSize&)), this, SIGNAL(signalNewFrameSize(const QSize&)) ); + m_video->setFocusPolicy( QWidget::ClickFocus ); + setWidget( m_video ); + + setXMLFile( "gstreamer_part.rc" ); + initActions(); + stateChanged( "disable_all" ); + + emit setStatusBarText( i18n("Ready") ); + + // be careful - we may be embedded + m_logoPath = locate( "data", "kaffeine/logo" ); + kdDebug() << "GStreamerPart: Found logo animation: " << m_logoPath << endl; + + connect( &busTimer, SIGNAL(timeout()), this, SLOT(slotReadBus()) ); +} + + + +GStreamerPart::~GStreamerPart() +{ + deletePlaybin(); + saveConfig(); + delete m_timer; + kdDebug() << "GStreamerPart: destructed" << endl; +} + + + +bool GStreamerPart::isPlaying() +{ + return ( (m_status==GST_STATE_PLAYING) && m_url != m_logoPath ); +} + + + +bool GStreamerPart::isPaused() +{ + if ( !m_play ) + return false; + return ( GST_STATE(m_play)==GST_STATE_PAUSED ); +} + + + +bool GStreamerPart::hasVideo() +{ + //FIXME: don't work with all video codecs + return ( !m_videoCodec.isNull() ); +} + + + +uint GStreamerPart::position() const +{ + return (uint)((1.0 / m_timer->getTotalTimeMS()) * m_timer->getCurrentTimeMS() * 100); +} + + + +bool GStreamerPart::closeURL() +{ + slotStop(); + return true; +} + + + +KAboutData *GStreamerPart::createAboutData() +{ + KAboutData* aboutData = new KAboutData( "gstreamerpart", I18N_NOOP("GStreamerPart"), + "0.1", "GStreamer based player part for Kaffeine.", + KAboutData::License_GPL, + "(c) 2005, Jürgen Kofler.", 0, "http://kaffeine.sourceforge.net"); + aboutData->addAuthor("Jürgen Kofler.",0, "kaffeine@gmx.net"); + + return aboutData; +} + + + +bool GStreamerPart::openURL(const MRL& mrl) +{ + kdDebug() << "GStreamerPart::openURL(): " << mrl.url() << endl; + + // FIXME: thats not the right place for that + if ( !m_posToolbar ) { + m_posToolbar = (KToolBar*)factory()->container( "gstPositionToolBar", this ); + if ( m_posToolbar ) { + m_posToolbar->setItemAutoSized( m_posToolbar->idAt(0), true ); //set position slider to maximum width + } + } + + m_mrl = mrl; + m_playlist.clear(); + m_current = 0; + bool playlist = false; + + currentDevice = ""; + + QString ext = m_mrl.kurl().fileName(); + ext = ext.remove( 0 , ext.findRev('.')+1 ).lower(); + + if ( m_mrl.mime().isNull() ) { + KMimeType::Ptr mime = KMimeType::findByURL( m_mrl.kurl().path() ); + m_mrl.setMime( mime->name() ); + } + + /* is m_mrl a playlist? */ + if ( (m_mrl.mime() == "text/plain") || (m_mrl.mime() == "text/xml") || (m_mrl.mime() == "application/x-kaffeine") + || (m_mrl.mime() == "audio/x-scpls") || (m_mrl.mime() == "audio/x-mpegurl") || (m_mrl.mime() == "audio/mpegurl") + || (ext == "asx") || (ext == "asf") || (ext == "wvx") || (ext == "wax") ) /* windows meta files */ + { + kdDebug() << "GStreamerPart: Check for kaffeine/noatun/m3u/pls/asx playlist\n"; + QString localFile; + if ( KIO::NetAccess::download(m_mrl.kurl(), localFile, widget()) ) { + QFile file( localFile ); + file.open( IO_ReadOnly ); + QTextStream stream( &file ); + QString firstLine = stream.readLine(); + QString secondLine = stream.readLine(); + file.close(); + + if ( secondLine.contains("kaffeine", false) ) { + kdDebug() << "GStreamerPart: Try loading kaffeine playlist\n"; + playlist = PlaylistImport::kaffeine( localFile, m_playlist ); + } + if ( secondLine.contains("noatun", false) ) { + kdDebug() << "GStreamerPart: Try loading noatun playlist\n"; + playlist = PlaylistImport::noatun( localFile, m_playlist); + } + if ( firstLine.contains("asx", false) ) { + kdDebug() << "GStreamerPart: Try loading asx playlist\n"; + playlist = PlaylistImport::asx( localFile, m_playlist ); + } + if ( firstLine.contains("[playlist]", false) ) { + kdDebug() << "GStreamerPart: Try loading pls playlist\n"; + playlist = PlaylistImport::pls( localFile, m_playlist ); + } + if (ext == "m3u") { //indentify by extension + kdDebug() << "GStreamerPart: Try loading m3u playlist\n"; + playlist = PlaylistImport::m3u( localFile, m_playlist ); + } + } + else + kdError() << "GStreamerPart: " << KIO::NetAccess::lastErrorString() << endl; + } + /* check for ram playlist */ + if ( (ext == "ra") || (ext == "rm") || (ext == "ram") || (ext == "lsc") || (ext == "pl") ) { + kdDebug() << "GStreamerPart: Try loading ram playlist\n"; + playlist = PlaylistImport::ram( m_mrl, m_playlist, widget() ); + } + + int pos; + QString s = mrl.url(); + if ( s.startsWith("cdda://") ) { + s = s.remove("cdda://"); + if ( (pos=s.findRev("/"))>-1 ) { + currentDevice=s.left( pos ); + s = s.right( s.length()-pos-1 ); + } + else + currentDevice=m_device; + m_mrl.setURL( "cdda://"+s ); + } + else if ( s.startsWith("dvd://") ) { + s = s.remove("dvd://"); + if ( s.startsWith("/") ) + currentDevice=s; + else + currentDevice=m_device; + m_mrl.setURL( "dvd://" ); + } + else if ( s.startsWith("vcd://") ) { + s = s.remove("vcd://"); + if ( s.startsWith("/") ) + currentDevice=s; + else + currentDevice=m_device; + m_mrl.setURL( "vcd://" ); + } + + if (!playlist) + { + kdDebug() << "GStreamerPart: Got single track" << endl; + m_playlist.append( m_mrl ); + } + + QTimer::singleShot(0, this, SLOT(slotPlay())); + return true; +} + + + +void GStreamerPart::slotPlay() +{ + if ( (m_play) && (GST_STATE(m_play)==GST_STATE_PAUSED) ) { + gst_element_set_state( m_play, GST_STATE_PLAYING ); + return; + } + + if ( m_playlist.count() > 0 ) { + emit setStatusBarText( i18n("Opening...") ); + MRL curMRL = m_playlist[m_current]; + m_url = curMRL.url(); + QString subUrl = QString::null; + if ( (!curMRL.subtitleFiles().isEmpty()) && (curMRL.currentSubtitle() > -1) ) + subUrl = curMRL.subtitleFiles()[curMRL.currentSubtitle()]; + + gstPlay(m_url, subUrl); + } + else { + emit signalRequestCurrentTrack(); + } +} + + + +void GStreamerPart::slotNext() +{ + if ( (m_playlist.count() > 0) && (m_current < m_playlist.count()-1) ) + { + m_current++; + slotPlay(); + } + else { + emit signalRequestNextTrack(); + } +} + + + +void GStreamerPart::slotPrevious() +{ + if ( m_current > 0 ) { + m_current--; + slotPlay(); + } + else { + emit signalRequestPreviousTrack(); + } +} + + + +void GStreamerPart::setDevice( QString device ) +{ + if ( !m_play ) + return; + + GObject *source=NULL; + g_object_get( m_play, "source", &source, NULL ); + if ( !source ) { + kdDebug() << "GStreamer: NO source for 'device' " << device << endl; + return; + } + + GObjectClass *klass = G_OBJECT_GET_CLASS( source ); + if ( g_object_class_find_property( klass, "device" ) ) { + kdDebug() << "GStreamer: Set source sink property 'device' to " << device << endl; + g_object_set(source, "device", device.ascii(), NULL); + } + g_object_unref (source); +} + + + +void GStreamerPart::slotTogglePause( bool ) +{ + if ( !m_play ) + return; + + if ( GST_STATE(m_play)==GST_STATE_PAUSED ) + gst_element_set_state( m_play, GST_STATE_PLAYING ); + else + gst_element_set_state( m_play, GST_STATE_PAUSED ); +} + + + +void GStreamerPart::slotSetPosition( uint percent ) +{ + if ( !m_play ) + return; + m_timer->seekPercent( percent ); +} + + + +void GStreamerPart::slotStop() +{ + if ( !m_play ) + return; + + gst_element_set_state( m_play, GST_STATE_READY ); + if ( !m_logoPath.isNull() ) { + m_url = m_logoPath; + gstPlay( m_logoPath, QString::null ); + } +} + + + +void GStreamerPart::slotVolume( int vol ) +{ + if ( !m_play ) + return; + + emit setStatusBarText(i18n("Volume") + ": " + QString::number(vol) + "%"); + double v = vol * 0.01; + kdDebug() << "GStreamerPart: Set volume to " << v << endl; + g_object_set( G_OBJECT(m_play), "volume", v, NULL ); +} + + + +void GStreamerPart::slotSetVolume( uint vol ) +{ + m_volume->setValue( vol ); +} + + + +uint GStreamerPart::volume() const +{ + if ( !m_play ) + return m_volume->value(); + + double v; + g_object_get( G_OBJECT(m_play), "volume", &v, NULL ); + + return (uint)(v*100 ); +} + + + +void GStreamerPart::slotMute() +{ + m_mute = !m_mute; + if ( m_mute ) { + muteVolume = m_volume->value(); + m_volume->setValue( 0 ); + emit setStatusBarText( i18n("Mute") + ": " + i18n("On") ); + } + else { + m_volume->setValue( muteVolume ); + emit setStatusBarText( i18n("Mute") + ": " + i18n("Off") ); + } +} + + + +void GStreamerPart::slotSaturation( int sat ) +{ + emit setStatusBarText( i18n("Saturation") + ": " + QString::number(sat) ); + g_object_set( G_OBJECT(m_videosink), "saturation", sat, NULL ); +} + + + +void GStreamerPart::slotHue( int hue ) +{ + emit setStatusBarText( i18n("Hue") + ": " + QString::number(hue) ); + g_object_set( G_OBJECT(m_videosink), "hue", hue, NULL ); +} + + + +void GStreamerPart::slotContrast( int contrast ) +{ + emit setStatusBarText( i18n("Contrast") + ": " + QString::number(contrast) ); + g_object_set( G_OBJECT(m_videosink), "contrast", contrast, NULL ); +} + + + +void GStreamerPart::slotBrightness( int brightness ) +{ + emit setStatusBarText( i18n("Brightness") + ": " + QString::number(brightness) ); + g_object_set( G_OBJECT(m_videosink), "brightness", brightness, NULL ); +} + + + +void GStreamerPart::gstStateChanged() +{ + if ( m_status == GST_STATE_PAUSED ) { + kdDebug() << "GStreamerPart: New gstreamer state: PAUSE" << endl; + emit setStatusBarText( i18n("Pause") ); + } + else if ( m_status == GST_STATE_PLAYING ) { + kdDebug() << "GStreamerPart: New gstreamer state: PLAYING" << endl; + if ( m_url != m_logoPath ) + stateChanged("playing"); + else + stateChanged("not_playing"); + QString caption = m_mrl.title(); + if ( !m_mrl.artist().isEmpty() ) + caption.append( QString(" (") + m_mrl.artist() + ")" ); + emit setWindowCaption(caption); + emit setStatusBarText(i18n("Playing")); + } + else if ( m_status == GST_STATE_READY ) { + kdDebug() << "GStreamerPart: New gstreamer state: READY" << endl; + if ( m_playlist.count() ) + stateChanged( "not_playing" ); + else + stateChanged( "disable_all" ); + emit setWindowCaption(""); + emit setStatusBarText( i18n("Stop") ); + } + m_video->newState(); +} + + + +void GStreamerPart::slotVideoSettings() +{ + if ( !m_videoSettings ) { + int hue = 0, sat = 0, contr = 0, bright = 0; + g_object_get( G_OBJECT(m_videosink), "hue", &hue, NULL ); + g_object_get( G_OBJECT(m_videosink), "saturation", &sat, NULL ); + g_object_get( G_OBJECT(m_videosink), "contrast", &contr, NULL ); + g_object_get( G_OBJECT(m_videosink), "brightness", &bright, NULL ); + + m_videoSettings = new VideoSettings( hue, sat, contr, bright ); + connect( m_videoSettings, SIGNAL(signalNewBrightness(int)), this, SLOT(slotBrightness(int)) ); + connect( m_videoSettings, SIGNAL(signalNewContrast(int)), this, SLOT(slotContrast(int)) ); + connect( m_videoSettings, SIGNAL(signalNewHue(int)), this, SLOT(slotHue(int)) ); + connect( m_videoSettings, SIGNAL(signalNewSaturation(int)), this, SLOT(slotSaturation(int)) ); + } + + m_videoSettings->show(); +} + + + +void GStreamerPart::slotConfigDialog() +{ + if ( m_gstConfig == NULL ) { + m_gstConfig = new GStreamerConfig( m_audioPluginList, m_videoPluginList ); + } + + m_gstConfig->setAudioDriver( m_audioSinkName ); + m_gstConfig->setVideoDriver( m_videoSinkName ); + m_gstConfig->setDrive( m_device ); + if ( m_gstConfig->exec() == KDialogBase::Accepted ) { + kdDebug() << "GStreamerPart: Apply new configuration" << endl; + if ( m_audioSinkName != m_gstConfig->getAudioDriver() ) + setAudioSink( m_gstConfig->getAudioDriver() ); + m_videoSinkName = m_gstConfig->getVideoDriver(); + m_device = m_gstConfig->getDrive(); + } +} + + + +void GStreamerPart::processMetaInfo() +{ + kdDebug() << "GStreamerPart: Processing meta info" << endl; + + MRL mrl = m_playlist[m_current]; + + if ( (mrl.title().contains("/") || mrl.title().contains(".") || (mrl.title().isEmpty())) + && !m_title.stripWhiteSpace().isEmpty() && m_title.length() > 1 ) + mrl.setTitle( m_title ); + if (mrl.artist().isEmpty() && !m_artist.stripWhiteSpace().isEmpty()) + mrl.setArtist(m_artist); + if (mrl.album().isEmpty() && !m_album.stripWhiteSpace().isEmpty()) + mrl.setAlbum(m_album); + if (mrl.genre().isEmpty() && !m_genre.stripWhiteSpace().isEmpty()) + mrl.setGenre(m_genre); + if (mrl.track().isEmpty() && !m_track.stripWhiteSpace().isEmpty()) + mrl.setTrack(m_track); + if (mrl.comment().isEmpty() && !m_comment.stripWhiteSpace().isEmpty()) + mrl.setComment(m_comment); + if (mrl.length().isNull()) { + QTime length = QTime().addMSecs(m_timer->getTotalTimeMS()); + if (!length.isNull()) + mrl.setLength(length); + } + m_playlist[m_current] = mrl; + + QString caption = mrl.title(); + if (!mrl.artist().isEmpty()) + caption.append(QString(" (") + mrl.artist() + ")"); + emit setWindowCaption(caption); + /* if we don't have a playlist emit signalNewMeta() */ + if (mrl.url() == m_mrl.url()) { + m_mrl = mrl; + emit signalNewMeta(m_mrl); + } +} + + + +void GStreamerPart::slotInfo() +{ + QString info; + QTextStream ts(&info, IO_WriteOnly); + ts << "<qt><table width=\"90%\">"; + ts << "<tr><td colspan=\"2\"><center><b>" << m_title << "</b></center></td></tr>"; + if (!m_artist.isNull()) + ts << "<tr><td><b>" << i18n("Artist") << ":</b></td><td> " << m_artist << "</td></tr>"; + if (!m_album.isNull()) + ts << "<tr><td><b>" << i18n("Album") << ":</b></td><td> " << m_album << "</td></tr>"; + if (!m_track.isNull()) + ts << "<tr><td><b>" << i18n("Track") << ":</b></td><td> " << m_track << "</td></tr>"; + if (!m_year.isNull()) + ts << "<tr><td><b>" << i18n("Year") << ":</b></td><td> " << m_year << "</td></tr>"; + if (!m_genre.isNull()) + ts << "<tr><td><b>" << i18n("Genre") << ":</b></td><td> " << m_genre << "</td></tr>"; + if (!m_comment.isNull()) + ts << "<tr><td><b>" << i18n("Comment") << ":</b></td><td> " << m_comment << "</td></tr>"; + + QTime length = QTime().addMSecs(m_timer->getTotalTimeMS()); + if (!length.isNull()) + ts << "<tr><td><b>" << i18n("Length") << ":</b></td><td> " << length.toString("h:mm:ss") << "</td></tr>"; + + ts << "<br><br>"; + + ts << "<tr><td><b>" << i18n("Audio") << ":</b></td><td> " << m_audioCodec << "</td></tr>"; + int width = m_video->getFrameSize().width(); + int height = m_video->getFrameSize().height(); + if (width > 0 && height > 0) + ts << "<tr><td><b>" << i18n("Video") << ":</b></td><td> " << m_videoCodec << " " << width << "x" << height << "</td></tr>"; + + ts << "</table></qt>"; + + KMessageBox::information(0, info); +} + + + +void GStreamerPart::slotSetVisualPlugin( const QString& name ) +{ + if ( name != "none" ) { + GstElement* visual = gst_element_factory_make( name.ascii(), "visualization" ); + if ( visual ) { + g_object_set( G_OBJECT(m_play), "vis-plugin", visual, NULL ); + if ( m_visual ) + g_object_unref( m_visual ); + m_visual = visual; + m_visualPluginName = name; + } + else + kdWarning() << "GStreamer: Initialization of visualization plugin failed (" << name << ")" << endl; + } + else { + if (m_visual) { + g_object_set(G_OBJECT (m_play), "vis-plugin", NULL, NULL); + g_object_unref(m_visual); + m_visual = NULL; + m_visualPluginName = "none"; + } + } +} + + + +void GStreamerPart::slotContextMenu( const QPoint& pos ) +{ + if (factory()) { + KPopupMenu *pop = (KPopupMenu*)factory()->container("context_menu", this); + if ( pop ) + pop->popup(pos); + } +} + + + +void GStreamerPart::loadConfig() +{ + kdDebug() << "GStreamerPart: Load config" << endl; + KConfig* config = instance()->config(); + config->setGroup("General Options"); + + m_audioSinkName = config->readEntry("Audio Sink", "alsasink"); + m_videoSinkName = config->readEntry("Video Sink", "xvimagesink"); + m_visualPluginName = config->readEntry("Visual Plugin", "goom"); + m_savedVolume = config->readNumEntry("Volume", 25); + m_device = config->readEntry("CD Device", "/dev/dvd"); +} + + + +void GStreamerPart::saveConfig() +{ + kdDebug() << "GStreamerPart: Save config" << endl; + + KConfig* config = instance()->config(); + config->setGroup("General Options"); + + config->writeEntry( "Audio Sink", m_audioSinkName ); + config->writeEntry( "Video Sink", m_videoSinkName ); + config->writeEntry( "Visual Plugin", m_visualPluginName ); + config->writeEntry( "Volume", m_volume->value() ); + config->writeEntry( "CD Device", m_device ); +} + + + +void GStreamerPart::slotPrepareForFullscreen( bool fullscreen ) +{ + if ( fullscreen ) + m_video->startMouseHideTimer(); + else + m_video->stopMouseHideTimer(); +} + + + +void GStreamerPart::initActions() +{ + new KAction(i18n("Toggle Minimal Mode"), 0, 0, this, SIGNAL(signalToggleMinimalMode()), actionCollection(), "player_minimal_mode"); + new KAction(i18n("Play"), "player_play", 0, this, SLOT(slotPlay()), actionCollection(), "player_play"); + new KAction(i18n("Pause"), "player_pause", Key_Space, this, SLOT(slotTogglePause()), actionCollection(), "player_pause"); + new KAction(i18n("Stop"), "player_stop", Key_Backspace, this, SLOT(slotStop()), actionCollection(), "player_stop"); + new KAction(i18n("&Next"), "player_end", Key_PageDown, this, SLOT(slotNext()), actionCollection(), "player_next"); + new KAction(i18n("&Previous"), "player_start", Key_PageUp, this, SLOT(slotPrevious()), actionCollection(), "player_previous"); + + m_timer = new Timer(); + new KWidgetAction(m_timer->getSlider(), i18n("Position"), 0, 0, 0, actionCollection(), "player_position"); + new KWidgetAction((QWidget*)m_timer->getLabel(), i18n("Playtime"), 0, 0, 0, actionCollection(), "player_playtime"); + + m_audioVisual = new KSelectAction(i18n("Audio &Visualization"), 0, actionCollection(), "audio_visual"); + connect(m_audioVisual, SIGNAL(activated(const QString&)), this, SLOT(slotSetVisualPlugin(const QString&))); + m_audioVisualPluginList.prepend("none"); + m_audioVisual->setItems(m_audioVisualPluginList); + m_audioVisual->setCurrentItem(m_audioVisual->items().findIndex(m_visualPluginName)); + + new KAction(i18n("&Mute"), "player_mute", Key_U, this, SLOT(slotMute()), actionCollection(), "audio_mute"); + new KAction(i18n("&Auto"), "viewmagfit", Key_F5, m_video, SLOT(slotAspectRatioAuto()), actionCollection(), "aspect_auto"); + new KAction(i18n("&4:3"), "viewmagfit", Key_F6, m_video, SLOT(slotAspectRatio4_3()), actionCollection(), "aspect_43"); + new KAction(i18n("A&namorphic"), "viewmagfit", Key_F7, m_video, SLOT(slotAspectRatioAnamorphic()), actionCollection(), "aspect_anamorphic"); + new KAction(i18n("&DVB"), "viewmagfit", Key_F8, m_video, SLOT(slotAspectRatioDVB()), actionCollection(), "aspect_dvb"); + new KAction(i18n("&Square"), "viewmagfit", Key_F9, m_video, SLOT(slotAspectRatioSquare()), actionCollection(), "aspect_square"); + new KAction(i18n("&Video Settings"), "configure", Key_V, this, SLOT(slotVideoSettings()), actionCollection(), "video_settings"); + new KAction(i18n("Track &Info"), "info", 0 , this, SLOT(slotInfo()), actionCollection(), "player_track_info"); + + m_volume = new QSlider(Horizontal, 0); + QToolTip::add(m_volume, i18n("Volume")); + m_volume->setRange(0, 100); + m_volume->setSteps(1, 5); + m_volume->setFixedWidth(75); + connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(slotVolume(int))); + slotSetVolume(m_savedVolume); + new KWidgetAction(m_volume, i18n("Volume"), 0, 0, 0, actionCollection(), "audio_volume"); + new KAction(i18n("&GStreamer Engine Parameters"), "edit", 0, this, SLOT(slotConfigDialog()), actionCollection(), "settings_gst_parameter"); + + connect(m_video, SIGNAL(signalRightClick(const QPoint&)), this, SLOT(slotContextMenu(const QPoint&))); +} + + +//Change audio sink on the fly +void GStreamerPart::setAudioSink( QString sinkName ) +{ + GstElement* sink = gst_element_factory_make(sinkName.ascii(), "audiosink"); + if ( !sink ) { + KMessageBox::error(0, i18n("Error: Can't init new Audio Driver %1 - using %2!").arg(sinkName).arg(m_audioSinkName)); + return; + } + + if ( m_play ) + g_object_set( G_OBJECT(m_play), "audio-sink", sink, NULL ); + m_audiosink = sink; + m_audioSinkName = sinkName; + kdDebug() << "GStreamerPart: Using audio driver: " << m_audioSinkName << endl; +} + +/** + * Initialize GStreamer + */ +bool GStreamerPart::initGStreamer() +{ + if ( !gst_init_check(NULL, NULL,NULL) ) { + KMessageBox::error(0, i18n("GStreamer could not be initialized!")); + return false; + } + + /* check GStreamer version */ + guint maj, min, mic, nan; + gst_version(&maj, &min, &mic, &nan); + kdDebug() << "GStreamerPart: Found GStreamer version "<<maj<<"."<<min<<"."<<mic<<"."<< nan <<endl<<endl; + + /* check for visualization plugins */ + GList* factories = gst_registry_get_feature_list(gst_registry_get_default (), GST_TYPE_ELEMENT_FACTORY); + QString name, cat; + while ( factories ) { + name = GST_PLUGIN_FEATURE_NAME(factories->data); + cat = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(factories->data)); + // kdDebug() << "GStreamerPart: Found plugin: " << name << " - Category: " << cat << endl; + if ( cat == "Visualization" ) + m_audioVisualPluginList.append(name); + else if ( cat == "Sink/Audio" ) + m_audioPluginList.append(name); + else if ( cat == "Sink/Video" ) + m_videoPluginList.append(name); + factories = g_list_next(factories); + } + g_list_free( factories ); + + /* + * Init audio driver + */ + m_audiosink = gst_element_factory_make( m_audioSinkName.ascii(), "audiosink" ); + if ( !m_audiosink ) { + KMessageBox::error(0, i18n("Can't init Audio Driver '%1' - trying another one...").arg(m_audioSinkName)); + if ( (m_audiosink = gst_element_factory_make("alsasink", "audiosink")) != NULL ) + kdDebug() << "GStreamerPart: Using audio-driver: alsasink" << endl; + else { + if ( (m_audiosink = gst_element_factory_make("osssink", "audiosink")) != NULL ) + kdDebug() << "GStreamerPart: Using audio-driver: osssink" << endl; + else { + if ( (m_audiosink = gst_element_factory_make("artsdsink", "audiosink")) != NULL ) + kdDebug() << "GStreamerPart: Using audio-driver: artsdsink" << endl; + else { + KMessageBox::error(0, i18n("No useable audio-driver found!") + " (" + m_audioSinkName + ")"); + return false; + } + } + } + } + else + kdDebug() << "GStreamerPart: Using audio driver: " << m_audioSinkName << endl; + gst_element_set_state( m_audiosink, GST_STATE_READY ); + /* + * Init video driver... + */ + m_videosink = gst_element_factory_make( m_videoSinkName.ascii(), "videosink" ); + if ( !m_videosink ) { + KMessageBox::error(0, i18n("Can't init Video Driver '%1' - trying another one...").arg(m_videoSinkName)); + if ((m_videosink = gst_element_factory_make("xvimagesink", "videosink")) != NULL) + kdDebug() << "GStreamerPart: Using video-driver: xvimagesink" << endl; + else { + if ( (m_videosink = gst_element_factory_make( "ximagesink", "videosink") ) != NULL) + kdDebug() << "GStreamerPart: Using video-driver: ximagesink" << endl; + else { + KMessageBox::error(0, i18n("No useable video-driver found!") + " (" + m_videoSinkName + ")"); + return false; + } + } + } + else + kdDebug() << "GStreamerPart: Using video driver: " << m_videoSinkName << endl; + gst_element_set_state( m_videosink, GST_STATE_READY ); + + /* + * Visualization + */ + kdDebug() << "GStreamerPart: Using visualization plugin: " << m_visualPluginName << endl; + if ( m_visualPluginName != "none" ) { + m_visual = gst_element_factory_make( m_visualPluginName.ascii(), "visualization" ); + if ( !m_visual ) + kdWarning() << "GStreamer: Initialization of visualization plugin failed" << endl; + } + + return true; +} + + + +bool GStreamerPart::createPlaybin() +{ + m_play = gst_element_factory_make( "playbin", "play" ); + if ( !m_play ) { + KMessageBox::error( 0, i18n("GStreamer playbin could not be initialized!") ); + return false; + } + if ( !m_videosink || !m_audiosink ) { + KMessageBox::error( 0, i18n("GStreamer sink(s) missing, playbin could not be initialized!") ); + gst_object_unref( GST_OBJECT(m_play) ); + return false; + } + + g_object_set( G_OBJECT(m_play), "video-sink", m_videosink, NULL ); + g_object_set( G_OBJECT(m_play), "audio-sink", m_audiosink, NULL ); + g_object_set( G_OBJECT(m_play), "vis-plugin", m_visual, NULL ); + gst_element_set_state( m_play, GST_STATE_READY ); + slotVolume( m_volume->value() ); + m_video->setPlaybin( m_play ); + m_timer->setPlaybin( m_play ); + + bus = gst_pipeline_get_bus( GST_PIPELINE(m_play) ); + busTimer.start( 5 ); + return true; +} + + + +void GStreamerPart::deletePlaybin() +{ + if ( bus ) { + busTimer.stop(); + gst_object_unref( GST_OBJECT(bus) ); + bus = NULL; + } + if ( m_play ) { + m_video->setPlaybin( NULL ); + m_timer->setPlaybin( NULL ); + gst_element_set_state( m_play, GST_STATE_NULL ); + gst_object_unref( GST_OBJECT(m_play) ); + m_play = NULL; + } +} + + + +void GStreamerPart::gstPlay( const QString& trackUrl, const QString& subtitleUrl ) +{ + if ( !m_play && !createPlaybin() ) + return; + + m_title = QString::null; + m_artist = QString::null; + m_album = QString::null; + m_year = QString::null; + m_genre = QString::null; + m_track = QString::null; + m_comment = QString::null; + m_audioCodec = QString::null; + m_videoCodec = QString::null; + QString url = trackUrl; + + if ( GST_STATE(m_play)!=GST_STATE_READY ) + gst_element_set_state( m_play, GST_STATE_READY ); + + /* make sure the xoverlay know the winId */ + m_video->refresh(); + + /* KDE has the habit to do "file:/location", whereas we + expect "file:///location" */ + if (url.left(7).lower() == "file://") { + url.insert(6, "/"); + } + else if (url[0] == '/') { + url.prepend("file://"); + } + gchar *uri = g_strdup( url.local8Bit() ); + kdDebug() << "GStreamerPart: play URL: " << uri << endl; + g_object_set( G_OBJECT(m_play), "uri", uri, NULL ); + g_free( uri ); + + //process subtitle url + if ( !subtitleUrl.isNull() ) { + QString sub = subtitleUrl; + if ( sub.left(7).lower() == "file://" ) { + sub.insert(6, "/"); + } + else if (sub[0] == '/') { + sub.prepend("file://"); + } + // set sub font + g_object_set( G_OBJECT(m_play), "subtitle-font-desc", "sans bold 18", NULL ); + + gchar* suburi = g_strdup( sub.local8Bit() ); + kdDebug() << "GStreamerPart: Setting subtitle URL to " << suburi << endl; + g_object_set( G_OBJECT(m_play), "suburi", suburi, NULL ); + g_free( suburi ); + } + else { + g_object_set( G_OBJECT(m_play), "suburi", NULL, NULL ); + } + + if ( !currentDevice.isEmpty() ) + setDevice( currentDevice ); + gst_element_set_state( m_play, GST_STATE_PLAYING ); + + m_timer->start(); +} + + + +void GStreamerPart::slotReadBus() +{ + //kdDebug() << "readBus : " << QTime::currentTime().toString("hh:mm:ss:zzz") << endl; + if ( !bus ) + return; + + GstMessage *msg = gst_bus_pop( bus ); + if ( !msg ) + return; + + gchar *debug = NULL; + GError *error = NULL; + GstState old, cur, pending; + + GstElement *src = (GstElement*)GST_MESSAGE_SRC( msg ); + switch( GST_MESSAGE_TYPE( msg ) ) { + case GST_MESSAGE_STATE_CHANGED: + if ( src==m_play ) { + gst_message_parse_state_changed( msg, &old, &cur, &pending ); + if ( cur==old ) + kdDebug() << "GST_MESSAGE_STATE_UNCHANGED" << endl; + else { + kdDebug() << "GST_MESSAGE_STATE_CHANGED" << endl; + m_status = cur; + gstStateChanged(); + } + } + break; + case GST_MESSAGE_ERROR: + gst_message_parse_error( msg, &error, &debug ); + emit setStatusBarText(i18n("Error")); + if (m_url != m_logoPath) { + errorMessage = error->message; + errorDetails = debug; + QTimer::singleShot( 0, this, SLOT(slotEngineError()) ); + } + g_error_free( error ); + g_free( debug ); + gst_element_set_state( m_play, GST_STATE_NULL ); + //gst_element_set_state( m_play, GST_STATE_READY ); + break; + case GST_MESSAGE_EOS: + if ( m_current < m_playlist.count()-1 ) { + m_current ++; + slotPlay(); + } + else { + kdDebug() << "GStreamerPart: Playback finished" << endl; + m_timer->stop(); + //m_status = GST_STATE_READY; + if (m_url != m_logoPath) + emit signalTrackFinished(); + } + break; + case GST_MESSAGE_TAG: + GstTagList *tagList; + gst_message_parse_tag( msg, &tagList ); + foundTag( tagList ); + break; + case GST_MESSAGE_WARNING: kdDebug() << "GST_MESSAGE_WARNING" << endl; break; + case GST_MESSAGE_INFO: kdDebug() << "GST_MESSAGE_INFO" << endl; break; + case GST_MESSAGE_BUFFERING: kdDebug() << "GST_MESSAGE_BUFFERING" << endl; break; + case GST_MESSAGE_STATE_DIRTY: kdDebug() << "GST_MESSAGE_STATE_DIRTY" << endl; break; + case GST_MESSAGE_STEP_DONE: kdDebug() << "GST_MESSAGE_STEP_DONE" << endl; break; + case GST_MESSAGE_CLOCK_PROVIDE: kdDebug() << "GST_MESSAGE_CLOCK_PROVIDE" << endl; break; + case GST_MESSAGE_CLOCK_LOST: kdDebug() << "GST_MESSAGE_CLOCK_LOST" << endl; break; + case GST_MESSAGE_NEW_CLOCK: kdDebug() << "GST_MESSAGE_NEW_CLOCK" << endl; break; + case GST_MESSAGE_STRUCTURE_CHANGE: kdDebug() << "GST_MESSAGE_STRUCTURE_CHANGE" << endl; break; + case GST_MESSAGE_STREAM_STATUS: kdDebug() << "GST_MESSAGE_STREAM_STATUS" << endl; break; + case GST_MESSAGE_APPLICATION: kdDebug() << "GST_MESSAGE_APPLICATION" << endl; break; + case GST_MESSAGE_ELEMENT: kdDebug() << "GST_MESSAGE_ELEMENT" << endl; break; + case GST_MESSAGE_SEGMENT_START: kdDebug() << "GST_MESSAGE_SEGMENT_START" << endl; break; + case GST_MESSAGE_SEGMENT_DONE: kdDebug() << "GST_MESSAGE_SEGMENT_DONE" << endl; break; + case GST_MESSAGE_DURATION: kdDebug() << "GST_MESSAGE_DURATION" << endl; break; + default: + kdDebug() << "GST_MESSAGE_OTHER" << endl; + } + gst_message_unref( msg ); +} + + + +void GStreamerPart::slotEngineError() +{ + KMessageBox::detailedError( NULL, errorMessage, errorDetails ); +} + + + +void GStreamerPart::foundTag( GstTagList *taglist ) +{ + kdDebug() << " Received meta tags..." << endl; + + char* string; + guint intVal = 0; + bool success = false; + + if ( gst_tag_list_get_string(taglist, GST_TAG_TITLE, &string) && string ) { + m_title = string; + success = true; + } + if ( gst_tag_list_get_string( taglist, GST_TAG_ARTIST, &string) && string ) { + m_artist = string; + success = true; + } + if ( gst_tag_list_get_string(taglist, GST_TAG_ALBUM, &string) && string ) { + m_album = string; + success = true; + } + if ( gst_tag_list_get_string(taglist, GST_TAG_GENRE, &string) && string ) { + m_genre = string; + success = true; + } + if ( gst_tag_list_get_uint(taglist, GST_TAG_TRACK_NUMBER, &intVal) && intVal > 0 ) { + m_track = QString::number(intVal); + success = true; + } + if ( gst_tag_list_get_string(taglist, GST_TAG_COMMENT, &string) && string ) { + m_comment = string; + success = true; + } + if ( gst_tag_list_get_string(taglist, GST_TAG_AUDIO_CODEC, &string) && string ) + m_audioCodec = string; + if ( gst_tag_list_get_string(taglist, GST_TAG_VIDEO_CODEC, &string) && string ) + m_videoCodec = string; + + if (success) + processMetaInfo(); +} diff --git a/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.desktop b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.desktop new file mode 100644 index 0000000..8a2015a --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.desktop @@ -0,0 +1,45 @@ +[Desktop Entry] +Encoding=UTF-8 +Icon=gstreamer +Name=Kaffeine-GStreamer +Name[af]=Kaffeine=GStremer +Name[nb]=Kaffeine GStreamer +Name[pa]=ਕੈਫ਼ੀਨ-ਜੀਸਟਰੀਮਰ +Name[se]=Kaffeine GStreamer +Name[sv]=Kaffeine-Gstreamer +Name[xx]=xxKaffeine-GStreamerxx +Comment=A Kaffeine engine based on GStreamer. +Comment[af]='n Kaffeine enjin wat op GStreamer gebaseer is. +Comment[bg]=Ядро за Kaffeine, базирано върху GStreamer. +Comment[ca]=Una part de reproductor basada en GStreamer. +Comment[cs]=Část přehrávače založená na GStreameru. +Comment[da]=En Kaffeine-grænseflade baseret på Gstreamer. +Comment[de]=Ein auf GStreamer basierendes Programmmodul. +Comment[el]=Μία μηχανή του Kaffeine βασισμένη στο GStreamer. +Comment[et]=Kaffeine mootor GStreameri põhjal. +Comment[ga]=Inneall Kaffeine bunaithe ar GStreamer. +Comment[gl]=Un motor para Kaffeine baseado en GStreamer. +Comment[hu]=GStreamer-alapú Kaffeine-alrendszer. +Comment[it]=Un motore di Kaffeine basato su GStreamer. +Comment[ja]=GStreamer に基づく Kaffeine エンジン +Comment[lt]=Kaffeine variklis, paremtas GStreamer. +Comment[nb]=En spillermotor basert på GStreamer. +Comment[nl]=Een Kaffeine-engine gebaseerd op GStreamer. +Comment[pa]=ਜੀਸਟੀਮਰ ਅਧਾਰਤ ਇੱਕ ਕੈਫ਼ੀਨ ਇੰਜਣ ਹੈ। +Comment[pl]=Moduł odtwarzacza do Kaffeine bazujący na GStreamer. +Comment[pt]=Um motor Kaffeine no GStreamer. +Comment[pt_BR]=Um componente de reprodução Kaffeine baseado no GStreamer. +Comment[se]=Čuojanmohtor vuođđoduvvon GStreameris. +Comment[sr]=Кафеинов мотор заснован на GStreamer-у. +Comment[sr@Latn]=Кафеинов мотор заснован на GStreamer-у. +Comment[sv]=Ett Kaffeine-gränssnitt baserad på Gstreamer. +Comment[tg]=Барномаи Kaffeine дар асоси GStreamer. +Comment[th]=โปรแกรมประมวลผลของ Kaffeine โดยใช้ GStreamer +Comment[tr]=GStreamer tabanlı bir Kaffeine motoru. +Comment[uk]=Рушій Kaffeine, оснований на GStreamer. +Comment[xx]=xxA Kaffeine engine based on GStreamer.xx +MimeType=application/x-ogg;video/x-matroska;audio/x-matroska;video/mpeg;video/msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-avi;video/x-fli;video/x-flic;video/x-ms-asf;video/x-ms-wmv;video/x-msvideo;application/x-mplayer2;application/smil;application/x-kaffeine;audio/x-musepack; +ServiceTypes=KParts/ReadOnlyPart,KaffeinePart +Type=Service +X-KDE-Library=libgstreamerpart +InitialPreference=1 diff --git a/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.h b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.h new file mode 100644 index 0000000..0f5c276 --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.h @@ -0,0 +1,154 @@ +/* + * gstreamer_part.h + * + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GSTREAMER_PART_H +#define GSTREAMER_PART_H + +#include <kparts/factory.h> + +#include <gst/gst.h> + +#include "kaffeinepart.h" + +#include "timer.h" +#include "video.h" +#include "videosettings.h" +#include "gstreamerconfig.h" + + + +class GStreamerPart : public KaffeinePart +{ + Q_OBJECT +public: + GStreamerPart( QWidget*, const char*, QObject*, const char*, const QStringList& ); + virtual ~GStreamerPart(); + + /* + *Reimplement from KaffeinePart + */ + bool isPlaying(); + bool isPaused(); + uint volume() const; /* percent */ + uint position() const; /* percent */ + bool hasVideo(); + + bool closeURL(); + static KAboutData* createAboutData(); + +public slots: + /* + * Reimplement from KaffeinePart + */ + bool openURL( const MRL& mrl ); + void slotPlay(); + void slotTogglePause( bool ); + void slotSetVolume( uint ); /* percent */ + void slotSetPosition( uint ); /* percent */ + void slotStop(); + void slotMute(); + void slotPrepareForFullscreen( bool fullscreen ); + +private slots: + void slotNext(); + void slotPrevious(); + void slotVolume( int ); + void slotSaturation( int ); /* -1000...1000 */ + void slotHue( int ); /* -1000...1000 */ + void slotContrast( int ); /* -1000...1000 */ + void slotBrightness( int ); /* -1000...1000 */ + void slotContextMenu( const QPoint& pos ); + void slotInfo(); + void slotSetVisualPlugin( const QString& ); + void slotVideoSettings(); + void slotConfigDialog(); + void slotEngineError(); + + void slotReadBus(); + +private: + bool initGStreamer(); + bool createPlaybin(); + void deletePlaybin(); + void loadConfig(); + void saveConfig(); + void gstPlay( const QString& trackUrl, const QString& subtitleUrl ); + void initActions(); + void showError(); + void gstStateChanged(); + void foundTag( GstTagList *taglist ); + void processMetaInfo(); + void setAudioSink( QString sinkName ); + void setDevice( QString device ); + +private: + GstElement *m_play; + GstElement *m_videosink; + GstElement *m_audiosink; + GstElement *m_visual; + GstBus *bus; + GstState m_status; + + QTimer busTimer; + + VideoWindow* m_video; + Timer* m_timer; + VideoSettings* m_videoSettings; + GStreamerConfig* m_gstConfig; + QSlider* m_volume; + KSelectAction* m_audioVisual; + + MRL m_mrl; + QValueList<MRL> m_playlist; + uint m_current; + bool m_mute; + QString m_logoPath; + + QString m_errorMsg; + QString m_errorDetails; + + QString m_url; + QString m_title; + QString m_artist; + QString m_album; + QString m_track; + QString m_year; + QString m_genre; + QString m_comment; + QString m_audioCodec; + QString m_videoCodec; + QString errorMessage; + QString errorDetails; + + QString m_audioSinkName; + QString m_videoSinkName; + QString m_visualPluginName; + QStringList m_audioVisualPluginList; + QStringList m_audioPluginList; + QStringList m_videoPluginList; + QString m_device, currentDevice; + int m_savedVolume; + int muteVolume; + + KToolBar* m_posToolbar; +}; + +#endif /* GSTREAMER_PART_H */ diff --git a/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.rc b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.rc new file mode 100644 index 0000000..2dcfc56 --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.rc @@ -0,0 +1,105 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="gstreamer_part" version="5"> +<MenuBar> + <Menu name="player"><text>&Player</text> + <Action name="player_play"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> + <Action name="player_previous"/> + <Separator/> + <Action name="audio_visual"/> + <Action name="audio_mute"/> + <Separator/> + <Menu name="video_aspect"><text>&Aspect Ratio</text> + <Action name="aspect_auto"/> + <Action name="aspect_43"/> + <Action name="aspect_anamorphic"/> + <Action name="aspect_dvb"/> + <Action name="aspect_square"/> + </Menu> + <Action name="video_settings"/> + <Action name="player_track_info"/> + </Menu> + <Menu name="settings"> + <Action name="settings_gst_parameter"/> + </Menu> +</MenuBar> +<ToolBar name="gstControlsToolBar" position="Bottom"><text>Controls Toolbar</text> + <Action name="player_previous"/> + <Action name="player_play"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> +</ToolBar> +<ToolBar name="gstVolumeToolBar"><text>Volume Toolbar</text> + <Action name="audio_volume"/> + <Action name="audio_mute"/> +</ToolBar> +<ToolBar name="gstPositionToolBar" fullWidth="true" position="Bottom"><text>Position Toolbar</text> + <Action name="player_position"/> + <Separator/> + <Action name="player_playtime"/> +</ToolBar> +<Menu name="context_menu"> + <Action name="player_minimal_mode"/> + <Separator/> + <Action name="player_play"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> + <Action name="player_previous"/> + <Separator/> + <Action name="audio_visual"/> + <Action name="audio_mute"/> + <Separator/> + <Menu name="video_aspect"><text>&Aspect Ratio</text> + <Action name="aspect_auto"/> + <Action name="aspect_43"/> + <Action name="aspect_anamorphic"/> + <Action name="aspect_dvb"/> + <Action name="aspect_square"/> + </Menu> + <Action name="video_settings"/> + <Action name="player_track_info"/> +</Menu> +<State name="disable_all"> + <enable> + <Action name="player_play"/> + </enable> + <disable> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_position"/> + <Action name="player_playtime"/> + <Action name="player_next"/> + <Action name="player_previous"/> + </disable> +</State> +<State name="not_playing"> + <enable> + <Action name="player_play"/> + <Action name="player_next"/> + <Action name="player_previous"/> + </enable> + <disable> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_position"/> + <Action name="player_playtime"/> + </disable> +</State> +<State name="playing"> + <enable> + <Action name="player_next"/> + <Action name="player_previous"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_position"/> + <Action name="player_playtime"/> + </enable> + <disable> + <Action name="player_play"/> + </disable> +</State> +</kpartgui>
\ No newline at end of file diff --git a/kaffeine/src/player-parts/gstreamer-part/gstreamerconfig.cpp b/kaffeine/src/player-parts/gstreamer-part/gstreamerconfig.cpp new file mode 100644 index 0000000..19025bc --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/gstreamerconfig.cpp @@ -0,0 +1,117 @@ +/* + * gstreamerconfig.cpp - config dialog for gstreamer parameters + * + * Copyright (C) 2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kcombobox.h> +#include <klineedit.h> +#include <kseparator.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> + +#include <qlayout.h> +#include <qlabel.h> + +#include "gstreamerconfig.h" +#include "gstreamerconfig.moc" + + +GStreamerConfig::GStreamerConfig(const QStringList& audioDrivers, const QStringList& videoDrivers) : + KDialogBase(KDialogBase::IconList, i18n("GStreamer Engine Parameters"), KDialogBase::Ok|KDialogBase::Cancel, + KDialogBase::Cancel) + +{ + setInitialSize(QSize(400,300), true); + + QFrame* frame = NULL; + QGridLayout* layout = NULL; + QLabel* label = NULL; + + //Audio Page + frame = addPage(i18n("Audio"), i18n("Audio Options"), KGlobal::iconLoader()->loadIcon("sound", KIcon::Panel, + KIcon::SizeMedium)); + layout = new QGridLayout(frame, 10, 2); + layout->setMargin(10); + layout->setSpacing(10); + m_audioDriverBox = new KComboBox(frame); + m_audioDriverBox->insertStringList(audioDrivers); + label = new QLabel(i18n("Prefered audio driver"), frame); + layout->addWidget(label, 1, 0); + layout->addWidget(m_audioDriverBox, 1, 1); + layout->addMultiCellWidget(new KSeparator(KSeparator::Horizontal, frame), 2, 2, 0, 1); + + //Video Page + frame = addPage(i18n("Video"), i18n("Video Options"), KGlobal::iconLoader()->loadIcon("video", KIcon::Panel, + KIcon::SizeMedium)); + layout = new QGridLayout(frame, 10, 2); + layout->setMargin(10); + layout->setSpacing(10); + m_videoDriverBox = new KComboBox(frame); + m_videoDriverBox->insertStringList(videoDrivers); + label = new QLabel(i18n("Prefered video driver")+ "*", frame); + layout->addWidget(label, 1, 0); + layout->addWidget(m_videoDriverBox, 1, 1); + layout->addMultiCellWidget(new KSeparator(KSeparator::Horizontal, frame), 2, 2, 0, 1); + layout->addWidget(new QLabel(QString("<small>") + i18n("* Restart required!") + "</small>", frame), 10, 1); + + //Media page + frame = addPage(i18n("Media"), i18n("Media Options"), KGlobal::iconLoader()->loadIcon("cdrom_unmount", KIcon::Panel, + KIcon::SizeMedium)); + layout = new QGridLayout(frame, 10, 2); + layout->setMargin(10); + layout->setSpacing(10); + m_driveEdit = new KLineEdit(frame); + label = new QLabel(i18n("CD, VCD, DVD drive"), frame); + layout->addWidget(label, 1, 0); + layout->addWidget(m_driveEdit, 1, 1); + layout->addMultiCellWidget(new KSeparator(KSeparator::Horizontal, frame), 2, 2, 0, 1); +} + +GStreamerConfig::~GStreamerConfig() +{} + +QString GStreamerConfig::getAudioDriver() const +{ + return m_audioDriverBox->currentText(); +} + +QString GStreamerConfig::getVideoDriver() const +{ + return m_videoDriverBox->currentText(); +} + +QString GStreamerConfig::getDrive() const +{ + return m_driveEdit->text(); +} + +void GStreamerConfig::setDrive(const QString& drive) +{ + m_driveEdit->setText(drive); +} + +void GStreamerConfig::setAudioDriver(const QString& name) +{ + m_audioDriverBox->setCurrentText(name); +} + +void GStreamerConfig::setVideoDriver(const QString& name) +{ + m_videoDriverBox->setCurrentText(name); +} diff --git a/kaffeine/src/player-parts/gstreamer-part/gstreamerconfig.h b/kaffeine/src/player-parts/gstreamer-part/gstreamerconfig.h new file mode 100644 index 0000000..f85d882 --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/gstreamerconfig.h @@ -0,0 +1,56 @@ +/* + * gstreamerconfig.h - config dialog for gstreamer parameters + * + * Copyright (C) 2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GSTREAMERCONFIG_H +#define GSTREAMERCONFIG_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kdialogbase.h> + +class QString; +class QStringList; +class KComboBox; +class KLineEdit; + +class GStreamerConfig : public KDialogBase +{ + Q_OBJECT +public: + GStreamerConfig(const QStringList& audioDrivers, const QStringList& videoDrivers); + ~GStreamerConfig(); + + QString getAudioDriver() const; + QString getVideoDriver() const; + QString getDrive() const; + + void setDrive(const QString& drive); + void setAudioDriver(const QString& name); + void setVideoDriver(const QString& name); + +private: + KComboBox* m_audioDriverBox; + KComboBox* m_videoDriverBox; + KLineEdit* m_driveEdit; +}; + +#endif /* GSTREAMERCONFIG_H */ diff --git a/kaffeine/src/player-parts/gstreamer-part/timer.cpp b/kaffeine/src/player-parts/gstreamer-part/timer.cpp new file mode 100644 index 0000000..93d0116 --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/timer.cpp @@ -0,0 +1,197 @@ +/* + * timer.cpp + * + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * heavily based on kiss by Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <klocale.h> +#include <kdebug.h> + +#include <qslider.h> +#include <qlabel.h> +#include <qtimer.h> + +#include "timer.h" + +#include "timer.moc" + + + +Timer::Timer() : QObject() +{ + m_label = new QLabel( "0:00 / 0:00", 0 ); + m_slider = new QSlider( Qt::Horizontal, 0 ); + m_slider->setMinValue( 0 ); + m_slider->setEnabled( false ); + + connect( &updateTimer, SIGNAL(timeout()), SLOT(slotUpdate()) ); + + m_pos = GST_CLOCK_TIME_NONE; + m_len = GST_CLOCK_TIME_NONE; + + m_play = NULL; + seeking = false; + + connect( m_slider, SIGNAL(sliderPressed()), this, SLOT(slotSeekStart()) ); + connect( m_slider, SIGNAL(sliderReleased()), this, SLOT(slotSeek()) ); +} + + + +void Timer::setPlaybin( GstElement *play ) +{ + m_play = play; + m_slider->setEnabled( false ); + m_slider->setValue( 0 ); +} + + + +Timer::~Timer() +{ +} + + + +static char *niceTime( guint64 t ) +{ + if ( t >= GST_SECOND*60*60 ) { + return g_strdup_printf("%d:%02d:%02d", + (int) (t / (GST_SECOND * 60 * 60)), + (int) ((t / (GST_SECOND * 60)) % 60), + (int) ((t / GST_SECOND) % 60)); + } + else { + return g_strdup_printf("%d:%02d", + (int) ((t / (GST_SECOND * 60)) % 60), + (int) ((t / GST_SECOND) % 60)); + } +} + + + +void Timer::slotSeekStart() +{ + seeking = true; +} + + + +void Timer::slotSeek() +{ + if ( !m_play ) + return; + + gint64 gval = m_slider->value(); + GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_KEY_UNIT); + gst_element_seek( m_play, 1.0, GST_FORMAT_TIME, flags, + GST_SEEK_TYPE_SET, gval*GST_SECOND, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + gst_element_get_state( m_play, NULL, NULL, 100*GST_MSECOND); + seeking = false; +} + + + +void Timer::seekPercent( uint percent ) +{ + //slotSeek( (m_slider->maxValue() * percent) / 100 ); +} + + + +void Timer::slotUpdate() +{ + gint64 t; + GstFormat fmt = GST_FORMAT_TIME; + char *txt; + + if ( seeking ) + return; + + if ( !m_play ) + return; + + if ( gst_element_query_duration( m_play, &fmt, &t ) ) { + m_len = t; + m_slider->setMaxValue( m_len / GST_SECOND ); + } + + if ( !gst_element_query_position( m_play, &fmt, &t ) ) + return; + + m_pos = t; + + m_currentTimeMS = m_pos/1000000; + m_totalTimeMS = m_len/1000000; + + if ( GST_CLOCK_TIME_IS_VALID (m_len) ) { + gchar *t1 = niceTime( m_pos ), *t2 = niceTime( m_len ); + txt = g_strdup_printf( "%s / %s", t1, t2) ; + g_free(t1); + g_free(t2); + } + else { + txt = niceTime( m_pos ); + } + m_label->setText( txt ); + g_free( txt ); + + m_slider->setValue( m_pos / GST_SECOND ); +} + + + +void Timer::start() +{ + if ( updateTimer.isActive() ) + return; + m_slider->setEnabled( true); + seeking = false; + updateTimer.start( 1000 ); +} + + + +void Timer::stop() +{ + if ( !updateTimer.isActive() ) + return; + updateTimer.stop(); + m_slider->setEnabled( false ); + m_slider->setValue(0); +} + + + +QLabel* Timer::getLabel() const +{ + return m_label; +} + + + +QSlider* Timer::getSlider() const +{ + return m_slider; +} diff --git a/kaffeine/src/player-parts/gstreamer-part/timer.h b/kaffeine/src/player-parts/gstreamer-part/timer.h new file mode 100644 index 0000000..9ee5bff --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/timer.h @@ -0,0 +1,77 @@ +/* + * timer.h + * + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * heavily based on kiss by Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef TIMER_H +#define TIMER_H + +#include <qobject.h> + +#include <gst/gst.h> + +class QSlider; +class QLabel; +class QTimer; +class QTime; + + + +class Timer : public QObject +{ + Q_OBJECT + +public: + Timer(); + ~Timer (); + + void start(); + void stop(); + void setPlaybin( GstElement *play ); + + long getTotalTimeMS() { return m_totalTimeMS; } + long getCurrentTimeMS() { return m_currentTimeMS; } + + void seekPercent(uint percent); + + QLabel* getLabel() const; + QSlider* getSlider() const; + +public slots: + void slotSeekStart(); + void slotSeek(); + +private slots: + void slotUpdate(); + +private: + QTimer updateTimer; + QLabel *m_label; + QSlider *m_slider; + GstElement *m_play; + bool seeking; + + long m_currentTimeMS; + long m_totalTimeMS; + guint64 m_len, m_pos; +}; + +#endif /* TIMER_H */ diff --git a/kaffeine/src/player-parts/gstreamer-part/video.cpp b/kaffeine/src/player-parts/gstreamer-part/video.cpp new file mode 100644 index 0000000..0fc76cd --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/video.cpp @@ -0,0 +1,332 @@ +/* + * video.cpp + * + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * heavily based on kiss by Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qpainter.h> +#include <qtimer.h> +#include <qcursor.h> + +#include <kstatusbar.h> +#include <ktoolbar.h> +#include <kmenubar.h> +#include <kdebug.h> + +#include <gst/interfaces/xoverlay.h> + +#include "video.h" + +#include "video.moc" + + + +VideoWindow::VideoWindow( QWidget *parent, GstElement *_element ) + : QWidget (parent), m_width(0), m_height(0), m_aspectRatio(AUTO) +{ + m_play = NULL; + + gst_object_ref( GST_OBJECT (_element) ); + m_element = _element; + + setPaletteBackgroundColor(QColor(0,0,0)); //black + setUpdatesEnabled(false); + + connect( &m_mouseHideTimer, SIGNAL(timeout()), this, SLOT(slotHideMouse()) ); +} + + + +void VideoWindow::setPlaybin( GstElement *play ) +{ + m_play = play; +} + + + +VideoWindow::~VideoWindow() +{ + if ( m_element && GST_IS_X_OVERLAY(m_element) ) { + gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_element), 0 ); + } + + gst_object_unref( GST_OBJECT(m_element) ); + kdDebug() << "VideoWindow: destructed" << endl; +} + + + +void VideoWindow::refresh() +{ + /* expose overlay */ + if ( m_element && GST_IS_X_OVERLAY(m_element) ) { + gst_x_overlay_set_xwindow_id( GST_X_OVERLAY (m_element), winId() ); + gst_x_overlay_expose( GST_X_OVERLAY (m_element) ); + } +} + + + +void VideoWindow::paintEvent(QPaintEvent */*event*/) +{ + refresh(); +} + + + +void VideoWindow::mousePressEvent(QMouseEvent* mev) +{ + if ( mev->button()==Qt::RightButton ) { + emit signalRightClick( mev->globalPos() ); + mev->accept(); + } + else { + mev->ignore(); + } +} + + + +void VideoWindow::newCapsset( const GstCaps *caps ) +{ + const GstStructure *s; + m_width = 0; + m_height = 0; + + s = gst_caps_get_structure(caps, 0); + if (s) { + const GValue *par; + + gst_structure_get_int(s, "width", &m_width); + gst_structure_get_int(s, "height", &m_height); + if ( (par = gst_structure_get_value (s, "pixel-aspect-ratio")) ) { + int num = gst_value_get_fraction_numerator(par), + den = gst_value_get_fraction_denominator(par); + + if (num > den) + m_width = (int) ((float) num * m_width / den); + else + m_height = (int) ((float) den * m_height / num); + } + } + + QSize frame = getFrameSize(); + correctByAspectRatio( frame ); + emit signalNewFrameSize( frame ); + //set correct geometry + setGeometry(); +} + + + +void VideoWindow::newState() +{ + if ( !m_play ) + return; + + const GList *list = NULL; + + g_object_get( G_OBJECT(m_play), "stream-info", &list, NULL ); + for ( ; list != NULL; list = list->next ) { + GObject *info = (GObject *)list->data; + gint type; + GParamSpec *pspec; + GEnumValue *val; + GstPad *pad = NULL; + + if ( !info ) + continue; + + g_object_get( info, "type", &type, NULL ); + pspec = g_object_class_find_property( G_OBJECT_GET_CLASS (info), "type" ); + val = g_enum_get_value( G_PARAM_SPEC_ENUM(pspec)->enum_class, type ); + + if ( !g_strcasecmp(val->value_nick, "video") ) { + GstCaps *caps; + g_object_get( info, "object", &pad, NULL ); + if ( (caps=gst_pad_get_negotiated_caps(pad)) ) { + newCapsset( caps ); + gst_caps_unref (caps); + return; + } + } + } + m_width = 0; + m_height = 0; + QSize frame = getFrameSize(); + correctByAspectRatio( frame ); + emit signalNewFrameSize( frame ); + //set correct geometry + setGeometry(); +} + + + +void VideoWindow::setGeometry( int, int, int, int ) +{ + QSize frame = getFrameSize(); + QSize window = parentWidget()->size(); + int x = 0, y = 0, width = 0, height = 0; + + if ( frame.width() == 0 || frame.height() == 0 ) { + QWidget::setGeometry(0, 0, window.width(), window.height()); + return; + } + + correctByAspectRatio( frame ); + + float frameAspect = (float)frame.width() / (float)frame.height(); + float windowAspect = (float)window.width() / (float)window.height(); + + if ( frameAspect >= windowAspect ) { + width = window.width(); + height = (int)((float)width / frameAspect); + y = (window.height() - height) / 2; + } + else { + height = window.height(); + width = (int)((float)height * frameAspect); + x = (window.width() - width) / 2; + } + + //kdDebug() << "VideoWindow::setGeometry: " << x << " : " << y << " : " << width << " : " << height << endl; + QWidget::setGeometry( x, y, width, height ); +} + + + +void VideoWindow::setGeometry() +{ + setGeometry(0, 0, 0, 0); +} + + + +void VideoWindow::correctByAspectRatio( QSize& frame ) +{ + float factor = 0; + + switch ( m_aspectRatio ) { + case AUTO: return; + case FOURBYTHREE: factor = 4.0 / 3.0; break; + case ANAMORPHIC: factor = 16.0 / 9.0; break; + case DVB: factor = 2.11; break; + case SQUARE: factor = 1.0; break; + } + + float frameAspect = (float)frame.width() / (float)frame.height(); + factor = factor / frameAspect; + if ( factor > 1.0 ) + frame.setWidth((int)((float)frame.width() * factor)); + else + frame.setHeight((int)((float)frame.height() / factor)); +} + + + +void VideoWindow::slotAspectRatioAuto() +{ + m_aspectRatio = AUTO; + QSize frame = getFrameSize(); + correctByAspectRatio( frame ); + emit signalNewFrameSize( frame ); + setGeometry(); +} + + + +void VideoWindow::slotAspectRatio4_3() +{ + m_aspectRatio = FOURBYTHREE; + QSize frame = getFrameSize(); + correctByAspectRatio( frame ); + emit signalNewFrameSize( frame ); + setGeometry(); +} + + + +void VideoWindow::slotAspectRatioAnamorphic() +{ + m_aspectRatio = ANAMORPHIC; + QSize frame = getFrameSize(); + correctByAspectRatio( frame ); + emit signalNewFrameSize( frame ); + setGeometry(); +} + + + +void VideoWindow::slotAspectRatioDVB() +{ + m_aspectRatio = DVB; + QSize frame = getFrameSize(); + correctByAspectRatio( frame ); + emit signalNewFrameSize( frame ); + setGeometry(); +} + + + +void VideoWindow::slotAspectRatioSquare() +{ + m_aspectRatio = SQUARE; + QSize frame = getFrameSize(); + correctByAspectRatio( frame ); + emit signalNewFrameSize( frame ); + setGeometry(); +} + +/******** mouse cursor hiding on fullscreen ****/ + +void VideoWindow::startMouseHideTimer() +{ + m_mouseHideTimer.start( 5000 ); + setMouseTracking( true ); +} + + + +void VideoWindow::stopMouseHideTimer() +{ + m_mouseHideTimer.stop(); + setMouseTracking( false ); + setCursor( QCursor(Qt::ArrowCursor) ); +} + + + +void VideoWindow::slotHideMouse() +{ + setCursor( QCursor(Qt::BlankCursor) ); +} + + + +void VideoWindow::mouseMoveEvent( QMouseEvent *mev ) +{ + if ( cursor().shape() == Qt::BlankCursor ) + setCursor( QCursor(Qt::ArrowCursor) ); + mev->ignore(); +} diff --git a/kaffeine/src/player-parts/gstreamer-part/video.h b/kaffeine/src/player-parts/gstreamer-part/video.h new file mode 100644 index 0000000..5878066 --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/video.h @@ -0,0 +1,95 @@ +/* + * video.h + * + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * heavily based on kiss by Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef VIDEO_H +#define VIDEO_H + +#include <qwidget.h> +#include <kapplication.h> + +#include <qsize.h> + +#include <gst/gst.h> + + + +class VideoWindow : public QWidget +{ + Q_OBJECT + +public: + VideoWindow( QWidget *parent, GstElement *element ); + ~VideoWindow (); + + enum AspectRatio { + AUTO, + FOURBYTHREE, + ANAMORPHIC, + DVB, + SQUARE + }; + + void newState(); + void newCapsset( const GstCaps *caps ); + void setPlaybin( GstElement *play ); + + void refresh(); + + QSize getFrameSize() { return QSize(m_width, m_height); } + + void startMouseHideTimer(); + void stopMouseHideTimer(); + +signals: + void signalNewFrameSize( const QSize& ); + void signalRightClick( const QPoint& ); + +public slots: +// void setGeometry(const QRect&); + void setGeometry(); + void setGeometry( int x, int y, int width, int heigth ); + void slotAspectRatioAuto(); + void slotAspectRatio4_3(); + void slotAspectRatioAnamorphic(); + void slotAspectRatioDVB(); + void slotAspectRatioSquare(); + +private slots: + void slotHideMouse(); + +protected: + void paintEvent( QPaintEvent *event ); + void mousePressEvent( QMouseEvent* ); + void mouseMoveEvent( QMouseEvent* ); + +private: + void correctByAspectRatio( QSize &frame ); + +private: + GstElement *m_element, *m_play; + int m_width, m_height; + AspectRatio m_aspectRatio; + QTimer m_mouseHideTimer; +}; + +#endif /* VIDEO_H */ diff --git a/kaffeine/src/player-parts/gstreamer-part/videosettings.cpp b/kaffeine/src/player-parts/gstreamer-part/videosettings.cpp new file mode 100644 index 0000000..2e59359 --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/videosettings.cpp @@ -0,0 +1,95 @@ +/* + * videosettings.cpp + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <klocale.h> + +#include <qslider.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qnamespace.h> + +#include "videosettings.h" +#include "videosettings.moc" + + +VideoSettings::VideoSettings(int hue, int sat, int contr, int bright, QWidget *parent, const char *name) + : KDialogBase(KDialogBase::Plain, i18n("Video Settings"), KDialogBase::Default | KDialogBase::Close, KDialogBase::Close, parent, name, false) +{ + setInitialSize(QSize(450,200), true); + QWidget* page = plainPage(); + + QGridLayout* grid = new QGridLayout(page, 4, 2); + grid->setSpacing(5); + + QLabel* hueText = new QLabel(i18n("Hue"), page); + hueText->setAlignment(AlignRight); + m_hueSlider = new QSlider(Qt::Horizontal, page); + m_hueSlider->setRange(-1000, 1000); + m_hueSlider->setSteps(10, 100); + m_hueSlider->setValue(hue); + connect(m_hueSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewHue(int))); + grid->addWidget(hueText, 0, 0); + grid->addWidget(m_hueSlider, 0, 1); + + QLabel* satText = new QLabel(i18n("Saturation"), page); + satText->setAlignment(AlignRight); + m_satSlider = new QSlider(Qt::Horizontal, page); + m_satSlider->setRange(-1000, 1000); + m_satSlider->setSteps(10, 100); + m_satSlider->setValue(sat); + connect(m_satSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewSaturation(int))); + grid->addWidget(satText, 1, 0); + grid->addWidget(m_satSlider, 1, 1); + + QLabel* contrastText = new QLabel(i18n("Contrast"), page); + contrastText->setAlignment(AlignRight); + m_contrastSlider = new QSlider(Qt::Horizontal, page); + m_contrastSlider->setRange(-1000, 1000); + m_contrastSlider->setSteps(10, 100); + m_contrastSlider->setValue(contr); + connect(m_contrastSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewContrast(int))); + grid->addWidget(contrastText, 2, 0); + grid->addWidget(m_contrastSlider, 2, 1); + + QLabel* brightText = new QLabel(i18n("Brightness"), page); + brightText->setAlignment(AlignRight); + m_brightSlider = new QSlider(Qt::Horizontal, page); + m_brightSlider->setRange(-1000, 1000); + m_brightSlider->setSteps(10, 100); + m_brightSlider->setValue(bright); + connect(m_brightSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewBrightness(int))); + grid->addWidget(brightText, 3, 0); + grid->addWidget(m_brightSlider, 3, 1); + + connect(this, SIGNAL(defaultClicked()), this, SLOT(slotSetDefaultValues())); +} + +VideoSettings::~VideoSettings() +{ + +} + +void VideoSettings::slotSetDefaultValues() +{ + m_hueSlider->setValue(1000); + m_satSlider->setValue(0); + m_contrastSlider->setValue(0); + m_brightSlider->setValue(0); +} diff --git a/kaffeine/src/player-parts/gstreamer-part/videosettings.h b/kaffeine/src/player-parts/gstreamer-part/videosettings.h new file mode 100644 index 0000000..62be08e --- /dev/null +++ b/kaffeine/src/player-parts/gstreamer-part/videosettings.h @@ -0,0 +1,58 @@ +/* + * videosettings.h + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef VIDEOSETTINGS_H +#define VIDEOSETTINGS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kdialogbase.h> + +class QSlider; + +class VideoSettings : public KDialogBase +{ + + Q_OBJECT + +public: + VideoSettings(int hue, int sat, int contr, int bright, QWidget *parent=0, const char *name=0); + ~VideoSettings(); + +signals: + void signalNewHue(int); + void signalNewSaturation(int); + void signalNewContrast(int); + void signalNewBrightness(int); + +private slots: + + void slotSetDefaultValues(); + +private: + QSlider* m_hueSlider; + QSlider* m_satSlider; + QSlider* m_contrastSlider; + QSlider* m_brightSlider; +}; + +#endif /* VIDEOSETTINGS_H */ diff --git a/kaffeine/src/player-parts/kaffeine-part/Makefile.am b/kaffeine/src/player-parts/kaffeine-part/Makefile.am new file mode 100644 index 0000000..7202c10 --- /dev/null +++ b/kaffeine/src/player-parts/kaffeine-part/Makefile.am @@ -0,0 +1,12 @@ +lib_LTLIBRARIES = libkaffeinepart.la + +INCLUDES = $(all_includes) + +kaffeineincludedir = $(includedir)/kaffeine +kaffeineinclude_HEADERS = kaffeinepart.h mrl.h + +METASOURCES = AUTO + +libkaffeinepart_la_SOURCES = kaffeinepart.cpp mrl.cpp playlistimport.cpp +libkaffeinepart_la_LIBADD = $(LIB_KPARTS) $(all_libraries) +libkaffeinepart_la_LDFLAGS = -no-undefined -avoid-version diff --git a/kaffeine/src/player-parts/kaffeine-part/kaffeinepart.cpp b/kaffeine/src/player-parts/kaffeine-part/kaffeinepart.cpp new file mode 100644 index 0000000..4be8b73 --- /dev/null +++ b/kaffeine/src/player-parts/kaffeine-part/kaffeinepart.cpp @@ -0,0 +1,58 @@ +/* + * kaffeinepart.cpp + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "kaffeinepart.h" +#include <kapplication.h> +#include <kservice.h> +#include <ktrader.h> +#include <kmessagebox.h> +#include <krun.h> +#include <klocale.h> + +#include "kaffeinepart.moc" + +KaffeinePart::KaffeinePart(QObject* parent, const char* name) + : KParts::ReadOnlyPart(parent, name) +{ +} + +KaffeinePart::~KaffeinePart() +{ +} + +bool KaffeinePart::installDistroCodec(QWidget* parent, const QString& engine, const QString& codec) +{ + QString query = QString("[X-KDE-Kaffeine-codec] == '%1' and \ + [X-KDE-Kaffeine-engine] == '%2'").arg(codec).arg(engine); + + KService::Ptr service = KTrader::self()->query( "Kaffeine/CodecInstall", query).first(); + + if (!service) + return false; + + QString installScript = service->exec(); + + if (installScript.isNull()) + return false; + + KRun::runCommand(installScript); + return true; +} + diff --git a/kaffeine/src/player-parts/kaffeine-part/kaffeinepart.h b/kaffeine/src/player-parts/kaffeine-part/kaffeinepart.h new file mode 100644 index 0000000..e569ead --- /dev/null +++ b/kaffeine/src/player-parts/kaffeine-part/kaffeinepart.h @@ -0,0 +1,222 @@ +/* + * kaffeinepart.h + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef KAFFEINEPART_H +#define KAFFEINEPART_H + +#include <kparts/part.h> + +#include "mrl.h" + +/* + * Base-Class for Kaffeine-Parts. Derive from this class if you want to develop a player part for Kaffeine. + * At least you have to reimplement openURL(const MRL&) and to emit signalTrackFinished()! + * + * IMPORTANT: Forward the double-click, middle-click and wheel mouse-events to the parent widget (don't use + * QMouseEvent::accept())! + * + * @author Jürgen Kofler + */ + +class KDE_EXPORT KaffeinePart : public KParts::ReadOnlyPart +{ + Q_OBJECT +public: + KaffeinePart(QObject* parent, const char* name); + virtual ~KaffeinePart(); + + /* + * Playback in progress? + */ + virtual bool isPlaying() + { + return false; + } + + /* + * Playback paused? + */ + virtual bool isPaused() + { + return false; + } + + /* + * Should look like "*.mp3 *.MP3 *.avi *.AVI", used as file filter for directory import + */ + virtual QString supportedExtensions() + { + return QString::null; + } + + /* + * Current volume. In percent. + */ + virtual uint volume() const + { + return 0; + } + /* + * Current position. In percent. + */ + virtual uint position() const + { + return 0; + } + + /* + * DVD etc. + */ + virtual bool hasChapters() + { + return false; + } + + virtual void setDVDChapter(uint) {} + + /* + * Contain the stream video? + */ + virtual bool hasVideo() + { + return false; + } + +signals: + /* + * Frame size of video changed + */ + void signalNewFrameSize(const QSize& frame); + + /* + * New meta information available + */ + void signalNewMeta(const MRL &mrl); + + /* + * Playback of current track finished + */ + void signalTrackFinished(); + + /* + * Playback failed + */ + void signalPlaybackFailed(); + + /* + * User pressed play button but queue is empty + */ + void signalRequestCurrentTrack(); + + /* + * User pressed next button but no track left in queue + */ + void signalRequestNextTrack(); + + /* + * User pressed previous button but queue is empty + */ + void signalRequestPreviousTrack(); + + void signalToggleMinimalMode(); + +public slots: + /* + * You have to reimplement this! Opens an url and plays it. + */ + virtual bool openURL(const MRL &mrl) = 0; + + /* + * Tells the player we go fullscreen now. Hide controls etc. + */ + virtual void slotPrepareForFullscreen(bool) + {} + + /* + * DVD etc. + */ + virtual void playNextChapter() + {} + + /* + * DVD etc. + */ + virtual void playPreviousChapter() + {} + + /* + * Toggle play/pause + */ + virtual void slotTogglePause(bool pauseLive = true) + { + pauseLive = true; + } + + /* + * Stop playback + */ + virtual void slotStop() + {} + + /* + * Sets Volume. In percent. + */ + virtual void slotSetVolume(uint) + {} + + /* + * Sets Position. In percent. + */ + virtual void slotSetPosition(uint) + {} + + virtual void slotPosPlusSmall() + {} + + virtual void slotPosMinusSmall() + {} + + /* + * Turn mute on/off + */ + virtual void slotMute() + {} + + /* + * Execute distro-dependent actions to install codecs + */ + static bool installDistroCodec(QWidget* parent, const QString& engine, const QString& codec); + +private: + /* + * Don't reimplement this, a player should be able to stream media + */ + bool openFile() + { + return false; + } + + bool openURL(const KURL &url) + { + return openURL(MRL(url)); + } +}; + +#endif /* KAFFEINEPART_H */ diff --git a/kaffeine/src/player-parts/kaffeine-part/mrl.cpp b/kaffeine/src/player-parts/kaffeine-part/mrl.cpp new file mode 100644 index 0000000..9ab6e52 --- /dev/null +++ b/kaffeine/src/player-parts/kaffeine-part/mrl.cpp @@ -0,0 +1,47 @@ +/* + * mrl.cpp + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "mrl.h" + +MRL::MRL() +{ + reset(); +} + +MRL::MRL(const KURL& url, const QString& title, const QTime& length, const QString& mime, + const QString& artist, const QString& album, const QString& track, + const QString& year, const QString& genre, const QString& comment, + const QStringList& subtitleFiles, const int currentSubtitle) : + m_url(url.prettyURL()), m_kurl(url), m_title(title), m_artist(artist), m_album(album), m_track(track), m_year(year), m_genre(genre), m_comment(comment), + m_mime(mime), m_length(length), m_subtitleFiles(subtitleFiles), m_currentSubtitle(currentSubtitle) +{} + +MRL::MRL(const QString& url, const QString& title, const QTime& length, const QString& mime, + const QString& artist, const QString& album, const QString& track, + const QString& year, const QString& genre, const QString& comment, + const QStringList& subtitleFiles, const int currentSubtitle) : + m_url(url), m_kurl(KURL::fromPathOrURL(url)), m_title(title), m_artist(artist), + m_album(album), m_track(track), m_year(year), m_genre(genre), m_comment(comment), + m_mime(mime), m_length(length), m_subtitleFiles(subtitleFiles), m_currentSubtitle(currentSubtitle) +{} + +MRL::~MRL() +{ +} diff --git a/kaffeine/src/player-parts/kaffeine-part/mrl.h b/kaffeine/src/player-parts/kaffeine-part/mrl.h new file mode 100644 index 0000000..04c6d27 --- /dev/null +++ b/kaffeine/src/player-parts/kaffeine-part/mrl.h @@ -0,0 +1,96 @@ +/* + * mrl.h + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef MRL_H +#define MRL_H + +#include <qdatetime.h> +#include <qstringlist.h> + +#include <kurl.h> + +/* + * media resource locator + * holds url and meta info + */ + +class KDE_EXPORT MRL +{ +public: + MRL(); + /* don't use this constructor to prevent encoding problems */ + MRL(const KURL& url, const QString& title = QString::null, const QTime& length = QTime(), const QString& mime = QString::null, + const QString& artist = QString::null, const QString& album = QString::null, const QString& track = QString::null, + const QString& year = QString::null, const QString& genre = QString::null, const QString& comment = QString::null, + const QStringList& subtitleFiles = QStringList(), const int currentSubtitle = -1); + MRL(const QString& url, const QString& title = QString::null, const QTime& length = QTime(), const QString& mime = QString::null, + const QString& artist = QString::null, const QString& album = QString::null, const QString& track = QString::null, + const QString& year = QString::null, const QString& genre = QString::null, const QString& comment = QString::null, + const QStringList& subtitleFiles = QStringList(), const int currentSubtitle = -1); + virtual ~MRL(); + + void reset() { m_url = QString::null; m_kurl = KURL(); } + bool isEmpty() { return (m_url == QString::null); } + + const QString& url() const { return m_url; } + const KURL& kurl() const { return m_kurl; } + const QString& title() const { return m_title; } + const QString& artist() const { return m_artist; } + const QString& album() const { return m_album; } + const QString& track() const { return m_track; } + const QString& year() const { return m_year; } + const QString& genre() const { return m_genre; } + const QString& comment() const { return m_comment; } + const QString& mime() const { return m_mime; } + const QTime& length() const { return m_length; } + const QStringList& subtitleFiles() const { return m_subtitleFiles; } + int currentSubtitle() const { return m_currentSubtitle; } + + void setURL(const QString& url) { m_url = url; m_kurl = KURL(url); } + void setTitle(const QString& title) { m_title = title; } + void setArtist(const QString& artist) { m_artist = artist; } + void setAlbum(const QString& album) { m_album = album; } + void setTrack(const QString& track) { m_track = track; } + void setYear(const QString& year) { m_year = year; } + void setGenre(const QString& genre) { m_genre = genre; } + void setComment(const QString& comment) { m_comment = comment; } + void setMime(const QString& mime) { m_mime = mime; } + void setLength(const QTime& length) { m_length = length; } + void setSubtitleFiles(const QStringList& urls) { m_subtitleFiles = urls; } + void addSubtitleFile(const QString& url) { m_subtitleFiles.append(url); } + void setCurrentSubtitle(int sub) { m_currentSubtitle = sub; } + +private: + QString m_url; + KURL m_kurl; + QString m_title; + QString m_artist; + QString m_album; + QString m_track; + QString m_year; + QString m_genre; + QString m_comment; + QString m_mime; + QTime m_length; + QStringList m_subtitleFiles; + int m_currentSubtitle; /* -1 -> off */ +}; + +#endif /* MRL_H */ diff --git a/kaffeine/src/player-parts/kaffeine-part/playlistimport.cpp b/kaffeine/src/player-parts/kaffeine-part/playlistimport.cpp new file mode 100644 index 0000000..13b5d63 --- /dev/null +++ b/kaffeine/src/player-parts/kaffeine-part/playlistimport.cpp @@ -0,0 +1,685 @@ +/* + * playlistimport.cpp + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <qdom.h> +#include <qregexp.h> +#include <qxml.h> + +#include <kdebug.h> +#include <kio/netaccess.h> + +#include "playlistimport.h" + +class MyXMLParser : public QXmlDefaultHandler +{ + +public: + QValueList<MRL> mrls; + bool isKaffeinePlaylist; + + + MyXMLParser() : isKaffeinePlaylist(false) + {} + + bool startElement(const QString&, const QString&, + const QString &qname, const QXmlAttributes &att) + { + + if (qname == "playlist") + if (att.value("client") == "kaffeine") + { + isKaffeinePlaylist = true; + return true; + } + else + { + return false; + } + + if (qname != "entry") return true; + + QStringList subs = QStringList(); + int currentSub = -1; + + if ((!att.value("subs").isNull()) && (!att.value("subs").isEmpty())) + subs = QStringList::split("&",att.value("subs"),false); + if ((!att.value("subs").isNull()) && (!att.value("subs").isEmpty())) + { + bool ok; + currentSub = att.value("currentSub").toInt(&ok); + if (!ok) + currentSub = -1; + } + + // kdDebug() << "PlaylistImport: kaffeine import url: " << att.value("url") << endl; + mrls.append(MRL(att.value("url"), att.value("title"), PlaylistImport::stringToTime(att.value("length")), + att.value("mime"), att.value("artist"), att.value("album"), att.value("track"), + att.value("year"), att.value("genre"), QString::null, subs, currentSub)); + return true; + } + +}; + +bool PlaylistImport::kaffeine(const QString& playlist, QValueList<MRL>& mrls) +{ + kdDebug() << "PlaylistImport: kaffeine: " << playlist << endl; + QFile file(playlist); + if (!file.open(IO_ReadOnly)) return false; + + QXmlInputSource source(&file); + QXmlSimpleReader reader; + + MyXMLParser parser; + reader.setContentHandler(&parser); + reader.parse(source); + file.close(); + + if (!parser.isKaffeinePlaylist) + { + return false; + } + else + { + QValueList<MRL>::ConstIterator end(parser.mrls.end()); + for (QValueList<MRL>::ConstIterator it = parser.mrls.begin(); it != end; ++it) + mrls.append(*it); + return true; + } +} + +class NoatunXMLParser : public QXmlDefaultHandler +{ + +public: + QValueList<MRL> mrls; + bool isNoatunPlaylist; + + NoatunXMLParser(): isNoatunPlaylist(false) + {} + + bool startElement(const QString&, const QString &, + const QString &qname, const QXmlAttributes &att) + { + + if (qname == "playlist") + if (att.value("client") == "noatun") + { + isNoatunPlaylist = true; + return true; + } + else + { + return false; + } + + if (qname != "item") return true; + + QString title = att.value("title"); + + if (title.isNull()) + title = att.value("url"); + + bool ok; + QTime length; + int time = att.value("length").toInt(&ok); + if ((ok) && (time > 0)) + { + length = QTime().addMSecs(time); + } + + kdDebug() << "PlaylistImport: noatun import url: " << att.value("url") << endl; + mrls.append(MRL(att.value("url"), title, length, QString::null, att.value("author"), + att.value("album"), att.value("track"))); + + return true; + } + +}; + +bool PlaylistImport::noatun(const QString& playlist, QValueList<MRL>& mrls) +{ + kdDebug() << "PlaylistImport: noatun: " << playlist << endl; + QFile file(playlist); + if (!file.open(IO_ReadOnly)) return false; + + QXmlInputSource source(&file); + QXmlSimpleReader reader; + + NoatunXMLParser parser; + reader.setContentHandler(&parser); + reader.parse(source); + file.close(); + + if (!parser.isNoatunPlaylist) + { + return false; + } + else + { + QValueList<MRL>::ConstIterator end(parser.mrls.end()); + for (QValueList<MRL>::ConstIterator it = parser.mrls.begin(); it != end; ++it) + mrls.append(*it); + return true; + } +} + +bool PlaylistImport::m3u(const QString& playlist , QValueList<MRL>& mrls) +{ + kdDebug() << "PlaylistImport: m3u: " << playlist << endl; + QFile file(playlist); + if (!file.open(IO_ReadOnly)) return false; + QTextStream stream(&file); + + // if (stream.readLine().upper() != "#EXTM3U") return false; + QString url; + int time; + bool ok; + QTime length; + QString title; + KURL kUrl; + bool foundAnyUrl = false; + KURL plurl(playlist); + plurl.setFileName (""); + + while (!stream.eof()) + { + url = stream.readLine(); + time = 0; + title = QString::null; + length = QTime(); + if (url.left(1) == "#") + { + if (url.left(7).upper() == "#EXTINF") + { + url = url.remove(0,8); + time = url.section(",", 0,0).toInt(&ok); + if ((ok) && (time > 0)) + { + length = QTime().addSecs(time); + } + + title = url.section(",",1,1); + url = stream.readLine(); + } + else + { + continue; + } + } + url.replace ('\\', '/'); /* for windows styled urls */ + kUrl = KURL (plurl, url); /* maybe a relative url */ + if (kUrl.isValid()) + { + kdDebug() << "PlaylistImport: m3u import url: " << kUrl.prettyURL() << endl; + + MRL mrl; + if (kUrl.isLocalFile()) + mrl.setURL(kUrl.path()); + else + mrl.setURL(kUrl.prettyURL()); + if (title.isNull()) + title = kUrl.fileName(); + mrl.setTitle(title); + mrl.setLength(length); + + mrls.append(mrl); + + foundAnyUrl = true; + } + else + kdDebug() << "PlaylistImport: M3U: Not valid: " << kUrl.prettyURL() << endl; + } + file.close(); + if (foundAnyUrl) + return true; + else + return false; +} + +bool PlaylistImport::pls(const QString& playlist, QValueList<MRL>& mrls) +{ + kdDebug() << "PlaylistImport: pls: " << playlist << endl; + QFile file(playlist); + if (!file.open(IO_ReadOnly)) return false; + + QTextStream stream(&file); + + //if (stream.readLine().upper() != "[PLAYLIST]") return false; + + // Better Handling of pls playlists - Taken from amaroK - amarok.kde.org + // Counted number of "File#=" lines. + uint entryCnt = 0; + // Value of the "NumberOfEntries=#" line. + uint numberOfEntries = 0; + bool havePlaylistSection = false; + QString tmp; + QStringList lines; + + // set Regexp keywords, Be case insensitive + const QRegExp regExp_NumberOfEntries("^NumberOfEntries\\s*=\\s*\\d+$", false); + const QRegExp regExp_File("^File\\d+\\s*=", false); + const QRegExp regExp_Title("^Title\\d+\\s*=", false); + const QRegExp regExp_Length("^Length\\d+\\s*=\\s*-?\\d+$", false); + const QRegExp regExp_Version("^Version\\s*=\\s*\\d+$", false); + const QString section_playlist("[playlist]"); + + /* Preprocess the input data. + * Read the lines into a buffer; Cleanup the line strings; + * Count the entries manually and read "NumberOfEntries". + */ + while (!stream.atEnd()) { + tmp = stream.readLine(); + tmp = tmp.stripWhiteSpace(); + if (tmp.isEmpty()) + continue; + lines.append(tmp); + + if (tmp == section_playlist) { + havePlaylistSection = true; + continue; + } + + if (tmp.contains(regExp_File)) { + entryCnt++; + continue; + } + + if (tmp.contains(regExp_NumberOfEntries)) { + numberOfEntries = tmp.section('=', -1).stripWhiteSpace().toUInt(); + continue; + } + } + + file.close(); + + if (numberOfEntries != entryCnt) { + kdError() << "PlaylistImport: Invalid \"NumberOfEntries\" value in .pls playlist. " + << "NumberOfEntries=" << numberOfEntries << " counted=" + << entryCnt << endl; + /* Corrupt file. The "NumberOfEntries" value is + * not correct. Fix it by setting it to the manually + * counted number and go on parsing. + */ + numberOfEntries = entryCnt; + } + // If we have no Entries, return + if (!numberOfEntries) + return true; + + int time; + bool ok; + uint index; + bool inPlaylistSection = false; + QString* titles = new QString[entryCnt]; + QString* files = new QString[entryCnt]; + QTime* length = new QTime[entryCnt]; + + QStringList::const_iterator i = lines.begin(), end = lines.end(); + for ( ; i != end; ++i) { + if (!inPlaylistSection && havePlaylistSection) { + /* The playlist begins with the "[playlist]" tag. + * Skip everything before this. + */ + if ((*i) == section_playlist) + inPlaylistSection = true; + continue; + } + if ((*i).contains(regExp_File)) { + // Have a "File#=XYZ" line. + index = extractIndex(*i); + if (index > numberOfEntries || index == 0) + continue; + files[index-1] = (*i).section('=', 1).stripWhiteSpace(); + continue; + } + if ((*i).contains(regExp_Title)) { + // Have a "Title#=XYZ" line. + index = extractIndex(*i); + if (index > numberOfEntries || index == 0) + continue; + titles[index-1] = (*i).section('=', 1).stripWhiteSpace(); + continue; + } + if ((*i).contains(regExp_Length)) { + // Have a "Length#=XYZ" line. + index = extractIndex(*i); + if (index > numberOfEntries || index == 0) + continue; + tmp = (*i).section('=', 1).stripWhiteSpace(); + time = tmp.toInt(&ok); + if ( !(ok) || !(time > 0) ) continue; + length[index-1] = QTime().addSecs(time); + continue; + } + if ((*i).contains(regExp_NumberOfEntries)) { + // Have the "NumberOfEntries=#" line. + continue; + } + if ((*i).contains(regExp_Version)) { + // Have the "Version=#" line. + tmp = (*i).section('=', 1).stripWhiteSpace(); + // We only support Version=2 + if (tmp.toUInt(&ok) != 2) + kdWarning() << "PlaylistImport: pls: Unsupported version." << endl; + continue; + } + kdWarning() << "PlaylistImport: pls: Unrecognized line: \"" << *i << "\"" << endl; + } + + for (uint i=0; i<entryCnt; i++) + { + if (files[i].isNull()) continue; + kdDebug() << "PlaylistImport: pls import url: " << files[i] << endl; + if (titles[i].isNull()) + titles[i] = files[i]; + mrls.append(MRL(files[i], titles[i], length[i])); + } + + delete [] titles; + delete [] files; + delete [] length; + + return true; +} + +// Helper for pls import +uint PlaylistImport::extractIndex( const QString &str ) +{ + /* Extract the index number out of a .pls line. + * Example: + * extractIndex("File2=foobar") == 2 + */ + bool ok = false; + unsigned int ret; + QString tmp(str.section('=', 0, 0)); + tmp.remove(QRegExp("^\\D*")); + ret = tmp.stripWhiteSpace().toUInt(&ok); + if (!ok) + kdError() << "PlaylistImport: pls: Corrupt pls file, Error extracting index." << endl; + return ret; +} + +bool PlaylistImport::ram(const MRL& playlist, QValueList<MRL>& mrls, QWidget* parent) +{ + Q_ULONG result; + char buf[10]; + + kdDebug() << "PlaylistImport: ram: " << playlist.url() << endl; + memset( buf, 0, 10 ); + + if (playlist.kurl().isLocalFile()) { + QFile file(playlist.kurl().path()); + if (!file.open(IO_ReadOnly)) { + kdError() << "PlaylistImport: Can't open " << playlist.url() << endl; + return false; + } + result = file.readBlock(buf, 4); + file.close(); + if (result != 4) { + kdError() << "PlaylistImport: Can't read " << playlist.url() << endl; + return false; + } + if (buf[0]=='.' && buf[1]=='R' && buf[2]=='M' && buf[3]=='F') { + kdDebug() << "PlaylistImport: Seems to be a real media file" << endl; + return false; + } + } + else if (!playlist.kurl().protocol().startsWith("http")) { + kdError() << "PlaylistImport: ram: Download via " << playlist.kurl().protocol() << " protocol not supported." << endl; + return false; + } + + kdDebug() << "PlaylistImport: Seems to be a ram playlist!" << endl; + + QString localFile, url; + if (KIO::NetAccess::mimetype(playlist.kurl(), parent) == "application/vnd.rn-realmedia") { + kdDebug() << "PlaylistImport: Seems to be a real media file" << endl; + return false; + } + + if (KIO::NetAccess::download(playlist.kurl(), localFile, parent)) + { + QFile plFile(localFile); + if (!plFile.open(IO_ReadOnly)) return false; + QTextStream stream( &plFile ); + + while (!stream.eof()) + { + url = stream.readLine(); + + if (url[0] == '#') continue; /* ignore comments */ + if (url == "--stop--") break; /* stop line */ + + if ((url.left(7) == "rtsp://") || (url.left(6) == "pnm://") || (url.left(7) == "http://")) + { + kdDebug() << "PlaylistImport: ram import url: " << url << endl; + mrls.append(MRL(url, url)); + } + } + } + else { + kdError() << "PlaylistImport: " << KIO::NetAccess::lastErrorString() << endl; + return false; + } + + return true; +} + +/********************************************************************************** + * load asx playlist * + * spec: http://msdn.microsoft.com/library/en-us/wmplay/mmp_sdk/asxelement.asp * + **********************************************************************************/ + +bool PlaylistImport::asx(const QString& playlist, QValueList<MRL>& mrls) +{ + kdDebug() << "PlaylistImport: asx: " << playlist << endl; + QFile file(playlist); + if (!file.open(IO_ReadOnly)) return false; + + QDomDocument doc; + QString errorMsg; + int errorLine, errorColumn; + if (!doc.setContent(&file, &errorMsg, &errorLine, &errorColumn)) + { + kdError() << "PlaylistImport: XML parse error: " << errorMsg + << " (line: " << errorLine << ", column: " << errorColumn << ")" << endl; + return false; + } + + QDomElement root = doc.documentElement(); + + QString url; + QString title; + QString author; + QTime length; + QString duration; + + if (root.nodeName().lower() != "asx") return false; + + QDomNode node = root.firstChild(); + QDomNode subNode; + QDomElement element; + + while (!node.isNull()) + { + url = QString::null; + title = QString::null; + author = QString::null; + length = QTime(); + if (node.nodeName().lower() == "entry") + { + subNode = node.firstChild(); + while (!subNode.isNull()) + { + if ((subNode.nodeName().lower() == "ref") && (subNode.isElement()) && (url.isNull())) + { + element = subNode.toElement(); + if (element.hasAttribute("href")) + url = element.attribute("href"); + if (element.hasAttribute("HREF")) + url = element.attribute("HREF"); + if (element.hasAttribute("Href")) + url = element.attribute("Href"); + if (element.hasAttribute("HRef")) + url = element.attribute("HRef"); + + } + + if ((subNode.nodeName().lower() == "duration") && (subNode.isElement())) + { + duration = QString::null; + element = subNode.toElement(); + if (element.hasAttribute("value")) + duration = element.attribute("value"); + if (element.hasAttribute("Value")) + duration = element.attribute("Value"); + if (element.hasAttribute("VALUE")) + duration = element.attribute("VALUE"); + + if (!duration.isNull()) + length = PlaylistImport::stringToTime(duration); + } + + if ((subNode.nodeName().lower() == "title") && (subNode.isElement())) + { + title = subNode.toElement().text(); + } + if ((subNode.nodeName().lower() == "author") && (subNode.isElement())) + { + author = subNode.toElement().text(); + } + + /* possible nodes we ignore: ABSTRACT, BANNER, BASE, COPYRIGHT, ENDMARKER, MOREINFO, PARAM, + * PREVIEWDURATION, STARTMARKER, STARTTIME + */ + subNode = subNode.nextSibling(); + } + + if (!url.isNull()) + { + if (title.isNull()) + title = url; + kdDebug() << "PlaylistImport: asx import url: " << url << endl; + mrls.append(MRL(url, title, length, QString::null, author)); + } + } + node = node.nextSibling(); + } + + file.close(); + + return true; +} + +/**************************************************************** + * Full SMIL support seems to be impossible at the moment... * + * spec: http://www.w3.org/TR/REC-smil/ * + ****************************************************************/ + +bool PlaylistImport::smil(const QString& playlist, const MRL& baseMRL, QValueList<MRL>& mrls) +{ + kdDebug() << "PlaylistImport: smil: " << playlist << endl; + QFile file(playlist); + if (!file.open(IO_ReadOnly)) return false; + + QDomDocument doc; + doc.setContent(&file); + QDomElement root = doc.documentElement(); + + if (root.nodeName().lower() != "smil") return false; + + bool anyURL = false; + KURL kurl; + QString url; + QDomNodeList nodeList; + QDomNode node; + QDomElement element; + + //video sources... + nodeList = doc.elementsByTagName("video"); + kdDebug() << "PlaylistImport: smil: " << nodeList.count() << " 'video' tags found" << endl; + for (uint i = 0; i < nodeList.count(); i++) + { + node = nodeList.item(i); + url = QString::null; + if ((node.nodeName().lower() == "video") && (node.isElement())) + { + element = node.toElement(); + if (element.hasAttribute("src")) + url = element.attribute("src"); + if (element.hasAttribute("Src")) + url = element.attribute("Src"); + if (element.hasAttribute("SRC")) + url = element.attribute("SRC"); + } + if (!url.isNull()) + { + kurl = KURL(baseMRL.kurl(), url); + kdDebug() << "PlaylistImport: smil: found video source: " << kurl.url() << endl; + mrls.append(kurl); + anyURL = true; + } + } + + //audio sources... + nodeList = doc.elementsByTagName("audio"); + kdDebug() << "PlaylistImport: smil: " << nodeList.count() << " 'audio' tags found" << endl; + for (uint i = 0; i < nodeList.count(); i++) + { + node = nodeList.item(i); + url = QString::null; + if ((node.nodeName().lower() == "audio") && (node.isElement())) + { + element = node.toElement(); + if (element.hasAttribute("src")) + url = element.attribute("src"); + if (element.hasAttribute("Src")) + url = element.attribute("Src"); + if (element.hasAttribute("SRC")) + url = element.attribute("SRC"); + } + if (!url.isNull()) + { + kurl = KURL(baseMRL.kurl(), url); + kdDebug() << "PlaylistImport: smil: found audio source: " << kurl.url() << endl; + mrls.append(kurl); + anyURL = true; + } + } + + file.close(); + return anyURL; +} + +QTime PlaylistImport::stringToTime(const QString& timeString) +{ + int sec = 0; + bool ok = false; + QStringList tokens = QStringList::split(':',timeString); + + sec += tokens[0].toInt(&ok)*3600; //hours + sec += tokens[1].toInt(&ok)*60; //minutes + sec += tokens[2].toInt(&ok); //secs + + if (ok) + return QTime().addSecs(sec); + else + return QTime(); +} diff --git a/kaffeine/src/player-parts/kaffeine-part/playlistimport.h b/kaffeine/src/player-parts/kaffeine-part/playlistimport.h new file mode 100644 index 0000000..16420eb --- /dev/null +++ b/kaffeine/src/player-parts/kaffeine-part/playlistimport.h @@ -0,0 +1,50 @@ +/* + * playlistimport.h + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef PLAYLISTIMPORT_H +#define PLAYLISTIMPORT_H + +#include "mrl.h" + +class QWidget; + +/* + * static methods for playlist import + */ + +class KDE_EXPORT PlaylistImport +{ +public: + + static bool kaffeine(const QString&, QValueList<MRL>&); + static bool noatun(const QString&, QValueList<MRL>&); + static bool m3u(const QString&, QValueList<MRL>&); + static bool pls(const QString&, QValueList<MRL>&); + static bool ram(const MRL&, QValueList<MRL>&, QWidget*); + static bool asx(const QString&, QValueList<MRL>&); + static bool smil(const QString&, const MRL&, QValueList<MRL>&); + /* helper */ + static QTime stringToTime(const QString&); + +private: + static uint extractIndex(const QString&); +}; + +#endif /* PLAYLISTIMPORT_H */ diff --git a/kaffeine/src/player-parts/xine-part/Makefile.am b/kaffeine/src/player-parts/xine-part/Makefile.am new file mode 100644 index 0000000..0325e1d --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/Makefile.am @@ -0,0 +1,26 @@ +kde_module_LTLIBRARIES = libxinepart.la + +INCLUDES = -I$(top_srcdir)/kaffeine/src/ -I$(top_srcdir)/kaffeine/src/player-parts/kaffeine-part $(all_includes) $(CFLAGS_XINERAMA) $(CFLAGS_XINE) $(CFLAGS_XCB) + +METASOURCES = AUTO + +kaffeineincludedir = $(includedir)/kaffeine +kaffeineinclude_HEADERS = xine_part.h xine_part_iface.h + +noinst_HEADERS = xine_part_iface.h kxinewidget.h postfilter.h deinterlacequality.h videosettings.h \ + filterdialog.h screenshotpreview.h xineconfig.h positionslider.h equalizer.h + +libxinepart_la_SOURCES = xine_part.cpp kxinewidget.cpp postfilter.cpp deinterlacequality.cpp \ + videosettings.cpp filterdialog.cpp screenshotpreview.cpp xineconfig.cpp positionslider.cpp \ + equalizer.cpp xine_part_iface.skel +libxinepart_la_LIBADD = $(LIB_XINERAMA) $(LIB_XINE) $(LIB_XCB) ../kaffeine-part/libkaffeinepart.la +libxinepart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -avoid-version -no-undefined + + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = xine_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/kaffeine +partrc_DATA = xine_part.rc diff --git a/kaffeine/src/player-parts/xine-part/README b/kaffeine/src/player-parts/xine-part/README new file mode 100644 index 0000000..b97bb38 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/README @@ -0,0 +1,81 @@ +******************** +* XINE-PART * +******************** + +The xine-based default player-part of Kaffeine. + + +*** IMPORTANT *** +The following example is outdated and unlikely to work. +*** IMPORTANT *** + + +You can use this part in your Application like this: +---------------------------------------------------- + +*** myplayer.h: *** + +#include <kmainwindow.h> + +class KaffeinePart; + +class MyPlayer : public MainWindow +{ + public: + MyPlayer(); + ~MyPlayer(); + + void loadURL(const QString&); + + //... + private: + KaffeinePart* m_player; +}; + +*** myplayer.cpp: *** + +#include <kaffeine/xine_part.h> + +MyPlayer::MyPlayer() : KMainWindow(0) +{ + initMyActions(); + createGUI(); + + //... + + m_player = new XinePart(this, "my_player", this, 0, QStringList()); + guiFactory()->addClient(m_player); + setCentralWidget(m_player->widget()); + + //.. +} + +void MyPlayer::loadURL(const QString& url) +{ + m_player->openURL(url); +} + +*** Makefile.am *** + +bin_PROGRAMS = myplayer + +INCLUDES = $(all_includes) + +noinst_HEADERS = myplayer.h + +METASOURCES = AUTO + +myplayer_SOURCES = myplayer.cpp +myplayer_LDFLAGS = $(KDE_RPATH) $(all_libraries) +myplayer_LDADD = $(LIB_KPARTS) -lxinepart + + +******************* + +Thats it. If you need to implement your own UI, check kxinewidget.h + + + + + + diff --git a/kaffeine/src/player-parts/xine-part/deinterlacequality.cpp b/kaffeine/src/player-parts/xine-part/deinterlacequality.cpp new file mode 100644 index 0000000..70ca01d --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/deinterlacequality.cpp @@ -0,0 +1,137 @@ +/* + * deinterlacequality.cpp - dialog for selecting the quality of deinterlacing + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kdebug.h> +#include <klocale.h> +#include <kpushbutton.h> + +#include <qcheckbox.h> +#include <qslider.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qlabel.h> +#include <qlayout.h> + +#include "deinterlacequality.h" +#include "deinterlacequality.moc" + + +DeinterlaceQuality::DeinterlaceQuality(QWidget* filterDialog, QWidget *parent, const char *name) +:KDialogBase(parent, name, true, i18n("Deinterlace Quality"), KDialogBase::Close) +{ + m_configStrings << "tvtime:method=Greedy2Frame,enabled=1,pulldown=vektor,framerate_mode=full,judder_correction=1,use_progressive_frame_flag=1,chroma_filter=1,cheap_mode=0"; + m_configStrings << "tvtime:method=Greedy2Frame,enabled=1,pulldown=vektor,framerate_mode=full,judder_correction=0,use_progressive_frame_flag=1,chroma_filter=0,cheap_mode=0"; + m_configStrings << "tvtime:method=Greedy,enabled=1,pulldown=none,framerate_mode=half_top,judder_correction=0,use_progressive_frame_flag=1,chroma_filter=0,cheap_mode=0"; + m_configStrings << "tvtime:method=Greedy,enabled=1,pulldown=none,framerate_mode=half_top,judder_correction=0,use_progressive_frame_flag=1,chroma_filter=0,cheap_mode=1"; + m_configStrings << "tvtime:method=LinearBlend,enabled=1,pulldown=none,framerate_mode=half_top,judder_correction=0,use_progressive_frame_flag=1,chroma_filter=0,cheap_mode=1"; + m_configStrings << "tvtime:method=LineDoubler,enabled=1,pulldown=none,framerate_mode=half_top,judder_correction=0,use_progressive_frame_flag=1,chroma_filter=0,cheap_mode=1"; + + setInitialSize(QSize(680, 480)); + QWidget* mainWidget = makeMainWidget(); + QGridLayout* grid = new QGridLayout( mainWidget, 9, 2 ); + grid->setSpacing(5); + grid->setMargin(5); + + m_qualitySlider = new QSlider( QSlider::Vertical, mainWidget ); + m_qualitySlider->setRange(0, 5); + m_qualitySlider->setSteps(1, 1); + m_qualitySlider->setTickmarks(QSlider::Right); + grid->addMultiCellWidget(m_qualitySlider, 0, 5, 0, 0); + + QLabel* level0Descr = new QLabel(i18n("<b>Very low cpu usage, worst quality.</b><br>Half of vertical resolution is lost. For some systems (with PCI video cards) this might decrease the cpu usage when compared to plain video playback (no deinterlacing)."), mainWidget); + grid->addWidget(level0Descr, 5, 1); + + QLabel* level1Descr = new QLabel(i18n("<b>Low cpu usage, poor quality.</b><br>Image is blurred vertically so interlacing effects are removed."), mainWidget); + grid->addWidget(level1Descr, 4, 1); + + QLabel* level2Descr = new QLabel(i18n("<b>Medium cpu usage, medium quality.</b><br>Image is analysed and areas showing interlacing artifacts are fixed (interpolated)."), mainWidget); + grid->addWidget(level2Descr, 3, 1); + + QLabel* level3Descr = new QLabel(i18n("<b>High cpu usage, good quality.</b><br>Conversion of dvd image format improves quality and fixes chroma upsampling bug."), mainWidget); + grid->addWidget(level3Descr, 2, 1); + + QLabel* level4Descr = new QLabel(i18n("<b>Very high cpu usage, great quality.</b><br>Besides using smart deinterlacing algorithms it will also double the frame rate (30->60fps) to match the field rate of TVs. Detects and reverts 3-2 pulldown. *"), mainWidget); + grid->addWidget(level4Descr, 1, 1); + + QLabel* level5Descr = new QLabel(i18n("<b>Very very high cpu usage, great quality with (experimental) improvements.</b><br>Enables judder correction (play films at their original 24 fps speed) and vertical color smoothing (fixes small color stripes seen in some dvds). *"), mainWidget); + grid->addWidget(level5Descr, 0, 1); + + m_customBox = new QCheckBox(i18n("User defined"), mainWidget); + grid->addMultiCellWidget(m_customBox, 6, 6, 0, 1); + connect(m_customBox, SIGNAL(toggled(bool)), this, SLOT(slotCustomBoxToggled(bool))); + + m_customConfigButton = new KPushButton(i18n("Configure tvtime Deinterlace Plugin..."), mainWidget); + m_customConfigButton->setSizePolicy(QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Fixed)); + grid->addWidget(m_customConfigButton, 7, 1); + connect(m_customConfigButton, SIGNAL(clicked()), filterDialog, SLOT(show())); + + QLabel* note = new QLabel(i18n("* <i>May require a patched 2.4 kernel (like RedHat one) or 2.6 kernel.</i>"), mainWidget); + note->setAlignment(QLabel::WordBreak | QLabel::AlignVCenter); + grid->addMultiCellWidget(note, 9, 9, 0, 1); +} + +DeinterlaceQuality::~DeinterlaceQuality() +{ + kdDebug() << "DeinterlaceQuality: destructed" << endl; +} + +void DeinterlaceQuality::slotLevelChanged( int level ) +{ + // kdDebug() << "DeinterlaceQuality: Change to quality " << level << endl; + emit signalSetDeinterlaceConfig(m_configStrings[level]); +} + +void DeinterlaceQuality::slotCustomBoxToggled(bool on) +{ + if (on) + { + m_customConfigButton->setEnabled(true); + m_qualitySlider->setEnabled(false); + } + else + { + m_customConfigButton->setEnabled(false); + m_qualitySlider->setEnabled(true); + } +} + +void DeinterlaceQuality::setQuality(uint qu) +{ + if (qu < 10) + { + m_qualitySlider->setValue(qu); + m_customBox->setChecked(false); + slotCustomBoxToggled(false); + } + else + { + m_qualitySlider->setValue(qu-10); + m_customBox->setChecked(true); + } + connect(m_qualitySlider, SIGNAL(valueChanged(int)), this, SLOT(slotLevelChanged(int))); +} + +uint DeinterlaceQuality::getQuality() const +{ + if (m_customBox->isChecked()) + return m_qualitySlider->value()+10; + else + return m_qualitySlider->value(); +} diff --git a/kaffeine/src/player-parts/xine-part/deinterlacequality.h b/kaffeine/src/player-parts/xine-part/deinterlacequality.h new file mode 100644 index 0000000..5a91ad4 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/deinterlacequality.h @@ -0,0 +1,78 @@ +/* + * deinterlacequality.h - dialog for selecting the quality of deinterlacing + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef DEINTERLACEQUALITY_H +#define DEINTERLACEQUALITY_H + +#include <kdialogbase.h> + +/** + *@author Juergen Kofler + */ + +class QCheckBox; +class QSlider; +class QString; +class QStringList; +class KPushButton; + +class DeinterlacerConfigDialog : public KDialogBase +{ + Q_OBJECT +public: + DeinterlacerConfigDialog(QWidget *parent=0, const char *name=0) + : KDialogBase( parent, name, true, i18n("Configure tvtime Deinterlace Plugin"), KDialogBase::Close ) + { + setInitialSize(QSize(450,400), true); + + mainWidget = makeVBoxMainWidget(); + } + + ~DeinterlacerConfigDialog() {} + QWidget* getMainWidget() const { return (QWidget*)mainWidget; } + +private: + QVBox* mainWidget; +}; + +class DeinterlaceQuality : public KDialogBase { + Q_OBJECT +public: + DeinterlaceQuality(QWidget* filterDialog, QWidget *parent=0, const char *name=0); + ~DeinterlaceQuality(); + + void setQuality(uint); + uint getQuality() const; + +signals: + void signalSetDeinterlaceConfig(const QString&); + +private slots: + void slotLevelChanged(int); + void slotCustomBoxToggled(bool); + +private: + QStringList m_configStrings; + QSlider* m_qualitySlider; + QCheckBox* m_customBox; + KPushButton* m_customConfigButton; +}; + +#endif /* DEINTERLACEQUALITY_H */ diff --git a/kaffeine/src/player-parts/xine-part/equalizer.cpp b/kaffeine/src/player-parts/xine-part/equalizer.cpp new file mode 100644 index 0000000..12e5f92 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/equalizer.cpp @@ -0,0 +1,245 @@ +/* + * equalizer.cpp + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <klocale.h> +#include <kdebug.h> + +#include <qlayout.h> +#include <qtooltip.h> +#include <qlabel.h> + +#include "equalizer.h" +#include "equalizer.moc" + +Equalizer::Equalizer(QWidget *parent, const char *name) + : KDialogBase(KDialogBase::Plain, i18n("Equalizer Settings"), KDialogBase::Default | KDialogBase::Close, KDialogBase::Close, parent, name, false) +{ + setInitialSize(QSize(450,250), true); + reparent(parent, pos(), false); + QWidget* page = plainPage(); + + QGridLayout* mainGrid = new QGridLayout( page, 3, 1 ); + + on = new QCheckBox( i18n("On"), page ); + mainGrid->addWidget( on, 0, 0 ); + connect(on, SIGNAL(toggled(bool)), this, SLOT(slotSetEnabled(bool))); + + volumeGain = new QCheckBox( i18n("Volume gain"), page ); + QToolTip::add(volumeGain, i18n("Volume Gain for Equalizer - If the sound becomes noisy disable this")); + mainGrid->addWidget( volumeGain, 1, 0 ); + connect(volumeGain, SIGNAL(toggled(bool)), this, SIGNAL(signalSetVolumeGain(bool))); + + equalGroup = new QGroupBox( QString::null, page ); + mainGrid->addWidget( equalGroup, 2, 0 ); + + QGridLayout* equalGrid = new QGridLayout(equalGroup, 2, 10); + equalGrid->setSpacing(5); + equalGrid->setMargin(10); + + QLabel* eq30Text = new QLabel("30Hz", equalGroup); + eq30Slider = new QSlider(Qt::Vertical, equalGroup); + eq30Slider->setRange(-100, -1); + eq30Slider->setSteps(1, 10); + eq30Slider->setTickInterval(50); + eq30Slider->setTickmarks(QSlider::Right); + connect(eq30Slider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq30(int))); + equalGrid->addWidget(eq30Text, 1, 0); + equalGrid->addWidget(eq30Slider, 0, 0); + + QLabel* eq60Text = new QLabel("60Hz", equalGroup); + eq60Slider = new QSlider(Qt::Vertical, equalGroup); + eq60Slider->setRange(-100, -1); + eq60Slider->setSteps(1, 10); + connect(eq60Slider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq60(int))); + equalGrid->addWidget(eq60Text, 1, 1); + equalGrid->addWidget(eq60Slider, 0, 1); + + QLabel* eq125Text = new QLabel("125Hz", equalGroup); + eq125Slider = new QSlider(Qt::Vertical, equalGroup); + eq125Slider->setRange(-100, -1); + eq125Slider->setSteps(1, 10); + connect(eq125Slider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq125(int))); + equalGrid->addWidget(eq125Text, 1, 2); + equalGrid->addWidget(eq125Slider, 0, 2); + + QLabel* eq250Text = new QLabel("250Hz", equalGroup); + eq250Slider = new QSlider(Qt::Vertical, equalGroup); + eq250Slider->setRange(-100, -1); + eq250Slider->setSteps(1, 10); + connect(eq250Slider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq250(int))); + equalGrid->addWidget(eq250Text, 1, 3); + equalGrid->addWidget(eq250Slider, 0, 3); + + QLabel* eq500Text = new QLabel("500Hz", equalGroup); + eq500Slider = new QSlider(Qt::Vertical, equalGroup); + eq500Slider->setRange(-100, -1); + eq500Slider->setSteps(1, 10); + connect(eq500Slider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq500(int))); + equalGrid->addWidget(eq500Text, 1, 4); + equalGrid->addWidget(eq500Slider, 0, 4); + + QLabel* eq1kText = new QLabel("1kHz", equalGroup); + eq1kSlider = new QSlider(Qt::Vertical, equalGroup); + eq1kSlider->setRange(-100, -1); + eq1kSlider->setSteps(1, 10); + connect(eq1kSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq1k(int))); + equalGrid->addWidget(eq1kText, 1, 5); + equalGrid->addWidget(eq1kSlider, 0, 5); + + QLabel* eq2kText = new QLabel("2kHz", equalGroup); + eq2kSlider = new QSlider(Qt::Vertical, equalGroup); + eq2kSlider->setRange(-100, -1); + eq2kSlider->setSteps(1, 10); + connect(eq2kSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq2k(int))); + equalGrid->addWidget(eq2kText, 1, 6); + equalGrid->addWidget(eq2kSlider, 0, 6); + + QLabel* eq4kText = new QLabel("4kHz", equalGroup); + eq4kSlider = new QSlider(Qt::Vertical, equalGroup); + eq4kSlider->setRange(-100, -1); + eq4kSlider->setSteps(1, 10); + connect(eq4kSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq4k(int))); + equalGrid->addWidget(eq4kText, 1, 7); + equalGrid->addWidget(eq4kSlider, 0, 7); + + QLabel* eq8kText = new QLabel("8kHz", equalGroup); + eq8kSlider = new QSlider(Qt::Vertical, equalGroup); + eq8kSlider->setRange(-100, -1); + eq8kSlider->setSteps(1, 10); + connect(eq8kSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq8k(int))); + equalGrid->addWidget(eq8kText, 1, 8); + equalGrid->addWidget(eq8kSlider, 0, 8); + + QLabel* eq16kText = new QLabel("16kHz", equalGroup); + eq16kSlider = new QSlider(Qt::Vertical, equalGroup); + eq16kSlider->setRange(-100, -1); + eq16kSlider->setSteps(1, 10); + eq16kSlider->setTickInterval(50); + eq16kSlider->setTickmarks(QSlider::Left); + connect(eq16kSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewEq16k(int))); + equalGrid->addWidget(eq16kText, 1, 9); + equalGrid->addWidget(eq16kSlider, 0, 9); + + connect(this, SIGNAL(defaultClicked()), this, SLOT(slotSetDefaultValues())); +} + + +Equalizer::~Equalizer() +{ +} + + +void Equalizer::slotSetEnabled( bool enabled ) +{ + equalGroup->setEnabled( enabled ); + volumeGain->setEnabled( enabled ); + enableButton( KDialogBase::Default, enabled ); + + if (enabled) + { + emit signalSetVolumeGain( volumeGain->isChecked() ); + emit signalNewEq30( eq30Slider->value() ); + emit signalNewEq60( eq60Slider->value() ); + emit signalNewEq125( eq125Slider->value() ); + emit signalNewEq250( eq250Slider->value() ); + emit signalNewEq500( eq500Slider->value() ); + emit signalNewEq1k( eq1kSlider->value() ); + emit signalNewEq2k( eq2kSlider->value() ); + emit signalNewEq4k( eq4kSlider->value() ); + emit signalNewEq8k( eq8kSlider->value() ); + emit signalNewEq16k( eq16kSlider->value() ); + } + else + { + emit signalSetVolumeGain( false ); + emit signalNewEq30( 0 ); + emit signalNewEq60( 0 ); + emit signalNewEq125( 0 ); + emit signalNewEq250( 0 ); + emit signalNewEq500( 0 ); + emit signalNewEq1k( 0 ); + emit signalNewEq2k( 0 ); + emit signalNewEq4k( 0 ); + emit signalNewEq8k( 0 ); + emit signalNewEq16k( 0 ); + } +} + + +void Equalizer::slotSetDefaultValues() +{ + eq30Slider->setValue(-50); + eq60Slider->setValue(-50); + eq125Slider->setValue(-50); + eq250Slider->setValue(-50); + eq500Slider->setValue(-50); + eq1kSlider->setValue(-50); + eq2kSlider->setValue(-50); + eq4kSlider->setValue(-50); + eq8kSlider->setValue(-50); + eq16kSlider->setValue(-50); +} + + +/******* read from config-file ********/ +void Equalizer::ReadValues(KConfig* config) +{ + config->setGroup("Equalizer"); + + bool enabled = config->readBoolEntry( "Enabled", false ); + on->setChecked( enabled ); + + bool gain = config->readBoolEntry( "Volume Gain", true ); + volumeGain->setChecked( gain ); + + eq30Slider->setValue( config->readNumEntry("30Hz", -50) ); + eq60Slider->setValue( config->readNumEntry("60Hz", -50) ); + eq125Slider->setValue( config->readNumEntry("125Hz", -50) ); + eq250Slider->setValue( config->readNumEntry("250Hz", -50) ); + eq500Slider->setValue( config->readNumEntry("500Hz", -50) ); + eq1kSlider->setValue( config->readNumEntry("1kHz", -50) ); + eq2kSlider->setValue( config->readNumEntry("2kHz", -50) ); + eq4kSlider->setValue( config->readNumEntry("4kHz", -50) ); + eq8kSlider->setValue( config->readNumEntry("8kHz", -50) ); + eq16kSlider->setValue( config->readNumEntry("16kHz", -50) ); + + if (!enabled) + slotSetEnabled( false ); +} + + +/************** save in config file *************/ +void Equalizer::SaveValues(KConfig* config) +{ + config->setGroup("Equalizer"); + + config->writeEntry( "Enabled", on->isChecked()); + config->writeEntry( "Volume Gain", volumeGain->isChecked()); + config->writeEntry("30Hz", eq30Slider->value()); + config->writeEntry("60Hz", eq60Slider->value()); + config->writeEntry("125Hz", eq125Slider->value()); + config->writeEntry("250Hz", eq250Slider->value()); + config->writeEntry("500Hz", eq500Slider->value()); + config->writeEntry("1kHz", eq1kSlider->value()); + config->writeEntry("2kHz", eq2kSlider->value()); + config->writeEntry("4kHz", eq4kSlider->value()); + config->writeEntry("8kHz", eq8kSlider->value()); + config->writeEntry("16kHz", eq16kSlider->value()); +} diff --git a/kaffeine/src/player-parts/xine-part/equalizer.h b/kaffeine/src/player-parts/xine-part/equalizer.h new file mode 100644 index 0000000..e912dcc --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/equalizer.h @@ -0,0 +1,79 @@ +/* + * equalizer.h + * + * Copyright (C) 2003-2004 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef EQUALIZER_H +#define EQUALIZER_H + +#include <kdialogbase.h> +#include <kconfig.h> + +#include <qwidget.h> +#include <qslider.h> +#include <qgroupbox.h> +#include <qcheckbox.h> + +/**equalizer widget + *@author Juergen Kofler + */ + +class Equalizer : public KDialogBase { + Q_OBJECT +public: + Equalizer(QWidget *parent=0, const char *name=0); + ~Equalizer(); + + void ReadValues(KConfig* config); + void SaveValues(KConfig* config); + +signals: + void signalNewEq30(int); + void signalNewEq60(int); + void signalNewEq125(int); + void signalNewEq250(int); + void signalNewEq500(int); + void signalNewEq1k(int); + void signalNewEq2k(int); + void signalNewEq4k(int); + void signalNewEq8k(int); + void signalNewEq16k(int); + void signalSetVolumeGain(bool); + + +private slots: + void slotSetDefaultValues(); + void slotSetEnabled( bool ); + +private: + QCheckBox* on; + QCheckBox* volumeGain; + QGroupBox* equalGroup; + QSlider* eq30Slider; + QSlider* eq60Slider; + QSlider* eq125Slider; + QSlider* eq250Slider; + QSlider* eq500Slider; + QSlider* eq1kSlider; + QSlider* eq2kSlider; + QSlider* eq4kSlider; + QSlider* eq8kSlider; + QSlider* eq16kSlider; +}; + +#endif /* EQUALIZER_H */ diff --git a/kaffeine/src/player-parts/xine-part/filterdialog.cpp b/kaffeine/src/player-parts/xine-part/filterdialog.cpp new file mode 100644 index 0000000..4468b31 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/filterdialog.cpp @@ -0,0 +1,135 @@ +/* + * filterdialog.cpp - config dialog for postprocessing filters + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <klocale.h> +#include <kdebug.h> +#include <kpushbutton.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kcombobox.h> + +#include <qvbox.h> +#include <qstringlist.h> +#include <qcheckbox.h> +#include <qlayout.h> +#include <qscrollview.h> + +#include "filterdialog.h" +#include "filterdialog.moc" + + +FilterDialog::FilterDialog(const QStringList& audioFilters, const QStringList& videoFilters, QWidget *parent, const char *name) : + KDialogBase(KDialogBase::IconList, i18n("Effect Plugins"), KDialogBase::Ok, KDialogBase::Ok, parent, name, false) +{ + reparent(parent, pos(), false); + setInitialSize(QSize(400,350), true); + +/****** Audio Filters ******/ + QWidget* audioPage = addPage(i18n("Audio"), i18n("Audio Filters"), + KGlobal::iconLoader()->loadIcon("sound", KIcon::Panel, KIcon::SizeMedium)); + QGridLayout* audioGrid = new QGridLayout( audioPage, 3, 3 ); + audioGrid->setSpacing( 5 ); + + QCheckBox* useAudioFiltersCB = new QCheckBox( audioPage ); + useAudioFiltersCB->setText( i18n("Enable audio filters") ); + useAudioFiltersCB->setChecked( true ); + connect( useAudioFiltersCB, SIGNAL(toggled(bool)), this, SLOT(slotUseAudioFilters(bool))); + + audioGrid->addMultiCellWidget( useAudioFiltersCB, 0, 0, 0, 2 ); + + m_audioFilterCombo = new KComboBox( audioPage ); + m_audioFilterCombo->insertStringList( audioFilters ); + + m_addAudioButton = new KPushButton( i18n("Add Filter"), audioPage ); + connect( m_addAudioButton, SIGNAL( clicked() ), this, SLOT( slotAddAudioClicked() )); + m_removeAudioButton = new KPushButton( i18n("Remove All Filters"), audioPage ); + connect( m_removeAudioButton, SIGNAL( clicked() ), this, SIGNAL( signalRemoveAllAudioFilters() )); + + audioGrid->addWidget( m_audioFilterCombo, 1, 0 ); + audioGrid->addWidget( m_removeAudioButton, 1, 2 ); + audioGrid->addWidget( m_addAudioButton, 1, 1 ); + + QScrollView* audioSv = new QScrollView( audioPage ); + audioSv->setResizePolicy(QScrollView::AutoOneFit); + m_audioFilterPage = new QVBox(audioSv->viewport()); + m_audioFilterPage->setMargin( 5 ); + audioSv->addChild(m_audioFilterPage); + + audioGrid->addMultiCellWidget( audioSv, 2, 2, 0, 2 ); + +/****** Video Filters ******/ + QWidget* videoPage = addPage(i18n("Video"), i18n("Video Filters"), + KGlobal::iconLoader()->loadIcon("video", KIcon::Panel, KIcon::SizeMedium)); + QGridLayout* videoGrid = new QGridLayout( videoPage, 3, 3 ); + videoGrid->setSpacing( 5 ); + + QCheckBox* useVideoFiltersCB = new QCheckBox( videoPage ); + useVideoFiltersCB->setText( i18n("Enable video filters") ); + useVideoFiltersCB->setChecked( true ); + connect( useVideoFiltersCB, SIGNAL(toggled(bool)), this, SLOT(slotUseVideoFilters(bool))); + + videoGrid->addMultiCellWidget( useVideoFiltersCB, 0, 0, 0, 2 ); + + m_videoFilterCombo = new KComboBox( videoPage ); + m_videoFilterCombo->insertStringList( videoFilters ); + + m_addVideoButton = new KPushButton( i18n("Add Filter"), videoPage ); + connect( m_addVideoButton, SIGNAL( clicked() ), this, SLOT( slotAddVideoClicked() )); + m_removeVideoButton = new KPushButton( i18n("Remove All Filters"), videoPage ); + connect( m_removeVideoButton, SIGNAL( clicked() ), this, SIGNAL( signalRemoveAllVideoFilters() )); + + videoGrid->addWidget( m_videoFilterCombo, 1, 0 ); + videoGrid->addWidget( m_removeVideoButton, 1, 2 ); + videoGrid->addWidget( m_addVideoButton, 1, 1 ); + + QScrollView* videoSv = new QScrollView( videoPage ); + videoSv->setResizePolicy(QScrollView::AutoOneFit); + m_videoFilterPage = new QVBox(videoSv->viewport()); + m_videoFilterPage->setMargin( 5 ); + videoSv->addChild(m_videoFilterPage); + + videoGrid->addMultiCellWidget( videoSv, 2, 2, 0, 2 ); +} + + +FilterDialog::~FilterDialog() +{ + kdDebug() << "FilterDialog: destructor" << endl; +} + + +void FilterDialog::slotUseAudioFilters( bool on ) +{ + m_audioFilterCombo->setEnabled( on ); + m_removeAudioButton->setEnabled( on ); + m_addAudioButton->setEnabled( on ); + m_audioFilterPage->setEnabled( on ); + emit signalUseAudioFilters( on ); +} + + +void FilterDialog::slotUseVideoFilters( bool on ) +{ + m_videoFilterCombo->setEnabled( on ); + m_removeVideoButton->setEnabled( on ); + m_addVideoButton->setEnabled( on ); + m_videoFilterPage->setEnabled( on ); + emit signalUseVideoFilters( on ); +} diff --git a/kaffeine/src/player-parts/xine-part/filterdialog.h b/kaffeine/src/player-parts/xine-part/filterdialog.h new file mode 100644 index 0000000..c730f69 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/filterdialog.h @@ -0,0 +1,68 @@ +/* + * filterdialog.h - config dialog for postprocessing filters + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef FILTERDIALOG_H +#define FILTERDIALOG_H + +#include <kdialogbase.h> + +class KComboBox; +class QVBox; +class QString; +class QStringList; + +/** + *@author Juergen Kofler + */ + +class FilterDialog : public KDialogBase +{ + Q_OBJECT +public: + FilterDialog(const QStringList& audiofilters, const QStringList& videofilters, QWidget *parent=0, const char *name=0); + ~FilterDialog(); + +signals: + void signalCreateAudioFilter(const QString& name, QWidget* parent); + void signalRemoveAllAudioFilters(); + void signalUseAudioFilters(bool); + void signalCreateVideoFilter(const QString& name, QWidget* parent); + void signalRemoveAllVideoFilters(); + void signalUseVideoFilters(bool); + +private slots: + void slotAddAudioClicked() { emit signalCreateAudioFilter(m_audioFilterCombo->currentText(), (QWidget*)m_audioFilterPage); } + void slotUseAudioFilters(bool on); + void slotAddVideoClicked() { emit signalCreateVideoFilter(m_videoFilterCombo->currentText(), (QWidget*)m_videoFilterPage); } + void slotUseVideoFilters(bool on); + +private: + KComboBox* m_audioFilterCombo; + QVBox* m_audioFilterPage; + KPushButton* m_addAudioButton; + KPushButton* m_removeAudioButton; + + KComboBox* m_videoFilterCombo; + QVBox* m_videoFilterPage; + KPushButton* m_addVideoButton; + KPushButton* m_removeVideoButton; +}; + +#endif /* FILTERDIALOG_H */ diff --git a/kaffeine/src/player-parts/xine-part/kxinewidget.cpp b/kaffeine/src/player-parts/xine-part/kxinewidget.cpp new file mode 100644 index 0000000..77f26e2 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/kxinewidget.cpp @@ -0,0 +1,4117 @@ +/* + * kxinewidget.cpp - a kde / qt api for xine-lib + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2005-2006 Christophe Thommeret <hftom@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <qapplication.h> +#include <qwidget.h> +#include <qstringlist.h> + +#include <qtimer.h> +#include <qevent.h> +#include <qdir.h> +#include <qcursor.h> +#include <qimage.h> +#include <qdatetime.h> +#include <qtextcodec.h> + +#include <xine/xineutils.h> + +#include <cmath> + +#include "kxinewidget.h" +#include "kaffeinepart.h" + +#ifdef HAVE_XINERAMA +#include <X11/extensions/Xinerama.h> +#endif + +#ifndef USE_QT_ONLY +#include "kxinewidget.moc" +#include <klocale.h> +#include <kdebug.h> +#endif + +#define TIMER_EVENT_PLAYBACK_FINISHED 100 +#define TIMER_EVENT_NEW_CHANNELS 101 +#define TIMER_EVENT_NEW_TITLE 102 +#define TIMER_EVENT_NEW_STATUS 103 +#define TIMER_EVENT_CHANGE_CURSOR 104 +#define TIMER_EVENT_NEW_MRL_REFERENCE 105 +#define TIMER_EVENT_NEW_XINE_MESSAGE 106 +#define TIMER_EVENT_NEW_XINE_ERROR 107 +#define TIMER_EVENT_FRAME_FORMAT_CHANGE 108 +#define TIMER_EVENT_NEW_VOLUME_LEVEL 109 +#define TIMER_EVENT_RESTART_PLAYBACK 200 +#define TIMER_EVENT_RESIZE_PARENT 300 + + +KXineWidget::KXineWidget(QWidget* parent, const char* name, + const QString& pathToConfigFile, const QString& pathToLogoFile, + const QString& audioDriver, const QString& videoDriver, + bool startManual, bool verbose) + : QWidget(parent,name), m_startXineManual(startManual), m_xineReady(false), + m_logoFile(pathToLogoFile), m_preferedAudio(audioDriver), m_preferedVideo(videoDriver), m_xineVerbose(verbose), + m_xineEngine(NULL), m_audioDriver(NULL), m_videoDriver(NULL), m_xineStream(NULL), connection(NULL), + m_eventQueue(NULL), m_osd(NULL), m_osdUnscaled(false), m_osdShow(false), m_osdSize(0), m_osdFont(NULL), + m_audioChoices(NULL), m_audioInfo(NULL), m_videoChoices(NULL), m_videoInfo(NULL), m_mixerInfo(NULL), + m_osdShowInfo(NULL), + m_osdSizeOptions(NULL), m_osdSizeInfo(NULL), m_osdFontInfo(NULL), +#ifndef USE_QT_ONLY + m_videoFiltersEnabled(true), m_audioFiltersEnabled(true), m_deinterlaceFilter(NULL), + m_deinterlaceEnabled(false), + m_visualPlugin(NULL), +#else + m_xinePost(NULL), m_postAudioSource(NULL), m_postInput(NULL), +#endif + m_visualPluginName(QString::null), m_currentSpeed(Normal), m_softwareMixer(false), m_volumeGain(false), + m_currentZoom(100), m_currentZoomX(100), m_currentZoomY(100), m_currentAudio(0), m_currentSub(0), m_savedPos(0), m_autoresizeEnabled(false) +{ + setMinimumSize(QSize(20,20)); // set a size hint + setPaletteBackgroundColor(QColor(0,0,0)); //black + + /* dvb */ + TimeShiftFilename = ""; + dvbHaveVideo = 0; + dvbOSD = 0; + dvbColor[0] = 0; + connect( &dvbOSDHideTimer, SIGNAL(timeout()), this, SLOT(dvbHideOSD()) ); + + if (pathToConfigFile.isNull()) + { + debugOut("Using default config file ~/.xine/config"); + m_configFilePath = QDir::homeDirPath(); + m_configFilePath.append("/.xine/config"); + } + else + m_configFilePath = pathToConfigFile; + + if (!m_logoFile.isNull()) + appendToQueue(m_logoFile); + +#ifndef USE_QT_ONLY + m_videoFilterList.setAutoDelete(true); /*** delete post plugin on removing from list ***/ + m_audioFilterList.setAutoDelete(true); /*** delete post plugin on removing from list ***/ +#endif + + connect(&m_posTimer, SIGNAL(timeout()), this, SLOT(slotSendPosition())); + connect(&m_lengthInfoTimer, SIGNAL(timeout()), this, SLOT(slotEmitLengthInfo())); + connect(&m_mouseHideTimer, SIGNAL(timeout()), this, SLOT(slotHideMouse())); + connect(&m_osdTimer, SIGNAL(timeout()), this, SLOT(slotOSDHide())); + connect(&m_recentMessagesTimer, SIGNAL(timeout()), this, SLOT(slotNoRecentMessage())); + + setUpdatesEnabled(false); + setMouseTracking(true); +} + + +KXineWidget::~KXineWidget() +{ + /* "careful" shutdown, maybe xine initialization was not successful */ + m_xineReady = false; + + /* stop all timers */ + m_posTimer.stop(); + m_mouseHideTimer.stop(); + +#ifndef USE_QT_ONLY + slotRemoveAllAudioFilters(); + slotRemoveAllVideoFilters(); +#endif + if (m_osd) + xine_osd_free(m_osd); + + if (m_xineStream) + xine_close(m_xineStream); + + debugOut("Shut down xine engine"); + +#ifndef USE_QT_ONLY + if (m_deinterlaceFilter) + { + debugOut("Unwire video filters"); + unwireVideoFilters(); + delete m_deinterlaceFilter; + m_deinterlaceFilter = NULL; + } + if (m_visualPlugin) + { + debugOut("Unwire audio filters"); + unwireAudioFilters(); + debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName )); + delete m_visualPlugin; + m_visualPlugin = NULL; + } +#else + if (m_xinePost) + { + debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName)); + m_postAudioSource = xine_get_audio_source(m_xineStream); + xine_post_wire_audio_port(m_postAudioSource, m_audioDriver); + xine_post_dispose(m_xineEngine, m_xinePost); + } +#endif + if (m_eventQueue) + { + debugOut("Dispose event queue"); + xine_event_dispose_queue(m_eventQueue); + } + if (m_xineStream) + { + debugOut("Dispose stream"); + xine_dispose(m_xineStream); + } + if (m_audioDriver) + { + debugOut("Close audio driver"); + xine_close_audio_driver(m_xineEngine, m_audioDriver); + } + if (m_videoDriver) + { + debugOut("Close video driver"); + xine_close_video_driver(m_xineEngine, m_videoDriver); + } + if (m_xineEngine) + { + saveXineConfig(); + debugOut("Close xine engine"); + xine_exit(m_xineEngine); + } + m_xineEngine = NULL; + + /* free xine config strings */ + if (m_osdShowInfo) free(m_osdShowInfo); + + if (m_osdFontInfo) free(m_osdFontInfo); + if (m_osdFont) free(m_osdFont); + + if (m_osdSizeInfo) free(m_osdSizeInfo); + if (m_osdSizeOptions) + { + int i=0; + while (m_osdSizeOptions[i]) + { + free(m_osdSizeOptions[i]); + i++; + } + delete [] m_osdSizeOptions; + } + + if (m_mixerInfo) free(m_mixerInfo); + + if (m_videoInfo) free(m_videoInfo); + if (m_videoChoices) + { + int i=0; + while (m_videoChoices[i]) + { + free(m_videoChoices[i]); + i++; + } + delete [] m_videoChoices; + } + + if (m_audioInfo) free(m_audioInfo); + if (m_audioChoices) + { + int i=0; + while (m_audioChoices[i]) + { + free(m_audioChoices[i]); + i++; + } + delete [] m_audioChoices; + } + + if (connection) + { + debugOut("Close xine display"); +#ifndef HAVE_XCB + XCloseDisplay(connection); /* close xine display */ +#else + xcb_disconnect(connection); /* close xine display */ +#endif + } + connection = NULL; + + debugOut("xine closed"); +} + + +void KXineWidget::saveXineConfig() +{ + debugOut("Set CD/VCD/DVD path back"); + xine_cfg_entry_t config; + + if (!m_cachedCDPath.isNull()) + { + xine_config_lookup_entry (m_xineEngine, "input.cdda_device", &config); + config.str_value = (char*)m_cachedCDPath.latin1(); + xine_config_update_entry (m_xineEngine, &config); + } + + if (!m_cachedVCDPath.isNull()) + { + xine_config_lookup_entry (m_xineEngine, "input.vcd_device", &config); + config.str_value = (char*)m_cachedVCDPath.latin1(); + xine_config_update_entry (m_xineEngine, &config); + } + + if (!m_cachedDVDPath.isNull()) + { + xine_config_lookup_entry (m_xineEngine, "input.dvd_device", &config); + config.str_value = (char*)m_cachedDVDPath.latin1(); + xine_config_update_entry (m_xineEngine, &config); + } + + debugOut(QString("Save xine config to: %1").arg(m_configFilePath)); + xine_config_save(m_xineEngine, m_configFilePath.ascii()); +} + + +/*************************************************** + * CALLBACKS + ***************************************************/ + +void KXineWidget::destSizeCallback(void* p, int /*video_width*/, int /*video_height*/, double /*video_aspect*/, + int* dest_width, int* dest_height, double* dest_aspect) + +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + *dest_width = vw->width(); + *dest_height = vw->height(); + *dest_aspect = vw->m_displayRatio; +} + + +void KXineWidget::frameOutputCallback(void* p, int video_width, int video_height, double video_aspect, + int* dest_x, int* dest_y, int* dest_width, int* dest_height, + double* dest_aspect, int* win_x, int* win_y) + +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + *dest_x = 0; + *dest_y = 0 ; + *dest_width = vw->width(); + *dest_height = vw->height(); + *win_x = vw->m_globalX; + *win_y = vw->m_globalY; + *dest_aspect = vw->m_displayRatio; + + /* give false aspect for audio visualization*/ + if ( !vw->hasVideo() ) { + *dest_aspect = (video_width*video_aspect)/((vw->width()*video_height/vw->height())-0.5); + } + + /* correct size with video aspect */ + if (video_aspect >= vw->m_displayRatio) + video_width = (int) ( (double) (video_width * video_aspect / vw->m_displayRatio + 0.5) ); + else + video_height = (int) ( (double) (video_height * vw->m_displayRatio / video_aspect) + 0.5); + + /* frame size changed */ + if ( (video_width != vw->m_videoFrameWidth) || (video_height != vw->m_videoFrameHeight) ) + { + debugOut(QString("New video frame size: %1x%2 - aspect ratio: %3").arg(video_width).arg(video_height).arg(video_aspect)); + vw->m_videoFrameWidth = video_width; + vw->m_videoFrameHeight = video_height; + vw->m_videoAspect = video_aspect; + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_FRAME_FORMAT_CHANGE)); + + /* auto-resize parent widget */ + if ((vw->m_autoresizeEnabled) && (vw->parentWidget()) && (vw->m_posTimer.isActive()) && (!vw->parentWidget()->isFullScreen()) + && (video_width > 0) && (video_height > 0)) + { + vw->m_newParentSize = vw->parentWidget()->size() - QSize((vw->width() - video_width), vw->height() - video_height); + + debugOut(QString("Resize video window to: %1x%2").arg(vw->m_newParentSize.width()).arg(vw->m_newParentSize.height())); + + /* we should not do a resize() inside a xine thread, + but post an event to the main thread */ + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_RESIZE_PARENT)); + } + } +} + + +/* + * XINE EVENT THREAD + * only the QT event thread should do GUI operations, + * we use QApplication::postEvent() and a reimplementation of QObject::timerEvent() to + * make sure all critical jobs are done within the QT main thread context + * + * for more information see http://doc.trolltech.com/3.1/threads.html + */ + +void KXineWidget::xineEventListener(void *p, const xine_event_t* xineEvent) +{ + + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + switch (xineEvent->type) + { + case XINE_EVENT_UI_PLAYBACK_FINISHED: + { + debugOut("xine event: playback finished"); + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_PLAYBACK_FINISHED )); + break; + } + case XINE_EVENT_UI_CHANNELS_CHANGED: /* new channel informations */ + { + debugOut("xine event: channels changed"); + int i,channels; + char* lang = new char[128]; + QString slang; + int num; + QStringList tmp; + bool update=false, sk; + + /*** get audio channels ***/ + tmp.append(i18n("auto")); + channels = xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_MAX_AUDIO_CHANNEL); + for(i = 0; i < channels; i++) + { + slang = QString("%1.").arg(i+1); + if (xine_get_audio_lang(vw->m_xineStream, i, lang)) + slang += lang; + tmp << slang; + } + if ( tmp!=vw->m_audioCh ) { + update = true; + vw->m_audioCh = tmp; + } + num = xine_get_param(vw->m_xineStream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL); + if ( vw->m_currentAudio!=num ) { + update = true; + if ( num>channels ) + vw->m_currentAudio = -1; + else + vw->m_currentAudio = num; + } + + /*** get subtitle channels ***/ + tmp.clear(); + tmp.append(i18n("off")); + channels = xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_MAX_SPU_CHANNEL); + for(i = 0; i<channels; i++) + { + slang = QString("%1.").arg(i+1); + if (xine_get_spu_lang(vw->m_xineStream, i, lang)) + slang += lang; + tmp << slang; + } + if ( tmp!=vw->m_subCh ) { + update = true; + vw->m_subCh = tmp; + } + num = xine_get_param(vw->m_xineStream, XINE_PARAM_SPU_CHANNEL); + if ( vw->m_currentSub!=num ) { + update = true; + if ( num>channels ) + vw->m_currentSub = -1; + else + vw->m_currentSub = num; + } + + delete [] lang; + + //check if stream is seekable + sk = (bool)xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_SEEKABLE); + if ( vw->m_trackIsSeekable!=sk ) { + update = true; + vw->m_trackIsSeekable = sk; + } + + if ( update ) + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_CHANNELS)); + break; + } + case XINE_EVENT_UI_SET_TITLE: /* set new title */ + { + debugOut("xine event: ui set title"); + xine_ui_data_t* xd = (xine_ui_data_t*)xineEvent->data; + vw->m_trackTitle = QString::fromLocal8Bit( (char*)xd->str ); + + vw->m_lengthInfoTries = 0; + vw->m_lengthInfoTimer.start(1000); /* May be new Length on Changing DVD/VCD titles */ + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_TITLE)); + break; + } + case XINE_EVENT_PROGRESS: + { + debugOut("xine event: progress info"); + xine_progress_data_t* pd = (xine_progress_data_t*)xineEvent->data; + + vw->m_statusString = QString::fromLocal8Bit(pd->description) + " " + QString::number(pd->percent) + "%"; + + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_STATUS)); + break; + } + case XINE_EVENT_DROPPED_FRAMES: + { + debugOut("xine event: dropped frames"); + xine_dropped_frames_t* dropped = (xine_dropped_frames_t*)xineEvent->data; + + warningOut(QString("Skipped frames: %1 - discarded frames: %2").arg(dropped->skipped_frames/10).arg(dropped->discarded_frames/10)); + + break; + } + case XINE_EVENT_SPU_BUTTON: + { + debugOut("xine event: spu button"); + xine_spu_button_t* button = (xine_spu_button_t*)xineEvent->data; + + if (button->direction == 1) /* enter a button */ + { + debugOut("DVD Menu: Mouse entered button"); + vw->m_DVDButtonEntered = true; + } + else + { + debugOut("DVD Menu: Mouse left button"); + vw->m_DVDButtonEntered = false; + } + + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_CHANGE_CURSOR)); + break; + } + case XINE_EVENT_UI_NUM_BUTTONS: + { + debugOut("xine event: ui num buttons"); + + break; + } + case XINE_EVENT_MRL_REFERENCE: + { + debugOut("xine event: mrl reference"); + xine_mrl_reference_data_t* mrldata = (xine_mrl_reference_data_t*)xineEvent->data; + vw->m_newMRLReference = mrldata->mrl; + + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_MRL_REFERENCE)); + break; + } + case XINE_EVENT_FRAME_FORMAT_CHANGE: + { + // debugOut("xine event: frame format change"); + + // QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_FRAME_FORMAT_CHANGE)); + break; + } + case XINE_EVENT_AUDIO_LEVEL: + { + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_VOLUME_LEVEL)); + break; + } + case XINE_EVENT_UI_MESSAGE: + { + debugOut("xine event: xine message"); + + xine_ui_message_data_t *data = (xine_ui_message_data_t *)xineEvent->data; + QString message; + + switch(data->type) + { + case XINE_MSG_NO_ERROR: + { + /* copy strings, and replace '\0' separators by '\n' */ + char* s = data->messages; + char* d = new char[2000]; + + while(s && (*s != '\0') && ((*s + 1) != '\0')) + { + switch(*s) + { + case '\0': + { + *d = '\n'; + break; + } + default: + { + *d = *s; + break; + } + } + s++; + d++; + } + *++d = '\0'; + + message = d; + delete [] d; + break; + } + case XINE_MSG_GENERAL_WARNING: + { + message = i18n("General Warning: \n"); + + if(data->explanation) + message = message + ((char *) data + data->explanation) + " " + ((char *) data + data->parameters); + else + message = message + i18n("No Informations available."); + + break; + } + case XINE_MSG_SECURITY: + { + message = i18n("Security Warning: \n"); + + if(data->explanation) + message = message + ((char *) data + data->explanation) + " " + ((char *) data + data->parameters); + + break; + } + case XINE_MSG_UNKNOWN_HOST: + { + message = i18n("The host you're trying to connect is unknown.\nCheck the validity of the specified hostname. "); + if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + break; + } + case XINE_MSG_UNKNOWN_DEVICE: + { + message = i18n("The device name you specified seems invalid. "); + if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + break; + } + case XINE_MSG_NETWORK_UNREACHABLE: + { + message = i18n("The network looks unreachable.\nCheck your network setup and the server name. "); + if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + break; + } + case XINE_MSG_AUDIO_OUT_UNAVAILABLE: + { + message = i18n("Audio output unavailable. Device is busy. "); + if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + break; + } + case XINE_MSG_CONNECTION_REFUSED: + { + message = i18n("The connection was refused.\nCheck the host name. "); + if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + break; + } + case XINE_MSG_FILE_NOT_FOUND: + { + message = "@"+i18n("The specified file or url was not found. Please check it. "); + if(data->explanation) + message = message + "(" + QString::fromLocal8Bit((char *) data + data->parameters) + ")"; + break; + } + case XINE_MSG_PERMISSION_ERROR: + { + message = i18n("Permission to this source was denied. "); + // if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + break; + } + case XINE_MSG_READ_ERROR: + { + message = i18n("The source can't be read.\nMaybe you don't have enough rights for this, or source doesn't contain data (e.g: no disc in drive). "); + if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + debugOut(message); + return; // This error is handled by autoinstallation + } + case XINE_MSG_LIBRARY_LOAD_ERROR: + { + message = i18n("A problem occur while loading a library or a decoder: "); + if(data->explanation) + message = message + ((char *) data + data->parameters); + break; + } + case XINE_MSG_ENCRYPTED_SOURCE: + { + message = i18n("The source seems encrypted, and can't be read. "); + if (vw->m_trackURL.contains("dvd:/")) + { + if (KaffeinePart::installDistroCodec(vw, "xine-engine", "dvdcss")) + return; + message = message + i18n("\nYour DVD is probably crypted. According to your country laws, you can or can't use libdvdcss to be able to read this disc. "); + } + if(data->explanation) + message = message + "(" + ((char *) data + data->parameters) + ")"; + break; + } + default: + { + message = i18n("Unknown error: \n"); + if(data->explanation) + message = message + ((char *) data + data->explanation) + " " + ((char *) data + data->parameters); + break; + } + } + + vw->m_xineMessage = message; + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_XINE_MESSAGE)); + break; + } + default: + { + //debugOut("xine event: unhandled type "); + break; + } + } +} + +void KXineWidget::timerEvent( QTimerEvent* tevent ) +{ + switch ( tevent->timerId() ) + { + case TIMER_EVENT_PLAYBACK_FINISHED: + { + if ( !TimeShiftFilename.isEmpty() ) + { + QTimer::singleShot(0, this, SLOT(slotPlayTimeShift())); + break; + } + if ( m_trackURL=="DVB" || m_trackURL.contains(".kaxtv") ) + break; + +#ifdef XINE_PARAM_GAPLESS_SWITCH + if ( xine_check_version(1,1,1) ) + xine_set_param( m_xineStream, XINE_PARAM_GAPLESS_SWITCH, 1); +#endif + if (isQueueEmpty()) + { + if (m_trackURL != m_logoFile) + emit signalPlaybackFinished(); + else + xine_stop(m_xineStream); + } + else + QTimer::singleShot(0, this, SLOT(slotPlay())); + break; + } + case TIMER_EVENT_NEW_CHANNELS: + { + emit signalNewChannels(m_audioCh, m_subCh, m_currentAudio, m_currentSub); + break; + } + case TIMER_EVENT_NEW_TITLE: + { + emit signalTitleChanged(); + break; + } + case TIMER_EVENT_FRAME_FORMAT_CHANGE: + { + if ((m_trackHasVideo) && (m_trackURL != m_logoFile)) + emit signalVideoSizeChanged(); + break; + } + case TIMER_EVENT_NEW_STATUS: + { + emit signalXineStatus(m_statusString); + break; + } + case TIMER_EVENT_CHANGE_CURSOR: + { + if (m_DVDButtonEntered) + setCursor(QCursor(Qt::PointingHandCursor)); + else + setCursor(QCursor(Qt::ArrowCursor)); + break; + } + case TIMER_EVENT_NEW_MRL_REFERENCE: + { + m_queue.prepend(m_newMRLReference ); + break; + } + case TIMER_EVENT_NEW_VOLUME_LEVEL: + { + emit signalSyncVolume(); + break; + } + case TIMER_EVENT_NEW_XINE_MESSAGE: + { + if (!m_recentMessagesTimer.isActive()) + { + m_recentMessagesTimer.start(1500); + emit signalXineMessage(m_xineMessage); + } + else + { + //restart + warningOut(QString("Message: '%1' was blocked!").arg(m_xineMessage)); + m_recentMessagesTimer.start(1500); + } + break; + } + case TIMER_EVENT_NEW_XINE_ERROR: + { + emit signalXineError(m_xineError); + break; + } + case TIMER_EVENT_RESTART_PLAYBACK: + { + appendToQueue(m_trackURL); + slotPlay(); + break; + } + case TIMER_EVENT_RESIZE_PARENT: + { + parentWidget()->resize(m_newParentSize); + break; + } + default: break; + } +} + +void KXineWidget::slotNoRecentMessage() +{ + m_recentMessagesTimer.stop(); +} + +/******************* new video driver *********************/ + +void KXineWidget::videoDriverChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + if (entry == NULL) return; +#ifndef USE_QT_ONLY + KXineWidget* vw = (KXineWidget*) p; + xine_video_port_t* oldVideoDriver = vw->m_videoDriver; + xine_video_port_t* noneVideoDriver; + + int pos, time, length; + + debugOut(QString("New video driver: %1").arg(entry->enum_values[entry->num_value])); + + if (vw->m_osd) + { + xine_osd_free(vw->m_osd); + vw->m_osd = NULL; + } + + noneVideoDriver = xine_open_video_driver(vw->m_xineEngine, "none", + XINE_VISUAL_TYPE_NONE, NULL); + if (!noneVideoDriver) + { + errorOut("Can't init Video Driver 'none', operation aborted."); + return; + } + + bool playing = false; + if (vw->isPlaying()) + { + playing = true; + vw->m_savedPos = 0; + + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(vw->m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + + if ( ret != 0 ) + vw->m_savedPos = pos; + } + + xine_close(vw->m_xineStream); + + /* wire filters to "none" driver so the old one can be safely disposed */ + vw->m_videoDriver = noneVideoDriver; + vw->unwireVideoFilters(); + vw->wireVideoFilters(); + + vw->unwireAudioFilters(); + if (vw->m_visualPlugin) + { + debugOut(QString("Dispose visual plugin: %1").arg(vw->m_visualPluginName)); + delete vw->m_visualPlugin; + vw->m_visualPlugin = NULL; + } + + xine_event_dispose_queue(vw->m_eventQueue); + xine_dispose(vw->m_xineStream); + + xine_close_video_driver(vw->m_xineEngine, oldVideoDriver); + + vw->m_videoDriver = xine_open_video_driver(vw->m_xineEngine, +#ifndef HAVE_XCB + entry->enum_values[entry->num_value], XINE_VISUAL_TYPE_X11, +#else + entry->enum_values[entry->num_value], XINE_VISUAL_TYPE_XCB, +#endif + (void *) &(vw->m_x11Visual)); + + if (!vw->m_videoDriver) + { + vw->m_xineError = i18n("Error: Can't init new Video Driver %1 - using %2!").arg(entry->enum_values[entry->num_value]).arg(vw->m_videoDriverName); + QApplication::postEvent(vw, new QTimerEvent( TIMER_EVENT_NEW_XINE_ERROR)); + playing = false; + vw->m_videoDriver = xine_open_video_driver(vw->m_xineEngine, +#ifndef HAVE_XCB + vw->m_videoDriverName.ascii(), XINE_VISUAL_TYPE_X11, +#else + vw->m_videoDriverName.ascii(), XINE_VISUAL_TYPE_XCB, +#endif + (void *) &(vw->m_x11Visual)); + } + else + { + vw->m_videoDriverName = entry->enum_values[entry->num_value]; + vw->m_statusString = i18n("Using Video Driver: %1").arg(vw->m_videoDriverName); + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_STATUS)); + } + + vw->m_xineStream = xine_stream_new(vw->m_xineEngine, vw->m_audioDriver, vw->m_videoDriver); + vw->m_eventQueue = xine_event_new_queue (vw->m_xineStream); + xine_event_create_listener_thread(vw->m_eventQueue, &KXineWidget::xineEventListener, p); + + /* rewire filters to the new driver */ + vw->unwireVideoFilters(); + vw->wireVideoFilters(); + + /* "none" can now be disposed too */ + xine_close_video_driver(vw->m_xineEngine, noneVideoDriver); + + vw->initOSD(); + + if (playing) + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_RESTART_PLAYBACK)); +#endif +} + +/*********************** new audio driver *************************/ + +void KXineWidget::audioDriverChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + if (entry == NULL) return; +#ifndef USE_QT_ONLY + KXineWidget* vw = (KXineWidget*) p; + + int pos, time, length; + + debugOut(QString("New audio driver: %1").arg(entry->enum_values[entry->num_value])); + + if (vw->m_osd) + { + xine_osd_free(vw->m_osd); + vw->m_osd = NULL; + } + + vw->unwireVideoFilters(); + + bool playing = false; + if (vw->isPlaying()) + { + playing = true; + vw->m_savedPos = 0; + + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(vw->m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + + if ( ret != 0 ) + vw->m_savedPos = pos; + } + + xine_close(vw->m_xineStream); + + vw->unwireAudioFilters(); + if (vw->m_visualPlugin) + { + debugOut(QString("Dispose visual plugin: %1").arg(vw->m_visualPluginName)); + delete vw->m_visualPlugin; + vw->m_visualPlugin = NULL; + } + + xine_event_dispose_queue(vw->m_eventQueue); + xine_dispose(vw->m_xineStream); + xine_close_audio_driver(vw->m_xineEngine, vw->m_audioDriver); + vw->m_audioDriver = NULL; + + vw->m_audioDriver = xine_open_audio_driver(vw->m_xineEngine, entry->enum_values[entry->num_value], NULL); + + if (!vw->m_audioDriver) + { + vw->m_xineError = i18n("Error: Can't init new Audio Driver %1 - using %2!").arg(entry->enum_values[entry->num_value]).arg(vw->m_audioDriverName); + QApplication::postEvent(vw, new QTimerEvent( TIMER_EVENT_NEW_XINE_ERROR)); + playing = false; + vw->m_audioDriver = xine_open_audio_driver(vw->m_xineEngine, vw->m_audioDriverName.ascii(), NULL); + } + else + { + vw->m_audioDriverName = entry->enum_values[entry->num_value]; + vw->m_statusString = i18n("Using Audio Driver: %1").arg(vw->m_audioDriverName); + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_STATUS)); + } + + vw->m_xineStream = xine_stream_new(vw->m_xineEngine, vw->m_audioDriver, vw->m_videoDriver); + vw->m_eventQueue = xine_event_new_queue (vw->m_xineStream); + xine_event_create_listener_thread(vw->m_eventQueue, &KXineWidget::xineEventListener, p); + + vw->wireVideoFilters(); + + vw->initOSD(); + + if (playing) + QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_RESTART_PLAYBACK)); +#endif +} + +/******** change audio mixer method ****************/ +void KXineWidget::audioMixerMethodChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + vw->m_softwareMixer = (bool)entry->num_value; +} + +/******** Callback for OSD configuration ****************/ +void KXineWidget::showOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + if (vw->m_osd) + vw->m_osdShow = (bool)entry->num_value; +} + +void KXineWidget::sizeForOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + const int fontsizetable[] = { 16,20,24,32,48,64 }; + + if (entry->num_value >= 6) + { + debugOut("Font size not defined: Shouldn't have happened"); + return; + } + + if (vw->m_osd) + { + vw->m_osdSize = entry->num_value; + xine_osd_set_font(vw->m_osd, vw->m_osdFont, fontsizetable[vw->m_osdSize]); + } +} + +void KXineWidget::fontForOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + const int fontsizetable[] = { 16,20,24,32,48,64 }; + + if (vw->m_osd) + if (entry->str_value) + { + free(vw->m_osdFont); + vw->m_osdFont = strdup(entry->str_value); + if (!xine_osd_set_font(vw->m_osd, vw->m_osdFont, fontsizetable[vw->m_osdSize])) + { + free(vw->m_osdFont); + vw->m_osdFont = strdup("sans"); + if (!xine_osd_set_font(vw->m_osd, vw->m_osdFont, fontsizetable[vw->m_osdSize])) + warningOut("Default SANS font not found: shouldn't have happened."); + } + } +} + +void KXineWidget::monitorXResChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + vw->monitorXRes = (double)entry->num_value; + double m_displayRatio = vw->monitorYRes / vw->monitorXRes; + if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) + m_displayRatio = 1; + vw->m_displayRatio = m_displayRatio; +} + +void KXineWidget::monitorYResChangedCallback(void* p, xine_cfg_entry_t* entry) +{ + if (p == NULL) return; + KXineWidget* vw = (KXineWidget*) p; + + vw->monitorYRes = (double)entry->num_value; + double m_displayRatio = vw->monitorYRes / vw->monitorXRes; + if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) + m_displayRatio = 1; + vw->m_displayRatio = m_displayRatio; +} + +/********************************************** + * EVENT LOOP + *********************************************/ + +#ifndef HAVE_XCB +bool KXineWidget::x11Event(XEvent *event) +{ + if (isXineReady()) + if (event->type == Expose) + if (event->xexpose.count == 0) + xine_port_send_gui_data(m_videoDriver, XINE_GUI_SEND_EXPOSE_EVENT, event); + + return false; +} +#else +void KXineWidget::paintEvent(QPaintEvent *event) +{ + if (isXineReady()) { + const QRect &rect = event->rect(); + + xcb_expose_event_t xcb_event; + memset(&xcb_event, 0, sizeof(xcb_event)); + + xcb_event.window = winId(); + xcb_event.x = rect.x(); + xcb_event.y = rect.y(); + xcb_event.width = rect.width(); + xcb_event.height = rect.height(); + xcb_event.count = 0; + + xine_port_send_gui_data(m_videoDriver, XINE_GUI_SEND_EXPOSE_EVENT, &xcb_event); + } + + QWidget::paintEvent(event); +} +#endif + +/********************************************************** + * INIT XINE ENGINE + *********************************************************/ + +void KXineWidget::polish() +{ + if ((!m_startXineManual) && (!isXineReady())) /* start xine engine automatically? */ + { + initXine(); + } +} + +bool KXineWidget::initXine() +{ + if (isXineReady()) + return true; + + emit signalXineStatus(i18n("Init xine...")); + globalPosChanged(); /* get global pos of the window */ + + /**** INIT XINE DISPLAY ****/ + +#ifndef HAVE_XCB + XInitThreads(); + + connection = XOpenDisplay(NULL); +#else + int screen_nbr = 0; + connection = xcb_connect(NULL, &screen_nbr); +#endif + + if (!connection) + { + emit signalXineFatal(i18n("Failed to connect to X-Server!")); + return false; + } + + int m_xineWindow = winId(); + +/* // determine display aspect ratio + double resHor = ((double) DisplayWidth(x11Display(), x11Screen())) / DisplayWidthMM(x11Display(), x11Screen()); + double resVer = ((double) DisplayHeight(x11Display(), x11Screen())) / DisplayHeightMM(x11Display(), x11Screen()); + + m_displayRatio = resVer / resHor; + + if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) + m_displayRatio = 1; + +#ifdef HAVE_XINERAMA + int dummy_event, dummy_error; + + if (XineramaQueryExtension(x11Display(), &dummy_event, &dummy_error)) + { + int count = 1; + debugOut("Xinerama extension present"); + XineramaQueryScreens(x11Display(), &count); + debugOut(QString("%1 screens detected").arg(count)); + if (count > 1) + // multihead -> assuming square pixels + m_displayRatio = 1.0; + } +#endif + + debugOut(QString("Display aspect ratio (v/h): %1").arg(m_displayRatio));*/ + + /**** INIT XINE ENGINE ****/ + + debugOut(QString("Using xine version %1").arg(xine_get_version_string())); + + m_xineEngine = xine_new(); + if (!m_xineEngine) + { + emit signalXineFatal(i18n("Can't init xine Engine!")); + return false; + } + + if (m_xineVerbose) + xine_engine_set_param(m_xineEngine, XINE_ENGINE_PARAM_VERBOSITY, 99); + + /* load configuration */ + + if (!QFile::exists(m_configFilePath)) + warningOut("No config file found, will create one..."); + else + xine_config_load(m_xineEngine, QFile::encodeName(m_configFilePath)); + + + debugOut("Post-init xine engine"); + xine_init(m_xineEngine); + + /** set xine parameters **/ + + const char* const* drivers = NULL; + drivers = xine_list_audio_output_plugins(m_xineEngine); + int i = 0; + while (drivers[i] != NULL) i++; + m_audioChoices = new char*[i+2]; + m_audioChoices[0] = strdup("auto"); + m_audioDriverList << m_audioChoices[0]; + i = 0; + while(drivers[i]) + { + m_audioChoices[i+1] = strdup(drivers[i]); + m_audioDriverList << m_audioChoices[i+1]; + i++; + } + m_audioChoices[i+1] = NULL; + + m_audioInfo = strdup(i18n("Audiodriver to use (default: auto)").local8Bit()); + i = xine_config_register_enum(m_xineEngine, "audio.driver", 0, + m_audioChoices, m_audioInfo, NULL, 10, &KXineWidget::audioDriverChangedCallback, this); + + if (m_audioDriverList.contains(m_preferedAudio)) + m_audioDriverName = m_preferedAudio; + else + m_audioDriverName = m_audioChoices[i]; + + debugOut(QString("Use audio driver %1").arg(m_audioDriverName)); + + drivers = xine_list_video_output_plugins(m_xineEngine); + i = 0; + while (drivers[i] != NULL) i++; + m_videoChoices = new char*[i+2]; + m_videoChoices[0] = strdup("auto"); + m_videoDriverList << m_videoChoices[0]; + i = 0; + while(drivers[i]) + { + m_videoChoices[i+1] = strdup(drivers[i]); + m_videoDriverList << m_videoChoices[i+1]; + i++; + } + m_videoChoices[i+1] = NULL; + + m_videoInfo = strdup(i18n("Videodriver to use (default: auto)").local8Bit()); + i = xine_config_register_enum(m_xineEngine, "video.driver", 0, + m_videoChoices, m_videoInfo, NULL, 10, &KXineWidget::videoDriverChangedCallback, this); + + if (m_videoDriverList.contains(m_preferedVideo)) + m_videoDriverName = m_preferedVideo; + else + m_videoDriverName = m_videoChoices[i]; + + debugOut(QString("Use video driver %1").arg(m_videoDriverName)); + + m_mixerInfo = strdup(i18n("Use software audio mixer").local8Bit()); + m_softwareMixer = (bool)xine_config_register_bool(m_xineEngine, "audio.mixer_software", 1, m_mixerInfo, + NULL, 10, &KXineWidget::audioMixerMethodChangedCallback, this); + + m_osdShowInfo = strdup(i18n("Show OSD Messages").local8Bit()); + m_osdShow = (bool)xine_config_register_bool(m_xineEngine, "osd.osd_messages", 1, m_osdShowInfo, + NULL, 10, &KXineWidget::showOSDMessagesChangedCallback, this); + + m_osdSizeOptions = new char*[7]; + m_osdSizeOptions[0] = strdup("tiny"); + m_osdSizeOptions[1] = strdup("small"); + m_osdSizeOptions[2] = strdup("medium"); + m_osdSizeOptions[3] = strdup("large"); + m_osdSizeOptions[4] = strdup("very large"); + m_osdSizeOptions[5] = strdup("huge"); + m_osdSizeOptions[6] = NULL; + + m_osdSizeInfo = strdup(i18n("Size of OSD text").local8Bit()); + m_osdSize = (int)xine_config_register_enum(m_xineEngine, "osd.osd_size", 1 /*small - 20P*/, m_osdSizeOptions, m_osdSizeInfo, + NULL, 10, &KXineWidget::sizeForOSDMessagesChangedCallback, this); + + m_osdFontInfo = strdup(i18n("Font for OSD Messages").local8Bit()); + m_osdFont = strdup((char*)xine_config_register_string(m_xineEngine, "osd.osd_font", "sans", m_osdFontInfo, + NULL, 10, &KXineWidget::fontForOSDMessagesChangedCallback, this)); + + xResInfo = strdup(i18n("Monitor horizontal resolution (dpi).").local8Bit()); + monitorXRes = (bool)xine_config_register_range(m_xineEngine, "video.screen_x_res", 78, 1, 200, xResInfo, + NULL, 10, &KXineWidget::monitorXResChangedCallback, this); + yResInfo = strdup(i18n("Monitor vertical resolution (dpi).").local8Bit()); + monitorYRes = (bool)xine_config_register_range(m_xineEngine, "video.screen_y_res", 78, 1, 200, yResInfo, + NULL, 10, &KXineWidget::monitorYResChangedCallback, this); + + double resHor = (double)monitorXRes; + double resVer = (double)monitorYRes; + m_displayRatio = resVer / resHor; + if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) + m_displayRatio = 1; + + /* init video driver */ + debugOut("Init video driver"); + +#ifndef HAVE_XCB + m_x11Visual.display = connection; + m_x11Visual.screen = DefaultScreen(connection); + m_x11Visual.d = m_xineWindow; +#else + xcb_screen_iterator_t screen_it = xcb_setup_roots_iterator(xcb_get_setup(connection)); + while ((screen_it.rem > 1) && (screen_nbr > 0)) { + xcb_screen_next(&screen_it); + --screen_nbr; + } + + m_x11Visual.connection = connection; + m_x11Visual.screen = screen_it.data; + m_x11Visual.window = m_xineWindow; +#endif + m_x11Visual.dest_size_cb = &KXineWidget::destSizeCallback; + m_x11Visual.frame_output_cb = &KXineWidget::frameOutputCallback; + m_x11Visual.user_data = (void*)this; + + m_videoDriver = xine_open_video_driver(m_xineEngine, +#ifndef HAVE_XCB + m_videoDriverName.ascii(), XINE_VISUAL_TYPE_X11, +#else + m_videoDriverName.ascii(), XINE_VISUAL_TYPE_XCB, +#endif + (void *) &(m_x11Visual)); + + if (!m_videoDriver && m_videoDriverName != "auto") + { + emit signalXineError(i18n("Can't init Video Driver '%1' - trying 'auto'...").arg(m_videoDriverName)); + m_videoDriverName = "auto"; + m_videoDriver = xine_open_video_driver(m_xineEngine, +#ifndef HAVE_XCB + m_videoDriverName.ascii(), XINE_VISUAL_TYPE_X11, +#else + m_videoDriverName.ascii(), XINE_VISUAL_TYPE_XCB, +#endif + (void *) &(m_x11Visual)); + } + + if (!m_videoDriver) + { + emit signalXineFatal(i18n("All Video Drivers failed to initialize!")); + return false; + } + + /* init audio driver */ + debugOut("Init audio driver"); + + m_audioDriver = xine_open_audio_driver(m_xineEngine, m_audioDriverName.ascii(), NULL); + + if (!m_audioDriver && m_audioDriverName != "auto") + { + emit signalXineError(i18n("Can't init Audio Driver '%1' - trying 'auto'...").arg(m_audioDriverName)); + m_audioDriverName = "auto"; + m_audioDriver = xine_open_audio_driver (m_xineEngine, m_audioDriverName.ascii(), NULL); + } + + if (!m_audioDriver) + { + emit signalXineFatal(i18n("All Audio Drivers failed to initialize!")); + return false; + } + + //debugOut("Open xine stream"); + + m_xineStream = xine_stream_new(m_xineEngine, m_audioDriver, m_videoDriver); + if (!m_xineStream) + { + emit signalXineFatal(i18n("Can't create a new xine Stream!")); + return false; + } + +#ifdef XINE_PARAM_EARLY_FINISHED_EVENT + if ( xine_check_version(1,1,1) ) { + // enable gapless playback + xine_set_param(m_xineStream, XINE_PARAM_EARLY_FINISHED_EVENT, 1 ); + } +#endif + + /*** OSD ***/ + + initOSD(); + + /** event handling **/ + + m_eventQueue = xine_event_new_queue (m_xineStream); + xine_event_create_listener_thread(m_eventQueue, &KXineWidget::xineEventListener, (void*)this); + + //maybe user closed player in muted state + if (m_softwareMixer) + xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_MUTE, 0); + else + xine_set_param(m_xineStream, XINE_PARAM_AUDIO_MUTE, 0); + + m_xineReady = true; + + debugOut("xine init successful"); + + emit signalXineStatus(i18n("Ready")); + emit signalXineReady(); + + /** something to play? **/ + slotPlay(); + + return true; +} + +void KXineWidget::initOSD() +{ + debugOut("Init OSD"); + const int fontsizetable[] = { 16,20,24,32,48,64 }; + m_osd = xine_osd_new(m_xineStream, 10, 10, 1000, 200); + if (m_osd) + { + if (!xine_osd_set_font(m_osd, m_osdFont, fontsizetable[m_osdSize])) + { + debugOut(QString("Font ->%1<- specified for OSD doesn't exists.").arg(m_osdFont)); + free(m_osdFont); + m_osdFont = strdup("sans"); + xine_osd_set_font(m_osd, m_osdFont, fontsizetable[m_osdSize]); + } + debugOut(QString("Font for OSD: %1").arg(m_osdFont)); + xine_osd_set_text_palette(m_osd, XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1); + m_osdUnscaled = (xine_osd_get_capabilities(m_osd) & XINE_OSD_CAP_UNSCALED); + if (m_osdUnscaled) + debugOut("Unscaled OSD available"); + } + else + warningOut("Initialisation of xine OSD failed."); +} + +/************************************************ + * PLAY MRL + ************************************************/ + +bool KXineWidget::playDvb() +{ +#ifndef USE_QT_ONLY + unwireAudioFilters(); + + QPtrList<PostFilter> activeList; + + if (m_audioFilterList.count() && m_audioFiltersEnabled) + activeList = m_audioFilterList; + + if ( !dvbHaveVideo ) + { + if (!m_visualPlugin) + { + debugOut(QString("Init visual plugin: %1").arg(m_visualPluginName)); + m_visualPlugin = new PostFilter(m_visualPluginName, m_xineEngine, m_audioDriver, m_videoDriver, NULL); + } + + activeList.insert (0, m_visualPlugin); + } + else + { + if (m_visualPlugin) + { + debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName)); + delete m_visualPlugin; + m_visualPlugin = NULL; + } + } + + if (activeList.count()) + { + xine_post_wire_audio_port(activeList.at(activeList.count()-1)->getOutput(), m_audioDriver); + + for (uint i = activeList.count()-1; i >0; i--) + { + xine_post_wire(activeList.at(i-1)->getOutput(), activeList.at(i)->getInput()); + } + + xine_post_wire( xine_get_audio_source(m_xineStream), activeList.at(0)->getInput()); + } +#endif + + if (!xine_play(m_xineStream, 0,0)) + { + sendXineError(); + return false; + } + + m_currentSpeed = Normal; + m_trackHasChapters = false; + m_trackArtist = QString::null; + m_trackAlbum = QString::null; + m_trackNumber = QString::null; + m_trackYear = QString::null; + m_trackComment = QString::null; + + m_trackIsSeekable = false; + + if ( !dvbHaveVideo ) m_trackHasVideo = false; + else m_trackHasVideo = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO); + if (m_trackHasVideo) + { + m_trackVideoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); + m_videoFrameWidth = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_WIDTH); + m_videoFrameHeight = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_HEIGHT); + m_trackVideoBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_BITRATE); + } + else + { + m_trackVideoCodec = QString::null; + m_videoFrameWidth = 0; + m_videoFrameHeight = 0; + m_trackVideoBitrate = 0; + } + + m_trackHasAudio = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_AUDIO); + if (m_trackHasAudio) + { + m_trackAudioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); + m_trackAudioBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_AUDIO_BITRATE); + } + else + { + m_trackAudioCodec = QString::null; + m_trackAudioBitrate = 0; + } + + m_trackLength = getLengthInfo(); + + slotSetAudioChannel(0); //refresh channel info + m_posTimer.start(1000); + + emit signalXinePlaying(); + emit signalXineStatus(i18n("Playing")); + + return true; +} + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN +#define rgb2yuv(R,G,B) ((((((66*R+129*G+25*B+128)>>8)+16)<<8)|(((112*R-94*G-18*B+128)>>8)+128))<<8|(((-38*R-74*G+112*B+128)>>8)+128)) +#else +#define rgb2yuv(R,G,B) (((((((-38*R-74*G+112*B+128)>>8)+128)<<8)|(((112*R-94*G-18*B+128)>>8)+128))<<8|(((66*R+129*G+25*B+128)>>8)+16))<<8) +#endif + +void KXineWidget::initDvbPalette() +{ + if ( dvbColor[0] ) return; + + memset( dvbColor, 0, sizeof(dvbColor) ); + memset( dvbTrans, 0, sizeof(dvbTrans) ); + dvbColor[0]=1; + + unsigned int blueText[11] = { + rgb2yuv(0,0,0), /* 0 : not used */ + rgb2yuv(0,0,0), /* 1 : font bg */ + rgb2yuv(10,50,40), /* 2 : transition bg->border */ + rgb2yuv(30,100,85), /* 3 */ + rgb2yuv(50,150,130), /* 4 */ + rgb2yuv(70,200,175), /* 5 */ + rgb2yuv(90,255,220), /* 6 : border */ + rgb2yuv(90,255,220), /* 7 : transition border->fg */ + rgb2yuv(90,255,220), /* 8 */ + rgb2yuv(90,255,220), /* 9 */ + rgb2yuv(90,255,220), /* 10 : font fg */ + }; + unsigned int whiteText[11] = { + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(50,50,50), + rgb2yuv(100,100,100), + rgb2yuv(150,150,150), + rgb2yuv(200,200,200), + rgb2yuv(255,255,255), + rgb2yuv(255,255,255), + rgb2yuv(255,255,255), + rgb2yuv(255,255,255), + rgb2yuv(255,255,255), + }; + unsigned int greenText[11] = { + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(30,50,30), + rgb2yuv(60,100,30), + rgb2yuv(90,150,90), + rgb2yuv(120,200,120), + rgb2yuv(150,255,150), + rgb2yuv(150,255,150), + rgb2yuv(150,255,150), + rgb2yuv(150,255,150), + rgb2yuv(150,255,150), + }; + unsigned char textAlpha[11] = { 0, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, }; +#define DVB_TEXT_WHITE 100 +#define DVB_TEXT_BLUE 111 +#define DVB_TEXT_GREEN 122 + int a; + for ( a=DVB_TEXT_BLUE; a<DVB_TEXT_BLUE+11; a++ ) + { + dvbColor[a]=blueText[a-DVB_TEXT_BLUE]; + dvbTrans[a]=textAlpha[a-DVB_TEXT_BLUE]; + } + for ( a=DVB_TEXT_GREEN; a<DVB_TEXT_GREEN+11; a++ ) + { + dvbColor[a]=greenText[a-DVB_TEXT_GREEN]; + dvbTrans[a]=textAlpha[a-DVB_TEXT_GREEN]; + } + for ( a=DVB_TEXT_WHITE; a<DVB_TEXT_WHITE+11; a++ ) + { + dvbColor[a]=whiteText[a-DVB_TEXT_WHITE]; + dvbTrans[a]=textAlpha[a-DVB_TEXT_WHITE]; + } +#define DVB_COLOR_RED 200 + dvbColor[DVB_COLOR_RED] = rgb2yuv(255,0,0); dvbTrans[DVB_COLOR_RED] = 15; +#define DVB_COLOR_GREEN 201 + dvbColor[DVB_COLOR_GREEN] = rgb2yuv(0,255,0); dvbTrans[DVB_COLOR_GREEN] = 15; +#define DVB_COLOR_MAGENTA 202 + dvbColor[DVB_COLOR_MAGENTA] = rgb2yuv(255,128,255); dvbTrans[DVB_COLOR_MAGENTA] = 15; +#define DVB_COLOR_BAR 203 + dvbColor[DVB_COLOR_BAR] = rgb2yuv(255,128,0); dvbTrans[DVB_COLOR_BAR] = 8; +} + +void getOSDLine( xine_osd_t *osd, int w, QCString &dest, QCString &source ) +{ + int prevPos, pos, tw, th; + bool wrap=false; + + pos = source.find(" "); + if ( pos==-1 ) { + dest = source; + source = ""; + return; + } + prevPos = pos; + dest = source.left( pos ); + while ( !wrap ) { + xine_osd_get_text_size( osd, dest, &tw, &th ); + if ( tw>w ) { + wrap = true; + break; + } + if ( pos==-1 ) + break; + prevPos = pos; + pos = source.find(" ",pos+1); + dest = source.left( pos ); + } + if ( wrap ) { + dest = source.left( prevPos ); + source = source.right( source.length()-dest.length()-1 ); + } + else { + dest = source; + source = ""; + } +} + +void KXineWidget::dvbShowOSD() +{ + if ( m_trackURL!="DVB" ) + return; + + if ( xine_get_status(m_xineStream)!=XINE_STATUS_PLAY ) + return; + + if ( !dvbHaveVideo ) + m_trackHasVideo = false; + else + m_trackHasVideo = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO); + + if (m_trackHasVideo) { + m_trackVideoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); + m_videoFrameWidth = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_WIDTH); + m_videoFrameHeight = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_HEIGHT); + m_trackVideoBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_BITRATE); + } + else { + m_trackVideoCodec = QString::null; + m_videoFrameWidth = 0; + m_videoFrameHeight = 0; + m_trackVideoBitrate = 0; + } + + m_trackHasAudio = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_AUDIO); + if (m_trackHasAudio) { + m_trackAudioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); + m_trackAudioBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_AUDIO_BITRATE); + } + else { + m_trackAudioCodec = QString::null; + m_trackAudioBitrate = 0; + } + + if ( dvbOSD ) { + xine_osd_free( dvbOSD ); + dvbOSD = 0; + } + + int border=40; + int w = m_videoFrameWidth; + int h = m_videoFrameHeight; + if ( !w || !h ) + return; + if ( w<800 ) { + if ( dvbCurrentNext[0]=="E" ) { + dvbOSDHideTimer.stop(); + dvbOSD = xine_osd_new( m_xineStream, border, border, w-(2*border), h-(2/border) ); + } + else + dvbOSD = xine_osd_new( m_xineStream, border, h-border-100, w-(2*border), 100 ); + } + if ( dvbOSD ) { + QCString ct, cs; + if ( !dvbColor[0] ) initDvbPalette(); + xine_osd_set_palette( dvbOSD, dvbColor, dvbTrans ); + xine_osd_set_font( dvbOSD, m_osdFont, 16 ); + xine_osd_set_encoding( dvbOSD, "utf-8" ); + if ( dvbCurrentNext[0]=="E" ) + xine_osd_draw_rect( dvbOSD, 0, 0, w-(2*border), h-(2*border), DVB_TEXT_WHITE+1, 1 ); + else + xine_osd_draw_rect( dvbOSD, 0, 0, w-(2*border), 100, DVB_TEXT_WHITE+1, 1 ); + QString t = QTime::currentTime().toString( "hh:mm" ); + int tw, th, len; + xine_osd_get_text_size(dvbOSD, t.utf8(), &tw, &th); + len = tw; + int offset = 5; + xine_osd_draw_text( dvbOSD, w-(2*border)-tw-offset, 5, t.utf8(), DVB_TEXT_BLUE ); + int i; + for ( i=0; i<(int)dvbCurrentNext.count(); i++ ) { + if ( dvbCurrentNext[i]=="R" ) { + xine_osd_draw_rect( dvbOSD, offset, 5, offset+16, 21, DVB_COLOR_RED, 1 ); + offset+=21; + } + else if ( dvbCurrentNext[i]=="T" ) { + xine_osd_draw_rect( dvbOSD, offset, 5, offset+16, 21, DVB_COLOR_GREEN, 1 ); + offset+=21; + } + } + if (m_dvbChannelName == "") + t = m_trackTitle; + else + t = m_dvbChannelName; + + i=0; + ct = t.utf8(); + while ( i<(int)t.length() ) { + xine_osd_get_text_size( dvbOSD, ct, &tw, &th ); + if ( tw<=(w-(2*border)-offset-5-len) ) break; + ct = ct.remove( ct.length()-1, ct.length() ); + i++; + } + xine_osd_draw_text( dvbOSD, offset, 5, ct, DVB_TEXT_BLUE ); + xine_osd_draw_line( dvbOSD, 5, 10+18, w-(2*border)-5, 10+18, DVB_COLOR_MAGENTA ); + + QString s, c; + int y=43; + int pos; + if ( dvbCurrentNext[0]=="E" ) { + if ( dvbCurrentNext.count()<2 ) { + xine_osd_show( dvbOSD, 0 ); + return; + } + if ( !dvbCurrentNext[1].isEmpty() ) { + s = dvbCurrentNext[1]; + pos = s.find("-"); + c = s.left( pos+1 ); + s = s.right( s.length()-pos-1 ); + t = s; + xine_osd_draw_text( dvbOSD, 10, y, c.utf8(), DVB_TEXT_GREEN ); + xine_osd_get_text_size( dvbOSD, c.utf8(), &offset, &th ); + i=0; + cs = s.utf8(); + while ( i<(int)t.length() ) { + ct = cs.remove( cs.length()-i, cs.length() ); + xine_osd_get_text_size( dvbOSD, ct, &tw, &th ); + if ( tw<=(w-(2*border)-20-offset) ) break; + i++; + } + xine_osd_draw_text( dvbOSD, 10+offset, y, ct, DVB_TEXT_WHITE ); + y+= 40; + } + if ( !dvbCurrentNext[2].isEmpty() ) { + cs = dvbCurrentNext[2].utf8(); + while ( y<(h-(2*border)-23) ) { + getOSDLine( dvbOSD, (w-(2*border)-20), ct, cs ); + xine_osd_draw_text( dvbOSD, 10, y, ct, DVB_TEXT_BLUE ); + y+= 28; + if ( !cs.length() ) + break; + } + y+= 40; + } + if ( !dvbCurrentNext[3].isEmpty() ) { + cs = dvbCurrentNext[3].utf8(); + while ( y<(h-(2*border)-23) ) { + getOSDLine( dvbOSD, (w-(2*border)-20), ct, cs ); + xine_osd_draw_text( dvbOSD, 10, y, ct, DVB_TEXT_WHITE ); + y+= 28; + if ( !cs.length() ) + break; + } + } + xine_osd_show( dvbOSD, 0 ); + return; + } + int bar=-1; + int barOffset=0; + for ( int j=0; j<(int)dvbCurrentNext.count(); j++ ) { + if ( (dvbCurrentNext[ j ]=="T") || (dvbCurrentNext[ j ]=="R") ) continue; + if ( dvbCurrentNext[ j ].startsWith("BAR") ) { + s = dvbCurrentNext[ j ]; + s = s.remove("BAR"); + bar = s.toInt(); + //fprintf( stderr, "BAR : %d\n", bar ); + continue; + } + s = dvbCurrentNext[ j ]; + pos = s.find("-"); + c = s.left( pos+1 ); + s = s.right( s.length()-pos-1 ); + ct = cs = s.utf8(); + xine_osd_draw_text( dvbOSD, 10, y, c.utf8(), DVB_TEXT_GREEN ); + xine_osd_get_text_size( dvbOSD, c.utf8(), &offset, &th ); + i=0; + while ( i<(int)t.length() ) { + ct = cs.remove( cs.length()-i, cs.length() ); + xine_osd_get_text_size( dvbOSD, ct, &tw, &th ); + if ( tw<=(w-(2*border)-20-offset-35) ) break; + i++; + } + if ( !barOffset ) + barOffset = tw; + xine_osd_draw_text( dvbOSD, 10+offset, y, ct, DVB_TEXT_WHITE ); + y+= 28; + if ( bar>-1 ) { + int x1=10+offset+barOffset+10; + int x2=w-(2*border)-5; + if ( (x1+30)>x2 ) + x1 = x2 - 30; + int x3=x1+((bar*(x2-x1))/100); + //fprintf( stderr, "x1=%d - x2=%d - x3=%d\n", x1, x2, x3 ); + int y1=46; + int y2=60; + xine_osd_draw_line( dvbOSD, x1, y1, x2, y1, DVB_COLOR_BAR ); + xine_osd_draw_line( dvbOSD, x1, y2, x2, y2, DVB_COLOR_BAR ); + xine_osd_draw_line( dvbOSD, x1, y1, x1, y2, DVB_COLOR_BAR ); + xine_osd_draw_line( dvbOSD, x2, y1, x2, y2, DVB_COLOR_BAR ); + xine_osd_draw_rect( dvbOSD, x1, y1, x3, y2, DVB_COLOR_BAR, 1 ); + } + } + xine_osd_show( dvbOSD, 0 ); + dvbOSDHideTimer.start( 5000, true ); + } +} + +void KXineWidget::dvbHideOSD() +{ + if ( dvbOSD ) { + xine_osd_hide( dvbOSD, 0 ); + xine_osd_free( dvbOSD ); + dvbOSD = 0; + + if (m_dvbChannelName != "") + m_dvbChannelName = ""; + + emit signalDvbOSDHidden(); + } +} + +void KXineWidget::setDvbCurrentNext( const QString &channelName, const QStringList &list ) +{ + if ( list[0]=="STOP" ) { + dvbHideOSD(); + return; + } + dvbCurrentNext = list; + + m_dvbChannelName = channelName; + + QTimer::singleShot( 0, this, SLOT(dvbShowOSD()) ); +} + +void KXineWidget::setDvb( const QString &pipeName, const QString &chanName, int haveVideo ) +{ + m_trackURL = pipeName; + m_trackTitle = chanName; + dvbHaveVideo = haveVideo; +} + +bool KXineWidget::openDvb() +{ + if ( dvbOSD ) { + dvbOSDHideTimer.stop(); + xine_osd_hide( dvbOSD, 0 ); + xine_osd_free( dvbOSD ); + dvbOSD = 0; + } + + clearQueue(); + m_lengthInfoTimer.stop(); + m_posTimer.stop(); + xine_set_param( m_xineStream, XINE_PARAM_METRONOM_PREBUFFER, 180000); + if (!xine_open(m_xineStream, QFile::encodeName(m_trackURL))) { + sendXineError(); + return false; + } + else fprintf(stderr,"xine pipe opened %s\n", m_trackURL.ascii()); + m_trackURL = "DVB"; + emit signalXineStatus(i18n("DVB: opening...")); + QTimer::singleShot( 0, this, SLOT(playDvb()) ); + + return true; +} + +void KXineWidget::slotPlayTimeShift() +{ + m_lengthInfoTimer.stop(); + m_posTimer.stop(); + xine_set_param( m_xineStream, XINE_PARAM_METRONOM_PREBUFFER, 0); + if (!xine_open(m_xineStream, QFile::encodeName(TimeShiftFilename))) { + sendXineError(); +#ifdef XINE_PARAM_GAPLESS_SWITCH + if ( xine_check_version(1,1,1) ) + xine_set_param( m_xineStream, XINE_PARAM_GAPLESS_SWITCH, 0); +#endif + return; + } + if (!xine_play(m_xineStream, 0,0)) { + sendXineError(); + return; + } + m_trackIsSeekable = true; + m_lengthInfoTimer.start(1000); + m_posTimer.start(1000); +} + +bool KXineWidget::unhandledStreamsPresent() +{ + unsigned int hasAudio = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_AUDIO); + unsigned int hasVideo = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO); + + return (hasAudio && !xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_AUDIO_HANDLED)) || + (hasVideo && !xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_HANDLED)); +} + +void KXineWidget::slotPlay() +{ + if ((!isXineReady()) || (isQueueEmpty())) + return; + + if (m_logoFile != NULL && m_trackURL == m_logoFile && isPlaying()) + return; + + /* dvb */ + if ( dvbOSD ) { + dvbOSDHideTimer.stop(); + xine_osd_hide( dvbOSD, 0 ); + xine_osd_free( dvbOSD ); + dvbOSD = 0; + } + + m_lengthInfoTimer.stop(); + m_posTimer.stop(); + m_currentSpeed = Normal; + + setCursor(QCursor(Qt::WaitCursor)); + + m_trackURL = m_queue.first(); + m_queue.remove(m_queue.find(m_trackURL)); + + if (m_trackURL != m_logoFile) + emit signalXineStatus(i18n("Opening...")); + + /* check for external subtitle file or save url */ + m_trackSubtitleURL = QString::null; + m_trackSaveURL = QString::null; + + /*for (int i = 1; i <= m_trackURL.contains('#'); i++) { + ref = m_trackURL.section('#', i, i); + if (ref.section(':', 0, 0) == "subtitle") + m_trackSubtitleURL = ref.section(':', 1); + if (ref.section(':', 0, 0) == "save") + m_trackSaveURL = ref.section(':', 1); + }*/ + + QString turl; + int pos; + + if ( (pos=m_trackURL.find("#subtitle:"))>-1 ) { + turl = m_trackURL.left(pos); + m_trackSubtitleURL = m_trackURL.right( m_trackURL.length()-pos ); + if ( (pos=m_trackSubtitleURL.find("#save:"))>-1 ) { + m_trackSaveURL = m_trackSubtitleURL.right( m_trackSubtitleURL.length()-pos ); + m_trackSubtitleURL = m_trackSubtitleURL.left(pos); + } + } + else if ( (pos=m_trackURL.find("#save:"))>-1 ) { + turl = m_trackURL.left(pos); + m_trackSaveURL = m_trackURL.right( m_trackURL.length()-pos ); + } + else turl = m_trackURL; + + m_trackSubtitleURL.remove("#subtitle:"); + m_trackSaveURL.remove("#save:"); + + turl = turl.replace( "%", "%25" ).replace( "#", "%23" ).replace( ";", "%3b" ).replace( " ", "%20" ); + if ( !m_trackSubtitleURL.isEmpty() ) + turl = turl + "#subtitle:" + m_trackSubtitleURL.replace( "%", "%25" ).replace( "#", "%23" ).replace( ";", "%3b" ).replace( " ", "%20" ); + if ( !m_trackSaveURL.isEmpty() ) + turl = turl + "#save:" + m_trackSaveURL.replace( "%", "%25" ).replace( "#", "%23" ).replace( ";", "%3b" ).replace( " ", "%20" ); + if ( turl.startsWith("/") ) + turl.prepend("file://"); + + debugOut(QString("Playing: %1").arg(turl.local8Bit())); + + xine_set_param( m_xineStream, XINE_PARAM_METRONOM_PREBUFFER, 12000 ); + if (!xine_open(m_xineStream, QFile::encodeName(turl))) { + + sendXineError(); + setCursor(QCursor(Qt::ArrowCursor)); +#ifdef XINE_PARAM_GAPLESS_SWITCH + if ( xine_check_version(1,1,1) ) + xine_set_param( m_xineStream, XINE_PARAM_GAPLESS_SWITCH, 0); +#endif + return; + } + + if (unhandledStreamsPresent()) + { + errorOut("No codecs to handle media"); + sendXineError(); + return; + } + + /**** use visualization ? ****/ +#ifndef USE_QT_ONLY + unwireAudioFilters(); + wireAudioFilters(); +#else + if ((xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_AUDIO)) && + (!xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO))) + { + if (m_visualPluginName && (!m_xinePost)) + { + debugOut(QString("Init visual plugin: %1").arg(m_visualPluginName)); + m_xinePost = xine_post_init(m_xineEngine, m_visualPluginName, 0, + &m_audioDriver, + &m_videoDriver); + + m_postAudioSource = xine_get_audio_source(m_xineStream); + m_postInput = (xine_post_in_t*)xine_post_input (m_xinePost, const_cast<char*>("audio in")); + xine_post_wire(m_postAudioSource, m_postInput); + } + } + else + { + if (m_xinePost) + { + debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName)); + m_postAudioSource = xine_get_audio_source(m_xineStream); + xine_post_wire_audio_port(m_postAudioSource, m_audioDriver); + xine_post_dispose(m_xineEngine, m_xinePost); + m_xinePost = NULL; + } + } +#endif + + /*** play ***/ + int savedPos = m_savedPos; + m_savedPos = 0; + if (!xine_play(m_xineStream, savedPos, 0)) + { + sendXineError(); + setCursor(QCursor(Qt::ArrowCursor)); + return; + } + + /* do the stream have chapters ? */ + m_trackHasChapters = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_CHAPTERS); + + /** information requirement **/ + + m_trackTitle = QString::null; + + bool currentUtf8Locale; + QTextCodec* testUtf8Local = QTextCodec::codecForLocale(); + if (!strcmp(testUtf8Local->name(),"UTF-8")) + currentUtf8Locale = true; + else + currentUtf8Locale = false; + + QTextCodec *CodecUtf8; + CodecUtf8 = QTextCodec::codecForName("UTF-8"); + + QString infotag; + infotag = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_TITLE)); + + if (currentUtf8Locale) + m_trackTitle = infotag; + else + m_trackTitle = QString::fromLocal8Bit(infotag.ascii()); + + if (CodecUtf8->heuristicContentMatch(infotag.ascii(), infotag.length()) >= 0) + m_trackTitle = QString::fromUtf8(infotag.ascii()); + + if ((!m_trackTitle.isNull()) && (!m_trackTitle.isEmpty())) /* no meta? */ + { + QString trackArtist=NULL; + QString trackAlbum=NULL; + QString trackComment=NULL; + trackArtist = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_ARTIST)); + trackAlbum = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_ALBUM)); + trackComment = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_COMMENT)); + if (currentUtf8Locale) + { + m_trackArtist = trackArtist; + m_trackAlbum = trackAlbum; + m_trackComment = trackComment; + } + else + { + m_trackArtist = QString::fromLocal8Bit(trackArtist.ascii()); + m_trackAlbum = QString::fromLocal8Bit(trackAlbum.ascii()); + m_trackComment = QString::fromLocal8Bit(trackComment.ascii()); + } + if (CodecUtf8->heuristicContentMatch(trackArtist.ascii(), trackArtist.length()) >= 0) + m_trackArtist = QString::fromUtf8(trackArtist.ascii()); + if (CodecUtf8->heuristicContentMatch(trackAlbum.ascii(), trackAlbum.length()) >= 0) + m_trackAlbum = QString::fromUtf8(trackAlbum.ascii()); + if (CodecUtf8->heuristicContentMatch(trackComment.ascii(), trackComment.length()) >= 0) + m_trackComment = QString::fromUtf8(trackComment.ascii()); + + m_trackYear = xine_get_meta_info(m_xineStream, XINE_META_INFO_YEAR); + m_trackNumber = xine_get_meta_info(m_xineStream, XINE_META_INFO_TRACK_NUMBER); + } + else + { + m_trackArtist = QString::null; + m_trackAlbum = QString::null; + m_trackNumber = QString::null; + m_trackYear = QString::null; + m_trackComment = QString::null; + } + + m_trackHasVideo = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO); + if (m_trackHasVideo) + { + m_trackVideoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); + m_videoFrameWidth = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_WIDTH); + m_videoFrameHeight = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_HEIGHT); + m_trackVideoBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_BITRATE); + } + else + { + m_trackVideoCodec = QString::null; + m_videoFrameWidth = 0; + m_videoFrameHeight = 0; + m_trackVideoBitrate = 0; + } + + m_trackHasAudio = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_AUDIO); + if (m_trackHasAudio) + { + m_trackAudioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); + m_trackAudioBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_AUDIO_BITRATE); + } + else + { + m_trackAudioCodec = QString::null; + m_trackAudioBitrate = 0; + } + + /*** we need a little delay for some meta info ***/ + QTimer::singleShot(1000, this, SLOT(slotGetInfoDelayed())); + + + m_trackLength = getLengthInfo(); + if ((m_trackLength.isNull()) && (m_trackURL != m_logoFile)) + { + debugOut("Wait for valid length information"); + m_lengthInfoTries = 0; + m_lengthInfoTimer.start(1000); /* wait for available track length info */ + } + + if (m_trackTitle.isNull() || m_trackTitle.isEmpty()) + { + /* no meta info */ + m_trackTitle = m_trackURL; + } + + slotSetAudioChannel(0); //refresh channel info + if (m_trackURL != m_logoFile) + m_posTimer.start(200); + setCursor(QCursor(Qt::ArrowCursor)); + + if (m_trackURL != m_logoFile) + { + emit signalXinePlaying(); + if (hasSaveURL()) + emit signalXineStatus(i18n("Recording")); + else + emit signalXineStatus(i18n("Playing")); + } +} + +int KXineWidget::getVideoWidth() +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_WIDTH); +} + +int KXineWidget::getVideoHeight() +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_HEIGHT); +} + +void KXineWidget::slotGetInfoDelayed() +{ + if (!m_xineStream) + return; + + if (m_trackHasVideo) + m_trackVideoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); + if (m_trackHasAudio) + m_trackAudioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); +} + + +/****** error processing ****/ + +void KXineWidget::sendXineError() +{ + QString error; + int errCode = xine_get_error(m_xineStream); + + QString addInfo; + QString audioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); + QString videoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); + if ((!audioCodec.isEmpty()) || (!videoCodec.isEmpty())) + { + if (!audioCodec.isEmpty()) + addInfo.append(QString("(") + i18n("Audio Codec") + ": " + audioCodec + ")"); + if (!videoCodec.isEmpty()) + addInfo.append(QString("(") + i18n("Video Codec") + ": " + videoCodec + ")"); + } + else + addInfo.append(QString("(") + m_trackURL + ")"); + + switch (errCode) + { + case XINE_ERROR_NO_INPUT_PLUGIN: + case XINE_ERROR_NO_DEMUX_PLUGIN: + { + if (m_trackURL.startsWith("dvd:/")) + { + if (KaffeinePart::installDistroCodec(this, "xine-engine", "dvdcss")) + return; + } + else + { + if (KaffeinePart::installDistroCodec(this, "xine-engine", "ffmpeg")) + return; + } + + error = i18n("No plugin found to handle this resource") + " " + addInfo; + break; + } + case XINE_ERROR_DEMUX_FAILED: + { + error = i18n("Resource seems to be broken") + " (" + m_trackURL + ")"; + break; + } + case XINE_ERROR_MALFORMED_MRL: + { + error = i18n("Requested resource does not exist") + " (" + m_trackURL + ")"; + break; + } + case XINE_ERROR_INPUT_FAILED: + { + error = i18n("Resource can not be opened") + " (" + m_trackURL + ")"; + break; + } + default: + { + if (unhandledStreamsPresent() && KaffeinePart::installDistroCodec(this, "xine-engine", "ffmpeg")) + return; + + error = i18n("Generic error") + " (" + m_trackURL + ")"; + break; + } + } + + if (isQueueEmpty()) + { + if (m_trackURL != m_logoFile) + { + emit signalXineStatus(i18n("Error")); + emit signalXineError(error); + } + else + errorOut("Can't find/play logo file!"); + } + else + { + errorOut(error); + errorOut(QString("Can't play: %1 - trying next").arg(m_trackURL)); + QTimer::singleShot(0, this, SLOT(slotPlay())); + } +} + +bool KXineWidget::isPlaying() const +{ + if (isXineReady()) + return ((xine_get_status(m_xineStream) == XINE_STATUS_PLAY) && (m_trackURL != m_logoFile)); + else + return false; +} + +QString KXineWidget::getXineLog() const +{ + QString logStr; + int i = 0; + QTextStream ts(&logStr, IO_WriteOnly); + + const char* const* log = xine_get_log(m_xineEngine, /* XINE_LOG_MSG*/ 0); + if (log == NULL) + return QString(); + + while(log[i]) + { + ts << QString::fromLocal8Bit(log[i]); + i++; + } + + return logStr; +} + +void KXineWidget::showOSDMessage(const QString& message, uint duration, int priority) +{ + if ((!m_osd) || (!m_osdShow) || (isHidden())) + return; + + static int prevOsdPriority = 0; + if (m_osdTimer.isActive() && prevOsdPriority > priority) + return; + prevOsdPriority = priority; + + //debugOut(QString("OSD: draw text: %1").arg(message)); + xine_osd_clear(m_osd); + xine_osd_draw_text(m_osd, 0, 0, message.local8Bit(), XINE_OSD_TEXT1); + + if (m_osdUnscaled) + xine_osd_show_unscaled(m_osd, 0); + else + xine_osd_show(m_osd, 0); + + m_osdTimer.start(duration); +} + +void KXineWidget::slotOSDHide() +{ + xine_osd_hide(m_osd, 0); + m_osdTimer.stop(); +} + +#ifndef USE_QT_ONLY +/****************** postprocessing filter management ****************/ + +QStringList KXineWidget::getVideoFilterNames() const +{ + QStringList filters; + const char* const* plugins = xine_list_post_plugins_typed(m_xineEngine, XINE_POST_TYPE_VIDEO_FILTER); + + for (int i = 0; plugins[i]; i++) + { + filters << plugins[i]; + } + return filters; +} + +QStringList KXineWidget::getAudioFilterNames() const +{ + QStringList filters; + const char* const* plugins = xine_list_post_plugins_typed(m_xineEngine, XINE_POST_TYPE_AUDIO_FILTER); + + for (int i = 0; plugins[i]; i++) + { + filters << plugins[i]; + } + return filters; +} +#endif + +void KXineWidget::slotCreateVideoFilter(const QString& name, QWidget* parent) +{ +#ifndef USE_QT_ONLY + unwireVideoFilters(); + + PostFilter* filter = new PostFilter(name, m_xineEngine, m_audioDriver, m_videoDriver, parent); + connect(filter, SIGNAL(signalDeleteMe(PostFilter*)), this, SLOT(slotDeleteVideoFilter(PostFilter*))); + m_videoFilterList.append(filter); + + wireVideoFilters(); +#else + parent = parent; + warningOut(QString("Not implemented [CreateVideoFilter %1]").arg(name)); +#endif +} + +void KXineWidget::slotCreateAudioFilter(const QString& name, QWidget* parent) +{ +#ifndef USE_QT_ONLY + unwireAudioFilters(); + + PostFilter* filter = new PostFilter(name, m_xineEngine, m_audioDriver, m_videoDriver, parent); + connect(filter, SIGNAL(signalDeleteMe(PostFilter*)), this, SLOT(slotDeleteAudioFilter(PostFilter*))); + m_audioFilterList.append(filter); + + wireAudioFilters(); +#else + parent = parent; + warningOut(QString("Not implemented [CreateAudioFilter %1]").arg(name)); +#endif +} + + +void KXineWidget::slotRemoveAllVideoFilters() +{ +#ifndef USE_QT_ONLY + unwireVideoFilters(); + while (m_videoFilterList.count()) + m_videoFilterList.removeLast(); + wireVideoFilters(); +#else + warningOut("Not implemented!"); +#endif +} + +void KXineWidget::slotRemoveAllAudioFilters() +{ +#ifndef USE_QT_ONLY + unwireAudioFilters(); + while (m_audioFilterList.count()) + m_audioFilterList.removeLast(); + wireAudioFilters(); +#else + warningOut("Not implemented!"); +#endif +} + +void KXineWidget::slotDeleteVideoFilter(PostFilter* filter) +{ +#ifndef USE_QT_ONLY + unwireVideoFilters(); + m_videoFilterList.remove(filter); + wireVideoFilters(); +#else + filter = filter; + warningOut("Not implemented!"); +#endif +} + +void KXineWidget::slotDeleteAudioFilter(PostFilter* filter) +{ +#ifndef USE_QT_ONLY + unwireAudioFilters(); + m_audioFilterList.remove(filter); + wireAudioFilters(); +#else + filter = filter; + warningOut("Not implemented!"); +#endif +} + +#ifndef USE_QT_ONLY +void KXineWidget::unwireVideoFilters() +{ + if (m_xineStream && m_videoDriver) + xine_post_wire_video_port(xine_get_video_source(m_xineStream), m_videoDriver); +} + +void KXineWidget::wireVideoFilters() +{ + if (!m_xineStream) + { + debugOut("wireVideoFilters() - xine stream not initialized, nothing happend."); + return; + } + + QPtrList<PostFilter> activeList; + + if (m_videoFilterList.count() && m_videoFiltersEnabled) + activeList = m_videoFilterList; + + if (m_deinterlaceFilter && m_deinterlaceEnabled ) + activeList.insert (0, m_deinterlaceFilter); + + if (activeList.count()) + { + xine_post_wire_video_port(activeList.at(activeList.count()-1)->getOutput(), m_videoDriver); + + for (uint i = activeList.count()-1; i >0; i--) + { + xine_post_wire(activeList.at( i-1 )->getOutput(), activeList.at(i)->getInput()); + } + + xine_post_wire( xine_get_video_source(m_xineStream), activeList.at(0)->getInput()); + } +} + +void KXineWidget::unwireAudioFilters() +{ + if (m_xineStream && m_audioDriver) + xine_post_wire_audio_port( xine_get_audio_source (m_xineStream), m_audioDriver); +} + +void KXineWidget::wireAudioFilters() +{ + if (!m_xineStream) + { + debugOut("wireAudioFilters() - xine stream not initialized, nothing happend."); + return; + } + + QPtrList<PostFilter> activeList; + + if (m_audioFilterList.count() && m_audioFiltersEnabled) + activeList = m_audioFilterList; + + if ((xine_get_stream_info (m_xineStream, XINE_STREAM_INFO_HAS_AUDIO)) && + (!xine_get_stream_info (m_xineStream, XINE_STREAM_INFO_HAS_VIDEO)) && + m_visualPluginName.ascii()) + { + if (!m_visualPlugin) + { + debugOut(QString("Init visual plugin: %1").arg(m_visualPluginName)); + m_visualPlugin = new PostFilter(m_visualPluginName, m_xineEngine, m_audioDriver, m_videoDriver, NULL); + } + + activeList.insert (0, m_visualPlugin); + } + else + { + if (m_visualPlugin) + { + debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName)); + delete m_visualPlugin; + m_visualPlugin = NULL; + } + } + + if (activeList.count()) + { + xine_post_wire_audio_port(activeList.at(activeList.count()-1)->getOutput(), m_audioDriver); + + for (uint i = activeList.count()-1; i >0; i--) + { + xine_post_wire(activeList.at(i-1)->getOutput(), activeList.at(i)->getInput()); + } + + xine_post_wire( xine_get_audio_source(m_xineStream), activeList.at(0)->getInput()); + } +} +#endif + +void KXineWidget::slotEnableVideoFilters(bool enable) +{ +#ifndef USE_QT_ONLY + m_videoFiltersEnabled = enable; + + unwireVideoFilters(); + wireVideoFilters(); +#else + enable = enable; + warningOut("Not implemented!"); +#endif +} + +void KXineWidget::slotEnableAudioFilters(bool enable) +{ +#ifndef USE_QT_ONLY + m_audioFiltersEnabled = enable; + + unwireAudioFilters(); + wireAudioFilters(); +#else + enable = enable; + warningOut("Not implemented!"); +#endif +} + +#ifndef USE_QT_ONLY +QStringList KXineWidget::getAudioFilterConfig() +{ + QStringList configStrings; + for (uint i=0; i<m_audioFilterList.count(); i++) + configStrings << m_audioFilterList.at(i)->getConfig(); + return configStrings; +} + +QStringList KXineWidget::getVideoFilterConfig() +{ + QStringList configStrings; + for (uint i=0; i<m_videoFilterList.count(); i++) + configStrings << m_videoFilterList.at(i)->getConfig(); + return configStrings; +} +#endif + +/**** visual plugin **********/ + +QStringList KXineWidget::getVisualPlugins() const +{ + QStringList visuals; + const char* const* plugins = xine_list_post_plugins_typed(m_xineEngine, XINE_POST_TYPE_AUDIO_VISUALIZATION); + + for (int i = 0; plugins[i]; i++) + { + visuals << plugins[i]; + } + return visuals; +} + +/**************** change visualization plugin *****************/ + +void KXineWidget::slotSetVisualPlugin(const QString& visual) +{ + if (m_visualPluginName == visual) return; + debugOut(QString("New visualization plugin: %1").arg(visual)); + +#ifndef USE_QT_ONLY + unwireAudioFilters(); + if(m_visualPlugin) + { + delete m_visualPlugin; + m_visualPlugin = NULL; + } + + if (visual == "none") + m_visualPluginName = QString::null; + else + m_visualPluginName = visual; + + wireAudioFilters(); +#else + if (visual == "none") + m_visualPluginName = QString::null; + else + m_visualPluginName = visual; + + if (m_xinePost) + { + xine_post_out_t *pp; + + pp = xine_get_audio_source(m_xineStream); + xine_post_wire_audio_port(pp, m_audioDriver); + xine_post_dispose(m_xineEngine, m_xinePost); + m_xinePost = NULL; + } + + if ( (xine_get_status(m_xineStream ) == XINE_STATUS_PLAY) + && (!xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO)) && (m_visualPluginName) ) + { + m_xinePost = xine_post_init(m_xineEngine, m_visualPluginName, 0, &m_audioDriver, &m_videoDriver); + m_postAudioSource = xine_get_audio_source(m_xineStream); + m_postInput = (xine_post_in_t*)xine_post_input(m_xinePost, const_cast<char*>("audio in")); + xine_post_wire(m_postAudioSource, m_postInput); + } +#endif +} + +/*****/ + +void KXineWidget::getAutoplayPlugins(QStringList& autoPlayList) const +{ + char** pluginIds = NULL; + int i = 0; + + pluginIds = (char**)xine_get_autoplay_input_plugin_ids(m_xineEngine); + + while(pluginIds[i]) + { + autoPlayList << pluginIds[i]; + + autoPlayList << xine_get_input_plugin_description(m_xineEngine, pluginIds[i]); + i++; + } +} + +bool KXineWidget::getAutoplayPluginURLS(const QString& plugin, QStringList& list) +{ + char** urls = NULL; + int num; + int i = 0; + + urls = xine_get_autoplay_mrls(m_xineEngine, plugin.ascii(), &num); + + if (urls) + { + while (urls[i]) + { + list << urls[i]; + i++; + } + return true; + } + else + { + return false; + } +} + +void KXineWidget::slotSetVolume(int vol) +{ + if (!isXineReady()) return; + if (m_softwareMixer) + { + //debugOut(QString("Set software amplification level: %1").arg(vol)); + if (m_volumeGain) + xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL, vol*2); + else + xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL, vol); + } + else + { + //debugOut(QString("Set audio mixer volume: %1").arg(vol)); + xine_set_param(m_xineStream, XINE_PARAM_AUDIO_VOLUME, vol); + } + emit signalXineStatus(i18n("Volume") + ": " + QString::number(vol) +"%"); +} + +uint KXineWidget::getVolume() const +{ + if (!isXineReady()) return 0; + uint vol; + if (m_softwareMixer) + { + vol = xine_get_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL); + if (vol > 200) + { + // when amp is high > 100, xine_get_param sometimes returns incorrect amp level + errorOut("Amp level returned weird results, set Amp to 100"); + vol = 100; + } + if (m_volumeGain) vol = vol/2; + } + else + { + vol = xine_get_param(m_xineStream, XINE_PARAM_AUDIO_VOLUME); + } + return vol; +} + +void KXineWidget::slotToggleMute() +{ + int muteParam; + if (m_softwareMixer) + muteParam = XINE_PARAM_AUDIO_AMP_MUTE; + else + muteParam = XINE_PARAM_AUDIO_MUTE; + + if (xine_get_param(m_xineStream, muteParam)) + { + xine_set_param(m_xineStream, muteParam, 0); /* mute off */ + emit signalXineStatus(i18n("Mute Off")); + } + else + { + xine_set_param(m_xineStream, muteParam, 1); /* mute on */ + emit signalXineStatus(i18n("Mute On")); + } +} + +bool KXineWidget::SoftwareMixing() const +{ + if (m_softwareMixer) + return true; + else + return false; +} + +void KXineWidget::mouseMoveEvent(QMouseEvent* mev) +{ + if (!m_xineReady) return; + + if (cursor().shape() == Qt::BlankCursor) + { + setCursor(QCursor(Qt::ArrowCursor)); + } + + x11_rectangle_t rect; + xine_event_t event; + xine_input_data_t input; + + rect.x = mev->x(); + rect.y = mev->y(); + rect.w = 0; + rect.h = 0; + + xine_port_send_gui_data (m_videoDriver, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&rect); + + event.type = XINE_EVENT_INPUT_MOUSE_MOVE; + event.data = &input; + event.data_length = sizeof(input); + input.button = 0; + input.x = rect.x; + input.y = rect.y; + xine_event_send(m_xineStream, &event); + mev->ignore(); +} + + +void KXineWidget::mousePressEvent(QMouseEvent* mev) +{ + if (!m_xineReady) return; + int cur = cursor().shape(); + + if (mev->button() == Qt::MidButton) + { + emit signalMiddleClick(); + mev->ignore(); + return; + } + + if (mev->button() == Qt::RightButton) + { + if ( (cur == Qt::ArrowCursor) || (cur == Qt::BlankCursor) ) + { + emit signalRightClick(mev->globalPos()); + mev->accept(); + return; + } + } + + if (mev->button() == Qt::LeftButton) + { + if ( (cur == Qt::ArrowCursor) || (cur == Qt::BlankCursor) ) + { + emit signalLeftClick(mev->globalPos()); + mev->ignore(); + return; + } + + x11_rectangle_t rect; + xine_event_t event; + xine_input_data_t input; + + rect.x = mev->x(); + rect.y = mev->y(); + rect.w = 0; + rect.h = 0; + + xine_port_send_gui_data(m_videoDriver, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&rect); + + event.type = XINE_EVENT_INPUT_MOUSE_BUTTON; + event.data = &input; + event.data_length = sizeof(input); + input.button = 1; + input.x = rect.x; + input.y = rect.y; + xine_event_send (m_xineStream, &event); + mev->accept(); /* don't send event to parent */ + } +} + +void KXineWidget::mouseDoubleClickEvent(QMouseEvent* mev) +{ + emit signalDoubleClick(); + mev->ignore(); +} + +void KXineWidget::wheelEvent(QWheelEvent* e) +{ + int oldVal = getPosition(); + if (oldVal == 0) // no valid position + return; + + float offset = log10( QABS(e->delta()) ) / 0.002; + int newVal = 0; + if (e->delta()>0) + newVal = oldVal - int(offset); + else + newVal = oldVal + int(offset); + if (newVal < 0) newVal = 0; + slotSeekToPosition(newVal); + e->accept(); +} + +void KXineWidget::playNextChapter() const +{ + xine_event_t xev; + + xev.type = XINE_EVENT_INPUT_NEXT; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::playPreviousChapter() const +{ + xine_event_t xev; + + xev.type = XINE_EVENT_INPUT_PREVIOUS; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotStop() +{ + m_posTimer.stop(); + if ( m_lengthInfoTimer.isActive() ) m_lengthInfoTimer.stop(); + //emit signalNewPosition(0, QTime()); + + xine_stop(m_xineStream); + + emit signalXineStatus(i18n("Stop")); +} + +void KXineWidget::slotSetAudiocdDevice(const QString& device) +{ + debugOut(QString("Set AudioCD device to %1").arg(device)); + + xine_cfg_entry_t config; + xine_config_lookup_entry(m_xineEngine, "input.cdda_device", &config); + if (m_cachedCDPath.isNull()) + m_cachedCDPath = config.str_value; + config.str_value = QFile::encodeName(device).data(); + xine_config_update_entry (m_xineEngine, &config); +} + +void KXineWidget::slotSetVcdDevice(const QString& device) +{ + debugOut(QString("Set VCD device to %1").arg(device)); + + xine_cfg_entry_t config; + xine_config_lookup_entry(m_xineEngine, "input.vcd_device", &config); + if (m_cachedVCDPath.isNull()) + m_cachedVCDPath = config.str_value; + config.str_value = QFile::encodeName(device).data(); + xine_config_update_entry (m_xineEngine, &config); +} + +void KXineWidget::slotSetDvdDevice(const QString& device) +{ + debugOut(QString("Set DVD device to %1").arg(device)); + + xine_cfg_entry_t config; + xine_config_lookup_entry(m_xineEngine, "input.dvd_device", &config); + if (m_cachedDVDPath.isNull()) + m_cachedDVDPath = config.str_value; + config.str_value = QFile::encodeName(device).data(); + xine_config_update_entry (m_xineEngine, &config); +} + +QString KXineWidget::audiocdDevice() const +{ + xine_cfg_entry_t config; + xine_config_lookup_entry(m_xineEngine, "input.cdda_device", &config); + + return QFile::decodeName(config.str_value); +} + +QString KXineWidget::vcdDevice() const +{ + xine_cfg_entry_t config; + xine_config_lookup_entry(m_xineEngine, "input.vcd_device", &config); + + return QFile::decodeName(config.str_value); +} + +QString KXineWidget::dvdDevice() const +{ + xine_cfg_entry_t config; + xine_config_lookup_entry(m_xineEngine, "input.dvd_device", &config); + + return QFile::decodeName(config.str_value); +} + +uint KXineWidget::currentDVDTitleNumber() const +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_TITLE_NUMBER); +} + +uint KXineWidget::getDVDTitleCount() const +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_TITLE_COUNT); +} + +uint KXineWidget::currentDVDChapterNumber() const +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER); +} + +uint KXineWidget::getDVDChapterCount() const +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_CHAPTER_COUNT); +} + +uint KXineWidget::currentDVDAngleNumber() const +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER); +} + +uint KXineWidget::getDVDAngleCount() const +{ + return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_ANGLE_COUNT); +} + +void KXineWidget::setStreamSaveDir(const QString& dir) +{ + xine_cfg_entry_t config; + + if (!xine_config_lookup_entry(m_xineEngine, "misc.save_dir", &config)) return; /* older xine-lib */ + + debugOut(QString("Set misc.save_dir to: %1").arg(dir)); + config.str_value = QFile::encodeName(dir).data(); + xine_config_update_entry (m_xineEngine, &config); +} + +const QString KXineWidget::getStreamSaveDir() +{ + xine_cfg_entry_t config; + + if (!xine_config_lookup_entry(m_xineEngine, "misc.save_dir", &config)) return QString::null; /* older xine-lib */ + + return QFile::decodeName(config.str_value); +} + +void KXineWidget::setBroadcasterPort(const uint port) +{ + debugOut(QString("Set broadcaster port to %1").arg(port)); + xine_set_param(m_xineStream, XINE_PARAM_BROADCASTER_PORT, port); +} + +void KXineWidget::slotSpeedPause() +{ + if (m_currentSpeed == Pause) + { + slotSpeedNormal(); + } + else if (m_trackURL != m_logoFile) // don't pause logo + { + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); + m_posTimer.stop(); + if (m_currentSpeed != Undefined) + emit signalXineStatus(i18n("Pause")); + m_currentSpeed = Pause; + } +} + +void KXineWidget::slotSpeedNormal() +{ + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); + m_posTimer.start(200); + m_currentSpeed = Normal; + emit signalXineStatus(i18n("Playing") + " "); +} + +void KXineWidget::slotSpeedFaster() +{ + switch (m_currentSpeed) + { + case Fast1: + { + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_FAST_4); + m_currentSpeed = Fast2; + emit signalXineStatus(i18n("Fast Forward %1").arg("x2")); + break; + } + case Fast2: + { + slotSpeedNormal(); + break; + } + case Slow1: + { + slotSpeedNormal(); + break; + } + case Slow2: + { + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_2); + m_currentSpeed = Slow1; + emit signalXineStatus(i18n("Slow Motion %1").arg("x1")); + break; + } + default: + { + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_FAST_2); + m_currentSpeed = Fast1; + emit signalXineStatus(i18n("Fast Forward %1").arg("x1")); + break; + } + } +} + +void KXineWidget::slotSpeedSlower() +{ + switch (m_currentSpeed) + { + case Slow1: + { + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_4); + m_currentSpeed = Slow2; + emit signalXineStatus(i18n("Slow Motion %1").arg("x2")); + break; + } + case Slow2: + { + slotSpeedNormal(); + break; + } + case Fast1: + { + slotSpeedNormal(); + break; + } + case Fast2: + { + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_FAST_2); + m_currentSpeed = Fast1; + emit signalXineStatus(i18n("Fast Forward %1").arg("x1")); + break; + } + default: + { + xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_2); + m_currentSpeed = Slow1; + emit signalXineStatus(i18n("Slow Motion %1").arg("x1")); + break; + } + } +} + +QString KXineWidget::getSupportedExtensions() const +{ + return xine_get_file_extensions(m_xineEngine); +} + +void KXineWidget::slotSetAudioChannel(int ch) +{ + debugOut(QString("Switch to audio channel %1").arg(ch-1)); + xine_set_param(m_xineStream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, ch-1); +} + + +void KXineWidget::slotSetSubtitleChannel(int ch) +{ + debugOut(QString("Switch to subtitle channel %1").arg(ch-1)); + xine_set_param(m_xineStream, XINE_PARAM_SPU_CHANNEL, ch-1); +} + + +void KXineWidget::slotSetFileSubtitles(QString url) +{ + int pos; + int time; + int length; + + m_queue.prepend(url); + + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + + if ( ret == 0 ) + { + debugOut("No valid stream position information"); + return; + } + + if (isPlaying()) + xine_stop(m_xineStream); + m_posTimer.stop(); + + slotPlay(); + slotSeekToPosition(pos); +} + + + +uint KXineWidget::getPosition() const +{ + if (!m_xineReady) return 0; + + int pos, time, length; + + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + + if ( ret == 0 ) + { + debugOut("No valid stream position information"); + return 0; + } + + return (uint)pos; +} + +QTime KXineWidget::getPlaytime() const +{ + if (!m_xineReady) return QTime(); + + int pos, time, length; + + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + + if ( ret == 0 ) + { + debugOut("No valid stream position information"); + return QTime(); + } + + return msToTime(time); +} + +void KXineWidget::slotSendPosition() +{ + if (!m_xineReady) return; + + int pos, time, length; + + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + + if ( ret == 0 ) + { + debugOut("No valid stream position information"); + return; + } + + emit signalNewPosition(pos, msToTime(time)); +} + +void KXineWidget::slotStartSeeking() +{ + debugOut("Seeking started"); + m_posTimer.stop(); +} + +void KXineWidget::run() +{ + if ( seekThreadPos ) + xine_play(m_xineStream, seekThreadPos, 0); + else if ( seekThreadTime ) + xine_play(m_xineStream, 0, seekThreadTime); + else + xine_play(m_xineStream, seekThreadPos, 0); + + if ( seekThreadPause ) { + m_currentSpeed = Undefined; + slotSpeedPause(); + } +} + +void KXineWidget::slotSeekToPositionBlocking(int pos) +{ + while ( running() ) + usleep( 1000 ); + if ( pos!=seekThreadPos ) + slotSeekToPosition(pos); +} + + +void KXineWidget::slotSeekToPosition(int pos) +{ + if ( running() ) { + return; + } + + if ((!isXineReady()) || (!isPlaying()) || (!isSeekable())) + return; + + seekThreadPause = false; + if (m_currentSpeed == Pause) + seekThreadPause = true; + seekThreadPos = pos; + seekThreadTime = 0; + start(); +} + +void KXineWidget::slotSeekToTime(const QTime& postime) +{ + if ( running() ) + return; + + if ((!isXineReady()) || (!isPlaying()) || (!isSeekable())) + return; + + seekThreadPause = false; + if (m_currentSpeed == Pause) + seekThreadPause = true; + seekThreadPos = 0; + seekThreadTime = QTime().msecsTo(postime); + start(); +} + +void KXineWidget::slotStopSeeking() +{ + debugOut("Seeking stopped"); + m_posTimer.start(200, false); +} + +void KXineWidget::slotEject() +{ + xine_eject(m_xineStream); +} + +void KXineWidget::slotEnableAutoresize(bool enable) +{ + m_autoresizeEnabled = enable; + if (!m_autoresizeEnabled) + { + m_videoFrameHeight = 0; + m_videoFrameWidth = 0; + } +} + +/*************************************** + * tvtime deinterlacer plugin * + ***************************************/ + +#ifndef USE_QT_ONLY +void KXineWidget::createDeinterlacePlugin(const QString& config, QWidget* parent) +{ + m_deinterlaceFilter = new PostFilter(config.section(':',0,0), m_xineEngine, m_audioDriver, m_videoDriver, parent); + if( !m_deinterlaceFilter->getInput() || !m_deinterlaceFilter->getOutput() ) + { + delete m_deinterlaceFilter; + m_deinterlaceFilter = NULL; + } + + slotSetDeinterlaceConfig(config); +} + +const QString KXineWidget::getDeinterlaceConfig() const +{ + if (m_deinterlaceFilter) + return m_deinterlaceFilter->getConfig(); + + return DEFAULT_TVTIME_CONFIG; +} +#endif + +void KXineWidget::slotSetDeinterlaceConfig(const QString& config) +{ +#ifndef USE_QT_ONLY + if (m_deinterlaceFilter) + m_deinterlaceFilter->setConfig(config); +#else + warningOut(QString ("Not implemented [SetDeinterlaceConfig %1]").arg(config)); +#endif +} + +void KXineWidget::slotToggleDeinterlace() +{ +#ifndef USE_QT_ONLY + QString s; + + if (m_deinterlaceFilter) + { + m_deinterlaceEnabled = !m_deinterlaceEnabled; + debugOut(QString("Deinterlace enabled: %1").arg(m_deinterlaceEnabled)); + if ( m_deinterlaceEnabled ) s = i18n("Deinterlace: on"); + else s = i18n("Deinterlace: off"); + showOSDMessage( s, 2000 ); + unwireVideoFilters(); + wireVideoFilters(); + } + else + { + /* fallback - this method is deprecated */ + if (xine_get_param(m_xineStream, XINE_PARAM_VO_DEINTERLACE)) + xine_set_param(m_xineStream, XINE_PARAM_VO_DEINTERLACE, false); + else + xine_set_param(m_xineStream, XINE_PARAM_VO_DEINTERLACE, true); + } +#else + warningOut("Not implemented!"); +#endif +} + +/**************************/ + +void KXineWidget::slotAspectRatioAuto() +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_AUTO); + emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("Auto")); +} + +void KXineWidget::slotAspectRatio4_3() +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_4_3); + emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("4:3")); +} + +void KXineWidget::slotAspectRatioAnamorphic() +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_ANAMORPHIC); + emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("16:9")); +} + +void KXineWidget::slotAspectRatioSquare() +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_SQUARE); + emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("1:1")); +} + +void KXineWidget::slotAspectRatioDVB() +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_DVB); + emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("2.11:1")); +} + +void KXineWidget::slotZoomOutX() +{ + if ((m_currentZoomX - 5) >= 100) + { + m_currentZoomX -= 5; + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoomX); + emit signalXineStatus(i18n("Zoom X") + ": " + QString::number(m_currentZoomX) + "%"); + } +} + +void KXineWidget::slotZoomInX() +{ + if ((m_currentZoomX + 5) <= XINE_VO_ZOOM_MAX) + { + m_currentZoomX += 5; + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoomX); + emit signalXineStatus(i18n("Zoom X") + ": " + QString::number(m_currentZoomX) + "%"); + } +} + +void KXineWidget::slotZoomOutY() +{ + if ((m_currentZoomY - 5) >= 100) + { + m_currentZoomY -= 5; + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoomY); + emit signalXineStatus(i18n("Zoom Y") + ": " + QString::number(m_currentZoomY) + "%"); + } +} + +void KXineWidget::slotZoomInY() +{ + if ((m_currentZoomY + 5) <= XINE_VO_ZOOM_MAX) + { + m_currentZoomY += 5; + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoomY); + emit signalXineStatus(i18n("Zoom Y") + ": " + QString::number(m_currentZoomY) + "%"); + } +} + +void KXineWidget::slotZoomOut() +{ + if ((m_currentZoom - 5) >= 100) + { + m_currentZoom -= 5; + m_currentZoomX = m_currentZoomY = m_currentZoom; + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoom); + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoom); + emit signalXineStatus(i18n("Zoom") + ": " + QString::number(m_currentZoom) + "%"); + } +} + +void KXineWidget::slotZoomIn() +{ + if ((m_currentZoom + 5) <= XINE_VO_ZOOM_MAX) + { + m_currentZoom += 5; + m_currentZoomX = m_currentZoomY = m_currentZoom; + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoom); + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoom); + emit signalXineStatus(i18n("Zoom") + ": " + QString::number(m_currentZoom) + "%"); + } +} + +void KXineWidget::slotZoomOff() +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, 100); + xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, 100); + m_currentZoom = 100; + m_currentZoomX = m_currentZoomY = m_currentZoom; + emit signalXineStatus(i18n("Zoom") + ": " + QString::number(m_currentZoom) + "%"); +} + +QTime KXineWidget::getLengthInfo() +{ + int pos, time, length; + + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + + if ( (ret != 0) && (length > 0) ) + { + return msToTime(length); + } + + return QTime(); +} + +void KXineWidget::slotEmitLengthInfo() +{ + QTime length = getLengthInfo(); + if (!(length.isNull())) + { + if ( m_trackURL!="DVB" ) m_lengthInfoTimer.stop(); + m_trackLength = length; + emit signalLengthChanged(); + } + else + { + if (m_lengthInfoTries > 10) // wait 10 seconds + m_lengthInfoTimer.stop(); + else + { + debugOut("Wait for valid length information"); + m_lengthInfoTries ++; + } + } +} + +void KXineWidget::globalPosChanged() +{ + QPoint g = mapToGlobal(QPoint(0,0)); + m_globalX = g.x(); + m_globalY = g.y(); +} + +const xine_t* const KXineWidget::getXineEngine()const +{ + return m_xineEngine; +} + +/************ video settings ****************/ + +void KXineWidget::getVideoSettings(int& hue, int& sat, int& contrast, int& bright, + int& avOffset, int& spuOffset) const +{ + hue = xine_get_param(m_xineStream, XINE_PARAM_VO_HUE); + sat = xine_get_param(m_xineStream, XINE_PARAM_VO_SATURATION); + contrast = xine_get_param(m_xineStream, XINE_PARAM_VO_CONTRAST); + bright = xine_get_param(m_xineStream, XINE_PARAM_VO_BRIGHTNESS); + avOffset = xine_get_param(m_xineStream, XINE_PARAM_AV_OFFSET); + spuOffset = xine_get_param(m_xineStream, XINE_PARAM_SPU_OFFSET); +} + +void KXineWidget::getspuOffset(int& spuOffset) const +{ + spuOffset = xine_get_param(m_xineStream, XINE_PARAM_SPU_OFFSET); +} + +void KXineWidget::slotSetHue(int hue) +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_HUE, hue); + emit signalXineStatus(i18n("Hue") + ": " + QString::number((hue*100)/65535) + "%"); +} + +void KXineWidget::slotSetSaturation(int sat) +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_SATURATION, sat); + emit signalXineStatus(i18n("Saturation") + ": " + QString::number((sat*100)/65535) + "%"); +} + +void KXineWidget::slotSetContrast(int contrast) +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_CONTRAST, contrast); + emit signalXineStatus(i18n("Contrast") + ": " + QString::number((contrast*100)/65535) + "%"); +} + +void KXineWidget::slotSetBrightness(int bright) +{ + xine_set_param(m_xineStream, XINE_PARAM_VO_BRIGHTNESS, bright); + emit signalXineStatus(i18n("Brightness") + ": " + QString::number((bright*100)/65535) + "%"); +} + +void KXineWidget::slotSetAVOffset(int av) +{ + xine_set_param(m_xineStream, XINE_PARAM_AV_OFFSET, av); + emit signalXineStatus(i18n("Audio/Video Offset") + ": " + QString::number(av/90) + i18n("msec")); +} + +void KXineWidget::slotSetSpuOffset(int spu) +{ + xine_set_param(m_xineStream, XINE_PARAM_SPU_OFFSET, spu); + emit signalXineStatus(i18n("Subtitle Offset") + ": " + QString::number(spu/90) + i18n("msec")); +} + + +/**************** equalizer *****************/ + +void KXineWidget::slotSetEq30(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_30HZ, -val); +} + +void KXineWidget::slotSetEq60(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_60HZ, -val); +} + +void KXineWidget::slotSetEq125(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_125HZ, -val); +} + +void KXineWidget::slotSetEq250(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_250HZ, -val); +} + +void KXineWidget::slotSetEq500(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_500HZ, -val); +} + +void KXineWidget::slotSetEq1k(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_1000HZ, -val); +} + +void KXineWidget::slotSetEq2k(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_2000HZ, -val); +} + +void KXineWidget::slotSetEq4k(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_4000HZ, -val); +} + +void KXineWidget::slotSetEq8k(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_8000HZ, -val); +} + +void KXineWidget::slotSetEq16k(int val) +{ + xine_set_param(m_xineStream, XINE_PARAM_EQ_16000HZ, -val); +} + +void KXineWidget::slotSetVolumeGain(bool gain) +{ + int amp = 0; + + if (gain) + { + if (m_softwareMixer) + amp = getVolume()*2; + else + amp = 200; + } + else + { + if (m_softwareMixer) + amp = getVolume(); + else + amp = 100; + } + xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL, amp); + + m_volumeGain = gain; +} + +/*************** dvd menus ******************/ + +void KXineWidget::slotMenuToggle() +{ + xine_event_t xev; + xev.type = XINE_EVENT_INPUT_MENU1; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotMenuTitle() +{ + xine_event_t xev; + xev.type = XINE_EVENT_INPUT_MENU2; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotMenuRoot() +{ + xine_event_t xev; + xev.type = XINE_EVENT_INPUT_MENU3; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotMenuSubpicture() +{ + xine_event_t xev; + xev.type = XINE_EVENT_INPUT_MENU4; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotMenuAudio() +{ + xine_event_t xev; + xev.type = XINE_EVENT_INPUT_MENU5; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotMenuAngle() +{ + xine_event_t xev; + xev.type = XINE_EVENT_INPUT_MENU6; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotMenuPart() +{ + xine_event_t xev; + xev.type = XINE_EVENT_INPUT_MENU7; + xev.data = NULL; + xev.data_length = 0; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotDVDMenuLeft() +{ + xine_event_t xev; + xev.data = NULL; + xev.data_length = 0; + xev.type = XINE_EVENT_INPUT_LEFT; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotDVDMenuRight() +{ + xine_event_t xev; + xev.data = NULL; + xev.data_length = 0; + xev.type = XINE_EVENT_INPUT_RIGHT; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotDVDMenuUp() +{ + xine_event_t xev; + xev.data = NULL; + xev.data_length = 0; + xev.type = XINE_EVENT_INPUT_UP; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotDVDMenuDown() +{ + xine_event_t xev; + xev.data = NULL; + xev.data_length = 0; + xev.type = XINE_EVENT_INPUT_DOWN; + + xine_event_send(m_xineStream, &xev); +} + +void KXineWidget::slotDVDMenuSelect() +{ + xine_event_t xev; + xev.data = NULL; + xev.data_length = 0; + xev.type = XINE_EVENT_INPUT_SELECT; + + xine_event_send(m_xineStream, &xev); +} + + +/******** mouse hideing at fullscreen ****/ + +void KXineWidget::startMouseHideTimer() +{ + m_mouseHideTimer.start(5000); +} + +void KXineWidget::stopMouseHideTimer() +{ + m_mouseHideTimer.stop(); +} + +void KXineWidget::slotHideMouse() +{ + if (cursor().shape() == Qt::ArrowCursor) + { + setCursor(QCursor(Qt::BlankCursor)); + } +} + + +/************************************************************ + * Take a Screenshot * + ************************************************************/ + +QImage KXineWidget::getScreenshot() const +{ + uchar *rgbPile = NULL; + int width, height; + double scaleFactor; + + getScreenshot(rgbPile, width, height, scaleFactor); + + if (!rgbPile) return QImage(); + + QImage screenShot(rgbPile, width, height, 32, 0, 0, QImage::IgnoreEndian); + if (scaleFactor >= 1.0) + width = (int)((double) width * scaleFactor + 0.5); + else + height = (int)((double) height / scaleFactor + 0.5); + + debugOut(QString("Screenshot: scale picture from %1x%2 to %3x%4").arg(screenShot.width()).arg(screenShot.height()).arg(width).arg(height)); + screenShot = screenShot.smoothScale(width, height); + + delete []rgbPile; + return screenShot; +} + +void KXineWidget::getScreenshot(uchar*& rgb32BitData, int& videoWidth, int& videoHeight, double& scaleFactor) const +{ + + uint8_t *yuv = NULL, *y = NULL, *u = NULL, *v =NULL; + + int width, height, ratio, format; + // double desired_ratio, image_ratio; + + if (!xine_get_current_frame(m_xineStream, &width, &height, &ratio, &format, NULL)) + return; + + yuv = new uint8_t[((width+8) * (height+1) * 2)]; + if (yuv == NULL) + { + errorOut("Not enough memory to make screenshot!"); + return; + } + + xine_get_current_frame(m_xineStream, &width, &height, &ratio, &format, yuv); + + videoWidth = width; + videoHeight = height; + + /* + * convert to yv12 if necessary + */ + + switch (format) + { + case XINE_IMGFMT_YUY2: + { + uint8_t *yuy2 = yuv; + + yuv = new uint8_t[(width * height * 2)]; + if (yuv == NULL) + { + errorOut("Not enough memory to make screenshot!"); + return; + } + y = yuv; + u = yuv + width * height; + v = yuv + width * height * 5 / 4; + + yuy2Toyv12 (y, u, v, yuy2, width, height); + + delete [] yuy2; + } + break; + case XINE_IMGFMT_YV12: + y = yuv; + u = yuv + width * height; + v = yuv + width * height * 5 / 4; + + break; + default: + { + warningOut(QString("Screenshot: Format %1 not supportet!").arg((char*)&format)); + delete [] yuv; + return; + } + } + + /* + * convert to rgb + */ + + rgb32BitData = yv12ToRgb (y, u, v, width, height); + + + // image_ratio = (double) height / (double) width; + + /* + switch (ratio) + { + case XINE_VO_ASPECT_ANAMORPHIC: + debugOut("Screenshot: got video aspect: anamorphic"); + desired_ratio = 16.0 /9.0; + break; + case XINE_VO_ASPECT_DVB: + debugOut("Screenshot: got video aspect: 2.11:1"); + desired_ratio = 2.11/1.0; + break; + case XINE_VO_ASPECT_4_3: + debugOut("Screenshot: got video aspect: 4:3"); + desired_ratio = 4.0 / 3.0; + break; + default: + warningOut(QString("Screenshot: Unknown aspect ratio: %1 - using 4:3").arg(ratio)); + case XINE_VO_ASPECT_SQUARE: + debugOut("Screenshot: got video aspect: 1:1"); + desired_ratio = image_ratio; + break; + } + */ + + debugOut(QString("Screenshot: using scale factor: %1").arg(m_videoAspect)); + scaleFactor = m_videoAspect; + + delete [] yuv; +} + + +/************************************************ + * HELPER FUNCTIONS * + ************************************************/ + +QTime KXineWidget::msToTime(int msec) +{ + QTime t; + t = t.addMSecs(msec); + return t; +} + +#ifdef USE_QT_ONLY +QString KXineWidget::i18n(const char *text) +{ + return QApplication::tr(text); +} +#endif + +void KXineWidget::debugOut (QString qsDebug) +{ +#ifdef USE_QT_ONLY + QString qsDebugging = QString ("Debug: ") + qsDebug +"\n"; + printf ((const char *)qsDebugging); +#else + kdDebug() << "KXineWidget: " << (const char *)qsDebug.ascii() << "\n"; +#endif +} +void KXineWidget::errorOut (QString qsError) +{ +#ifdef USE_QT_ONLY + QString qsErroring = QString ("Error: ") + qsError+ "\n"; + printf ((const char *)qsErroring); +#else + kdError() << "KXineWidget: " << (const char *)qsError.ascii() << "\n"; +#endif +} +void KXineWidget::warningOut (QString qsWarning) +{ +#ifdef USE_QT_ONLY + QString qsWarninging = QString ("Warning: ") + qsWarning + "\n"; + printf ((const char *)qsWarninging); +#else + kdWarning() << "KXineWidget: " << (const char *)qsWarning.ascii() << "\n"; +#endif +} + + +/************************************************************ + * Helpers to convert yuy and yv12 frames to rgb * + * code from gxine modified for 32bit output * + * Copyright (C) 2000-2003 the xine project * + ************************************************************/ + +void KXineWidget::yuy2Toyv12 (uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int width, int height) +{ + + int i, j, w2; + + w2 = width / 2; + + for (i = 0; i < height; i += 2) + { + for (j = 0; j < w2; j++) + { + /* + * packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] + */ + *(y++) = *(input++); + *(u++) = *(input++); + *(y++) = *(input++); + *(v++) = *(input++); + } + + /* + * down sampling + */ + + for (j = 0; j < w2; j++) + { + /* + * skip every second line for U and V + */ + *(y++) = *(input++); + input++; + *(y++) = *(input++); + input++; + } + } +} + + +uchar* KXineWidget::yv12ToRgb (uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, int width, int height) +{ + /* + * Create rgb data from yv12 + */ + +#define clip_8_bit(val) \ +{ \ + if (val < 0) \ + val = 0; \ + else \ + if (val > 255) val = 255; \ +} + + int i, j; + + int y, u, v; + int r, g, b; + + int sub_i_uv; + int sub_j_uv; + + int uv_width, uv_height; + + uchar *rgb; + + uv_width = width / 2; + uv_height = height / 2; + + rgb = new uchar[(width * height * 4)]; //qt needs a 32bit align + if (!rgb) + { + // kdError(555) << "Not enough memory!" << endl; + return NULL; + } + + for (i = 0; i < height; ++i) + { + /* + * calculate u & v rows + */ + sub_i_uv = ((i * uv_height) / height); + + for (j = 0; j < width; ++j) + { + /* + * calculate u & v columns + */ + sub_j_uv = ((j * uv_width) / width); + + /*************************************************** + * + * Colour conversion from + * http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30 + * + * Thanks to Billy Biggs <vektor@dumbterm.net> + * for the pointer and the following conversion. + * + * R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ]) + * G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ]) + * B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ]) + * + * Where in xine the above values are represented as + * + * Y' == image->y + * Cb == image->u + * Cr == image->v + * + ***************************************************/ + + y = src_y[(i * width) + j] - 16; + u = src_u[(sub_i_uv * uv_width) + sub_j_uv] - 128; + v = src_v[(sub_i_uv * uv_width) + sub_j_uv] - 128; + + r = (int)((1.1644 * (double)y) + (1.5960 * (double)v)); + g = (int)((1.1644 * (double)y) - (0.3918 * (double)u) - (0.8130 * (double)v)); + b = (int)((1.1644 * (double)y) + (2.0172 * (double)u)); + + clip_8_bit (r); + clip_8_bit (g); + clip_8_bit (b); + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + rgb[(i * width + j) * 4 + 0] = b; + rgb[(i * width + j) * 4 + 1] = g; + rgb[(i * width + j) * 4 + 2] = r; + rgb[(i * width + j) * 4 + 3] = 0; +#else + rgb[(i * width + j) * 4 + 3] = b; + rgb[(i * width + j) * 4 + 2] = g; + rgb[(i * width + j) * 4 + 1] = r; + rgb[(i * width + j) * 4 + 0] = 0; +#endif + + } + } + + return rgb; +} diff --git a/kaffeine/src/player-parts/xine-part/kxinewidget.h b/kaffeine/src/player-parts/xine-part/kxinewidget.h new file mode 100644 index 0000000..aac81b4 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/kxinewidget.h @@ -0,0 +1,518 @@ +/* + * kxinewidget.h - a kde / qt api for xine-lib + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2005-2006 Christophe Thommeret <hftom@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef KXINEWIDGET_H +#define KXINEWIDGET_H + +/* define this if you want to use QT only - disables post plugin support (except visualization)! */ +//#define USE_QT_ONLY 1 + +/* Usage: + * + * KXineWidget m_xine = new KXineWidget(parent); + * m_xine->appendToQueue("the_best_song_of_the_world.mp3"); + * + * You should at least connect to signalXineFatal(const QString&)! + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +/* forward declaration */ +class QWidget; +class QString; +class QStringList; +class QMouseEvent; +class QTimerEvent; +class QTime; + +#include <qtimer.h> +#include <qptrlist.h> +#include <qthread.h> +#include <X11/Xlib.h> +#include <xine.h> + + +#ifndef USE_QT_ONLY +#include "postfilter.h" +#else +typedef int PostFilter; /* dummy type */ +#endif + +#ifdef HAVE_XCB +#include <xcb/xcb.h> +#endif + +#define SUPPORTED_PROTOCOLS "file,http,mms,mmst,rtsp,rtp,tcp,pnm,cdda,vcd,vcdo,dvd,dvb,pvr,v4l,net,vdr,smb" + +#define DEFAULT_TVTIME_CONFIG "tvtime:method=LinearBlend,enabled=1,pulldown=none,framerate_mode=half_top,judder_correction=0,use_progressive_frame_flag=1,chroma_filter=0,cheap_mode=1" + +#define DEFAULT_OSD_FONT_SIZE 18 +#define DEFAULT_OSD_DURATION 5000 +#define OSD_MESSAGE_LOW_PRIORITY 1 +#define OSD_MESSAGE_NORMAL_PRIORITY 2 + +class KXineWidget : public QWidget, public QThread +{ + + Q_OBJECT + +public: + KXineWidget(QWidget *parent=0, const char *name=0, + const QString& pathToConfigFile = QString::null, const QString& pathToLogoFile = QString::null, + const QString& audioDriver = QString::null, const QString& videoDriver = QString::null, + bool startManual = false, bool verbose = false); + ~KXineWidget(); + + enum Speed { Normal = 0, Pause, Fast1, Fast2, Slow1, Slow2, Undefined }; + + bool initXine(); /* call this only if you set startManual = true in the constructor */ + bool isXineReady() const { return m_xineReady; } + bool isPlaying() const; + Speed getSpeed() const { return m_currentSpeed; } + QString getXineLog() const; + + void setQueue(const QStringList& urls) { m_queue = urls; } + void appendToQueue(const QString& url) { m_queue.append(url); } /* file to play; call slotPlay() to play queue... */ + void clearQueue() { m_queue.clear(); } + bool isQueueEmpty() const { return m_queue.empty(); } + + void showOSDMessage(const QString& message, uint duration = DEFAULT_OSD_DURATION /* ms */, int priority = OSD_MESSAGE_NORMAL_PRIORITY); + + /* stream info */ + const QString& getURL() const { return m_trackURL; } + const QString& getTitle() const { return m_trackTitle; } + const QString& getArtist() const { return m_trackArtist; } + const QString& getAlbum() const { return m_trackAlbum; } + const QString& getTrackNumber() const { return m_trackNumber; } //may return an empty string with xine-lib 1.0 + const QString& getGenre() const { return m_trackGenre; } + const QString& getYear() const { return m_trackYear; } + const QString& getComment() const { return m_trackComment; } + const QTime& getLength() const { return m_trackLength; } + const QString& getVideoCodec() const { return m_trackVideoCodec; } + QSize getVideoSize() const { return QSize(m_videoFrameWidth, m_videoFrameHeight); } + int getVideoWidth(); + int getVideoHeight(); + uint getVideoBitrate() const { return m_trackVideoBitrate; } + const QString& getAudioCodec() const { return m_trackAudioCodec; } + uint getAudioBitrate() const { return m_trackAudioBitrate; } + const QString& getSubtitleURL() const { return m_trackSubtitleURL; } + const QString& getSaveURL() const { return m_trackSaveURL; } + bool hasVideo() const { return m_trackHasVideo; } + bool hasAudio() const { return m_trackHasAudio; } + bool hasChapters() const { return m_trackHasChapters; } + bool hasSubtitleURL() const { return !m_trackSubtitleURL.isNull(); } + bool hasSaveURL() const { return !m_trackSaveURL.isNull(); } + bool isSeekable() const { return m_trackIsSeekable; } + + uint getVolume() const; /* percent */ + bool SoftwareMixing() const; + QTime getPlaytime() const; + uint getPosition() const; /* 0..65535 */ + + void savePosition(const int pos) { (pos > 0) ? m_savedPos = pos : m_savedPos = 0; } + + /* plugin handling */ + QStringList getAudioDrivers() const { return m_audioDriverList; } + QStringList getVideoDrivers() const { return m_videoDriverList; } + void getAutoplayPlugins(QStringList& pluginNames) const; + bool getAutoplayPluginURLS(const QString& name, QStringList& urls); + QStringList getVisualPlugins() const; + +#ifndef USE_QT_ONLY + void createDeinterlacePlugin(const QString& configString, QWidget* parent); + const QString getDeinterlaceConfig() const; + QStringList getVideoFilterNames() const; + QStringList getAudioFilterNames() const; + QStringList getAudioFilterConfig(); + QStringList getVideoFilterConfig(); +#endif + + QString getSupportedExtensions() const; /* get mime types xine can play */ + + void playNextChapter() const; + void playPreviousChapter() const; + + /* config stuff */ + void setStreamSaveDir(const QString& dir); + const QString getStreamSaveDir(); + void setBroadcasterPort(const uint port); + void getVideoSettings(int& hue, int& sat, int& contrast, int& bright, + int& avOffset, int& spuOffset) const; + void getspuOffset(int& spuOffset) const; + /* disc drives devices*/ + QString audiocdDevice() const; + QString vcdDevice() const; + QString dvdDevice() const; + + /* dvd stuff */ + uint currentDVDTitleNumber() const; + uint getDVDTitleCount() const; + uint currentDVDChapterNumber() const; + uint getDVDChapterCount() const; + uint currentDVDAngleNumber() const; + uint getDVDAngleCount() const; + + /* take a screenshot */ + QImage getScreenshot() const; + void getScreenshot(uchar*& rgb32BitData, int& videoWidth, int& videoHeight, double& scaleFactor) const; + + /* get the xine engine, needed by the xine configuration dialog */ + const xine_t* const getXineEngine() const; + void saveXineConfig(); + + /* for fullscreen */ + void startMouseHideTimer(); + void stopMouseHideTimer(); + + void globalPosChanged(); /* call this if global pos of parent was changed */ + +signals: + void signalXineFatal(const QString& message); /** initXine() not successfull! **/ + void signalXineError(const QString& message); + void signalXineMessage(const QString& message); + void signalXineStatus(const QString& status); + void signalXineReady(); + void signalXinePlaying(); + + void signalHasChapters(bool); + void signalPlaybackFinished(); + void signalNewChannels(const QStringList& audio, const QStringList& sub, int currentAudio, int currentSub); + void signalNewPosition(int, const QTime&); + + void signalTitleChanged(); + void signalVideoSizeChanged(); + void signalLengthChanged(); + + void signalRightClick(const QPoint&); /* right-click on video window */ + void signalLeftClick(const QPoint&); + void signalMiddleClick(); + void signalDoubleClick(); + void signalSyncVolume(); + + void signalDvbOSDHidden(); + +public slots: + virtual void polish(); + + void slotPlay(); + void slotStop(); + + void slotSetVolume(int); + void slotToggleMute(); + void slotSpeedPause(); + void slotSpeedNormal(); + void slotSpeedFaster(); + void slotSpeedSlower(); + void slotSetVisualPlugin(const QString& name); + void slotSetAudioChannel(int); + void slotSetSubtitleChannel(int); + void slotSetFileSubtitles(QString); + void slotStartSeeking(); + void slotSeekToPosition(int pos); + void slotSeekToPositionBlocking(int pos); + void slotSeekToTime(const QTime&); + void slotStopSeeking(); + void slotEnableVideoFilters(bool enable); + void slotEnableAudioFilters(bool enable); + void slotEject(); + void slotEnableAutoresize(bool enable); + void slotAspectRatioAuto(); + void slotAspectRatio4_3(); + void slotAspectRatioAnamorphic(); /* 16:9 */ + void slotAspectRatioSquare(); /* 1:1 */ + void slotAspectRatioDVB(); /* 2.11:1 */ + void slotZoomIn(); + void slotZoomOut(); + void slotZoomOff(); + void slotZoomOutX(); + void slotZoomInX(); + void slotZoomOutY(); + void slotZoomInY(); + void slotToggleDeinterlace(); + void slotSetDeinterlaceConfig(const QString&); + void slotGetInfoDelayed(); + + /* disc drive devices */ + void slotSetAudiocdDevice(const QString&); + void slotSetVcdDevice(const QString&); + void slotSetDvdDevice(const QString&); + + void slotSetHue(int hue); + void slotSetSaturation(int sat); + void slotSetContrast(int contrast); + void slotSetBrightness(int bright); + void slotSetAVOffset(int av); + void slotSetSpuOffset(int spu); + + void slotSetEq30(int val); + void slotSetEq60(int val); + void slotSetEq125(int val); + void slotSetEq250(int val); + void slotSetEq500(int val); + void slotSetEq1k(int val); + void slotSetEq2k(int val); + void slotSetEq4k(int val); + void slotSetEq8k(int val); + void slotSetEq16k(int val); + void slotSetVolumeGain(bool gain); + + /* DVD Menus */ + void slotMenuToggle(); + void slotMenuTitle(); + void slotMenuRoot(); + void slotMenuSubpicture(); + void slotMenuAudio(); + void slotMenuAngle(); + void slotMenuPart(); + void slotDVDMenuLeft(); + void slotDVDMenuRight(); + void slotDVDMenuUp(); + void slotDVDMenuDown(); + void slotDVDMenuSelect(); + + /***** postprocess filters ****/ + void slotCreateVideoFilter(const QString& name, QWidget* parent); + void slotCreateAudioFilter(const QString& name, QWidget* parent); + void slotDeleteVideoFilter(PostFilter* filter); + void slotDeleteAudioFilter(PostFilter* filter); + void slotRemoveAllVideoFilters(); + void slotRemoveAllAudioFilters(); + +protected slots: + void slotSendPosition(); + void slotEmitLengthInfo(); + void slotHideMouse(); + void slotOSDHide(); + void slotNoRecentMessage(); + +protected: + void mouseMoveEvent(QMouseEvent*); + void mousePressEvent(QMouseEvent*); + void timerEvent(QTimerEvent*); + void mouseDoubleClickEvent(QMouseEvent*); + void wheelEvent(QWheelEvent*); + void initOSD(); + virtual void run(); + +private: +#ifndef HAVE_XCB + bool x11Event(XEvent *); +#else + void paintEvent(QPaintEvent *); +#endif + bool unhandledStreamsPresent(); + + /********* callbacks and threads ************/ + static void destSizeCallback(void* p, int video_width, int video_height, double video_aspect, + int* dest_width, int* dest_height, double* dest_aspect); + + static void frameOutputCallback(void* p, int video_width, int video_height, double video_aspect, + int* dest_x, int* dest_y, int* dest_width, int* dest_height, + double* dest_aspect, int* win_x, int* win_y); + + static void videoDriverChangedCallback(void* p, xine_cfg_entry_t* entry); + static void audioDriverChangedCallback(void* p, xine_cfg_entry_t* entry); + static void audioMixerMethodChangedCallback(void* p, xine_cfg_entry_t* entry); + static void showOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry); + static void sizeForOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry); + static void fontForOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry); + static void monitorXResChangedCallback(void* p, xine_cfg_entry_t* entry); + static void monitorYResChangedCallback(void* p, xine_cfg_entry_t* entry); + + static void xineEventListener(void* p, const xine_event_t*); + +protected: + void sendXineError(); + QTime getLengthInfo(); +#ifndef USE_QT_ONLY + void wireVideoFilters(); + void unwireVideoFilters(); + void wireAudioFilters(); + void unwireAudioFilters(); +#endif + + /* HELPER FUNCTIONS */ +public: +#ifdef USE_QT_ONLY + static QString i18n(const char *text); +#endif + static void debugOut(QString); + static void errorOut(QString); + static void warningOut(QString); + static QTime msToTime(int msec); + static void yuy2Toyv12(uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int width, int height); + static uchar *yv12ToRgb(uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, int width, int height); + +protected: + QStringList m_queue; + + int seekThreadPos; + int seekThreadTime; + bool seekThreadPause; + bool m_startXineManual; + bool m_xineReady; + QString m_logoFile; + QString m_preferedAudio; + QString m_preferedVideo; + bool m_xineVerbose; + QString m_configFilePath; + QStringList m_audioDriverList; + QStringList m_videoDriverList; + + /*x11*/ +#ifndef HAVE_XCB + Display *connection; +#else + xcb_connection_t *connection; +#endif + + /*xine*/ +#ifndef HAVE_XCB + x11_visual_t m_x11Visual; +#else + xcb_visual_t m_x11Visual; +#endif + xine_t* m_xineEngine; + xine_audio_port_t* m_audioDriver; + xine_video_port_t* m_videoDriver; + xine_stream_t* m_xineStream; + xine_event_queue_t* m_eventQueue; + xine_osd_t* m_osd; + bool m_osdUnscaled; + bool m_osdShow; + int m_osdSize; + char* m_osdFont; + char** m_audioChoices; + char* m_audioInfo; + char** m_videoChoices; + char* m_videoInfo; + char* m_mixerInfo; + char* m_osdShowInfo; + char** m_osdSizeOptions; + char* m_osdSizeInfo; + char* m_osdFontInfo; + double monitorXRes, monitorYRes; + char *xResInfo; + char *yResInfo; + +#ifndef USE_QT_ONLY + /*postfilter*/ + QPtrList<PostFilter> m_videoFilterList; + bool m_videoFiltersEnabled; + QPtrList<PostFilter> m_audioFilterList; + bool m_audioFiltersEnabled; + PostFilter* m_deinterlaceFilter; + bool m_deinterlaceEnabled; + PostFilter* m_visualPlugin; +#else + xine_post_t* m_xinePost; + xine_post_out_t* m_postAudioSource; + xine_post_in_t* m_postInput; +#endif + + QString m_audioDriverName; + QString m_videoDriverName; + QString m_visualPluginName; + + int m_videoFrameWidth; + int m_videoFrameHeight; + double m_videoAspect; + int m_globalX; + int m_globalY; + + Speed m_currentSpeed; + QString m_xineMessage; + QString m_xineError; + QString m_statusString; + bool m_softwareMixer; + bool m_volumeGain; + double m_displayRatio; + QString m_cachedCDPath, m_cachedVCDPath, m_cachedDVDPath; + QSize m_newParentSize; + int m_currentZoom; + int m_currentZoomX, m_currentZoomY; + QStringList m_audioCh; + QStringList m_subCh; + int m_currentAudio, m_currentSub; + int m_savedPos; + bool m_autoresizeEnabled; + bool m_DVDButtonEntered; + QString m_newMRLReference; + + QTimer m_posTimer; + QTimer m_lengthInfoTimer; + uint m_lengthInfoTries; + QTimer m_mouseHideTimer; + QTimer m_osdTimer; + QTimer m_recentMessagesTimer; + + QString m_trackURL; + QString m_trackTitle; + QString m_trackArtist; + QString m_trackAlbum; + QString m_trackNumber; + QString m_trackGenre; + QString m_trackYear; + QString m_trackComment; + QTime m_trackLength; + QString m_trackVideoCodec; + uint m_trackVideoBitrate; + QString m_trackAudioCodec; + uint m_trackAudioBitrate; + QString m_trackSubtitleURL; + QString m_trackSaveURL; + bool m_trackHasVideo; + bool m_trackHasAudio; + bool m_trackHasChapters; + bool m_trackIsSeekable; + + /* dvb */ +signals: + void stopDvb(); + +public: + QString TimeShiftFilename; + +public slots: + void setDvb( const QString &pipeName, const QString &chanName, int haveVideo ); + void setDvbCurrentNext( const QString &channelName, const QStringList &list ); + bool openDvb(); + bool playDvb(); + void slotPlayTimeShift(); + void dvbShowOSD(); + void dvbHideOSD(); + +private: + QString m_dvbChannelName; + bool m_dvbSaveTitle; + int dvbHaveVideo; + QTimer dvbOSDHideTimer; + xine_osd_t *dvbOSD; + QStringList dvbCurrentNext; + unsigned int dvbColor[256]; + unsigned char dvbTrans[256]; + void initDvbPalette(); +}; + +#endif /* KXINEWIDGET_H */ diff --git a/kaffeine/src/player-parts/xine-part/positionslider.cpp b/kaffeine/src/player-parts/xine-part/positionslider.cpp new file mode 100644 index 0000000..9091dbe --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/positionslider.cpp @@ -0,0 +1,113 @@ +/* + * positionslider.cpp + * + * Copyright (C) 2004-2005 Giorgos Gousios + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2004-2005 Miguel Freitas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <math.h> + +#include "positionslider.h" +#include "positionslider.moc" + + +PositionSlider::PositionSlider(Qt::Orientation o, QWidget *parent, const char* name) : + QSlider(o, parent, name), m_userChange(false) +{ + connect(this, SIGNAL(sliderPressed()), this ,SLOT(slotSliderPressed())); + connect(this, SIGNAL(sliderReleased()), this, SLOT(slotSliderReleased())); + installEventFilter(this); +} + + + +PositionSlider::~PositionSlider() +{ +} + + + +void PositionSlider::setPosition(int val, bool changePosition) +{ + if(!m_userChange) + setValue(val); + if(changePosition) { + setValue(val); + emit sliderMoved(val); + } +} + + + +void PositionSlider::slotSliderPressed() +{ + m_userChange = true; + emit signalStartSeeking(); +} + + + +void PositionSlider::slotSliderReleased() +{ + emit sliderLastMove(this->value()); + emit signalStopSeeking(); + m_userChange = false; +} + + + +void PositionSlider::wheelEvent(QWheelEvent* e) +{ + float offset = log10( QABS(e->delta()) ) / 0.002; + int newVal = 0; + if (e->delta()>0) + newVal = value() - int(offset); + else + newVal = value() + int(offset); + if (newVal < 0) newVal = 0; + //setPosition(newVal, true); + emit sliderLastMove( newVal ); + e->accept(); +} + + + +bool PositionSlider::eventFilter(QObject *obj, QEvent *ev) +{ + if( obj == this && (ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick) ) { + QMouseEvent *e = (QMouseEvent *)ev; + QRect r = sliderRect(); + + if( r.contains( e->pos() ) || e->button() != LeftButton ) + return false; + + int range = maxValue() - minValue(); + int pos = (orientation() == Horizontal) ? e->pos().x() : e->pos().y(); + int maxpos = (orientation() == Horizontal) ? width() : height(); + int value = pos * range / maxpos + minValue(); + + if (QApplication::reverseLayout()) + value = maxValue() - (value - minValue()); + + setPosition(value, true); + return true; + } + else { + return false; + } +} diff --git a/kaffeine/src/player-parts/xine-part/positionslider.h b/kaffeine/src/player-parts/xine-part/positionslider.h new file mode 100644 index 0000000..34c78e6 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/positionslider.h @@ -0,0 +1,55 @@ +/* + * positionslider.h + * + * Copyright (C) 2004-2005 Giorgos Gousios + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2004-2005 Miguel Freitas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef POSITIONSLIDER_H +#define POSITIONSLIDER_H + +#include <qslider.h> + +class PositionSlider : public QSlider +{ + Q_OBJECT + + public: + PositionSlider(Qt::Orientation, QWidget * parent = 0, const char* name = 0); + virtual ~PositionSlider(); + + void setPosition(int, bool); + +signals: + void signalStartSeeking(); + void signalStopSeeking(); + void sliderLastMove(int); + + protected: + virtual void wheelEvent(QWheelEvent* e); + bool eventFilter(QObject *obj, QEvent *ev); + + private slots: + void slotSliderPressed(); + void slotSliderReleased(); + + private: + bool m_userChange; +}; + +#endif /* POSITIONSLIDER_H */ diff --git a/kaffeine/src/player-parts/xine-part/postfilter.cpp b/kaffeine/src/player-parts/xine-part/postfilter.cpp new file mode 100644 index 0000000..32c898c --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/postfilter.cpp @@ -0,0 +1,398 @@ +/* + * postfilter.cpp - wrapper for xine's postprocessing filters + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2003-2005 Miguel Freitas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kpushbutton.h> +#include <kseparator.h> +#include <klocale.h> +#include <kdebug.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qwidget.h> +#include <qobject.h> +#include <qstring.h> +#include <qgroupbox.h> +#include <qtextedit.h> + +#include <stdio.h> + +#include "postfilter.h" + +PostFilter::PostFilter(const QString& name, xine_t* engine, xine_audio_port_t* audioDriver, + xine_video_port_t* videoDriver, QWidget *parent) : QObject(parent), m_data(NULL), m_groupBox(NULL) +{ + m_filterName = name; + kdDebug() << "PostFilter: Create Postprocessing Filter: " << m_filterName << endl; + + m_xineEngine = engine; + + m_xinePost = xine_post_init(m_xineEngine, m_filterName.ascii(), 0, &audioDriver, &videoDriver ); + if(m_xinePost) + { + xine_post_in_t* inputAPI = NULL; + + m_groupBox = new QGroupBox(name, parent); + m_groupBox->setSizePolicy(QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Fixed)); + QGridLayout* grid = new QGridLayout(m_groupBox, 2, 2); + grid->setMargin( 20 ); + grid->setSpacing( 5 ); + int row = 0; + + if ((inputAPI = (xine_post_in_t*)xine_post_input(m_xinePost, const_cast<char*>("parameters")))) + { + m_xinePostAPI = (xine_post_api_t*)inputAPI->data; + m_xinePostDescr = m_xinePostAPI->get_param_descr(); + m_xinePostParameter = m_xinePostDescr->parameter; + + m_data = new char[m_xinePostDescr->struct_size]; + m_xinePostAPI->get_parameters(m_xinePost, m_data); + + QLabel* descr; + + while (m_xinePostParameter->type != POST_PARAM_TYPE_LAST) + { + kdDebug() << "PostFilter: Parameter: " << m_xinePostParameter->name << endl; + if (m_xinePostParameter->readonly) continue; + + switch (m_xinePostParameter->type) + { + case POST_PARAM_TYPE_INT: + { + if (m_xinePostParameter->enum_values) + { + PostFilterParameterCombo* parameter = new + PostFilterParameterCombo(m_xinePostParameter->name, m_xinePostParameter->offset, + *(int*)(m_data+m_xinePostParameter->offset), + m_xinePostParameter->enum_values, m_groupBox ); + connect(parameter, SIGNAL(signalIntValue(int, int)), this, SLOT(slotApplyIntValue(int, int))); + m_parameterList.append(parameter); + + grid->addWidget(parameter->getWidget(), row, 0); + } + else + { + PostFilterParameterInt* parameter = new + PostFilterParameterInt(m_xinePostParameter->name, m_xinePostParameter->offset, + *(int*)(m_data+m_xinePostParameter->offset), + (int)m_xinePostParameter->range_min, (int)m_xinePostParameter->range_max, + m_groupBox); + + connect(parameter, SIGNAL( signalIntValue(int, int)), this, SLOT( slotApplyIntValue(int, int))); + m_parameterList.append(parameter); + + grid->addWidget(parameter->getWidget(), row, 0); + } + break; + } + case POST_PARAM_TYPE_DOUBLE: + { + PostFilterParameterDouble* parameter = new + PostFilterParameterDouble(m_xinePostParameter->name, m_xinePostParameter->offset, + *(double*)(m_data+m_xinePostParameter->offset), + (double)m_xinePostParameter->range_min, (double)m_xinePostParameter->range_max, + m_groupBox); + + connect(parameter, SIGNAL(signalDoubleValue(int, double)), this, SLOT(slotApplyDoubleValue(int, double))); + m_parameterList.append(parameter); + + grid->addWidget(parameter->getWidget(), row, 0); + break; + } + case POST_PARAM_TYPE_CHAR: + { + PostFilterParameterChar* parameter = new + PostFilterParameterChar(m_xinePostParameter->name, m_xinePostParameter->offset, + (char*)(m_data+m_xinePostParameter->offset), m_xinePostParameter->size, + m_groupBox); + + connect( parameter, SIGNAL(signalCharValue(int, const QString&)), this, SLOT(slotApplyCharValue(int, const QString&))); + m_parameterList.append(parameter); + + grid->addWidget(parameter->getWidget(), row, 0); + break; + } + case POST_PARAM_TYPE_STRING: + case POST_PARAM_TYPE_STRINGLIST: break; /* not implemented */ + case POST_PARAM_TYPE_BOOL: + { + PostFilterParameterBool* parameter = new + PostFilterParameterBool(m_xinePostParameter->name, m_xinePostParameter->offset, + (bool) *(int*)(m_data+m_xinePostParameter->offset), m_groupBox); + + connect(parameter, SIGNAL(signalIntValue(int, int)), this, SLOT(slotApplyIntValue(int, int))); + m_parameterList.append(parameter); + + grid->addWidget(parameter->getWidget(), row, 0); + break; + } + default: break; + } + + descr = new QLabel(QString::fromUtf8(m_xinePostParameter->description ), m_groupBox); + descr->setAlignment(QLabel::WordBreak | QLabel::AlignVCenter); + grid->addWidget(descr, row, 1); + row++; + m_xinePostParameter++; + } + } + KSeparator* sep = new KSeparator(KSeparator::Horizontal, m_groupBox); + grid->addMultiCellWidget(sep, row, row, 0, 1); + row++; + KPushButton* deleteButton = new KPushButton(i18n("Delete Filter"), m_groupBox); + deleteButton->setSizePolicy (QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Fixed)); + connect(deleteButton, SIGNAL(clicked()), this, SLOT( slotDeletePressed())); + grid->addWidget(deleteButton, row, 0); + + if(inputAPI) + { + KPushButton* helpButton = new KPushButton(i18n("Help"), m_groupBox); + helpButton->setSizePolicy(QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Fixed)); + connect(helpButton, SIGNAL(clicked()), this, SLOT(slotHelpPressed())); + grid->addWidget(helpButton, row, 1); + } + + if (parent) + m_groupBox->show(); + } +} + +PostFilter::~PostFilter() +{ + kdDebug() << "PostFilter: Delete Postprocessing Filter: " << m_filterName << endl; + if(m_xinePost) + { + delete m_groupBox; + delete [] m_data; + xine_post_dispose(m_xineEngine, m_xinePost); + } +} + +void PostFilter::slotApplyIntValue(int offset, int val) +{ + kdDebug() << "PostFilter: " << m_filterName << " Apply integer value " << val << " on offset " << offset << endl; + *(int*)(m_data+offset) = val; + m_xinePostAPI->set_parameters(m_xinePost, m_data); +} + +void PostFilter::slotApplyDoubleValue(int offset, double val) +{ + kdDebug() << "PostFilter: " << m_filterName << " Apply double value " << val << " on offset " << offset << endl; + *(double*)(m_data+offset) = val; + m_xinePostAPI->set_parameters(m_xinePost, m_data); +} + +void PostFilter::slotApplyCharValue(int offset, const QString& val) +{ + kdDebug() << "PostFilter: " << m_filterName << " Apply char value '" << val << "' on offset " << offset << endl; + sprintf((char *)(m_data+offset), "%s", val.latin1()); + m_xinePostAPI->set_parameters(m_xinePost, m_data); +} + +xine_post_in_t* PostFilter::getInput() const +{ + xine_post_in_t* input = NULL; + + kdDebug() << "PostFilter: Get input" << endl; + if(m_xinePost) + { + /* look for known input ports */ + input = xine_post_input(m_xinePost, const_cast<char*>("video")); + if( !input ) + input = xine_post_input(m_xinePost, const_cast<char*>("video in") ); + if( !input ) + input = xine_post_input(m_xinePost, const_cast<char*>("audio")); + if( !input ) + input = xine_post_input(m_xinePost, const_cast<char*>("audio in")); + } + return input; +} + + +xine_post_out_t* PostFilter::getOutput() const +{ + xine_post_out_t* output = NULL; + + kdDebug() << "PostFilter: Get output" << endl; + + if(m_xinePost) + { + /* look for known output ports */ + output = xine_post_output(m_xinePost, const_cast<char*>("video")); + if( !output ) + output = xine_post_output(m_xinePost, const_cast<char*>("video out")); + if( !output ) + output = xine_post_output(m_xinePost, const_cast<char*>("audio")); + if( !output ) + output = xine_post_output(m_xinePost, const_cast<char*>("audio out")); + + if(!output) + { + /* fallback to the first available output port. + * some video plugins have funky port names :) + */ + const char *const *outs = xine_post_list_outputs(m_xinePost); + return (xine_post_out_t*)xine_post_output(m_xinePost, (char *) *outs); + } + } + + return output; +} + + +void PostFilter::slotHelpPressed() +{ + kdDebug() << "PostFilter: Help pressed" << endl; + + PostFilterHelp* filterHelp = new PostFilterHelp(NULL, m_filterName.ascii(), QString::fromUtf8(m_xinePostAPI->get_help())); + filterHelp->exec(); + + delete filterHelp; +} + + +QString PostFilter::getConfig() +{ + /* + * returns a string like "filtername:parameter=value,parameter=value,..." + */ + + QString configString; + QTextOStream configStream(&configString); + + configStream << m_filterName << ":"; + for (uint i = 0; i < m_parameterList.count(); i++) + { + configStream << m_parameterList.at( i )->name() << "=" << m_parameterList.at( i )->getValue(); + if( i != m_parameterList.count()-1 ) + configStream << ","; + } + + kdDebug() << "PostFilter: GetConfig " << configString << endl; + + return configString; +} + +void PostFilter::setConfig(const QString &configString) +{ + /* + * expects a string like filtername:parameter=value,parameter=value,... + * or filtername:parameter="value",parameter="value",... + */ + + kdDebug() << "PostFilter: SetConfig " << configString << endl; + + QString configStr; + if (configString.section(':',0,0) == m_filterName) + { + configStr = configString.section(':',1,1); + } + else + { + kdWarning() << "PostFilter: Config string doesn't match filter name " << m_filterName << endl; + kdDebug() << "PostFilter: Don't apply new configuration" << endl; + return; + } + + for( int i = 0; i < configStr.contains(',') + 1; i++ ) + { + QString parameterConfig = configStr.section(',', i, i); + QString parameterName = parameterConfig.section('=', 0, 0); + QString parameterValue = parameterConfig.section('=', 1, 1); + parameterValue = parameterValue.remove('"'); + + for (uint j = 0; j < m_parameterList.count(); j++) + { + if(parameterName == m_parameterList.at(j)->name()) + { + kdDebug() << "PostFilter: Set parameter '" << parameterName << "' to value '" << parameterValue << "'" << endl; + m_parameterList.at(j)->setValue(parameterValue); + } + } + } +} + +PostFilterParameterInt::PostFilterParameterInt(const QString& name, int offset, int value, int min, int max, QWidget* parent) + : PostFilterParameter (name, offset, parent ) +{ + m_numInput = new KIntNumInput(value, parent); + m_numInput->setRange( min, max, 1, false); + connect(m_numInput, SIGNAL(valueChanged(int)), this, SLOT(slotIntValue(int))); +} + +PostFilterParameterDouble::PostFilterParameterDouble(const QString& name, int offset, double value, double min, double max, QWidget* parent) + : PostFilterParameter (name, offset, parent ) +{ + m_numInput = new KDoubleNumInput(parent); + m_numInput->setValue(value); + m_numInput->setRange(min, max, 0.01, false); + connect(m_numInput, SIGNAL(valueChanged( double)), this, SLOT(slotDoubleValue( double))); +} + +PostFilterParameterChar::PostFilterParameterChar(const QString& name, int offset, char *value, int size, QWidget* parent) + : PostFilterParameter (name, offset, parent ) +{ + m_charInput = new KLineEdit(value, parent); + m_charInput->setMaxLength(size); + connect(m_charInput, SIGNAL(returnPressed(const QString&)), this, SLOT(slotCharValue(const QString&))); +} + +PostFilterParameterCombo::PostFilterParameterCombo(const QString& name, int offset, int value, char **enums, QWidget* parent) + : PostFilterParameter (name, offset, parent) +{ + m_comboBox = new KComboBox(parent); + for (int i = 0; enums[i]; i++) + { + m_comboBox->insertItem(enums[i]); + } + m_comboBox->setCurrentItem(value); + connect(m_comboBox, SIGNAL( activated(int)), this, SLOT( slotIntValue(int))); +} + +PostFilterParameterBool::PostFilterParameterBool(const QString& name, int offset, bool value, QWidget* parent) + : PostFilterParameter (name, offset, parent ) +{ + m_checkBox = new QCheckBox(parent); + m_checkBox->setChecked(value); + connect(m_checkBox, SIGNAL(toggled(bool)), this, SLOT(slotBoolValue( bool))); +} + +PostFilterHelp::PostFilterHelp(QWidget *parent, const char *name, const QString& text) + : KDialogBase( parent, name, true, QString(name) + " - " + i18n("Help"), KDialogBase::Close ) +{ + setInitialSize( QSize(500,500) ); + + QWidget* mainWidget = makeMainWidget(); + QGridLayout* grid = new QGridLayout( mainWidget, 1, 1 ); + grid->setSpacing( 5 ); + + //QString help = QString::fromUtf8(text); + m_textEdit = new QTextEdit(text, QString::null, mainWidget, name); + m_textEdit->setReadOnly(true); + grid->addWidget(m_textEdit, 0, 0); +} + + +PostFilterHelp::~PostFilterHelp() +{ + delete m_textEdit; +} + +#include "postfilter.moc" diff --git a/kaffeine/src/player-parts/xine-part/postfilter.h b/kaffeine/src/player-parts/xine-part/postfilter.h new file mode 100644 index 0000000..ce04359 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/postfilter.h @@ -0,0 +1,229 @@ +/* + * postfilter.h - wrapper for xine's postprocessing filters + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2003-2005 Miguel Freitas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef POSTFILTER_H +#define POSTFILTER_H + +/* forward declaration */ +class QWidget; +class QObject; +class QString; +class QGroupBox; +class QTextEdit; +class KPushButton; + +#include <klineedit.h> +#include <knuminput.h> +#include <kcombobox.h> +#include <kdialogbase.h> + +#include <qcheckbox.h> + +#include <xine.h> + + +class PostFilterParameter : public QObject +{ + Q_OBJECT +public: + PostFilterParameter(const QString& name, int offset, QWidget* parent) + : QObject(parent, name.ascii()), m_offset(offset) + {} + ~PostFilterParameter() {}; + + virtual void setValue( const QString& ) = 0; + virtual QString getValue() = 0; + virtual QWidget *getWidget() = 0; + +protected: + int m_offset; +}; + + +class PostFilterParameterInt : public PostFilterParameter +{ + Q_OBJECT +public: + PostFilterParameterInt(const QString& name, int offset, int value, int min, int max, QWidget* parent); + ~PostFilterParameterInt() {}; + + void setValue( const QString &value ) + { int i = value.toInt(); m_numInput->setValue(i); slotIntValue(i); } + QString getValue() + { QString s; s.sprintf("%d", m_numInput->value()); return s; } + QWidget *getWidget() { return m_numInput; } + +signals: + void signalIntValue( int, int ); + +public slots: + void slotIntValue(int val) { emit signalIntValue(m_offset, val); } + +private: + KIntNumInput* m_numInput; +}; + + +class PostFilterParameterDouble : public PostFilterParameter +{ + Q_OBJECT +public: + PostFilterParameterDouble(const QString& name, int offset, double value, double min, double max, QWidget* parent); + ~PostFilterParameterDouble() {}; + + void setValue( const QString &value ) + { double d = value.toDouble(); m_numInput->setValue(d); slotDoubleValue(d); } + QString getValue() + { QString s; s.sprintf("%lf",m_numInput->value()); return s; } + QWidget *getWidget() { return m_numInput; } + +signals: + void signalDoubleValue(int, double); + +public slots: + void slotDoubleValue(double val) { emit signalDoubleValue(m_offset, val); } + +private: + KDoubleNumInput* m_numInput; +}; + + +class PostFilterParameterChar : public PostFilterParameter +{ + Q_OBJECT +public: + PostFilterParameterChar(const QString& name, int offset, char *value, int size, QWidget* parent); + ~PostFilterParameterChar() {}; + + void setValue(const QString &value) + { m_charInput->setText(value); slotCharValue(value); } + QString getValue() { return m_charInput->text(); } + QWidget *getWidget() { return m_charInput; } + +signals: + void signalCharValue(int, const QString&); + +public slots: + void slotCharValue(const QString& val) { emit signalCharValue(m_offset, val); } + +private: + KLineEdit* m_charInput; +}; + + +class PostFilterParameterCombo : public PostFilterParameter +{ + Q_OBJECT +public: + PostFilterParameterCombo(const QString& name, int offset, int value, char **enums, QWidget* parent); + ~PostFilterParameterCombo() {}; + + void setValue(const QString &value) { m_comboBox->setCurrentItem(value); slotIntValue(m_comboBox->currentItem()); } + QString getValue() { return m_comboBox->currentText(); } + QWidget *getWidget() { return m_comboBox; } + +signals: + void signalIntValue(int, int); + +public slots: + void slotIntValue(int val) { emit signalIntValue(m_offset, val); } + +private: + KComboBox* m_comboBox; +}; + + +class PostFilterParameterBool : public PostFilterParameter +{ + Q_OBJECT +public: + PostFilterParameterBool(const QString& name, int offset, bool value, QWidget* parent); + ~PostFilterParameterBool() {}; + + void setValue(const QString &value) + { bool b = (bool)value.toInt(); m_checkBox->setChecked(b); slotBoolValue(b); } + QString getValue() + { QString s; s.sprintf("%d",(int)m_checkBox->isOn()); return s; } + QWidget *getWidget() { return m_checkBox; } + +signals: + void signalIntValue(int, int); + +public slots: + void slotBoolValue(bool val) { emit signalIntValue(m_offset, (int)val); } + +private: + QCheckBox* m_checkBox; +}; + + +class PostFilterHelp : public KDialogBase +{ + Q_OBJECT +public: + PostFilterHelp(QWidget *parent=0, const char *name=0, const QString& text = QString::null); + ~PostFilterHelp(); + +private: + QTextEdit *m_textEdit; +}; + + +class PostFilter : public QObject +{ + Q_OBJECT +public: + PostFilter(const QString& name, xine_t* engine, xine_audio_port_t* audioDriver, + xine_video_port_t* videoDriver, QWidget *parent); + ~PostFilter(); + + xine_post_in_t* getInput() const; + xine_post_out_t* getOutput() const; + void setConfig(const QString &); + QString getConfig(); + + +signals: + void signalDeleteMe( PostFilter* me ); + + +private slots: + void slotDeletePressed() { emit signalDeleteMe(this); } + void slotApplyIntValue(int offset, int val); + void slotApplyDoubleValue(int offset, double val); + void slotApplyCharValue(int offset, const QString& val); + void slotHelpPressed(); + +private: + xine_t* m_xineEngine; + xine_post_t* m_xinePost; + xine_post_api_t* m_xinePostAPI; + xine_post_api_descr_t* m_xinePostDescr; + xine_post_api_parameter_t* m_xinePostParameter; + char* m_data; + + QGroupBox* m_groupBox; + QString m_filterName; + + QPtrList<PostFilterParameter> m_parameterList; +}; + +#endif /* POSTFILTER_H */ diff --git a/kaffeine/src/player-parts/xine-part/screenshotpreview.cpp b/kaffeine/src/player-parts/xine-part/screenshotpreview.cpp new file mode 100644 index 0000000..300ff83 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/screenshotpreview.cpp @@ -0,0 +1,70 @@ +/* + * screenshotpreview.cpp + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kglobalsettings.h> + +#include <qpainter.h> +#include <qwidget.h> +#include <qimage.h> + +#include "screenshotpreview.h" +#include "screenshotpreview.moc" + + +ScreenshotPreview::ScreenshotPreview(const QImage& img, QWidget *parent, const char *name ) + : KPreviewWidgetBase(parent,name) +{ + setMinimumWidth(200); + m_previewImg = img.copy(); /* deep copy */ +} + +ScreenshotPreview::~ScreenshotPreview() +{ +} + +void ScreenshotPreview::showPreview(const KURL&) /* reimplemented to do nothing */ +{ +} + +void ScreenshotPreview::clearPreview() /* reimplemented to do nothing */ +{ +} + +/* show preview picture */ + +void ScreenshotPreview::paintEvent(QPaintEvent*) +{ + int imgHeight, posy; + + imgHeight = (int)((double) m_previewImg.height() / m_previewImg.width() * (width()-5)); + posy = (height() - imgHeight) / 2; + + QString info = QString::number(m_previewImg.width()) + "x" + QString::number(m_previewImg.height()); + + QFont font = KGlobalSettings::generalFont(); + font.setPointSize(10); + QFontMetrics met(font); + + QPainter painter(this); + painter.drawImage(QRect(5, posy, width(), imgHeight), m_previewImg); + + painter.setFont(font); + painter.drawText((width()-met.width(info))/2, posy + imgHeight + 20, info); +} diff --git a/kaffeine/src/player-parts/xine-part/screenshotpreview.h b/kaffeine/src/player-parts/xine-part/screenshotpreview.h new file mode 100644 index 0000000..d3a62de --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/screenshotpreview.h @@ -0,0 +1,52 @@ +/* + * screenshotpreview.h + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef SCREENSHOTPREVIEW_H +#define SCREENSHOTPREVIEW_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kpreviewwidgetbase.h> + +class QImage; +class QWidget; + +class ScreenshotPreview : public KPreviewWidgetBase +{ + Q_OBJECT +public: + ScreenshotPreview(const QImage& img, QWidget *parent=0, const char *name=0); + ~ScreenshotPreview(); + +public slots: + virtual void showPreview(const KURL&); + virtual void clearPreview(); + +protected: + virtual void paintEvent(QPaintEvent*); + +private: + QImage m_previewImg; + +}; + +#endif /* SCREENSHOTPREVIEW_H */ diff --git a/kaffeine/src/player-parts/xine-part/videosettings.cpp b/kaffeine/src/player-parts/xine-part/videosettings.cpp new file mode 100644 index 0000000..e552df7 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/videosettings.cpp @@ -0,0 +1,129 @@ +/* + * videosettings.cpp + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <klocale.h> + +#include <qslider.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qnamespace.h> +#include <qgroupbox.h> + +#include "videosettings.h" +#include "videosettings.moc" + + +VideoSettings::VideoSettings(int hue, int sat, int contrast, int bright, + int avOffset, int spuOffset, QWidget *parent, const char *name) + : KDialogBase(KDialogBase::Plain, i18n("Video Settings"), KDialogBase::Default | KDialogBase::Close, KDialogBase::Close, parent, name, false) +{ + reparent(parent, pos(), false); + setInitialSize(QSize(450,250), true); + QWidget* page = plainPage(); + + QVBoxLayout* b = new QVBoxLayout(page); + + QGroupBox *videoGroup = new QGroupBox(QString::null, page); + b->addWidget(videoGroup); + + QGridLayout* videoGrid = new QGridLayout(videoGroup, 6, 2); + videoGrid->setSpacing(5); + videoGrid->setMargin(10); + + QLabel* hueText = new QLabel(i18n("Hue"), videoGroup); + hueText->setAlignment(AlignRight); + m_hueSlider = new QSlider(Qt::Horizontal, videoGroup); + m_hueSlider->setRange(0, 65535); + m_hueSlider->setSteps(10, 1000); + m_hueSlider->setValue(hue); + connect(m_hueSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewHue(int))); + videoGrid->addWidget(hueText, 0, 0); + videoGrid->addWidget(m_hueSlider, 0, 1); + + QLabel* satText = new QLabel(i18n("Saturation"), videoGroup); + satText->setAlignment(AlignRight); + m_satSlider = new QSlider(Qt::Horizontal, videoGroup); + m_satSlider->setRange(0, 65535); + m_satSlider->setSteps(10, 1000); + m_satSlider->setValue(sat); + connect(m_satSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewSaturation(int))); + videoGrid->addWidget(satText, 1, 0); + videoGrid->addWidget(m_satSlider, 1, 1); + + QLabel* contrastText = new QLabel(i18n("Contrast"), videoGroup); + contrastText->setAlignment(AlignRight); + m_contrastSlider = new QSlider(Qt::Horizontal, videoGroup); + m_contrastSlider->setRange(0, 65535); + m_contrastSlider->setSteps(10, 1000); + m_contrastSlider->setValue(contrast); + connect(m_contrastSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewContrast(int))); + videoGrid->addWidget(contrastText, 2, 0); + videoGrid->addWidget(m_contrastSlider, 2, 1); + + QLabel* brightText = new QLabel(i18n("Brightness"), videoGroup); + brightText->setAlignment(AlignRight); + m_brightSlider = new QSlider(Qt::Horizontal, videoGroup); + m_brightSlider->setRange(0, 65535); + m_brightSlider->setSteps(10, 1000); + m_brightSlider->setValue(bright); + connect(m_brightSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewBrightness(int))); + videoGrid->addWidget(brightText, 3, 0); + videoGrid->addWidget(m_brightSlider, 3, 1); + + QLabel* avOffsetText = new QLabel(i18n("Audio/Video Offset"), videoGroup); + avOffsetText->setAlignment(AlignRight); + m_avOffsetSlider = new QSlider(Qt::Horizontal, videoGroup); + m_avOffsetSlider->setRange(-90000, 90000); // +/- 1 sec + m_avOffsetSlider->setSteps(100, 10000); + m_avOffsetSlider->setValue(avOffset); + connect(m_avOffsetSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewAVOffset(int))); + videoGrid->addWidget(avOffsetText, 4, 0); + videoGrid->addWidget(m_avOffsetSlider, 4, 1); + + QLabel* spuOffsetText = new QLabel(i18n("Subtitle Offset"), videoGroup); + spuOffsetText->setAlignment(AlignRight); + m_spuOffsetSlider = new QSlider(Qt::Horizontal, videoGroup); + m_spuOffsetSlider->setRange(-90000, 90000); // +/- 1 sec + m_spuOffsetSlider->setSteps(100, 10000); + m_spuOffsetSlider->setValue(spuOffset); + connect(m_spuOffsetSlider, SIGNAL(valueChanged(int)), this, SIGNAL(signalNewSpuOffset(int))); + videoGrid->addWidget(spuOffsetText, 5, 0); + videoGrid->addWidget(m_spuOffsetSlider, 5, 1); + + connect(this, SIGNAL(defaultClicked()), this, SLOT(slotSetDefaultValues())); +} + + + +VideoSettings::~VideoSettings() +{ + +} + + +void VideoSettings::slotSetDefaultValues() +{ + m_hueSlider->setValue(32768); + m_satSlider->setValue(32768); + m_contrastSlider->setValue(32768); + m_brightSlider->setValue(32768); + m_avOffsetSlider->setValue(0); + m_spuOffsetSlider->setValue(0); +} diff --git a/kaffeine/src/player-parts/xine-part/videosettings.h b/kaffeine/src/player-parts/xine-part/videosettings.h new file mode 100644 index 0000000..7641bf8 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/videosettings.h @@ -0,0 +1,65 @@ +/* + * videosettings.h + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef VIDEOSETTINGS_H +#define VIDEOSETTINGS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kdialogbase.h> + +class QSlider; + +class VideoSettings : public KDialogBase +{ + + Q_OBJECT + +public: + VideoSettings(int hue, int sat, int contrast, int bright, int avOffset, + int spuOffset, QWidget *parent=0, const char *name=0); + ~VideoSettings(); + +signals: + void signalNewHue(int); + void signalNewSaturation(int); + void signalNewContrast(int); + void signalNewBrightness(int); + void signalNewAVOffset(int); + void signalNewSpuOffset(int); + +private slots: + + void slotSetDefaultValues(); + + +private: + QSlider* m_hueSlider; + QSlider* m_satSlider; + QSlider* m_contrastSlider; + QSlider* m_brightSlider; + QSlider* m_avOffsetSlider; + QSlider* m_spuOffsetSlider; + +}; + +#endif /* VIDEOSETTINGS_H */ diff --git a/kaffeine/src/player-parts/xine-part/xine_part.cpp b/kaffeine/src/player-parts/xine-part/xine_part.cpp new file mode 100644 index 0000000..920a2ac --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/xine_part.cpp @@ -0,0 +1,2111 @@ +/* + * xine_part.cpp + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "xine_part.h" + +#include <kapplication.h> +#include <kinstance.h> +#include <kiconloader.h> +#include <kaction.h> +#include <kstdaction.h> +#include <kfiledialog.h> +#include <kmessagebox.h> +#include <kinputdialog.h> +#include <kxmlguifactory.h> +#include <kpopupmenu.h> +#include <kparts/genericfactory.h> +#include <kprogress.h> +#include <kio/netaccess.h> +#include <kstandarddirs.h> +#include <dcopclient.h> +#include <kprocess.h> +#include <kprotocolinfo.h> +#include <ktoolbar.h> + +#include <qvbox.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qimage.h> +#include <qfontmetrics.h> +#include <qregexp.h> +#include <qtooltip.h> +#include <qdatetimeedit.h> + +#include <xine/xineutils.h> + +#include "mrl.h" +#include "kxinewidget.h" +#include "videosettings.h" +#include "equalizer.h" +#include "deinterlacequality.h" +#include "filterdialog.h" +#include "screenshotpreview.h" +#include "xineconfig.h" +#include "positionslider.h" +#include "playlistimport.h" +#include "version.h" + +typedef KParts::GenericFactory<XinePart> XinePartFactory; +K_EXPORT_COMPONENT_FACTORY (libxinepart, XinePartFactory) + + +XinePart::XinePart(QWidget* parentWidget, const char* widgetName, QObject* parent, const char* name, const QStringList& args) + : DCOPObject("XinePartIface"), + KaffeinePart(parent, name ? name : "XinePart"), + m_current(0), m_xine(NULL), m_pictureSettings(NULL), m_deinterlacerConfigWidget(NULL), + m_filterDialog(NULL), m_embeddedContext(NULL) +{ + kdDebug() << "XinePart: Creating new XinePart..." << endl; + + /* + * Parsing parameter given by kaffeine (audiodriver, videodriver, verbose) + * or parameters of <embed>...</embed> + * + * format: param="value" + */ + QString audioDriver = QString::null; + QString videoDriver = QString::null; + bool verbose = false; + TimeShiftFilename = ""; + + for (uint i=0; i<args.count(); i++) + { + kdDebug() << "XinePart: Argument: " << args[i] << endl; + if (args[i].left(11).lower() == "audiodriver") + { + audioDriver = args[i].section( '"',1, 1 ); + kdDebug() << "XinePart: Found audiodriver parameter, value: " << audioDriver << endl; + + } + if (args[i].left(11).lower() == "videodriver") + { + videoDriver = args[i].section( '"',1, 1 ); + kdDebug() << "XinePart: Found videodriver parameter, value: " << videoDriver << endl; + } + if (args[i].left(7).lower() == "verbose") + { + if (args[i].section( '"', 1, 1 ).lower() == "true") + { + kdDebug() << "XinePart: Found parameter verbose, set xine engine verbosity to max..." << endl; + verbose = true; + } + } + } + + // we need an instance + setInstance(XinePartFactory::instance()); + + // be careful - we may be embedded + QString configPath = locate("data", "kaffeine/xine-config"); + QString logoPath = locate("data", "kaffeine/logo"); + + kdDebug() << "XinePart: Using xine-config file: " << configPath << endl; + + m_xine = new KXineWidget(parentWidget, widgetName, configPath, logoPath, + audioDriver, videoDriver, /* start xine manual*/true, verbose); + connect(m_xine, SIGNAL(signalXineFatal(const QString&)), this, SIGNAL(canceled(const QString&))); + connect(m_xine, SIGNAL(stopDvb()), this, SIGNAL(stopDvb())); + connect(m_xine, SIGNAL(signalDvbOSDHidden()), this, SIGNAL(dvbOSDHide())); + m_xine->setFocusPolicy(QWidget::ClickFocus); + setWidget(m_xine); + + // set our XML-UI resource file + setXMLFile("xine_part.rc"); + initActions(); + initConnections(); + + QTimer::singleShot(0, this, SLOT(slotDisableAllActions())); + m_oldPosition = m_xine->mapToGlobal(QPoint(0,0)); + m_posCheckTimer.start(333); +} + +XinePart::~XinePart() +{ + kdDebug() << "XinePart: destructor" << endl; + kdDebug() << "XinePart destructor: calling saveConfig()" << endl; + saveConfig(); + if (m_embeddedContext) + delete m_embeddedContext; +} + +KAboutData *XinePart::createAboutData() +{ + KAboutData* aboutData = new KAboutData( "kaffeine", I18N_NOOP("XinePart"), + KAFFEINE_VERSION, I18N_NOOP("A xine based player part for Kaffeine."), + KAboutData::License_GPL, + "(c) 2003-2004, Jürgen Kofler.", 0, "http://kaffeine.sourceforge.net"); + aboutData->addAuthor("Jürgen Kofler.",0, "kaffeine@gmx.net"); + + return aboutData; +} + +bool XinePart::openURL(const MRL& mrl) +{ + kdDebug() << "XinePart::openURL(): " << mrl.url() << endl; + + // if (!mrl.kurl().isValid()) + // return false; + + m_mrl = mrl; + m_playlist.clear(); + m_current = 0; + bool playlist = false; + + QString ext = m_mrl.kurl().fileName(); + ext = ext.remove( 0 , ext.findRev('.')+1 ).lower(); + + if (!m_mrl.mime().isNull()) + { + KMimeType::Ptr mime = KMimeType::findByURL(m_mrl.kurl().path()); + m_mrl.setMime(mime->name()); + } + + /* is m_mrl a playlist? */ + if ((m_mrl.mime() == "text/plain") || (m_mrl.mime() == "text/xml") || (m_mrl.mime() == "application/x-kaffeine") + || (m_mrl.mime() == "audio/x-scpls") || (m_mrl.mime() == "audio/x-mpegurl") || (m_mrl.mime() == "audio/mpegurl") + || (m_mrl.mime() == "application/smil") + || (ext == "asx") || (ext == "asf") || (ext == "wvx") || (ext == "wax")) /* windows meta files */ + { + kdDebug() << "XinePart: Check for kaffeine/noatun/m3u/pls/asx playlist\n"; + QString localFile; + if (KIO::NetAccess::download(m_mrl.kurl(), localFile, widget())) + { + QFile file(localFile); + file.open(IO_ReadOnly); + QTextStream stream(&file); + QString firstLine = stream.readLine(); + QString secondLine = stream.readLine(); + file.close(); + + if (secondLine.contains("kaffeine", false)) + { + kdDebug() << "KafeinePart: Try loading kaffeine playlist\n"; + playlist = PlaylistImport::kaffeine(localFile, m_playlist); + } + if (secondLine.contains("noatun", false)) + { + kdDebug() << "XinePart: Try loading noatun playlist\n"; + playlist = PlaylistImport::noatun(localFile, m_playlist); + } + if (firstLine.contains("asx", false)) + { + kdDebug() << "XinePart: Try loading asx playlist\n"; + playlist = PlaylistImport::asx(localFile, m_playlist); + } + if (firstLine.contains("smil", false)) + { + kdDebug() << "XinePart: Try loading smil playlist\n"; + if (KMessageBox::warningYesNo(0, i18n("SMIL (Synchronized Multimedia Integration Language) support is rudimentary!\nXinePart can now try to playback contained video sources without any layout. Proceed?"), QString::null, KStdGuiItem::yes(), KStdGuiItem::no(), "smil_warning") == KMessageBox::Yes) + { + if (!PlaylistImport::smil(localFile, m_mrl, m_playlist)) + { + emit signalTrackFinished(); + return false; + } + } + else + return false; + } + if (firstLine.contains("[playlist]", false)) + { + kdDebug() << "XinePart: Try loading pls playlist\n"; + playlist = PlaylistImport::pls(localFile, m_playlist); + } + if (ext == "m3u") //indentify by extension + { + kdDebug() << "XinePart: Try loading m3u playlist\n"; + playlist = PlaylistImport::m3u(localFile, m_playlist); + } + } + else + kdError() << "XinePart: " << KIO::NetAccess::lastErrorString() << endl; + } + /* check for ram playlist */ + if ( (ext == "ra") || (ext == "rm") || (ext == "ram") || (ext == "lsc") || (ext == "pl") ) + { + kdDebug() << "XinePart: Try loading ram playlist\n"; + playlist = PlaylistImport::ram(m_mrl, m_playlist, widget()); + } + /* urls from audiocd kio-slave */ + if (m_mrl.kurl().protocol() == "audiocd") + { + QString audioTrack = QString::number(m_mrl.kurl().fileName().remove( QRegExp("\\D" ) ).left(2).toUInt()); + m_mrl = MRL(audioTrack.prepend( "cdda:/" )); + } + + if (!playlist) + { + kdDebug() << "XinePart: Got single track\n"; + m_playlist.append(m_mrl); + } + + slotPlay(true); + + return true; +} + +bool XinePart::closeURL() +{ + kdDebug() << "XinePart::closeURL()" << endl; + // m_playlist.clear(); + // m_mrl = MRL(); + slotStop(); + + return true; +} + +void XinePart::slotPlay(bool forcePlay) +{ + kdDebug() << "XinePart::slotPlay()" << endl; + + m_pauseButton->setChecked(false); + if (m_xine->isPlaying()) + { + if ( (m_xine->getSpeed() != KXineWidget::Normal) && !forcePlay ) + { + m_xine->slotSpeedNormal(); + slotEnablePlayActions(); + return; + } + else + emit stopDvb(); + } + + if (m_playlist.count() == 0) + { + emit signalRequestCurrentTrack(); + return; + } + + MRL mrl = m_playlist[m_current]; + + /* + * is protocol supported by xine or not known by KIO? + */ + if ((QString(SUPPORTED_PROTOCOLS).contains(mrl.kurl().protocol())) + || (!KProtocolInfo::isKnownProtocol(mrl.kurl()))) + { + QString sub; + if ((!mrl.subtitleFiles().isEmpty()) && (mrl.currentSubtitle() > -1)) + sub = QString("#subtitle:%1").arg(mrl.subtitleFiles()[mrl.currentSubtitle()]); + + m_xine->clearQueue(); + m_xine->appendToQueue(mrl.url() + sub ); + if (!m_xine->isXineReady()) + { + if (!m_xine->initXine()) + return; + } + else + QTimer::singleShot(0, m_xine, SLOT(slotPlay())); + } + else + { + kdDebug() << "XinePart: Protocol not supported by xine, try to download it..." << endl; + + QString localFile; + if (KIO::NetAccess::download(mrl.kurl(), localFile, widget())) + { + m_xine->clearQueue(); + m_xine->appendToQueue(localFile); + if (!m_xine->isXineReady()) + { + if (!m_xine->initXine()) + return; + } + else + QTimer::singleShot(0, m_xine, SLOT(slotPlay())); + } + else + kdError() << "XinePart: " << KIO::NetAccess::lastErrorString() << endl; + } +} + +void XinePart::slotStop() +{ + if (!m_xine->isXineReady()) + return; + + emit stopDvb(); + + /* if we play a DVD we cache current title and chapter */ + if (m_playlist[m_current].url().startsWith("dvd:/")) + { + uint title = m_xine->currentDVDTitleNumber(); + uint chapter = m_xine->currentDVDChapterNumber(); + + m_playlist[m_current] = MRL("dvd://" + QString::number(title) + "." + QString::number(chapter)); + } + + QTimer::singleShot(0, m_xine, SLOT(slotStop())); + stateChanged("not_playing"); + m_pauseButton->setChecked(false); + m_playTime->setText("0:00:00"); + emit setWindowCaption(""); +} + +void XinePart::slotNext() +{ + if (m_xine->hasChapters()) + { + m_xine->playNextChapter(); + return; + } + + if ((m_playlist.count() > 0) && (m_current < m_playlist.count()-1)) + { + m_current++; + slotPlay(); + } + else + { + emit signalRequestNextTrack(); + } +} + +void XinePart::slotPrevious() +{ + if (m_xine->hasChapters()) + { + m_xine->playPreviousChapter(); + return; + } + + if (m_current > 0) + { + m_current--; + slotPlay(); + } + else + { + emit signalRequestPreviousTrack(); + } +} + +void XinePart::requestForOSD( const QString &text, int duration, int priority ) +{ + m_xine->showOSDMessage( text, duration, priority ); +} + +void XinePart::setDvbCurrentNext( const QString &channelName, const QStringList &list ) +{ + m_xine->setDvbCurrentNext( channelName, list ); +} + +void XinePart::slotDvbOpen( const QString &filename, const QString &chanName, int haveVideo ) +{ + if (!m_xine->isXineReady()) + if (!m_xine->initXine()) + return; + m_playlist.clear(); + m_xine->setDvb( filename, chanName, haveVideo ); + QTimer::singleShot(0, m_xine, SLOT(openDvb())); + //m_xine->openDvb( filename, chanName, haveVideo ); +} + +void XinePart::getTimeShiftFilename( const QString &filename ) +{ + TimeShiftFilename = filename; + m_xine->TimeShiftFilename = TimeShiftFilename; +} + +void XinePart::slotTogglePause( bool pauseLive ) +{ + kdDebug() << "slotSpeedPause()" << endl; + if (!m_xine->isXineReady()) + return; + + if (m_xine->getSpeed() == KXineWidget::Pause) + { + m_xine->slotSpeedNormal(); + slotEnablePlayActions(); + m_pauseButton->setChecked(false); + } + else + { + if ( pauseLive ) + emit playerPause(); + m_xine->slotSpeedPause(); + // kdDebug() << "XinePart: Set state to paused" << endl; + stateChanged("paused"); + m_pauseButton->setChecked(true); + } +} + +void XinePart::speedFaster() +{ + slotFastForward(); +} + +void XinePart::slotFastForward() +{ + if (m_xine->getSpeed() == KXineWidget::Pause) + { + m_pauseButton->setChecked(false); + slotEnablePlayActions(); + } + m_xine->slotSpeedFaster(); +} + +void XinePart::speedSlower() +{ + slotSlowMotion(); +} + +void XinePart::slotSlowMotion() +{ + if (m_xine->getSpeed() == KXineWidget::Pause) + { + m_pauseButton->setChecked(false); + slotEnablePlayActions(); + } + + m_xine->slotSpeedSlower(); +} + +void XinePart::slotMute() +{ + if (!m_xine->isXineReady()) + return; + + m_xine->slotToggleMute(); +} + +void XinePart::slotVolumeUp() +{ + int newVol = volume() + 5; + if (newVol >100) + newVol = 100; + slotSetVolume(newVol); +} + +void XinePart::slotVolumeDown() +{ + int newVol = volume() - 5; + if (newVol <0) + newVol = 0; + slotSetVolume(newVol); +} + +void XinePart::slotPosPlusSmall() +{ + slotJumpIncrement( 20 ); +} + +void XinePart::slotPosMinusSmall() +{ + slotJumpIncrement( -20 ); +} + +void XinePart::slotPosPlusMedium() +{ + slotJumpIncrement( 60 ); +} + +void XinePart::slotPosMinusMedium() +{ + slotJumpIncrement( -60 ); +} + +void XinePart::slotPosPlusLarge() +{ + slotJumpIncrement( 600 ); +} + +void XinePart::slotPosMinusLarge() +{ + slotJumpIncrement( -600 ); +} + +void XinePart::slotJumpIncrement(int increment) +{ + if (!m_xine->isSeekable()) + return; + + QTime timeNow; + QTime projectedTime; + QTime startTime; + + if (!m_xine->getLength().isNull()) + { + timeNow = m_xine->getPlaytime(); + if ( increment < 0 && timeNow.msecsTo(startTime) > increment * 1000 ) + { + m_xine->slotSeekToTime(startTime); + } + else + { + projectedTime = timeNow.addSecs(increment); + m_xine->slotSeekToTime(projectedTime); + } + } +} + +void XinePart::slotAdvanceSubTitle() +{ + int spuOffset; + m_xine->getspuOffset(spuOffset); + m_xine->slotSetSpuOffset(spuOffset+45000); +} + +void XinePart::slotDelaySubTitle() +{ + int spuOffset; + m_xine->getspuOffset(spuOffset); + m_xine->slotSetSpuOffset(spuOffset-45000); +} + +void XinePart::slotSaveStream() +{ + if (m_mrl.isEmpty()) + return; + + QString saveDir = m_xine->getStreamSaveDir(); + + KURL kurl = KFileDialog::getSaveURL(saveDir + "/" + m_playlist[m_current].kurl().fileName(), QString::null, 0, i18n("Save Stream As")); + if (!kurl.isValid()) + return; + + if ( saveDir != kurl.directory() ) + m_xine->setStreamSaveDir(kurl.directory()); + + m_xine->clearQueue(); + m_xine->appendToQueue(m_playlist[m_current].url() + "#save:" + kurl.path()); + QTimer::singleShot(0, m_xine, SLOT(slotPlay())); + m_pauseButton->setChecked(false); +} + +void XinePart::slotSetSubtitle(int channel) +{ + if (m_playlist[m_current].subtitleFiles().isEmpty()) + { + m_xine->slotSetSubtitleChannel(channel); + } + else + { + m_playlist[m_current].setCurrentSubtitle(channel - 1); + emit signalNewMeta(m_mrl); + m_xine->savePosition(m_xine->getPosition()-200); + slotPlay(true); //force load of new subtitle + } + emit setStatusBarText(i18n("Subtitle") + ": " + m_subtitles->items()[channel]); + m_xine->showOSDMessage(i18n("Subtitle") + ": " + m_subtitles->items()[channel], DEFAULT_OSD_DURATION); +} + +void XinePart::slotAddSubtitle(void) +{ + QString subtitleURL = KFileDialog::getOpenURL(m_mrl.kurl().directory(), + i18n("*.smi *.srt *.sub *.txt *.ssa *.asc|Subtitle Files\n*.*|All Files"), + 0, i18n("Select Subtitle File")).path(); + + if (!(subtitleURL.isEmpty())) + { + if (!m_playlist[m_current].subtitleFiles().contains(subtitleURL)) + { + m_playlist[m_current].addSubtitleFile(subtitleURL); + } + + int subchannel = m_playlist[m_current].subtitleFiles().size() - 1; + m_playlist[m_current].setCurrentSubtitle(subchannel); + emit signalNewMeta(m_mrl); + m_xine->savePosition(m_xine->getPosition()-200); + slotPlay(true); //force load of new subtitle + + emit setStatusBarText(i18n("Subtitle") + ": " + m_subtitles->items()[subchannel]); + m_xine->showOSDMessage(i18n("Subtitle") + ": " + m_subtitles->items()[subchannel], DEFAULT_OSD_DURATION); + } + +} + +void XinePart::slotSetAudioChannel(int channel) +{ + m_xine->slotSetAudioChannel(channel); + emit setStatusBarText(i18n("Audiochannel") + ": " + m_audioChannels->items()[channel]); + m_xine->showOSDMessage(i18n("Audiochannel") + ": " + m_audioChannels->items()[channel], DEFAULT_OSD_DURATION); +} + +void XinePart::slotSetDVDTitle(const QString& titleStr) +{ + bool ok; + uint title = titleStr.toInt(&ok); + if (ok && title > 0 && title <= m_xine->getDVDTitleCount()) + { + KURL url = m_mrl.kurl(); + url.addPath(QString::number(title)); + m_playlist[m_current] = MRL(url); + slotPlay(true); + } +} + +void XinePart::slotSetDVDChapter(const QString& chapterStr) +{ + bool ok; + uint chapter = chapterStr.toInt(&ok); + if (ok) + setDVDChapter(chapter); +} + +void XinePart::slotSetDVDAngle(const QString& angleStr) +{ + bool ok; + uint angle = angleStr.toInt(&ok); + if (ok && angle > 0 && angle <= m_xine->getDVDAngleCount()) + { + uint title = m_xine->currentDVDTitleNumber(); + uint chapter = m_xine->currentDVDChapterNumber(); + KURL url = m_mrl.kurl(); + + url.addPath(QString::number(title) + "." + QString::number(chapter) + "." + QString::number(angle)); + m_playlist[m_current] = MRL(url); + slotPlay(true); + } +} + +void XinePart::setDVDChapter(uint chapter) +{ + if (chapter > 0 && chapter <= m_xine->getDVDChapterCount()) + { + uint title = m_xine->currentDVDTitleNumber(); + KURL url = m_mrl.kurl(); + + url.addPath(QString::number(title) + "." + QString::number(chapter)); + m_playlist[m_current] = MRL(url); + slotPlay(true); + } +} + +void XinePart::slotChannelInfo(const QStringList& audio, const QStringList& sub, int currentAudio, int currentSub) +{ + kdDebug() << "XinePart: slotChannelInfo: currentAudio="<<currentAudio<< " currentSub="<<currentSub<<"\n"; + m_audioChannels->setItems(audio); + m_audioChannels->setCurrentItem(currentAudio+1); + + if (m_playlist[m_current].subtitleFiles().isEmpty()) + { + m_subtitles->setItems(sub); + m_subtitles->setCurrentItem(currentSub+1); + } + else + { + QStringList subFiles = m_playlist[m_current].subtitleFiles(); + QStringList subs(i18n("off")); + QString sub; + QStringList::ConstIterator end(subFiles.end()); + for (QStringList::ConstIterator it = subFiles.begin(); it != end; ++it) + { + sub = (*it); + sub = sub.remove(0 , sub.findRev('/')+1); + subs.append(sub); + } + m_subtitles->setItems(subs); + m_subtitles->setCurrentItem(m_playlist[m_current].currentSubtitle() + 1); + } + + /* if we play a DVD enable and fill menus */ + if (m_playlist[m_current].url().startsWith("dvd:/")) + { + QStringList titles; + QStringList chapters; + QStringList angles; + uint titlesCount = m_xine->getDVDTitleCount(); + uint chaptersCount = m_xine->getDVDChapterCount(); + uint anglesCount = m_xine->getDVDAngleCount(); + + for (uint i = 1; i <= titlesCount; i++) + titles.append(QString::number(i)); + for (uint i = 1; i <= chaptersCount; i++) + chapters.append(QString::number(i)); + for (uint i = 1; i <= anglesCount; i++) + angles.append(QString::number(i)); + + m_dvdTitles->setItems(titles); + m_dvdTitles->setCurrentItem(m_xine->currentDVDTitleNumber() - 1); + m_dvdChapters->setItems(chapters); + m_dvdChapters->setCurrentItem(m_xine->currentDVDChapterNumber() - 1); + m_dvdAngles->setItems(angles); + m_dvdAngles->setCurrentItem(m_xine->currentDVDAngleNumber() - 1); + stateChanged("dvd_playback"); + } + else + { + stateChanged("dvd_playback", StateReverse); + } +} + +void XinePart::slotNewPosition(int pos, const QTime& playtime) +{ + QTime length = m_xine->getLength(); + QTime calcLength; + + //if (!m_xine->isSeekable() || length.isNull() || length < playtime) + if (!m_xine->isSeekable() ) + { + m_position->setPosition(0,false); + m_position->setEnabled(false); + } + else + { + m_position->setPosition(pos, false); + m_position->setEnabled(true); + } + + if (m_timerDirection == BACKWARD_TIMER && !length.isNull() && length >= playtime) + calcLength = length.addSecs(-playtime.second()-playtime.minute()*60-playtime.hour()*60*60); + else + calcLength = playtime; + + if (m_timerDirection == BACKWARD_TIMER) + m_playTime->setText("-" + calcLength.toString("h:mm:ss")); + else + m_playTime->setText(calcLength.toString("h:mm:ss")); + + QString timeMessage; + if (m_isOsdTimer) + { + if (m_timerDirection == BACKWARD_TIMER || length.isNull() || length < playtime) + { + timeMessage = calcLength.toString("h:mm:ss"); + m_xine->showOSDMessage("-" + timeMessage, 600, OSD_MESSAGE_LOW_PRIORITY); + } + else + { + timeMessage = i18n("%1 of %2").arg(calcLength.toString("h:mm:ss")).arg(length.toString("h:mm:ss")); + m_xine->showOSDMessage(timeMessage, 600, OSD_MESSAGE_LOW_PRIORITY); + } + } + currentPosition = (playtime.hour()*3600)+(playtime.minute()*60)+playtime.second(); +} + +QString XinePart::screenShot() +{ + QString filename = QDir::homeDirPath()+"/kaffeinedcopshot.jpg"; + QImage shot = m_xine->getScreenshot(); + if ( shot.save( filename, "JPEG" ) ) + return filename; + else + return ""; +} + +void XinePart::slotScreenshot() +{ + QImage shot = m_xine->getScreenshot(); + + KFileDialog dlg(":kaffeineMain_Screenshot", i18n("*.png|PNG-File\n*.bmp|BMP-File\n*.xbm|XBM-File"), + 0, "save screenshot", true); + dlg.setOperationMode(KFileDialog::Saving); + dlg.setCaption(i18n("Save Screenshot As")); + dlg.setSelection("screenshot.png"); + + ScreenshotPreview* prev = new ScreenshotPreview(shot, &dlg); + dlg.setPreviewWidget(prev); + + dlg.exec(); + QString fileName = dlg.selectedFile(); + + if (fileName.isEmpty()) + return; + + QString type = dlg.currentFilter(); + type = (type.remove(0,2)).upper(); + + kdDebug() << "XinePart: Save screenshot as " << type << "\n"; + if (!shot.save(fileName, type.ascii())) + kdError() << "XinePart: Screenshot not saved successfully!" << endl; +} + +void XinePart::slotFilterDialog() +{ + if (!m_filterDialog) + { + m_filterDialog = new FilterDialog(m_xine->getAudioFilterNames(), m_xine->getVideoFilterNames()); + connect(m_filterDialog, SIGNAL(signalCreateAudioFilter(const QString&, QWidget*)), + m_xine, SLOT(slotCreateAudioFilter(const QString&, QWidget*))); + connect(m_filterDialog, SIGNAL(signalCreateVideoFilter(const QString&, QWidget*)), + m_xine, SLOT(slotCreateVideoFilter(const QString&, QWidget*))); + connect(m_filterDialog, SIGNAL(signalRemoveAllAudioFilters()), m_xine, SLOT(slotRemoveAllAudioFilters())); + connect(m_filterDialog, SIGNAL(signalRemoveAllVideoFilters()), m_xine, SLOT(slotRemoveAllVideoFilters())); + connect(m_filterDialog, SIGNAL(signalUseAudioFilters(bool)), m_xine, SLOT(slotEnableAudioFilters(bool))); + connect(m_filterDialog, SIGNAL(signalUseVideoFilters(bool)), m_xine, SLOT(slotEnableVideoFilters(bool))); + } + m_filterDialog->show(); + m_filterDialog->raise(); +} + +void XinePart::slotDeinterlaceQuality() +{ + if (!m_deinterlacerConfigWidget) + return; + DeinterlaceQuality* deinterlaceQuality = new DeinterlaceQuality((QWidget*)m_deinterlacerConfigWidget); + deinterlaceQuality->setQuality(m_lastDeinterlaceQuality); + connect(deinterlaceQuality, SIGNAL(signalSetDeinterlaceConfig(const QString&)), + m_xine, SLOT(slotSetDeinterlaceConfig(const QString&))); + + deinterlaceQuality->exec(); + + m_lastDeinterlaceQuality = deinterlaceQuality->getQuality(); + m_lastDeinterlacerConfig = m_xine->getDeinterlaceConfig(); + delete deinterlaceQuality; +} + +void XinePart::slotSetHue( int i ) +{ + m_hue = i; + if ( i==-1 ) + return; + m_xine->slotSetHue( i ); +} + +void XinePart::slotSetSaturation( int i ) +{ + m_saturation = i; + if ( i==-1 ) + return; + m_xine->slotSetSaturation( i ); +} + +void XinePart::slotSetContrast( int i ) +{ + m_contrast = i; + if ( i==-1 ) + return; + m_xine->slotSetContrast( i ); +} + +void XinePart::slotSetBrightness( int i ) +{ + m_brightness = i; + if ( i==-1 ) + return; + m_xine->slotSetBrightness( i ); +} + +void XinePart::slotPictureSettings() +{ + if (!m_pictureSettings) + { + int hue, sat, contrast, bright, avOffset, spuOffset; + m_xine->getVideoSettings(hue, sat, contrast, bright, avOffset, spuOffset); + m_pictureSettings = new VideoSettings(hue, sat, contrast, bright, avOffset, spuOffset); + connect(m_pictureSettings, SIGNAL(signalNewHue(int)), this, SLOT(slotSetHue(int))); + connect(m_pictureSettings, SIGNAL(signalNewSaturation(int)), this, SLOT(slotSetSaturation(int))); + connect(m_pictureSettings, SIGNAL(signalNewContrast(int)), this, SLOT(slotSetContrast(int))); + connect(m_pictureSettings, SIGNAL(signalNewBrightness(int)), this, SLOT(slotSetBrightness(int))); + connect(m_pictureSettings, SIGNAL(signalNewAVOffset(int)), m_xine, SLOT(slotSetAVOffset(int))); + connect(m_pictureSettings, SIGNAL(signalNewSpuOffset(int)), m_xine, SLOT(slotSetSpuOffset(int))); + } + m_pictureSettings->show(); + m_pictureSettings->raise(); +} + +void XinePart::slotEqualizer() +{ + m_equalizer->show(); + m_equalizer->raise(); +} + +void XinePart::slotToggleBroadcastSend() +{ + bool ok = false; + + if (m_broadcastSend->isChecked()) + { + m_broadcastPort = (uint)KInputDialog::getInteger( QString::null, i18n("Broadcasting port:"), m_broadcastPort, 0, 1000000, 1, &ok); + if (!ok) + { + m_broadcastSend->setChecked(false); + return; + } + m_xine->setBroadcasterPort(m_broadcastPort); + } + else + { + m_xine->setBroadcasterPort(0); /* disable */ + } +} + +void XinePart::slotBroadcastReceive() +{ + if (!m_xine->isXineReady()) + { + if (!m_xine->initXine()) + return; + } + + KDialogBase* dialog = new KDialogBase(0, "configmaster", true, i18n("Configure Receive Broadcast Stream"), KDialogBase::Ok|KDialogBase::Cancel); + QVBox* page = dialog->makeVBoxMainWidget(); + new QLabel(i18n("Sender address:"), page); + KLineEdit* address = new KLineEdit(m_broadcastAddress, page); + new QLabel(i18n("Port:"), page); + QSpinBox* port = new QSpinBox(0, 1000000, 1, page); + port->setValue(m_broadcastPort); + + if (dialog->exec() == KDialogBase::Accepted) + { + m_broadcastPort = port->value(); + m_broadcastAddress = address->text(); + openURL(MRL(QString("slave://") + m_broadcastAddress + ":" + QString::number(m_broadcastPort))); + } + delete dialog; +} + +void XinePart::slotJumpToPosition() +{ + if (!m_xine->isSeekable()) + return; + + KDialogBase* dialog = new KDialogBase( 0, "configmaster", true, QString::null, KDialogBase::Ok|KDialogBase::Cancel ); + QVBox* page = dialog->makeVBoxMainWidget(); + page->setMargin(5); + page->setSpacing(5); + dialog->disableResize(); + new QLabel(i18n("Jump to position:"), page); + QTimeEdit* timeEdit = new QTimeEdit(page); + if (!m_xine->getLength().isNull()) + { + timeEdit->setMaxValue(m_xine->getLength()); + timeEdit->setTime(m_xine->getPlaytime()); + } + + if (dialog->exec() == KDialogBase::Accepted) + { + m_xine->slotSeekToTime(timeEdit->time()); + } + delete dialog; +} + +void XinePart::slotButtonTimerPressed() +{ + m_osdTimerEnabler.start(500, true); /* Long Click is 500ms */ +} + +void XinePart::slotButtonTimerReleased() +{ + if (!m_osdTimerEnabler.isActive()) + return; /* If short click toggle timer Mode*/ + m_osdTimerEnabler.stop(); + //kdDebug() << "XinePart: Toggling forward/backward Timer." << endl; + QTime length = m_xine->getLength(); + + if (!length.isNull()) /* if length not available counting backwards has no meaning */ + { + if (m_timerDirection == FORWARD_TIMER) + m_timerDirection = BACKWARD_TIMER; + else + m_timerDirection = FORWARD_TIMER; + slotNewPosition(m_xine->getPosition(),m_xine->getPlaytime()); + } +} + +void XinePart::slotToggleOsdTimer() +{ + kdDebug() << "XinePart: Toggling Osd Timer." << endl; + m_isOsdTimer = !m_isOsdTimer; +} + +void XinePart::slotConfigXine() +{ + if (!m_xine->isXineReady()) + { + if (!m_xine->initXine()) + return; + } + + XineConfig* xineConfigDialog = new XineConfig(m_xine->getXineEngine()); + xineConfigDialog->exec(); + delete xineConfigDialog; +} + +void XinePart::slotError(const QString& errMessage) +{ + if ((m_playlist.count() > 0) && (m_current < m_playlist.count() - 1)) + { + slotNext(); // try next before aborting playback; e.g. we play a PLS playlist, primary server is full, now try secondary + } + else + { + //KMessageBox::detailedError(0, errMessage, m_xine->getXineLog(), i18n("xine Error")); + stateChanged("not_playing"); + KMessageBox::detailedError(0, errMessage, m_xine->getXineLog(), i18n("xine Error")); + emit signalPlaybackFailed(); + } +} + +void XinePart::slotMessage(const QString& message) +{ + QString msg = message; + if ( msg.startsWith("@") ) { + if ( m_xine->isPlaying() && m_xine->getURL().contains("#") ) // do not warn for url containing # + return; + msg.remove(0,1); + } + KMessageBox::information(0, msg, i18n("xine Message")); +} + +void XinePart::slotStatus(const QString& status) +{ + emit setStatusBarText(status); + if ((status != i18n("Ready")) && (status != i18n("Playing"))) + { + m_xine->showOSDMessage(status, DEFAULT_OSD_DURATION); + } +} + +void XinePart::slotTrackPlaying() +{ + QString caption; + + kdDebug() << "XinePart: xine is playing" << endl; + m_pauseButton->setChecked(false); + QTimer::singleShot(100, this, SLOT(slotEnablePlayActions())); + + if ( m_xine->getURL()=="DVB" ) + { + caption = m_xine->getTitle(); + emit setWindowCaption(caption); + m_xine->showOSDMessage(caption, DEFAULT_OSD_DURATION); + return; + } + + /* fill current mrl with meta info */ + MRL mrl = m_playlist[m_current]; + + if (mrl.length().isNull()) /* no meta */ + { + if ((!m_xine->getTitle().isEmpty()) && (!m_xine->getTitle().contains('/')) + && (m_xine->getTitle().contains(QRegExp("\\w")) > 2) && (m_xine->getTitle().left(5).lower() != "track")) + mrl.setTitle(m_xine->getTitle()); + if ((mrl.artist().isEmpty()) && (!m_xine->getArtist().isEmpty())) + mrl.setArtist(m_xine->getArtist()); + if ((mrl.album().isEmpty()) && (!m_xine->getAlbum().isEmpty())) + mrl.setAlbum(m_xine->getAlbum()); + if ((mrl.year().isEmpty()) && (!m_xine->getYear().isEmpty())) + mrl.setYear(m_xine->getYear()); + if ((mrl.genre().isEmpty()) && (!m_xine->getGenre().isEmpty())) + mrl.setGenre(m_xine->getGenre()); + if ((mrl.comment().isEmpty()) && (!m_xine->getComment().isEmpty())) + mrl.setComment(m_xine->getComment()); + mrl.setLength(m_xine->getLength()); + m_playlist[m_current] = mrl; + } + /* if we don't have a playlist emit signalNewMeta() */ + if (mrl.url() == m_mrl.url()) + { + m_mrl = mrl; + emit signalNewMeta(m_mrl); + } + + caption = mrl.title(); + if (!mrl.artist().isEmpty()) + caption.append(QString(" (") + mrl.artist() + ")"); + emit setWindowCaption(caption); + m_xine->showOSDMessage(caption, DEFAULT_OSD_DURATION); + //emit signalNewFrameSize(m_xine->getVideoSize()); +} + +void XinePart::slotPlaybackFinished() +{ + if ((m_playlist.count() > 0) && (m_current < m_playlist.count()-1)) + { + slotNext(); + } + else + { + stateChanged("not_playing"); + emit signalTrackFinished(); + } +} + +void XinePart::slotNewLength() +{ + m_mrl.setLength(m_xine->getLength()); + emit signalNewMeta(m_mrl); +} + +void XinePart::slotNewTitle() +{ + m_mrl.setTitle(m_xine->getTitle()); + emit signalNewMeta(m_mrl); + emit setWindowCaption(m_mrl.title()); +} + +void XinePart::slotNewFrameSize() +{ + kdDebug() << "XinePart: got new frame size from xine" << endl; + emit signalNewFrameSize(m_xine->getVideoSize()); +} + +void XinePart::slotContextMenu(const QPoint& pos) +{ + if (factory()) + { + KPopupMenu *pop = (KPopupMenu*)factory()->container("context_menu", this); + if (pop) + pop->popup(pos); + } + else + { + if (m_embeddedContext) + m_embeddedContext->popup(pos); + } +} + +void XinePart::slotDVDMenuLeft() +{ + if (m_xine) + m_xine->slotDVDMenuLeft(); +} + +void XinePart::slotDVDMenuRight() +{ + if (m_xine) + m_xine->slotDVDMenuRight(); +} + +void XinePart::slotDVDMenuUp() +{ + if (m_xine) + m_xine->slotDVDMenuUp(); +} + +void XinePart::slotDVDMenuDown() +{ + if (m_xine) + m_xine->slotDVDMenuDown(); +} + +void XinePart::slotDVDMenuSelect() +{ + if (m_xine) + m_xine->slotDVDMenuSelect(); +} + +void XinePart::slotInfo() +{ + MRL mrl; + if ( m_xine->getURL()=="DVB" ) + mrl=MRL( QString("DVB"), m_xine->getTitle() ); + else + { + if ((m_mrl.isEmpty()) || (m_xine->getTitle().isNull())) + return; + mrl = m_playlist[m_current]; + } + + QString info; + QTextStream ts(&info, IO_WriteOnly); + ts << "<qt><table width=\"90%\">"; + ts << "<tr><td colspan=\"2\"><center><b>" << mrl.title() << "</b></center></td></tr>"; + if (!mrl.artist().isNull()) + ts << "<tr><td><b>" << i18n("Artist") << ":</b></td><td> " << mrl.artist() << "</td></tr>"; + if (!mrl.album().isNull()) + ts << "<tr><td><b>" << i18n("Album") << ":</b></td><td> " << mrl.album() << "</td></tr>"; + if (!mrl.track().isNull()) + ts << "<tr><td><b>" << i18n("Track") << ":</b></td><td> " << mrl.track() << "</td></tr>"; + if (!mrl.year().isNull()) + ts << "<tr><td><b>" << i18n("Year") << ":</b></td><td> " << mrl.year() << "</td></tr>"; + if (!mrl.genre().isNull()) + ts << "<tr><td><b>" << i18n("Genre") << ":</b></td><td> " << mrl.genre() << "</td></tr>"; + if (!(mrl.length().isNull())) + ts << "<tr><td><b>" << i18n("Length") << ":</b></td><td> " << mrl.length().toString("h:mm:ss") << "</td></tr>"; + + ts << "<br>"; + ts << "<tr><td><b>" << i18n("Mime") << ":</b></td><td> " << mrl.mime() << "</td></tr>"; + if (m_xine->hasAudio()) + ts << "<tr><td><b>" << i18n("Audio") << ":</b></td><td> " << m_xine->getAudioCodec() << " " << QString::number(m_xine->getAudioBitrate()/1000) + << "kb/s</td></tr>"; + if (m_xine->hasVideo()) + ts << "<tr><td><b>" << i18n("Video") << ":</b></td><td> " << m_xine->getVideoCodec() << " " << m_xine->getVideoSize().width() << "x" + << m_xine->getVideoSize().height() << "(" << m_xine->getVideoWidth() << "x" << m_xine->getVideoHeight() << ")"<< "</td></tr>"; + + ts << "<br>"; + if (m_xine->hasSubtitleURL()) + ts << "<tr><td><b>" << i18n("Subtitle File") << ":</b></td><td> " << m_xine->getSubtitleURL() << "</td></tr>"; + if (m_xine->hasSaveURL()) + ts << "<tr><td><b>" << i18n("Save Stream as") << ":</b></td><td> " << m_xine->getSaveURL() << "</td></tr>"; + + ts << "<tr><td></td><td></td></tr>"; // added for better layout + ts << "</table></qt>"; + KMessageBox::information(0, info, i18n("Track info") ); +} + +void XinePart::slotFinalize() +{ + if (factory()) + { + KToolBar *pos = (KToolBar*)factory()->container("positionToolBar", this); + if (pos) + { + // pos->alignItemRight(pos->idAt(1), true); //align time widget right + pos->setItemAutoSized(pos->idAt(0), true); //set position slider to maximum width + } + else + kdWarning("Position toolbar not found"); + } + else + { + kdDebug() << "XinePart: no xmlguifactory, will create a simple context menu..." << endl; + KAction* action = NULL; + m_embeddedContext = new KPopupMenu(0); + m_embeddedContext->insertTitle(instance()->iconLoader()->loadIcon("kaffeine", KIcon::Small), i18n("Kaffeine Player")); + actionCollection()->action("player_play")->plug(m_embeddedContext); + actionCollection()->action("player_pause")->plug(m_embeddedContext); + actionCollection()->action("player_stop")->plug(m_embeddedContext); + actionCollection()->action("volume_increase")->plug(m_embeddedContext); + actionCollection()->action("volume_decrease")->plug(m_embeddedContext); + actionCollection()->action("audio_mute")->plug(m_embeddedContext); + m_embeddedContext->insertSeparator(); + actionCollection()->action("player_track_info")->plug(m_embeddedContext); + m_embeddedContext->insertSeparator(); + actionCollection()->action("file_save_screenshot")->plug(m_embeddedContext); + actionCollection()->action("file_save_stream")->plug(m_embeddedContext); + m_embeddedContext->insertSeparator(); + action = new KAction(i18n("Copy URL to Clipboard"), "editcopy", 0, this, SLOT(slotCopyToClipboard()), actionCollection(), "copy_to_clipboard"); + action->plug(m_embeddedContext); + action = new KAction(i18n("Play in Kaffeine Externally"), "gear", 0, this, SLOT(slotLaunchExternally()), actionCollection(), "play_externally"); + action->plug(m_embeddedContext); + } + + QStringList visuals = m_xine->getVisualPlugins(); + visuals.prepend("none"); + m_audioVisual->setItems(visuals); + + loadConfig(); + QTimer::singleShot(0, this, SLOT(slotEnableAllActions())); +} + +void XinePart::slotCopyToClipboard() +{ + kdDebug() << "XinePart: Send URL to klipper: " << m_mrl.url() << endl; + DCOPClient* client = KApplication::dcopClient(); + if (!client->send("klipper", "klipper", "setClipboardContents(QString)", m_mrl.url())) + kdError() << "Can't send current URL to klipper" << endl; +} + +void XinePart::slotLaunchExternally() +{ + slotStop(); + + QTimer::singleShot(1000, this, SLOT(slotLaunchDelayed())); +} + +void XinePart::slotLaunchDelayed() +{ + kdDebug() << "XinePart: Start Kaffeine with argument: " << m_mrl.url() << endl; + KProcess process; + process << "kaffeine" << m_mrl.url(); + kdDebug() << "XinePart: Launching Kaffeine externaly..." << endl; + process.start(KProcess::DontCare); + process.detach(); +} + +void XinePart::initActions() +{ + KAction* action = NULL; + /* file menu */ + m_broadcastSend = new KToggleAction(i18n("&Send Broadcast Stream..."), 0, 0, this, SLOT(slotToggleBroadcastSend()), actionCollection(), "network_send"); + new KAction(i18n("&Receive Broadcast Stream..."), "network", 0, this, SLOT(slotBroadcastReceive()), actionCollection(), "network_receive"); + new KAction(i18n("&Save Screenshot..."), "frame_image", CTRL|Key_S, this, SLOT(slotScreenshot()), actionCollection(), "file_save_screenshot"); + action = new KAction(i18n("Save Stream..."), "player_record", Key_R, this, SLOT(slotSaveStream()), actionCollection(), "file_save_stream"); + action->setWhatsThis(i18n("Saves current stream to harddisc. This feature was disabled for some formats (e.g. Real Media) to prevent potential legal problems.")); + + /* player menu */ + + new KAction(i18n("Toggle Minimal Mode"), 0, 0, this, SIGNAL(signalToggleMinimalMode()), actionCollection(), "player_minimal_mode"); + + new KAction(i18n("Play"), "player_play", 0, this, SLOT(slotPlay()), actionCollection(), "player_play"); + m_pauseButton = new KToggleAction(i18n("Pause"), "player_pause", Key_Space, this, SLOT(slotTogglePause()), actionCollection(), "player_pause"); + new KAction(i18n("&Next"), "player_end", Key_PageDown, this, SLOT(slotNext()), actionCollection(), "player_next"); + new KAction(i18n("&Previous"), "player_start", Key_PageUp, this, SLOT(slotPrevious()), actionCollection(), "player_previous"); + new KAction(i18n("Stop"), "player_stop", Key_Backspace, this, SLOT(slotStop()), actionCollection(), "player_stop"); + + new KAction(i18n("&Fast Forward"), "player_fwd", ALT|Key_Right, this, SLOT(slotFastForward()), actionCollection(), "player_ff"); + new KAction(i18n("Slow &Motion"), 0, ALT|Key_Left, this, SLOT(slotSlowMotion()), actionCollection(), "player_slowmotion"); + + new KAction(i18n("Skip Forward (20s)"), NULL, Key_Right, this, SLOT(slotPosPlusSmall()), actionCollection(), "player_posplus_small"); + new KAction(i18n("Skip Backward (20s)"), NULL, Key_Left, this, SLOT(slotPosMinusSmall()), actionCollection(), "player_posminus_small"); + new KAction(i18n("Skip Forward (1m)"), NULL, CTRL|Key_PageUp, this, SLOT(slotPosPlusMedium()), actionCollection(), "player_posplus_medium"); + new KAction(i18n("Skip Backward (1m)"), NULL, CTRL|Key_PageDown, this, SLOT(slotPosMinusMedium()), actionCollection(), "player_posminus_medium"); + new KAction(i18n("Skip Forward (10m)"), NULL, ALT|Key_PageUp, this, SLOT(slotPosPlusLarge()), actionCollection(), "player_posplus_large"); + new KAction(i18n("Skip Backward (10m)"), NULL, ALT|Key_PageDown, this, SLOT(slotPosMinusLarge()), actionCollection(), "player_posminus_large"); + new KAction(i18n("Jump to Position..."), "goto", CTRL|Key_J, this, SLOT(slotJumpToPosition()), actionCollection(), "player_jump_to"); + + new KAction(i18n("DVD Menu Left"), 0, CTRL|Key_Left, this, SLOT(slotDVDMenuLeft()), actionCollection(), "dvdmenuleft"); + new KAction(i18n("DVD Menu Right"), 0, CTRL|Key_Right, this, SLOT(slotDVDMenuRight()), actionCollection(), "dvdmenuright"); + new KAction(i18n("DVD Menu Up"), 0, CTRL|Key_Up, this, SLOT(slotDVDMenuUp()), actionCollection(), "dvdmenuup"); + new KAction(i18n("DVD Menu Down"), 0, CTRL|Key_Down, this, SLOT(slotDVDMenuDown()), actionCollection(), "dvdmenudown"); + new KAction(i18n("DVD Menu Select"), 0, CTRL|Key_Return, this, SLOT(slotDVDMenuSelect()), actionCollection(), "dvdmenuselect"); + + m_audioChannels = new KSelectAction(i18n("Audio Channel"), 0, actionCollection(), "audio_channels"); + m_audioChannels->setToolTip(i18n("Select audio channel")); + m_audioChannels->setComboWidth( 50 ); + connect(m_audioChannels, SIGNAL(activated(int)), this, SLOT(slotSetAudioChannel(int ))); + new KAction(i18n("&Next Audio Channel"), 0, 0, this, SLOT(slotNextAudioChannel()), actionCollection(), "next_audio_channels"); + m_audioVisual = new KSelectAction(i18n("Audio &Visualization"), 0, actionCollection(), "audio_visualization"); + connect(m_audioVisual, SIGNAL(activated(const QString&)), m_xine, SLOT(slotSetVisualPlugin(const QString&))); + new KAction(i18n("&Mute"), "player_mute", Key_U, this, SLOT(slotMute()), actionCollection(), "audio_mute"); + new KAction(i18n("Volume Up"), NULL, Key_Plus, this, SLOT(slotVolumeUp()), actionCollection(), "volume_increase"); + new KAction(i18n("Volume Down"), NULL, Key_Minus, this, SLOT(slotVolumeDown()), actionCollection(), "volume_decrease"); + + m_deinterlaceEnabled = new KToggleAction(i18n("&Deinterlace"), 0, Key_I, m_xine, SLOT(slotToggleDeinterlace()), actionCollection(), "video_deinterlace"); + m_deinterlaceEnabled->setWhatsThis(i18n("Activate this for interlaced streams, some DVD's for example.")); + new KAction(i18n("&Auto"), "viewmagfit", Key_F5, m_xine, SLOT(slotAspectRatioAuto()), actionCollection(), "aspect_auto"); + new KAction(i18n("&4:3"), "viewmagfit", Key_F6, m_xine, SLOT(slotAspectRatio4_3()), actionCollection(), "aspect_43"); + new KAction(i18n("A&namorphic"), "viewmagfit", Key_F7, m_xine, SLOT(slotAspectRatioAnamorphic()), actionCollection(), "aspect_anamorphic"); + new KAction(i18n("&DVB"), "viewmagfit", Key_F8, m_xine, SLOT(slotAspectRatioDVB()), actionCollection(), "aspect_dvb"); + new KAction(i18n("&Square"), "viewmagfit", Key_F9, m_xine, SLOT(slotAspectRatioSquare()), actionCollection(), "aspect_square"); + KStdAction::zoomIn(m_xine, SLOT(slotZoomIn()), actionCollection(), "zoom_in"); + KStdAction::zoomOut(m_xine, SLOT(slotZoomOut()), actionCollection(), "zoom_out"); + KStdAction::fitToPage(m_xine, SLOT(slotZoomOff()), actionCollection(), "zoom_off"); + new KAction(i18n("Zoom In Horizontal"), NULL, CTRL|Key_H, m_xine, SLOT(slotZoomInX()), actionCollection(), "zoom_in_x"); + new KAction(i18n("Zoom Out Horizontal"), NULL, CTRL|SHIFT|Key_H, m_xine, SLOT(slotZoomOutX()), actionCollection(), "zoom_out_x"); + new KAction(i18n("Zoom In Vertical"), NULL, CTRL|Key_V, m_xine, SLOT(slotZoomInY()), actionCollection(), "zoom_in_y"); + new KAction(i18n("Zoom Out Vertical"), NULL, CTRL|SHIFT|Key_V, m_xine, SLOT(slotZoomOutY()), actionCollection(), "zoom_out_y"); + new KAction(i18n("Deinterlace &Quality"), "blend", CTRL|Key_I, this, SLOT(slotDeinterlaceQuality()), actionCollection(), "video_deinterlace_quality"); + new KAction(i18n("&Video Settings"), "configure", Key_V, this, SLOT(slotPictureSettings()), actionCollection(), "video_picture"); + new KAction(i18n("&Equalizer"), NULL, Key_E, this, SLOT(slotEqualizer()), actionCollection(), "equalizer"); + + + m_subtitles = new KSelectAction(i18n("Subtitle"), 0, actionCollection(), "player_subtitles"); + m_subtitles->setToolTip(i18n("Select Subtitle")); + m_subtitles->setComboWidth( 50 ); + connect(m_subtitles, SIGNAL(activated(int)), this, SLOT(slotSetSubtitle(int))); + new KAction(i18n("&Next Subtitle Channel"), 0, 0, this, SLOT(slotNextSubtitleChannel()), actionCollection(), "next_player_subtitles"); + new KAction(i18n("Delay Subtitle"), 0, CTRL|ALT|Key_Left, this, SLOT(slotDelaySubTitle()), actionCollection(), "adv_sub"); + new KAction(i18n("Advance Subtitle"), 0, CTRL|ALT|Key_Right, this, SLOT(slotAdvanceSubTitle()), actionCollection(), "delay_sub"); + new KAction(i18n("Add subtitle..."), 0, 0, this, SLOT(slotAddSubtitle()), actionCollection(), "add_subtitle"); + + new KAction(i18n("&Menu Toggle"), "view_detailed", Key_D, m_xine, SLOT(slotMenuToggle()), actionCollection(), "dvd_toggle"); + new KAction(i18n("&Title"), NULL, 0, m_xine, SLOT(slotMenuTitle()), actionCollection(), "dvd_title"); + new KAction(i18n("&Root"), NULL, 0, m_xine, SLOT(slotMenuRoot()), actionCollection(), "dvd_root"); + new KAction(i18n("&Subpicture"), NULL, 0, m_xine, SLOT(slotMenuSubpicture()), actionCollection(), "dvd_subpicture"); + new KAction(i18n("&Audio"), NULL, 0, m_xine, SLOT(slotMenuAudio()), actionCollection(), "dvd_audio"); + new KAction(i18n("An&gle"), NULL, 0, m_xine, SLOT(slotMenuAngle()), actionCollection(), "dvd_angle"); + new KAction(i18n("&Part"), NULL, 0, m_xine, SLOT(slotMenuPart()), actionCollection(), "dvd_part"); + + m_dvdTitles = new KSelectAction(i18n("Titles"), 0, actionCollection(), "dvd_title_menu"); + connect(m_dvdTitles, SIGNAL(activated(const QString&)), this, SLOT(slotSetDVDTitle(const QString&))); + m_dvdChapters = new KSelectAction(i18n("Chapters"), 0, actionCollection(), "dvd_chapter_menu"); + connect(m_dvdChapters, SIGNAL(activated(const QString&)), this, SLOT(slotSetDVDChapter(const QString&))); + m_dvdAngles = new KSelectAction(i18n("Angles"), 0, actionCollection(), "dvd_angle_menu"); + connect(m_dvdAngles, SIGNAL(activated(const QString&)), this, SLOT(slotSetDVDAngle(const QString&))); + + new KAction(i18n("Track &Info"), "info", 0 , this, SLOT(slotInfo()), actionCollection(), "player_track_info"); + new KAction(i18n("Effect &Plugins..."), "filter", Key_X, this, SLOT(slotFilterDialog()), actionCollection(), "player_post_filters"); + + /* settings menu */ + new KAction(i18n("&xine Engine Parameters"), "edit", 0, this, SLOT(slotConfigXine()), actionCollection(), "settings_xine_parameter"); + + m_volume = new VolumeSlider(); + QToolTip::add + (m_volume, i18n("Volume")); + m_volume->setRange(0, 100); + m_volume->setSteps(1, 10); + m_volume->setFocusPolicy(QWidget::NoFocus); + m_volume->setFixedWidth(75); + connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(slotVolumeChanged(int))); + connect(m_xine, SIGNAL(signalSyncVolume()), this, SLOT(slotSyncVolume())); + new KWidgetAction(m_volume, i18n("Volume"), 0, 0, 0, actionCollection(), "audio_volume"); + + m_position = new PositionSlider(Horizontal); + QToolTip::add + (m_position, i18n("Position")); + m_position->setRange(0, 65535); + m_position->setSteps(100, 1000); + m_position->setTracking(false); + m_position->setFocusPolicy(QWidget::NoFocus); + m_position->setMinimumWidth(180); + connect(m_position, SIGNAL(sliderMoved(int)), m_xine, SLOT(slotSeekToPosition(int))); + connect(m_position, SIGNAL(sliderLastMove(int)), m_xine, SLOT(slotSeekToPositionBlocking(int))); + connect(m_position, SIGNAL(signalStartSeeking()), m_xine, SLOT(slotStartSeeking())); + connect(m_position, SIGNAL(signalStopSeeking()), m_xine, SLOT(slotStopSeeking())); + new KWidgetAction(m_position, i18n("Position"), 0, 0, 0, actionCollection(), "player_position"); + + m_playTime = new QPushButton(0); + QToolTip::add + (m_playTime, i18n("Short click: Toggle Timer Forward/Backward\nLong click: Toggle Timer OSD")); + QFontMetrics met(KGlobalSettings::generalFont()); + m_playTime->setFixedWidth(met.width("-55:55:55") + 6); + m_playTime->setSizePolicy(QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + m_playTime->setFocusPolicy(QWidget::NoFocus); + new KWidgetAction(m_playTime, i18n("Playtime"), 0, 0, 0, actionCollection(), "player_playtime"); + connect(m_playTime, SIGNAL(pressed()), this, SLOT(slotButtonTimerPressed())); + connect(m_playTime, SIGNAL(released()), this, SLOT(slotButtonTimerReleased())); + m_playTime->setText("0:00:00"); + + m_equalizer = new Equalizer(); + connect(m_equalizer, SIGNAL(signalNewEq30(int)), m_xine, SLOT(slotSetEq30(int))); + connect(m_equalizer, SIGNAL(signalNewEq60(int)), m_xine, SLOT(slotSetEq60(int))); + connect(m_equalizer, SIGNAL(signalNewEq125(int)), m_xine, SLOT(slotSetEq125(int))); + connect(m_equalizer, SIGNAL(signalNewEq250(int)), m_xine, SLOT(slotSetEq250(int))); + connect(m_equalizer, SIGNAL(signalNewEq500(int)), m_xine, SLOT(slotSetEq500(int))); + connect(m_equalizer, SIGNAL(signalNewEq1k(int)), m_xine, SLOT(slotSetEq1k(int))); + connect(m_equalizer, SIGNAL(signalNewEq2k(int)), m_xine, SLOT(slotSetEq2k(int))); + connect(m_equalizer, SIGNAL(signalNewEq4k(int)), m_xine, SLOT(slotSetEq4k(int))); + connect(m_equalizer, SIGNAL(signalNewEq8k(int)), m_xine, SLOT(slotSetEq8k(int))); + connect(m_equalizer, SIGNAL(signalNewEq16k(int)), m_xine, SLOT(slotSetEq16k(int))); + connect(m_equalizer, SIGNAL(signalSetVolumeGain(bool)), m_xine, SLOT(slotSetVolumeGain(bool))); + +} + +void XinePart::initConnections() +{ + connect(&m_posCheckTimer, SIGNAL(timeout()), this, SLOT(slotCheckMoved())); + connect(&m_osdTimerEnabler, SIGNAL(timeout()), this, SLOT(slotToggleOsdTimer())); + connect(m_xine, SIGNAL(signalXineReady()), this, SLOT(slotFinalize())); + connect(m_xine, SIGNAL(signalNewChannels(const QStringList&, const QStringList&, int, int )), + this, SLOT(slotChannelInfo(const QStringList&, const QStringList&, int, int ))); + connect(m_xine, SIGNAL(signalXinePlaying()), this, SLOT(slotTrackPlaying())); + connect(m_xine, SIGNAL(signalNewPosition(int, const QTime&)), this, SLOT(slotNewPosition(int, const QTime&))); + connect(m_xine, SIGNAL(signalXineStatus(const QString&)), this, SLOT(slotStatus(const QString&))); + connect(m_xine, SIGNAL(signalXineError(const QString&)), this, SLOT(slotError(const QString&))); + connect(m_xine, SIGNAL(signalXineMessage(const QString&)), this, SLOT(slotMessage(const QString&))); + connect(m_xine, SIGNAL(signalPlaybackFinished()), this, SLOT(slotPlaybackFinished())); + connect(m_xine, SIGNAL(signalTitleChanged()), this, SLOT(slotNewTitle())); + connect(m_xine, SIGNAL(signalLengthChanged()), this, SLOT(slotNewLength())); + connect(m_xine, SIGNAL(signalVideoSizeChanged()), this, SLOT(slotNewFrameSize())); + connect(m_xine, SIGNAL(signalRightClick(const QPoint&)), this, SLOT(slotContextMenu(const QPoint&))); +} + +void XinePart::loadConfig() +{ + kdDebug() << "XinePart: load config" << endl; + + KConfig* config = instance()->config(); + + config->setGroup("General Options"); + if (m_xine->SoftwareMixing()) + { + int vol = config->readNumEntry("Volume", 70); + slotSetVolume(vol); + } + else + slotSyncVolume(); + m_timerDirection = config->readNumEntry("Timer Direction", FORWARD_TIMER); + m_isOsdTimer = config->readBoolEntry("Osd Timer", false); + + config->setGroup("Visualization"); + QString visual = config->readEntry("Visual Plugin", "goom"); + m_audioVisual->setCurrentItem(m_audioVisual->items().findIndex(visual)); + m_xine->slotSetVisualPlugin(visual); + + config->setGroup("Deinterlace"); + m_lastDeinterlaceQuality = config->readNumEntry("Quality Level", 4); + m_lastDeinterlacerConfig = config->readEntry("Config String", DEFAULT_TVTIME_CONFIG); + DeinterlacerConfigDialog* deinterlacerConfigDialog = new DeinterlacerConfigDialog(); + m_xine->createDeinterlacePlugin(m_lastDeinterlacerConfig, deinterlacerConfigDialog->getMainWidget()); + m_deinterlacerConfigWidget = (QWidget*)deinterlacerConfigDialog; + bool deinterlaceEnabled = config->readBoolEntry("Enabled", true); + if (deinterlaceEnabled) + { + m_deinterlaceEnabled->setChecked(deinterlaceEnabled); + m_xine->slotToggleDeinterlace(); + } + + config->setGroup("Broadcasting Options"); + m_broadcastPort = config->readNumEntry("Port", 8080); + m_broadcastAddress = config->readEntry("Master Address", "localhost"); + + config->setGroup( "Video Settings" ); + slotSetHue(config->readNumEntry( "Hue", -1)); + slotSetSaturation(config->readNumEntry( "Saturation", -1)); + slotSetContrast(config->readNumEntry( "Contrast", -1)); + slotSetBrightness(config->readNumEntry( "Brigthness", -1)); + + m_equalizer->ReadValues(config); +} + +void XinePart::saveConfig() +{ + if (!m_audioVisual->items().count()) // no config loaded + return; + + kdDebug() << "XinePart: save config" << endl; + + KConfig* config = instance()->config(); + + config->setGroup("General Options"); + config->writeEntry("Volume", m_volume->value()); + config->writeEntry("Timer Direction", m_timerDirection); + config->writeEntry("Osd Timer", m_isOsdTimer); + + config->setGroup("Visualization"); + config->writeEntry("Visual Plugin", m_audioVisual->currentText()); + + config->setGroup("Deinterlace"); + config->writeEntry("Quality Level", m_lastDeinterlaceQuality); + config->writeEntry("Config String", m_lastDeinterlacerConfig); + config->writeEntry("Enabled", m_deinterlaceEnabled->isChecked()); + + config->setGroup("Broadcasting Options"); + config->writeEntry("Port", m_broadcastPort); + config->writeEntry("Master Address", m_broadcastAddress); + + config->setGroup( "Video Settings" ); + config->writeEntry( "Hue", m_hue ); + config->writeEntry( "Saturation", m_saturation ); + config->writeEntry( "Contrast", m_contrast ); + config->writeEntry( "Brigthness", m_brightness ); + + m_equalizer->SaveValues(config); +} + +/* check if shell was moved, send new global position + of the part to xine */ +void XinePart::slotCheckMoved() +{ + QPoint newPos = m_xine->mapToGlobal(QPoint(0,0)); + if (newPos != m_oldPosition) + { + m_xine->globalPosChanged(); + m_oldPosition = newPos; + } +} + +bool XinePart::isPlaying() +{ + return m_xine->isPlaying(); +} + +bool XinePart::isPaused() +{ + return (m_xine->getSpeed() == KXineWidget::Pause); +} + +#if 0 +void XinePart::audiocdMRLS(MRL::List& mrls, bool& ok, bool& supported, const QString& device) +{ + if (!m_xine->isXineReady()) + { + if (!m_xine->initXine()) + { + supported = false; + return; + } + } + supported = true; + + if (!device.isNull()) + m_xine->slotSetAudiocdDevice(device); + + QStringList list; + if (!m_xine->getAutoplayPluginURLS("CD", list)) + { + ok = false; + return; + } + + MRL mrl; + /* use xine to connect to CDDB */ + xine_stream_t* xineStreamForMeta = xine_stream_new((xine_t*)m_xine->getXineEngine(), NULL, NULL); + + KProgressDialog* progress = new KProgressDialog(0, "cddbprogress", QString::null, i18n("Looking for CDDB entries...")); + progress->progressBar()->setTotalSteps(list.count()); + progress->show(); + QString title; + bool cddb = true; + for (uint i = 0; i < list.count(); i++) + { + mrl = MRL(list[i]); + mrl.setTitle(i18n("AudioCD Track %1").arg(i+1)); + mrl.setTrack(QString::number(i+1)); + if (xine_open(xineStreamForMeta, QFile::encodeName(mrl.url()))) + { + if (cddb) + { + title = QString::fromUtf8(xine_get_meta_info(xineStreamForMeta, XINE_META_INFO_TITLE)); + if ((!title.isNull()) && (!title.isEmpty()) ) //no meta info + { + mrl.setTitle(title); + mrl.setArtist(QString::fromUtf8(xine_get_meta_info(xineStreamForMeta, XINE_META_INFO_ARTIST))); + mrl.setAlbum(QString::fromUtf8(xine_get_meta_info(xineStreamForMeta, XINE_META_INFO_ALBUM))); + mrl.setYear(QString::fromUtf8(xine_get_meta_info(xineStreamForMeta, XINE_META_INFO_YEAR))); + mrl.setGenre(QString::fromUtf8(xine_get_meta_info(xineStreamForMeta, XINE_META_INFO_GENRE))); + mrl.setTrack(QString::number(i+1)); + } + else + cddb = false; + } + + int pos, time, len; + int t = 0, ret = 0; + while(((ret = xine_get_pos_length(xineStreamForMeta, &pos, &time, &len)) == 0) && (++t < 5)) + xine_usec_sleep(100000); + if ( ( ret != 0 ) && (len > 0) ) + mrl.setLength(QTime().addMSecs(len)); + + xine_close( xineStreamForMeta ); + } + + mrl.setMime("audio/cd"); + mrls.append(mrl); + if (progress->wasCancelled()) + break; + progress->progressBar()->setProgress(i+1); + KApplication::kApplication()->processEvents(); + } + + xine_dispose(xineStreamForMeta); + delete progress; + if (mrls.count()) + ok = true; +} + +void XinePart::vcdMRLS(MRL::List& mrls, bool& ok, bool& supported, const QString& device) +{ + if (!m_xine->isXineReady()) + { + if (!m_xine->initXine()) + { + supported = false; + return; + } + } + supported = true; + + if (!device.isNull()) + m_xine->slotSetVcdDevice(device); + + QStringList list; + if (!m_xine->getAutoplayPluginURLS("VCD", list)) + { + if (!m_xine->getAutoplayPluginURLS("VCDO", list)) + { + ok = false; + return; + } + } + + MRL mrl; + for (uint i = 0; i < list.count(); i++) + { + mrl = MRL(list[i]); + mrl.setMime("video/vcd"); + mrl.setTrack(QString::number(i+1)); + mrl.setTitle(i18n("VCD Track %1").arg(i+1)); + mrls.append(mrl); + } + if (mrls.count()) + ok = true; +} + +void XinePart::dvdMRLS(MRL::List& mrls, bool& ok, bool& supported, const QString& device) +{ + if (!m_xine->isXineReady()) + { + if (!m_xine->initXine()) + { + supported = false; + return; + } + } + supported = true; + + if (!device.isNull()) + m_xine->slotSetDvdDevice(device); + + QStringList list; + if (!m_xine->getAutoplayPluginURLS("DVD", list)) + { + ok = false; + return; + } + + MRL mrl; + for (uint i = 0; i < list.count(); i++) + { + mrl = MRL(list[i]); + mrl.setMime("video/dvd"); + mrl.setTitle("DVD"); + mrl.setTrack(QString::number(i+1)); + mrls.append(mrl); + } + if (mrls.count()) + ok = true; +} +#endif + +bool XinePart::hasChapters() +{ + if (m_xine->isXineReady()) + return m_xine->hasChapters(); + else + return false; +} + +bool XinePart::hasVideo() +{ + return m_xine->hasVideo(); +} + +void XinePart::playNextChapter() +{ + if (m_xine->isXineReady()) + m_xine->playNextChapter(); +} + +void XinePart::playPreviousChapter() +{ + if (m_xine->isXineReady()) + m_xine->playPreviousChapter(); +} + +void XinePart::slotPrepareForFullscreen(bool fullscreen) +{ + if (fullscreen) + m_xine->startMouseHideTimer(); + else + m_xine->stopMouseHideTimer(); +} + +uint XinePart::volume() const +{ + if (!m_xine->isXineReady()) + return 0; + + return m_xine->getVolume(); +} + +uint XinePart::position() const +{ + if (!m_xine->isXineReady()) + return 0; + + if ( m_xine->isPlaying() ) + return currentPosition; + else + return 0; +} + +void XinePart::slotSetVolume(uint vol) +{ + if (!m_xine->isXineReady()) + return; + + kdDebug() << "Set volume to: " << vol << endl; + m_volume->setValue(vol); +} + +void XinePart::slotVolumeChanged(int vol) +{ + m_xine->slotSetVolume(vol); +} + +void XinePart::slotSyncVolume() +{ + if (!m_xine->isXineReady()) + return; + + uint vol = volume(); + slotSetVolume(vol); +} + +void XinePart::slotSetPosition(uint pos) +{ + if (!m_xine->isXineReady()) + return; + + m_xine->slotSeekToPosition((int)(pos * 655.35)); +} + +QString XinePart::supportedExtensions() +{ + if (!m_xine->isXineReady()) + return QString::null; + + QString ext = m_xine->getSupportedExtensions(); + ext = ext.remove("txt"); + ext = "*." + ext; + ext.append(" smil"); + ext = ext.replace( ' ', " *." ); + ext = ext + " " + ext.upper(); + + return ext; +} + +void* XinePart::engine() +{ + if (!m_xine->isXineReady()) + return NULL; + + return (void*)m_xine->getXineEngine(); +} + +void XinePart::slotDisableAllActions() +{ + stateChanged("xine_not_ready"); +} + +void XinePart::slotEnableAllActions() +{ + stateChanged("xine_not_ready", StateReverse); + stateChanged("not_playing"); +} + +void XinePart::slotEnablePlayActions() +{ + if ((m_playlist.count() > 1) || (m_xine->hasChapters())) // we need next/previous buttons + stateChanged("play_multiple_tracks"); + else + stateChanged("play_single_track"); +} + +void XinePart::slotNextAudioChannel() +{ + nextAudioChannel(); +} + +void XinePart::slotNextSubtitleChannel() +{ + nextSubtitleChannel(); +} + +/********* DCOP INTERFACE *********/ + +void XinePart::nextAudioChannel() +{ + int num = m_audioChannels->items().count(); + int index = m_audioChannels->currentItem()+1; + if ( index>=num ) + index = 0; + m_audioChannels->setCurrentItem( index ); + slotSetAudioChannel( index ); +} + +void XinePart::nextSubtitleChannel() +{ + int num = m_subtitles->items().count(); + int index = m_subtitles->currentItem()+1; + if ( index>=num ) + index = 0; + m_subtitles->setCurrentItem( index ); + slotSetSubtitle( index ); +} + +int XinePart::getContrast() +{ + int hue, sat, contrast, bright, avOffset, spuOffset; + if (!m_xine->isXineReady()) + return -1; + m_xine->getVideoSettings(hue, sat, contrast, bright, avOffset, spuOffset); + return contrast; +} + +void XinePart::setContrast(int c) +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotSetContrast(c); +} + +int XinePart::getBrightness() +{ + int hue, sat, contrast, bright, avOffset, spuOffset; + if (!m_xine->isXineReady()) + return -1; + m_xine->getVideoSettings(hue, sat, contrast, bright, avOffset, spuOffset); + return bright; +} + +void XinePart::setBrightness(int b) +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotSetBrightness(b); +} + +void XinePart::dvdMenuUp() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotDVDMenuUp(); +} + +void XinePart::dvdMenuDown() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotDVDMenuDown(); +} + +void XinePart::dvdMenuLeft() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotDVDMenuLeft(); +} + +void XinePart::dvdMenuRight() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotDVDMenuRight(); +} + +void XinePart::dvdMenuSelect() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotDVDMenuSelect(); +} + +void XinePart::dvdMenuToggle() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotMenuToggle(); +} + +void XinePart::aspectRatioAuto() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotAspectRatioAuto(); +} + +void XinePart::aspectRatio4_3() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotAspectRatio4_3(); +} + +void XinePart::aspectRatioAnamorphic() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotAspectRatioAnamorphic(); +} + +void XinePart::aspectRatioSquare() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotAspectRatioSquare(); +} + +void XinePart::aspectRatioDVB() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotAspectRatioDVB(); +} + +void XinePart::zoomInX() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotZoomInX(); +} + +void XinePart::zoomOutX() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotZoomOutX(); +} + +void XinePart::zoomInY() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotZoomInY(); +} + +void XinePart::zoomOutY() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotZoomOutY(); +} + +void XinePart::zoomIn() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotZoomIn(); +} + +void XinePart::zoomOut() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotZoomOut(); +} + +void XinePart::zoomOff() +{ + if (!m_xine->isXineReady()) + return; + m_xine->slotZoomOff(); +} + + + +/********** volume slider ****************/ + + +VolumeSlider::VolumeSlider() : QSlider(Horizontal, 0) +{ + installEventFilter(this); +} + +VolumeSlider::~VolumeSlider() +{} + +void VolumeSlider::wheelEvent(QWheelEvent* e) +{ + int newVal = value(); + if (e->delta() > 0) + newVal -= 5; + else if (e->delta() < 0) + newVal += 5; + setValue(newVal); + e->accept(); +} + +/*bool VolumeSlider::eventFilter(QObject *obj, QEvent *ev) +{ + if( obj == this && (ev->type() == QEvent::MouseButtonPress || + ev->type() == QEvent::MouseButtonDblClick) ) + { + QMouseEvent *e = (QMouseEvent *)ev; + QRect r = sliderRect(); + + if( r.contains( e->pos() ) || e->button() != LeftButton ) + return FALSE; + + int range = maxValue() - minValue(); + int pos = (orientation() == Horizontal) ? e->pos().x() : e->pos().y(); + int maxpos = (orientation() == Horizontal) ? width() : height(); + int value = pos * range / maxpos + minValue(); + + if (QApplication::reverseLayout()) + value = maxValue() - (value - minValue()); + + setValue(value); + return TRUE; + } + else + { + return FALSE; + } +}*/ + +#include "xine_part.moc" diff --git a/kaffeine/src/player-parts/xine-part/xine_part.desktop b/kaffeine/src/player-parts/xine-part/xine_part.desktop new file mode 100644 index 0000000..ce9f2ab --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/xine_part.desktop @@ -0,0 +1,43 @@ +[Desktop Entry] +Encoding=UTF-8 +Icon=kaffeine +MimeType=application/x-ogg;video/x-matroska;audio/x-matroska;video/mpeg;video/msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-avi;video/x-fli;video/x-flic;video/x-ms-asf;video/x-ms-wmv;video/x-msvideo;application/x-mplayer2;application/smil;application/x-kaffeine;audio/x-musepack; +Name=Kaffeine-Xine +Name[nb]=Kaffeine Xine +Name[pa]=ਕੈਫੀਨ-ਜ਼ਾਇਨ +Name[se]=Kaffeine Xine +Name[xx]=xxKaffeine-Xinexx +Comment=A Kaffeine engine based on xine. +Comment[af]='n Kaffeine enjin wat op Xine gebaseer is. +Comment[bg]=Ядро за Kaffeine, базирано на xine. +Comment[ca]=Una part de reproductor basada en xine. +Comment[cs]=Část přehrávače založená na xine. +Comment[da]=En Kaffeine-grænseflade baseret på Xine. +Comment[de]=Ein auf Xine basierendes Programmmodul. +Comment[el]=Μία μηχανή του Kaffeine βασισμένη στο xine. +Comment[et]=Kaffeine mootor xine põhjal. +Comment[ga]=Inneall Kaffeine bunaithe ar xine. +Comment[gl]=Un motor para Kaffeine baseado en Xine. +Comment[hu]=Xine-alapú Kaffeine-alrendszer. +Comment[it]=Un motore di Kaffeine basato su xine. +Comment[ja]=xine に基づく Kaffeine エンジン +Comment[lt]=Kaffeine variklis, paremtas Xine. +Comment[nb]=En spillermotor basert på xine. +Comment[nl]=Een Kaffeine-engine gebaseerd op xine. +Comment[pa]=ਜ਼ਾਈਨ ਅਧਾਰਤ ਇੱਕ ਕੈਫ਼ੀਨ ਇੰਜਣ ਹੈ। +Comment[pl]=Moduł odtwarzacza do Kaffeine bazujący na xine. +Comment[pt]=Um motor Kaffeine no xine. +Comment[pt_BR]=Um componente de reprodução Kaffeine baseado no xine. +Comment[se]=Čuojanmohtor vuođđoduvvon xineáis. +Comment[sr]=Кафеинов мотор заснован на Xine-у. +Comment[sr@Latn]=Кафеинов мотор заснован на Xine-у. +Comment[sv]=En Kaffeine-gränssnitt baserad på Xine. +Comment[tg]=Барномаи Kaffeine дар асоси xine. +Comment[th]=โปรแกรมประมวลผลของ Kaffeine โดยใช้ xine +Comment[tr]=xine tabanlı bir Kaffeine motoru. +Comment[uk]=Рушій Kaffeine, оснований на xine. +Comment[xx]=xxA Kaffeine engine based on xine.xx +ServiceTypes=KParts/ReadOnlyPart,KaffeinePart +Type=Service +X-KDE-Library=libxinepart +InitialPreference=9 diff --git a/kaffeine/src/player-parts/xine-part/xine_part.h b/kaffeine/src/player-parts/xine-part/xine_part.h new file mode 100644 index 0000000..71769d3 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/xine_part.h @@ -0,0 +1,276 @@ +/* + * xine_part.h + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XINEPART_H +#define XINEPART_H + + +#include <kparts/factory.h> + +#include <qtimer.h> + +#include "kaffeinepart.h" +#include "xine_part_iface.h" + +#define FORWARD_TIMER 0 +#define BACKWARD_TIMER 1 + +class QWidget; +class QSlider; +class QLabel; +class QPushButton; +class MRL; +class KXineWidget; +class QPoint; +class KSelectAction; +class KToggleAction; +class Equalizer; +class VideoSettings; +class FilterDialog; +class PositionSlider; +class KProgressDialog; +class KPopupMenu; + +/** + * Kaffeine Part - xine based player part + * @author Jürgen Kofler <kaffeine@gmx.net> + * + */ +class XinePart : public KaffeinePart, public XinePartIface +{ + Q_OBJECT +public: + XinePart(QWidget*, const char*, QObject*, const char*, const QStringList&); + virtual ~XinePart(); + + /* + *Reimplemented from KaffeinePart + */ + bool isPlaying(); + bool isPaused(); + + bool hasChapters(); /* e.g. DVD */ + void playNextChapter(); + void playPreviousChapter(); + void setDVDChapter(uint chapter); + + bool hasVideo(); + QString supportedExtensions(); + void* engine(); + uint volume() const; /* percent */ + uint position() const; /* percent */ + + bool closeURL(); /* stops playback and shows kaffeine logo */ + static KAboutData* createAboutData(); + + /* + * DCOP functions... + */ + int getContrast(); + void setContrast(int c); + int getBrightness(); + void setBrightness(int b); + void dvdMenuUp(); + void dvdMenuDown(); + void dvdMenuLeft(); + void dvdMenuRight(); + void dvdMenuSelect(); + void dvdMenuToggle(); + void aspectRatioAuto(); + void aspectRatio4_3(); + void aspectRatioAnamorphic(); + void aspectRatioSquare(); + void aspectRatioDVB(); + void zoomInX(); + void zoomOutX(); + void zoomInY(); + void zoomOutY(); + void zoomIn(); + void zoomOut(); + void zoomOff(); + QString screenShot(); + void nextAudioChannel(); + void nextSubtitleChannel(); + void speedFaster(); + void speedSlower(); + +public slots: + /* + * Reimplemented from KaffeinePart + */ + bool openURL(const MRL& mrl); + void slotPrepareForFullscreen(bool); + void slotPlay(bool forcePlay=false); + void slotTogglePause(bool pauseLive=true); + void slotSetVolume(uint); /* percent */ + void slotSetPosition(uint); /* percent */ + void slotPosPlusSmall(); + void slotPosMinusSmall(); + void slotSyncVolume(); + void slotStop(); + void slotMute(); /* toggle mute */ + + void slotVolumeUp(); + void slotVolumeDown(); + void slotPosPlusMedium(); + void slotPosMinusMedium(); + void slotPosPlusLarge(); + void slotPosMinusLarge(); + void slotJumpIncrement(int); + void slotDelaySubTitle(); + void slotAdvanceSubTitle(); + void slotAddSubtitle(); + void slotNextAudioChannel(); + void slotNextSubtitleChannel(); + /***************** Private ********************/ + +private slots: + void slotFinalize(); + void slotTrackPlaying(); + void slotCheckMoved(); + void slotNext(); + void slotPrevious(); + void slotSaveStream(); + void slotChannelInfo(const QStringList&, const QStringList&, int, int); + void slotSetSubtitle(int); + void slotSetAudioChannel(int); + void slotNewPosition(int, const QTime&); + void slotVolumeChanged(int); + void slotPictureSettings(); + void slotEqualizer(); + void slotDeinterlaceQuality(); + void slotFilterDialog(); + void slotInfo(); + void slotToggleBroadcastSend(); + void slotBroadcastReceive(); + void slotJumpToPosition(); + void slotButtonTimerPressed(); + void slotButtonTimerReleased(); + void slotToggleOsdTimer(); + void slotScreenshot(); + void slotConfigXine(); + void slotError(const QString&); + void slotMessage(const QString&); + void slotStatus(const QString&); + void slotNewTitle(); + void slotNewLength(); + void slotNewFrameSize(); + void slotPlaybackFinished(); + void slotContextMenu(const QPoint&); + void slotDisableAllActions(); + void slotEnableAllActions(); + void slotEnablePlayActions(); + void slotCopyToClipboard(); + void slotLaunchExternally(); + void slotLaunchDelayed(); + void slotFastForward(); + void slotSlowMotion(); + void slotSetDVDTitle(const QString&); + void slotSetDVDChapter(const QString&); + void slotSetDVDAngle(const QString&); + void slotDVDMenuLeft(); + void slotDVDMenuRight(); + void slotDVDMenuUp(); + void slotDVDMenuDown(); + void slotDVDMenuSelect(); + void slotSetHue(int); + void slotSetSaturation(int); + void slotSetContrast(int); + void slotSetBrightness(int); + +private: + void initActions(); + void initConnections(); + void loadConfig(); + void saveConfig(); + +private: + QPoint m_oldPosition; + QTimer m_posCheckTimer; + QTimer m_osdTimerEnabler; /* Provide Long click on timer button */ + bool m_isOsdTimer; /* Status of Osd Timer (on/off) */ + int m_timerDirection; /* Counting Up or Down */ + int m_brightness, m_hue, m_contrast, m_saturation; + + MRL m_mrl; + QValueList<MRL> m_playlist; + uint m_current; + uint m_lastDeinterlaceQuality; + QString m_lastDeinterlacerConfig; + uint m_broadcastPort; + QString m_broadcastAddress; + + QSlider* m_volume; + PositionSlider* m_position; + QPushButton* m_playTime; + uint currentPosition; + + KSelectAction* m_audioChannels; + KSelectAction* m_audioVisual; + KSelectAction* m_subtitles; + KSelectAction* m_dvdChapters; + KSelectAction* m_dvdTitles; + KSelectAction* m_dvdAngles; + KToggleAction* m_deinterlaceEnabled; + KToggleAction* m_broadcastSend; + KToggleAction* m_pauseButton; + + KXineWidget* m_xine; + VideoSettings* m_pictureSettings; + Equalizer* m_equalizer; + QWidget* m_deinterlacerConfigWidget; + FilterDialog* m_filterDialog; + + KPopupMenu* m_embeddedContext; + + /* dvb */ +public: + QString TimeShiftFilename; + +public slots: + void slotDvbOpen( const QString &filename, const QString &chanName, int haveVideo ); + void getTimeShiftFilename( const QString &filename ); + void requestForOSD( const QString &text, int duration, int priority ); + void setDvbCurrentNext( const QString &channelname, const QStringList &list ); + +signals: + void stopDvb(); + void playerPause(); + void dvbOSDHide(); +}; + + +#include <qslider.h> + +class VolumeSlider : public QSlider +{ + +public: + + VolumeSlider(); + ~VolumeSlider(); + +protected: + + void wheelEvent(QWheelEvent* e); + //bool eventFilter(QObject *obj, QEvent *ev); +}; + +#endif /* XINEPART_H */ diff --git a/kaffeine/src/player-parts/xine-part/xine_part.rc b/kaffeine/src/player-parts/xine-part/xine_part.rc new file mode 100644 index 0000000..8d4c1a7 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/xine_part.rc @@ -0,0 +1,347 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="xine_part" version="21"> +<MenuBar> + <Menu name="file"> + <Menu name="network_broadcasting"><text>&Network Broadcasting</text> + <Action name="network_send"/> + <Action name="network_receive"/> + </Menu> + <Separator/> + <Action name="file_save_stream"/> + <Action name="file_save_screenshot"/> + </Menu> + <Menu name="player"><text>&Player</text> + <Action name="player_play"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> + <Action name="player_previous"/> + <Separator/> + <Action name="player_ff"/> + <Action name="player_slowmotion"/> + <Separator/> + <Action name="player_jump_to"/> + <Menu name="navigation"><text>&Navigation</text> + <Action name="player_posplus_small"/> + <Action name="player_posminus_small"/> + <Action name="player_posplus_medium"/> + <Action name="player_posminus_medium"/> + <Action name="player_posplus_large"/> + <Action name="player_posminus_large"/> + </Menu> + <Separator/> + <Menu name="dvd"><text>&DVD</text> + <Menu name="dvdnav"><text>&Navigation</text> + <Action name="dvdmenuleft"/> + <Action name="dvdmenuright"/> + <Action name="dvdmenuup"/> + <Action name="dvdmenudown"/> + <Action name="dvdmenuselect"/> + </Menu> + <Menu name="dvdmenus"><text>&DVD Menus</text> + <Action name="dvd_toggle"/> + <Separator/> + <Action name="dvd_title"/> + <Action name="dvd_root"/> + <Action name="dvd_subpicture"/> + <Action name="dvd_audio"/> + <Action name="dvd_angle"/> + <Action name="dvd_part"/> + </Menu> + <Action name="dvd_title_menu"/> + <Action name="dvd_chapter_menu"/> + <Action name="dvd_angle_menu"/> + </Menu> + <Separator/> + <Menu name="audio"><text>&Audio</text> + <Action name="audio_channels"/> + <Action name="next_audio_channels"/> + <Action name="audio_visualization"/> + <Action name="equalizer"/> + <Action name="audio_mute"/> + <Action name="volume_increase"/> + <Action name="volume_decrease"/> + </Menu> + <Menu name="video"><text>&Video</text> + <Action name="video_deinterlace"/> + <Menu name="video_aspect"><text>&Aspect Ratio</text> + <Action name="aspect_auto"/> + <Action name="aspect_43"/> + <Action name="aspect_anamorphic"/> + <Action name="aspect_dvb"/> + <Action name="aspect_square"/> + </Menu> + <Menu name="video_zoom"><text>&Zoom</text> + <Action name="zoom_off"/> + <Action name="zoom_in"/> + <Action name="zoom_out"/> + <Action name="zoom_in_x"/> + <Action name="zoom_out_x"/> + <Action name="zoom_in_y"/> + <Action name="zoom_out_y"/> + </Menu> + <Separator/> + <Action name="video_deinterlace_quality"/> + <Action name="video_picture"/> + </Menu> + <Menu name="subtitles"><text>&Subtitles</text> + <Action name="adv_sub"/> + <Action name="delay_sub"/> + <Action name="player_subtitles"/> + <Action name="next_player_subtitles"/> + <Action name="add_subtitle"/> + </Menu> + <Separator/> + <Action name="player_track_info"/> + <Action name="player_post_filters"/> + </Menu> + <Menu name="settings"> + <Action name="settings_xine_parameter"/> + </Menu> +</MenuBar> + +<ToolBar name="snapshotToolBar" hidden="true"><text>Screenshot Toolbar</text> + <Action name="file_save_screenshot"/> +</ToolBar> +<ToolBar name="controlsToolBar" position="Bottom"><text>Controls Toolbar</text> + <Action name="player_previous"/> + <Action name="player_play"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> + <Action name="audio_channels"/> + <Action name="player_subtitles"/> +</ToolBar> +<ToolBar name="volumeToolBar" position="Bottom"><text>Volume Toolbar</text> + <Action name="audio_volume"/> + <Action name="audio_mute"/> +</ToolBar> +<ToolBar name="positionToolBar" fullWidth="true" position="Bottom"><text>Position Toolbar</text> + <Action name="player_position"/> + <Separator/> + <Action name="player_playtime"/> +</ToolBar> + +<Menu name="context_menu"> + <Action name="player_minimal_mode"/> + <Separator/> + <Action name="player_play"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> + <Action name="player_previous"/> + <Action name="file_save_stream"/> + <Separator/> + <Action name="player_ff"/> + <Action name="player_slowmotion"/> + <Separator/> + <Action name="player_jump_to"/> + <Menu name="navigation"><text>&Navigation</text> + <Action name="player_posplus_small"/> + <Action name="player_posminus_small"/> + <Action name="player_posplus_medium"/> + <Action name="player_posminus_medium"/> + <Action name="player_posplus_large"/> + <Action name="player_posminus_large"/> + </Menu> + <Separator/> + <Menu name="dvd"><text>&DVD</text> + <Menu name="dvdmenus"><text>&DVD Menus</text> + <Action name="dvd_toggle"/> + <Separator/> + <Action name="dvd_title"/> + <Action name="dvd_root"/> + <Action name="dvd_subpicture"/> + <Action name="dvd_audio"/> + <Action name="dvd_angle"/> + <Action name="dvd_part"/> + </Menu> + <Action name="dvd_title_menu"/> + <Action name="dvd_chapter_menu"/> + <Action name="dvd_angle_menu"/> + </Menu> + <Separator/> + <Action name="audio_channels"/> + <Action name="audio_visualization"/> + <Action name="audio_mute"/> + <Separator/> + <Menu name="subtitles"><text>&Subtitles</text> + <Action name="adv_sub"/> + <Action name="delay_sub"/> + <Action name="player_subtitles"/> + <Action name="add_subtitle"/> + </Menu> + <Separator/> + <Action name="player_track_info"/> +</Menu> + +<State name="xine_not_ready"> + <enable> + <Action name="player_play"/> + </enable> + <disable> + <Action name="network_send"/> + <Action name="file_save_stream"/> + <Action name="file_save_screenshot"/> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> + <Action name="player_previous"/> + <Action name="player_ff"/> + <Action name="player_slowmotion"/> + <Action name="audio_channels"/> + <Action name="audio_visualization"/> + <Action name="audio_volume"/> + <Action name="audio_mute"/> + <Action name="player_position"/> + <Action name="player_posplus_small"/> + <Action name="player_posminus_small"/> + <Action name="player_posplus_medium"/> + <Action name="player_posminus_medium"/> + <Action name="player_posplus_large"/> + <Action name="player_posminus_large"/> + <Action name="player_jump_to"/> + <Action name="player_playtime"/> + <Action name="aspect_auto"/> + <Action name="aspect_43"/> + <Action name="aspect_anamorphic"/> + <Action name="aspect_dvb"/> + <Action name="aspect_square"/> + <Action name="zoom_off"/> + <Action name="zoom_in"/> + <Action name="zoom_out"/> + <Action name="zoom_in_x"/> + <Action name="zoom_out_x"/> + <Action name="zoom_in_y"/> + <Action name="zoom_out_y"/> + <Action name="video_deinterlace"/> + <Action name="video_deinterlace_quality"/> + <Action name="video_picture"/> + <Action name="equalizer"/> + <Action name="player_subtitles"/> + <Action name="add_subtitle"/> + <Action name="dvd_toggle"/> + <Action name="dvd_title"/> + <Action name="dvd_root"/> + <Action name="dvd_subpicture"/> + <Action name="dvd_audio"/> + <Action name="dvd_angle"/> + <Action name="dvd_part"/> + <Action name="dvd_title_menu"/> + <Action name="dvd_chapter_menu"/> + <Action name="dvd_angle_menu"/> + <Action name="dvdmenuleft"/> + <Action name="dvdmenuright"/> + <Action name="dvdmenuup"/> + <Action name="dvdmenudown"/> + <Action name="dvdmenuselect"/> + <Action name="player_track_info"/> + <Action name="player_post_filters"/> + </disable> +</State> +<State name="not_playing"> + <enable> + <Action name="player_play"/> + <Action name="player_next"/> + <Action name="player_previous"/> + </enable> + <disable> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_ff"/> + <Action name="player_slowmotion"/> + <Action name="player_position"/> + <Action name="player_posplus_small"/> + <Action name="player_posminus_small"/> + <Action name="player_posplus_medium"/> + <Action name="player_posminus_medium"/> + <Action name="player_posplus_large"/> + <Action name="player_posminus_large"/> + <Action name="player_jump_to"/> + <Action name="player_playtime"/> + <Action name="dvd_toggle"/> + <Action name="dvd_title"/> + <Action name="dvd_root"/> + <Action name="dvd_subpicture"/> + <Action name="dvd_audio"/> + <Action name="dvd_angle"/> + <Action name="dvd_part"/> + <Action name="dvd_title_menu"/> + <Action name="dvd_chapter_menu"/> + <Action name="dvd_angle_menu"/> + <Action name="dvdmenuleft"/> + <Action name="dvdmenuright"/> + <Action name="dvdmenuup"/> + <Action name="dvdmenudown"/> + <Action name="dvdmenuselect"/> + </disable> +</State> +<State name="play_single_track"> + <enable> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_ff"/> + <Action name="player_slowmotion"/> + <Action name="player_position"/> + <Action name="player_posplus_small"/> + <Action name="player_posminus_small"/> + <Action name="player_posplus_medium"/> + <Action name="player_posminus_medium"/> + <Action name="player_posplus_large"/> + <Action name="player_posminus_large"/> + <Action name="player_jump_to"/> + <Action name="player_playtime"/> + <Action name="player_next"/> + <Action name="player_previous"/> + </enable> + <disable> + <Action name="player_play"/> + </disable> +</State> +<State name="play_multiple_tracks"> + <enable> + <Action name="player_pause"/> + <Action name="player_stop"/> + <Action name="player_next"/> + <Action name="player_previous"/> + <Action name="player_ff"/> + <Action name="player_slowmotion"/> + <Action name="player_position"/> + <Action name="player_posplus_small"/> + <Action name="player_posminus_small"/> + <Action name="player_posplus_medium"/> + <Action name="player_posminus_medium"/> + <Action name="player_posplus_large"/> + <Action name="player_posminus_large"/> + <Action name="player_jump_to"/> + <Action name="player_playtime"/> + </enable> + <disable> + <Action name="player_play"/> + </disable> +</State> +<State name="paused"> + <enable> + <Action name="player_play"/> + </enable> +</State> +<State name="dvd_playback"> + <enable> + <Action name="dvd_toggle"/> + <Action name="dvd_title"/> + <Action name="dvd_root"/> + <Action name="dvd_subpicture"/> + <Action name="dvd_audio"/> + <Action name="dvd_angle"/> + <Action name="dvd_part"/> + <Action name="dvd_title_menu"/> + <Action name="dvd_chapter_menu"/> + <Action name="dvd_angle_menu"/> + <Action name="dvdmenuleft"/> + <Action name="dvdmenuright"/> + <Action name="dvdmenuup"/> + <Action name="dvdmenudown"/> + <Action name="dvdmenuselect"/> + </enable> +</State> +</kpartgui> diff --git a/kaffeine/src/player-parts/xine-part/xine_part_iface.h b/kaffeine/src/player-parts/xine-part/xine_part_iface.h new file mode 100644 index 0000000..4380a84 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/xine_part_iface.h @@ -0,0 +1,135 @@ +/* + * xine_part_iface.h + * + * Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XINE_PART_IFACE_H +#define XINE_PART_IFACE_H + +#include <dcopobject.h> + +class XinePartIface : virtual public DCOPObject +{ + K_DCOP +public: + +k_dcop: + /* + * Returns the contrast value (0 - 65535). + */ + virtual int getContrast() = 0; + + /* + * Set the contrast (0 - 65535). + */ + virtual void setContrast(int c) = 0; + + /* + * Returns the brightness value (0 - 65535). + */ + virtual int getBrightness() = 0; + + /* + * Set the brightness (0 - 65535). + */ + virtual void setBrightness(int b) = 0; + + /* + * Move the menu cursor upwards. + */ + virtual void dvdMenuUp() = 0; + + /* + * Move the menu cursor downwards. + */ + virtual void dvdMenuDown() = 0; + + /* + * Move the menu cursor to the left. + */ + virtual void dvdMenuLeft() = 0; + + /* + * Move the menu cursor to the right. + */ + virtual void dvdMenuRight() = 0; + + /* + * Select the menu item pointed by the cursor. + */ + virtual void dvdMenuSelect() = 0; + + /* + *Toggle DVD menu on/off + */ + virtual void dvdMenuToggle() = 0; + + /* + * Set the aspect ratio automatically. + */ + virtual void aspectRatioAuto() = 0; + + /* + * Set the aspect ratio to 4:3. + */ + virtual void aspectRatio4_3() = 0; + + /* + * Set the aspect ration to 16:9. + */ + virtual void aspectRatioAnamorphic() = 0; + + /* + * Set the aspect ratio to 1:1. + */ + virtual void aspectRatioSquare() = 0; + + /* + * Set the aspect ratio to 2.11:1. + */ + virtual void aspectRatioDVB() = 0; + + /* + * Zoom in. + */ + virtual void zoomIn() = 0; + + /* + * Zoom out. + */ + virtual void zoomOut() = 0; + + /* + * Zoom off. + */ + virtual void zoomOff() = 0; + + virtual void zoomInX() = 0; + virtual void zoomOutX() = 0; + virtual void zoomInY() = 0; + virtual void zoomOutY() = 0; + + virtual QString screenShot() = 0; + + virtual void nextAudioChannel() = 0; + virtual void nextSubtitleChannel() = 0; + virtual void speedFaster() = 0; + virtual void speedSlower() = 0; +}; + +#endif /* XINE_PART_IFACE_H */ diff --git a/kaffeine/src/player-parts/xine-part/xineconfig.cpp b/kaffeine/src/player-parts/xine-part/xineconfig.cpp new file mode 100644 index 0000000..55ec752 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/xineconfig.cpp @@ -0,0 +1,404 @@ +/* + * xineconfig.cpp - config dialog for xine parameters + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kglobal.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <kcombobox.h> +#include <kpushbutton.h> +#include <kdialogbase.h> +#include <klocale.h> +#include <kdebug.h> +#include <kseparator.h> + +#include <qcolor.h> +#include <qfont.h> +#include <qwidget.h> +#include <qlayout.h> +#include <qtabwidget.h> +#include <qvbox.h> +#include <qcheckbox.h> +#include <qspinbox.h> +#include <qptrlist.h> +#include <qstringlist.h> +#include <qlabel.h> +#include <qscrollview.h> +#include <qsignalmapper.h> + +#include "xineconfig.h" +#include "xineconfig.moc" + +#define NON_EXPERT_OPTIONS_DEPRECATED "audio.speaker_arrangement;audio.driver;audio.mixer_software;video.driver;dxr3.device_number;dxr3.enc_add_bars;dxr3.enc_alt_play_mode;input.dvd_language;input.dvd_region;input.cdda_device;input.cdda_use_cddb;input.drive_slowdown;input.dvd_device;input.vcd_device;input.http_no_proxy;input.http_proxy_host;input.http_proxy_password;input.http_proxy_port;input.http_proxy_user;codec.real_codecs_path;codec.win32_path;post.goom_fps;post.goom_height;post.goom_width;misc.spu_subtitle_size;misc.spu_vertical_offset;misc.spu_src_encoding;misc.sub_timeout;osd.osd_messages;vcd.default_device;" + +#define NON_EXPERT_OPTIONS_NEW "audio.output.speaker_arrangement;audio.driver;audio.mixer_software;video.driver;dxr3.device_number;dxr3.encoding.add_bars;dxr3.encoding.alt_play_mode;media.dvd.language;media.dvd.region;media.audio_cd.device;media.audio_cd.use_cddb;media.audio_cd.drive_slowdown;media.dvd.device;media.vcd.device;media.network.http_no_proxy;media.network.http_proxy_host;media.network.http_proxy_password;media.network.http_proxy_port;media.network.http_proxy_user;decoder.external.real_codecs_path;decoder.external.win32_codecs_path;effects.goom.csc_method;effects.goom.fps;effects.goom.height;effects.goom.width;subtitles.separate.subtitle_size;subtitles.separate.vertical_offset;subtitles.separate.src_encoding;subtitles.separate.timeout;media.vcd.device;osd.osd_messages;osd.osd_size" + +#define NON_EXPERT_OPTIONS NON_EXPERT_OPTIONS_NEW NON_EXPERT_OPTIONS_DEPRECATED + +XineConfigEntry::XineConfigEntry(QWidget* parent, QGridLayout* grid, int row, xine_cfg_entry_t* entry) : + m_valueChanged(false), m_key(QString(entry->key)), m_numValue(entry->num_value), + m_numDefault(entry->num_default), m_stringValue(entry->str_value), m_stringDefault(entry->str_default), + m_stringEdit(NULL), m_enumEdit(NULL), m_numEdit(NULL), m_boolEdit(NULL) +{ + switch (entry->type) + { + case XINE_CONFIG_TYPE_UNKNOWN: + break; + case XINE_CONFIG_TYPE_STRING: + { + m_stringEdit = new KLineEdit(entry->str_value, parent); + if (!strcmp(entry->str_value,entry->str_default)) + m_stringEdit->setPaletteForegroundColor(QColor(darkMagenta)); + else + m_stringEdit->setPaletteForegroundColor(QColor(black)); + grid->addWidget(m_stringEdit, row ,0); + connect(m_stringEdit, SIGNAL(textChanged(const QString&)), this, SLOT(slotStringChanged(const QString&))); + break; + } + case XINE_CONFIG_TYPE_ENUM: + { + m_enumEdit = new KComboBox(parent); + int i = 0; + while (entry->enum_values[i]) + { + m_enumEdit->insertItem(entry->enum_values[i]); + i++; + } + m_enumEdit->setCurrentItem(entry->num_value); + if (entry->num_value == entry->num_default) + m_enumEdit->setPaletteForegroundColor(QColor(darkMagenta)); + else + m_enumEdit->setPaletteForegroundColor(QColor(black)); + grid->addWidget(m_enumEdit, row, 0); + connect(m_enumEdit, SIGNAL(activated(int)), this, SLOT(slotNumChanged(int))); + break; + } + case XINE_CONFIG_TYPE_NUM: + { + m_numEdit = new QSpinBox(-999999, 999999, 1, parent); + m_numEdit->setValue(entry->num_value); + if (entry->num_value == entry->num_default) + m_numEdit->setPaletteForegroundColor(QColor(darkMagenta)); + else + m_numEdit->setPaletteForegroundColor(QColor(black)); + grid->addWidget(m_numEdit, row, 0); + connect(m_numEdit, SIGNAL(valueChanged(int)), this, SLOT(slotNumChanged(int))); + break; + } + case XINE_CONFIG_TYPE_RANGE: + { + m_numEdit = new QSpinBox(parent); + m_numEdit->setValue(entry->num_value); + m_numEdit->setRange(entry->range_min, entry->range_max); + if (entry->num_value == entry->num_default) + m_numEdit->setPaletteForegroundColor(QColor(darkMagenta)); + else + m_numEdit->setPaletteForegroundColor(QColor(black)); + grid->addWidget(m_numEdit, row, 0); + connect(m_numEdit, SIGNAL(valueChanged(int)), this, SLOT(slotNumChanged(int))); + break; + } + case XINE_CONFIG_TYPE_BOOL: + { + m_boolEdit = new QCheckBox(parent); + m_boolEdit->setChecked(entry->num_value); + if (entry->num_value == entry->num_default) + m_boolEdit->setPaletteForegroundColor(QColor(darkMagenta)); + else + m_boolEdit->setPaletteForegroundColor(QColor(black)); + grid->addWidget(m_boolEdit, row, 0); + connect(m_boolEdit, SIGNAL(toggled(bool)), this, SLOT(slotBoolChanged(bool))); + break; + } + } + + QString m_keyName(entry->key); + m_keyName.remove( 0, m_keyName.find(".") + 1 ); + + QLabel* description = new QLabel(m_keyName + "\n" + QString::fromLocal8Bit(entry->description), parent); + description->setAlignment( QLabel::WordBreak | QLabel::AlignVCenter ); + grid->addWidget(description, row, 1); + + KSeparator* separator = new KSeparator(KSeparator::Horizontal, parent); + grid->addMultiCellWidget(separator, row+1, row+1, 0, 1); +} + +XineConfigEntry::~XineConfigEntry() +{} + +void XineConfigEntry::slotNumChanged(int val) +{ + m_numValue = val; + m_valueChanged = true; + + if (m_numValue == m_numDefault) + { + if (m_numEdit) + { + m_numEdit->setPaletteForegroundColor(darkMagenta); + m_numEdit->update(); + } + else + { + m_enumEdit->setPaletteForegroundColor(darkMagenta); + m_enumEdit->update(); + } + } + else + { + if (m_numEdit) + { + m_numEdit->setPaletteForegroundColor(black); + m_numEdit->update(); + } + else + { + m_enumEdit->setPaletteForegroundColor(black); + m_enumEdit->update(); + } + } +} + +void XineConfigEntry::slotBoolChanged(bool val) +{ + m_numValue = val; + m_valueChanged = true; + if (m_numValue == m_numDefault) + { + m_boolEdit->setPaletteForegroundColor(QColor(darkMagenta)); + m_boolEdit->update(); + } + else + { + m_boolEdit->setPaletteForegroundColor(QColor(black)); + m_boolEdit->update(); + } +} + +void XineConfigEntry::slotStringChanged(const QString& val) +{ + m_stringValue = val; + m_valueChanged = true; + if (m_stringValue == m_stringDefault) + { + m_stringEdit->setPaletteForegroundColor(QColor(darkMagenta)); + m_stringEdit->update(); + } + else + { + m_stringEdit->setPaletteForegroundColor(QColor(black)); + m_stringEdit->update(); + } +} + +bool XineConfigEntry::valueChanged() const +{ + return m_valueChanged; +} + +const QString& XineConfigEntry::getKey() const +{ + return m_key; +} + +int XineConfigEntry::getNumValue() const +{ + return m_numValue; +} + +const QString& XineConfigEntry::getStringValue() const +{ + return m_stringValue; +} + + +/************** XINE CONFIG CLASS **************************/ + +XineConfig::XineConfig(const xine_t* const xine) : + KDialogBase(KDialogBase::IconList, i18n("xine Engine Parameters"), + KDialogBase::Ok|KDialogBase::Apply|KDialogBase::Cancel, KDialogBase::Cancel) + +{ + setInitialSize(QSize(650,500), true); + + m_xine = (xine_t*)xine; + + QStringList cats = getCategories(); + QTabWidget* tabWidget = NULL; + QFrame* xineFrame = NULL; + QVBoxLayout* xineLayout = NULL; + QVBox* xineBeginnerPage = NULL; + QVBox* xineExpertPage = NULL; + QString icon; + + QStringList::ConstIterator end ( cats.end()); + for (QStringList::ConstIterator it = cats.begin(); it != end; ++it) + { + // kdDebug() << "XineConfig: add page: " << *it << endl; + if (*it == "audio") + icon = "sound"; + else if (*it == "video") + icon = "video"; + else if (*it == "vcd") + icon = "cdrom_unmount"; + else if (*it == "input") + icon = "connect_established"; + else if (*it == "effects") + icon = "wizard"; + else if (*it == "media") + icon = "cdrom_unmount"; + else if (*it == "subtitles") + icon = "font_bitmap"; + else if (*it == "osd") + icon = "font_bitmap"; + else if (*it == "engine") + icon = "exec"; + else + icon = "edit"; + + xineFrame = addPage(*it, i18n("%1 Options").arg(*it), KGlobal::iconLoader()->loadIcon(icon, KIcon::Panel, + KIcon::SizeMedium)); + xineLayout = new QVBoxLayout(xineFrame, marginHint(), spacingHint()); + tabWidget = new QTabWidget(xineFrame); + xineLayout->addWidget(tabWidget); + xineBeginnerPage = new QVBox(tabWidget); + xineBeginnerPage->setMargin(5); + tabWidget->addTab(xineBeginnerPage, i18n("Beginner Options")); + createPage(*it, false, xineBeginnerPage); + xineExpertPage = new QVBox(tabWidget); + xineExpertPage->setMargin(5); + tabWidget->addTab(xineExpertPage, i18n("Expert Options")); + createPage(*it, true, xineExpertPage); + } + + connect(this, SIGNAL(okClicked()), SLOT(slotOkPressed())); + connect(this, SIGNAL(applyClicked()), SLOT(slotApplyPressed())); +} + +XineConfig::~XineConfig() +{ + m_entries.setAutoDelete(true); + m_entries.clear(); + kdDebug() << "XineConfig: destructed" << endl; +} + +void XineConfig::createPage(const QString& cat, bool expert, QWidget* parent) +{ + xine_cfg_entry_t* ent; + + QScrollView* sv = new QScrollView(parent); + sv->setResizePolicy(QScrollView::AutoOneFit); + parent = new QWidget(sv->viewport()); + sv->addChild(parent); + + QGridLayout* grid = new QGridLayout(parent, 20 ,2); + grid->setColStretch(1,8); + grid->setSpacing(10); + grid->setMargin(10); + + uint row = 0; + QString entCat; + + /*********** read in xine config entries ***********/ + ent = new xine_cfg_entry_t; + xine_config_get_first_entry(m_xine, ent); + + do + { + entCat = QString(ent->key); + entCat = entCat.left(entCat.find(".")); + if (entCat == cat) + { + if (((!expert) && (QString(NON_EXPERT_OPTIONS).contains(ent->key))) || + ((expert) && (!QString(NON_EXPERT_OPTIONS).contains(ent->key)))) + { + m_entries.append(new XineConfigEntry(parent, grid, row, ent)); + delete ent; + ent = new xine_cfg_entry_t; + row += 2; + } + } + } + while(xine_config_get_next_entry(m_xine, ent)); + + delete ent; +} + +const QStringList XineConfig::getCategories() +{ + QStringList cats; + xine_cfg_entry_t* ent = new xine_cfg_entry_t; + if (!xine_config_get_first_entry(m_xine, ent)) + return cats; + QString entCat; + + do + { + entCat = QString(ent->key); + entCat = entCat.left(entCat.find(".")); + if (cats.findIndex(entCat) == -1) + { + // kdDebug() << "XineConfig: new category: " << entCat << endl; + cats.append(entCat); + } + delete ent; + ent = new xine_cfg_entry_t; + + } + while(xine_config_get_next_entry(m_xine, ent)); + + delete ent; + return cats; +} + +void XineConfig::slotOkPressed() +{ + slotApplyPressed(); + QDialog::close(); +} + +/*************** apply changed entries *****************/ + +void XineConfig::slotApplyPressed() +{ + xine_cfg_entry_t *entry; + + + for(uint i = 0; i < m_entries.count(); i++) /* check all entries */ + { + if (m_entries.at(i)->valueChanged()) /* changed? */ + { + entry = new xine_cfg_entry_t; + if (xine_config_lookup_entry(m_xine, m_entries.at(i)->getKey().ascii(), entry)) + { + kdDebug() << "XineConfig: Apply: " << m_entries.at(i)->getKey() << "\n"; + + entry->num_value = m_entries.at(i)->getNumValue(); + + if (m_entries.at(i)->getStringValue().ascii()) + entry->str_value = (char*) (const char*)m_entries.at(i)->getStringValue().latin1(); + + xine_config_update_entry(m_xine, entry); + delete entry; + + m_entries.at(i)->setValueUnchanged(); + } + } + } +} diff --git a/kaffeine/src/player-parts/xine-part/xineconfig.h b/kaffeine/src/player-parts/xine-part/xineconfig.h new file mode 100644 index 0000000..c3c7532 --- /dev/null +++ b/kaffeine/src/player-parts/xine-part/xineconfig.h @@ -0,0 +1,96 @@ +/* + * xineconfig.h - config dialog for xine parameters + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XINECONFIG_H +#define XINECONFIG_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +class KLineEdit; +class KComboBox; +class QWidget; +class QHBox; +class QCheckBox; +class QSpinBox; +class QStringList; +class QGridLayout; + +#include <xine.h> + +/* stores a single config entry of the config file */ +class XineConfigEntry : public QHBox +{ + Q_OBJECT +public: + XineConfigEntry(QWidget* parent, QGridLayout* grid, int row, xine_cfg_entry_t* entry); + ~XineConfigEntry(); + + bool valueChanged() const; /* was the value changed by user? */ + const QString& getKey() const; /* the key (name) of the entry */ + int getNumValue() const; + const QString& getStringValue() const; + void setValueUnchanged() + { + m_valueChanged = false; + } + +private slots: + void slotNumChanged(int); + void slotBoolChanged(bool); + void slotStringChanged(const QString&); + +private: + bool m_valueChanged; + QString m_key; + int m_numValue; + int m_numDefault; + QString m_stringValue; + QString m_stringDefault; + + KLineEdit* m_stringEdit; + KComboBox* m_enumEdit; + QSpinBox* m_numEdit; + QCheckBox* m_boolEdit; +}; + + +class XineConfig : public KDialogBase +{ + Q_OBJECT +public: + XineConfig(const xine_t* const xine); + ~XineConfig(); + +private slots: + void slotOkPressed(); + void slotApplyPressed(); + +private: + void createPage(const QString& cat, bool expert, QWidget* parent); + const QStringList getCategories(); + +private: + QPtrList<XineConfigEntry> m_entries; + xine_t* m_xine; +}; + +#endif /* XINECONFIG_H */ |