util/src/3rdparty/phonon/ds9/backend.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /*  This file is part of the KDE project.
       
     2 
       
     3 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 
       
     5 This library is free software: you can redistribute it and/or modify
       
     6 it under the terms of the GNU Lesser General Public License as published by
       
     7 the Free Software Foundation, either version 2.1 or 3 of the License.
       
     8 
       
     9 This library is distributed in the hope that it will be useful,
       
    10 but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12 GNU Lesser General Public License for more details.
       
    13 
       
    14 You should have received a copy of the GNU Lesser General Public License
       
    15 along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    16 */
       
    17 
       
    18 #include "backend.h"
       
    19 #include "backendnode.h"
       
    20 
       
    21 #include "audiooutput.h"
       
    22 #include "effect.h"
       
    23 #include "mediaobject.h"
       
    24 #include "videowidget.h"
       
    25 #include "volumeeffect.h"
       
    26 
       
    27 //windows specific (DirectX Media Object)
       
    28 #include <dmo.h>
       
    29 
       
    30 #include <QtCore/QSettings>
       
    31 #include <QtCore/QSet>
       
    32 #include <QtCore/QVariant>
       
    33 
       
    34 #include <QtCore/QtPlugin>
       
    35 
       
    36 QT_BEGIN_NAMESPACE
       
    37 
       
    38 Q_EXPORT_PLUGIN2(phonon_ds9, Phonon::DS9::Backend);
       
    39 
       
    40 namespace Phonon
       
    41 {
       
    42     namespace DS9
       
    43     {
       
    44         QMutex *Backend::directShowMutex = 0;
       
    45 
       
    46         bool Backend::AudioMoniker::operator==(const AudioMoniker &other)
       
    47         {
       
    48             return other->IsEqual(*this) == S_OK;
       
    49         }
       
    50 
       
    51 
       
    52         Backend::Backend(QObject *parent, const QVariantList &)
       
    53             : QObject(parent)
       
    54         {
       
    55             directShowMutex = &m_directShowMutex;
       
    56 
       
    57             ::CoInitialize(0);
       
    58 
       
    59             //registering meta types
       
    60             qRegisterMetaType<HRESULT>("HRESULT");
       
    61             qRegisterMetaType<Graph>("Graph");
       
    62         }
       
    63 
       
    64         Backend::~Backend()
       
    65         {
       
    66             m_audioOutputs.clear();
       
    67             m_audioEffects.clear();
       
    68             ::CoUninitialize();
       
    69 
       
    70             directShowMutex = 0;
       
    71         }
       
    72 
       
    73         QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
       
    74         {
       
    75             switch (c)
       
    76             {
       
    77             case MediaObjectClass:
       
    78                 return new MediaObject(parent);
       
    79             case AudioOutputClass:
       
    80                 return new AudioOutput(this, parent);
       
    81 #ifndef QT_NO_PHONON_EFFECT
       
    82             case EffectClass:
       
    83                 return new Effect(m_audioEffects[ args[0].toInt() ], parent);
       
    84 #endif //QT_NO_PHONON_EFFECT
       
    85 #ifndef QT_NO_PHONON_VIDEO
       
    86             case VideoWidgetClass:
       
    87                 return new VideoWidget(qobject_cast<QWidget *>(parent));
       
    88 #endif //QT_NO_PHONON_VIDEO
       
    89 #ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
       
    90             case VolumeFaderEffectClass:
       
    91                 return new VolumeEffect(parent);
       
    92 #endif //QT_NO_PHONON_VOLUMEFADEREFFECT
       
    93             default:
       
    94                 return 0;
       
    95             }
       
    96         }
       
    97 
       
    98         bool Backend::supportsVideo() const
       
    99         {
       
   100 #ifndef QT_NO_PHONON_VIDEO
       
   101             return true;
       
   102 #else
       
   103             return false;
       
   104 #endif //QT_NO_PHONON_VIDEO
       
   105         }
       
   106 
       
   107         QStringList Backend::availableMimeTypes() const
       
   108         {
       
   109             QStringList ret;
       
   110             {
       
   111                 QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\mplayer2\\mime types"), QSettings::NativeFormat);
       
   112                 ret += settings.childGroups();
       
   113             }
       
   114             {
       
   115                 QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\wmplayer\\mime types"), QSettings::NativeFormat);
       
   116                 ret += settings.childGroups();
       
   117             }
       
   118 
       
   119             ret.removeDuplicates();
       
   120             ret.replaceInStrings("\\", "/");
       
   121             qSort(ret);
       
   122             return ret;
       
   123         }
       
   124 
       
   125 		Filter Backend::getAudioOutputFilter(int index) const
       
   126 		{
       
   127 			Filter ret;
       
   128 			if (index >= 0 && index < m_audioOutputs.count()) {
       
   129 				m_audioOutputs.at(index)->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(&ret));
       
   130 			} else {
       
   131 				//just return the default audio renderer (not directsound)
       
   132 				ret = Filter(CLSID_AudioRender, IID_IBaseFilter);
       
   133 			}
       
   134 			return ret;
       
   135 		}
       
   136 
       
   137 
       
   138         QList<int> Backend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const
       
   139         {
       
   140             QMutexLocker locker(&m_directShowMutex);
       
   141             QList<int> ret;
       
   142 
       
   143             switch(type)
       
   144             {
       
   145             case Phonon::AudioOutputDeviceType:
       
   146                 {
       
   147 #ifdef Q_OS_WINCE
       
   148 					ret << 0; // only one audio device with index 0
       
   149 #else
       
   150 					ComPointer<ICreateDevEnum> devEnum(CLSID_SystemDeviceEnum, IID_ICreateDevEnum);
       
   151 					if (!devEnum) {
       
   152 						return ret; //it is impossible to enumerate the devices
       
   153 					}
       
   154                     ComPointer<IEnumMoniker> enumMon;
       
   155                     HRESULT hr = devEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, enumMon.pparam(), 0);
       
   156                     if (FAILED(hr)) {
       
   157                         break;
       
   158                     }
       
   159                     AudioMoniker mon;
       
   160 
       
   161                     //let's reorder the devices so that directshound appears first
       
   162                     int nbds = 0; //number of directsound devices
       
   163 
       
   164                     while (S_OK == enumMon->Next(1, mon.pparam(), 0)) {
       
   165                         LPOLESTR str = 0;
       
   166                         mon->GetDisplayName(0,0,&str);
       
   167                         const QString name = QString::fromWCharArray(str);
       
   168 						ComPointer<IMalloc> alloc;
       
   169 						::CoGetMalloc(1, alloc.pparam());
       
   170                         alloc->Free(str);
       
   171 
       
   172                         int insert_pos = 0;
       
   173                         if (!m_audioOutputs.contains(mon)) {
       
   174                             insert_pos = m_audioOutputs.count();
       
   175                             m_audioOutputs.append(mon);
       
   176                         } else {
       
   177                             insert_pos = m_audioOutputs.indexOf(mon);
       
   178                         }
       
   179 
       
   180                         if (name.contains(QLatin1String("DirectSound"))) {
       
   181                             ret.insert(nbds++, insert_pos);
       
   182                         } else {
       
   183                             ret.append(insert_pos);
       
   184                         }
       
   185                     }
       
   186 #endif
       
   187 					break;
       
   188                 }
       
   189 #ifndef QT_NO_PHONON_EFFECT
       
   190             case Phonon::EffectType:
       
   191                 {
       
   192                     m_audioEffects.clear();
       
   193                     ComPointer<IEnumDMO> enumDMO;
       
   194                     HRESULT hr = ::DMOEnum(DMOCATEGORY_AUDIO_EFFECT, DMO_ENUMF_INCLUDE_KEYED, 0, 0, 0, 0, enumDMO.pparam());
       
   195                     if (SUCCEEDED(hr)) {
       
   196                         CLSID clsid;
       
   197                         while (S_OK == enumDMO->Next(1, &clsid, 0, 0)) {
       
   198                             ret += m_audioEffects.count();
       
   199                             m_audioEffects.append(clsid);
       
   200                         }
       
   201                     }
       
   202                     break;
       
   203                 }
       
   204                 break;
       
   205 #endif //QT_NO_PHONON_EFFECT
       
   206             default:
       
   207                 break;
       
   208             }
       
   209 			return ret;
       
   210         }
       
   211 
       
   212         QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const
       
   213         {
       
   214             QMutexLocker locker(&m_directShowMutex);
       
   215             QHash<QByteArray, QVariant> ret;
       
   216             switch (type)
       
   217             {
       
   218             case Phonon::AudioOutputDeviceType:
       
   219                 {
       
   220 #ifdef Q_OS_WINCE
       
   221 					ret["name"] = QLatin1String("default audio device");
       
   222 #else
       
   223                     const AudioMoniker &mon = m_audioOutputs[index];
       
   224                     LPOLESTR str = 0;
       
   225                     HRESULT hr = mon->GetDisplayName(0,0, &str);
       
   226                     if (SUCCEEDED(hr)) {
       
   227                         QString name = QString::fromWCharArray(str);
       
   228 						ComPointer<IMalloc> alloc;
       
   229 						::CoGetMalloc(1, alloc.pparam());
       
   230                         alloc->Free(str);
       
   231                         ret["name"] = name.mid(name.indexOf('\\') + 1);
       
   232 					}
       
   233 #endif
       
   234                 }
       
   235                 break;
       
   236 #ifndef QT_NO_PHONON_EFFECT
       
   237             case Phonon::EffectType:
       
   238                 {
       
   239                     WCHAR name[80]; // 80 is clearly stated in the MSDN doc
       
   240                     HRESULT hr = ::DMOGetName(m_audioEffects[index], name);
       
   241                     if (SUCCEEDED(hr)) {
       
   242                         ret["name"] = QString::fromWCharArray(name);
       
   243                     }
       
   244                 }
       
   245                 break;
       
   246 #endif //QT_NO_PHONON_EFFECT
       
   247             default:
       
   248                 break;
       
   249             }
       
   250 			return ret;
       
   251         }
       
   252 
       
   253         bool Backend::endConnectionChange(QSet<QObject *> objects)
       
   254         {
       
   255             //end of a transaction
       
   256             for(QSet<QObject *>::const_iterator it = objects.begin(); it != objects.end(); ++it) {
       
   257                 if (BackendNode *node = qobject_cast<BackendNode*>(*it)) {
       
   258                     MediaObject *mo = node->mediaObject();
       
   259                     if (mo) {
       
   260                         switch(mo->transactionState)
       
   261                         {
       
   262                         case Phonon::ErrorState:
       
   263                         case Phonon::StoppedState:
       
   264                         case Phonon::LoadingState:
       
   265                             //nothing to do
       
   266                             break;
       
   267                         case Phonon::PausedState:
       
   268                             mo->transactionState = Phonon::StoppedState;
       
   269                             mo->pause();
       
   270                             break;
       
   271                         default:
       
   272                             mo->transactionState = Phonon::StoppedState;
       
   273                             mo->play();
       
   274                             break;
       
   275                         }
       
   276 
       
   277                         if (mo->state() == Phonon::ErrorState)
       
   278                             return false;
       
   279                     }
       
   280                 }
       
   281             }
       
   282 
       
   283             return true;
       
   284         }
       
   285 
       
   286 
       
   287         bool Backend::startConnectionChange(QSet<QObject *> objects)
       
   288         {
       
   289             //let's save the state of the graph (before we stop it)
       
   290             for(QSet<QObject *>::const_iterator it = objects.begin(); it != objects.end(); ++it) {
       
   291                 if (BackendNode *node = qobject_cast<BackendNode*>(*it)) {
       
   292                     if (MediaObject *mo = node->mediaObject()) {
       
   293                         if (mo->state() != Phonon::StoppedState) {
       
   294                             mo->transactionState = mo->state();
       
   295                             mo->ensureStopped(); //we have to stop the graph..
       
   296                             if (mo->state() == Phonon::ErrorState)
       
   297                                 return false;
       
   298                         }
       
   299                     }
       
   300                 }
       
   301             }
       
   302 
       
   303             return true;
       
   304         }
       
   305 
       
   306         bool Backend::connectNodes(QObject *_source, QObject *_sink)
       
   307         {
       
   308             BackendNode *source = qobject_cast<BackendNode*>(_source);
       
   309             if (!source) {
       
   310                 return false;
       
   311             }
       
   312             BackendNode *sink = qobject_cast<BackendNode*>(_sink);
       
   313             if (!sink) {
       
   314                 return false;
       
   315             }
       
   316 
       
   317             //setting the graph if needed
       
   318             if (source->mediaObject() == 0 && sink->mediaObject() == 0) {
       
   319                     //error: no graph selected
       
   320                     return false;
       
   321             } else if (source->mediaObject() && source->mediaObject() != sink->mediaObject()) {
       
   322                 //this' graph becomes the common one
       
   323                 source->mediaObject()->grabNode(sink);
       
   324             } else if (source->mediaObject() == 0) {
       
   325                 //sink's graph becomes the common one
       
   326                 sink->mediaObject()->grabNode(source);
       
   327             }
       
   328 
       
   329             return source->mediaObject()->connectNodes(source, sink);
       
   330         }
       
   331 
       
   332         bool Backend::disconnectNodes(QObject *_source, QObject *_sink)
       
   333         {
       
   334             BackendNode *source = qobject_cast<BackendNode*>(_source);
       
   335             if (!source) {
       
   336                 return false;
       
   337             }
       
   338             BackendNode *sink = qobject_cast<BackendNode*>(_sink);
       
   339             if (!sink) {
       
   340                 return false;
       
   341             }
       
   342 
       
   343             return source->mediaObject() == 0 ||
       
   344                 source->mediaObject()->disconnectNodes(source, sink);
       
   345         }
       
   346     }
       
   347 }
       
   348 
       
   349 QT_END_NAMESPACE
       
   350 
       
   351 #include "moc_backend.cpp"