File indexing completed on 2024-06-02 04:59:55

0001 /*
0002     This file belong to the KMPlayer project, a movie player plugin for Konqueror
0003     SPDX-FileCopyrightText: 2007 Koos Vriezen <koos.vriezen@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <sys/types.h>
0009 #include <unistd.h>
0010 
0011 #include "phononplayer.h"
0012 
0013 #include <QApplication>
0014 #include <QBoxLayout>
0015 #include <QDBusConnection>
0016 #include <QDBusMessage>
0017 #include <QMap>
0018 #include <QTimer>
0019 #include <QUrl>
0020 #include <QX11Info>
0021 
0022 #include <phonon/audiooutput.h>
0023 #include <phonon/mediaobject.h>
0024 #include <phonon/videoplayer.h>
0025 #include <phonon/videowidget.h>
0026 
0027 #include "agentadaptor.h"
0028 #include "streamagentadaptor.h"
0029 
0030 #include <xcb/xcb.h>
0031 
0032 static QString control_service;
0033 static QString control_path;
0034 static Agent *agent;
0035 typedef QMap <uint64_t, Stream *> AgentMap;
0036 static AgentMap agent_map;
0037 
0038 Agent::Agent ()
0039     : stay_alive_timer (0)
0040 {
0041     QString service = QString ("org.kde.kphononplayer-%1").arg (getpid ());
0042     (void) new AgentAdaptor (this);
0043     QDBusConnection::sessionBus().registerObject ("/phonon", this);
0044     if (QDBusConnection::sessionBus().interface()->registerService (service) ==
0045             QDBusConnectionInterface::ServiceNotRegistered) {
0046         qDebug ("%s", qPrintable(QDBusConnection::sessionBus().lastError().message()));
0047         service = QDBusConnection::sessionBus().baseService ();
0048     }
0049     qDebug ("register as %s", qPrintable (service));
0050     if (!control_service.isEmpty ()) {
0051         QDBusMessage msg = QDBusMessage::createMethodCall (
0052                 control_service, control_path, "org.kde.kmplayer.Master",
0053                 "running");
0054         msg << service;
0055         QDBusConnection::sessionBus().send (msg);
0056     }
0057 }
0058 
0059 void Agent::newStream (const QString &url, uint64_t wid)
0060 {
0061     if (stay_alive_timer) {
0062         killTimer (stay_alive_timer);
0063         stay_alive_timer = 0;
0064     }
0065     agent_map.insert (wid, new Stream (nullptr, url, wid));
0066 }
0067 
0068 void Agent::quit ()
0069 {
0070     qDebug ("quit");
0071     qApp->quit ();
0072 }
0073 
0074 void Agent::streamDestroyed (uint64_t wid)
0075 {
0076     agent_map.remove (wid);
0077     if (!agent_map.size ())
0078         stay_alive_timer = startTimer (5000);
0079 }
0080 
0081 void Agent::timerEvent (QTimerEvent *)
0082 {
0083     quit ();
0084 }
0085 
0086 Stream::Stream (QWidget *parent, const QString &url, unsigned long wid)
0087     : QX11EmbedWidget (parent), m_url (url), video_handle (wid)
0088     //: QWidget (parent), video_handle (wid)
0089 {
0090     setAttribute(Qt::WA_NativeWindow);
0091     setAttribute(Qt::WA_DontCreateNativeAncestors);
0092     createWinId();
0093     xcb_reparent_window(QX11Info::connection(), winId(), wid, 0, 0);
0094     //embedInto (wid);
0095     show ();
0096     m_master_stream_path = QString ("%1/stream_%2").arg(control_path).arg (wid);
0097     QTimer::singleShot (0, this, &Stream::init);
0098     qDebug ("newStream xembed cont: %lu", wid);
0099 }
0100 
0101 void Stream::init () {
0102     (void) new StreamAgentAdaptor (this);
0103     QDBusConnection::sessionBus().registerObject (
0104             QString ("/stream_%1").arg (video_handle), this);
0105 
0106     m_media = new Phonon::MediaObject(this);
0107     // might need VideoCategory here
0108     m_aoutput = new Phonon::AudioOutput (Phonon::MusicCategory, this);
0109     m_vwidget = new Phonon::VideoWidget(this);
0110     Phonon::createPath (m_media, m_aoutput);
0111     Phonon::createPath (m_media, m_vwidget);
0112 
0113     QVBoxLayout *layout = new QVBoxLayout(this);
0114     layout->setContentsMargins (0, 0, 0, 0);
0115     layout->addWidget(m_vwidget);
0116     m_vwidget->hide();
0117 
0118     connect(m_media, &Phonon::MediaObject::hasVideoChanged, this, &Stream::hasVideoChanged);
0119     connect (m_media, &Phonon::MediaObject::bufferStatus, this, &Stream::bufferStatus);
0120     connect (m_media, &Phonon::MediaObject::metaDataChanged, this, &Stream::metaDataChanged);
0121     connect (m_media, &Phonon::MediaObject::tick, this, &Stream::tick);
0122     connect (m_media, &Phonon::MediaObject::stateChanged, this, &Stream::stateChanged);
0123     connect (m_media, &Phonon::MediaObject::finished, this, &Stream::finished);
0124 
0125     if (m_url.startsWith ("dvd:"))
0126         m_media->setCurrentSource (Phonon::Dvd);
0127     else if (m_url.startsWith ("vcd:"))
0128         m_media->setCurrentSource (Phonon::Vcd);
0129     else if (m_url.startsWith ("cdda:"))
0130         m_media->setCurrentSource (Phonon::Cd);
0131     else if (m_url.startsWith ("/"))
0132         m_media->setCurrentSource (QUrl::fromLocalFile(m_url));
0133     else
0134         m_media->setCurrentSource (QUrl (m_url));
0135     play ();
0136 }
0137 
0138 Stream::~Stream () {
0139     delete m_media;
0140     agent->streamDestroyed (video_handle);
0141 }
0142 
0143 /*bool Stream::x11Event (XEvent *event) {
0144     switch (event->type) {
0145         case PropertyNotify:
0146             return QWidget::x11Event (event);
0147     }
0148     return QX11EmbedWidget::x11Event (event);
0149 }*/
0150 
0151 void Stream::play () {
0152     qDebug ("play %s@%lu", qPrintable (m_url), video_handle);
0153     m_media->setTickInterval (500);
0154     m_media->play ();
0155     m_vwidget->setVisible (m_media->hasVideo ());
0156 }
0157 
0158 void Stream::pause () {
0159     if (m_media->state () == Phonon::PausedState)
0160         m_media->play ();
0161     else
0162         m_media->pause ();
0163 }
0164 
0165 void Stream::stop () {
0166     qDebug() << "stop" << video_handle;
0167     m_media->stop ();
0168     delete this;
0169 }
0170 
0171 void Stream::seek (uint64_t position, bool /*absolute*/) {
0172     m_media->seek (position * 100);
0173 }
0174 
0175 void Stream::volume (int value) {
0176     m_aoutput->setVolume (1.0 * value / 100);
0177 }
0178 
0179 void Stream::hasVideoChanged (bool hasVideo) {
0180     qDebug ("hasVideoChanged %d", hasVideo);
0181     m_vwidget->setVisible (hasVideo);
0182 }
0183 
0184 void Stream::bufferStatus (int percent_filled) {
0185     QDBusMessage msg = QDBusMessage::createMethodCall (
0186             control_service, m_master_stream_path,
0187             "org.kde.kmplayer.StreamMaster", "loading");
0188     msg << percent_filled;
0189     QDBusConnection::sessionBus().send (msg);
0190 }
0191 
0192 void Stream::metaDataChanged () {
0193     QString info;
0194 
0195     QString artist = m_media->metaData (Phonon::ArtistMetaData).join (" ");
0196     QString title = m_media->metaData (Phonon::TitleMetaData).join (" ");
0197     QString desc = m_media->metaData (Phonon::DescriptionMetaData).join (" ");
0198     qDebug ("metadata artist:%s title:%s desc:%s",
0199             artist.toUtf8 ().data (), title.toUtf8 ().data (), desc.toUtf8 ().data ());
0200     if (!title.isEmpty ()) {
0201         if (artist.isEmpty ())
0202             info = QString ("<b>") + title + QString ("</b>");
0203         else
0204             info = QString ("<b>") + artist + QString( " - " ) + title + QString ("</b>");
0205     }
0206     if (!desc.isEmpty ()) {
0207         if (!info.isEmpty ())
0208             info += QString ("<hr>");
0209         info += QString ("<i>") + desc + QString ("</i>");
0210     }
0211 
0212     QDBusMessage msg = QDBusMessage::createMethodCall (
0213             control_service, m_master_stream_path,
0214             "org.kde.kmplayer.StreamMaster", "streamMetaInfo");
0215     msg << info;
0216     QDBusConnection::sessionBus().send (msg);
0217 }
0218 
0219 void Stream::tick (qint64 t) {
0220     QDBusMessage msg = QDBusMessage::createMethodCall (
0221             control_service, m_master_stream_path,
0222             "org.kde.kmplayer.StreamMaster", "progress");
0223     msg << (qulonglong) t/100;
0224     QDBusConnection::sessionBus().send (msg);
0225 }
0226 
0227 void Stream::stateChanged (Phonon::State newstate, Phonon::State) {
0228     if (Phonon::PlayingState == newstate) {
0229         qDebug() << "playing" << video_handle << "len:" << m_media->totalTime();
0230         QDBusMessage msg = QDBusMessage::createMethodCall (
0231                 control_service, m_master_stream_path,
0232                 "org.kde.kmplayer.StreamMaster", "streamInfo");
0233         msg << (qulonglong) m_media->totalTime()/100 << (double)0.0; //FIXME:
0234         QDBusConnection::sessionBus().send (msg);
0235 
0236         QDBusMessage msg2 = QDBusMessage::createMethodCall (
0237                 control_service, m_master_stream_path,
0238                 "org.kde.kmplayer.StreamMaster", "playing");
0239         QDBusConnection::sessionBus().send (msg2);
0240     }
0241 }
0242 
0243 void Stream::finished () {
0244     qDebug ("finished %lu", video_handle);
0245     QDBusMessage msg = QDBusMessage::createMethodCall (
0246             control_service, m_master_stream_path,
0247             "org.kde.kmplayer.StreamMaster", "eof");
0248     QDBusConnection::sessionBus().send (msg);
0249     delete this;
0250 }
0251 
0252 int main (int argc, char **argv) {
0253     QString url;
0254     QApplication app (argc, argv);
0255     QApplication::setApplicationName ("Phonon Agent");
0256     for (int i = 1; i < argc; i++) {
0257         if (!strcmp (argv[i], "-cb") && ++i < argc) {
0258             QString s (argv[i]);
0259             int p = s.indexOf (QChar ('/'));
0260             if (p > -1) {
0261                 control_service = s.left (p);
0262                 control_path = s.mid (p);
0263             }
0264         } else {
0265             url = argv[i];
0266         }
0267     }
0268     agent = new Agent;
0269     if (!url.isEmpty ()) {
0270         new Stream (nullptr, url, 0);
0271         //mw.show ();
0272         //mw.play ();
0273     }
0274     return app.exec ();
0275 }
0276 
0277 #include "moc_phononplayer.cpp"