Subject: Make KSCD play audio CDs with gstreamer backend
From: upstream
Signed-Off-By: Lubos Lunak
Bug: 588230
Patch-upstream: yes


commit 67f58d71d5b27e5a15a8e88eff1b3b26628ae37f
Author: Jens Bache-Wiig <jbache@trolltech.com>
Date:   Thu Mar 19 11:18:16 2009 +0000

    Phonon/Gstreamer: Add support for audio CD tracks
    through the MediaController interface.
    
    This patch adds basic support for cdda to the
    GStreamer backend. It's still somewhat limited
    by the limited autodetection of CD media but should
    be enough to make KSCD work with GStreamer.

diff --git a/gstreamer/mediaobject.cpp b/gstreamer/mediaobject.cpp
index 5398f0c..74fc1b4 100644
--- a/gstreamer/mediaobject.cpp
+++ b/gstreamer/mediaobject.cpp
@@ -14,7 +14,6 @@
     You should have received a copy of the GNU Lesser General Public License
     along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
-
 #include <cmath>
 #include <gst/interfaces/propertyprobe.h>
 #include "common.h"
@@ -24,7 +23,6 @@
 #include "backend.h"
 #include "streamreader.h"
 #include "phononsrc.h"
-
 #include <QtCore>
 #include <QtCore/QTimer>
 #include <QtCore/QVector>
@@ -78,6 +76,9 @@ MediaObject::MediaObject(Backend *backend, QObject *parent)
         , m_videoGraph(0)
         , m_previousTickTime(-1)
         , m_resetNeeded(false)
+        , m_autoplayTitles(true)
+        , m_availableTitles(0)
+        , m_currentTitle(1)
 {
     qRegisterMetaType<GstCaps*>("GstCaps*");
     qRegisterMetaType<State>("State");
@@ -902,8 +903,13 @@ void MediaObject::setSource(const MediaSource &source)
         break;
 
     case MediaSource::Disc: // CD tracks can be specified by setting the url in the following way uri=cdda:4
-        m_backend->logMessage("Source type Disc not currently supported", Backend::Warning, this);
-        setError(tr("Could not open media source."), Phonon::NormalError);
+        {
+            QUrl cdurl(QLatin1String("cdda://"));
+            if (createPipefromURL(cdurl))
+                m_loading = true;
+            else
+                setError(tr("Could not open media source."));
+        }
         break;
 
     default:
@@ -954,6 +960,19 @@ void MediaObject::getStreamInfo()
         m_hasVideo = m_videoStreamFound;
         emit hasVideoChanged(m_hasVideo);
     }
+
+    m_availableTitles = 1;
+    gint64 titleCount;
+    GstFormat format = gst_format_get_by_nick("track");
+    if (gst_element_query_duration (m_pipeline, &format, &titleCount)) {
+        int oldAvailableTitles = m_availableTitles;
+        m_availableTitles = (int)titleCount;
+        if (m_availableTitles != oldAvailableTitles) {
+            emit availableTitlesChanged(m_availableTitles);
+            m_backend->logMessage(QString("Available titles changed: %0").arg(m_availableTitles), Backend::Info, this);
+        }
+    }
+
 }
 
 void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
@@ -1351,6 +1370,13 @@ void MediaObject::handleEndOfStream()
     if (!m_seekable)
         m_atEndOfStream = true;
 
