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 <QUrl>
9 #include <QFileInfo>
10 
11 // mythtv
12 #include <mythdownloadmanager.h>
13 #include <mythdirs.h>
14 #include <mythlogging.h>
15 #include <compat.h> // For random() on MINGW32
16 #include <remotefile.h>
17 #include <mythcorecontext.h>
18 #include <musicmetadata.h>
19 
20 
21 // mythmusic
22 #include "decoderhandler.h"
23 #include "decoder.h"
24 
25 /**********************************************************************/
26 
27 QEvent::Type DecoderHandlerEvent::Ready = (QEvent::Type) QEvent::registerEventType();
28 QEvent::Type DecoderHandlerEvent::Meta = (QEvent::Type) QEvent::registerEventType();
29 QEvent::Type DecoderHandlerEvent::BufferStatus = (QEvent::Type) QEvent::registerEventType();
30 QEvent::Type DecoderHandlerEvent::OperationStart = (QEvent::Type) QEvent::registerEventType();
31 QEvent::Type DecoderHandlerEvent::OperationStop = (QEvent::Type) QEvent::registerEventType();
32 QEvent::Type DecoderHandlerEvent::Error = (QEvent::Type) QEvent::registerEventType();
33 
35  : MythEvent(type)
36 {
37  m_meta = new MusicMetadata(meta);
38 }
39 
41 {
42  delete m_msg;
43  delete m_meta;
44 }
45 
47 {
48  auto *result = new DecoderHandlerEvent(*this);
49 
50  if (m_msg)
51  result->m_msg = new QString(*m_msg);
52 
53  if (m_meta)
54  result->m_meta = new MusicMetadata(*m_meta);
55 
56  result->m_available = m_available;
57  result->m_maxSize = m_maxSize;
58 
59  return result;
60 }
61 
62 void DecoderHandlerEvent::getBufferStatus(int *available, int *maxSize) const
63 {
64  *available = m_available;
65  *maxSize = m_maxSize;
66 }
67 
68 /**********************************************************************/
69 
71 {
72  stop();
73 }
74 
76 {
77  m_state = LOADING;
78 
79  m_playlist.clear();
80  m_meta = *mdata;
81  m_playlistPos = -1;
82  m_redirects = 0;
83 
84  if (QFileInfo(mdata->Filename()).isAbsolute())
85  m_url = QUrl::fromLocalFile(mdata->Filename());
86  else
87  m_url.setUrl(mdata->Filename());
88 
90 }
91 
92 void DecoderHandler::doStart(bool result)
93 {
95 
96  if (QFileInfo(m_meta.Filename()).isAbsolute())
97  m_url = QUrl::fromLocalFile(m_meta.Filename());
98  else
99  m_url.setUrl(m_meta.Filename());
100 
101  if (m_state == LOADING && result)
102  {
103  for (int ii = 0; ii < m_playlist.size(); ii++)
104  LOG(VB_PLAYBACK, LOG_INFO, QString("Track %1 = %2")
105  .arg(ii) .arg(m_playlist.get(ii)->File()));
106  next();
107  }
108  else
109  {
110  if (m_state == STOPPED)
111  {
112  doFailed(m_url, "Could not get playlist");
113  }
114  }
115 }
116 
117 void DecoderHandler::error(const QString &e)
118 {
119  auto *str = new QString(e);
121  dispatch(ev);
122 }
123 
125 {
126  if (m_state == STOPPED)
127  return true;
128 
129  if (m_playlistPos + 1 >= m_playlist.size())
130  {
131  m_state = STOPPED;
132  return true;
133  }
134 
135  return false;
136 }
137 
139 {
140  if (done())
141  return false;
142 
143  if (m_meta.Format() == "cast")
144  {
146  }
147  else
148  {
149  m_playlistPos++;
150  }
151 
153 
154  if (QFileInfo(entry->File()).isAbsolute())
155  m_url = QUrl::fromLocalFile(entry->File());
156  else
157  m_url.setUrl(entry->File());
158 
159  LOG(VB_PLAYBACK, LOG_INFO, QString("Now playing '%1'").arg(m_url.toString()));
160 
161  // we use the avfdecoder for everything except CD tracks
162  if (m_url.toString().endsWith(".cda"))
163  doConnectDecoder(m_url, ".cda");
164  else
165  {
166  // we don't know what format radio stations are so fake a format
167  // and hope avfdecoder can decode it
168  doConnectDecoder(m_url, ".mp3");
169  }
170 
171  m_state = ACTIVE;
172 
173  return true;
174 }
175 
177 {
178  LOG(VB_PLAYBACK, LOG_INFO, QString("DecoderHandler: Stopping decoder"));
179 
180  if (m_decoder && m_decoder->isRunning())
181  {
182  m_decoder->lock();
183  m_decoder->stop();
184  m_decoder->unlock();
185  }
186 
187  if (m_decoder)
188  {
189  m_decoder->lock();
190  m_decoder->cond()->wakeAll();
191  m_decoder->unlock();
192  }
193 
194  if (m_decoder)
195  {
196  m_decoder->wait();
197  delete m_decoder;
198  m_decoder = nullptr;
199  }
200 
201  doOperationStop();
202 
203  m_state = STOPPED;
204 }
205 
206 void DecoderHandler::customEvent(QEvent *event)
207 {
208  if (event == nullptr)
209  return;
210 
211  if (auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event))
212  {
213  // Proxy all DecoderHandlerEvents
214  dispatch(*dhe);
215  return;
216  }
217  if (event->type() == MythEvent::MythEventMessage)
218  {
219  auto *me = dynamic_cast<MythEvent *>(event);
220  if (me == nullptr)
221  return;
222 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
223  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
224 #else
225  QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
226 #endif
227 
228  if (tokens.isEmpty())
229  return;
230 
231  if (tokens[0] == "DOWNLOAD_FILE")
232  {
233  QStringList args = me->ExtraDataList();
234 
235  if (tokens[1] == "UPDATE")
236  {
237  }
238  else if (tokens[1] == "FINISHED")
239  {
240  QString downloadUrl = args[0];
241  int fileSize = args[2].toInt();
242  int errorCode = args[4].toInt();
243  QString filename = args[1];
244 
245  if ((errorCode != 0) || (fileSize == 0))
246  {
247  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler: failed to download playlist from '%1'")
248  .arg(downloadUrl));
249  QUrl url(downloadUrl);
250  m_state = STOPPED;
251  doOperationStop();
252  doFailed(url, "Could not get playlist");
253  }
254  else
255  {
256  QUrl fileUrl = QUrl::fromLocalFile(filename);
257  createPlaylistFromFile(fileUrl);
258  }
259  }
260  }
261  }
262 }
263 
264 void DecoderHandler::createPlaylist(const QUrl &url)
265 {
266  QString extension = QFileInfo(url.path()).suffix();
267  LOG(VB_NETWORK, LOG_INFO,
268  QString("File %1 has extension %2")
269  .arg(QFileInfo(url.path()).fileName(), extension));
270 
271  if (extension == "pls" || extension == "m3u" || extension == "asx")
272  {
273  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
275  else
277 
278  return;
279  }
280 
282 }
283 
285 {
286  auto *entry = new PlayListFileEntry;
287 
288  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
289  entry->setFile(url.toLocalFile());
290  else
291  entry->setFile(url.toString());
292 
293  m_playlist.add(entry);
294 
295  doStart((m_playlist.size() > 0));
296 }
297 
299 {
300  QString file = url.toLocalFile();
301 
303 
304  doStart((m_playlist.size() > 0));
305 }
306 
308 {
309  LOG(VB_NETWORK, LOG_INFO,
310  QString("Retrieving playlist from '%1'").arg(url.toString()));
311 
312  doOperationStart(tr("Retrieving playlist"));
313 
314  QString extension = QFileInfo(url.path()).suffix().toLower();
315  QString saveFilename = GetConfDir() + "/MythMusic/playlist." + extension;
316  GetMythDownloadManager()->queueDownload(url.toString(), saveFilename, this);
317 
318  //TODO should find a better way to do this
319  QElapsedTimer time;
320  time.start();
321  while (m_state == LOADING)
322  {
323  if (time.hasExpired(30000))
324  {
325  doOperationStop();
326  GetMythDownloadManager()->cancelDownload(url.toString());
327  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler:: Timed out trying to download playlist from: %1")
328  .arg(url.toString()));
329  m_state = STOPPED;
330  }
331 
332  QCoreApplication::processEvents();
333  usleep(500);
334  }
335 }
336 
337 void DecoderHandler::doConnectDecoder(const QUrl &url, const QString &format)
338 {
339  if (m_decoder && !m_decoder->factory()->supports(format))
340  {
341  delete m_decoder;
342  m_decoder = nullptr;
343  }
344 
345  if (!m_decoder)
346  {
347  if ((m_decoder = Decoder::create(format, nullptr, true)) == 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 
360 void 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::Error, new QString(message));
365  dispatch(ev);
366 }
367 
368 void 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 }
DecoderHandlerEvent::Ready
static Type Ready
Definition: decoderhandler.h:47
DecoderHandlerEvent::BufferStatus
static Type BufferStatus
Definition: decoderhandler.h:49
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:965
MythEvent::MythEventMessage
static Type MythEventMessage
Definition: mythevent.h:78
DecoderHandler::m_url
QUrl m_url
Definition: decoderhandler.h:126
DecoderHandlerEvent::OperationStart
static Type OperationStart
Definition: decoderhandler.h:50
DecoderHandler::LOADING
@ LOADING
Definition: decoderhandler.h:86
DecoderHandler::doConnectDecoder
void doConnectDecoder(const QUrl &url, const QString &format)
Definition: decoderhandler.cpp:337
Decoder::factory
DecoderFactory * factory() const
Definition: decoder.h:79
DecoderHandlerEvent::m_available
int m_available
Definition: decoderhandler.h:65
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:303
DecoderHandlerEvent::clone
MythEvent * clone(void) const override
Definition: decoderhandler.cpp:46
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:23
MusicMetadata
Definition: musicmetadata.h:80
DecoderHandlerEvent::Meta
static Type Meta
Definition: decoderhandler.h:48
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:40
DecoderHandler::m_playlistPos
int m_playlistPos
Definition: decoderhandler.h:122
DecoderHandler::next
bool next(void)
Definition: decoderhandler.cpp:138
Decoder::cond
QWaitCondition * cond()
Definition: decoder.h:89
DecoderHandlerEvent::m_meta
MusicMetadata * m_meta
Definition: decoderhandler.h:64
MythEvent::Message
const QString & Message() const
Definition: mythevent.h:65
DecoderHandlerEvent::m_msg
QString * m_msg
Definition: decoderhandler.h:63
DecoderHandler::error
void error(const QString &e)
Definition: decoderhandler.cpp:117
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:26
mythlogging.h
DecoderHandlerEvent::getBufferStatus
void getBufferStatus(int *available, int *maxSize) const
Definition: decoderhandler.cpp:62
Decoder::create
static Decoder * create(const QString &source, AudioOutput *output, bool deletable=false)
Definition: decoder.cpp:100
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
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
compat.h
DecoderHandler::m_meta
MusicMetadata m_meta
Definition: decoderhandler.h:125
DecoderHandler::createPlaylistForSingleFile
void createPlaylistForSingleFile(const QUrl &url)
Definition: decoderhandler.cpp:284
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:264
DecoderHandler::m_decoder
Decoder * m_decoder
Definition: decoderhandler.h:124
MythDownloadManager::cancelDownload
void cancelDownload(const QString &url, bool block=true)
Cancel a queued or current download.
Definition: mythdownloadmanager.cpp:1013
DecoderHandler::customEvent
void customEvent(QEvent *e) override
Definition: decoderhandler.cpp:206
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:121
Decoder::setURL
void setURL(const QString &url)
Definition: decoder.h:83
DecoderHandlerEvent::DecoderHandlerEvent
DecoderHandlerEvent(Type type)
Definition: decoderhandler.h:29
DecoderHandler::start
void start(MusicMetadata *mdata)
Definition: decoderhandler.cpp:75
DecoderHandlerEvent::Error
static Type Error
Definition: decoderhandler.h:52
DecoderHandler::doOperationStop
void doOperationStop(void)
Definition: decoderhandler.cpp:375
DecoderHandler::done
bool done(void)
Definition: decoderhandler.cpp:124
DecoderHandler::m_op
bool m_op
Definition: decoderhandler.h:127
DecoderHandler::STOPPED
@ STOPPED
Definition: decoderhandler.h:87
DecoderHandler::~DecoderHandler
~DecoderHandler(void) override
Definition: decoderhandler.cpp:70
DecoderHandlerEvent::OperationStop
static Type OperationStop
Definition: decoderhandler.h:51
mythcorecontext.h
random
static long int random(void)
Definition: compat.h:161
DecoderHandler::doStart
void doStart(bool result)
Definition: decoderhandler.cpp:92
MusicMetadata::Format
QString Format() const
Definition: musicmetadata.h:237
DecoderHandler::doFailed
void doFailed(const QUrl &url, const QString &message)
Definition: decoderhandler.cpp:360
DecoderHandler::m_redirects
uint m_redirects
Definition: decoderhandler.h:128
DecoderHandler::createPlaylistFromRemoteUrl
void createPlaylistFromRemoteUrl(const QUrl &url)
Definition: decoderhandler.cpp:307
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:266
DecoderHandlerEvent::m_maxSize
int m_maxSize
Definition: decoderhandler.h:66
DecoderHandler::ACTIVE
@ ACTIVE
Definition: decoderhandler.h:85
DecoderHandler::stop
void stop(void)
Definition: decoderhandler.cpp:176
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:123
mythdownloadmanager.h
build_compdb.filename
filename
Definition: build_compdb.py:21
DecoderHandler::doOperationStart
void doOperationStart(const QString &name)
Definition: decoderhandler.cpp:368
DecoderHandler::createPlaylistFromFile
void createPlaylistFromFile(const QUrl &url)
Definition: decoderhandler.cpp:298
musicmetadata.h
decoder.h
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145