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
13 #include <libmythbase/mythdirs.h>
16 #include <libmythbase/mythrandom.h>
17 #include <libmythbase/remotefile.h>
19 
20 // mythmusic
21 #include "decoder.h"
22 #include "decoderhandler.h"
23 
24 /**********************************************************************/
25 
26 const QEvent::Type DecoderHandlerEvent::kReady = (QEvent::Type) QEvent::registerEventType();
27 const QEvent::Type DecoderHandlerEvent::kMeta = (QEvent::Type) QEvent::registerEventType();
28 const QEvent::Type DecoderHandlerEvent::kBufferStatus = (QEvent::Type) QEvent::registerEventType();
29 const QEvent::Type DecoderHandlerEvent::kOperationStart = (QEvent::Type) QEvent::registerEventType();
30 const QEvent::Type DecoderHandlerEvent::kOperationStop = (QEvent::Type) QEvent::registerEventType();
31 const QEvent::Type DecoderHandlerEvent::kError = (QEvent::Type) QEvent::registerEventType();
32 
34  : MythEvent(type)
35 {
36  m_meta = new MusicMetadata(meta);
37 }
38 
40 {
41  delete m_msg;
42  delete m_meta;
43 }
44 
46 {
47  auto *result = new DecoderHandlerEvent(*this);
48 
49  if (m_msg)
50  result->m_msg = new QString(*m_msg);
51 
52  if (m_meta)
53  result->m_meta = new MusicMetadata(*m_meta);
54 
55  result->m_available = m_available;
56  result->m_maxSize = m_maxSize;
57 
58  return result;
59 }
60 
61 void DecoderHandlerEvent::getBufferStatus(int *available, int *maxSize) const
62 {
63  *available = m_available;
64  *maxSize = m_maxSize;
65 }
66 
67 /**********************************************************************/
68 
70 {
71  stop();
72 }
73 
75 {
76  m_state = LOADING;
77 
78  m_playlist.clear();
79  m_meta = *mdata;
80  m_playlistPos = -1;
81  m_redirects = 0;
82 
83  if (QFileInfo(mdata->Filename()).isAbsolute())
84  m_url = QUrl::fromLocalFile(mdata->Filename());
85  else
86  m_url.setUrl(mdata->Filename());
87 
89 }
90 
91 void DecoderHandler::doStart(bool result)
92 {
94 
95  if (QFileInfo(m_meta.Filename()).isAbsolute())
96  m_url = QUrl::fromLocalFile(m_meta.Filename());
97  else
98  m_url.setUrl(m_meta.Filename());
99 
100  if (m_state == LOADING && result)
101  {
102  for (int ii = 0; ii < m_playlist.size(); ii++)
103  LOG(VB_PLAYBACK, LOG_INFO, QString("Track %1 = %2")
104  .arg(ii) .arg(m_playlist.get(ii)->File()));
105  next();
106  }
107  else
108  {
109  if (m_state == STOPPED)
110  {
111  doFailed(m_url, "Could not get playlist");
112  }
113  }
114 }
115 
116 void DecoderHandler::error(const QString &e)
117 {
118  auto *str = new QString(e);
120  dispatch(ev);
121 }
122 
124 {
125  if (m_state == STOPPED)
126  return true;
127 
128  if (m_playlistPos + 1 >= m_playlist.size())
129  {
130  m_state = STOPPED;
131  return true;
132  }
133 
134  return false;
135 }
136 
138 {
139  if (done())
140  return false;
141 
142  if (m_meta.Format() == "cast")
143  {
145  }
146  else
147  {
148  m_playlistPos++;
149  }
150 
152 
153  if (QFileInfo(entry->File()).isAbsolute())
154  m_url = QUrl::fromLocalFile(entry->File());
155  else
156  m_url.setUrl(entry->File());
157 
158  LOG(VB_PLAYBACK, LOG_INFO, QString("Now playing '%1'").arg(m_url.toString()));
159 
160  // we use the avfdecoder for everything except CD tracks
161  if (m_url.toString().endsWith(".cda"))
162  doConnectDecoder(m_url, ".cda");
163  else
164  {
165  // we don't know what format radio stations are so fake a format
166  // and hope avfdecoder can decode it
167  doConnectDecoder(m_url, ".mp3");
168  }
169 
170  m_state = ACTIVE;
171 
172  return true;
173 }
174 
176 {
177  LOG(VB_PLAYBACK, LOG_INFO, QString("DecoderHandler: Stopping decoder"));
178 
179  if (m_decoder && m_decoder->isRunning())
180  {
181  m_decoder->lock();
182  m_decoder->stop();
183  m_decoder->unlock();
184  }
185 
186  if (m_decoder)
187  {
188  m_decoder->lock();
189  m_decoder->cond()->wakeAll();
190  m_decoder->unlock();
191  }
192 
193  if (m_decoder)
194  {
195  m_decoder->wait();
196  delete m_decoder;
197  m_decoder = nullptr;
198  }
199 
200  doOperationStop();
201 
202  m_state = STOPPED;
203 }
204 
205 void DecoderHandler::customEvent(QEvent *event)
206 {
207  if (event == nullptr)
208  return;
209 
210  if (auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event))
211  {
212  // Proxy all DecoderHandlerEvents
213  dispatch(*dhe);
214  return;
215  }
216  if (event->type() == MythEvent::kMythEventMessage)
217  {
218  auto *me = dynamic_cast<MythEvent *>(event);
219  if (me == nullptr)
220  return;
221 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
222  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
223 #else
224  QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
225 #endif
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  QString downloadUrl = args[0];
240  int fileSize = args[2].toInt();
241  int errorCode = args[4].toInt();
242  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);
249  m_state = STOPPED;
250  doOperationStop();
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 
263 void DecoderHandler::createPlaylist(const QUrl &url)
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  {
324  doOperationStop();
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()));
328  m_state = STOPPED;
329  }
330 
331  QCoreApplication::processEvents();
332  usleep(500);
333  }
334 }
335 
336 void 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  if ((m_decoder = Decoder::create(format, nullptr, true)) == nullptr)
347  {
348  doFailed(url, QString("No decoder for this format '%1'").arg(format));
349  return;
350  }
351  }
352 
353  m_decoder->setURL(url.toString());
354 
356  dispatch(ev);
357 }
358 
359 void DecoderHandler::doFailed(const QUrl &url, const QString &message)
360 {
361  LOG(VB_NETWORK, LOG_ERR,
362  QString("DecoderHandler error: '%1' - %2").arg(message, url.toString()));
363  DecoderHandlerEvent ev(DecoderHandlerEvent::kError, new QString(message));
364  dispatch(ev);
365 }
366 
367 void DecoderHandler::doOperationStart(const QString &name)
368 {
369  m_op = true;
371  dispatch(ev);
372 }
373 
375 {
376  if (!m_op)
377  return;
378 
379  m_op = false;
381  dispatch(ev);
382 }
build_compdb.args
args
Definition: build_compdb.py:11
DecoderFactory::supports
virtual bool supports(const QString &source) const =0
MusicMetadata::Filename
QString Filename(bool find=true)
Definition: musicmetadata.cpp:959
DecoderHandler::m_url
QUrl m_url
Definition: decoderhandler.h:125
MythEvent::kMythEventMessage
static const Type kMythEventMessage
Definition: mythevent.h:79
DecoderHandler::LOADING
@ LOADING
Definition: decoderhandler.h:85
DecoderHandler::doConnectDecoder
void doConnectDecoder(const QUrl &url, const QString &format)
Definition: decoderhandler.cpp:336
Decoder::factory
DecoderFactory * factory() const
Definition: decoder.h:79
mythrandom.h
DecoderHandlerEvent::m_available
int m_available
Definition: decoderhandler.h:64
decoderhandler.h
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
DecoderHandlerEvent::clone
MythEvent * clone(void) const override
Definition: decoderhandler.cpp:45
PlayListFile::get
PlayListFileEntry * get(int i)
Get a file entry.
Definition: pls.h:60
PlayListFileEntry::File
QString File(void)
Definition: pls.h:25
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
MythDownloadManager::queueDownload
void queueDownload(const QString &url, const QString &dest, QObject *caller, bool reload=false)
Adds a url to the download queue.
Definition: mythdownloadmanager.cpp:393
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MusicMetadata
Definition: musicmetadata.h:80
build_compdb.file
file
Definition: build_compdb.py:55
mythdirs.h
MythObservable::dispatch
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
Definition: mythobservable.cpp:73
DecoderHandlerEvent::~DecoderHandlerEvent
~DecoderHandlerEvent() override
Definition: decoderhandler.cpp:39
DecoderHandler::m_playlistPos
int m_playlistPos
Definition: decoderhandler.h:121
DecoderHandler::next
bool next(void)
Definition: decoderhandler.cpp:137
Decoder::cond
QWaitCondition * cond()
Definition: decoder.h:89
DecoderHandlerEvent::m_meta
MusicMetadata * m_meta
Definition: decoderhandler.h:63
MythEvent::Message
const QString & Message() const
Definition: mythevent.h:65
DecoderHandlerEvent::m_msg
QString * m_msg
Definition: decoderhandler.h:62
DecoderHandler::error
void error(const QString &e)
Definition: decoderhandler.cpp:116
PlayListFileEntry
Class for representing entries in a pls file.
Definition: pls.h:19
DecoderHandlerEvent
Events sent by the DecoderHandler and it's helper classes.
Definition: decoderhandler.h:25
mythlogging.h
DecoderHandlerEvent::getBufferStatus
void getBufferStatus(int *available, int *maxSize) const
Definition: decoderhandler.cpp:61
Decoder::create
static Decoder * create(const QString &source, AudioOutput *output, bool deletable=false)
Definition: decoder.cpp:99
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:256
Decoder::unlock
virtual void unlock(void)
Definition: decoder.h:86
remotefile.h
PlayListFile::parse
static int parse(PlayListFile *pls, const QString &filename)
Parse a pls, m3u or asx playlist file.
Definition: pls.cpp:34
DecoderHandlerEvent::kOperationStart
static const Type kOperationStart
Definition: decoderhandler.h:49
DecoderHandler::m_meta
MusicMetadata m_meta
Definition: decoderhandler.h:124
DecoderHandler::createPlaylistForSingleFile
void createPlaylistForSingleFile(const QUrl &url)
Definition: decoderhandler.cpp:283
Decoder::stop
virtual void stop()=0
PlayListFileEntry::setFile
void setFile(const QString &f)
Definition: pls.h:29
DecoderHandler::createPlaylist
void createPlaylist(const QUrl &url)
Definition: decoderhandler.cpp:263
DecoderHandler::m_decoder
Decoder * m_decoder
Definition: decoderhandler.h:123
MythDownloadManager::cancelDownload
void cancelDownload(const QString &url, bool block=true)
Cancel a queued or current download.
Definition: mythdownloadmanager.cpp:1014
DecoderHandler::customEvent
void customEvent(QEvent *e) override
Definition: decoderhandler.cpp:205
DecoderHandlerEvent::kOperationStop
static const Type kOperationStop
Definition: decoderhandler.h:50
DecoderHandlerEvent::kMeta
static const Type kMeta
Definition: decoderhandler.h:47
PlayListFile::size
int size(void) const
Get the number of entries in the pls file.
Definition: pls.h:54
PlayListFile::clear
void clear(void)
Clear out all the entries.
Definition: pls.h:83
DecoderHandler::m_state
int m_state
Definition: decoderhandler.h:120
Decoder::setURL
void setURL(const QString &url)
Definition: decoder.h:83
DecoderHandlerEvent::DecoderHandlerEvent
DecoderHandlerEvent(Type type)
Definition: decoderhandler.h:28
DecoderHandler::start
void start(MusicMetadata *mdata)
Definition: decoderhandler.cpp:74
DecoderHandler::doOperationStop
void doOperationStop(void)
Definition: decoderhandler.cpp:374
DecoderHandler::done
bool done(void)
Definition: decoderhandler.cpp:123
DecoderHandler::m_op
bool m_op
Definition: decoderhandler.h:126
DecoderHandler::STOPPED
@ STOPPED
Definition: decoderhandler.h:86
DecoderHandler::~DecoderHandler
~DecoderHandler(void) override
Definition: decoderhandler.cpp:69
mythcorecontext.h
DecoderHandler::doStart
void doStart(bool result)
Definition: decoderhandler.cpp:91
MusicMetadata::Format
QString Format() const
Definition: musicmetadata.h:236
DecoderHandler::doFailed
void doFailed(const QUrl &url, const QString &message)
Definition: decoderhandler.cpp:359
DecoderHandlerEvent::kError
static const Type kError
Definition: decoderhandler.h:51
DecoderHandler::m_redirects
uint m_redirects
Definition: decoderhandler.h:127
DecoderHandlerEvent::kBufferStatus
static const Type kBufferStatus
Definition: decoderhandler.h:48
DecoderHandlerEvent::kReady
static const Type kReady
Definition: decoderhandler.h:46
DecoderHandler::createPlaylistFromRemoteUrl
void createPlaylistFromRemoteUrl(const QUrl &url)
Definition: decoderhandler.cpp:306
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:263
DecoderHandlerEvent::m_maxSize
int m_maxSize
Definition: decoderhandler.h:65
DecoderHandler::ACTIVE
@ ACTIVE
Definition: decoderhandler.h:84
DecoderHandler::stop
void stop(void)
Definition: decoderhandler.cpp:175
Decoder::lock
virtual void lock(void)
Definition: decoder.h:85
PlayListFile::add
void add(PlayListFileEntry *e)
Add a entry to the playlist.
Definition: pls.h:80
DecoderHandler::m_playlist
PlayListFile m_playlist
Definition: decoderhandler.h:122
mythdownloadmanager.h
build_compdb.filename
filename
Definition: build_compdb.py:21
DecoderHandler::doOperationStart
void doOperationStart(const QString &name)
Definition: decoderhandler.cpp:367
DecoderHandler::createPlaylistFromFile
void createPlaylistFromFile(const QUrl &url)
Definition: decoderhandler.cpp:297
MythRandomStd::MythRandom
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
musicmetadata.h
decoder.h
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145