[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    extragear/multimedia/amarok/src/engine/phonon/directshow
From:       Shane King <kde () dontletsstart ! com>
Date:       2007-12-06 13:40:16
Message-ID: 1196948416.050642.28708.nullmailer () svn ! kde ! org
[Download RAW message or body]

SVN commit 745583 by shakes:

Implementation of an audio backend for windows using DirectShow. Should mostly work, \
at least well enough to play a few tunes from the collection.

Amarok is now somewhat usable under windows! :)

TODO: 
* Code needs more documentation. 
* Some methods are left unimplementation because Amarok doesn't currently make use of \
                them but probably will before release (eg cross fading). 
* Doesn't do streaming, only local files.


 M  +8 -1      CMakeLists.txt  
 AM            ComPtr.h   [License: GPL (v2+)]
 AM            ComVariant.h   [License: GPL (v2+)]
 D             DirectShow.h  
 M  +12 -6     DirectShowAudioOutput.cpp  
 M  +15 -0     DirectShowAudioOutput.h  
 M  +170 -12   DirectShowBackend.cpp  
 M  +25 -0     DirectShowBackend.h  
 AM            DirectShowGraph.cpp   [License: GPL (v2+)]
 A             DirectShowGraph.h   DirectShow.h#745115 [License: GPL (v2+)]
 M  +42 -9     DirectShowMediaObject.cpp  
 M  +24 -0     DirectShowMediaObject.h  


--- trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/CMakeLists.txt \
#745582:745583 @@ -1,11 +1,18 @@
 set(phonon_amarok_directshow_PART_SRCS
 	DirectShowAudioOutput.cpp
 	DirectShowBackend.cpp
+	DirectShowGraph.cpp
 	DirectShowMediaObject.cpp
    )
    
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../..
+     ${KDE4_INCLUDE_DIR}
+     ${QT_INCLUDES} )
+   
+add_definitions( -DUNICODE -DWINVER=0x0500 )
+   
 kde4_add_plugin(phonon_amarok_directshow ${phonon_amarok_directshow_PART_SRCS})
-target_link_libraries(phonon_amarok_directshow ${KDE4_KDECORE_LIBS} \
${KDE4_PHONON_LIBS} Strmiids.lib Ole32.lib) \
+target_link_libraries(phonon_amarok_directshow ${KDE4_KDECORE_LIBS} \
${KDE4_PHONON_LIBS} amaroklib Strmiids.lib Ole32.lib Oleaut32.lib uuid.lib)  
 install(TARGETS phonon_amarok_directshow DESTINATION ${PLUGIN_INSTALL_DIR})
 install(FILES amarok_directshow.desktop DESTINATION \
                ${SERVICES_INSTALL_DIR}/phononbackends)
** trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/ComPtr.h #property \
svn:eol-style  + native
** trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/ComVariant.h \
#property svn:eol-style  + native
--- trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/DirectShowAudioOutput.cpp \
#745582:745583 @@ -14,7 +14,9 @@
 
 
 DirectShowAudioOutput::DirectShowAudioOutput(QObject *parent)
-    : QObject(parent)
+    : QObject( parent ), 
+      m_device( 0 ),
+      m_volume( 0 )
 {
 }
 
@@ -27,25 +29,29 @@
 qreal 
 DirectShowAudioOutput::volume() const
 {
-    return 0;
+    return m_volume;
 }
 
 
 void 
-DirectShowAudioOutput::setVolume(qreal)
+DirectShowAudioOutput::setVolume(qreal volume)
 {
+    m_volume = volume;
+    emit volumeChanged(m_volume);
 }
 
 
 int 
 DirectShowAudioOutput::outputDevice() const
 {
-    return 0;
+    return m_device;
 }
 
 
 bool 
-DirectShowAudioOutput::setOutputDevice(int)
+DirectShowAudioOutput::setOutputDevice( int device )
 {
-    return false;
+    m_device = device;
+    emit outputDeviceChanged( device );
+    return true;
 }
--- trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/DirectShowAudioOutput.h \
#745582:745583 @@ -13,6 +13,11 @@
 
 #include <phonon/audiooutputinterface.h>
 
+class DirectShowBackend;
+class DirectShowGraph;
+
+// Phonon AudioOutput implementation.
+// Mostly just emits signals which attached graphs use.
 class DirectShowAudioOutput : public QObject, public Phonon::AudioOutputInterface
 {
     Q_OBJECT
@@ -27,6 +32,16 @@
 
         int outputDevice() const;
         bool setOutputDevice(int);
+
+    signals:
+        void volumeChanged(qreal newVolume);
+        void audioDeviceFailed();
+
+        void outputDeviceChanged( int device );
+
+    private:
+        int m_device;
+        qreal m_volume;
 };
 
 #endif // AMAROK_DIRECTSHOWAUDIOOUTPUT_H
--- trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/DirectShowBackend.cpp \
#745582:745583 @@ -10,15 +10,22 @@
 
 #define DEBUG_PREFIX "phonon-directshow"
 
-#include "DirectShow.h"
-
 #include "DirectShowBackend.h"
+#include "DirectShowAudioOutput.h"
+#include "DirectShowGraph.h"
 #include "DirectShowMediaObject.h"
-#include "DirectShowAudioOutput.h"
+#include "ComVariant.h"
 
+#include "debug.h"
+
 #include <kpluginfactory.h>
 
+#include <QtGui>
 
+#include <dshow.h>
+#include <ocidl.h>
+
+
 K_PLUGIN_FACTORY(DirectShowBackendFactory, registerPlugin<DirectShowBackend>();)
 K_EXPORT_PLUGIN(DirectShowBackendFactory("amarokdirectshowbackend"))
 
@@ -26,24 +33,29 @@
 DirectShowBackend::DirectShowBackend(QObject *parent, const QVariantList &args)
     : QObject(parent)
 {
+    m_initialized = createMessageWindow() && loadAudioDevices();
 }
 
 
 DirectShowBackend::~DirectShowBackend()
 {
+    destroyMessageWindow();
 }
 
 
 QObject *
 DirectShowBackend::createObject(BackendInterface::Class objectClass, QObject \
*parent, const QList<QVariant> &args)  {
-    switch( objectClass )
+    if( m_initialized )
     {
-        case BackendInterface::MediaObjectClass:
-            return new DirectShowMediaObject(parent);
+        switch( objectClass )
+        {
+            case BackendInterface::MediaObjectClass:
+                return new DirectShowMediaObject(parent);
 
-        case BackendInterface::AudioOutputClass:
-            return new DirectShowAudioOutput(parent);
+            case BackendInterface::AudioOutputClass:
+                return new DirectShowAudioOutput(parent);
+        }
     }
     return 0;
 }
@@ -52,14 +64,52 @@
 QList<int> 
 DirectShowBackend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) \
const  {
-    return QList<int>();
+    QList<int> result;
+
+    if( type == Phonon::AudioOutputDeviceType )
+    {
+        for( int i = 0; i < m_audioDevices.size(); ++i )
+        {
+            result.push_back( i );
+        }
+    }
+
+    return result;
 }
 
 
 QHash<QByteArray, QVariant> 
 DirectShowBackend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, \
int index) const  {
-    return QHash<QByteArray, QVariant>();
+    QHash<QByteArray, QVariant> result;
+
+    if( type == Phonon::AudioOutputDeviceType )
+    {
+        ComPtr<IMoniker> moniker = m_audioDevices[ index ];
+
+        HResult hr;
+        ComPtr<IPropertyBag> propBag;
+        hr = moniker->BindToStorage( 0, 0, IID_IPropertyBag, reinterpret_cast<void \
**>( &propBag ) ); +        if( SUCCEEDED( hr ) )
+        {
+            ComVariant name;
+            hr = propBag->Read(L"FriendlyName", &name, 0);
+            if( SUCCEEDED( hr ) )
+            {
+                result.insert( "name", name.AsString() );
+            }
+            else
+            {
+                debug() << "Failed to get FriendlyName: " << hr;
+            }
+        }
+        else
+        {
+            debug() << "Failed to get properties for audio device: " << hr;
+        }
+    }
+
+    return result;
 }
 
 