+    if (m_autoplayTitles &&
+        m_availableTitles > 1 &&
+        m_currentTitle < m_availableTitles) {
+        _iface_setCurrentTitle(m_currentTitle + 1);
+        return;
+    }
+
     if (m_nextSource.type() != MediaSource::Invalid
         && m_nextSource.type() != MediaSource::Empty) {  // We only emit finish when the queue is actually empty
         QTimer::singleShot (qMax(0, transitionTime()), this, SLOT(beginPlay()));
@@ -1379,6 +1405,72 @@ void MediaObject::notifyStateChange(Phonon::State newstate, Phonon::State oldsta
     notify(&event);
 }
 
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+//interface management
+bool MediaObject::hasInterface(Interface iface) const
+{
+    return iface == AddonInterface::TitleInterface;
+}
+
+QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> &params)
+{
+    if (hasInterface(iface)) {
+
+        switch (iface)
+        {
+        case TitleInterface:
+            switch (command)
+            {
+            case availableTitles:
+                return _iface_availableTitles();
+            case title:
+                return _iface_currentTitle();
+            case setTitle:
+                _iface_setCurrentTitle(params.first().toInt());
+                break;
+            case autoplayTitles:
+                return m_autoplayTitles;
+            case setAutoplayTitles:
+                m_autoplayTitles = params.first().toBool();
+                break;
+            }
+            break;
+                default:
+            break;
+        }
+    }
+    return QVariant();
+}
+#endif
+
+int MediaObject::_iface_availableTitles() const
+{
+    return m_availableTitles;
+}
+
+int MediaObject::_iface_currentTitle() const
+{
+    return m_currentTitle;
+}
+
+void MediaObject::_iface_setCurrentTitle(int title)
+{
+    GstFormat trackFormat = gst_format_get_by_nick("track");
+    m_backend->logMessage(QString("setCurrentTitle %0").arg(title), Backend::Info, this);
+    if ((title == m_currentTitle) || (title < 1) || (title > m_availableTitles))
+        return;
+
+    m_currentTitle = title;
+
+    //let's seek to the beginning of the song
+    if (gst_element_seek_simple(m_pipeline, trackFormat, GST_SEEK_FLAG_FLUSH, m_currentTitle - 1)) {
+        updateTotalTime();
+        m_atEndOfStream = false;
+        emit titleChanged(title);
+        emit totalTimeChanged(totalTime());
+    }
+}
+
 } // ns Gstreamer
 } // ns Phonon
 
diff --git a/gstreamer/mediaobject.h b/gstreamer/mediaobject.h
index 4dc3f12..64b3510 100644
--- a/gstreamer/mediaobject.h
+++ b/gstreamer/mediaobject.h
@@ -21,7 +21,6 @@
 #include "backend.h"
 #include "common.h"
 #include "medianode.h"
-
 #include <phonon/mediaobjectinterface.h>
 #include <phonon/addoninterface.h>
 
@@ -32,7 +31,6 @@
 #include <QtCore/QDate>
 #include <QtCore/QEvent>
 #include <QtCore/QUrl>
-
 #include <gst/gst.h>
 
 QT_BEGIN_NAMESPACE
@@ -50,11 +48,20 @@ class AudioPath;
 class VideoPath;
 class AudioOutput;
 
-class MediaObject : public QObject, public MediaObjectInterface, public AddonInterface, public MediaNode
+class MediaObject : public QObject, public MediaObjectInterface
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+        , public AddonInterface
+#endif
+        , public MediaNode
 {
     friend class Stream;
     Q_OBJECT
-    Q_INTERFACES(Phonon::MediaObjectInterface Phonon::AddonInterface Phonon::Gstreamer::MediaNode)
+    Q_INTERFACES(Phonon::MediaObjectInterface
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+                 Phonon::AddonInterface
+#endif
+                 Phonon::Gstreamer::MediaNode
+    )
 
 public:
 
@@ -93,16 +100,10 @@ public:
     MediaSource source() const;
 
     // No additional interfaces currently supported
-    bool hasInterface(Interface) const
-    {
-        return false;
-    }
-
-    QVariant interfaceCall(Interface, int, const QList<QVariant> &)
-    {
-        return QVariant();
-    }
-
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+    bool hasInterface(Interface) const;
+    QVariant interfaceCall(Interface, int, const QList<QVariant> &);
+#endif
     bool isLoading()
     {
         return m_loading;
@@ -176,6 +177,19 @@ Q_SIGNALS:
     QMultiMap<QString, QString> metaData();
     void setMetaData(QMultiMap<QString, QString> newData);
 
+    // AddonInterface:
+    void titleChanged(int);
+    void availableTitlesChanged(int);
+
+    // Not implemented
+    void chapterChanged(int);
+    void availableChaptersChanged(int);
+    void angleChanged(int);
+    void availableAnglesChanged(int);
+
+    void availableSubtitlesChanged();
+    void availableAudioChannelsChanged();
+
 protected:
     void beginLoad();
     void loadingComplete();
@@ -219,6 +233,10 @@ private:
     void updateSeekable();
     qint64 getPipelinePos() const;
 
+    int _iface_availableTitles() const;
+    int _iface_currentTitle() const;
+    void _iface_setCurrentTitle(int title);
+
     bool m_resumeState;
     State m_oldState;
     quint64 m_oldPos;
@@ -264,6 +282,9 @@ private:
     bool m_resetNeeded;
     QStringList m_missingCodecs;
     QMultiMap<QString, QString> m_metaData;
+    bool m_autoplayTitles;
+    int m_availableTitles;
+    int m_currentTitle;
 };
 }
 } //namespace Phonon::Gstreamer
