You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kaffeine/kaffeine/src/player-parts/gstreamer-part/gstreamer_part.cpp

1093 lines
32 KiB

/*
* 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 <tdeparts/genericfactory.h>
#include <tdeaction.h>
#include <tdemessagebox.h>
#include <kstandarddirs.h>
#include <kxmlguifactory.h>
#include <tdetoolbar.h>
#include <tdeio/netaccess.h>
#include <kmimetype.h>
#include <tdepopupmenu.h>
#include <tqtimer.h>
#include <tqslider.h>
#include <tqfile.h>
#include <tqtooltip.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( TQWidget* parentWidget, const char* /*widgetName*/, TQObject* parent, const char* name,
const TQStringList& /*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(TQString()), m_posToolbar(NULL)
{
// we need an instance
setInstance( GStreamerPartFactory::instance() );
parentWidget->setPaletteBackgroundColor( TQColor(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, TQ_SIGNAL(signalNewFrameSize(const TQSize&)), this, TQ_SIGNAL(signalNewFrameSize(const TQSize&)) );
m_video->setFocusPolicy( TQWidget::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, TQ_SIGNAL(timeout()), this, TQ_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;
}
TDEAboutData *GStreamerPart::createAboutData()
{
TDEAboutData* aboutData = new TDEAboutData( "gstreamerpart", I18N_NOOP("GStreamerPart"),
"0.1", "GStreamer based player part for Kaffeine.",
TDEAboutData::License_GPL,
"(c) 2005, Jürgen Kofler.", 0 );
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 = (TDEToolBar*)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 = "";
TQString 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";
TQString localFile;
if ( TDEIO::NetAccess::download(m_mrl.kurl(), localFile, widget()) ) {
TQFile file( localFile );
file.open( IO_ReadOnly );
TQTextStream stream( &file );
TQString firstLine = stream.readLine();
TQString 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: " << TDEIO::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;
TQString 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 );
}
TQTimer::singleShot(0, this, TQ_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();
TQString subUrl = TQString();
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( TQString 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, TQString() );
}
}
void GStreamerPart::slotVolume( int vol )
{
if ( !m_play )
return;
emit setStatusBarText(i18n("Volume") + ": " + TQString::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") + ": " + TQString::number(sat) );
g_object_set( G_OBJECT(m_videosink), "saturation", sat, NULL );
}
void GStreamerPart::slotHue( int hue )
{
emit setStatusBarText( i18n("Hue") + ": " + TQString::number(hue) );
g_object_set( G_OBJECT(m_videosink), "hue", hue, NULL );
}
void GStreamerPart::slotContrast( int contrast )
{
emit setStatusBarText( i18n("Contrast") + ": " + TQString::number(contrast) );
g_object_set( G_OBJECT(m_videosink), "contrast", contrast, NULL );
}
void GStreamerPart::slotBrightness( int brightness )
{
emit setStatusBarText( i18n("Brightness") + ": " + TQString::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");
TQString caption = m_mrl.title();
if ( !m_mrl.artist().isEmpty() )
caption.append( TQString(" (") + 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, TQ_SIGNAL(signalNewBrightness(int)), this, TQ_SLOT(slotBrightness(int)) );
connect( m_videoSettings, TQ_SIGNAL(signalNewContrast(int)), this, TQ_SLOT(slotContrast(int)) );
connect( m_videoSettings, TQ_SIGNAL(signalNewHue(int)), this, TQ_SLOT(slotHue(int)) );
connect( m_videoSettings, TQ_SIGNAL(signalNewSaturation(int)), this, TQ_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()) {
TQTime length = TQTime().addMSecs(m_timer->getTotalTimeMS());
if (!length.isNull())
mrl.setLength(length);
}
m_playlist[m_current] = mrl;
TQString caption = mrl.title();
if (!mrl.artist().isEmpty())
caption.append(TQString(" (") + 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()
{
TQString info;
TQTextStream 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>";
TQTime length = TQTime().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 TQString& 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 TQPoint& pos )
{
if (factory()) {
TDEPopupMenu *pop = (TDEPopupMenu*)factory()->container("context_menu", this);
if ( pop )
pop->popup(pos);
}
}
void GStreamerPart::loadConfig()
{
kdDebug() << "GStreamerPart: Load config" << endl;
TDEConfig* 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;
TDEConfig* 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 TDEAction(i18n("Toggle Minimal Mode"), 0, 0, this, TQ_SIGNAL(signalToggleMinimalMode()), actionCollection(), "player_minimal_mode");
new TDEAction(i18n("Play"), "media-playback-start", 0, this, TQ_SLOT(slotPlay()), actionCollection(), "player_play");
new TDEAction(i18n("Pause"), "media-playback-pause", Key_Space, this, TQ_SLOT(slotTogglePause()), actionCollection(), "player_pause");
new TDEAction(i18n("Stop"), "media-playback-stop", Key_Backspace, this, TQ_SLOT(slotStop()), actionCollection(), "player_stop");
new TDEAction(i18n("&Next"), "media-skip-forward", Key_PageDown, this, TQ_SLOT(slotNext()), actionCollection(), "player_next");
new TDEAction(i18n("&Previous"), "media-skip-backward", Key_PageUp, this, TQ_SLOT(slotPrevious()), actionCollection(), "player_previous");
m_timer = new Timer();
new KWidgetAction(m_timer->getSlider(), i18n("Position"), 0, 0, 0, actionCollection(), "player_position");
new KWidgetAction((TQWidget*)m_timer->getLabel(), i18n("Playtime"), 0, 0, 0, actionCollection(), "player_playtime");
m_audioVisual = new TDESelectAction(i18n("Audio &Visualization"), 0, actionCollection(), "audio_visual");
connect(m_audioVisual, TQ_SIGNAL(activated(const TQString&)), this, TQ_SLOT(slotSetVisualPlugin(const TQString&)));
m_audioVisualPluginList.prepend("none");
m_audioVisual->setItems(m_audioVisualPluginList);
m_audioVisual->setCurrentItem(m_audioVisual->items().findIndex(m_visualPluginName));
new TDEAction(i18n("&Mute"), "player_mute", Key_U, this, TQ_SLOT(slotMute()), actionCollection(), "audio_mute");
new TDEAction(i18n("&Auto"), "zoom-fit-best", Key_F5, m_video, TQ_SLOT(slotAspectRatioAuto()), actionCollection(), "aspect_auto");
new TDEAction(i18n("&4:3"), "zoom-fit-best", Key_F6, m_video, TQ_SLOT(slotAspectRatio4_3()), actionCollection(), "aspect_43");
new TDEAction(i18n("A&namorphic"), "zoom-fit-best", Key_F7, m_video, TQ_SLOT(slotAspectRatioAnamorphic()), actionCollection(), "aspect_anamorphic");
new TDEAction(i18n("&DVB"), "zoom-fit-best", Key_F8, m_video, TQ_SLOT(slotAspectRatioDVB()), actionCollection(), "aspect_dvb");
new TDEAction(i18n("&Square"), "zoom-fit-best", Key_F9, m_video, TQ_SLOT(slotAspectRatioSquare()), actionCollection(), "aspect_square");
new TDEAction(i18n("&Video Settings"), "configure", Key_V, this, TQ_SLOT(slotVideoSettings()), actionCollection(), "video_settings");
new TDEAction(i18n("Track &Info"), "application-vnd.tde.info", 0 , this, TQ_SLOT(slotInfo()), actionCollection(), "player_track_info");
m_volume = new TQSlider(TQt::Horizontal, 0);
TQToolTip::add(m_volume, i18n("Volume"));
m_volume->setRange(0, 100);
m_volume->setSteps(1, 5);
m_volume->setFixedWidth(75);
connect(m_volume, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(slotVolume(int)));
slotSetVolume(m_savedVolume);
new KWidgetAction(m_volume, i18n("Volume"), 0, 0, 0, actionCollection(), "audio_volume");
new TDEAction(i18n("&GStreamer Engine Parameters"), "edit", 0, this, TQ_SLOT(slotConfigDialog()), actionCollection(), "settings_gst_parameter");
connect(m_video, TQ_SIGNAL(signalRightClick(const TQPoint&)), this, TQ_SLOT(slotContextMenu(const TQPoint&)));
}
//Change audio sink on the fly
void GStreamerPart::setAudioSink( TQString 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(
#if GST_CHECK_VERSION(1,0,0)
gst_registry_get (),
#else
gst_registry_get_default (),
#endif
GST_TYPE_ELEMENT_FACTORY);
TQString name, cat;
while ( factories ) {
#if GST_CHECK_VERSION(1,0,0)
name = GST_OBJECT_NAME(factories->data);
#else
name = GST_PLUGIN_FEATURE_NAME(factories->data);
#endif
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 TQString& trackUrl, const TQString& subtitleUrl )
{
if ( !m_play && !createPlaybin() )
return;
m_title = TQString();
m_artist = TQString();
m_album = TQString();
m_year = TQString();
m_genre = TQString();
m_track = TQString();
m_comment = TQString();
m_audioCodec = TQString();
m_videoCodec = TQString();
TQString 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() ) {
TQString 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 : " << TQTime::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;
TQTimer::singleShot( 0, this, TQ_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 = TQString::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();
}