1 /*
2 * Cantata
3 *
4 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5 *
6 * ----
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "httpstream.h"
25 #include "mpdconnection.h"
26 #include "mpdstatus.h"
27 #include "gui/settings.h"
28 #include "support/globalstatic.h"
29 #include "support/configuration.h"
30 #ifndef LIBVLC_FOUND
31 #include <QtMultimedia/QMediaPlayer>
32 #endif
33 #include <QTimer>
34
35 static const int constPlayerCheckPeriod = 250;
36 static const int constMaxPlayStateChecks= 2000 / constPlayerCheckPeriod;
37
38 #include <QDebug>
39 static bool debugEnabled = false;
40 #define DBUG if (debugEnabled) qWarning() << metaObject()->className() << __FUNCTION__
enableDebug()41 void HttpStream::enableDebug()
42 {
43 debugEnabled = true;
44 }
45
GLOBAL_STATIC(HttpStream,instance)46 GLOBAL_STATIC(HttpStream, instance)
47
48 HttpStream::HttpStream(QObject *p)
49 : QObject(p)
50 , enabled(false)
51 , muted(false)
52 , state(MPDState_Inactive)
53 , playStateChecks(0)
54 , currentVolume(50)
55 , unmuteVol(50)
56 , playStateCheckTimer(nullptr)
57 , player(nullptr)
58 {
59 }
60
save() const61 void HttpStream::save() const
62 {
63 Configuration config(metaObject()->className());
64 config.set("volume", currentVolume);
65 }
66
setEnabled(bool e)67 void HttpStream::setEnabled(bool e)
68 {
69 if (e == enabled) {
70 return;
71 }
72
73 enabled = e;
74 if (enabled) {
75 connect(MPDConnection::self(), SIGNAL(streamUrl(QString)), this, SLOT(streamUrl(QString)));
76 connect(MPDStatus::self(), SIGNAL(updated()), this, SLOT(updateStatus()));
77 streamUrl(MPDConnection::self()->getDetails().streamUrl);
78 } else {
79 disconnect(MPDConnection::self(), SIGNAL(streamUrl(QString)), this, SLOT(streamUrl(QString)));
80 disconnect(MPDStatus::self(), SIGNAL(updated()), this, SLOT(updateStatus()));
81 if (player) {
82 save();
83 #ifdef LIBVLC_FOUND
84 libvlc_media_player_stop(player);
85 #else
86 player->stop();
87 #endif
88 }
89 }
90 emit isEnabled(enabled);
91 }
92
setVolume(int vol)93 void HttpStream::setVolume(int vol)
94 {
95 DBUG << vol;
96 if (player) {
97 currentVolume = vol;
98 #ifdef LIBVLC_FOUND
99 libvlc_audio_set_volume(player, vol);
100 #else
101 player->setVolume(vol);
102 #endif
103 emit update();
104 }
105 }
106
volume()107 int HttpStream::volume()
108 {
109 if (!enabled) {
110 return -1;
111 }
112
113 int vol = currentVolume;
114 if (player && !isMuted()) {
115 #ifdef LIBVLC_FOUND
116 vol = libvlc_audio_get_volume(player);
117 #else
118 vol = player->volume();
119 #endif
120 if (vol < 0) {
121 vol = currentVolume;
122 } else {
123 currentVolume = vol;
124 }
125 }
126 DBUG << vol;
127 return vol;
128 }
129
toggleMute()130 void HttpStream::toggleMute()
131 {
132 DBUG << isMuted();
133 if (player) {
134 muted =! muted;
135 #ifdef LIBVLC_FOUND
136 libvlc_audio_set_mute(player, muted);
137 #else
138 player->setMuted(!muted);
139 #endif
140 emit update();
141 }
142 }
143
streamUrl(const QString & url)144 void HttpStream::streamUrl(const QString &url)
145 {
146 DBUG << url;
147 #ifdef LIBVLC_FOUND
148 if (player) {
149 libvlc_media_player_stop(player);
150 libvlc_media_player_release(player);
151 libvlc_release(instance);
152 player = 0;
153 }
154 #else
155 if (player) {
156 QMediaContent media = player->media();
157 if (media != nullptr && media.canonicalUrl() != url) {
158 player->stop();
159 player->deleteLater();
160 player = nullptr;
161 }
162 }
163 #endif
164 QUrl qUrl(url);
165 if (!url.isEmpty() && qUrl.isValid() && qUrl.scheme().startsWith("http") && !player) {
166 #ifdef LIBVLC_FOUND
167 instance = libvlc_new(0, NULL);
168 QByteArray byteArrayUrl = url.toUtf8();
169 media = libvlc_media_new_location(instance, byteArrayUrl.constData());
170 player = libvlc_media_player_new_from_media(media);
171 libvlc_media_release(media);
172 #else
173 player = new QMediaPlayer(this);
174 player->setMedia(qUrl);
175 connect(player, &QMediaPlayer::bufferStatusChanged, this, &HttpStream::bufferingProgress);
176 #endif
177 muted = false;
178 setVolume(Configuration(metaObject()->className()).get("volume", currentVolume));
179 }
180 if (player) {
181 state = 0xFFFF; // Force an update...
182 updateStatus();
183 } else {
184 state = MPDState_Inactive;
185 }
186 emit update();
187 }
188
189 #ifndef LIBVLC_FOUND
bufferingProgress(int progress)190 void HttpStream::bufferingProgress(int progress)
191 {
192 MPDStatus * const status = MPDStatus::self();
193 if (status->state() == MPDState_Playing) {
194 if (progress == 100) {
195 player->play();
196 } else {
197 player->pause();
198 }
199 }
200 }
201 #endif
202
updateStatus()203 void HttpStream::updateStatus()
204 {
205 if (!player) {
206 return;
207 }
208
209 MPDStatus * const status = MPDStatus::self();
210 DBUG << status->state() << state;
211
212 // evaluates to true when it is needed to start or restart media player
213 bool playerNeedsToStart = status->state() == MPDState_Playing;
214 #ifdef LIBVLC_FOUND
215 playerNeedsToStart = playerNeedsToStart && libvlc_media_player_get_state(player) != libvlc_Playing;
216 #else
217 playerNeedsToStart = playerNeedsToStart && player->state() == QMediaPlayer::StoppedState;
218 #endif
219
220 if (status->state() == state && !playerNeedsToStart) {
221 return;
222 }
223
224 state = status->state();
225 switch (status->state()) {
226 case MPDState_Playing:
227 // Only start playback if not aready playing
228 if (playerNeedsToStart) {
229 #ifdef LIBVLC_FOUND
230 libvlc_media_player_play(player);
231 startTimer();
232 #else
233 QUrl url = player->media().canonicalUrl();
234 player->setMedia(url);
235 #endif
236 }
237 break;
238 case MPDState_Paused:
239 case MPDState_Inactive:
240 case MPDState_Stopped:
241 #ifdef LIBVLC_FOUND
242 libvlc_media_player_stop(player);
243 stopTimer();
244 #else
245 player->stop();
246 #endif
247 break;
248 default:
249 #ifdef LIBVLC_FOUND
250 stopTimer();
251 #endif
252 break;
253 }
254 }
255
checkPlayer()256 void HttpStream::checkPlayer()
257 {
258 #ifdef LIBVLC_FOUND
259 if (0 == --playStateChecks) {
260 stopTimer();
261 DBUG << "Max checks reached";
262 }
263 if (libvlc_media_player_get_state(player) == libvlc_Playing) {
264 DBUG << "Playing";
265 stopTimer();
266 } else {
267 DBUG << "Try again";
268 libvlc_media_player_play(player);
269 }
270 #endif
271 }
272
startTimer()273 void HttpStream::startTimer()
274 {
275 if (!playStateCheckTimer) {
276 playStateCheckTimer = new QTimer(this);
277 playStateCheckTimer->setSingleShot(false);
278 playStateCheckTimer->setInterval(constPlayerCheckPeriod);
279 connect(playStateCheckTimer, SIGNAL(timeout()), SLOT(checkPlayer()));
280 }
281 playStateChecks = constMaxPlayStateChecks;
282 DBUG << playStateChecks;
283 playStateCheckTimer->start();
284 }
285
stopTimer()286 void HttpStream::stopTimer()
287 {
288 if (playStateCheckTimer) {
289 DBUG;
290 playStateCheckTimer->stop();
291 }
292 playStateChecks = 0;
293 }
294
295 #include "moc_httpstream.cpp"
296