@@ -71,15 +121,34 @@
 
 
 bool 
-DirectShowBackend::connectNodes(QObject *, QObject *)
+DirectShowBackend::connectNodes(QObject *source, QObject *sink)
 {
+    DirectShowMediaObject *mediaObject = qobject_cast<DirectShowMediaObject *>( \
source ); +    DirectShowAudioOutput *audioOutput = \
qobject_cast<DirectShowAudioOutput *>( sink ); +    if( mediaObject && audioOutput )
+    {
+        if( mediaObject->getGraph() )
+            debug() << "Can't connect multiple outputs to a media object";
+        else
+            return ( new DirectShowGraph( this, mediaObject, audioOutput ) \
)->initialized(); +    }
+    else
+    {
+        debug() << "Can't connect nodes: mediaObject = " << (mediaObject == 0) << " \
audioOutput = " << (audioOutput == 0); +    }
     return false;
 }
 
 
 bool 
-DirectShowBackend::disconnectNodes(QObject *, QObject *)
+DirectShowBackend::disconnectNodes(QObject *source, QObject *sink)
 {
+    DirectShowMediaObject *mediaObject = qobject_cast<DirectShowMediaObject *>( \
source ); +    DirectShowAudioOutput *audioOutput = \
qobject_cast<DirectShowAudioOutput *>( sink ); +    
+    delete mediaObject->getGraph();
+    mediaObject->setGraph( 0 );
+
     return false;
 }
 
@@ -94,5 +163,94 @@
 QStringList 
 DirectShowBackend::availableMimeTypes() const
 {
+    // no way to get a list from DirectShow here :(
     return QStringList();
 }
+
+
+extern "C" LRESULT CALLBACK WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM \
lParam ) +{
+    if( uMsg == DirectShowBackend::WM_GRAPH_EVENT )
+    {
+        return reinterpret_cast<DirectShowGraph *>( lParam )->onEvent() ? 0 : -1;
+    }
+    return DefWindowProc( hwnd, uMsg, wParam, lParam );
+}
+
+
+static const wchar_t * WINDOW_CLASS = L"PhononAmarokDirectShow";
+const int DirectShowBackend::WM_GRAPH_EVENT = WM_APP + 1;
+
+bool
+DirectShowBackend::createMessageWindow()
+{
+    WNDCLASS cls;
+    cls.style = 0;
+    cls.lpfnWndProc = WndProc;
+    cls.cbClsExtra = 0;
+    cls.cbWndExtra = 0;
+    cls.hInstance = qWinAppInst();
+    cls.hIcon = 0;
+    cls.hCursor = 0;
+    cls.hbrBackground = 0;
+    cls.lpszMenuName = 0;
+    cls.lpszClassName = WINDOW_CLASS;
+
+    if( RegisterClass(&cls) )
+    {
+        m_window = CreateWindowEx( 0, WINDOW_CLASS, 
+            L"PhononAmarokDirectShowWindow",0 ,0 ,0 ,0 ,0 ,HWND_MESSAGE,0, \
cls.hInstance, 0); +        if(m_window)
+            return true;
+        else
+            debug() << "Failed to create message window";
+    }
+    else
+    {
+        debug() << "Failed to register window class";
+    }
+    return false;
+}
+
+
+void
+DirectShowBackend::destroyMessageWindow()
+{
+    DestroyWindow(m_window);
+    UnregisterClass(WINDOW_CLASS, qWinAppInst());
+}
+
+
+bool
+DirectShowBackend::loadAudioDevices()
+{
+    // Create the System Device Enumerator.
+    HResult hr;
+    ComPtr<ICreateDevEnum> sysDevEnum;
+    hr = sysDevEnum.CreateInstance(CLSID_SystemDeviceEnum, IID_ICreateDevEnum);
+    if ( SUCCEEDED( hr ) )
+    {
+        ComPtr<IEnumMoniker> enumCat;
+        hr = sysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, \
&enumCat, 0); +        if ( SUCCEEDED( hr ) && enumCat )
+        {
+            ComPtr<IMoniker> moniker;
+            unsigned long fetched;
+            int i = 1;
+            while( enumCat->Next( 1, &moniker, &fetched ) == S_OK )
+            {
+                m_audioDevices.push_back( moniker );
+            }
+            return true;
+        }
+        else
+        {
+            debug() << "Failure enumerating device: " << hr;
+        }
+    }
+    else
+    {
+        debug() << "Failed to create system device enumerator: " << hr;
+    }
+    return false;
+}
--- trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/DirectShowBackend.h \
#745582:745583 @@ -11,8 +11,19 @@
 #ifndef AMAROK_DIRECTSHOWBACKEND_H
 #define AMAROK_DIRECTSHOWBACKEND_H
 
