PDA

View Full Version : QDBusConnection error: Invalid object path passed in arguments



jiveaxe
10th January 2014, 15:58
Hi, I added mpris support to my application. It works well in all tests except one: Now Playing plasmoid of KDE Plasma Workspace. Clicking on the seek widget to change playback position, Plasma Desktop crashes miserably. In console the following message appears:


process 2395: arguments to dbus_message_iter_append_basic() were incorrect, assertion "_dbus_check_is_valid_path (*string_p)" failed in file dbus-message.c line 2603.

Not very explicative, unfortunately. Not knowing how debug dbus I tried a different approach: I recompiled dbus commenting out the offensive line and fortunately I got a more friendly error message:


QDBusConnection: error: could not send message to service "org.mpris.MediaPlayer2.myapp" path "/org/mpris/MediaPlayer2" interface "org.mpris.MediaPlayer2.Player" member "SetPosition": Marshalling failed: Invalid object path passed in arguments

Then I used qdbusviewer to inspect all mpris methods of my application and all work fine except SetPosition, where it gives the error:


Unable to find method SetPosition on path /org/mpris/MediaPlayer2 in interface org.mpris.MediaPlayer2.Player

Here it is an excerpt of the code I'm using:


#include "MprisPlugin.h"
#include "MprisPluginRootAdaptor.h"
#include "MprisPluginPlayerAdaptor.h"

namespace mpris
{

const char* MprisPlugin::mprisObjectPath = "/org/mpris/MediaPlayer2";
const char* MprisPlugin::serviceName = "org.mpris.MediaPlayer2.myapp";
const char* MprisPlugin::freedesktopPath = "org.freedesktop.DBus.Properties";

static QString s_mpInfoIdentifier = QString( "MPRISPLUGIN" );

MprisPlugin::MprisPlugin(QObject* parent) :
QObject(parent)
{
// DBus connection
new MprisPluginRootAdaptor( this );
new MprisPluginPlayerAdaptor( this );
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.registerObject( mprisObjectPath, this );
dbus.registerService( serviceName );

// Listen to volume changes
connect( The::audioEngine(), SIGNAL( volumeChanged( int ) ),
SLOT( onVolumeChanged( int ) ) );

// When a track is added or removed, CanGoNext updated signal is sent
if ( !The::playlist() )
{
connect( The::playlist(), SIGNAL( itemCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) );
}

// Connect to AudioEngine's seeked signal
connect( The::audioEngine(), SIGNAL( seeked( qint64, bool ) ),
SLOT( onSeeked( qint64, bool ) ) );

connect( The::audioEngine(), SIGNAL( stateChanged(Phonon::State, Phonon::State ) ),
SLOT( engineStateChanged(Phonon::State, Phonon::State ) ) );

connect( The::audioEngine(), SIGNAL( sourceChanged() ),
SLOT( trackChanged() ) );

connect( The::audioEngine(), SIGNAL( seekableChanged(bool) ),
SLOT( seekableChanged(bool) ) );

connect( The::audioEngine(), SIGNAL( trackLengthChanged(qint64) ),
SLOT( trackLengthChanged(qint64) ) );
}

void MprisPlugin::notifyPropertyChanged( const QString& interface, const QString& propertyName )
{
QDBusMessage signal = QDBusMessage::createSignal(
mprisObjectPath,
freedesktopPath,
"PropertiesChanged" );
signal << interface;
QVariantMap changedProps;
changedProps.insert(propertyName, property(propertyName.toLatin1()));
signal << changedProps;
signal << QStringList();
qDebug() << propertyName;
qDebug() << changedProps;
QDBusConnection::sessionBus().send(signal);
}

// ...

// org.mpris.MediaPlayer2.Player

// ...

qlonglong MprisPlugin::position() const
{
return (qlonglong) ( The::audioEngine()->currentTime() * 1000 );
}

// ...

void MprisPlugin::SetPosition( const QDBusObjectPath& TrackId, qlonglong Position )
{
if ( !canSeek() )
return;

if ( TrackId.path() != QString( "/track/" ) + QString::number( The::audioEngine()->currentTrackID() ) )
return;

if ( ( Position < 0) || ( Position > The::audioEngine()->currentTrackTotalTime() * 1000 ) )
return;

The::audioEngine()->seek( (qint64) (Position / 1000 ) );
}

// ...

void MprisPlugin::Seek( qlonglong Offset )
{
if ( !canSeek() )
return;

qlonglong seekTime = position() + Offset;
if ( seekTime < 0 )
The::audioEngine()->seek( 0 );
else if ( seekTime > The::audioEngine()->currentTrackTotalTime() * 1000 )
Next();
else
The::audioEngine()->seek( (qint64) ( seekTime / 1000 ) );

}

void MprisPlugin::onSeeked( qint64 ms, bool userSeek )
{
if ( userSeek )
emit Seeked( ms * 1000 );
}

void MprisPlugin::engineStateChanged(Phonon::State newState, Phonon::State oldState)
{
if(newState != Phonon::PlayingState && newState != Phonon::PausedState) {
m_lastMetadata= QVariantMap();
notifyPropertyChanged("org.mpris.MediaPlayer2.Player", "Metadata");
}

notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "CanGoNext" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "CanGoPrevious" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "CanPause" );
}