commit e7708bbf07cbb74e07124cccc1997a8a77a15b7b
Author: Jens Bache-Wiig <jbache@trolltech.com>
Date:   Fri Feb 27 12:08:24 2009 +0000

    Fixes incorrect URL encoding - task 173789
    
    We no longer require string encoding URL's at all
    and strict encoding prevented certain filenames from showing.

diff --git a/gstreamer/mediaobject.cpp b/gstreamer/mediaobject.cpp
index cd593a4..30d6cd0 100644
--- a/gstreamer/mediaobject.cpp
+++ b/gstreamer/mediaobject.cpp
@@ -347,11 +347,8 @@ void MediaObject::cb_pad_added(GstElement *decodebin,
  *
  * returns true if successful
  */
-bool MediaObject::createPipefromURL(const QString &encodedUrl)
+bool MediaObject::createPipefromURL(const QUrl &url)
 {
-    // Convert back to URL
-    QUrl url(encodedUrl, QUrl::StrictMode);
-
     // Remove any existing data source
     if (m_datasource) {
         gst_bin_remove(GST_BIN(m_pipeline), m_datasource);
@@ -361,7 +358,7 @@ bool MediaObject::createPipefromURL(const QString &encodedUrl)
 
     // Verify that the uri can be parsed
     if (!url.isValid()) {
-        m_backend->logMessage(QString("%1 is not a valid URI").arg(encodedUrl));
+        m_backend->logMessage(QString("%1 is not a valid URI").arg(url.toString()));
         return false;
     }
 
@@ -875,8 +872,7 @@ void MediaObject::setSource(const MediaSource &source)
 
     switch (source.type()) {
     case MediaSource::Url: {            
-            QString urlString = source.url().toEncoded();
-            if (createPipefromURL(urlString))
+            if (createPipefromURL(source.url()))
                 m_loading = true;
             else
                 setError(tr("Could not open media source."));
@@ -884,8 +880,7 @@ void MediaObject::setSource(const MediaSource &source)
         break;
 
     case MediaSource::LocalFile: {
-            QString urlString = QUrl::fromLocalFile(source.fileName()).toString();
-            if (createPipefromURL(urlString))
+            if (createPipefromURL(QUrl::fromLocalFile(source.fileName())))
                 m_loading = true;
             else
                 setError(tr("Could not open media source."));
diff --git a/gstreamer/mediaobject.h b/gstreamer/mediaobject.h
index c5b7ccd..4dc3f12 100644
--- a/gstreamer/mediaobject.h
+++ b/gstreamer/mediaobject.h
@@ -185,7 +185,7 @@ protected:
     /*
      * @param encodedUrl percent-encoded QString for source compat reasons.  Should change to QUrl
      */
-    bool createPipefromURL(const QString &encodedUrl);
+    bool createPipefromURL(const QUrl &url);
     bool createPipefromStream(const MediaSource &);
 
 private Q_SLOTS:
