Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / runtime / phonon / xine / xineengine.cpp
blob872b505b8540e6b9899509bbd7b0d74f50523513
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>
29 #include <QList>
30 #include <kconfiggroup.h>
31 #include <kicon.h>
32 #include "videowidget.h"
33 #include <klocale.h>
34 #include "xineengine_p.h"
35 #include "backend.h"
36 #include "events.h"
37 #include "xinethread.h"
39 namespace Phonon
41 namespace Xine
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()
52 kDebug(610) ;
53 emit objectDescriptionChanged(AudioOutputDeviceType);
56 static XineEngine *s_instance = 0;
58 XineEngine::XineEngine(const KSharedConfigPtr &_config)
59 : m_xine(xine_new()),
60 m_config(_config),
61 m_useOss(XineEngine::Unknown),
62 m_inShutdown(false),
63 d(new XineEnginePrivate),
64 m_nullPort(0),
65 m_nullVideoPort(0),
66 m_thread(0)
68 Q_ASSERT(s_instance == 0);
69 s_instance = this;
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()
80 m_inShutdown = true;
81 /*if (m_thread) {
82 m_thread->stopAllStreams();
83 }*/
84 QList<QObject *> cleanupObjects(m_cleanupObjects);
85 const QList<QObject *>::Iterator end = cleanupObjects.end();
86 QList<QObject *>::Iterator it = cleanupObjects.begin();
87 while (it != end) {
88 kDebug(610) << "delete" << (*it)->metaObject()->className();
89 delete *it;
90 ++it;
92 //qDeleteAll(cleanupObjects);
93 if (m_thread) {
94 m_thread->quit();
95 if (!m_thread->wait(10000)) {
96 // timed out
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();
102 delete m_thread;
104 //kDebug(610) ;
105 if (m_nullPort) {
106 xine_close_audio_driver(m_xine, m_nullPort);
108 if (m_nullVideoPort) {
109 xine_close_video_driver(m_xine, m_nullVideoPort);
111 xine_exit(m_xine);
112 m_xine = 0;
113 s_instance = 0;
114 delete d;
117 XineEngine *XineEngine::self()
119 Q_ASSERT(s_instance);
120 return s_instance;
123 const QObject *XineEngine::sender()
125 return self()->d;
128 xine_t *XineEngine::xine()
130 return self()->m_xine;
133 XineThread *XineEngine::thread()
135 XineEngine *const e = self();
136 if (!e->m_thread) {
137 e->m_thread = new XineThread;
138 e->m_thread->moveToThread(e->m_thread);
139 e->m_thread->start();
140 e->m_thread->waitForEventLoop();
142 return e->m_thread;
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) {
168 return;
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));
177 break;
178 case XINE_EVENT_UI_PLAYBACK_FINISHED: /* frontend can e.g. move on to next playlist entry */
179 QCoreApplication::postEvent(xs, new QEVENT(MediaFinished));
180 break;
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));
186 break;
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));
192 } else {
193 xs->handleDownstreamEvent(new QEVENT(NavButtonOut));
196 break;
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));
202 break;
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));
214 break;
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));
221 break;
222 case XINE_EVENT_AUDIO_LEVEL: /* report current audio level (l/r/mute) */
223 kDebug(610) << "XINE_EVENT_AUDIO_LEVEL";
224 break;
225 case XINE_EVENT_QUIT: /* last event sent when stream is disposed */
226 kDebug(610) << "XINE_EVENT_QUIT";
227 break;
228 case XINE_EVENT_UI_NUM_BUTTONS: /* number of buttons for interactive menus */
229 kDebug(610) << "XINE_EVENT_UI_NUM_BUTTONS";
230 break;
231 case XINE_EVENT_DROPPED_FRAMES: /* number of dropped frames is too high */
232 kDebug(610) << "XINE_EVENT_DROPPED_FRAMES";
233 break;
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));
245 break;
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();
271 QList<int> list;
272 for (int i = 0; i < that->m_audioOutputInfos.size(); ++i) {
273 list << that->m_audioOutputInfos[i].index;
275 return list;
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);
313 return ret;
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);
321 return ret;
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;
333 return QByteArray();
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) {
354 return;
356 s_instance->m_useOss = tmp;
357 if (useOss) {
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");
368 signalTimer.start();
369 return;
372 } else {
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);
379 } else {
380 ++it;
383 signalTimer.start();
387 void XineEngine::addAudioOutput(const AudioDevice &dev, const QByteArray &driver)
389 QString mixerDevice;
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="));
395 if (idx > 0) {
396 id = id.mid(idx + 5);
397 const int commaidx = id.indexOf(QLatin1Char(','));
398 if (commaidx > 0) {
399 id = id.left(commaidx);
401 mixerDevice = QLatin1String("hw:") + id;
402 break;
404 mixerDevice = 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);
440 } else {
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;
472 //X }
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
482 //X }
483 //X }
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();
497 while (it != end) {
498 if (it->driver == "oss") {
499 it = m_audioOutputInfos.erase(it);
500 } else {
501 ++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")) {
516 if (m_useOss) {
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(),
533 QString());
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());
547 } else {
548 addAudioOutput(nextIndex++, -20, outputPlugins[i],
549 xine_get_audio_driver_plugin_description(xine(), outputPlugins[i]),
550 /*icon name */outputPlugins[i], outputPlugins[i], QStringList(),
551 QString());
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()) {
568 return;
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");
576 signalTimer.start();
579 qSort(s_instance->m_audioOutputInfos);
580 break;
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");
586 signalTimer.start();
590 qSort(s_instance->m_audioOutputInfos);
591 break;
592 case Solid::AudioInterface::UnknownAudioDriver:
593 break;
597 void XineEnginePrivate::deviceUnplugged(const AudioDevice &dev)
599 kDebug(610) << dev.cardName();
600 if (!dev.isPlaybackDevice()) {
601 return;
603 QByteArray driver;
604 switch (dev.driver()) {
605 case Solid::AudioInterface::Alsa:
606 driver = "alsa";
607 break;
608 case Solid::AudioInterface::OpenSoundSystem:
609 driver = "oss";
610 break;
611 case Solid::AudioInterface::UnknownAudioDriver:
612 break;
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";
620 return;
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);
627 signalTimer.start();
629 } // namespace Xine
630 } // namespace Phonon
632 #include "xineengine_p.moc"
633 // vim: sw=4 ts=4 tw=100 et