1 | /* |
2 | Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com> |
3 | Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com> |
4 | Copyright (C) 2009 Fathi Boudra <fabo@kde.org> |
5 | Copyright (C) 2013-2018 Harald Sitter <sitter@kde.org> |
6 | |
7 | This library is free software; you can redistribute it and/or |
8 | modify it under the terms of the GNU Lesser General Public |
9 | License as published by the Free Software Foundation; either |
10 | version 2.1 of the License, or (at your option) any later version. |
11 | |
12 | This library is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | Lesser General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Lesser General Public |
18 | License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "audiooutput.h" |
22 | |
23 | #include <phonon/pulsesupport.h> |
24 | |
25 | #include <vlc/vlc.h> |
26 | |
27 | #include "backend.h" |
28 | #include "utils/debug.h" |
29 | #include "devicemanager.h" |
30 | #include "mediaobject.h" |
31 | #include "media.h" |
32 | |
33 | namespace Phonon { |
34 | namespace VLC { |
35 | |
36 | AudioOutput::AudioOutput(QObject *parent) |
37 | : QObject(parent) |
38 | , m_volume(0.75) |
39 | , m_explicitVolume(false) |
40 | , m_muted(false) |
41 | , m_category(Phonon::NoCategory) |
42 | { |
43 | } |
44 | |
45 | AudioOutput::~AudioOutput() |
46 | { |
47 | } |
48 | |
49 | static libvlc_media_player_role categoryToRole(Category category) |
50 | { |
51 | switch(category) { |
52 | case NoCategory: |
53 | return libvlc_role_None; |
54 | case NotificationCategory: |
55 | return libvlc_role_Notification; |
56 | case MusicCategory: |
57 | return libvlc_role_Music; |
58 | case VideoCategory: |
59 | return libvlc_role_Video; |
60 | case CommunicationCategory: |
61 | return libvlc_role_Communication; |
62 | case GameCategory: |
63 | return libvlc_role_Game; |
64 | case AccessibilityCategory: |
65 | return libvlc_role_Accessibility; |
66 | } |
67 | return libvlc_role_None; |
68 | } |
69 | |
70 | void AudioOutput::handleConnectToMediaObject(MediaObject *mediaObject) |
71 | { |
72 | Q_UNUSED(mediaObject); |
73 | setOutputDeviceImplementation(); |
74 | if (!PulseSupport::getInstance()->isActive()) { |
75 | // Rely on libvlc for updates if PASupport is not active |
76 | connect(sender: m_player, SIGNAL(mutedChanged(bool)), |
77 | receiver: this, SLOT(onMutedChanged(bool))); |
78 | connect(sender: m_player, SIGNAL(volumeChanged(float)), |
79 | receiver: this, SLOT(onVolumeChanged(float))); |
80 | applyVolume(); |
81 | } |
82 | libvlc_media_player_set_role(p_mi: *m_player, role: categoryToRole(category: m_category)); |
83 | } |
84 | |
85 | void AudioOutput::handleAddToMedia(Media *media) |
86 | { |
87 | media->addOption(option: ":audio" ); |
88 | PulseSupport *pulse = PulseSupport::getInstance(); |
89 | if (pulse && pulse->isActive()) { |
90 | pulse->setupStreamEnvironment(m_streamUuid); |
91 | } |
92 | } |
93 | |
94 | qreal AudioOutput::volume() const |
95 | { |
96 | return m_volume; |
97 | } |
98 | |
99 | void AudioOutput::setVolume(qreal volume) |
100 | { |
101 | if (m_player) { |
102 | debug() << "async setting of volume to" << volume; |
103 | m_volume = volume; |
104 | m_explicitVolume = true; |
105 | applyVolume(); |
106 | } |
107 | } |
108 | |
109 | void AudioOutput::setMuted(bool mute) |
110 | { |
111 | if (mute == m_player->mute()) { |
112 | // Make sure we actually have propagated the mutness into the frontend. |
113 | onMutedChanged(mute); |
114 | return; |
115 | } |
116 | m_player->setMute(mute); |
117 | } |
118 | |
119 | void AudioOutput::setCategory(Category category) |
120 | { |
121 | m_category = category; |
122 | } |
123 | |
124 | int AudioOutput::outputDevice() const |
125 | { |
126 | return m_device.index(); |
127 | } |
128 | |
129 | bool AudioOutput::setOutputDevice(int deviceIndex) |
130 | { |
131 | const AudioOutputDevice device = AudioOutputDevice::fromIndex(index: deviceIndex); |
132 | if (!device.isValid()) { |
133 | error() << Q_FUNC_INFO << "Unable to find the output device with index" << deviceIndex; |
134 | return false; |
135 | } |
136 | return setOutputDevice(device); |
137 | } |
138 | |
139 | bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice) |
140 | { |
141 | debug() << Q_FUNC_INFO; |
142 | |
143 | if (!newDevice.isValid()) { |
144 | error() << "Invalid audio output device" ; |
145 | return false; |
146 | } |
147 | |
148 | if (newDevice == m_device) |
149 | return true; |
150 | |
151 | m_device = newDevice; |
152 | if (m_player) { |
153 | setOutputDeviceImplementation(); |
154 | } |
155 | |
156 | return true; |
157 | } |
158 | |
159 | void AudioOutput::setStreamUuid(QString uuid) |
160 | { |
161 | DEBUG_BLOCK; |
162 | debug() << uuid; |
163 | m_streamUuid = uuid; |
164 | } |
165 | |
166 | void AudioOutput::setOutputDeviceImplementation() |
167 | { |
168 | Q_ASSERT(m_player); |
169 | |
170 | // VLC 2.2 has the PulseSupport overrides always disabled because of |
171 | // incompatibility. Also see backend.cpp for more detals. |
172 | // To get access to the correct activity state we need to temporarily |
173 | // enable pulse and then disable it again. This is necessary because isActive |
174 | // is in fact isActive&isEnabled............. |
175 | PulseSupport::getInstance()->enable(enabled: true); |
176 | const bool pulseActive = PulseSupport::getInstance()->isActive(); |
177 | PulseSupport::getInstance()->enable(enabled: false); |
178 | |
179 | if (pulseActive) { |
180 | m_player->setAudioOutput("pulse" ); |
181 | debug() << "Setting aout to pulse" ; |
182 | return; |
183 | } |
184 | |
185 | const QVariant dalProperty = m_device.property(name: "deviceAccessList" ); |
186 | if (!dalProperty.isValid()) { |
187 | error() << "Device" << m_device.property(name: "name" ) << "has no access list" ; |
188 | return; |
189 | } |
190 | const DeviceAccessList deviceAccessList = dalProperty.value<DeviceAccessList>(); |
191 | if (deviceAccessList.isEmpty()) { |
192 | error() << "Device" << m_device.property(name: "name" ) << "has an empty access list" ; |
193 | return; |
194 | } |
195 | |
196 | // ### we're not trying the whole access list (could mean same device on different soundsystems) |
197 | const DeviceAccess &firstDeviceAccess = deviceAccessList.first(); |
198 | |
199 | QByteArray soundSystem = firstDeviceAccess.first; |
200 | debug() << "Setting output soundsystem to" << soundSystem; |
201 | m_player->setAudioOutput(soundSystem); |
202 | |
203 | QByteArray deviceName = firstDeviceAccess.second.toLatin1(); |
204 | if (!deviceName.isEmpty()) { |
205 | // print the name as possibly messed up by toLatin1() to see conversion problems |
206 | debug() << "Setting output device to" << deviceName << '(' << m_device.property(name: "name" ) << ')'; |
207 | m_player->setAudioOutputDevice(outputName: soundSystem, deviceName); |
208 | } |
209 | } |
210 | |
211 | void AudioOutput::applyVolume() |
212 | { |
213 | if (m_player && m_explicitVolume) { |
214 | const int preVolume = m_player->audioVolume(); |
215 | const int newVolume = m_volume * 100; |
216 | m_player->setAudioVolume(newVolume); |
217 | |
218 | debug() << "Volume changed from" << preVolume << "to" << newVolume; |
219 | } |
220 | } |
221 | |
222 | void AudioOutput::onMutedChanged(bool mute) |
223 | { |
224 | m_muted = mute; |
225 | emit mutedChanged(mute); |
226 | } |
227 | |
228 | void AudioOutput::onVolumeChanged(float volume) |
229 | { |
230 | m_volume = volume; |
231 | emit volumeChanged(volume); |
232 | } |
233 | |
234 | } |
235 | } // Namespace Phonon::VLC |
236 | |