+#include "ComPtr.h"
+
 #include <phonon/backendinterface.h>
 
+#include <windows.h>
+#include <objidl.h>
+
+class DirectShowGraph;
+
+// Phonon backend class.
+// Does basic initialization of what we need for DirectShow
+// including setup up a (non-UI) "window" to handle events
+// which are dispatched to the appropriate graph object.
 class DirectShowBackend : public QObject, public Phonon::BackendInterface
 {
     Q_OBJECT
@@ -30,6 +41,20 @@
         bool disconnectNodes(QObject *, QObject *);
         bool endConnectionChange(QSet<QObject *>);
         QStringList availableMimeTypes() const;
+
+        ComPtr< IMoniker > getDevice( int index ) const { return m_audioDevices.at( \
index ); } +        HWND window() { return m_window; }
+
+        static const int WM_GRAPH_EVENT;
+
+    private:
+        bool createMessageWindow();
+        void destroyMessageWindow();
+        bool loadAudioDevices();
+
+        bool m_initialized;
+        HWND m_window;
+        QList< ComPtr<IMoniker> > m_audioDevices;
 };
 
 #endif // AMAROK_DIRECTSHOWBACKEND_H
** trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/DirectShowGraph.cpp \
#property svn:eol-style  + native
--- trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/DirectShowMediaObject.cpp \
#745582:745583 @@ -11,12 +11,14 @@
 #define DEBUG_PREFIX "phonon-directshow"
 
 #include "DirectShowMediaObject.h"
+#include "DirectShowGraph.h"
 
 #include <kpluginfactory.h>
 
 
 DirectShowMediaObject::DirectShowMediaObject(QObject *parent)
-    : QObject(parent)
+    : QObject( parent ),
+      m_graph( 0 )
 {
 }
 
@@ -29,24 +31,32 @@
 void 
 DirectShowMediaObject::play()
 {
+    if( m_graph )
+        m_graph->play();
 }
 
 
 void 
 DirectShowMediaObject::pause()
 {
+    if( m_graph )
+        m_graph->pause();
 }
 
 
 void
 DirectShowMediaObject::stop()
 {
+    if( m_graph )
+        m_graph->stop();
 }
 
 
 void 
 DirectShowMediaObject::seek(qint64 milliseconds)
 {
+    if( m_graph )
+        m_graph->seek( milliseconds );
 }
 
 
@@ -72,55 +82,57 @@
 bool
 DirectShowMediaObject::isSeekable() const
 {
-    return false;
+    return m_graph && m_graph->isSeekable();
 }
 
 
 qint64 
 DirectShowMediaObject::currentTime() const
 {
-    return 0;
+    return m_graph ? m_graph->currentTime() : 0;
 }
 
 
 Phonon::State 
 DirectShowMediaObject::state() const
 {
-    return Phonon::StoppedState;
+    return m_graph ? m_graph->state() : Phonon::StoppedState;
 }
 
 
 qint64 
 DirectShowMediaObject::totalTime() const
 {
-    return 0;
+    return m_graph ? m_graph->totalTime() : 0;
 }
 
 
 QString 
 DirectShowMediaObject::errorString() const
 {
-    return "";
+    return m_graph ? m_graph->errorString() : "";
 }
 
 
 Phonon::ErrorType 
 DirectShowMediaObject::errorType() const
 {
-    return Phonon::NoError;
+    return m_graph ? m_graph->errorType() : Phonon::NoError;
 }
 
 
 Phonon::MediaSource 
 DirectShowMediaObject::source() const
 {
-    return Phonon::MediaSource();
+    return m_graph->source();
 }
 
 
 void 