// ...

void MprisPlugin::trackChanged()
{
PlaylistItemPtr track = The::audioEngine()->currentTrack();
m_lastMetadata = QVariantMap();
m_lastMetadata.insert( "mpris:trackid", QString( "/track/" ) + QString::number( The::audioEngine()->currentTrackID() ) );
m_lastMetadata.insert( "mpris:length", static_cast<qlonglong>( The::audioEngine()->currentTrackTotalTime() ) * 1000 );
m_lastMetadata.insert( "xesam:album", track->albumName() );
m_lastMetadata.insert( "xesam:artist", QStringList( track->artistName() ) );
m_lastMetadata.insert( "xesam:title", track->songName() );

m_lastMetadata.insert( "mpris:artUrl", QString( QUrl::fromLocalFile( track->coverArtFilename() ).toEncoded() ) );

notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}

void MprisPlugin::seekableChanged( bool seekable )
{
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "CanSeek");
}

void MprisPlugin::trackLengthChanged( qint64 milliseconds )
{
if ( milliseconds >= 0 )
trackChanged();
}

} //mpris



Can you explain why just SetPosition does not work?

Very thanks

anda_skoa
10th January 2014, 16:15
Does the adator for that interface have that method?

Cheers,
_

jiveaxe
10th January 2014, 16:19
yep,

http://pastebin.kde.org/pfc6839a8

anda_skoa
13th January 2014, 09:20
That's the interface description. Have you checked the adaptor MprisPluginPlayerAdaptor?

Cheers,
_

jiveaxe
13th January 2014, 11:05
This is a cmake project so MprisPluginPlayerAdaptor is auto-generated. I'm using the following macros:


qt4_add_dbus_adaptor(myapp_SRCS
dbus/MprisPluginRootAdaptor.xml
dbus/MprisPlugin.h mpris::MprisPlugin MprisPluginRootAdaptor MprisPluginRootAdaptor)
qt4_add_dbus_adaptor(myapp_SRCS
dbus/MprisPluginPlayerAdaptor.xml
dbus/MprisPlugin.h mpris::MprisPlugin MprisPluginPlayerAdaptor MprisPluginPlayerAdaptor)


in resulting MprisPluginPlayerAdaptor I've searched for "position" and found this:


qlonglong MprisPluginPlayerAdaptor::position() const
{
// get the value of property Position
return qvariant_cast< qlonglong >(parent()->property("Position"));
}

// ...

void MprisPluginPlayerAdaptor::SetPosition(const QDBusObjectPath &TrackId, qlonglong Position)
{
// handle method call org.mpris.MediaPlayer2.Player.SetPosition
parent()->SetPosition(TrackId, Position);
}

So it seems that SetPosition is in place.

Thanks for your time

anda_skoa
13th January 2014, 11:39
Hmm, very strange.

Can you try to make the call using dbus-send? In contrast to qdbus or qdbusviewer it doesn't use introspection at runtime.
Alternatively you could try to generate interfaces in a small test program and use it to call your object's methods.

The thing with the assert is also puzzling.

Maybe try running the Plasma applet in plasma-windowed and attach a debugger to it and see what the stack looks like when the assert is triggered?

Cheers,
_

jiveaxe
25th January 2014, 10:12
Just a quick update: at the end I dropped my implementation and wrote adaptors from scratch (no more qt4_add_dbus_adaptor macro). Now it works.