1 /* This file is part of the KDE project
2 Copyright (C) 2006 Tim Beaulen <tbscope@gmail.com>
3 Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 #include "xineengine.h"
24 #include "mediaobject.h"
25 #include <QCoreApplication>
26 #include <QtDBus/QDBusConnection>
27 #include <phonon/audiodeviceenumerator.h>
28 #include <phonon/audiodevice.h>
30 #include <kconfiggroup.h>
32 #include "videowidget.h"
34 #include "xineengine_p.h"
37 #include "xinethread.h"
43 XineEnginePrivate::XineEnginePrivate()
45 signalTimer
.setSingleShot(true);
46 connect(&signalTimer
, SIGNAL(timeout()), SLOT(emitAudioDeviceChange()));
47 QDBusConnection::sessionBus().registerObject("/internal/PhononXine", this, QDBusConnection::ExportScriptableSlots
);
50 void XineEnginePrivate::emitAudioDeviceChange()
53 emit
objectDescriptionChanged(AudioOutputDeviceType
);
56 static XineEngine
*s_instance
= 0;
58 XineEngine::XineEngine(const KSharedConfigPtr
&_config
)
61 m_useOss(XineEngine::Unknown
),
63 d(new XineEnginePrivate
),
68 Q_ASSERT(s_instance
== 0);
70 KConfigGroup
cg(m_config
, "Settings");
72 m_deinterlaceDVD
= cg
.readEntry("deinterlaceDVD", true);
73 m_deinterlaceVCD
= cg
.readEntry("deinterlaceVCD", false);
74 m_deinterlaceFile
= cg
.readEntry("deinterlaceFile", false);
75 m_deinterlaceMethod
= cg
.readEntry("deinterlaceMethod", 0);
78 XineEngine::~XineEngine()
82 m_thread->stopAllStreams();
84 QList
<QObject
*> cleanupObjects(m_cleanupObjects
);
85 const QList
<QObject
*>::Iterator end
= cleanupObjects
.end();
86 QList
<QObject
*>::Iterator it
= cleanupObjects
.begin();
88 kDebug(610) << "delete" << (*it
)->metaObject()->className();
92 //qDeleteAll(cleanupObjects);
95 if (!m_thread
->wait(10000)) {
97 // assuming a deadlock, we better create a backtrace than block something important
98 kFatal(610) << "Xine Thread took longer than 4s to quit. Assuming a deadlock. Please report a useful backtrace (including all threads) to bugs.kde.org";
99 // if somebody made kFatal non-fatal
100 m_thread
->terminate();
106 xine_close_audio_driver(m_xine
, m_nullPort
);
108 if (m_nullVideoPort
) {
109 xine_close_video_driver(m_xine
, m_nullVideoPort
);
117 XineEngine
*XineEngine::self()
119 Q_ASSERT(s_instance
);
123 const QObject
*XineEngine::sender()
128 xine_t
*XineEngine::xine()
130 return self()->m_xine
;
133 XineThread
*XineEngine::thread()
135 XineEngine
*const e
= self();
137 e
->m_thread
= new XineThread
;
138 e
->m_thread
->moveToThread(e
->m_thread
);
139 e
->m_thread
->start();
140 e
->m_thread
->waitForEventLoop();
145 bool XineEngine::deinterlaceDVD()
147 return s_instance
->m_deinterlaceDVD
;
150 bool XineEngine::deinterlaceVCD()
152 return s_instance
->m_deinterlaceVCD
;
155 bool XineEngine::deinterlaceFile()
157 return s_instance
->m_deinterlaceFile
;
160 int XineEngine::deinterlaceMethod()
162 return s_instance
->m_deinterlaceMethod
;
165 void XineEngine::xineEventListener(void *p
, const xine_event_t
*xineEvent
)
167 if (!p
|| !xineEvent
) {
170 //kDebug(610) << "Xine event: " << xineEvent->type << QByteArray((char *)xineEvent->data, xineEvent->data_length);
172 XineStream
*xs
= static_cast<XineStream
*>(p
);
174 switch (xineEvent
->type
) {
175 case XINE_EVENT_UI_SET_TITLE
: /* request title display change in ui */
176 QCoreApplication::postEvent(xs
, new QEVENT(NewMetaData
));
178 case XINE_EVENT_UI_PLAYBACK_FINISHED
: /* frontend can e.g. move on to next playlist entry */
179 QCoreApplication::postEvent(xs
, new QEVENT(MediaFinished
));
181 case XINE_EVENT_PROGRESS
: /* index creation/network connections */
183 xine_progress_data_t
*progress
= static_cast<xine_progress_data_t
*>(xineEvent
->data
);
184 QCoreApplication::postEvent(xs
, new ProgressEvent(QString::fromUtf8(progress
->description
), progress
->percent
));
187 case XINE_EVENT_SPU_BUTTON
: // the mouse pointer enter/leave a button, used to change the cursor
189 xine_spu_button_t
*button
= static_cast<xine_spu_button_t
*>(xineEvent
->data
);
190 if (button
->direction
== 1) { // enter a button
191 xs
->handleDownstreamEvent(new QEVENT(NavButtonIn
));
193 xs
->handleDownstreamEvent(new QEVENT(NavButtonOut
));
197 case XINE_EVENT_UI_CHANNELS_CHANGED
: /* inform ui that new channel info is available */
198 kDebug(610) << "XINE_EVENT_UI_CHANNELS_CHANGED";
200 QCoreApplication::postEvent(xs
, new QEVENT(UiChannelsChanged
));
203 case XINE_EVENT_UI_MESSAGE
: /* message (dialog) for the ui to display */
205 kDebug(610) << "XINE_EVENT_UI_MESSAGE";
206 const xine_ui_message_data_t
*message
= static_cast<xine_ui_message_data_t
*>(xineEvent
->data
);
207 if (message
->type
== XINE_MSG_AUDIO_OUT_UNAVAILABLE
) {
208 kDebug(610) << "XINE_MSG_AUDIO_OUT_UNAVAILABLE";
209 // we don't know for sure which AudioOutput failed. but the one without any
210 // capabilities must be the guilty one
211 xs
->handleDownstreamEvent(new QEVENT(AudioDeviceFailed
));
215 case XINE_EVENT_FRAME_FORMAT_CHANGE
: /* e.g. aspect ratio change during dvd playback */
216 kDebug(610) << "XINE_EVENT_FRAME_FORMAT_CHANGE";
218 xine_format_change_data_t
*data
= static_cast<xine_format_change_data_t
*>(xineEvent
->data
);
219 xs
->handleDownstreamEvent(new FrameFormatChangeEvent(data
->width
, data
->height
, data
->aspect
, data
->pan_scan
));
222 case XINE_EVENT_AUDIO_LEVEL
: /* report current audio level (l/r/mute) */
223 kDebug(610) << "XINE_EVENT_AUDIO_LEVEL";
225 case XINE_EVENT_QUIT
: /* last event sent when stream is disposed */
226 kDebug(610) << "XINE_EVENT_QUIT";
228 case XINE_EVENT_UI_NUM_BUTTONS
: /* number of buttons for interactive menus */
229 kDebug(610) << "XINE_EVENT_UI_NUM_BUTTONS";
231 case XINE_EVENT_DROPPED_FRAMES
: /* number of dropped frames is too high */
232 kDebug(610) << "XINE_EVENT_DROPPED_FRAMES";
234 case XINE_EVENT_MRL_REFERENCE_EXT
: /* demuxer->frontend: MRL reference(s) for the real stream */
236 xine_mrl_reference_data_ext_t
*reference
= static_cast<xine_mrl_reference_data_ext_t
*>(xineEvent
->data
);
237 kDebug(610) << "XINE_EVENT_MRL_REFERENCE_EXT: " << reference
->alternative
238 << ", " << reference
->start_time
239 << ", " << reference
->duration
240 << ", " << reference
->mrl
241 << ", " << (reference
->mrl
+ strlen(reference
->mrl
) + 1)
243 QCoreApplication::postEvent(xs
, new ReferenceEvent(reference
->alternative
, reference
->mrl
));
249 xine_audio_port_t
*XineEngine::nullPort()
251 if (!s_instance
->m_nullPort
) {
252 s_instance
->m_nullPort
= xine_open_audio_driver(s_instance
->m_xine
, "none", 0);
254 Q_ASSERT(s_instance
->m_nullPort
);
255 return s_instance
->m_nullPort
;
258 xine_video_port_t
*XineEngine::nullVideoPort()
260 if (!s_instance
->m_nullVideoPort
) {
261 s_instance
->m_nullVideoPort
= xine_open_video_driver(s_instance
->m_xine
, "auto", XINE_VISUAL_TYPE_NONE
, 0);
263 Q_ASSERT(s_instance
->m_nullVideoPort
);
264 return s_instance
->m_nullVideoPort
;
267 QList
<int> XineEngine::audioOutputIndexes()
269 XineEngine
*that
= self();
270 that
->checkAudioOutputs();
272 for (int i
= 0; i
< that
->m_audioOutputInfos
.size(); ++i
) {
273 list
<< that
->m_audioOutputInfos
[i
].index
;
278 QHash
<QByteArray
, QVariant
> XineEngine::audioOutputProperties(int audioDevice
)
280 QHash
<QByteArray
, QVariant
> ret
;
281 XineEngine
*that
= self();
282 that
->checkAudioOutputs();
284 for (int i
= 0; i
< that
->m_audioOutputInfos
.size(); ++i
) {
285 if (that
->m_audioOutputInfos
[i
].index
== audioDevice
) {
286 switch (that
->m_useOss
) {
287 case XineEngine::True
: // postfix
288 if (that
->m_audioOutputInfos
[i
].driver
== "oss") {
289 ret
.insert("name", i18n("%1 (OSS)", that
->m_audioOutputInfos
[i
].name
));
290 } else if (that
->m_audioOutputInfos
[i
].driver
== "alsa") {
291 ret
.insert("name", i18n("%1 (ALSA)", that
->m_audioOutputInfos
[i
].name
));
293 // no postfix: fall through
294 case XineEngine::False
: // no postfix
295 case XineEngine::Unknown
: // no postfix
296 ret
.insert("name", that
->m_audioOutputInfos
[i
].name
);
298 ret
.insert("description", that
->m_audioOutputInfos
[i
].description
);
300 const QString iconName
= that
->m_audioOutputInfos
[i
].icon
;
301 if (!iconName
.isEmpty()) {
302 ret
.insert("icon", KIcon(iconName
));
304 ret
.insert("available", that
->m_audioOutputInfos
[i
].available
);
306 if (that
->m_audioOutputInfos
[i
].driver
== "alsa") {
307 ret
.insert("mixerDeviceId", that
->m_audioOutputInfos
[i
].mixerDevice
);
310 ret
.insert("initialPreference", that
->m_audioOutputInfos
[i
].initialPreference
);
311 ret
.insert("isAdvanced", that
->m_audioOutputInfos
[i
].isAdvanced
);
316 ret
.insert("name", QString());
317 ret
.insert("description", QString());
318 ret
.insert("available", false);
319 ret
.insert("initialPreference", 0);
320 ret
.insert("isAdvanced", false);
324 QByteArray
XineEngine::audioDriverFor(int audioDevice
)
326 XineEngine
*that
= self();
327 that
->checkAudioOutputs();
328 for (int i
= 0; i
< that
->m_audioOutputInfos
.size(); ++i
) {
329 if (that
->m_audioOutputInfos
[i
].index
== audioDevice
) {
330 return that
->m_audioOutputInfos
[i
].driver
;
336 QStringList
XineEngine::alsaDevicesFor(int audioDevice
)
338 XineEngine
*that
= self();
339 that
->checkAudioOutputs();
340 for (int i
= 0; i
< that
->m_audioOutputInfos
.size(); ++i
) {
341 if (that
->m_audioOutputInfos
[i
].index
== audioDevice
) {
342 if (that
->m_audioOutputInfos
[i
].driver
== "alsa") { // only for ALSA
343 return that
->m_audioOutputInfos
[i
].devices
;
347 return QStringList();
350 void XineEnginePrivate::ossSettingChanged(bool useOss
)
352 const XineEngine::UseOss tmp
= useOss
? XineEngine::True
: XineEngine::False
;
353 if (tmp
== s_instance
->m_useOss
) {
356 s_instance
->m_useOss
= tmp
;
358 // add OSS devices if xine supports OSS output
359 const char *const *outputPlugins
= xine_list_audio_output_plugins(s_instance
->xine());
360 for (int i
= 0; outputPlugins
[i
]; ++i
) {
361 if (0 == strcmp(outputPlugins
[i
], "oss")) {
362 QList
<AudioDevice
> audioDevices
= AudioDeviceEnumerator::availablePlaybackDevices();
363 foreach (const AudioDevice
&dev
, audioDevices
) {
364 if (dev
.driver() == Solid::AudioInterface::OpenSoundSystem
) {
365 s_instance
->addAudioOutput(dev
, "oss");
373 // remove all OSS devices
374 typedef QList
<XineEngine::AudioOutputInfo
>::iterator Iterator
;
375 Iterator it
= s_instance
->m_audioOutputInfos
.begin();
376 while (it
!= s_instance
->m_audioOutputInfos
.end()) {
377 if (it
->driver
== "oss") {
378 it
= s_instance
->m_audioOutputInfos
.erase(it
);
387 void XineEngine::addAudioOutput(const AudioDevice
&dev
, const QByteArray
&driver
)
390 int initialPreference
= dev
.initialPreference();
391 if (dev
.driver() == Solid::AudioInterface::Alsa
) {
392 initialPreference
+= 100;
393 foreach (QString id
, dev
.deviceIds()) {
394 const int idx
= id
.indexOf(QLatin1String("CARD="));
396 id
= id
.mid(idx
+ 5);
397 const int commaidx
= id
.indexOf(QLatin1Char(','));
399 id
= id
.left(commaidx
);
401 mixerDevice
= QLatin1String("hw:") + id
;
406 } else if (!dev
.deviceIds().isEmpty()) {
407 initialPreference
+= 50;
408 mixerDevice
= dev
.deviceIds().first();
410 const QString description
= dev
.deviceIds().isEmpty() ?
411 i18n("<html>This device is currently not available (either it is unplugged or the "
412 "driver is not loaded).</html>") :
413 i18n("<html>This will try the following devices and use the first that works: "
414 "<ol><li>%1</li></ol></html>", dev
.deviceIds().join("</li><li>"));
415 AudioOutputInfo
info(dev
.index(), initialPreference
, dev
.cardName(),
416 description
, dev
.iconName(), driver
, dev
.deviceIds(), mixerDevice
);
417 info
.available
= dev
.isAvailable();
418 info
.isAdvanced
= dev
.isAdvancedDevice();
419 if (m_audioOutputInfos
.contains(info
)) {
420 m_audioOutputInfos
.removeAll(info
); // the latest is more up to date wrt availability
422 m_audioOutputInfos
<< info
;
425 void XineEngine::addAudioOutput(int index
, int initialPreference
, const QString
&name
, const QString
&description
,
426 const QString
&icon
, const QByteArray
&driver
, const QStringList
&deviceIds
, const QString
&mixerDevice
, bool isAdvanced
)
428 AudioOutputInfo
info(index
, initialPreference
, name
, description
, icon
, driver
, deviceIds
, mixerDevice
);
429 info
.isAdvanced
= isAdvanced
;
430 const int listIndex
= m_audioOutputInfos
.indexOf(info
);
431 if (listIndex
== -1) {
432 info
.available
= true;
433 m_audioOutputInfos
<< info
;
434 //X KConfigGroup config(m_config, QLatin1String("AudioOutputDevice_") + QString::number(index));
435 //X config.writeEntry("name", name);
436 //X config.writeEntry("description", description);
437 //X config.writeEntry("driver", driver);
438 //X config.writeEntry("icon", icon);
439 //X config.writeEntry("initialPreference", initialPreference);
441 AudioOutputInfo
&infoInList
= m_audioOutputInfos
[listIndex
];
442 if (infoInList
.icon
!= icon
|| infoInList
.initialPreference
!= initialPreference
) {
443 //X KConfigGroup config(m_config, QLatin1String("AudioOutputDevice_") + QString::number(infoInList.index));
445 //X config.writeEntry("icon", icon);
446 //X config.writeEntry("initialPreference", initialPreference);
448 infoInList
.icon
= icon
;
449 infoInList
.initialPreference
= initialPreference
;
451 infoInList
.devices
= deviceIds
;
452 infoInList
.mixerDevice
= mixerDevice
;
453 infoInList
.available
= true;
457 void XineEngine::checkAudioOutputs()
459 if (m_audioOutputInfos
.isEmpty()) {
460 kDebug(610) << "isEmpty";
461 QObject::connect(AudioDeviceEnumerator::self(), SIGNAL(devicePlugged(const AudioDevice
&)),
462 d
, SLOT(devicePlugged(const AudioDevice
&)));
463 QObject::connect(AudioDeviceEnumerator::self(), SIGNAL(deviceUnplugged(const AudioDevice
&)),
464 d
, SLOT(deviceUnplugged(const AudioDevice
&)));
465 int nextIndex
= 10000;
466 //X QStringList groups = m_config->groupList();
467 //X foreach (QString group, groups) {
468 //X if (group.startsWith("AudioOutputDevice_")) {
469 //X const int index = group.right(group.size() - 18/*strlen("AudioOutputDevice_") */).toInt();
470 //X if (index >= nextIndex) {
471 //X nextIndex = index + 1;
473 //X KConfigGroup config(m_config, group);
474 //X m_audioOutputInfos << AudioOutputInfo(index,
475 //X config.readEntry("initialPreference", 0),
476 //X config.readEntry("name", QString()),
477 //X config.readEntry("description", QString()),
478 //X config.readEntry("icon", QString()),
479 //X config.readEntry("driver", QByteArray()),
480 //X QStringList(), QString()); // the device list can change and needs to be queried
481 //X // from the actual hardware configuration
485 // This will list the audio drivers, not the actual devices.
486 const char *const *outputPlugins
= xine_list_audio_output_plugins(xine());
487 for (int i
= 0; outputPlugins
[i
]; ++i
) {
488 kDebug(610) << "outputPlugin: " << outputPlugins
[i
];
489 if (0 == strcmp(outputPlugins
[i
], "alsa")) {
490 if (m_useOss
== XineEngine::Unknown
) {
491 m_useOss
= KConfigGroup(m_config
, "Settings").readEntry("showOssDevices", false) ? XineEngine::True
: XineEngine::False
;
492 if (m_useOss
== XineEngine::False
) {
493 // remove all OSS devices
494 typedef QList
<AudioOutputInfo
>::iterator Iterator
;
495 const Iterator end
= m_audioOutputInfos
.end();
496 Iterator it
= m_audioOutputInfos
.begin();
498 if (it
->driver
== "oss") {
499 it
= m_audioOutputInfos
.erase(it
);
507 QList
<AudioDevice
> alsaDevices
= AudioDeviceEnumerator::availablePlaybackDevices();
508 foreach (const AudioDevice
&dev
, alsaDevices
) {
509 if (dev
.driver() == Solid::AudioInterface::Alsa
) {
510 addAudioOutput(dev
, "alsa");
513 } else if (0 == strcmp(outputPlugins
[i
], "none") || 0 == strcmp(outputPlugins
[i
], "file")) {
514 // ignore these devices
515 } else if (0 == strcmp(outputPlugins
[i
], "oss")) {
517 QList
<AudioDevice
> audioDevices
= AudioDeviceEnumerator::availablePlaybackDevices();
518 foreach (const AudioDevice
&dev
, audioDevices
) {
519 if (dev
.driver() == Solid::AudioInterface::OpenSoundSystem
) {
520 addAudioOutput(dev
, "oss");
524 } else if (0 == strcmp(outputPlugins
[i
], "jack")) {
525 addAudioOutput(nextIndex
++, 9, i18n("Jack Audio Connection Kit"),
526 i18n("<html><p>JACK is a low-latency audio server. It can connect a number "
527 "of different applications to an audio device, as well as allowing "
528 "them to share audio between themselves.</p>"
529 "<p>JACK was designed from the ground up for professional audio "
530 "work, and its design focuses on two key areas: synchronous "
531 "execution of all clients, and low latency operation.</p></html>"),
532 /*icon name */"audio-backend-jack", outputPlugins
[i
], QStringList(),
534 } else if (0 == strcmp(outputPlugins
[i
], "arts")) {
535 addAudioOutput(nextIndex
++, -100, i18n("aRts"),
536 i18n("<html><p>aRts is the old soundserver and media framework that was used "
537 "in KDE2 and KDE3. Its use is discouraged.</p></html>"),
538 /*icon name */"audio-backend-arts", outputPlugins
[i
], QStringList(), QString());
539 } else if (0 == strcmp(outputPlugins
[i
], "pulseaudio")) {
540 addAudioOutput(nextIndex
++, 10, i18n("PulseAudio"),
541 xine_get_audio_driver_plugin_description(xine(), outputPlugins
[i
]),
542 /*icon name */"audio-backend-pulseaudio", outputPlugins
[i
], QStringList(), QString(), true /*isAdvanced*/);
543 } else if (0 == strcmp(outputPlugins
[i
], "esd")) {
544 addAudioOutput(nextIndex
++, 8, i18n("Esound (ESD)"),
545 xine_get_audio_driver_plugin_description(xine(), outputPlugins
[i
]),
546 /*icon name */"audio-backend-esd", outputPlugins
[i
], QStringList(), QString());
548 addAudioOutput(nextIndex
++, -20, outputPlugins
[i
],
549 xine_get_audio_driver_plugin_description(xine(), outputPlugins
[i
]),
550 /*icon name */outputPlugins
[i
], outputPlugins
[i
], QStringList(),
555 qSort(m_audioOutputInfos
);
557 // now m_audioOutputInfos holds all devices this computer has ever seen
558 foreach (const AudioOutputInfo
&info
, m_audioOutputInfos
) {
559 kDebug(610) << info
.index
<< info
.name
<< info
.driver
<< info
.devices
;
564 void XineEnginePrivate::devicePlugged(const AudioDevice
&dev
)
566 kDebug(610) << dev
.cardName();
567 if (!dev
.isPlaybackDevice()) {
570 const char *const *outputPlugins
= xine_list_audio_output_plugins(XineEngine::xine());
571 switch (dev
.driver()) {
572 case Solid::AudioInterface::Alsa
:
573 for (int i
= 0; outputPlugins
[i
]; ++i
) {
574 if (0 == strcmp(outputPlugins
[i
], "alsa")) {
575 s_instance
->addAudioOutput(dev
, "alsa");
579 qSort(s_instance
->m_audioOutputInfos
);
581 case Solid::AudioInterface::OpenSoundSystem
:
582 if (s_instance
->m_useOss
) {
583 for (int i
= 0; outputPlugins
[i
]; ++i
) {
584 if (0 == strcmp(outputPlugins
[i
], "oss")) {
585 s_instance
->addAudioOutput(dev
, "oss");
590 qSort(s_instance
->m_audioOutputInfos
);
592 case Solid::AudioInterface::UnknownAudioDriver
:
597 void XineEnginePrivate::deviceUnplugged(const AudioDevice
&dev
)
599 kDebug(610) << dev
.cardName();
600 if (!dev
.isPlaybackDevice()) {
604 switch (dev
.driver()) {
605 case Solid::AudioInterface::Alsa
:
608 case Solid::AudioInterface::OpenSoundSystem
:
611 case Solid::AudioInterface::UnknownAudioDriver
:
614 XineEngine::AudioOutputInfo
info(dev
.index(), 0, dev
.cardName(), QString(), dev
.iconName(),
615 driver
, dev
.deviceIds(), QString());
616 const int indexOfInfo
= s_instance
->m_audioOutputInfos
.indexOf(info
);
617 if (indexOfInfo
< 0) {
618 kDebug(610) << "told to remove " << dev
.cardName() <<
619 " with driver " << driver
<< " but the device was not present in m_audioOutputInfos";
622 const XineEngine::AudioOutputInfo oldInfo
= s_instance
->m_audioOutputInfos
.takeAt(indexOfInfo
);
623 Q_ASSERT(!s_instance
->m_audioOutputInfos
.contains(info
));
624 info
.initialPreference
= oldInfo
.initialPreference
;
625 s_instance
->m_audioOutputInfos
<< info
; // now the device is listed as not available
626 qSort(s_instance
->m_audioOutputInfos
);
630 } // namespace Phonon
632 #include "xineengine_p.moc"
633 // vim: sw=4 ts=4 tw=100 et