-DirectShowMediaObject::setSource(const Phonon::MediaSource &)
+DirectShowMediaObject::setSource(const Phonon::MediaSource &source)
 {
+    if( m_graph )
+        m_graph->setSource( source );
 }
 
 
@@ -129,6 +141,7 @@
 {
 }
 
+
 qint32 
 DirectShowMediaObject::prefinishMark() const
 {
@@ -153,3 +166,23 @@
 DirectShowMediaObject::setTransitionTime(qint32)
 {
 }
+
+
+void
+DirectShowMediaObject::setGraph( DirectShowGraph *graph )
+{
+    m_graph = graph;
+
+    connect( m_graph, SIGNAL( aboutToFinish() ), SIGNAL( aboutToFinish() ) );
+    connect( m_graph, SIGNAL( finished() ), SIGNAL( finished() ) );
+    connect( m_graph, SIGNAL( prefinishMarkReached(qint32) ), SIGNAL( \
prefinishMarkReached(qint32) ) ); +    connect( m_graph, SIGNAL( \
totalTimeChanged(qint64) ), SIGNAL( prefinishMarkReached(qint32) ) ); +    connect( \
m_graph, SIGNAL( currentSourceChanged(const Phonon::MediaSource &) ), SIGNAL( \
currentSourceChanged(const Phonon::MediaSource &) ) ); +
+    connect( m_graph, SIGNAL( stateChanged( Phonon::State, Phonon::State ) ), \
SIGNAL( stateChanged( Phonon::State, Phonon::State ) ) ); +    connect( m_graph, \
SIGNAL( tick(qint64) ), SIGNAL( tick(qint64) ) ); +    connect( m_graph, SIGNAL( \
metaDataChanged(const QMultiMap<QString, QString> &) ), SIGNAL( metaDataChanged(const \
QMultiMap<QString, QString> &) ) ); +    connect( m_graph, SIGNAL( \
seekableChanged(bool) ), SIGNAL( seekableChanged(bool) ) ); +    connect( m_graph, \
SIGNAL( hasVideoChanged(bool) ), SIGNAL( hasVideoChanged(bool) ) ); +    connect( \
m_graph, SIGNAL( bufferStatus(int) ), SIGNAL( bufferStatus(int) ) ); +}
--- trunk/extragear/multimedia/amarok/src/engine/phonon/directshow/DirectShowMediaObject.h \
#745582:745583 @@ -13,6 +13,10 @@
 
 #include <phonon/mediaobjectinterface.h>
 
+class DirectShowGraph;
+
+// Phonon MediaObject implementation.
+// Mostly just forwards everything on to its associated graph object.
 class DirectShowMediaObject : public QObject, public Phonon::MediaObjectInterface
 {
     Q_OBJECT
@@ -49,6 +53,26 @@
 
         qint32 transitionTime() const;
         void setTransitionTime(qint32);
+
+        void setGraph( DirectShowGraph *graph );
+        DirectShowGraph *getGraph() { return m_graph; }
+
+    signals:
+        void aboutToFinish();
+        void finished();
+        void prefinishMarkReached(qint32 msec);
+        void totalTimeChanged(qint64 length);
+        void currentSourceChanged(const Phonon::MediaSource &);
+
+        void stateChanged(Phonon::State newstate, Phonon::State oldstate);
+        void tick(qint64 time);
+        void metaDataChanged(const QMultiMap<QString, QString> &);
+        void seekableChanged(bool);
+        void hasVideoChanged(bool);
+        void bufferStatus(int);
+
+    private:
+        DirectShowGraph *m_graph;
 };
 
 #endif // AMAROK_DIRECTSHOWMEDIAOBJECT_H


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic