MythTV master
decoderhandler.cpp
Go to the documentation of this file.
1// c/c++
2#include <cassert>
3#include <cstdio>
4#include <unistd.h>
5
6// qt
7#include <QApplication>
8#include <QFileInfo>
9#include <QUrl>
10
11// MythTV
18
19// mythmusic
20#include "decoder.h"
21#include "decoderhandler.h"
22
23/**********************************************************************/
24
25const QEvent::Type DecoderHandlerEvent::kReady = (QEvent::Type) QEvent::registerEventType();
26const QEvent::Type DecoderHandlerEvent::kMeta = (QEvent::Type) QEvent::registerEventType();
27const QEvent::Type DecoderHandlerEvent::kBufferStatus = (QEvent::Type) QEvent::registerEventType();
28const QEvent::Type DecoderHandlerEvent::kOperationStart = (QEvent::Type) QEvent::registerEventType();
29const QEvent::Type DecoderHandlerEvent::kOperationStop = (QEvent::Type) QEvent::registerEventType();
30const QEvent::Type DecoderHandlerEvent::kError = (QEvent::Type) QEvent::registerEventType();
31
33 : MythEvent(type),
34 m_meta(new MusicMetadata(meta))
35{
36}
37
39{
40 delete m_msg;
41 delete m_meta;
42}
43
45{
46 auto *result = new DecoderHandlerEvent(*this);
47
48 if (m_msg)
49 result->m_msg = new QString(*m_msg);
50
51 if (m_meta)
52 result->m_meta = new MusicMetadata(*m_meta);
53
54 result->m_available = m_available;
55 result->m_maxSize = m_maxSize;
56
57 return result;
58}
59
60void DecoderHandlerEvent::getBufferStatus(int *available, int *maxSize) const
61{
62 *available = m_available;
63 *maxSize = m_maxSize;
64}
65
66/**********************************************************************/
67
69{
70 stop();
71}
72
74{
76
78 m_meta = *mdata;
79 m_playlistPos = -1;
80 m_redirects = 0;
81
82 if (QFileInfo(mdata->Filename()).isAbsolute())
83 m_url = QUrl::fromLocalFile(mdata->Filename());
84 else
85 m_url.setUrl(mdata->Filename());
86
88}
89
90void DecoderHandler::doStart(bool result)
91{
93
94 if (QFileInfo(m_meta.Filename()).isAbsolute())
95 m_url = QUrl::fromLocalFile(m_meta.Filename());
96 else
97 m_url.setUrl(m_meta.Filename());
98
99 if (m_state == LOADING && result)
100 {
101 for (int ii = 0; ii < m_playlist.size(); ii++)
102 {
104 LOG(VB_PLAYBACK, LOG_INFO, QString("Track %1 = %2")
105 .arg(ii) .arg(file ? file->File() : "<invalid>"));
106 }
107 next();
108 }
109 else
110 {
111 if (m_state == STOPPED)
112 {
113 doFailed(m_url, "Could not get playlist");
114 }
115 }
116}
117
118void DecoderHandler::error(const QString &e)
119{
120 auto *str = new QString(e);
122 dispatch(ev);
123}
124
126{
127 if (m_state == STOPPED)
128 return true;
129
130 if (m_playlistPos + 1 >= m_playlist.size())
131 {
133 return true;
134 }
135
136 return false;
137}
138
140{
141 if (done())
142 return false;
143
144 if (m_meta.Format() == "cast")
145 {
147 }
148 else
149 {
151 }
152
154 if (nullptr == entry)
155 return false;
156
157 if (QFileInfo(entry->File()).isAbsolute())
158 m_url = QUrl::fromLocalFile(entry->File());
159 else
160 m_url.setUrl(entry->File());
161
162 LOG(VB_PLAYBACK, LOG_INFO, QString("Now playing '%1'").arg(m_url.toString()));
163
164 // we use the avfdecoder for everything except CD tracks
165 if (m_url.toString().endsWith(".cda"))
166 doConnectDecoder(m_url, ".cda");
167 else
168 {
169 // we don't know what format radio stations are so fake a format
170 // and hope avfdecoder can decode it
171 doConnectDecoder(m_url, ".mp3");
172 }
173
174 m_state = ACTIVE;
175
176 return true;
177}
178
180{
181 LOG(VB_PLAYBACK, LOG_INFO, QString("DecoderHandler: Stopping decoder"));
182
183 if (m_decoder && m_decoder->isRunning())
184 {
185 m_decoder->lock();
186 m_decoder->stop();
187 m_decoder->unlock();
188 }
189
190 if (m_decoder)
191 {
192 m_decoder->lock();
193 m_decoder->cond()->wakeAll();
194 m_decoder->unlock();
195 }
196
197 if (m_decoder)
198 {
199 m_decoder->wait();
200 delete m_decoder;
201 m_decoder = nullptr;
202 }
203
205
207}
208
210{
211 if (event == nullptr)
212 return;
213
214 if (auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event))
215 {
216 // Proxy all DecoderHandlerEvents
217 dispatch(*dhe);
218 return;
219 }
220 if (event->type() == MythEvent::kMythEventMessage)
221 {
222 auto *me = dynamic_cast<MythEvent *>(event);
223 if (me == nullptr)
224 return;
225 QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
226
227 if (tokens.isEmpty())
228 return;
229
230 if (tokens[0] == "DOWNLOAD_FILE")
231 {
232 QStringList args = me->ExtraDataList();
233
234 if (tokens[1] == "UPDATE")
235 {
236 }
237 else if (tokens[1] == "FINISHED")
238 {
239 const QString& downloadUrl = args[0];
240 int fileSize = args[2].toInt();
241 int errorCode = args[4].toInt();
242 const QString& filename = args[1];
243
244 if ((errorCode != 0) || (fileSize == 0))
245 {
246 LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler: failed to download playlist from '%1'")
247 .arg(downloadUrl));
248 QUrl url(downloadUrl);
251 doFailed(url, "Could not get playlist");
252 }
253 else
254 {
255 QUrl fileUrl = QUrl::fromLocalFile(filename);
256 createPlaylistFromFile(fileUrl);
257 }
258 }
259 }
260 }
261}
262
264{
265 QString extension = QFileInfo(url.path()).suffix();
266 LOG(VB_NETWORK, LOG_INFO,
267 QString("File %1 has extension %2")
268 .arg(QFileInfo(url.path()).fileName(), extension));
269
270 if (extension == "pls" || extension == "m3u" || extension == "asx")
271 {
272 if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
274 else
276
277 return;
278 }
279
281}
282
284{
285 auto *entry = new PlayListFileEntry;
286
287 if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
288 entry->setFile(url.toLocalFile());
289 else
290 entry->setFile(url.toString());
291
292 m_playlist.add(entry);
293
294 doStart((m_playlist.size() > 0));
295}
296
298{
299 QString file = url.toLocalFile();
300
302
303 doStart((m_playlist.size() > 0));
304}
305
307{
308 LOG(VB_NETWORK, LOG_INFO,
309 QString("Retrieving playlist from '%1'").arg(url.toString()));
310
311 doOperationStart(tr("Retrieving playlist"));
312
313 QString extension = QFileInfo(url.path()).suffix().toLower();
314 QString saveFilename = GetConfDir() + "/MythMusic/playlist." + extension;
315 GetMythDownloadManager()->queueDownload(url.toString(), saveFilename, this);
316
317 //TODO should find a better way to do this
318 QElapsedTimer time;
319 time.start();
320 while (m_state == LOADING)
321 {
322 if (time.hasExpired(30000))
323 {
325 GetMythDownloadManager()->cancelDownload(url.toString());
326 LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler:: Timed out trying to download playlist from: %1")
327 .arg(url.toString()));
329 }
330
331 QCoreApplication::processEvents();
332 usleep(500);
333 }
334}
335
336void DecoderHandler::doConnectDecoder(const QUrl &url, const QString &format)
337{
338 if (m_decoder && !m_decoder->factory()->supports(format))
339 {
340 delete m_decoder;
341 m_decoder = nullptr;
342 }
343
344 if (!m_decoder)
345 {
346 m_decoder = Decoder::create(format, nullptr, true);
347 if (m_decoder == nullptr)
348 {
349 doFailed(url, QString("No decoder for this format '%1'").arg(format));
350 return;
351 }
352 }
353
354 m_decoder->setURL(url.toString());
355
357 dispatch(ev);
358}
359
360void DecoderHandler::doFailed(const QUrl &url, const QString &message)
361{
362 LOG(VB_NETWORK, LOG_ERR,
363 QString("DecoderHandler error: '%1' - %2").arg(message, url.toString()));
364 DecoderHandlerEvent ev(DecoderHandlerEvent::kError, new QString(message));
365 dispatch(ev);
366}
367
368void DecoderHandler::doOperationStart(const QString &name)
369{
370 m_op = true;
372 dispatch(ev);
373}
374
376{
377 if (!m_op)
378 return;
379
380 m_op = false;
382 dispatch(ev);
383}
virtual bool supports(const QString &source) const =0
Events sent by the DecoderHandler and it's helper classes.
MusicMetadata * m_meta
DecoderHandlerEvent(Type type)
~DecoderHandlerEvent() override
static const Type kBufferStatus
static const Type kError
static const Type kOperationStart
void getBufferStatus(int *available, int *maxSize) const
static const Type kReady
static const Type kOperationStop
static const Type kMeta
MythEvent * clone(void) const override
void doOperationStop(void)
MusicMetadata m_meta
void createPlaylist(const QUrl &url)
~DecoderHandler(void) override
void doConnectDecoder(const QUrl &url, const QString &format)
void doFailed(const QUrl &url, const QString &message)
void customEvent(QEvent *e) override
PlayListFile m_playlist
void start(MusicMetadata *mdata)
void error(const QString &e)
void doOperationStart(const QString &name)
void doStart(bool result)
void createPlaylistFromRemoteUrl(const QUrl &url)
void createPlaylistFromFile(const QUrl &url)
void createPlaylistForSingleFile(const QUrl &url)
Decoder * m_decoder
virtual void lock(void)
Definition: decoder.h:85
virtual void stop()=0
virtual void unlock(void)
Definition: decoder.h:86
static Decoder * create(const QString &source, AudioOutput *output, bool deletable=false)
Definition: decoder.cpp:98
void setURL(const QString &url)
Definition: decoder.h:83
QWaitCondition * cond()
Definition: decoder.h:89
DecoderFactory * factory() const
Definition: decoder.h:79
bool isRunning(void) const
Definition: mthread.cpp:263
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
QString Filename(bool find=true)
QString Format() const
void cancelDownload(const QString &url, bool block=true)
Cancel a queued or current download.
void queueDownload(const QString &url, const QString &dest, QObject *caller, bool reload=false)
Adds a url to the download queue.
This class is used as a container for messages.
Definition: mythevent.h:17
const QString & Message() const
Definition: mythevent.h:65
static const Type kMythEventMessage
Definition: mythevent.h:79
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
Class for representing entries in a pls file.
Definition: pls.h:20
QString File(void)
Definition: pls.h:25
void setFile(const QString &f)
Definition: pls.h:29
void add(PlayListFileEntry *e)
Add a entry to the playlist.
Definition: pls.h:80
void clear(void)
Clear out all the entries.
Definition: pls.h:83
static int parse(PlayListFile *pls, const QString &filename)
Parse a pls, m3u or asx playlist file.
Definition: pls.cpp:34
PlayListFileEntry * get(int i)
Get a file entry.
Definition: pls.h:60
int size(void) const
Get the number of entries in the pls file.
Definition: pls.h:54
QString GetConfDir(void)
Definition: mythdirs.cpp:263
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20